171 Commits

Author SHA1 Message Date
cb39bec3a6 do not filter out m3u8
Some checks failed
Build on push and PRs / build (push) Failing after 5s
Automatic version updates / version_update (push) Failing after 7s
2025-02-09 17:53:12 +03:00
1815e33339 m3u8_native
Some checks failed
Build on push and PRs / build (push) Failing after 5s
Automatic version updates / version_update (push) Failing after 6s
2025-02-07 16:31:17 +03:00
646859b4c1 add foramt chose for subtitlse
Some checks failed
Build on push and PRs / build (push) Failing after 5s
Automatic version updates / version_update (push) Failing after 7s
2025-02-03 20:52:03 +03:00
55e7486f50 add foramt chose for subtitlse
Some checks failed
Build on push and PRs / build (push) Failing after 4s
2025-02-03 20:45:42 +03:00
506804ebb3 captions err
Some checks failed
Build on push and PRs / build (push) Failing after 4s
2025-02-03 20:18:53 +03:00
cafbe3c1a9 captions err
Some checks failed
Build on push and PRs / build (push) Failing after 4s
2025-02-03 20:15:24 +03:00
63a707e70a add err
Some checks failed
Build on push and PRs / build (push) Failing after 4s
2025-02-03 20:14:44 +03:00
c84fe2feb6 add caption
Some checks failed
Build on push and PRs / build (push) Failing after 6s
2025-02-03 19:58:09 +03:00
24cc3575e4 info args
Some checks failed
Build on push and PRs / build (push) Failing after 5s
Automatic version updates / version_update (push) Failing after 6s
2025-01-22 22:51:12 +03:00
1a1941ca29 rename fork
Some checks failed
Build on push and PRs / build (push) Failing after 5s
Automatic version updates / version_update (push) Failing after 7s
2025-01-14 22:09:14 +03:00
faa303a45c rename fork
Some checks failed
Build on push and PRs / build (push) Failing after 4s
2025-01-14 21:08:33 +03:00
b534e45a9c add extractor args
Some checks failed
Build on push and PRs / build (push) Failing after 14s
2025-01-14 21:06:06 +03:00
e8e101773e Merge pull request #221 from wader/bump-yt-dlp-2025.01.12
Update yt-dlp to 2025.01.12 from 2024.12.23
2025-01-13 18:02:23 +01:00
2ce13a3453 Update yt-dlp to 2025.01.12 from 2024.12.23
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2025.01.12
2025-01-13 16:02:24 +00:00
33e26ae818 Merge pull request #219 from wader/bump-yt-dlp-2024.12.23
Update yt-dlp to 2024.12.23 from 2024.12.13
2024-12-24 17:04:41 +01:00
940ca8b439 Update yt-dlp to 2024.12.23 from 2024.12.13
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.12.23
2024-12-24 16:02:12 +00:00
8ebc956d58 Merge pull request #218 from wader/bump-yt-dlp-2024.12.13
Update yt-dlp to 2024.12.13 from 2024.12.06
2024-12-13 17:35:00 +01:00
fe2e84d624 Update yt-dlp to 2024.12.13 from 2024.12.06
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.12.13
2024-12-13 16:02:29 +00:00
4749af12f9 Merge pull request #217 from wader/bump-yt-dlp-2024.12.06
Update yt-dlp to 2024.12.06 from 2024.12.03
2024-12-11 13:28:18 +01:00
b80c451351 Update yt-dlp to 2024.12.06 from 2024.12.03
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.12.06
2024-12-07 16:02:15 +00:00
63dcb4b7f5 Merge pull request #214 from wader/bump-golang-1.23.4
Update golang to 1.23.4 from 1.23.3
2024-12-04 17:57:58 +01:00
56b36e04e0 Merge pull request #215 from wader/bump-yt-dlp-2024.12.03
Update yt-dlp to 2024.12.03 from 2024.11.18
2024-12-04 17:57:52 +01:00
2623ce39cc Update yt-dlp to 2024.12.03 from 2024.11.18
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.12.03
2024-12-04 16:02:38 +00:00
a8a0daa240 Update golang to 1.23.4 from 1.23.3
Release notes https://golang.org/doc/devel/release.html
2024-12-04 16:02:36 +00:00
5e1bb9940f Merge pull request #213 from wader/bump-yt-dlp-2024.11.18
Update yt-dlp to 2024.11.18 from 2024.11.04
2024-11-18 17:08:03 +01:00
736ec471fb Update yt-dlp to 2024.11.18 from 2024.11.04
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.11.18
2024-11-18 16:02:24 +00:00
f902ca5cee Merge pull request #212 from wader/bump-golang-1.23.3
Update golang to 1.23.3 from 1.23.2
2024-11-07 17:49:26 +01:00
ce1d7dd3a4 Update golang to 1.23.3 from 1.23.2
Release notes https://golang.org/doc/devel/release.html
2024-11-07 16:02:18 +00:00
d51f319f36 Merge pull request #211 from wader/bump-yt-dlp-2024.11.04
Update yt-dlp to 2024.11.04 from 2024.10.22
2024-11-04 17:50:05 +01:00
f16338a0be Update yt-dlp to 2024.11.04 from 2024.10.22
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.11.04
2024-11-04 16:02:45 +00:00
bb47becd77 Merge pull request #210 from wader/bump-yt-dlp-2024.10.22
Update yt-dlp to 2024.10.22 from 2024.10.07
2024-10-22 19:06:23 +02:00
137b17daf1 Update yt-dlp to 2024.10.22 from 2024.10.07
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.10.22
2024-10-22 16:02:37 +00:00
1b8fc57051 Merge pull request #209 from wader/bump-yt-dlp-2024.10.07
Update yt-dlp to 2024.10.07 from 2024.09.27
2024-10-08 20:49:52 +02:00
7f2cc0f2b4 Update yt-dlp to 2024.10.07 from 2024.09.27
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.10.07
2024-10-08 16:02:15 +00:00
eb679c5ee1 Merge pull request #206 from wader/bump-yt-dlp-2024.09.27
Update yt-dlp to 2024.09.27 from 2024.08.06
2024-10-07 23:05:50 +02:00
c71ec4178b Update yt-dlp to 2024.09.27 from 2024.08.06
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.09.27
2024-10-07 22:58:10 +02:00
24655e2ef8 Merge pull request #207 from wader/bump-golang-1.23.2
Update golang to 1.23.2 from 1.22.6
2024-10-07 22:56:42 +02:00
74ed4258b3 Update golang to 1.23.2 from 1.22.6
Release notes https://golang.org/doc/devel/release.html
2024-10-07 22:53:53 +02:00
3d96f632e1 Merge pull request #208 from wader/repair-most-tests
Make most tests work again
2024-10-07 22:53:18 +02:00
eb9555dbed Make most tests work again 2024-10-07 22:43:05 +02:00
fed2a6ab45 Merge pull request #204 from NexonSU/master
impersonate support
2024-09-10 10:11:16 +02:00
945bbd6026 result.Options.Impersonate removal 2024-09-09 11:05:09 +03:00
d104f30286 impersonate arg 2024-09-08 22:29:37 +03:00
a623bde37b Merge pull request #195 from gamersindo1223/master
Added Download Audio Only
2024-08-18 12:19:19 +02:00
90467bcf8a Merge pull request #202 from ar2rworld/AddCookiesOption
Adding Cookies to Options
2024-08-17 19:07:30 +02:00
d47fecba92 Adding Cookies to Options 2024-08-16 22:55:25 -07:00
65804d5c0f Moved to DownloadOptions 2024-08-12 12:02:26 +00:00
95037e2caf Merge branch 'wader:master' into master 2024-08-12 18:36:45 +07:00
346cfb47a0 Update goutubedl.go 2024-08-12 18:15:44 +07:00
5d0a261ec7 Merge pull request #200 from wader/bump-golang-1.22.6
Update golang to 1.22.6 from 1.22.5
2024-08-07 18:20:54 +02:00
e76467b99a Update golang to 1.22.6 from 1.22.5
Release notes https://golang.org/doc/devel/release.html
2024-08-07 16:02:25 +00:00
ccf6ee70f7 Merge pull request #199 from wader/bump-yt-dlp-2024.08.06
Update yt-dlp to 2024.08.06 from 2024.08.01
2024-08-06 18:05:00 +02:00
1397992b2e Update yt-dlp to 2024.08.06 from 2024.08.01
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.08.06
2024-08-06 16:02:17 +00:00
d5bf934f84 Merge pull request #198 from wader/bump-yt-dlp-2024.08.01
Update yt-dlp to 2024.08.01 from 2024.07.25
2024-08-01 18:22:26 +02:00
f62b41e2a6 Update yt-dlp to 2024.08.01 from 2024.07.25
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.08.01
2024-08-01 16:12:53 +00:00
4a4a53c745 Merge pull request #197 from wader/bump-yt-dlp-2024.07.25
Update yt-dlp to 2024.07.25 from 2024.07.16
2024-07-25 19:24:41 +02:00
242d6af088 Update yt-dlp to 2024.07.25 from 2024.07.16
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.25
2024-07-25 16:12:54 +00:00
5d7e2d8fc4 Merge pull request #196 from wader/bump-yt-dlp-2024.07.16
Update yt-dlp to 2024.07.16 from 2024.07.09
2024-07-17 20:20:40 +02:00
131c95e1dd Update yt-dlp to 2024.07.16 from 2024.07.09
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.16
2024-07-17 16:02:25 +00:00
3abe5ae66a Added Download Audio Only 2024-07-11 16:13:41 +00:00
0986214517 Merge pull request #194 from wader/bump-yt-dlp-2024.07.09
Update yt-dlp to 2024.07.09 from 2024.07.07
2024-07-09 18:13:10 +02:00
a52c8dca32 Update yt-dlp to 2024.07.09 from 2024.07.07
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.09
2024-07-09 16:02:11 +00:00
f4ee4aff9a Merge pull request #193 from wader/bump-yt-dlp-2024.07.07
Update yt-dlp to 2024.07.07 from 2024.07.02
2024-07-08 18:41:14 +02:00
b5b253140e Update yt-dlp to 2024.07.07 from 2024.07.02
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.07
2024-07-08 16:02:19 +00:00
4b7a642b1f go fmt and some style fixes 2024-07-08 12:17:00 +02:00
15628e7770 Merge pull request #192 from gamersindo1223/master
Add force IPV4 support
2024-07-08 12:15:41 +02:00
4b359ddd61 Use existing Force IPV4 Config 2024-07-08 10:06:40 +00:00
7993835dad Add some changes (DownloadOptions, Add comments) 2024-07-07 22:45:24 +00:00
0846732ece Add force IPV4 support 2024-07-07 12:20:23 +00:00
3eb676f2fd Merge pull request #190 from wader/bump-golang-1.22.5
Update golang to 1.22.5 from 1.22.4
2024-07-03 18:12:54 +02:00
dfbb1770e4 Merge pull request #191 from wader/bump-yt-dlp-2024.07.02
Update yt-dlp to 2024.07.02 from 2024.07.01
2024-07-03 18:06:42 +02:00
3004939371 Update yt-dlp to 2024.07.02 from 2024.07.01
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.02
2024-07-03 16:02:16 +00:00
79030f02cf Update golang to 1.22.5 from 1.22.4
Release notes https://golang.org/doc/devel/release.html
2024-07-03 16:02:14 +00:00
b861e6f3a2 Merge pull request #189 from wader/bump-yt-dlp-2024.07.01
Update yt-dlp to 2024.07.01 from 2024.05.27
2024-07-02 18:13:31 +02:00
cb86a6c681 Update yt-dlp to 2024.07.01 from 2024.05.27
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.07.01
2024-07-02 16:02:03 +00:00
0ca6990373 Run CI on PR or master push
Previously two CI jobs were done for each PR
2024-06-26 09:11:25 +02:00
8cef76d0c0 Merge pull request #188 from wader/use-cache-and-netrc
Remove --no-cahce-dir and add --netrc
2024-06-26 09:06:46 +02:00
cad168222b Remove --no-cahce-dir and add --netrc
--no-cache-dir was probably added in the belif to prevent storing big cache files
but it seems to be extractor session data to speed up auth etc.
--netrc is now default so that yt-dlp plugins works better

