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 ¶
- Variables
- func IgnoredCodecsFromConfig(cfg interface{ ... }) map[string]bool
- type CoverageRecorder
- type Engine
- func (e *Engine) HashFile(ctx context.Context, path string) (hash string, size int64, err error)
- func (e *Engine) InventoryCoverage(ctx context.Context, req *api.SearchRequest, videoPath string) bool
- func (e *Engine) ProviderTimeouts() (map[api.ProviderID]api.ProviderStatus, bool)
- func (e *Engine) ResetTimeouts()
- func (e *Engine) ScoreSubtitles(req *api.SearchRequest, results []api.Subtitle) []api.ScoredResult
- func (e *Engine) SearchTargets(ctx context.Context, req *api.SearchRequest, videoPath string, ...) (api.SearchResult, error)
- func (e *Engine) SimulateScore(mediaType api.MediaType, videoRelease, subRelease string, ...) api.ScoreResult
- func (e *Engine) SyncAndPostProcess(ctx context.Context, data []byte, videoPath, lang string, variant api.Variant) (synced []byte, offsetMs int64)
- type FileWriter
- type NoopDetector
- type Option
- func WithConfig(c SearchCfg) Option
- func WithMetrics(m SearchMetrics) Option
- func WithScorer(s api.Scorer) Option
- func WithStore(s SearchStore) Option
- func WithSyncExec(x syncing.SyncExec) Option
- func WithSyncer(s SubtitleSyncer) Option
- func WithTimeout(h timeout.ProviderHealth) Option
- func WithTracks(t TrackDetector) Option
- type SearchCfg
- type SearchFlowStore
- type SearchMetrics
- type SearchStore
- type SubtitleSyncer
- type TrackDetector
Constants ¶
This section is empty.
Variables ¶
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 ¶
New creates a search engine. The providers slice is required; all other dependencies are supplied via functional options.
func (*Engine) HashFile ¶
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:
- If sync_subtitles is enabled, try reference-based sync (embedded SRT track extracted from the video container).
- If that produces no offset and audio_sync_fallback is enabled, try audio-based sync (PCM extraction + VAD correlation).
- 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 ¶
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 WithSyncExec ¶ added in v0.1.148
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 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).
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.
Source Files
¶
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. |