scanning

package
v0.1.142 Latest Latest
Warning

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

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

Documentation

Overview

Package scanning implements the full-scan orchestration engine.

It iterates all wanted episodes and movies from arr APIs, sorts them alphabetically, searches for missing subtitles, and records results. The scheduling infrastructure (timers, goroutine lifecycle) remains in the parent server package.

Index

Constants

View Source
const (
	ScanFound     = api.ScanFound
	ScanSkipped   = api.ScanSkipped
	ScanNoResult  = api.ScanNoResult
	ScanBackedOff = api.ScanBackedOff
)

Scan outcome constants re-exported from api for local use.

Variables

This section is empty.

Functions

func EpisodeSearchRequest

func EpisodeSearchRequest(series *arrapi.Series, ep *arrapi.Episode, langs []string) api.SearchRequest

EpisodeSearchRequest builds a SearchRequest from arr Series+Episode data. This is the single source of truth for the episode→SearchRequest mapping, used by both scanning and polling.

func ExtractAltTitles

func ExtractAltTitles(alts []arrapi.AlternateTitle, primary string) []string

ExtractAltTitles returns unique alternative titles, excluding the primary.

func MovieSearchRequest

func MovieSearchRequest(m *arrapi.Movie, langs []string) api.SearchRequest

MovieSearchRequest builds a SearchRequest from arr Movie data. This is the single source of truth for the movie→SearchRequest mapping, used by both scanning and polling.

func RunFullScan

func RunFullScan(ctx context.Context, deps *Deps, ls *LiveState)

RunFullScan iterates all wanted episodes and movies, searching for missing subtitles. Episodes and movies are sorted alphabetically by title.

func ScanItemSeasonEp

func ScanItemSeasonEp(item ScanItem) (season, episode int)

ScanItemSeasonEp returns season and episode for sorting.

func ScanItemTitle

func ScanItemTitle(item ScanItem) string

ScanItemTitle returns the title used for sort ordering.

func SceneOrPath

func SceneOrPath(sceneName, filePath string) string

SceneOrPath returns sceneName if non-empty, otherwise filePath.

func SkipResumed

func SkipResumed(item ScanItem, recent map[string]bool, stats *api.ScanStats) bool

SkipResumed checks if a scan item was already processed recently.

Types

type ActivityTracker

type ActivityTracker interface {
	Start(action, detail string, source activity.ActivitySource) string
	End(id string)
	Fail(id string)
	Progress(id string, current, total int, msg string)
	SetQueued(id string, queued bool)
	IsCancelled(id string) bool
}

ActivityTracker manages scan activity lifecycle.

type AlertRecorder

type AlertRecorder interface {
	Record(category, msg string)
	RecordInfo(msg string)
}

AlertRecorder records alerts visible in the UI.

type BGTracker

type BGTracker interface {
	Add(delta int)
	Done()
}

BGTracker allows the scanning handler to register background goroutines with the server's WaitGroup for graceful shutdown.

type Deps

type Deps struct {
	DB            ScanStore
	Metrics       ScanMetrics
	Events        EventPublisher
	Activity      ActivityTracker
	Alerts        AlertRecorder
	ShowSkipCache *showskip.Cache
	// SleepCtx is a context-aware sleep function injected to avoid importing provider/.
	SleepCtx func(ctx context.Context, d time.Duration) error
	// ClearCaches clears provider download caches after scan completion.
	ClearCaches func(providers []api.Provider)
}

Deps holds the narrow dependencies the scan orchestration needs from the server. This avoids importing the full Server struct.

type EventPublisher

type EventPublisher interface {
	PublishCoverageUpdate(mediaType api.MediaType, mediaID string)
	PublishScanStart(action, detail string, source activity.ActivitySource)
	PublishScanDone(action, detail string, source activity.ActivitySource, ok bool)
}

EventPublisher publishes events to SSE clients.

type Handler

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