Fixes #187
2024-06-26 09:00:49 +02:00
8598c9e6bb Merge pull request #186 from wader/bump-golang-1.22.4
Update golang to 1.22.4 from 1.22.3
2024-06-05 18:51:52 +02:00
5cb220b4d7 Update golang to 1.22.4 from 1.22.3
Release notes https://golang.org/doc/devel/release.html
2024-06-05 16:02:02 +00:00
74fbd541cd Merge pull request #185 from wader/bump-yt-dlp-2024.05.27
Update yt-dlp to 2024.05.27 from 2024.05.26
2024-05-28 18:12:42 +02:00
4dfa55bcfd Update yt-dlp to 2024.05.27 from 2024.05.26
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.05.27
2024-05-28 16:02:04 +00:00
fdbd42f666 Merge pull request #184 from wader/bump-yt-dlp-2024.05.26
Update yt-dlp to 2024.05.26 from 2024.04.09
2024-05-27 18:32:10 +02:00
2ac97f392a Update yt-dlp to 2024.05.26 from 2024.04.09
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.05.26
2024-05-27 16:02:07 +00:00
aba4746463 Merge pull request #183 from wader/bump-golang-1.22.3
Update golang to 1.22.3 from 1.22.2
2024-05-09 00:00:59 +02:00
1fdc3448b1 Update golang to 1.22.3 from 1.22.2
Release notes https://golang.org/doc/devel/release.html
2024-05-08 16:02:12 +00:00
598fce21e7 Merge pull request #182 from wader/bump-yt-dlp-2024.04.09
Update yt-dlp to 2024.04.09 from 2024.03.10
2024-04-10 18:32:30 +02:00
ff8aaa0333 Update yt-dlp to 2024.04.09 from 2024.03.10
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.04.09
2024-04-10 16:02:06 +00:00
7b1a15b73a Merge pull request #181 from wader/bump-golang-1.22.2
Update golang to 1.22.2 from 1.22.1
2024-04-04 18:15:12 +02:00
24fd49fe06 Update golang to 1.22.2 from 1.22.1
Release notes https://golang.org/doc/devel/release.html
2024-04-04 16:02:18 +00:00
04eb47cbe8 Merge pull request #179 from j1cs/master
new feature: cookies from browser option
2024-03-14 09:32:54 +01:00
ae007cbb01 new feature
new feature added. now you can use cookies from browser option
2024-03-13 23:46:25 -03:00
95260c8d4e Merge pull request #178 from wader/download-error
Handle errors at download better
2024-03-13 21:01:52 +01:00
8b5d2141e0 Handle errors at download better
Related to #173
2024-03-13 20:54:07 +01:00
95b1142af6 Merge pull request #176 from wader/cleanup-typos
Fix deprecations and typos
2024-03-12 01:01:56 +01:00
8dad7bee96 Fix deprecations and typos 2024-03-12 00:57:21 +01:00
396be61f8d Merge pull request #175 from wader/bump-yt-dlp-2024.03.10
Update yt-dlp to 2024.03.10 from 2023.12.30
2024-03-10 23:29:15 +01:00
98ea7a02a4 Update yt-dlp to 2024.03.10 from 2023.12.30
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2024.03.10
2024-03-10 22:26:24 +00:00
c309f999af Merge pull request #174 from wader/bump-golang-1.22.1
Update golang to 1.22.1 from 1.22.0
2024-03-06 17:15:36 +01:00
47d887d633 Update golang to 1.22.1 from 1.22.0
Release notes https://golang.org/doc/devel/release.html
2024-03-06 16:02:12 +00:00
05e5ea7b72 Merge pull request #172 from hejops/channel
add support for channel URLs
2024-02-24 16:32:20 +01:00
49794b3db4 fix incorrect condition
should have tested!
2024-02-24 11:23:48 +00:00
bbd58324d8 make loop more readable 2024-02-24 11:22:45 +00:00
912248327a simplify retrieval of Entries 2024-02-24 11:08:45 +00:00
3f0fe87c2e also require Options.PlaylistIndex for TypeChannel 2024-02-24 10:22:35 +00:00
7f2357b6ae add channelRawURL
whoops
2024-02-24 09:47:59 +00:00
bab64bab72 add support for youtube-dl channels 2024-02-24 00:29:36 +00:00
0d8b592770 update video title 2024-02-23 23:23:47 +00:00
8b34407df2 Merge pull request #171 from wader/bump-golang-1.22.0
Update golang to 1.22.0 from 1.21.6
2024-02-07 17:07:46 +01:00
683e9549e7 Update golang to 1.22.0 from 1.21.6
Release notes https://golang.org/doc/devel/release.html
2024-02-07 16:01:57 +00:00
01bac34c4c Merge pull request #170 from wader/path-probe
By default probe for youtube-dl or yt-dlp binary
2024-01-13 18:11:34 +01:00
efe7640980 By default probe for youtube-dl or yt-dlp binary
Still possible to override with Path
2024-01-13 18:07:51 +01:00
adddb11ead Merge pull request #169 from wader/bump-golang-1.21.6
Update golang to 1.21.6 from 1.21.5
2024-01-10 17:46:36 +01:00
a86f15df0f Update golang to 1.21.6 from 1.21.5
Release notes https://golang.org/doc/devel/release.html
2024-01-10 16:02:14 +00:00
7e5db67282 Merge pull request #168 from wader/bump-yt-dlp-2023.12.30
Update yt-dlp to 2023.12.30 from 2023.11.16
2023-12-31 17:08:41 +01:00
e3c7c3e3f5 Update yt-dlp to 2023.12.30 from 2023.11.16
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.12.30
2023-12-31 16:01:58 +00:00
5019379921 Merge pull request #167 from wader/bump-golang-1.21.5
Update golang to 1.21.5 from 1.21.4
2023-12-06 17:45:17 +01:00
8ba6398d97 Update golang to 1.21.5 from 1.21.4
Release notes https://golang.org/doc/devel/release.html
2023-12-06 16:02:10 +00:00
f88434dfb2 Merge pull request #166 from wader/bump-yt-dlp-2023.11.16
Update yt-dlp to 2023.11.16 from 2023.11.14
2023-11-16 17:29:35 +01:00
dbbbde44cb Update yt-dlp to 2023.11.16 from 2023.11.14
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.11.16
2023-11-16 16:02:07 +00:00
e00731da32 Merge pull request #165 from wader/bump-yt-dlp-2023.11.14
Update yt-dlp to 2023.11.14 from 2023.10.13
2023-11-15 17:22:12 +01:00
8249795c0a Update yt-dlp to 2023.11.14 from 2023.10.13
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.11.14
2023-11-15 16:02:13 +00:00
9bed0812c3 Merge pull request #164 from wader/bump-golang-1.21.4
Update golang to 1.21.4 from 1.21.3
2023-11-08 19:02:03 +01:00
392bd0f15e Update golang to 1.21.4 from 1.21.3
Release notes https://golang.org/doc/devel/release.html
2023-11-08 16:02:13 +00:00
307a49a1d3 Merge pull request #163 from wader/bump-yt-dlp-2023.10.13
Update yt-dlp to 2023.10.13 from 2023.10.07
2023-10-14 18:37:28 +02:00
c702698079 Update yt-dlp to 2023.10.13 from 2023.10.07
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.10.13
2023-10-14 16:02:07 +00:00
782e0b7262 Merge pull request #162 from wader/bump-golang-1.21.3
Update golang to 1.21.3 from 1.21.2
2023-10-11 18:14:02 +02:00
ab26347454 Update golang to 1.21.3 from 1.21.2
Release notes https://golang.org/doc/devel/release.html
2023-10-11 16:02:19 +00:00
02a0a71721 Merge pull request #161 from wader/bump-yt-dlp-2023.10.07
Update yt-dlp to 2023.10.07 from 2023.09.24
2023-10-07 18:09:16 +02:00
ed998eb769 Update yt-dlp to 2023.10.07 from 2023.09.24
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.10.07
2023-10-07 16:01:57 +00:00
14a15258d0 Merge pull request #160 from wader/bump-golang-1.21.2
Update golang to 1.21.2 from 1.21.1
2023-10-06 18:05:56 +02:00
5300e997b8 Update golang to 1.21.2 from 1.21.1
Release notes https://golang.org/doc/devel/release.html
2023-10-06 16:02:10 +00:00
3f0889f7e8 Merge pull request #157 from ar2rworld/DownloadSections
DownloadSections option
2023-10-03 23:54:39 +02:00
12053d10b1 fix file close 2023-10-03 17:09:40 -04:00
fc0dec1f1f ffprobe testing duration 2023-10-03 14:58:09 -04:00
427b7fa536 Merge pull request #159 from wader/bump-yt-dlp-2023.09.24
Update yt-dlp to 2023.09.24 from 2023.07.06
2023-09-24 18:57:37 +02:00
e431cded03 Update yt-dlp to 2023.09.24 from 2023.07.06
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.09.24
2023-09-24 16:02:05 +00:00
fcb3278031 Merge pull request #158 from wader/bump-golang-1.21.1
Update golang to 1.21.1 from 1.21.0
2023-09-07 18:10:40 +02:00
cfdeb18339 Update golang to 1.21.1 from 1.21.0
Release notes https://golang.org/doc/devel/release.html
2023-09-07 16:02:13 +00:00
d87bdbe81c add -y 2023-09-05 16:27:53 -04:00
2438eebf7a fixing Dockerfile 2023-09-05 15:47:25 -04:00
9a0dc509a3 change Download filter to best 2023-09-01 16:28:30 -04:00
36319b5e0f add ffmpeg check to test 2023-09-01 16:28:03 -04:00
89ddc38573 add ffmpeg to Dockerfile 2023-09-01 16:27:39 -04:00
e29655f2bd add TestDownloadSections 2023-08-23 16:48:24 -04:00
3184158e02 DownloadSections option 2023-08-21 17:20:19 -04:00
89e825670c Merge pull request #156 from nonoo/master
Add the NoInfoDownload option
2023-08-17 11:58:31 +02:00
b6c1793051 Unexport the Options.noInfoDownload field
There is a package Download() function now which handle downloading without
using the Info struct so this field no longer needs to be exported.
2023-08-17 11:47:20 +02:00
a230313e47 Add main Download() function and its test
// It 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.
2023-08-17 11:19:19 +02:00
ddaf5ad2fa Add the NoInfoDownload option
Set it to true if you don't want to use the result.Info structure.

