57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
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
|
|
}
|
|
}
|