Handler provides HTTP handlers for the /api/scan/* endpoints.

func NewHandler

func NewHandler(deps HandlerDeps) *Handler

NewHandler creates a scan Handler with the given dependencies.

func (*Handler) HandleScanItem

func (h *Handler) HandleScanItem(w http.ResponseWriter, r *http.Request)

HandleScanItem scans a single episode or movie. POST /api/scan/item with JSON body.

func (*Handler) HandleScanMovie

func (h *Handler) HandleScanMovie(w http.ResponseWriter, r *http.Request)

HandleScanMovie scans all missing subtitles for a specific movie. POST /api/scan/movie/{radarrId}

func (*Handler) HandleScanSeason

func (h *Handler) HandleScanSeason(w http.ResponseWriter, r *http.Request)

HandleScanSeason scans all missing subtitles for a specific season. POST /api/scan/season/{sonarrId}/{seasonNum}

func (*Handler) HandleScanSeries

func (h *Handler) HandleScanSeries(w http.ResponseWriter, r *http.Request)

HandleScanSeries scans all missing subtitles for a specific series. POST /api/scan/series/{sonarrId}

type HandlerDeps

type HandlerDeps struct {
	// StateFunc returns the current live state snapshot.
	StateFunc func() *HandlerState
	// CtxFunc returns the server-level context (outlives individual requests).
	CtxFunc func() context.Context
	// ScanDeps provides the scanning engine dependencies.
	ScanDeps func() *Deps
	// ScanLiveStateFunc converts HandlerState to scanning.LiveState.
	ScanLiveStateFunc func() *LiveState
	// Activity tracks scan activity lifecycle.
	Activity ActivityTracker
	// ScanGuard serializes manual scan requests.
	ScanGuard *ScanGuard
	// Alerts records alerts visible in the UI.
	Alerts AlertRecorder
	// Events publishes SSE events.
	Events EventPublisher
	// InvalidateStats clears the stats cache after scan completion.
	InvalidateStats func()
	// BGTracker tracks background goroutine lifecycle for graceful shutdown.
	BGTracker BGTracker
}

HandlerDeps holds the dependencies for the scan HTTP handler family.

type HandlerState

type HandlerState struct {
	Cfg    api.ConfigProvider
	Engine api.SearchEngine
	Sonarr ScanHandlerSonarr // nil when sonarr not configured
	Radarr ScanHandlerRadarr // nil when radarr not configured
}

HandlerState holds the runtime state needed by scan HTTP handlers. Provided by the server on each request via the StateFunc callback.

type LiveState

type LiveState struct {
	Cfg         api.ConfigProvider
	Engine      api.SearchEngine
	Sonarr      ScanSonarrClient
	Radarr      ScanRadarrClient
	ShowCounter api.ShowSubtitleCounter
	Providers   []api.Provider
}

LiveState holds the runtime state needed for a scan pass.

type ScanGuard

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

ScanGuard serializes manual scan requests. Extracted from activity.Log to separate coordination concerns from data concerns.

func (*ScanGuard) Lock

func (g *ScanGuard) Lock()

Lock acquires the scan serialization lock.

func (*ScanGuard) Unlock

func (g *ScanGuard) Unlock()

Unlock releases the scan serialization lock.

type ScanHandlerRadarr added in v0.1.106

type ScanHandlerRadarr interface {
	GetMovieByID(ctx context.Context, id int) (arrapi.Movie, error)
}

ScanHandlerRadarr is the Radarr surface the manual scan HTTP handlers call.

type ScanHandlerSonarr added in v0.1.106

type ScanHandlerSonarr interface {
	GetSeriesByID(ctx context.Context, id int) (arrapi.Series, error)
	GetEpisodes(ctx context.Context, seriesID int) ([]arrapi.Episode, error)
}

ScanHandlerSonarr is the Sonarr surface the manual scan HTTP handlers call.

type ScanItem

type ScanItem struct {
	Series *arrapi.Series  // non-nil for episodes
	Ep     *arrapi.Episode // non-nil for episodes
	Movie  *arrapi.Movie   // non-nil for movies
}

ScanItem holds either an episode or a movie for alphabetical scanning.

func SortByTitle

func SortByTitle(episodes, movies []ScanItem) []ScanItem

SortByTitle merges episodes and movies into a single slice sorted alphabetically by title (case-insensitive).

type ScanMetrics

type ScanMetrics interface {
	RecordScan(items, found int, dur time.Duration)
	AdaptiveSkip()
}

ScanMetrics records scan-level metrics.

type ScanOutcome

type ScanOutcome = api.ScanOutcome

ScanOutcome is a type alias for api.ScanOutcome.

func ScanEpisode

func ScanEpisode(ctx context.Context, deps *Deps, ls *LiveState, series *arrapi.Series, ep *arrapi.Episode, forceUpgrade ...bool) (outcome ScanOutcome, searchedLangs, foundLangs []string)

ScanEpisode searches for subtitles for a single episode. Returns the scan outcome, the language codes whose group actually ran (searchedLangs — the only langs the season tracker may record), and the language codes that had at least one subtitle downloaded (foundLangs, a subset of searchedLangs).

func ScanMovie

func ScanMovie(ctx context.Context, deps *Deps, ls *LiveState, m *arrapi.Movie, forceUpgrade ...bool) ScanOutcome

ScanMovie searches for subtitles for a single movie.

type ScanRadarrClient added in v0.1.106

type ScanRadarrClient interface {
	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
}

ScanRadarrClient is the Radarr surface the full-scan engine needs.

type ScanSonarrClient added in v0.1.106

type ScanSonarrClient interface {
	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
}

ScanSonarrClient is the Sonarr surface the full-scan engine needs: wanted-episode iteration, exclude-tag resolution, and a post-download rescan.

type ScanStore

type ScanStore interface {
	RecentlyScanned(ctx context.Context, cutoff time.Time) (map[string]bool, error)
	RecordScanState(ctx context.Context, rec *api.ScanRecord) error
}

ScanStore is the narrow store interface for scan state tracking.

Jump to

Keyboard shortcuts

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