search

package
v0.1.151 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: GPL-2.0, GPL-3.0 Imports: 24 Imported by: 0

Documentation

Overview

Package search implements the subtitle search engine.

orchestrate.go: search pipeline orchestration (lang-group search, variant processing) orchestrate_filter.go: eligibility checks, variant/score filtering, provider filtering provider_sweep.go: provider sweep, singleflight dedup, download target_state.go: target state building, decision logic, backoff

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrProviderNotFound indicates the provider name doesn't match any registered provider.
	ErrProviderNotFound = errors.New("provider not found")
	// ErrEmptyResponse indicates the provider responded with zero bytes.
	ErrEmptyResponse = errors.New("provider returned empty data")
	// ErrInvalidContent indicates the provider returned non-subtitle content.
	ErrInvalidContent = errors.New("provider returned invalid data")
)

Functions

func IgnoredCodecsFromConfig

func IgnoredCodecsFromConfig(cfg interface{ EmbeddedPolicy() api.EmbeddedPolicy }) map[string]bool

IgnoredCodecsFromConfig builds the set of embedded codecs that should be treated as "present but not usable" from the typed embedded_subtitles policy. This is the ONE resolver every consumer (engine + server handlers) goes through. Accepts any type that provides EmbeddedPolicy() (satisfied by both api.ConfigProvider and search.SearchCfg).

Types

type CoverageRecorder

type CoverageRecorder interface {
	RecordSubtitleFiles(ctx context.Context, mediaType api.MediaType, mediaID string, files []api.SubtitleFile) (bool, error)
	UpsertSubtitleFile(ctx context.Context, mediaType api.MediaType, mediaID string, f *api.SubtitleFile) error
	SetSyncOffset(ctx context.Context, path string, offsetMs int64) error
	RecordScanState(ctx context.Context, rec *api.ScanRecord) error
}

CoverageRecorder is the narrow store interface for coverage tracking: subtitle file recording, sync offsets, and scan state. Consumed by SearchTargets and downloadAndSave.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine coordinates subtitle searches.

func New

func New(providers []api.Provider, opts ...Option) *Engine

New creates a search engine. The providers slice is required; all other dependencies are supplied via functional options.

func (*Engine) HashFile

func (e *Engine) HashFile(ctx context.Context, path string) (hash string, size int64, err error)

HashFile computes the OpenSubtitles hash and file size for a video file. Concurrent calls for the same path are deduplicated via singleflight.

func (*Engine) InventoryCoverage added in v0.1.148

func (e *Engine) InventoryCoverage(ctx context.Context, req *api.SearchRequest, videoPath string) bool

InventoryCoverage implements the local-only half of api.SubtitleSearcher: it refreshes the on-disk/embedded subtitle inventory for a media item and stamps its scan state as inventoried-not-searched, with zero provider work. Scan skip paths (season early stop, show-level skip) call this so coverage badges stay truthful for items the scanner deliberately does not search: "skip" means skip PROVIDER work, not local bookkeeping.

func (*Engine) ProviderTimeouts

func (e *Engine) ProviderTimeouts() (map[api.ProviderID]api.ProviderStatus, bool)

ProviderTimeouts returns a snapshot of all provider timeout states. Returns (status, true) if timeouts are enabled, (nil, false) otherwise.

func (*Engine) ResetTimeouts

func (e *Engine) ResetTimeouts()

ResetTimeouts clears all provider timeout state and re-enables all providers.

func (*Engine) ScoreSubtitles

func (e *Engine) ScoreSubtitles(req *api.SearchRequest, results []api.Subtitle) []api.ScoredResult

ScoreSubtitles scores and ranks subtitles for a search request.

func (*Engine) SearchTargets

func (e *Engine) SearchTargets(ctx context.Context, req *api.SearchRequest,
	videoPath string, targets []api.SubtitleTarget,
) (api.SearchResult, error)

SearchTargets searches for subtitles using resolved SubtitleTargets. Always searches for regular (non-HI, non-forced) subs, with HI as fallback. Respects per-target provider filtering and min scores.

func (*Engine) SimulateScore

func (e *Engine) SimulateScore(mediaType api.MediaType, videoRelease, subRelease string, matchedBy api.MatchMethod) api.ScoreResult