If it is set to true, then the New() call won't call youtube-dl to request info
about the source in JSON format, and result.Info structure will not be
populated. The given URL will be downloaded in a single pass in the Download()
call.
2023-08-17 11:18:47 +02:00
a46b2837fc Merge pull request #155 from Galiley/master
add options to handle downloading one entry from a playlist
2023-08-16 18:50:49 +02:00
f97b09d082 fix typo 2023-08-16 17:19:03 +02:00
fcfd4acd74 edit default value 2023-08-16 11:59:17 +02:00
de2e0d1840 add tests 2023-08-16 01:28:47 +02:00
ad9066c4f4 add options to handle downloading one entry from a playlist 2023-08-16 01:28:23 +02:00
02ec4fe77d Merge pull request #154 from nonoo/master
Add 2 new options, and make filter use optional
2023-08-15 23:25:31 +02:00
4b1de1d3ad Make Download() function comment more clear 2023-08-15 13:06:07 +02:00
f483ae69c8 Update Download() usage comment lines 2023-08-15 11:51:02 +02:00
c1dcced791 Add options to set merge output format and sorting format
yt-dlp uses mp4 format by default to merge sources with separate video and
audio streams. mp4 format does not handle VP9 codec, so adding the ability
to set the merge output format, we can set it to use for ex. mkv, which
handles VP9 well.

