From 6e6404af73357b4abcf0d45d2e8114f4404d1b5c Mon Sep 17 00:00:00 2001 From: dertasiu <11269785+dertasiu@users.noreply.github.com> Date: Wed, 2 Nov 2022 23:10:23 +0100 Subject: [PATCH] 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 --- server/ctrlsubsonic/ctrl.go | 6 +-- server/ctrlsubsonic/handlers_by_folder.go | 21 +++++++++- server/ctrlsubsonic/handlers_by_tags.go | 46 ++++++++++++++++++++-- server/ctrlsubsonic/handlers_common.go | 12 ++++++ server/ctrlsubsonic/handlers_playlist.go | 14 +++++-- server/ctrlsubsonic/handlers_raw.go | 12 ++++++ server/ctrlsubsonic/spec/spec.go | 48 ++++++++++++----------- transcode/transcode.go | 24 ++++++------ 8 files changed, 137 insertions(+), 46 deletions(-) diff --git a/server/ctrlsubsonic/ctrl.go b/server/ctrlsubsonic/ctrl.go index ae549f5..5159fda 100644 --- a/server/ctrlsubsonic/ctrl.go +++ b/server/ctrlsubsonic/ctrl.go @@ -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" ) diff --git a/server/ctrlsubsonic/handlers_by_folder.go b/server/ctrlsubsonic/handlers_by_folder.go index 7ffbb46..72836ab 100644 --- a/server/ctrlsubsonic/handlers_by_folder.go +++ b/server/ctrlsubsonic/handlers_by_folder.go @@ -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() diff --git a/server/ctrlsubsonic/handlers_by_tags.go b/server/ctrlsubsonic/handlers_by_tags.go index 55d687e..4422b6b 100644 --- a/server/ctrlsubsonic/handlers_by_tags.go +++ b/server/ctrlsubsonic/handlers_by_tags.go @@ -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 } diff --git a/server/ctrlsubsonic/handlers_common.go b/server/ctrlsubsonic/handlers_common.go index 602f35f..281c969 100644 --- a/server/ctrlsubsonic/handlers_common.go +++ b/server/ctrlsubsonic/handlers_common.go @@ -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 } diff --git a/server/ctrlsubsonic/handlers_playlist.go b/server/ctrlsubsonic/handlers_playlist.go index 343ea97..7e77b0e 100644 --- a/server/ctrlsubsonic/handlers_playlist.go +++ b/server/ctrlsubsonic/handlers_playlist.go @@ -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 } diff --git a/server/ctrlsubsonic/handlers_raw.go b/server/ctrlsubsonic/handlers_raw.go index 2c1cdc0..e6f009d 100644 --- a/server/ctrlsubsonic/handlers_raw.go +++ b/server/ctrlsubsonic/handlers_raw.go @@ -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 diff --git a/server/ctrlsubsonic/spec/spec.go b/server/ctrlsubsonic/spec/spec.go index e993612..397f917 100644 --- a/server/ctrlsubsonic/spec/spec.go +++ b/server/ctrlsubsonic/spec/spec.go @@ -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"` diff --git a/transcode/transcode.go b/transcode/transcode.go index fdfd4e5..5209e56 100644 --- a/transcode/transcode.go +++ b/transcode/transcode.go @@ -31,8 +31,8 @@ var UserProfiles = map[string]Profile{ // Store as simple strings, since we may let the user provide their own profiles soon var ( - MP3 = NewProfile("audio/mpeg", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libmp3lame -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 mp3 -`) - MP3RG = NewProfile("audio/mpeg", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libmp3lame -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 mp3 -`) + MP3 = NewProfile("audio/mpeg", "mp3", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libmp3lame -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 mp3 -`) + MP3RG = NewProfile("audio/mpeg", "mp3", 128, `ffmpeg -v 0 -i -ss -map 0:a:0 -vn -b:a -c:a libmp3lame -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 mp3 -`) // this sets a baseline gain which results in the final track being +3~5dB louder than // Foobar2000's default ReplayGain target volume. @@ -45,14 +45,14 @@ var ( // on my Ryzen 3600 to transcode an 8-minute FLAC with 2x upsample and RG applied. // // -- @spijet - OpusCar = NewProfile("audio/ogg", 96, `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" -f opus -`) - Opus = NewProfile("audio/ogg", 96, `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 -`) - OpusRG = NewProfile("audio/ogg", 96, `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 -`) - Opus128Car = NewProfile("audio/ogg", 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" -f opus -`) - Opus128 = NewProfile("audio/ogg", 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 -`) - Opus128RG = NewProfile("audio/ogg", 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 -`) + OpusCar = NewProfile("audio/ogg", "ogg", 96, `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" -f opus -`) + Opus = NewProfile("audio/ogg", "ogg", 96, `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 -`) + OpusRG = NewProfile("audio/ogg", "ogg", 96, `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 -`) + Opus128Car = NewProfile("audio/ogg", "ogg", 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" -f opus -`) + Opus128 = NewProfile("audio/ogg", "ogg", 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 -`) + Opus128RG = NewProfile("audio/ogg", "ogg", 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 -`) - PCM16le = NewProfile("audio/wav", 0, `ffmpeg -v 0 -i -ss -c:a pcm_s16le -ac 2 -f s16le -`) + PCM16le = NewProfile("audio/wav", "wav", 0, `ffmpeg -v 0 -i -ss -c:a pcm_s16le -ac 2 -f s16le -`) ) type BitRate int // kb/s @@ -61,15 +61,17 @@ type Profile struct { bitrate BitRate // the default bitrate, but the user can request a different one seek time.Duration mime string + suffix string exec string } func (p *Profile) BitRate() BitRate { return p.bitrate } func (p *Profile) Seek() time.Duration { return p.seek } +func (p *Profile) Suffix() string { return p.suffix } func (p *Profile) MIME() string { return p.mime } -func NewProfile(mime string, bitrate BitRate, exec string) Profile { - return Profile{mime: mime, bitrate: bitrate, exec: exec} +func NewProfile(mime string, suffix string, bitrate BitRate, exec string) Profile { + return Profile{mime: mime, suffix: suffix, bitrate: bitrate, exec: exec} } func WithBitrate(p Profile, bitRate BitRate) Profile {