feat(database): listening-events log with skip counters
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 5m23s
CI / e2e (pull_request) Successful in 11m9s

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.
This commit is contained in:
2026-09-09 09:19:36 -04:00
parent a3b5b43777
commit 68e7edb8c9
14 changed files with 403 additions and 90 deletions
+5 -4
View File
@@ -269,10 +269,11 @@ var tables = []Table{
"from owned files plus the LRCLIB backfill.",
},
{
Name: "play_history", Kind: Authored, Lifetime: Cascade,
Note: "Listening history. Authored, but intentionally cascades " +
"with its track — history for a file no longer in the library " +
"has nothing to point at.",
Name: "listening_events", Kind: Authored, Lifetime: Cascade,
Note: "Listening history, one row per track exit (complete, play " +
"or skip). Authored, but intentionally cascades with its " +
"track — history for a file no longer in the library has " +
"nothing to point at.",
},
{
Name: "player_state", Kind: Authored, Lifetime: Retained,
+3 -3
View File
@@ -212,14 +212,14 @@ func TestLifetimesMatchSchema(t *testing.T) {
// Authored data is unrecoverable, so it must never be removed as a side
// effect of deleting owned data. Cascade is allowed only where the
// catalog explains why (play_history, queue_tracks); this test pins the
// catalog explains why (listening_events, queue_tracks); this test pins the
// set so a new cascade onto authored data is a deliberate decision.
func TestAuthoredCascadesAreDeliberate(t *testing.T) {
t.Parallel()
allowed := map[string]bool{
"play_history": true,
"queue_tracks": true,
"listening_events": true,
"queue_tracks": true,
// Download history is scoped to the library it imported into.
// When that library is removed the files it acquired go with