refactor: consolidate specid <-> filesystem mapping and always use abs paths (#309)

This commit is contained in:
Senan Kelly
2023-04-22 18:23:17 +01:00
committed by GitHub
parent efe72fc447
commit 74de06430a
10 changed files with 232 additions and 173 deletions

View File

@@ -17,6 +17,7 @@ import (
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
"go.senan.xyz/gonic/server/ctrlsubsonic/specidpaths"
"go.senan.xyz/gonic/transcode"
)
@@ -57,32 +58,6 @@ func streamGetTransPrefProfile(dbc *db.DB, userID int, client string) (mime stri
var errUnknownMediaType = fmt.Errorf("media type is unknown")
// TODO: there is a mismatch between abs paths for podcasts and music. if they were the same, db.AudioFile
// could have an AbsPath() method. and we wouldn't need to pass podcastsPath or return 3 values
func streamGetAudio(dbc *db.DB, podcastsPath string, user *db.User, id specid.ID) (db.AudioFile, string, error) {
switch t := id.Type; t {
case specid.Track:
var track db.Track
if err := dbc.Preload("Album").Preload("Artist").First(&track, id.Value).Error; err != nil {
return nil, "", fmt.Errorf("find track: %w", err)
}
if track.Artist != nil && track.Album != nil {
log.Printf("%s requests %s - %s from %s", user.Name, track.Artist.Name, track.TagTitle, track.Album.TagTitle)
}
return &track, path.Join(track.AbsPath()), nil
case specid.PodcastEpisode:
var podcast db.PodcastEpisode
if err := dbc.First(&podcast, id.Value).Error; err != nil {
return nil, "", fmt.Errorf("find podcast: %w", err)
}
return &podcast, path.Join(podcastsPath, podcast.Path), nil
default:
return nil, "", fmt.Errorf("%w: %q", errUnknownMediaType, t)
}
}
func streamUpdateStats(dbc *db.DB, userID, albumID int, playTime time.Time) error {
var play db.Play
err := dbc.
@@ -134,6 +109,7 @@ var (
errCoverEmpty = errors.New("no cover found for that folder")
)
// TODO: can we use specidpaths.Locate here?
func coverGetPath(dbc *db.DB, podcastPath string, id specid.ID) (string, error) {
switch id.Type {
case specid.Album:
@@ -281,12 +257,17 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
return spec.NewError(10, "please provide an `id` parameter")
}
file, audioPath, err := streamGetAudio(c.DB, c.PodcastsPath, user, id)
file, err := specidpaths.Locate(c.DB, c.PodcastsPath, id)
if err != nil {
return spec.NewError(70, "error finding media: %v", err)
return spec.NewError(0, "error looking up id %s: %v", id, err)
}
if track, ok := file.(*db.Track); ok && track.Album != nil {
audioFile, ok := file.(db.AudioFile)
if !ok {
return spec.NewError(0, "type of id does not contain audio")
}
if track, ok := audioFile.(*db.Track); ok && track.Album != nil {
defer func() {
if err := streamUpdateStats(c.DB, user.ID, track.Album.ID, time.Now()); err != nil {
log.Printf("error updating track status: %v", err)
@@ -294,7 +275,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
}()
}
if pe, ok := file.(*db.PodcastEpisode); ok {
if pe, ok := audioFile.(*db.PodcastEpisode); ok {
defer func() {
if err := streamUpdatePodcastEpisodeStats(c.DB, pe.ID); err != nil {
log.Printf("error updating podcast episode status: %v", err)
@@ -305,8 +286,8 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
maxBitRate, _ := params.GetInt("maxBitRate")
format, _ := params.Get("format")
if format == "raw" || maxBitRate >= file.AudioBitrate() {
http.ServeFile(w, r, audioPath)
if format == "raw" || maxBitRate >= audioFile.AudioBitrate() {
http.ServeFile(w, r, file.AbsPath())
return nil
}
@@ -315,7 +296,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
return spec.NewError(0, "couldn't find transcode preference: %v", err)
}
if pref == nil {
http.ServeFile(w, r, audioPath)
http.ServeFile(w, r, file.AbsPath())
return nil
}
@@ -330,7 +311,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
log.Printf("trancoding to %q with max bitrate %dk", profile.MIME(), profile.BitRate())
w.Header().Set("Content-Type", profile.MIME())
if err := c.Transcoder.Transcode(r.Context(), profile, audioPath, w); err != nil && !errors.Is(err, transcode.ErrFFmpegKilled) {
if err := c.Transcoder.Transcode(r.Context(), profile, file.AbsPath(), w); err != nil && !errors.Is(err, transcode.ErrFFmpegKilled) {
return spec.NewError(0, "error transcoding: %v", err)
}