Compare commits

..

286 Commits

Author SHA1 Message Date
github-actions
926ccc84ef [version] update
Created by: pukkandan

:ci skip all
2022-05-18 03:37:32 +00:00
pukkandan
b14d523558 Release 2022.05.18 2022-05-18 09:04:38 +05:30
pukkandan
21633673c3 [cleanup] Minor fixes 2022-05-18 09:04:30 +05:30
pukkandan
80e8493ee7 [utils] is_html: Handle double BOM
Closes #2885
2022-05-18 07:03:50 +05:30
pukkandan
aedaa455d9 [vimeo] Fix extractors
Closes #3037, Closes #2858, Closes #2880, Closes #3712
May also fix #3602, #3360
2022-05-18 05:16:08 +05:30
pukkandan
d6bf1161db [generic] Refactor _extract_rss
Closes #3738
2022-05-18 04:17:59 +05:30
pukkandan
7896214c42 Bugfix for 591bb9d355
Closes #3769
2022-05-17 22:33:15 +05:30
pukkandan
5792c950bf [compat] Implement compat.imghdr
Python 3.11 deprecates `imghdr` module
2022-05-17 19:46:01 +05:30
pukkandan
7a96d0b39c [build] More test-runners
* GHA does not cache python 3.6 for Windows, so use 3.8 instead
* Add tests for 3.11, PyPy3.8/3.9
* CPython 3.10 is now stable
* Do not pin Ubuntu to 18.04
2022-05-17 19:40:54 +05:30
pukkandan
591bb9d355 Fix color in -q -F
and convert `ydl._out_files`/`ydl._allow_colors` to `Namespace`

