Documentation
¶
Overview ¶
Package waxbin is the public facade for the WaxBin audio library engine.
WaxBin is a CGO-free Go library and waxbin CLI that owns the catalog database for an audio collection. It indexes files, records stable identities, searches the catalog, and can organize managed libraries on disk. It can be used standalone, much like beets, or embedded by WaxDeck.
Boundaries ¶
Cataloging is pure Go. Scanning reads tags and decoder-independent identity data without decoding PCM; PCM work belongs to the separate analysis pass, which is itself pure Go: decode, loudness, fingerprint, and waveforms all run through WaxFlow with no CGO and no external binaries. The read side is owned by WaxBin so consumers share one catalog view.
Layout ¶
The stable facade lives in this root package. Implementation subsystems live in their own packages: model (domain types + repository interfaces), query (the shared selection engine), identity (entity identity + essence hashing), store/sqlite (the SQLite DataStore, write coordinator, and flock ownership), read (facet/browse/search/pagination), art (CAS + thumbnails), decode (pure-Go PCM decoding via WaxFlow) and analyze (the PCM analysis pass), scan, organize, inbox, trash, playback, playlist, podcast, source (the acquisition port), enrich (metadata brain), audit (quality/repair), jobs, meta, config, proxy (the local unix-socket control channel behind serve), and pidpath (item PID -> file location, cached off the change feed, for a consumer that serves audio by PID); the CLI lives under cmd/waxbin. The structured-curation edit surface (scalar/ entity/credit/custom-tag/lyrics/chapters/art edits, with opt-in on-disk write-back) lives on the facade alongside the read API.
The engine covers the full lifecycle: scan, analyze, organize, read/browse, curation edits (with opt-in on-disk write-back), playback state, podcasts, audiobooks, enrichment, and audit/quality/repair, all on the same package boundaries. A local control socket (Serve) lets a second process mutate the catalog while one holds the write lock. The merge primitive and the maintenance commands (db verify/vacuum/migrate) close the loop.
Index ¶
- Constants
- func ReadLockOwner(dbPath string) (sqlite.OwnerInfo, error)
- type AcquiredFile
- type AcquiredMeta
- type AcquiredResult
- type AltEncoding
- type AnalyzeOptions
- type AnalyzeResult
- type AuditOptions
- type BatchEditResult
- type CreditEditOptions
- type DoctorReport
- type EditOptions
- type EmptyReport
- type EmptyTrashOptions
- type EnrichOptions
- type EnrichResult
- type EntityEditOptions
- type ImportRequest
- type Library
- func (l *Library) Acquisition(ctx context.Context, pid model.PID) (*model.Acquisition, error)
- func (l *Library) AddRoot(ctx context.Context, spec config.Root) (*model.Library, error)
- func (l *Library) Analyze(ctx context.Context, opts AnalyzeOptions) (*AnalyzeResult, error)
- func (l *Library) ApplyDelete(ctx context.Context, plan *trash.Plan) (*trash.Report, error)
- func (l *Library) ApplyImport(ctx context.Context, plan *inbox.Plan) (*inbox.Report, error)
- func (l *Library) ApplyOrganize(ctx context.Context, plan *organize.Plan) (*organize.Report, error)
- func (l *Library) ArtRoles(ctx context.Context, ref model.EntityRef) ([]model.ArtRoleInfo, error)
- func (l *Library) Audit(ctx context.Context, opts AuditOptions) (*audit.Report, error)
- func (l *Library) Backup(ctx context.Context, dest string, redact bool) error
- func (l *Library) BeginMaintenance(ctx context.Context) error
- func (l *Library) Book(ctx context.Context, pid model.PID) (*model.BookDetail, error)
- func (l *Library) BookResume(ctx context.Context, userPID, bookPID model.PID) (*model.PlayState, *model.Chapter, error)
- func (l *Library) BooksInSeries(ctx context.Context, seriesPID model.PID) ([]*model.ItemView, error)
- func (l *Library) Browse(ctx context.Context, list read.DiscoveryList, opt read.BrowseOptions) (*read.Page, error)
- func (l *Library) Changes(ctx context.Context, sinceSeq int64) ([]model.Change, error)
- func (l *Library) Chapters(ctx context.Context, pid model.PID) ([]model.Chapter, error)
- func (l *Library) Close() error
- func (l *Library) Count(ctx context.Context, q query.Query, userPID model.PID) (int, error)
- func (l *Library) Coverage() []decode.FormatSupport
- func (l *Library) CreateUser(ctx context.Context, name string) (*model.User, error)
- func (l *Library) Credits(ctx context.Context, itemPID model.PID) ([]model.Contributor, error)
- func (l *Library) CurrentChapter(ctx context.Context, pid model.PID, positionMS int64) (*model.Chapter, error)
- func (l *Library) DataVersion(ctx context.Context) (int64, error)
- func (l *Library) DefaultUser(ctx context.Context) (*model.User, error)
- func (l *Library) DeleteSecret(ctx context.Context, key string) error
- func (l *Library) DiagnosticSummary(ctx context.Context, filter model.DiagnosticFilter) ([]model.DiagnosticCount, error)
- func (l *Library) Doctor(ctx context.Context) (*DoctorReport, error)
- func (l *Library) EditEntity(ctx context.Context, entityType model.MergeEntity, entityPID model.PID, ...) error
- func (l *Library) EditField(ctx context.Context, itemPID model.PID, field, value string, opts EditOptions) error
- func (l *Library) EditFields(ctx context.Context, itemPID model.PID, edits map[string]string, ...) error
- func (l *Library) EditItemsFields(ctx context.Context, edits []model.ItemFieldEdit, opts EditOptions) (*BatchEditResult, error)
- func (l *Library) EditManyFields(ctx context.Context, itemPIDs []model.PID, edits map[string]string, ...) (*BatchEditResult, error)
- func (l *Library) EmptyTrash(ctx context.Context, opts EmptyTrashOptions) (*EmptyReport, error)
- func (l *Library) EndMaintenance(ctx context.Context) error
- func (l *Library) Enrich(ctx context.Context, opts EnrichOptions) (*EnrichResult, error)
- func (l *Library) EnrichmentCoverage(ctx context.Context) (model.EnrichmentCoverage, error)
- func (l *Library) EntityByPID(ctx context.Context, kind read.EntityKind, pid model.PID) (*read.EntityInfo, error)
- func (l *Library) EntityByPIDs(ctx context.Context, kind read.EntityKind, pids []model.PID) (map[model.PID]*read.EntityInfo, error)
- func (l *Library) EntityCuration(ctx context.Context, entityType model.MergeEntity, entityPID model.PID) ([]model.EntityCuration, error)
- func (l *Library) EntityPlayState(ctx context.Context, userPID model.PID, kind model.MergeEntity, ...) (*model.EntityPlayState, error)
- func (l *Library) Export(ctx context.Context, w io.Writer) (*port.Manifest, error)
- func (l *Library) ExportPlaylistRefs(ctx context.Context, playlistPID, userPID model.PID) ([]model.PortableRef, error)
- func (l *Library) Facet(ctx context.Context, q query.Query, g read.GroupBy, userPID model.PID) (*read.FacetResult, error)
- func (l *Library) File(ctx context.Context, filePID model.PID) (*model.File, error)
- func (l *Library) FileDiagnostics(ctx context.Context, filter model.DiagnosticFilter) ([]model.FileDiagnostic, error)
- func (l *Library) FindAltEncodings(ctx context.Context, itemPID model.PID) ([]AltEncoding, error)
- func (l *Library) FindUpgrades(ctx context.Context) ([]UpgradeGroup, error)
- func (l *Library) GCArt(ctx context.Context) (sources, thumbnails int, err error)
- func (l *Library) GCOrphans(ctx context.Context) (*model.OrphanGCReport, error)
- func (l *Library) Get(ctx context.Context, pid model.PID) (*model.ItemView, error)
- func (l *Library) GetMany(ctx context.Context, pids []model.PID) ([]*model.ItemView, error)
- func (l *Library) GetSecret(ctx context.Context, key string) (string, error)
- func (l *Library) ImportAcquired(ctx context.Context, file AcquiredFile, kind model.Kind, meta AcquiredMeta) (*AcquiredResult, error)
- func (l *Library) ImportBatches(ctx context.Context, limit int) ([]*model.ImportBatch, error)
- func (l *Library) InboxFolders() []string
- func (l *Library) IntegrityCheck(ctx context.Context) ([]string, error)
- func (l *Library) ItemTags(ctx context.Context, itemPID model.PID) ([]model.ItemTag, error)
- func (l *Library) ItemsByContentHash(ctx context.Context, hash string) ([]*model.ItemView, error)
- func (l *Library) ItemsByEssence(ctx context.Context, essence string) ([]*model.ItemView, error)
- func (l *Library) Job(ctx context.Context, pid model.PID) (*model.Job, error)
- func (l *Library) Jobs(ctx context.Context, limit int) ([]*model.Job, error)
- func (l *Library) Libraries(ctx context.Context) ([]*model.Library, error)
- func (l *Library) Lock(ctx context.Context, pid model.PID, fields ...string) error
- func (l *Library) Loudness(ctx context.Context, itemPID model.PID) (*model.Loudness, error)
- func (l *Library) Lyrics(ctx context.Context, pid model.PID) (*model.Lyrics, error)
- func (l *Library) Merge(ctx context.Context, entityType model.MergeEntity, ...) (*model.MergeReport, error)
- func (l *Library) MergeMany(ctx context.Context, entityType model.MergeEntity, survivorPID model.PID, ...) ([]*model.MergeReport, error)
- func (l *Library) OwnerInfo() (sqlite.OwnerInfo, error)
- func (l *Library) Peaks(ctx context.Context, itemPID model.PID) (*model.PeaksData, error)
- func (l *Library) PlanDelete(ctx context.Context, q query.Query, mode model.DeleteMode) (*trash.Plan, error)
- func (l *Library) PlanDeletePIDs(ctx context.Context, pids []model.PID, mode model.DeleteMode) (*trash.Plan, error)
- func (l *Library) PlanImport(ctx context.Context, req ImportRequest) (*inbox.Plan, error)
- func (l *Library) PlanOrganize(ctx context.Context, q query.Query, profileName string) (*organize.Plan, error)
- func (l *Library) PlayStatesForItems(ctx context.Context, itemPIDs []model.PID) (map[model.PID][]model.PlayState, error)
- func (l *Library) Playback() *playback.Service
- func (l *Library) Playlists() *playlist.Service
- func (l *Library) Podcasts() *podcast.Service
- func (l *Library) Profiles() []string
- func (l *Library) Provenance(ctx context.Context, pid model.PID) ([]model.FieldProvenance, error)
- func (l *Library) PruneChangeLog(ctx context.Context, keep int) (int, error)
- func (l *Library) PurgeTrash(ctx context.Context, trashPID model.PID) (int64, error)
- func (l *Library) Query(ctx context.Context, q query.Query, userPID model.PID) ([]*model.ItemView, error)
- func (l *Library) QueryPage(ctx context.Context, q query.Query, cursor read.Cursor, limit int, desc bool, ...) (*read.Page, error)
- func (l *Library) ReSealSecrets(ctx context.Context) (int, error)
- func (l *Library) ReadOnly() bool
- func (l *Library) RefreshAlbumGain(ctx context.Context) error
- func (l *Library) RefreshRollups(ctx context.Context) error
- func (l *Library) RelocateRoot(ctx context.Context, libPID model.PID, newRoot string) error
- func (l *Library) Reopen(ctx context.Context) error
- func (l *Library) ResolveArt(ctx context.Context, ref model.EntityRef, role model.ArtRole, size int) (*model.ArtBlob, error)
- func (l *Library) ResolvePlaylistRefs(ctx context.Context, refs []model.PortableRef) ([]model.RefResolution, error)
- func (l *Library) ResolveRef(ctx context.Context, ref model.PortableRef) (*model.ItemView, model.MatchRung, error)
- func (l *Library) RestoreTrash(ctx context.Context, trashPID model.PID) error
- func (l *Library) RotateSecrets(ctx context.Context, oldCipher, newCipher model.SecretCipher, newKeyID string) (int, error)
- func (l *Library) RunOrganize(ctx context.Context, q query.Query, profileName string) (model.PID, error)
- func (l *Library) Scan(ctx context.Context, req ScanRequest) (*ScanResult, error)
- func (l *Library) Search(ctx context.Context, q string, opt read.SearchOptions) (*read.SearchResult, error)
- func (l *Library) Serve(ctx context.Context, socketPath string) error
- func (l *Library) SetChapters(ctx context.Context, itemPID model.PID, chapters []model.Chapter, ...) error
- func (l *Library) SetCredits(ctx context.Context, itemPID model.PID, role model.ContributorRole, ...) (int, error)
- func (l *Library) SetEntityArt(ctx context.Context, entityType model.ArtEntity, entityPID model.PID, ...) error
- func (l *Library) SetEntityRating(ctx context.Context, userPID model.PID, kind model.MergeEntity, ...) error
- func (l *Library) SetEntityStar(ctx context.Context, userPID model.PID, kind model.MergeEntity, ...) error
- func (l *Library) SetItemArt(ctx context.Context, itemPID model.PID, role model.ArtRole, raw []byte, ...) error
- func (l *Library) SetItemTag(ctx context.Context, itemPID model.PID, key string, values []string, ...) (string, int, error)
- func (l *Library) SetLyrics(ctx context.Context, itemPID model.PID, ly *model.Lyrics, lock, force bool) error
- func (l *Library) SetSecret(ctx context.Context, key, value string) error
- func (l *Library) StarredEntities(ctx context.Context, userPID model.PID, kind model.MergeEntity) ([]model.EntityPlayState, error)
- func (l *Library) StartAnalyze(ctx context.Context, opts AnalyzeOptions) (model.PID, error)
- func (l *Library) StartEnrich(ctx context.Context, opts EnrichOptions) (model.PID, error)
- func (l *Library) StartScan(ctx context.Context, req ScanRequest) (model.PID, error)
- func (l *Library) Stats(ctx context.Context, userPID model.PID, topN int) (*read.Stats, error)
- func (l *Library) Subscribe() (<-chan model.Change, func())
- func (l *Library) TagKeys(ctx context.Context) ([]read.TagKeyCount, error)
- func (l *Library) Trash(ctx context.Context, includeRestored bool, limit int) ([]model.TrashEntry, error)
- func (l *Library) Unlock(ctx context.Context, pid model.PID, fields ...string) error
- func (l *Library) Users(ctx context.Context) ([]*model.User, error)
- func (l *Library) Vacuum(ctx context.Context) (*VacuumReport, error)
- func (l *Library) VerifyDerived(ctx context.Context) (*sqlite.DerivedReport, error)
- func (l *Library) Watch(ctx context.Context, opts WatchOptions) error
- func (l *Library) YearInReview(ctx context.Context, userPID model.PID, year, topN int) (*read.YearReview, error)
- type Options
- type ScanRequest
- type ScanResult
- type TagEditOptions
- type UpgradeCandidate
- type UpgradeGroup
- type VacuumReport
- type WatchActivity
- type WatchOptions
- type WriteBackError
- type WriteBackFailure
Constants ¶
const OrphanGraceWindow = 24 * time.Hour
OrphanGraceWindow is how long an entity must stay childless before the manual orphan GC sweeps it. It is the safety backstop to the scanner's survival gate: a transient reconciliation blip that briefly orphans an entity will not delete it unless it is still orphaned a full window (and a second manual run) later.
Variables ¶
This section is empty.
Functions ¶
func ReadLockOwner ¶
ReadLockOwner reads the write-owner metadata from a catalog's lockfile without opening the catalog. It is how a CLI discovers whether a server is running and on which socket, before deciding to proxy a mutation. It returns an error when the lockfile is absent or unreadable (no live owner).
Types ¶
type AcquiredFile ¶
type AcquiredFile struct {
Path string
}
AcquiredFile is a local media file to ingest as externally-acquired media (for example one a source provider already fetched to disk). Path is required for a track/book and optional for an episode (an episode may be ingested remote, to be downloaded later from meta.SourceURL).
type AcquiredMeta ¶
type AcquiredMeta struct {
// Origin provenance recorded in the acquisition table. SourceType defaults to
// manual when empty; an explicitly acquired item is never plain local. Local is
// the read-side default for an item with no acquisition row.
SourceType model.SourceType
SourceURL string
SourceID string
Provider string
ProviderVersion string
OptionsJSON string
// Track/book placement.
Profile string // organization profile override (empty = the target library's)
Copy bool // copy instead of move the source file
DupPolicy model.DupPolicy // catalog-duplicate policy (default skip)
// Episode ingest.
ShowPID model.PID // existing show to add the episode under; empty creates a manual show
ShowTitle string // manual show title when ShowPID is empty (default "Acquired")
Title string // episode title (default the file base name)
Pinned *bool // pinned episode; default true for acquired episodes
}
AcquiredMeta carries the origin provenance recorded against an acquired item plus the per-kind ingest options.
type AcquiredResult ¶
type AcquiredResult struct {
Kind model.Kind
Plan *inbox.Plan // track/book: review, then ApplyImport
EpisodePID model.PID // episode: the ingested episode
FilePID model.PID // episode: its attached file, when a local file was provided
Path string // episode: the placed file path, when attached
// AlreadyPresent reports that the acquired file's audio essence is already in the
// catalog, resolved independent of DupPolicy (so a DupAllow import of a genuine
// duplicate still reports it). AlreadyPresentPID names the existing local item when
// one resolves by essence. They let the host tell the user "already in your library"
// on a receive, e.g. "3 of 12 already present". Set for the track/book path only;
// the host can pre-check an episode via ResolveRef by essence. The action's Outcome
// separately conveys whether the file was skipped (DupSkip) or imported (DupAllow).
AlreadyPresent bool
AlreadyPresentPID model.PID
}
AcquiredResult reports an ImportAcquired. For a track/book it carries a reviewable import Plan (apply it with ApplyImport); for an episode the ingest is immediate and EpisodePID/FilePID/Path name the result.
type AltEncoding ¶
AltEncoding is one verified alternate encoding of a query item: a different file whose fingerprint matches above the similarity threshold.
type AnalyzeOptions ¶
type AnalyzeOptions struct {
// WriteReplayGainTags mirrors computed ReplayGain into files after aggregation,
// for this run. It is OR-ed with the library's configured toggle, so a run enables
// write-back if either the config or this flag asks for it.
WriteReplayGainTags bool
}
AnalyzeOptions controls one analyze run.
type AnalyzeResult ¶
AnalyzeResult reports an analyze run and the job it ran under.
type AuditOptions ¶
type AuditOptions struct {
// Only, when non-empty, restricts the run to these checks.
Only []model.AuditCheck
// Integrity re-reads every audio file to detect bitrot, missing files, and
// corrupt audio. Off by default (I/O heavy).
Integrity bool
// Sample caps the per-check finding sample (0 uses a default).
Sample int
}
AuditOptions selects which audit checks run.
type BatchEditResult ¶
type BatchEditResult struct {
Edited []model.PID
Skipped []model.PID
WriteBackErrors map[model.PID]*WriteBackError
}
BatchEditResult reports a multi-item edit's outcome: the items whose catalog edit applied, the items skipped because a target field was locked (skip-locked mode), and, for a write-back batch, the per-item on-disk write-back failures. The catalog edit is atomic; a WriteBackErrors entry means that item's catalog edit stands but its file tags did not follow.
type CreditEditOptions ¶
type CreditEditOptions struct {
// WriteBack also writes the role's names into each backing file's on-disk tag: a
// track's music role, or a book's author (ALBUMARTIST) / narrator (NARRATOR+COMPOSER).
// A book translator/editor credit has no round-trippable tag and is refused.
WriteBack bool
// Lock locks the credit.<role> field against enrichment/organize; on by default.
Lock bool
// Force overrides a locked credit role.
Force bool
}
CreditEditOptions configures a credit edit, mirroring EditOptions.
type DoctorReport ¶
type DoctorReport struct {
DBPath string
// SchemaVersion is the catalog's actual applied version; BuildSchemaVersion is
// what this binary supports. They differ when a catalog has not yet been
// migrated by a read-write command (doctor itself never migrates).
SchemaVersion int
BuildSchemaVersion int
ReadOnly bool
Owner sqlite.OwnerInfo
LibraryCount int
ItemCount int
FingerprintCount int
LoudnessCount int // files with a stored ReplayGain measurement
PodcastCount int // subscribed feeds
// Enrichment coverage: entities looked up, and how many a provider matched.
EnrichedEntities int
EnrichedMatched int
// Diagnostic coverage: how many persisted file diagnostics exist, and how many
// audio files have not had diagnostics derived under the current rule set. A
// non-zero DiagnosticsStale is why "no diagnostics" must not be read as "clean".
DiagnosticCount int
DiagnosticsStale int
// EnrichmentEnabled reports whether a MusicBrainz contact is configured.
EnrichmentEnabled bool
// Fpcalc is the sole remaining optional helper (Chromaprint for AcoustID); it is
// never required for core use. Decoding is pure-Go via WaxFlow, so there is no
// ffmpeg capability to report. Coverage below is the honest "what can this build
// decode" answer.
Fpcalc bool
// Coverage reports, per codec, how the analyze pass decodes it in this build.
Coverage []decode.FormatSupport
}
DoctorReport summarizes catalog health and detected capabilities.
func (*DoctorReport) NeedsMigration ¶
func (r *DoctorReport) NeedsMigration() bool
NeedsMigration reports whether the catalog is behind the build (a read-write command would upgrade it).
type EditOptions ¶
type EditOptions struct {
// WriteBack also writes the new value into each backing file's on-disk tags. It is
// off by default, so an edit is catalog-only unless the caller opts in.
WriteBack bool
// Lock locks each edited field against enrichment and organize overwrites. A user
// edit is authoritative, so the CLI sets this by default. Pass false to leave the
// field unlocked.
Lock bool
// Force overrides a lock. Without it, editing a locked field returns CodeLocked.
Force bool
// SkipLocked applies only to a multi-item batch: a locked item is skipped and
// reported rather than failing the whole batch. Ignored by a single-item edit.
SkipLocked bool
}
EditOptions controls a catalog field edit.
type EmptyReport ¶
EmptyReport summarizes an empty-trash pass.
type EmptyTrashOptions ¶
type EmptyTrashOptions struct {
// OlderThan keeps recently-trashed files: only entries trashed strictly
// before now-OlderThan are purged, so an undo window survives a routine
// space-reclaim pass. Zero purges everything; negative is refused.
OlderThan time.Duration
}
EmptyTrashOptions scopes an empty-trash pass.
type EnrichOptions ¶
type EnrichOptions struct {
Force bool // re-enrich already-enriched entities
Limit int // cap on entities processed (0 = all needing enrichment)
ItemPID model.PID // scope to one item's targets ("" = no item scope)
EntityType read.EntityKind // with EntityPID: scope to one entity
EntityPID model.PID
}
EnrichOptions controls a metadata enrichment run. ItemPID or EntityType+ EntityPID (mutually exclusive) scope the pass to one item's or entity's enrichable targets: a track scopes to its artist, album artist, release group, and lyrics; a book to its contributors and identifier fill; an entity scope takes artist, release_group, or album (an album resolves to its parent release group). A scoped run implies Force and runs the full pipeline for its targets, provenance, markers, and change deltas included.
type EnrichResult ¶
EnrichResult reports an enrichment run and the job it ran under.
type EntityEditOptions ¶
type EntityEditOptions struct {
// WriteBack also fans the edited identifiers and sort across the entity's member
// files' on-disk tags: an album's BARCODE, LABEL, CATALOGNUMBER, and ALBUMSORT, and an
// artist's ARTISTSORT. A release-group field, a release-group type, and an entity MBID
// have no fanned tag and stay DB-only.
WriteBack bool
// Lock locks each edited entity field against enrichment overwrites; on by default.
Lock bool
// Force overrides a locked entity field.
Force bool
}
EntityEditOptions controls an entity field edit, mirroring EditOptions.
type ImportRequest ¶
type ImportRequest struct {
Source string // staging folder to import (required)
LibraryPID model.PID // target managed library; empty uses the single managed one
Profile string // layout; empty uses the library's configured profile
DupPolicy model.DupPolicy // how to treat catalog duplicates (default skip)
Copy bool // copy (keep originals) instead of move
}
ImportRequest selects a staging folder and how to import it.
type Library ¶
type Library struct {
// contains filtered or unexported fields
}
Library is the public handle to a WaxBin catalog. It is safe for concurrent use. A read-only Library refuses mutating operations.
func Open ¶
Open opens (creating if needed) the catalog and wires the subsystems. A read-write open acquires the write lock, migrates, reclaims orphaned jobs, and ensures the configured roots; a read-only open does none of those.
func (*Library) Acquisition ¶
Acquisition returns an item's origin provenance, or CodeNotFound when it was locally scanned (no acquisition row).
func (*Library) AddRoot ¶
AddRoot registers a library root at runtime, without reopening the Library. The spec is validated against every root already registered (plus the configured inbox folders and podcast download dir), exactly as Open validates the configured set, then upserted: a new path emits a `library` create delta, and re-adding an existing path refreshes its mode/media/profile under the same pid. Spec defaults match config loading (in-place, mixed, waxbin-native).
The store is the single source of truth for roots. Scan, organize, and import resolve libraries from store rows, so they pick the new root up immediately, and the row survives a process restart even if the embedder never adds the root to its own configuration: a later Open validates only its configured roots against each other and re-upserts them, never pruning rows absent from the config. A running Watch does NOT pick the root up, because watch snapshots its roots at start; restart the watcher after adding one. The create delta on the change feed is the machine-readable signal for that.
func (*Library) Analyze ¶
func (l *Library) Analyze(ctx context.Context, opts AnalyzeOptions) (*AnalyzeResult, error)
Analyze runs the resumable analyze pass: it decodes (the only PCM-decoding stage), fingerprints, and indexes every audio file whose fingerprint is missing or stale, under an "analyze"-scoped job. Files whose codec this build cannot decode are reported as skipped, not failed.
func (*Library) ApplyDelete ¶
ApplyDelete executes a deletion plan under a "delete"-scoped job.
func (*Library) ApplyImport ¶
ApplyImport executes an import plan under an "import"-scoped job.
func (*Library) ApplyOrganize ¶
ApplyOrganize executes a plan under an "organize"-scoped job.
func (*Library) ArtRoles ¶
ArtRoles lists the artwork slots an entity holds at its own level (no chain fallback): each stored role with the source image's format, dimensions, and content hash. An entity with no art returns an empty list.
func (*Library) Audit ¶
Audit runs the quality/integrity checks and returns their findings. It is read-only. The default run covers the catalog checks (duplicates, split albums, inconsistent metadata, missing art/ReplayGain, bad filenames, orphan sidecars, path conflicts, invalid feeds, derived-data drift); Integrity adds the on-disk bitrot and corrupt-audio passes.
func (*Library) Backup ¶
Backup writes a self-contained byte copy of the catalog to dest. The copy contains every table, including the secret table; with redact, secrets are stripped from the copy while the live catalog is untouched. A full backup is the disaster-recovery artifact.
func (*Library) BeginMaintenance ¶
BeginMaintenance suspends the Library and releases the write lock so a foreground process can take it. It implements proxy.Maintainer; the Library object is kept so EndMaintenance can restore it in place.
It refuses while a background job is running: server-run scan/analyze/enrich/ organize passes run in this process, and closing the store out from under one would abort it partway. The foreground caller gets a CodeConflict and should retry once the job completes. Unlike Close, it suspends (keeping in-process change subscribers alive) so an embedder's subscription survives the hand-off.
func (*Library) Book ¶
Book returns the full detail for an audiobook: subtitle, series placement, role-tagged contributors (author/narrator/...), backing parts in reading order, and chapters resolved to book-timeline offsets with the total duration. CodeInvalid when pid is not a book.
func (*Library) BookResume ¶
func (l *Library) BookResume(ctx context.Context, userPID, bookPID model.PID) (*model.PlayState, *model.Chapter, error)
BookResume returns a user's play state for a book together with the chapter their resume position falls in, the chapter-level resume answer. An empty userPID selects the default user.
func (*Library) BooksInSeries ¶
func (l *Library) BooksInSeries(ctx context.Context, seriesPID model.PID) ([]*model.ItemView, error)
BooksInSeries lists a series' books in sequence order (decimal/string aware).
func (*Library) Browse ¶
func (l *Library) Browse(ctx context.Context, list read.DiscoveryList, opt read.BrowseOptions) (*read.Page, error)
Browse returns one keyset-paginated window for a discovery list such as newest, recently-added, most-played, recently-played, random, starred, by-year, by-genre, or alphabetical. Play-derived lists read opt.UserPID's play_state (empty selects the default user). The by-year, by-genre, and random lists use opt.Year, opt.GenrePID, and opt.Seed respectively. Pagination is stable under concurrent mutation.
func (*Library) Chapters ¶
Chapters returns a book's chapters in book-timeline order. CodeInvalid when pid is not a book.
func (*Library) Close ¶
Close flushes buffered playback progress, then releases the catalog and write lock. The flush is best effort: shutdown should save resume positions, but a flush error should not block release. Read-only handles skip the flush.
It first drains any in-flight server-run jobs so they finalize against the still-open store; otherwise a shutdown mid-scan would leave a job row stuck "running" (reclaimed as crashed only on a later open). The jobs run under a server's lifetime context, so a shutdown that cancels that context makes them return promptly.
func (*Library) Count ¶
Count returns the number of items matching q. userPID scopes any per-user field the same way Query does.
func (*Library) Coverage ¶
func (l *Library) Coverage() []decode.FormatSupport
Coverage reports per-codec analysis decode support for doctor.
func (*Library) CreateUser ¶
CreateUser adds a playback user.
func (*Library) CurrentChapter ¶
func (l *Library) CurrentChapter(ctx context.Context, pid model.PID, positionMS int64) (*model.Chapter, error)
CurrentChapter resolves the chapter a resume position falls in (the nearest preceding chapter when between spans). It returns nil when the book has no chapters.
func (*Library) DataVersion ¶
DataVersion returns SQLite's data_version, which moves whenever any connection commits. A consumer in another process polls it and pulls Changes when it changes.
func (*Library) DefaultUser ¶
DefaultUser returns the seeded default user.
func (*Library) DeleteSecret ¶
DeleteSecret removes a stored credential.
func (*Library) DiagnosticSummary ¶
func (l *Library) DiagnosticSummary(ctx context.Context, filter model.DiagnosticFilter) ([]model.DiagnosticCount, error)
DiagnosticSummary returns the matching diagnostics grouped by writer, code, and severity, most severe first. The filter's dimensions apply; its Limit and Offset do not (a summary aggregates the whole match).
func (*Library) Doctor ¶
func (l *Library) Doctor(ctx context.Context) (*DoctorReport, error)
Doctor reports catalog stats and capability coverage. It is read-only: it never migrates, and it reports the catalog's applied schema version next to the build's so an operator can see when a read-write command would upgrade.
func (*Library) EditEntity ¶
func (l *Library) EditEntity(ctx context.Context, entityType model.MergeEntity, entityPID model.PID, edits map[string]string, opts EntityEditOptions) error
EditEntity applies curation edits to one shared entity (an artist, release group, or album): sort-name overrides and release identifiers (barcode/label/catalog number and the entity MBIDs, plus the release-group type). It records user provenance and, by default, locks each edited field so enrichment leaves it alone. The catalog write is atomic. A field that does not apply to the entity type, or an invalid value, is rejected; a locked field returns CodeLocked unless opts.Force is set.
With opts.WriteBack the edited values that round-trip through a rescan are also fanned out across the entity's member files' on-disk tags. Write-back runs after the catalog edit committed, so a file that cannot be written is reported through a *WriteBackError naming the failed files while the entity edit stands.
func (*Library) EditField ¶
func (l *Library) EditField(ctx context.Context, itemPID model.PID, field, value string, opts EditOptions) error
EditField edits one metadata field on a track or book item. See EditFields.
func (*Library) EditFields ¶
func (l *Library) EditFields(ctx context.Context, itemPID model.PID, edits map[string]string, opts EditOptions) error
EditFields applies metadata-field edits to a track or book item. It records the edit as user provenance and, unless told otherwise, locks each field so enrichment and organize leave it alone. The catalog write is atomic and runs first. Which fields are editable depends on the kind (a track has artist, album, and the rest; a book has author, narrator, series), and a field that does not apply to the item's kind is rejected.
With opts.WriteBack set, the new values are also written into the backing files' on-disk tags through the WaxLabel writer seam. A track writes every edited scalar to its file; a book writes the audiobook tags a scan reads back (title→ALBUM, author→ALBUMARTIST, author_sort→ALBUMARTISTSORT, narrator→NARRATOR+COMPOSER, series+sequence→GROUPING, genre/year) across all its parts, leaving the enrichment-only book fields (subtitle, identifiers, publisher, description) DB-only. A file that cannot be written is reported through a *WriteBackError while the catalog edit stands.
Write-back is a separate step, not part of the atomic catalog edit. The edit has already committed by the time it runs, so a file that cannot be written does not roll the edit back. That covers a read-only mount, a permission error, a value the format cannot store, and a file shared by several items whose tags must not be clobbered per item. In those cases EditFields returns a *WriteBackError naming the failed files and records a per-file drift diagnostic, while the catalog edit stands. Callers should surface that as "catalog updated, on-disk tag sync failed" rather than a failed edit.
func (*Library) EditItemsFields ¶
func (l *Library) EditItemsFields(ctx context.Context, edits []model.ItemFieldEdit, opts EditOptions) (*BatchEditResult, error)
EditItemsFields applies a per-item field-edit map to several items in one atomic transaction: each entry carries its own values (distinct titles and track numbers across an album, say) where EditManyFields applies one map to every item. The whole catalog batch commits or rolls back together; a duplicate pid or any invalid entry rejects it. Write-back then mirrors each edited item's own map into its on-disk tags, per item and best-effort, exactly as EditManyFields does; see there for the error contract (the returned result stays meaningful when err reports a post-commit write-back interruption).
func (*Library) EditManyFields ¶
func (l *Library) EditManyFields(ctx context.Context, itemPIDs []model.PID, edits map[string]string, opts EditOptions) (*BatchEditResult, error)
EditManyFields applies the same field edits to several items in one atomic transaction, then optionally mirrors each edited item's new values into its on-disk tags. The catalog edit commits or rolls back as a whole. Write-back is per-item and best-effort: an item whose on-disk sync failed is recorded in the result's WriteBackErrors rather than failing the batch, matching single-item semantics. With opts.SkipLocked a locked item is skipped (reported in Skipped) instead of failing.
The returned *BatchEditResult is non-nil and meaningful even when err is non-nil: the catalog batch already committed, and err is only returned for a non-write-back failure during the on-disk pass (such as a canceled context). Callers should inspect the result's Edited list in that case, since those items were edited.
func (*Library) EmptyTrash ¶
func (l *Library) EmptyTrash(ctx context.Context, opts EmptyTrashOptions) (*EmptyReport, error)
EmptyTrash permanently removes active trashed files from disk and drops their journal rows, reclaiming space. Options.OlderThan narrows the pass to entries older than that window. It runs under a "delete"-scoped job.
func (*Library) EndMaintenance ¶
EndMaintenance reopens the Library after a maintenance hand-off. It implements proxy.Maintainer.
func (*Library) Enrich ¶
func (l *Library) Enrich(ctx context.Context, opts EnrichOptions) (*EnrichResult, error)
Enrich runs the metadata enrichment pass under an "enrich"-scoped job: MusicBrainz release-group/artist/genre resolution (MBID-first), Cover Art Archive covers, and the optional AcoustID fingerprint fallback. It is resumable and lock-respecting (never overwriting a tagged or user-locked field), caches provider responses, and degrades gracefully offline. Enrichment requires a MusicBrainz contact (Options.Enrichment.Contact); without one it returns CodeUnsupported. A scoped run (see EnrichOptions) holds the same "enrich" job lease as a full pass, so the two exclude each other (CodeConflict while one runs).
func (*Library) EnrichmentCoverage ¶
EnrichmentCoverage reports how many entities have been enriched, for doctor.
func (*Library) EntityByPID ¶
func (l *Library) EntityByPID(ctx context.Context, kind read.EntityKind, pid model.PID) (*read.EntityInfo, error)
EntityByPID returns the summary info for one shared entity (artist, release group, album, genre, or series) by its public id: name, sort key, external identifiers, parent links, membership counts, and the libraries its members' files live in. It answers the pid a facet bucket, an entity-handle query field, or an item view hands out, without a facet scan. An unknown kind is CodeInvalid; an unknown pid is CodeNotFound.
func (*Library) EntityByPIDs ¶
func (l *Library) EntityByPIDs(ctx context.Context, kind read.EntityKind, pids []model.PID) (map[model.PID]*read.EntityInfo, error)
EntityByPIDs is the batched form of EntityByPID: it returns summary info for many entities of one kind keyed by pid, omitting unknown pids and collapsing a repeated pid. It retires the per-hit EntityByPID cost when a consumer hydrates a page of entity pids (a restricted-user entity search that scopes each hit to the user's libraries, for instance). The caller batches within a single kind; a set larger than one internal chunk is not an atomic snapshot across chunks.
func (*Library) EntityCuration ¶
func (l *Library) EntityCuration(ctx context.Context, entityType model.MergeEntity, entityPID model.PID) ([]model.EntityCuration, error)
EntityCuration returns an entity's curation rows (only non-default fields have rows, so an un-curated entity returns an empty slice).
func (*Library) EntityPlayState ¶
func (l *Library) EntityPlayState(ctx context.Context, userPID model.PID, kind model.MergeEntity, entityPID model.PID) (*model.EntityPlayState, error)
EntityPlayState returns a user's star/rating state for one catalog entity. An unknown user or entity pid is CodeNotFound (like PlayStateFor); an entity the user has never starred or rated reads back zero-valued, not an error.
func (*Library) Export ¶
Export writes a versioned logical JSON export of catalog metadata plus critical per-user playback state. It never contains secrets and is for inspection and cross-tool portability; a byte Backup is the disaster-recovery path. It returns the export manifest.
func (*Library) ExportPlaylistRefs ¶
func (l *Library) ExportPlaylistRefs(ctx context.Context, playlistPID, userPID model.PID) ([]model.PortableRef, error)
ExportPlaylistRefs returns portable identity descriptors for a playlist's members in order (a static list's stored order, a smart list evaluated for userPID). A playlist of local PIDs cannot cross catalogs, so the host ships these refs and rebuilds the list on the far side via ResolvePlaylistRefs. An empty userPID selects the default user.
func (*Library) Facet ¶
func (l *Library) Facet(ctx context.Context, q query.Query, g read.GroupBy, userPID model.PID) (*read.FacetResult, error)
Facet groups the items matching q by a dimension and counts each bucket. The CLI, OpenSubsonic adapters, and stats code use this same API, so they share one canonical grouping result. userPID scopes any per-user filter in q.
func (*Library) File ¶
File returns a backing file's identity and quality by its public id: size, mtime, content and essence hashes, the analyzed-essence stamp, and the audio quality fields (codec, bitrate, sample rate, bit depth), or CodeNotFound. It is the file-level companion to Get, so an embedder can pin an item's file identity without re-scanning.
func (*Library) FileDiagnostics ¶
func (l *Library) FileDiagnostics(ctx context.Context, filter model.DiagnosticFilter) ([]model.FileDiagnostic, error)
FileDiagnostics returns the persisted per-file diagnostics matching the filter (the zero filter returns everything), each joined to its file's display path, in a stable path/origin/code order. It is the query surface over the rows scan, organize, analyze, and edit write-back record, so a consumer can drive a review queue ("which files have unsynced tags in this library") without running a full audit.
func (*Library) FindAltEncodings ¶
FindAltEncodings returns other catalog items that are alt encodings of the given item: the inverted index proposes candidates (shared terms within the duration bucket), then each is verified by full-fingerprint similarity. The item must already be analyzed; an unanalyzed item yields no matches.
func (*Library) FindUpgrades ¶
func (l *Library) FindUpgrades(ctx context.Context) ([]UpgradeGroup, error)
FindUpgrades groups the catalog's alt encodings (the same recording in different files) and ranks each group by audio quality, so a consumer can keep the best and prune or upgrade the rest. Grouping uses the fingerprint index via FindAltEncodings. MBID- and essence-identical encodings already collapse to one item during scan, so this surfaces the cross-encoding case. Items must be analyzed (fingerprinted); unanalyzed items never group.
It is a maintenance scan: it walks every track item and probes the fingerprint index for each, so it is meant for occasional use, not the hot path.
func (*Library) GCArt ¶
GCArt reclaims orphaned art: map rows whose entity is gone, then source images without live map references and their cached thumbnails. It returns the source and thumbnail counts removed. It is the repair for the orphan counts VerifyDerived reports.
func (*Library) GCOrphans ¶
GCOrphans deletes childless artist/release_group/album/genre/series rows that have stayed orphaned past the grace window, recording the rest for a later sweep. It is manual-only (invoked by Vacuum and db verify --fix), never the watch loop.
func (*Library) GetMany ¶
GetMany returns item views for the given pids in input order, skipping any pid with no matching item and collapsing a duplicate pid to its first position.
It is NOT an atomic snapshot: a pid array longer than the internal batch size is read across multiple SELECTs, so a concurrent write between batches can yield a mixed view. A UI list can feed from it; a caller that needs a consistent view of every pid at one instant cannot.
func (*Library) ImportAcquired ¶
func (l *Library) ImportAcquired(ctx context.Context, file AcquiredFile, kind model.Kind, meta AcquiredMeta) (*AcquiredResult, error)
ImportAcquired routes an acquired or manual file by kind. Tracks and books go through the import planner for the matching managed library, including duplicate checks, destination rendering, free-space checks, and acquisition provenance. Episodes go into the internal podcast library under an existing or manual show and are pinned by default. WaxBin never performs platform extraction itself; callers hand it an already acquired file or a remote enclosure URL.
func (*Library) ImportBatches ¶
ImportBatches lists recorded import batches, newest first (limit 0 = all).
func (*Library) InboxFolders ¶
InboxFolders returns the configured staging folders.
func (*Library) IntegrityCheck ¶
IntegrityCheck runs SQLite's PRAGMA integrity_check and returns the problems it reports (a healthy database returns a single "ok"). It is read-only.
func (*Library) ItemTags ¶
ItemTags returns an item's custom tags (the non-standard frames WaxBin's model does not map, plus user-set tags), grouped by key.
func (*Library) ItemsByContentHash ¶
ItemsByContentHash returns every item backed by a file with the given whole-file content hash (identity.ContentHash, "sha256:" plus hex). Unlike the essence hash it changes on any byte change, tag writes included, which makes it the pre-transfer byte-identity probe: "do I already hold these exact bytes". Same result shape as ItemsByEssence.
func (*Library) ItemsByEssence ¶
ItemsByEssence returns every item backed by a file with the given audio essence hash, matching any of an item's files. The essence hash is tag-stable (it survives a retag), which makes it the dedup oracle: "do I already hold this audio". The result is 0, 1, or N items; a single-file CUE album returns one item per virtual track carved from the shared file. A clean miss is an empty slice, not an error.
func (*Library) Job ¶
Job returns a job by public id, the read a client uses to tail a server-run job it submitted: progress and status while running, the terminal state, and the JSON Result summary once done.
func (*Library) Lock ¶
Lock marks item fields as protected from enrichment and organize tag write-back. Unknown fields are rejected.
func (*Library) Loudness ¶
Loudness returns an item's measured ReplayGain (track and album gain/peak), or CodeNotFound when it has not been analyzed for loudness.
func (*Library) Lyrics ¶
Lyrics returns an item's structured lyrics (synced timed lines and/or an unsynchronized block), or CodeNotFound when it has none. Lyrics come from a sibling .lrc sidecar or embedded USLT/SYLT tags, captured at scan time; the catalog row is authoritative for reads.
func (*Library) Merge ¶
func (l *Library) Merge(ctx context.Context, entityType model.MergeEntity, survivorPID, loserPID model.PID) (*model.MergeReport, error)
Merge collapses the loser entity onto the survivor: children (tracks, albums, genre links, contributor credits) are re-pointed onto the survivor, its MBID and enrichment marker are unioned when it lacks one, rollups are recomputed, and the loser is deleted. The survivor keeps its PID. This is the first-class entity-merge primitive for artists, release groups, albums, and genres. It repairs the duplicate-entity findings audit reports, and is the seam late enrichment uses to unify two heuristic rows that resolve to one MBID.
func (*Library) MergeMany ¶
func (l *Library) MergeMany(ctx context.Context, entityType model.MergeEntity, survivorPID model.PID, loserPIDs []model.PID) ([]*model.MergeReport, error)
MergeMany collapses several loser entities onto the survivor in one atomic transaction: if any loser fails (e.g. an unknown PID), the whole batch rolls back, so a partial merge can never be left behind. Returns one report per loser.
func (*Library) PlanDelete ¶
func (l *Library) PlanDelete(ctx context.Context, q query.Query, mode model.DeleteMode) (*trash.Plan, error)
PlanDelete computes a dry-run deletion plan for the items matching q under a mode (trash|prune|permanent). DeleteTrash moves files to the reversible per-library trash; the other modes bypass it to reclaim space. Every mode keeps the logical item (archived when it loses its last file).
func (*Library) PlanDeletePIDs ¶
func (l *Library) PlanDeletePIDs(ctx context.Context, pids []model.PID, mode model.DeleteMode) (*trash.Plan, error)
PlanDeletePIDs computes a deletion plan for explicit item pids. It is the `rm <pid>` path; PlanDelete is the query-driven path used by retention/dedup.
func (*Library) PlanImport ¶
PlanImport computes a reviewable import plan for a staging folder: which files would be imported (with destinations), which are catalog duplicates, and which are quarantined. It is read-only.
func (*Library) PlanOrganize ¶
func (l *Library) PlanOrganize(ctx context.Context, q query.Query, profileName string) (*organize.Plan, error)
PlanOrganize computes a dry-run move plan for the selected items across every managed library, routing each item to the library whose root already contains it. Roots are non-overlapping, so kind routing is implicit in the current file path. A single managed library behaves exactly as before. profileName overrides each library's configured profile when non-empty.
func (*Library) PlayStatesForItems ¶
func (l *Library) PlayStatesForItems(ctx context.Context, itemPIDs []model.PID) (map[model.PID][]model.PlayState, error)
PlayStatesForItems returns every user's playback state for each of the given items, keyed by item pid, each item's states ordered by user pid. Untouched and unknown items are absent. It goes through the playback service, so this process's buffered (not yet flushed) resume positions are overlaid; a reader in another process sees flushed state only, and a buffered-but-unflushed pair may synthesize a position-only state (see playback.Service.StatesForItems for the exact contract). It is the bulk "is anyone using these" read a multi-user consumer runs before dropping items.
func (*Library) Playback ¶
Playback returns the playback-state service for progress, played status, ratings, stars, bookmarks, queue, and play sessions.
func (*Library) Playlists ¶
Playlists returns the playlist service for static and smart playlists plus M3U8 import/export.
func (*Library) Podcasts ¶
Podcasts returns the podcast service: subscribe/sync feeds, download episodes, store transcripts/artwork, OPML import/export, and retention.
func (*Library) Profiles ¶
Profiles lists the organization profile names available to this library (built-ins plus any configured custom profiles), sorted.
func (*Library) Provenance ¶
Provenance returns an item's field provenance. Only non-default fields have rows, so a tag-only item returns an empty slice.
func (*Library) PruneChangeLog ¶
PruneChangeLog trims the change_log to its newest keep rows, returning how many were deleted. A consumer that has fallen behind the retained horizon must full-resync (the documented delta-sync contract).
func (*Library) PurgeTrash ¶
PurgeTrash permanently removes a single active trash entry (file and journal row), returning the bytes reclaimed. A pid that is unknown, already purged, or already restored is CodeNotFound. Like EmptyTrash it runs under an fs-mutate job, one lease per call; the entry is resolved inside the lease, so a restore racing in (it holds the same scope) cannot slip between the check and the purge and lose its journal row.
func (*Library) Query ¶
func (l *Library) Query(ctx context.Context, q query.Query, userPID model.PID) ([]*model.ItemView, error)
Query runs a compiled selection and returns matching item views. A query that references a per-user field (starred, rating, play_count, played, finished, or last_played) evaluates against userPID's play_state. An empty userPID selects the default user. A query with no user-state field is not scoped by user.
func (*Library) QueryPage ¶
func (l *Library) QueryPage(ctx context.Context, q query.Query, cursor read.Cursor, limit int, desc bool, userPID model.PID) (*read.Page, error)
QueryPage returns one keyset-paginated, collation-correct window of items. Pass an empty cursor for the first page and the returned Next cursor for each subsequent page; pagination is stable under concurrent mutation. userPID scopes any per-user filter in q.
func (*Library) ReSealSecrets ¶
ReSealSecrets seals every plaintext secret with the configured cipher, leaving already-sealed values untouched, in one transaction. It is the one-time adoption step after a secret cipher is first configured and is idempotent. It requires a configured cipher (Options.SecretCipher). Returns the number of secrets sealed.
func (*Library) RefreshAlbumGain ¶
RefreshAlbumGain recomputes album-aware ReplayGain from the per-file loudness. Analyze runs it automatically; it is exposed for repair after a manual import.
func (*Library) RefreshRollups ¶
RefreshRollups recomputes the maintained rollups, the repair for the rollup drift that VerifyDerived can report.
func (*Library) RelocateRoot ¶
RelocateRoot re-points a library and every file under it at a new root path, for a portable restore onto a different machine or mount. The new path is validated against the other registered roots, the inbox folders, and the podcast download dir with the moved library's entry substituted, so a relocation cannot create the overlap Open would refuse. The internal podcast library is not relocatable: its root follows the podcasts dir config.
func (*Library) Reopen ¶
Reopen restores a Library that was Closed for a maintenance-mode hand-off. It reopens the store in place (every subsystem keeps its store handle) and re-ensures the configured roots, mirroring a read-write Open. It refuses on a read-only library.
func (*Library) ResolveArt ¶
func (l *Library) ResolveArt(ctx context.Context, ref model.EntityRef, role model.ArtRole, size int) (*model.ArtBlob, error)
ResolveArt resolves artwork for an entity in one role (empty = front). The front cover walks the fallback chain (track -> album -> release_group -> artist -> genre) to the first level that has one; every other role (back, disc, booklet, background) resolves at the requested level only, since an ancestor's auxiliary image would be misleading. A non-positive size returns the original image; a positive size returns a thumbnail scaled to fit a square box with that maximum side (generated once and cached). The blob reports the answering Level and whether an album's answer was Derived from a member track's cover. CodeNotFound means no consulted level has art in that role.
func (*Library) ResolvePlaylistRefs ¶
func (l *Library) ResolvePlaylistRefs(ctx context.Context, refs []model.PortableRef) ([]model.RefResolution, error)
ResolvePlaylistRefs resolves a batch of portable refs to local items, preserving input order and reporting each entry's matched rung (MatchNone for a miss). The host derives the resolved and missing sets and rebuilds a local playlist from the resolved PIDs with the existing Playlists() primitives.
func (*Library) ResolveRef ¶
func (l *Library) ResolveRef(ctx context.Context, ref model.PortableRef) (*model.ItemView, model.MatchRung, error)
ResolveRef walks the match ladder from most to least confident and returns the local item a portable ref names, along with the rung that matched so the host can report how sure the match is. The rungs are essence (exact bytes), strong id (an exact external identifier), fingerprint (the same recording in another encoding), then descriptive (fuzzy metadata). A clean miss returns (nil, MatchNone, nil); only a real IO failure returns an error. Every rung treats an empty or not-found result as a fall-through to the next. The strong-id and descriptive rungs dispatch on ref.Kind; essence and fingerprint do not care about kind.
func (*Library) RestoreTrash ¶
RestoreTrash undoes a delete: it moves the trashed file back to its original path and re-scans it so the catalog re-links it (un-archiving its item). It refuses if the original path is occupied.
func (*Library) RotateSecrets ¶
func (l *Library) RotateSecrets(ctx context.Context, oldCipher, newCipher model.SecretCipher, newKeyID string) (int, error)
RotateSecrets re-seals every secret from oldCipher to newCipher under newKeyID in one transaction, so a crash rolls the whole rotation back rather than leaving a mix of key generations. After it succeeds the caller reopens the Library with newCipher as Options.SecretCipher. Returns the number of secrets rotated.
func (*Library) RunOrganize ¶
func (l *Library) RunOrganize(ctx context.Context, q query.Query, profileName string) (model.PID, error)
RunOrganize submits an organize pass as a background job and returns its PID. The job plans across the managed libraries and executes the moves in this process, so a server stays available while it runs. profileName overrides each library's configured profile when non-empty. The client tails the job and reads the organize.Report summary from Result.
func (*Library) Scan ¶
func (l *Library) Scan(ctx context.Context, req ScanRequest) (*ScanResult, error)
Scan indexes the selected libraries under a single "scan"-scoped job.
Rollups are maintained transactionally for the entities touched by each scanned track, so no whole-catalog refresh is needed here; RefreshRollups is the repair path for drift reported by `db verify`. StartScan is the asynchronous variant a server exposes so a CLI can submit a scan without pausing it.
func (*Library) Search ¶
func (l *Library) Search(ctx context.Context, q string, opt read.SearchOptions) (*read.SearchResult, error)
Search runs a grouped, BM25-ranked search across artists, albums, tracks, books, and episodes (episode transcript-body hits rank below metadata hits). Field weighting puts title hits above artist and album hits. opt.MaxCandidates bounds how many matches are ranked (recency-biased under truncation) and opt.Libraries scopes the search to items playable from those libraries; see read.SearchOptions for both contracts.
func (*Library) Serve ¶
Serve runs the local control server: it listens on the unix socket at socketPath and dispatches proxied catalog mutations (and the reads a mutating CLI command needs for its confirmation output) to this Library, plus the maintenance-mode hand-off. It blocks until ctx is canceled, then returns nil.
The socket is created owner-only (0600); the endpoint drives admin mutations, so a broader mode would let any local user issue unauthenticated writes. socketPath should match the Options.IPCSocket advertised in the lockfile so a CLI can discover it. Serve refuses on a read-only library.
func (*Library) SetChapters ¶
func (l *Library) SetChapters(ctx context.Context, itemPID model.PID, chapters []model.Chapter, lock, force bool) error
SetChapters replaces a book's user-curated chapters (which win on read over the scanned ones), locking the "chapters" field by default. An empty list clears them. The input is a flat book-timeline list (StartMS/EndMS, what Chapters returns); for a multi-file book the store splits it across the parts.
func (*Library) SetCredits ¶
func (l *Library) SetCredits(ctx context.Context, itemPID model.PID, role model.ContributorRole, names []string, opts CreditEditOptions) (int, error)
SetCredits replaces the contributors of one role on an item (music roles on a track, book roles on a book), recording user provenance and, by default, locking the credit.<role> field. With opts.WriteBack it also mirrors the credit into the backing file's on-disk tag: a track's music role, or a book's author/narrator across its parts. A book translator/editor credit has no round-trippable tag and is refused (returned as a *WriteBackError) while the catalog edit stands. It returns the number of contributor names actually stored (after trimming blanks and de-duplicating by artist), so a caller does not report a wipe (an unresolvable name that cleared the role) as a set.
func (*Library) SetEntityArt ¶
func (l *Library) SetEntityArt(ctx context.Context, entityType model.ArtEntity, entityPID model.PID, role model.ArtRole, raw []byte, writeBack bool) error
SetEntityArt sets a durable image on a non-item entity (album, artist, release group, genre, or podcast) under one role (empty = front; the closed model.ArtRole vocabulary, validated by the store). This makes album art durable: ResolveArt prefers it over the read-derived track cover. Entity art takes no lock/force (the lock system is item-scoped). With writeBack an album front cover is also embedded into every member track's file; other entity covers stay catalog-only on disk (they have no single natural file target), and a non-front role is refused with writeBack for the same reason an item's is.
func (*Library) SetEntityRating ¶
func (l *Library) SetEntityRating(ctx context.Context, userPID model.PID, kind model.MergeEntity, entityPID model.PID, rating *int, asOf *int64) error
SetEntityRating sets (0..100) or clears (rating nil) a user's rating for a catalog entity. asOf carries the recorded change time and enforces recorded-time last-writer-wins; see SetEntityStar.
func (*Library) SetEntityStar ¶
func (l *Library) SetEntityStar(ctx context.Context, userPID model.PID, kind model.MergeEntity, entityPID model.PID, starred bool, asOf *int64) error
SetEntityStar stars or unstars a catalog entity for a user, recording the star time for recency ordering of the starred list. asOf (unix nanoseconds, nil = server now) is the recorded flip time; when supplied the engine enforces recorded-time last-writer-wins, so a replayed offline toggle cannot undo a later out-of-band change, exactly as item SetStar does. A value-identical call is a silent no-op that preserves the stored star time.
func (*Library) SetItemArt ¶
func (l *Library) SetItemArt(ctx context.Context, itemPID model.PID, role model.ArtRole, raw []byte, lock, force, writeBack bool) error
SetItemArt sets (or, with empty bytes, clears) one artwork role on a track/book item from raw image bytes. An empty role means the front cover, which locks the "art" field by default (the lock guards the scan's cover re-derive; the other roles have no scan producer, so lock/force are ignored for them). A clear deletes only the named role. With writeBack the cover is also embedded into the item's backing file, or into every part of a multi-file book so an external player sees the same cover on each part; only the front cover has an embedded representation, so writeBack with any other role is refused with CodeInvalid before anything is written. A file that cannot be written is reported through a *WriteBackError while the catalog edit stands.
func (*Library) SetItemTag ¶
func (l *Library) SetItemTag(ctx context.Context, itemPID model.PID, key string, values []string, opts TagEditOptions) (string, int, error)
SetItemTag replaces a custom tag's ordered values on an item, locking "tag.<KEY>" by default so a scan does not re-derive it from the file. Empty (or whitespace-only) values clear the tag. The key is normalized to canonical uppercase; a reserved key (one WaxBin owns through the scalar, credit, or identifier APIs) is rejected. It returns the canonical key stored and the number of values actually stored after trimming (0 means the tag was cleared).
func (*Library) SetLyrics ¶
func (l *Library) SetLyrics(ctx context.Context, itemPID model.PID, ly *model.Lyrics, lock, force bool) error
SetLyrics replaces an item's lyrics with a user-curated set, locking the "lyrics" field by default. Passing nil clears the lyrics.
func (*Library) SetSecret ¶
SetSecret stores a named credential in the secret table. Values are never logged or written to a logical export, but a full byte Backup contains them unless redacted.
func (*Library) StarredEntities ¶
func (l *Library) StarredEntities(ctx context.Context, userPID model.PID, kind model.MergeEntity) ([]model.EntityPlayState, error)
StarredEntities lists a user's starred entities of one kind, most-recently-starred first. Pair it with EntityByPIDs to hydrate display names in one batch.
func (*Library) StartAnalyze ¶
StartAnalyze submits the analyze pass as a background job and returns its PID.
func (*Library) StartEnrich ¶
StartEnrich submits the enrichment pass as a background job and returns its PID. It refuses (before starting a job) when enrichment is not configured or the scoping options do not resolve, matching the synchronous Enrich.
func (*Library) StartScan ¶
StartScan submits a scan as a background job and returns its PID immediately. The job runs to completion in this process; a client follows it through Job and reads the scan.Result summary from the finished job's Result.
func (*Library) Stats ¶
Stats returns a library summary using the same Facet grouping as browse plus per-user playback state. An empty userPID selects the default user; topN caps the ranked lists.
func (*Library) Subscribe ¶
Subscribe registers an in-process listener for change_log rows after each mutating commit. The cancel func unsubscribes. Cross-process consumers should poll DataVersion and then call Changes.
func (*Library) TagKeys ¶
TagKeys returns every custom-tag key in the catalog with the number of distinct items carrying it, most-used first. It is the discovery primitive for custom-tag browse dimensions: list the keys, then facet or filter on tag.<KEY>.
func (*Library) Trash ¶
func (l *Library) Trash(ctx context.Context, includeRestored bool, limit int) ([]model.TrashEntry, error)
Trash lists trash journal entries, newest first. includeRestored controls whether already-restored rows are shown; limit 0 returns all.
func (*Library) Unlock ¶
Unlock clears the lock on each field, dropping rows that no longer carry any curated state so provenance stays sparse.
func (*Library) Vacuum ¶
func (l *Library) Vacuum(ctx context.Context) (*VacuumReport, error)
Vacuum GCs orphaned entities and art, then compacts the database file, returning what was reclaimed. It takes the write lock. Orphan entities are swept before art so their freed art-map rows are reclaimed in the same pass.
func (*Library) VerifyDerived ¶
VerifyDerived runs the derived-data consistency check (FTS, rollups, and generated sort keys versus the source rows). It is read-only; it reports drift rather than repairing it.
func (*Library) Watch ¶
func (l *Library) Watch(ctx context.Context, opts WatchOptions) error
Watch runs a foreground watcher that keeps the catalog in sync with the filesystem until ctx is canceled (returning a CodeCanceled error on a clean shutdown). It refuses on a read-only library.
WATCH IS A FOREGROUND MODE. A read-write WaxBin holds an exclusive advisory lock on the catalog for the whole process lifetime, so while watch runs, every OTHER mutating command in another terminal (organize, analyze, enrich, import, scan --force) is refused (read-only queries are always allowed). Stop the watcher to do manual mutation, or run waxbin serve instead when other terminals need to mutate concurrently: it proxies mutations over a local control socket (see Library.Serve). Idle lock release is deliberately post-1.0.
The watched roots are snapshotted here, at start. A root registered later through AddRoot is scanned and organized (those resolve roots per run) but not watched until the watcher restarts; the root's create delta on the change feed is the signal to do so.
func (*Library) YearInReview ¶
func (l *Library) YearInReview(ctx context.Context, userPID model.PID, year, topN int) (*read.YearReview, error)
YearInReview returns a per-user listening recap for one calendar year (UTC): session/minute/track totals, catalog additions that year, and the top artists/genres/tracks by play count. An empty userPID uses the default user.
type Options ¶
type Options struct {
// DBPath is the catalog database (local filesystem only).
DBPath string
// Roots are library roots to ensure on open (upserted; never deleted here).
Roots []config.Root
// ReadOnly opens without taking the write lock and forbids mutations.
ReadOnly bool
// Logger receives structured logs; nil discards (the library never prints).
Logger *slog.Logger
// WriteOwner identifies this owner in the lockfile and job rows; defaulted
// from hostname + pid when empty.
WriteOwner string
// IPCSocket, if set, is advertised in the lockfile for local proxy support.
IPCSocket string
// Profiles defines additional organization profiles and built-in overrides.
Profiles []config.ProfileDef
// Inbox folders are staging directories importable into a managed library.
Inbox []string
// FreeSpaceReserveBytes is headroom an import preflight keeps free.
FreeSpaceReserveBytes int64
// WriteReplayGainTags mirrors computed ReplayGain into files after analyze (off
// by default; the catalog stays authoritative).
WriteReplayGainTags bool
// StampItemPID stamps the backing item's WaxBin PID into a tag during organize's
// tag write-back on managed roots (off by default).
StampItemPID bool
// Podcasts configures the podcast engine (download dir + network policy).
Podcasts config.PodcastConfig
// Enrichment configures the metadata enrichment pass (MusicBrainz/CAA/AcoustID).
Enrichment config.EnrichConfig
// SourceProviders are injected acquisition providers, such as a youtube provider
// supplied by another module. The built-in netsafe rss provider is always
// registered; these register under their own source types. The default CLI build
// does not ship extra providers.
SourceProviders []source.Provider
// EnrichmentProviders are injected metadata-enrichment providers (Discogs, Last.fm,
// Audnexus, Hardcover, fanart.tv, ...) supplied by an embedding module. They take
// priority over the key-free built-ins (Cover Art Archive, ListenBrainz, LRCLIB)
// for a value conflict; the MusicBrainz identity spine still resolves the anchoring
// MBID first. The default CLI build ships none.
EnrichmentProviders []enrich.Provider
// SecretCipher, when set, seals secret-table values (private-feed passwords) at
// rest; an embedder supplies one to own the key. Nil keeps secrets in plaintext.
SecretCipher model.SecretCipher
// SecretKeyID labels the key/epoch sealed values are written under; defaults to
// "1" when a cipher is set without one. Change it when rotating to a new key.
SecretKeyID string
// Storage tuning; zero values fall back to library defaults.
BusyTimeoutMS int
CacheSizeKB int
MmapSizeBytes int64
ReadPoolSize int
}
Options configures opening a Library.
type ScanRequest ¶
type ScanRequest struct {
LibraryPID model.PID // empty scans every library
SubPath string // optional sub-path under a single library's root
// Force bypasses the incremental fast-path, re-hashing and re-parsing every file
// even when its size and mtime are unchanged (repair, or after an essence bump).
Force bool
// AdoptStampedPIDs restores item PIDs from WAXBIN_ITEM_PID tags during a rebuild
// (essence-first, adopted only when unambiguous). Off for a normal scan.
AdoptStampedPIDs bool
// ForceReconcile bypasses the survival-gate floor so a deliberate large deletion
// is reconciled to missing (the recovery path). An explicit operator action; the
// watcher never sets it.
ForceReconcile bool
// IgnoreLocks re-derives every field from disk even when the user locked it, so a
// `scan --force --ignore-locks` discards curated edits. Off by default: a scan
// (including --force) preserves locked fields, and write-back is what propagates a
// DB edit back onto disk.
IgnoreLocks bool
}
ScanRequest selects what to scan.
type ScanResult ¶
ScanResult reports a scan, including the job it ran under.
type TagEditOptions ¶
type TagEditOptions struct {
// Lock locks the "tag.<KEY>" field against a scan re-deriving it from the file; on
// by default.
Lock bool
// Force overrides a locked custom tag.
Force bool
}
TagEditOptions configures a custom-tag edit, mirroring EditOptions.
type UpgradeCandidate ¶
type UpgradeCandidate struct {
ItemPID model.PID
FilePID model.PID
Title string
Artist string
Codec string
Bitrate int
SampleRate int
BitDepth int
Lossless bool
// Best marks the highest-quality member of the group (the recommended keeper);
// the rest are lower-quality alt encodings that could be pruned or upgraded.
Best bool
}
UpgradeCandidate is one encoding of a recording, with the quality fields the policy ranks on.
type UpgradeGroup ¶
type UpgradeGroup struct {
Members []UpgradeCandidate
}
UpgradeGroup is a set of catalog items that are the same recording in different encodings (grouped by fingerprint), ordered best-quality first.
type VacuumReport ¶
type VacuumReport struct {
ArtSourcesReclaimed int
ThumbnailsReclaimed int
OrphansDeleted int
OrphansPending int
}
VacuumReport summarizes a vacuum: the derived garbage reclaimed before the on-disk compaction.
type WatchActivity ¶
WatchActivity summarizes one watch cycle for a heartbeat consumer.
type WatchOptions ¶
type WatchOptions struct {
LibraryPID model.PID // empty watches every user library root
Interval time.Duration
FullRescanInterval time.Duration
Live bool
WriteSettle time.Duration
MaxWatchDirs int // 0 = unlimited; caps live fsnotify watches (see watch.Options)
Analyze bool
SyncSources bool
// OnActivity, when set, is called after each cycle for a CLI heartbeat.
OnActivity func(WatchActivity)
}
WatchOptions configures a foreground watch (see watch.Options).
type WriteBackError ¶
type WriteBackError struct {
ItemPID model.PID
Edits map[string]string
Failures []WriteBackFailure
}
WriteBackError reports that a catalog edit committed but its on-disk tag write-back did not fully apply. The catalog holds the new values. The named files' tags stay out of sync until they are re-written, which is also recorded as a per-file diagnostic.
func (*WriteBackError) Error ¶
func (e *WriteBackError) Error() string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package analyze owns the PCM-decoding pass behind the scan/analyze boundary.
|
Package analyze owns the PCM-decoding pass behind the scan/analyze boundary. |
|
Package art contains WaxBin's pure-Go image handling for the read-side art resolver: content hashing for the content-addressed store, format and dimension probing, and thumbnail generation (decode, scale to fit, re-encode).
|
Package art contains WaxBin's pure-Go image handling for the read-side art resolver: content hashing for the content-addressed store, format and dimension probing, and thumbnail generation (decode, scale to fit, re-encode). |
|
Package audit inspects a catalog for quality and integrity problems and reports severity-ranked findings: duplicate/split entities, inconsistent metadata, missing art/ReplayGain, bad filenames, orphaned sidecars, path conflicts, invalid feeds, derived-data drift, and (opt-in) on-disk integrity/corruption.
|
Package audit inspects a catalog for quality and integrity problems and reports severity-ranked findings: duplicate/split entities, inconsistent metadata, missing art/ReplayGain, bad filenames, orphaned sidecars, path conflicts, invalid feeds, derived-data drift, and (opt-in) on-disk integrity/corruption. |
|
cmd
|
|
|
waxbin
command
Command waxbin is the scriptable CLI for the WaxBin audio library engine.
|
Command waxbin is the scriptable CLI for the WaxBin audio library engine. |
|
Package config loads WaxBin's configuration with the precedence flag > env (WAXBIN_*) > json > default, and validates that library roots are absolute and non-overlapping (a file belongs to exactly one library).
|
Package config loads WaxBin's configuration with the precedence flag > env (WAXBIN_*) > json > default, and validates that library roots are absolute and non-overlapping (a file belongs to exactly one library). |
|
Package decode is WaxBin's PCM-decoding layer, and the only package in the cataloging and analysis path that knows WaxFlow exists (test fixtures and pidpath's integration Examples name it too, neither of which ships).
|
Package decode is WaxBin's PCM-decoding layer, and the only package in the cataloging and analysis path that knows WaxFlow exists (test fixtures and pidpath's integration Examples name it too, neither of which ships). |
|
Package enrich populates catalog entities from external metadata providers: MusicBrainz (release-group type, artist aliases/relations, genres, and the MBIDs that anchor identity) and the Cover Art Archive (release-group cover art), with an optional AcoustID fingerprint fallback for release groups that text search cannot resolve.
|
Package enrich populates catalog entities from external metadata providers: MusicBrainz (release-group type, artist aliases/relations, genres, and the MBIDs that anchor identity) and the Cover Art Archive (release-group cover art), with an optional AcoustID fingerprint fallback for release groups that text search cannot resolve. |
|
Package envelope is the version envelope for persisted, re-parsed artifacts (query rule documents, organization profile definitions, export manifests).
|
Package envelope is the version envelope for persisted, re-parsed artifacts (query rule documents, organization profile definitions, export manifests). |
|
Package fingerprint computes WaxBin's internal acoustic fingerprint and the inverted-index terms used for alt-encoding grouping.
|
Package fingerprint computes WaxBin's internal acoustic fingerprint and the inverted-index terms used for alt-encoding grouping. |
|
Package identity computes the content hash and the entity-identity keys that keep public ids stable across rescans, retags, and moves.
|
Package identity computes the content hash and the entity-identity keys that keep public ids stable across rescans, retags, and moves. |
|
Package inbox imports audio staged outside the library.
|
Package inbox imports audio staged outside the library. |
|
internal
|
|
|
caps
Package caps detects the optional external helper the analyze pass can use.
|
Package caps detects the optional external helper the analyze pass can use. |
|
diskfree
Package diskfree reports the free space on the filesystem holding a path, for import/move preflight checks.
|
Package diskfree reports the free space on the filesystem holding a path, for import/move preflight checks. |
|
fsx
Package fsx provides the long-path-safe filesystem move/copy primitives shared by organize, inbox, and trash.
|
Package fsx provides the long-path-safe filesystem move/copy primitives shared by organize, inbox, and trash. |
|
netsafe
Package netsafe provides the HTTP client used for remote feeds, enclosures, transcripts, artwork, and future metadata lookups.
|
Package netsafe provides the HTTP client used for remote feeds, enclosures, transcripts, artwork, and future metadata lookups. |
|
pathx
Package pathx holds small filesystem-path helpers shared across subsystems.
|
Package pathx holds small filesystem-path helpers shared across subsystems. |
|
testaudio
Package testaudio synthesizes minimal tagged audio files for tests.
|
Package testaudio synthesizes minimal tagged audio files for tests. |
|
Package jobs runs mutating background work under a scoped advisory lease and a tracked job row.
|
Package jobs runs mutating background work under a scoped advisory lease and a tracked job row. |
|
Package loudness models EBU R128 / ReplayGain 2.0 loudness for the catalog: the Result a track's measurement is stored as, the reference level its gain is computed against, and the conversion from a measured dB domain into that Result.
|
Package loudness models EBU R128 / ReplayGain 2.0 loudness for the catalog: the Result a track's measurement is stored as, the reference level its gain is computed against, and the conversion from a measured dB domain into that Result. |
|
Package meta reads catalog metadata, cheap audio properties, and the tag-independent audio-essence hash without decoding PCM.
|
Package meta reads catalog metadata, cheap audio properties, and the tag-independent audio-essence hash without decoding PCM. |
|
Package model holds WaxBin's unified domain types and the repository interfaces the rest of the engine depends on.
|
Package model holds WaxBin's unified domain types and the repository interfaces the rest of the engine depends on. |
|
Package organize renders managed-library paths and applies file relocations.
|
Package organize renders managed-library paths and applies file relocations. |
|
Package peaks computes compact max-amplitude waveforms for scrubbers and track overviews.
|
Package peaks computes compact max-amplitude waveforms for scrubbers and track overviews. |
|
Package pidpath resolves an item PID to where its audio lives, cached, with the cache invalidated from the catalog's change feed (a cheap DataVersion read, then Changes rows).
|
Package pidpath resolves an item PID to where its audio lives, cached, with the cache invalidated from the catalog's change feed (a cheap DataVersion read, then Changes rows). |
|
Package playback is the consumer-facing playback-state service.
|
Package playback is the consumer-facing playback-state service. |
|
Package playlist is the consumer-facing playlist service: static and smart playlist CRUD plus M3U8 import/export.
|
Package playlist is the consumer-facing playlist service: static and smart playlist CRUD plus M3U8 import/export. |
|
Package podcast parses RSS podcast feeds and OPML, then exposes the service for subscriptions, sync, downloads, transcripts, artwork, and retention.
|
Package podcast parses RSS podcast feeds and OPML, then exposes the service for subscriptions, sync, downloads, transcripts, artwork, and retention. |
|
Package port handles catalog backup, restore, redaction, and logical JSON export.
|
Package port handles catalog backup, restore, redaction, and logical JSON export. |
|
Package proxy is WaxBin's local control channel.
|
Package proxy is WaxBin's local control channel. |
|
Package query is WaxBin's shared selection engine.
|
Package query is WaxBin's shared selection engine. |
|
Package read holds WaxBin's canonical read/consumer API primitives: faceted aggregation, collation-correct keyset pagination, and the canonical "unknown" buckets.
|
Package read holds WaxBin's canonical read/consumer API primitives: faceted aggregation, collation-correct keyset pagination, and the canonical "unknown" buckets. |
|
Package scan walks library roots and persists catalog rows.
|
Package scan walks library roots and persists catalog rows. |
|
Package source defines the provider interface used to resolve, enumerate, and fetch remote media.
|
Package source defines the provider interface used to resolve, enumerate, and fetch remote media. |
|
store
|
|
|
sqlite
Package sqlite is WaxBin's SQLite-only DataStore.
|
Package sqlite is WaxBin's SQLite-only DataStore. |
|
Package trash plans and applies file removal without deleting catalog history.
|
Package trash plans and applies file removal without deleting catalog history. |
|
Package watch runs a long-lived watcher that keeps the catalog in sync with the filesystem while WaxBin holds the write lock.
|
Package watch runs a long-lived watcher that keeps the catalog in sync with the filesystem while WaxBin holds the write lock. |
|
Package waxerr defines WaxBin's typed errors.
|
Package waxerr defines WaxBin's typed errors. |