Documentation
¶
Overview ¶
Package manualops implements the business logic for manual subtitle search and download operations. The HTTP handler glue remains in the parent server package; this package owns validation, query parsing, result building, and the background download pipeline.
Index ¶
- Constants
- Variables
- func IsValidLangCode(lang string) bool
- func LookupEpisodeMediaID(ctx context.Context, ls *LiveState, seriesID, season, episode int) string
- func LookupMediaTitle(ctx context.Context, ls *LiveState, mediaType api.MediaType, arrID int) string
- func LookupMovieMediaID(ctx context.Context, ls *LiveState, arrID int) string
- func NotifyError(deps *SearchDeps, source, alertMsg, uiMsg string)
- func ParseSearchQuery(r *http.Request) (req api.SearchRequest, lang string, mediaType api.MediaType, filePath string)
- func PostDownloadUpdate(ctx context.Context, ls *LiveState, db DownloadStore, req *DownloadRequest, ...)
- func QueryInt(q interface{ ... }, key string) int
- func ResolveMediaIDs(ctx context.Context, ls *LiveState, mediaType api.MediaType, ...) (coverageID, historyID string)
- func RunClearLock(ctx context.Context, deps *SearchDeps, mediaType, mediaID, language string) error
- func RunDownload(ctx context.Context, deps *SearchDeps, ls *LiveState, db DownloadStore, ...) bool
- func TryComputeHash(ctx context.Context, ls *LiveState, req *api.SearchRequest, filePath string)
- func ValidateDownloadRequest(req *DownloadRequest) error
- type ActivityTracker
- type BGTracker
- type DownloadRequest
- type DownloadStore
- type EventPublisher
- type Handler
- type HandlerDeps
- type LiveState
- type ManualArrClient
- type ManualSearchResponse
- type Refresher
- type SearchDeps
- type SearchResult
- type SearchStore
Constants ¶
const DownloadTimeout = 5 * time.Minute
DownloadTimeout is the context timeout for manual downloads.
const MaxLangCodeLen = 20
MaxLangCodeLen caps language code length. BCP 47 codes are typically ≤11 chars (e.g. "pt-BR"); 20 provides headroom for unusual subtags.
const MaxResults = 50
MaxResults caps the number of results returned by manual search.
Variables ¶
var ( ErrMissingRequired = errors.New("provider, subtitle_id, file_path, and language are required") ErrInvalidLangCode = errors.New("invalid language code") ErrInvalidMediaType = errors.New("invalid media_type") )
Validation errors returned by ValidateDownloadRequest.
Functions ¶
func IsValidLangCode ¶
IsValidLangCode rejects language codes that are too long, contain path separators, traversal sequences, or control characters (including null bytes that cause path truncation).
func LookupEpisodeMediaID ¶
LookupEpisodeMediaID finds the tvdb-based media ID for a Sonarr episode.
func LookupMediaTitle ¶
func LookupMediaTitle(ctx context.Context, ls *LiveState, mediaType api.MediaType, arrID int) string
LookupMediaTitle resolves the title for a media item from the arr client.
func LookupMovieMediaID ¶
LookupMovieMediaID finds the tmdb-based media ID for a Radarr movie.
func NotifyError ¶
func NotifyError(deps *SearchDeps, source, alertMsg, uiMsg string)
NotifyError publishes an error notification and records an alert.
func ParseSearchQuery ¶
func ParseSearchQuery(r *http.Request) (req api.SearchRequest, lang string, mediaType api.MediaType, filePath string)
ParseSearchQuery extracts search parameters from the request URL.
func PostDownloadUpdate ¶
func PostDownloadUpdate(ctx context.Context, ls *LiveState, db DownloadStore, req *DownloadRequest, mediaType api.MediaType, coverageMediaID, subPath string, variant api.Variant, )
PostDownloadUpdate updates coverage DB and refreshes the arr after a successful manual download.
func QueryInt ¶
QueryInt parses a URL query parameter as a non-negative integer, returning 0 on missing, invalid, or negative values.
func ResolveMediaIDs ¶
func ResolveMediaIDs(ctx context.Context, ls *LiveState, mediaType api.MediaType, arrID, season, episode int, ) (coverageID, historyID string)
ResolveMediaIDs determines the coverage and history media IDs for a manual download.
func RunClearLock ¶
func RunClearLock(ctx context.Context, deps *SearchDeps, mediaType, mediaID, language string) error
RunClearLock clears the manual lock for a media+language combination.
func RunDownload ¶
func RunDownload(ctx context.Context, deps *SearchDeps, ls *LiveState, db DownloadStore, prov api.Provider, req *DownloadRequest, ) bool
RunDownload performs the actual download, post-processing, and save. Returns true on success.
func TryComputeHash ¶
TryComputeHash attempts to compute the video hash for the search request. Logs warnings on validation or hash failures; updates req in place on success.
func ValidateDownloadRequest ¶
func ValidateDownloadRequest(req *DownloadRequest) error
ValidateDownloadRequest checks that the download request has all required fields and valid values. It normalises MediaType to "movie" when empty. Returns nil on success.
Types ¶
type ActivityTracker ¶
type ActivityTracker interface {
Start(action, detail string, source activity.ActivitySource) string
End(id string)
Fail(id string)
}
ActivityTracker manages activity lifecycle.
type BGTracker ¶
type BGTracker interface {
Add(delta int)
Done()
}
BGTracker allows the handler to register background goroutines for graceful shutdown tracking.
type DownloadRequest ¶
type DownloadRequest struct {
Provider api.ProviderID `json:"provider"`
SubtitleID string `json:"subtitle_id"`
FilePath string `json:"file_path"`
Language string `json:"language"`
ReleaseName string `json:"release_name,omitempty"`
MediaType api.MediaType `json:"media_type,omitempty"`
Score int `json:"score,omitempty"`
Season int `json:"season,omitempty"`
Episode int `json:"episode,omitempty"`
ArrID int `json:"media_id,omitempty"`
TopPick bool `json:"top_pick,omitempty"`
HearingImp bool `json:"hearing_impaired,omitempty"`
Forced bool `json:"forced,omitempty"`
}
DownloadRequest holds the parsed fields for a manual download.
type DownloadStore ¶
type DownloadStore interface {
SearchStore
NextManualNumber(ctx context.Context, mediaType api.MediaType, mediaID, language string) int
UpsertSubtitleFile(ctx context.Context, mediaType api.MediaType, mediaID string, sf *api.SubtitleFile) error
SetSyncOffset(ctx context.Context, path string, offsetMs int64) error
SaveDownload(ctx context.Context, rec *api.DownloadRecord) error
}
DownloadStore is the narrow store interface for manual download operations.
type EventPublisher ¶
type EventPublisher interface {
PublishNotify(level events.NotifyLevel, text string)
PublishCoverageUpdate(mediaType api.MediaType, mediaID, language, source, path string)
}
EventPublisher publishes events to SSE clients.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler provides HTTP handlers for manual search and download endpoints.
func NewHandler ¶
func NewHandler(deps HandlerDeps) *Handler
NewHandler creates a manual ops Handler with the given dependencies.
func (*Handler) HandleClearLock ¶
func (h *Handler) HandleClearLock(w http.ResponseWriter, r *http.Request)
HandleClearLock handles POST /api/search/clear-lock.
func (*Handler) HandleManualDownload ¶
func (h *Handler) HandleManualDownload(w http.ResponseWriter, r *http.Request)
HandleManualDownload handles POST /api/search/download.
func (*Handler) HandleManualSearch ¶
func (h *Handler) HandleManualSearch(w http.ResponseWriter, r *http.Request)
HandleManualSearch handles GET /api/search?imdb=tt1234567&lang=fr&type=movie
type HandlerDeps ¶
type HandlerDeps struct {
DBFunc func() DownloadStore
Activity ActivityTracker
Alerts activity.WarnRecorder
Events EventPublisher
StateFunc func() *LiveState
BGTracker BGTracker
ServerCtx func() context.Context
ValidatePath func(w http.ResponseWriter, r *http.Request, path, label string) bool
DecodeJSON func(w http.ResponseWriter, r *http.Request, v any, maxSize int64) bool
}
HandlerDeps holds the dependencies for the manual search/download HTTP handlers.
type LiveState ¶
type LiveState struct {
Cfg api.ConfigProvider
Engine api.SearchEngine
Sonarr ManualArrClient
Radarr ManualArrClient
Providers []api.Provider
}
LiveState holds the runtime state needed for a manual search pass.
type ManualArrClient ¶
type ManualArrClient interface {
GetSeriesByID(ctx context.Context, seriesID int) (*api.Series, error)
GetMovieByID(ctx context.Context, movieID int) (*api.Movie, error)
RefreshSeries(ctx context.Context, seriesID int) error
RefreshMovie(ctx context.Context, movieID int) error
}
ManualArrClient is the narrow interface for arr operations needed by manual download handlers: media lookup + refresh after download.
type ManualSearchResponse ¶
type ManualSearchResponse struct {
Results []SearchResult `json:"results"`
ManuallyLocked bool `json:"manually_locked"`
}
ManualSearchResponse is the typed response from RunSearch.
func RunSearch ¶
func RunSearch(ctx context.Context, deps *SearchDeps, ls *LiveState, req *api.SearchRequest, lang string, mediaType api.MediaType, filePath string, ) ManualSearchResponse
RunSearch executes the manual search against all providers and returns the JSON-ready response payload.
type Refresher ¶
type Refresher interface {
RefreshSeries(ctx context.Context, seriesID int) error
RefreshMovie(ctx context.Context, movieID int) error
}
Refresher is the narrow interface for triggering arr metadata refreshes after manual downloads. Only RefreshSeries and RefreshMovie are needed.
type SearchDeps ¶
type SearchDeps struct {
DB SearchStore
Activity ActivityTracker
Alerts activity.WarnRecorder
Events EventPublisher
}
SearchDeps holds the narrow dependencies for manual search execution.
type SearchResult ¶
type SearchResult struct {
Matches map[string]int `json:"matches,omitempty"`
Provider api.ProviderID `json:"provider"`
Language string `json:"language"`
ReleaseName string `json:"release_name"`
MatchedBy string `json:"matched_by"`
SubtitleID string `json:"subtitle_id"`
Score int `json:"score"`
HearingImp bool `json:"hearing_impaired"`
Forced bool `json:"forced"`
OnDisk bool `json:"on_disk"`
}
SearchResult is a single result returned by the manual search API.
func BuildSearchResults ¶
func BuildSearchResults(scored []api.ScoredResult, refs []api.DownloadedRef) []SearchResult
BuildSearchResults converts scored results to API response format.
type SearchStore ¶
type SearchStore interface {
IsManuallyLocked(ctx context.Context, mediaType api.MediaType, mediaID, language string) (bool, error)
DownloadedRefs(ctx context.Context, mediaType api.MediaType, mediaID, language string) ([]api.DownloadedRef, error)
ClearManualLock(ctx context.Context, mediaType api.MediaType, mediaID, language string) error
}
SearchStore is the narrow store interface for manual search operations.