db

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: GPL-3.0 Imports: 38 Imported by: 0

Documentation

Overview

Package db exposes the database surface as the Store interface. Callers hold db.Store (or *DB directly) and invoke methods, rather than calling top-level functions that take an *ent.Client.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Open

func Open(ctx context.Context, dsn string) (*ent.Client, error)

Open opens the SQLite database with OTel instrumentation wired via otelsql. Every ent query becomes a child span with db.* semconv attributes, and connection-pool stats are registered with the global meter provider.

func RegisterEntityMetrics

func RegisterEntityMetrics(meter metric.Meter, db *ent.Client) error

RegisterEntityMetrics registers observable gauges reporting the current number of domain entities in the database. The callbacks run on each metric collection cycle and query the database directly.

Types

type ActivityFilter

type ActivityFilter struct {
	Types   []movieevent.Type
	MovieID *uint32
	Since   *time.Time
	Before  *time.Time
	Limit   int
	Cursor  string
}

type ActivityResult

type ActivityResult struct {
	Events     []*ent.MovieEvent
	NextCursor string
}

type CreateAPIKeyParams

type CreateAPIKeyParams struct {
	Name    string
	KeyHash string
	OwnerID uint32
}

type CreateDownloadRecordParams

type CreateDownloadRecordParams struct {
	Title              string
	Size               int64
	TorrentHash        string
	Status             downloadrecord.Status
	MovieID            uint32
	EpisodeID          uint32
	DownloadClientName string
	IndexerName        string
	// Adoption proposals persist these so the pending queue and a later
	// import have the parsed quality, on-disk path, and human reason.
	SavePath      string
	Quality       string
	FailureReason string
}

type CreateImportScanFileParams

type CreateImportScanFileParams struct {
	SourcePath         string
	Size               int64
	ParsedTitle        string
	ParsedYear         *uint16
	ParsedQuality      string
	ParsedReleaseGroup string
	Classification     entimportscanfile.Classification
	Candidates         []schema.ScannedCandidate
	TMDBID             uint32 // 0 = unset
	ExistingMovieID    uint32 // 0 = unset
}

type CreateImportScanParams

type CreateImportScanParams struct {
	SourcePath string
	Kind       entimportscan.Kind // empty = movie (schema default)
	Mode       entimportscan.Mode
	ImportMode entimportscan.ImportMode // empty = fall back to library.import_mode at commit time
}

type CreateImportScanShowParams

type CreateImportScanShowParams struct {
	FolderPath       string
	ParsedTitle      string
	ParsedYear       *uint16
	Classification   entimportscanshow.Classification
	TVDBID           *uint32
	Candidates       []schema.ScannedShowCandidate
	ExistingTvshowID *uint32
	FileCount        uint16
}

type CreateInviteParams

type CreateInviteParams struct {
	TokenHash   string
	Email       string
	Role        invite.Role
	ExpiresAt   time.Time
	CreatedByID uint32
}

type CreateMediaFileParams

type CreateMediaFileParams struct {
	Path         string
	Size         int64
	Quality      string
	Format       string
	ReleaseGroup string
	MovieID      uint32 // set for movie files; mutually exclusive with EpisodeID
	EpisodeID    uint32 // set for episode files (e.g. series adoption)
	Source       mediafile.Source
}

type CreateMovieParams

type CreateMovieParams struct {
	Title          string
	OriginalTitle  string
	Year           uint16
	TmdbID         uint32
	Status         movie.Status
	Overview       string
	Runtime        uint16
	QualityProfile string
}

type CreateOIDCIdentityParams

type CreateOIDCIdentityParams struct {
	Provider string
	Subject  string
	Email    string
	OwnerID  uint32
}

type CreateRequestParams

type CreateRequestParams struct {
	MediaType   string // "movie" | "tvshow"
	MediaID     uint32
	Title       string
	RequesterID uint32
}

type CreateSessionParams

type CreateSessionParams struct {
	JTI       string
	UserID    uint32
	IP        string
	UserAgent string
	ExpiresAt time.Time
}

type CreateTVShowParams

type CreateTVShowParams struct {
	Title          string
	OriginalTitle  string
	Year           uint16
	Overview       string
	TvdbID         uint32
	SeriesStatus   string
	Type           string
	Network        string
	Creator        string
	Runtime        uint16
	Rating         float64
	Genres         []string
	PosterPath     string
	QualityProfile string
	Seasons        []SeasonSeed
}

type CreateUserParams

type CreateUserParams struct {
	Email        string
	DisplayName  string
	PasswordHash string
	Role         user.Role
	AuthMethod   user.AuthMethod
}

type DB

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

DB is the ent-backed implementation of Store.

func New

func New(c *ent.Client) *DB

New constructs a *DB from an open ent client.

func (*DB) AbortInflightImportScans

func (db *DB) AbortInflightImportScans(
	ctx context.Context,
	reason string,
) (uint32, error)

func (*DB) AllDownloadRecordHashes

func (db *DB) AllDownloadRecordHashes(
	ctx context.Context,
) (map[string]struct{}, error)

AllDownloadRecordHashes returns the set of non-empty torrent hashes across every download_record (any status). The adoption pass uses it to skip torrents streamline already tracks.

func (*DB) ApproveRequest

func (db *DB) ApproveRequest(ctx context.Context, id, adminID uint32) error

func (*DB) BulkCreateImportScanFiles

func (db *DB) BulkCreateImportScanFiles(
	ctx context.Context,
	scanID uint32,
	files []CreateImportScanFileParams,
) error

func (*DB) BulkCreateImportScanShows

func (db *DB) BulkCreateImportScanShows(
	ctx context.Context, scanID uint32, shows []CreateImportScanShowParams,
) error

func (*DB) BumpMediaFileLastSeen

func (db *DB) BumpMediaFileLastSeen(ctx context.Context, id uint32) error

func (*DB) CascadeSeasonMonitored

func (db *DB) CascadeSeasonMonitored(
	ctx context.Context,
	seasonID uint32,
	monitored bool,
) error

CascadeSeasonMonitored sets a season and all its episodes to monitored in one transaction, so a season toggle flows down to its episodes.

func (*DB) CascadeShowMonitored

func (db *DB) CascadeShowMonitored(
	ctx context.Context,
	showID uint32,
	monitored bool,
) error

CascadeShowMonitored sets every season and episode of a show to monitored, so toggling a series' monitor flag flows down to its whole tree (an unmonitored show must not leave monitored episodes for the fetcher to grab). Both bulk updates run in one transaction.

func (*DB) CountActiveImportScans

func (db *DB) CountActiveImportScans(ctx context.Context) (uint32, error)

func (*DB) CountMovies

func (db *DB) CountMovies(ctx context.Context) (int, error)

func (*DB) CountMoviesByStatus

func (db *DB) CountMoviesByStatus(
	ctx context.Context,
	status movie.Status,
) (int, error)

func (*DB) CountRequestsByStatus

func (db *DB) CountRequestsByStatus(
	ctx context.Context,
	status request.Status,
) (int, error)

func (*DB) CountTVShows

func (db *DB) CountTVShows(ctx context.Context) (int, error)

func (*DB) CountTVShowsByStatus

func (db *DB) CountTVShowsByStatus(
	ctx context.Context,
	status tvshow.SeriesStatus,
) (int, error)

CountTVShowsByStatus counts shows in the given series_status. Powers the continuing/ended tallies on the series counts endpoint.

func (*DB) CountUsers

func (db *DB) CountUsers(ctx context.Context) (int, error)

func (*DB) CountUsersByRole

func (db *DB) CountUsersByRole(ctx context.Context, role user.Role) (int, error)

CountUsersByRole returns the number of users with the given role. Used to protect the last-admin invariant.

func (*DB) CreateAPIKey

func (db *DB) CreateAPIKey(
	ctx context.Context,
	p CreateAPIKeyParams,
) (*ent.ApiKey, error)

func (*DB) CreateDownloadRecord

