Skip to content

Commit

Permalink
Merge pull request #351 from bcc-code/feat/merge-and-import-subs
Browse files Browse the repository at this point in the history
Merge and import subs based on CSV spec
  • Loading branch information
KillerX authored Nov 28, 2024
2 parents c019023 + c868a70 commit 41f5f88
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 5 deletions.
16 changes: 16 additions & 0 deletions activities/transcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,22 @@ func (aa AudioActivities) TranscodeMergeAudio(ctx context.Context, params common
return result, nil
}

func (aa AudioActivities) MergeSubtitlesByOffset(ctx context.Context, params common.MergeInput) (*common.MergeResult, error) {
log := activity.GetLogger(ctx)
activity.RecordHeartbeat(ctx, "MergeSubtitlesByOffset")
log.Info("Starting MergeSubtitlesByOffsetActivity")

// No easy way of reporting progress, so this just triggers heartbeats
stopChan, progressCallback := registerProgressCallback(ctx)
defer close(stopChan)

result, err := transcode.MergeSubtitlesByOffset(params, progressCallback)
if err != nil {
return nil, err
}
return result, nil
}

func (va VideoActivities) TranscodeMergeSubtitles(ctx context.Context, params common.MergeInput) (*common.MergeResult, error) {
log := activity.GetLogger(ctx)
activity.RecordHeartbeat(ctx, "TranscodeMergeSubtitles")
Expand Down
9 changes: 5 additions & 4 deletions common/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import (
)

type MergeInputItem struct {
Path paths.Path
Start float64
End float64
Streams []vidispine.AudioStream
Path paths.Path
Start float64
End float64
StartOffset float64
Streams []vidispine.AudioStream
}

type MergeInput struct {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/glebarez/go-sqlite v1.21.2
github.com/go-resty/resty/v2 v2.11.0
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
github.com/google/uuid v1.6.0
github.com/jlaffaye/ftp v0.2.0
github.com/orsinium-labs/enum v1.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,8 @@ github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaC
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ=
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
Expand Down
2 changes: 1 addition & 1 deletion services/ffmpeg/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func parseProgressCallback(command []string, info StreamInfo, cb func(Progress))
} else if parts[0] == "speed" {
progress.Speed = parts[1]
}
if parts[0] == "progress" {
if parts[0] == "progress" && cb != nil {
cb(progress)
}
}
Expand Down
82 changes: 82 additions & 0 deletions services/transcode/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,88 @@ func formatDuration(seconds float64) string {
return fmt.Sprintf("%02d:%02d:%02d,%03d", hours, minutes, wholeSeconds, milliseconds)
}

// MergeSubtitlesByOffset merges subtitles based on a specified offset
//
// This is used for example when you have several movies played in a feast.
// this way the offset indicates the offset from the start of the event, and the subtitles will be placed there
func MergeSubtitlesByOffset(input common.MergeInput, progressCallback ffmpeg.ProgressCallback) (*common.MergeResult, error) {
var files []string

for index, item := range input.Items {
fileOut := filepath.Join(input.WorkDir.Local(), fmt.Sprintf("%s-%d-out.srt", input.Title, index))
path := item.Path.Local()

cmd := exec.Command("ffmpeg",
"-hide_banner",
"-itsoffset", fmt.Sprintf("%f", item.StartOffset),
"-i", path,
"-y", fileOut,
)
_, err := utils.ExecuteCmd(cmd, nil)
if err != nil {
return nil, err
}

files = append(files, fileOut)
}

// the files have to be present in a text file for ffmpeg to concatenate them.
// #subtitles.txt
// file /path/to/file/0.srt
// file /path/to/file/1.srt
var content string
for _, f := range files {
content += fmt.Sprintf("file '%s'\n", f)
}

subtitlesFile := filepath.Join(input.WorkDir.Local(), input.Title+"-subtitles.txt")

err := os.WriteFile(subtitlesFile, []byte(content), os.ModePerm)
if err != nil {
return nil, err
}

for _, f := range files {
err = ensureValidSrtFile(f)
if err != nil {
return nil, err
}
}

concatStr := fmt.Sprintf("concat:%s", strings.Join(files, "|"))

outputFilePath := filepath.Join(input.OutputDir.Local(), filepath.Clean(input.Title)+".srt")
params := []string{
"-hide_banner",
"-progress", "pipe:1",
"-hide_banner",
"-i", concatStr,
"-c", "copy",
"-y",
outputFilePath,
}

_, err = ffmpeg.Do(params, ffmpeg.StreamInfo{}, progressCallback)
if err != nil {
return nil, err
}

err = ensureValidSrtFile(outputFilePath)
if err != nil {
return nil, err
}

outputPath, err := paths.Parse(outputFilePath)
if err != nil {
return nil, err
}

return &common.MergeResult{
Path: outputPath,
}, err
}

// MergeSubtitles does the merging of subtitles for the mormal mediabanken export
func MergeSubtitles(input common.MergeInput, progressCallback ffmpeg.ProgressCallback) (*common.MergeResult, error) {
var files []string
// for each file, extract the specified range and save the result to a file.
Expand Down
65 changes: 65 additions & 0 deletions services/transcode/merge_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transcode

import (
"os"
"testing"

"github.com/bcc-code/bcc-media-flows/common"
Expand Down Expand Up @@ -101,3 +102,67 @@ func Test_mergeItemsToStereoStream_64Chan(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "[0:a]pan=stereo|c0=c5|c1=c6[a0]", str)
}

func Test_MergeSubtitles(t *testing.T) {
output := paths.MustParse("./testdata/generated/")
subPath := paths.MustParse("./testdata/sub1.srt")

input := common.MergeInput{
OutputDir: output,
WorkDir: output,
Title: t.Name(),
Items: []common.MergeInputItem{
common.MergeInputItem{
Path: subPath,
Start: 0,
End: 5,
},
common.MergeInputItem{
Path: subPath,
Start: 10,
End: 15,
},
},
}

res, err := MergeSubtitles(input, nil)
assert.NoError(t, err)
assert.Equal(t, paths.MustParse("./testdata/generated/Test_MergeSubtitles.srt"), res.Path)
assert.FileExists(t, res.Path.Local())

actual, _ := os.ReadFile(res.Path.Local())
expected, _ := os.ReadFile("./testdata/subtitles_merge_result.srt")

assert.Equal(t, expected, actual)
}

func Test_MergeSubtitlesByOffset(t *testing.T) {
output := paths.MustParse("./testdata/generated/")
subPath := paths.MustParse("./testdata/sub1.srt")

input := common.MergeInput{
OutputDir: output,
WorkDir: output,
Title: t.Name(),
Items: []common.MergeInputItem{
common.MergeInputItem{
Path: subPath,
StartOffset: 0,
},
common.MergeInputItem{
Path: subPath,
StartOffset: 100,
},
},
}

res, err := MergeSubtitlesByOffset(input, nil)
assert.NoError(t, err)
assert.Equal(t, paths.MustParse("./testdata/generated/Test_MergeSubtitlesByOffset.srt"), res.Path)
assert.FileExists(t, res.Path.Local())

actual, _ := os.ReadFile(res.Path.Local())
expected, _ := os.ReadFile("./testdata/subtitles_merge_by_offset_result.srt")

assert.Equal(t, expected, actual)
}
19 changes: 19 additions & 0 deletions services/transcode/testdata/sub1.srt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
1
00:00:01,000 --> 00:00:04,000
Hello, this is the first subtitle
Second line of the first subtitle

2
00:00:04,500 --> 00:00:07,800
This is the second subtitle
It can also have multiple lines
And even more lines

3
00:00:08,000 --> 00:00:12,000
Third subtitle entry

4
00:00:13,000 --> 00:00:15,500
- Multiple speakers can be shown
- Using dashes like this
40 changes: 40 additions & 0 deletions services/transcode/testdata/subtitles_merge_by_offset_result.srt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
1
00:00:01,000 --> 00:00:04,000
Hello, this is the first subtitle
Second line of the first subtitle

2
00:00:04,500 --> 00:00:07,800
This is the second subtitle
It can also have multiple lines
And even more lines

3
00:00:08,000 --> 00:00:12,000
Third subtitle entry

4
00:00:13,000 --> 00:00:15,500
- Multiple speakers can be shown
- Using dashes like this

5
00:01:41,000 --> 00:01:44,000
Hello, this is the first subtitle
Second line of the first subtitle

6
00:01:44,500 --> 00:01:47,800
This is the second subtitle
It can also have multiple lines
And even more lines

7
00:01:48,000 --> 00:01:52,000
Third subtitle entry

8
00:01:53,000 --> 00:01:55,500
- Multiple speakers can be shown
- Using dashes like this

16 changes: 16 additions & 0 deletions services/transcode/testdata/subtitles_merge_result.srt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
1
00:00:01,000 --> 00:00:04,000
Hello, this is the first subtitle
Second line of the first subtitle

2
00:00:04,500 --> 00:00:07,800
This is the second subtitle
It can also have multiple lines
And even more lines

3
00:00:18,000 --> 00:00:20,500
- Multiple speakers can be shown
- Using dashes like this

Loading

0 comments on commit 41f5f88

Please sign in to comment.