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"`
|
||||
|
||||
@@ -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -map 0:a:0 -vn -b:a <bitrate> -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 <file> -ss <seek> -c:a pcm_s16le -ac 2 -f s16le -`)
|
||||
PCM16le = NewProfile("audio/wav", "wav", 0, `ffmpeg -v 0 -i <file> -ss <seek> -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 {
|
||||
|
||||
Reference in New Issue
Block a user