Replace play_history with listening_events — one row per track exit, kind (complete/play/skip) plus raw position/duration — and add skip_count/last_skipped to audio_files beside play_count/last_played. The classifier that writes these lands later (plan 021); this is the schema it records into. Also drop the dead queue.source_playlist_id column and remove the stale references to the squashed migration chain in download_*.sql and tagging_items.sql, declaring the missing download-request indexes inline.
77 lines
3.2 KiB
SQL
77 lines
3.2 KiB
SQL
-- A Request is a persistent "I want this", stored as a MusicBrainz ID
|
|
-- and almost nothing else. It outlives every download attempt made on
|
|
-- its behalf: nothing being findable today is the normal case for
|
|
-- obscure music, and the correct response is to try again next week,
|
|
-- not to show the user a failed row they have to remember to retry.
|
|
--
|
|
-- Because a Request is only an MBID, it stays true when everything
|
|
-- around it changes: the explore index is rebuilt, a provider is
|
|
-- swapped out, the release the user originally saw is superseded by a
|
|
-- remaster. The display fields are a cache for the list view and are
|
|
-- never consulted for matching.
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS download_requests (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
mbid TEXT NOT NULL,
|
|
entity TEXT NOT NULL
|
|
CHECK(entity IN ('artist', 'release-group', 'release', 'recording')),
|
|
library_id INTEGER NOT NULL,
|
|
|
|
-- Display text, cached so the list renders without touching the
|
|
-- explore index. Neither is authoritative; the MBID is.
|
|
artist TEXT NOT NULL DEFAULT '',
|
|
title TEXT NOT NULL DEFAULT '',
|
|
|
|
scope TEXT NOT NULL DEFAULT 'future'
|
|
CHECK(scope IN ('future', 'all')),
|
|
|
|
-- secondary controls whether an artist request's expansion includes
|
|
-- compilations, live albums and remixes. Off by default: someone
|
|
-- subscribing to an artist wants the albums, not six versions of
|
|
-- the same greatest-hits package.
|
|
secondary INTEGER NOT NULL DEFAULT 0,
|
|
|
|
state TEXT NOT NULL DEFAULT 'wanted'
|
|
CHECK(state IN ('wanted', 'satisfied', 'paused')),
|
|
|
|
-- parent_id links a request the reconciler derived from an artist
|
|
-- request. Deleting the artist takes its derived children with
|
|
-- it, but children the user pinned themselves have no parent and
|
|
-- stay.
|
|
parent_id INTEGER,
|
|
|
|
-- Retry bookkeeping. attempts drives the backoff; last_error is
|
|
-- the most recent reason it did not work out, which for a request
|
|
-- is information rather than a failure.
|
|
attempts INTEGER NOT NULL DEFAULT 0,
|
|
last_error TEXT NOT NULL DEFAULT '',
|
|
last_tried_at DATETIME,
|
|
next_try_at DATETIME,
|
|
|
|
-- external_ids maps provider row ID to that provider's own
|
|
-- identifier for this request, for clients that keep their own
|
|
-- persistent list (a Lidarr artist ID). JSON object.
|
|
external_ids TEXT NOT NULL DEFAULT '{}',
|
|
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- One request per thing per library. Asking twice is not two
|
|
-- requests, and this is what lets an artist expansion re-run every
|
|
-- reconcile without accumulating duplicates.
|
|
UNIQUE(mbid, library_id),
|
|
|
|
FOREIGN KEY(library_id) REFERENCES libraries(id) ON DELETE CASCADE,
|
|
FOREIGN KEY(parent_id) REFERENCES download_requests(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_download_requests_due
|
|
ON download_requests(state, next_try_at);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_download_requests_entity
|
|
ON download_requests(entity, state);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_download_requests_parent
|
|
ON download_requests(parent_id) WHERE parent_id IS NOT NULL;
|