fix(stream): compute the estimated content length in kilobits of 1000 bits

estimateContentLength=true announced Duration * bitRate / 8 * 1024 bytes, but a
bitrate in kbps is 1000 bits per second, so the header overstated the body by
2.4%. A client that trusts it waits for bytes that never arrive: on a 285s track
at 128kbps it asked for 4,669,440 bytes of a 4,561,876 byte body.

Reported in #6170.
This commit is contained in:
Deluan 2026-09-24 16:42:35 -04:00 • committed by Deluan Quintão
commit 76df3bb7fa
2 changed files with 13 additions and 1 deletions

View file

@ -147,7 +147,7 @@ func (s *Stream) ContentType() string { return mime.TypeByExtension("." + s.form
func (s *Stream) Name() string { return s.mf.Title + "." + s.format }
func (s *Stream) ModTime() time.Time { return s.mf.UpdatedAt }
func (s *Stream) EstimatedContentLength() int {
return int(s.mf.Duration * float32(s.bitRate) / 8 * 1024)
return int(s.mf.Duration * float32(s.bitRate) / 8 * 1000)
}
// Serve writes the stream to the HTTP response. For seekable streams it uses http.ServeContent

View file

@ -183,6 +183,18 @@ var _ = Describe("MediaStreamer", func() {
_, err = io.ReadAll(resp.Body)
Expect(err).To(HaveOccurred())
})
It("estimates the content length from a bitrate in kilobits of 1000 bits", func() {
hundredSeconds := *mf
hundredSeconds.Duration = 100
s := stream.NewStream(&hundredSeconds, "mp3", 128, io.NopCloser(bytes.NewReader(nil)))
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/?estimateContentLength=true", nil)
_, _ = s.Serve(ctx, w, r)
Expect(w.Header().Get("Content-Length")).To(Equal("1600000"))
})
})
})