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:
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
|
||||
Reference in New Issue
Block a user