diff --git a/core/ffmpeg/ffmpeg.go b/core/ffmpeg/ffmpeg.go index d530891bb..bab3263a0 100644 --- a/core/ffmpeg/ffmpeg.go +++ b/core/ffmpeg/ffmpeg.go @@ -7,12 +7,12 @@ import ( "io" "os" "os/exec" - "slices" "strconv" "strings" "sync" "github.com/navidrome/navidrome/conf" + "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/log" ) @@ -172,19 +172,6 @@ func (j *ffCmd) wait() { _ = j.out.Close() } -// defaultCommands maps format to the known default command templates. -// Used to detect whether a user has customized their transcoding command. -// Multiple entries per format support smooth upgrades (e.g. aac changed from adts to ipod). -var defaultCommands = map[string][]string{ - "mp3": {"ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -f mp3 -"}, - "opus": {"ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a libopus -f opus -"}, - "aac": { - "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f ipod -movflags frag_keyframe+empty_moov -", - "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f adts -", // legacy default - }, - "flac": {"ffmpeg -i %s -ss %t -map 0:a:0 -v 0 -c:a flac -f flac -"}, -} - // formatCodecMap maps target format to ffmpeg codec flag. var formatCodecMap = map[string]string{ "mp3": "libmp3lame", @@ -201,9 +188,18 @@ var formatOutputMap = map[string]string{ "flac": "flac", } -// isDefaultCommand returns true if the command matches any known default for this format. +// defaultCommands is used to detect whether a user has customized their transcoding command. +var defaultCommands = func() map[string]string { + m := make(map[string]string, len(consts.DefaultTranscodings)) + for _, t := range consts.DefaultTranscodings { + m[t.TargetFormat] = t.Command + } + return m +}() + +// isDefaultCommand returns true if the command matches the known default for this format. func isDefaultCommand(format, command string) bool { - return slices.Contains(defaultCommands[format], command) + return defaultCommands[format] == command } // buildDynamicArgs programmatically constructs ffmpeg arguments for known formats, diff --git a/core/ffmpeg/ffmpeg_test.go b/core/ffmpeg/ffmpeg_test.go index dbd4c1123..1649700d5 100644 --- a/core/ffmpeg/ffmpeg_test.go +++ b/core/ffmpeg/ffmpeg_test.go @@ -88,9 +88,6 @@ var _ = Describe("ffmpeg", func() { It("returns true for known default aac command", func() { Expect(isDefaultCommand("aac", "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f ipod -movflags frag_keyframe+empty_moov -")).To(BeTrue()) }) - It("returns true for legacy default aac command", func() { - Expect(isDefaultCommand("aac", "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f adts -")).To(BeTrue()) - }) It("returns true for known default flac command", func() { Expect(isDefaultCommand("flac", "ffmpeg -i %s -ss %t -map 0:a:0 -v 0 -c:a flac -f flac -")).To(BeTrue()) }) diff --git a/db/migrations/20260205120000_add_codec_and_update_transcodings.go b/db/migrations/20260205120000_add_codec_and_update_transcodings.go new file mode 100644 index 000000000..c481c9b42 --- /dev/null +++ b/db/migrations/20260205120000_add_codec_and_update_transcodings.go @@ -0,0 +1,63 @@ +package migrations + +import ( + "context" + "database/sql" + + "github.com/navidrome/navidrome/model/id" + "github.com/pressly/goose/v3" +) + +func init() { + goose.AddMigrationContext(upAddCodecAndUpdateTranscodings, downAddCodecAndUpdateTranscodings) +} + +func upAddCodecAndUpdateTranscodings(_ context.Context, tx *sql.Tx) error { + // Add codec column to media_file. + _, err := tx.Exec(`ALTER TABLE media_file ADD COLUMN codec VARCHAR(255) DEFAULT '' NOT NULL`) + if err != nil { + return err + } + _, err = tx.Exec(`CREATE INDEX IF NOT EXISTS media_file_codec ON media_file(codec)`) + if err != nil { + return err + } + + // Update old AAC default (adts) to new default (ipod with fragmented MP4). + // Only affects users who still have the unmodified old default command. + _, err = tx.Exec( + `UPDATE transcoding SET command = ? WHERE target_format = 'aac' AND command = ?`, + "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f ipod -movflags frag_keyframe+empty_moov -", + "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f adts -", + ) + if err != nil { + return err + } + + // Add FLAC transcoding for existing installations that were seeded before FLAC was added. + var count int + err = tx.QueryRow("SELECT COUNT(*) FROM transcoding WHERE target_format = 'flac'").Scan(&count) + if err != nil { + return err + } + if count == 0 { + _, err = tx.Exec( + "INSERT INTO transcoding (id, name, target_format, default_bit_rate, command) VALUES (?, ?, ?, ?, ?)", + id.NewRandom(), "flac audio", "flac", 0, + "ffmpeg -i %s -ss %t -map 0:a:0 -v 0 -c:a flac -f flac -", + ) + if err != nil { + return err + } + } + return nil +} + +func downAddCodecAndUpdateTranscodings(_ context.Context, tx *sql.Tx) error { + _, err := tx.Exec(`DROP INDEX IF EXISTS media_file_codec`) + if err != nil { + return err + } + _, err = tx.Exec(`ALTER TABLE media_file DROP COLUMN codec`) + return err +} diff --git a/db/migrations/20260205120000_add_codec_to_media_file.sql b/db/migrations/20260205120000_add_codec_to_media_file.sql deleted file mode 100644 index 724d85ebf..000000000 --- a/db/migrations/20260205120000_add_codec_to_media_file.sql +++ /dev/null @@ -1,11 +0,0 @@ --- +goose Up --- +goose StatementBegin -ALTER TABLE media_file ADD COLUMN codec VARCHAR(255) DEFAULT '' NOT NULL; -CREATE INDEX IF NOT EXISTS media_file_codec ON media_file(codec); --- +goose StatementEnd - --- +goose Down --- +goose StatementBegin -DROP INDEX IF EXISTS media_file_codec; -ALTER TABLE media_file DROP COLUMN codec; --- +goose StatementEnd