fix: prevent raw file being returned when explicit transcode format is requested

When a client requests transcoding with an explicit format (e.g.,
format=opus) but no maxBitRate, buildLegacyClientInfo was adding a
direct play profile matching the source format. Since there was no
bitrate constraint to block it, MakeDecision would match the source
against the direct play profile and return the raw file instead of
transcoding. This fix only adds the direct play profile when no
explicit format was requested (bitrate-only downsampling) or when the
requested format matches the source format (allowing direct play when
no actual transcoding is needed).
This commit is contained in:
Deluan
2026-03-10 17:08:35 -04:00
parent 767744a301
commit 053a0fd6c0
3 changed files with 154 additions and 5 deletions
+10 -2
View File
@@ -2,6 +2,7 @@ package stream
import (
"context"
"strings"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
@@ -25,8 +26,15 @@ func buildLegacyClientInfo(mf *model.MediaFile, reqFormat string, reqBitRate int
}
if targetFormat != "" {
ci.DirectPlayProfiles = []DirectPlayProfile{
{Containers: []string{mf.Suffix}, AudioCodecs: []string{mf.AudioCodec()}, Protocols: []string{ProtocolHTTP}},
// Add a direct play profile for the source format when no explicit
// format was requested (bitrate-only downsampling) or when the
// requested format matches the source. When the client explicitly
// requests a different format, direct play must not match the
// source — otherwise the source is returned untranscoded.
if reqFormat == "" || strings.EqualFold(reqFormat, mf.Suffix) {
ci.DirectPlayProfiles = []DirectPlayProfile{
{Containers: []string{mf.Suffix}, AudioCodecs: []string{mf.AudioCodec()}, Protocols: []string{ProtocolHTTP}},
}
}
ci.TranscodingProfiles = []Profile{
{Container: targetFormat, AudioCodec: targetFormat, Protocol: ProtocolHTTP},
+17 -3
View File
@@ -25,10 +25,25 @@ var _ = Describe("buildLegacyClientInfo", func() {
Expect(ci.TranscodingProfiles[0].Protocol).To(Equal(ProtocolHTTP))
Expect(ci.MaxAudioBitrate).To(BeZero())
Expect(ci.MaxTranscodingAudioBitrate).To(BeZero())
Expect(ci.DirectPlayProfiles).To(BeEmpty())
})
It("does not add direct play profile when explicit format differs from source (no bitrate)", func() {
ci := buildLegacyClientInfo(mf, "opus", 0)
Expect(ci.TranscodingProfiles).To(HaveLen(1))
Expect(ci.TranscodingProfiles[0].Container).To(Equal("opus"))
Expect(ci.DirectPlayProfiles).To(BeEmpty())
})
It("adds direct play profile when explicit format matches source format", func() {
ci := buildLegacyClientInfo(mf, "flac", 0)
Expect(ci.TranscodingProfiles).To(HaveLen(1))
Expect(ci.TranscodingProfiles[0].Container).To(Equal("flac"))
Expect(ci.DirectPlayProfiles).To(HaveLen(1))
Expect(ci.DirectPlayProfiles[0].Containers).To(Equal([]string{"flac"}))
Expect(ci.DirectPlayProfiles[0].AudioCodecs).To(Equal([]string{mf.AudioCodec()}))
Expect(ci.DirectPlayProfiles[0].Protocols).To(Equal([]string{ProtocolHTTP}))
})
It("sets transcoding profile and bitrate for explicit format with bitrate", func() {
@@ -39,8 +54,7 @@ var _ = Describe("buildLegacyClientInfo", func() {
Expect(ci.TranscodingProfiles[0].AudioCodec).To(Equal("mp3"))
Expect(ci.MaxAudioBitrate).To(Equal(192))
Expect(ci.MaxTranscodingAudioBitrate).To(Equal(192))
Expect(ci.DirectPlayProfiles).To(HaveLen(1))
Expect(ci.DirectPlayProfiles[0].Containers).To(Equal([]string{"flac"}))
Expect(ci.DirectPlayProfiles).To(BeEmpty())
})
It("returns direct play profile when no format and no bitrate", func() {