library

package
v0.97.2 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package library persists per-user metadata about every torrent the user has streamed. Stores the magnet URI so we can re-play from disk-cached pieces after restart, and tracks resume position so the player can seek to where the user left off.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DismissKey

func DismissKey(kind string, tmdbID int) string

DismissKey builds the canonical set key for a dismissed recommendation so the store and the generator agree on the identity (kind + tmdbID).

Types

type Entry

type Entry struct {
	ID               int    `json:"id"`
	UserID           int    `json:"userId"`
	InfoHash         string `json:"infoHash"`
	Magnet           string `json:"magnet"`
	Name             string `json:"name"`
	PrimaryFileIndex int    `json:"primaryFileIndex"`
	// LastFileIndex is the file the user actually last watched (for multi-file
	// torrents / season packs). -1 = never tracked → fall back to primary.
	LastFileIndex   int       `json:"lastFileIndex"`
	TotalSize       int64     `json:"totalSize"`
	ResumeSeconds   float64   `json:"resumeSeconds"`
	DurationSeconds float64   `json:"durationSeconds"`
	Kind            string    `json:"kind"` // "video" | "audio" | ""
	LastPlayedAt    time.Time `json:"lastPlayedAt"`
	AddedAt         time.Time `json:"addedAt"`
}

Entry is one row in the library — a torrent the user has touched.

type PrimaryFileLookup

type PrimaryFileLookup func(infoHash string) (primaryFile int, ok bool)

PrimaryFileLookup is the contract a one-shot migration needs from any metadata source: given an info_hash, return the primary file index (or false when no snapshot exists).

type Store

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

func New

func New(pool *sql.DB) (*Store, error)

New wires the library store onto the shared Postgres pool. Schema is applied centrally (internal/db migrations).

func (*Store) Close

func (s *Store) Close()

Close is a no-op: the shared pool's lifecycle is owned by main.

func (*Store) Delete

func (s *Store) Delete(id, userID int, includeAll bool) error

Delete removes an entry. Returns an error if the entry isn't owned by user (unless admin).

func (*Store) DeleteAll

func (s *Store) DeleteAll(userID int, includeAll bool) (int64, error)

DeleteAll removes every library entry owned by the user. Admins can pass includeAll=true to wipe the entire history across users (rarely useful — meant mostly for support / DB resets).

func (*Store) DeleteAllIncognito

func (s *Store) DeleteAllIncognito() error

DeleteAllIncognito removes every incognito-flagged entry across all users. Called once at startup: after a restart the in-memory heartbeat map (which the reaper relies on) is empty, so any incognito row left in the DB is orphaned and would persist forever — defeating the purpose of incognito.

func (*Store) DeleteIncognito

func (s *Store) DeleteIncognito(userID int) error

DeleteIncognito removes all incognito-flagged entries for a user. Called when the user ends their incognito session (logout or toggle-off).

func (*Store) DismissRecommendation

func (s *Store) DismissRecommendation(userID int, kind string, tmdbID int) error

DismissRecommendation records that the user wants to never see this recommended title again. Idempotent: re-dismissing the same (kind, tmdb_id) is a no-op (PRIMARY KEY conflict is ignored). Scoped per user. kind is "movie" | "tv".

func (*Store) DismissedRecommendations

func (s *Store) DismissedRecommendations(userID int) (map[string]bool, error)

DismissedRecommendations returns the user's dismissed recommendations as a set keyed by "kind:tmdbID" (e.g. "movie:603"). Used by the generator to exclude them from both the seed and the result list. Empty map when none / nil store.

func (*Store) GetByHash

func (s *Store) GetByHash(userID int, infoHash string) (*Entry, error)

GetByHash returns the user's library entry for a given info_hash (if any). Incognito entries are returned regardless — callers need the entry ID for resume updates.

func (*Store) GetByHashPublic

func (s *Store) GetByHashPublic(userID int, infoHash string) (*Entry, error)

GetByHashPublic is like GetByHash but filters out incognito entries. Used by endpoints that expose data to the user's UI.

func (*Store) GetByID

func (s *Store) GetByID(id int, userID int, includeAll bool) (*Entry, error)

GetByID returns one entry, optionally bypassing user check (admin).

func (*Store) List

func (s *Store) List(userID int, includeAll bool, limit int) ([]Entry, error)

List returns the user's library, ordered by most recently played first. includeAll lets admin see everyone.

func (*Store) RefreshStalePrimary

func (s *Store) RefreshStalePrimary(lookup PrimaryFileLookup) (int, error)

RefreshStalePrimary scans library rows whose primary_file_index is still 0 (the column default — almost always means "never had a real pickPrimaryFile computed against the latest code") and refreshes them from the supplied lookup. Returns the number of rows updated.

Why this exists: before pickPrimaryFile learned to skip featurettes/extras, every series-pack entry got primary_file_index=0 — a file that's commonly "Featurettes/.../Behind The Scenes.mkv" sorted to the start of the torrent. That stale 0 then leaked through LibraryPage into the player, which happily streamed the wrong file. A one-shot startup migration cures all pre-fix rows without forcing the user to re-add anything.

Only positive lookups (primaryFile > 0) update anything — keeps audio-only torrents where pickPrimaryFile returns -1 untouched, since 0 may be the correct "first track" choice for those.

func (*Store) UpdateResume

func (s *Store) UpdateResume(id, userID int, resumeSeconds, durationSeconds float64, fileIndex int, includeAll bool) error

UpdateResume saves the current playback position. Called periodically by the player. Optionally updates duration if not yet known.

func (*Store) Upsert

func (s *Store) Upsert(in UpsertInput) (*Entry, error)

Upsert inserts a fresh row OR updates an existing (user, info_hash) tuple, refreshing last_played_at to now. Used on every /stream/add call. When incognito=true the row is written with incognito=1 so it is excluded from normal queries and deleted when the user ends their incognito session.

type UpsertInput

type UpsertInput struct {
	UserID      int
	InfoHash    string
	Magnet      string
	Name        string
	PrimaryFile int
	TotalSize   int64
	Kind        string
	Incognito   bool
}

UpsertInput groups the parameters for Upsert.

Jump to

Keyboard shortcuts

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