func (db *DB) CreateDownloadRecord(
	ctx context.Context,
	p CreateDownloadRecordParams,
) (*ent.DownloadRecord, error)

func (*DB) CreateImportScan

func (db *DB) CreateImportScan(
	ctx context.Context,
	p CreateImportScanParams,
) (*ent.ImportScan, error)

func (*DB) CreateInvite

func (db *DB) CreateInvite(
	ctx context.Context,
	p CreateInviteParams,
) (*ent.Invite, error)

func (*DB) CreateMediaFile

func (db *DB) CreateMediaFile(
	ctx context.Context,
	p CreateMediaFileParams,
) (*ent.MediaFile, error)

func (*DB) CreateMovie

func (db *DB) CreateMovie(
	ctx context.Context,
	p CreateMovieParams,
) (*ent.Movie, error)

func (*DB) CreateOIDCIdentity

func (db *DB) CreateOIDCIdentity(
	ctx context.Context,
	p CreateOIDCIdentityParams,
) (*ent.OIDCIdentity, error)

func (*DB) CreateRequest

func (db *DB) CreateRequest(
	ctx context.Context,
	p CreateRequestParams,
) (*ent.Request, error)

func (*DB) CreateSession

func (db *DB) CreateSession(
	ctx context.Context,
	p CreateSessionParams,
) (*ent.Session, error)

func (*DB) CreateTVShow

func (db *DB) CreateTVShow(
	ctx context.Context,
	p CreateTVShowParams,
) (*ent.TVShow, error)

CreateTVShow inserts the show + its seasons + episodes in one transaction.

func (*DB) CreateUser

func (db *DB) CreateUser(
	ctx context.Context,
	p CreateUserParams,
) (*ent.User, error)

func (*DB) DeleteAPIKeyByID

func (db *DB) DeleteAPIKeyByID(
	ctx context.Context,
	userID, keyID uint32,
) (int, error)

DeleteAPIKeyByID deletes an API key scoped by userID. Returns the number of rows deleted (0 if the key does not exist or belongs to another user).

func (*DB) DeleteAllCompletedDownloadRecords

func (db *DB) DeleteAllCompletedDownloadRecords(
	ctx context.Context,
) (int, error)

DeleteAllCompletedDownloadRecords removes every completed record (the "Clear completed" action). Returns the number of rows deleted.

func (*DB) DeleteCompletedDownloadRecordsBefore

func (db *DB) DeleteCompletedDownloadRecordsBefore(
	ctx context.Context,
	cutoff time.Time,
) (int, error)

DeleteCompletedDownloadRecordsBefore deletes records whose status is completed and whose imported_at is older than cutoff. Returns the number of rows deleted.

func (*DB) DeleteDownloadRecord

func (db *DB) DeleteDownloadRecord(ctx context.Context, id uint32) error

DeleteDownloadRecord deletes one record by ID. Returns ent.NotFound when absent (handler maps to 404).

func (*DB) DeleteFailedDownloadRecordsBefore

func (db *DB) DeleteFailedDownloadRecordsBefore(
	ctx context.Context,
	cutoff time.Time,
) (int, error)

DeleteFailedDownloadRecordsBefore deletes records whose status is failed and whose update_time is older than cutoff. Returns the number of rows deleted.

func (*DB) DeleteImportScan

func (db *DB) DeleteImportScan(ctx context.Context, id uint32) error

func (*DB) DeleteMediaFileAndRevertEpisode

func (db *DB) DeleteMediaFileAndRevertEpisode(
	ctx context.Context,
	mediaFileID, episodeID uint32,
) error

DeleteMediaFileAndRevertEpisode removes the MediaFile row and flips the owning episode's status back to "wanted" inside a single transaction.

func (*DB) DeleteMediaFileAndRevertMovie

func (db *DB) DeleteMediaFileAndRevertMovie(
	ctx context.Context,
	mediaFileID, movieID uint32,
) error

DeleteMediaFileAndRevertMovie removes the MediaFile row and flips the owning movie's status back to "wanted" inside a single transaction. Used by drift_check when a tracked file disappears from disk.

func (*DB) DeleteMovie

func (db *DB) DeleteMovie(ctx context.Context, id uint32) error

func (*DB) DeleteStalePendingAdoptions

func (db *DB) DeleteStalePendingAdoptions(
	ctx context.Context,
	clientName string,
	liveHashes []string,
) (int, error)

