diff --git a/http/subtitle.go b/http/subtitle.go index 07c2dcfc..24b49256 100644 --- a/http/subtitle.go +++ b/http/subtitle.go @@ -2,7 +2,9 @@ package fbhttp import ( "bytes" + "io" "net/http" + "regexp" "strings" "github.com/asticode/go-astisub" @@ -10,6 +12,8 @@ import ( "github.com/filebrowser/filebrowser/v2/files" ) +var srtLineBreakTag = regexp.MustCompile(`(?i)]*)?\s*/?>`) + var subtitleHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { if !d.user.Perm.Download { return http.StatusAccepted, nil @@ -49,7 +53,11 @@ func subtitleFileHandler(w http.ResponseWriter, r *http.Request, file *files.Fil // load subtitle for conversion to vtt var sub *astisub.Subtitles if strings.HasSuffix(file.Name, ".srt") { - sub, err = astisub.ReadFromSRT(fd) + content, readErr := io.ReadAll(fd) + if readErr != nil { + return http.StatusInternalServerError, readErr + } + sub, err = astisub.ReadFromSRT(bytes.NewReader(normalizeSRTLineBreaks(content))) } else if strings.HasSuffix(file.Name, ".ass") || strings.HasSuffix(file.Name, ".ssa") { sub, err = astisub.ReadFromSSA(fd) } @@ -78,3 +86,7 @@ func subtitleFileHandler(w http.ResponseWriter, r *http.Request, file *files.Fil http.ServeContent(w, r, file.Name, file.ModTime, bytes.NewReader(buf.Bytes())) return 0, nil } + +func normalizeSRTLineBreaks(content []byte) []byte { + return srtLineBreakTag.ReplaceAll(content, []byte("\n")) +} diff --git a/http/subtitle_test.go b/http/subtitle_test.go new file mode 100644 index 00000000..2520d453 --- /dev/null +++ b/http/subtitle_test.go @@ -0,0 +1,62 @@ +package fbhttp + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/spf13/afero" + + "github.com/filebrowser/filebrowser/v2/files" +) + +func TestNormalizeSRTLineBreaks(t *testing.T) { + input := []byte("first
second
third
fourth
fifth") + got := string(normalizeSRTLineBreaks(input)) + want := "first\nsecond\nthird\nfourth\nfifth" + if got != want { + t.Fatalf("normalizeSRTLineBreaks() = %q, want %q", got, want) + } +} + +func TestSubtitleFileHandlerConvertsSRTBreakTags(t *testing.T) { + fs := afero.NewMemMapFs() + const path = "/sample.srt" + const content = "1\n" + + "00:00:01,000 --> 00:00:02,000\n" + + "First
Second
Third
Fourth\n\n" + + if err := afero.WriteFile(fs, path, []byte(content), 0o644); err != nil { + t.Fatalf("failed to write subtitle: %v", err) + } + info, err := fs.Stat(path) + if err != nil { + t.Fatalf("failed to stat subtitle: %v", err) + } + + file := &files.FileInfo{ + Fs: fs, + Path: path, + Name: "sample.srt", + ModTime: info.ModTime(), + } + req := httptest.NewRequest(http.MethodGet, "/api/subtitle/sample.srt?inline=true", http.NoBody) + rec := httptest.NewRecorder() + + status, err := subtitleFileHandler(rec, req, file) + if err != nil { + t.Fatalf("subtitleFileHandler returned error: %v", err) + } + if status != 0 { + t.Fatalf("subtitleFileHandler status = %d, want 0", status) + } + + body := rec.Body.String() + if strings.Contains(body, "FirstSecond") { + t.Fatalf("WebVTT output collapsed SRT
tags: %q", body) + } + if !strings.Contains(body, "First\nSecond\nThird\nFourth") { + t.Fatalf("WebVTT output = %q, want converted SRT
tags as line breaks", body) + } +}