feat(subsonic): return transcoded mime and transcoded suffix in subsonic responses

fixes #106

* Added support to TranscodedContentType and TranscodedSuffix

* Make sure that we have a profile

* Fix linting

* Fixed use of NewTCTrackByFolder instead of NewTrackByTags in handlers_by_tags.go

simplify a bit
This commit is contained in:
dertasiu
2022-11-02 23:10:23 +01:00
committed by sentriz
parent 692ec68282
commit 6e6404af73
8 changed files with 137 additions and 46 deletions

View File

@@ -9,12 +9,12 @@ import (
"log"
"net/http"
"go.senan.xyz/gonic/server/ctrlbase"
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
"go.senan.xyz/gonic/jukebox"
"go.senan.xyz/gonic/podcasts"
"go.senan.xyz/gonic/scrobble"
"go.senan.xyz/gonic/server/ctrlbase"
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
"go.senan.xyz/gonic/transcode"
)

View File

@@ -95,6 +95,9 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response {
Preload("TrackRating", "user_id=?", user.ID).
Order("filename").
Find(&childTracks)
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for _, ch := range childTracks {
toAppend := spec.NewTCTrackByFolder(ch, folder)
if v, _ := params.Get("c"); v == "Jamstash" {
@@ -102,6 +105,8 @@ func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response {
toAppend.ContentType = "audio/mpeg"
toAppend.Suffix = "mp3"
}
toAppend.TranscodedContentType = transcodeMIME
toAppend.TranscodedSuffix = transcodeSuffix
childrenObj = append(childrenObj, toAppend)
}
// respond section
@@ -259,8 +264,14 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
if err := q.Find(&tracks).Error; err != nil {
return spec.NewError(0, "find tracks: %v", err)
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for _, t := range tracks {
results.Tracks = append(results.Tracks, spec.NewTCTrackByFolder(t, t.Album))
track := spec.NewTCTrackByFolder(t, t.Album)
track.TranscodedContentType = transcodeMIME
track.TranscodedSuffix = transcodeSuffix
results.Tracks = append(results.Tracks, track)
}
sub := spec.NewResponse()
@@ -335,8 +346,14 @@ func (c *Controller) ServeGetStarred(r *http.Request) *spec.Response {
if err := q.Find(&tracks).Error; err != nil {
return spec.NewError(0, "find tracks: %v", err)
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for _, t := range tracks {
results.Tracks = append(results.Tracks, spec.NewTCTrackByFolder(t, t.Album))
track := spec.NewTCTrackByFolder(t, t.Album)
track.TranscodedContentType = transcodeMIME
track.TranscodedSuffix = transcodeSuffix
results.Tracks = append(results.Tracks, track)
}
sub := spec.NewResponse()

View File

@@ -117,8 +117,13 @@ func (c *Controller) ServeGetAlbum(r *http.Request) *spec.Response {
sub := spec.NewResponse()
sub.Album = spec.NewAlbumByTags(album, album.TagArtist)
sub.Album.Tracks = make([]*spec.TrackChild, len(album.Tracks))
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(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
}
return sub
}
@@ -270,8 +275,14 @@ func (c *Controller) ServeSearchThree(r *http.Request) *spec.Response {
if err := q.Find(&tracks).Error; err != nil {
return spec.NewError(0, "find tracks: %v", err)
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for _, t := range tracks {
results.Tracks = append(results.Tracks, spec.NewTrackByTags(t, t.Album))
track := spec.NewTrackByTags(t, t.Album)
track.TranscodedContentType = transcodeMIME
track.TranscodedSuffix = transcodeSuffix
results.Tracks = append(results.Tracks, track)
}
sub := spec.NewResponse()
@@ -411,9 +422,15 @@ func (c *Controller) ServeGetSongsByGenre(r *http.Request) *spec.Response {
sub.TracksByGenre = &spec.TracksByGenre{
List: make([]*spec.TrackChild, len(tracks)),
}
for i, track := range tracks {
sub.TracksByGenre.List[i] = spec.NewTrackByTags(track, track.Album)
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(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
}
return sub
}
@@ -475,8 +492,14 @@ func (c *Controller) ServeGetStarredTwo(r *http.Request) *spec.Response {
if err := q.Find(&tracks).Error; err != nil {
return spec.NewError(0, "find tracks: %v", err)
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for _, t := range tracks {
results.Tracks = append(results.Tracks, spec.NewTrackByTags(t, t.Album))
track := spec.NewTrackByTags(t, t.Album)
track.TranscodedContentType = transcodeMIME
track.TranscodedSuffix = transcodeSuffix
results.Tracks = append(results.Tracks, track)
}
sub := spec.NewResponse()
@@ -546,8 +569,13 @@ func (c *Controller) ServeGetTopSongs(r *http.Request) *spec.Response {
sub.TopSongs = &spec.TopSongs{
Tracks: make([]*spec.TrackChild, len(tracks)),
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for i, track := range tracks {
sub.TopSongs.Tracks[i] = spec.NewTrackByTags(track, track.Album)
sub.TopSongs.Tracks[i].TranscodedContentType = transcodeMIME
sub.TopSongs.Tracks[i].TranscodedSuffix = transcodeSuffix
}
return sub
}
@@ -612,8 +640,13 @@ func (c *Controller) ServeGetSimilarSongs(r *http.Request) *spec.Response {
sub.SimilarSongs = &spec.SimilarSongs{
Tracks: make([]*spec.TrackChild, len(tracks)),
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(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
}
return sub
}
@@ -676,8 +709,13 @@ func (c *Controller) ServeGetSimilarSongsTwo(r *http.Request) *spec.Response {
sub.SimilarSongsTwo = &spec.SimilarSongsTwo{
Tracks: make([]*spec.TrackChild, len(tracks)),
}
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(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
}
return sub
}

View File

@@ -127,6 +127,7 @@ func (c *Controller) ServeNotFound(r *http.Request) *spec.Response {
}
func (c *Controller) ServeGetPlayQueue(r *http.Request) *spec.Response {
params := r.Context().Value(CtxParams).(params.Params)
user := r.Context().Value(CtxUser).(*db.User)
var queue db.PlayQueue
err := c.DB.
@@ -143,8 +144,12 @@ func (c *Controller) ServeGetPlayQueue(r *http.Request) *spec.Response {
sub.PlayQueue.Current = queue.CurrentSID()
sub.PlayQueue.Changed = queue.UpdatedAt
sub.PlayQueue.ChangedBy = queue.ChangedBy
trackIDs := queue.GetItems()
sub.PlayQueue.List = make([]*spec.TrackChild, len(trackIDs))
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for i, id := range trackIDs {
track := db.Track{}
c.DB.
@@ -154,6 +159,8 @@ 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
}
return sub
}
@@ -241,8 +248,13 @@ func (c *Controller) ServeGetRandomSongs(r *http.Request) *spec.Response {
sub := spec.NewResponse()
sub.RandomTracks = &spec.RandomTracks{}
sub.RandomTracks.List = make([]*spec.TrackChild, len(tracks))
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(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
}
return sub
}

View File

@@ -13,7 +13,7 @@ import (
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
)
func playlistRender(c *Controller, playlist *db.Playlist) *spec.Playlist {
func playlistRender(c *Controller, playlist *db.Playlist, params params.Params) *spec.Playlist {
user := &db.User{}
c.DB.Where("id=?", playlist.UserID).Find(user)
@@ -29,6 +29,9 @@ func playlistRender(c *Controller, playlist *db.Playlist) *spec.Playlist {
trackIDs := playlist.GetItems()
resp.List = make([]*spec.TrackChild, len(trackIDs))
transcodeMIME, transcodeSuffix := streamGetTransPrefProfile(c.DB, user.ID, params.GetOr("c", ""))
for i, id := range trackIDs {
track := db.Track{}
err := c.DB.
@@ -44,12 +47,15 @@ func playlistRender(c *Controller, playlist *db.Playlist) *spec.Playlist {
continue
}
resp.List[i] = spec.NewTCTrackByFolder(&track, track.Album)
resp.List[i].TranscodedContentType = transcodeMIME
resp.List[i].TranscodedSuffix = transcodeSuffix
resp.Duration += track.Length
}
return resp
}
func (c *Controller) ServeGetPlaylists(r *http.Request) *spec.Response {
params := r.Context().Value(CtxParams).(params.Params)
user := r.Context().Value(CtxUser).(*db.User)
var playlists []*db.Playlist
c.DB.Where("user_id=?", user.ID).Or("is_public=?", true).Find(&playlists)
@@ -58,7 +64,7 @@ func (c *Controller) ServeGetPlaylists(r *http.Request) *spec.Response {
List: make([]*spec.Playlist, len(playlists)),
}
for i, playlist := range playlists {
sub.Playlists.List[i] = playlistRender(c, playlist)
sub.Playlists.List[i] = playlistRender(c, playlist, params)
}
return sub
}
@@ -78,7 +84,7 @@ func (c *Controller) ServeGetPlaylist(r *http.Request) *spec.Response {
return spec.NewError(70, "playlist with id `%d` not found", playlistID)
}
sub := spec.NewResponse()
sub.Playlist = playlistRender(c, &playlist)
sub.Playlist = playlistRender(c, &playlist, params)
return sub
}
@@ -114,7 +120,7 @@ func (c *Controller) ServeCreatePlaylist(r *http.Request) *spec.Response {
c.DB.Save(playlist)
sub := spec.NewResponse()
sub.Playlist = playlistRender(c, &playlist)
sub.Playlist = playlistRender(c, &playlist, params)
return sub
}

View File

@@ -43,6 +43,18 @@ 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)
if pref == nil {
return "", ""
}
profile, ok := transcode.UserProfiles[pref.Profile]
if !ok {
return "", ""
}
return profile.MIME(), profile.Suffix()
}
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

View File

@@ -8,6 +8,8 @@ import (
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
)
// https://web.archive.org/web/20220707025402/https://www.subsonic.org/pages/api.jsp
const (
apiVersion = "1.15.0"
xmlns = "http://subsonic.org/restapi"
@@ -135,28 +137,30 @@ type TracksByGenre struct {
}
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"`
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"`
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"`
// star / rating
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
UserRating int `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`