Ships the fresh-start schema cleanup: rebuilt explore catalog index pipeline (dump import, artifact fetch/build, incremental listen-count refresh), a new download subsystem (Lidarr/Prowlarr/qBittorrent/SABnzbd/ slskd/yt-dlp providers, staging, reconciliation, wanted list), and the supporting schema/query/store changes across backend and frontend. Also includes two smaller follow-ups: bump the central index's rebuild-after cadence from 90 to 180 days, and remove the Explore "library only" online/offline toggle entirely (frontend-only, no backend counterpart) rather than carry unused UI/state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2Agd9af5hE7qzti2ackiS
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
package explore
|
|
|
|
import (
|
|
"context"
|
|
|
|
"yellowjacket/backend/jobs"
|
|
)
|
|
|
|
// Fetching and merging the prebuilt catalog artifact.
|
|
//
|
|
// This is the whole catalog build on a user's machine. The import that
|
|
// derives the catalog from the MetaBrainz dumps lives behind the
|
|
// `indexbuild` tag and runs only in CI (see dumpbuild_stub.go).
|
|
|
|
// Artifact build stages, shown in the jobs panel.
|
|
const (
|
|
artifactStageDownload = iota
|
|
artifactStageMerge
|
|
)
|
|
|
|
var artifactStageNames = [...]string{
|
|
"Downloading catalog",
|
|
"Merging catalog",
|
|
}
|
|
|
|
// tryCoreArtifact fetches and merges the prebuilt catalog. Every
|
|
// failure path is non-fatal by design: the caller falls back, and a
|
|
// fresh install with no network still gets its own library in Explore.
|
|
func (si *SearchIndex) tryCoreArtifact(ctx context.Context) error {
|
|
si.mu.Lock()
|
|
si.buildStatus = IndexStatus{
|
|
Building: true,
|
|
Tiers: []TierStatus{
|
|
{Name: artifactStageNames[artifactStageDownload], State: "pending"},
|
|
{Name: artifactStageNames[artifactStageMerge], State: "pending"},
|
|
},
|
|
}
|
|
si.mu.Unlock()
|
|
si.refreshStatusCounts()
|
|
|
|
fetcher, err := newArtifactFetcher(si)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
si.setTierStatus(artifactStageNames[artifactStageDownload], "running", 0, 0)
|
|
si.logIndexJob(jobs.LevelInfo, "Fetching prebuilt catalog")
|
|
|
|
path, err := fetcher.fetch(ctx)
|
|
if err != nil {
|
|
si.setTierStatus(artifactStageNames[artifactStageDownload], "error", 0, 0)
|
|
|
|
return err
|
|
}
|
|
|
|
si.setTierStatus(artifactStageNames[artifactStageDownload], "complete", 0, 0)
|
|
si.setTierStatus(artifactStageNames[artifactStageMerge], "running", 0, 0)
|
|
|
|
if err := si.importCoreArtifact(ctx, path); err != nil {
|
|
si.setTierStatus(artifactStageNames[artifactStageMerge], "error", 0, 0)
|
|
si.removeArtifactFile(path)
|
|
|
|
return err
|
|
}
|
|
|
|
si.removeArtifactFile(path)
|
|
si.setTierStatus(artifactStageNames[artifactStageMerge], "complete", 0, 0)
|
|
|
|
si.mu.Lock()
|
|
si.buildStatus.Building = false
|
|
si.mu.Unlock()
|
|
|
|
// Fold the user's own library into the freshly-merged catalog:
|
|
// owned entities the artifact does not cover are inserted, and
|
|
// covered ones are flagged in_library.
|
|
si.PopulateLocalCrossReferences()
|
|
si.refreshStatusCounts()
|
|
si.scheduleChampionRebuild()
|
|
|
|
return nil
|
|
}
|
|
|
|
// artifactAlreadyMerged reports whether this index already carries a
|
|
// merged artifact, so a restart does not re-download one.
|
|
func (si *SearchIndex) artifactAlreadyMerged() bool {
|
|
return si.hasMeta(coreArtifactVersionKey)
|
|
}
|