mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
fix: small issues
Updated mock AAC transcoding command to use the new default (ipod with fragmented MP4) matching the migration, ensuring tests exercise the same buildDynamicArgs code path as production. Improved archiver test mock to match on the whole StreamRequest struct instead of decomposing fields, making it resilient to future field additions. Added named constants for JWT claim keys in the transcode token and wrapped ParseTranscodeParams errors with ErrTokenInvalid for consistency. Documented the IsLossless BitDepth fallback heuristic as temporary until Codec column is populated. Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
1d3f3fdca2
commit
004ebea7d0
@ -44,7 +44,7 @@ var _ = Describe("Archiver", func() {
|
||||
}}).Return(mfs, nil)
|
||||
|
||||
ds.On("MediaFile", mock.Anything).Return(mfRepo)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128, 0, 0, 0, 0).Return(io.NopCloser(strings.NewReader("test")), nil).Times(3)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, core.StreamRequest{Format: "mp3", BitRate: 128}).Return(io.NopCloser(strings.NewReader("test")), nil).Times(3)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
err := arch.ZipAlbum(context.Background(), "1", "mp3", 128, out)
|
||||
@ -73,7 +73,7 @@ var _ = Describe("Archiver", func() {
|
||||
}}).Return(mfs, nil)
|
||||
|
||||
ds.On("MediaFile", mock.Anything).Return(mfRepo)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128, 0, 0, 0, 0).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, core.StreamRequest{Format: "mp3", BitRate: 128}).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
err := arch.ZipArtist(context.Background(), "1", "mp3", 128, out)
|
||||
@ -104,7 +104,7 @@ var _ = Describe("Archiver", func() {
|
||||
}
|
||||
|
||||
sh.On("Load", mock.Anything, "1").Return(share, nil)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128, 0, 0, 0, 0).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, core.StreamRequest{Format: "mp3", BitRate: 128}).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
err := arch.ZipShare(context.Background(), "1", out)
|
||||
@ -136,7 +136,7 @@ var _ = Describe("Archiver", func() {
|
||||
plRepo := &mockPlaylistRepository{}
|
||||
plRepo.On("GetWithTracks", "1", true, false).Return(pls, nil)
|
||||
ds.On("Playlist", mock.Anything).Return(plRepo)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128, 0, 0, 0, 0).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
ms.On("DoStream", mock.Anything, mock.Anything, core.StreamRequest{Format: "mp3", BitRate: 128}).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
err := arch.ZipPlaylist(context.Background(), "1", "mp3", 128, out)
|
||||
@ -218,7 +218,7 @@ type mockMediaStreamer struct {
|
||||
}
|
||||
|
||||
func (m *mockMediaStreamer) DoStream(ctx context.Context, mf *model.MediaFile, req core.StreamRequest) (*core.Stream, error) {
|
||||
args := m.Called(ctx, mf, req.Format, req.BitRate, req.SampleRate, req.BitDepth, req.Channels, req.Offset)
|
||||
args := m.Called(ctx, mf, req)
|
||||
if args.Error(1) != nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
@ -15,6 +15,16 @@ import (
|
||||
const (
|
||||
tokenTTL = 12 * time.Hour
|
||||
defaultBitrate = 256 // kbps
|
||||
|
||||
// JWT claim keys for transcode params tokens
|
||||
claimMediaID = "mid" // Media file ID
|
||||
claimDirectPlay = "dp" // Direct play flag (bool)
|
||||
claimUpdatedAt = "ua" // Source file updated-at (Unix seconds)
|
||||
claimFormat = "fmt" // Target transcoding format
|
||||
claimBitrate = "br" // Target bitrate (kbps)
|
||||
claimChannels = "ch" // Target channels
|
||||
claimSampleRate = "sr" // Target sample rate (Hz)
|
||||
claimBitDepth = "bd" // Target bit depth
|
||||
)
|
||||
|
||||
func NewDecider(ds model.DataStore) Decider {
|
||||
@ -299,21 +309,21 @@ func (s *deciderService) applyCodecLimitations(ctx context.Context, sourceBitrat
|
||||
func (s *deciderService) CreateTranscodeParams(decision *Decision) (string, error) {
|
||||
exp := time.Now().Add(tokenTTL)
|
||||
claims := map[string]any{
|
||||
"mid": decision.MediaID,
|
||||
"dp": decision.CanDirectPlay,
|
||||
"ua": decision.SourceUpdatedAt.Truncate(time.Second).Unix(),
|
||||
claimMediaID: decision.MediaID,
|
||||
claimDirectPlay: decision.CanDirectPlay,
|
||||
claimUpdatedAt: decision.SourceUpdatedAt.Truncate(time.Second).Unix(),
|
||||
}
|
||||
if decision.CanTranscode && decision.TargetFormat != "" {
|
||||
claims["fmt"] = decision.TargetFormat
|
||||
claims["br"] = decision.TargetBitrate
|
||||
claims[claimFormat] = decision.TargetFormat
|
||||
claims[claimBitrate] = decision.TargetBitrate
|
||||
if decision.TargetChannels > 0 {
|
||||
claims["ch"] = decision.TargetChannels
|
||||
claims[claimChannels] = decision.TargetChannels
|
||||
}
|
||||
if decision.TargetSampleRate > 0 {
|
||||
claims["sr"] = decision.TargetSampleRate
|
||||
claims[claimSampleRate] = decision.TargetSampleRate
|
||||
}
|
||||
if decision.TargetBitDepth > 0 {
|
||||
claims["bd"] = decision.TargetBitDepth
|
||||
claims[claimBitDepth] = decision.TargetBitDepth
|
||||
}
|
||||
}
|
||||
return auth.CreateExpiringPublicToken(exp, claims)
|
||||
@ -328,38 +338,38 @@ func (s *deciderService) ParseTranscodeParams(token string) (*Params, error) {
|
||||
params := &Params{}
|
||||
|
||||
// Required claims
|
||||
mid, ok := claims["mid"].(string)
|
||||
mid, ok := claims[claimMediaID].(string)
|
||||
if !ok || mid == "" {
|
||||
return nil, fmt.Errorf("invalid transcode token: missing media ID")
|
||||
return nil, fmt.Errorf("%w: invalid transcode token: missing media ID", ErrTokenInvalid)
|
||||
}
|
||||
params.MediaID = mid
|
||||
|
||||
dp, ok := claims["dp"].(bool)
|
||||
dp, ok := claims[claimDirectPlay].(bool)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid transcode token: missing direct play flag")
|
||||
return nil, fmt.Errorf("%w: invalid transcode token: missing direct play flag", ErrTokenInvalid)
|
||||
}
|
||||
params.DirectPlay = dp
|
||||
|
||||
// Optional claims (legitimately absent for direct-play tokens)
|
||||
if f, ok := claims["fmt"].(string); ok {
|
||||
if f, ok := claims[claimFormat].(string); ok {
|
||||
params.TargetFormat = f
|
||||
}
|
||||
if br, ok := claims["br"].(float64); ok {
|
||||
if br, ok := claims[claimBitrate].(float64); ok {
|
||||
params.TargetBitrate = int(br)
|
||||
}
|
||||
if ch, ok := claims["ch"].(float64); ok {
|
||||
if ch, ok := claims[claimChannels].(float64); ok {
|
||||
params.TargetChannels = int(ch)
|
||||
}
|
||||
if sr, ok := claims["sr"].(float64); ok {
|
||||
if sr, ok := claims[claimSampleRate].(float64); ok {
|
||||
params.TargetSampleRate = int(sr)
|
||||
}
|
||||
if bd, ok := claims["bd"].(float64); ok {
|
||||
if bd, ok := claims[claimBitDepth].(float64); ok {
|
||||
params.TargetBitDepth = int(bd)
|
||||
}
|
||||
|
||||
ua, ok := claims["ua"].(float64)
|
||||
ua, ok := claims[claimUpdatedAt].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid transcode token: missing source timestamp")
|
||||
return nil, fmt.Errorf("%w: invalid transcode token: missing source timestamp", ErrTokenInvalid)
|
||||
}
|
||||
params.SourceUpdatedAt = time.Unix(int64(ua), 0)
|
||||
|
||||
|
||||
@ -239,7 +239,9 @@ func (mf MediaFile) IsLossless() bool {
|
||||
if slices.Contains(confmime.LosslessFormats, mf.Suffix) {
|
||||
return true
|
||||
}
|
||||
// Fallback heuristic: if BitDepth is set, it's likely lossless
|
||||
// Fallback heuristic: if BitDepth is set, it's likely lossless.
|
||||
// This may produce false positives for lossy formats that report bit depth,
|
||||
// but it becomes irrelevant once the Codec column is populated after a full rescan.
|
||||
return mf.BitDepth > 0
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ func (m *MockTranscodingRepo) FindByFormat(format string) (*model.Transcoding, e
|
||||
case "flac":
|
||||
return &model.Transcoding{ID: "flac1", TargetFormat: "flac", DefaultBitRate: 0, Command: "ffmpeg -i %s -ss %t -map 0:a:0 -v 0 -c:a flac -f flac -"}, nil
|
||||
case "aac":
|
||||
return &model.Transcoding{ID: "aac1", TargetFormat: "aac", DefaultBitRate: 256, Command: "ffmpeg -i %s -ss %t -map 0:a:0 -b:a %bk -v 0 -c:a aac -f adts -"}, nil
|
||||
return &model.Transcoding{ID: "aac1", TargetFormat: "aac", DefaultBitRate: 256, 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 -"}, nil
|
||||
default:
|
||||
return nil, model.ErrNotFound
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user