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
+6 -4
View File
@@ -6,7 +6,7 @@ import (
"yellowjacket/backend/events"
)
// recordPlay inserts a play_history row and updates the denormalized
// recordPlay inserts a listening_events row and updates the denormalized
// play_count / last_played columns on audio_files. Called from
// OnPlaybackFinished for the track that just finished.
//
@@ -20,10 +20,12 @@ func (q *Queue) recordPlay(audioFileID int64) {
now := time.Now().UTC().Format(time.DateTime)
// Insert play_history row.
// Insert the listening event. A natural finish is a 'complete' by
// construction; position/duration are the classifier's to fill once
// skips are recorded (see .planning/plans/active/021).
_, err := q.db.ExecContext(
`INSERT INTO play_history (audio_file_id, played_at)
VALUES (?, ?)`,
`INSERT INTO listening_events (audio_file_id, kind, occurred_at)
VALUES (?, 'complete', ?)`,
audioFileID, now,
)
if err != nil {