mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
feat(stream): integrate TranscodingThrottle into NewStream
This commit is contained in:
parent
b018d7b634
commit
4d14ad812c
@ -92,6 +92,11 @@ func (ms *mediaStreamer) NewStream(ctx context.Context, mf *model.MediaFile, req
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Acquire throttle slot before accessing cache (which may spawn ffmpeg on miss)
|
||||||
|
if err := ms.throttle.Acquire(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
job := &streamJob{
|
job := &streamJob{
|
||||||
ms: ms,
|
ms: ms,
|
||||||
mf: mf,
|
mf: mf,
|
||||||
@ -105,12 +110,19 @@ func (ms *mediaStreamer) NewStream(ctx context.Context, mf *model.MediaFile, req
|
|||||||
}
|
}
|
||||||
r, err := ms.cache.Get(ctx, job)
|
r, err := ms.cache.Get(ctx, job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
ms.throttle.Release()
|
||||||
log.Error(ctx, "Error accessing transcoding cache", "id", mf.ID, err)
|
log.Error(ctx, "Error accessing transcoding cache", "id", mf.ID, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cached = r.Cached
|
cached = r.Cached
|
||||||
|
if cached {
|
||||||
s.ReadCloser = r
|
// Cache hit — no ffmpeg process running, release the slot immediately
|
||||||
|
ms.throttle.Release()
|
||||||
|
s.ReadCloser = r
|
||||||
|
} else {
|
||||||
|
// Cache miss — slot released when stream is closed
|
||||||
|
s.ReadCloser = &releaseOnClose{ReadCloser: r, release: ms.throttle.Release}
|
||||||
|
}
|
||||||
s.Seeker = r.Seeker
|
s.Seeker = r.Seeker
|
||||||
|
|
||||||
log.Debug(ctx, "Streaming TRANSCODED file", "id", mf.ID, "path", filePath,
|
log.Debug(ctx, "Streaming TRANSCODED file", "id", mf.ID, "path", filePath,
|
||||||
|
|||||||
@ -73,4 +73,41 @@ var _ = Describe("MediaStreamer", func() {
|
|||||||
Expect(s.Seekable()).To(BeTrue())
|
Expect(s.Seekable()).To(BeTrue())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("NewStream with throttle", func() {
|
||||||
|
var mf *model.MediaFile
|
||||||
|
BeforeEach(func() {
|
||||||
|
var err error
|
||||||
|
mf, err = ds.MediaFile(ctx).Get("123")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns ErrTranscodingBusy when throttle rejects", func() {
|
||||||
|
throttle := stream.NewTranscodingThrottle(1, 1, 50*time.Millisecond)
|
||||||
|
Expect(throttle.Acquire(context.Background())).To(Succeed())
|
||||||
|
|
||||||
|
testCache := stream.NewTranscodingCache()
|
||||||
|
Eventually(func() bool { return testCache.Available(context.TODO()) }).Should(BeTrue())
|
||||||
|
throttledStreamer := stream.NewMediaStreamer(ds, ffmpeg, testCache, throttle)
|
||||||
|
|
||||||
|
_, err := throttledStreamer.NewStream(ctx, mf, stream.Request{Format: "mp3", BitRate: 64})
|
||||||
|
Expect(err).To(MatchError(stream.ErrTranscodingBusy))
|
||||||
|
throttle.Release()
|
||||||
|
})
|
||||||
|
|
||||||
|
It("does not throttle raw/direct-play requests", func() {
|
||||||
|
throttle := stream.NewTranscodingThrottle(1, 1, 50*time.Millisecond)
|
||||||
|
Expect(throttle.Acquire(context.Background())).To(Succeed())
|
||||||
|
|
||||||
|
testCache := stream.NewTranscodingCache()
|
||||||
|
Eventually(func() bool { return testCache.Available(context.TODO()) }).Should(BeTrue())
|
||||||
|
throttledStreamer := stream.NewMediaStreamer(ds, ffmpeg, testCache, throttle)
|
||||||
|
|
||||||
|
s, err := throttledStreamer.NewStream(ctx, mf, stream.Request{Format: "raw"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(s).ToNot(BeNil())
|
||||||
|
_ = s.Close()
|
||||||
|
throttle.Release()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user