DeleteStalePendingAdoptions removes pending adoption proposals for clientName whose torrent_hash is no longer among liveHashes (the client's current managed torrents). An empty liveHashes means the client reported zero torrents, so every pending proposal for it is stale. Returns the count removed. Call only with a client that listed successfully — otherwise a transient outage would purge valid proposals.

func (*DB) DeleteTVShow

func (db *DB) DeleteTVShow(ctx context.Context, id uint32) error

func (*DB) DeleteUser

func (db *DB) DeleteUser(ctx context.Context, id uint32) error

DeleteUser permanently removes the user and all dependent data (api keys, oidc identities, requests, sessions) via ON DELETE CASCADE at the schema level. Requests the user approved for others are preserved with approved_by set to NULL (ON DELETE SET NULL) so the request history of other users is not corrupted.

GDPR-compliant: every row that belongs to the user is erased atomically.

func (*DB) DenyRequest

func (db *DB) DenyRequest(
	ctx context.Context,
	id, adminID uint32,
	reason string,
) error

func (*DB) FilterImportScanFiles

func (db *DB) FilterImportScanFiles(
	ctx context.Context,
	p FilterImportScanFilesParams,
) ([]*ent.ImportScanFile, uint32, error)

func (*DB) FilterMovies

func (db *DB) FilterMovies(
	ctx context.Context,
	p FilterMoviesParams,
) ([]*ent.Movie, int, error)

func (*DB) FindAPIKeyByHash

func (db *DB) FindAPIKeyByHash(
	ctx context.Context,
	hash string,
) (*ent.ApiKey, error)

func (*DB) FindActiveDownloadRecordByID

func (db *DB) FindActiveDownloadRecordByID(
	ctx context.Context,
	id uint32,
) (*ent.DownloadRecord, error)

FindActiveDownloadRecordByID fetches an in-flight record by ID with movie + download_client edges. Returns ent.NotFound when absent or already terminal.

func (*DB) FindActiveRequest

func (db *DB) FindActiveRequest(
	ctx context.Context,
	mediaType string,
	mediaID uint32,
) (*ent.Request, error)

FindActiveRequest returns an existing pending/approved/available request for the given media, or nil when none exists (used for dedup).

func (*DB) FindImportScan

func (db *DB) FindImportScan(
	ctx context.Context,
	id uint32,
) (*ent.ImportScan, error)

func (*DB) FindImportScanFile

func (db *DB) FindImportScanFile(
	ctx context.Context,
	scanID, fileID uint32,
) (*ent.ImportScanFile, error)

func (*DB) FindImportScanShow

func (db *DB) FindImportScanShow(
	ctx context.Context, scanID, showID uint32,
) (*ent.ImportScanShow, error)

func (*DB) FindImportingDownloadRecordByID

func (db *DB) FindImportingDownloadRecordByID(
	ctx context.Context,
	id uint32,
) (*ent.DownloadRecord, error)

FindImportingDownloadRecordByID fetches a single importing record by ID with its Movie + DownloadClient edges eager-loaded. Returns ent.NotFound when the record is absent or no longer in status=importing (both treated as "nothing to do" by the worker).

func (*DB) FindInviteByTokenHash

func (db *DB) FindInviteByTokenHash(
	ctx context.Context,
	hash string,
) (*ent.Invite, error)

func (*DB) FindMediaFileByEpisodeID

func (db *DB) FindMediaFileByEpisodeID(
	ctx context.Context,
	episodeID uint32,
) (*ent.MediaFile, error)

FindMediaFileByEpisodeID returns the MediaFile owned by an episode (an episode has at most one), or ent NotFound when it has none.

func (*DB) FindMediaFileByID

func (db *DB) FindMediaFileByID(
	ctx context.Context,
	id uint32,
) (*ent.MediaFile, error)

FindMediaFileByID returns a single MediaFile by ID, or ent NotFound.

func (*DB) FindMovieByID

func (db *DB) FindMovieByID(ctx context.Context, id uint32) (*ent.Movie, error)

func (*DB) FindMovieByTMDBID

func (db *DB) FindMovieByTMDBID(
	ctx context.Context,
	tmdbID uint32,
) (*ent.Movie, error)

func (*DB) FindMoviesByTMDBIDs

func (db *DB) FindMoviesByTMDBIDs(
	ctx context.Context,
	tmdbIDs []uint32,
) ([]*ent.Movie, error)

FindMoviesByTMDBIDs returns movies whose tmdb_id is in tmdbIDs. Used by movie.AnnotateTMDBResults to flag already-added rows in TMDB search results without an N+1 lookup loop.

func (*DB) FindOIDCIdentity

func (db *DB) FindOIDCIdentity(
	ctx context.Context,
	provider, subject string,
) (*ent.OIDCIdentity, error)

FindOIDCIdentity returns the identity matching (provider, subject) with its owner preloaded.

func (*DB) FindOpenImportScanForSource

func (db *DB) FindOpenImportScanForSource(
	ctx context.Context,
	sourcePath string,
) (*ent.ImportScan, error)

FindOpenImportScanForSource returns the oldest awaiting_review scan for sourcePath, or ent NotFound. The orphan scanner uses it to fold new orphans into the one open review queue for a directory instead of minting a fresh scan on every run.

func (*DB) FindPendingDownloadRecordByID

func (db *DB) FindPendingDownloadRecordByID(
	ctx context.Context,
	id uint32,
) (*ent.DownloadRecord, error)

FindPendingDownloadRecordByID returns a single status=pending record with its media edges, or ent NotFound.

func (*DB) FindSessionByJTI

func (db *DB) FindSessionByJTI(
	ctx context.Context,
	jti string,
) (*ent.Session, error)

func (*DB) FindTVShowByID

func (db *DB) FindTVShowByID(ctx context.Context, id uint32) (*ent.TVShow, error)

func (*DB) FindTVShowByTVDBID

func (db *DB) FindTVShowByTVDBID(
	ctx context.Context,
	tvdbID uint32,
) (*ent.TVShow, error)

func (*DB) FindUnusedInviteForEmail

func (db *DB) FindUnusedInviteForEmail(
	ctx context.Context,
	email string,
	now time.Time,
) (*ent.Invite, error)

FindUnusedInviteForEmail returns the earliest unused + unexpired invite bound to the given email.

func (*DB) FindUserByEmail

func (db *DB) FindUserByEmail(ctx context.Context, email string) (*ent.User, error)

func (*DB) FindUserByID

func (db *DB) FindUserByID(ctx context.Context, id uint32) (*ent.User, error)

func (*DB) GetRequest

func (db *DB) GetRequest(ctx context.Context, id uint32) (*ent.Request, error)

func (*DB) IncrementEpisodeGrabFailures

func (db *DB) IncrementEpisodeGrabFailures(ctx context.Context, id uint32) error

func (*DB) IncrementImportScanProgress

func (db *DB) IncrementImportScanProgress(
	ctx context.Context,
	id uint32,
	processedDelta uint32,
) error

func (*DB) IncrementMovieGrabFailures

func (db *DB) IncrementMovieGrabFailures(ctx context.Context, id uint32) error

func (*DB) LatestImportedRecordForEpisode

func (db *DB) LatestImportedRecordForEpisode(
	ctx context.Context,
	episodeID uint32,
) (*ent.DownloadRecord, error)

LatestImportedRecordForEpisode is the episode twin of the above.

func (*DB) LatestImportedRecordForMovie

func (db *DB) LatestImportedRecordForMovie(
	ctx context.Context,
	movieID uint32,
) (*ent.DownloadRecord, error)

LatestImportedRecordForMovie returns the most recent record for a movie that carries a torrent hash (so file-delete can remove the source torrent). ent NotFound when none.

func (*DB) ListAPIKeysByUser

func (db *DB) ListAPIKeysByUser(
	ctx context.Context,
	userID uint32,
) ([]*ent.ApiKey, error)

func (*DB) ListActiveDownloadRecords

func (db *DB) ListActiveDownloadRecords(
	ctx context.Context,
) ([]*ent.DownloadRecord, error)

ListActiveDownloadRecords returns records still in flight (downloading or importing) with movie / download_client / indexer edges eager-loaded. Powers the live queue snapshot.

func (*DB) ListAllEpisodeMediaFilePaths

func (db *DB) ListAllEpisodeMediaFilePaths(ctx context.Context) ([]string, error)

ListAllEpisodeMediaFilePaths returns every episode media_file path, for the series scanner's tracked-file gate (a show whose files are all tracked is skipped).

func (*DB) ListAllMediaFilesWithMovie

func (db *DB) ListAllMediaFilesWithMovie(
	ctx context.Context,
) ([]*ent.MediaFile, error)

func (*DB) ListDownloadHistory

func (db *DB) ListDownloadHistory(
	ctx context.Context,
	limit int,
	cursor string,
) (*DownloadHistoryResult, error)

ListDownloadHistory returns terminal records (completed or failed) newest first, keyset-paginated on (update_time, id). Reuses the activity cursor scheme.

func (*DB) ListDownloadingRecords

func (db *DB) ListDownloadingRecords(
	ctx context.Context,
) ([]*ent.DownloadRecord, error)

ListDownloadingRecords returns every download_record whose status is "downloading". Used by the download manager's status-poll loop.

func (*DB) ListDownloadingRecordsWithMovie

func (db *DB) ListDownloadingRecordsWithMovie(
	ctx context.Context,
) ([]*ent.DownloadRecord, error)

ListDownloadingRecordsWithMovie returns every "downloading" download_record with its Movie edge preloaded. Used by the orphan-torrent reconciliation pass.

func (*DB) ListEligibleMoviesForSync

func (db *DB) ListEligibleMoviesForSync(
	ctx context.Context,
	maxGrabFailures uint8,
	notSearchedSince time.Time,
) ([]*ent.Movie, error)

ListEligibleMoviesForSync returns wanted movies not over the failure cap whose cooldown window has expired (or has never run). Movies with an in-flight download_record (downloading or importing) are excluded so a stale movie.status row doesn't trigger a redundant grab while the download pipeline is still working — defense against state drift.

func (*DB) ListImportScanFilesForCommit

func (db *DB) ListImportScanFilesForCommit(
	ctx context.Context,
	scanID uint32,
) ([]*ent.ImportScanFile, error)

func (*DB) ListImportScanShows

func (db *DB) ListImportScanShows(
	ctx context.Context, p ListImportScanShowsParams,
) ([]*ent.ImportScanShow, uint32, error)

func (*DB) ListImportScanShowsForCommit

func (db *DB) ListImportScanShowsForCommit(
	ctx context.Context, scanID uint32,
) ([]*ent.ImportScanShow, error)

ListImportScanShowsForCommit returns the shows to adopt when a series scan is committed: everything not explicitly skipped that was either accepted by the reviewer or auto-matched (confirmed / existing). Mirrors the movie ListImportScanFilesForCommit filter.

func (*DB) ListImportScans

func (db *DB) ListImportScans(
	ctx context.Context,
	offset, limit uint32,
) ([]*ent.ImportScan, uint32, error)

func (*DB) ListImportingDownloadRecords

func (db *DB) ListImportingDownloadRecords(
	ctx context.Context,
) ([]*ent.DownloadRecord, error)

ListImportingDownloadRecords returns records currently in status=importing. Used by the import_scan scheduler job for restart-safety.

func (*DB) ListInvites

func (db *DB) ListInvites(ctx context.Context) ([]*ent.Invite, error)

func (*DB) ListMediaFilesByMovieID

func (db *DB) ListMediaFilesByMovieID(
	ctx context.Context,
	movieID uint32,
) ([]*ent.MediaFile, error)

func (*DB) ListMovies

func (db *DB) ListMovies(
	ctx context.Context,
	offset, limit uint32,
) ([]*ent.Movie, error)

ListMovies returns a page of movies newest first.

func (*DB) ListMoviesForAdoption

func (db *DB) ListMoviesForAdoption(ctx context.Context) ([]*ent.Movie, error)

ListMoviesForAdoption returns every movie as an adoption-match candidate. The set is small (in-memory matched against untracked torrent names), so a full fetch is fine.

func (*DB) ListMoviesStaleSince

func (db *DB) ListMoviesStaleSince(
	ctx context.Context,
	cutoff time.Time,
) ([]*ent.Movie, error)

ListMoviesStaleSince returns movies whose updated_at is older than cutoff. Used by metadata-refresh to bound TMDB calls per tick.

func (*DB) ListPendingDownloadRecords

func (db *DB) ListPendingDownloadRecords(
	ctx context.Context,
) ([]*ent.DownloadRecord, error)

ListPendingDownloadRecords returns all status=pending records with Movie and Episode (+ its season and show) edges eager-loaded for the needs-attention queue, newest first.

func (*DB) ListPendingImportScanFilePaths

func (db *DB) ListPendingImportScanFilePaths(ctx context.Context) ([]string, error)

ListPendingImportScanFilePaths returns the source_path of every ImportScanFile attached to a scan whose status is still "awaiting_review". Used by orphan_scan to skip files already queued for human review.

func (*DB) ListPendingImportScanShowFolders

func (db *DB) ListPendingImportScanShowFolders(
	ctx context.Context,
) ([]string, error)

ListPendingImportScanShowFolders returns folder_path of every show attached to a scan still awaiting_review. The series scanner uses it to skip folders already queued for review (the show-level analogue of pending file paths).

func (*DB) ListRequests

func (db *DB) ListRequests(
	ctx context.Context,
	p ListRequestsParams,
) ([]*ent.Request, int, error)

func (*DB) ListTVShows

func (db *DB) ListTVShows(
	ctx context.Context,
	offset, limit uint32,
) ([]*ent.TVShow, error)

func (*DB) ListTvShowsForAdoption

func (db *DB) ListTvShowsForAdoption(ctx context.Context) ([]*ent.TVShow, error)

ListTvShowsForAdoption returns every show with its seasons → episodes → media files eager-loaded, so the adoption pass can match a torrent to an episode and check whether that episode already has a file in-memory.

func (*DB) ListUpcomingEpisodes

func (db *DB) ListUpcomingEpisodes(
	ctx context.Context,
	from, to time.Time,
) ([]*ent.Episode, error)

ListUpcomingEpisodes returns monitored episodes whose air_date falls within [from, to], oldest first, with season + show eager-loaded for the calendar.

func (*DB) ListUserSessions

func (db *DB) ListUserSessions(
	ctx context.Context,
	userID uint32,
) ([]*ent.Session, error)

func (*DB) ListUsers

func (db *DB) ListUsers(
	ctx context.Context,
	p ListUsersParams,
) ([]*ent.User, int, error)

ListUsers returns a filtered, paginated list of users along with the total count matching the filter. Sort/Order default to newest-first.

func (*DB) ListWantedEpisodes

func (db *DB) ListWantedEpisodes(ctx context.Context) ([]*ent.TVShow, error)

ListWantedEpisodes returns shows (with seasons+episodes eager-loaded) that have at least one monitored, wanted episode. The episode edges are filtered to only those wanted+monitored rows; the caller (rss/missing searcher) applies the aired-date cutoff.

func (*DB) ListWantedMovies

func (db *DB) ListWantedMovies(ctx context.Context) ([]*ent.Movie, error)

ListWantedMovies returns every movie with status = wanted, regardless of cooldown or grab-failure state. Used by the rss-sync feed scanner to build a per-tick title+year lookup map.

func (*DB) MarkDownloadRecordReplaceExisting

func (db *DB) MarkDownloadRecordReplaceExisting(
	ctx context.Context,
	id uint32,
) error

func (*DB) MarkInviteUsed

func (db *DB) MarkInviteUsed(
	ctx context.Context,
	id uint32,
	when time.Time,
) (*ent.Invite, error)

MarkInviteUsed sets used_at on the invite.

func (*DB) MarkInviteUsedWithUser

func (db *DB) MarkInviteUsedWithUser(
	ctx context.Context,
	id, userID uint32,
	when time.Time,
) (*ent.Invite, error)

MarkInviteUsedWithUser sets used_at and records the consuming user id. Used by RegisterWithInvite inside a transaction.

func (*DB) MarkRequestsAvailable

func (db *DB) MarkRequestsAvailable(
	ctx context.Context,
	mediaType string,
	mediaID uint32,
) error

MarkRequestsAvailable flips every approved request for the given media to available. Called best-effort from the importer success paths.

func (*DB) MovieCreateTimesSince

func (db *DB) MovieCreateTimesSince(
	ctx context.Context,
	since time.Time,
) ([]time.Time, error)

MovieCreateTimesSince returns the create_time of every movie added on or after `since`, oldest first — used to bucket library growth into a trend.

func (*DB) MovieHasMediaFile

func (db *DB) MovieHasMediaFile(ctx context.Context, tmdbID uint32) (bool, error)

func (*DB) PurgeExpiredSessions

func (db *DB) PurgeExpiredSessions(
	ctx context.Context,
	before time.Time,
) (int, error)

func (*DB) RecentActivity

func (db *DB) RecentActivity(
	ctx context.Context,
	f ActivityFilter,
) (*ActivityResult, error)

func (*DB) ReconcileEpisodes

func (db *DB) ReconcileEpisodes(
	ctx context.Context,
	showID uint32,
	seasons []SeasonSeed,
) ([]string, error)

ReconcileEpisodes syncs the stored season/episode tree with freshly fetched provider metadata: existing rows get their season name and episode title/air date refreshed, seasons/episodes the provider now reports but we don't have yet are inserted, and seasons/episodes the provider no longer reports are deleted (their media_file/download_record rows cascade). It returns the on-disk paths of files whose episodes were removed so the caller can delete them from disk — the DB layer never touches the filesystem. User-owned state (monitored, status, grab counters) on surviving rows is preserved; new episodes inherit their season's monitored flag, a brand-new season defaults to monitored.

func (*DB) RecordEpisodeImportSuccess

func (db *DB) RecordEpisodeImportSuccess(
	ctx context.Context,
	p RecordEpisodeImportSuccessParams,
) error

RecordEpisodeImportSuccess mirrors RecordImportSuccess for the TV path: writes the MediaFile (owned by the episode), flips the DownloadRecord to completed, and marks the Episode available — all in one tx. Used per-file so a season pack records each matched episode independently.

func (*DB) RecordImportFailure

func (db *DB) RecordImportFailure(
	ctx context.Context,
	p RecordImportFailureParams,
) error

RecordImportFailure writes attempt counter on retryable; on terminal also flips DownloadRecord + Movie to failed with reason.

func (*DB) RecordImportSuccess

func (db *DB) RecordImportSuccess(
	ctx context.Context,
	p RecordImportSuccessParams,
) error

RecordImportSuccess writes MediaFile row, flips DownloadRecord to completed, flips Movie to available — all in one tx. On error, caller retries.

func (*DB) ReopenRequest

func (db *DB) ReopenRequest(ctx context.Context, id uint32) error

func (*DB) ResetEpisodeGrabFailures

func (db *DB) ResetEpisodeGrabFailures(ctx context.Context, id uint32) error

func (*DB) ResetMovieGrabFailures

func (db *DB) ResetMovieGrabFailures(ctx context.Context, id uint32) error

func (*DB) RevertMovieToWantedIfNoFile

func (db *DB) RevertMovieToWantedIfNoFile(
	ctx context.Context,
	movieID uint32,
) error

RevertMovieToWantedIfNoFile flips a movie back to "wanted" only when it has no MediaFile, so cancelling a download doesn't clobber an available movie that already has a prior file from an upgrade grab.

func (*DB) RevertOrphanedDownloadingEpisodes

func (db *DB) RevertOrphanedDownloadingEpisodes(
	ctx context.Context,
) (int, error)

RevertOrphanedDownloadingEpisodes flips back to "wanted" every episode stuck in "downloading" that has no media file and whose season has no active (downloading/importing) download record. This reconciles the season-pack fan-out: a pack marks every episode downloading but links only one record, so cancelling or losing that record leaves the rest stranded. Granularity is the season — an episode is spared while any download in its season is still active, so it self-heals once that download settles. Returns rows reverted.

func (*DB) RevokeAllUserSessions

func (db *DB) RevokeAllUserSessions(
	ctx context.Context,
	userID uint32,
	when time.Time,
) error

func (*DB) RevokeInvite

func (db *DB) RevokeInvite(ctx context.Context, id uint32, now time.Time) error

RevokeInvite expires the invite immediately by setting expires_at=now.

func (*DB) RevokeOtherUserSessions

func (db *DB) RevokeOtherUserSessions(
	ctx context.Context,
	userID uint32,
	keepJTI string,
	when time.Time,
) error

func (*DB) RevokeSessionByJTI

func (db *DB) RevokeSessionByJTI(
	ctx context.Context,
	jti string,
	when time.Time,
) error

RevokeSessionByJTI marks the session with the given jti as revoked. A second call against an already-revoked row is a no-op.

func (*DB) RevokeUserSessionByID

func (db *DB) RevokeUserSessionByID(
	ctx context.Context,
	userID, sessionID uint32,
	when time.Time,
) (int, error)

RevokeUserSessionByID revokes a session owned by userID. Returns the number of rows affected (0 if already revoked, missing, or not owned by userID).

func (*DB) SetDownloadRecordSavePath

func (db *DB) SetDownloadRecordSavePath(
	ctx context.Context,
	id uint32,
	path string,
) error

SetDownloadRecordSavePath persists save_path so import_scan can resume after a restart without re-querying the download client.

func (*DB) SetEpisodeLastSearchAt

func (db *DB) SetEpisodeLastSearchAt(
	ctx context.Context,
	id uint32,
	when time.Time,
) error

func (*DB) SetEpisodeMonitored

func (db *DB) SetEpisodeMonitored(
	ctx context.Context,
	id uint32,
	monitored bool,
) error

func (*DB) SetEpisodeStatus

func (db *DB) SetEpisodeStatus(
	ctx context.Context,
	id uint32,
	status episode.Status,
) error

func (*DB) SetMovieDigitalReleaseDate

func (db *DB) SetMovieDigitalReleaseDate(
	ctx context.Context,
	id uint32,
	date *time.Time,
) error

SetMovieDigitalReleaseDate sets or clears digital_release_date based on whether `date` is non-nil. Used by metadata-refresh after the TMDB release_dates lookup.

func (*DB) SetMovieLastSearchAt

func (db *DB) SetMovieLastSearchAt(
	ctx context.Context,
	id uint32,
	when time.Time,
) error

func (*DB) SetSeasonMonitored

func (db *DB) SetSeasonMonitored(
	ctx context.Context,
	id uint32,
	monitored bool,
) error

func (*DB) SetTVShowRefreshedAt

func (db *DB) SetTVShowRefreshedAt(
	ctx context.Context,
	id uint32,
	when time.Time,
) error

func (*DB) SyncSeasonDownloadStateForRecord

func (db *DB) SyncSeasonDownloadStateForRecord(
	ctx context.Context,
	recordID uint32,
	paused bool,
) error

SyncSeasonDownloadStateForRecord reflects a download's live torrent state onto its episode badges: when paused, the record's-season episodes still in "downloading" flip to "paused"; when active again they flip back. Season-level so a paused season pack pauses all its episodes, not just the linked one. A no-op for movie records (no episode/season behind them).

func (*DB) TouchSession

func (db *DB) TouchSession(ctx context.Context, jti string, when time.Time) error

func (*DB) TruncateSessions

func (db *DB) TruncateSessions(ctx context.Context) error

TruncateSessions deletes every row in the sessions table. Called by auth.RotateJWTSecret to invalidate every outstanding session in one shot (old tokens are also signed with the old secret, so this is belt-and- braces — but it stops dead rows accumulating).

func (*DB) Tx

func (db *DB) Tx(ctx context.Context) (Tx, error)

Tx starts a transaction and returns a Tx-bound Store. Caller owns Commit/Rollback.

func (*DB) UpcomingReleases

func (db *DB) UpcomingReleases(
	ctx context.Context,
	from, to time.Time,
) ([]*ent.Movie, error)

UpcomingReleases returns wanted movies whose digital_release_date falls in [from, to), ordered by release date ascending. Used by the dashboard calendar modal.

func (*DB) UpdateDownloadRecordStatus

func (db *DB) UpdateDownloadRecordStatus(
	ctx context.Context,
	id uint32,
	status downloadrecord.Status,
) error

func (*DB) UpdateImportScanFileDecision

func (db *DB) UpdateImportScanFileDecision(
	ctx context.Context,
	id uint32,
	decision entimportscanfile.Decision,
	tmdbID *uint32,
) error

func (*DB) UpdateImportScanFileOutcome

func (db *DB) UpdateImportScanFileOutcome(
	ctx context.Context,
	id uint32,
	outcome entimportscanfile.Outcome,
	opts UpdateScanFileOutcomeOpts,
) error

func (*DB) UpdateImportScanShowDecision

func (db *DB) UpdateImportScanShowDecision(
	ctx context.Context, id uint32,
	decision entimportscanshow.Decision, tvdbID *uint32,
) error

func (*DB) UpdateImportScanShowOutcome

func (db *DB) UpdateImportScanShowOutcome(
	ctx context.Context, id uint32,
	outcome entimportscanshow.Outcome, opts UpdateScanShowOutcomeOpts,
) error

func (*DB) UpdateImportScanStatus

func (db *DB) UpdateImportScanStatus(
	ctx context.Context,
	id uint32,
	status entimportscan.Status,
	opts UpdateScanStatusOpts,
) error

func (*DB) UpdateMediaFilePath

func (db *DB) UpdateMediaFilePath(
	ctx context.Context,
	id uint32,
	path string,
) error

func (*DB) UpdateMovie

func (db *DB) UpdateMovie(
	ctx context.Context,
	id uint32,
	p UpdateMovieParams,
) (*ent.Movie, error)

func (*DB) UpdateMovieMetadata

func (db *DB) UpdateMovieMetadata(
	ctx context.Context,
	id uint32,
	p UpdateMovieMetadataParams,
) error

UpdateMovieMetadata updates only the TMDB-sourced metadata fields (title, original_title, year, overview, runtime). Status and QualityProfileID are intentionally not touched — those are owned by the lifecycle update path.

func (*DB) UpdateMovieStatus

func (db *DB) UpdateMovieStatus(
	ctx context.Context,
	id uint32,
	status movie.Status,
) error

func (*DB) UpdateTVShow

func (db *DB) UpdateTVShow(
	ctx context.Context,
	id uint32,
	p UpdateTVShowParams,
) (*ent.TVShow, error)

func (*DB) UpdateTVShowMetadata

func (db *DB) UpdateTVShowMetadata(
	ctx context.Context,
	id uint32,
	p UpdateTVShowMetadataParams,
) error

UpdateTVShowMetadata persists refreshed provider metadata onto an existing show (used by RefreshOne so a metadata refresh actually surfaces changes).

func (*DB) UpdateUser

func (db *DB) UpdateUser(
	ctx context.Context,
	id uint32,
	p UpdateUserParams,
) (*ent.User, error)

UpdateUser applies every non-nil field in p in a single SQL UPDATE, so admin PATCH /users/{uid} does not fan out into three queries.

func (*DB) UpdateUserPassword

func (db *DB) UpdateUserPassword(ctx context.Context, id uint32, hash string) error

func (*DB) UserSessionExists

func (db *DB) UserSessionExists(
	ctx context.Context,
	userID, sessionID uint32,
) (bool, error)

UserSessionExists reports whether a session with sessionID exists and is owned by userID, regardless of its revoked state.

type DownloadHistoryResult

type DownloadHistoryResult struct {
	Records    []*ent.DownloadRecord
	NextCursor string
}

type EpisodeSeed

type EpisodeSeed struct {
	Number         uint16
	AbsoluteNumber uint16
	Title          string
	AirDate        *time.Time
}

type FilterImportScanFilesParams

type FilterImportScanFilesParams struct {
	ScanID         uint32
	Classification entimportscanfile.Classification // empty = all
	Query          string
	Offset         uint32
	Limit          uint32
}

type FilterMoviesParams

type FilterMoviesParams struct {
	Status movie.Status
	Query  string
	Sort   string // "title" | "year" | "create_time"
	Order  string // "asc" | "desc"
	Offset uint32
	Limit  uint32
}

FilterMoviesParams: Status/Query empty disables the respective clause; Limit must be > 0.

type ListImportScanShowsParams

type ListImportScanShowsParams struct {
	ScanID         uint32
	Classification entimportscanshow.Classification // empty = all
	Offset, Limit  uint32
}

type ListRequestsParams

type ListRequestsParams struct {
	Status      string // "" = all
	MediaType   string // "" = all
	RequesterID uint32 // 0 = all requesters (admin view)
	Offset      uint32
	Limit       uint32
}

type ListUsersParams

type ListUsersParams struct {
	Q      string
	Role   user.Role
	Limit  uint32
	Offset uint32
	Sort   UserSort
	Order  UserOrder
}

ListUsersParams is the filter/pagination bundle for admin user listing. All fields are optional; Limit defaults to 25 when zero, and is capped at 100 by the caller. Sort/Order default to created/desc.

type MediaFileRow

type MediaFileRow struct {
	Path         string
	Size         int64
	Quality      string
	Format       string
	ReleaseGroup string
}

type RecordEpisodeImportSuccessParams

type RecordEpisodeImportSuccessParams struct {
	RecordID  uint32
	EpisodeID uint32
	File      MediaFileRow
}

type RecordImportFailureParams

type RecordImportFailureParams struct {
	RecordID uint32
	// Exactly one of MovieID / EpisodeID is set, identifying the media this
	// record imports. On terminal failure the movie flips to failed; the
	// episode flips back to wanted so the next search re-grabs it.
	MovieID   uint32
	EpisodeID uint32
	Terminal  bool
	Reason    string
	Attempts  uint8
}

type RecordImportSuccessParams

type RecordImportSuccessParams struct {
	RecordID uint32
	MovieID  uint32
	File     MediaFileRow
}

type SeasonSeed

type SeasonSeed struct {
	Number   uint16
	Name     string
	Episodes []EpisodeSeed
}

type Store

type Store interface {
	// Tx starts a transaction and returns a Tx-bound Store. Caller owns
	// Commit/Rollback.
	Tx(ctx context.Context) (Tx, error)

	// users
	FindUserByEmail(ctx context.Context, email string) (*ent.User, error)
	FindUserByID(ctx context.Context, id uint32) (*ent.User, error)
	CountUsers(ctx context.Context) (int, error)
	CreateUser(ctx context.Context, p CreateUserParams) (*ent.User, error)
	UpdateUserPassword(ctx context.Context, id uint32, hash string) error
	UpdateUser(
		ctx context.Context,
		id uint32,
		p UpdateUserParams,
	) (*ent.User, error)
	ListUsers(
		ctx context.Context,
		p ListUsersParams,
	) ([]*ent.User, int, error)
	CountUsersByRole(ctx context.Context, role user.Role) (int, error)
	DeleteUser(ctx context.Context, id uint32) error

	// sessions
	CreateSession(ctx context.Context, p CreateSessionParams) (*ent.Session, error)
	FindSessionByJTI(ctx context.Context, jti string) (*ent.Session, error)
	TouchSession(ctx context.Context, jti string, when time.Time) error
	RevokeSessionByJTI(ctx context.Context, jti string, when time.Time) error
	RevokeUserSessionByID(
		ctx context.Context,
		userID, sessionID uint32,
		when time.Time,
	) (int, error)
	UserSessionExists(ctx context.Context, userID, sessionID uint32) (bool, error)
	RevokeAllUserSessions(ctx context.Context, userID uint32, when time.Time) error
	RevokeOtherUserSessions(
		ctx context.Context,
		userID uint32,
		keepJTI string,
		when time.Time,
	) error
	ListUserSessions(ctx context.Context, userID uint32) ([]*ent.Session, error)
	PurgeExpiredSessions(ctx context.Context, before time.Time) (int, error)
	TruncateSessions(ctx context.Context) error

	// api keys
	CreateAPIKey(ctx context.Context, p CreateAPIKeyParams) (*ent.ApiKey, error)
	FindAPIKeyByHash(ctx context.Context, hash string) (*ent.ApiKey, error)
	ListAPIKeysByUser(ctx context.Context, userID uint32) ([]*ent.ApiKey, error)
	DeleteAPIKeyByID(ctx context.Context, userID, keyID uint32) (int, error)

	// invites
	CreateInvite(ctx context.Context, p CreateInviteParams) (*ent.Invite, error)
	FindInviteByTokenHash(ctx context.Context, hash string) (*ent.Invite, error)
	FindUnusedInviteForEmail(
		ctx context.Context,
		email string,
		now time.Time,
	) (*ent.Invite, error)
	ListInvites(ctx context.Context) ([]*ent.Invite, error)
	MarkInviteUsed(
		ctx context.Context,
		id uint32,
		when time.Time,
	) (*ent.Invite, error)
	MarkInviteUsedWithUser(
		ctx context.Context,
		id, userID uint32,
		when time.Time,
	) (*ent.Invite, error)
	RevokeInvite(ctx context.Context, id uint32, now time.Time) error

	// oidc identities
	FindOIDCIdentity(
		ctx context.Context,
		provider, subject string,
	) (*ent.OIDCIdentity, error)
	CreateOIDCIdentity(
		ctx context.Context,
		p CreateOIDCIdentityParams,
	) (*ent.OIDCIdentity, error)

	// movies
	CreateMovie(ctx context.Context, p CreateMovieParams) (*ent.Movie, error)
	FindMovieByID(ctx context.Context, id uint32) (*ent.Movie, error)
	FindMovieByTMDBID(ctx context.Context, tmdbID uint32) (*ent.Movie, error)
	FindMoviesByTMDBIDs(ctx context.Context, tmdbIDs []uint32) ([]*ent.Movie, error)
	CountMovies(ctx context.Context) (int, error)
	CountMoviesByStatus(ctx context.Context, status movie.Status) (int, error)
	MovieCreateTimesSince(ctx context.Context, since time.Time) ([]time.Time, error)
	ListMovies(ctx context.Context, offset, limit uint32) ([]*ent.Movie, error)
	FilterMovies(
		ctx context.Context,
		p FilterMoviesParams,
	) ([]*ent.Movie, int, error)
	ListEligibleMoviesForSync(
		ctx context.Context,
		maxGrabFailures uint8,
		notSearchedSince time.Time,
	) ([]*ent.Movie, error)
	ListWantedMovies(ctx context.Context) ([]*ent.Movie, error)
	ListMoviesStaleSince(
		ctx context.Context,
		cutoff time.Time,
	) ([]*ent.Movie, error)
	DeleteMovie(ctx context.Context, id uint32) error
	UpdateMovie(
		ctx context.Context,
		id uint32,
		p UpdateMovieParams,
	) (*ent.Movie, error)
	UpdateMovieMetadata(
		ctx context.Context,
		id uint32,
		p UpdateMovieMetadataParams,
	) error
	UpdateMovieStatus(ctx context.Context, id uint32, status movie.Status) error
	SetMovieLastSearchAt(ctx context.Context, id uint32, when time.Time) error
	SetMovieDigitalReleaseDate(
		ctx context.Context,
		id uint32,
		date *time.Time,
	) error
	IncrementMovieGrabFailures(ctx context.Context, id uint32) error
	ResetMovieGrabFailures(ctx context.Context, id uint32) error

	// movie events
	RecentActivity(
		ctx context.Context,
		f ActivityFilter,
	) (*ActivityResult, error)
	UpcomingReleases(
		ctx context.Context,
		from, to time.Time,
	) ([]*ent.Movie, error)
	ListUpcomingEpisodes(
		ctx context.Context,
		from, to time.Time,
	) ([]*ent.Episode, error)

	// download records — used by the download manager
	CreateDownloadRecord(
		ctx context.Context,
		p CreateDownloadRecordParams,
	) (*ent.DownloadRecord, error)
	// ListMoviesForAdoption returns all movies as adoption-match candidates.
	ListMoviesForAdoption(ctx context.Context) ([]*ent.Movie, error)
	// ListTvShowsForAdoption returns all shows with seasons → episodes → media
	// files eager-loaded, for episode adoption matching.
	ListTvShowsForAdoption(ctx context.Context) ([]*ent.TVShow, error)
	ListDownloadingRecords(ctx context.Context) ([]*ent.DownloadRecord, error)
	ListDownloadingRecordsWithMovie(
		ctx context.Context,
	) ([]*ent.DownloadRecord, error)
	UpdateDownloadRecordStatus(
		ctx context.Context,
		id uint32,
		status downloadrecord.Status,
	) error
	// MarkDownloadRecordReplaceExisting flags a record so the importer
	// overwrites already-present file(s) instead of skipping them.
	MarkDownloadRecordReplaceExisting(ctx context.Context, id uint32) error
	ListImportingDownloadRecords(ctx context.Context) ([]*ent.DownloadRecord, error)
	FindImportingDownloadRecordByID(
		ctx context.Context,
		id uint32,
	) (*ent.DownloadRecord, error)
	RecordImportSuccess(ctx context.Context, p RecordImportSuccessParams) error
	RecordEpisodeImportSuccess(
		ctx context.Context,
		p RecordEpisodeImportSuccessParams,
	) error
	RecordImportFailure(ctx context.Context, p RecordImportFailureParams) error
	SetDownloadRecordSavePath(ctx context.Context, id uint32, path string) error
	DeleteCompletedDownloadRecordsBefore(
		ctx context.Context,
		cutoff time.Time,
	) (int, error)
	DeleteFailedDownloadRecordsBefore(
		ctx context.Context,
		cutoff time.Time,
	) (int, error)
	ListActiveDownloadRecords(ctx context.Context) ([]*ent.DownloadRecord, error)
	FindActiveDownloadRecordByID(
		ctx context.Context,
		id uint32,
	) (*ent.DownloadRecord, error)
	ListDownloadHistory(
		ctx context.Context,
		limit int,
		cursor string,
	) (*DownloadHistoryResult, error)
	DeleteDownloadRecord(ctx context.Context, id uint32) error
	DeleteAllCompletedDownloadRecords(ctx context.Context) (int, error)
	RevertMovieToWantedIfNoFile(ctx context.Context, movieID uint32) error
	RevertOrphanedDownloadingEpisodes(ctx context.Context) (int, error)
	SyncSeasonDownloadStateForRecord(
		ctx context.Context,
		recordID uint32,
		paused bool,
	) error
	// AllDownloadRecordHashes returns the set of non-empty torrent hashes
	// across every record. The adoption pass uses it to skip already-tracked
	// torrents.
	AllDownloadRecordHashes(ctx context.Context) (map[string]struct{}, error)
	ListPendingDownloadRecords(ctx context.Context) ([]*ent.DownloadRecord, error)
	// DeleteStalePendingAdoptions prunes pending proposals for a client whose
	// torrent_hash is absent from its current managed torrents (liveHashes).
	DeleteStalePendingAdoptions(
		ctx context.Context,
		clientName string,
		liveHashes []string,
	) (int, error)
	FindPendingDownloadRecordByID(
		ctx context.Context,
		id uint32,
	) (*ent.DownloadRecord, error)
	// LatestImportedRecordForMovie returns the newest hash-carrying record for
	// a movie (file-delete uses it to remove the source torrent). NotFound when
	// none. LatestImportedRecordForEpisode is the episode twin.
	LatestImportedRecordForMovie(
		ctx context.Context,
		movieID uint32,
	) (*ent.DownloadRecord, error)
	LatestImportedRecordForEpisode(
		ctx context.Context,
		episodeID uint32,
	) (*ent.DownloadRecord, error)

	// media files — used by the library importer
	CreateMediaFile(
		ctx context.Context,
		p CreateMediaFileParams,
	) (*ent.MediaFile, error)
	// FindMediaFileByID returns one MediaFile by ID, or ent NotFound.
	FindMediaFileByID(ctx context.Context, id uint32) (*ent.MediaFile, error)
	// FindMediaFileByEpisodeID returns the MediaFile owned by an episode (at
	// most one), or ent NotFound when it has none.
	FindMediaFileByEpisodeID(
		ctx context.Context,
		episodeID uint32,
	) (*ent.MediaFile, error)
	// MovieHasMediaFile reports whether the movie identified by tmdbID has at
	// least one MediaFile row. Returns (false, nil) when the movie row itself
	// is absent.
	MovieHasMediaFile(ctx context.Context, tmdbID uint32) (bool, error)
	// ListAllMediaFilesWithMovie returns every MediaFile row joined to its
	// owning movie. Used by drift_check; loads edges eagerly via WithMovie().
	ListAllMediaFilesWithMovie(ctx context.Context) ([]*ent.MediaFile, error)
	// ListMediaFilesByMovieID returns every MediaFile attached to the given
	// movie. Empty slice (no error) when the movie has no files.
	ListMediaFilesByMovieID(
		ctx context.Context,
		movieID uint32,
	) ([]*ent.MediaFile, error)
	// BumpMediaFileLastSeen sets last_seen_at = now for the given row.
	BumpMediaFileLastSeen(ctx context.Context, id uint32) error
	// UpdateMediaFilePath rewrites a MediaFile's path (used by rename).
	UpdateMediaFilePath(ctx context.Context, id uint32, path string) error
	// DeleteMediaFileAndRevertMovie removes the MediaFile row and sets the
	// owning movie's status back to "wanted" in a single transaction.
	DeleteMediaFileAndRevertMovie(
		ctx context.Context,
		mediaFileID, movieID uint32,
	) error
	// DeleteMediaFileAndRevertEpisode is the episode twin of
	// DeleteMediaFileAndRevertMovie.
	DeleteMediaFileAndRevertEpisode(
		ctx context.Context,
		mediaFileID, episodeID uint32,
	) error

	// bulk-import scans
	CreateImportScan(
		ctx context.Context,
		p CreateImportScanParams,
	) (*ent.ImportScan, error)
	FindImportScan(ctx context.Context, id uint32) (*ent.ImportScan, error)
	// FindOpenImportScanForSource returns the oldest awaiting_review scan for
	// sourcePath, or ent NotFound.
	FindOpenImportScanForSource(
		ctx context.Context,
		sourcePath string,
	) (*ent.ImportScan, error)
	ListImportScans(
		ctx context.Context,
		offset, limit uint32,
	) ([]*ent.ImportScan, uint32, error)
	UpdateImportScanStatus(
		ctx context.Context,
		id uint32,
		status importscan.Status,
		opts UpdateScanStatusOpts,
	) error
	IncrementImportScanProgress(
		ctx context.Context,
		id uint32,
		processedDelta uint32,
	) error
	CountActiveImportScans(ctx context.Context) (uint32, error)
	DeleteImportScan(ctx context.Context, id uint32) error
	AbortInflightImportScans(ctx context.Context, reason string) (uint32, error)

	// bulk-import scan files
	BulkCreateImportScanFiles(
		ctx context.Context,
		scanID uint32,
		files []CreateImportScanFileParams,
	) error
	FilterImportScanFiles(
		ctx context.Context,
		p FilterImportScanFilesParams,
	) ([]*ent.ImportScanFile, uint32, error)
	// FindImportScanFile returns the file with fileID scoped to scanID, or an
	// ent NotFound error when no such row exists under that scan.
	FindImportScanFile(
		ctx context.Context,
		scanID, fileID uint32,
	) (*ent.ImportScanFile, error)
	UpdateImportScanFileDecision(
		ctx context.Context,
		id uint32,
		decision importscanfile.Decision,
		tmdbID *uint32,
	) error
	UpdateImportScanFileOutcome(
		ctx context.Context,
		id uint32,
		outcome importscanfile.Outcome,
		opts UpdateScanFileOutcomeOpts,
	) error
	ListImportScanFilesForCommit(
		ctx context.Context,
		scanID uint32,
	) ([]*ent.ImportScanFile, error)
	// ListPendingImportScanFilePaths returns the source_path of every
	// ImportScanFile attached to a scan whose status is still
	// "awaiting_review". Used by the orphan_scan dedup gate.
	ListPendingImportScanFilePaths(ctx context.Context) ([]string, error)

	// series import scans (import_scan_show children)
	ListPendingImportScanShowFolders(ctx context.Context) ([]string, error)
	BulkCreateImportScanShows(
		ctx context.Context,
		scanID uint32,
		shows []CreateImportScanShowParams,
	) error
	ListImportScanShows(
		ctx context.Context,
		p ListImportScanShowsParams,
	) ([]*ent.ImportScanShow, uint32, error)
	FindImportScanShow(
		ctx context.Context,
		scanID, showID uint32,
	) (*ent.ImportScanShow, error)
	UpdateImportScanShowDecision(
		ctx context.Context,
		id uint32,
		decision importscanshow.Decision,
		tvdbID *uint32,
	) error
	ListImportScanShowsForCommit(
		ctx context.Context,
		scanID uint32,
	) ([]*ent.ImportScanShow, error)
	UpdateImportScanShowOutcome(
		ctx context.Context,
		id uint32,
		outcome importscanshow.Outcome,
		opts UpdateScanShowOutcomeOpts,
	) error
	ListAllEpisodeMediaFilePaths(ctx context.Context) ([]string, error)

	// tv shows / seasons / episodes
	CreateTVShow(ctx context.Context, p CreateTVShowParams) (*ent.TVShow, error)
	FindTVShowByID(ctx context.Context, id uint32) (*ent.TVShow, error)
	FindTVShowByTVDBID(ctx context.Context, tvdbID uint32) (*ent.TVShow, error)
	ListTVShows(ctx context.Context, offset, limit uint32) ([]*ent.TVShow, error)
	CountTVShows(ctx context.Context) (int, error)
	CountTVShowsByStatus(
		ctx context.Context,
		status tvshow.SeriesStatus,
	) (int, error)
	UpdateTVShow(
		ctx context.Context,
		id uint32,
		p UpdateTVShowParams,
	) (*ent.TVShow, error)
	UpdateTVShowMetadata(
		ctx context.Context,
		id uint32,
		p UpdateTVShowMetadataParams,
	) error
	SetTVShowRefreshedAt(ctx context.Context, id uint32, when time.Time) error
	ReconcileEpisodes(
		ctx context.Context,
		showID uint32,
		seasons []SeasonSeed,
	) ([]string, error)
	DeleteTVShow(ctx context.Context, id uint32) error
	SetSeasonMonitored(ctx context.Context, id uint32, monitored bool) error
	SetEpisodeMonitored(ctx context.Context, id uint32, monitored bool) error
	CascadeShowMonitored(ctx context.Context, showID uint32, monitored bool) error
	CascadeSeasonMonitored(
		ctx context.Context,
		seasonID uint32,
		monitored bool,
	) error
	SetEpisodeStatus(ctx context.Context, id uint32, status episode.Status) error
	SetEpisodeLastSearchAt(ctx context.Context, id uint32, when time.Time) error
	IncrementEpisodeGrabFailures(ctx context.Context, id uint32) error
	ResetEpisodeGrabFailures(ctx context.Context, id uint32) error
	ListWantedEpisodes(ctx context.Context) ([]*ent.TVShow, error)

	// requests
	CreateRequest(ctx context.Context, p CreateRequestParams) (*ent.Request, error)
	FindActiveRequest(
		ctx context.Context,
		mediaType string,
		mediaID uint32,
	) (*ent.Request, error)
	ListRequests(
		ctx context.Context,
		p ListRequestsParams,
	) ([]*ent.Request, int, error)
	GetRequest(ctx context.Context, id uint32) (*ent.Request, error)
	ApproveRequest(ctx context.Context, id, adminID uint32) error
	DenyRequest(ctx context.Context, id, adminID uint32, reason string) error
	ReopenRequest(ctx context.Context, id uint32) error
	MarkRequestsAvailable(
		ctx context.Context,
		mediaType string,
		mediaID uint32,
	) error
	CountRequestsByStatus(
		ctx context.Context,
		status request.Status,
	) (int, error)
}

Store is the full database surface. Implementations: *DB (prod) and generated mocks (tests).

type Tx

type Tx interface {
	Store
	Commit() error
	Rollback() error
}

Tx is a transaction-bound Store. Caller invokes regular Store methods, then Commit or Rollback. Either method is terminal — calling both, or calling the same method twice, is a programmer error.

type UpdateMovieMetadataParams

type UpdateMovieMetadataParams struct {
	Title         string
	OriginalTitle string
	Overview      string
	Year          uint16
	Runtime       uint16
}

type UpdateMovieParams

type UpdateMovieParams struct {
	Status         *movie.Status
	QualityProfile *string
	Monitored      *bool
}

type UpdateScanFileOutcomeOpts

type UpdateScanFileOutcomeOpts struct {
	Message        string
	CreatedMovieID uint32
}

type UpdateScanShowOutcomeOpts

type UpdateScanShowOutcomeOpts struct {
	Message         string
	CreatedTvshowID uint32
}

type UpdateScanStatusOpts

type UpdateScanStatusOpts struct {
	TotalCount         *uint32
	FailureReason      *string
	ScannedAt          *time.Time
	CommittedAt        *time.Time
	CommitSuccessCount *uint32
	CommitFailedCount  *uint32
}

type UpdateTVShowMetadataParams

type UpdateTVShowMetadataParams struct {
	Title         string
	OriginalTitle string
	Year          uint16
	Overview      string
	Network       string
	Creator       string
	SeriesStatus  string
	Type          string
	Runtime       uint16
	Rating        float64
	Genres        []string
}

UpdateTVShowMetadataParams carries the provider-sourced fields refreshed from TVDB. User-owned fields (monitored, quality_profile) are left untouched.

type UpdateTVShowParams

type UpdateTVShowParams struct {
	Monitored      *bool
	QualityProfile *string
}

type UpdateUserParams

type UpdateUserParams struct {
	Role                   *user.Role
	AuthMethod             *user.AuthMethod
	DisplayName            *string
	Email                  *string
	FailedLoginCount       *uint8
	LastFailedLoginAt      *time.Time
	ClearLastFailedLoginAt bool
	LockedUntil            *time.Time
	ClearLockedUntil       bool
}

UpdateUserParams bundles the fields that admin PATCH /users/{uid} may mutate in a single SQL UPDATE. Nil fields are left untouched. The Clear* flags exist because the underlying ent setters distinguish "set to value" from "clear to nil"; setting both ClearLockedUntil and a non-nil LockedUntil is a programmer error and the non-nil pointer wins.

type UserOrder

type UserOrder uint8

UserOrder is the direction applied to UserSort.

const (
	UserOrderDesc UserOrder = iota
	UserOrderAsc
)

type UserSort

type UserSort uint8

UserSort selects the column ordering applied to admin user listings.

const (
	UserSortCreated UserSort = iota
	UserSortName
	UserSortRole
	UserSortAuth
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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