diff --git a/Dockerfile b/Dockerfile index 410c281..132e76a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,8 @@ RUN \ apt-get update -q && \ apt-get install -y -q python-is-python3 && \ curl -L https://github.com/yt-dlp/yt-dlp/releases/download/$YT_DLP/yt-dlp -o /usr/local/bin/yt-dlp && \ - chmod a+x /usr/local/bin/yt-dlp + chmod a+x /usr/local/bin/yt-dlp && \ + apt-get install -y ffmpeg FROM base AS dev diff --git a/goutubedl.go b/goutubedl.go index 1631ba1..6340d1a 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -207,6 +207,7 @@ type Options struct { Downloader string // --downloader DownloadThumbnail bool DownloadSubtitles bool + DownloadSections string // --download-sections ProxyUrl string // --proxy URL http://host:port or socks5://host:port DebugLog Printer StderrFn func(cmd *exec.Cmd) io.Writer // if not nil, function to get Writer for stderr @@ -555,6 +556,10 @@ func (result Result) DownloadWithOptions(ctx context.Context, options DownloadOp if result.Options.Downloader != "" { cmd.Args = append(cmd.Args, "--downloader", result.Options.Downloader) } + + if result.Options.DownloadSections != "" { + cmd.Args = append(cmd.Args, "--download-sections", result.Options.DownloadSections) + } if result.Options.MergeOutputFormat != "" { cmd.Args = append(cmd.Args, diff --git a/goutubedl_test.go b/goutubedl_test.go index b5dbd95..ce9f3b5 100644 --- a/goutubedl_test.go +++ b/goutubedl_test.go @@ -7,9 +7,12 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" + "os" "os/exec" "regexp" + "strconv" "strings" "testing" @@ -285,6 +288,74 @@ func TestSubtitles(t *testing.T) { } } +func TestDownloadSections(t *testing.T) { + defer leakChecks(t)() + + fileName := "durationTestingFile" + duration := 5 + + cmd := exec.Command("ffmpeg", "-version") + _, err := cmd.Output() + if err != nil { + t.Errorf("failed to check ffmpeg installed: %s", err) + } + + ydlResult, ydlResultErr := goutubedl.New( + context.Background(), + "https://www.youtube.com/watch?v=OyuL5biOQ94", + goutubedl.Options{ + DownloadSections: fmt.Sprintf("*0:0-0:%d", duration), + }) + + + if ydlResult.Options.DownloadSections != "*0:0-0:5" { + t.Errorf("failed to setup --download-sections") + } + if ydlResultErr != nil { + t.Errorf("failed to download: %s", ydlResultErr) + } + dr, err := ydlResult.Download(context.Background(), "best") + if err != nil { + t.Fatal(err) + } + + f, err := os.Create(fileName) + if err != nil { + t.Fatal(err) + } + defer f.Close() + _, err = io.Copy(f, dr) + if err != nil { + t.Fatal(err) + } + + cmd = exec.Command("ffprobe", "-v", "quiet", "-show_entries", "format=duration", fileName) + stdout, err := cmd.Output() + if err != nil { + t.Fatal(err) + } + + var gotDurationString string + output := string(stdout) + for _, line := range strings.Split(output, "\n") { + if strings.Contains(line, "duration") { + if d, found := strings.CutPrefix(line, "duration="); found { + gotDurationString = d + } + } + } + + gotDuration, err := strconv.ParseFloat(gotDurationString, 32) + if err != nil { + t.Fatal(err) + } + seconds := int(gotDuration) + if seconds != duration { + t.Fatalf("didnot get expected duration of %d, but got %d", duration, seconds) + } + dr.Close() +} + func TestErrorNotAPlaylist(t *testing.T) { defer leakChecks(t)()