Skip to content

Commit

Permalink
feat(subsonic): support timeOffset in stream.view (#384)
Browse files Browse the repository at this point in the history
as per
opensubsonic/open-subsonic-api#54
opensubsonic/open-subsonic-api#21

dont cache partial transcodes

add a transcode seek test
  • Loading branch information
sentriz committed Oct 8, 2023
1 parent 318b624 commit 7eaf602
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
4 changes: 3 additions & 1 deletion server/ctrlsubsonic/handlers_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ func (c *Controller) ServePing(_ *http.Request) *spec.Response {

func (c *Controller) ServeGetOpenSubsonicExtensions(_ *http.Request) *spec.Response {
sub := spec.NewResponse()
sub.OpenSubsonicExtensions = &spec.OpenSubsonicExtensions{}
sub.OpenSubsonicExtensions = &spec.OpenSubsonicExtensions{
{Name: "transcodeOffset", Versions: []int{1}},
}
return sub
}

Expand Down
4 changes: 4 additions & 0 deletions server/ctrlsubsonic/handlers_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R

maxBitRate, _ := params.GetInt("maxBitRate")
format, _ := params.Get("format")
timeOffset, _ := params.GetInt("timeOffset")

if format == "raw" || maxBitRate >= audioFile.AudioBitrate() {
http.ServeFile(w, r, file.AbsPath())
Expand All @@ -208,6 +209,9 @@ func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.R
if maxBitRate > 0 && int(profile.BitRate()) > maxBitRate {
profile = transcode.WithBitrate(profile, transcode.BitRate(maxBitRate))
}
if timeOffset > 0 {
profile = transcode.WithSeek(profile, time.Second*time.Duration(timeOffset))
}

log.Printf("trancoding to %q with max bitrate %dk", profile.MIME(), profile.BitRate())

Expand Down
36 changes: 36 additions & 0 deletions transcode/transcode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,39 @@ func TestTranscode(t *testing.T) {
// we should have 5 seconds of PCM data
require.Equal(t, testFileLen*bytesPerSec, buf.Len())
}

// TestTranscodeWithSeek starts a web server that transcodes a 5s FLAC file to PCM audio, but with a 2 second offset.
// A client consumes the result over a 3 second period.
func TestTranscodeWithSeek(t *testing.T) {
t.Parallel()

testFile := "testdata/5s.flac"
testFileLen := 5

seekSecs := 2
profile := transcode.WithSeek(testProfile, time.Duration(seekSecs)*time.Second)

tr := transcode.NewFFmpegTranscoder()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.NoError(t, tr.Transcode(r.Context(), profile, testFile, w))
w.(http.Flusher).Flush()
}))
defer server.Close()

resp, err := server.Client().Get(server.URL)
require.NoError(t, err)
defer resp.Body.Close()

var buf bytes.Buffer
for {
n, err := io.Copy(&buf, io.LimitReader(resp.Body, bytesPerSec))
require.NoError(t, err)
if n == 0 {
break
}
time.Sleep(1 * time.Second)
}

// since we seeked 2 seconds, we should have 5-2 = 3 seconds of PCM data
require.Equal(t, (testFileLen-seekSecs)*bytesPerSec, buf.Len())
}
5 changes: 5 additions & 0 deletions transcode/transcoder_caching.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ func NewCachingTranscoder(t Transcoder, cachePath string) *CachingTranscoder {
}

func (t *CachingTranscoder) Transcode(ctx context.Context, profile Profile, in string, out io.Writer) error {
// don't try cache partial transcodes
if profile.Seek() > 0 {
return t.transcoder.Transcode(ctx, profile, in, out)
}

if err := os.MkdirAll(t.cachePath, perm^0o111); err != nil {
return fmt.Errorf("make cache path: %w", err)
}
Expand Down

0 comments on commit 7eaf602

Please sign in to comment.