feat(api): capture contextual_likes on like during open play_event

Two new helpers in internal/playevents (CaptureContextualLikeIfPlaying,
SoftDeleteContextualLikes) called by both api and subsonic surfaces.
Like inserts a new contextual_likes row when LikeTrack actually
inserted (rows=1) AND there's an open play_event with a vector.
Unlike soft-deletes via deleted_at. Idempotent in both directions.

Subsonic wiring lands in the next commit.
This commit is contained in:
2026-04-27 11:22:35 -04:00
parent d6acb3b63c
commit fe1cc46752
4 changed files with 279 additions and 2 deletions
+12 -2
View File
@@ -11,6 +11,7 @@ import (
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/playevents"
)
type likedIDsResponse struct {
@@ -40,11 +41,15 @@ func (h *handlers) handleLikeTrack(w http.ResponseWriter, r *http.Request) {
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
return
}
if _, err := q.LikeTrack(r.Context(), dbq.LikeTrackParams{UserID: user.ID, TrackID: id}); err != nil {
rows, err := q.LikeTrack(r.Context(), dbq.LikeTrackParams{UserID: user.ID, TrackID: id})
if err != nil {
h.logger.Error("api: like track insert", "err", err)
writeErr(w, http.StatusInternalServerError, "server_error", "insert failed")
return
}
if rows == 1 {
_ = playevents.CaptureContextualLikeIfPlaying(r.Context(), q, user.ID, id, h.logger)
}
w.WriteHeader(http.StatusNoContent)
}
@@ -59,11 +64,16 @@ func (h *handlers) handleUnlikeTrack(w http.ResponseWriter, r *http.Request) {
writeErr(w, http.StatusBadRequest, "bad_request", "invalid track id")
return
}
if err := dbq.New(h.pool).UnlikeTrack(r.Context(), dbq.UnlikeTrackParams{UserID: user.ID, TrackID: id}); err != nil {
q := dbq.New(h.pool)
if err := q.UnlikeTrack(r.Context(), dbq.UnlikeTrackParams{UserID: user.ID, TrackID: id}); err != nil {
h.logger.Error("api: unlike track", "err", err)
writeErr(w, http.StatusInternalServerError, "server_error", "delete failed")
return
}
if err := playevents.SoftDeleteContextualLikes(r.Context(), q, user.ID, id); err != nil {
h.logger.Error("api: soft-delete contextual_likes", "err", err)
// Don't fail the response — soft-delete is best-effort.
}
w.WriteHeader(http.StatusNoContent)
}