Web UI: setup wizard, auth pages, library browsing, player, admin, streaming, cover art

This commit is contained in:
2026-08-22 23:42:09 -05:00
parent aa29f13846
commit e0bd8bc3cb
42 changed files with 3012 additions and 8 deletions
+56
View File
@@ -0,0 +1,56 @@
package web
import (
"net/http"
"strings"
"git.noroutes.net/NoRoutes/vibetunes/internal/auth"
"git.noroutes.net/NoRoutes/vibetunes/internal/database"
"git.noroutes.net/NoRoutes/vibetunes/internal/streaming"
)
// handleStream serves a song's audio bytes directly (no transcoding in
// Phase 1). Authorization happens here, before any file is opened: the
// caller must be a signed-in, non-disabled user (enforced by the
// RequireUser middleware wrapping this route), and the resolved path is
// always re-validated against its music folder root.
func (h *Web) handleStream(w http.ResponseWriter, r *http.Request) {
user := auth.UserFromContext(r.Context())
songID := r.PathValue("id")
song, err := database.GetSong(h.DB, songID)
if err != nil {
http.NotFound(w, r)
return
}
mediaFile, err := database.GetMediaFile(h.DB, song.MediaFileID)
if err != nil {
http.NotFound(w, r)
return
}
folder, err := database.GetMusicFolder(h.DB, mediaFile.MusicFolderID)
if err != nil {
http.NotFound(w, r)
return
}
absPath, err := streaming.ResolvePath(folder.Path, mediaFile.RelativePath)
if err != nil {
http.Error(w, "403 Forbidden", http.StatusForbidden)
return
}
// Best-effort playback tracking: only record once per playback (on
// the request for the start of the file, not every range chunk a
// browser issues while seeking/buffering).
rangeHeader := r.Header.Get("Range")
if rangeHeader == "" || strings.HasPrefix(rangeHeader, "bytes=0-") {
_ = database.SetNowPlaying(h.DB, user.ID, song.ID, "web", 0)
_ = database.RecordPlayback(h.DB, user.ID, song.ID, 0)
}
if err := streaming.ServeFile(w, r, absPath, mediaFile.RelativePath); err != nil {
http.Error(w, "could not read media file", http.StatusInternalServerError)
return
}
}