manualops

package
v0.1.21 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: GPL-2.0, GPL-3.0 Imports: 13 Imported by: 0

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

View Source
const DownloadTimeout = 5 * time.Minute

DownloadTimeout is the context timeout for manual downloads.

View Source
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.

View Source
const MaxResults = 50

MaxResults caps the number of results returned by manual search.

Variables

View Source
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

func IsValidLangCode(lang string) bool

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

func LookupEpisodeMediaID(ctx context.Context, ls *LiveState, seriesID, season, episode int) string

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

func LookupMovieMediaID(ctx context.Context, ls *LiveState, arrID int) string

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

func QueryInt(q interface{ Get(string) string }, key string) int

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

func TryComputeHash(ctx context.Context, ls *LiveState, req *api.SearchRequest, filePath string)

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.

Jump to

Keyboard shortcuts

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