SimulateScore simulates scoring a subtitle against a video using release names.

func (*Engine) SyncAndPostProcess

func (e *Engine) SyncAndPostProcess(ctx context.Context, data []byte,
	videoPath, lang string, variant api.Variant,
) (synced []byte, offsetMs int64)

SyncAndPostProcess syncs subtitle timing and normalizes content using the user's configured SyncConfig. It is the single entry point used by manual downloads and any caller that wants the full "match the auto path" behavior:

  1. If sync_subtitles is enabled, try reference-based sync (embedded SRT track extracted from the video container).
  2. If that produces no offset and audio_sync_fallback is enabled, try audio-based sync (PCM extraction + VAD correlation).
  3. Apply the configured post-processing (HI strip, tag strip, encoding normalization, line-ending normalization, whitespace cleanup).

The variant argument selects the same StripHI policy as the auto path's postProcessSub: when the caller is saving an HI-variant file the global strip_hi setting is overridden to false, because the user deliberately asked for HI annotations. For any other variant (standard/forced) the configured strip_hi value is honored. Callers that need just the timing step without post-processing should use syncSubtitle directly.

type FileWriter

type FileWriter interface {
	WriteFile(ctx context.Context, path string, data []byte) error
}

FileWriter abstracts atomic file writes, decoupling the search engine from the concrete atomicfile implementation. The default wraps atomicfile.WriteFile; tests can inject a stub that records writes without touching disk.

type NoopDetector added in v0.1.148

type NoopDetector struct{}

NoopDetector is an explicit no-detection TrackDetector for callers that have no use for embedded track inspection (search.New requires a detector, so "none" must be said out loud rather than left nil).

func (NoopDetector) DetectTracks added in v0.1.148

func (NoopDetector) DetectTracks(_ context.Context, _ string) ([]api.EmbeddedTrack, error)

DetectTracks reports no embedded tracks.

type Option

type Option func(*Engine)

Option configures the search Engine.

func WithConfig

func WithConfig(c SearchCfg) Option

WithConfig sets the search configuration.

func WithMetrics

func WithMetrics(m SearchMetrics) Option

WithMetrics sets the metrics recorder.

func WithScorer

func WithScorer(s api.Scorer) Option

WithScorer sets the subtitle scorer.

func WithStore

func WithStore(s SearchStore) Option

WithStore sets the search store.

func WithSyncExec added in v0.1.148

func WithSyncExec(x syncing.SyncExec) Option

WithSyncExec sets the executor for the engine's own heavy sync calls (the audio fallback). Defaults to in-process; server mode installs the sync-worker client so alignment memory lives in a disposable child (P13).

func WithSyncer

func WithSyncer(s SubtitleSyncer) Option

WithSyncer sets the subtitle syncer.

func WithTimeout

func WithTimeout(h timeout.ProviderHealth) Option

WithTimeout sets the provider health tracker. When not set, the engine constructs one from config (or uses noopHealth if disabled).

func WithTracks

func WithTracks(t TrackDetector) Option

WithTracks sets the track detector.

type SearchCfg

type SearchCfg interface {
	// Scores returns the scoring weights. Must return non-zero values;
	// zero scores disable all attribute matching.
	Scores() api.Scores
	// Search returns the top-level search configuration (concurrency, etc.).
	Search() api.SearchConfig
	// Adaptive returns the adaptive search configuration.
	Adaptive() api.AdaptiveConfig
	// SyncConfig returns subtitle sync/timing configuration.
	SyncConfig() api.SyncConfig
	// PostProcessConfig returns post-processing settings.
	PostProcessConfig() api.PostProcessConfig
	// ProvidersForTarget returns provider names allowed for this target.
	// Empty slice means no providers will be searched for this target.
	ProvidersForTarget(t *api.SubtitleTarget, allProviders []api.ProviderID) []api.ProviderID
	// MinScoreForTarget returns the minimum acceptable score for a target.
	// Returns 0 to accept any score.
	MinScoreForTarget(t *api.SubtitleTarget, mediaType api.MediaType) int
	// ProviderPriority returns the priority of a provider by name.
	// Returns 0 for unknown providers (lowest priority = tried last in tiebreakers).
	ProviderPriority(name api.ProviderID) int
	// EmbeddedPolicy returns the typed embedded subtitle codec policy
	// (top-level embedded_subtitles config section).
	EmbeddedPolicy() api.EmbeddedPolicy
}

