Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyMediaUserFlags(ctx context.Context, db *Database, systemID, path string, mediaDBID int64, ...) (map[MediaUserFlag]bool, error)
- func ApplyMediaUserLauncherOverride(ctx context.Context, db *Database, systemID, path string, mediaDBID int64, ...) error
- func BuildTitleZapScript(systemID, name string, tags []TagInfo) string
- func CanonicalScrapePath(value string, subtree bool) (string, error)
- func CheckSchemaVersion(db *sql.DB, migrationFiles embed.FS, migrationDir string) error
- func ClearCorruptMarker(dbPath string) error
- func ClearCorruptMarkerFS(fs afero.Fs, dbPath string) error
- func ComputeMediaIdentityFingerprint(identity *MediaIdentity) (string, error)
- func CorruptBackupPath(dbPath string) string
- func CorruptMarkerPath(dbPath string) string
- func DecodeTagStrings(raw string) []string
- func EncodeDeckCardScripts(scripts []DeckCardScript) string
- func EncodeMediaIdentity(identity *MediaIdentity) string
- func EncodeTagStrings(tags []string) string
- func GroupTagFiltersByOperator(filters []zapscript.TagFilter) (and, not, or []zapscript.TagFilter)
- func IntegrityReport(ctx context.Context, sqlDB *sql.DB, maxRows int) []string
- func IsCorruptionError(err error) bool
- func IsMarkedCorrupt(dbPath string) bool
- func IsMintedDeckID(deckID string) bool
- func IsOptimizationCanceled(err error) bool
- func LogEffectivePragmas(event *zerolog.Event, p EffectivePragmas) *zerolog.Event
- func LogEffectivePragmasForDB(ctx context.Context, sqlDB *sql.DB, dbLabel string, ...)
- func MarkCorrupt(dbPath, reason string, now time.Time)
- func MediaIdentityFingerprintPayload(identity *MediaIdentity) ([]byte, error)
- func MediaKey(systemID, path string) string
- func MigrateDownTo(db *sql.DB, migrationFiles embed.FS, migrationDir string, version int64) error
- func MigrateUp(db *sql.DB, migrationFiles embed.FS, migrationDir, dbPath, sidecarPath string) error
- func NewDeckID() (string, error)
- func NormalizeDeckID(raw string) (string, error)
- func NoteCorruption(dbPath string, err error, now time.Time) bool
- func PreserveCorruptFile(path, dbLabel string)
- func ReconcileMediaUserData(ctx context.Context, db *Database) error
- func RemoveSidecars(dbPath string) error
- func RemoveSidecarsFS(fs afero.Fs, dbPath string) error
- func SetMigrationReporter(fn MigrationReporter) (restore func())
- func TagKey(tagType, tagValue string) string
- func TagTypeDisplayRank(tagType string) int
- func TitleKey(systemID, slug string) string
- func ValidTitleCandidateName(name string) bool
- func WaitForLongMediaWrites(ctx context.Context, mediaDB MediaDBI, poll time.Duration) bool
- type BackupInfo
- type BrowseCursor
- type BrowseDirCountOptions
- type BrowseDirectoriesOptions
- type BrowseDirectoryResult
- type BrowseFileCountOptions
- type BrowseFilesOptions
- type BrowseIndexBucket
- type BrowseIndexOptions
- type BrowseIndexResult
- type BrowseOverlay
- type BrowseRouteCount
- type BrowseRouteCountsOptions
- type BrowseSource
- type BrowseSystemRootCandidates
- type BrowseSystemRootCandidatesOptions
- type BrowseVirtualScheme
- type BrowseVirtualSchemesOptions
- type Client
- type Conn
- type Database
- type Deck
- type DeckCardScript
- type DeckItem
- type DeckItemAnchor
- type DeckSyncRow
- type DeckTagQueue
- type DirectoryProperty
- type EffectivePragmas
- type FetchedDeckResult
- type FileInfo
- type GenericDBI
- type HistoryEntry
- type InboxMessage
- type JournalMode
- type LibraryInventoryState
- type LibraryMediaRow
- type LibraryOrdinal
- type LibraryStateSyncRow
- type Mapping
- type Media
- type MediaBlob
- type MediaDBI
- type MediaDBWriteCoordinator
- type MediaFullRow
- type MediaHistoryEntry
- type MediaHistorySyncRef
- type MediaHistoryTopEntry
- type MediaIdentity
- type MediaIdentityTag
- type MediaIdentityTagRole
- type MediaPathID
- type MediaProperty
- type MediaQuery
- type MediaRef
- type MediaSource
- type MediaTag
- type MediaTagBatchUpdater
- type MediaTagLink
- type MediaTagRef
- type MediaTagUpdate
- type MediaTitle
- type MediaUserData
- type MediaUserDataReconciler
- type MediaUserFlag
- type MediaWithFullPath
- type MediaWriteArbiter
- type MediaWriteConflictError
- type MediaWriteLease
- type MediaWriteOperation
- type MigrationReporter
- type Profile
- type RemoteCommand
- type RestoreInfo
- type ScanReconcileOpts
- type ScanReconcileStats
- type ScanStagedMedia
- type ScanStagedProperty
- type ScanStagedSource
- type ScanStagedTag
- type ScrapeJob
- type ScrapeResultBatchApplier
- type ScrapeScope
- type ScrapeWrite
- type ScrapeWriteTarget
- type ScrapingOperation
- type SearchCursor
- type SearchFilters
- type SearchResult
- type SearchResultWithCursor
- type SingletonAliasCandidate
- type SingletonContainerAlias
- type SlugResolution
- type System
- type SystemMediaCount
- type Tag
- type TagInfo
- type TagType
- type TitleCandidate
- type TitleWithSystem
- type TransactionOptions
- type UserDBI
- type WALCheckpointMode
Constants ¶
const ( DeckItemKindCard = "card" DeckItemKindScript = "script" // DeckIDLength is the length of a deck ID this device mints, in // characters of DeckIDAlphabet. DeckIDLegacyLength is the length of IDs // allocated before minting moved to devices, which are still valid. DeckIDLength = 12 DeckIDLegacyLength = 8 // DeckIDAlphabet is Crockford base32: digits and upper-case letters // without I, L, O and U. Core stores and shows IDs lower-case; matching // is case-insensitive everywhere. DeckIDAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" // DeckIDLegacyAlphabet is the alphabet of legacy IDs, which encoded a row // number over every digit and letter. I, L, O and U are distinct // characters in them, not misreadings of 1 and 0. DeckIDLegacyAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" // DeckMaxItems bounds one deck. How many decks a device holds is not // bounded: a deck costs well under ten kilobytes, and the background work // a deck creates is per item, so this is the limit that matters. DeckMaxItems = 120 DeckNameMaxLen = 100 DeckDescriptionMaxLen = 1000 DeckZapScriptMaxLen = 5000 )
const ( LibraryIntentNone = "none" LibraryIntentPlayLater = "play_later" LibraryReactionNone = "none" LibraryReactionLiked = "liked" LibraryReactionDisliked = "disliked" )
Personal state values of the Library sync contract.
const ( SynchronousOff = 0 SynchronousNormal = 1 SynchronousFull = 2 )
SQLite's PRAGMA synchronous integer values, for comparing against EffectivePragmas.Synchronous without a magic number at each call site.
const ( TitleCandidateLimit = 5 TitleCandidateNameLimit = 256 )
const CorruptMarkerSuffix = ".corrupt"
CorruptMarkerSuffix names the sidecar file written next to a database to flag detected corruption. It is the DB-independent signal the recovery paths key on: unlike a status row it does not require writing to the (possibly unwritable) database itself.
const ( // CurrentMediaIdentityPolicyVersion identifies the immutable role and // fingerprint policy emitted by this Core version. CurrentMediaIdentityPolicyVersion = 1 )
const DefaultIntegrityReportRows = 20
DefaultIntegrityReportRows caps PRAGMA integrity_check output so a badly corrupt database cannot flood the log; the first rows identify the damaged pages.
const DeviceStateKeyActiveProfile = "active_profile_id"
DeviceStateKeyActiveProfile is the DeviceState key holding the ProfileID of the device's active profile.
const DeviceStateKeyDeckTagsQueue = "deck_tags_queue"
DeviceStateKeyDeckTagsQueue is the DeviceState key holding the decks whose membership tags still have to be brought up to date, as JSON, so the work survives a restart.
const DeviceStateKeyMediaHistoryIdentitySweep = "media_history_identity_sweep"
DeviceStateKeyMediaHistoryIdentitySweep is the DeviceState key recording the last completed media history identity backfill sweep, as "<policy version>:<media LastGeneratedAt unix>". A matching value means no history row below the current policy version can newly resolve, so sweeps skip the table walk entirely.
const DeviceStateKeyMediaPreferencesRevision = "media_preferences_revision"
DeviceStateKeyMediaPreferencesRevision invalidates browse cursor totals when durable media preferences change. The counter survives unhide and rebuild.
const DeviceStateKeyMediaUserDataReconcile = "media_user_data_reconcile"
DeviceStateKeyMediaUserDataReconcile is the DeviceState key marking that the MediaDB projection of media user data still has to be rebuilt from UserDB. It is kept in UserDB so a restore's restart carries it.
const DeviceStateKeyPlaytimeExtensions = "playtime_extensions"
DeviceStateKeyPlaytimeExtensions is the DeviceState key holding granted playtime extensions: the current session's duration grant and any unexpired per-profile day waivers, as versioned JSON. It stores resolved profile IDs only, never the switch IDs used to authorize a grant.
const MaxMediaPropertyBinaryBytes = 16 * 1024 * 1024
MaxMediaPropertyBinaryBytes caps decoded binary property payloads hydrated into memory. Larger blobs remain addressable via BlobDBID/BlobSize but Binary is left nil so API handlers can return a controlled error instead of OOMing.
const UnsetPageSize int64 = 0
UnsetPageSize is the wantPageSize value meaning "this database does not configure page_size, so do not treat whatever it has as a mismatch".
Variables ¶
var ( ErrDeckNotFound = errors.New("deck not found") ErrDeckLimit = errors.New("deck limit reached") ErrDeckItemLimit = errors.New("deck item limit reached") // ErrDeckItemNotFound and ErrDeckItemRepeated reject an edit that keeps // an existing item the deck does not hold, or keeps one twice. ErrDeckItemNotFound = errors.New("deck item not found") ErrDeckItemRepeated = errors.New("deck item listed more than once") ErrDeckOwned = errors.New("deck is owned by this device") ErrDeckReadOnly = errors.New("deck is read-only") ErrInvalidDeckID = errors.New("invalid deck id") )
var ( ErrMediaWriteConflict = errors.New("media database write operation conflict") ErrMediaWriteLease = errors.New("invalid media database write lease") )
var ErrMediaBlobTooLarge = errors.New("media blob too large")
ErrMediaBlobTooLarge indicates a blob exists but exceeds a caller-provided read cap.
var ErrMediaUserFlagConflict = errors.New("media user flags conflict")
ErrMediaUserFlagConflict reports a user-data row that holds two flags the model forbids together: liked with disliked, or favorite with disliked.
var ErrSchemaAhead = errors.New("database schema is newer than this binary supports")
ErrSchemaAhead is returned when the database schema version is newer than the binary supports. This happens when switching from a newer binary (e.g. beta) to an older one (e.g. stable).
var MediaUserFlags = []MediaUserFlag{ MediaUserFlagFavorite, MediaUserFlagHidden, MediaUserFlagLiked, MediaUserFlagDisliked, MediaUserFlagPlayLater, }
MediaUserFlags lists every flag in a stable order.
var TagTypeDisplayPriority = []string{
"unfinished", "unlicensed", "patch", "region", "video", "disc", "disctotal", "edition",
"rev", "arcadeboard", "cabinet", "protection", "set", "input", "dump", "alt", "compatibility", "builddate",
"lang", "distribution", "media", "addon", "release", "year",
"players", "developer", "publisher", "copyright", "credit",
"track",
}
TagTypeDisplayPriority orders the eligible disambiguation tag types from most to least important for display. Clients render the emitted disambiguating tags left-to-right and truncate when space runs out, so the most decisive distinctions come first: variant flags (beta/proto/hack) before region, then the specific-variant markers, then extra context. A tag type only appears on an entry when it actually differs across the title's siblings, so a sole differentiator always survives truncation regardless of its rank. Rank is the slice index.
var ZapScriptTagTypes = TagTypeDisplayPriority
ZapScriptTagTypes is the allowlist of tag types eligible for sibling disambiguation: only these types are considered when deciding whether a title's media differ. It is the same set as TagTypeDisplayPriority (order is irrelevant here, used only for SQL membership), so it aliases the priority list to keep the two in sync. "unknown" is deliberately absent — unclassified tokens never disambiguate.
Functions ¶
func ApplyMediaUserFlags ¶ added in v2.18.0
func ApplyMediaUserFlags( ctx context.Context, db *Database, systemID, path string, mediaDBID int64, changes map[MediaUserFlag]bool, ) (map[MediaUserFlag]bool, error)
ApplyMediaUserFlags records flag changes for one media path in UserDB, the source of truth, then brings the file's user tags in MediaDB in line with the row UserDB holds afterwards. Only the requested flags are written; the UserDB write itself clears a flag the model forbids beside a set one.
The projection compares every flag with the file's current tags and writes only the differences, so a retry after a failed projection still removes a tag the failed attempt left behind. A mediaDBID of 0 skips the projection. It returns the flags whose MediaDB tag changed, with their new value.
func ApplyMediaUserLauncherOverride ¶ added in v2.18.0
func ApplyMediaUserLauncherOverride( ctx context.Context, db *Database, systemID, path string, mediaDBID int64, launcherID string, ) error
ApplyMediaUserLauncherOverride records a launcher override for one media path in UserDB, the source of truth, then writes it to the file's MediaDB property. An empty launcherID clears both. A mediaDBID of 0 skips the projection. It shares a lock with ApplyMediaUserFlags and ReconcileMediaUserData, so a reconcile cannot overwrite an edit with the value it read before the edit.
func BuildTitleZapScript ¶ added in v2.10.0
BuildTitleZapScript builds a ZapScript title command string from a system ID, media name, and disambiguating tags. Format: @SystemID/Name (year:YYYY) (type:value) Multiple values of the same type are grouped into one parens as a comma-separated shorthand: (region:eu, region:us). Types are emitted in the order they first appear in the input (callers pass tags pre-sorted by display priority). Only non-empty tags are included; year values must be exactly 4 digits.
func CanonicalScrapePath ¶ added in v2.18.0
CanonicalScrapePath accepts indexed URI identities for files, and absolute native filesystem paths otherwise. It never accesses or resolves symlinks.
func CheckSchemaVersion ¶ added in v2.11.0
CheckSchemaVersion compares the database's migration version against the latest migration embedded in the current binary. Returns ErrSchemaAhead if the database is ahead, preventing the older binary from running against an incompatible schema.
func ClearCorruptMarker ¶ added in v2.15.0
ClearCorruptMarker removes the corrupt marker sidecar for dbPath. No-op when absent.
func ClearCorruptMarkerFS ¶ added in v2.17.0
ClearCorruptMarkerFS removes the corrupt marker through fs.
func ComputeMediaIdentityFingerprint ¶ added in v2.16.1
func ComputeMediaIdentityFingerprint(identity *MediaIdentity) (string, error)
ComputeMediaIdentityFingerprint returns a deterministic SHA-256 fingerprint for a normalized scanner observation.
func CorruptBackupPath ¶ added in v2.17.0
CorruptBackupPath returns where a database file is renamed aside when it is replaced but worth keeping. Both databases do this, so the mapping lives here rather than being spelled out at each site.
func CorruptMarkerPath ¶ added in v2.15.0
CorruptMarkerPath returns the sidecar marker path for a database file.
func DecodeTagStrings ¶ added in v2.16.0
DecodeTagStrings parses a legacy userdb tags column value. Empty or malformed input decodes to nil because snapshots are best-effort data.
func EncodeDeckCardScripts ¶ added in v2.18.0
func EncodeDeckCardScripts(scripts []DeckCardScript) string
EncodeDeckCardScripts serializes a card item's scripts for a UserDB TEXT column. Nil or empty input encodes to the empty string.
func EncodeMediaIdentity ¶ added in v2.16.1
func EncodeMediaIdentity(identity *MediaIdentity) string
EncodeMediaIdentity serializes a complete identity snapshot for UserDB.
func EncodeTagStrings ¶ added in v2.16.0
EncodeTagStrings serializes legacy flat tags for a userdb TEXT column. Nil or empty input encodes to the empty string.
func GroupTagFiltersByOperator ¶ added in v2.7.0
GroupTagFiltersByOperator groups tag filters by operator type for consistent processing. Returns (andFilters, notFilters, orFilters) to enable both SQL generation and in-memory filtering to use the same grouping logic.
func IntegrityReport ¶ added in v2.15.0
IntegrityReport runs PRAGMA integrity_check(maxRows) against sqlDB and returns the result rows, capped so a badly corrupt database cannot flood the log. A healthy database returns a single "ok" row. Callers hold their own connection lock and nil-check; sqlDB must be non-nil.
func IsCorruptionError ¶ added in v2.15.0
IsCorruptionError reports whether err indicates SQLite database corruption (a malformed disk image or a non-database file). It matches both the typed sqlite3 error codes and the message text, so corruption surfaced through a wrapped string error is still detected.
func IsMarkedCorrupt ¶ added in v2.15.0
IsMarkedCorrupt reports whether the corrupt marker sidecar exists for dbPath.
func IsMintedDeckID ¶ added in v2.18.0
IsMintedDeckID reports whether an ID is one a device minted, rather than a legacy ID issued before minting moved to devices. Only a minted ID can be used to create a deck on an account, so a legacy one is never offered as a create. The ID must already be normalized.
func IsOptimizationCanceled ¶ added in v2.18.0
IsOptimizationCanceled reports cancellation without a concurrent operation or cleanup failure. Joined failures must remain reportable and must not be mistaken for an ordinary interrupted optimization.
func LogEffectivePragmas ¶ added in v2.17.0
func LogEffectivePragmas(event *zerolog.Event, p EffectivePragmas) *zerolog.Event
LogEffectivePragmas attaches p's fields to event for a single reportable log line proving what SQLite actually applied, as opposed to what the DSN asked for.
func LogEffectivePragmasForDB ¶ added in v2.17.0
func LogEffectivePragmasForDB( ctx context.Context, sqlDB *sql.DB, dbLabel string, wantSynchronous, wantPageSize int64, )
LogEffectivePragmasForDB pins a connection from sqlDB, reads back the pragmas SQLite actually applied, and logs one line labelled dbLabel. It warns instead of the usual info when journal_mode is not "wal" or the effective synchronous/page_size do not match what the DSN asked for — a DSN pragma can silently fail to take (e.g. a filesystem that falls back to a rollback journal instead of WAL), and nothing upstream reported that before this. Best-effort: a failure to acquire a connection or read the pragmas back is logged and does not fail the open.
func MarkCorrupt ¶ added in v2.15.0
MarkCorrupt writes the corrupt marker sidecar next to dbPath recording that corruption was detected. Best-effort — failures are logged, not returned.
func MediaIdentityFingerprintPayload ¶ added in v2.16.1
func MediaIdentityFingerprintPayload(identity *MediaIdentity) ([]byte, error)
MediaIdentityFingerprintPayload returns the canonical cross-service bytes hashed for an observation fingerprint. Display name and raw path are intentionally absent.
func MediaKey ¶ added in v2.9.1
MediaKey builds a composite key for media deduplication: "systemID:path"
func MigrateDownTo ¶ added in v2.18.0
MigrateDownTo rolls a database back to the given schema version, running every later migration's Down step in reverse order. It holds the same lock as MigrateUp because goose keeps its filesystem and dialect in global state. It exists for tests that exercise an upgrade from an older schema; a bare single-step Down would revert whichever migration happens to be newest and stop testing the one intended once another migration lands.
func MigrateUp ¶ added in v2.7.0
func MigrateUp( db *sql.DB, migrationFiles embed.FS, migrationDir, dbPath, sidecarPath string, ) error
MigrateUp provides thread-safe database migration using goose. It locks access to goose's global state to prevent race conditions between multiple databases setting their migration filesystems.
dbPath and sidecarPath enable a fast path that skips goose's metadata queries (which on a cold SQLite file can cost hundreds of milliseconds) when a previously-written sidecar reports the live DB file is already at the latest embedded migration version. Pass both as empty strings to disable the sidecar fast path (e.g. in tests against in-memory DBs).
func NewDeckID ¶ added in v2.18.0
NewDeckID mints a deck ID from the operating system's random source: DeckIDLength characters of DeckIDAlphabet, lower-cased. One byte per character modulo 32 is unbiased because 256 is a multiple of 32.
func NormalizeDeckID ¶ added in v2.18.0
NormalizeDeckID trims and lower-cases a deck ID and checks it is a minted ID drawn from DeckIDAlphabet or a legacy ID drawn from DeckIDLegacyAlphabet. Characters an alphabet omits are refused rather than folded, so the stored ID is always the one that was issued.
func NoteCorruption ¶ added in v2.15.0
NoteCorruption flags dbPath corrupt when err indicates SQLite corruption, so any path that first touches a malformed page routes into the recovery flow instead of silently failing. It only writes the marker once. Returns true when err was a corruption error.
func PreserveCorruptFile ¶ added in v2.17.0
func PreserveCorruptFile(path, dbLabel string)
PreserveCorruptFile renames path aside to its CorruptBackupPath, so a file about to be replaced or deleted survives as a forensic copy. A missing source file is a silent no-op (nothing to preserve); any other stat or rename failure is logged, not returned — recovery must proceed even when the forensic copy could not be made. dbLabel names the database in the log line (e.g. "media", "user").
func ReconcileMediaUserData ¶ added in v2.18.0
ReconcileMediaUserData makes the MediaDB projection of media user data, the five user flag tags and the launcher override property, match UserDB exactly: missing ones are added and ones UserDB no longer holds are removed. It is for when UserDB was replaced as a whole, as by a backup restore, where the projection still describes the database that was replaced. Live edits and the post-index re-apply only ever need to add.
Only the five flag tags and the override property are touched; deck tags and metadata tags stay as they are. A projection already in line writes nothing. Rows whose system or path is not indexed are skipped.
func RemoveSidecars ¶ added in v2.15.0
RemoveSidecars deletes the -wal and -shm sidecar files for dbPath. A stale WAL left next to a freshly restored or recreated database would re-corrupt it.
func RemoveSidecarsFS ¶ added in v2.17.0
RemoveSidecarsFS deletes database sidecars through fs.
func SetMigrationReporter ¶ added in v2.18.0
func SetMigrationReporter(fn MigrationReporter) (restore func())
SetMigrationReporter installs fn for the duration of a startup and returns a function that removes it again. It exists so startup can say that a database is being upgraded while that is actually happening, without threading a parameter through the two databases' MigrateUp interface methods and every mock that implements them.
The returned restore function must be called — a reporter that outlives the startup it belongs to would report into a server that has moved on.
func TagTypeDisplayRank ¶ added in v2.15.0
TagTypeDisplayRank returns the display-importance rank of a tag type (lower is more important). Unknown types sort last. Used to order emitted disambiguating tags.
func TitleKey ¶ added in v2.9.1
TitleKey builds a composite key for title deduplication: "systemID:slug"
func ValidTitleCandidateName ¶ added in v2.18.0
ValidTitleCandidateName bounds work before normalization, checking bytes before rune counting. UTF-8 has at most four bytes per Unicode code point.
func WaitForLongMediaWrites ¶ added in v2.18.0
WaitForLongMediaWrites blocks while an index, optimization, recovery or maintenance job owns the media database, checking again every poll, and reports false if ctx ended first. Those jobs hold long write transactions that a small write would time out behind. Scraping is not waited for: it can run for hours and commits in short transactions.
Types ¶
type BackupInfo ¶ added in v2.15.0
type BrowseCursor ¶ added in v2.10.0
type BrowseCursor struct {
SortValue string
SortMode string
Phase string
DirName string
RootView string
// Sources is the merged system root's resolved routes, carried forward from
// the page that discovered them so later pages do not rediscover the scope.
// Empty for an ordinary path browse, whose scope is the path itself.
Sources []BrowseSource
LastID int64
TotalFiles int
TotalDirs int
}
BrowseCursor holds the keyset pagination state for browse queries.
media.browse pages directories first (ordered by Name), then files, under a single cursor. Phase selects which stream the cursor resumes: "dirs" uses DirName as the keyset, "files" (or empty, for legacy file-only cursors) uses SortValue/SortMode/LastID. TotalFiles and TotalDirs carry the first-page counts so cursor pages do not rerun the count queries.
type BrowseDirCountOptions ¶ added in v2.15.1
type BrowseDirCountOptions struct {
PathPrefix string
Overlay *BrowseOverlay
Systems []systemdefs.System
ExcludeHidden bool
}
BrowseDirCountOptions contains parameters for the BrowseDirCount query.
type BrowseDirectoriesOptions ¶ added in v2.12.0
type BrowseDirectoriesOptions struct {
PathPrefix string
Overlay *BrowseOverlay
AfterName string
Systems []systemdefs.System
Limit int
ExcludeHidden bool
}
BrowseDirectoriesOptions contains parameters for the BrowseDirectories query. AfterName is the keyset cursor for directory pagination: only directories whose Name sorts strictly after it are returned (directory names are unique within a parent, so Name alone is a stable keyset). Limit caps the number of directories returned; 0 means no limit (full listing).
type BrowseDirectoryResult ¶ added in v2.10.0
type BrowseDirectoryResult struct {
Name string
Path string
SystemIDs []string
FileCount int
HasCover bool
}
BrowseDirectoryResult represents a subdirectory found during browse navigation.
type BrowseFileCountOptions ¶ added in v2.12.0
type BrowseFileCountOptions struct {
Letter *string
PathPrefix string
Overlay *BrowseOverlay
Systems []systemdefs.System
Tags []zapscript.TagFilter
ExcludeHidden bool
}
BrowseFileCountOptions contains parameters for the BrowseFileCount query.
type BrowseFilesOptions ¶ added in v2.10.0
type BrowseFilesOptions struct {
Cursor *BrowseCursor
Letter *string
PathPrefix string
Overlay *BrowseOverlay
Sort string
Systems []systemdefs.System
Tags []zapscript.TagFilter
Limit int
ExcludeHidden bool
}
BrowseFilesOptions contains parameters for the BrowseFiles query.
type BrowseIndexBucket ¶ added in v2.15.0
type BrowseIndexBucket struct {
Key string
SortValue string
LastID int64
Count int
Offset int
AtStart bool
}
BrowseIndexBucket is one first-character bucket of a browse scope. SortValue and LastID are the keyset of the row immediately before the bucket's first row, so a media.browse cursor built from them lands a page on the bucket's first item. Offset is the bucket's 0-based position among the scope's files (its row number in the ordered query, so it can't drift from the browse order); it excludes leading directories, which the caller adds. AtStart is true for the bucket that begins the list (no preceding row), in which case the caller should produce an empty cursor.
type BrowseIndexOptions ¶ added in v2.15.0
type BrowseIndexOptions struct {
PathPrefix string
Overlay *BrowseOverlay
Sort string
Systems []systemdefs.System
Tags []zapscript.TagFilter
ExcludeHidden bool
}
BrowseIndexOptions contains parameters for the BrowseIndex facet query. It mirrors the scoping fields of BrowseFilesOptions so the index describes the exact list a media.browse call would return.
type BrowseIndexResult ¶ added in v2.15.0
type BrowseIndexResult struct {
Scheme string
SortMode string
Buckets []BrowseIndexBucket
TotalFiles int
}
BrowseIndexResult is the ordered set of first-character buckets for a browse scope. Buckets are ordered to match the active sort. Scheme reports the collation used to derive the buckets ("latin"); it is "none" when the directory's effective sort is not alphabetical, in which case Buckets is empty and no rail applies. SortMode is the resolved browse sort mode the buckets were computed under and must be embedded into the seek cursors so the subsequent media.browse page continues in the same order.
type BrowseOverlay ¶ added in v2.17.0
type BrowseOverlay struct {
Sources []BrowseSource
}
type BrowseRouteCount ¶ added in v2.12.0
BrowseRouteCount represents a populated browse route and its media count. CountUnknown is set when the route is known to contain media but the exact FileCount could not be computed within the deadline (degraded fallback); callers should treat such routes as present with an unknown count rather than as empty.
type BrowseRouteCountsOptions ¶ added in v2.12.0
type BrowseRouteCountsOptions struct {
Systems []systemdefs.System
Routes []string
ExcludeHidden bool
}
BrowseRouteCountsOptions contains candidate route paths to resolve against indexed media for system-scoped browse root discovery.
type BrowseSource ¶ added in v2.17.0
BrowseSource is one ordered physical directory contributing to a pathless system-root contents view. Earlier sources have higher overlay priority. IncludeDirs is false for an ancestor route retained only for direct media.
type BrowseSystemRootCandidates ¶ added in v2.12.0
BrowseSystemRootCandidates is the cache-backed result of resolving a list of filesystem roots against system-scoped browse data. Children holds the immediate subdirectory names of each root that contain media for the requested systems; HasMedia is true when a root has any media in its subtree (even purely via descendants).
type BrowseSystemRootCandidatesOptions ¶ added in v2.12.0
type BrowseSystemRootCandidatesOptions struct {
Roots []string
Systems []systemdefs.System
ExcludeHidden bool
}
BrowseSystemRootCandidatesOptions parameterises the batched lookup used to build `media.browse({systems:[...], path:""})` candidates in two queries against the BrowseDirCounts cache.
type BrowseVirtualScheme ¶ added in v2.10.0
BrowseVirtualScheme represents a virtual URI scheme with indexed content.
type BrowseVirtualSchemesOptions ¶ added in v2.12.0
type BrowseVirtualSchemesOptions struct {
Systems []systemdefs.System
ExcludeHidden bool
}
BrowseVirtualSchemesOptions contains parameters for BrowseVirtualSchemes.
type Client ¶ added in v2.11.0
type Client struct {
ClientID string `json:"clientId"`
ClientName string `json:"clientName"`
AuthToken string `json:"-"`
// Role is the client's permission role ("admin" or "member"), chosen
// at pairing approval. See pkg/api/permissions.
Role string `json:"role"`
PairingKey []byte `json:"-"`
DBID int64 `json:"-"`
CreatedAt int64 `json:"createdAt"`
LastSeenAt int64 `json:"lastSeenAt"`
}
Client represents a paired API client. AuthToken and PairingKey are hidden from JSON (API uses models.PairedClient instead).
type Conn ¶ added in v2.15.0
type Conn struct {
// contains filtered or unexported fields
}
Conn holds a *sql.DB handle that may be swapped at runtime — corruption recovery and backup restore close the old connection and open a new one while other goroutines are still using the database. The swap (Store) and every read (Load) go through an atomic pointer, so reassigning the handle is race-free without serializing queries: SQLite's WAL mode and busy_timeout already handle query concurrency. A reader that loads the handle mid-swap sees either the old connection (returning a clean "database is closed" error) or the new one, never a torn pointer. Both UserDB and MediaDB embed Conn so they manage their connection handle identically.
Invariant: production code never clears the handle back to nil. A runtime swap always goes Open -> Store(new), so once a database has been opened Load() stays non-nil. Several call sites rely on this by reading Load() more than once per method without re-checking: because the handle is never swapped to nil, a guard check (Load() == nil) followed by a later Load() for the query cannot observe nil in between. Only tests call Store(nil). A future change that clears the handle in production would reintroduce that nil-dereference race across those call sites.
type Database ¶
type Database struct {
UserDB UserDBI
MediaDB MediaDBI
// DeckTags brings deck membership tags up to date in the background. It
// is nil where nothing runs that work, such as in tools and most tests.
DeckTags DeckTagQueue
// MediaUserData rebuilds the MediaDB projection of media user data in the
// background. It is nil wherever DeckTags is.
MediaUserData MediaUserDataReconciler
}
Database is a portable interface for ENV bindings
func (*Database) QueueAllDeckTags ¶ added in v2.18.0
func (db *Database) QueueAllDeckTags()
QueueAllDeckTags schedules every deck's tags when a queue is attached.
func (*Database) QueueDeckTags ¶ added in v2.18.0
QueueDeckTags schedules the given decks' tags when a queue is attached.
func (*Database) QueueMediaUserDataReconcile ¶ added in v2.18.0
func (db *Database) QueueMediaUserDataReconcile()
QueueMediaUserDataReconcile schedules the reconcile when one is attached.
type Deck ¶ added in v2.18.0
type Deck struct {
DeckID string
Name string
Description string
SourceURL string
// PlaylistID is the playlist ID the deck's source served it under, kept
// verbatim so the local copy and the served playlist are one playlist.
// Empty for a deck that was not fetched from a link.
PlaylistID string
Metadata json.RawMessage
Items []DeckItem
DBID int64
CreatedAt int64
UpdatedAt int64
FetchedAt int64
ItemCount int
Owned bool
}
Deck is a user's list. Items is populated by GetDeck and left nil by ListDecks, which fills ItemCount instead.
type DeckCardScript ¶ added in v2.18.0
type DeckCardScript struct {
Name string `json:"name,omitempty"`
ZapScript string `json:"zapscript"`
}
DeckCardScript is one script a card item runs.
func DecodeDeckCardScripts ¶ added in v2.18.0
func DecodeDeckCardScripts(raw string) []DeckCardScript
DecodeDeckCardScripts parses a stored scripts column. Empty or malformed input decodes to nil.
type DeckItem ¶ added in v2.18.0
type DeckItem struct {
Kind string
Name string
ZapScript string
CardID string
Anchor DeckItemAnchor
Scripts []DeckCardScript
Metadata json.RawMessage
DBID int64
DeckDBID int64
CreatedAt int64
UpdatedAt int64
Position int
}
DeckItem is one member of a deck.
type DeckItemAnchor ¶ added in v2.18.0
DeckItemAnchor is the per-device link from a game item to the indexed file it was added from, with the scanner identity snapshot that lets the row be re-linked after the media database is rebuilt.
type DeckSyncRow ¶ added in v2.18.0
type DeckSyncRow struct {
DeckID string
Snapshot string
RejectedCode string
RejectedHash string
Revision int64
UpdatedAt int64
Conflicts int
Locked bool
}
DeckSyncRow is the last copy of an owned deck this device agreed with the account. Snapshot is the agreed deck content as JSON; Revision 0 means the deck was never created on the account.
type DeckTagQueue ¶ added in v2.18.0
type DeckTagQueue interface {
// QueueDeckTags schedules the tags of the given decks, including decks
// that were just deleted.
QueueDeckTags(deckIDs ...string)
// QueueAllDeckTags schedules every deck's tags, and the removal of tags
// belonging to decks that no longer exist, as after a reindex.
QueueAllDeckTags()
}
DeckTagQueue brings the deck membership tags in MediaDB in line with the decks in UserDB. Queueing returns at once; the work happens later.
type DirectoryProperty ¶ added in v2.18.0
DirectoryProperty is one file-backed property attached to a stable (SystemDBID, Path) directory identity in MediaDB.
type EffectivePragmas ¶ added in v2.17.0
type EffectivePragmas struct {
JournalMode string
Synchronous int64
PageSize int64
WALAutoCheckpoint int64
TempStore int64
CacheSize int64
BusyTimeout int64
ForeignKeys int64
}
EffectivePragmas is what SQLite actually applied for a connection, read back after open rather than trusted from the DSN. A DSN pragma can silently fail to take (e.g. a filesystem that does not support WAL falls back to a rollback journal), and nothing upstream of this reports that today.
func ReadEffectivePragmas ¶ added in v2.17.0
ReadEffectivePragmas queries the pragmas SQLite actually has set on conn. Pragmas are per-connection state, so the caller must pass a connection pinned via sqlDB.Conn (or the transaction's connection) rather than the pool — querying through *sql.DB can silently land on a different physical connection than the one being verified.
type FetchedDeckResult ¶ added in v2.18.0
type FetchedDeckResult struct {
// DeckID is the ID the copy is kept under on this device.
DeckID string
// Evicted lists the fetched decks removed to make room.
Evicted []string
// Changed reports whether the stored deck changed.
Changed bool
}
FetchedDeckResult reports what keeping a deck fetched from a link did.
type GenericDBI ¶
type HistoryEntry ¶
type HistoryEntry struct {
Time time.Time `json:"time"`
CreatedAt time.Time `json:"createdAt,omitempty"`
SyncedAt *time.Time `json:"syncedAt,omitempty"`
DeviceID *string `json:"deviceId,omitempty"`
BootUUID string `json:"bootUuid,omitempty"`
ID string `json:"uuid,omitempty"`
TokenData string `json:"tokenData"`
TokenValue string `json:"tokenValue"`
TokenID string `json:"tokenId"`
Type string `json:"type"`
DBID int64 `db:"DBID" json:"id"`
MonotonicStart int64 `json:"monotonicStart,omitempty"`
Success bool `json:"success"`
ClockReliable bool `json:"clockReliable"`
IsDeleted bool `json:"isDeleted,omitempty"`
}
type InboxMessage ¶ added in v2.8.0
type JournalMode ¶ added in v2.7.0
type JournalMode string
const ( JournalModeWAL JournalMode = "WAL" JournalModeDELETE JournalMode = "DELETE" )
Journal mode constants
type LibraryInventoryState ¶ added in v2.18.0
type LibraryInventoryState struct {
CommittedAt time.Time `json:"committedAt"`
ConfirmedAt time.Time `json:"confirmedAt"`
// Endpoint is the Library sync base URL the ordinal cache and the
// committed inventory belong to.
Endpoint string `json:"endpoint"`
// Credential tags the device link the inventory was committed under.
Credential string `json:"credential"`
SHA256 string `json:"sha256"`
// TooLarge is set when the inventory for Generation exceeded what the
// account accepts, so it is not built again until the index changes.
TooLarge bool `json:"tooLarge,omitempty"`
Generation int64 `json:"generation"`
ItemCount int `json:"itemCount"`
}
LibraryInventoryState records the inventory this device last committed, so a pass can skip rebuilding a bitmap for an index generation it already uploaded.
type LibraryMediaRow ¶ added in v2.18.0
LibraryMediaRow is one present media row with the fields its identity observation is built from. Tags are loaded separately so rows outside the synced media types never pay for them.
type LibraryOrdinal ¶ added in v2.18.0
type LibraryOrdinal struct {
ResolvedAt time.Time
Fingerprint string
Code string
SeenGeneration int64
Ordinal uint32
}
LibraryOrdinal is the cached answer to resolving one identity fingerprint with Zaparoo Online: the inventory ordinal, or a rejection code with Ordinal 0. SeenGeneration is the last index generation whose inventory walk met the fingerprint, so answers for files long gone can be pruned.
type LibraryStateSyncRow ¶ added in v2.18.0
type LibraryStateSyncRow struct {
IdentityKey string
MediaType string
SystemID string
CoreSlug string
Title string
Intent string
Reaction string
RejectedCode string
RejectedHash string
VariantTags []string
PreferredTags []string
// SentPreferredTags is the version this device last named from its
// starred copy, so a star that moves is told apart from a version the
// account chose.
SentPreferredTags []string
Revision int64
UpdatedAt int64
Favorite bool
Deleted bool
Unmatched bool
}
LibraryStateSyncRow is the last personal state row this device agreed with the account for one game, or the row it holds no copy of yet.
type MediaBlob ¶ added in v2.12.0
MediaBlob is a row from the MediaBlobs content-addressed store. Data is identified by the hex-encoded SHA-256 of its framed content type and bytes.
type MediaDBI ¶
type MediaDBI interface {
GenericDBI
BeginTransaction(batchEnabled bool) error
CommitTransaction() error
CommitTransactionWithOptions(options TransactionOptions) error
FlushBatchInserters() error
RollbackTransaction() error
Exists() bool
UpdateLastGenerated() error
GetLastGenerated() (time.Time, error)
SetOptimizationStatus(status string) error
GetOptimizationStatus() (string, error)
SetOptimizationStep(step string) error
GetOptimizationStep() (string, error)
IsOptimizing() bool
BeginBrowseCacheRebuild()
EndBrowseCacheRebuild()
RunBackgroundOptimization(statusCallback func(optimizing bool), pauser *syncutil.Pauser)
WaitForBackgroundOperations()
BeginRecovery()
EndRecovery()
TrackBackgroundOperation()
HasBackgroundOperations() bool
SetIndexingConnBoost(active bool)
BackgroundOperationDone()
InvalidateCountCache() error
RebuildSlugSearchCache() error
RebuildTagCache() error
WALCheckpoint() error
QuickCheck() (bool, error)
IntegrityReport() []string
MarkCorrupt(reason string)
IsMarkedCorrupt() bool
ClearCorruptMarker() error
NoteCorruption(err error) bool
Recreate(keepBackup bool) error
// On-disk persistence for rebuilt caches. Persist* writes the current
// in-memory cache atomically; LoadCached* reads it back at startup and
// returns (false, nil) when a SQL rebuild is still required. A valid
// selective slug cache may be installed while returning false so covered
// systems remain searchable during the complete rebuild.
PersistTagCache() error
LoadCachedTagCache() (bool, error)
PersistSlugSearchCache() error
LoadCachedSlugSearchCache() (bool, error)
// IndexGeneration is bumped at the end of every successful indexing
// run. Persisted cache files embed the value they were built against
// so a stale cache from a previous run is rejected on next load.
IndexGeneration() (int64, error)
BumpIndexGeneration() (int64, error)
// Library sync: a paged walk of present media, the cache of resolve
// answers keyed by identity fingerprint, and the record of the last
// committed inventory. All of it is disposable with this database.
LibraryMediaPage(ctx context.Context, afterMediaDBID int64, limit int) ([]LibraryMediaRow, error)
GetLibraryOrdinals(ctx context.Context, fingerprints []string) (map[string]LibraryOrdinal, error)
PutLibraryOrdinals(ctx context.Context, ordinals []LibraryOrdinal) error
MarkLibraryOrdinalsSeen(ctx context.Context, fingerprints []string, generation int64) error
PruneLibraryOrdinals(ctx context.Context, generation int64) (int64, error)
ClearLibraryOrdinalCache(ctx context.Context) error
GetLibraryInventoryState(ctx context.Context) (LibraryInventoryState, error)
SetLibraryInventoryState(ctx context.Context, state *LibraryInventoryState) error
// Slug resolution cache methods
GetCachedSlugResolution(
ctx context.Context, systemID, slug string, tagFilters []zapscript.TagFilter,
) (SlugResolution, bool)
SetCachedSlugResolution(
ctx context.Context, systemID, slug string, tagFilters []zapscript.TagFilter, resolution SlugResolution,
) error
InvalidateSlugCache(ctx context.Context) error
InvalidateSlugCacheForSystems(ctx context.Context, systemIDs []string) error
GetMediaByDBID(ctx context.Context, mediaDBID int64) (SearchResultWithCursor, error)
GetZapScriptTagsBySystemAndPath(ctx context.Context, systemID, path string) ([]TagInfo, error)
SetIndexingCacheSize(enable bool)
SetWALAutoCheckpoint(pages int)
DropSecondaryIndexes() error
CreateSecondaryIndexes() error
SetIndexingStatus(status string) error
GetIndexingStatus() (string, error)
GetIndexResumeAttempts() (int, error)
IncrementIndexResumeAttempts() (int, error)
ResetIndexResumeAttempts() error
GetIndexResumeCheckpoint() (string, error)
SetIndexResumeCheckpoint(checkpoint string) error
SetScrapingStatus(status string) error
GetScrapingStatus() (string, error)
SetScrapingOperation(operation ScrapingOperation) error
GetScrapingOperation() (ScrapingOperation, bool, error)
ClearScrapingOperation() error
SetLastIndexedSystem(systemID string) error
GetLastIndexedSystem() (string, error)
SetIndexingSystems(systemIDs []string) error
GetIndexingSystems() ([]string, error)
TruncateSystems(systemIDs []string) error
// Scanner staging: files are streamed into staging tables inside the open
// batch transaction, then folded into the media tables with set-based SQL
// so indexing memory does not scale with database size.
StageScannedMedia(media *ScanStagedMedia) error
ReconcileStagedSystem(ctx context.Context, systemID string, opts ScanReconcileOpts) (ScanReconcileStats, error)
ClearScanStage() error
SeedCanonicalTagDefinitions(ctx context.Context) error
SearchMediaPathExact(ctx context.Context, systems []systemdefs.System, query string) ([]SearchResult, error)
SearchMediaWithFilters(ctx context.Context, filters *SearchFilters) ([]SearchResultWithCursor, error)
SearchMediaBySlug(
ctx context.Context, systemID string, slug string, tags []zapscript.TagFilter,
) ([]SearchResultWithCursor, error)
SearchMediaBySecondarySlug(
ctx context.Context, systemID string, secondarySlug string, tags []zapscript.TagFilter,
) ([]SearchResultWithCursor, error)
SearchMediaBySlugPrefix(
ctx context.Context, systemID string, slugPrefix string, tags []zapscript.TagFilter,
) ([]SearchResultWithCursor, error)
SearchMediaBySlugIn(
ctx context.Context, systemID string, slugs []string, tags []zapscript.TagFilter,
) ([]SearchResultWithCursor, error)
TitleCandidates(ctx context.Context, systemID, name string, limit int) ([]TitleCandidate, error)
GetTitlesWithPreFilter(
ctx context.Context, systemID string, minLength, maxLength, minWordCount, maxWordCount int,
) ([]MediaTitle, error)
GetLaunchCommandForMedia(ctx context.Context, systemID, path string) (string, error)
// SearchMediaByProperty finds media by a stored property value. systemID is an optional scope.
SearchMediaByProperty(ctx context.Context, systemID, property, value string) ([]SearchResult, error)
// HasMediaPropertyForPath reports whether indexed media already has a stored property.
HasMediaPropertyForPath(ctx context.Context, systemID, path, property string) (bool, error)
GetTags(ctx context.Context, systems []systemdefs.System) ([]TagInfo, error)
GetAllUsedTags(ctx context.Context) ([]TagInfo, error)
PopulateSystemTagsCache(ctx context.Context) error
PopulateSystemTagsCacheForSystems(ctx context.Context, systems []systemdefs.System) error
AnalyzeApproximate() error
RefreshSlugSearchCacheForSystems(ctx context.Context, systemIDs []string) error
GetSystemTagsCached(ctx context.Context, systems []systemdefs.System) ([]TagInfo, error)
InvalidateSystemTagsCache(ctx context.Context, systems []systemdefs.System) error
SearchMediaPathGlob(systems []systemdefs.System, query string) ([]SearchResult, error)
// Browse methods for directory-style navigation of indexed content
BrowseDirectories(ctx context.Context, opts BrowseDirectoriesOptions) ([]BrowseDirectoryResult, error)
BrowseDirCount(ctx context.Context, opts BrowseDirCountOptions) (int, error)
BrowseFiles(ctx context.Context, opts *BrowseFilesOptions) ([]SearchResultWithCursor, error)
// GetMediaCoverStatus returns statuses keyed by MediaDBID. True means a media-
// or title-level image exists; absent keys mean no cover.
GetMediaCoverStatus(ctx context.Context, refs []MediaRef) (map[int64]bool, error)
BrowseFileCount(ctx context.Context, opts BrowseFileCountOptions) (int, error)
BrowseIndex(ctx context.Context, opts BrowseIndexOptions) (BrowseIndexResult, error)
BrowseVirtualSchemes(ctx context.Context, opts BrowseVirtualSchemesOptions) ([]BrowseVirtualScheme, error)
BrowseRootCounts(ctx context.Context, rootDirs []string, excludeHidden bool) (map[string]*int, error)
BrowseRouteCounts(ctx context.Context, opts BrowseRouteCountsOptions) (map[string]BrowseRouteCount, error)
BrowseSystemRootCandidates(
ctx context.Context, opts BrowseSystemRootCandidatesOptions,
) (result BrowseSystemRootCandidates, cacheReady bool, err error)
PopulateBrowseCache(ctx context.Context) error
PopulateBrowseCacheForSystems(ctx context.Context, systemIDs []string) error
BrowseCacheNeedsRebuild(ctx context.Context) (bool, error)
IndexedSystems() ([]string, error)
SystemMediaCounts(
ctx context.Context, tags []zapscript.TagFilter, excludeHidden bool,
) ([]SystemMediaCount, error)
SystemIndexed(system *systemdefs.System) bool
RandomGame(ctx context.Context, systems []systemdefs.System) (SearchResult, error)
RandomGameWithQuery(ctx context.Context, query *MediaQuery) (SearchResult, error)
GetTotalMediaCount() (int, error)
HasAnyMedia() (bool, error)
GetMissingMediaCount() (int, error)
GetScrapedMediaCount(ctx context.Context, scraperID string) (int, error)
GetTotalScrapedMediaCount(ctx context.Context) (int, error)
FindSystem(row System) (System, error)
FindSystemBySystemID(systemID string) (System, error)
InsertSystem(row System) (System, error)
FindOrInsertSystem(row System) (System, error)
FindMediaTitle(row *MediaTitle) (MediaTitle, error)
InsertMediaTitle(row *MediaTitle) (MediaTitle, error)
FindOrInsertMediaTitle(row *MediaTitle) (MediaTitle, error)
FindMedia(row Media) (Media, error)
InsertMedia(row Media) (Media, error)
FindOrInsertMedia(row Media) (Media, error)
DeleteMediaTag(mediaDBID, tagDBID int64) error
UpdateMediaTags(
ctx context.Context,
mediaDBID int64,
remove []MediaTagRef,
add []MediaTagRef,
) error
SetMediaTagMembership(ctx context.Context, ref MediaTagRef, mediaDBIDs []int64) (bool, error)
ListMediaTagValues(ctx context.Context, tagType, valuePrefix string) ([]string, error)
TemporaryRepairJobsPending(ctx context.Context) (bool, error)
FindTagType(row TagType) (TagType, error)
InsertTagType(row TagType) (TagType, error)
FindOrInsertTagType(row TagType) (TagType, error)
FindTag(row Tag) (Tag, error)
InsertTag(row Tag) (Tag, error)
FindOrInsertTag(row Tag) (Tag, error)
FindMediaTag(row MediaTag) (MediaTag, error)
InsertMediaTag(row MediaTag) (MediaTag, error)
FindOrInsertMediaTag(row MediaTag) (MediaTag, error)
// CleanMediaOrphans removes Media rows where IsMissing=1 together with
// their associated MediaTags and MediaProperties. MediaTitles that are
// no longer referenced by any Media row are also removed (including their
// MediaTitleTags and MediaTitleProperties). Tags unreferenced by any join
// table are pruned. A VACUUM is issued on success.
//
// Returns the count of Media rows deleted, or one of ErrIndexingInProgress,
// ErrOptimizationInProgress, or ErrTransactionActive when the operation
// cannot safely run.
CleanMediaOrphans(ctx context.Context) (int64, error)
GetAllSystems() ([]System, error)
// GetExistingMediaUserData returns user-authored data (favourites, launcher
// overrides) already stored in media.db, for the one-time UserDB backfill.
GetExistingMediaUserData(ctx context.Context) ([]MediaUserData, error)
MediaPreferencesRevision(ctx context.Context) (string, error)
// Per-system query methods for scrapers
GetTitlesBySystemID(systemID string) ([]TitleWithSystem, error)
GetMediaBySystemID(systemID string) ([]MediaWithFullPath, error)
// GetMediaSourceRoots returns distinct local metadata roots for present media in a system.
GetMediaSourceRoots(ctx context.Context, systemID string) ([]string, error)
// GetMediaSourcesForScrape returns source rows for a full system or optional scrape scope.
// Unique is evaluated against the full system before scope filtering.
GetMediaSourcesForScrape(ctx context.Context, systemID string, scope *ScrapeScope) ([]MediaSource, error)
// GetScrapeMedia selects present indexed media and their titles within an exact resolved scope.
GetScrapeMedia(ctx context.Context, scope ScrapeScope) ([]MediaFullRow, error)
// GetScopedScrapeMediaIDs selects sentinel or force-run markers without loading an entire system.
GetScopedScrapeMediaIDs(ctx context.Context, scope ScrapeScope, scraperID, runID string) (map[int64]struct{}, error)
// FindMediaBySystemAndPath returns the Media row matching systemDBID and path,
// or nil, nil when no row is found.
FindMediaBySystemAndPath(ctx context.Context, systemDBID int64, path string) (*Media, error)
FindMediaBySystemAndPaths(ctx context.Context, systemDBID int64, paths []string) (map[string]Media, error)
// FindMediaIDsByPaths returns the system ID, path, media DBID, and title DBID
// of every Media row whose Path is in paths, in one query across all systems.
FindMediaIDsByPaths(ctx context.Context, paths []string) ([]MediaPathID, error)
// FindSingleContainerLaunchMedia returns the one logical launch target in the
// direct contents of containerPath for systemDBID, or nil, nil when the
// container is empty, nested-only, or ambiguous.
FindSingleContainerLaunchMedia(ctx context.Context, systemDBID int64, containerPath string) (*Media, error)
// FindSingleContainerLaunchMediaBySystemID is FindSingleContainerLaunchMedia
// keyed by system ID, for callers that address a system by name rather than
// by row.
FindSingleContainerLaunchMediaBySystemID(ctx context.Context, systemID, containerPath string) (*Media, error)
// ResolveSingletonContainerAliases resolves the given candidate child
// directories for systemDBID in a single batch query, returning one
// SingletonContainerAlias per candidate that collapses to a single launch
// target. Candidates with nested subdirs (recursive FileCount exceeding
// their direct media rows) or ambiguous contents are omitted.
// ZapScriptTags are populated via in-memory disambiguation (same approach
// as the search path) and will be empty for unambiguous titles.
ResolveSingletonContainerAliases(
ctx context.Context, systemDBID int64, candidates []SingletonAliasCandidate,
) ([]SingletonContainerAlias, error)
// FindMediaBySystemAndPathFold returns the Media row matching systemDBID and
// path using a case-insensitive path comparison, or nil, nil when no row is
// found. Intended for scrapers where the incoming path casing may differ from
// what the indexer recorded (e.g. Windows filesystem with mixed-case system
// directory names).
FindMediaBySystemAndPathFold(ctx context.Context, systemDBID int64, path string) (*Media, error)
// FindMediaBySystemAndPathSuffix returns all Media rows for the given system
// whose Path ends with "/" + filename. Used when companion XML child paths are
// root-relative (./file.rom) and the indexed path may be in any subdirectory.
FindMediaBySystemAndPathSuffix(ctx context.Context, systemDBID int64, filename string) ([]Media, error)
// MediaHasTag returns true when the Media row has a tag whose full string
// (type:value) equals tagValue.
MediaHasTag(ctx context.Context, mediaDBID int64, tagValue string) (bool, error)
// GetScrapedMediaIDs returns media DBIDs in a system already marked as scraped
// by scraperID.
GetScrapedMediaIDs(ctx context.Context, scraperID string, systemDBID int64) (map[int64]struct{}, error)
// GetScrapeRunMediaIDs returns media DBIDs in a system completed during a
// specific scraper run.
GetScrapeRunMediaIDs(ctx context.Context, scraperID, runID string, systemDBID int64) (map[int64]struct{}, error)
// ClearScrapeRunMarkers removes per-run completion markers after a scraper
// operation reaches a terminal state.
ClearScrapeRunMarkers(ctx context.Context, scraperID, runID string) error
// UpsertMediaTags writes tags to MediaTags for a specific Media row.
// Exclusive types (TagTypes.IsExclusive=1) delete existing tags of that type
// for the entity before inserting; additive types use INSERT OR IGNORE.
UpsertMediaTags(ctx context.Context, mediaDBID int64, tags []TagInfo) error
// UpsertMediaTitleTags writes tags to MediaTitleTags for a specific MediaTitle row.
// Exclusive/additive behaviour is identical to UpsertMediaTags.
UpsertMediaTitleTags(ctx context.Context, mediaTitleDBID int64, tags []TagInfo) error
// RecomputeTitleDisambiguation recomputes the stored disambiguating tag types
// for the given MediaTitle DBIDs. Called after writes that change a title's
// media or tags so reads can rely on the stored, title-global value.
RecomputeTitleDisambiguation(ctx context.Context, titleDBIDs []int64) error
// RecomputeSystemDisambiguation recomputes the stored disambiguating tag types
// for every MediaTitle belonging to the given system DBIDs. Used at index time.
RecomputeSystemDisambiguation(ctx context.Context, systemDBIDs []int64) error
// UpsertMediaTitleProperties upserts properties into MediaTitleProperties.
// Conflicts on (MediaTitleDBID, TypeTagDBID) update data columns; DBID is preserved.
UpsertMediaTitleProperties(ctx context.Context, mediaTitleDBID int64, props []MediaProperty) error
// UpsertMediaProperties upserts properties into MediaProperties.
// Conflicts on (MediaDBID, TypeTagDBID) update data columns; DBID is preserved.
UpsertMediaProperties(ctx context.Context, mediaDBID int64, props []MediaProperty) error
// ReplaceDirectoryProperties atomically replaces the complete file-backed
// property snapshot for one system. It reports whether stored rows changed.
ReplaceDirectoryProperties(ctx context.Context, systemDBID int64, props []DirectoryProperty) (bool, error)
// GetDirectoryProperties returns properties for one canonical directory path.
GetDirectoryProperties(ctx context.Context, systemDBID int64, path string) ([]MediaProperty, error)
// ApplyScrapeResult atomically writes all scraper metadata for a Media row and
// writes the sentinel tag last.
ApplyScrapeResult(ctx context.Context, mediaDBID, mediaTitleDBID int64, write *ScrapeWrite) error
// ConsumeScrapeImageChanges returns and clears systems with materially changed
// image properties. all is true when tracking could not resolve a safe targeted
// set and callers must conservatively invalidate the full cache.
ConsumeScrapeImageChanges() (systems []string, all bool)
// RefreshScrapeTagCache rebuilds the tag caches for systems whose tags
// changed in committed scrape writes. It is a no-op when nothing changed.
RefreshScrapeTagCache(ctx context.Context) error
// MarkScrapeTagCacheStale records systems whose tags changed in scrape
// writes that were not tracked, so the next RefreshScrapeTagCache rebuilds
// them. An empty list marks every system.
MarkScrapeTagCacheStale(systemIDs []string)
// FindMediaTitlesWithoutSentinel returns MediaTitle rows for the given system
// that have no Media row with the given sentinel tag value.
FindMediaTitlesWithoutSentinel(ctx context.Context, systemDBID int64, sentinelTag string) ([]MediaTitle, error)
// FindMediaTitleByDBID returns the MediaTitle with the given DBID,
// or nil, nil when no row is found.
FindMediaTitleByDBID(ctx context.Context, dbid int64) (*MediaTitle, error)
// FindMediaTitleBySystemAndSlug returns the MediaTitle matching systemDBID and
// slug, or nil, nil when no row is found.
FindMediaTitleBySystemAndSlug(ctx context.Context, systemDBID int64, slug string) (*MediaTitle, error)
// GetMediaTitleProperties returns all properties for a MediaTitle row,
// with TypeTagDBID resolved to the tag value string. Binary blobs are capped
// at MaxMediaPropertyBinaryBytes; larger blobs populate BlobSize but not Binary.
GetMediaTitleProperties(ctx context.Context, mediaTitleDBID int64) ([]MediaProperty, error)
GetMediaTitlePropertiesByMediaTitleDBIDs(
ctx context.Context, mediaTitleDBIDs []int64,
) (map[int64][]MediaProperty, error)
GetMediaTitlePropertyMetadata(ctx context.Context, mediaTitleDBID int64) ([]MediaProperty, error)
GetMediaTitlePropertyMetadataByMediaTitleDBIDs(
ctx context.Context, mediaTitleDBIDs []int64,
) (map[int64][]MediaProperty, error)
// GetMediaProperties returns all properties for a Media row,
// with TypeTagDBID resolved to the tag value string. Binary blobs are capped
// at MaxMediaPropertyBinaryBytes; larger blobs populate BlobSize but not Binary.
GetMediaProperties(ctx context.Context, mediaDBID int64) ([]MediaProperty, error)
GetMediaPropertiesByMediaDBIDs(ctx context.Context, mediaDBIDs []int64) (map[int64][]MediaProperty, error)
GetMediaPropertyMetadata(ctx context.Context, mediaDBID int64) ([]MediaProperty, error)
GetMediaPropertyMetadataByMediaDBIDs(ctx context.Context, mediaDBIDs []int64) (map[int64][]MediaProperty, error)
// DeleteMediaTitleProperty removes a single property row from MediaTitleProperties
// identified by (mediaTitleDBID, typeTagDBID). A no-op if the row does not exist.
DeleteMediaTitleProperty(ctx context.Context, mediaTitleDBID int64, typeTagDBID int64) error
// DeleteMediaProperty removes a single property row from MediaProperties
// identified by (mediaDBID, typeTagDBID). A no-op if the row does not exist.
DeleteMediaProperty(ctx context.Context, mediaDBID int64, typeTagDBID int64) error
// GetMediaWithTitleAndSystem fetches a Media record together with its parent
// MediaTitle and System via a single JOIN query. Returns nil, nil when no
// Media row with the given DBID exists. IsMissing is NOT filtered — metadata
// remains accessible for missing files.
GetMediaWithTitleAndSystem(ctx context.Context, mediaDBID int64) (*MediaFullRow, error)
GetMediaWithTitleAndSystemByIDs(ctx context.Context, mediaDBIDs []int64) (map[int64]MediaFullRow, error)
// GetMediaTagsByMediaDBID returns the file-level tags (MediaTags) for a
// single Media row. Does not include title-level tags.
GetMediaTagsByMediaDBID(ctx context.Context, mediaDBID int64) ([]TagInfo, error)
GetMediaTagsByMediaDBIDs(ctx context.Context, mediaDBIDs []int64) (map[int64][]TagInfo, error)
// GetMediaTitleTagsByMediaTitleDBID returns the title-level tags
// (MediaTitleTags) for a single MediaTitle row.
GetMediaTitleTagsByMediaTitleDBID(ctx context.Context, mediaTitleDBID int64) ([]TagInfo, error)
GetMediaTitleTagsByMediaTitleDBIDs(ctx context.Context, mediaTitleDBIDs []int64) (map[int64][]TagInfo, error)
// GetMediaTagsByMediaRefs returns the merged file-level (MediaTags) and
// title-level (MediaTitleTags) tags for each referenced media row, keyed
// by MediaDBID. This is the same tag view media.search attaches to
// results, unlike GetMediaTagsByMediaDBIDs which is file-level only.
// Tags are deduplicated by (type, tag) and sorted by type then tag.
// Untagged media have no map entry. Refs with MediaDBID <= 0 are ignored.
// The lookup is batched internally under the SQLite parameter limit.
GetMediaTagsByMediaRefs(ctx context.Context, refs []MediaRef) (map[int64][]TagInfo, error)
// UpsertMediaBlob inserts a blob into MediaBlobs when no row with the same
// SHA-256 hash of framed content type and bytes already exists, then returns its DBID.
// Hash computation is performed internally; callers supply only contentType and raw data.
UpsertMediaBlob(ctx context.Context, contentType string, data []byte) (int64, error)
// GetMediaBlob returns the MediaBlob row for the given DBID,
// or nil, nil when not found.
GetMediaBlob(ctx context.Context, blobDBID int64) (*MediaBlob, error)
GetMediaBlobDataCapped(ctx context.Context, blobDBID int64, maxBytes int64) ([]byte, string, error)
// PruneOrphanedBlobs deletes MediaBlobs rows that are not referenced by
// any MediaTitleProperties or MediaProperties row. Returns the count of
// rows deleted. Safe to call from CleanMediaOrphans.
PruneOrphanedBlobs(ctx context.Context) (int64, error)
}
type MediaDBWriteCoordinator ¶ added in v2.17.0
type MediaDBWriteCoordinator interface {
AcquireMediaWrite(operation MediaWriteOperation) (*MediaWriteLease, error)
ActiveMediaWriteOperation() MediaWriteOperation
RunBackgroundOptimizationWithLease(
statusCallback func(optimizing bool), pauser *syncutil.Pauser, lease *MediaWriteLease,
) error
}
MediaDBWriteCoordinator is optional process-local write arbitration layered over MediaDBI without expanding that compatibility-sensitive interface.
func GetMediaDBWriteCoordinator ¶ added in v2.17.0
func GetMediaDBWriteCoordinator(mediaDB MediaDBI) (MediaDBWriteCoordinator, error)
GetMediaDBWriteCoordinator returns write arbitration supported by current MediaDB implementations.
type MediaFullRow ¶ added in v2.12.0
type MediaFullRow struct {
System System
Media
Title MediaTitle
}
MediaFullRow is the result of a joined query fetching a Media record together with its parent MediaTitle and System in a single round-trip.
type MediaHistoryEntry ¶ added in v2.7.0
type MediaHistoryEntry struct {
StartTime time.Time `json:"startTime"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
EndTime *time.Time `json:"endTime,omitempty"`
SyncedAt *time.Time `json:"syncedAt,omitempty"`
DeviceID *string `json:"deviceId,omitempty"`
ProfileID *string `json:"profileId,omitempty"`
MediaIdentity *MediaIdentity `json:"mediaIdentity,omitempty"`
BootUUID string `json:"bootUuid,omitempty"`
ClockSource string `json:"clockSource,omitempty"`
SystemID string `json:"systemId"`
ID string `json:"uuid,omitempty"`
LauncherID string `json:"launcherId"`
SystemName string `json:"systemName"`
MediaPath string `json:"mediaPath"`
MediaName string `json:"mediaName"`
Tags []string `json:"tags,omitempty"`
DBID int64 `db:"DBID" json:"id"`
WallDuration int `json:"wallDuration"`
DurationSec int `json:"durationSec"`
MonotonicStart int64 `json:"monotonicStart,omitempty"`
PlayTime int `json:"playTime"`
TimeSkewFlag bool `json:"timeSkewFlag"`
ClockReliable bool `json:"clockReliable"`
IsDeleted bool `json:"isDeleted,omitempty"`
}
type MediaHistorySyncRef ¶ added in v2.16.0
MediaHistorySyncRef identifies the exact local version acknowledged by a play-history upload. UpdatedAt is a monotonic per-row version watermark.
type MediaHistoryTopEntry ¶ added in v2.10.0
type MediaIdentity ¶ added in v2.16.0
type MediaIdentity struct {
MediaType string `json:"media_type"`
CanonicalSystemID string `json:"canonical_system_id"`
DisplayName string `json:"display_name"`
CoreSlug string `json:"core_slug"`
ObservationFingerprint string `json:"observation_fingerprint"`
Tags []MediaIdentityTag `json:"tags"`
PolicyVersion int `json:"policy_version"`
}
MediaIdentity is Core's versioned scanner-derived identity observation for one indexed media path.
func BuildMediaIdentity ¶ added in v2.18.0
func BuildMediaIdentity( mediaType slugs.MediaType, canonicalSystemID string, displayName string, coreSlug string, tagInfos []TagInfo, ) (MediaIdentity, error)
BuildMediaIdentity returns the identity observation for one indexed media row, the same observation LookupMediaIdentity builds from a path lookup.
func DecodeMediaIdentity ¶ added in v2.16.1
func DecodeMediaIdentity(raw string) *MediaIdentity
DecodeMediaIdentity parses a UserDB identity snapshot. Empty or malformed input returns nil so legacy history remains readable.
func LookupMediaIdentity ¶ added in v2.16.0
func LookupMediaIdentity( ctx context.Context, mediaDB MediaDBI, systemID, path string, ) (MediaIdentity, bool, error)
LookupMediaIdentity resolves an exact indexed media path to Core's complete scanner identity observation. found=false covers both a successful no-row result and rows that can never resolve (unknown system, unbuildable identity); an error is returned only for transient query failures, which remain distinguishable for bounded history enrichment retries.
func (*MediaIdentity) LegacyTags ¶ added in v2.16.1
func (identity *MediaIdentity) LegacyTags() []string
LegacyTags returns the complete scanner tag snapshot as sorted type:value strings for compatibility fields retained during API migration.
type MediaIdentityTag ¶ added in v2.16.1
type MediaIdentityTag struct {
Type string `json:"type"`
Value string `json:"value"`
Role MediaIdentityTagRole `json:"role"`
}
type MediaIdentityTagRole ¶ added in v2.16.1
type MediaIdentityTagRole string
const ( MediaIdentityTagRoleIdentity MediaIdentityTagRole = "identity" MediaIdentityTagRoleContext MediaIdentityTagRole = "context" )
func MediaIdentityTagRoleForPolicy ¶ added in v2.16.1
func MediaIdentityTagRoleForPolicy(policyVersion int, tagType string) (MediaIdentityTagRole, error)
MediaIdentityTagRoleForPolicy returns a tag's immutable role under a known identity policy version.
type MediaPathID ¶ added in v2.15.0
MediaPathID identifies a Media row and its title by system ID and path, used for batch API response enrichment.
type MediaProperty ¶ added in v2.12.0
type MediaProperty struct {
BlobDBID *int64
TypeTag string
Text string
ContentType string
Binary []byte
TypeTagDBID int64
BlobSize int64
}
MediaProperty is a static content property attached to a MediaTitle or Media record. Properties are fetched for display, not filtered by value.
For writes: set TypeTag to the full "type:value" string (e.g. "property:description"). The database layer resolves the TypeTagDBID from TypeTag automatically. To associate binary data with a property, call UpsertMediaBlob first to obtain a BlobDBID, then set that field before upserting the property. For reads: TypeTag is populated from the joined Tags row; TypeTagDBID is also set. ContentType, BlobSize, and Binary are hydrated from the MediaBlobs JOIN and are read-only — do not set them for writes.
type MediaQuery ¶ added in v2.7.0
type MediaQuery struct {
PathGlob string `json:"pathGlob,omitempty"`
PathPrefix string `json:"pathPrefix,omitempty"`
Systems []string `json:"systems,omitempty"`
Tags []zapscript.TagFilter `json:"tags,omitempty"`
}
MediaQuery represents parameters for querying media counts used in random selection
type MediaRef ¶ added in v2.17.0
MediaRef identifies a Media row and its MediaTitle row for batch lookups such as cover status and tags.
type MediaSource ¶ added in v2.18.0
type MediaSource struct {
MediaPath string
SourcePath string
SourceKey string
SourceRoot string
SourceKind string
MediaDBID int64
SystemDBID int64
Unique bool
}
MediaSource is scanner-owned local metadata provenance for an indexed virtual Media row. Unique and SharedGame are computed across all present rows in the system before any scrape scope is applied. SharedGame reports that every row on this source carries the same non-empty group, so they are variants of one game rather than different games.
type MediaTagBatchUpdater ¶ added in v2.18.0
type MediaTagBatchUpdater interface {
UpdateMediaTagsBatch(ctx context.Context, updates []MediaTagUpdate) error
}
MediaTagBatchUpdater is optional batch tag editing layered over MediaDBI without expanding that interface. It applies every edit in one transaction.
type MediaTagLink ¶ added in v2.11.0
type MediaTagRef ¶ added in v2.16.1
MediaTagRef identifies a tag by its stable type and value.
type MediaTagUpdate ¶ added in v2.18.0
type MediaTagUpdate struct {
Remove []MediaTagRef
Add []MediaTagRef
MediaDBID int64
}
MediaTagUpdate is one file's tag edit in a batch: removals run before additions, as in MediaDBI.UpdateMediaTags.
type MediaTitle ¶
type MediaTitle struct {
Slug string
Name string
// DisambiguationTypes is the title's stored comma-separated set of tag types
// whose values differ across its non-missing media (see RecomputeTitleDisambiguation).
DisambiguationTypes string
SecondarySlug sql.NullString
DBID int64
SystemDBID int64
SlugLength int
SlugWordCount int
}
type MediaUserData ¶ added in v2.15.0
type MediaUserData struct {
SystemID string
Path string
LauncherOverride string
// MediaName, Slug and Tags snapshot the scanner's identity for this path
// at write time (display name, title slug and complete canonical
// type:value tags), so the row stays matchable to a canonical game after
// MediaDB is rebuilt or the file disappears. Empty when no scanner entry
// existed.
MediaName string
Slug string
Tags []string
DBID int64
CreatedAt int64
UpdatedAt int64
IsFavorite bool
IsHidden bool
IsLiked bool
IsDisliked bool
IsPlayLater bool
}
MediaUserData is the source-of-truth record for user-authored data about a single media path: the favorite, hidden, liked, disliked and play later flags and any per-game launcher override. It lives in UserDB (durable, power-loss safe) and is materialized into media.db's MediaTags/ MediaProperties projection both on edit and on reindex. Keyed by (SystemID, Path) because a Media row's DBID is not stable across a full media.db rebuild. A row with no flag set and an empty LauncherOverride should be deleted rather than kept.
func (*MediaUserData) Flag ¶ added in v2.18.0
func (d *MediaUserData) Flag(flag MediaUserFlag) bool
Flag reports the value of one flag.
func (*MediaUserData) HasIntent ¶ added in v2.18.0
func (d *MediaUserData) HasIntent() bool
HasIntent reports whether the row records any preference worth keeping.
func (*MediaUserData) ValidateFlags ¶ added in v2.18.0
func (d *MediaUserData) ValidateFlags() error
ValidateFlags reports ErrMediaUserFlagConflict when the row holds a forbidden pair. A game cannot be liked and disliked, and a favorite cannot be disliked.
type MediaUserDataReconciler ¶ added in v2.18.0
type MediaUserDataReconciler interface {
QueueMediaUserDataReconcile()
}
MediaUserDataReconciler brings the user flag tags and launcher overrides in MediaDB in line with UserDB after UserDB was replaced as a whole. Queueing returns at once; the work happens later.
type MediaUserFlag ¶ added in v2.18.0
type MediaUserFlag string
MediaUserFlag names one boolean preference a user can set on a media path.
const ( MediaUserFlagFavorite MediaUserFlag = "favorite" MediaUserFlagHidden MediaUserFlag = "hidden" MediaUserFlagLiked MediaUserFlag = "liked" MediaUserFlagDisliked MediaUserFlag = "disliked" MediaUserFlagPlayLater MediaUserFlag = "playlater" )
type MediaWithFullPath ¶ added in v2.7.0
type MediaWithFullPath struct {
Path string
ParentDir string
TitleSlug string
SystemID string
SortName string
DBID int64
MediaTitleDBID int64
IsMissing bool
}
MediaWithFullPath represents a Media item with its associated title and system information.
type MediaWriteArbiter ¶ added in v2.17.0
type MediaWriteArbiter struct {
// contains filtered or unexported fields
}
MediaWriteArbiter serializes process-local MediaDB mutations. Its zero value is ready to use.
func (*MediaWriteArbiter) Active ¶ added in v2.17.0
func (a *MediaWriteArbiter) Active() MediaWriteOperation
Active reports current process-local owner, or MediaWriteOperationNone.
func (*MediaWriteArbiter) TryAcquire ¶ added in v2.17.0
func (a *MediaWriteArbiter) TryAcquire(operation MediaWriteOperation) (*MediaWriteLease, error)
TryAcquire atomically claims the write slot.
type MediaWriteConflictError ¶ added in v2.17.0
type MediaWriteConflictError struct {
Requested MediaWriteOperation
Active MediaWriteOperation
}
MediaWriteConflictError reports which process-local owner blocked a request.
func (*MediaWriteConflictError) Error ¶ added in v2.17.0
func (e *MediaWriteConflictError) Error() string
func (*MediaWriteConflictError) Unwrap ¶ added in v2.17.0
func (*MediaWriteConflictError) Unwrap() error
type MediaWriteLease ¶ added in v2.17.0
type MediaWriteLease struct {
// contains filtered or unexported fields
}
MediaWriteLease is exclusive ownership of one MediaDB write-operation slot. Release is idempotent. Handoff changes operation type without an unowned gap.
func (*MediaWriteLease) Handoff ¶ added in v2.17.0
func (l *MediaWriteLease) Handoff(operation MediaWriteOperation) error
Handoff atomically transfers ownership to another operation type.
func (*MediaWriteLease) Operation ¶ added in v2.17.0
func (l *MediaWriteLease) Operation() MediaWriteOperation
Operation reports operation currently owned by this lease.
func (*MediaWriteLease) Release ¶ added in v2.17.0
func (l *MediaWriteLease) Release()
Release relinquishes ownership exactly once. Duplicate calls are no-ops.
func (*MediaWriteLease) ValidFor ¶ added in v2.17.0
func (l *MediaWriteLease) ValidFor(operation MediaWriteOperation) bool
ValidFor reports whether lease still owns operation.
type MediaWriteOperation ¶ added in v2.17.0
type MediaWriteOperation string
MediaWriteOperation identifies one process-local MediaDB write owner.
const ( MediaWriteOperationNone MediaWriteOperation = "" MediaWriteOperationIndexing MediaWriteOperation = "indexing" MediaWriteOperationScraping MediaWriteOperation = "scraping" MediaWriteOperationOptimization MediaWriteOperation = "optimization" MediaWriteOperationMaintenance MediaWriteOperation = "maintenance" MediaWriteOperationRecovery MediaWriteOperation = "recovery" )
type MigrationReporter ¶ added in v2.18.0
databaseLabel names a database in logs by its file name, so a line says which of the two is being worked on without the caller passing a label down. MigrationReporter is told that a database is about to have migrations applied, and how many. It is called before the work starts, on the goroutine running it.
type Profile ¶ added in v2.16.0
type Profile struct {
LimitsEnabled *bool `json:"limitsEnabled,omitempty"`
DailyLimit *string `json:"dailyLimit,omitempty"`
SessionLimit *string `json:"sessionLimit,omitempty"`
LastUsedAt *int64 `json:"lastUsedAt,omitempty"`
ProfileID string `json:"profileId"`
Name string `json:"name"`
Role string `json:"role"`
SwitchID string `json:"switchId"`
PINHash string `json:"-"`
DBID int64 `json:"-"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
}
Profile represents a device profile: a named bucket of preferences and limits with no credentials. PINHash is hidden from JSON (API uses models.ProfileResponse instead). Nil limit fields mean "inherit the global config value"; a "0" duration string means "explicitly unlimited".
type RemoteCommand ¶ added in v2.17.0
type RemoteCommand struct {
DeadlineAt time.Time
ExecutionExpiresAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
Origin json.RawMessage
Result json.RawMessage
CommandID string
OperationID string
OperationType string
ParamsDigest string
State string
ResultStatus string
ErrorCode string
ProtocolVersion int
ResultReported bool
}
JournalMode represents SQLite journal mode RemoteCommand is one durable typed-operation ledger entry. State changes are persisted before side effects so a crash can never cause re-execution.
type RestoreInfo ¶ added in v2.15.0
type RestoreInfo struct {
PreRestoreBackup *BackupInfo `json:"preRestoreBackup,omitempty"`
RestoredFrom BackupInfo `json:"restoredFrom"`
}
type ScanReconcileOpts ¶ added in v2.16.0
type ScanReconcileOpts struct {
// Yield runs between set-based reconcile steps and chunked missing-media
// updates. Background indexers use it for cooperative pacing; nil preserves
// unpaced callers such as foreground maintenance and tests.
Yield func() error
// IncompleteScan means file collection for this system hit errors (an
// unreadable path, a failed launcher scanner), so the staged set may be a
// subset of what actually exists. Staged files are still upserted and
// re-found rows still clear their missing flag, but media absent from the
// stage keep their current missing state instead of being flagged missing.
IncompleteScan bool
}
ScanReconcileOpts adjusts how a staged-system reconcile treats the staged file set.
type ScanReconcileStats ¶ added in v2.16.0
type ScanReconcileStats struct {
SystemDBID int64
TitlesInserted int64
TitlesRenamed int64
MediaUpserted int64
MediaMissing int64
TagsInserted int64
TagLinksAdded int64
TagLinksDeleted int64
TouchedTitles int64
// SystemKnown is false when the system has no DB row and nothing was staged,
// meaning the reconcile was a no-op and no Systems row was created.
SystemKnown bool
}
ScanReconcileStats reports what a staged-system reconcile changed. Counts are per-statement sqlite changes() values, for logging and tests.
type ScanStagedMedia ¶ added in v2.16.0
type ScanStagedMedia struct {
Source *ScanStagedSource
Path string
ParentDir string
Slug string
TitleName string
SortName string
SecondarySlug string
Tags []ScanStagedTag
Properties []ScanStagedProperty
SlugLength int
SlugWordCount int
}
ScanStagedMedia is one scanned file's parsed fragments, staged into the ScanStage/ScanStageTags tables for set-based reconcile against the media tables. SecondarySlug is empty when the title has none.
type ScanStagedProperty ¶ added in v2.16.0
ScanStagedProperty is one property derived from a scanned file, staged for set-based reconcile after the corresponding Media row exists.
type ScanStagedSource ¶ added in v2.18.0
ScanStagedSource is optional local metadata provenance for a virtual media row, normalized before it reaches database staging.
type ScanStagedTag ¶ added in v2.16.0
ScanStagedTag is one tag derived from a scanned file, staged for set-based reconcile. Value is the natural (unpadded) form; the DB layer applies tags.PadTagValue when writing the staging row.
type ScrapeJob ¶ added in v2.18.0
type ScrapeJob struct {
// Scope narrows the job the same way ScrapingOperation.Scope does, so a
// scoped request that is queued behind another still runs scoped.
Scope *ScrapeScope `json:"scope,omitempty"`
ScraperID string `json:"scraperId"`
RunID string `json:"runId,omitempty"`
Systems []string `json:"systems"`
Force bool `json:"force"`
FillMissing bool `json:"fillMissing,omitempty"`
}
ScrapeJob is shared by explicit requests, index-triggered requests, and recovery. RunID scopes completed-row markers to this particular request.
type ScrapeResultBatchApplier ¶ added in v2.14.0
type ScrapeResultBatchApplier interface {
ApplyScrapeResults(ctx context.Context, targets []ScrapeWriteTarget) error
}
ScrapeResultBatchApplier optionally batches scrape writes for DB implementations that can keep multiple targets in one transaction.
type ScrapeScope ¶ added in v2.18.0
type ScrapeScope struct {
SystemID string `json:"system"`
Path string `json:"path"`
MediaID int64 `json:"mediaId,omitempty"`
Subtree bool `json:"subtree,omitempty"`
}
ScrapeScope is a resolved selection, persisted unchanged across restarts. Single items pin all three identity fields so a reused DBID cannot select a different path after an index rebuild. A nil scope retains legacy selection.
func (ScrapeScope) Validate ¶ added in v2.18.0
func (s ScrapeScope) Validate() error
Validate fails closed for malformed persisted selectors rather than treating an empty or unrecognized selection as a whole-system request.
type ScrapeWrite ¶ added in v2.12.0
type ScrapeWrite struct {
Sentinel TagInfo
MediaTags []TagInfo
TitleTags []TagInfo
TitleProps []MediaProperty
MediaProps []MediaProperty
// FillMissing preserves existing fields; zero retains manual scrape semantics.
FillMissing bool
}
ScrapeWrite is the database-level write payload produced by a scraper for a matched Media row. Sentinel is written after all metadata so interrupted runs can safely retry the record.
type ScrapeWriteTarget ¶ added in v2.14.0
type ScrapeWriteTarget struct {
Write *ScrapeWrite
MediaDBID int64
MediaTitleDBID int64
}
ScrapeWriteTarget pairs a scraper write payload with the existing Media and MediaTitle rows it should enrich.
type ScrapingOperation ¶ added in v2.14.0
type ScrapingOperation struct {
// Status is authoritative for versioned jobs, so queue acceptance and
// cancellation do not depend on a second config-key write.
// Scope narrows the run to one indexed item, file or subtree. A queued job
// carries its own, or resuming after an index would silently widen a
// single-file request into a whole-system scrape.
Scope *ScrapeScope `json:"scope,omitempty"`
Status string `json:"status,omitempty"`
ScraperID string `json:"scraperId"`
RunID string `json:"runId,omitempty"`
Systems []string `json:"systems"`
Pending []ScrapeJob `json:"pending,omitempty"`
Version int `json:"version,omitempty"`
Force bool `json:"force"`
FillMissing bool `json:"fillMissing,omitempty"`
}
ScrapingOperation retains the legacy current-job fields while adding ordinary pending jobs. Version zero is the existing standalone record format.
func (*ScrapingOperation) IsResumable ¶ added in v2.18.0
func (o *ScrapingOperation) IsResumable(legacyStatus string) bool
IsResumable prefers the versioned job's atomic state over the legacy status key.
func (*ScrapingOperation) Validate ¶ added in v2.18.0
func (o *ScrapingOperation) Validate() error
Validate checks persisted job options before any scraper can execute them.
type SearchCursor ¶ added in v2.17.0
type SearchFilters ¶ added in v2.7.0
type SearchFilters struct {
Cursor *int64 `json:"cursor,omitempty"`
SortCursor *SearchCursor `json:"-"`
Letter *string `json:"letter,omitempty"`
PathPrefix string `json:"pathPrefix,omitempty"`
Query string `json:"query"`
Sort string `json:"sort,omitempty"`
Systems []systemdefs.System `json:"systems,omitempty"`
Tags []zapscript.TagFilter `json:"tags,omitempty"`
Limit int `json:"limit"`
// ExcludeHidden is set only for discovery, never explicit launch resolution.
ExcludeHidden bool `json:"-"`
}
SearchFilters represents parameters for filtered media search
type SearchResult ¶
type SearchResultWithCursor ¶ added in v2.7.0
type SearchResultWithCursor struct {
SystemID string
Name string
Path string
// DisambiguationTypes is the title's stored comma-separated set of tag types
// that distinguish its variants (see RecomputeTitleDisambiguation). Empty for
// titles with no variants, which lets the read path skip the tag lookup.
DisambiguationTypes string
SortValue string
SortMode string
Tags []TagInfo
ZapScriptTags []TagInfo
MediaID int64
MediaTitleID int64 `json:"-"`
HasCover bool
}
func (*SearchResultWithCursor) ZapScript ¶ added in v2.10.0
func (r *SearchResultWithCursor) ZapScript() string
ZapScript returns the ZapScript title command string for this search result. Uses ZapScriptTags (disambiguating tags only). If ZapScriptTags has not been populated (nil), no tags are emitted — callers that need disambiguation must run the result through attachZapScriptTags, which reads the title's stored DisambiguationTypes (see RecomputeTitleDisambiguation).
type SingletonAliasCandidate ¶ added in v2.14.1
SingletonAliasCandidate identifies a child directory to consider for singleton-container alias resolution. ChildDir must end with a trailing slash. FileCount is the recursive per-system media count for the directory (from the browse cache) — when it exceeds the number of direct media rows, the directory contains nested subdirectories and is not a singleton container.
type SingletonContainerAlias ¶ added in v2.14.1
type SingletonContainerAlias struct {
ChildDir string
Tags []TagInfo
ZapScriptTags []TagInfo
Row MediaFullRow
HasCover bool
}
SingletonContainerAlias is the resolved launch media for a child directory whose contents collapse to a single logical launch target.
type SlugResolution ¶ added in v2.18.0
SlugResolution is a title resolution as the slug resolution cache keeps it: the media it landed on, the strategy that found it, and the confidence it scored, so a cache hit reports the same confidence as the first resolution.
type SystemMediaCount ¶ added in v2.17.0
type TitleCandidate ¶ added in v2.18.0
type TitleCandidate struct {
SystemID string `json:"systemId"`
Name string `json:"name"`
MatchType string `json:"matchType"`
Rank int `json:"rank"`
Confidence float64 `json:"confidence"`
}
TitleCandidate is title-level discovery evidence, not a resolved launch target. Confidence is advisory; only MatchType and Rank have stable client semantics.
type TitleWithSystem ¶ added in v2.7.0
TitleWithSystem represents a MediaTitle with its associated System information
type TransactionOptions ¶ added in v2.14.1
type TransactionOptions struct {
WALCheckpoint WALCheckpointMode
}
type UserDBI ¶
type UserDBI interface {
GenericDBI
AddHistory(entry *HistoryEntry) error
GetHistory(lastID int64) ([]HistoryEntry, error)
CleanupHistory(retentionDays int) (int64, error)
AddMediaHistory(entry *MediaHistoryEntry) (int64, error)
UpdateMediaHistoryTime(dbid int64, playTime int) error
UpdateMediaHistoryIdentity(dbid int64, identity *MediaIdentity) (bool, error)
// UpdateMediaHistoryIdentityAndPath is UpdateMediaHistoryIdentity plus a
// MediaPath correction, for backfilling legacy rows recorded under a
// non-path external identifier (e.g. a MiSTer arcade set name).
UpdateMediaHistoryIdentityAndPath(dbid int64, path string, identity *MediaIdentity) (bool, error)
CloseMediaHistory(dbid int64, endTime time.Time, playTime int) error
GetMediaHistory(systemIDs []string, lastID int64, limit int) ([]MediaHistoryEntry, error)
GetDistinctMediaHistory(
ctx context.Context, systemIDs []string, lastID int64, limit int,
) ([]MediaHistoryEntry, error)
GetLatestMediaHistory() (MediaHistoryEntry, bool, error)
GetMediaHistoryTop(systemIDs []string, since *time.Time, limit int) ([]MediaHistoryTopEntry, error)
CloseHangingMediaHistory() error
CleanupMediaHistory(retentionDays int, requireSynced bool) (int64, error)
BackfillMediaHistoryUUIDs() (int64, error)
GetMediaHistoryIdentityBackfillBatch(
afterDBID int64, policyVersion int, limit int,
) ([]MediaHistoryEntry, error)
ResetMediaHistorySyncAfter(watermark *time.Time) error
GetMediaHistorySyncBatch(after time.Time, afterDBID int64, limit int) ([]MediaHistoryEntry, error)
MarkMediaHistorySynced(refs []MediaHistorySyncRef, syncedAt time.Time) error
HealTimestamps(bootUUID string, trueBootTime time.Time) (int64, error)
SumMediaPlayTimeForDay(dayStart time.Time) (int64, error)
SumMediaPlayTimeForDayByProfile(dayStart time.Time, profileID string) (int64, error)
AddMapping(m *Mapping) error
GetMapping(id int64) (Mapping, error)
DeleteMapping(id int64) error
UpdateMapping(id int64, m *Mapping) error
GetAllMappings() ([]Mapping, error)
GetEnabledMappings() ([]Mapping, error)
GetMediaUserData(systemID, path string) (MediaUserData, bool, error)
SetMediaUserFavorite(systemID, path string, favorite bool) error
SetMediaUserHidden(systemID, path string, hidden bool) error
SetMediaUserFlag(systemID, path string, flag MediaUserFlag, value bool) error
SetMediaUserLauncherOverride(systemID, path, launcherID string) error
SetMediaUserSnapshot(systemID, path, mediaName, slug string, tags []string) error
UpsertMediaUserData(data *MediaUserData) error
DeleteMediaUserData(systemID, path string) error
ListMediaUserData() ([]MediaUserData, error)
// Library sync bookkeeping for personal state.
ListLibraryStateSync() ([]LibraryStateSyncRow, error)
UpsertLibraryStateSync(rows []LibraryStateSyncRow) error
DeleteLibraryStateSync(identityKeys []string) error
ClearLibraryStateSync() error
// Decks
CreateDeck(deck *Deck) error
GetDeck(deckID string) (*Deck, error)
ListDecks() ([]Deck, error)
UpdateDeck(deckID string, edit func(deck *Deck) error) (*Deck, error)
DeleteDeck(deckID string) (bool, error)
UpsertRemoteDeck(deck *Deck) (bool, error)
UpsertFetchedDeck(deck *Deck, maxCopies int) (FetchedDeckResult, error)
SetDeckItemAnchor(itemDBID int64, anchor *DeckItemAnchor) error
CountOwnedDecks() (int, error)
RenameDeck(oldID, newID string) error
// Library sync bookkeeping for decks.
ListDeckSync() ([]DeckSyncRow, error)
GetDeckSync(deckID string) (DeckSyncRow, bool, error)
UpsertDeckSync(rows []DeckSyncRow) error
DeleteDeckSync(deckIDs []string) error
ClearDeckSync() error
UpdateZapLinkHost(host string, zapscript int) error
GetZapLinkHost(host string) (supported, found bool, err error)
GetSupportedZapLinkHosts() ([]string, error)
PruneExpiredZapLinkHosts(olderThan time.Duration) (int64, error)
UpdateZapLinkCache(url string, zapscript string) error
GetZapLinkCache(url string) (string, error)
AddInboxMessage(msg *InboxMessage) (*InboxMessage, error)
GetInboxMessages() ([]InboxMessage, error)
DeleteInboxMessage(id int64) error
DeleteAllInboxMessages() (int64, error)
CreateClient(c *Client) error
GetClientByToken(authToken string) (*Client, error)
ListClients() ([]Client, error)
ReplaceAllClients(clients []Client) error
DeleteClient(clientID string) error
UpdateClientLastSeen(authToken string, lastSeenAt int64) error
CountClients() (int, error)
CreateProfile(p *Profile) error
GetProfile(profileID string) (*Profile, error)
GetProfileBySwitchID(switchID string) (*Profile, error)
ListProfiles() ([]Profile, error)
UpdateProfile(p *Profile) error
ActivateProfile(profileID string, lastUsedAt int64) error
DeleteProfile(profileID string) error
SetDeviceState(key, value string) error
GetDeviceState(key string) (string, bool, error)
DeleteDeviceState(key string) error
ClaimRemoteCommand(command *RemoteCommand) (*RemoteCommand, bool, error)
TransitionRemoteCommand(commandID, fromState, toState string, executionExpiresAt *time.Time) (bool, error)
StoreRemoteCommandResult(
commandID, fromState, status string, result json.RawMessage, errorCode string,
) (bool, error)
MarkRemoteCommandResultReported(commandID string) error
ListUnreportedRemoteCommands(limit int) ([]RemoteCommand, error)
ListRecentRemoteCommands(limit int) ([]RemoteCommand, error)
PruneRemoteCommands(before time.Time) (int64, error)
Backup(reason string, manual bool) (BackupInfo, error)
BackupForUpdate(targetVersion string) (BackupInfo, func() error, error)
BackupForTransfer(ctx context.Context, reason string) (BackupInfo, func() error, error)
EnsureRecentBackup(maxAge time.Duration) (BackupInfo, bool, error)
ListBackups() ([]BackupInfo, error)
RestoreBackup(name string) (RestoreInfo, error)
IntegrityReport() []string
MarkCorrupt(reason string)
IsMarkedCorrupt() bool
ClearCorruptMarker() error
NoteCorruption(err error) bool
RecoverFromCorruption() (RestoreInfo, error)
}
type WALCheckpointMode ¶ added in v2.14.1
type WALCheckpointMode int
const ( WALCheckpointAuto WALCheckpointMode = iota WALCheckpointSkip WALCheckpointForce )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package container decides when a directory of indexed media collapses to a single logical launch target, such as a disc folder holding one cue sheet beside its bin tracks.
|
Package container decides when a directory of indexed media collapses to a single logical launch target, such as a disc folder holding one cue sheet beside its bin tracks. |
|
Package perfmetrics captures coarse process and SQLite resource counters for long media operations.
|
Package perfmetrics captures coarse process and SQLite resource counters for long media operations. |
|
Package scraper defines the metadata scraper types and generic run loop.
|
Package scraper defines the metadata scraper types and generic run loop. |
|
gamelistxml
Package gamelistxml implements a scraper that reads EmulationStation gamelist.xml files to enrich the Zaparoo MediaDB with developer, publisher, genre, rating, year, artwork paths, and descriptions.
|
Package gamelistxml implements a scraper that reads EmulationStation gamelist.xml files to enrich the Zaparoo MediaDB with developer, publisher, genre, rating, year, artwork paths, and descriptions. |
|
localmedia
Package localmedia imports EmulationStation-style media folder artwork.
|
Package localmedia imports EmulationStation-style media folder artwork. |
|
misterarcade
Package misterarcade imports the MiSTer arcade catalog's metadata onto indexed .mra rows.
|
Package misterarcade imports the MiSTer arcade catalog's metadata onto indexed .mra rows. |
|
misterdocs
Package misterdocs imports metadata from MiSTer Downloader content installed under docs/<system> directories.
|
Package misterdocs imports metadata from MiSTer Downloader content installed under docs/<system> directories. |
|
mra
Package mra reads the MAME set name out of a MiSTer arcade descriptor.
|
Package mra reads the MAME set name out of a MiSTer arcade descriptor. |
|
pinuppopper
Package pinuppopper imports table metadata and artwork from a PinUP Popper install into the media database.
|
Package pinuppopper imports table metadata and artwork from a PinUP Popper install into the media database. |
|
scrapertest
Package scrapertest holds assertions shared by the scraper packages' tests.
|
Package scrapertest holds assertions shared by the scraper packages' tests. |
|
ssgenre
Package ssgenre maps ScreenScraper genre names onto Core's genre vocabulary.
|
Package ssgenre maps ScreenScraper genre names onto Core's genre vocabulary. |
|
Zaparoo Core Copyright (c) 2026 The Zaparoo Project Contributors.
|
Zaparoo Core Copyright (c) 2026 The Zaparoo Project Contributors. |