From 0d8b5927704052cb55889b623b8eab1a47e4d69d Mon Sep 17 00:00:00 2001 From: Joseph Date: Fri, 23 Feb 2024 23:23:47 +0000 Subject: [PATCH 1/7] update video title --- goutubedl_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goutubedl_test.go b/goutubedl_test.go index 49cd334..08c3153 100644 --- a/goutubedl_test.go +++ b/goutubedl_test.go @@ -135,7 +135,7 @@ func TestParseInfo(t *testing.T) { expectedTitle string }{ {"https://soundcloud.com/avalonemerson/avalon-emerson-live-at-printworks-london-march-2017", "Avalon Emerson Live at Printworks London 2017"}, - {"https://www.infoq.com/presentations/Simple-Made-Easy", "Simple Made Easy"}, + {"https://www.infoq.com/presentations/Simple-Made-Easy", "Simple Made Easy - InfoQ"}, {"https://www.youtube.com/watch?v=uVYWQJ5BB_w", "A Radiolab Producer on the Making of a Podcast"}, } { t.Run(c.url, func(t *testing.T) { From bab64bab72052c0aa2b2a0b4eb8453878b826914 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 00:29:36 +0000 Subject: [PATCH 2/7] add support for youtube-dl channels --- goutubedl.go | 47 +++++++++++++++++++++++++++++++++++++++++------ goutubedl_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/goutubedl.go b/goutubedl.go index 7b86e6a..677cf60 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -205,12 +205,15 @@ const ( TypeSingle // TypePlaylist playlist with multiple tracks, files etc TypePlaylist + // TypeChannel channel containing one or more playlists, which will be flattened + TypeChannel ) var TypeFromString = map[string]Type{ "any": TypeAny, "single": TypeSingle, "playlist": TypePlaylist, + "channel": TypeChannel, } // Options for New() @@ -248,7 +251,12 @@ func Version(ctx context.Context) (string, error) { // Downloads given URL using the given options and filter (usually a format id or quality designator). // If filter is empty, then youtube-dl will use its default format selector. -func Download(ctx context.Context, rawURL string, options Options, filter string) (*DownloadResult, error) { +func Download( + ctx context.Context, + rawURL string, + options Options, + filter string, +) (*DownloadResult, error) { options.noInfoDownload = true d, err := New(ctx, rawURL, options) if err != nil { @@ -286,7 +294,11 @@ func New(ctx context.Context, rawURL string, options Options) (result Result, er }, nil } -func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info, rawJSON []byte, err error) { +func infoFromURL( + ctx context.Context, + rawURL string, + options Options, +) (info Info, rawJSON []byte, err error) { cmd := exec.CommandContext( ctx, ProbePath(), @@ -309,7 +321,7 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info cmd.Args = append(cmd.Args, "--downloader", options.Downloader) } switch options.Type { - case TypePlaylist: + case TypePlaylist, TypeChannel: cmd.Args = append(cmd.Args, "--yes-playlist") if options.PlaylistStart > 0 { @@ -453,6 +465,23 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info info.Entries = filteredEntrise } + // channels contain playlists, so recurse into them + if options.Type == TypeChannel { + var filteredEntrise []Info + for _, p := range info.Entries { + if p.Type != "playlist" { + continue + } + for _, e := range p.Entries { + if e.ID == "" { + continue + } + filteredEntrise = append(filteredEntrise, e) + } + } + info.Entries = filteredEntrise + } + return info, stdoutBuf.Bytes(), nil } @@ -488,12 +517,18 @@ type DownloadOptions struct { PlaylistIndex int } -func (result Result) DownloadWithOptions(ctx context.Context, options DownloadOptions) (*DownloadResult, error) { +func (result Result) DownloadWithOptions( + ctx context.Context, + options DownloadOptions, +) (*DownloadResult, error) { debugLog := result.Options.DebugLog if !result.Options.noInfoDownload { - if (result.Info.Type == "playlist" || result.Info.Type == "multi_video") && options.PlaylistIndex == 0 { - return nil, fmt.Errorf("can't download a playlist when the playlist index options is not set") + if (result.Info.Type == "playlist" || result.Info.Type == "multi_video") && + options.PlaylistIndex == 0 { + return nil, fmt.Errorf( + "can't download a playlist when the playlist index options is not set", + ) } } diff --git a/goutubedl_test.go b/goutubedl_test.go index 08c3153..7d7fd6c 100644 --- a/goutubedl_test.go +++ b/goutubedl_test.go @@ -217,6 +217,38 @@ func TestPlaylist(t *testing.T) { } } +func TestChannel(t *testing.T) { + defer leakChecks(t)() + + ydlResult, ydlResultErr := goutubedl.New( + context.Background(), + channelRawURL, + goutubedl.Options{ + Type: goutubedl.TypeChannel, + DownloadThumbnail: false, + }, + ) + + if ydlResultErr != nil { + t.Errorf("failed to download: %s", ydlResultErr) + } + + expectedTitle := "Simon Yapp" + if ydlResult.Info.Title != expectedTitle { + t.Errorf("expected title %q got %q", expectedTitle, ydlResult.Info.Title) + } + + expectedEntries := 5 + if len(ydlResult.Info.Entries) != expectedEntries { + t.Errorf("expected %d entries got %d", expectedEntries, len(ydlResult.Info.Entries)) + } + + expectedTitleOne := "#RNLI Shoreham #LifeBoat demo of launch." + if ydlResult.Info.Entries[0].Title != expectedTitleOne { + t.Errorf("expected title %q got %q", expectedTitleOne, ydlResult.Info.Entries[0].Title) + } +} + func TestUnsupportedURL(t *testing.T) { defer leaktest.Check(t)() From 7f2357b6ae76182d26b427586d63e83efc27fb1a Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 09:47:59 +0000 Subject: [PATCH 3/7] add channelRawURL whoops --- goutubedl_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/goutubedl_test.go b/goutubedl_test.go index 7d7fd6c..711aa40 100644 --- a/goutubedl_test.go +++ b/goutubedl_test.go @@ -21,9 +21,12 @@ import ( "github.com/wader/osleaktest" ) -const testVideoRawURL = "https://www.youtube.com/watch?v=C0DPdy98e4c" -const playlistRawURL = "https://soundcloud.com/mattheis/sets/kindred-phenomena" -const subtitlesTestVideoRawURL = "https://www.youtube.com/watch?v=QRS8MkLhQmM" +const ( + testVideoRawURL = "https://www.youtube.com/watch?v=C0DPdy98e4c" + playlistRawURL = "https://soundcloud.com/mattheis/sets/kindred-phenomena" + channelRawURL = "https://www.youtube.com/channel/UCHDm-DKoMyJxKVgwGmuTaQA" + subtitlesTestVideoRawURL = "https://www.youtube.com/watch?v=QRS8MkLhQmM" +) func leakChecks(t *testing.T) func() { leakFn := leaktest.Check(t) From 3f0fe87c2e2d8cd3709fc205e4fee8adbc9d9db3 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 10:22:35 +0000 Subject: [PATCH 4/7] also require Options.PlaylistIndex for TypeChannel --- goutubedl.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/goutubedl.go b/goutubedl.go index 677cf60..f8c8f3b 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -524,7 +524,9 @@ func (result Result) DownloadWithOptions( debugLog := result.Options.DebugLog if !result.Options.noInfoDownload { - if (result.Info.Type == "playlist" || result.Info.Type == "multi_video") && + if (result.Info.Type == "playlist" || + result.Info.Type == "multi_video" || + result.Info.Type == "channel") && options.PlaylistIndex == 0 { return nil, fmt.Errorf( "can't download a playlist when the playlist index options is not set", From 912248327a5a9ea40db762184372e8fa279f2ca7 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 11:08:45 +0000 Subject: [PATCH 5/7] simplify retrieval of Entries --- goutubedl.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/goutubedl.go b/goutubedl.go index f8c8f3b..a254b41 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -454,30 +454,31 @@ func infoFromURL( } // as we ignore errors for playlists some entries might show up as null - if options.Type == TypePlaylist { + // + // note: instead of doing full recursion, we assume entries in + // playlists and channels are at most 2 levels deep, and we just + // collect entries from both levels. + // + // the following cases have not been tested: + // + // - entries that are more than 2 levels deep (will be missed) + // - the ability to restrict entries to a single level (we include both levels) + if options.Type == TypePlaylist || options.Type == TypeChannel { var filteredEntrise []Info for _, e := range info.Entries { if e.ID == "" { continue } - filteredEntrise = append(filteredEntrise, e) - } - info.Entries = filteredEntrise - } - - // channels contain playlists, so recurse into them - if options.Type == TypeChannel { - var filteredEntrise []Info - for _, p := range info.Entries { - if p.Type != "playlist" { + if e.Type == "playlist" { + for _, ee := range e.Entries { + if ee.ID == "" { + continue + } + filteredEntrise = append(filteredEntrise, ee) + } continue } - for _, e := range p.Entries { - if e.ID == "" { - continue - } - filteredEntrise = append(filteredEntrise, e) - } + filteredEntrise = append(filteredEntrise, e) } info.Entries = filteredEntrise } From bbd58324d8863df86279c8419fd164e54cecfda0 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 11:22:45 +0000 Subject: [PATCH 6/7] make loop more readable --- goutubedl.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/goutubedl.go b/goutubedl.go index a254b41..fcacc87 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -466,9 +466,6 @@ func infoFromURL( if options.Type == TypePlaylist || options.Type == TypeChannel { var filteredEntrise []Info for _, e := range info.Entries { - if e.ID == "" { - continue - } if e.Type == "playlist" { for _, ee := range e.Entries { if ee.ID == "" { @@ -477,8 +474,9 @@ func infoFromURL( filteredEntrise = append(filteredEntrise, ee) } continue + } else if e.ID == "" { + filteredEntrise = append(filteredEntrise, e) } - filteredEntrise = append(filteredEntrise, e) } info.Entries = filteredEntrise } From 49794b3db465531662a622e13f0b052a2dcee171 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sat, 24 Feb 2024 11:23:48 +0000 Subject: [PATCH 7/7] fix incorrect condition should have tested! --- goutubedl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goutubedl.go b/goutubedl.go index fcacc87..d76d302 100644 --- a/goutubedl.go +++ b/goutubedl.go @@ -474,7 +474,7 @@ func infoFromURL( filteredEntrise = append(filteredEntrise, ee) } continue - } else if e.ID == "" { + } else if e.ID != "" { filteredEntrise = append(filteredEntrise, e) } }