Closes #3761
2022-05-17 18:41:46 +05:30
coletdjnz
5faf6528fb [http] Fix bug in retrying on read timeout in py < 3.10
socket.timeout is not an alias of TimeoutError in py < 3.10
Fixes bug in a2e77303e3
Authored-by: coletdjnz
2022-05-17 09:17:37 +12:00
pukkandan
0fa7d2c8e4 Bugfix for 2414649192 2022-05-17 00:55:37 +05:30
pukkandan
b2a2d91310 [adobepass] Allow cookies for authenticating MSO
Possible workaround for #1034
2022-05-17 00:07:46 +05:30
pukkandan
490110c543 --max-downloads should obey --break-per-input 2022-05-17 00:07:44 +05:30
pukkandan
2414649192 [cleanup] Misc cleanup 2022-05-17 00:07:43 +05:30
pukkandan
5d5c0f7e99 [Hotstar] Bugfix for a1ddaa899c
Closes #3595
2022-05-17 00:07:41 +05:30
MrRawes
b4d3738338 [build] Add make uninstall (#3747)
Authored by: MrRawes
2022-05-15 19:04:27 -07:00
Dzmitry Neviadomski
3ac7b66047 [goodgame] Add extractor (#3686)
Authored by: nevack
2022-05-15 10:05:27 -07:00
P-reducible
c9b2b368b3 [rokfin:search] Add extractor (#2992)
Authored by: P-reducible, pukkandan
2022-05-15 09:49:19 -07:00
pukkandan
e037c405ad [rokfin] Implement login (#2992)
Authored by: P-reducible, pukkandan
2022-05-15 22:17:04 +05:30
Conner
4bf72cc1c9 [Podchaser] Add extractors (#3665)
Authored by; connercsbn
2022-05-15 08:53:34 -07:00
Elyse
25f0e68f97 [wat] Fix extraction of multi-language videos and subtitles (#3739)
Closes #982, closes #1683
Authored by: elyse0
2022-05-15 05:09:14 -07:00
Elyse
3358f89361 [adn] Update AES key (#3743)
Closes #2002
Authored by: elyse0
2022-05-15 04:57:53 -07:00
Henrik Heimbuerger
f3b3fe16af [nebula] Add support for subscriptions (#3719)
Closes #3609 
Authored by: hheimbuerger
2022-05-15 04:55:44 -07:00
Jordan Weatherby
d1c4f6d4da [youtube] Added piped instance urls (#3714)
Closes #3661
Authored by: JordanWeatherby
2022-05-12 13:52:13 -07:00
pukkandan
82d020804d [extractor] Use classmethod/property where possible
and refactor lazy extractors accordingly.

This reduces the need to create extractor instances
2022-05-13 00:23:26 +05:30
Filip Hedman
7ddbf09c25 [DRTV] Improve _VALID_URL (#3708)
Authored by: vertan
2022-05-12 11:12:01 -07:00
pukkandan
2e4585da92 [cookies] Throttle progress-bar
Closes #3710
2022-05-12 10:54:49 +05:30
pukkandan
8dcce6a89c [extractor] Document netrc machines
Closes #3169
2022-05-11 22:10:15 +05:30
pukkandan
494f52308b [FixupM3u8] Obey --hls-prefer-mpegts
Closes #3697
2022-05-11 11:03:36 +05:30
pukkandan
3d38b2d6d0 Fix --date today
Closes #3704
2022-05-11 07:25:56 +05:30
pukkandan
3a408f9d19 Show name of downloader in verbose log
Closes #3703
2022-05-11 07:20:54 +05:30
pukkandan
d76fa1f3d4 [cookies] Allow cookiefile to be a text stream
Closes #3674
2022-05-11 05:52:55 +05:30
pukkandan
fe1daad3cb Bugfix for 59f943cd50
Fixes: 59f943cd50 (commitcomment-73251597)
2022-05-10 11:08:58 +05:30
pukkandan
0f06bcd759 [cleanup] Minor fixes (See desc)
* [youtube] Fix `--youtube-skip-dash-manifest`
* [build] Use `$()` in `Makefile`. Closes #3684
* Fix bug in 385ffb467b
* Fix bug in 43d7f5a5d0
* [cleanup] Remove unnecessary `utf-8` from `str.encode`/`bytes.decode`
* [utils] LazyList: Expose unnecessarily "protected" attributes
and other minor cleanup
2022-05-09 17:59:26 +05:30
ca-za
d239db0306 [toggo] Improve _VALID_URL (#3689)
Authored by: ca-za
2022-05-09 04:42:22 -07:00
pukkandan
385ffb467b [wistia] Fix _VALID_URL
Closes #2866
Authored by: dirkf
2022-05-09 09:14:21 +05:30
Evan Spensley
5f8ea7e0d8 [Jamendo] Extract more metadata (#3672)
Authored by: evansp
2022-05-07 15:48:34 -07:00
pukkandan
d7a1aa00c6 Run FFmpegFixupM3u8PP for live-streams if needed
Closes #3669
2022-05-07 22:36:23 +05:30
MMM
5747d4f4e8 [kaltura] Update API calls (#3657)
Authored by: flashdagger
2022-05-07 09:06:05 -07:00
Lesmiscore
1f8b4ab733 [radiko] Fix extractor (#3655)
Authored by: Lesmiscore
2022-05-08 00:47:51 +09:00
pukkandan
d4736fdb43 Remove warning for videos with an empty title 2022-05-07 19:45:33 +05:30
pukkandan
895aeb71d7 [toggo] Fix _VALID_URL
Closes #2610
2022-05-07 19:24:04 +05:30
pukkandan
4f28b537d9 Allow use of weaker ciphers with --legacy-server-connect
Closes #2043
2022-05-07 18:40:44 +05:30
pukkandan
6b70527f9d [cleanup, zingmp3] Refactor extractors 2022-05-07 18:39:58 +05:30
Teemu Ikonen
a0fe51d562 [ruutu] Support hs.fi embeds (#3547)
Authored by: tpikonen, pukkandan
2022-05-07 04:24:41 -07:00
diegorodriguezv
bd18c5d170 [cleanup, tmz] Update tests (#3654)
Authored by: diegorodriguezv
2022-05-07 04:21:55 -07:00
Ha Tien Loi
54044decd0 [ZingMp3] Add chart and user extractors (#3423)
Authored by: hatienl0i261299
2022-05-07 03:25:58 -07:00
Ha Tien Loi
89f383c4ee [gronkh] Add playlist extractors (#3337)
Closes #3300
Authored by: hatienl0i261299
2022-05-07 01:44:41 -07:00
pukkandan
91e5e839d3 [youtube] Deprioritize format 22
Reduces chance of encountering #3372
2022-05-07 08:05:32 +05:30
FestplattenSchnitzel
10fa2471fc [VideocampusSachsen] Improve extractor (#3604)
Authored by: FestplattenSchnitzel
2022-05-05 10:31:54 -07:00
i6t
ff4d7860d5 [iwara] Add playlist extractors (#3639)
Authored by: i6t
2022-05-04 08:49:46 -07:00
rand-net
4f7a98c565 [KhanAcademy] Fix extractor (#3462)
Authored by: rand-net
2022-05-04 07:26:45 -07:00
Lesmiscore
b58f8d8f2c [TVer] Improve extraction (#3634)
Authored by: Lesmiscore
2022-05-04 23:16:56 +09:00
Ha Tien Loi
f963b7ab18 [Likee] Add extractor (#3625)
Closes #3603
Authored by: hatienl0i261299
2022-05-04 07:13:52 -07:00
pukkandan
86925f6334 [Fifa] Sort formats
Closes #3632
2022-05-04 19:07:34 +05:30
m4tu4g
468f104ce7 [masters] Add extractor (#3358)
Closes #3240
Authored by: m4tu4g
2022-05-02 14:36:37 -07:00
Bricio
cbc6ee10da [Fifa] Add Extractor (#3414)
Closes #3408
Authored by: Bricio
2022-05-02 14:26:28 -07:00
nyuszika7h
6ef5ad9e29 [trovo] Update to new API (#3509)
Closes #3457
Authored by: nyuszika7h
2022-05-02 08:13:18 -07:00
coletdev
bb58c9ed5c Add support for SSL client certificate authentication (#3435)
Adds `--client-certificate`, `--client-certificate-key`, `--client-certificate-password`

Authored-by: coletdjnz
Co-authored-by: df <fieldhouse@gmx.net>
Co-authored-by: pukkandan <pukkandan.ytdlp@gmail.com>
2022-05-02 07:59:45 +00:00
coletdev
afac4caa7d Fix redirect HTTP method handling (#3577)
Authored by: coletdjnz
2022-05-01 20:40:26 -07:00
HE7086
b4f536626a [BilibiliLive] Add extractor (#3406)
Authored by: HE7086, pukkandan
2022-05-01 19:09:11 -07:00
felix
e4fa34a13e [hls] Fix unapplied byte_range for EXT-X-MAP fragment
Cherry-picked from #3302
Authored by: fstirlitz
2022-05-02 07:20:08 +05:30
Marwen Dallel
1a7cd9c487 [LCI] Fix extractor (#3534)
Authored by: MarwenDallel
2022-05-01 17:59:48 -07:00
Justin Keogh
131e14dc66 [utils] locked_file: Ignore illegal seek on truncate (#3610)
Closes #3557

Authored by: jakeogh
2022-05-01 13:31:06 -07:00
coletdev
6e634cbe42 [youtube] Add YoutubeStoriesIE (#3362)
Get channel stories with `ytstories:<channel UCID>`

Authored-by: coletdjnz
2022-05-01 06:46:28 +00:00
pukkandan
3fe75fdc80 [cleanup] Misc fixes (see desc)
* Do not warn when fixup is skipped for existing file
* [fragment] Fix `--skip-unavailable-fragments` for HTTP Errors
* [utils] write_string: Fix bug in 59f943cd50
* [utils] parse_codecs: Subtitle codec is generally referred to as `scodec`. https://github.com/yt-dlp/yt-dlp/pull/2174#discussion_r790156048
* [docs] Remove note about permissions. Closes #3597
2022-05-01 04:58:38 +05:30
pukkandan
6f7563beb7 [XAttrMetadata] Refactor and document dependencies 2022-05-01 04:58:38 +05:30
pukkandan
43d7f5a5d0 [EmbedThumbnail] Do not obey -k 2022-05-01 04:58:26 +05:30
Lesmiscore
94aa064497 [utils] YoutubeDLCookieJar: Detect and reject JSON file (#3599)
Authored by: Lesmiscore
2022-05-01 00:38:30 +09:00
pukkandan
07689fc149 [reddit] Prevent infinite loop
Closes #3588
2022-04-29 07:41:39 +05:30
pukkandan
bfec31bec8 [youtube] De-prioritize auto-generated thumbnails
Closes #3112
2022-04-29 07:41:39 +05:30
pukkandan
1d485a1a79 [cleanup] Misc fixes
Closes #3565, https://github.com/yt-dlp/yt-dlp/issues/3514#issuecomment-1105944364
2022-04-29 07:39:33 +05:30
pukkandan
0a41f331cc [doc] Minor improvements
Closes #3518, Closes #3560
2022-04-29 06:53:36 +05:30
pukkandan
e1e1ea54ae [build] Fix --onedir on macOS
Closes #3584
2022-04-28 22:18:03 +05:30
pukkandan
492272fed6 --match-filter - to interactively ask for each video 2022-04-28 20:04:40 +05:30
pukkandan
59f943cd50 [utils] write_string: Workaround newline issue in conhost
On windows `conhost`, when `WINDOWS_VT_MODE` is enabled, `\n` is not
actually sent if the window is exactly the length of printed line,
and the line does not end with a white-space character. So the
line-break disappears when resizing the window.

Fixes #1863
2022-04-28 20:04:40 +05:30
pukkandan
0a5a191a2a Improve --clean-infojson
It should not removes fields that may be needed for `--load-infojson`.
Eg: `_ffmpeg_args`, `_has_drm`
2022-04-28 20:04:40 +05:30
Giedrius Statkevičius
4877f9055c [lrt] Support livestreams (#3555)
Authored by: GiedriusS
2022-04-28 04:38:36 -07:00
Elyse
a076c1f97a [extractor] Update manifest_urls after redirect (#3575)
Authored by: elyse0
2022-04-27 15:50:01 -07:00
Evan Spensley
b3602f6824 [InfoQ] Don't fail on missing audio format (#3573)
Closes #3441 
Authored by: evansp
2022-04-27 14:30:24 -07:00
Elyse
779da8e31b [extractor] Update dash manifest_url after redirects (#3563)
Closes #2696 
Authored by: elyse0
2022-04-27 11:01:35 -07:00
Lesmiscore
997378f9df [twitcasting] Pass headers for each formats (#3568)
Authored by: Lesmiscore
2022-04-28 01:59:45 +09:00
ekangmonyet
83bfb5e290 [Niconico] Support 2FA (#3559)
Authored by: ekangmonyet
2022-04-27 09:44:29 -07:00
pukkandan
c171445431 [cleanup,build] Cleanup some build-related code
Fixes an issue in 7ab56be2c7
2022-04-27 16:12:36 +05:30
pukkandan
4f80952353 [cleanup] Delete unused extractors 2022-04-27 08:24:25 +05:30
pukkandan
e13945a2fe [ffmpeg] Fix features detection 2022-04-27 05:36:06 +05:30
pukkandan
ca04e1bf49 [Metadata] Remove filename from attached info-json 2022-04-27 01:19:06 +05:30
Elyse
00828e2c93 [downloader/ffmpeg] Specify headers for each URL (#3553)
Closes #2696
Authored by: elyse0
2022-04-26 02:54:56 -07:00
pukkandan
7ab56be2c7 [build] Ensure compat._legacy is packed in executables
Fixes 9196cbfe8b (commitcomment-72192406)
2022-04-26 15:13:17 +05:30
pukkandan
059bc4db19 [compat/asyncio] Use asyncio.all_tasks 2022-04-26 05:45:18 +05:30
pukkandan
9196cbfe8b [compat] Ensure submodules are correctly wrapped 2022-04-26 05:43:20 +05:30
pukkandan
9cd080508d Revert acbc642250
Reverts "[utils] WebSocketsWrapper: Ignore warnings at websockets instantiation"

The warning should not be suppressed. We need to address it
2022-04-26 05:43:19 +05:30
Lesmiscore
69b59b4b4b [downloader/fc2] Stop heatbeating once FFmpeg finishes
Authored by: Lesmiscore
2022-04-25 00:45:25 +09:00
Lesmiscore
acbc642250 [utils] WebSocketsWrapper: Ignore warnings at websockets instantiation
This also fixes crash caused by moving asyncio to .compat.

Authored by: Lesmiscore
Thanks: J.Chung at Discord (581418557871620106)
2022-04-25 00:44:30 +09:00
Yipten
96b49af01c [bandcamp] Exclude merch links (#3368)
Closes #3318
Authored by: Yipten
2022-04-23 19:40:20 -07:00
Teemu Ikonen
52c2af8298 [icareus] Add extractor (#3320)
Authored by: tpikonen, pukkandan
2022-04-23 18:18:04 -07:00
pukkandan
a1ddaa899c [hotstar] Refactor extractors
Closes #3517
2022-04-23 22:47:31 +05:30
pukkandan
6534298b12 [build] Avoid use of install -D
Closes #3429
2022-04-23 22:46:03 +05:30
pukkandan
90f4229409 [telegram] Fix metadata extraction
Closes #3528
2022-04-23 22:45:54 +05:30
pukkandan
b0f636beb4 [Sponsorblock] Don't crash when duration is unknown
CLoses #3529
2022-04-23 22:45:42 +05:30
Ha Tien Loi
d14b920c33 [PearVideo] Add fallback for formats (#3438)
Closes #3425
Authored by: hatienl0i261299
2022-04-22 06:45:52 -07:00
Evan Spensley
7774db5bf9 [EmbedThumbnail] Disable thumbnail conversion for mkv (#3512)
Closes #3209
Authored by: evansp
2022-04-21 14:26:10 -07:00
pukkandan
9b8ee23b99 [dependencies] Create module with all dependency imports 2022-04-21 00:48:52 +05:30
pukkandan
62f6f1cbf2 Don't imply -s for later stages of -O 2022-04-20 21:01:34 +05:30
mehq
e08585b0f8 [Gofile] Support password-protected links (#3488)
Closes #3465
Authored by: mehq
2022-04-20 02:43:15 -07:00
pukkandan
2d3b3feb7e [Olympics] Fix format extension
Closes #3481
2022-04-19 22:44:29 +05:30
pukkandan
6f638d325e Fix Makefile
Closes #3467, #35

Authored by: putnam
2022-04-19 14:54:31 +05:30
Lesmiscore
fdfc8149e1 [openrec:movie] Enable fallback for /movie/ URLs
Closes #3474
2022-04-19 11:06:55 +09:00
pukkandan
1e9969f4f5 bugfix for a44ca5a470, 19a0394044, 77f9033095
Closes #3472
2022-04-19 02:59:20 +05:30
pukkandan
43cc91ad75 bugfix for 19a0394044, 3d3bb1688b 2022-04-18 05:41:56 +05:30
felix
77f9033095 [compat] Split into sub-modules (#2173)
Authored by: fstirlitz, pukkandan
2022-04-18 04:26:43 +05:30
pukkandan
19a0394044 [cleanup] Misc cleanup and refactor (#2173) 2022-04-18 02:28:28 +05:30
pukkandan
b6dc37fe2a [test] Convert warnings into errors
* And fix some existing warnings

Authored by: fstirlitz
2022-04-18 02:12:48 +05:30
pukkandan
3d3bb1688b [docs] Improve embedding docs and other minor fixes 2022-04-18 00:33:00 +05:30
pukkandan
2e25ce3a05 [niconico] Set expected_protocol to a public field
Closes #3440
2022-04-17 23:00:22 +05:30
Lesmiscore (Naoya Ozaki)
c854208ccf [downloader/fragment] Make single thread download work for --live-from-start (#3446)
Authored by: Lesmiscore
2022-04-16 21:11:09 +09:00
pukkandan
e06bd8800f Fix --skip-unavailable-fragments
Bug in d71fd41249
Closes #3437
2022-04-15 23:47:37 +05:30
pukkandan
abfecb7bc1 [utils] Fix WebSocketsWrapper
Bug in 3cea3edd1a
Closes #3422
2022-04-15 23:47:37 +05:30
pukkandan
3b9d9f4374 Do not change fragment chunk-size when --test
Closes #3434
2022-04-15 17:05:56 +05:30
coletdjnz
affc4fefea [youtube] Fix episode metadata extraction 2022-04-15 16:22:03 +12:00
pukkandan
583910682f [chingari] Fix archiving and tests 2022-04-14 20:45:00 +05:30
Felix S
a49e777d59 [spotify] Detect iframe embeds (#3430)
Authored by: fstirlitz
2022-04-14 06:22:47 -07:00
Akmal
cda1bc5197 [facebook] Improve thumbnail extraction (#3392)
Authored by: Wikidepia
2022-04-12 18:21:23 -07:00
pukkandan
743f39750c Fix bug in 66cf3e1001 2022-04-12 19:57:08 +05:30
pukkandan
66cf3e1001 [EmbedSubtitle] Enable for more video extensions
Closes #3382
2022-04-12 05:32:53 +05:30
pukkandan
b07897ef5b [utils] certifi: Make sure the pem file exists
Closes #3353
2022-04-12 05:32:53 +05:30
pukkandan
e5a998f368 [cleanup] Misc cleanup (#2173)
Authored by: fstirlitz, pukkandan
2022-04-12 05:32:52 +05:30
pukkandan
f82711587c [cleanup] Sort imports
Using https://github.com/PyCQA/isort

    isort -m VERTICAL_HANGING_INDENT --py 36 -l 80 --rr -n --tc .
2022-04-12 05:32:52 +05:30
pukkandan
86e5f3ed2e [cleanup] Upgrade syntax
Using https://github.com/asottile/pyupgrade

1. `__future__` imports and `coding: utf-8` were removed
2. Files were rewritten with `pyupgrade --py36-plus --keep-percent-format`
3. f-strings were cherry-picked from `pyupgrade --py36-plus`

Extractors are left untouched (except removing header) to avoid unnecessary merge conflicts
2022-04-12 05:32:51 +05:30
pukkandan
f9934b9614 [cleanup] Mark some compat variables for removal (#2173)
Authored by fstirlitz, pukkandan
2022-04-12 05:32:50 +05:30
felix
cfb0511d82 [cleanup] Remove unused code paths (#2173)
Notes:

* `_windows_write_string`: Fixed in 3.6
  * https://bugs.python.org/issue1602
  * PEP: https://www.python.org/dev/peps/pep-0528

* Windows UTF-8 fix: Fixed in 3.3
  * https://bugs.python.org/issue13216

* `__loader__`: is always present in 3.3+
  * https://bugs.python.org/issue14646

* `workaround_optparse_bug9161`: Fixed in 2.7
  * https://bugs.python.org/issue9161

Authored by: fstirlitz
2022-04-12 05:32:50 +05:30
felix
ab96d1ad1b [cleanup] Remove unused scripts/tests (#2173)
Authored by fstirlitz, pukkandan
2022-04-12 01:15:21 +05:30
pukkandan
5a727063c5 [FFmpegMetadataPP] Remove \0 from metadata 2022-04-12 01:15:19 +05:30
mehq
fcdb8d6e88 [Gofile] Fix extraction (#3386)
Closes #3380
Authored by: mehq
2022-04-10 21:29:19 -07:00
krichbanana
ca5300c7ed [youtube] Add :ytnotifications extractor (#3347)
Authored by: krichbanana
2022-04-09 12:55:24 -07:00
pukkandan
97ec5bc550 [cookies] Report progress when importing cookies 2022-04-10 01:21:35 +05:30
pukkandan
a25bca9f89 [youtube, cleanup] Minor refactoring
Authored by: coletdjnz, pukkandan
2022-04-10 01:21:34 +05:30
pukkandan
f894294636 [EmbedThumbnail] Do not remove id3v1 tags 2022-04-10 01:21:34 +05:30
Lesmiscore
98804d034d [utils] locked_file: Do not give executable bits for newly created files
Authored by: Lesmiscore
2022-04-10 01:23:27 +09:00
pukkandan
4abea8ca0a [utils] sanitize_path: Fix when path is empty string 2022-04-09 10:11:25 +05:30
pukkandan
d46a3e7a12 [rai] Add release_year
Closes #2319
2022-04-08 22:32:05 +05:30
Ashish Gupta
2d2b5493ee [ZEE5] Fix extractor.
Authored by: Ashish0804
Closes: https://github.com/yt-dlp/yt-dlp/issues/3105
2022-04-08 21:03:50 +05:30
github-actions
dee1d65dc3 [version] update
Created by: pukkandan

:ci skip all
2022-04-08 09:57:06 +00:00
pukkandan
7884ade65e Release 2022.04.08 2022-04-08 15:21:27 +05:30
Jacek Nowacki
89fabf1125 [bilibili] Fix extraction of title with quotes (#3350)
Closes #3289
Authored by: dzek69
2022-04-08 02:21:37 -07:00
pukkandan
11e1c2e3f8 [TikTokVM] Fix redirect to user URL
Closes #3349, Closes #3351
2022-04-08 14:46:45 +05:30
pukkandan
ebc7d3ff1f [docs] Minor improvements (#3309, #3343)
Authored by: cffswb, danielyli


Co-authored-by: Daniel Li <dan@danielyli.com>
Co-authored-by: cffswb <karte577@gmail.com>
2022-04-08 14:09:10 +05:30
pukkandan
d8a58ddce7 De-prioritize automatic-subtitles when no --sub-lang is given
Closes #3314
2022-04-08 14:01:23 +05:30
mehq
4d57133095 [Jable] Add extractor (#3341)
Closes #3284
Authored by: mehq
2022-04-07 23:49:14 -07:00
Alexander Seiler
9b8b7a7b5e [Zattoo] Fix extractors (#2288)
Closes: #1244
Authored by: goggle
2022-04-07 23:44:58 -07:00
Ha Tien Loi
ab0970b233 [NRK] Extract timestamp (#3231)
Closes #3211
Authored by: hatienl0i261299
2022-04-07 08:52:27 -07:00
Lesmiscore
b52e788eb2 [Piapro] Extract description with break lines
Authored by: Lesmiscore
Closes #3334
2022-04-07 20:21:42 +09:00
pukkandan
316f2650f8 Ignore mhtml formats from -f mergeall
Closes #3324
2022-04-07 16:42:14 +05:30
Ha Tien Loi
bd4073c535 [AfreecaTV] Add AfreecaTVUserIE (#3286)
Closes #3257
Authored by: hatienl0i261299
2022-04-07 04:03:13 -07:00
pukkandan
22fba53fbd [FfmpegMetadata] Write id3v1 tags 2022-04-07 15:51:23 +05:30
coletdev
61d3665d9d [youtube] Fix uploader for collaborative playlists (#3332)
Authored by: coletdjnz
2022-04-07 01:11:16 -07:00
Lesmiscore (Naoya Ozaki)
870efdee28 [TVer] Fix extractor (#3268)
Authored by: Lesmiscore
2022-04-07 16:19:36 +09:00
pukkandan
b506289fe2 [test] Add test_locked_file 2022-04-07 12:05:44 +05:30
pukkandan
b63837bce0 [utils] locked_file: Fix non-blocking non-exclusive lock 2022-04-07 12:02:13 +05:30
Justin Keogh
fcfa8853e4 [utils] locked_file: Do not truncate files before locking (#2994)
Authored by: jakeogh, pukkandan
2022-04-06 22:58:56 -07:00
Lesmiscore (Naoya Ozaki)
06b1628d3e [twitcasting] Don't return multi_video for archive with single hls manifest (#3319)
Authored by: Lesmiscore
2022-04-07 13:42:01 +09:00
panatexxa
da1ffde15d [Moviepilot] Add extractor (#3282)
Authored by: panatexxa
2022-04-06 19:26:12 -07:00
Ha Tien Loi
42a4f21a03 [fptplay] Fix metadata extraction (#3218)
Authored by: hatienl0i261299
2022-04-06 01:52:08 -07:00
pukkandan
8973767198 Do not lock downloading file on Windows
Closes #3124
2022-04-05 23:32:22 +05:30
pukkandan
0edb3e336c Do not prevent download if locking is unsupported
Closes #3022

Failure to lock download-archive is still fatal.
This is consistent with youtube-dl's behavior
2022-04-05 23:32:22 +05:30
pukkandan
ce0593ef61 [http] Fix #3215 2022-04-05 21:31:59 +05:30
pukkandan
a44ca5a470 [cleanup] Misc fixes
Closes https://github.com/yt-dlp/yt-dlp/pull/3213, Closes https://github.com/yt-dlp/yt-dlp/pull/3117

Related: https://github.com/yt-dlp/yt-dlp/issues/3146#issuecomment-1077323114, https://github.com/yt-dlp/yt-dlp/pull/3277#discussion_r841019671, a825ffbffa (commitcomment-68538986), https://github.com/yt-dlp/yt-dlp/issues/2360, 5fa3c9a88f (r70393519), 5fa3c9a88f (r70393254)
2022-04-05 18:12:18 +05:30
Teemu Ikonen
0a8a7e68fa [ruutu] Detect embeds (#3294)
Authored by: tpikonen
2022-04-05 05:15:47 -07:00
Jeff Huffman
f4d706a931 [crunchyroll:playlist] Implement beta API (#2955)
Closes #3121, #2930

Authored by: tejing1
2022-04-05 03:51:12 -07:00
Ha Tien Loi
5fa3c9a88f [TikTok] Fix URLs with user id (#3295)
Closes #3243
Authored by: hatienl0i261299
2022-04-04 03:07:07 -07:00
pukkandan
04f3fd2c89 [cleanup] Use _html_extract_title 2022-04-04 15:13:30 +05:30
pukkandan
85e801a9db Fallback to video-only format when selecting by extension
Closes #3296
2022-04-04 15:13:21 +05:30
pukkandan
5127e92a94 Fix filepath sanitization in --print-to-file 2022-04-04 12:59:44 +05:30
Ha Tien Loi
18eac302a2 [Imdb] Improve extractor (#3291)
Closes #3283
Authored by: hatienl0i261299
2022-04-04 00:29:35 -07:00
Tim Schindler
12e022d074 [Cybrary] Add extractor (#3264)
Authored by: aaearon
2022-04-04 00:20:14 -07:00
Lesmiscore (Naoya Ozaki)
265e586d96 [openrec] Download archived livestreams (#3267)
Authored by: Lesmiscore
2022-04-04 00:41:14 +09:00
Fam0r
fbfde1c3e6 [elonet] Rewrite extractor (#3277)
Closes #2911
Authored by: Fam0r, pukkandan
2022-04-03 08:11:50 -07:00
aarubui
dc57e74a7f [tenplay] Improve extractor (#3280)
Authored by: aarubui
2022-04-03 06:53:22 -07:00
pukkandan
a17526e427 [youtube:tab] Minor improvements (See desc)
* Support shorts on channel homepage
* Extract thumbnail of OLAK playlists
2022-04-03 19:01:03 +05:30
coletdev
ad210f4fd4 [youtube:search] Support hashtag entries (#3265)
Authored-by: coletdjnz
2022-04-02 06:11:14 +00:00
coletdjnz
c8e856a551 [web.archive:youtube] Make CDX API requests non-fatal
Partial fix for https://github.com/yt-dlp/yt-dlp/issues/3278
Authored-by: coletdjnz
2022-04-02 19:07:13 +13:00
nixxo
c085e4ec47 [rai] Fix extraction of http formats (#3272)
Closes #3270
Authored by: nixxo
2022-04-01 22:57:56 -07:00
pukkandan
4c268f9cb7 [Nebula] Fix bug in 52efa4b312 2022-04-02 11:22:17 +05:30
Lesmiscore (Naoya Ozaki)
5d45484cc7 [niconico] Fix extraction of thumbnails and uploader (#3266) 2022-04-01 19:31:58 +09:00
pukkandan
e6f868a63c [utils] traverse_obj: Allow filtering by value 2022-03-31 13:33:28 +05:30
pukkandan
c4f60dd7cd [utils] Add try_call 2022-03-31 13:33:27 +05:30
pukkandan
f189faf1ce [BRMediathek] Fix VALID_URL
Closes #2466
2022-03-31 13:33:17 +05:30
Alexander Seiler
504f789ad5 [AZMedien] Support tv.telezueri.ch (#3251)
Authored by: goggle
2022-03-30 20:23:32 -07:00
Bricio
bb5a7cb8ad [Craftsy] Add extractor (#3208)
Authored by: Bricio
2022-03-30 20:04:55 -07:00
zackmark29
c418e6b5a6 [viu] Fix bypass for preview (#3247)
Authored by: zackmark29
2022-03-30 19:47:58 -07:00
pukkandan
11078c6d57 [crunhyroll] Fix inheritance
https://github.com/yt-dlp/yt-dlp/pull/2955#issuecomment-1083060465
2022-03-30 18:19:51 +05:30
MrRawes
5d0aeac0e9 [docs] Clarify the exact BSD license of dependencies (#3197)
Authored by: MrRawes
2022-03-30 04:35:06 -07:00
Felix S
180c81509f [docs] Add an .editorconfig file (#3220)
Authored by: fstirlitz
2022-03-30 04:31:25 -07:00
Daniel
ab2579bb45 [xnxx] Add xnxx3.com (#3188)
Authored by: rozari0
2022-03-30 03:54:35 -07:00
Ha Tien Loi
48e15bb6b1 [dailymotion] Support geo.dailymotion.com (#3230)
Closes #3229
Authored by: hatienl0i261299
2022-03-30 03:04:00 -07:00
pukkandan
af4944d84b Fix bug in 8a7f68d0b1
Closes #3241
2022-03-30 12:22:36 +05:30
David
e7870111e8 [YouTube] Add new age-gate bypass (#3233)
Closes #3182
Authored by: zerodytrash, pukkandan
2022-03-29 03:05:31 -07:00
pukkandan
8a7f68d0b1 [ffmpeg] Cache version data
Related: https://github.com/dasl-/pifi/issues/9
2022-03-29 03:44:51 +05:30
Ha Tien Loi
9139d2fae0 [WasdTV] Add extractor (#3045)
Closes #3041
Authored by: un-def, hatienl0i261299
2022-03-27 20:27:41 -07:00
nyuszika7h
bdd60588b0 [viki] Don't attempt to modify URLs with signature (#3222)
Closes #1379
Authored by: nyuszika7h
2022-03-27 20:23:44 -07:00
Luc Ritchie
f5f15c9993 [BiliIntl] Support user-generated videos (#3203)
Authored by: wlritchi
2022-03-27 20:21:42 -07:00
pukkandan
cb96c5be70 Fix --no-overwrite for playlist infojson
Fixes: https://github.com/yt-dlp/yt-dlp/issues/1467#issuecomment-1079922971
2022-03-28 08:45:23 +05:30
pukkandan
90137ca4be [utils] Add filter_dict 2022-03-28 08:25:04 +05:30
coletdev
1c1b2f96ae [youtube:tab] Fix duration extraction for shorts (#3171)
Related: https://github.com/TeamNewPipe/NewPipe/issues/8034
Authored-by: coletdjnz
2022-03-28 00:49:42 +00:00
Felix S
47b8bf207b [go,viu] Extract subtitles from the m3u8 manifest (#3219)
Authored by: fstirlitz
2022-03-27 02:35:14 -07:00
Tim Schindler
4628a3aa75 [ITProTV] Add extractor (#3196)
Authored by: aaearon
2022-03-27 02:00:38 -07:00
mehq
5b4bb715e6 [BanBye] Add extractor (#3177)
Closes #3175
Authored by: mehq
2022-03-27 01:57:05 -07:00
pukkandan
1235d333ab [youtube] Fix auto-translated automatic captions
d49669acad only covered ASR

Closes #2956
2022-03-27 14:06:26 +05:30
pukkandan
18e4940825 [youtube] Add extractor-arg to skip auto-translated subs 2022-03-27 14:04:20 +05:30
pukkandan
c0b6e5c74d Show warning when all media formats have DRM
Related: #1379
2022-03-27 11:39:35 +05:30
shirt
727029c508 [youtube] Detect DRM better
Authored by: shirt-dev
2022-03-27 11:27:27 +05:30
pukkandan
5c3895fff1 [outtmpl] Limit changes during sanitization
Closes #2761
2022-03-27 11:18:35 +05:30
coletdev
fd2ad7cb24 [youtube:tab] Return shorts url if video is a short (#3168)
Allows filtering out shorts from feeds with `--match-filter`
Closes #3165
Authored-by: coletdjnz
2022-03-27 05:20:25 +00:00
pukkandan
4a3175fc4c [VideoConvertor] Ensure all streams are copied
Closes #3200
2022-03-27 09:28:58 +05:30
pukkandan
5cf34021f5 [Concat] Ensure final directory exists
Fixes https://github.com/yt-dlp/yt-dlp/issues/3181#issuecomment-1079622589
2022-03-27 04:52:11 +05:30
pukkandan
34baa9fdf0 [outtmpl] Fix replacement/default when used with alternate 2022-03-26 07:39:59 +05:30
pukkandan
6db9c4d57d Ignore format-specific fields in initial pass of --match-filter
Closes #3074
2022-03-25 14:27:09 +05:30
Lesmiscore (Naoya Ozaki)
3cea3edd1a [utils] WebSocketsWrapper: Allow omitting __enter__ invocation (#3187)
Authored by: Lesmiscore
2022-03-25 17:24:39 +09:00
pukkandan
b1a7cd056a Treat multiple --match-filters as OR
Closes #3144
2022-03-25 13:33:46 +05:30
pukkandan
28787f16c6 [downloader] Fix invocation of HttpieFD
Closes #3154
2022-03-25 13:00:42 +05:30
zackmark29
1fb707badb [viu] Fixed extractor (#3136)
Closes #3133
Authored by: zackmark29, pukkandan
2022-03-24 20:23:54 -07:00
pukkandan
a3f2445e29 [postprocessor,cleanup] Create _download_json 2022-03-25 08:45:35 +05:30
pukkandan
ae72962643 [youtube] Try embedded client variants before agegate
agegate variants appears to be broken, but don't remove them for the time-being
2022-03-25 05:00:41 +05:30
pukkandan
ae6a1b9585 [docs] Minor improvements
Closes #3127, Closes #3081, Closes #3177
2022-03-24 07:30:25 +05:30
pukkandan
231025c463 Fix bug in 52efa4b312
Closes #3173
2022-03-24 07:28:10 +05:30
pukkandan
700ccbe3f1 [extractor] Allow control characters inside json
Closes #3174
2022-03-24 07:28:07 +05:30
vvto33
12a64f2777 [TVer] Support landing page (#3075)
Authored by: vvto33
2022-03-23 18:11:13 -07:00
mehq
b8f2f8f6b3 [LastFM] Add extractors (#3141)
Closes #2967
Authored by: mehq
2022-03-23 11:35:42 -07:00
coletdev
af14914baa Remove Accept-Encoding header from std_headers (#3153)
This should be set by each downloader to what it supports.
Fixes https://github.com/yt-dlp/yt-dlp/issues/3142
Authored-by: coletdjnz
2022-03-23 07:47:02 +00:00
pukkandan
ea5ca8e7fc [ellentube] Extract subtitles from manifest
Fixes https://github.com/ytdl-org/youtube-dl/issues/30761
2022-03-23 12:36:49 +05:30
Lesmiscore (Naoya Ozaki)
c2d2ee40eb [generic] Extract subtitles from video.js (#3156)
Authored by: Lesmiscore
2022-03-22 23:28:53 -07:00
pukkandan
c70c418d33 Fix --abort-on-error for subtitles
Closes #3163
2022-03-23 08:53:16 +05:30
pukkandan
b9c7b1e9b4 [cleanup, vimeo] Fix tests 2022-03-23 08:26:48 +05:30
coletdev
d5820461e8 Use certificates from certifi if installed (#3115)
Fixes #3102 and most `CERTIFICATE_VERIFY_FAILED` issues

Authored by: coletdjnz
2022-03-22 16:26:55 -07:00
coletdev
8a23db9519 [wget] Fix proxy (#3152)
Upstream PR: https://github.com/ytdl-org/youtube-dl/pull/29343
Authored-by: kikuyan, coletdjnz
2022-03-22 14:24:27 -07:00
CplPwnies
1f1df1251e [adobepass] Fix Suddenlink MSO (#3148)
Authored by: CplPwnies
2022-03-22 14:09:38 -07:00
1-Byte
84842aee2b [azmedien] Add TVO Online to supported hosts (#3125)
Authored by: 1-Byte
2022-03-20 10:49:00 -07:00
Lesmiscore (Naoya Ozaki)
be4685ab7b [http] Reject broken range before request (#3079)
* And fix filesize estimate for byterange downloads

Closes #2001
Authored by: Lesmiscore, Jules-A, pukkandan
2022-03-18 18:15:01 -07:00
coletdev
e6552207da [panopto] Improve subtitle extraction and support slides (#3009)
Related: #1946, #2908
Authored-by: coletdjnz
2022-03-18 22:19:36 +00:00
coletdev
a2e77303e3 [downloader/http] Retry on more errors (#3065)
Closes #3056, #2071
Related: #3034, #2969
Authored-by: coletdjnz
2022-03-18 22:10:20 +00:00
foghawk
510809f1aa [nitter] Minor fixes and update instance list (#3099)
Authored by: foghawk
2022-03-18 14:08:38 -07:00
i6t
f4ad919298 [Veo] Fix extractor (#3101)
Authored by: i6t
2022-03-18 14:06:52 -07:00
s0u1h
eeb2a770f3 [utils] format_decimal_suffix: Fix for very large numbers (#3109)
Authored by: s0u1h
2022-03-18 14:03:09 -07:00
pukkandan
0c14d66ad9 Fix autonumber
Bug in 09b49e1f68
2022-03-19 02:29:02 +05:30
pukkandan
52efa4b312 [extractor] Add _perform_login function (#2943)
* Adds new functions `_initialize_pre_login` and `_perform_login` as part of the extractor API
* Adds `ie.supports_login` to the public API
2022-03-18 13:53:33 -07:00
Luc Ritchie
028f6437f1 [afreecatv] Match new vod url (#3097)
Authored by: wlritchi
2022-03-18 02:53:07 -07:00
Sipherdrakon
43c38abd1f [ParamountPlus,CBS] Change VALID_URL (#3098)
Closes #3096

Authored by: Sipherdrakon
2022-03-18 02:49:31 -07:00
pukkandan
e4b98809cf [youtube] Fix pagination of membership tab 2022-03-18 05:23:51 +05:30
pukkandan
16c620bc55 Handle float in --wait-for-video
Closes #3082
2022-03-18 03:25:47 +05:30
pukkandan
5a373d9768 [veo] Fix _VALID_URL
Closes #3095
2022-03-18 03:01:07 +05:30
Ha Tien Loi
7e6a187096 [Huya] Add extractor (#3035)
Closes #3033
Authored by: hatienl0i261299
2022-03-17 07:24:15 -07:00
Lesmiscore (Naoya Ozaki)
3f168f0e45 [RUTV] Fix format sorting (#3085)
Closes #3084
Authored by: Lesmiscore
2022-03-17 07:11:36 -07:00
Lesmiscore (Naoya Ozaki)
7bdcb4a40e [niconico] Rewrite NiconicoIE (#3018)
Closes https://github.com/yt-dlp/yt-dlp/issues/2636, partially fixes https://github.com/yt-dlp/yt-dlp/issues/367
Authored by: Lesmiscore
2022-03-17 05:22:14 -07:00
Soebb
497a6c5f57 [daftsex] Fix extractor (#2757)
Closes #2637

Authored by: Soebb
2022-03-16 17:44:21 -07:00
BohwaZ
4b3c5d1b81 [FranceCulture] Support playlists (#1872)
Authored by: bohwaz
2022-03-16 17:40:27 -07:00
Dorian Westacott
ec47c12f69 [ParamountPlusSeries] Support multiple pages (#3026)
Authored by: dodrian
2022-03-16 16:54:20 -07:00
pukkandan
25791435b7 [arte] Add format_note to m3u8 formats
Related: #3086
2022-03-17 02:00:47 +05:30
pukkandan
4e34889f1c [rumble] unescape title 2022-03-17 01:37:04 +05:30
pukkandan
a1b2d84360 [youtube] Avoid false positives when detecting damaged formats
Closes #3083
2022-03-16 19:46:29 +05:30
coletdjnz
5dbc77df26 [youtube:api] Prefer minified JSON response
Authored-by: coletdjnz
2022-03-16 09:29:15 +13:00
Lesmiscore (Naoya Ozaki)
d71fd41249 [fragment] Read downloaded fragments only when needed (#3069)
Authored by: Lesmiscore
2022-03-15 12:27:41 +09:00
shirt
d69e55c1d8 [cleanup] Remove readthedocs from README.md 2022-03-14 12:19:33 -04:00
shirt
9f2a6352ea [docs] Remove readthedocs 2022-03-14 16:17:01 +00:00
pukkandan
aeb21b98f1 [phantomjs] Fix bug in 8b7539d27c
Closes #3066
2022-03-14 16:19:23 +05:30
coletdev
b3edc8068e [downloader/mhtml] Fix fragments with absolute urls (#3044)
Authored-by: coletdjnz
2022-03-13 22:03:40 +00:00
coletdev
17322130a9 [youtube] Improve video upload date handling (#3029)
* Don't prefer UTC upload date for past live streams/premieres
* Improve regex (fixes a regression)

Authored-by: coletdjnz
2022-03-13 22:02:44 +00:00
pukkandan
5ca764c506 [FFmpegVideoConvertor] Add more formats to --remux-video 2022-03-13 22:26:03 +05:30
pukkandan
e880c92c65 Exit after --dump-user-agent
Bug in d1b5f70bc9

Closes #3055
2022-03-13 14:38:39 +05:30
coletdjnz
a825ffbffa [extractor] Support merging subtitles with data
Authored-by: coletdjnz
2022-03-12 11:22:28 +13:00
pukkandan
592b748582 [cleanup] Minor cleanup
Closes #3006
2022-03-11 19:40:15 +05:30
pukkandan
cf4f42cb97 Protect stdout from unexpected progress and console-title
Closes #3023
2022-03-11 19:29:45 +05:30
pukkandan
da1d734fbe Remove incorrect warning for --dateafter
Closes #3030
2022-03-11 19:29:44 +05:30
pukkandan
2b38f7b2bc [MetadataParser] Validate outtmpl early 2022-03-11 19:29:43 +05:30
pukkandan
76aa991374 Fix case of http_headers
Bug in 8b7539d27c

Fixes https://github.com/yt-dlp/yt-dlp/issues/1346#issuecomment-1064527765
2022-03-11 19:29:34 +05:30
Lesmiscore (Naoya Ozaki)
24e3d87431 [PokemonSoundLibrary] Add extractor (#3001)
Authored by: Lesmiscore
2022-03-10 22:24:50 +09:00
Ha Tien Loi
63b2f88bc7 [Zingmp3] Fix signature (#3004)
Authored by: hatienl0i261299
2022-03-09 22:13:19 -08:00
pukkandan
07ff290dce Fix --sleep-interval
Bug in d1b5f70bc9

Closes #3012
2022-03-10 11:38:34 +05:30
pukkandan
51c22ef4e2 Fix --throttled-rate
Typo in d1b5f70bc9

Closes #2996
2022-03-10 03:29:01 +05:30
Ha Tien Loi
33b8c411bc [MangoTV] Improve extractor (#2971)
Authored by: hatienl0i261299
2022-03-09 13:54:26 -08:00
MMM
10331a2672 Fix --print with --ignore-no-formats when url is None (#3000)
Authored by: flashdagger
2022-03-09 13:12:23 -08:00
Lesmiscore (Naoya Ozaki)
6e6beffd04 [openrec] Refactor extractors (#2941)
Authored by: Lesmiscore
2022-03-09 21:08:09 +09:00
pukkandan
e491d06d34 [utils] ExtractorError: Fix for older python versions
Closes #2993
2022-03-09 06:42:25 +05:30
pukkandan
7a0ba75857 [build] Add requirements.txt to pip distributions
Closes #2995
2022-03-09 06:42:24 +05:30
coletdev
e248be3319 [panopto] Add extractors (#2908)
Based on https://github.com/ytdl-org/youtube-dl/pull/13449
Closes #1946
Authored by: coletdjnz, kmark
2022-03-08 13:00:57 -08:00
pukkandan
ff91cf7483 [utils] Add get_first 2022-03-09 02:26:52 +05:30
1123 changed files with 11235 additions and 12662 deletions

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
root = true
[**.py]
charset = utf-8
indent_size = 4
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true

2
.gitattributes vendored
View File

@@ -2,3 +2,5 @@
Makefile* text whitespace=-tab-in-indent
*.sh text eol=lf
*.md diff=markdown
*.py diff=python

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a broken site
required: true
- label: I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **2022.05.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
required: true
@@ -51,12 +51,12 @@ body:
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2022.03.08.1 (exe)
[debug] yt-dlp version 2022.05.18 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2022.03.08.1)
yt-dlp is up to date (2022.05.18)
<more lines>
render: shell
validations:

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a new site support request
required: true
- label: I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **2022.05.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that none of provided URLs [violate any copyrights](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
required: true
@@ -62,12 +62,12 @@ body:
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2022.03.08.1 (exe)
[debug] yt-dlp version 2022.05.18 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2022.03.08.1)
yt-dlp is up to date (2022.05.18)
<more lines>
render: shell
validations:

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a site feature request
required: true
- label: I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **2022.05.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
required: true
@@ -60,12 +60,12 @@ body:
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2022.03.08.1 (exe)
[debug] yt-dlp version 2022.05.18 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2022.03.08.1)
yt-dlp is up to date (2022.05.18)
<more lines>
render: shell
validations:

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a bug unrelated to a specific site
required: true
- label: I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **2022.05.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
required: true
@@ -45,12 +45,12 @@ body:
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2022.03.08.1 (exe)
[debug] yt-dlp version 2022.05.18 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2022.03.08.1)
yt-dlp is up to date (2022.05.18)
<more lines>
render: shell
validations:

View File

@@ -13,7 +13,7 @@ body:
required: true
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
required: true
- label: I've verified that I'm running yt-dlp version **2022.03.08.1**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **2022.05.18** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
required: true
@@ -30,3 +30,24 @@ body:
placeholder: WRITE DESCRIPTION HERE
validations:
required: true
- type: textarea
id: log
attributes:
label: Verbose log
description: |
If your feature request involves an existing yt-dlp command, provide the complete verbose output of that command.
Add the `-vU` flag to **your** command line you run yt-dlp with (`yt-dlp -vU <your command line>`), copy the WHOLE output and insert it below.
It should look similar to this:
placeholder: |
[debug] Command-line config: ['-vU', 'http://www.youtube.com/watch?v=BaW_jenozKc']
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2021.12.01 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2021.12.01)
<more lines>
render: shell

View File

@@ -35,7 +35,7 @@ body:
attributes:
label: Verbose log
description: |
If your question involes a yt-dlp command, provide the complete verbose output of that command.
If your question involves a yt-dlp command, provide the complete verbose output of that command.
Add the `-vU` flag to **your** command line you run yt-dlp with (`yt-dlp -vU <your command line>`), copy the WHOLE output and insert it below.
It should look similar to this:
placeholder: |

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a broken site
required: true
- label: I've verified that I'm running yt-dlp version **%(version)s**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
required: true

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a new site support request
required: true
- label: I've verified that I'm running yt-dlp version **%(version)s**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that none of provided URLs [violate any copyrights](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
required: true

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a site feature request
required: true
- label: I've verified that I'm running yt-dlp version **%(version)s**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
required: true

View File

@@ -11,9 +11,9 @@ body:
options:
- label: I'm reporting a bug unrelated to a specific site
required: true
- label: I've verified that I'm running yt-dlp version **%(version)s**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've checked that all provided URLs are alive and playable in a browser
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
required: true
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
required: true

View File

@@ -13,7 +13,7 @@ body:
required: true
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
required: true
- label: I've verified that I'm running yt-dlp version **%(version)s**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
required: true
- label: I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
required: true
@@ -30,3 +30,24 @@ body:
placeholder: WRITE DESCRIPTION HERE
validations:
required: true
- type: textarea
id: log
attributes:
label: Verbose log
description: |
If your feature request involves an existing yt-dlp command, provide the complete verbose output of that command.
Add the `-vU` flag to **your** command line you run yt-dlp with (`yt-dlp -vU <your command line>`), copy the WHOLE output and insert it below.
It should look similar to this:
placeholder: |
[debug] Command-line config: ['-vU', 'http://www.youtube.com/watch?v=BaW_jenozKc']
[debug] Portable config file: yt-dlp.conf
[debug] Portable config: ['-i']
[debug] Encodings: locale cp1252, fs utf-8, stdout utf-8, stderr utf-8, pref cp1252
[debug] yt-dlp version 2021.12.01 (exe)
[debug] Python version 3.8.8 (CPython 64bit) - Windows-10-10.0.19041-SP0
[debug] exe versions: ffmpeg 3.0.1, ffprobe 3.0.1
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
yt-dlp is up to date (2021.12.01)
<more lines>
render: shell

View File

@@ -35,7 +35,7 @@ body:
attributes:
label: Verbose log
description: |
If your question involes a yt-dlp command, provide the complete verbose output of that command.
If your question involves a yt-dlp command, provide the complete verbose output of that command.
Add the `-vU` flag to **your** command line you run yt-dlp with (`yt-dlp -vU <your command line>`), copy the WHOLE output and insert it below.
It should look similar to this:
placeholder: |

View File

@@ -1,28 +1,29 @@
## Please follow the guide below
<!--
# Please follow the guide below
- You will be asked some questions, please read them **carefully** and answer honestly
- Put an `x` into all the boxes [ ] relevant to your *pull request* (like that [x])
- Put an `x` into all the boxes `[ ]` relevant to your *pull request* (like [x])
- Use *Preview* tab to see how your *pull request* will actually look like
---
-->
### Before submitting a *pull request* make sure you have:
- [ ] At least skimmed through [contributing guidelines](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions) including [yt-dlp coding conventions](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#yt-dlp-coding-conventions)
- [ ] [Searched](https://github.com/yt-dlp/yt-dlp/search?q=is%3Apr&type=Issues) the bugtracker for similar pull requests
- [ ] Checked the code with [flake8](https://pypi.python.org/pypi/flake8)
- [ ] Checked the code with [flake8](https://pypi.python.org/pypi/flake8) and [ran relevant tests](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions)
### In order to be accepted and merged into yt-dlp each piece of code must be in public domain or released under [Unlicense](http://unlicense.org/). Check one of the following options:
- [ ] I am the original author of this code and I am willing to release it under [Unlicense](http://unlicense.org/)
- [ ] I am not the original author of this code but it is in public domain or released under [Unlicense](http://unlicense.org/) (provide reliable evidence)
### What is the purpose of your *pull request*?
- [ ] Bug fix
- [ ] Improvement
- [ ] New extractor
- [ ] New feature
- [ ] Fix or improvement to an extractor (Make sure to add/update tests)
- [ ] New extractor ([Piracy websites will not be accepted](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy))
- [ ] Core bug fix/improvement
- [ ] New feature (It is strongly [recommended to open an issue first](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#adding-new-feature-or-making-overarching-changes))
---
### Description of your *pull request* and other information
Explanation of your *pull request* in arbitrary form goes here. Please make sure the description explains the purpose and effect of your *pull request* and is worded well enough to be understood. Provide as much context and examples as possible.
Explanation of your *pull request* in arbitrary form goes here. Please **make sure the description explains the purpose and effect** of your *pull request* and is worded well enough to be understood. Provide as much **context and examples** as possible.

View File

@@ -8,14 +8,14 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04]
# py3.9 is in quick-test
python-version: [3.7, 3.8, 3.10-dev, pypy-3.6, pypy-3.7]
os: [ubuntu-latest]
# CPython 3.9 is in quick-test
python-version: ['3.6', '3.7', '3.10', 3.11-dev, pypy-3.6, pypy-3.7, pypy-3.8, pypy-3.9]
run-tests-ext: [sh]
include:
# atleast one of the tests must be in windows
- os: windows-latest
python-version: 3.6
python-version: 3.8
run-tests-ext: bat
steps:
- uses: actions/checkout@v2

View File

@@ -8,12 +8,12 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ubuntu-18.04]
python-version: [3.7, 3.8, 3.9, 3.10-dev, pypy-3.6, pypy-3.7]
os: [ubuntu-latest]
python-version: ['3.6', '3.7', '3.9', '3.10', 3.11-dev, pypy-3.6, pypy-3.7, pypy-3.8, pypy-3.9]
run-tests-ext: [sh]
include:
- os: windows-latest
python-version: 3.6
python-version: 3.8
run-tests-ext: bat
steps:
- uses: actions/checkout@v2

4
.gitignore vendored
View File

@@ -82,6 +82,7 @@ updates_key.pem
*.egg-info
.tox
*.class
*.isorted
# Generated
AUTHORS
@@ -116,3 +117,6 @@ yt-dlp.zip
ytdlp_plugins/extractor/*
!ytdlp_plugins/extractor/__init__.py
!ytdlp_plugins/extractor/sample.py
ytdlp_plugins/postprocessor/*
!ytdlp_plugins/postprocessor/__init__.py
!ytdlp_plugins/postprocessor/sample.py

View File

@@ -1,22 +0,0 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Optionally build your docs in additional formats such as PDF
formats:
- epub
- pdf
- htmlzip
# Optionally set the version of Python and requirements required to build your docs
python:
version: 3
install:
- requirements: docs/requirements.txt

View File

@@ -178,7 +178,6 @@ After you have ensured this site is distributing its content legally, you can fo
1. Start with this simple template and save it to `yt_dlp/extractor/yourextractor.py`:
```python
# coding: utf-8
from .common import InfoExtractor
@@ -375,21 +374,21 @@ When extracting metadata try to do so from multiple sources. For example if `tit
#### Example
Say `meta` from the previous example has a `title` and you are about to extract it. Since `title` is a mandatory meta field you should end up with something like:
Say `meta` from the previous example has a `title` and you are about to extract it like:
```python
title = meta['title']
title = meta.get('title')
```
If `title` disappears from `meta` in future due to some changes on the hoster's side the extraction would fail since `title` is mandatory. That's expected.
If `title` disappears from `meta` in future due to some changes on the hoster's side the title extraction would fail.
Assume that you have some another source you can extract `title` from, for example `og:title` HTML meta of a `webpage`. In this case you can provide a fallback scenario:
Assume that you have some another source you can extract `title` from, for example `og:title` HTML meta of a `webpage`. In this case you can provide a fallback like:
```python
title = meta.get('title') or self._og_search_title(webpage)
```
This code will try to extract from `meta` first and if it fails it will try extracting `og:title` from a `webpage`.
This code will try to extract from `meta` first and if it fails it will try extracting `og:title` from a `webpage`, making the extractor more robust.
### Regular expressions
@@ -534,13 +533,13 @@ Extracting variables is acceptable for reducing code duplication and improving r
Correct:
```python
title = self._html_search_regex(r'<title>([^<]+)</title>', webpage, 'title')
title = self._html_search_regex(r'<h1>([^<]+)</h1>', webpage, 'title')
```
Incorrect:
```python
TITLE_RE = r'<title>([^<]+)</title>'
TITLE_RE = r'<h1>([^<]+)</h1>'
# ...some lines of code...
title = self._html_search_regex(TITLE_RE, webpage, 'title')
```
@@ -643,7 +642,7 @@ Wrap all extracted numeric data into safe functions from [`yt_dlp/utils.py`](yt_
Use `url_or_none` for safe URL processing.
Use `try_get`, `dict_get` and `traverse_obj` for safe metadata extraction from parsed JSON.
Use `traverse_obj` and `try_call` (superseeds `dict_get` and `try_get`) for safe metadata extraction from parsed JSON.
Use `unified_strdate` for uniform `upload_date` or any `YYYYMMDD` meta field extraction, `unified_timestamp` for uniform `timestamp` extraction, `parse_filesize` for `filesize` extraction, `parse_count` for count meta fields extraction, `parse_resolution`, `parse_duration` for `duration` extraction, `parse_age_limit` for `age_limit` extraction.

View File

@@ -214,3 +214,37 @@ pycabbage
regarten
Ronnnny
schn0sch
s0u1h
MrRawes
cffswb
danielyli
1-Byte
mehq
dzek69
aaearon
panatexxa
kmark
un-def
goggle
Soebb
Fam0r
bohwaz
dodrian
vvto33
ca-za
connercsbn
diegorodriguezv
ekangmonyet
elyse0
evansp
GiedriusS
HE7086
JordanWeatherby
m4tu4g
MarwenDallel
nevack
putnam
rand-net
vertan
Wikidepia
Yipten

View File

@@ -11,6 +11,262 @@
-->
### 2022.05.18
* Add support for SSL client certificate authentication by [coletdjnz](https://github.com/coletdjnz), [dirkf](https://github.com/dirkf)
* Adds `--client-certificate`, `--client-certificate-key`, `--client-certificate-password`
* Add `--match-filter -` to interactively ask for each video
* `--max-downloads` should obey `--break-per-input`
* Allow use of weaker ciphers with `--legacy-server-connect`
* Don't imply `-s` for later stages of `-O`
* Fix `--date today`
* Fix `--skip-unavailable-fragments`
* Fix color in `-q -F`
* Fix redirect HTTP method handling by [coletdjnz](https://github.com/coletdjnz)
* Improve `--clean-infojson`
* Remove warning for videos with an empty title
* Run `FFmpegFixupM3u8PP` for live-streams if needed
* Show name of downloader in verbose log
* [cookies] Allow `cookiefile` to be a text stream
* [cookies] Report progress when importing cookies
* [downloader/ffmpeg] Specify headers for each URL by [elyse0](https://github.com/elyse0)
* [fragment] Do not change chunk-size when `--test`
* [fragment] Make single thread download work for `--live-from-start` by [Lesmiscore](https://github.com/Lesmiscore)
* [hls] Fix `byte_range` for `EXT-X-MAP` fragment by [fstirlitz](https://github.com/fstirlitz)
* [http] Fix retrying on read timeout by [coletdjnz](https://github.com/coletdjnz)
* [ffmpeg] Fix features detection
* [EmbedSubtitle] Enable for more video extensions
* [EmbedThumbnail] Disable thumbnail conversion for mkv by [evansp](https://github.com/evansp)
* [EmbedThumbnail] Do not obey `-k`
* [EmbedThumbnail] Do not remove id3v1 tags
* [FFmpegMetadata] Remove `\0` from metadata
* [FFmpegMetadata] Remove filename from attached info-json
* [FixupM3u8] Obey `--hls-prefer-mpegts`
* [Sponsorblock] Don't crash when duration is unknown
* [XAttrMetadata] Refactor and document dependencies
* [extractor] Document netrc machines
* [extractor] Update `manifest_url`s after redirect by [elyse0](https://github.com/elyse0)
* [extractor] Update dash `manifest_url` after redirects by [elyse0](https://github.com/elyse0)
* [extractor] Use `classmethod`/`property` where possible
* [generic] Refactor `_extract_rss`
* [utils] `is_html`: Handle double BOM
* [utils] `locked_file`: Ignore illegal seek on `truncate` by [jakeogh](https://github.com/jakeogh)
* [utils] `sanitize_path`: Fix when path is empty string
* [utils] `write_string`: Workaround newline issue in `conhost`
* [utils] `certifi`: Make sure the pem file exists
* [utils] Fix `WebSocketsWrapper`
* [utils] `locked_file`: Do not give executable bits for newly created files by [Lesmiscore](https://github.com/Lesmiscore)
* [utils] `YoutubeDLCookieJar`: Detect and reject JSON file by [Lesmiscore](https://github.com/Lesmiscore)
* [test] Convert warnings into errors and fix some existing warnings by [fstirlitz](https://github.com/fstirlitz)
* [dependencies] Create module with all dependency imports
* [compat] Split into sub-modules by [fstirlitz](https://github.com/fstirlitz), [pukkandan](https://github.com/pukkandan)
* [compat] Implement `compat.imghdr`
* [build] Add `make uninstall` by [MrRawes](https://github.com/MrRawes)
* [build] Avoid use of `install -D`
* [build] Fix `Makefile` by [putnam](https://github.com/putnam)
* [build] Fix `--onedir` on macOS
* [build] Add more test-runners
* [cleanup] Deprecate some compat vars by [fstirlitz](https://github.com/fstirlitz), [pukkandan](https://github.com/pukkandan)
* [cleanup] Remove unused code paths, extractors, scripts and tests by [fstirlitz](https://github.com/fstirlitz)
* [cleanup] Upgrade syntax (`pyupgrade`) and sort imports (`isort`)
* [cleanup, docs, build] Misc fixes
* [BilibiliLive] Add extractor by [HE7086](https://github.com/HE7086), [pukkandan](https://github.com/pukkandan)
* [Fifa] Add Extractor by [Bricio](https://github.com/Bricio)
* [goodgame] Add extractor by [nevack](https://github.com/nevack)
* [gronkh] Add playlist extractors by [hatienl0i261299](https://github.com/hatienl0i261299)
* [icareus] Add extractor by [tpikonen](https://github.com/tpikonen), [pukkandan](https://github.com/pukkandan)
* [iwara] Add playlist extractors by [i6t](https://github.com/i6t)
* [Likee] Add extractor by [hatienl0i261299](https://github.com/hatienl0i261299)
* [masters] Add extractor by [m4tu4g](https://github.com/m4tu4g)
* [nebula] Add support for subscriptions by [hheimbuerger](https://github.com/hheimbuerger)
* [Podchaser] Add extractors by [connercsbn](https://github.com/connercsbn)
* [rokfin:search] Add extractor by [P-reducible](https://github.com/P-reducible), [pukkandan](https://github.com/pukkandan)
* [youtube] Add `:ytnotifications` extractor by [krichbanana](https://github.com/krichbanana)
* [youtube] Add YoutubeStoriesIE (`ytstories:<channel UCID>`) by [coletdjnz](https://github.com/coletdjnz)
* [ZingMp3] Add chart and user extractors by [hatienl0i261299](https://github.com/hatienl0i261299)
* [adn] Update AES key by [elyse0](https://github.com/elyse0)
* [adobepass] Allow cookies for authenticating MSO
* [bandcamp] Exclude merch links by [Yipten](https://github.com/Yipten)
* [chingari] Fix archiving and tests
* [DRTV] Improve `_VALID_URL` by [vertan](https://github.com/vertan)
* [facebook] Improve thumbnail extraction by [Wikidepia](https://github.com/Wikidepia)
* [fc2] Stop heatbeating once FFmpeg finishes by [Lesmiscore](https://github.com/Lesmiscore)
* [Gofile] Fix extraction and support password-protected links by [mehq](https://github.com/mehq)
* [hotstar, cleanup] Refactor extractors
* [InfoQ] Don't fail on missing audio format by [evansp](https://github.com/evansp)
* [Jamendo] Extract more metadata by [evansp](https://github.com/evansp)
* [kaltura] Update API calls by [flashdagger](https://github.com/flashdagger)
* [KhanAcademy] Fix extractor by [rand-net](https://github.com/rand-net)
* [LCI] Fix extractor by [MarwenDallel](https://github.com/MarwenDallel)
* [lrt] Support livestreams by [GiedriusS](https://github.com/GiedriusS)
* [niconico] Set `expected_protocol` to a public field
* [Niconico] Support 2FA by [ekangmonyet](https://github.com/ekangmonyet)
* [Olympics] Fix format extension
* [openrec:movie] Enable fallback for /movie/ URLs
* [PearVideo] Add fallback for formats by [hatienl0i261299](https://github.com/hatienl0i261299)
* [radiko] Fix extractor by [Lesmiscore](https://github.com/Lesmiscore)
* [rai] Add `release_year`
* [reddit] Prevent infinite loop
* [rokfin] Implement login by [P-reducible](https://github.com/P-reducible), [pukkandan](https://github.com/pukkandan)
* [ruutu] Support hs.fi embeds by [tpikonen](https://github.com/tpikonen), [pukkandan](https://github.com/pukkandan)
* [spotify] Detect iframe embeds by [fstirlitz](https://github.com/fstirlitz)
* [telegram] Fix metadata extraction
* [tmz, cleanup] Update tests by [diegorodriguezv](https://github.com/diegorodriguezv)
* [toggo] Fix `_VALID_URL` by [ca-za](https://github.com/ca-za)
* [trovo] Update to new API by [nyuszika7h](https://github.com/nyuszika7h)
* [TVer] Improve extraction by [Lesmiscore](https://github.com/Lesmiscore)
* [twitcasting] Pass headers for each formats by [Lesmiscore](https://github.com/Lesmiscore)
* [VideocampusSachsen] Improve extractor by [FestplattenSchnitzel](https://github.com/FestplattenSchnitzel)
* [vimeo] Fix extractors
* [wat] Fix extraction of multi-language videos and subtitles by [elyse0](https://github.com/elyse0)
* [wistia] Fix `_VALID_URL` by [dirkf](https://github.com/dirkf)
* [youtube, cleanup] Minor refactoring by [coletdjnz](https://github.com/coletdjnz), [pukkandan](https://github.com/pukkandan)
* [youtube] Added piped instance urls by [JordanWeatherby](https://github.com/JordanWeatherby)
* [youtube] Deprioritize auto-generated thumbnails
* [youtube] Deprioritize format 22 (often damaged)
* [youtube] Fix episode metadata extraction
* [zee5] Fix extractor by [Ashish0804](https://github.com/Ashish0804)
* [zingmp3, cleanup] Refactor extractors
### 2022.04.08
* Use certificates from `certifi` if installed by [coletdjnz](https://github.com/coletdjnz)
* Treat multiple `--match-filters` as OR
* File locking improvements:
* Do not lock downloading file on Windows
* Do not prevent download if locking is unsupported
* Do not truncate files before locking by [jakeogh](https://github.com/jakeogh), [pukkandan](https://github.com/pukkandan)
* Fix non-blocking non-exclusive lock
* De-prioritize automatic-subtitles when no `--sub-lang` is given
* Exit after `--dump-user-agent`
* Fallback to video-only format when selecting by extension
* Fix `--abort-on-error` for subtitles
* Fix `--no-overwrite` for playlist infojson
* Fix `--print` with `--ignore-no-formats` when url is `None` by [flashdagger](https://github.com/flashdagger)
* Fix `--sleep-interval`
* Fix `--throttled-rate`
* Fix `autonumber`
* Fix case of `http_headers`
* Fix filepath sanitization in `--print-to-file`
* Handle float in `--wait-for-video`
* Ignore `mhtml` formats from `-f mergeall`
* Ignore format-specific fields in initial pass of `--match-filter`
* Protect stdout from unexpected progress and console-title
* Remove `Accept-Encoding` header from `std_headers` by [coletdjnz](https://github.com/coletdjnz)
* Remove incorrect warning for `--dateafter`
* Show warning when all media formats have DRM
* [downloader] Fix invocation of `HttpieFD`
* [http] Fix #3215
* [http] Reject broken range before request by [Lesmiscore](https://github.com/Lesmiscore), [Jules-A](https://github.com/Jules-A), [pukkandan](https://github.com/pukkandan)
* [fragment] Read downloaded fragments only when needed by [Lesmiscore](https://github.com/Lesmiscore)
* [http] Retry on more errors by [coletdjnz](https://github.com/coletdjnz)
* [mhtml] Fix fragments with absolute urls by [coletdjnz](https://github.com/coletdjnz)
* [extractor] Add `_perform_login` function
* [extractor] Allow control characters inside json
* [extractor] Support merging subtitles with data by [coletdjnz](https://github.com/coletdjnz)
* [generic] Extract subtitles from video.js by [Lesmiscore](https://github.com/Lesmiscore)
* [ffmpeg] Cache version data
* [FFmpegConcat] Ensure final directory exists
* [FfmpegMetadata] Write id3v1 tags
* [FFmpegVideoConvertor] Add more formats to `--remux-video`
* [FFmpegVideoConvertor] Ensure all streams are copied
* [MetadataParser] Validate outtmpl early
* [outtmpl] Fix replacement/default when used with alternate
* [outtmpl] Limit changes during sanitization
* [phantomjs] Fix bug
* [test] Add `test_locked_file`
* [utils] `format_decimal_suffix`: Fix for very large numbers by [s0u1h](https://github.com/s0u1h)
* [utils] `traverse_obj`: Allow filtering by value
* [utils] Add `filter_dict`, `get_first`, `try_call`
* [utils] ExtractorError: Fix for older python versions
* [utils] WebSocketsWrapper: Allow omitting `__enter__` invocation by [Lesmiscore](https://github.com/Lesmiscore)
* [docs] Add an `.editorconfig` file by [fstirlitz](https://github.com/fstirlitz)
* [docs] Clarify the exact `BSD` license of dependencies by [MrRawes](https://github.com/MrRawes)
* [docs] Minor improvements by [pukkandan](https://github.com/pukkandan), [cffswb](https://github.com/cffswb), [danielyli](https://github.com/danielyli)
* [docs] Remove readthedocs
* [build] Add `requirements.txt` to pip distributions
* [cleanup, postprocessor] Create `_download_json`
* [cleanup, vimeo] Fix tests
* [cleanup] Misc fixes and minor cleanup
* [cleanup] Use `_html_extract_title`
* [AfreecaTV] Add `AfreecaTVUserIE` by [hatienl0i261299](https://github.com/hatienl0i261299)
* [arte] Add `format_note` to m3u8 formats
* [azmedien] Add TVO Online to supported hosts by [1-Byte](https://github.com/1-Byte)
* [BanBye] Add extractor by [mehq](https://github.com/mehq)
* [bilibili] Fix extraction of title with quotes by [dzek69](https://github.com/dzek69)
* [Craftsy] Add extractor by [Bricio](https://github.com/Bricio)
* [Cybrary] Add extractor by [aaearon](https://github.com/aaearon)
* [Huya] Add extractor by [hatienl0i261299](https://github.com/hatienl0i261299)
* [ITProTV] Add extractor by [aaearon](https://github.com/aaearon)
* [Jable] Add extractors by [mehq](https://github.com/mehq)
* [LastFM] Add extractors by [mehq](https://github.com/mehq)
* [Moviepilot] Add extractor by [panatexxa](https://github.com/panatexxa)
* [panopto] Add extractors by [coletdjnz](https://github.com/coletdjnz), [kmark](https://github.com/kmark)
* [PokemonSoundLibrary] Add extractor by [Lesmiscore](https://github.com/Lesmiscore)
* [WasdTV] Add extractor by [un-def](https://github.com/un-def), [hatienl0i261299](https://github.com/hatienl0i261299)
* [adobepass] Fix Suddenlink MSO by [CplPwnies](https://github.com/CplPwnies)
* [afreecatv] Match new vod url by [wlritchi](https://github.com/wlritchi)
* [AZMedien] Support `tv.telezueri.ch` by [goggle](https://github.com/goggle)
* [BiliIntl] Support user-generated videos by [wlritchi](https://github.com/wlritchi)
* [BRMediathek] Fix VALID_URL
* [crunchyroll:playlist] Implement beta API by [tejing1](https://github.com/tejing1)
* [crunchyroll] Fix inheritance
* [daftsex] Fix extractor by [Soebb](https://github.com/Soebb)
* [dailymotion] Support `geo.dailymotion.com` by [hatienl0i261299](https://github.com/hatienl0i261299)
* [ellentube] Extract subtitles from manifest
* [elonet] Rewrite extractor by [Fam0r](https://github.com/Fam0r), [pukkandan](https://github.com/pukkandan)
* [fptplay] Fix metadata extraction by [hatienl0i261299](https://github.com/hatienl0i261299)
* [FranceCulture] Support playlists by [bohwaz](https://github.com/bohwaz)
* [go, viu] Extract subtitles from the m3u8 manifest by [fstirlitz](https://github.com/fstirlitz)
* [Imdb] Improve extractor by [hatienl0i261299](https://github.com/hatienl0i261299)
* [MangoTV] Improve extractor by [hatienl0i261299](https://github.com/hatienl0i261299)
* [Nebula] Fix bug in 52efa4b31200119adaa8acf33e50b84fcb6948f0
* [niconico] Fix extraction of thumbnails and uploader (#3266)
* [niconico] Rewrite NiconicoIE by [Lesmiscore](https://github.com/Lesmiscore)
* [nitter] Minor fixes and update instance list by [foghawk](https://github.com/foghawk)
* [NRK] Extract timestamp by [hatienl0i261299](https://github.com/hatienl0i261299)
* [openrec] Download archived livestreams by [Lesmiscore](https://github.com/Lesmiscore)
* [openrec] Refactor extractors by [Lesmiscore](https://github.com/Lesmiscore)
* [panopto] Improve subtitle extraction and support slides by [coletdjnz](https://github.com/coletdjnz)
* [ParamountPlus, CBS] Change VALID_URL by [Sipherdrakon](https://github.com/Sipherdrakon)
* [ParamountPlusSeries] Support multiple pages by [dodrian](https://github.com/dodrian)
* [Piapro] Extract description with break lines by [Lesmiscore](https://github.com/Lesmiscore)
* [rai] Fix extraction of http formas by [nixxo](https://github.com/nixxo)
* [rumble] unescape title
* [RUTV] Fix format sorting by [Lesmiscore](https://github.com/Lesmiscore)
* [ruutu] Detect embeds by [tpikonen](https://github.com/tpikonen)
* [tenplay] Improve extractor by [aarubui](https://github.com/aarubui)
* [TikTok] Fix URLs with user id by [hatienl0i261299](https://github.com/hatienl0i261299)
* [TikTokVM] Fix redirect to user URL
* [TVer] Fix extractor by [Lesmiscore](https://github.com/Lesmiscore)
* [TVer] Support landing page by [vvto33](https://github.com/vvto33)
* [twitcasting] Don't return multi_video for archive with single hls manifest by [Lesmiscore](https://github.com/Lesmiscore)
* [veo] Fix `_VALID_URL`
* [Veo] Fix extractor by [i6t](https://github.com/i6t)
* [viki] Don't attempt to modify URLs with signature by [nyuszika7h](https://github.com/nyuszika7h)
* [viu] Fix bypass for preview by [zackmark29](https://github.com/zackmark29)
* [viu] Fixed extractor by [zackmark29](https://github.com/zackmark29), [pukkandan](https://github.com/pukkandan)
* [web.archive:youtube] Make CDX API requests non-fatal by [coletdjnz](https://github.com/coletdjnz)
* [wget] Fix proxy by [kikuyan](https://github.com/kikuyan), [coletdjnz](https://github.com/coletdjnz)
* [xnxx] Add `xnxx3.com` by [rozari0](https://github.com/rozari0)
* [youtube] **Add new age-gate bypass** by [zerodytrash](https://github.com/zerodytrash), [pukkandan](https://github.com/pukkandan)
* [youtube] Add extractor-arg to skip auto-translated subs
* [youtube] Avoid false positives when detecting damaged formats
* [youtube] Detect DRM better by [shirt](https://github.com/shirt-dev)
* [youtube] Fix auto-translated automatic captions
* [youtube] Fix pagination of `membership` tab
* [youtube] Fix uploader for collaborative playlists by [coletdjnz](https://github.com/coletdjnz)
* [youtube] Improve video upload date handling by [coletdjnz](https://github.com/coletdjnz)
* [youtube:api] Prefer minified JSON response by [coletdjnz](https://github.com/coletdjnz)
* [youtube:search] Support hashtag entries by [coletdjnz](https://github.com/coletdjnz)
* [youtube:tab] Fix duration extraction for shorts by [coletdjnz](https://github.com/coletdjnz)
* [youtube:tab] Minor improvements
* [youtube:tab] Return shorts url if video is a short by [coletdjnz](https://github.com/coletdjnz)
* [Zattoo] Fix extractors by [goggle](https://github.com/goggle)
* [Zingmp3] Fix signature by [hatienl0i261299](https://github.com/hatienl0i261299)
### 2022.03.08.1
* [cleanup] Refactor `__init__.py`
@@ -34,7 +290,7 @@
* Set `webpage_url_...` from `webpage_url` and not input URL
* Tolerate failure to `--write-link` due to unknown URL
* [aria2c] Add `--http-accept-gzip=true`
* [build] Update pyinstaller to 4.10 by [shirt-dev](https://github.com/shirt-dev)
* [build] Update pyinstaller to 4.10 by [shirt](https://github.com/shirt-dev)
* [cookies] Update MacOS12 `Cookies.binarycookies` location by [mdpauley](https://github.com/mdpauley)
* [devscripts] Improve `prepare_manpage`
* [downloader] Do not use aria2c for non-native `m3u8`
@@ -647,7 +903,7 @@
* [build] Improvements
* Build standalone MacOS packages by [smplayer-dev](https://github.com/smplayer-dev)
* Release windows exe built with `py2exe`
* Enable lazy-extractors in releases.
* Enable lazy-extractors in releases
* Set env var `YTDLP_NO_LAZY_EXTRACTORS` to forcefully disable this (experimental)
* Clean up error reporting in update
* Refactor `pyinst.py`, misc cleanup and improve docs
@@ -1255,7 +1511,7 @@
* [youtube] Non-fatal alert reporting for unavailable videos page by [coletdjnz](https://github.com/coletdjnz)
* [twitcasting] Websocket support by [nao20010128nao](https://github.com/nao20010128nao)
* [mediasite] Extract slides by [fstirlitz](https://github.com/fstirlitz)
* [funimation] Extract subtitles
* [funimation] Extract subtitles
* [pornhub] Extract `cast`
* [hotstar] Use server time for authentication instead of local time
* [EmbedThumbnail] Fix for already downloaded thumbnail
@@ -1351,7 +1607,7 @@
### 2021.05.20
* **Youtube improvements**:
* **Youtube improvements**:
* Support youtube music `MP`, `VL` and `browse` pages
* Extract more formats for youtube music by [craftingmod](https://github.com/craftingmod), [coletdjnz](https://github.com/coletdjnz) and [pukkandan](https://github.com/pukkandan)
* Extract multiple subtitles in same language by [pukkandan](https://github.com/pukkandan) and [tpikonen](https://github.com/tpikonen)
@@ -1893,7 +2149,7 @@
* **Format Sort:** Added `--format-sort` (`-S`), `--format-sort-force` (`--S-force`) - See [Sorting Formats](README.md#sorting-formats) for details
* **Format Selection:** See [Format Selection](README.md#format-selection) for details
* New format selectors: `best*`, `worst*`, `bestvideo*`, `bestaudio*`, `worstvideo*`, `worstaudio*`
* Changed video format sorting to show video only files and video+audio files together.
* Changed video format sorting to show video only files and video+audio files together
* Added `--video-multistreams`, `--no-video-multistreams`, `--audio-multistreams`, `--no-audio-multistreams`
* Added `b`,`w`,`v`,`a` as alias for `best`, `worst`, `video` and `audio` respectively
* Shortcut Options: Added `--write-link`, `--write-url-link`, `--write-webloc-link`, `--write-desktop-link` by [h-h-h-h](https://github.com/h-h-h-h) - See [Internet Shortcut Options](README.md#internet-shortcut-options) for details

View File

@@ -29,6 +29,7 @@ You can also find lists of all [contributors of yt-dlp](CONTRIBUTORS) and [autho
* YouTube improvements including: age-gate bypass, private playlists, multiple-clients (to avoid throttling) and a lot of under-the-hood improvements
* Added support for downloading YoutubeWebArchive videos
* Added support for new websites MainStreaming, PRX, nzherald, etc

View File

@@ -5,5 +5,6 @@ include README.md
include completions/*/*
include supportedsites.md
include yt-dlp.1
include requirements.txt
recursive-include devscripts *
recursive-include test *

View File

@@ -22,7 +22,9 @@ clean-dist:
rm -rf yt-dlp.1.temp.md yt-dlp.1 README.txt MANIFEST build/ dist/ .coverage cover/ yt-dlp.tar.gz completions/ \
yt_dlp/extractor/lazy_extractors.py *.spec CONTRIBUTING.md.tmp yt-dlp yt-dlp.exe yt_dlp.egg-info/ AUTHORS .mailmap
clean-cache:
find . \( -name "*.pyc" -o -name "*.class" \) -delete
find . \( \
-type d -name .pytest_cache -o -type d -name __pycache__ -o -name "*.pyc" -o -name "*.class" \
\) -prune -exec rm -rf {} \;
completion-bash: completions/bash/yt-dlp
completion-fish: completions/fish/yt-dlp.fish
@@ -40,14 +42,26 @@ PYTHON ?= /usr/bin/env python3
SYSCONFDIR = $(shell if [ $(PREFIX) = /usr -o $(PREFIX) = /usr/local ]; then echo /etc; else echo $(PREFIX)/etc; fi)
# set markdown input format to "markdown-smart" for pandoc version 2 and to "markdown" for pandoc prior to version 2
MARKDOWN = $(shell if [ `pandoc -v | head -n1 | cut -d" " -f2 | head -c1` = "2" ]; then echo markdown-smart; else echo markdown; fi)
MARKDOWN = $(shell if [ "$(pandoc -v | head -n1 | cut -d" " -f2 | head -c1)" = "2" ]; then echo markdown-smart; else echo markdown; fi)
install: lazy-extractors yt-dlp yt-dlp.1 completions
install -Dm755 yt-dlp $(DESTDIR)$(BINDIR)/yt-dlp
install -Dm644 yt-dlp.1 $(DESTDIR)$(MANDIR)/man1/yt-dlp.1
install -Dm644 completions/bash/yt-dlp $(DESTDIR)$(SHAREDIR)/bash-completion/completions/yt-dlp
install -Dm644 completions/zsh/_yt-dlp $(DESTDIR)$(SHAREDIR)/zsh/site-functions/_yt-dlp
install -Dm644 completions/fish/yt-dlp.fish $(DESTDIR)$(SHAREDIR)/fish/vendor_completions.d/yt-dlp.fish
mkdir -p $(DESTDIR)$(BINDIR)
install -m755 yt-dlp $(DESTDIR)$(BINDIR)/yt-dlp
mkdir -p $(DESTDIR)$(MANDIR)/man1
install -m644 yt-dlp.1 $(DESTDIR)$(MANDIR)/man1/yt-dlp.1
mkdir -p $(DESTDIR)$(SHAREDIR)/bash-completion/completions
install -m644 completions/bash/yt-dlp $(DESTDIR)$(SHAREDIR)/bash-completion/completions/yt-dlp
mkdir -p $(DESTDIR)$(SHAREDIR)/zsh/site-functions
install -m644 completions/zsh/_yt-dlp $(DESTDIR)$(SHAREDIR)/zsh/site-functions/_yt-dlp
mkdir -p $(DESTDIR)$(SHAREDIR)/fish/vendor_completions.d
install -m644 completions/fish/yt-dlp.fish $(DESTDIR)$(SHAREDIR)/fish/vendor_completions.d/yt-dlp.fish
uninstall:
rm -f $(DESTDIR)$(BINDIR)/yt-dlp
rm -f $(DESTDIR)$(MANDIR)/man1/yt-dlp.1
rm -f $(DESTDIR)$(SHAREDIR)/bash-completion/completions/yt-dlp
rm -f $(DESTDIR)$(SHAREDIR)/zsh/site-functions/_yt-dlp
rm -f $(DESTDIR)$(SHAREDIR)/fish/vendor_completions.d/yt-dlp.fish
codetest:
flake8 .
@@ -59,15 +73,18 @@ test:
offlinetest: codetest
$(PYTHON) -m pytest -k "not download"
# XXX: This is hard to maintain
CODE_FOLDERS = yt_dlp yt_dlp/downloader yt_dlp/extractor yt_dlp/postprocessor yt_dlp/compat \
yt_dlp/extractor/anvato_token_generator
yt-dlp: yt_dlp/*.py yt_dlp/*/*.py
mkdir -p zip
for d in yt_dlp yt_dlp/downloader yt_dlp/extractor yt_dlp/postprocessor ; do \
for d in $(CODE_FOLDERS) ; do \
mkdir -p zip/$$d ;\
cp -pPR $$d/*.py zip/$$d/ ;\
done
touch -t 200001010101 zip/yt_dlp/*.py zip/yt_dlp/*/*.py
touch -t 200001010101 zip/yt_dlp/*.py zip/yt_dlp/*/*.py zip/yt_dlp/*/*/*.py
mv zip/yt_dlp/__main__.py zip/
cd zip ; zip -q ../yt-dlp yt_dlp/*.py yt_dlp/*/*.py __main__.py
cd zip ; zip -q ../yt-dlp yt_dlp/*.py yt_dlp/*/*.py yt_dlp/*/*/*.py __main__.py
rm -rf zip
echo '#!$(PYTHON)' > yt-dlp
cat yt-dlp.zip >> yt-dlp
@@ -123,6 +140,7 @@ yt-dlp.tar.gz: all
--exclude '*.pyo' \
--exclude '*~' \
--exclude '__pycache__' \
--exclude '.pytest_cache' \
--exclude '.git' \
-- \
README.md supportedsites.md Changelog.md LICENSE \

346
README.md
View File

@@ -3,15 +3,14 @@
[![YT-DLP](https://raw.githubusercontent.com/yt-dlp/yt-dlp/master/.github/banner.svg)](#readme)
[![Release version](https://img.shields.io/github/v/release/yt-dlp/yt-dlp?color=blue&label=Download&style=for-the-badge)](#release-files "Release")
[![License: Unlicense](https://img.shields.io/badge/-Unlicense-brightgreen.svg?style=for-the-badge)](LICENSE "License")
[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)](Collaborators.md#collaborators "Donate")
[![Docs](https://img.shields.io/badge/-Docs-blue.svg?color=blue&style=for-the-badge)](https://readthedocs.org/projects/yt-dlp/ "Docs")
[![Supported Sites](https://img.shields.io/badge/-Supported_Sites-brightgreen.svg?style=for-the-badge)](supportedsites.md "Supported Sites")
[![Release version](https://img.shields.io/github/v/release/yt-dlp/yt-dlp?color=brightgreen&label=Download&style=for-the-badge)](#release-files "Release")
[![PyPi](https://img.shields.io/badge/-PyPi-blue.svg?logo=pypi&labelColor=555555&style=for-the-badge)](https://pypi.org/project/yt-dlp "PyPi")
[![CI Status](https://img.shields.io/github/workflow/status/yt-dlp/yt-dlp/Core%20Tests/master?label=Tests&style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/actions "CI Status")
[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)](https://discord.gg/H5MNcFW63r "Discord")
[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)](Collaborators.md#collaborators "Donate")
[![Matrix](https://img.shields.io/matrix/yt-dlp:matrix.org?color=brightgreen&labelColor=555555&label=&logo=element&style=for-the-badge)](https://matrix.to/#/#yt-dlp:matrix.org "Matrix")
[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)](https://discord.gg/H5MNcFW63r "Discord")
[![Supported Sites](https://img.shields.io/badge/-Supported_Sites-brightgreen.svg?style=for-the-badge)](supportedsites.md "Supported Sites")
[![License: Unlicense](https://img.shields.io/badge/-Unlicense-blue.svg?style=for-the-badge)](LICENSE "License")
[![CI Status](https://img.shields.io/github/workflow/status/yt-dlp/yt-dlp/Core%20Tests/master?label=Tests&style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/actions "CI Status")
[![Commits](https://img.shields.io/github/commit-activity/m/yt-dlp/yt-dlp?label=commits&style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/commits "Commit History")
[![Last Commit](https://img.shields.io/github/last-commit/yt-dlp/yt-dlp/master?label=&style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/commits "Commit History")
@@ -61,6 +60,7 @@ yt-dlp is a [youtube-dl](https://github.com/ytdl-org/youtube-dl) fork based on t
* [EXTRACTOR ARGUMENTS](#extractor-arguments)
* [PLUGINS](#plugins)
* [EMBEDDING YT-DLP](#embedding-yt-dlp)
* [Embedding examples](#embedding-examples)
* [DEPRECATED OPTIONS](#deprecated-options)
* [CONTRIBUTING](CONTRIBUTING.md#contributing-to-yt-dlp)
* [Opening an Issue](CONTRIBUTING.md#opening-an-issue)
@@ -71,16 +71,16 @@ yt-dlp is a [youtube-dl](https://github.com/ytdl-org/youtube-dl) fork based on t
# NEW FEATURES
* Based on **youtube-dl 2021.12.17 [commit/5add3f4](https://github.com/ytdl-org/youtube-dl/commit/5add3f4373287e6346ca3551239edab549284db3)** and **youtube-dlc 2020.11.11-3 [commit/f9401f2](https://github.com/blackjack4494/yt-dlc/commit/f9401f2a91987068139c5f757b12fc711d4c0cee)**: You get all the features and patches of [youtube-dlc](https://github.com/blackjack4494/yt-dlc) in addition to the latest [youtube-dl](https://github.com/ytdl-org/youtube-dl)
* Based on **youtube-dl 2021.12.17 [commit/6508688](https://github.com/ytdl-org/youtube-dl/commit/6508688e88c83bb811653083db9351702cd39a6a)** ([exceptions](https://github.com/yt-dlp/yt-dlp/issues/21)) and **youtube-dlc 2020.11.11-3 [commit/f9401f2](https://github.com/blackjack4494/yt-dlc/commit/f9401f2a91987068139c5f757b12fc711d4c0cee)**: You get all the features and patches of [youtube-dlc](https://github.com/blackjack4494/yt-dlc) in addition to the latest [youtube-dl](https://github.com/ytdl-org/youtube-dl)
* **[SponsorBlock Integration](#sponsorblock-options)**: You can mark/remove sponsor sections in youtube videos by utilizing the [SponsorBlock](https://sponsor.ajay.app) API
* **[Format Sorting](#sorting-formats)**: The default format sorting options have been changed so that higher resolution and better codecs will be now preferred instead of simply using larger bitrate. Furthermore, you can now specify the sort order using `-S`. This allows for much easier format selection than what is possible by simply using `--format` ([examples](#format-selection-examples))
* **Merged with animelover1984/youtube-dl**: You get most of the features and improvements from [animelover1984/youtube-dl](https://github.com/animelover1984/youtube-dl) including `--write-comments`, `BiliBiliSearch`, `BilibiliChannel`, Embedding thumbnail in mp4/ogg/opus, playlist infojson etc. Note that the NicoNico improvements are not available. See [#31](https://github.com/yt-dlp/yt-dlp/pull/31) for details.
* **Merged with animelover1984/youtube-dl**: You get most of the features and improvements from [animelover1984/youtube-dl](https://github.com/animelover1984/youtube-dl) including `--write-comments`, `BiliBiliSearch`, `BilibiliChannel`, Embedding thumbnail in mp4/ogg/opus, playlist infojson etc. Note that the NicoNico livestreams are not available. See [#31](https://github.com/yt-dlp/yt-dlp/pull/31) for details.
* **Youtube improvements**:
* All Feeds (`:ytfav`, `:ytwatchlater`, `:ytsubs`, `:ythistory`, `:ytrec`) and private playlists supports downloading multiple pages of content
* All Feeds (`:ytfav`, `:ytwatchlater`, `:ytsubs`, `:ythistory`, `:ytrec`, `:ytnotif`) and private playlists supports downloading multiple pages of content
* Search (`ytsearch:`, `ytsearchdate:`), search URLs and in-channel search works
* Mixes supports downloading multiple pages of content
* Some (but not all) age-gated content can be downloaded without cookies
@@ -89,6 +89,7 @@ yt-dlp is a [youtube-dl](https://github.com/ytdl-org/youtube-dl) fork based on t
* `255kbps` audio is extracted (if available) from youtube music when premium cookies are given
* Youtube music Albums, channels etc can be downloaded ([except self-uploaded music](https://github.com/yt-dlp/yt-dlp/issues/723))
* Download livestreams from the start using `--live-from-start` (experimental)
* Support for downloading stories (`ytstories:<channel UCID>`)
* **Cookies from browser**: Cookies can be automatically extracted from all major web browsers using `--cookies-from-browser BROWSER[+KEYRING][:PROFILE]`
@@ -126,6 +127,7 @@ Some of yt-dlp's default options are different from that of youtube-dl and youtu
* The options `--auto-number` (`-A`), `--title` (`-t`) and `--literal` (`-l`), no longer work. See [removed options](#Removed) for details
* `avconv` is not supported as an alternative to `ffmpeg`
* yt-dlp stores config files in slightly different locations to youtube-dl. See [configuration](#configuration) for a list of correct locations
* The default [output template](#output-template) is `%(title)s [%(id)s].%(ext)s`. There is no real reason for this change. This was changed before yt-dlp was ever made public and now there are no plans to change it back to `%(title)s-%(id)s.%(ext)s`. Instead, you may use `--compat-options filename`
* The default [format sorting](#sorting-formats) is different from youtube-dl and prefers higher resolution and better codecs rather than higher bitrates. You can use the `--format-sort` option to change this to any order you prefer, or use `--compat-options format-sort` to use youtube-dl's sorting order
* The default format selector is `bv*+ba/b`. This means that if a combined video + audio format that is better than the best video-only format is found, the former will be preferred. Use `-f bv+ba/b` or `--compat-options format-spec` to revert this
@@ -144,8 +146,11 @@ Some of yt-dlp's default options are different from that of youtube-dl and youtu
* Thumbnail embedding in `mp4` is done with mutagen if possible. Use `--compat-options embed-thumbnail-atomicparsley` to force the use of AtomicParsley instead
* Some private fields such as filenames are removed by default from the infojson. Use `--no-clean-infojson` or `--compat-options no-clean-infojson` to revert this
* When `--embed-subs` and `--write-subs` are used together, the subtitles are written to disk and also embedded in the media file. You can use just `--embed-subs` to embed the subs and automatically delete the separate file. See [#630 (comment)](https://github.com/yt-dlp/yt-dlp/issues/630#issuecomment-893659460) for more info. `--compat-options no-keep-subs` can be used to revert this
* `certifi` will be used for SSL root certificates, if installed. If you want to use system certificates (e.g. self-signed), use `--compat-options no-certifi`
* youtube-dl tries to remove some superfluous punctuations from filenames. While this can sometimes be helpfull, it is often undesirable. So yt-dlp tries to keep the fields in the filenames as close to their original values as possible. You can use `--compat-options filename-sanitization` to revert to youtube-dl's behavior
For ease of use, a few more compat options are available:
* `--compat-options all`: Use all compat options
* `--compat-options youtube-dl`: Same as `--compat-options all,-multistreams`
* `--compat-options youtube-dlc`: Same as `--compat-options all,-no-live-chat,-no-youtube-channel-redirect`
@@ -161,10 +166,11 @@ You can simply download the [correct binary file](#release-files) for your OS
<!-- MANPAGE: BEGIN EXCLUDED SECTION -->
[![Windows](https://img.shields.io/badge/-Windows_x64-blue.svg?style=for-the-badge&logo=windows)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe)
[![Linux](https://img.shields.io/badge/-Linux/MacOS/BSD-red.svg?style=for-the-badge&logo=linux)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp)
[![Linux](https://img.shields.io/badge/-Linux/BSD-red.svg?style=for-the-badge&logo=linux)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp)
[![MacOS](https://img.shields.io/badge/-MacOS-lightblue.svg?style=for-the-badge&logo=apple)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos)
[![Source Tarball](https://img.shields.io/badge/-Source_tar-green.svg?style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.tar.gz)
[![Other variants](https://img.shields.io/badge/-Other-grey.svg?style=for-the-badge)](#release-files)
[![ALl versions](https://img.shields.io/badge/-All_Versions-lightgrey.svg?style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/releases)
[![All versions](https://img.shields.io/badge/-All_Versions-lightgrey.svg?style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/releases)
<!-- MANPAGE: END EXCLUDED SECTION -->
Note: The manpages, shell completion files etc. are available in the [source tarball](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.tar.gz)
@@ -202,7 +208,7 @@ python3 -m pip install --no-deps -U yt-dlp
If you want to be on the cutting edge, you can also install the master branch with:
```
python3 -m pip install --force-reinstall https://github.com/yt-dlp/yt-dlp/archive/master.zip
python3 -m pip install --force-reinstall https://github.com/yt-dlp/yt-dlp/archive/master.tar.gz
```
Note that on some systems, you may need to use `py` or `python` instead of `python3`
@@ -230,16 +236,16 @@ If you [installed using Homebrew](#with-homebrew), run `brew upgrade yt-dlp/taps
File|Description
:---|:---
[yt-dlp](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp)|Platform-independant binary. Needs Python (recommended for **UNIX-like systems**)
[yt-dlp](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp)|Platform-independant binary. Needs Python (recommended for **Linux/BSD**)
[yt-dlp.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe)|Windows (Win7 SP1+) standalone x64 binary (recommended for **Windows**)
[yt-dlp_macos](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos)|MacOS (10.15+) standalone executable (recommended for **MacOS**)
#### Alternatives
File|Description
:---|:---
[yt-dlp_macos](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos)|MacOS (10.15+) standalone executable
[yt-dlp_x86.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_x86.exe)|Windows (Vista SP2+) standalone x86 (32-bit) binary
[yt-dlp_min.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_min.exe)|Windows (Win7 SP1+) standalone x64 binary built with `py2exe`.<br/> Does not contain `pycryptodomex`, needs VC++14
[yt-dlp_min.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_min.exe)|Windows (Win7 SP1+) standalone x64 binary built with `py2exe`.<br/> Does not contain `pycryptodomex`/`certifi`, needs VC++14
[yt-dlp_win.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_win.zip)|Unpackaged Windows executable (no auto-update)
[yt-dlp_macos.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos.zip)|Unpackaged MacOS (10.15+) executable (no auto-update)
@@ -263,21 +269,23 @@ On windows, [Microsoft Visual C++ 2010 SP1 Redistributable Package (x86)](https:
While all the other dependencies are optional, `ffmpeg` and `ffprobe` are highly recommended
* [**ffmpeg** and **ffprobe**](https://www.ffmpeg.org) - Required for [merging separate video and audio files](#format-selection) as well as for various [post-processing](#post-processing-options) tasks. License [depends on the build](https://www.ffmpeg.org/legal.html)
* [**mutagen**](https://github.com/quodlibet/mutagen) - For embedding thumbnail in certain formats. Licensed under [GPLv2+](https://github.com/quodlibet/mutagen/blob/master/COPYING)
* [**pycryptodomex**](https://github.com/Legrandin/pycryptodome) - For decrypting AES-128 HLS streams and various other data. Licensed under [BSD2](https://github.com/Legrandin/pycryptodome/blob/master/LICENSE.rst)
* [**websockets**](https://github.com/aaugustin/websockets) - For downloading over websocket. Licensed under [BSD3](https://github.com/aaugustin/websockets/blob/main/LICENSE)
* [**secretstorage**](https://github.com/mitya57/secretstorage) - For accessing the Gnome keyring while decrypting cookies of Chromium-based browsers on Linux. Licensed under [BSD](https://github.com/mitya57/secretstorage/blob/master/LICENSE)
* [**mutagen**](https://github.com/quodlibet/mutagen)\* - For embedding thumbnail in certain formats. Licensed under [GPLv2+](https://github.com/quodlibet/mutagen/blob/master/COPYING)
* [**pycryptodomex**](https://github.com/Legrandin/pycryptodome)\* - For decrypting AES-128 HLS streams and various other data. Licensed under [BSD-2-Clause](https://github.com/Legrandin/pycryptodome/blob/master/LICENSE.rst)
* [**websockets**](https://github.com/aaugustin/websockets)\* - For downloading over websocket. Licensed under [BSD-3-Clause](https://github.com/aaugustin/websockets/blob/main/LICENSE)
* [**secretstorage**](https://github.com/mitya57/secretstorage) - For accessing the Gnome keyring while decrypting cookies of Chromium-based browsers on Linux. Licensed under [BSD-3-Clause](https://github.com/mitya57/secretstorage/blob/master/LICENSE)
* [**brotli**](https://github.com/google/brotli)\* or [**brotlicffi**](https://github.com/python-hyper/brotlicffi) - [Brotli](https://en.wikipedia.org/wiki/Brotli) content encoding support. Both licensed under MIT <sup>[1](https://github.com/google/brotli/blob/master/LICENSE) [2](https://github.com/python-hyper/brotlicffi/blob/master/LICENSE) </sup>
* [**certifi**](https://github.com/certifi/python-certifi)\* - Provides Mozilla's root certificate bundle. Licensed under [MPLv2](https://github.com/certifi/python-certifi/blob/master/LICENSE)
* [**xattr**](https://github.com/xattr/xattr), [**pyxattr**](https://github.com/iustin/pyxattr) or [**setfattr**](http://savannah.nongnu.org/projects/attr) - For writing xattr metadata on Linux. Licensed under [MIT](https://github.com/xattr/xattr/blob/master/LICENSE.txt), [LGPL2.1](https://github.com/iustin/pyxattr/blob/master/COPYING) and [GPLv2+](http://git.savannah.nongnu.org/cgit/attr.git/tree/doc/COPYING) respectively
* [**AtomicParsley**](https://github.com/wez/atomicparsley) - For embedding thumbnail in mp4/m4a if mutagen/ffmpeg cannot. Licensed under [GPLv2+](https://github.com/wez/atomicparsley/blob/master/COPYING)
* [**brotli**](https://github.com/google/brotli) or [**brotlicffi**](https://github.com/python-hyper/brotlicffi) - [Brotli](https://en.wikipedia.org/wiki/Brotli) content encoding support. Both licensed under MIT <sup>[1](https://github.com/google/brotli/blob/master/LICENSE) [2](https://github.com/python-hyper/brotlicffi/blob/master/LICENSE) </sup>
* [**rtmpdump**](http://rtmpdump.mplayerhq.hu) - For downloading `rtmp` streams. ffmpeg will be used as a fallback. Licensed under [GPLv2+](http://rtmpdump.mplayerhq.hu)
* [**mplayer**](http://mplayerhq.hu/design7/info.html) or [**mpv**](https://mpv.io) - For downloading `rstp` streams. ffmpeg will be used as a fallback. Licensed under [GPLv2+](https://github.com/mpv-player/mpv/blob/master/Copyright)
* [**phantomjs**](https://github.com/ariya/phantomjs) - Used in extractors where javascript needs to be run. Licensed under [BSD3](https://github.com/ariya/phantomjs/blob/master/LICENSE.BSD)
* [**phantomjs**](https://github.com/ariya/phantomjs) - Used in extractors where javascript needs to be run. Licensed under [BSD-3-Clause](https://github.com/ariya/phantomjs/blob/master/LICENSE.BSD)
* [**sponskrub**](https://github.com/faissaloo/SponSkrub) - For using the now **deprecated** [sponskrub options](#sponskrub-options). Licensed under [GPLv3+](https://github.com/faissaloo/SponSkrub/blob/master/LICENCE.md)
* Any external downloader that you want to use with `--downloader`
To use or redistribute the dependencies, you must agree to their respective licensing terms.
The Windows and MacOS standalone release binaries are already built with the python interpreter, mutagen, pycryptodomex and websockets included.
The Windows and MacOS standalone release binaries are built with the Python interpreter and the packages marked with \* included.
<!-- TODO: ffmpeg has merged this patch. Remove this note once there is new release -->
**Note**: There are some regressions in newer ffmpeg versions that causes various issues when used alongside yt-dlp. Since ffmpeg is such an important dependency, we provide [custom builds](https://github.com/yt-dlp/FFmpeg-Builds#ffmpeg-static-auto-builds) with patches for these issues at [yt-dlp/FFmpeg-Builds](https://github.com/yt-dlp/FFmpeg-Builds). See [the readme](https://github.com/yt-dlp/FFmpeg-Builds#patches-applied) for details on the specific issues solved by these builds
@@ -314,9 +322,7 @@ You can also fork the project on github and run your fork's [build workflow](.gi
## General Options:
-h, --help Print this help text and exit
--version Print program version and exit
-U, --update Update this program to latest version. Make
sure that you have sufficient permissions
(run with sudo if needed)
-U, --update Update this program to latest version
-i, --ignore-errors Ignore download and postprocessing errors.
The download will be considered successful
even if the postprocessing fails
@@ -367,8 +373,7 @@ You can also fork the project on github and run your fork's [build workflow](.gi
available. Pass the minimum number of
seconds (or range) to wait between retries
--no-wait-for-video Do not wait for scheduled streams (default)
--mark-watched Mark videos watched (even with --simulate).
Currently only supported for YouTube
--mark-watched Mark videos watched (even with --simulate)
--no-mark-watched Do not mark videos watched (default)
--no-colors Do not emit color codes in output
--compat-options OPTS Options that can help keep compatibility
@@ -423,31 +428,34 @@ You can also fork the project on github and run your fork's [build workflow](.gi
(e.g. 50k or 44.6m)
--date DATE Download only videos uploaded on this date.
The date can be "YYYYMMDD" or in the format
"(now|today)[+-][0-9](day|week|month|year)(s)?"
[now|today|yesterday][-N[day|week|month|year]].
Eg: --date today-2weeks
--datebefore DATE Download only videos uploaded on or before
this date. The date formats accepted is the
same as --date
--dateafter DATE Download only videos uploaded on or after
this date. The date formats accepted is the
same as --date
--match-filter FILTER Generic video filter. Any field (see
--match-filters FILTER Generic video filter. Any field (see
"OUTPUT TEMPLATE") can be compared with a
number or a string using the operators
defined in "Filtering formats". You can
also simply specify a field to match if the
field is present and "!field" to check if
the field is not present. In addition,
Python style regular expression matching
can be done using "~=", and multiple
filters can be checked with "&". Use a "\"
to escape "&" or quotes if needed. Eg:
--match-filter "!is_live & like_count>?100
& description~='(?i)\bcats \& dogs\b'"
matches only videos that are not live, has
a like count more than 100 (or the like
field is not available), and also has a
description that contains the phrase "cats
& dogs" (ignoring case)
field is present, use "!field" to check if
the field is not present, and "&" to check
multiple conditions. Use a "\" to escape
"&" or quotes if needed. If used multiple
times, the filter matches if atleast one of
the conditions are met. Eg: --match-filter
!is_live --match-filter "like_count>?100 &
description~='(?i)\bcats \& dogs\b'"
matches only videos that are not live OR
those that have a like count more than 100
(or the like field is not available) and
also has a description that contains the
phrase "cats & dogs" (ignoring case). Use
"--match-filter -" to interactively ask
whether to download each video
--no-match-filter Do not use generic video filter (default)
--no-playlist Download only the video, if the URL refers
to a video and a playlist
@@ -464,9 +472,10 @@ You can also fork the project on github and run your fork's [build workflow](.gi
a file that is in the archive
--break-on-reject Stop the download process when encountering
a file that has been filtered out
--break-per-input Make --break-on-existing and --break-on-
reject act only on the current input URL
--no-break-per-input --break-on-existing and --break-on-reject
--break-per-input Make --break-on-existing, --break-on-reject
and --max-downloads act only on the current
input URL
--no-break-per-input --break-on-existing and similar options
terminates the entire download queue
--skip-playlist-after-errors N Number of allowed failures until the rest
of the playlist is skipped
@@ -483,7 +492,7 @@ You can also fork the project on github and run your fork's [build workflow](.gi
-R, --retries RETRIES Number of retries (default is 10), or
"infinite"
--file-access-retries RETRIES Number of times to retry on file access
error (default is 10), or "infinite"
error (default is 3), or "infinite"
--fragment-retries RETRIES Number of retries for a fragment (default
is 10), or "infinite" (DASH, hlsnative and
ISM)
@@ -527,10 +536,10 @@ You can also fork the project on github and run your fork's [build workflow](.gi
(http, ftp, m3u8, dash, rstp, rtmp, mms) to
use it for. Currently supports native,
aria2c, avconv, axel, curl, ffmpeg, httpie,
wget (Recommended: aria2c). You can use
this option multiple times to set different
downloaders for different protocols. For
example, --downloader aria2c --downloader
wget. You can use this option multiple
times to set different downloaders for
different protocols. For example,
--downloader aria2c --downloader
"dash,m3u8:native" will use aria2c for
http/ftp downloads, and the native
downloader for dash/m3u8 downloads (Alias:
@@ -686,9 +695,10 @@ You can also fork the project on github and run your fork's [build workflow](.gi
print it, separated by a ":". Supported
values of "WHEN" are the same as that of
--use-postprocessor, and "video" (default).
Implies --quiet and --simulate (unless
--no-simulate is used). This option can be
used multiple times
Implies --quiet. Implies --simulate unless
--no-simulate or later stages of WHEN are
used. This option can be used multiple
times
--print-to-file [WHEN:]TEMPLATE FILE
Append given template to the file. The
values of WHEN and TEMPLATE are same as
@@ -781,8 +791,8 @@ You can also fork the project on github and run your fork's [build workflow](.gi
containers irrespective of quality
--no-prefer-free-formats Don't give any special preference to free
containers (default)
--check-formats Check that the selected formats are
actually downloadable
--check-formats Make sure formats are selected only from
those that are actually downloadable
--check-all-formats Check all formats for whether they are
actually downloadable
--no-check-formats Do not check that the formats are actually
@@ -833,21 +843,32 @@ You can also fork the project on github and run your fork's [build workflow](.gi
interactively
--ap-list-mso List all supported multiple-system
operators
--client-certificate CERTFILE Path to client certificate file in PEM
format. May include the private key
--client-certificate-key KEYFILE Path to private key file for client
certificate
--client-certificate-password PASSWORD
Password for client certificate private
key, if encrypted. If not provided and the
key is encrypted, yt-dlp will ask
interactively
## Post-Processing Options:
-x, --extract-audio Convert video files to audio-only files
(requires ffmpeg and ffprobe)
--audio-format FORMAT Specify audio format to convert the audio
to when -x is used. Currently supported
formats are: best (default) or one of
best|aac|flac|mp3|m4a|opus|vorbis|wav|alac
--audio-quality QUALITY Specify ffmpeg audio quality, insert a
formats are: best (default) or one of aac,
flac, mp3, m4a, opus, vorbis, wav, alac
--audio-quality QUALITY Specify ffmpeg audio quality to use when
converting the audio with -x. Insert a
value between 0 (best) and 10 (worst) for
VBR or a specific bitrate like 128K
(default 5)
--remux-video FORMAT Remux the video into another container if
necessary (currently supported: mp4|mkv|flv
|webm|mov|avi|mp3|mka|m4a|ogg|opus). If
necessary (currently supported: mp4, mkv,
flv, webm, mov, avi, mka, ogg, aac, flac,
mp3, m4a, opus, vorbis, wav, alac). If
target container does not support the
video/audio codec, remuxing will fail. You
can specify multiple rules; Eg.
@@ -921,8 +942,8 @@ You can also fork the project on github and run your fork's [build workflow](.gi
same codecs and number of streams to be
concatable. The "pl_video:" prefix can be
used with "--paths" and "--output" to set
the output filename for the split files.
See "OUTPUT TEMPLATE" for details
the output filename for the concatenated
files. See "OUTPUT TEMPLATE" for details
--fixup POLICY Automatically correct known faults of the
file. One of never (do nothing), warn (only
emit a warning), detect_or_warn (the
@@ -947,10 +968,10 @@ You can also fork the project on github and run your fork's [build workflow](.gi
option can be used multiple times
--no-exec Remove any previously defined --exec
--convert-subs FORMAT Convert the subtitles to another format
(currently supported: srt|vtt|ass|lrc)
(currently supported: srt, vtt, ass, lrc)
(Alias: --convert-subtitles)
--convert-thumbnails FORMAT Convert the thumbnails to another format
(currently supported: jpg|png|webp)
(currently supported: jpg, png, webp)
--split-chapters Split video into multiple files based on
internal chapters. The "chapter:" prefix
can be used with "--paths" and "--output"
@@ -1059,8 +1080,9 @@ You can configure yt-dlp by placing any supported command line option to a confi
* `%APPDATA%/yt-dlp/config.txt`
* `~/yt-dlp.conf`
* `~/yt-dlp.conf.txt`
`%XDG_CONFIG_HOME%` defaults to `~/.config` if undefined. On windows, `%APPDATA%` generally points to `C:\Users\<user name>\AppData\Roaming` and `~` points to `%HOME%` if present, `%USERPROFILE%` (generally `C:\Users\<user name>`), or `%HOMEDRIVE%%HOMEPATH%`
1. **System Configuration**: `/etc/yt-dlp.conf`
For example, with the following configuration file yt-dlp will always extract the audio, not copy the mtime, use a proxy and save all videos under `YouTube` directory in your home directory:
@@ -1117,6 +1139,7 @@ The simplest usage of `-o` is not to set any template arguments when downloading
It may however also contain special sequences that will be replaced when downloading each video. The special sequences may be formatted according to [Python string formatting operations](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting). For example, `%(NAME)s` or `%(NAME)05d`. To clarify, that is a percent symbol followed by a name in parentheses, followed by formatting operations.
The field names themselves (the part inside the parenthesis) can also have some special formatting:
1. **Object traversal**: The dictionaries and lists available in metadata can be traversed by using a `.` (dot) separator. You can also do python slicing using `:`. Eg: `%(tags.0)s`, `%(subtitles.en.-1.ext)s`, `%(id.3:7:-1)s`, `%(formats.:.format_id)s`. `%()s` refers to the entire infodict. Note that all the fields that become available using this method are not listed below. Use `-j` to see such fields
1. **Addition**: Addition and subtraction of numeric fields can be done using `+` and `-` respectively. Eg: `%(playlist_index+10)03d`, `%(n_entries+1-playlist_index)d`
@@ -1154,11 +1177,11 @@ The available fields are:
- `license` (string): License name the video is licensed under
- `creator` (string): The creator of the video
- `timestamp` (numeric): UNIX timestamp of the moment the video became available
- `upload_date` (string): Video upload date (YYYYMMDD)
- `upload_date` (string): Video upload date in UTC (YYYYMMDD)
- `release_timestamp` (numeric): UNIX timestamp of the moment the video was released
- `release_date` (string): The date (YYYYMMDD) when the video was released
- `release_date` (string): The date (YYYYMMDD) when the video was released in UTC
- `modified_timestamp` (numeric): UNIX timestamp of the moment the video was last modified
- `modified_date` (string): The date (YYYYMMDD) when the video was last modified
- `modified_date` (string): The date (YYYYMMDD) when the video was last modified in UTC
- `uploader_id` (string): Nickname or id of the video uploader
- `channel` (string): Full name of the channel the video is uploaded on
- `channel_id` (string): Id of the channel
@@ -1363,7 +1386,7 @@ You can also use special names to select particular edge case formats:
- `bv`, `bestvideo`: Select the best quality **video-only** format. Equivalent to `best*[acodec=none]`
- `bv*`, `bestvideo*`: Select the best quality format that **contains video**. It may also contain audio. Equivalent to `best*[vcodec!=none]`
- `ba`, `bestaudio`: Select the best quality **audio-only** format. Equivalent to `best*[vcodec=none]`
- `ba*`, `bestaudio*`: Select the best quality format that **contains audio**. It may also contain video. Equivalent to `best*[acodec!=none]`
- `ba*`, `bestaudio*`: Select the best quality format that **contains audio**. It may also contain video. Equivalent to `best*[acodec!=none]` ([Do not use!](https://github.com/yt-dlp/yt-dlp/issues/979#issuecomment-919629354))
- `w*`, `worst*`: Select the worst quality format that contains either a video or an audio
- `w`, `worst`: Select the worst quality format that contains both video and audio. Equivalent to `worst*[vcodec!=none][acodec!=none]`
- `wv`, `worstvideo`: Select the worst quality video-only format. Equivalent to `worst*[acodec=none]`
@@ -1371,7 +1394,7 @@ You can also use special names to select particular edge case formats:
- `wa`, `worstaudio`: Select the worst quality audio-only format. Equivalent to `worst*[vcodec=none]`
- `wa*`, `worstaudio*`: Select the worst quality format that contains audio. It may also contain video. Equivalent to `worst*[acodec!=none]`
For example, to download the worst quality video-only format you can use `-f worstvideo`. It is however recommended not to use `worst` and related options. When your format selector is `worst`, the format which is worst in all respects is selected. Most of the time, what you actually want is the video with the smallest filesize instead. So it is generally better to use `-f best -S +size,+br,+res,+fps` instead of `-f worst`. See [sorting formats](#sorting-formats) for more details.
For example, to download the worst quality video-only format you can use `-f worstvideo`. It is however recommended not to use `worst` and related options. When your format selector is `worst`, the format which is worst in all respects is selected. Most of the time, what you actually want is the video with the smallest filesize instead. So it is generally better to use `-S +size` or more rigorously, `-S +size,+br,+res,+fps` instead of `-f worst`. See [sorting formats](#sorting-formats) for more details.
You can select the n'th best format of a type by using `best<type>.<n>`. For example, `best.2` will select the 2nd best combined format. Similarly, `bv*.3` will select the 3rd best format that contains a video stream.
@@ -1597,7 +1620,9 @@ The general syntax of `--parse-metadata FROM:TO` is to give the name of a field
Note that any field created by this can be used in the [output template](#output-template) and will also affect the media file's metadata added when using `--add-metadata`.
This option also has a few special uses:
* You can download an additional URL based on the metadata of the currently downloaded video. To do this, set the field `additional_urls` to the URL that you want to download. Eg: `--parse-metadata "description:(?P<additional_urls>https?://www\.vimeo\.com/\d+)` will download the first vimeo video found in the description
* You can use this to change the metadata that is embedded in the media file. To do this, set the value of the corresponding field with a `meta_` prefix. For example, any value you set to `meta_description` field will be added to the `description` field in the file. For example, you can use this to set a different "description" and "synopsis". To modify the metadata of individual streams, use the `meta<n>_` prefix (Eg: `meta1_language`). Any value set to the `meta_` field will overwrite all default values.
**Note**: Metadata modification happens before format selection, post-extraction and other post-processing operations. Some fields may be added or changed during these steps, overriding your changes.
@@ -1637,7 +1662,11 @@ $ yt-dlp --parse-metadata "description:Artist - (?P<artist>.+)"
# Set title as "Series name S01E05"
$ yt-dlp --parse-metadata "%(series)s S%(season_number)02dE%(episode_number)02d:%(title)s"
# Set "comment" field in video metadata using description instead of webpage_url
# Prioritize uploader as the "artist" field in video metadata
$ yt-dlp --parse-metadata "%(uploader|)s:%(meta_artist)s" --add-metadata
# Set "comment" field in video metadata using description instead of webpage_url,
# handling multiple lines correctly
$ yt-dlp --parse-metadata "description:(?s)(?P<meta_comment>.+)" --add-metadata
# Remove "formats" field from the infojson by setting it to an empty string
@@ -1650,20 +1679,18 @@ $ yt-dlp --replace-in-metadata "title,uploader" "[ _]" "-"
# EXTRACTOR ARGUMENTS
Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. Eg: `--extractor-args "youtube:player-client=android_agegate,web;include_live_dash" --extractor-args "funimation:version=uncut"`
Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. Eg: `--extractor-args "youtube:player-client=android_embedded,web;include_live_dash" --extractor-args "funimation:version=uncut"`
The following extractors use this feature:
#### youtube
* `skip`: `hls` or `dash` (or both) to skip download of the respective manifests
* `player_client`: Clients to extract video data from. The main clients are `web`, `android`, `ios`, `mweb`. These also have `_music`, `_embedded`, `_agegate`, and `_creator` variants (Eg: `web_embedded`) (`mweb` has only `_agegate`). By default, `android,web` is used, but the agegate and creator variants are added as required for age-gated videos. Similarly the music variants are added for `music.youtube.com` urls. You can also use `all` to use all the clients, and `default` for the default clients.
* `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and auto-translated subtitles respectively
* `player_client`: Clients to extract video data from. The main clients are `web`, `android` and `ios` with variants `_music`, `_embedded`, `_embedscreen`, `_creator` (Eg: `web_embedded`); and `mweb` and `tv_embedded` (agegate bypass) with no variants. By default, `android,web` is used, but tv_embedded and creator variants are added as required for age-gated videos. Similarly the music variants are added for `music.youtube.com` urls. You can use `all` to use all the clients, and `default` for the default clients.
* `player_skip`: Skip some network requests that are generally needed for robust extraction. One or more of `configs` (skip client configs), `webpage` (skip initial webpage), `js` (skip js player). While these options can help reduce the number of requests needed or avoid some rate-limiting, they could cause some issues. See [#860](https://github.com/yt-dlp/yt-dlp/pull/860) for more details
* `include_live_dash`: Include live dash formats even without `--live-from-start` (These formats don't download properly)
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
* `max_comments`: Limit the amount of comments to gather. Comma-separated list of integers representing `max-comments,max-parents,max-replies,max-replies-per-thread`. Default is `all,all,all,all`.
* E.g. `all,all,1000,10` will get a maximum of 1000 replies total, with up to 10 replies per thread. `1000,all,100` will get a maximum of 1000 comments, with a maximum of 100 replies total.
* `max_comment_depth` Maximum depth for nested comments. YouTube supports depths 1 or 2 (default)
* **Deprecated**: Set `max-replies` to `0` or `all` in `max_comments` instead (e.g. `max_comments=all,all,0` to get no replies)
* `max_comments`: Limit the amount of comments to gather. Comma-separated list of integers representing `max-comments,max-parents,max-replies,max-replies-per-thread`. Default is `all,all,all,all`
* E.g. `all,all,1000,10` will get a maximum of 1000 replies total, with up to 10 replies per thread. `1000,all,100` will get a maximum of 1000 comments, with a maximum of 100 replies total
#### youtubetab (YouTube playlists, channels, feeds, etc.)
* `skip`: One or more of `webpage` (skip initial webpage download), `authcheck` (allow the download of playlists requiring authentication when no initial webpage is downloaded. This may cause unwanted behavior, see [#1122](https://github.com/yt-dlp/yt-dlp/pull/1122) for more details)
@@ -1677,7 +1704,7 @@ The following extractors use this feature:
* `language`: Languages to extract. Eg: `crunchyroll:language=jaJp`
* `hardsub`: Which hard-sub versions to extract. Eg: `crunchyroll:hardsub=None,enUS`
#### crunchyroll:beta
#### crunchyrollbeta
* `format`: Which stream type(s) to extract. Default is `adaptive_hls` Eg: `crunchyrollbeta:format=vo_adaptive_hls`
* Potentially useful values include `adaptive_hls`, `adaptive_dash`, `vo_adaptive_hls`, `vo_adaptive_dash`, `download_hls`, `trailer_hls`, `trailer_dash`
* `hardsub`: Preference order for which hardsub versions to extract. Default is `None` (no hardsubs). Eg: `crunchyrollbeta:hardsub=en-US,None`
@@ -1685,6 +1712,9 @@ The following extractors use this feature:
#### vikichannel
* `video_types`: Types of videos to download - one or more of `episodes`, `movies`, `clips`, `trailers`
#### niconico
* `segment_duration`: Segment duration in milliseconds for HLS-DMC formats. Use it at your own risk since this feature **may result in your account termination.**
#### youtubewebarchive
* `check_all`: Try to check more at the cost of more requests. One or more of `thumbnails`, `captures`
@@ -1734,19 +1764,94 @@ From a Python program, you can embed yt-dlp in a more powerful fashion, like thi
```python
from yt_dlp import YoutubeDL
ydl_opts = {'format': 'bestaudio'}
with YoutubeDL(ydl_opts) as ydl:
ydl.download(['https://www.youtube.com/watch?v=BaW_jenozKc'])
URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
with YoutubeDL() as ydl:
ydl.download(URLS)
```
Most likely, you'll want to use various options. For a list of options available, have a look at [`yt_dlp/YoutubeDL.py`](yt_dlp/YoutubeDL.py#L191).
Most likely, you'll want to use various options. For a list of options available, have a look at [`yt_dlp/YoutubeDL.py`](yt_dlp/YoutubeDL.py#L181).
Here's a more complete example demonstrating various functionality:
**Tip**: If you are porting your code from youtube-dl to yt-dlp, one important point to look out for is that we do not guarantee the return value of `YoutubeDL.extract_info` to be json serializable, or even be a dictionary. It will be dictionary-like, but if you want to ensure it is a serializable dictionary, pass it through `YoutubeDL.sanitize_info` as shown in the [example below](#extracting-information)
## Embedding examples
#### Extracting information
```python
import json
import yt_dlp
URL = 'https://www.youtube.com/watch?v=BaW_jenozKc'
# See help(yt_dlp.YoutubeDL) for a list of available options and public functions
ydl_opts = {}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(URL, download=False)
# ydl.sanitize_info makes the info json-serializable
print(json.dumps(ydl.sanitize_info(info)))
```
#### Download using an info-json
```python
import yt_dlp
INFO_FILE = 'path/to/video.info.json'
with yt_dlp.YoutubeDL() as ydl:
error_code = ydl.download_with_info_file(INFO_FILE)
print('Some videos failed to download' if error_code
else 'All videos successfully downloaded')
```
#### Extract audio
```python
import yt_dlp
URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
ydl_opts = {
'format': 'm4a/bestaudio/best',
# See help(yt_dlp.postprocessor) for a list of available Postprocessors and their arguments
'postprocessors': [{ # Extract audio using ffmpeg
'key': 'FFmpegExtractAudio',
'preferredcodec': 'm4a',
}]
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
error_code = ydl.download(URLS)
```
#### Filter videos
```python
import yt_dlp
URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
def longer_than_a_minute(info, *, incomplete):
"""Download only videos longer than a minute (or with unknown duration)"""
duration = info.get('duration')
if duration and duration < 60:
return 'The video is too short'
ydl_opts = {
'match_filter': longer_than_a_minute,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
error_code = ydl.download(URLS)
```
#### Adding logger and progress hook
```python
import yt_dlp
URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
class MyLogger:
def debug(self, msg):
@@ -1767,23 +1872,51 @@ class MyLogger:
print(msg)
# See the docstring of yt_dlp.postprocessor.common.PostProcessor
# See "progress_hooks" in help(yt_dlp.YoutubeDL)
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now post-processing ...')
ydl_opts = {
'logger': MyLogger(),
'progress_hooks': [my_hook],
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download(URLS)
```
#### Add a custom PostProcessor
```python
import yt_dlp
URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
# See help(yt_dlp.postprocessor.PostProcessor)
class MyCustomPP(yt_dlp.postprocessor.PostProcessor):
# See docstring of yt_dlp.postprocessor.common.PostProcessor.run
def run(self, info):
self.to_screen('Doing stuff')
return [], info
# See "progress_hooks" in the docstring of yt_dlp.YoutubeDL
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')
with yt_dlp.YoutubeDL() as ydl:
ydl.add_post_processor(MyCustomPP())
ydl.download(URLS)
```
#### Use a custom format selector
```python
import yt_dlp
URL = ['https://www.youtube.com/watch?v=BaW_jenozKc']
def format_selector(ctx):
""" Select the best video and the best audio that won't result in an mkv.
This is just an example and does not handle all cases """
NOTE: This is just an example and does not handle all cases """
# formats are already sorted worst to best
formats = ctx.get('formats')[::-1]
@@ -1798,8 +1931,8 @@ def format_selector(ctx):
best_audio = next(f for f in formats if (
f['acodec'] != 'none' and f['vcodec'] == 'none' and f['ext'] == audio_ext))
# These are the minimum required fields for a merged format
yield {
# These are the minimum required fields for a merged format
'format_id': f'{best_video["format_id"]}+{best_audio["format_id"]}',
'ext': best_video['ext'],
'requested_formats': [best_video, best_audio],
@@ -1808,36 +1941,14 @@ def format_selector(ctx):
}
# See docstring of yt_dlp.YoutubeDL for a description of the options
ydl_opts = {
'format': format_selector,
'postprocessors': [{
# Embed metadata in video using ffmpeg.
# See yt_dlp.postprocessor.FFmpegMetadataPP for the arguments it accepts
'key': 'FFmpegMetadata',
'add_chapters': True,
'add_metadata': True,
}],
'logger': MyLogger(),
'progress_hooks': [my_hook],
# Add custom headers
'http_headers': {'Referer': 'https://www.google.com'}
}
# See the public functions in yt_dlp.YoutubeDL for for other available functions.
# Eg: "ydl.download", "ydl.download_with_info_file"
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.add_post_processor(MyCustomPP())
info = ydl.extract_info('https://www.youtube.com/watch?v=BaW_jenozKc')
# ydl.sanitize_info makes the info json-serializable
print(json.dumps(ydl.sanitize_info(info)))
ydl.download(URLS)
```
**Tip**: If you are porting your code from youtube-dl to yt-dlp, one important point to look out for is that we do not guarantee the return value of `YoutubeDL.extract_info` to be json serializable, or even be a dictionary. It will be dictionary-like, but if you want to ensure it is a serializable dictionary, pass it through `YoutubeDL.sanitize_info` as shown in the example above
<!-- MANPAGE: MOVE "NEW FEATURES" SECTION HERE -->
# DEPRECATED OPTIONS
@@ -1951,8 +2062,7 @@ These options may no longer work as intended
These options were deprecated since 2014 and have now been entirely removed
-A, --auto-number -o "%(autonumber)s-%(id)s.%(ext)s"
-t, --title -o "%(title)s-%(id)s.%(ext)s"
-l, --literal -o accepts literal names
-t, -l, --title, --literal -o "%(title)s-%(id)s.%(ext)s"
# CONTRIBUTING
See [CONTRIBUTING.md](CONTRIBUTING.md#contributing-to-yt-dlp) for instructions on [Opening an Issue](CONTRIBUTING.md#opening-an-issue) and [Contributing code to the project](CONTRIBUTING.md#developer-instructions)

View File

@@ -1,11 +1,9 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import os
from os.path import dirname as dirn
import sys
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import yt_dlp
BASH_COMPLETION_FILE = "completions/bash/yt-dlp"
@@ -26,5 +24,5 @@ def build_completion(opt_parser):
f.write(filled_template)
parser = yt_dlp.parseOpts()[0]
parser = yt_dlp.parseOpts(ignore_config_files=True)[0]
build_completion(parser)

View File

@@ -1,435 +0,0 @@
# UNUSED
#!/usr/bin/python3
import argparse
import ctypes
import functools
import shutil
import subprocess
import sys
import tempfile
import threading
import traceback
import os.path
sys.path.insert(0, os.path.dirname(os.path.dirname((os.path.abspath(__file__)))))
from yt_dlp.compat import (
compat_input,
compat_http_server,
compat_str,
compat_urlparse,
)
# These are not used outside of buildserver.py thus not in compat.py
try:
import winreg as compat_winreg
except ImportError: # Python 2
import _winreg as compat_winreg
try:
import socketserver as compat_socketserver
except ImportError: # Python 2
import SocketServer as compat_socketserver
class BuildHTTPServer(compat_socketserver.ThreadingMixIn, compat_http_server.HTTPServer):
allow_reuse_address = True
advapi32 = ctypes.windll.advapi32
SC_MANAGER_ALL_ACCESS = 0xf003f
SC_MANAGER_CREATE_SERVICE = 0x02
SERVICE_WIN32_OWN_PROCESS = 0x10
SERVICE_AUTO_START = 0x2
SERVICE_ERROR_NORMAL = 0x1
DELETE = 0x00010000
SERVICE_STATUS_START_PENDING = 0x00000002
SERVICE_STATUS_RUNNING = 0x00000004
SERVICE_ACCEPT_STOP = 0x1
SVCNAME = 'youtubedl_builder'
LPTSTR = ctypes.c_wchar_p
START_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_int, ctypes.POINTER(LPTSTR))
class SERVICE_TABLE_ENTRY(ctypes.Structure):
_fields_ = [
('lpServiceName', LPTSTR),
('lpServiceProc', START_CALLBACK)
]
HandlerEx = ctypes.WINFUNCTYPE(
ctypes.c_int, # return
ctypes.c_int, # dwControl
ctypes.c_int, # dwEventType
ctypes.c_void_p, # lpEventData,
ctypes.c_void_p, # lpContext,
)
def _ctypes_array(c_type, py_array):
ar = (c_type * len(py_array))()
ar[:] = py_array
return ar
def win_OpenSCManager():
res = advapi32.OpenSCManagerW(None, None, SC_MANAGER_ALL_ACCESS)
if not res:
raise Exception('Opening service manager failed - '
'are you running this as administrator?')
return res
def win_install_service(service_name, cmdline):
manager = win_OpenSCManager()
try:
h = advapi32.CreateServiceW(
manager, service_name, None,
SC_MANAGER_CREATE_SERVICE, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
cmdline, None, None, None, None, None)
if not h:
raise OSError('Service creation failed: %s' % ctypes.FormatError())
advapi32.CloseServiceHandle(h)
finally:
advapi32.CloseServiceHandle(manager)
def win_uninstall_service(service_name):
manager = win_OpenSCManager()
try:
h = advapi32.OpenServiceW(manager, service_name, DELETE)
if not h:
raise OSError('Could not find service %s: %s' % (
service_name, ctypes.FormatError()))
try:
if not advapi32.DeleteService(h):
raise OSError('Deletion failed: %s' % ctypes.FormatError())
finally:
advapi32.CloseServiceHandle(h)
finally:
advapi32.CloseServiceHandle(manager)
def win_service_report_event(service_name, msg, is_error=True):
with open('C:/sshkeys/log', 'a', encoding='utf-8') as f:
f.write(msg + '\n')
event_log = advapi32.RegisterEventSourceW(None, service_name)
if not event_log:
raise OSError('Could not report event: %s' % ctypes.FormatError())
try:
type_id = 0x0001 if is_error else 0x0004
event_id = 0xc0000000 if is_error else 0x40000000
lines = _ctypes_array(LPTSTR, [msg])
if not advapi32.ReportEventW(
event_log, type_id, 0, event_id, None, len(lines), 0,
lines, None):
raise OSError('Event reporting failed: %s' % ctypes.FormatError())
finally:
advapi32.DeregisterEventSource(event_log)
def win_service_handler(stop_event, *args):
try:
raise ValueError('Handler called with args ' + repr(args))
TODO
except Exception as e:
tb = traceback.format_exc()
msg = str(e) + '\n' + tb
win_service_report_event(service_name, msg, is_error=True)
raise
def win_service_set_status(handle, status_code):
svcStatus = SERVICE_STATUS()
svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
svcStatus.dwCurrentState = status_code
svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
svcStatus.dwServiceSpecificExitCode = 0
if not advapi32.SetServiceStatus(handle, ctypes.byref(svcStatus)):
raise OSError('SetServiceStatus failed: %r' % ctypes.FormatError())
def win_service_main(service_name, real_main, argc, argv_raw):
try:
# args = [argv_raw[i].value for i in range(argc)]
stop_event = threading.Event()
handler = HandlerEx(functools.partial(stop_event, win_service_handler))
h = advapi32.RegisterServiceCtrlHandlerExW(service_name, handler, None)
if not h:
raise OSError('Handler registration failed: %s' %
ctypes.FormatError())
TODO
except Exception as e:
tb = traceback.format_exc()
msg = str(e) + '\n' + tb
win_service_report_event(service_name, msg, is_error=True)
raise
def win_service_start(service_name, real_main):
try:
cb = START_CALLBACK(
functools.partial(win_service_main, service_name, real_main))
dispatch_table = _ctypes_array(SERVICE_TABLE_ENTRY, [
SERVICE_TABLE_ENTRY(
service_name,
cb
),
SERVICE_TABLE_ENTRY(None, ctypes.cast(None, START_CALLBACK))
])
if not advapi32.StartServiceCtrlDispatcherW(dispatch_table):
raise OSError('ctypes start failed: %s' % ctypes.FormatError())
except Exception as e:
tb = traceback.format_exc()
msg = str(e) + '\n' + tb
win_service_report_event(service_name, msg, is_error=True)
raise
def main(args=None):
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--install',
action='store_const', dest='action', const='install',
help='Launch at Windows startup')
parser.add_argument('-u', '--uninstall',
action='store_const', dest='action', const='uninstall',
help='Remove Windows service')
parser.add_argument('-s', '--service',
action='store_const', dest='action', const='service',
help='Run as a Windows service')
parser.add_argument('-b', '--bind', metavar='<host:port>',
action='store', default='0.0.0.0:8142',
help='Bind to host:port (default %default)')
options = parser.parse_args(args=args)
if options.action == 'install':
fn = os.path.abspath(__file__).replace('v:', '\\\\vboxsrv\\vbox')
cmdline = '%s %s -s -b %s' % (sys.executable, fn, options.bind)
win_install_service(SVCNAME, cmdline)
return
if options.action == 'uninstall':
win_uninstall_service(SVCNAME)
return
if options.action == 'service':
win_service_start(SVCNAME, main)
return
host, port_str = options.bind.split(':')
port = int(port_str)
print('Listening on %s:%d' % (host, port))
srv = BuildHTTPServer((host, port), BuildHTTPRequestHandler)
thr = threading.Thread(target=srv.serve_forever)
thr.start()
compat_input('Press ENTER to shut down')
srv.shutdown()
thr.join()
def rmtree(path):
for name in os.listdir(path):
fname = os.path.join(path, name)
if os.path.isdir(fname):
rmtree(fname)
else:
os.chmod(fname, 0o666)
os.remove(fname)
os.rmdir(path)
class BuildError(Exception):
def __init__(self, output, code=500):
self.output = output
self.code = code
def __str__(self):
return self.output
class HTTPError(BuildError):
pass
class PythonBuilder(object):
def __init__(self, **kwargs):
python_version = kwargs.pop('python', '3.4')
python_path = None
for node in ('Wow6432Node\\', ''):
try:
key = compat_winreg.OpenKey(
compat_winreg.HKEY_LOCAL_MACHINE,
r'SOFTWARE\%sPython\PythonCore\%s\InstallPath' % (node, python_version))
try:
python_path, _ = compat_winreg.QueryValueEx(key, '')
finally:
compat_winreg.CloseKey(key)
break
except Exception:
pass
if not python_path:
raise BuildError('No such Python version: %s' % python_version)
self.pythonPath = python_path
super(PythonBuilder, self).__init__(**kwargs)
class GITInfoBuilder(object):
def __init__(self, **kwargs):
try:
self.user, self.repoName = kwargs['path'][:2]
self.rev = kwargs.pop('rev')
except ValueError:
raise BuildError('Invalid path')
except KeyError as e:
raise BuildError('Missing mandatory parameter "%s"' % e.args[0])
path = os.path.join(os.environ['APPDATA'], 'Build archive', self.repoName, self.user)
if not os.path.exists(path):
os.makedirs(path)
self.basePath = tempfile.mkdtemp(dir=path)
self.buildPath = os.path.join(self.basePath, 'build')
super(GITInfoBuilder, self).__init__(**kwargs)
class GITBuilder(GITInfoBuilder):
def build(self):
try:
subprocess.check_output(['git', 'clone', 'git://github.com/%s/%s.git' % (self.user, self.repoName), self.buildPath])
subprocess.check_output(['git', 'checkout', self.rev], cwd=self.buildPath)
except subprocess.CalledProcessError as e:
raise BuildError(e.output)
super(GITBuilder, self).build()
class YoutubeDLBuilder(object):
authorizedUsers = ['fraca7', 'phihag', 'rg3', 'FiloSottile', 'ytdl-org']
def __init__(self, **kwargs):
if self.repoName != 'yt-dlp':
raise BuildError('Invalid repository "%s"' % self.repoName)
if self.user not in self.authorizedUsers:
raise HTTPError('Unauthorized user "%s"' % self.user, 401)
super(YoutubeDLBuilder, self).__init__(**kwargs)
def build(self):
try:
proc = subprocess.Popen([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'], stdin=subprocess.PIPE, cwd=self.buildPath)
proc.wait()
#subprocess.check_output([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'],
# cwd=self.buildPath)
except subprocess.CalledProcessError as e:
raise BuildError(e.output)
super(YoutubeDLBuilder, self).build()
class DownloadBuilder(object):
def __init__(self, **kwargs):
self.handler = kwargs.pop('handler')
self.srcPath = os.path.join(self.buildPath, *tuple(kwargs['path'][2:]))
self.srcPath = os.path.abspath(os.path.normpath(self.srcPath))
if not self.srcPath.startswith(self.buildPath):
raise HTTPError(self.srcPath, 401)
super(DownloadBuilder, self).__init__(**kwargs)
def build(self):
if not os.path.exists(self.srcPath):
raise HTTPError('No such file', 404)
if os.path.isdir(self.srcPath):
raise HTTPError('Is a directory: %s' % self.srcPath, 401)
self.handler.send_response(200)
self.handler.send_header('Content-Type', 'application/octet-stream')
self.handler.send_header('Content-Disposition', 'attachment; filename=%s' % os.path.split(self.srcPath)[-1])
self.handler.send_header('Content-Length', str(os.stat(self.srcPath).st_size))
self.handler.end_headers()
with open(self.srcPath, 'rb') as src:
shutil.copyfileobj(src, self.handler.wfile)
super(DownloadBuilder, self).build()
class CleanupTempDir(object):
def build(self):
try:
rmtree(self.basePath)
except Exception as e:
print('WARNING deleting "%s": %s' % (self.basePath, e))
super(CleanupTempDir, self).build()
class Null(object):
def __init__(self, **kwargs):
pass
def start(self):
pass
def close(self):
pass
def build(self):
pass
class Builder(PythonBuilder, GITBuilder, YoutubeDLBuilder, DownloadBuilder, CleanupTempDir, Null):
pass
class BuildHTTPRequestHandler(compat_http_server.BaseHTTPRequestHandler):
actionDict = {'build': Builder, 'download': Builder} # They're the same, no more caching.
def do_GET(self):
path = compat_urlparse.urlparse(self.path)
paramDict = dict([(key, value[0]) for key, value in compat_urlparse.parse_qs(path.query).items()])
action, _, path = path.path.strip('/').partition('/')
if path:
path = path.split('/')
if action in self.actionDict:
try:
builder = self.actionDict[action](path=path, handler=self, **paramDict)
builder.start()
try:
builder.build()
finally:
builder.close()
except BuildError as e:
self.send_response(e.code)
msg = compat_str(e).encode('UTF-8')
self.send_header('Content-Type', 'text/plain; charset=UTF-8')
self.send_header('Content-Length', len(msg))
self.end_headers()
self.wfile.write(msg)
else:
self.send_response(500, 'Unknown build method "%s"' % action)
else:
self.send_response(500, 'Malformed URL')
if __name__ == '__main__':
main()

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
"""
This script employs a VERY basic heuristic ('porn' in webpage.lower()) to check
if we are not 'age_limit' tagging some porn site
@@ -12,11 +10,12 @@ pass the list filename as the only argument
# Allow direct execution
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import gettestcases
from yt_dlp.utils import compat_urllib_parse_urlparse
from yt_dlp.utils import compat_urllib_request
from yt_dlp.utils import compat_urllib_parse_urlparse, compat_urllib_request
if len(sys.argv) > 1:
METHOD = 'LIST'
@@ -29,7 +28,7 @@ for test in gettestcases():
try:
webpage = compat_urllib_request.urlopen(test['url'], timeout=10).read()
except Exception:
print('\nFail: {0}'.format(test['name']))
print('\nFail: {}'.format(test['name']))
continue
webpage = webpage.decode('utf8', 'replace')
@@ -39,7 +38,7 @@ for test in gettestcases():
elif METHOD == 'LIST':
domain = compat_urllib_parse_urlparse(test['url']).netloc
if not domain:
print('\nFail: {0}'.format(test['name']))
print('\nFail: {}'.format(test['name']))
continue
domain = '.'.join(domain.split('.')[-2:])
@@ -47,11 +46,11 @@ for test in gettestcases():
if RESULT and ('info_dict' not in test or 'age_limit' not in test['info_dict']
or test['info_dict']['age_limit'] != 18):
print('\nPotential missing age_limit check: {0}'.format(test['name']))
print('\nPotential missing age_limit check: {}'.format(test['name']))
elif not RESULT and ('info_dict' in test and 'age_limit' in test['info_dict']
and test['info_dict']['age_limit'] == 18):
print('\nPotential false negative: {0}'.format(test['name']))
print('\nPotential false negative: {}'.format(test['name']))
else:
sys.stdout.write('.')

View File

@@ -1,112 +0,0 @@
# Unused
#!/usr/bin/env python3
from __future__ import unicode_literals
import io
import json
import mimetypes
import netrc
import optparse
import os
import re
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp.compat import (
compat_basestring,
compat_getpass,
compat_print,
compat_urllib_request,
)
from yt_dlp.utils import (
make_HTTPS_handler,
sanitized_Request,
)
class GitHubReleaser(object):
_API_URL = 'https://api.github.com/repos/ytdl-org/youtube-dl/releases'
_UPLOADS_URL = 'https://uploads.github.com/repos/ytdl-org/youtube-dl/releases/%s/assets?name=%s'
_NETRC_MACHINE = 'github.com'
def __init__(self, debuglevel=0):
self._init_github_account()
https_handler = make_HTTPS_handler({}, debuglevel=debuglevel)
self._opener = compat_urllib_request.build_opener(https_handler)
def _init_github_account(self):
try:
info = netrc.netrc().authenticators(self._NETRC_MACHINE)
if info is not None:
self._token = info[2]
compat_print('Using GitHub credentials found in .netrc...')
return
else:
compat_print('No GitHub credentials found in .netrc')
except (IOError, netrc.NetrcParseError):
compat_print('Unable to parse .netrc')
self._token = compat_getpass(
'Type your GitHub PAT (personal access token) and press [Return]: ')
def _call(self, req):
if isinstance(req, compat_basestring):
req = sanitized_Request(req)
req.add_header('Authorization', 'token %s' % self._token)
response = self._opener.open(req).read().decode('utf-8')
return json.loads(response)
def list_releases(self):
return self._call(self._API_URL)
def create_release(self, tag_name, name=None, body='', draft=False, prerelease=False):
data = {
'tag_name': tag_name,
'target_commitish': 'master',
'name': name,
'body': body,
'draft': draft,
'prerelease': prerelease,
}
req = sanitized_Request(self._API_URL, json.dumps(data).encode('utf-8'))
return self._call(req)
def create_asset(self, release_id, asset):
asset_name = os.path.basename(asset)
url = self._UPLOADS_URL % (release_id, asset_name)
# Our files are small enough to be loaded directly into memory.
data = open(asset, 'rb').read()
req = sanitized_Request(url, data)
mime_type, _ = mimetypes.guess_type(asset_name)
req.add_header('Content-Type', mime_type or 'application/octet-stream')
return self._call(req)
def main():
parser = optparse.OptionParser(usage='%prog CHANGELOG VERSION BUILDPATH')
options, args = parser.parse_args()
if len(args) != 3:
parser.error('Expected a version and a build directory')
changelog_file, version, build_path = args
with io.open(changelog_file, encoding='utf-8') as inf:
changelog = inf.read()
mobj = re.search(r'(?s)version %s\n{2}(.+?)\n{3}' % version, changelog)
body = mobj.group(1) if mobj else ''
releaser = GitHubReleaser()
new_release = releaser.create_release(
version, name='yt-dlp %s' % version, body=body)
release_id = new_release['id']
for asset in os.listdir(build_path):
compat_print('Uploading %s...' % asset)
releaser.create_asset(release_id, os.path.join(build_path, asset))
if __name__ == '__main__':
main()

View File

@@ -1,12 +1,10 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import optparse
import os
from os.path import dirname as dirn
import sys
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import yt_dlp
from yt_dlp.utils import shell_quote
@@ -46,5 +44,5 @@ def build_completion(opt_parser):
f.write(filled_template)
parser = yt_dlp.parseOpts()[0]
parser = yt_dlp.parseOpts(ignore_config_files=True)[0]
build_completion(parser)

View File

@@ -1,15 +1,13 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import codecs
import subprocess
import os
import subprocess
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp.utils import intlist_to_bytes
from yt_dlp.aes import aes_encrypt, key_expansion
from yt_dlp.utils import intlist_to_bytes
secret_msg = b'Secret message goes here'

View File

@@ -1,43 +0,0 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import json
import sys
import hashlib
import os.path
if len(sys.argv) <= 1:
print('Specify the version number as parameter')
sys.exit()
version = sys.argv[1]
with open('update/LATEST_VERSION', 'w') as f:
f.write(version)
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
new_version = {}
filenames = {
'bin': 'yt-dlp',
'exe': 'yt-dlp.exe',
'tar': 'yt-dlp-%s.tar.gz' % version}
build_dir = os.path.join('..', '..', 'build', version)
for key, filename in filenames.items():
url = 'https://yt-dl.org/downloads/%s/%s' % (version, filename)
fn = os.path.join(build_dir, filename)
with open(fn, 'rb') as f:
data = f.read()
if not data:
raise ValueError('File %s is empty!' % fn)
sha256sum = hashlib.sha256(data).hexdigest()
new_version[key] = (url, sha256sum)
versions_info['versions'][version] = new_version
versions_info['latest'] = version
with open('update/versions.json', 'w') as jsonf:
json.dump(versions_info, jsonf, indent=4, sort_keys=True)

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import json
versions_info = json.load(open('update/versions.json'))
version = versions_info['latest']
version_dict = versions_info['versions'][version]
# Read template page
with open('download.html.in', 'r', encoding='utf-8') as tmplf:
template = tmplf.read()
template = template.replace('@PROGRAM_VERSION@', version)
template = template.replace('@PROGRAM_URL@', version_dict['bin'][0])
template = template.replace('@PROGRAM_SHA256SUM@', version_dict['bin'][1])
template = template.replace('@EXE_URL@', version_dict['exe'][0])
template = template.replace('@EXE_SHA256SUM@', version_dict['exe'][1])
template = template.replace('@TAR_URL@', version_dict['tar'][0])
template = template.replace('@TAR_SHA256SUM@', version_dict['tar'][1])
with open('download.html', 'w', encoding='utf-8') as dlf:
dlf.write(template)

View File

@@ -1,34 +0,0 @@
#!/usr/bin/env python3
from __future__ import unicode_literals, with_statement
import rsa
import json
from binascii import hexlify
try:
input = raw_input
except NameError:
pass
versions_info = json.load(open('update/versions.json'))
if 'signature' in versions_info:
del versions_info['signature']
print('Enter the PKCS1 private key, followed by a blank line:')
privkey = b''
while True:
try:
line = input()
except EOFError:
break
if line == '':
break
privkey += line.encode('ascii') + b'\n'
privkey = rsa.PrivateKey.load_pkcs1(privkey)
signature = hexlify(rsa.pkcs1.sign(json.dumps(versions_info, sort_keys=True).encode('utf-8'), privkey, 'SHA-256')).decode()
print('signature: ' + signature)
versions_info['signature'] = signature
with open('update/versions.json', 'w') as versionsf:
json.dump(versions_info, versionsf, indent=4, sort_keys=True)

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import with_statement, unicode_literals
import datetime
import glob
import io # For Python 2 compatibility
import os
import re
year = str(datetime.datetime.now().year)
for fn in glob.glob('*.html*'):
with io.open(fn, encoding='utf-8') as f:
content = f.read()
newc = re.sub(r'(?P<copyright>Copyright © 2011-)(?P<year>[0-9]{4})', 'Copyright © 2011-' + year, content)
if content != newc:
tmpFn = fn + '.part'
with io.open(tmpFn, 'wt', encoding='utf-8') as outf:
outf.write(newc)
os.rename(tmpFn, fn)

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import datetime
import io
import json
import textwrap
atom_template = textwrap.dedent("""\
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<link rel="self" href="http://ytdl-org.github.io/youtube-dl/update/releases.atom" />
<title>yt-dlp releases</title>
<id>https://yt-dl.org/feed/yt-dlp-updates-feed</id>
<updated>@TIMESTAMP@</updated>
@ENTRIES@
</feed>""")
entry_template = textwrap.dedent("""
<entry>
<id>https://yt-dl.org/feed/yt-dlp-updates-feed/yt-dlp-@VERSION@</id>
<title>New version @VERSION@</title>
<link href="http://ytdl-org.github.io/yt-dlp" />
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
Downloads available at <a href="https://yt-dl.org/downloads/@VERSION@/">https://yt-dl.org/downloads/@VERSION@/</a>
</div>
</content>
<author>
<name>The yt-dlp maintainers</name>
</author>
<updated>@TIMESTAMP@</updated>
</entry>
""")
now = datetime.datetime.now()
now_iso = now.isoformat() + 'Z'
atom_template = atom_template.replace('@TIMESTAMP@', now_iso)
versions_info = json.load(open('update/versions.json'))
versions = list(versions_info['versions'].keys())
versions.sort()
entries = []
for v in versions:
fields = v.split('.')
year, month, day = map(int, fields[:3])
faked = 0
patchlevel = 0
while True:
try:
datetime.date(year, month, day)
except ValueError:
day -= 1
faked += 1
assert day > 0
continue
break
if len(fields) >= 4:
try:
patchlevel = int(fields[3])
except ValueError:
patchlevel = 1
timestamp = '%04d-%02d-%02dT00:%02d:%02dZ' % (year, month, day, faked, patchlevel)
entry = entry_template.replace('@TIMESTAMP@', timestamp)
entry = entry.replace('@VERSION@', v)
entries.append(entry)
entries_str = textwrap.indent(''.join(entries), '\t')
atom_template = atom_template.replace('@ENTRIES@', entries_str)
with io.open('update/releases.atom', 'w', encoding='utf-8') as atom_file:
atom_file.write(atom_template)

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import sys
import os
import textwrap
# We must be able to import yt_dlp
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import yt_dlp
def main():
with open('supportedsites.html.in', 'r', encoding='utf-8') as tmplf:
template = tmplf.read()
ie_htmls = []
for ie in yt_dlp.list_extractors(age_limit=None):
ie_html = '<b>{}</b>'.format(ie.IE_NAME)
ie_desc = getattr(ie, 'IE_DESC', None)
if ie_desc is False:
continue
elif ie_desc is not None:
ie_html += ': {}'.format(ie.IE_DESC)
if not ie.working():
ie_html += ' (Currently broken)'
ie_htmls.append('<li>{}</li>'.format(ie_html))
template = template.replace('@SITES@', textwrap.indent('\n'.join(ie_htmls), '\t'))
with open('supportedsites.html', 'w', encoding='utf-8') as sitesf:
sitesf.write(template)
if __name__ == '__main__':
main()

View File

@@ -1,31 +1,33 @@
# coding: utf-8
import importlib
import random
import re
from ..utils import bug_reports_message, write_string
from ..utils import (
age_restricted,
bug_reports_message,
classproperty,
write_string,
)
class LazyLoadMetaClass(type):
def __getattr__(cls, name):
if '_real_class' not in cls.__dict__:
# "_TESTS" bloat the lazy_extractors
if '_real_class' not in cls.__dict__ and name != 'get_testcases':
write_string(
f'WARNING: Falling back to normal extractor since lazy extractor '
f'{cls.__name__} does not have attribute {name}{bug_reports_message()}')
return getattr(cls._get_real_class(), name)
'WARNING: Falling back to normal extractor since lazy extractor '
f'{cls.__name__} does not have attribute {name}{bug_reports_message()}\n')
return getattr(cls.real_class, name)
class LazyLoadExtractor(metaclass=LazyLoadMetaClass):
_module = None
_WORKING = True
@classmethod
def _get_real_class(cls):
@classproperty
def real_class(cls):
if '_real_class' not in cls.__dict__:
mod = __import__(cls._module, fromlist=(cls.__name__,))
cls._real_class = getattr(mod, cls.__name__)
cls._real_class = getattr(importlib.import_module(cls._module), cls.__name__)
return cls._real_class
def __new__(cls, *args, **kwargs):
real_cls = cls._get_real_class()
instance = real_cls.__new__(real_cls)
instance = cls.real_class.__new__(cls.real_class)
instance.__init__(*args, **kwargs)
return instance

View File

@@ -1,7 +1,4 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import io
import optparse
import re
@@ -16,7 +13,7 @@ def main():
infile, outfile = args
with io.open(infile, encoding='utf-8') as inf:
with open(infile, encoding='utf-8') as inf:
readme = inf.read()
bug_text = re.search(
@@ -26,7 +23,7 @@ def main():
out = bug_text + dev_text
with io.open(outfile, 'w', encoding='utf-8') as outf:
with open(outfile, 'w', encoding='utf-8') as outf:
outf.write(out)

View File

@@ -1,10 +1,19 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import io
import optparse
def read(fname):
with open(fname, encoding='utf-8') as f:
return f.read()
# Get the version from yt_dlp/version.py without importing the package
def read_version(fname):
exec(compile(read(fname), fname, 'exec'))
return locals()['__version__']
def main():
parser = optparse.OptionParser(usage='%prog INFILE OUTFILE')
options, args = parser.parse_args()
@@ -12,18 +21,10 @@ def main():
parser.error('Expected an input and an output filename')
infile, outfile = args
with open(outfile, 'w', encoding='utf-8') as outf:
outf.write(
read(infile) % {'version': read_version('yt_dlp/version.py')})
with io.open(infile, encoding='utf-8') as inf:
issue_template_tmpl = inf.read()
# Get the version from yt_dlp/version.py without importing the package
exec(compile(open('yt_dlp/version.py').read(),
'yt_dlp/version.py', 'exec'))
out = issue_template_tmpl % {'version': locals()['__version__']}
with io.open(outfile, 'w', encoding='utf-8') as outf:
outf.write(out)
if __name__ == '__main__':
main()

View File

@@ -1,105 +1,125 @@
#!/usr/bin/env python3
from __future__ import unicode_literals, print_function
from inspect import getsource
import io
import optparse
import os
from os.path import dirname as dirn
import sys
from inspect import getsource
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
lazy_extractors_filename = sys.argv[1] if len(sys.argv) > 1 else 'yt_dlp/extractor/lazy_extractors.py'
if os.path.exists(lazy_extractors_filename):
os.remove(lazy_extractors_filename)
# Block plugins from loading
plugins_dirname = 'ytdlp_plugins'
plugins_blocked_dirname = 'ytdlp_plugins_blocked'
if os.path.exists(plugins_dirname):
os.rename(plugins_dirname, plugins_blocked_dirname)
from yt_dlp.extractor import _ALL_CLASSES
from yt_dlp.extractor.common import InfoExtractor, SearchInfoExtractor
if os.path.exists(plugins_blocked_dirname):
os.rename(plugins_blocked_dirname, plugins_dirname)
with open('devscripts/lazy_load_template.py', 'rt') as f:
module_template = f.read()
CLASS_PROPERTIES = ['ie_key', 'working', '_match_valid_url', 'suitable', '_match_id', 'get_temp_id']
module_contents = [
module_template,
*[getsource(getattr(InfoExtractor, k)) for k in CLASS_PROPERTIES],
'\nclass LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n']
ie_template = '''
NO_ATTR = object()
STATIC_CLASS_PROPERTIES = ['IE_NAME', 'IE_DESC', 'SEARCH_KEY', '_WORKING', '_NETRC_MACHINE', 'age_limit']
CLASS_METHODS = [
'ie_key', 'working', 'description', 'suitable', '_match_valid_url', '_match_id', 'get_temp_id', 'is_suitable'
]
IE_TEMPLATE = '''
class {name}({bases}):
_module = '{module}'
_module = {module!r}
'''
with open('devscripts/lazy_load_template.py', encoding='utf-8') as f:
MODULE_TEMPLATE = f.read()
def get_base_name(base):
if base is InfoExtractor:
return 'LazyLoadExtractor'
elif base is SearchInfoExtractor:
return 'LazyLoadSearchExtractor'
else:
return base.__name__
def main():
parser = optparse.OptionParser(usage='%prog [OUTFILE.py]')
args = parser.parse_args()[1] or ['yt_dlp/extractor/lazy_extractors.py']
if len(args) != 1:
parser.error('Expected only an output filename')
lazy_extractors_filename = args[0]
if os.path.exists(lazy_extractors_filename):
os.remove(lazy_extractors_filename)
_ALL_CLASSES = get_all_ies() # Must be before import
from yt_dlp.extractor.common import InfoExtractor, SearchInfoExtractor
DummyInfoExtractor = type('InfoExtractor', (InfoExtractor,), {'IE_NAME': NO_ATTR})
module_src = '\n'.join((
MODULE_TEMPLATE,
' _module = None',
*extra_ie_code(DummyInfoExtractor),
'\nclass LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n',
*build_ies(_ALL_CLASSES, (InfoExtractor, SearchInfoExtractor), DummyInfoExtractor),
))
with open(lazy_extractors_filename, 'wt', encoding='utf-8') as f:
f.write(f'{module_src}\n')
def build_lazy_ie(ie, name):
s = ie_template.format(
name=name,
bases=', '.join(map(get_base_name, ie.__bases__)),
module=ie.__module__)
def get_all_ies():
PLUGINS_DIRNAME = 'ytdlp_plugins'
BLOCKED_DIRNAME = f'{PLUGINS_DIRNAME}_blocked'
if os.path.exists(PLUGINS_DIRNAME):
os.rename(PLUGINS_DIRNAME, BLOCKED_DIRNAME)
try:
from yt_dlp.extractor import _ALL_CLASSES
finally:
if os.path.exists(BLOCKED_DIRNAME):
os.rename(BLOCKED_DIRNAME, PLUGINS_DIRNAME)
return _ALL_CLASSES
def extra_ie_code(ie, base=None):
for var in STATIC_CLASS_PROPERTIES:
val = getattr(ie, var)
if val != (getattr(base, var) if base else NO_ATTR):
yield f' {var} = {val!r}'
yield ''
for name in CLASS_METHODS:
f = getattr(ie, name)
if not base or f.__func__ != getattr(base, name).__func__:
yield getsource(f)
def build_ies(ies, bases, attr_base):
names = []
for ie in sort_ies(ies, bases):
yield build_lazy_ie(ie, ie.__name__, attr_base)
if ie in ies:
names.append(ie.__name__)
yield f'\n_ALL_CLASSES = [{", ".join(names)}]'
def sort_ies(ies, ignored_bases):
"""find the correct sorting and add the required base classes so that subclasses can be correctly created"""
classes, returned_classes = ies[:-1], set()
assert ies[-1].__name__ == 'GenericIE', 'Last IE must be GenericIE'
while classes:
for c in classes[:]:
bases = set(c.__bases__) - {object, *ignored_bases}
restart = False
for b in bases:
if b not in classes and b not in returned_classes:
assert b.__name__ != 'GenericIE', 'Cannot inherit from GenericIE'
classes.insert(0, b)
restart = True
if restart:
break
if bases <= returned_classes:
yield c
returned_classes.add(c)
classes.remove(c)
break
yield ies[-1]
def build_lazy_ie(ie, name, attr_base):
bases = ', '.join({
'InfoExtractor': 'LazyLoadExtractor',
'SearchInfoExtractor': 'LazyLoadSearchExtractor',
}.get(base.__name__, base.__name__) for base in ie.__bases__)
s = IE_TEMPLATE.format(name=name, module=ie.__module__, bases=bases)
valid_url = getattr(ie, '_VALID_URL', None)
if not valid_url and hasattr(ie, '_make_valid_url'):
valid_url = ie._make_valid_url()
if valid_url:
s += f' _VALID_URL = {valid_url!r}\n'
if not ie._WORKING:
s += ' _WORKING = False\n'
if ie.suitable.__func__ is not InfoExtractor.suitable.__func__:
s += f'\n{getsource(ie.suitable)}'
return s
return s + '\n'.join(extra_ie_code(ie, attr_base))
# find the correct sorting and add the required base classes so that subclasses
# can be correctly created
classes = _ALL_CLASSES[:-1]
ordered_cls = []
while classes:
for c in classes[:]:
bases = set(c.__bases__) - set((object, InfoExtractor, SearchInfoExtractor))
stop = False
for b in bases:
if b not in classes and b not in ordered_cls:
if b.__name__ == 'GenericIE':
exit()
classes.insert(0, b)
stop = True
if stop:
break
if all(b in ordered_cls for b in bases):
ordered_cls.append(c)
classes.remove(c)
break
ordered_cls.append(_ALL_CLASSES[-1])
names = []
for ie in ordered_cls:
name = ie.__name__
src = build_lazy_ie(ie, name)
module_contents.append(src)
if ie in _ALL_CLASSES:
names.append(name)
module_contents.append(
'\n_ALL_CLASSES = [{0}]'.format(', '.join(names)))
module_src = '\n'.join(module_contents) + '\n'
with io.open(lazy_extractors_filename, 'wt', encoding='utf-8') as f:
f.write(module_src)
if __name__ == '__main__':
main()

View File

@@ -2,30 +2,29 @@
# yt-dlp --help | make_readme.py
# This must be run in a console of correct width
from __future__ import unicode_literals
import io
import sys
import re
import sys
README_FILE = 'README.md'
OPTIONS_START = 'General Options:'
OPTIONS_END = 'CONFIGURATION'
EPILOG_START = 'See full documentation'
helptext = sys.stdin.read()
if isinstance(helptext, bytes):
helptext = helptext.decode('utf-8')
helptext = helptext.decode()
with io.open(README_FILE, encoding='utf-8') as f:
oldreadme = f.read()
start, end = helptext.index(f'\n {OPTIONS_START}'), helptext.index(f'\n{EPILOG_START}')
options = re.sub(r'(?m)^ (\w.+)$', r'## \1', helptext[start + 1: end + 1])
header = oldreadme[:oldreadme.index('## General Options:')]
footer = oldreadme[oldreadme.index('# CONFIGURATION'):]
with open(README_FILE, encoding='utf-8') as f:
readme = f.read()
options = helptext[helptext.index(' General Options:'):]
options = re.sub(r'(?m)^ (\w.+)$', r'## \1', options)
options = options + '\n'
header = readme[:readme.index(f'## {OPTIONS_START}')]
footer = readme[readme.index(f'# {OPTIONS_END}'):]
with io.open(README_FILE, 'w', encoding='utf-8') as f:
f.write(header)
f.write(options)
f.write(footer)
with open(README_FILE, 'w', encoding='utf-8') as f:
for part in (header, options, footer):
f.write(part)

View File

@@ -1,48 +1,23 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import io
import optparse
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Import yt_dlp
ROOT_DIR = os.path.join(os.path.dirname(__file__), '..')
sys.path.insert(0, ROOT_DIR)
import yt_dlp
from yt_dlp.extractor import list_extractor_classes
def main():
parser = optparse.OptionParser(usage='%prog OUTFILE.md')
options, args = parser.parse_args()
_, args = parser.parse_args()
if len(args) != 1:
parser.error('Expected an output filename')
outfile, = args
out = '\n'.join(ie.description() for ie in list_extractor_classes() if ie.IE_DESC is not False)
def gen_ies_md(ies):
for ie in ies:
ie_md = '**{0}**'.format(ie.IE_NAME)
ie_desc = getattr(ie, 'IE_DESC', None)
if ie_desc is False:
continue
if ie_desc is not None:
ie_md += ': {0}'.format(ie.IE_DESC)
search_key = getattr(ie, 'SEARCH_KEY', None)
if search_key is not None:
ie_md += f'; "{ie.SEARCH_KEY}:" prefix'
if not ie.working():
ie_md += ' (Currently broken)'
yield ie_md
ies = sorted(yt_dlp.gen_extractors(), key=lambda i: i.IE_NAME.lower())
out = '# Supported sites\n' + ''.join(
' - ' + md + '\n'
for md in gen_ies_md(ies))
with io.open(outfile, 'w', encoding='utf-8') as outf:
outf.write(out)
with open(args[0], 'w', encoding='utf-8') as outf:
outf.write(f'# Supported sites\n{out}\n')
if __name__ == '__main__':

View File

@@ -1,6 +0,0 @@
# source this file in your shell to get a POSIX locale (which will break many programs, but that's kind of the point)
export LC_ALL=POSIX
export LANG=POSIX
export LANGUAGE=POSIX

View File

@@ -1,7 +1,4 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import io
import optparse
import os.path
import re
@@ -32,14 +29,14 @@ def main():
outfile, = args
with io.open(README_FILE, encoding='utf-8') as f:
with open(README_FILE, encoding='utf-8') as f:
readme = f.read()
readme = filter_excluded_sections(readme)
readme = move_sections(readme)
readme = filter_options(readme)
with io.open(outfile, 'w', encoding='utf-8') as outf:
with open(outfile, 'w', encoding='utf-8') as outf:
outf.write(PREFIX + readme)

View File

@@ -1,143 +0,0 @@
# Unused
#!/bin/bash
# IMPORTANT: the following assumptions are made
# * the GH repo is on the origin remote
# * the gh-pages branch is named so locally
# * the git config user.signingkey is properly set
# You will need
# pip install coverage nose rsa wheel
# TODO
# release notes
# make hash on local files
set -e
skip_tests=true
gpg_sign_commits=""
buildserver='localhost:8142'
while true
do
case "$1" in
--run-tests)
skip_tests=false
shift
;;
--gpg-sign-commits|-S)
gpg_sign_commits="-S"
shift
;;
--buildserver)
buildserver="$2"
shift 2
;;
--*)
echo "ERROR: unknown option $1"
exit 1
;;
*)
break
;;
esac
done
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi
version="$1"
major_version=$(echo "$version" | sed -n 's#^\([0-9]*\.[0-9]*\.[0-9]*\).*#\1#p')
if test "$major_version" '!=' "$(date '+%Y.%m.%d')"; then
echo "$version does not start with today's date!"
exit 1
fi
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi
if [ ! -z "`git status --porcelain | grep -v CHANGELOG`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
useless_files=$(find yt_dlp -type f -not -name '*.py')
if [ ! -z "$useless_files" ]; then echo "ERROR: Non-.py files in yt_dlp: $useless_files"; exit 1; fi
if [ ! -f "updates_key.pem" ]; then echo 'ERROR: updates_key.pem missing'; exit 1; fi
if ! type pandoc >/dev/null 2>/dev/null; then echo 'ERROR: pandoc is missing'; exit 1; fi
if ! python3 -c 'import rsa' 2>/dev/null; then echo 'ERROR: python3-rsa is missing'; exit 1; fi
if ! python3 -c 'import wheel' 2>/dev/null; then echo 'ERROR: wheel is missing'; exit 1; fi
read -p "Is Changelog up to date? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
/bin/echo -e "\n### First of all, testing..."
make clean
if $skip_tests ; then
echo 'SKIPPING TESTS'
else
nosetests --verbose --with-coverage --cover-package=yt_dlp --cover-html test --stop || exit 1
fi
/bin/echo -e "\n### Changing version in version.py..."
sed -i "s/__version__ = '.*'/__version__ = '$version'/" yt_dlp/version.py
/bin/echo -e "\n### Changing version in Changelog..."
sed -i "s/<unreleased>/$version/" Changelog.md
/bin/echo -e "\n### Committing documentation, templates and yt_dlp/version.py..."
make README.md CONTRIBUTING.md issuetemplates supportedsites
git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE/1_broken_site.md .github/ISSUE_TEMPLATE/2_site_support_request.md .github/ISSUE_TEMPLATE/3_site_feature_request.md .github/ISSUE_TEMPLATE/4_bug_report.md .github/ISSUE_TEMPLATE/5_feature_request.md .github/ISSUE_TEMPLATE/6_question.md docs/supportedsites.md yt_dlp/version.py Changelog.md
git commit $gpg_sign_commits -m "release $version"
/bin/echo -e "\n### Now tagging, signing and pushing..."
git tag -s -m "Release $version" "$version"
git show "$version"
read -p "Is it good, can I push? (y/n) " -n 1
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
echo
MASTER=$(git rev-parse --abbrev-ref HEAD)
git push origin $MASTER:master
git push origin "$version"
/bin/echo -e "\n### OK, now it is time to build the binaries..."
REV=$(git rev-parse HEAD)
make yt-dlp yt-dlp.tar.gz
read -p "VM running? (y/n) " -n 1
wget "http://$buildserver/build/ytdl-org/youtube-dl/yt-dlp.exe?rev=$REV" -O yt-dlp.exe
mkdir -p "build/$version"
mv yt-dlp yt-dlp.exe "build/$version"
mv yt-dlp.tar.gz "build/$version/yt-dlp-$version.tar.gz"
RELEASE_FILES="yt-dlp yt-dlp.exe yt-dlp-$version.tar.gz"
(cd build/$version/ && md5sum $RELEASE_FILES > MD5SUMS)
(cd build/$version/ && sha1sum $RELEASE_FILES > SHA1SUMS)
(cd build/$version/ && sha256sum $RELEASE_FILES > SHA2-256SUMS)
(cd build/$version/ && sha512sum $RELEASE_FILES > SHA2-512SUMS)
/bin/echo -e "\n### Signing and uploading the new binaries to GitHub..."
for f in $RELEASE_FILES; do gpg --passphrase-repeat 5 --detach-sig "build/$version/$f"; done
ROOT=$(pwd)
python devscripts/create-github-release.py Changelog.md $version "$ROOT/build/$version"
ssh ytdl@yt-dl.org "sh html/update_latest.sh $version"
/bin/echo -e "\n### Now switching to gh-pages..."
git clone --branch gh-pages --single-branch . build/gh-pages
(
set -e
ORIGIN_URL=$(git config --get remote.origin.url)
cd build/gh-pages
"$ROOT/devscripts/gh-pages/add-version.py" $version
"$ROOT/devscripts/gh-pages/update-feed.py"
"$ROOT/devscripts/gh-pages/sign-versions.py" < "$ROOT/updates_key.pem"
"$ROOT/devscripts/gh-pages/generate-download.py"
"$ROOT/devscripts/gh-pages/update-copyright.py"
"$ROOT/devscripts/gh-pages/update-sites.py"
git add *.html *.html.in update
git commit $gpg_sign_commits -m "release $version"
git push "$ROOT" gh-pages
git push "$ORIGIN_URL" gh-pages
)
rm -rf build
make pypi-files
echo "Uploading to PyPi ..."
python setup.py sdist bdist_wheel upload
make clean
/bin/echo -e "\n### DONE!"

View File

@@ -13,4 +13,5 @@ if ["%~1"]==[""] (
exit /b 1
)
set PYTHONWARNINGS=error
pytest %test_set%

View File

@@ -11,4 +11,4 @@ else
exit 1
fi
python3 -m pytest "$test_set"
python3 -bb -Werror -m pytest "$test_set"

View File

@@ -1,49 +0,0 @@
# Unused
#!/usr/bin/env python3
from __future__ import unicode_literals
import itertools
import json
import os
import re
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp.compat import (
compat_print,
compat_urllib_request,
)
from yt_dlp.utils import format_bytes
def format_size(bytes):
return '%s (%d bytes)' % (format_bytes(bytes), bytes)
total_bytes = 0
for page in itertools.count(1):
releases = json.loads(compat_urllib_request.urlopen(
'https://api.github.com/repos/ytdl-org/youtube-dl/releases?page=%s' % page
).read().decode('utf-8'))
if not releases:
break
for release in releases:
compat_print(release['name'])
for asset in release['assets']:
asset_name = asset['name']
total_bytes += asset['download_count'] * asset['size']
if all(not re.match(p, asset_name) for p in (
r'^yt-dlp$',
r'^yt-dlp-\d{4}\.\d{2}\.\d{2}(?:\.\d+)?\.tar\.gz$',
r'^yt-dlp\.exe$')):
continue
compat_print(
' %s size: %s downloads: %d'
% (asset_name, format_size(asset['size']), asset['download_count']))
compat_print('total downloads traffic: %s' % format_size(total_bytes))

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import json
import os
import re
@@ -10,7 +8,6 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp.compat import compat_urllib_request
# usage: python3 ./devscripts/update-formulae.py <path-to-formulae-rb> <version>
# version can be either 0-aligned (yt-dlp version) or normalized (PyPl version)
@@ -20,14 +17,14 @@ normalized_version = '.'.join(str(int(x)) for x in version.split('.'))
pypi_release = json.loads(compat_urllib_request.urlopen(
'https://pypi.org/pypi/yt-dlp/%s/json' % normalized_version
).read().decode('utf-8'))
).read().decode())
tarball_file = next(x for x in pypi_release['urls'] if x['filename'].endswith('.tar.gz'))
sha256sum = tarball_file['digests']['sha256']
url = tarball_file['url']
with open(filename, 'r') as r:
with open(filename) as r:
formulae_text = r.read()
formulae_text = re.sub(r'sha256 "[0-9a-f]*?"', 'sha256 "%s"' % sha256sum, formulae_text)

View File

@@ -1,10 +1,9 @@
#!/usr/bin/env python3
from datetime import datetime
import sys
import subprocess
import sys
from datetime import datetime
with open('yt_dlp/version.py', 'rt') as f:
with open('yt_dlp/version.py') as f:
exec(compile(f.read(), 'yt_dlp/version.py', 'exec'))
old_version = locals()['__version__']

View File

@@ -1,58 +0,0 @@
# UNUSED
#!/bin/bash
# Run with as parameter a setup.py that works in the current directory
# e.g. no os.chdir()
# It will run twice, the first time will crash
set -e
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
if [ ! -d wine-py2exe ]; then
sudo apt-get install wine1.3 axel bsdiff
mkdir wine-py2exe
cd wine-py2exe
export WINEPREFIX=`pwd`
axel -a "http://www.python.org/ftp/python/2.7/python-2.7.msi"
axel -a "http://downloads.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.win32-py2.7.exe"
#axel -a "http://winetricks.org/winetricks"
# http://appdb.winehq.org/objectManager.php?sClass=version&iId=21957
echo "Follow python setup on screen"
wine msiexec /i python-2.7.msi
echo "Follow py2exe setup on screen"
wine py2exe-0.6.9.win32-py2.7.exe
#echo "Follow Microsoft Visual C++ 2008 Redistributable Package setup on screen"
#bash winetricks vcrun2008
rm py2exe-0.6.9.win32-py2.7.exe
rm python-2.7.msi
#rm winetricks
# http://bugs.winehq.org/show_bug.cgi?id=3591
mv drive_c/Python27/Lib/site-packages/py2exe/run.exe drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run.exe "$SCRIPT_DIR/SizeOfImage.patch"
mv drive_c/Python27/Lib/site-packages/py2exe/run_w.exe drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup
bspatch drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run_w.exe "$SCRIPT_DIR/SizeOfImage_w.patch"
cd -
else
export WINEPREFIX="$( cd wine-py2exe && pwd )"
fi
wine "C:\\Python27\\python.exe" "$1" py2exe > "py2exe.log" 2>&1 || true
echo '# Copying python27.dll' >> "py2exe.log"
cp "$WINEPREFIX/drive_c/windows/system32/python27.dll" build/bdist.win32/winexe/bundle-2.7/
wine "C:\\Python27\\python.exe" "$1" py2exe >> "py2exe.log" 2>&1

View File

@@ -1,11 +1,9 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
import os
from os.path import dirname as dirn
import sys
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import yt_dlp
ZSH_COMPLETION_FILE = "completions/zsh/_yt-dlp"
@@ -45,5 +43,5 @@ def build_completion(opt_parser):
f.write(template)
parser = yt_dlp.parseOpts()[0]
parser = yt_dlp.parseOpts(ignore_config_files=True)[0]
build_completion(parser)

1
docs/.gitignore vendored
View File

@@ -1 +0,0 @@
_build/

View File

@@ -1,5 +0,0 @@
---
orphan: true
---
```{include} ../Changelog.md
```

View File

@@ -1,5 +0,0 @@
---
orphan: true
---
```{include} ../Collaborators.md
```

View File

@@ -1,5 +0,0 @@
---
orphan: true
---
```{include} ../Contributing.md
```

View File

@@ -1,6 +0,0 @@
---
orphan: true
---
# LICENSE
```{include} ../LICENSE
```

View File

@@ -1,177 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yt-dlp.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yt-dlp.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/yt-dlp"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yt-dlp"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

View File

@@ -1,2 +0,0 @@
```{include} ../README.md
```

View File

@@ -1,68 +0,0 @@
# coding: utf-8
#
# yt-dlp documentation build configuration file
import sys
import os
# Allows to import yt-dlp
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration ------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'myst_parser',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The master toctree document.
master_doc = 'README'
# General information about the project.
project = u'yt-dlp'
author = u'yt-dlp'
copyright = u'UNLICENSE'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
from yt_dlp.version import __version__
version = __version__
# The full version, including alpha/beta/rc tags.
release = version
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Disable highlights
highlight_language = 'none'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# Enable heading anchors
myst_heading_anchors = 4
# Suppress heading warnings
suppress_warnings = [
'myst.header',
]

View File

@@ -1 +0,0 @@
myst-parser

View File

@@ -1,5 +0,0 @@
---
orphan: true
---
```{include} ../supportedsites.md
```

View File

@@ -1,6 +0,0 @@
---
orphan: true
---
# ytdlp_plugins
See [https://github.com/yt-dlp/yt-dlp/tree/master/ytdlp_plugins](https://github.com/yt-dlp/yt-dlp/tree/master/ytdlp_plugins).

View File

@@ -1,32 +1,41 @@
#!/usr/bin/env python3
# coding: utf-8
import os
import platform
import sys
from PyInstaller.utils.hooks import collect_submodules
from PyInstaller.__main__ import run as run_pyinstaller
OS_NAME = platform.system()
if OS_NAME == 'Windows':
from PyInstaller.utils.win32.versioninfo import (
VarStruct, VarFileInfo, StringStruct, StringTable,
StringFileInfo, FixedFileInfo, VSVersionInfo, SetVersion,
FixedFileInfo,
SetVersion,
StringFileInfo,
StringStruct,
StringTable,
VarFileInfo,
VarStruct,
VSVersionInfo,
)
elif OS_NAME == 'Darwin':
pass
else:
raise Exception('{OS_NAME} is not supported')
raise Exception(f'{OS_NAME} is not supported')
ARCH = platform.architecture()[0][:2]
def main():
opts = parse_options()
version = read_version()
version = read_version('yt_dlp/version.py')
suffix = '_macos' if OS_NAME == 'Darwin' else '_x86' if ARCH == '32' else ''
final_file = 'dist/%syt-dlp%s%s' % (
'yt-dlp/' if '--onedir' in opts else '', suffix, '.exe' if OS_NAME == 'Windows' else '')
onedir = '--onedir' in opts or '-D' in opts
if not onedir and '-F' not in opts and '--onefile' not in opts:
opts.append('--onefile')
name = 'yt-dlp%s' % ('_macos' if OS_NAME == 'Darwin' else '_x86' if ARCH == '32' else '')
final_file = ''.join((
'dist/', f'{name}/' if onedir else '', name, '.exe' if OS_NAME == 'Windows' else ''))
print(f'Building yt-dlp v{version} {ARCH}bit for {OS_NAME} with options {opts}')
print('Remember to update the version using "devscripts/update-version.py"')
@@ -36,20 +45,20 @@ def main():
print(f'Destination: {final_file}\n')
opts = [
f'--name=yt-dlp{suffix}',
f'--name={name}',
'--icon=devscripts/logo.ico',
'--upx-exclude=vcruntime140.dll',
'--noconfirm',
# NB: Modules that are only imported dynamically must be added here.
# --collect-submodules may not work correctly if user has a yt-dlp installed via PIP
'--hidden-import=yt_dlp.compat._legacy',
*dependency_options(),
*opts,
'yt_dlp/__main__.py',
]
print(f'Running PyInstaller with {opts}')
import PyInstaller.__main__
PyInstaller.__main__.run(opts)
run_pyinstaller(opts)
set_version_info(final_file, version)
@@ -60,12 +69,14 @@ def parse_options():
if ARCH != opts[0]:
raise Exception(f'{opts[0]}bit executable cannot be built on a {ARCH}bit system')
opts = opts[1:]
return opts or ['--onefile']
return opts
def read_version():
exec(compile(open('yt_dlp/version.py').read(), 'yt_dlp/version.py', 'exec'))
return locals()['__version__']
# Get the version from yt_dlp/version.py without importing the package
def read_version(fname):
with open(fname, encoding='utf-8') as f:
exec(compile(f.read(), fname, 'exec'))
return locals()['__version__']
def version_to_list(version):
@@ -74,10 +85,12 @@ def version_to_list(version):
def dependency_options():
dependencies = [pycryptodome_module(), 'mutagen', 'brotli'] + collect_submodules('websockets')
excluded_modules = ['test', 'ytdlp_plugins', 'youtube-dl', 'youtube-dlc']
# Due to the current implementation, these are auto-detected, but explicitly add them just in case
dependencies = [pycryptodome_module(), 'mutagen', 'brotli', 'certifi', 'websockets']
excluded_modules = ['test', 'ytdlp_plugins', 'youtube_dl', 'youtube_dlc']
yield from (f'--hidden-import={module}' for module in dependencies)
yield '--collect-submodules=websockets'
yield from (f'--exclude-module={module}' for module in excluded_modules)

View File

@@ -2,4 +2,5 @@ mutagen
pycryptodomex
websockets
brotli; platform_python_implementation=='CPython'
brotlicffi; platform_python_implementation!='CPython'
brotlicffi; platform_python_implementation!='CPython'
certifi

View File

@@ -2,5 +2,5 @@
universal = True
[flake8]
exclude = yt_dlp/extractor/__init__.py,devscripts/buildserver.py,devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git,venv,devscripts/create-github-release.py,devscripts/release.sh,devscripts/show-downloads-statistics.py
ignore = E402,E501,E731,E741,W503
exclude = devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git,venv
ignore = E402,E501,E731,E741,W503

View File

@@ -1,29 +1,38 @@
#!/usr/bin/env python3
# coding: utf-8
import os.path
import warnings
import sys
import warnings
try:
from setuptools import setup, Command, find_packages
from setuptools import Command, find_packages, setup
setuptools_available = True
except ImportError:
from distutils.core import setup, Command
from distutils.core import Command, setup
setuptools_available = False
from distutils.spawn import spawn
# Get the version from yt_dlp/version.py without importing the package
exec(compile(open('yt_dlp/version.py').read(), 'yt_dlp/version.py', 'exec'))
def read(fname):
with open(fname, encoding='utf-8') as f:
return f.read()
# Get the version from yt_dlp/version.py without importing the package
def read_version(fname):
exec(compile(read(fname), fname, 'exec'))
return locals()['__version__']
VERSION = read_version('yt_dlp/version.py')
DESCRIPTION = 'A youtube-dl fork with additional features and patches'
LONG_DESCRIPTION = '\n\n'.join((
'Official repository: <https://github.com/yt-dlp/yt-dlp>',
'**PS**: Some links in this document will not work since this is a copy of the README.md from Github',
open('README.md', encoding='utf-8').read()))
read('README.md')))
REQUIREMENTS = open('requirements.txt', encoding='utf-8').read().splitlines()
REQUIREMENTS = read('requirements.txt').splitlines()
if sys.argv[1:2] == ['py2exe']:
@@ -35,11 +44,11 @@ if sys.argv[1:2] == ['py2exe']:
'console': [{
'script': './yt_dlp/__main__.py',
'dest_base': 'yt-dlp',
'version': __version__,
'version': VERSION,
'description': DESCRIPTION,
'comments': LONG_DESCRIPTION.split('\n')[0],
'product_name': 'yt-dlp',
'product_version': __version__,
'product_version': VERSION,
}],
'options': {
'py2exe': {
@@ -49,6 +58,8 @@ if sys.argv[1:2] == ['py2exe']:
'dist_dir': './dist',
'excludes': ['Crypto', 'Cryptodome'], # py2exe cannot import Crypto
'dll_excludes': ['w9xpopen.exe', 'crypt32.dll'],
# Modules that are only imported dynamically must be added here
'includes': ['yt_dlp.compat._legacy'],
}
},
'zipfile': None
@@ -106,7 +117,7 @@ else:
setup(
name='yt-dlp',
version=__version__,
version=VERSION,
maintainer='pukkandan',
maintainer_email='pukkandan.ytdlp@gmail.com',
description=DESCRIPTION,
@@ -116,7 +127,7 @@ setup(
packages=packages,
install_requires=REQUIREMENTS,
project_urls={
'Documentation': 'https://yt-dlp.readthedocs.io',
'Documentation': 'https://github.com/yt-dlp/yt-dlp#readme',
'Source': 'https://github.com/yt-dlp/yt-dlp',
'Tracker': 'https://github.com/yt-dlp/yt-dlp/issues',
'Funding': 'https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators',

View File

@@ -24,12 +24,12 @@
- **abcnews:video**
- **abcotvs**: ABC Owned Television Stations
- **abcotvs:clips**
- **AbemaTV**
- **AbemaTV**: [<abbr title="netrc machine"><em>abematv</em></abbr>]
- **AbemaTVTitle**
- **AcademicEarth:Course**
- **acast**
- **acast:channel**
- **ADN**: Anime Digital Network
- **ADN**: [<abbr title="netrc machine"><em>animedigitalnetwork</em></abbr>] Anime Digital Network
- **AdobeConnect**
- **adobetv**
- **adobetv:channel**
@@ -40,8 +40,9 @@
- **aenetworks**: A+E Networks: A&E, Lifetime, History.com, FYI Network and History Vault
- **aenetworks:collection**
- **aenetworks:show**
- **afreecatv**: afreecatv.com
- **afreecatv:live**: afreecatv.com
- **afreecatv**: [<abbr title="netrc machine"><em>afreecatv</em></abbr>] afreecatv.com
- **afreecatv:live**: [<abbr title="netrc machine"><em>afreecatv</em></abbr>] afreecatv.com
- **afreecatv:user**
- **AirMozilla**
- **AliExpressLive**
- **AlJazeera**
@@ -49,8 +50,8 @@
- **AlphaPorno**
- **Alsace20TV**
- **Alsace20TVEmbed**
- **Alura**
- **AluraCourse**
- **Alura**: [<abbr title="netrc machine"><em>alura</em></abbr>]
- **AluraCourse**: [<abbr title="netrc machine"><em>aluracourse</em></abbr>]
- **Amara**
- **AmazonStore**
- **AMCNetworks**
@@ -59,9 +60,9 @@
- **AmHistoryChannel**
- **anderetijden**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
- **AnimalPlanet**
- **AnimeLab**
- **AnimeLabShows**
- **AnimeOnDemand**
- **AnimeLab**: [<abbr title="netrc machine"><em>animelab</em></abbr>]
- **AnimeLabShows**: [<abbr title="netrc machine"><em>animelab</em></abbr>]
- **AnimeOnDemand**: [<abbr title="netrc machine"><em>animeondemand</em></abbr>]
- **ant1newsgr:article**: ant1news.gr articles
- **ant1newsgr:embed**: ant1news.gr embedded videos
- **ant1newsgr:watch**: ant1news.gr videos
@@ -87,7 +88,7 @@
- **ArteTVPlaylist**
- **AsianCrush**
- **AsianCrushPlaylist**
- **AtresPlayer**
- **AtresPlayer**: [<abbr title="netrc machine"><em>atresplayer</em></abbr>]
- **ATTTechChannel**
- **ATVAt**
- **AudiMedia**
@@ -104,6 +105,8 @@
- **awaan:video**
- **AZMedien**: AZ Medien videos
- **BaiduVideo**: 百度视频
- **BanBye**
- **BanByeChannel**
- **bandaichannel**
- **Bandcamp**
- **Bandcamp:album**
@@ -111,13 +114,13 @@
- **Bandcamp:weekly**
- **bangumi.bilibili.com**: BiliBili番剧
- **BannedVideo**
- **bbc**: BBC
- **bbc.co.uk**: BBC iPlayer
- **bbc**: [<abbr title="netrc machine"><em>bbc</em></abbr>] BBC
- **bbc.co.uk**: [<abbr title="netrc machine"><em>bbc</em></abbr>] BBC iPlayer
- **bbc.co.uk:article**: BBC articles
- **bbc.co.uk:iplayer:episodes**
- **bbc.co.uk:iplayer:group**
- **bbc.co.uk:playlist**
- **BBVTV**
- **BBVTV**: [<abbr title="netrc machine"><em>bbvtv</em></abbr>]
- **Beatport**
- **Beeg**
- **BehindKink**
@@ -139,8 +142,9 @@
- **BilibiliChannel**
- **BiliBiliPlayer**
- **BiliBiliSearch**: Bilibili video search; "bilisearch:" prefix
- **BiliIntl**
- **BiliIntlSeries**
- **BiliIntl**: [<abbr title="netrc machine"><em>biliintl</em></abbr>]
- **BiliIntlSeries**: [<abbr title="netrc machine"><em>biliintl</em></abbr>]
- **BiliLive**
- **BioBioChileTV**
- **Biography**
- **BIQLE**
@@ -245,13 +249,14 @@
- **cpac:playlist**
- **Cracked**
- **Crackle**
- **Craftsy**
- **CrooksAndLiars**
- **CrowdBunker**
- **CrowdBunkerChannel**
- **crunchyroll**
- **crunchyroll:beta**
- **crunchyroll:playlist**
- **crunchyroll:playlist:beta**
- **crunchyroll**: [<abbr title="netrc machine"><em>crunchyroll</em></abbr>]
- **crunchyroll:beta**: [<abbr title="netrc machine"><em>crunchyroll</em></abbr>]
- **crunchyroll:playlist**: [<abbr title="netrc machine"><em>crunchyroll</em></abbr>]
- **crunchyroll:playlist:beta**: [<abbr title="netrc machine"><em>crunchyroll</em></abbr>]
- **CSpan**: C-SPAN
- **CSpanCongress**
- **CtsNews**: 華視新聞
@@ -259,16 +264,18 @@
- **CTVNews**
- **cu.ntv.co.jp**: Nippon Television Network
- **CultureUnplugged**
- **curiositystream**
- **curiositystream:collections**
- **curiositystream:series**
- **curiositystream**: [<abbr title="netrc machine"><em>curiositystream</em></abbr>]
- **curiositystream:collections**: [<abbr title="netrc machine"><em>curiositystream</em></abbr>]
- **curiositystream:series**: [<abbr title="netrc machine"><em>curiositystream</em></abbr>]
- **CWTV**
- **Cybrary**: [<abbr title="netrc machine"><em>cybrary</em></abbr>]
- **CybraryCourse**: [<abbr title="netrc machine"><em>cybrary</em></abbr>]
- **Daftsex**
- **DagelijkseKost**: dagelijksekost.een.be
- **DailyMail**
- **dailymotion**
- **dailymotion:playlist**
- **dailymotion:user**
- **dailymotion**: [<abbr title="netrc machine"><em>dailymotion</em></abbr>]
- **dailymotion:playlist**: [<abbr title="netrc machine"><em>dailymotion</em></abbr>]
- **dailymotion:user**: [<abbr title="netrc machine"><em>dailymotion</em></abbr>]
- **damtomo:record**
- **damtomo:video**
- **daum.net**
@@ -285,7 +292,7 @@
- **DestinationAmerica**
- **DHM**: Filmarchiv - Deutsches Historisches Museum
- **Digg**
- **DigitalConcertHall**: DigitalConcertHall extractor
- **DigitalConcertHall**: [<abbr title="netrc machine"><em>digitalconcerthall</em></abbr>] DigitalConcertHall extractor
- **DigitallySpeaking**
- **Digiteka**
- **Discovery**
@@ -309,7 +316,7 @@
- **DRBonanza**
- **Drooble**
- **Dropbox**
- **Dropout**
- **Dropout**: [<abbr title="netrc machine"><em>dropout</em></abbr>]
- **DropoutSeason**
- **DrTuber**
- **drtv**
@@ -328,7 +335,7 @@
- **egghead:lesson**: egghead.io lesson
- **ehftv**
- **eHow**
- **EinsUndEinsTV**
- **EinsUndEinsTV**: [<abbr title="netrc machine"><em>1und1tv</em></abbr>]
- **Einthusan**
- **eitb.tv**
- **EllenTube**
@@ -342,7 +349,7 @@
- **Epicon**
- **EpiconSeries**
- **Eporner**
- **EroProfile**
- **EroProfile**: [<abbr title="netrc machine"><em>eroprofile</em></abbr>]
- **EroProfile:album**
- **ertflix**: ERTFLIX videos
- **ertflix:codename**: ERTFLIX videos by codename
@@ -355,20 +362,21 @@
- **Europa**
- **EuropeanTour**
- **EUScreen**
- **EWETV**
- **EWETV**: [<abbr title="netrc machine"><em>ewetv</em></abbr>]
- **ExpoTV**
- **Expressen**
- **ExtremeTube**
- **EyedoTV**
- **facebook**
- **facebook**: [<abbr title="netrc machine"><em>facebook</em></abbr>]
- **FacebookPluginsVideo**
- **fancode:live**
- **fancode:vod**
- **fancode:live**: [<abbr title="netrc machine"><em>fancode</em></abbr>]
- **fancode:vod**: [<abbr title="netrc machine"><em>fancode</em></abbr>]
- **faz.net**
- **fc2**
- **fc2**: [<abbr title="netrc machine"><em>fc2</em></abbr>]
- **fc2:embed**
- **fc2:live**
- **Fczenit**
- **Fifa**
- **Filmmodu**
- **filmon**
- **filmon:channel**
@@ -395,19 +403,19 @@
- **FranceTVSite**
- **Freesound**
- **freespeech.org**
- **FrontendMasters**
- **FrontendMastersCourse**
- **FrontendMastersLesson**
- **FrontendMasters**: [<abbr title="netrc machine"><em>frontendmasters</em></abbr>]
- **FrontendMastersCourse**: [<abbr title="netrc machine"><em>frontendmasters</em></abbr>]
- **FrontendMastersLesson**: [<abbr title="netrc machine"><em>frontendmasters</em></abbr>]
- **FujiTVFODPlus7**
- **Funimation**
- **funimation:page**
- **funimation:show**
- **Funimation**: [<abbr title="netrc machine"><em>funimation</em></abbr>]
- **funimation:page**: [<abbr title="netrc machine"><em>funimation</em></abbr>]
- **funimation:show**: [<abbr title="netrc machine"><em>funimation</em></abbr>]
- **Funk**
- **Fusion**
- **Fux**
- **Gab**
- **GabTV**
- **Gaia**
- **Gaia**: [<abbr title="netrc machine"><em>gaia</em></abbr>]
- **GameInformer**
- **GameJolt**
- **GameJoltCommunity**
@@ -419,20 +427,19 @@
- **GameStar**
- **Gaskrank**
- **Gazeta**
- **GDCVault**
- **GDCVault**: [<abbr title="netrc machine"><em>gdcvault</em></abbr>]
- **GediDigital**
- **gem.cbc.ca**
- **gem.cbc.ca**: [<abbr title="netrc machine"><em>cbcgem</em></abbr>]
- **gem.cbc.ca:live**
- **gem.cbc.ca:playlist**
- **generic**: Generic downloader that works on some sites
- **Gettr**
- **GettrStreaming**
- **Gfycat**
- **GiantBomb**
- **Giga**
- **GlattvisionTV**
- **GlattvisionTV**: [<abbr title="netrc machine"><em>glattvisiontv</em></abbr>]
- **Glide**: Glide mobile video messages (glide.me)
- **Globo**
- **Globo**: [<abbr title="netrc machine"><em>globo</em></abbr>]
- **GloboArticle**
- **glomex**: Glomex videos
- **glomex:embed**: Glomex embedded videos
@@ -441,6 +448,7 @@
- **GodTube**
- **Gofile**
- **Golem**
- **goodgame:stream**
- **google:podcasts**
- **google:podcasts:feed**
- **GoogleDrive**
@@ -449,6 +457,8 @@
- **GoToStage**
- **GPUTechConf**
- **Gronkh**
- **gronkh:feed**
- **gronkh:vods**
- **Groupon**
- **hbo**
- **HearThisAt**
@@ -460,7 +470,7 @@
- **hgtv.com:show**
- **HGTVDe**
- **HGTVUsa**
- **HiDive**
- **HiDive**: [<abbr title="netrc machine"><em>hidive</em></abbr>]
- **HistoricFilms**
- **history:player**
- **history:topic**: History.com Topic
@@ -475,8 +485,8 @@
- **Howcast**
- **HowStuffWorks**
- **hrfernsehen**
- **HRTi**
- **HRTiPlaylist**
- **HRTi**: [<abbr title="netrc machine"><em>hrti</em></abbr>]
- **HRTiPlaylist**: [<abbr title="netrc machine"><em>hrti</em></abbr>]
- **HSEProduct**
- **HSEShow**
- **Huajiao**: 花椒直播
@@ -484,7 +494,9 @@
- **Hungama**
- **HungamaAlbumPlaylist**
- **HungamaSong**
- **huya:live**: huya.com
- **Hypem**
- **Icareus**
- **ign.com**
- **IGNArticle**
- **IGNVideo**
@@ -499,19 +511,21 @@
- **Inc**
- **IndavideoEmbed**
- **InfoQ**
- **Instagram**
- **instagram:story**
- **instagram:tag**: Instagram hashtag search URLs
- **instagram:user**: Instagram user profile
- **Instagram**: [<abbr title="netrc machine"><em>instagram</em></abbr>]
- **instagram:story**: [<abbr title="netrc machine"><em>instagram</em></abbr>]
- **instagram:tag**: [<abbr title="netrc machine"><em>instagram</em></abbr>] Instagram hashtag search URLs
- **instagram:user**: [<abbr title="netrc machine"><em>instagram</em></abbr>] Instagram user profile
- **InstagramIOS**: IOS instagram:// URL
- **Internazionale**
- **InternetVideoArchive**
- **InvestigationDiscovery**
- **IPrima**
- **IPrima**: [<abbr title="netrc machine"><em>iprima</em></abbr>]
- **IPrimaCNN**
- **iq.com**: International version of iQiyi
- **iq.com:album**
- **iqiyi**: 爱奇艺
- **iqiyi**: [<abbr title="netrc machine"><em>iqiyi</em></abbr>] 爱奇艺
- **ITProTV**
- **ITProTVCourse**
- **ITTF**
- **ITV**
- **ITVBTCC**
@@ -519,7 +533,11 @@
- **ivi:compilation**: ivi.ru compilations
- **ivideon**: Ivideon TV
- **Iwara**
- **iwara:playlist**
- **iwara:user**
- **Izlesene**
- **Jable**
- **JablePlaylist**
- **Jamendo**
- **JamendoAlbum**
- **JeuxVideo**
@@ -555,6 +573,9 @@
- **la7.it:podcast**
- **laola1tv**
- **laola1tv:embed**
- **LastFM**
- **LastFMPlaylist**
- **LastFMUser**
- **lbry**
- **lbry:channel**
- **LCI**
@@ -562,9 +583,9 @@
- **LcpPlay**
- **Le**: 乐视网
- **Lecture2Go**
- **Lecturio**
- **LecturioCourse**
- **LecturioDeCourse**
- **Lecturio**: [<abbr title="netrc machine"><em>lecturio</em></abbr>]
- **LecturioCourse**: [<abbr title="netrc machine"><em>lecturio</em></abbr>]
- **LecturioDeCourse**: [<abbr title="netrc machine"><em>lecturio</em></abbr>]
- **LEGO**
- **Lemonde**
- **Lenta**
@@ -573,15 +594,17 @@
- **Libsyn**
- **life**: Life.ru
- **life:embed**
- **likee**
- **likee:user**
- **limelight**
- **limelight:channel**
- **limelight:channel_list**
- **LineLive**
- **LineLiveChannel**
- **LinkedIn**
- **linkedin:learning**
- **linkedin:learning:course**
- **LinuxAcademy**
- **LinkedIn**: [<abbr title="netrc machine"><em>linkedin</em></abbr>]
- **linkedin:learning**: [<abbr title="netrc machine"><em>linkedin</em></abbr>]
- **linkedin:learning:course**: [<abbr title="netrc machine"><em>linkedin</em></abbr>]
- **LinuxAcademy**: [<abbr title="netrc machine"><em>linuxacademy</em></abbr>]
- **LiTV**
- **LiveJournal**
- **livestream**
@@ -591,9 +614,10 @@
- **loc**: Library of Congress
- **LocalNews8**
- **LoveHomePorn**
- **lrt.lt**
- **lynda**: lynda.com videos
- **lynda:course**: lynda.com online courses
- **LRTStream**
- **LRTVOD**
- **lynda**: [<abbr title="netrc machine"><em>lynda</em></abbr>] lynda.com videos
- **lynda:course**: [<abbr title="netrc machine"><em>lynda</em></abbr>] lynda.com online courses
- **m6**
- **MagentaMusik360**
- **mailru**: Видео@Mail.Ru
@@ -603,6 +627,7 @@
- **MallTV**
- **mangomolo:live**
- **mangomolo:video**
- **MangoTV**: 芒果TV
- **ManotoTV**: Manoto TV (Episode)
- **ManotoTVLive**: Manoto TV (Live)
- **ManotoTVShow**: Manoto TV (Show)
@@ -611,6 +636,7 @@
- **Markiza**
- **MarkizaPage**
- **massengeschmack.tv**
- **Masters**
- **MatchTV**
- **MDR**: MDR.DE and KiKA
- **MedalTV**
@@ -635,7 +661,6 @@
- **Metacritic**
- **mewatch**
- **Mgoon**
- **MGTV**: 芒果TV
- **MiaoPai**
- **microsoftstream**: Microsoft Stream
- **mildom**: Record ongoing live by specific user in Mildom
@@ -660,7 +685,7 @@
- **MLBVideo**
- **MLSSoccer**
- **Mnet**
- **MNetTV**
- **MNetTV**: [<abbr title="netrc machine"><em>mnettv</em></abbr>]
- **MoeVideo**: LetitBit video services: moevideo.net, playreplay.net and videochart.net
- **Mofosex**
- **MofosexEmbed**
@@ -671,6 +696,7 @@
- **Motorsport**: motorsport.com
- **MovieClips**
- **MovieFap**
- **Moviepilot**
- **Moviezine**
- **MovingImage**
- **MSN**
@@ -705,7 +731,6 @@
- **MyVideoGe**
- **MyVidster**
- **MyviEmbed**
- **MyVisionTV**
- **n-tv.de**
- **N1Info:article**
- **N1InfoAsset**
@@ -732,8 +757,9 @@
- **ndr:embed**
- **ndr:embed:base**
- **NDTV**
- **Nebula**
- **nebula:collection**
- **Nebula**: [<abbr title="netrc machine"><em>watchnebula</em></abbr>]
- **nebula:channel**: [<abbr title="netrc machine"><em>watchnebula</em></abbr>]
- **nebula:subscriptions**: [<abbr title="netrc machine"><em>watchnebula</em></abbr>]
- **NerdCubedFeed**
- **netease:album**: 网易云音乐 - 专辑
- **netease:djradio**: 网易云音乐 - 电台
@@ -742,7 +768,7 @@
- **netease:program**: 网易云音乐 - 电台节目
- **netease:singer**: 网易云音乐 - 歌手
- **netease:song**: 网易云音乐
- **NetPlus**
- **NetPlus**: [<abbr title="netrc machine"><em>netplus</em></abbr>]
- **Netzkino**
- **Newgrounds**
- **Newgrounds:playlist**
@@ -756,8 +782,8 @@
- **NexxEmbed**
- **NFB**
- **NFHSNetwork**
- **nfl.com** (Currently broken)
- **nfl.com:article** (Currently broken)
- **nfl.com**: (**Currently broken**)
- **nfl.com:article**: (**Currently broken**)
- **NhkForSchoolBangumi**
- **NhkForSchoolProgramList**
- **NhkForSchoolSubject**: Portal page for each school subjects, like Japanese (kokugo, 国語) or math (sansuu/suugaku or 算数・数学)
@@ -769,7 +795,7 @@
- **nickelodeon:br**
- **nickelodeonru**
- **nicknight**
- **niconico**: ニコニコ動画
- **niconico**: [<abbr title="netrc machine"><em>niconico</em></abbr>] ニコニコ動画
- **niconico:history**: NicoNico user history. Requires cookies.
- **niconico:playlist**
- **niconico:series**
@@ -782,7 +808,7 @@
- **Nitter**
- **njoy**: N-JOY
- **njoy:embed**
- **NJPWWorld**: 新日本プロレスワールド
- **NJPWWorld**: [<abbr title="netrc machine"><em>njpwworld</em></abbr>] 新日本プロレスワールド
- **NobelPrize**
- **NonkTube**
- **NoodleMagazine**
@@ -855,14 +881,17 @@
- **orf:tvthek**: ORF TVthek
- **orf:vorarlberg**: Radio Vorarlberg
- **orf:wien**: Radio Wien
- **OsnatelTV**
- **OsnatelTV**: [<abbr title="netrc machine"><em>osnateltv</em></abbr>]
- **OutsideTV**
- **PacktPub**
- **PacktPub**: [<abbr title="netrc machine"><em>packtpub</em></abbr>]
- **PacktPubCourse**
- **PalcoMP3:artist**
- **PalcoMP3:song**
- **PalcoMP3:video**
- **pandora.tv**: 판도라TV
- **Panopto**
- **PanoptoList**
- **PanoptoPlaylist**
- **ParamountNetwork**
- **ParamountPlus**
- **ParamountPlusSeries**
@@ -876,7 +905,7 @@
- **peer.tv**
- **PeerTube**
- **PeerTube:Playlist**
- **peloton**
- **peloton**: [<abbr title="netrc machine"><em>peloton</em></abbr>]
- **peloton:live**: Peloton Live
- **People**
- **PerformGroup**
@@ -885,7 +914,7 @@
- **PhilharmonieDeParis**: Philharmonie de Paris
- **phoenix.de**
- **Photobucket**
- **Piapro**
- **Piapro**: [<abbr title="netrc machine"><em>piapro</em></abbr>]
- **Picarto**
- **PicartoVod**
- **Piksel**
@@ -896,25 +925,27 @@
- **pixiv:sketch:user**
- **Pladform**
- **PlanetMarathi**
- **Platzi**
- **PlatziCourse**
- **Platzi**: [<abbr title="netrc machine"><em>platzi</em></abbr>]
- **PlatziCourse**: [<abbr title="netrc machine"><em>platzi</em></abbr>]
- **play.fm**
- **player.sky.it**
- **PlayPlusTV**
- **PlayPlusTV**: [<abbr title="netrc machine"><em>playplustv</em></abbr>]
- **PlayStuff**
- **PlaysTV**
- **Playtvak**: Playtvak.cz, iDNES.cz and Lidovky.cz
- **Playvid**
- **PlayVids**
- **Playwire**
- **pluralsight**
- **pluralsight**: [<abbr title="netrc machine"><em>pluralsight</em></abbr>]
- **pluralsight:course**
- **PlutoTV**
- **Podchaser**
- **podomatic**
- **Pokemon**
- **PokemonSoundLibrary**
- **PokemonWatch**
- **PokerGo**
- **PokerGoCollection**
- **PokerGo**: [<abbr title="netrc machine"><em>pokergo</em></abbr>]
- **PokerGoCollection**: [<abbr title="netrc machine"><em>pokergo</em></abbr>]
- **PolsatGo**
- **PolskieRadio**
- **polskieradio:kierowcow**
@@ -929,11 +960,11 @@
- **Pornez**
- **PornFlip**
- **PornHd**
- **PornHub**: PornHub and Thumbzilla
- **PornHubPagedVideoList**
- **PornHubPlaylist**
- **PornHubUser**
- **PornHubUserVideosUpload**
- **PornHub**: [<abbr title="netrc machine"><em>pornhub</em></abbr>] PornHub and Thumbzilla
- **PornHubPagedVideoList**: [<abbr title="netrc machine"><em>pornhub</em></abbr>]
- **PornHubPlaylist**: [<abbr title="netrc machine"><em>pornhub</em></abbr>]
- **PornHubUser**: [<abbr title="netrc machine"><em>pornhub</em></abbr>]
- **PornHubUserVideosUpload**: [<abbr title="netrc machine"><em>pornhub</em></abbr>]
- **Pornotube**
- **PornoVoisines**
- **PornoXO**
@@ -955,10 +986,8 @@
- **qqmusic:playlist**: QQ音乐 - 歌单
- **qqmusic:singer**: QQ音乐 - 歌手
- **qqmusic:toplist**: QQ音乐 - 排行榜
- **QuantumTV**
- **QuantumTV**: [<abbr title="netrc machine"><em>quantumtv</em></abbr>]
- **Qub**
- **Quickline**
- **QuicklineLive**
- **R7**
- **R7Article**
- **Radiko**
@@ -1010,11 +1039,12 @@
- **RICE**
- **RMCDecouverte**
- **RockstarGames**
- **Rokfin**
- **rokfin:channel**
- **rokfin:stack**
- **RoosterTeeth**
- **RoosterTeethSeries**
- **Rokfin**: [<abbr title="netrc machine"><em>rokfin</em></abbr>]
- **rokfin:channel**: Rokfin Channels
- **rokfin:search**: Rokfin Search; "rkfnsearch:" prefix
- **rokfin:stack**: Rokfin Stacks
- **RoosterTeeth**: [<abbr title="netrc machine"><em>roosterteeth</em></abbr>]
- **RoosterTeethSeries**: [<abbr title="netrc machine"><em>roosterteeth</em></abbr>]
- **RottenTomatoes**
- **Rozhlas**
- **RTBF**
@@ -1053,12 +1083,12 @@
- **Ruutu**
- **Ruv**
- **ruv.is:spila**
- **safari**: safaribooksonline.com online video
- **safari:api**
- **safari:course**: safaribooksonline.com online courses
- **safari**: [<abbr title="netrc machine"><em>safari</em></abbr>] safaribooksonline.com online video
- **safari:api**: [<abbr title="netrc machine"><em>safari</em></abbr>]
- **safari:course**: [<abbr title="netrc machine"><em>safari</em></abbr>] safaribooksonline.com online courses
- **Saitosan**
- **SAKTV**
- **SaltTV**
- **SAKTV**: [<abbr title="netrc machine"><em>saktv</em></abbr>]
- **SaltTV**: [<abbr title="netrc machine"><em>salttv</em></abbr>]
- **SampleFocus**
- **Sapo**: SAPO Vídeos
- **savefrom.net**
@@ -1070,8 +1100,8 @@
- **ScreencastOMatic**
- **ScrippsNetworks**
- **scrippsnetworks:watch**
- **SCTE**
- **SCTECourse**
- **SCTE**: [<abbr title="netrc machine"><em>scte</em></abbr>]
- **SCTECourse**: [<abbr title="netrc machine"><em>scte</em></abbr>]
- **Seeker**
- **SenateGov**
- **SenateISVP**
@@ -1080,7 +1110,7 @@
- **Sexu**
- **SeznamZpravy**
- **SeznamZpravyArticle**
- **Shahid**
- **Shahid**: [<abbr title="netrc machine"><em>shahid</em></abbr>]
- **ShahidShow**
- **Shared**: shared.sx
- **ShemarooMe**
@@ -1105,15 +1135,15 @@
- **Slutload**
- **Snotr**
- **Sohu**
- **SonyLIV**
- **SonyLIV**: [<abbr title="netrc machine"><em>sonyliv</em></abbr>]
- **SonyLIVSeries**
- **soundcloud**
- **soundcloud:playlist**
- **soundcloud:related**
- **soundcloud:search**: Soundcloud search; "scsearch:" prefix
- **soundcloud:set**
- **soundcloud:trackstation**
- **soundcloud:user**
- **soundcloud**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **soundcloud:playlist**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **soundcloud:related**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **soundcloud:search**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>] Soundcloud search; "scsearch:" prefix
- **soundcloud:set**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **soundcloud:trackstation**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **soundcloud:user**: [<abbr title="netrc machine"><em>soundcloud</em></abbr>]
- **SoundcloudEmbed**
- **soundgasm**
- **soundgasm:profile**
@@ -1131,8 +1161,8 @@
- **Sport5**
- **SportBox**
- **SportDeutschland**
- **spotify**
- **spotify:show**
- **spotify**: Spotify episodes
- **spotify:show**: Spotify shows
- **Spreaker**
- **SpreakerPage**
- **SpreakerShow**
@@ -1174,13 +1204,13 @@
- **Tass**
- **TBS**
- **TDSLifeway**
- **Teachable**
- **TeachableCourse**
- **Teachable**: [<abbr title="netrc machine"><em>teachable</em></abbr>]
- **TeachableCourse**: [<abbr title="netrc machine"><em>teachable</em></abbr>]
- **teachertube**: teachertube.com videos
- **teachertube:user:collection**: teachertube.com user and collection videos
- **TeachingChannel**
- **Teamcoco**
- **TeamTreeHouse**
- **TeamTreeHouse**: [<abbr title="netrc machine"><em>teamtreehouse</em></abbr>]
- **TechTalks**
- **techtv.mit.edu**
- **TedEmbed**
@@ -1202,8 +1232,8 @@
- **TeleQuebecVideo**
- **TeleTask**
- **Telewebion**
- **TennisTV**
- **TenPlay**
- **TennisTV**: [<abbr title="netrc machine"><em>tennistv</em></abbr>]
- **TenPlay**: [<abbr title="netrc machine"><em>10play</em></abbr>]
- **TF1**
- **TFO**
- **TheIntercept**
@@ -1234,10 +1264,10 @@
- **Tokentube**
- **Tokentube:channel**
- **ToonGoggles**
- **tou.tv**
- **tou.tv**: [<abbr title="netrc machine"><em>toutv</em></abbr>]
- **Toypics**: Toypics video
- **ToypicsUser**: Toypics user profile
- **TrailerAddict** (Currently broken)
- **TrailerAddict**: (**Currently broken**)
- **TravelChannel**
- **Trilulilu**
- **Trovo**
@@ -1248,9 +1278,9 @@
- **TruNews**
- **TruTV**
- **Tube8**
- **TubiTv**
- **TubiTv**: [<abbr title="netrc machine"><em>tubitv</em></abbr>]
- **TubiTvShow**
- **Tumblr**
- **Tumblr**: [<abbr title="netrc machine"><em>tumblr</em></abbr>]
- **tunein:clip**
- **tunein:program**
- **tunein:station**
@@ -1298,23 +1328,23 @@
- **TwitCasting**
- **TwitCastingLive**
- **TwitCastingUser**
- **twitch:clips**
- **twitch:stream**
- **twitch:vod**
- **TwitchCollection**
- **TwitchVideos**
- **TwitchVideosClips**
- **TwitchVideosCollections**
- **twitch:clips**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **twitch:stream**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **twitch:vod**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **TwitchCollection**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **TwitchVideos**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **TwitchVideosClips**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **TwitchVideosCollections**: [<abbr title="netrc machine"><em>twitch</em></abbr>]
- **twitter**
- **twitter:amplify**
- **twitter:broadcast**
- **twitter:card**
- **twitter:shortener**
- **udemy**
- **udemy:course**
- **udemy**: [<abbr title="netrc machine"><em>udemy</em></abbr>]
- **udemy:course**: [<abbr title="netrc machine"><em>udemy</em></abbr>]
- **UDNEmbed**: 聯合影音
- **UFCArabia**
- **UFCTV**
- **UFCArabia**: [<abbr title="netrc machine"><em>ufcarabia</em></abbr>]
- **UFCTV**: [<abbr title="netrc machine"><em>ufctv</em></abbr>]
- **ukcolumn**
- **UKTVPlay**
- **umg:de**: Universal Music Deutschland
@@ -1342,7 +1372,7 @@
- **VevoPlaylist**
- **VGTV**: VGTV, BTTV, FTV, Aftenposten and Aftonbladet
- **vh1.com**
- **vhx:embed**
- **vhx:embed**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **Viafree**
- **vice**
- **vice:article**
@@ -1354,50 +1384,49 @@
- **video.google:search**: Google Video search; "gvsearch:" prefix
- **video.sky.it**
- **video.sky.it:live**
- **VideocampusSachsen**
- **VideocampusSachsenEmbed**
- **VideoDetective**
- **videofy.me**
- **videomore**
- **videomore:season**
- **videomore:video**
- **VideoPress**
- **Vidio**
- **VidioLive**
- **VidioPremier**
- **Vidio**: [<abbr title="netrc machine"><em>vidio</em></abbr>]
- **VidioLive**: [<abbr title="netrc machine"><em>vidio</em></abbr>]
- **VidioPremier**: [<abbr title="netrc machine"><em>vidio</em></abbr>]
- **VidLii**
- **vier**: vier.be and vijf.be
- **vier**: [<abbr title="netrc machine"><em>vier</em></abbr>] vier.be and vijf.be
- **vier:videos**
- **viewlift**
- **viewlift:embed**
- **Viidea**
- **viki**
- **viki:channel**
- **vimeo**
- **vimeo:album**
- **vimeo:channel**
- **vimeo:group**
- **vimeo:likes**: Vimeo user likes
- **vimeo:ondemand**
- **vimeo:review**: Review pages on vimeo
- **vimeo:user**
- **vimeo:watchlater**: Vimeo watch later list, "vimeowatchlater" keyword (requires authentication)
- **viki**: [<abbr title="netrc machine"><em>viki</em></abbr>]
- **viki:channel**: [<abbr title="netrc machine"><em>viki</em></abbr>]
- **vimeo**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:album**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:channel**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:group**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:likes**: [<abbr title="netrc machine"><em>vimeo</em></abbr>] Vimeo user likes
- **vimeo:ondemand**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:review**: [<abbr title="netrc machine"><em>vimeo</em></abbr>] Review pages on vimeo
- **vimeo:user**: [<abbr title="netrc machine"><em>vimeo</em></abbr>]
- **vimeo:watchlater**: [<abbr title="netrc machine"><em>vimeo</em></abbr>] Vimeo watch later list, ":vimeowatchlater" keyword (requires authentication)
- **Vimm:recording**
- **Vimm:stream**
- **Vimp**
- **Vimple**: Vimple - one-click video hosting
- **Vine**
- **vine:user**
- **Viqeo**
- **Viu**
- **viu:ott**
- **viu:ott**: [<abbr title="netrc machine"><em>viu</em></abbr>]
- **viu:playlist**
- **Vivo**: vivo.sx
- **vk**: VK
- **vk:uservideos**: VK - User's Videos
- **vk:wallpost**
- **vlive**
- **vlive:channel**
- **vlive:post**
- **vk**: [<abbr title="netrc machine"><em>vk</em></abbr>] VK
- **vk:uservideos**: [<abbr title="netrc machine"><em>vk</em></abbr>] VK - User's Videos
- **vk:wallpost**: [<abbr title="netrc machine"><em>vk</em></abbr>]
- **vlive**: [<abbr title="netrc machine"><em>vlive</em></abbr>]
- **vlive:channel**: [<abbr title="netrc machine"><em>vlive</em></abbr>]
- **vlive:post**: [<abbr title="netrc machine"><em>vlive</em></abbr>]
- **vm.tiktok**
- **Vodlocker**
- **VODPl**
@@ -1412,12 +1441,12 @@
- **vpro**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
- **Vrak**
- **VRT**: VRT NWS, Flanders News, Flandern Info and Sporza
- **VrtNU**: VrtNU.be
- **vrv**
- **VrtNU**: [<abbr title="netrc machine"><em>vrtnu</em></abbr>] VrtNU.be
- **vrv**: [<abbr title="netrc machine"><em>vrv</em></abbr>]
- **vrv:series**
- **VShare**
- **VTM**
- **VTXTV**
- **VTXTV**: [<abbr title="netrc machine"><em>vtxtv</em></abbr>]
- **VuClip**
- **Vupload**
- **VVVVID**
@@ -1426,14 +1455,17 @@
- **Vzaar**
- **Wakanim**
- **Walla**
- **WalyTV**
- **WalyTV**: [<abbr title="netrc machine"><em>walytv</em></abbr>]
- **wasdtv:clip**
- **wasdtv:record**
- **wasdtv:stream**
- **washingtonpost**
- **washingtonpost:article**
- **wat.tv**
- **WatchBox**
- **WatchIndianPorn**: Watch Indian Porn
- **WDR**
- **wdr:mobile** (Currently broken)
- **wdr:mobile**: (**Currently broken**)
- **WDRElefant**
- **WDRPage**
- **web.archive:youtube**: web.archive.org saved youtube videos, "ytarchive:" prefix
@@ -1506,11 +1538,13 @@
- **youtube:favorites**: YouTube liked videos; ":ytfav" keyword (requires cookies)
- **youtube:history**: Youtube watch history; ":ythis" keyword (requires cookies)
- **youtube:music:search_url**: YouTube music search URLs with selectable sections (Eg: #songs)
- **youtube:notif**: YouTube notifications; ":ytnotif" keyword (requires cookies)
- **youtube:playlist**: YouTube playlists
- **youtube:recommended**: YouTube recommended videos; ":ytrec" keyword
- **youtube:search**: YouTube search; "ytsearch:" prefix
- **youtube:search:date**: YouTube search, newest videos first; "ytsearchdate:" prefix
- **youtube:search_url**: YouTube search URLs with sorting and filter support
- **youtube:stories**: YouTube channel stories; "ytstories:" prefix
- **youtube:subscriptions**: YouTube subscriptions feed; ":ytsubs" keyword (requires cookies)
- **youtube:tab**: YouTube Tabs
- **youtube:user**: YouTube user videos; "ytuser:" prefix
@@ -1518,16 +1552,23 @@
- **YoutubeLivestreamEmbed**: YouTube livestream embeds
- **YoutubeYtBe**: youtu.be
- **Zapiks**
- **Zattoo**
- **ZattooLive**
- **Zattoo**: [<abbr title="netrc machine"><em>zattoo</em></abbr>]
- **ZattooLive**: [<abbr title="netrc machine"><em>zattoo</em></abbr>]
- **ZattooMovies**: [<abbr title="netrc machine"><em>zattoo</em></abbr>]
- **ZattooRecordings**: [<abbr title="netrc machine"><em>zattoo</em></abbr>]
- **ZDF**
- **ZDFChannel**
- **Zee5**
- **Zee5**: [<abbr title="netrc machine"><em>zee5</em></abbr>]
- **zee5:series**
- **ZenYandex**
- **ZenYandexChannel**
- **Zhihu**
- **zingmp3**: zingmp3.vn
- **zingmp3:album**
- **zingmp3:chart-home**
- **zingmp3:chart-music-video**
- **zingmp3:user**
- **zingmp3:week-chart**
- **zoom**
- **Zype**
- **generic**: Generic downloader that works on some sites

View File

@@ -1,26 +1,16 @@
from __future__ import unicode_literals
import errno
import io
import hashlib
import json
import os.path
import re
import types
import ssl
import sys
import types
import yt_dlp.extractor
from yt_dlp import YoutubeDL
from yt_dlp.compat import (
compat_os_name,
compat_str,
)
from yt_dlp.utils import (
preferredencoding,
write_string,
)
from yt_dlp.compat import compat_os_name, compat_str
from yt_dlp.utils import preferredencoding, write_string
if 'pytest' in sys.modules:
import pytest
@@ -35,10 +25,10 @@ def get_params(override=None):
'parameters.json')
LOCAL_PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'local_parameters.json')
with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
with open(PARAMETERS_FILE, encoding='utf-8') as pf:
parameters = json.load(pf)
if os.path.exists(LOCAL_PARAMETERS_FILE):
with io.open(LOCAL_PARAMETERS_FILE, encoding='utf-8') as pf:
with open(LOCAL_PARAMETERS_FILE, encoding='utf-8') as pf:
parameters.update(json.load(pf))
if override:
parameters.update(override)
@@ -63,8 +53,8 @@ def report_warning(message):
_msg_header = '\033[0;33mWARNING:\033[0m'
else:
_msg_header = 'WARNING:'
output = '%s %s\n' % (_msg_header, message)
if 'b' in getattr(sys.stderr, 'mode', '') or sys.version_info[0] < 3:
output = f'{_msg_header} {message}\n'
if 'b' in getattr(sys.stderr, 'mode', ''):
output = output.encode(preferredencoding())
sys.stderr.write(output)
@@ -74,7 +64,7 @@ class FakeYDL(YoutubeDL):
# Different instances of the downloader can't share the same dictionary
# some test set the "sublang" parameter, which would break the md5 checks.
params = get_params(override=override)
super(FakeYDL, self).__init__(params, auto_init=False)
super().__init__(params, auto_init=False)
self.result = []
def to_screen(self, s, skip_eol=None):
@@ -99,11 +89,10 @@ class FakeYDL(YoutubeDL):
def gettestcases(include_onlymatching=False):
for ie in yt_dlp.extractor.gen_extractors():
for tc in ie.get_testcases(include_onlymatching):
yield tc
yield from ie.get_testcases(include_onlymatching)
md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
md5 = lambda s: hashlib.md5(s.encode()).hexdigest()
def expect_value(self, got, expected, field):
@@ -113,33 +102,30 @@ def expect_value(self, got, expected, field):
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
f'Expected a {compat_str.__name__} object, but got {type(got).__name__} for field {field}')
self.assertTrue(
match_rex.match(got),
'field %s (value: %r) should match %r' % (field, got, match_str))
f'field {field} (value: {got!r}) should match {match_str!r}')
elif isinstance(expected, compat_str) and expected.startswith('startswith:'):
start_str = expected[len('startswith:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
f'Expected a {compat_str.__name__} object, but got {type(got).__name__} for field {field}')
self.assertTrue(
got.startswith(start_str),
'field %s (value: %r) should start with %r' % (field, got, start_str))
f'field {field} (value: {got!r}) should start with {start_str!r}')
elif isinstance(expected, compat_str) and expected.startswith('contains:'):
contains_str = expected[len('contains:'):]
self.assertTrue(
isinstance(got, compat_str),
'Expected a %s object, but got %s for field %s' % (
compat_str.__name__, type(got).__name__, field))
f'Expected a {compat_str.__name__} object, but got {type(got).__name__} for field {field}')
self.assertTrue(
contains_str in got,
'field %s (value: %r) should contain %r' % (field, got, contains_str))
f'field {field} (value: {got!r}) should contain {contains_str!r}')
elif isinstance(expected, type):
self.assertTrue(
isinstance(got, expected),
'Expected type %r for field %s, but got value %r of type %r' % (expected, field, got, type(got)))
f'Expected type {expected!r} for field {field}, but got value {got!r} of type {type(got)!r}')
elif isinstance(expected, dict) and isinstance(got, dict):
expect_dict(self, got, expected)
elif isinstance(expected, list) and isinstance(got, list):
@@ -159,13 +145,12 @@ def expect_value(self, got, expected, field):
if isinstance(expected, compat_str) and expected.startswith('md5:'):
self.assertTrue(
isinstance(got, compat_str),
'Expected field %s to be a unicode object, but got value %r of type %r' % (field, got, type(got)))
f'Expected field {field} to be a unicode object, but got value {got!r} of type {type(got)!r}')
got = 'md5:' + md5(got)
elif isinstance(expected, compat_str) and re.match(r'^(?:min|max)?count:\d+', expected):
self.assertTrue(
isinstance(got, (list, dict)),
'Expected field %s to be a list or a dict, but it is of type %s' % (
field, type(got).__name__))
f'Expected field {field} to be a list or a dict, but it is of type {type(got).__name__}')
op, _, expected_num = expected.partition(':')
expected_num = int(expected_num)
if op == 'mincount':
@@ -185,7 +170,7 @@ def expect_value(self, got, expected, field):
return
self.assertEqual(
expected, got,
'Invalid value for field %s, expected %r, got %r' % (field, expected, got))
f'Invalid value for field {field}, expected {expected!r}, got {got!r}')
def expect_dict(self, got_dict, expected_dict):
@@ -196,15 +181,7 @@ def expect_dict(self, got_dict, expected_dict):
def sanitize_got_info_dict(got_dict):
IGNORED_FIELDS = (
# Format keys
'url', 'manifest_url', 'format', 'format_id', 'format_note', 'width', 'height', 'resolution',
'dynamic_range', 'tbr', 'abr', 'acodec', 'asr', 'vbr', 'fps', 'vcodec', 'container', 'filesize',
'filesize_approx', 'player_url', 'protocol', 'fragment_base_url', 'fragments', 'preference',
'language', 'language_preference', 'quality', 'source_preference', 'http_headers',
'stretched_ratio', 'no_resume', 'has_drm', 'downloader_options',
# RTMP formats
'page_url', 'app', 'play_path', 'tc_url', 'flash_version', 'rtmp_live', 'rtmp_conn', 'rtmp_protocol', 'rtmp_real_time',
*YoutubeDL._format_fields,
# Lists
'formats', 'thumbnails', 'subtitles', 'automatic_captions', 'comments', 'entries',
@@ -268,13 +245,13 @@ def expect_info_dict(self, got_dict, expected_dict):
info_dict_str = ''
if len(missing_keys) != len(expected_dict):
info_dict_str += ''.join(
' %s: %s,\n' % (_repr(k), _repr(v))
f' {_repr(k)}: {_repr(v)},\n'
for k, v in test_info_dict.items() if k not in missing_keys)
if info_dict_str:
info_dict_str += '\n'
info_dict_str += ''.join(
' %s: %s,\n' % (_repr(k), _repr(test_info_dict[k]))
f' {_repr(k)}: {_repr(test_info_dict[k])},\n'
for k in missing_keys)
write_string(
'\n\'info_dict\': {\n' + info_dict_str + '},\n', out=sys.stderr)
@@ -303,21 +280,21 @@ def assertRegexpMatches(self, text, regexp, msg=None):
def assertGreaterEqual(self, got, expected, msg=None):
if not (got >= expected):
if msg is None:
msg = '%r not greater than or equal to %r' % (got, expected)
msg = f'{got!r} not greater than or equal to {expected!r}'
self.assertTrue(got >= expected, msg)
def assertLessEqual(self, got, expected, msg=None):
if not (got <= expected):
if msg is None:
msg = '%r not less than or equal to %r' % (got, expected)
msg = f'{got!r} not less than or equal to {expected!r}'
self.assertTrue(got <= expected, msg)
def assertEqual(self, got, expected, msg=None):
if not (got == expected):
if msg is None:
msg = '%r not equal to %r' % (got, expected)
msg = f'{got!r} not equal to {expected!r}'
self.assertTrue(got == expected, msg)

View File

@@ -1 +0,0 @@
*.swf

View File

@@ -1,19 +0,0 @@
// input: [["a", "b", "c", "d"]]
// output: ["c", "b", "a", "d"]
package {
public class ArrayAccess {
public static function main(ar:Array):Array {
var aa:ArrayAccess = new ArrayAccess();
return aa.f(ar, 2);
}
private function f(ar:Array, num:Number):Array{
var x:String = ar[0];
var y:String = ar[num % ar.length];
ar[0] = y;
ar[num] = x;
return ar;
}
}
}

View File

@@ -1,17 +0,0 @@
// input: []
// output: 121
package {
public class ClassCall {
public static function main():int{
var f:OtherClass = new OtherClass();
return f.func(100,20);
}
}
}
class OtherClass {
public function func(x: int, y: int):int {
return x+y+1;
}
}

View File

@@ -1,15 +0,0 @@
// input: []
// output: 0
package {
public class ClassConstruction {
public static function main():int{
var f:Foo = new Foo();
return 0;
}
}
}
class Foo {
}

View File

@@ -1,18 +0,0 @@
// input: []
// output: 4
package {
public class ConstArrayAccess {
private static const x:int = 2;
private static const ar:Array = ["42", "3411"];
public static function main():int{
var c:ConstArrayAccess = new ConstArrayAccess();
return c.f();
}
public function f(): int {
return ar[1].length;
}
}
}

View File

@@ -1,12 +0,0 @@
// input: []
// output: 2
package {
public class ConstantInt {
private static const x:int = 2;
public static function main():int{
return x;
}
}
}

View File

@@ -1,10 +0,0 @@
// input: [{"x": 1, "y": 2}]
// output: 3
package {
public class DictCall {
public static function main(d:Object):int{
return d.x + d.y;
}
}
}

View File

@@ -1,10 +0,0 @@
// input: []
// output: false
package {
public class EqualsOperator {
public static function main():Boolean{
return 1 == 2;
}
}
}

View File

@@ -1,13 +0,0 @@
// input: [1, 2]
// output: 3
package {
public class LocalVars {
public static function main(a:int, b:int):int{
var c:int = a + b + b;
var d:int = c - b;
var e:int = d;
return e;
}
}
}

View File

@@ -1,22 +0,0 @@
// input: [1]
// output: 2
package {
public class MemberAssignment {
public var v:int;
public function g():int {
return this.v;
}
public function f(a:int):int{
this.v = a;
return this.v + this.g();
}
public static function main(a:int): int {
var v:MemberAssignment = new MemberAssignment();
return v.f(a);
}
}
}

View File

@@ -1,24 +0,0 @@
// input: []
// output: 123
package {
public class NeOperator {
public static function main(): int {
var res:int = 0;
if (1 != 2) {
res += 3;
} else {
res += 4;
}
if (2 != 2) {
res += 10;
} else {
res += 20;
}
if (9 == 9) {
res += 100;
}
return res;
}
}
}

View File

@@ -1,21 +0,0 @@
// input: []
// output: 9
package {
public class PrivateCall {
public static function main():int{
var f:OtherClass = new OtherClass();
return f.func();
}
}
}
class OtherClass {
private function pf():int {
return 9;
}
public function func():int {
return this.pf();
}
}

View File

@@ -1,22 +0,0 @@
// input: []
// output: 9
package {
public class PrivateVoidCall {
public static function main():int{
var f:OtherClass = new OtherClass();
f.func();
return 9;
}
}
}
class OtherClass {
private function pf():void {
;
}
public function func():void {
this.pf();
}
}

View File

@@ -1,13 +0,0 @@
// input: [1]
// output: 1
package {
public class StaticAssignment {
public static var v:int;
public static function main(a:int):int{
v = a;
return v;
}
}
}

View File

@@ -1,16 +0,0 @@
// input: []
// output: 1
package {
public class StaticRetrieval {
public static var v:int;
public static function main():int{
if (v) {
return 0;
} else {
return 1;
}
}
}
}

View File

@@ -1,11 +0,0 @@
// input: []
// output: 3
package {
public class StringBasics {
public static function main():int{
var s:String = "abc";
return s.length;
}
}
}

View File

@@ -1,11 +0,0 @@
// input: []
// output: 9897
package {
public class StringCharCodeAt {
public static function main():int{
var s:String = "abc";
return s.charCodeAt(1) * 100 + s.charCodeAt();
}
}
}

View File

@@ -1,11 +0,0 @@
// input: []
// output: 2
package {
public class StringConversion {
public static function main():int{
var s:String = String(99);
return s.length;
}
}
}

View File

@@ -1,21 +1,23 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
# Allow direct execution
import io
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import FakeYDL, expect_dict, expect_value, http_server_port
from yt_dlp.compat import compat_etree_fromstring, compat_http_server
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.extractor import YoutubeIE, get_info_extractor
from yt_dlp.utils import encode_data_uri, strip_jsonp, ExtractorError, RegexNotFoundError
import threading
from test.helper import FakeYDL, expect_dict, expect_value, http_server_port
from yt_dlp.compat import compat_etree_fromstring, compat_http_server
from yt_dlp.extractor import YoutubeIE, get_info_extractor
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.utils import (
ExtractorError,
RegexNotFoundError,
encode_data_uri,
strip_jsonp,
)
TEAPOT_RESPONSE_STATUS = 418
TEAPOT_RESPONSE_BODY = "<h1>418 I'm a teapot</h1>"
@@ -1011,8 +1013,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
]
for m3u8_file, m3u8_url, expected_formats, expected_subs in _TEST_CASES:
with io.open('./test/testdata/m3u8/%s.m3u8' % m3u8_file,
mode='r', encoding='utf-8') as f:
with open('./test/testdata/m3u8/%s.m3u8' % m3u8_file, encoding='utf-8') as f:
formats, subs = self.ie._parse_m3u8_formats_and_subtitles(
f.read(), m3u8_url, ext='mp4')
self.ie._sort_formats(formats)
@@ -1357,10 +1358,9 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
]
for mpd_file, mpd_url, mpd_base_url, expected_formats, expected_subtitles in _TEST_CASES:
with io.open('./test/testdata/mpd/%s.mpd' % mpd_file,
mode='r', encoding='utf-8') as f:
with open('./test/testdata/mpd/%s.mpd' % mpd_file, encoding='utf-8') as f:
formats, subtitles = self.ie._parse_mpd_formats_and_subtitles(
compat_etree_fromstring(f.read().encode('utf-8')),
compat_etree_fromstring(f.read().encode()),
mpd_base_url=mpd_base_url, mpd_url=mpd_url)
self.ie._sort_formats(formats)
expect_value(self, formats, expected_formats, None)
@@ -1549,10 +1549,9 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
]
for ism_file, ism_url, expected_formats, expected_subtitles in _TEST_CASES:
with io.open('./test/testdata/ism/%s.Manifest' % ism_file,
mode='r', encoding='utf-8') as f:
with open('./test/testdata/ism/%s.Manifest' % ism_file, encoding='utf-8') as f:
formats, subtitles = self.ie._parse_ism_formats_and_subtitles(
compat_etree_fromstring(f.read().encode('utf-8')), ism_url=ism_url)
compat_etree_fromstring(f.read().encode()), ism_url=ism_url)
self.ie._sort_formats(formats)
expect_value(self, formats, expected_formats, None)
expect_value(self, subtitles, expected_subtitles, None)
@@ -1576,10 +1575,9 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
]
for f4m_file, f4m_url, expected_formats in _TEST_CASES:
with io.open('./test/testdata/f4m/%s.f4m' % f4m_file,
mode='r', encoding='utf-8') as f:
with open('./test/testdata/f4m/%s.f4m' % f4m_file, encoding='utf-8') as f:
formats = self.ie._parse_f4m_formats(
compat_etree_fromstring(f.read().encode('utf-8')),
compat_etree_fromstring(f.read().encode()),
f4m_url, None)
self.ie._sort_formats(formats)
expect_value(self, formats, expected_formats, None)
@@ -1624,10 +1622,9 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
]
for xspf_file, xspf_url, expected_entries in _TEST_CASES:
with io.open('./test/testdata/xspf/%s.xspf' % xspf_file,
mode='r', encoding='utf-8') as f:
with open('./test/testdata/xspf/%s.xspf' % xspf_file, encoding='utf-8') as f:
entries = self.ie._parse_xspf(
compat_etree_fromstring(f.read().encode('utf-8')),
compat_etree_fromstring(f.read().encode()),
xspf_file, xspf_url=xspf_url, xspf_base_url=xspf_url)
expect_value(self, entries, expected_entries, None)
for i in range(len(entries)):

View File

@@ -1,31 +1,38 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import copy
import json
from test.helper import FakeYDL, assertRegexpMatches
from yt_dlp import YoutubeDL
from yt_dlp.compat import compat_os_name, compat_setenv, compat_str, compat_urllib_error
from yt_dlp.compat import (
compat_os_name,
compat_setenv,
compat_str,
compat_urllib_error,
)
from yt_dlp.extractor import YoutubeIE
from yt_dlp.extractor.common import InfoExtractor
from yt_dlp.postprocessor.common import PostProcessor
from yt_dlp.utils import ExtractorError, int_or_none, match_filter_func, LazyList
from yt_dlp.utils import (
ExtractorError,
LazyList,
int_or_none,
match_filter_func,
)
TEST_URL = 'http://localhost/sample.mp4'
class YDL(FakeYDL):
def __init__(self, *args, **kwargs):
super(YDL, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.downloaded_info_dicts = []
self.msgs = []
@@ -551,11 +558,11 @@ class TestYoutubeDL(unittest.TestCase):
def s_formats(lang, autocaption=False):
return [{
'ext': ext,
'url': 'http://localhost/video.%s.%s' % (lang, ext),
'url': f'http://localhost/video.{lang}.{ext}',
'_auto': autocaption,
} for ext in ['vtt', 'srt', 'ass']]
subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
subtitles = {l: s_formats(l) for l in ['en', 'fr', 'es']}
auto_captions = {l: s_formats(l, True) for l in ['it', 'pt', 'es']}
info_dict = {
'id': 'test',
'title': 'Test',
@@ -580,7 +587,7 @@ class TestYoutubeDL(unittest.TestCase):
result = get_info({'writesubtitles': True})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['en']))
self.assertEqual(set(subs.keys()), {'en'})
self.assertTrue(subs['en'].get('data') is None)
self.assertEqual(subs['en']['ext'], 'ass')
@@ -591,39 +598,39 @@ class TestYoutubeDL(unittest.TestCase):
result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['es', 'fr']))
self.assertEqual(set(subs.keys()), {'es', 'fr'})
result = get_info({'writesubtitles': True, 'subtitleslangs': ['all', '-en']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['es', 'fr']))
self.assertEqual(set(subs.keys()), {'es', 'fr'})
result = get_info({'writesubtitles': True, 'subtitleslangs': ['en', 'fr', '-en']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['fr']))
self.assertEqual(set(subs.keys()), {'fr'})
result = get_info({'writesubtitles': True, 'subtitleslangs': ['-en', 'en']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['en']))
self.assertEqual(set(subs.keys()), {'en'})
result = get_info({'writesubtitles': True, 'subtitleslangs': ['e.+']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['es', 'en']))
self.assertEqual(set(subs.keys()), {'es', 'en'})
result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['es', 'pt']))
self.assertEqual(set(subs.keys()), {'es', 'pt'})
self.assertFalse(subs['es']['_auto'])
self.assertTrue(subs['pt']['_auto'])
result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
subs = result['requested_subtitles']
self.assertTrue(subs)
self.assertEqual(set(subs.keys()), set(['es', 'pt']))
self.assertEqual(set(subs.keys()), {'es', 'pt'})
self.assertTrue(subs['es']['_auto'])
self.assertTrue(subs['pt']['_auto'])
@@ -654,7 +661,7 @@ class TestYoutubeDL(unittest.TestCase):
'duration': 100000,
'playlist_index': 1,
'playlist_autonumber': 2,
'_last_playlist_index': 100,
'__last_playlist_index': 100,
'n_entries': 10,
'formats': [{'id': 'id 1'}, {'id': 'id 2'}, {'id': 'id 3'}]
}
@@ -818,6 +825,8 @@ class TestYoutubeDL(unittest.TestCase):
test('%(id&foo)s.bar', 'foo.bar')
test('%(title&foo)s.bar', 'NA.bar')
test('%(title&foo|baz)s.bar', 'baz.bar')
test('%(x,id&foo|baz)s.bar', 'foo.bar')
test('%(x,title&foo|baz)s.bar', 'baz.bar')
# Laziness
def gen():
@@ -931,7 +940,7 @@ class TestYoutubeDL(unittest.TestCase):
res = get_videos()
self.assertEqual(res, ['1', '2'])
def f(v):
def f(v, incomplete):
if v['id'] == '1':
return None
else:
@@ -1080,7 +1089,7 @@ class TestYoutubeDL(unittest.TestCase):
class _YDL(YDL):
def __init__(self, *args, **kwargs):
super(_YDL, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def trouble(self, s, tb=None):
pass

View File

@@ -1,13 +1,10 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import unicode_literals
import os
import re
import sys
import tempfile
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp.utils import YoutubeDLCookieJar
@@ -20,7 +17,7 @@ class TestYoutubeDLCookieJar(unittest.TestCase):
tf = tempfile.NamedTemporaryFile(delete=False)
try:
cookiejar.save(filename=tf.name, ignore_discard=True, ignore_expires=True)
temp = tf.read().decode('utf-8')
temp = tf.read().decode()
self.assertTrue(re.search(
r'www\.foobar\.foobar\s+FALSE\s+/\s+TRUE\s+0\s+YoutubeDLExpiresEmpty\s+YoutubeDLExpiresEmptyValue', temp))
self.assertTrue(re.search(

View File

@@ -1,30 +1,30 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import base64
from yt_dlp.aes import (
aes_decrypt,
aes_encrypt,
aes_ecb_encrypt,
aes_ecb_decrypt,
BLOCK_SIZE_BYTES,
aes_cbc_decrypt,
aes_cbc_decrypt_bytes,
aes_cbc_encrypt,
aes_ctr_decrypt,
aes_ctr_encrypt,
aes_decrypt,
aes_decrypt_text,
aes_ecb_decrypt,
aes_ecb_encrypt,
aes_encrypt,
aes_gcm_decrypt_and_verify,
aes_gcm_decrypt_and_verify_bytes,
aes_decrypt_text,
BLOCK_SIZE_BYTES,
)
from yt_dlp.compat import compat_pycrypto_AES
from yt_dlp.dependencies import Cryptodome_AES
from yt_dlp.utils import bytes_to_intlist, intlist_to_bytes
import base64
# the encrypted data can be generate with 'devscripts/generate_aes_testdata.py'
@@ -45,7 +45,7 @@ class TestAES(unittest.TestCase):
data = b'\x97\x92+\xe5\x0b\xc3\x18\x91ky9m&\xb3\xb5@\xe6\x27\xc2\x96.\xc8u\x88\xab9-[\x9e|\xf1\xcd'
decrypted = intlist_to_bytes(aes_cbc_decrypt(bytes_to_intlist(data), self.key, self.iv))
self.assertEqual(decrypted.rstrip(b'\x08'), self.secret_msg)
if compat_pycrypto_AES:
if Cryptodome_AES:
decrypted = aes_cbc_decrypt_bytes(data, intlist_to_bytes(self.key), intlist_to_bytes(self.iv))
self.assertEqual(decrypted.rstrip(b'\x08'), self.secret_msg)
@@ -75,25 +75,25 @@ class TestAES(unittest.TestCase):
decrypted = intlist_to_bytes(aes_gcm_decrypt_and_verify(
bytes_to_intlist(data), self.key, bytes_to_intlist(authentication_tag), self.iv[:12]))
self.assertEqual(decrypted.rstrip(b'\x08'), self.secret_msg)
if compat_pycrypto_AES:
if Cryptodome_AES:
decrypted = aes_gcm_decrypt_and_verify_bytes(
data, intlist_to_bytes(self.key), authentication_tag, intlist_to_bytes(self.iv[:12]))
self.assertEqual(decrypted.rstrip(b'\x08'), self.secret_msg)
def test_decrypt_text(self):
password = intlist_to_bytes(self.key).decode('utf-8')
password = intlist_to_bytes(self.key).decode()
encrypted = base64.b64encode(
intlist_to_bytes(self.iv[:8])
+ b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae'
).decode('utf-8')
).decode()
decrypted = (aes_decrypt_text(encrypted, password, 16))
self.assertEqual(decrypted, self.secret_msg)
password = intlist_to_bytes(self.key).decode('utf-8')
password = intlist_to_bytes(self.key).decode()
encrypted = base64.b64encode(
intlist_to_bytes(self.iv[:8])
+ b'\x0b\xe6\xa4\xd9z\x0e\xb8\xb9\xd0\xd4i_\x85\x1d\x99\x98_\xe5\x80\xe7.\xbf\xa5\x83'
).decode('utf-8')
).decode()
decrypted = (aes_decrypt_text(encrypted, password, 32))
self.assertEqual(decrypted, self.secret_msg)

View File

@@ -1,13 +1,12 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import try_rm, is_download_test
from test.helper import is_download_test, try_rm
from yt_dlp import YoutubeDL

View File

@@ -1,22 +1,16 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
# Allow direct execution
import collections
import os
import sys
import unittest
import collections
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import gettestcases
from yt_dlp.extractor import (
FacebookIE,
gen_extractors,
YoutubeIE,
)
from yt_dlp.extractor import FacebookIE, YoutubeIE, gen_extractors
class TestAllURLsMatching(unittest.TestCase):
@@ -81,11 +75,11 @@ class TestAllURLsMatching(unittest.TestCase):
url = tc['url']
for ie in ies:
if type(ie).__name__ in ('GenericIE', tc['name'] + 'IE'):
self.assertTrue(ie.suitable(url), '%s should match URL %r' % (type(ie).__name__, url))
self.assertTrue(ie.suitable(url), f'{type(ie).__name__} should match URL {url!r}')
else:
self.assertFalse(
ie.suitable(url),
'%s should not match URL %r . That URL belongs to %s.' % (type(ie).__name__, url, tc['name']))
f'{type(ie).__name__} should not match URL {url!r} . That URL belongs to {tc["name"]}.')
def test_keywords(self):
self.assertMatch(':ytsubs', ['youtube:subscriptions'])
@@ -120,7 +114,7 @@ class TestAllURLsMatching(unittest.TestCase):
for (ie_name, ie_list) in name_accu.items():
self.assertEqual(
len(ie_list), 1,
'Multiple extractors with the same IE_NAME "%s" (%s)' % (ie_name, ', '.join(ie_list)))
f'Multiple extractors with the same IE_NAME "{ie_name}" ({", ".join(ie_list)})')
if __name__ == '__main__':

View File

@@ -1,18 +1,15 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import unicode_literals
import shutil
# Allow direct execution
import os
import shutil
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import FakeYDL
from yt_dlp.cache import Cache

View File

@@ -1,26 +1,20 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import unicode_literals
# Allow direct execution
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp import compat
from yt_dlp.compat import (
compat_getenv,
compat_setenv,
compat_etree_Element,
compat_etree_fromstring,
compat_expanduser,
compat_shlex_split,
compat_getenv,
compat_setenv,
compat_str,
compat_struct_unpack,
compat_urllib_parse_quote,
compat_urllib_parse_quote_plus,
compat_urllib_parse_unquote,
compat_urllib_parse_unquote_plus,
compat_urllib_parse_urlencode,
@@ -28,6 +22,12 @@ from yt_dlp.compat import (
class TestCompat(unittest.TestCase):
def test_compat_passthrough(self):
with self.assertWarns(DeprecationWarning):
compat.compat_basestring
compat.asyncio.events # Must not raise error
def test_compat_getenv(self):
test_str = 'тест'
compat_setenv('yt_dlp_COMPAT_GETENV', test_str)
@@ -42,39 +42,12 @@ class TestCompat(unittest.TestCase):
def test_compat_expanduser(self):
old_home = os.environ.get('HOME')
test_str = r'C:\Documents and Settings\тест\Application Data'
compat_setenv('HOME', test_str)
self.assertEqual(compat_expanduser('~'), test_str)
compat_setenv('HOME', old_home or '')
def test_all_present(self):
import yt_dlp.compat
all_names = yt_dlp.compat.__all__
present_names = set(filter(
lambda c: '_' in c and not c.startswith('_'),
dir(yt_dlp.compat))) - set(['unicode_literals'])
self.assertEqual(all_names, sorted(present_names))
def test_compat_urllib_parse_quote(self):
self.assertEqual(compat_urllib_parse_quote('abc def'), 'abc%20def')
self.assertEqual(compat_urllib_parse_quote('/user/abc+def'), '/user/abc%2Bdef')
self.assertEqual(compat_urllib_parse_quote('/user/abc+def', safe='+'), '%2Fuser%2Fabc+def')
self.assertEqual(compat_urllib_parse_quote(''), '')
self.assertEqual(compat_urllib_parse_quote('%'), '%25')
self.assertEqual(compat_urllib_parse_quote('%', safe='%'), '%')
self.assertEqual(compat_urllib_parse_quote('津波'), '%E6%B4%A5%E6%B3%A2')
self.assertEqual(
compat_urllib_parse_quote('''<meta property="og:description" content="▁▂▃▄%▅▆▇█" />
%<a href="https://ar.wikipedia.org/wiki/تسونامي">%a''', safe='<>=":%/ \r\n'),
'''<meta property="og:description" content="%E2%96%81%E2%96%82%E2%96%83%E2%96%84%%E2%96%85%E2%96%86%E2%96%87%E2%96%88" />
%<a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%B3%D9%88%D9%86%D8%A7%D9%85%D9%8A">%a''')
self.assertEqual(
compat_urllib_parse_quote('''(^◣_◢^)っ︻デ═一 ⇀ ⇀ ⇀ ⇀ ⇀ ↶%I%Break%25Things%''', safe='% '),
'''%28%5E%E2%97%A3_%E2%97%A2%5E%29%E3%81%A3%EF%B8%BB%E3%83%87%E2%95%90%E4%B8%80 %E2%87%80 %E2%87%80 %E2%87%80 %E2%87%80 %E2%87%80 %E2%86%B6%I%Break%25Things%''')
def test_compat_urllib_parse_quote_plus(self):
self.assertEqual(compat_urllib_parse_quote_plus('abc def'), 'abc+def')
self.assertEqual(compat_urllib_parse_quote_plus('/abc def'), '%2Fabc+def')
test_str = R'C:\Documents and Settings\тест\Application Data'
try:
compat_setenv('HOME', test_str)
self.assertEqual(compat_expanduser('~'), test_str)
finally:
compat_setenv('HOME', old_home or '')
def test_compat_urllib_parse_unquote(self):
self.assertEqual(compat_urllib_parse_unquote('abc%20def'), 'abc def')
@@ -109,17 +82,6 @@ class TestCompat(unittest.TestCase):
self.assertEqual(compat_urllib_parse_urlencode([(b'abc', 'def')]), 'abc=def')
self.assertEqual(compat_urllib_parse_urlencode([(b'abc', b'def')]), 'abc=def')
def test_compat_shlex_split(self):
self.assertEqual(compat_shlex_split('-option "one two"'), ['-option', 'one two'])
self.assertEqual(compat_shlex_split('-option "one\ntwo" \n -flag'), ['-option', 'one\ntwo', '-flag'])
self.assertEqual(compat_shlex_split('-val 中文'), ['-val', '中文'])
def test_compat_etree_Element(self):
try:
compat_etree_Element.items
except AttributeError:
self.fail('compat_etree_Element is not a type')
def test_compat_etree_fromstring(self):
xml = '''
<root foo="bar" spam="中文">
@@ -128,7 +90,7 @@ class TestCompat(unittest.TestCase):
<foo><bar>spam</bar></foo>
</root>
'''
doc = compat_etree_fromstring(xml.encode('utf-8'))
doc = compat_etree_fromstring(xml.encode())
self.assertTrue(isinstance(doc.attrib['foo'], compat_str))
self.assertTrue(isinstance(doc.attrib['spam'], compat_str))
self.assertTrue(isinstance(doc.find('normal').text, compat_str))

View File

@@ -6,10 +6,10 @@ from yt_dlp.cookies import (
LinuxChromeCookieDecryptor,
MacChromeCookieDecryptor,
WindowsChromeCookieDecryptor,
parse_safari_cookies,
pbkdf2_sha1,
_get_linux_desktop_environment,
_LinuxDesktopEnvironment,
parse_safari_cookies,
pbkdf2_sha1,
)

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env python3
from __future__ import unicode_literals
# Allow direct execution
import hashlib
import json
import os
import socket
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from test.helper import (
@@ -19,25 +20,19 @@ from test.helper import (
try_rm,
)
import hashlib
import io
import json
import socket
import yt_dlp.YoutubeDL
from yt_dlp.compat import (
compat_http_client,
compat_urllib_error,
compat_HTTPError,
compat_urllib_error,
)
from yt_dlp.extractor import get_info_extractor
from yt_dlp.utils import (
DownloadError,
ExtractorError,
format_bytes,
UnavailableVideoError,
format_bytes,
)
from yt_dlp.extractor import get_info_extractor
RETRIES = 3
@@ -46,7 +41,7 @@ class YoutubeDL(yt_dlp.YoutubeDL):
def __init__(self, *args, **kwargs):
self.to_stderr = self.to_screen
self.processed_info_dicts = []
super(YoutubeDL, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def report_warning(self, message):
# Don't accept warnings during tests
@@ -54,7 +49,7 @@ class YoutubeDL(yt_dlp.YoutubeDL):
def process_info(self, info_dict):
self.processed_info_dicts.append(info_dict.copy())
return super(YoutubeDL, self).process_info(info_dict)
return super().process_info(info_dict)
def _file_md5(fn):
@@ -80,7 +75,7 @@ class TestDownload(unittest.TestCase):
def strclass(cls):
"""From 2.7's unittest; 2.6 had _strclass so we can't import it."""
return '%s.%s' % (cls.__module__, cls.__name__)
return f'{cls.__module__}.{cls.__name__}'
add_ie = getattr(self, self._testMethodName).add_ie
return '%s (%s)%s:' % (self._testMethodName,
@@ -179,7 +174,7 @@ def generator(test_case, tname):
report_warning('%s failed due to network errors, skipping...' % tname)
return
print('Retrying: {0} failed tries\n\n##########\n\n'.format(try_num))
print(f'Retrying: {try_num} failed tries\n\n##########\n\n')
try_num += 1
else:
@@ -245,7 +240,7 @@ def generator(test_case, tname):
self.assertTrue(
os.path.exists(info_json_fn),
'Missing info file %s' % info_json_fn)
with io.open(info_json_fn, encoding='utf-8') as infof:
with open(info_json_fn, encoding='utf-8') as infof:
info_dict = json.load(infof)
expect_info_dict(self, info_dict, tc.get('info_dict', {}))
finally:

View File

@@ -1,20 +1,19 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import unicode_literals
# Allow direct execution
import os
import re
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import threading
from test.helper import http_server_port, try_rm
from yt_dlp import YoutubeDL
from yt_dlp.compat import compat_http_server
from yt_dlp.downloader.http import HttpFD
from yt_dlp.utils import encodeFilename
import threading
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -66,7 +65,7 @@ class HTTPTestRequestHandler(compat_http_server.BaseHTTPRequestHandler):
assert False
class FakeLogger(object):
class FakeLogger:
def debug(self, msg):
pass

Some files were not shown because too many files have changed in this diff Show More