With the possibility of setting the sorting format we can tell yt-dlp which
criteria to consider as the best format.
2023-08-14 13:48:26 +02:00
3c1f4ce6da Only use -f arg if filter is given
If filter is not given then we let youtube-dl/yt-dlp decide the format to download.
2023-08-14 13:47:06 +02:00
2d7d4563d9 Merge pull request #153 from wader/bump-golang-1.21.0
Update golang to 1.21.0 from 1.20.7
2023-08-09 18:07:17 +02:00
59d0f9fc9e Update golang to 1.21.0 from 1.20.7
Release notes https://golang.org/doc/devel/release.html
2023-08-09 16:02:16 +00:00
b2b2500ae9 Merge pull request #152 from wader/bump-golang-1.20.7
Update golang to 1.20.7 from 1.20.6
2023-08-02 18:10:25 +02:00
b5d5a177c0 Update golang to 1.20.7 from 1.20.6
Release notes https://golang.org/doc/devel/release.html
2023-08-02 16:02:13 +00:00
f84c8607fa Merge pull request #150 from wader/bump-golang-1.20.6
Update golang to 1.20.6 from 1.20.5
2023-07-12 18:39:21 +02:00
ce02413ad4 Update golang to 1.20.6 from 1.20.5
Release notes https://golang.org/doc/devel/release.html
2023-07-12 16:02:22 +00:00
20583d83fd Merge pull request #149 from wader/bump-yt-dlp-2023.07.06
Update yt-dlp to 2023.07.06 from 2023.06.22
2023-07-07 18:07:56 +02:00
42e6060738 Update yt-dlp to 2023.07.06 from 2023.06.22
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.07.06
2023-07-07 16:02:21 +00:00
3d23796f5f Merge pull request #148 from wader/bump-yt-dlp-2023.06.22
Update yt-dlp to 2023.06.22 from 2023.03.04
2023-06-22 18:15:22 +02:00
87e9b10e45 Update yt-dlp to 2023.06.22 from 2023.03.04
Release notes https://github.com/yt-dlp/yt-dlp/releases/tag/2023.06.22
2023-06-22 16:02:05 +00:00
a1ae14605c Add --target dev to dev instruction to skip tests 2023-06-21 18:25:12 +02:00
8 changed files with 674 additions and 93 deletions

View File

@ -1,6 +1,10 @@
name: Build on push and PRs
on: [push,pull_request]
on:
push:
branches:
- "master"
pull_request:
jobs:
build:

View File

@ -1,9 +1,9 @@
# bump: golang /GOLANG_VERSION=([\d.]+)/ docker:golang|^1
# bump: golang link "Release notes" https://golang.org/doc/devel/release.html
ARG GOLANG_VERSION=1.20.5
ARG GOLANG_VERSION=1.23.4
# bump: yt-dlp /YT_DLP=([\d.-]+)/ https://github.com/yt-dlp/yt-dlp.git|/^\d/|sort
# bump: yt-dlp link "Release notes" https://github.com/yt-dlp/yt-dlp/releases/tag/$LATEST
ARG YT_DLP=2023.06.21
ARG YT_DLP=2025.01.12
FROM golang:$GOLANG_VERSION AS base
ARG YT_DLP
@ -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

View File