SearchCfg is the narrow configuration interface consumed by the search engine. Only the methods actually called by search are declared here. The concrete config.Config satisfies this via structural typing.

type SearchFlowStore

type SearchFlowStore interface {
	RecordNoResult(ctx context.Context, mediaType api.MediaType, mediaID, language string, providerName api.ProviderID, bp api.BackoffParams) error
	BackedOffProviders(ctx context.Context, mediaType api.MediaType, mediaID, language string, maxAttempts int) ([]api.ProviderID, error)
	SaveDownload(ctx context.Context, rec *api.DownloadRecord) error
	CurrentScore(ctx context.Context, mediaType api.MediaType, mediaID, language string, variant api.Variant) (score int, mediaImported time.Time, found bool, err error)
	IsManuallyLocked(ctx context.Context, mediaType api.MediaType, mediaID, language string, variant api.Variant) (bool, error)
}

SearchFlowStore is the narrow store interface for search flow operations: backoff tracking, download recording, score queries, and manual locks. Consumed by orchestrate.go and search_download.go.

type SearchMetrics

type SearchMetrics interface {
	RecordSearch(provider api.ProviderID, dur time.Duration, err error)
	RecordDownload(provider api.ProviderID, err error)
	AdaptiveSkip()
	// RecordEmbeddedDetectorError counts a failed embedded track probe
	// (subflux_embedded_detector_errors_total). Context cancellation is
	// excluded by the caller.
	RecordEmbeddedDetectorError()
}

SearchMetrics is the narrow observability interface consumed by the search engine. Only the 4 methods actually called are required; the concrete *metrics.Metrics satisfies this via structural typing.

type SearchStore

type SearchStore interface {
	SearchFlowStore
	CoverageRecorder
}

SearchStore is the composite store interface consumed by the search engine. It combines SearchFlowStore (backoff + download + lock) and CoverageRecorder (file tracking + scan state). The concrete store.DB satisfies this via structural typing.

type SubtitleSyncer

type SubtitleSyncer interface {
	// Sync adjusts subtitle timing against a reference (embedded, external, or audio).
	// Returns the (possibly modified) data and the applied offset in milliseconds
	// (0 if no sync was applied or confidence was too low).
	Sync(ctx context.Context, data []byte, videoPath, lang string) (synced []byte, offsetMs int64)

	// PostProcess applies encoding normalization, HI removal, tag stripping, etc.
	PostProcess(data []byte, pp api.PostProcessConfig) []byte
}

SubtitleSyncer synchronizes subtitle timing and applies post-processing.

type TrackDetector

type TrackDetector interface {
	// DetectTracks returns ALL normalized embedded subtitle tracks in the
	// given video file (including bitmap formats), or an error when the
	// probe fails (ffprobe error, corrupt file, timeout). The engine owns
	// the error policy: log/metric observability, fail-open search, and
	// skipping the coverage replacement so a failed probe never deletes
	// persisted rows. (nil, nil) means "no tracks", distinct from an error.
	DetectTracks(ctx context.Context, videoPath string) ([]api.EmbeddedTrack, error)
}

TrackDetector detects embedded subtitle tracks in video containers. Implemented by internal/embedded's ffprobe-backed Detector.

Directories

Path Synopsis
Package release provides release name parsing via PCRE-compatible regex patterns sourced from TRaSH Guides and Sonarr/Radarr QualityParser.
Package release provides release name parsing via PCRE-compatible regex patterns sourced from TRaSH Guides and Sonarr/Radarr QualityParser.
Package scoring provides pure subtitle scoring and identity filtering.
Package scoring provides pure subtitle scoring and identity filtering.
Package syncing provides subtitle timing synchronization for the search engine.
Package syncing provides subtitle timing synchronization for the search engine.
Package timeout provides provider health tracking with sliding-window failure detection and cooldown-based timeout.
Package timeout provides provider health tracking with sliding-window failure detection and cooldown-based timeout.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL