Documentation
¶
Overview ¶
Package api defines the internal contracts between subflux components. All cross-component calls go through these interfaces, enabling testability (mock any component) and swappability (e.g. SQLite → PostgreSQL).
This package imports only stdlib. Implementation packages import api, never the reverse.
This file contains consumer contracts: interfaces that consuming code depends on (Store, AuthStore, ConfigProvider, SearchEngine). Implementation/provider contracts live in interfaces_provider.go.
Index ¶
- Constants
- Variables
- func AudioLanguages(mi *arrapi.MediaInfo) []string
- func BadGatewayC(w http.ResponseWriter, r *http.Request, code, msg string)
- func BadRequestC(w http.ResponseWriter, r *http.Request, code, msg string)
- func BuildEpisodeID(tvdbID int, imdbID string, season, episode int) string
- func BuildMediaID(req *SearchRequest) string
- func BuildMovieID(tmdbID int, imdbID string) string
- func BuildSeriesPrefix(tvdbID int, imdbID string) string
- func ConflictC(w http.ResponseWriter, r *http.Request, code, msg string)
- func CountNonTextBytes(data []byte) int
- func ForbiddenC(w http.ResponseWriter, r *http.Request, code, msg string)
- func HasExcludeTag(tags []int, excludeIDs map[int]struct{}) bool
- func InternalErrorC(w http.ResponseWriter, r *http.Request, err error, code string, ...)
- func IsValidMediaPrefix(prefix string) bool
- func JSONError(w http.ResponseWriter, err error, code int)
- func JSONErrorWithCode(w http.ResponseWriter, r *http.Request, status int, code, msg string)
- func LangNameToISO(name string) string
- func ManualSubtitlePath(videoPath, lang string, n int, hi, forced bool) string
- func MethodNotAllowedC(w http.ResponseWriter, r *http.Request, code string)
- func NewSessionHashContext(ctx context.Context, sessHash string) context.Context
- func NewUserContext(ctx context.Context, u *auth.User) context.Context
- func NotFoundC(w http.ResponseWriter, r *http.Request, code, msg string)
- func Ok(w http.ResponseWriter)
- func OriginalLangCode(lang *arrapi.Language) string
- func ParseAudioLangs(raw string) []string
- func PayloadTooLargeC(w http.ResponseWriter, r *http.Request, code, msg string)
- func SeasonEpisodeFileCount(s *arrapi.Series, seasonNum int) int
- func ServiceUnavailableC(w http.ResponseWriter, r *http.Request, code, msg string)
- func SessionHashFromContext(ctx context.Context) string
- func SubtitlePath(videoPath, lang string, hi, forced bool) string
- func TooManyRequestsC(w http.ResponseWriter, r *http.Request, code, msg string)
- func UnauthorizedC(w http.ResponseWriter, r *http.Request, code, msg string)
- func UserFromContext(ctx context.Context) *auth.User
- func ValidateSubtitleData(data []byte) error
- func WithRequestID(ctx context.Context, id string) context.Context
- func WriteJSON(w http.ResponseWriter, v any)
- func WriteJSONStatus(w http.ResponseWriter, code int, v any)
- type AdaptiveConfig
- type AdminUserCreatedResponse
- type ArrConfig
- type ArrConfigProvider
- type AudioRuleJSON
- type AudioSyncResult
- type AuthConfigProvider
- type AuthError
- type BackoffEntry
- type BackoffParams
- type BackoffStore
- type CacheClearer
- type CleanupResult
- type ConfigDrift
- type ConfigLoader
- type ConfigProvider
- type CoverageStore
- type DownloadMeta
- type DownloadRecord
- type DownloadStore
- type DownloadedRef
- type EmbeddedTrack
- type HistoryStore
- type KeyGenerated
- type LanguageResolver
- type LanguageRulesJSON
- type LogFormat
- type LogLevel
- type LoginSuccess
- type MaintStore
- type ManualLockEntry
- type ManualLockStore
- type MatchMethod
- type MatchSet
- type MeResponse
- type MediaType
- type PasskeyRegistered
- type PathValidator
- type PollKey
- type PollStore
- type PostProcessConfig
- type Provider
- type ProviderCfg
- type ProviderConfigProvider
- type ProviderID
- type ProviderRegistry
- type ProviderSchema
- type ProviderSchemaField
- type ProviderStatus
- type ProviderTimeoutManager
- type ProvidersResponse
- type QueryStore
- type RadarrClient
- type RateLimitError
- type ReconcileResult
- type ScanOutcome
- type ScanRecord
- type ScanStateRow
- type ScanStats
- type SchemaField
- type SchemaFunc
- type SchemaOption
- type SchemaSection
- type ScorePreview
- type ScoreResult
- type ScoreSimulator
- type ScoreTier
- type ScoredResult
- type Scorer
- type Scores
- type ScoringConfig
- type SearchConfig
- type SearchConfigProvider
- type SearchEngine
- type SearchRequest
- type SearchResult
- type SearchTarget
- type SearchTargets
- type ServerConfig
- type SetupStatus
- type ShowSubtitleCounter
- type SignalData
- type SonarrClient
- type StateEntry
- type StateQuery
- type Stats
- type Store
- type Subtitle
- type SubtitleCue
- type SubtitleEntry
- type SubtitleFile
- type SubtitleInfo
- type SubtitlePostProcessor
- type SubtitleProcessor
- type SubtitleSearcher
- type SubtitleSource
- type SubtitleTargJSON
- type SubtitleTarget
- type SyncConfig
- type SyncOffsetStore
- type Transient
- type UIConfigProvider
- type Variant
- type VideoInfo
- type WebAuthnUnknownCredentialResponse
Constants ¶
const ( CodeBadRequest = "bad_request" CodeForbidden = "forbidden" CodeNotFound = "not_found" CodeMethodNotAllowed = "method_not_allowed" CodeConflict = "conflict" CodePayloadTooLarge = "payload_too_large" CodeRateLimited = "rate_limited" CodeBadGateway = "bad_gateway" CodeInternalError = "internal_error" )
Generic codes (status mapped 1:1).
const ( CodeAuthInvalidCredentials = "auth_invalid_credentials" CodeAuthAccountDisabled = "auth_account_disabled" CodeAuthAccountNotSetup = "auth_account_not_setup" CodeAuthPasswordTooShort = "auth_password_too_short" CodeAuthPasswordBreached = "auth_password_breached" CodeAuthSessionInvalid = "auth_session_invalid" CodeAuthSessionRequired = "auth_session_required" CodeAuthRoleRequired = "auth_role_required" CodeAuthAPIKeyInvalid = "auth_apikey_invalid" CodeAuthAPIKeyDisabled = "auth_apikey_disabled" CodeAuthCSRF = "auth_csrf" )
Auth codes.
const ( CodeWebAuthnSessionInvalid = "webauthn_session_invalid" CodeWebAuthnRegisterFailed = "webauthn_register_failed" CodeWebAuthnAssertionFailed = "webauthn_assertion_failed" CodeWebAuthnUnsupportedOrigin = "webauthn_unsupported_origin" )
WebAuthn codes.
const ( CodeOIDCStateInvalid = "oidc_state_invalid" CodeOIDCNonceInvalid = "oidc_nonce_invalid" CodeOIDCExchangeFailed = "oidc_exchange_failed" CodeOIDCUserInfoFailed = "oidc_userinfo_failed" CodeOIDCAccountNotProvisioned = "oidc_account_not_provisioned" )
OIDC codes.
const ( CodeSetupAlreadyComplete = "setup_already_complete" CodeSetupPasswordInvalid = "setup_password_invalid" )
Setup codes.
const ( CodeConfigInvalid = "config_invalid" CodeConfigUnreachableArr = "config_unreachable_arr" CodeConfigYAMLParse = "config_yaml_parse" CodeConfigTooLarge = "config_too_large" CodeConfigReloadFailed = "config_reload_failed" )
Config codes.
const ( CodeScanInProgress = "scan_in_progress" CodeScanNoTargets = "scan_no_targets" CodeSearchInProgress = "search_in_progress" CodeSearchProviderDisabled = "search_provider_disabled" CodeSearchNoResults = "search_no_results" CodeDownloadFailed = "download_failed" CodeUnlockNotHeld = "unlock_not_held" )
Scan / search / manual ops codes.
const ( CodePathNotAllowed = "path_not_allowed" CodeMediaNotFound = "media_not_found" CodeSubtitleNotFound = "subtitle_not_found" CodeSyncUnsupportedFormat = "sync_unsupported_format" CodeSyncNoReference = "sync_no_reference" CodeSyncLowConfidence = "sync_low_confidence" )
File / preview / sync codes.
const ( CodeQueryInvalidFilter = "query_invalid_filter" CodeQueryLimitExceeded = "query_limit_exceeded" )
Query codes.
const ( CodeProviderTimedOut = "provider_timed_out" CodeProviderNotConfigured = "provider_not_configured" CodeArrUnreachable = "arr_unreachable" )
Provider / arr action codes.
const ( // KeyStatus is the canonical JSON key for operation result status responses. // Used by server, scanning, and confighandlers packages. KeyStatus = "status" // ErrMsgSonarrNotConfigured is the canonical error message when Sonarr is nil. ErrMsgSonarrNotConfigured = "sonarr not configured" // ErrMsgRadarrNotConfigured is the canonical error message when Radarr is nil. ErrMsgRadarrNotConfigured = "radarr not configured" // HeaderXAPIKey is the canonical HTTP header name for API key authentication. // Used by both inbound (subflux API key verification) and outbound (Sonarr/Radarr) requests. HeaderXAPIKey = "X-Api-Key" //nolint:gosec // G101 false positive: header name, not a credential )
Canonical JSON response keys and sentinel error messages shared across every helper below. Extracted to silence goconst warnings and to make a wire-format rename land in one place.
const ( VariantStandard Variant = "standard" DefaultVariant = VariantStandard VariantHI Variant = "hi" VariantForced Variant = "forced" // VariantAliasSDH is the alternative name for HI subtitles used in external // subtitle filenames and provider metadata. VariantAliasSDH = "sdh" // VariantAliasForeign is the alternative name for forced subtitles used in // external subtitle filenames and provider metadata. VariantAliasForeign = "foreign" )
Subtitle variant identifiers. Shared across api, search, server, and cli so the auto and manual download paths agree on a single vocabulary.
const DefaultDownloadMaxAttempts = 3
DefaultDownloadMaxAttempts is the fallback number of download attempts per search when the config value is zero.
const DefaultManualProviderTimeout = 30 * time.Second
DefaultManualProviderTimeout is the per-provider search timeout for CLI and manual searches. Shared between clisearch and manualops to prevent silent divergence.
const DefaultProviderConcurrency = 4
DefaultProviderConcurrency is the default maximum number of parallel provider searches when the config value is zero.
const DefaultProviderPriority = 99
DefaultProviderPriority is used when no priority is configured for a provider.
const DefaultSyncMinConfidence = 0.6
DefaultSyncMinConfidence is the default minimum confidence for auto-sync to be applied. Used by config and syncing packages.
const EmbeddedSettingIgnoreASS = "ignore_ass"
EmbeddedSettingIgnoreASS is the setting key controlling ASS/SSA subtitle filtering.
const EmbeddedSettingIgnorePGS = "ignore_pgs"
EmbeddedSettingIgnorePGS is the setting key controlling PGS subtitle filtering.
const EmbeddedSettingIgnoreVobSub = "ignore_vobsub"
EmbeddedSettingIgnoreVobSub is the setting key controlling VobSub subtitle filtering.
const MaxSafeFileBytes = 10 << 20
MaxSafeFileBytes is the maximum file size (10 MB) for config files, subtitle files, and other user-supplied payloads. Prevents OOM from oversized or malicious inputs. Shared across config, subsync, and provider packages.
const SubtitleExtSRT = ".srt"
SubtitleExtSRT is the default subtitle file extension.
Variables ¶
var DefaultScores = Scores{
Hash: 100,
Source: 28,
ReleaseGroup: 23,
StreamingService: 14,
VideoCodec: 10,
HDR: 8,
Edition: 15,
SeasonPack: 15,
}
DefaultScores for release attribute matching.
Movie (98): source=28, release_group=23, streaming_service=14, edition=15, video_codec=10, hdr=8. Episode (98): source=28, release_group=23, streaming_service=14, season_pack=15, video_codec=10, hdr=8.
Hash match = 100 directly, bypasses attribute scoring.
var ErrBinaryData = errors.New("binary archive data, not a subtitle")
ErrBinaryData indicates the downloaded data is a binary archive that could not be extracted, not a subtitle file.
var SubtitleExtsOnDisk = map[string]bool{ ".srt": true, ".ass": true, ".ssa": true, ".sub": true, }
SubtitleExtsOnDisk is the set of subtitle extensions recognized as standalone files on disk (from Sonarr/Radarr libraries). This excludes .vtt which only appears inside archives.
Functions ¶
func AudioLanguages ¶ added in v0.1.106
AudioLanguages parses a file's MediaInfo audio-languages string into deduplicated ISO 639-1 codes. Returns nil when mi is nil or empty.
func BadGatewayC ¶
func BadGatewayC(w http.ResponseWriter, r *http.Request, code, msg string)
BadGatewayC writes a 502 with the given code and message.
func BadRequestC ¶
func BadRequestC(w http.ResponseWriter, r *http.Request, code, msg string)
BadRequestC writes a 400 with the given code and message.
func BuildEpisodeID ¶
BuildEpisodeID returns the canonical media ID for an episode. Prefer TVDB (Sonarr's canonical source), fall back to IMDB.
func BuildMediaID ¶
func BuildMediaID(req *SearchRequest) string
BuildMediaID creates a stable identifier for a media item from a search request. Movies use TMDB ID (canonical for Radarr), episodes use TVDB ID (canonical for Sonarr). IMDB is only used as a last resort fallback.
func BuildMovieID ¶
BuildMovieID returns the canonical media ID for a movie. Prefer TMDB (Radarr's canonical source), fall back to IMDB.
func BuildSeriesPrefix ¶
BuildSeriesPrefix returns the media ID prefix for all episodes of a series. Used by coverage and missing-count queries to match all episodes via LIKE.
func ConflictC ¶
func ConflictC(w http.ResponseWriter, r *http.Request, code, msg string)
ConflictC writes a 409 with the given code and message.
func CountNonTextBytes ¶
CountNonTextBytes returns the number of bytes in data that are not printable text (control characters below TAB, or between CR and SPACE, excluding ESC). Used by ValidateSubtitleData and archive extraction.
func ForbiddenC ¶
func ForbiddenC(w http.ResponseWriter, r *http.Request, code, msg string)
ForbiddenC writes a 403 with the given code and message.
func HasExcludeTag ¶
HasExcludeTag reports whether any of the item's tags are in the exclude set.
func InternalErrorC ¶
func InternalErrorC(w http.ResponseWriter, r *http.Request, err error, code string, logAttrs ...any)
InternalErrorC writes a 500 with the given code and a generic message, and logs the raw error at ERROR level. The raw error is never echoed to the client to avoid leaking internal details.
func IsValidMediaPrefix ¶
IsValidMediaPrefix checks that a prefix parameter matches expected media ID formats. Prevents arbitrary prefix queries that could produce confusing results if the app is exposed without auth.
func JSONError ¶
func JSONError(w http.ResponseWriter, err error, code int)
JSONError writes {"error": err.Error()} at the given status code.
SECURITY: err.Error() is echoed verbatim to the client. Only use when the error text is author-controlled or explicitly user-safe (e.g. errors.New("short, static message")). For wrapped internal errors, use InternalError(w, err, ...) which logs the raw error and returns a generic message. For user-facing validation failures, prefer BadRequest(w, r, code, msg) with an author-controlled message.
func JSONErrorWithCode ¶
JSONErrorWithCode is JSONError + an error-code envelope. Use when the error string is safe to surface verbatim AND a stable machine- readable code is meaningful for the client. Delegates to webhttp.WriteError so the envelope and its request-id population match writeError.
func LangNameToISO ¶
LangNameToISO converts a language name (as returned by Sonarr/Radarr) to an ISO 639-1 code. Accepts full names ("english") or 2-letter ASCII codes. Returns empty string for unrecognized input.
func ManualSubtitlePath ¶
ManualSubtitlePath computes a numbered manual subtitle path. e.g., movie.fr.1.srt, movie.fr.2.srt for regular subs; movie.fr.hi.1.srt or movie.fr.forced.1.srt when the user deliberately downloaded an HI or forced variant. The variant tag appears before the number so parseExternalSubPath continues to recognize it on the next scan.
func MethodNotAllowedC ¶
func MethodNotAllowedC(w http.ResponseWriter, r *http.Request, code string)
MethodNotAllowedC writes a 405 with the given code.
func NewSessionHashContext ¶
NewSessionHashContext returns a new context carrying the session token hash for the current request. Only requireAuth populates this; API-key callers have an empty session hash.
func NewUserContext ¶
NewUserContext returns a new context with the given user stored in it.
func NotFoundC ¶
func NotFoundC(w http.ResponseWriter, r *http.Request, code, msg string)
NotFoundC writes a 404 with the given code and message.
func Ok ¶
func Ok(w http.ResponseWriter)
Ok writes a 200 {"ok": true} response — the standard "action succeeded" reply for endpoints that don't return data. Delegates to webhttp.Ok so the body matches the {"ok":true} every webhttp consumer emits.
func OriginalLangCode ¶ added in v0.1.106
OriginalLangCode returns the ISO 639-1 code for an arr original-language reference, or "" when the reference is nil or the name is unmapped.
func ParseAudioLangs ¶
ParseAudioLangs splits a comma/slash-separated audio languages string into deduplicated ISO 639-1 codes.
func PayloadTooLargeC ¶
func PayloadTooLargeC(w http.ResponseWriter, r *http.Request, code, msg string)
PayloadTooLargeC writes a 413 with the given code and message.
func SeasonEpisodeFileCount ¶ added in v0.1.106
SeasonEpisodeFileCount returns the number of episode files for a specific season, using Sonarr's per-season statistics. Returns 0 if the season is not found or statistics are unavailable.
func ServiceUnavailableC ¶
func ServiceUnavailableC(w http.ResponseWriter, r *http.Request, code, msg string)
ServiceUnavailableC writes a 503 with the given code and message.
func SessionHashFromContext ¶
SessionHashFromContext returns the session token hash for the current request, or "" if the request was authenticated via API key (no session). Handlers that need to touch the current session (delete on logout, exclude from bulk session invalidation) read it here instead of re-parsing the cookie.
func SubtitlePath ¶
SubtitlePath computes the subtitle file path for a video.
func TooManyRequestsC ¶
func TooManyRequestsC(w http.ResponseWriter, r *http.Request, code, msg string)
TooManyRequestsC writes a 429 with the given code and message. The caller is responsible for setting the Retry-After header when appropriate.
func UnauthorizedC ¶
func UnauthorizedC(w http.ResponseWriter, r *http.Request, code, msg string)
UnauthorizedC writes a 401 with the given code and message.
func UserFromContext ¶
UserFromContext extracts the authenticated user from the request context. Returns nil if no user is present.
func ValidateSubtitleData ¶
ValidateSubtitleData checks whether data looks like subtitle text rather than a binary archive. Returns ErrBinaryData if the data matches a known archive magic signature or has too many non-text bytes.
func WithRequestID ¶
WithRequestID attaches the given id to ctx under webhttp's request-id key. Production requests receive the id transparently from webhttp.Logging; tests use this to inject a known id.
func WriteJSON ¶
func WriteJSON(w http.ResponseWriter, v any)
WriteJSON encodes v as JSON with status 200.
func WriteJSONStatus ¶
func WriteJSONStatus(w http.ResponseWriter, code int, v any)
WriteJSONStatus encodes v as JSON with the given status code. The encode failure is logged at Debug (deliberately quiet: the status line is already on the wire and an app JSON payload that fails to encode is exceptional) rather than delegating to webhttp.WriteJSONStatus, which logs at Warn.
Types ¶
type AdaptiveConfig ¶
type AdaptiveConfig struct {
InitialDelay time.Duration
MaxDelay time.Duration
BackoffMultiplier float64
MaxAttempts int
Enabled bool
}
AdaptiveConfig controls adaptive search backoff.
type AdminUserCreatedResponse ¶
type AdminUserCreatedResponse struct {
Username string `json:"username"`
Email string `json:"email"`
Role auth.Role `json:"role"`
ID int64 `json:"id"`
}
AdminUserCreatedResponse is the JSON response after admin creates a user.
type ArrConfigProvider ¶
ArrConfigProvider provides Sonarr/Radarr connection configuration.
type AudioRuleJSON ¶
type AudioRuleJSON struct {
Audio string `json:"audio"`
Subtitles []SubtitleTargJSON `json:"subtitles"`
}
AudioRuleJSON is a JSON-friendly audio rule.
type AudioSyncResult ¶
type AudioSyncResult struct {
Method string
Cues []SubtitleCue
Offset int64 // milliseconds
Confidence float64 // 0.0 to 1.0
Applied bool // true if sync was applied and should be saved
}
AudioSyncResult holds the output of an audio-based sync operation.
type AuthConfigProvider ¶
type AuthConfigProvider interface {
AuthEnabled() bool
BasicAuthEnabled() bool
OIDCEnabled() bool
OIDCConfig() auth.OIDCConfig
SessionIdleTimeout() time.Duration
SessionAbsoluteTimeout() time.Duration
CheckBreachedPasswords() bool
WebAuthnRPID() string
}
AuthConfigProvider provides authentication configuration.
type AuthError ¶
type AuthError struct{ Msg string }
AuthError indicates invalid or expired credentials.
type BackoffEntry ¶
type BackoffEntry struct {
LastTried time.Time `json:"last_tried"`
NextRetry time.Time `json:"next_retry"`
MediaType MediaType `json:"media_type"`
MediaID string `json:"media_id"`
Language string `json:"language"`
Provider ProviderID `json:"provider"`
Failures int `json:"failures"`
}
BackoffEntry represents an item in adaptive search backoff.
type BackoffParams ¶
BackoffParams groups adaptive backoff configuration for RecordNoResult.
type BackoffStore ¶
type BackoffStore interface {
RecordNoResult(ctx context.Context, mediaType MediaType, mediaID, language string, providerName ProviderID, bp BackoffParams) error
BackedOffProviders(ctx context.Context, mediaType MediaType, mediaID, language string, maxAttempts int) ([]ProviderID, error)
}
BackoffStore groups the adaptive-backoff persistence methods.
type CacheClearer ¶
type CacheClearer interface {
ClearCache()
}
CacheClearer is an optional interface for providers that cache download data (e.g. season pack zips). Called after scan completion to free memory. Providers implementing this get compile-time verification via var _ api.CacheClearer = (*Provider)(nil).
type CleanupResult ¶
type CleanupResult struct {
// Paths are subtitle file paths whose DB entries were removed.
// The caller is responsible for validating and deleting files from disk.
Paths []string
}
CleanupResult holds the outcome of a media cleanup operation. Used by both CleanupForMediaUpgrade and ReconcileState.
type ConfigDrift ¶
type ConfigDrift struct {
// RemovedLanguages are language codes that were in the old config
// but not in the new one. Their search_attempts should be cleared.
RemovedLanguages []string
// RemovedProviders are provider names that were enabled in the old
// config but disabled or removed in the new one. Their
// search_attempts should be cleared.
RemovedProviders []ProviderID
// AdaptiveDisabled is true when adaptive search was enabled in the
// old config but disabled in the new one. All search_attempts
// should be cleared.
AdaptiveDisabled bool
}
ConfigDrift describes DB cleanup actions needed after a config change.
func DetectDrift ¶
func DetectDrift( oldLangs, newLangs []string, oldProviders, newProviders []ProviderID, oldAdaptiveEnabled, newAdaptiveEnabled bool, ) ConfigDrift
DetectDrift compares old and new config state to determine what DB cleanup is needed. Parameters are the extracted sets from each config. Duplicate entries in old slices are deduplicated; each removed item appears at most once in the result.
func (*ConfigDrift) Empty ¶
func (d *ConfigDrift) Empty() bool
Empty returns true if no cleanup is needed.
type ConfigLoader ¶
type ConfigLoader func(data []byte) (ConfigProvider, error)
ConfigLoader parses and validates config from raw YAML bytes.
type ConfigProvider ¶
type ConfigProvider interface {
ScoringConfig
LanguageResolver
ArrConfigProvider
ProviderConfigProvider
ServerConfig
PathValidator
SearchConfigProvider
AuthConfigProvider
UIConfigProvider
}
ConfigProvider gives read access to configuration. It composes the focused sub-interfaces defined in config_iface.go. Consumers should accept the narrowest sub-interface that satisfies their needs; ConfigProvider is for composition roots that need everything.
type CoverageStore ¶
type CoverageStore interface {
RecordSubtitleFiles(ctx context.Context, mediaType MediaType, mediaID string, files []SubtitleFile) (bool, error)
UpsertSubtitleFile(ctx context.Context, mediaType MediaType, mediaID string, f *SubtitleFile) error
GetSubtitleFiles(ctx context.Context, mediaType MediaType, mediaIDPrefix string) ([]SubtitleEntry, error)
DeleteSubtitleFile(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant, source SubtitleSource, path string) error
RecordScanState(ctx context.Context, rec *ScanRecord) error
GetScanStates(ctx context.Context, mediaType MediaType, mediaIDPrefix string) ([]ScanStateRow, error)
RecentlyScanned(ctx context.Context, cutoff time.Time) (map[string]bool, error)
TotalSubtitleFiles(ctx context.Context) (int, error)
LastScanTime(ctx context.Context) (string, error)
}
CoverageStore groups subtitle file tracking and scan state methods.
type DownloadMeta ¶
type DownloadMeta struct {
Title string
ImdbID string
ReleaseTag string
VideoPath string // Path to the video file (for reconciliation and upgrades).
Season int
Episode int
Manual bool // True if user manually selected this subtitle.
}
DownloadMeta holds optional metadata for a subtitle state record.
type DownloadRecord ¶
type DownloadRecord struct {
Meta *DownloadMeta
MediaType MediaType
MediaID string
Language string
Variant Variant // subtitle variant (standard/hi/forced); empty is normalized to standard
ProviderName ProviderID
ReleaseName string
Path string
Score int
}
DownloadRecord groups the parameters for Store.SaveDownload. Using a named struct (rather than positional string arguments) makes call sites self-documenting and prevents silent swaps of same-typed fields like Language/ProviderName.
type DownloadStore ¶
type DownloadStore interface {
SaveDownload(ctx context.Context, rec *DownloadRecord) error
DownloadedRefs(ctx context.Context, mediaType MediaType, mediaID, language string) ([]DownloadedRef, error)
CurrentScore(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) (score int, mediaImported time.Time, found bool, err error)
}
DownloadStore groups subtitle download record persistence.
subtitle_state rows are keyed by the (media_type, media_id, language, variant) quad, so CurrentScore is answered per variant: an fr/forced download never shadows the fr/standard score. DownloadedRefs deliberately stays language-scoped (all variants): it feeds the manual-search popup's "on disk" markers, and the popup lists every variant of the language.
type DownloadedRef ¶
type DownloadedRef struct {
ReleaseName string
Provider ProviderID
}
DownloadedRef identifies a previously-downloaded subtitle by its release name and provider. Returned by Store.DownloadedRefs to mark matching entries in the manual search popup as already on disk.
type EmbeddedTrack ¶
type EmbeddedTrack struct {
Codec string
Lang string
Name string
Index int
Forced bool
HearingImpaired bool
}
EmbeddedTrack represents a subtitle track detected inside a video container.
type HistoryStore ¶
type HistoryStore interface {
HistoryMediaIDs(ctx context.Context, mediaType MediaType, mediaIDPrefix string) ([]string, error)
}
HistoryStore groups download-history lookup methods.
type KeyGenerated ¶ added in v0.1.29
type KeyGenerated struct {
CreatedAt time.Time `json:"created_at"`
Key string `json:"key"`
KeyPrefix string `json:"key_prefix"`
KeySuffix string `json:"key_suffix"`
Label string `json:"label"`
ID int64 `json:"id"`
}
KeyGenerated is the JSON response after generating an API key.
type LanguageResolver ¶
type LanguageResolver interface {
ResolveTargetsWithFallback(originalLang string, audioLangs []string) []SubtitleTarget
LanguageCodes() []string
ProvidersForTarget(t *SubtitleTarget, allProviders []ProviderID) []ProviderID
MinScoreForTarget(t *SubtitleTarget, mediaType MediaType) int
}
LanguageResolver resolves subtitle targets from audio language context.
type LanguageRulesJSON ¶
type LanguageRulesJSON struct {
Rules []AudioRuleJSON `json:"rules,omitempty"`
Default []SubtitleTargJSON `json:"default,omitempty"`
}
LanguageRulesJSON is the language rules in a JSON-serializable format for the settings UI.
type LoginSuccess ¶ added in v0.1.29
type LoginSuccess struct {
Redirect string `json:"redirect"`
User MeResponse `json:"user"`
}
LoginSuccess is the JSON response after successful login.
type MaintStore ¶
type MaintStore interface {
DeleteStateByPaths(ctx context.Context, paths []string) (CleanupResult, error)
CleanupDrift(ctx context.Context, drift ConfigDrift) error
ReconcileState(ctx context.Context) (ReconcileResult, error)
}
MaintStore groups maintenance and cleanup methods.
type ManualLockEntry ¶
type ManualLockEntry struct {
MediaType MediaType `json:"media_type"`
MediaID string `json:"media_id"`
Language string `json:"language"`
Variant Variant `json:"variant"`
Count int `json:"count"`
}
ManualLockEntry represents a manually locked media+language+variant quad.
type ManualLockStore ¶
type ManualLockStore interface {
// IsManuallyLocked reports whether the quad has a manual row. An empty
// variant asks whether ANY variant of the language is locked.
IsManuallyLocked(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) (bool, error)
// ClearManualLock clears the quad's lock. An empty variant clears the
// locks of ALL variants of the language.
ClearManualLock(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) error
// ManualDownloadCount counts the quad's manual rows (exact variant).
ManualDownloadCount(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) (int, error)
// ManualSubtitlePaths returns the manual rows' file paths. An empty
// variant returns the paths of ALL variants of the language.
ManualSubtitlePaths(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) ([]string, error)
// NextManualNumber returns the next manual ordinal for the quad (exact
// variant): movie.fr.1.srt and movie.fr.forced.1.srt count independently.
NextManualNumber(ctx context.Context, mediaType MediaType, mediaID, language string, variant Variant) int
}
ManualLockStore groups manual override lock persistence. Locks live on the (media_type, media_id, language, variant) quad: a manual forced download locks only the forced target, leaving standard/hi automation untouched.
Methods documented as accepting an empty variant treat "" as "any/all variants of the language"; the rest require an exact variant.
type MatchMethod ¶
type MatchMethod string
MatchMethod is a typed string identifying how a subtitle was matched to the requested media. Use the exported constants rather than raw string literals. Follows the same pattern as MediaType and AuthMethod.
const ( MatchByHash MatchMethod = "hash" MatchByIMDB MatchMethod = "imdb" MatchByTitle MatchMethod = "title" MatchByTVDB MatchMethod = "tvdb" MatchByTMDB MatchMethod = "tmdb" MatchByEmbedded MatchMethod = "embedded" )
Match method constants. Canonical source for all packages.
type MatchSet ¶
type MatchSet struct {
Hash bool
Source bool
ReleaseGroup bool
StreamingService bool
VideoCodec bool
HDR bool
Edition bool
SeasonPack bool
SeriesIMDB bool
IMDB bool
}
MatchSet tracks which attributes matched between a subtitle and a video. Each field corresponds to a release attribute; struct fields make invalid keys impossible at compile time.
type MeResponse ¶ added in v0.1.29
type MeResponse struct {
Username string `json:"username"`
Role auth.Role `json:"role"`
ID int64 `json:"id"`
HasPasskeys bool `json:"has_passkeys"`
OIDCLinked bool `json:"oidc_linked"`
HasPassword bool `json:"has_password"`
CanLinkOIDC bool `json:"can_link_oidc"`
}
MeResponse is the JSON response for GET /api/auth/me.
type MediaType ¶
type MediaType string
MediaType is a typed string identifying the kind of media being searched. Use the exported constants rather than raw string literals.
Media type constants used by SearchRequest.MediaType and related helpers. Canonical source for all packages; import from here instead of declaring local unexported copies.
type PasskeyRegistered ¶ added in v0.1.29
type PasskeyRegistered struct {
CreatedAt time.Time `json:"created_at"`
Name string `json:"name"`
Transport string `json:"transport"`
ID int64 `json:"id"`
}
PasskeyRegistered is the JSON response after successful passkey registration.
type PathValidator ¶
type PathValidator interface {
MediaRoots() []string
ValidatePath(ctx context.Context, path string) error
RemoveUnderRoot(ctx context.Context, path string) error
}
PathValidator provides media path validation.
type PollKey ¶
type PollKey string
PollKey identifies an arr-source poll-timestamp row in the store. Using a typed string instead of bare string prevents typo-induced silent failures (e.g. "Sonarr" capitalization or "sonar" typo would silently insert a new row and force history re-fetch).
Canonical poll-key values. New arr sources should add a constant here.
type PollStore ¶
type PollStore interface {
GetPollTimestamp(ctx context.Context, key PollKey) (time.Time, error)
SetPollTimestamp(ctx context.Context, key PollKey, t time.Time) error
}
PollStore groups arr webhook/poll timestamp persistence.
type PostProcessConfig ¶
type PostProcessConfig struct {
StripHI bool `json:"strip_hi"`
StripTags bool `json:"strip_tags"`
NormalizeUTF8 bool `json:"normalize_utf8"`
CleanWhitespace bool `json:"clean_whitespace"`
NormalizeEndings bool `json:"normalize_endings"`
RemoveEmpty bool `json:"remove_empty"`
}
PostProcessConfig controls subtitle post-processing.
type Provider ¶
type Provider interface {
// Name returns the provider identifier (e.g. "opensubtitles", "yifysubtitles").
Name() ProviderID
// Search finds subtitles matching the request.
Search(ctx context.Context, req *SearchRequest) ([]Subtitle, error)
// Download fetches the subtitle content for the given search result.
Download(ctx context.Context, sub *Subtitle) ([]byte, error)
}
Provider is the interface all subtitle providers must implement.
type ProviderCfg ¶
type ProviderCfg struct {
Settings map[string]any
Priority int // Lower = higher trust. 0 means unset (defaults to 99).
Enabled bool
}
ProviderCfg is a generic provider configuration block.
type ProviderConfigProvider ¶
type ProviderConfigProvider interface {
ProviderConfigs() map[ProviderID]ProviderCfg
ProviderPriority(name ProviderID) int
}
ProviderConfigProvider provides provider settings.
type ProviderID ¶
type ProviderID string
ProviderID is a typed string identifying a subtitle provider. Use this instead of raw strings when passing provider names to metrics, backoff, and other subsystems that key on provider identity.
const ( ProviderNameEmbedded ProviderID = "embedded" ProviderNameMock ProviderID = "mock" ProviderNameHDBits ProviderID = "hdbits" ProviderNameOpenSubtitles ProviderID = "opensubtitles" ProviderNameSubSource ProviderID = "subsource" ProviderNameAnimeTosho ProviderID = "animetosho" ProviderNameYifySubtitles ProviderID = "yifysubtitles" ProviderNameSubDL ProviderID = "subdl" ProviderNameBetaSeries ProviderID = "betaseries" ProviderNameGestdown ProviderID = "gestdown" )
Provider name constants (canonical SSOT). All packages MUST reference these instead of declaring local string literals.
type ProviderRegistry ¶
type ProviderRegistry interface {
// LoadAll instantiates providers from the given config map, skipping
// unconfigured entries. Returns an error if a configured provider fails.
LoadAll(ctx context.Context, configs map[ProviderID]ProviderCfg) ([]Provider, error)
// ProviderNames returns all registered provider names in priority order.
ProviderNames() []ProviderID
// Schema returns the UI label and settings fields for a named provider.
Schema(name ProviderID) (label string, fields []ProviderSchemaField)
}
ProviderRegistry manages provider factories and schema metadata.
type ProviderSchema ¶
type ProviderSchema struct {
Name string `json:"name"`
Label string `json:"label"`
Settings []SchemaField `json:"settings,omitempty"`
AlwaysEnabled bool `json:"always_enabled,omitempty"`
}
ProviderSchema describes a single provider's settings fields.
func BuildProviderSchemas ¶
func BuildProviderSchemas(reg ProviderRegistry, exclude ...string) []ProviderSchema
BuildProviderSchemas converts the registry's provider metadata into ProviderSchema entries for the UI. Names in exclude are omitted.
type ProviderSchemaField ¶
type ProviderSchemaField struct {
Key string `json:"key"`
Label string `json:"label"`
Type string `json:"type"` // text, secret, bool
Default string `json:"default,omitempty"`
Help string `json:"help,omitempty"`
Secret bool `json:"secret,omitempty"`
}
ProviderSchemaField describes a single provider setting for the UI schema.
type ProviderStatus ¶ added in v0.1.29
type ProviderStatus struct {
LastError string `json:"last_error,omitempty"`
CooldownRemaining time.Duration `json:"cooldown_remaining,omitempty"`
RecentFailures int `json:"recent_failures"`
Threshold int `json:"threshold"`
TimedOut bool `json:"timed_out"`
}
ProviderStatus is the state of a single provider's timeout.
type ProviderTimeoutManager ¶
type ProviderTimeoutManager interface {
ProviderTimeouts() (status map[ProviderID]ProviderStatus, enabled bool)
ResetTimeouts()
}
ProviderTimeoutManager manages provider timeout state.
type ProvidersResponse ¶
type ProvidersResponse struct {
Providers map[ProviderID]ProviderStatus `json:"providers"`
Enabled bool `json:"enabled"`
}
ProvidersResponse is the JSON response for GET /api/providers/timeout.
type QueryStore ¶
type QueryStore interface {
GetState(ctx context.Context, q *StateQuery) ([]StateEntry, error)
GetBackoffItems(ctx context.Context) ([]BackoffEntry, error)
GetBackoffByPrefix(ctx context.Context, mediaType MediaType, mediaIDPrefix string) ([]BackoffEntry, error)
GetManualLocks(ctx context.Context) ([]ManualLockEntry, error)
Stats(ctx context.Context) (downloads, attempts int, err error)
}
QueryStore groups read-only state inspection methods.
type RadarrClient ¶ added in v0.1.106
type RadarrClient interface {
Ping(ctx context.Context) error
GetMovies(ctx context.Context) ([]arrapi.Movie, error)
GetMovieByID(ctx context.Context, id int) (arrapi.Movie, error)
GetHistorySince(ctx context.Context, since time.Time, eventTypes ...arrapi.EventType) ([]arrapi.HistoryRecord, error)
GetWantedMovies(ctx context.Context, excludeTagIDs map[int]struct{}, fn func(arrapi.Movie) error) error
ResolveExcludeTagIDs(ctx context.Context, tagNames []string, logMissing bool) map[int]struct{}
RescanMovie(ctx context.Context, movieID int) error
}
RadarrClient is the Radarr-side surface subflux consumes: library reads, per-item lookups, import-history polling, wanted-movie iteration, exclude-tag resolution, and a post-download rescan.
type RateLimitError ¶
RateLimitError indicates the provider's rate limit was exceeded. RetryAfter, when non-zero, is the hint from the upstream's Retry-After header (delta-seconds or HTTP-date resolved to a positive duration). Consumers may use it to schedule the next attempt; a zero value means no hint was provided.
func (*RateLimitError) Error ¶
func (e *RateLimitError) Error() string
type ReconcileResult ¶
type ReconcileResult struct {
// Deleted contains subtitle paths removed because the video is gone.
Deleted CleanupResult
// ResetCount is the number of entries reset for re-search because
// the subtitle file was missing but the video still exists.
ResetCount int64
}
ReconcileResult holds the outcome of a state reconciliation pass.
type ScanOutcome ¶
type ScanOutcome string
ScanOutcome is a typed string for scan result classification. Canonical source; scanning/ and server/ should import from here.
const ( ScanFound ScanOutcome = "found" ScanSkipped ScanOutcome = "skipped" ScanNoResult ScanOutcome = "none" // ScanBackedOff means every language target that needed a search had all // of its providers in adaptive backoff, so no provider was queried at // all. Distinct from ScanNoResult ("we looked, nothing found") and // ScanSkipped ("nothing needed a search"): "we already know there is // probably nothing, so we didn't look". Never recorded in the season // early-termination tracker and counted in its own stats bucket. ScanBackedOff ScanOutcome = "backed_off" )
Scan outcome constants.
type ScanRecord ¶
type ScanRecord struct {
MediaType MediaType
MediaID string
Title string
AudioLang string
Season int
Episode int
}
ScanRecord groups the parameters for CoverageStore.RecordScanState.
type ScanStateRow ¶
type ScanStateRow struct {
ScannedAt string `json:"scanned_at"`
MediaID string `json:"media_id"`
Title string `json:"title"`
AudioLang string `json:"audio_lang"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
}
ScanStateRow records when a media item was last scanned.
type ScanStats ¶
type ScanStats struct {
// Pre-scan totals (from API).
TotalSeries int
TotalEpisodeFiles int // Sum of episodeFileCount across all series.
TotalMovies int
TotalMovieFiles int // Movies with hasFile=true.
// Post-scan outcomes for episodes. EpisodesSearched counts every episode
// the scan loop processed (it drives progress reporting); the other
// fields are its per-outcome buckets.
EpisodesSearched int // Episodes processed by the scan loop.
EpisodesSkipped int // Episodes with existing subs (no search needed).
EpisodesFound int // Episodes where a subtitle was downloaded.
EpisodesNoResult int // Episodes searched but no subtitle found.
EpisodesBackedOff int // Episodes where every needed provider was in adaptive backoff (no query ran).
SeriesSkipped int // Series skipped by show-level pre-check.
// Post-scan outcomes for movies.
MoviesSearched int
MoviesSkipped int
MoviesFound int
MoviesNoResult int
MoviesBackedOff int
}
ScanStats tracks scan progress and outcomes for logging.
type SchemaField ¶
type SchemaField struct {
Key string `json:"key"`
Label string `json:"label"`
Type string `json:"type"`
Default string `json:"default,omitempty"`
Help string `json:"help,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
Min string `json:"min,omitempty"`
Max string `json:"max,omitempty"`
ShowWhen string `json:"show_when,omitempty"`
Requires string `json:"requires,omitempty"`
Group string `json:"group,omitempty"`
Fields []SchemaField `json:"fields,omitempty"`
Options []SchemaOption `json:"options,omitempty"`
Secret bool `json:"secret,omitempty"`
Required bool `json:"required,omitempty"`
}
SchemaField describes a single configuration field for the UI.
type SchemaFunc ¶
type SchemaFunc func(providers []ProviderSchema) []SchemaSection
SchemaFunc returns the full configuration schema for the UI.
type SchemaOption ¶
SchemaOption is a value+label pair for select fields.
type SchemaSection ¶
type SchemaSection struct {
Key string `json:"key"`
Title string `json:"title"`
Type string `json:"type"`
Help string `json:"help,omitempty"`
RequiredGroup string `json:"required_group,omitempty"`
EnableKey string `json:"enable_key,omitempty"`
Fields []SchemaField `json:"fields,omitempty"`
ProviderTemplate []SchemaField `json:"provider_template,omitempty"`
Providers []ProviderSchema `json:"providers,omitempty"`
}
SchemaSection describes a top-level config section.
type ScorePreview ¶ added in v0.1.29
type ScorePreview struct {
Tier ScoreTier `json:"tier"`
Score int `json:"score"`
ScoreNoHash int `json:"score_no_hash"`
}
ScorePreview is the JSON response for POST /api/score/preview.
type ScoreResult ¶
type ScoreResult struct {
Tier ScoreTier // Quality tier label (excellent/good/acceptable/minimal/none).
Score int // Final score including hash match bonus.
ScoreNoHash int // Score from release attributes only, excluding hash match.
}
ScoreResult holds the output of a score simulation.
type ScoreSimulator ¶
type ScoreSimulator interface {
SimulateScore(mediaType MediaType, videoRelease, subRelease string, matchedBy MatchMethod) ScoreResult
ScoreSubtitles(req *SearchRequest, results []Subtitle) []ScoredResult
}
ScoreSimulator provides subtitle scoring capabilities.
type ScoredResult ¶
type ScoredResult struct {
Matches map[string]int // Per-category score contributions.
Sub Subtitle
Score int
}
ScoredResult is a scored subtitle for use by the manual search and CLI APIs.
type Scorer ¶
type Scorer interface {
// Score computes a quality score for a subtitle match. Returns the full
// score (including hash bonus) and the release-attribute-only score.
Score(video *VideoInfo, sub SubtitleInfo, matches MatchSet) (score, scoreNoHash int)
// ScoreToTier maps a numeric score to a human-readable tier label
// (excellent/good/acceptable/minimal/none) based on media type thresholds.
ScoreToTier(score int, mediaType MediaType) ScoreTier
}
Scorer evaluates subtitle matches against video metadata.
type Scores ¶
type Scores struct {
Hash int `json:"hash" yaml:"hash"`
Source int `json:"source" yaml:"source"`
ReleaseGroup int `json:"release_group" yaml:"release_group"`
StreamingService int `json:"streaming_service" yaml:"streaming_service"`
VideoCodec int `json:"video_codec" yaml:"video_codec"`
HDR int `json:"hdr" yaml:"hdr"`
Edition int `json:"edition" yaml:"edition"` // movies only
SeasonPack int `json:"season_pack" yaml:"season_pack"` // episodes only
}
Scores defines the weight for each release attribute match type.
Identity fields (series, season, episode, title, year) are not scored here; they are enforced by the provider query (identity gate). Resolution is excluded; its signal is captured by source + release_group.
Movie-only: Edition. For episodes, edition=0 and season_pack takes its place. Applicable non-hash weights sum to 98 for each media type. Hash match bypasses attribute scoring entirely (score = 100).
type ScoringConfig ¶
type ScoringConfig interface {
Scores() Scores
}
ScoringConfig provides scoring weight configuration.
type SearchConfig ¶
type SearchConfig struct {
ExcludeArrTags []string
ScanInterval time.Duration
ProviderTimeout time.Duration
ScanDelay time.Duration
MinScore int
UpgradeWindowDays int
DownloadMaxAttempts int // Max download attempts per search (0 = default DefaultDownloadMaxAttempts)
MaxProviderConcurrency int // Max parallel provider searches (0 = default DefaultProviderConcurrency)
MaxSSEClients int // Max concurrent SSE connections (0 = default 32)
UpgradeEnabled bool
}
SearchConfig controls search behavior.
type SearchConfigProvider ¶
type SearchConfigProvider interface {
Search() SearchConfig
Adaptive() AdaptiveConfig
PostProcessConfig() PostProcessConfig
SyncConfig() SyncConfig
}
SearchConfigProvider provides search behavior configuration.
type SearchEngine ¶
type SearchEngine interface {
SubtitleSearcher
ScoreSimulator
ProviderTimeoutManager
SubtitlePostProcessor
}
SearchEngine composes all search sub-interfaces for composition roots.
type SearchRequest ¶
type SearchRequest struct {
MediaType MediaType // "episode" or "movie"
ImdbID string // IMDB ID; fallback for providers without TMDB/TVDB support
Title string
AlternativeTitles []string // from Sonarr/Radarr alternateTitles
EpisodeTitle string // Episode title from Sonarr (for identity validation across numbering schemes)
ReleaseName string
VideoPath string // Absolute path to the video file (for embedded provider and sync)
VideoHash string // OpenSubtitles hash
AudioLang string // resolved audio language (for coverage tracking)
Languages []string // ISO 639-1 codes to search for
TmdbID int // Movie TMDB ID from Radarr; 0 for episodes
VideoSize int64 // file size for hash-based search
Year int
Season int // 0 for movies
Episode int // 0 for movies
TvdbID int // Series TVDB ID from Sonarr; 0 for movies
MaxResults int // 0 = provider default (typically one page)
// Alternate episode numbering from Sonarr (0 = not available).
// Scene numbers come from TheXEM; absolute numbers from TVDB.
SceneSeason int
SceneEpisode int
AbsoluteEpisode int
// ForceUpgrade makes the search engine treat all external subtitles
// as eligible for upgrade, regardless of the upgrade_enabled config.
// Set by manual scan triggers (series/season search buttons).
ForceUpgrade bool
}
SearchRequest holds the parameters for a subtitle search across providers.
func (*SearchRequest) MediaLabel ¶
func (r *SearchRequest) MediaLabel() string
MediaLabel returns a human-readable label for log messages. Movies: "Inception (2010)", episodes: "Bleach (2004) - S09E15".
type SearchResult ¶
type SearchResult struct {
Paths []string // Subtitle files downloaded.
FoundLangs []string // Language codes that had at least one subtitle downloaded.
SearchedLangs []string // Language codes whose group actually ran (not skipped); superset of FoundLangs.
Searched int // Languages where providers were actually queried.
Skipped int // Languages skipped (subs already exist, not eligible for upgrade).
BackedOff int // Languages that needed a search but had every provider in adaptive backoff (no query ran).
CoverageChanged bool // True if RecordSubtitleFiles detected changes on disk.
}
SearchResult holds the outcome of a SearchTargets call.
type SearchTarget ¶
type SearchTarget struct {
MinScore *int `json:"min_score,omitempty"`
Code string `json:"code"`
Variant string `json:"variant"`
Providers []string `json:"providers,omitempty"`
Exclude []string `json:"exclude,omitempty"`
}
SearchTarget describes a single subtitle search target in the API response.
type SearchTargets ¶ added in v0.1.29
type SearchTargets struct {
OrigLang string `json:"orig_lang"`
AudioLangs []string `json:"audio_langs"`
Targets []SearchTarget `json:"targets"`
}
SearchTargets is the JSON response for GET /api/search/targets.
type ServerConfig ¶
type ServerConfig interface {
ServerPort() int
PollInterval() time.Duration
LoggingLevel() LogLevel
LoggingFormat() LogFormat
}
ServerConfig provides server runtime configuration.
type SetupStatus ¶ added in v0.1.29
type SetupStatus struct {
SetupRequired bool `json:"setup_required"`
ConfigValid bool `json:"config_valid"`
}
SetupStatus is the JSON response for GET /api/auth/setup.
type ShowSubtitleCounter ¶
type ShowSubtitleCounter interface {
// CountShowSubtitles returns the total number of subtitles available for
// a show in the given language. The request should have ImdbID set and
// Season/Episode set to 0.
CountShowSubtitles(ctx context.Context, imdbID, lang string) (int, error)
}
ShowSubtitleCounter can count total subtitles for a show+language without specifying season/episode. Used for show-level pre-checks: if a show has very few subtitles relative to its episode count, skip the entire series. Only providers with show-level query support implement this (OpenSubtitles).
type SignalData ¶ added in v0.1.29
type SignalData struct {
RPID string `json:"rp_id"`
UserID string `json:"user_id"`
Name string `json:"name"`
DisplayName string `json:"display_name"`
CredentialIDs []string `json:"credential_ids"`
}
SignalData is the JSON response for GET /api/auth/webauthn/signal-data.
type SonarrClient ¶ added in v0.1.106
type SonarrClient interface {
Ping(ctx context.Context) error
GetSeries(ctx context.Context) ([]arrapi.Series, error)
GetEpisodes(ctx context.Context, seriesID int) ([]arrapi.Episode, error)
GetSeriesByID(ctx context.Context, id int) (arrapi.Series, error)
GetEpisodeByID(ctx context.Context, id int) (arrapi.Episode, error)
GetHistorySince(ctx context.Context, since time.Time, eventTypes ...arrapi.EventType) ([]arrapi.HistoryRecord, error)
GetWantedEpisodes(ctx context.Context, excludeTagIDs map[int]struct{}, fn func(arrapi.Series, arrapi.Episode) error) error
ResolveExcludeTagIDs(ctx context.Context, tagNames []string, logMissing bool) map[int]struct{}
RescanSeries(ctx context.Context, seriesID int) error
}
SonarrClient is the Sonarr-side surface subflux consumes: library reads, per-item lookups, import-history polling, wanted-episode iteration, exclude-tag resolution, and a post-download rescan.
type StateEntry ¶
type StateEntry struct {
MediaImported time.Time `json:"media_imported"`
Title string `json:"title"`
MediaID string `json:"media_id"`
Language string `json:"language"`
Variant Variant `json:"variant"`
Provider ProviderID `json:"provider"`
Path string `json:"path"`
ReleaseName string `json:"release_name"`
ImdbID string `json:"imdb_id,omitempty"`
MediaType MediaType `json:"media_type"`
ID int64 `json:"id"`
Score int `json:"score"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
Manual bool `json:"manual"`
}
StateEntry represents a subtitle state record for API responses.
type StateQuery ¶
type StateQuery struct {
MediaType MediaType
Language string
Provider ProviderID
Search string
Limit int
Offset int
}
StateQuery groups the filter parameters for QueryStore.GetState. Using a named struct (rather than positional string/int arguments) makes call sites self-documenting and prevents silent swaps of same-typed fields like Language/Provider/Search.
type Stats ¶ added in v0.1.29
type Stats struct {
LastScan string `json:"last_scan"`
Downloads int `json:"downloads"`
Attempts int64 `json:"attempts"`
ScanIntervalSeconds int `json:"scan_interval_seconds"`
TotalSubs int `json:"total_subs"`
TotalSeries int `json:"total_series"`
TotalMovies int `json:"total_movies"`
MissingSubs int `json:"missing_subs"`
Partial bool `json:"partial"`
}
Stats is the JSON response for GET /api/stats.
type Store ¶
type Store interface {
BackoffStore
DownloadStore
ManualLockStore
QueryStore
HistoryStore
CoverageStore
SyncOffsetStore
MaintStore
PollStore
Close(ctx context.Context) error
}
Store persists search state and subtitle state. All methods accept context.Context for cancellation and timeout propagation.
Methods are grouped by domain concern into composable sub-interfaces defined in store_iface.go. Consumers should accept the narrowest sub-interface that satisfies their needs.
type Subtitle ¶
type Subtitle struct {
Provider ProviderID
ID string // provider-specific identifier
Language string // ISO 639-1 code
ReleaseName string
DownloadURL string
MatchedBy MatchMethod // how this subtitle was matched (hash, title, imdb, etc.)
Title string // show/movie title from provider (for identity validation)
Year int // year from provider (0 if unknown)
Season int // season from provider (0 if unknown or movie)
Episode int // episode from provider (0 if unknown or movie)
HearingImp bool
Forced bool
}
Subtitle is a search result returned by a provider.
type SubtitleCue ¶
SubtitleCue represents a single subtitle entry with timing.
type SubtitleEntry ¶ added in v0.1.29
type SubtitleEntry struct {
MediaID string `json:"media_id"`
Language string `json:"language"`
Variant string `json:"variant"`
Source string `json:"source"`
Codec string `json:"codec,omitempty"`
Path string `json:"path,omitempty"`
VideoPath string `json:"video_path,omitempty"`
Score int `json:"score,omitempty"`
OffsetMs int64 `json:"offset_ms,omitempty"`
}
SubtitleEntry is the JSON shape returned by coverage queries.
type SubtitleFile ¶
type SubtitleFile struct {
Language string // ISO 639-1
Variant Variant // "standard", "hi", "forced"
Source SubtitleSource // SourceEmbedded or SourceExternal
Codec string // e.g. "subrip", "ass"; empty for external
Path string // file path for external; empty for embedded
}
SubtitleFile represents a discovered subtitle (embedded or external) for a media file.
type SubtitleInfo ¶
type SubtitleInfo struct {
HashVerifiable bool
}
SubtitleInfo describes a subtitle result for scoring purposes.
type SubtitlePostProcessor ¶
type SubtitlePostProcessor interface {
SyncAndPostProcess(ctx context.Context, data []byte, videoPath, lang string, variant Variant) (synced []byte, offsetMs int64)
HashFile(ctx context.Context, path string) (hash string, size int64, err error)
}
SubtitlePostProcessor handles post-download subtitle processing.
type SubtitleProcessor ¶
type SubtitleProcessor interface {
// NormalizeEncoding converts subtitle data to UTF-8 from detected encoding.
NormalizeEncoding(data []byte) []byte
// ParseSRT parses SRT subtitle data into individual cues.
ParseSRT(data []byte) ([]SubtitleCue, error)
// WriteSRT serializes cues back to SRT format.
WriteSRT(cues []SubtitleCue) ([]byte, error)
// ShiftCues applies a timing offset to all cues.
ShiftCues(cues []SubtitleCue, offset time.Duration) []SubtitleCue
// SyncFromAudio runs audio-based sync on subtitle data against the video.
SyncFromAudio(ctx context.Context, data []byte, videoPath, subtitlePath string) AudioSyncResult
}
SubtitleProcessor provides low-level SRT manipulation operations. Used by sync handlers to avoid importing the subsync package directly.
type SubtitleSearcher ¶
type SubtitleSearcher interface {
SearchTargets(ctx context.Context, req *SearchRequest, videoPath string, targets []SubtitleTarget) (SearchResult, error)
}
SubtitleSearcher orchestrates subtitle search across providers.
type SubtitleSource ¶
type SubtitleSource string
SubtitleSource identifies where a subtitle was found.
const ( // SourceEmbedded indicates a subtitle embedded in the video container. SourceEmbedded SubtitleSource = "embedded" // SourceExternal indicates a subtitle in a separate file on disk. SourceExternal SubtitleSource = "external" )
type SubtitleTargJSON ¶
type SubtitleTargJSON struct {
MinScore *int `json:"min_score,omitempty"`
Code string `json:"code"`
Variant string `json:"variant,omitempty"`
Variants []string `json:"variants,omitempty"`
Providers []string `json:"providers,omitempty"`
Exclude []string `json:"exclude,omitempty"`
}
SubtitleTargJSON is a JSON-friendly subtitle target.
type SubtitleTarget ¶
type SubtitleTarget struct {
MinScore *int
Code string
Variant Variant
Variants []string
Providers []ProviderID
Exclude []ProviderID
}
SubtitleTarget defines a single subtitle to search for.
func (*SubtitleTarget) EffectiveVariant ¶
func (t *SubtitleTarget) EffectiveVariant() Variant
EffectiveVariant returns the variant, defaulting to DefaultVariant.
type SyncConfig ¶
type SyncConfig struct {
// SyncSubtitles enables automatic timing sync against embedded
// reference subtitles when a new subtitle is downloaded.
SyncSubtitles bool `json:"sync_subtitles"`
// AudioSyncFallback enables audio-based sync as a fallback when
// no embedded reference subtitle is available or sync fails.
AudioSyncFallback bool `json:"audio_sync_fallback"`
// SyncMinConfidence is the minimum confidence threshold (0.0–1.0) for
// accepting an automatic sync result. Defaults to DefaultSyncMinConfidence when zero.
SyncMinConfidence float64 `json:"sync_min_confidence,omitempty"`
}
SyncConfig controls subtitle synchronization on import.
type SyncOffsetStore ¶
type SyncOffsetStore interface {
SetSyncOffset(ctx context.Context, path string, offsetMs int64) error
GetSyncOffset(ctx context.Context, path string) (int64, error)
}
SyncOffsetStore groups subtitle timing adjustment persistence.
type Transient ¶
type Transient interface {
IsTransient() bool
}
Transient is implemented by errors that can classify themselves as retryable (transient server/network failures) vs permanent. Used by retry logic to decide whether to retry without importing concrete error packages.
type UIConfigProvider ¶
type UIConfigProvider interface {
LanguageRulesForUI() LanguageRulesJSON
}
UIConfigProvider provides UI-specific configuration.
type Variant ¶
type Variant string
Variant is a typed string enum for subtitle variant identifiers. Valid variants: standard (default), hi (hearing impaired), forced.
func VariantFromFlags ¶
VariantFromFlags derives the variant from HI/forced flags on a picked subtitle. HI wins over forced when both happen to be set.
type VideoInfo ¶
type VideoInfo struct {
ReleaseGroup string // Full release name for parsing.
MediaType MediaType // "episode" or "movie"
}
VideoInfo describes the video file for scoring purposes. Only MediaType and ReleaseGroup are used by the scorer and match builder.
type WebAuthnUnknownCredentialResponse ¶
type WebAuthnUnknownCredentialResponse struct {
Error string `json:"error"`
Signal string `json:"signal"`
}
WebAuthnUnknownCredentialResponse signals an unknown credential to the client.
Source Files
¶
- arr_types.go
- auth_response_types.go
- config_iface.go
- context.go
- drift.go
- error_codes.go
- http.go
- http_errors.go
- interfaces.go
- interfaces_provider.go
- lang_resolve.go
- log_once.go
- media_id.go
- media_paths.go
- provider_schema.go
- request_id.go
- store_iface.go
- subtitle_validate.go
- types.go
- types_coverage.go
- types_response.go
- types_scoring.go
- types_search.go
- types_store.go