@ -1,18 +1,19 @@
## goutubedl
Go wrapper for [youtube-dl](https://github.com/ytdl-org/youtube-dl) and [yt-dlp](https://github.com/yt-dlp/yt-dlp), currently tested and
developed using yt-dlp.
API documentation can be found at [godoc.org](https://pkg.go.dev/github.com/wader/goutubedl?tab=doc).
Go wrapper for
[youtube-dl](https://github.com/ytdl-org/youtube-dl) and
[yt-dlp](https://github.com/yt-dlp/yt-dlp).
Currently only tested and developed using yt-dlp.
API documentation can be found at [godoc.org](https://pkg.go.dev/gitea.kaz62.ru/dilap54/goutubedl?tab=doc).
See [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp) for how to
install and what is recommended to install in addition to youtube-dl.
install and what is recommended to install in addition to yt-dl.
goutubedl default uses `PATH` to find youtube-dl but it can be configured with the `goutubedl.Path`
variable. Default is currently `youtube-dl` for backwards compability. If your using yt-dlp you
probably want to set it to `yt-dlp`.
goutubedl default uses `PATH` to find `youtube-dl` or `yt-dlp` (in that order) but can be configured with the
`goutubedl.Path` variable.
Due to the nature and frequent updates of youtube-dl only the latest version
is tested. But it seems to work well with older versions also.
Due to the nature of and frequent updates of yt-dl only the latest version is tested.
But it seems to work well with older versions also.
### Usage
@ -26,7 +27,7 @@ import (
"log"
"os"
"github.com/wader/goutubedl"
"gitea.kaz62.ru/dilap54/goutubedl"
)
func main() {
@ -52,10 +53,20 @@ func main() {
See [goutubedl cmd tool](cmd/goutubedl/main.go) or [ydls](https://github.com/wader/ydls)
for usage examples.
### Default options and cache
#### .netrc
goutubedl by default uses `--netrc` to use `~/.netrc` authentication data.
#### Cache directory
yt-dlp stores various extractor session data to speed up things in `${XDG_CACHE_HOME}/yt-dlp` (usually `~/.cache/yt-dlp`). You might want to preverse this directory if your running things in ephemeral conatiners etc.
### Development
```sh
docker build -t goutubedl-dev .
docker build --target dev -t goutubedl-dev .
docker run --rm -ti -v "$PWD:$PWD" -w "$PWD" goutubedl-dev
go test -v -race -cover
```

View File

@ -6,7 +6,7 @@ import (
"log"
"os"
"github.com/wader/goutubedl"
"gitea.kaz62.ru/dilap54/goutubedl"
)
func main() {

View File

@ -7,8 +7,9 @@ import (
"io"
"log"
"os"
"os/exec"
"github.com/wader/goutubedl"
"gitea.kaz62.ru/dilap54/goutubedl"
)
var dumpFlag = flag.Bool("J", false, "Dump JSON")
@ -24,7 +25,11 @@ func main() {
result, err := goutubedl.New(
context.Background(),
flag.Arg(0),
goutubedl.Options{Type: optType, DebugLog: log.Default()},
goutubedl.Options{
Type: optType,
DebugLog: log.Default(),
StderrFn: func(cmd *exec.Cmd) io.Writer { return os.Stderr },
},
)
if err != nil {
log.Fatal(err)

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/wader/goutubedl
module gitea.kaz62.ru/dilap54/goutubedl
go 1.12

View File

@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
@ -18,8 +17,22 @@ import (
"strings"
)
// Path to youtube-dl binary. Default look for "youtube-dl" in PATH.
var Path = "youtube-dl"
// Path to youtube-dl binary. If not set look for "youtube-dl" then "yt-dlp" in PATH.
var Path = ""
func ProbePath() string {
if Path != "" {
return Path
}
for _, n := range []string{"youtube-dl", "yt-dlp"} {
if p, err := exec.LookPath(n); err == nil {
return p
}
}
return "youtube-dl"
}
// Printer is something that can print
type Printer interface {
@ -115,12 +128,13 @@ type Info struct {
WebpageURL string `json:"webpage_url"`
Description string `json:"description"`
Thumbnail string `json:"thumbnail"`
// not unmarshalled, populated from image thumbnail file
// don't unmarshal, populated from image thumbnail file
ThumbnailBytes []byte `json:"-"`
Thumbnails []Thumbnail `json:"thumbnails"`
Formats []Format `json:"formats"`
Subtitles map[string][]Subtitle `json:"subtitles"`
Formats []Format `json:"formats"`
Subtitles map[string][]Subtitle `json:"subtitles"`
AutomaticCaptions map[string][]*Caption `json:"automatic_captions"`
// Playlist entries if _type is playlist
Entries []Info `json:"entries"`
@ -166,10 +180,18 @@ type Subtitle struct {
URL string `json:"url"`
Ext string `json:"ext"`
Language string `json:"-"`
// not unmarshalled, populated from subtitle file
// don't unmarshal, populated from subtitle file
Bytes []byte `json:"-"`
}
type Caption struct {
Ext string `json:"ext"`
Protocol string `json:"protocol"`
URL string `json:"url"`
Name string `json:"name"`
Bytes []byte `json:"-"`
}
func (f Format) String() string {
return fmt.Sprintf("%s:%s:%s abr:%f vbr:%f tbr:%f",
f.FormatID,
@ -191,32 +213,52 @@ 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()
type Options struct {
Type Type
PlaylistStart uint // --playlist-start
PlaylistEnd uint // --playlist-end
Downloader string // --downloader
DownloadThumbnail bool
DownloadSubtitles bool
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
HTTPClient *http.Client // Client for download thumbnail and subtitles (nil use http.DefaultClient)
Type Type
PlaylistStart uint // --playlist-start
PlaylistEnd uint // --playlist-end
Downloader string // --downloader
DownloadThumbnail bool
DownloadSubtitles bool
DownloadSubtitlesLang []string
DownloadSubtitlesFormat string
DownloadSections string // --download-sections
Impersonate string // --impersonate
ProxyUrl string // --proxy URL http://host:port or socks5://host:port
UseIPV4 bool // -4 Make all connections via IPv4
Cookies string // --cookies FILE
CookiesFromBrowser string // --cookies-from-browser BROWSER[:FOLDER]
DebugLog Printer
StderrFn func(cmd *exec.Cmd) io.Writer // if not nil, function to get Writer for stderr
HTTPClient *http.Client // Client for download thumbnail and subtitles (nil use http.DefaultClient)
MergeOutputFormat string // --merge-output-format
SortingFormat string // --format-sort
ExtractorArgs string // --extractor-args
InfoArgs []string
DownloadArgs []string
// Set to true if you don't want to use the result.Info structure after the goutubedl.New() call,
// so the given URL will be downloaded in a single pass in the DownloadResult.Download() call.
noInfoDownload bool
}
// Version of youtube-dl.
// Might be a good idea to call at start to assert that youtube-dl can be found.
func Version(ctx context.Context) (string, error) {
cmd := exec.CommandContext(ctx, Path, "--version")
cmd := exec.CommandContext(ctx, ProbePath(), "--version")
versionBytes, cmdErr := cmd.Output()
if cmdErr != nil {
return "", cmdErr
@ -225,12 +267,35 @@ func Version(ctx context.Context) (string, error) {
return strings.TrimSpace(string(versionBytes)), nil
}
// 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) {
options.noInfoDownload = true
d, err := New(ctx, rawURL, options)
if err != nil {
return nil, err
}
return d.Download(ctx, filter)
}
// New downloads metadata for URL
func New(ctx context.Context, rawURL string, options Options) (result Result, err error) {
if options.DebugLog == nil {
options.DebugLog = nopPrinter{}
}
if options.noInfoDownload {
return Result{
RawURL: rawURL,
Options: options,
}, nil
}
info, rawJSON, err := infoFromURL(ctx, rawURL, options)
if err != nil {
return Result{}, err
@ -241,35 +306,69 @@ func New(ctx context.Context, rawURL string, options Options) (result Result, er
return Result{
Info: info,
RawURL: rawURL,
RawJSON: rawJSONCopy,
Options: options,
}, 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,
Path,
ProbePath(),
// see comment below about ignoring errors for playlists
"--ignore-errors",
// TODO: deprecated in yt-dlp?
"--no-call-home",
"--no-cache-dir",
"--skip-download",
// use safer output filenmaes
// TODO: needed?
"--restrict-filenames",
// provide URL via stdin for security, youtube-dl has some run command args
// use .netrc authentication data
"--netrc",
// provide url via stdin for security, youtube-dl has some run command args
"--batch-file", "-",
"-J",
// dump info json
"--dump-single-json",
)
if options.ProxyUrl != "" {
cmd.Args = append(cmd.Args, "--proxy", options.ProxyUrl)
}
if options.UseIPV4 {
cmd.Args = append(cmd.Args, "-4")
}
if options.Downloader != "" {
cmd.Args = append(cmd.Args, "--downloader", options.Downloader)
}
if options.Type == TypePlaylist {
if options.Impersonate != "" {
cmd.Args = append(cmd.Args, "--impersonate", options.Impersonate)
}
if options.Cookies != "" {
cmd.Args = append(cmd.Args, "--cookies", options.Cookies)
}
if options.CookiesFromBrowser != "" {
cmd.Args = append(cmd.Args, "--cookies-from-browser", options.CookiesFromBrowser)
}
if options.ExtractorArgs != "" {
cmd.Args = append(cmd.Args,
"--extractor-args", options.ExtractorArgs,
)
}
cmd.Args = append(cmd.Args, options.InfoArgs...)
switch options.Type {
case TypePlaylist, TypeChannel:
cmd.Args = append(cmd.Args, "--yes-playlist")
if options.PlaylistStart > 0 {
@ -282,7 +381,7 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
"--playlist-end", strconv.Itoa(int(options.PlaylistEnd)),
)
}
} else {
case TypeSingle:
if options.DownloadSubtitles {
cmd.Args = append(cmd.Args,
"--all-subs",
@ -291,14 +390,18 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
cmd.Args = append(cmd.Args,
"--no-playlist",
)
case TypeAny:
break
default:
return Info{}, nil, fmt.Errorf("unhandled options type value: %d", options.Type)
}
tempPath, _ := ioutil.TempDir("", "ydls")
tempPath, _ := os.MkdirTemp("", "ydls")
defer os.RemoveAll(tempPath)
stdoutBuf := &bytes.Buffer{}
stderrBuf := &bytes.Buffer{}
stderrWriter := ioutil.Discard
stderrWriter := io.Discard
if options.StderrFn != nil {
stderrWriter = options.StderrFn(cmd)
}
@ -372,7 +475,7 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
if options.DownloadThumbnail && info.Thumbnail != "" {
resp, respErr := get(info.Thumbnail)
if respErr == nil {
buf, _ := ioutil.ReadAll(resp.Body)
buf, _ := io.ReadAll(resp.Body)
resp.Body.Close()
info.ThumbnailBytes = buf
}
@ -384,12 +487,37 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
}
}
for _, lang := range options.DownloadSubtitlesLang {
if _, ok := info.AutomaticCaptions[lang]; !ok {
continue
}
for _, caption := range info.AutomaticCaptions[lang] {
if options.DownloadSubtitlesFormat != "" && caption.Ext != options.DownloadSubtitlesFormat {
continue
}
resp, respErr := get(caption.URL)
if respErr == nil {
buf, err := io.ReadAll(resp.Body)
if err != nil {
options.DebugLog.Print("err", "download captions "+caption.URL, err)
}
resp.Body.Close()
caption.Bytes = buf
} else {
options.DebugLog.Print("err", "download captions "+caption.URL, respErr)
}
}
}
if options.DownloadSubtitles {
for _, subtitles := range info.Subtitles {
for i, subtitle := range subtitles {
resp, respErr := get(subtitle.URL)
if respErr == nil {
buf, _ := ioutil.ReadAll(resp.Body)
buf, _ := io.ReadAll(resp.Body)
resp.Body.Close()
subtitles[i].Bytes = buf
}
@ -398,15 +526,31 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
}
// as we ignore errors for playlists some entries might show up as null
if options.Type == TypePlaylist {
var filteredEntrise []Info
//
// 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 filteredEntries []Info
for _, e := range info.Entries {
if e.ID == "" {
if e.Type == "playlist" {
for _, ee := range e.Entries {
if ee.ID == "" {
continue
}
filteredEntries = append(filteredEntries, ee)
}
continue
} else if e.ID != "" {
filteredEntries = append(filteredEntries, e)
}
filteredEntrise = append(filteredEntrise, e)
}
info.Entries = filteredEntrise
info.Entries = filteredEntries
}
return info, stdoutBuf.Bytes(), nil
@ -415,6 +559,7 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
// Result metadata for a URL
type Result struct {
Info Info
RawURL string
RawJSON []byte // saved raw JSON. Used later when downloading
Options Options // options passed to New
}
@ -425,24 +570,55 @@ type DownloadResult struct {
waitCh chan struct{}
}
// Download format matched by filter (usually a format id or "best").
// Filter should not be a combine filter like "1+2" as then youtube-dl
// won't write to stdout.
// Download format matched by filter (usually a format id or quality designator).
// If filter is empty, then youtube-dl will use its default format selector.
// It's a shortcut of DownloadWithOptions where the options use the default value
func (result Result) Download(ctx context.Context, filter string) (*DownloadResult, error) {
return result.DownloadWithOptions(ctx, DownloadOptions{
Filter: filter,
})
}
type DownloadOptions struct {
AudioFormats string // --audio-formats Download audio using formats (best, aac, alac, flac, m4a, mp3, opus, vorbis, wav)
DownloadAudioOnly bool // -x Download audio only from video
// Download format matched by filter (usually a format id or quality designator).
// If filter is empty, then youtube-dl will use its default format selector.
Filter string
// The index of the entry to download from the playlist that would be
// passed to youtube-dl via --playlist-items. The index value starts at 1
PlaylistIndex int
}
func (result Result) DownloadWithOptions(
ctx context.Context,
options DownloadOptions,
) (*DownloadResult, error) {
debugLog := result.Options.DebugLog
if result.Info.Type == "playlist" || result.Info.Type == "multi_video" {
return nil, fmt.Errorf("can't download a playlist")
if !result.Options.noInfoDownload {
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",
)
}
}
tempPath, tempErr := ioutil.TempDir("", "ydls")
tempPath, tempErr := os.MkdirTemp("", "ydls")
if tempErr != nil {
return nil, tempErr
}
jsonTempPath := path.Join(tempPath, "info.json")
if err := ioutil.WriteFile(jsonTempPath, result.RawJSON, 0600); err != nil {
os.RemoveAll(tempPath)
return nil, err
var jsonTempPath string
if !result.Options.noInfoDownload {
jsonTempPath = path.Join(tempPath, "info.json")
if err := os.WriteFile(jsonTempPath, result.RawJSON, 0600); err != nil {
os.RemoveAll(tempPath)
return nil, err
}
}
dr := &DownloadResult{
@ -451,19 +627,68 @@ func (result Result) Download(ctx context.Context, filter string) (*DownloadResu
cmd := exec.CommandContext(
ctx,
Path,
"--no-call-home",
"--no-cache-dir",
ProbePath(),
// see comment below about ignoring errors for playlists
"--ignore-errors",
// TODO: deprecated in yt-dlp?
"--no-call-home",
// use non-fancy progress bar
"--newline",
// use safer output filenmaes
// TODO: needed?
"--restrict-filenames",
"--load-info", jsonTempPath,
"-o", "-",
// use .netrc authentication data
"--netrc",
// write to stdout
"--output", "-",
)
if result.Options.noInfoDownload {
// provide URL via stdin for security, youtube-dl has some run command args
cmd.Args = append(cmd.Args, "--batch-file", "-")
cmd.Stdin = bytes.NewBufferString(result.RawURL + "\n")
if result.Options.Type == TypePlaylist {
cmd.Args = append(cmd.Args, "--yes-playlist")
if result.Options.PlaylistStart > 0 {
cmd.Args = append(cmd.Args,
"--playlist-start", strconv.Itoa(int(result.Options.PlaylistStart)),
)
}
if result.Options.PlaylistEnd > 0 {
cmd.Args = append(cmd.Args,
"--playlist-end", strconv.Itoa(int(result.Options.PlaylistEnd)),
)
}
} else {
cmd.Args = append(cmd.Args,
"--no-playlist",
)
}
} else {
cmd.Args = append(cmd.Args, "--load-info", jsonTempPath)
}
// force IPV4 Usage
if result.Options.UseIPV4 {
cmd.Args = append(cmd.Args, "-4")
}
// don't need to specify if direct as there is only one
// also seems to be issues when using filter with generic extractor
if !result.Info.Direct {
cmd.Args = append(cmd.Args, "-f", filter)
if !result.Info.Direct && options.Filter != "" {
cmd.Args = append(cmd.Args, "-f", options.Filter)
}
if options.PlaylistIndex > 0 {
cmd.Args = append(cmd.Args, "--playlist-items", fmt.Sprint(options.PlaylistIndex))
}
if options.DownloadAudioOnly {
cmd.Args = append(cmd.Args, "-x")
}
if options.AudioFormats != "" {
cmd.Args = append(cmd.Args, "--audio-format", options.AudioFormats)
}
if result.Options.ProxyUrl != "" {
@ -474,16 +699,46 @@ func (result Result) Download(ctx context.Context, filter string) (*DownloadResu
cmd.Args = append(cmd.Args, "--downloader", result.Options.Downloader)
}
cmd.Dir = tempPath
var w io.WriteCloser
dr.reader, w = io.Pipe()
stderrWriter := ioutil.Discard
if result.Options.StderrFn != nil {
stderrWriter = result.Options.StderrFn(cmd)
if result.Options.DownloadSections != "" {
cmd.Args = append(cmd.Args, "--download-sections", result.Options.DownloadSections)
}
cmd.Stdout = w
cmd.Stderr = stderrWriter
if result.Options.CookiesFromBrowser != "" {
cmd.Args = append(cmd.Args, "--cookies-from-browser", result.Options.CookiesFromBrowser)
}
if result.Options.MergeOutputFormat != "" {
cmd.Args = append(cmd.Args,
"--merge-output-format", result.Options.MergeOutputFormat,
)
}
if result.Options.SortingFormat != "" {
cmd.Args = append(cmd.Args,
"--format-sort", result.Options.SortingFormat,
)
}
if result.Options.ExtractorArgs != "" {
cmd.Args = append(cmd.Args,
"--extractor-args", result.Options.ExtractorArgs,
)
}
cmd.Args = append(cmd.Args, result.Options.DownloadArgs...)
cmd.Dir = tempPath
var stdoutW io.WriteCloser
var stderrW io.WriteCloser
var stderrR io.Reader
dr.reader, stdoutW = io.Pipe()
stderrR, stderrW = io.Pipe()
optStderrWriter := io.Discard
if result.Options.StderrFn != nil {
optStderrWriter = result.Options.StderrFn(cmd)
}
cmd.Stdout = stdoutW
cmd.Stderr = io.MultiWriter(optStderrWriter, stderrW)
debugLog.Print("cmd", " ", cmd.Args)
if err := cmd.Start(); err != nil {
@ -493,12 +748,32 @@ func (result Result) Download(ctx context.Context, filter string) (*DownloadResu
go func() {
_ = cmd.Wait()
w.Close()
stdoutW.Close()
stderrW.Close()
os.RemoveAll(tempPath)
close(dr.waitCh)
}()
return dr, nil
// blocks return until yt-dlp is downloading or has errored
ytErrCh := make(chan error)
go func() {
stderrLineScanner := bufio.NewScanner(stderrR)
for stderrLineScanner.Scan() {
const downloadPrefix = "[download]"
const errorPrefix = "ERROR: "
line := stderrLineScanner.Text()
if strings.HasPrefix(line, downloadPrefix) {
break
} else if strings.HasPrefix(line, errorPrefix) {
ytErrCh <- errors.New(line[len(errorPrefix):])
return
}
}
ytErrCh <- nil
_, _ = io.Copy(io.Discard, stderrR)
}()
return dr, <-ytErrCh
}
func (dr *DownloadResult) Read(p []byte) (n int, err error) {

View File

@ -7,25 +7,26 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"testing"
"gitea.kaz62.ru/dilap54/goutubedl"
"github.com/fortytw2/leaktest"
"github.com/wader/goutubedl"
"github.com/wader/osleaktest"
)
func init() {
// we're using yt-dlp at the moment
goutubedl.Path = "yt-dlp"
}
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://vimeo.com/454525548"
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)
@ -99,14 +100,46 @@ func TestDownload(t *testing.T) {
}
}
func TestDownloadWithoutInfo(t *testing.T) {
defer leakChecks(t)()
stderrBuf := &bytes.Buffer{}
dr, err := goutubedl.Download(context.Background(), testVideoRawURL, goutubedl.Options{
StderrFn: func(cmd *exec.Cmd) io.Writer {
return stderrBuf
},
}, "")
if err != nil {
t.Fatal(err)
}
downloadBuf := &bytes.Buffer{}
n, err := io.Copy(downloadBuf, dr)
if err != nil {
t.Fatal(err)
}
dr.Close()
if n != int64(downloadBuf.Len()) {
t.Errorf("copy n not equal to download buffer: %d!=%d", n, downloadBuf.Len())
}
if n < 10000 {
t.Errorf("should have copied at least 10000 bytes: %d", n)
}
if !strings.Contains(stderrBuf.String(), "Destination") {
t.Errorf("did not find expected log message on stderr: %q", stderrBuf.String())
}
}
func TestParseInfo(t *testing.T) {
for _, c := range []struct {
url string
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.youtube.com/watch?v=uVYWQJ5BB_w", "A Radiolab Producer on the Making of a Podcast"},
{"https://www.infoq.com/presentations/Simple-Made-Easy", "Simple Made Easy - InfoQ"},
{"https://vimeo.com/454525548", "Sample Video - 3 minutemp4.mp4"},
} {
t.Run(c.url, func(t *testing.T) {
defer leakChecks(t)()
@ -187,6 +220,40 @@ func TestPlaylist(t *testing.T) {
}
}
func TestChannel(t *testing.T) {
t.Skip("skip youtube for now")
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)()
@ -202,6 +269,8 @@ func TestUnsupportedURL(t *testing.T) {
}
func TestPlaylistWithPrivateVideo(t *testing.T) {
t.Skip("skip youtube for now")
defer leaktest.Check(t)()
playlistRawURL := "https://www.youtube.com/playlist?list=PLX0g748fkegS54oiDN4AXKl7BR7mLIydP"
@ -222,6 +291,8 @@ func TestPlaylistWithPrivateVideo(t *testing.T) {
}
func TestSubtitles(t *testing.T) {
t.Skip("skip youtube for now")
defer leakChecks(t)()
ydlResult, ydlResultErr := goutubedl.New(
@ -253,6 +324,73 @@ 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://vimeo.com/454525548",
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(), "")
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("did not get expected duration of %d, but got %d", duration, seconds)
}
dr.Close()
}
func TestErrorNotAPlaylist(t *testing.T) {
defer leakChecks(t)()
@ -301,3 +439,150 @@ func TestOptionDownloader(t *testing.T) {
}
dr.Close()
}
func TestInvalidOptionTypeField(t *testing.T) {
defer leakChecks(t)()
_, err := goutubedl.New(context.Background(), playlistRawURL, goutubedl.Options{
Type: 42,
})
if err == nil {
t.Error("should have failed")
}
}
func TestDownloadPlaylistEntry(t *testing.T) {
defer leakChecks(t)()
// Download file by specifying the playlist index
stderrBuf := &bytes.Buffer{}
r, err := goutubedl.New(context.Background(), playlistRawURL, goutubedl.Options{
StderrFn: func(cmd *exec.Cmd) io.Writer {
return stderrBuf
},
})
if err != nil {
t.Fatal(err)
}
expectedTitle := "Kindred Phenomena"
if r.Info.Title != expectedTitle {
t.Errorf("expected title %q got %q", expectedTitle, r.Info.Title)
}
expectedEntries := 8
if len(r.Info.Entries) != expectedEntries {
t.Errorf("expected %d entries got %d", expectedEntries, len(r.Info.Entries))
}
expectedTitleOne := "B1 Mattheis - Ben M"
playlistIndex := 2
if r.Info.Entries[playlistIndex].Title != expectedTitleOne {
t.Errorf("expected title %q got %q", expectedTitleOne, r.Info.Entries[playlistIndex].Title)
}
dr, err := r.DownloadWithOptions(context.Background(), goutubedl.DownloadOptions{
PlaylistIndex: int(r.Info.Entries[playlistIndex].PlaylistIndex),
Filter: r.Info.Entries[playlistIndex].Formats[0].FormatID,
})
if err != nil {
t.Fatal(err)
}
playlistBuf := &bytes.Buffer{}
n, err := io.Copy(playlistBuf, dr)
if err != nil {
t.Fatal(err)
}
dr.Close()
if n != int64(playlistBuf.Len()) {
t.Errorf("copy n not equal to download buffer: %d!=%d", n, playlistBuf.Len())
}
if n < 10000 {
t.Errorf("should have copied at least 10000 bytes: %d", n)
}
if !strings.Contains(stderrBuf.String(), "Destination") {
t.Errorf("did not find expected log message on stderr: %q", stderrBuf.String())
}
// Download the same file but with the direct link
url := "https://soundcloud.com/mattheis/b1-mattheis-ben-m"
stderrBuf = &bytes.Buffer{}
r, err = goutubedl.New(context.Background(), url, goutubedl.Options{
StderrFn: func(cmd *exec.Cmd) io.Writer {
return stderrBuf
},
})
if err != nil {
t.Fatal(err)
}
if r.Info.Title != expectedTitleOne {
t.Errorf("expected title %q got %q", expectedTitleOne, r.Info.Title)
}
expectedEntries = 0
if len(r.Info.Entries) != expectedEntries {
t.Errorf("expected %d entries got %d", expectedEntries, len(r.Info.Entries))
}
dr, err = r.Download(context.Background(), r.Info.Formats[0].FormatID)
if err != nil {
t.Fatal(err)
}
directLinkBuf := &bytes.Buffer{}
n, err = io.Copy(directLinkBuf, dr)
if err != nil {
t.Fatal(err)
}
dr.Close()
if n != int64(directLinkBuf.Len()) {
t.Errorf("copy n not equal to download buffer: %d!=%d", n, directLinkBuf.Len())
}
if n < 10000 {
t.Errorf("should have copied at least 10000 bytes: %d", n)
}
if !strings.Contains(stderrBuf.String(), "Destination") {
t.Errorf("did not find expected log message on stderr: %q", stderrBuf.String())
}
if directLinkBuf.Len() != playlistBuf.Len() {
t.Errorf("not the same content size between the playlist index entry and the direct link entry: %d != %d", playlistBuf.Len(), directLinkBuf.Len())
}
if !bytes.Equal(directLinkBuf.Bytes(), playlistBuf.Bytes()) {
t.Error("not the same content between the playlist index entry and the direct link entry")
}
}
func TestFormatDownloadError(t *testing.T) {
t.Skip("test URL broken")
defer leaktest.Check(t)()
ydl, ydlErr := goutubedl.New(
context.Background(),
"https://www.reddit.com/r/newsbabes/s/92rflI0EB0",
goutubedl.Options{},
)
if ydlErr != nil {
// reddit seems to not like github action hosts
if strings.Contains(ydlErr.Error(), "HTTPError 403: Blocked") {
t.Skip()
}
t.Error(ydlErr)
}
// no pre-muxed audio/video format available
_, ytDlErr := ydl.Download(context.Background(), "best")
expectedErr := "Requested format is not available"
if ydlErr != nil && !strings.Contains(ytDlErr.Error(), expectedErr) {
t.Errorf("expected error prefix %q got %q", expectedErr, ytDlErr.Error())
}
}