mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
refactor(transcoding): rename TranscodeDecision to Decider and update related methods for clarity
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
d4357c670b
commit
1cc7a2cd66
@ -21,6 +21,7 @@ import (
|
||||
"github.com/navidrome/navidrome/core/playback"
|
||||
"github.com/navidrome/navidrome/core/playlists"
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/core/transcode"
|
||||
"github.com/navidrome/navidrome/db"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/persistence"
|
||||
@ -105,8 +106,8 @@ func CreateSubsonicAPIRouter(ctx context.Context) *subsonic.Router {
|
||||
playTracker := scrobbler.GetPlayTracker(dataStore, broker, manager)
|
||||
playbackServer := playback.GetInstance(dataStore)
|
||||
lyricsLyrics := lyrics.NewLyrics(manager)
|
||||
transcodeDecision := core.NewTranscodeDecision(dataStore)
|
||||
router := subsonic.New(dataStore, artworkArtwork, mediaStreamer, archiver, players, provider, modelScanner, broker, playlistsPlaylists, playTracker, share, playbackServer, metricsMetrics, lyricsLyrics, transcodeDecision)
|
||||
decider := transcode.NewDecider(dataStore)
|
||||
router := subsonic.New(dataStore, artworkArtwork, mediaStreamer, archiver, players, provider, modelScanner, broker, playlistsPlaylists, playTracker, share, playbackServer, metricsMetrics, lyricsLyrics, decider)
|
||||
return router
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package core
|
||||
package transcode
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -13,15 +13,15 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
transcodeTokenTTL = 12 * time.Hour
|
||||
defaultTranscodeBitrate = 256 // kbps
|
||||
tokenTTL = 12 * time.Hour
|
||||
defaultBitrate = 256 // kbps
|
||||
)
|
||||
|
||||
// TranscodeDecision is the core service interface for making transcoding decisions
|
||||
type TranscodeDecision interface {
|
||||
// Decider is the core service interface for making transcoding decisions
|
||||
type Decider interface {
|
||||
MakeDecision(ctx context.Context, mf *model.MediaFile, clientInfo *ClientInfo) (*Decision, error)
|
||||
CreateTranscodeParams(decision *Decision) (string, error)
|
||||
ParseTranscodeParams(token string) (*TranscodeParams, error)
|
||||
ParseTranscodeParams(token string) (*Params, error)
|
||||
}
|
||||
|
||||
// ClientInfo represents client playback capabilities.
|
||||
@ -32,7 +32,7 @@ type ClientInfo struct {
|
||||
MaxAudioBitrate int
|
||||
MaxTranscodingAudioBitrate int
|
||||
DirectPlayProfiles []DirectPlayProfile
|
||||
TranscodingProfiles []TranscodingProfile
|
||||
TranscodingProfiles []Profile
|
||||
CodecProfiles []CodecProfile
|
||||
}
|
||||
|
||||
@ -44,8 +44,8 @@ type DirectPlayProfile struct {
|
||||
MaxAudioChannels int
|
||||
}
|
||||
|
||||
// TranscodingProfile describes a transcoding target the client supports
|
||||
type TranscodingProfile struct {
|
||||
// Profile describes a transcoding target the client supports
|
||||
type Profile struct {
|
||||
Container string
|
||||
AudioCodec string
|
||||
Protocol string
|
||||
@ -125,9 +125,9 @@ type StreamDetails struct {
|
||||
IsLossless bool
|
||||
}
|
||||
|
||||
// TranscodeParams contains the parameters extracted from a transcode token.
|
||||
// Params contains the parameters extracted from a transcode token.
|
||||
// TargetBitrate is in kilobits per second (kbps).
|
||||
type TranscodeParams struct {
|
||||
type Params struct {
|
||||
MediaID string
|
||||
DirectPlay bool
|
||||
TargetFormat string
|
||||
@ -135,17 +135,17 @@ type TranscodeParams struct {
|
||||
TargetChannels int
|
||||
}
|
||||
|
||||
func NewTranscodeDecision(ds model.DataStore) TranscodeDecision {
|
||||
return &transcodeDecisionService{
|
||||
func NewDecider(ds model.DataStore) Decider {
|
||||
return &deciderService{
|
||||
ds: ds,
|
||||
}
|
||||
}
|
||||
|
||||
type transcodeDecisionService struct {
|
||||
type deciderService struct {
|
||||
ds model.DataStore
|
||||
}
|
||||
|
||||
func (s *transcodeDecisionService) MakeDecision(ctx context.Context, mf *model.MediaFile, clientInfo *ClientInfo) (*Decision, error) {
|
||||
func (s *deciderService) MakeDecision(ctx context.Context, mf *model.MediaFile, clientInfo *ClientInfo) (*Decision, error) {
|
||||
decision := &Decision{
|
||||
MediaID: mf.ID,
|
||||
}
|
||||
@ -224,7 +224,7 @@ func (s *transcodeDecisionService) MakeDecision(ctx context.Context, mf *model.M
|
||||
|
||||
// checkDirectPlayProfile returns "" if the profile matches (direct play OK),
|
||||
// or a typed reason string if it doesn't match.
|
||||
func (s *transcodeDecisionService) checkDirectPlayProfile(mf *model.MediaFile, sourceBitrate int, profile *DirectPlayProfile, clientInfo *ClientInfo) string {
|
||||
func (s *deciderService) checkDirectPlayProfile(mf *model.MediaFile, sourceBitrate int, profile *DirectPlayProfile, clientInfo *ClientInfo) string {
|
||||
// Check protocol (only http for now)
|
||||
if len(profile.Protocols) > 0 && !containsIgnoreCase(profile.Protocols, ProtocolHTTP) {
|
||||
return "protocol not supported"
|
||||
@ -303,7 +303,7 @@ const (
|
||||
|
||||
// computeTranscodedStream attempts to build a valid transcoded stream for the given profile.
|
||||
// Returns nil if the profile cannot produce a valid output.
|
||||
func (s *transcodeDecisionService) computeTranscodedStream(ctx context.Context, mf *model.MediaFile, sourceBitrate int, profile *TranscodingProfile, clientInfo *ClientInfo) *StreamDetails {
|
||||
func (s *deciderService) computeTranscodedStream(ctx context.Context, mf *model.MediaFile, sourceBitrate int, profile *Profile, clientInfo *ClientInfo) *StreamDetails {
|
||||
// Check protocol (only http for now)
|
||||
if profile.Protocol != "" && !strings.EqualFold(profile.Protocol, ProtocolHTTP) {
|
||||
log.Trace(ctx, "Skipping transcoding profile: unsupported protocol", "protocol", profile.Protocol)
|
||||
@ -348,7 +348,7 @@ func (s *transcodeDecisionService) computeTranscodedStream(ctx context.Context,
|
||||
if clientInfo.MaxTranscodingAudioBitrate > 0 {
|
||||
ts.Bitrate = clientInfo.MaxTranscodingAudioBitrate
|
||||
} else {
|
||||
ts.Bitrate = defaultTranscodeBitrate
|
||||
ts.Bitrate = defaultBitrate
|
||||
}
|
||||
} else {
|
||||
// Lossless to lossless: check if bitrate is under the global max
|
||||
@ -490,8 +490,8 @@ func applyIntLimitation(comparison string, values []string, current int, setter
|
||||
return adjustNone
|
||||
}
|
||||
|
||||
func (s *transcodeDecisionService) CreateTranscodeParams(decision *Decision) (string, error) {
|
||||
exp := time.Now().Add(transcodeTokenTTL)
|
||||
func (s *deciderService) CreateTranscodeParams(decision *Decision) (string, error) {
|
||||
exp := time.Now().Add(tokenTTL)
|
||||
claims := map[string]any{
|
||||
"mid": decision.MediaID,
|
||||
"dp": decision.CanDirectPlay,
|
||||
@ -506,13 +506,13 @@ func (s *transcodeDecisionService) CreateTranscodeParams(decision *Decision) (st
|
||||
return auth.CreateExpiringPublicToken(exp, claims)
|
||||
}
|
||||
|
||||
func (s *transcodeDecisionService) ParseTranscodeParams(token string) (*TranscodeParams, error) {
|
||||
func (s *deciderService) ParseTranscodeParams(token string) (*Params, error) {
|
||||
claims, err := auth.Validate(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := &TranscodeParams{}
|
||||
params := &Params{}
|
||||
if mid, ok := claims["mid"].(string); ok {
|
||||
params.MediaID = mid
|
||||
}
|
||||
17
core/transcode/transcode_suite_test.go
Normal file
17
core/transcode/transcode_suite_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
package transcode
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestTranscode(t *testing.T) {
|
||||
tests.Init(t, false)
|
||||
log.SetLevel(log.LevelFatal)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Transcode Suite")
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package core
|
||||
package transcode
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -10,10 +10,10 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("TranscodeDecision", func() {
|
||||
var _ = Describe("Decider", func() {
|
||||
var (
|
||||
ds *tests.MockDataStore
|
||||
svc TranscodeDecision
|
||||
svc Decider
|
||||
ctx context.Context
|
||||
)
|
||||
|
||||
@ -24,7 +24,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
MockedTranscoding: &tests.MockTranscodingRepo{},
|
||||
}
|
||||
auth.Init(ds)
|
||||
svc = NewTranscodeDecision(ds)
|
||||
svc = NewDecider(ds)
|
||||
})
|
||||
|
||||
Describe("MakeDecision", func() {
|
||||
@ -151,7 +151,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
DirectPlayProfiles: []DirectPlayProfile{
|
||||
{Containers: []string{"flac"}, Protocols: []string{"http"}},
|
||||
},
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -171,7 +171,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
DirectPlayProfiles: []DirectPlayProfile{
|
||||
{Containers: []string{"mp3"}, Protocols: []string{"http"}},
|
||||
},
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http", MaxAudioChannels: 2},
|
||||
},
|
||||
}
|
||||
@ -187,7 +187,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
It("rejects lossy to lossless transcoding", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "mp3", Codec: "MP3", BitRate: 320, Channels: 2}
|
||||
ci := &ClientInfo{
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "flac", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -199,21 +199,21 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
It("uses default bitrate when client doesn't specify", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2, BitDepth: 16}
|
||||
ci := &ClientInfo{
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
decision, err := svc.MakeDecision(ctx, mf, ci)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(decision.CanTranscode).To(BeTrue())
|
||||
Expect(decision.TargetBitrate).To(Equal(defaultTranscodeBitrate)) // 256 kbps
|
||||
Expect(decision.TargetBitrate).To(Equal(defaultBitrate)) // 256 kbps
|
||||
})
|
||||
|
||||
It("preserves lossy bitrate when under max", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "ogg", BitRate: 192, Channels: 2}
|
||||
ci := &ClientInfo{
|
||||
MaxTranscodingAudioBitrate: 256, // kbps
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -226,7 +226,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
It("rejects unsupported transcoding format", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2}
|
||||
ci := &ClientInfo{
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "aac", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -239,7 +239,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "mp3", Codec: "MP3", BitRate: 320, Channels: 2}
|
||||
ci := &ClientInfo{
|
||||
MaxAudioBitrate: 96, // kbps
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -256,7 +256,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
DirectPlayProfiles: []DirectPlayProfile{
|
||||
{Containers: []string{"mp3"}, Protocols: []string{"http"}},
|
||||
},
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "opus", AudioCodec: "opus", Protocol: "http"},
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http", MaxAudioChannels: 2},
|
||||
},
|
||||
@ -278,7 +278,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
DirectPlayProfiles: []DirectPlayProfile{
|
||||
{Containers: []string{"flac"}, Protocols: []string{"http"}},
|
||||
},
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -292,14 +292,14 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
// Simulate DSD→FLAC transcoding by using a mock that supports "flac"
|
||||
mockTranscoding := &tests.MockTranscodingRepo{}
|
||||
ds.MockedTranscoding = mockTranscoding
|
||||
svc = NewTranscodeDecision(ds)
|
||||
svc = NewDecider(ds)
|
||||
|
||||
// MockTranscodingRepo doesn't support flac, so this will skip lossless profile.
|
||||
// Use mp3 which is supported as the fallback.
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2, SampleRate: 96000, BitDepth: 24}
|
||||
ci := &ClientInfo{
|
||||
MaxTranscodingAudioBitrate: 320,
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -480,7 +480,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "mp3", Codec: "MP3", BitRate: 192, Channels: 2, SampleRate: 44100}
|
||||
ci := &ClientInfo{
|
||||
MaxAudioBitrate: 96, // force transcode
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
CodecProfiles: []CodecProfile{
|
||||
@ -503,7 +503,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 6, SampleRate: 48000, BitDepth: 16}
|
||||
ci := &ClientInfo{
|
||||
MaxTranscodingAudioBitrate: 320,
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
CodecProfiles: []CodecProfile{
|
||||
@ -526,7 +526,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2, SampleRate: 96000, BitDepth: 24}
|
||||
ci := &ClientInfo{
|
||||
MaxTranscodingAudioBitrate: 320,
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
CodecProfiles: []CodecProfile{
|
||||
@ -549,7 +549,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
mf := &model.MediaFile{ID: "1", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2, SampleRate: 44100, BitDepth: 16}
|
||||
ci := &ClientInfo{
|
||||
MaxTranscodingAudioBitrate: 320,
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
CodecProfiles: []CodecProfile{
|
||||
@ -577,7 +577,7 @@ var _ = Describe("TranscodeDecision", func() {
|
||||
{Containers: []string{"mp3"}, AudioCodecs: []string{"mp3"}, Protocols: []string{"http"}},
|
||||
{Containers: []string{"m4a", "mp4"}, AudioCodecs: []string{"aac"}, Protocols: []string{"http"}},
|
||||
},
|
||||
TranscodingProfiles: []TranscodingProfile{
|
||||
TranscodingProfiles: []Profile{
|
||||
{Container: "mp3", AudioCodec: "mp3", Protocol: "http"},
|
||||
},
|
||||
}
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/navidrome/navidrome/core/playback"
|
||||
"github.com/navidrome/navidrome/core/playlists"
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/core/transcode"
|
||||
)
|
||||
|
||||
var Set = wire.NewSet(
|
||||
@ -22,7 +23,7 @@ var Set = wire.NewSet(
|
||||
NewLibrary,
|
||||
NewUser,
|
||||
NewMaintenance,
|
||||
NewTranscodeDecision,
|
||||
transcode.NewDecider,
|
||||
agents.GetAgents,
|
||||
external.NewProvider,
|
||||
wire.Bind(new(external.Agents), new(*agents.Agents)),
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
"github.com/navidrome/navidrome/core/playback"
|
||||
playlistsvc "github.com/navidrome/navidrome/core/playlists"
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/core/transcode"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/server"
|
||||
@ -50,13 +51,13 @@ type Router struct {
|
||||
playback playback.PlaybackServer
|
||||
metrics metrics.Metrics
|
||||
lyrics lyricssvc.Lyrics
|
||||
transcodeDecision core.TranscodeDecision
|
||||
transcodeDecision transcode.Decider
|
||||
}
|
||||
|
||||
func New(ds model.DataStore, artwork artwork.Artwork, streamer core.MediaStreamer, archiver core.Archiver,
|
||||
players core.Players, provider external.Provider, scanner model.Scanner, broker events.Broker,
|
||||
playlists playlistsvc.Playlists, scrobbler scrobbler.PlayTracker, share core.Share, playback playback.PlaybackServer,
|
||||
metrics metrics.Metrics, lyrics lyricssvc.Lyrics, transcodeDecision core.TranscodeDecision,
|
||||
metrics metrics.Metrics, lyrics lyricssvc.Lyrics, transcodeDecision transcode.Decider,
|
||||
) *Router {
|
||||
r := &Router{
|
||||
ds: ds,
|
||||
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/navidrome/navidrome/core"
|
||||
"github.com/navidrome/navidrome/core/transcode"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
@ -56,10 +56,10 @@ type limitationReq struct {
|
||||
Required bool `json:"required,omitempty"`
|
||||
}
|
||||
|
||||
// toCoreClientInfo converts the API request struct to the core.ClientInfo struct.
|
||||
// toCoreClientInfo converts the API request struct to the transcode.ClientInfo struct.
|
||||
// The OpenSubsonic spec uses bps for bitrate values; core uses kbps.
|
||||
func (r *clientInfoRequest) toCoreClientInfo() *core.ClientInfo {
|
||||
ci := &core.ClientInfo{
|
||||
func (r *clientInfoRequest) toCoreClientInfo() *transcode.ClientInfo {
|
||||
ci := &transcode.ClientInfo{
|
||||
Name: r.Name,
|
||||
Platform: r.Platform,
|
||||
MaxAudioBitrate: bpsToKbps(r.MaxAudioBitrate),
|
||||
@ -67,7 +67,7 @@ func (r *clientInfoRequest) toCoreClientInfo() *core.ClientInfo {
|
||||
}
|
||||
|
||||
for _, dp := range r.DirectPlayProfiles {
|
||||
ci.DirectPlayProfiles = append(ci.DirectPlayProfiles, core.DirectPlayProfile{
|
||||
ci.DirectPlayProfiles = append(ci.DirectPlayProfiles, transcode.DirectPlayProfile{
|
||||
Containers: dp.Containers,
|
||||
AudioCodecs: dp.AudioCodecs,
|
||||
Protocols: dp.Protocols,
|
||||
@ -76,7 +76,7 @@ func (r *clientInfoRequest) toCoreClientInfo() *core.ClientInfo {
|
||||
}
|
||||
|
||||
for _, tp := range r.TranscodingProfiles {
|
||||
ci.TranscodingProfiles = append(ci.TranscodingProfiles, core.TranscodingProfile{
|
||||
ci.TranscodingProfiles = append(ci.TranscodingProfiles, transcode.Profile{
|
||||
Container: tp.Container,
|
||||
AudioCodec: tp.AudioCodec,
|
||||
Protocol: tp.Protocol,
|
||||
@ -85,19 +85,19 @@ func (r *clientInfoRequest) toCoreClientInfo() *core.ClientInfo {
|
||||
}
|
||||
|
||||
for _, cp := range r.CodecProfiles {
|
||||
coreCP := core.CodecProfile{
|
||||
coreCP := transcode.CodecProfile{
|
||||
Type: cp.Type,
|
||||
Name: cp.Name,
|
||||
}
|
||||
for _, lim := range cp.Limitations {
|
||||
coreLim := core.Limitation{
|
||||
coreLim := transcode.Limitation{
|
||||
Name: lim.Name,
|
||||
Comparison: lim.Comparison,
|
||||
Values: lim.Values,
|
||||
Required: lim.Required,
|
||||
}
|
||||
// Convert audioBitrate limitation values from bps to kbps
|
||||
if lim.Name == core.LimitationAudioBitrate {
|
||||
if lim.Name == transcode.LimitationAudioBitrate {
|
||||
coreLim.Values = convertBitrateValues(lim.Values)
|
||||
}
|
||||
coreCP.Limitations = append(coreCP.Limitations, coreLim)
|
||||
@ -163,26 +163,26 @@ func (r *clientInfoRequest) validate() error {
|
||||
}
|
||||
|
||||
func isValidProtocol(p string) bool {
|
||||
return p == core.ProtocolHTTP || p == core.ProtocolHLS
|
||||
return p == transcode.ProtocolHTTP || p == transcode.ProtocolHLS
|
||||
}
|
||||
|
||||
func isValidCodecProfileType(t string) bool {
|
||||
return t == core.CodecProfileTypeAudio
|
||||
return t == transcode.CodecProfileTypeAudio
|
||||
}
|
||||
|
||||
func isValidLimitationName(n string) bool {
|
||||
return n == core.LimitationAudioChannels ||
|
||||
n == core.LimitationAudioBitrate ||
|
||||
n == core.LimitationAudioProfile ||
|
||||
n == core.LimitationAudioSamplerate ||
|
||||
n == core.LimitationAudioBitdepth
|
||||
return n == transcode.LimitationAudioChannels ||
|
||||
n == transcode.LimitationAudioBitrate ||
|
||||
n == transcode.LimitationAudioProfile ||
|
||||
n == transcode.LimitationAudioSamplerate ||
|
||||
n == transcode.LimitationAudioBitdepth
|
||||
}
|
||||
|
||||
func isValidComparison(c string) bool {
|
||||
return c == core.ComparisonEquals ||
|
||||
c == core.ComparisonNotEquals ||
|
||||
c == core.ComparisonLessThanEqual ||
|
||||
c == core.ComparisonGreaterThanEqual
|
||||
return c == transcode.ComparisonEquals ||
|
||||
c == transcode.ComparisonNotEquals ||
|
||||
c == transcode.ComparisonLessThanEqual ||
|
||||
c == transcode.ComparisonGreaterThanEqual
|
||||
}
|
||||
|
||||
// GetTranscodeDecision handles the OpenSubsonic getTranscodeDecision endpoint.
|
||||
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/navidrome/navidrome/core"
|
||||
"github.com/navidrome/navidrome/core/transcode"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@ -146,10 +146,10 @@ var _ = Describe("Transcode endpoints", func() {
|
||||
mockMFRepo.SetData(model.MediaFiles{
|
||||
{ID: "song-1", Suffix: "mp3", Codec: "MP3", BitRate: 320, Channels: 2, SampleRate: 44100},
|
||||
})
|
||||
mockTD.decision = &core.Decision{
|
||||
mockTD.decision = &transcode.Decision{
|
||||
MediaID: "song-1",
|
||||
CanDirectPlay: true,
|
||||
SourceStream: core.StreamDetails{
|
||||
SourceStream: transcode.StreamDetails{
|
||||
Container: "mp3", Codec: "mp3", Bitrate: 320,
|
||||
SampleRate: 44100, Channels: 2,
|
||||
},
|
||||
@ -174,18 +174,18 @@ var _ = Describe("Transcode endpoints", func() {
|
||||
mockMFRepo.SetData(model.MediaFiles{
|
||||
{ID: "song-2", Suffix: "flac", Codec: "FLAC", BitRate: 1000, Channels: 2, SampleRate: 96000, BitDepth: 24},
|
||||
})
|
||||
mockTD.decision = &core.Decision{
|
||||
mockTD.decision = &transcode.Decision{
|
||||
MediaID: "song-2",
|
||||
CanDirectPlay: false,
|
||||
CanTranscode: true,
|
||||
TargetFormat: "mp3",
|
||||
TargetBitrate: 256,
|
||||
TranscodeReasons: []string{"container not supported"},
|
||||
SourceStream: core.StreamDetails{
|
||||
SourceStream: transcode.StreamDetails{
|
||||
Container: "flac", Codec: "flac", Bitrate: 1000,
|
||||
SampleRate: 96000, BitDepth: 24, Channels: 2,
|
||||
},
|
||||
TranscodeStream: &core.StreamDetails{
|
||||
TranscodeStream: &transcode.StreamDetails{
|
||||
Container: "mp3", Codec: "mp3", Bitrate: 256,
|
||||
SampleRate: 96000, Channels: 2,
|
||||
},
|
||||
@ -224,7 +224,7 @@ var _ = Describe("Transcode endpoints", func() {
|
||||
})
|
||||
|
||||
It("returns error when mediaId doesn't match token", func() {
|
||||
mockTD.params = &core.TranscodeParams{MediaID: "other-id", DirectPlay: true}
|
||||
mockTD.params = &transcode.Params{MediaID: "other-id", DirectPlay: true}
|
||||
r := newGetRequest("mediaId=wrong-id", "mediaType=song", "transcodeParams=valid-token")
|
||||
_, err := router.GetTranscodeStream(w, r)
|
||||
Expect(err).To(HaveOccurred())
|
||||
@ -242,25 +242,25 @@ func newJSONPostRequest(queryParams string, jsonBody string) *http.Request {
|
||||
|
||||
// mockTranscodeDecision is a test double for core.TranscodeDecision
|
||||
type mockTranscodeDecision struct {
|
||||
decision *core.Decision
|
||||
decision *transcode.Decision
|
||||
token string
|
||||
tokenErr error
|
||||
params *core.TranscodeParams
|
||||
params *transcode.Params
|
||||
parseErr error
|
||||
}
|
||||
|
||||
func (m *mockTranscodeDecision) MakeDecision(_ context.Context, _ *model.MediaFile, _ *core.ClientInfo) (*core.Decision, error) {
|
||||
func (m *mockTranscodeDecision) MakeDecision(_ context.Context, _ *model.MediaFile, _ *transcode.ClientInfo) (*transcode.Decision, error) {
|
||||
if m.decision != nil {
|
||||
return m.decision, nil
|
||||
}
|
||||
return &core.Decision{}, nil
|
||||
return &transcode.Decision{}, nil
|
||||
}
|
||||
|
||||
func (m *mockTranscodeDecision) CreateTranscodeParams(_ *core.Decision) (string, error) {
|
||||
func (m *mockTranscodeDecision) CreateTranscodeParams(_ *transcode.Decision) (string, error) {
|
||||
return m.token, m.tokenErr
|
||||
}
|
||||
|
||||
func (m *mockTranscodeDecision) ParseTranscodeParams(_ string) (*core.TranscodeParams, error) {
|
||||
func (m *mockTranscodeDecision) ParseTranscodeParams(_ string) (*transcode.Params, error) {
|
||||
if m.parseErr != nil {
|
||||
return nil, m.parseErr
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user