v2026.06.03 — Media3 like button + Bluetooth/UPnP picker + system playlist daily rotation #77

Merged
bvandeusen merged 29 commits from dev into main 2026-06-03 14:09:23 -04:00
Showing only changes of commit 7c11cdc4d1 - Show all commits
+12 -4
View File
@@ -14,6 +14,8 @@ import (
"strconv"
"strings"
"github.com/go-chi/chi/v5"
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
"git.fabledsword.com/bvandeusen/minstrel/internal/coverart"
@@ -163,15 +165,21 @@ func (h *handlers) streamAuthOk(r *http.Request, trackID string) bool {
// permissive middleware the route is wrapped with) OR a signed token
// (UPnP slice). streamAuthOk gates both paths.
func (h *handlers) handleGetStream(w http.ResponseWriter, r *http.Request) {
// Auth check before the DB lookup: a 404 ahead of the auth check
// would let unauth callers probe which track IDs exist via the
// 404/401 response differential. streamAuthOk is keyed on the
// path's id directly (the HMAC token is signed over the same id
// string, so we don't need the resolved row yet).
rawID := chi.URLParam(r, "id")
if !h.streamAuthOk(r, rawID) {
writeErr(w, apierror.ErrUnauthorized)
return
}
track, apiErr := resolveByID(r, "id", dbq.New(h.pool).GetTrackByID, "track")
if apiErr != nil {
writeErr(w, apiErr)
return
}
if !h.streamAuthOk(r, uuidToString(track.ID)) {
writeErr(w, apierror.ErrUnauthorized)
return
}
f, err := os.Open(track.FilePath)
if err != nil {
// File vanished (scanner indexed it, filesystem lost it). Treat as