diff --git a/server/ctrlsubsonic/handlers_by_folder.go b/server/ctrlsubsonic/handlers_by_folder.go index 6dd83a3..677dbfb 100644 --- a/server/ctrlsubsonic/handlers_by_folder.go +++ b/server/ctrlsubsonic/handlers_by_folder.go @@ -96,7 +96,7 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response { Order("filename"). Find(&childTracks) - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, ch := range childTracks { toAppend := spec.NewTCTrackByFolder(ch, folder) @@ -105,8 +105,7 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response { toAppend.ContentType = "audio/mpeg" toAppend.Suffix = "mp3" } - toAppend.TranscodedContentType = transcodeMIME - toAppend.TranscodedSuffix = transcodeSuffix + toAppend.TranscodeMeta = transcodeMeta childrenObj = append(childrenObj, toAppend) } // respond section @@ -270,12 +269,11 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response { return spec.NewError(0, "find tracks: %v", err) } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, t := range tracks { track := spec.NewTCTrackByFolder(t, t.Album) - track.TranscodedContentType = transcodeMIME - track.TranscodedSuffix = transcodeSuffix + track.TranscodeMeta = transcodeMeta results.Tracks = append(results.Tracks, track) } @@ -352,12 +350,11 @@ func (c *Controller) ServeGetStarred(r *http.Request) *spec.Response { return spec.NewError(0, "find tracks: %v", err) } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, t := range tracks { track := spec.NewTCTrackByFolder(t, t.Album) - track.TranscodedContentType = transcodeMIME - track.TranscodedSuffix = transcodeSuffix + track.TranscodeMeta = transcodeMeta results.Tracks = append(results.Tracks, track) } diff --git a/server/ctrlsubsonic/handlers_by_tags.go b/server/ctrlsubsonic/handlers_by_tags.go index c308aec..3612d94 100644 --- a/server/ctrlsubsonic/handlers_by_tags.go +++ b/server/ctrlsubsonic/handlers_by_tags.go @@ -120,12 +120,11 @@ func (c *Controller) ServeGetAlbum(r *http.Request) *spec.Response { sub.Album = spec.NewAlbumByTags(album, album.Artists) sub.Album.Tracks = make([]*spec.TrackChild, len(album.Tracks)) - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, track := range album.Tracks { sub.Album.Tracks[i] = spec.NewTrackByTags(track, album) - sub.Album.Tracks[i].TranscodedContentType = transcodeMIME - sub.Album.Tracks[i].TranscodedSuffix = transcodeSuffix + sub.Album.Tracks[i].TranscodeMeta = transcodeMeta } return sub } @@ -288,12 +287,11 @@ func (c *Controller) ServeSearchThree(r *http.Request) *spec.Response { return spec.NewError(0, "find tracks: %v", err) } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, t := range tracks { track := spec.NewTrackByTags(t, t.Album) - track.TranscodedContentType = transcodeMIME - track.TranscodedSuffix = transcodeSuffix + track.TranscodeMeta = transcodeMeta results.Tracks = append(results.Tracks, track) } @@ -430,12 +428,11 @@ func (c *Controller) ServeGetSongsByGenre(r *http.Request) *spec.Response { List: make([]*spec.TrackChild, len(tracks)), } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, t := range tracks { sub.TracksByGenre.List[i] = spec.NewTrackByTags(t, t.Album) - sub.TracksByGenre.List[i].TranscodedContentType = transcodeMIME - sub.TracksByGenre.List[i].TranscodedSuffix = transcodeSuffix + sub.TracksByGenre.List[i].TranscodeMeta = transcodeMeta } return sub @@ -503,12 +500,11 @@ func (c *Controller) ServeGetStarredTwo(r *http.Request) *spec.Response { return spec.NewError(0, "find tracks: %v", err) } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, t := range tracks { track := spec.NewTrackByTags(t, t.Album) - track.TranscodedContentType = transcodeMIME - track.TranscodedSuffix = transcodeSuffix + track.TranscodeMeta = transcodeMeta results.Tracks = append(results.Tracks, track) } @@ -580,12 +576,11 @@ func (c *Controller) ServeGetTopSongs(r *http.Request) *spec.Response { return sub } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, track := range tracks { tc := spec.NewTrackByTags(track, track.Album) - tc.TranscodedContentType = transcodeMIME - tc.TranscodedSuffix = transcodeSuffix + tc.TranscodeMeta = transcodeMeta sub.TopSongs.Tracks = append(sub.TopSongs.Tracks, tc) } return sub @@ -650,12 +645,11 @@ func (c *Controller) ServeGetSimilarSongs(r *http.Request) *spec.Response { Tracks: make([]*spec.TrackChild, len(tracks)), } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, track := range tracks { sub.SimilarSongs.Tracks[i] = spec.NewTrackByTags(track, track.Album) - sub.SimilarSongs.Tracks[i].TranscodedContentType = transcodeMIME - sub.SimilarSongs.Tracks[i].TranscodedSuffix = transcodeSuffix + sub.SimilarSongs.Tracks[i].TranscodeMeta = transcodeMeta } return sub } @@ -721,12 +715,10 @@ func (c *Controller) ServeGetSimilarSongsTwo(r *http.Request) *spec.Response { Tracks: make([]*spec.TrackChild, len(tracks)), } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) - + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, track := range tracks { sub.SimilarSongsTwo.Tracks[i] = spec.NewTrackByTags(track, track.Album) - sub.SimilarSongsTwo.Tracks[i].TranscodedContentType = transcodeMIME - sub.SimilarSongsTwo.Tracks[i].TranscodedSuffix = transcodeSuffix + sub.SimilarSongsTwo.Tracks[i].TranscodeMeta = transcodeMeta } return sub } diff --git a/server/ctrlsubsonic/handlers_common.go b/server/ctrlsubsonic/handlers_common.go index f617f0a..270725b 100644 --- a/server/ctrlsubsonic/handlers_common.go +++ b/server/ctrlsubsonic/handlers_common.go @@ -165,7 +165,7 @@ func (c *Controller) ServeGetPlayQueue(r *http.Request) *spec.Response { trackIDs := queue.GetItems() sub.PlayQueue.List = make([]*spec.TrackChild, len(trackIDs)) - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, id := range trackIDs { switch id.Type { @@ -178,8 +178,7 @@ func (c *Controller) ServeGetPlayQueue(r *http.Request) *spec.Response { Preload("TrackRating", "user_id=?", user.ID). Find(&track) sub.PlayQueue.List[i] = spec.NewTCTrackByFolder(&track, track.Album) - sub.PlayQueue.List[i].TranscodedContentType = transcodeMIME - sub.PlayQueue.List[i].TranscodedSuffix = transcodeSuffix + sub.PlayQueue.List[i].TranscodeMeta = transcodeMeta case specid.PodcastEpisode: pe := db.PodcastEpisode{} c.DB. @@ -190,8 +189,7 @@ func (c *Controller) ServeGetPlayQueue(r *http.Request) *spec.Response { Where("id=?", pe.PodcastID). Find(&p) sub.PlayQueue.List[i] = spec.NewTCPodcastEpisode(&pe, &p) - sub.PlayQueue.List[i].TranscodedContentType = transcodeMIME - sub.PlayQueue.List[i].TranscodedSuffix = transcodeSuffix + sub.PlayQueue.List[i].TranscodeMeta = transcodeMeta } } return sub @@ -243,8 +241,14 @@ func (c *Controller) ServeGetSong(r *http.Request) *spec.Response { if errors.Is(err, gorm.ErrRecordNotFound) { return spec.NewError(10, "couldn't find a track with that id") } + + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) + sub := spec.NewResponse() sub.Track = spec.NewTrackByTags(&track, track.Album) + + sub.Track.TranscodeMeta = transcodeMeta + return sub } @@ -280,12 +284,11 @@ func (c *Controller) ServeGetRandomSongs(r *http.Request) *spec.Response { sub.RandomTracks = &spec.RandomTracks{} sub.RandomTracks.List = make([]*spec.TrackChild, len(tracks)) - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for i, track := range tracks { sub.RandomTracks.List[i] = spec.NewTrackByTags(track, track.Album) - sub.RandomTracks.List[i].TranscodedContentType = transcodeMIME - sub.RandomTracks.List[i].TranscodedSuffix = transcodeSuffix + sub.RandomTracks.List[i].TranscodeMeta = transcodeMeta } return sub } diff --git a/server/ctrlsubsonic/handlers_playlist.go b/server/ctrlsubsonic/handlers_playlist.go index bc3c101..b6e1c14 100644 --- a/server/ctrlsubsonic/handlers_playlist.go +++ b/server/ctrlsubsonic/handlers_playlist.go @@ -204,7 +204,7 @@ func playlistRender(c *Controller, params params.Params, playlistID string, play return resp, nil } - transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", "")) + transcodeMeta := streamGetTranscodeMeta(c.DB, user.ID, params.GetOr("c", "")) for _, path := range playlist.Items { file, err := specidpaths.Lookup(c.DB, PathsOf(c.MusicPaths), c.PodcastsPath, path) @@ -236,8 +236,7 @@ func playlistRender(c *Controller, params params.Params, playlistID string, play default: continue } - trch.TranscodedContentType = transcodeMIME - trch.TranscodedSuffix = transcodeSuffix + trch.TranscodeMeta = transcodeMeta resp.List = append(resp.List, trch) } diff --git a/server/ctrlsubsonic/handlers_raw.go b/server/ctrlsubsonic/handlers_raw.go index c21ad33..95ca645 100644 --- a/server/ctrlsubsonic/handlers_raw.go +++ b/server/ctrlsubsonic/handlers_raw.go @@ -30,7 +30,7 @@ import ( // b) return a non-nil spec.Response // _but not both_ -func streamGetTransPref(dbc *db.DB, userID int, client string) (*db.TranscodePreference, error) { +func streamGetTransodePreference(dbc *db.DB, userID int, client string) (*db.TranscodePreference, error) { var pref db.TranscodePreference err := dbc. Where("user_id=?", userID). @@ -47,16 +47,19 @@ func streamGetTransPref(dbc *db.DB, userID int, client string) (*db.TranscodePre return &pref, nil } -func streamGetTransPrefProfile(dbc *db.DB, userID int, client string) (mime string, suffix string) { - pref, _ := streamGetTransPref(dbc, userID, client) +func streamGetTranscodeMeta(dbc *db.DB, userID int, client string) spec.TranscodeMeta { + pref, _ := streamGetTransodePreference(dbc, userID, client) if pref == nil { - return "", "" + return spec.TranscodeMeta{} } profile, ok := transcode.UserProfiles[pref.Profile] if !ok { - return "", "" + return spec.TranscodeMeta{} + } + return spec.TranscodeMeta{ + TranscodedContentType: profile.MIME(), + TranscodedSuffix: profile.Suffix(), } - return profile.MIME(), profile.Suffix() } var errUnknownMediaType = fmt.Errorf("media type is unknown") @@ -287,7 +290,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R return nil } - pref, err := streamGetTransPref(c.DB, user.ID, params.GetOr("c", "")) + pref, err := streamGetTransodePreference(c.DB, user.ID, params.GetOr("c", "")) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return spec.NewError(0, "couldn't find transcode preference: %v", err) } diff --git a/server/ctrlsubsonic/spec/spec.go b/server/ctrlsubsonic/spec/spec.go index 6a500c8..2ef1ce5 100644 --- a/server/ctrlsubsonic/spec/spec.go +++ b/server/ctrlsubsonic/spec/spec.go @@ -151,35 +151,40 @@ type TracksByGenre struct { List []*TrackChild `xml:"song" json:"song"` } +type TranscodeMeta struct { + TranscodedContentType string `xml:"transcodedContentType,attr,omitempty" json:"transcodedContentType,omitempty"` + TranscodedSuffix string `xml:"transcodedSuffix,attr,omitempty" json:"transcodedSuffix,omitempty"` +} + type TrackChild struct { - ID *specid.ID `xml:"id,attr,omitempty" json:"id,omitempty"` - Album string `xml:"album,attr,omitempty" json:"album,omitempty"` - AlbumID *specid.ID `xml:"albumId,attr,omitempty" json:"albumId,omitempty"` - Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"` - ArtistID *specid.ID `xml:"artistId,attr,omitempty" json:"artistId,omitempty"` - Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate,omitempty"` - ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"` - TranscodedContentType string `xml:"transcodedContentType,attr,omitempty" json:"transcodedContentType,omitempty"` - CoverID *specid.ID `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"` - CreatedAt time.Time `xml:"created,attr,omitempty" json:"created,omitempty"` - Duration int `xml:"duration,attr,omitempty" json:"duration,omitempty"` - Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"` - IsDir bool `xml:"isDir,attr" json:"isDir"` - IsVideo bool `xml:"isVideo,attr" json:"isVideo"` - ParentID *specid.ID `xml:"parent,attr,omitempty" json:"parent,omitempty"` - Path string `xml:"path,attr,omitempty" json:"path,omitempty"` - Size int `xml:"size,attr,omitempty" json:"size,omitempty"` - Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"` - TranscodedSuffix string `xml:"transcodedSuffix,attr,omitempty" json:"transcodedSuffix,omitempty"` - Title string `xml:"title,attr" json:"title"` - TrackNumber int `xml:"track,attr,omitempty" json:"track,omitempty"` - DiscNumber int `xml:"discNumber,attr,omitempty" json:"discNumber,omitempty"` - Type string `xml:"type,attr,omitempty" json:"type,omitempty"` - Year int `xml:"year,attr,omitempty" json:"year,omitempty"` + ID *specid.ID `xml:"id,attr,omitempty" json:"id,omitempty"` + Album string `xml:"album,attr,omitempty" json:"album,omitempty"` + AlbumID *specid.ID `xml:"albumId,attr,omitempty" json:"albumId,omitempty"` + Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"` + ArtistID *specid.ID `xml:"artistId,attr,omitempty" json:"artistId,omitempty"` + Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate,omitempty"` + ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"` + CoverID *specid.ID `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"` + CreatedAt time.Time `xml:"created,attr,omitempty" json:"created,omitempty"` + Duration int `xml:"duration,attr,omitempty" json:"duration,omitempty"` + Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"` + IsDir bool `xml:"isDir,attr" json:"isDir"` + IsVideo bool `xml:"isVideo,attr" json:"isVideo"` + ParentID *specid.ID `xml:"parent,attr,omitempty" json:"parent,omitempty"` + Path string `xml:"path,attr,omitempty" json:"path,omitempty"` + Size int `xml:"size,attr,omitempty" json:"size,omitempty"` + Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"` + Title string `xml:"title,attr" json:"title"` + TrackNumber int `xml:"track,attr,omitempty" json:"track,omitempty"` + DiscNumber int `xml:"discNumber,attr,omitempty" json:"discNumber,omitempty"` + Type string `xml:"type,attr,omitempty" json:"type,omitempty"` + Year int `xml:"year,attr,omitempty" json:"year,omitempty"` // star / rating Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"` UserRating int `xml:"userRating,attr,omitempty" json:"userRating,omitempty"` AverageRating string `xml:"averageRating,attr,omitempty" json:"averageRating,omitempty"` + + TranscodeMeta } type Artists struct { @@ -292,7 +297,7 @@ type ArtistInfo struct { MediumImageURL string `xml:"mediumImageUrl" json:"mediumImageUrl"` LargeImageURL string `xml:"largeImageUrl" json:"largeImageUrl"` ArtistImageURL string `xml:"artistImageUrl" json:"artistImageUrl"` // not sure where this comes from but other clients seem to expect it - Similar []*Artist `xml:"similarArtist,omitempty" json:"similarArtist,omitempty"` + Similar []*Artist `xml:"similarArtist,omitempty" json:"similarArtist,omitempty"` } type Genres struct { diff --git a/transcode/transcode.go b/transcode/transcode.go index 3037f0b..d895ef0 100644 --- a/transcode/transcode.go +++ b/transcode/transcode.go @@ -20,7 +20,7 @@ type Transcoder interface { var UserProfiles = map[string]Profile{ "mp3": MP3, - "mp3_320": MP3320, + "mp3_320": MP3320, "mp3_rg": MP3RG, "opus_car": OpusRGLoud, "opus": Opus, @@ -45,7 +45,7 @@ var ( Opus128RG = NewProfile("audio/ogg", "opus", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libopus -vbr on -af "volume=replaygain=track:replaygain_preamp=6dB:replaygain_noclip=0, alimiter=level=disabled, asidedata=mode=delete:type=REPLAYGAIN" -metadata replaygain_album_gain= -metadata replaygain_album_peak= -metadata replaygain_track_gain= -metadata replaygain_track_peak= -metadata r128_album_gain= -metadata r128_track_gain= -f opus -`) Opus128RGLoud = NewProfile("audio/ogg", "opus", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libopus -vbr on -af "aresample=96000:resampler=soxr, volume=replaygain=track:replaygain_preamp=15dB:replaygain_noclip=0, alimiter=level=disabled, asidedata=mode=delete:type=REPLAYGAIN" -metadata replaygain_album_gain= -metadata replaygain_album_peak= -metadata replaygain_track_gain= -metadata replaygain_track_peak= -metadata r128_album_gain= -metadata r128_track_gain= -f opus -`) - Opus192 = NewProfile("audio/ogg", "opus", 192, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libopus -vbr on -f opus -`) + Opus192 = NewProfile("audio/ogg", "opus", 192, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libopus -vbr on -f opus -`) ) type BitRate uint // kilobits/s