mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-02 11:26:41 +00:00
parent
e0fd95737d
commit
b69fd25c25
|
@ -34,7 +34,7 @@ body:
|
||||||
label: Example URLs
|
label: Example URLs
|
||||||
description: |
|
description: |
|
||||||
Provide all kinds of example URLs for which support should be added
|
Provide all kinds of example URLs for which support should be added
|
||||||
value: |
|
placeholder: |
|
||||||
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
- Single video: https://youtu.be/BaW_jenozKc
|
- Single video: https://youtu.be/BaW_jenozKc
|
||||||
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -2,4 +2,4 @@ blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Get help from the community on Discord
|
- name: Get help from the community on Discord
|
||||||
url: https://discord.gg/H5MNcFW63r
|
url: https://discord.gg/H5MNcFW63r
|
||||||
about: Join the yt-dlp Discord for community-powered support!
|
about: Join the yt-dlp Discord for community-powered support!
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: Broken site support
|
name: Broken site support
|
||||||
description: Report broken or misfunctioning site
|
description: Report broken or misfunctioning site
|
||||||
labels: [triage, extractor-bug]
|
labels: [triage, site-bug]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: checklist
|
id: checklist
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: Site feature request
|
name: Site feature request
|
||||||
description: Request a new functionality for a site
|
description: Request a new functionality for a supported site
|
||||||
labels: [triage, site-enhancement]
|
labels: [triage, site-enhancement]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
|
@ -47,3 +47,26 @@ body:
|
||||||
placeholder: WRITE DESCRIPTION HERE
|
placeholder: WRITE DESCRIPTION HERE
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Verbose log
|
||||||
|
description: |
|
||||||
|
Provide the complete verbose output of yt-dlp that demonstrates the need for the enhancement.
|
||||||
|
Add the `-Uv` flag to your command line you run yt-dlp with (`yt-dlp -Uv <your command line>`), copy the WHOLE output and insert it below.
|
||||||
|
It should look similar to this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-Uv', '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 %(version)s (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 (%(version)s)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
2
.github/ISSUE_TEMPLATE_tmpl/4_bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE_tmpl/4_bug_report.yml
vendored
|
@ -1,6 +1,6 @@
|
||||||
name: Bug report
|
name: Bug report
|
||||||
description: Report a bug unrelated to any particular site or extractor
|
description: Report a bug unrelated to any particular site or extractor
|
||||||
labels: [triage,bug]
|
labels: [triage, bug]
|
||||||
body:
|
body:
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: checklist
|
id: checklist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: Feature request request
|
name: Feature request
|
||||||
description: Request a new functionality unrelated to any particular site or extractor
|
description: Request a new functionality unrelated to any particular site or extractor
|
||||||
labels: [triage, enhancement]
|
labels: [triage, enhancement]
|
||||||
body:
|
body:
|
||||||
|
|
5
.github/ISSUE_TEMPLATE_tmpl/6_question.yml
vendored
5
.github/ISSUE_TEMPLATE_tmpl/6_question.yml
vendored
|
@ -9,7 +9,7 @@ body:
|
||||||
description: |
|
description: |
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
options:
|
options:
|
||||||
- label: I'm asking a question and not reporting a bug/feature request
|
- label: I'm asking a question and **not** reporting a bug/feature request
|
||||||
required: true
|
required: true
|
||||||
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
||||||
required: true
|
required: true
|
||||||
|
@ -24,7 +24,8 @@ body:
|
||||||
description: |
|
description: |
|
||||||
Ask your question in an arbitrary form.
|
Ask your question in an arbitrary form.
|
||||||
Please make sure it's worded well enough to be understood, see [is-the-description-of-the-issue-itself-sufficient](https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient).
|
Please make sure it's worded well enough to be understood, see [is-the-description-of-the-issue-itself-sufficient](https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient).
|
||||||
Provide any additional information and as much context and examples as possible
|
Provide any additional information and as much context and examples as possible.
|
||||||
|
If your question contains "isn't working" or "can you add", this is most likely the wrong template
|
||||||
placeholder: WRITE QUESTION HERE
|
placeholder: WRITE QUESTION HERE
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
27
.gitignore
vendored
27
.gitignore
vendored
|
@ -1,27 +1,32 @@
|
||||||
# Config
|
# Config
|
||||||
*.conf
|
*.conf
|
||||||
*.spec
|
|
||||||
cookies
|
cookies
|
||||||
*cookies.txt
|
*cookies.txt
|
||||||
.netrc
|
.netrc
|
||||||
|
|
||||||
# Downloaded
|
# Downloaded
|
||||||
*.3gp
|
|
||||||
*.annotations.xml
|
*.annotations.xml
|
||||||
*.ape
|
|
||||||
*.aria2
|
*.aria2
|
||||||
*.avi
|
|
||||||
*.description
|
*.description
|
||||||
*.desktop
|
|
||||||
*.dump
|
*.dump
|
||||||
*.flac
|
|
||||||
*.flv
|
|
||||||
*.frag
|
*.frag
|
||||||
|
*.frag.aria2
|
||||||
*.frag.urls
|
*.frag.urls
|
||||||
*.info.json
|
*.info.json
|
||||||
|
*.live_chat.json
|
||||||
|
*.part*
|
||||||
|
*.unknown_video
|
||||||
|
*.ytdl
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
*.3gp
|
||||||
|
*.ape
|
||||||
|
*.avi
|
||||||
|
*.desktop
|
||||||
|
*.flac
|
||||||
|
*.flv
|
||||||
*.jpeg
|
*.jpeg
|
||||||
*.jpg
|
*.jpg
|
||||||
*.live_chat.json
|
|
||||||
*.m4a
|
*.m4a
|
||||||
*.m4v
|
*.m4v
|
||||||
*.mhtml
|
*.mhtml
|
||||||
|
@ -31,23 +36,18 @@ cookies
|
||||||
*.mp4
|
*.mp4
|
||||||
*.ogg
|
*.ogg
|
||||||
*.opus
|
*.opus
|
||||||
*.part
|
|
||||||
*.part-*
|
|
||||||
*.png
|
*.png
|
||||||
*.sbv
|
*.sbv
|
||||||
*.srt
|
*.srt
|
||||||
*.swf
|
*.swf
|
||||||
*.swp
|
*.swp
|
||||||
*.ttml
|
*.ttml
|
||||||
*.unknown_video
|
|
||||||
*.url
|
*.url
|
||||||
*.vtt
|
*.vtt
|
||||||
*.wav
|
*.wav
|
||||||
*.webloc
|
*.webloc
|
||||||
*.webm
|
*.webm
|
||||||
*.webp
|
*.webp
|
||||||
*.ytdl
|
|
||||||
.cache/
|
|
||||||
|
|
||||||
# Allow config/media files in testdata
|
# Allow config/media files in testdata
|
||||||
!test/**
|
!test/**
|
||||||
|
@ -86,7 +86,6 @@ README.txt
|
||||||
*.1
|
*.1
|
||||||
*.bash-completion
|
*.bash-completion
|
||||||
*.fish
|
*.fish
|
||||||
*.exe
|
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zsh
|
*.zsh
|
||||||
*.spec
|
*.spec
|
||||||
|
|
|
@ -227,6 +227,13 @@ ## Adding support for a new site
|
||||||
|
|
||||||
In any case, thank you very much for your contributions!
|
In any case, thank you very much for your contributions!
|
||||||
|
|
||||||
|
**Tip:** To test extractors that require login information, create a file `test/local_parameters.json` and add `"usenetrc": true` or your username and password in it:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "your user name",
|
||||||
|
"password": "your password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## yt-dlp coding conventions
|
## yt-dlp coding conventions
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ ## [coletdjnz](https://github.com/coletdjnz)
|
||||||
[![gh-sponsor](https://img.shields.io/badge/_-Sponsor-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)](https://github.com/sponsors/coletdjnz)
|
[![gh-sponsor](https://img.shields.io/badge/_-Sponsor-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)](https://github.com/sponsors/coletdjnz)
|
||||||
|
|
||||||
* YouTube improvements including: age-gate bypass, private playlists, multiple-clients (to avoid throttling) and a lot of under-the-hood improvements
|
* 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -13,10 +13,10 @@ pypi-files: AUTHORS Changelog.md LICENSE README.md README.txt supportedsites com
|
||||||
.PHONY: all clean install test tar pypi-files completions ot offlinetest codetest supportedsites
|
.PHONY: all clean install test tar pypi-files completions ot offlinetest codetest supportedsites
|
||||||
|
|
||||||
clean-test:
|
clean-test:
|
||||||
rm -rf *.3gp *.annotations.xml *.ape *.avi *.description *.dump *.flac *.flv *.frag *.frag.aria2 *.frag.urls \
|
rm -rf test/testdata/player-*.js tmp/ *.annotations.xml *.aria2 *.description *.dump *.frag \
|
||||||
*.info.json *.jpeg *.jpg *.live_chat.json *.m4a *.m4v *.mkv *.mp3 *.mp4 *.ogg *.opus *.part* *.png *.sbv *.srt \
|
*.frag.aria2 *.frag.urls *.info.json *.live_chat.json *.part* *.unknown_video *.ytdl \
|
||||||
*.swf *.swp *.ttml *.vtt *.wav *.webm *.webp *.mhtml *.mov *.unknown_video *.desktop *.url *.webloc *.ytdl \
|
*.3gp *.ape *.avi *.desktop *.flac *.flv *.jpeg *.jpg *.m4a *.m4v *.mhtml *.mkv *.mov *.mp3 \
|
||||||
test/testdata/player-*.js tmp/
|
*.mp4 *.ogg *.opus *.png *.sbv *.srt *.swf *.swp *.ttml *.url *.vtt *.wav *.webloc *.webm *.webp
|
||||||
clean-dist:
|
clean-dist:
|
||||||
rm -rf yt-dlp.1.temp.md yt-dlp.1 README.txt MANIFEST build/ dist/ .coverage cover/ yt-dlp.tar.gz completions/ \
|
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
|
yt_dlp/extractor/lazy_extractors.py *.spec CONTRIBUTING.md.tmp yt-dlp yt-dlp.exe yt_dlp.egg-info/ AUTHORS .mailmap
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
|
|
||||||
# NEW FEATURES
|
# NEW FEATURES
|
||||||
|
|
||||||
* Based on **youtube-dl 2021.06.06 [commit/379f52a](https://github.com/ytdl-org/youtube-dl/commit/379f52a4954013767219d25099cce9e0f9401961)** and **youtube-dlc 2020.11.11-3 [commit/98e248f](https://github.com/blackjack4494/yt-dlc/commit/98e248faa49e69d795abc60f7cdefcf91e2612aa)**: 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/5014bd6](https://github.com/ytdl-org/youtube-dl/commit/5014bd67c22b421207b2650d4dc874b95b36dda1)** 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
|
* **[SponsorBlock Integration](#sponsorblock-options)**: You can mark/remove sponsor sections in youtube videos by utilizing the [SponsorBlock](https://sponsor.ajay.app) API
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ ### Differences in default behavior
|
||||||
Some of yt-dlp's default options are different from that of youtube-dl and youtube-dlc:
|
Some of yt-dlp's default options are different from that of youtube-dl and youtube-dlc:
|
||||||
|
|
||||||
* The options `--auto-number` (`-A`), `--title` (`-t`) and `--literal` (`-l`), no longer work. See [removed options](#Removed) for details
|
* The options `--auto-number` (`-A`), `--title` (`-t`) and `--literal` (`-l`), no longer work. See [removed options](#Removed) for details
|
||||||
* `avconv` is not supported as as an alternative to `ffmpeg`
|
* `avconv` is not supported as an alternative to `ffmpeg`
|
||||||
* 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 [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 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
|
* 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
|
||||||
|
@ -172,7 +172,7 @@ ### Using the release binary
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo aria2c https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
|
sudo aria2c https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp --dir /usr/local/bin -o yt-dlp
|
||||||
sudo chmod a+rx /usr/local/bin/yt-dlp
|
sudo chmod a+rx /usr/local/bin/yt-dlp
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ ## DEPENDENCIES
|
||||||
|
|
||||||
While all the other dependencies are optional, `ffmpeg` and `ffprobe` are highly recommended
|
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. Licence [depends on the build](https://www.ffmpeg.org/legal.html)
|
* [**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)
|
* [**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)
|
* [**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)
|
* [**websockets**](https://github.com/aaugustin/websockets) - For downloading over websocket. Licensed under [BSD3](https://github.com/aaugustin/websockets/blob/main/LICENSE)
|
||||||
|
|
|
@ -27,13 +27,13 @@
|
||||||
except Exception:
|
except Exception:
|
||||||
GIT_HEAD = None
|
GIT_HEAD = None
|
||||||
|
|
||||||
VERSION_FILE = f'''
|
VERSION_FILE = f'''\
|
||||||
# Autogenerated by devscripts/update-version.py
|
# Autogenerated by devscripts/update-version.py
|
||||||
|
|
||||||
__version__ = {VERSION!r}
|
__version__ = {VERSION!r}
|
||||||
|
|
||||||
RELEASE_GIT_HEAD = {GIT_HEAD!r}
|
RELEASE_GIT_HEAD = {GIT_HEAD!r}
|
||||||
'''.lstrip()
|
'''
|
||||||
|
|
||||||
with open('yt_dlp/version.py', 'wt') as f:
|
with open('yt_dlp/version.py', 'wt') as f:
|
||||||
f.write(VERSION_FILE)
|
f.write(VERSION_FILE)
|
||||||
|
|
5
docs/Contributing.md
Normal file
5
docs/Contributing.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
orphan: true
|
||||||
|
---
|
||||||
|
```{include} ../Contributing.md
|
||||||
|
```
|
|
@ -780,8 +780,8 @@ def expect_same_infodict(out):
|
||||||
test('%(title5)+#U', 'a\u0301e\u0301i\u0301 A')
|
test('%(title5)+#U', 'a\u0301e\u0301i\u0301 A')
|
||||||
test('%(height)D', '1K')
|
test('%(height)D', '1K')
|
||||||
test('%(height)5.2D', ' 1.08K')
|
test('%(height)5.2D', ' 1.08K')
|
||||||
test('%(title4).10F', ('foo \'bar\' ', 'foo \'bar\'#'))
|
|
||||||
test('%(title4)#F', 'foo_bar_test')
|
test('%(title4)#F', 'foo_bar_test')
|
||||||
|
test('%(title4).10F', ('foo \'bar\' ', 'foo \'bar\'' + ('#' if compat_os_name == 'nt' else ' ')))
|
||||||
if compat_os_name == 'nt':
|
if compat_os_name == 'nt':
|
||||||
test('%(title4)q', ('"foo \\"bar\\" test"', "'foo _'bar_' test'"))
|
test('%(title4)q', ('"foo \\"bar\\" test"', "'foo _'bar_' test'"))
|
||||||
test('%(formats.:.id)#q', ('"id 1" "id 2" "id 3"', "'id 1' 'id 2' 'id 3'"))
|
test('%(formats.:.id)#q', ('"id 1" "id 2" "id 3"', "'id 1' 'id 2' 'id 3'"))
|
||||||
|
|
|
@ -82,6 +82,10 @@
|
||||||
'https://www.youtube.com/s/player/f1ca6900/player_ias.vflset/en_US/base.js',
|
'https://www.youtube.com/s/player/f1ca6900/player_ias.vflset/en_US/base.js',
|
||||||
'cu3wyu6LQn2hse', 'jvxetvmlI9AN9Q',
|
'cu3wyu6LQn2hse', 'jvxetvmlI9AN9Q',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/8040e515/player_ias.vflset/en_US/base.js',
|
||||||
|
'wvOFaY-yjgDuIEg5', 'HkfBFDHmgw4rsw',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1495,7 +1495,7 @@ def process_ie_result(self, ie_result, download=True, extra_info=None):
|
||||||
self.write_debug('Additional URLs: "%s"' % '", "'.join(additional_urls))
|
self.write_debug('Additional URLs: "%s"' % '", "'.join(additional_urls))
|
||||||
ie_result['additional_entries'] = [
|
ie_result['additional_entries'] = [
|
||||||
self.extract_info(
|
self.extract_info(
|
||||||
url, download, extra_info,
|
url, download, extra_info=extra_info,
|
||||||
force_generic_extractor=self.params.get('force_generic_extractor'))
|
force_generic_extractor=self.params.get('force_generic_extractor'))
|
||||||
for url in additional_urls
|
for url in additional_urls
|
||||||
]
|
]
|
||||||
|
@ -2474,10 +2474,7 @@ def is_wellformed(f):
|
||||||
info_dict['id'], automatic_captions, 'automatic captions')
|
info_dict['id'], automatic_captions, 'automatic captions')
|
||||||
self.list_subtitles(info_dict['id'], subtitles, 'subtitles')
|
self.list_subtitles(info_dict['id'], subtitles, 'subtitles')
|
||||||
if self.params.get('listformats') or interactive_format_selection:
|
if self.params.get('listformats') or interactive_format_selection:
|
||||||
if not info_dict.get('formats') and not info_dict.get('url'):
|
self.list_formats(info_dict)
|
||||||
self.to_screen('%s has no formats' % info_dict['id'])
|
|
||||||
else:
|
|
||||||
self.list_formats(info_dict)
|
|
||||||
if list_only:
|
if list_only:
|
||||||
# Without this printing, -F --print-json will not work
|
# Without this printing, -F --print-json will not work
|
||||||
self.__forced_printings(info_dict, self.prepare_filename(info_dict), incomplete=True)
|
self.__forced_printings(info_dict, self.prepare_filename(info_dict), incomplete=True)
|
||||||
|
@ -3361,6 +3358,11 @@ def _list_format_headers(self, *headers):
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
def list_formats(self, info_dict):
|
def list_formats(self, info_dict):
|
||||||
|
if not info_dict.get('formats') and not info_dict.get('url'):
|
||||||
|
self.to_screen('%s has no formats' % info_dict['id'])
|
||||||
|
return
|
||||||
|
self.to_screen('[info] Available formats for %s:' % info_dict['id'])
|
||||||
|
|
||||||
formats = info_dict.get('formats', [info_dict])
|
formats = info_dict.get('formats', [info_dict])
|
||||||
new_format = self.params.get('listformats_table', True) is not False
|
new_format = self.params.get('listformats_table', True) is not False
|
||||||
if new_format:
|
if new_format:
|
||||||
|
@ -3375,7 +3377,7 @@ def list_formats(self, info_dict):
|
||||||
delim,
|
delim,
|
||||||
format_field(f, 'filesize', ' \t%s', func=format_bytes) + format_field(f, 'filesize_approx', '~\t%s', func=format_bytes),
|
format_field(f, 'filesize', ' \t%s', func=format_bytes) + format_field(f, 'filesize_approx', '~\t%s', func=format_bytes),
|
||||||
format_field(f, 'tbr', '\t%dk'),
|
format_field(f, 'tbr', '\t%dk'),
|
||||||
shorten_protocol_name(f.get('protocol', '').replace('native', 'n')),
|
shorten_protocol_name(f.get('protocol', '')),
|
||||||
delim,
|
delim,
|
||||||
format_field(f, 'vcodec', default='unknown').replace(
|
format_field(f, 'vcodec', default='unknown').replace(
|
||||||
'none',
|
'none',
|
||||||
|
@ -3411,8 +3413,6 @@ def list_formats(self, info_dict):
|
||||||
if f.get('preference') is None or f['preference'] >= -1000]
|
if f.get('preference') is None or f['preference'] >= -1000]
|
||||||
header_line = ['format code', 'extension', 'resolution', 'note']
|
header_line = ['format code', 'extension', 'resolution', 'note']
|
||||||
|
|
||||||
self.to_screen(
|
|
||||||
'[info] Available formats for %s:' % info_dict['id'])
|
|
||||||
self.to_stdout(render_table(
|
self.to_stdout(render_table(
|
||||||
header_line, table,
|
header_line, table,
|
||||||
extra_gap=(0 if new_format else 1),
|
extra_gap=(0 if new_format else 1),
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
)
|
)
|
||||||
from .compat import (
|
from .compat import (
|
||||||
compat_getpass,
|
compat_getpass,
|
||||||
|
compat_os_name,
|
||||||
compat_shlex_quote,
|
compat_shlex_quote,
|
||||||
workaround_optparse_bug9161,
|
workaround_optparse_bug9161,
|
||||||
)
|
)
|
||||||
|
@ -95,7 +96,8 @@ def _real_main(argv=None):
|
||||||
if opts.batchfile is not None:
|
if opts.batchfile is not None:
|
||||||
try:
|
try:
|
||||||
if opts.batchfile == '-':
|
if opts.batchfile == '-':
|
||||||
write_string('Reading URLs from stdin:\n')
|
write_string('Reading URLs from stdin - EOF (%s) to end:\n' % (
|
||||||
|
'Ctrl+Z' if compat_os_name == 'nt' else 'Ctrl+D'))
|
||||||
batchfd = sys.stdin
|
batchfd = sys.stdin
|
||||||
else:
|
else:
|
||||||
batchfd = io.open(
|
batchfd = io.open(
|
||||||
|
@ -518,7 +520,7 @@ def report_unplayable_conflict(opt_name, arg, default=False, allowed=None):
|
||||||
if len(dur) == 2 and all(t is not None for t in dur):
|
if len(dur) == 2 and all(t is not None for t in dur):
|
||||||
remove_ranges.append(tuple(dur))
|
remove_ranges.append(tuple(dur))
|
||||||
continue
|
continue
|
||||||
parser.error(f'invalid --remove-chapters time range {regex!r}. Must be of the form ?start-end')
|
parser.error(f'invalid --remove-chapters time range {regex!r}. Must be of the form *start-end')
|
||||||
try:
|
try:
|
||||||
remove_chapters_patterns.append(re.compile(regex))
|
remove_chapters_patterns.append(re.compile(regex))
|
||||||
except re.error as err:
|
except re.error as err:
|
||||||
|
|
|
@ -397,6 +397,7 @@ def download(self, filename, info_dict, subtitle=False):
|
||||||
'status': 'finished',
|
'status': 'finished',
|
||||||
'total_bytes': os.path.getsize(encodeFilename(filename)),
|
'total_bytes': os.path.getsize(encodeFilename(filename)),
|
||||||
}, info_dict)
|
}, info_dict)
|
||||||
|
self._finish_multiline_status()
|
||||||
return True, False
|
return True, False
|
||||||
|
|
||||||
if subtitle is False:
|
if subtitle is False:
|
||||||
|
|
|
@ -340,7 +340,7 @@ def _find_secret_formats(self, formats, video_id):
|
||||||
yield {
|
yield {
|
||||||
**base_format,
|
**base_format,
|
||||||
'format_id': join_nonempty('sec', height),
|
'format_id': join_nonempty('sec', height),
|
||||||
'url': re.sub(r'(QualityLevels\()\d+(\))', fr'\<1>{bitrate}\2', base_url),
|
'url': re.sub(r'(QualityLevels\()\d+(\))', fr'\1{bitrate}\2', base_url),
|
||||||
'width': int_or_none(video_quality.attrib.get('MaxWidth')),
|
'width': int_or_none(video_quality.attrib.get('MaxWidth')),
|
||||||
'tbr': bitrate / 1000.0,
|
'tbr': bitrate / 1000.0,
|
||||||
'height': height,
|
'height': height,
|
||||||
|
|
|
@ -616,7 +616,7 @@ def extract(self, url):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'video_id': e.video_id or self.get_temp_id(url),
|
'video_id': e.video_id or self.get_temp_id(url),
|
||||||
'ie': self.IE_NAME,
|
'ie': self.IE_NAME,
|
||||||
'tb': e.traceback,
|
'tb': e.traceback or sys.exc_info()[2],
|
||||||
'expected': e.expected,
|
'expected': e.expected,
|
||||||
'cause': e.cause
|
'cause': e.cause
|
||||||
}
|
}
|
||||||
|
@ -1574,7 +1574,7 @@ class FormatSort:
|
||||||
'vcodec': {'type': 'ordered', 'regex': True,
|
'vcodec': {'type': 'ordered', 'regex': True,
|
||||||
'order': ['av0?1', 'vp0?9.2', 'vp0?9', '[hx]265|he?vc?', '[hx]264|avc', 'vp0?8', 'mp4v|h263', 'theora', '', None, 'none']},
|
'order': ['av0?1', 'vp0?9.2', 'vp0?9', '[hx]265|he?vc?', '[hx]264|avc', 'vp0?8', 'mp4v|h263', 'theora', '', None, 'none']},
|
||||||
'acodec': {'type': 'ordered', 'regex': True,
|
'acodec': {'type': 'ordered', 'regex': True,
|
||||||
'order': ['opus', 'vorbis', 'aac', 'mp?4a?', 'mp3', 'e-?a?c-?3', 'ac-?3', 'dts', '', None, 'none']},
|
'order': ['[af]lac', 'wav|aiff', 'opus', 'vorbis', 'aac', 'mp?4a?', 'mp3', 'e-?a?c-?3', 'ac-?3', 'dts', '', None, 'none']},
|
||||||
'hdr': {'type': 'ordered', 'regex': True, 'field': 'dynamic_range',
|
'hdr': {'type': 'ordered', 'regex': True, 'field': 'dynamic_range',
|
||||||
'order': ['dv', '(hdr)?12', r'(hdr)?10\+', '(hdr)?10', 'hlg', '', 'sdr', None]},
|
'order': ['dv', '(hdr)?12', r'(hdr)?10\+', '(hdr)?10', 'hlg', '', 'sdr', None]},
|
||||||
'proto': {'type': 'ordered', 'regex': True, 'field': 'protocol',
|
'proto': {'type': 'ordered', 'regex': True, 'field': 'protocol',
|
||||||
|
|
|
@ -41,7 +41,7 @@ class FancodeVodIE(InfoExtractor):
|
||||||
_ACCESS_TOKEN = None
|
_ACCESS_TOKEN = None
|
||||||
_NETRC_MACHINE = 'fancode'
|
_NETRC_MACHINE = 'fancode'
|
||||||
|
|
||||||
_LOGIN_HINT = 'Use "--user refresh --password <refresh_token>" to login using a refresh token'
|
_LOGIN_HINT = 'Use "--username refresh --password <refresh_token>" to login using a refresh token'
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
|
|
|
@ -258,8 +258,7 @@ def _extract_urls(webpage):
|
||||||
webpage)
|
webpage)
|
||||||
|
|
||||||
def _extract_count(self, pattern, webpage, name):
|
def _extract_count(self, pattern, webpage, name):
|
||||||
return str_to_int(self._search_regex(
|
return str_to_int(self._search_regex(pattern, webpage, '%s count' % name, default=None))
|
||||||
pattern, webpage, '%s count' % name, fatal=False))
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = self._match_valid_url(url)
|
mobj = self._match_valid_url(url)
|
||||||
|
|
|
@ -99,7 +99,7 @@ class RoosterTeethIE(RoosterTeethBaseIE):
|
||||||
'series': 'Million Dollars, But...',
|
'series': 'Million Dollars, But...',
|
||||||
'episode': 'Million Dollars, But... The Game Announcement',
|
'episode': 'Million Dollars, But... The Game Announcement',
|
||||||
},
|
},
|
||||||
'skip_download': 'm3u8',
|
'params': {'skip_download': True},
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://roosterteeth.com/watch/rwby-bonus-25',
|
'url': 'https://roosterteeth.com/watch/rwby-bonus-25',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@ -112,7 +112,7 @@ class RoosterTeethIE(RoosterTeethBaseIE):
|
||||||
'thumbnail': r're:^https?://.*\.(png|jpe?g)$',
|
'thumbnail': r're:^https?://.*\.(png|jpe?g)$',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
},
|
},
|
||||||
'skip_download': 'm3u8',
|
'params': {'skip_download': True},
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://achievementhunter.roosterteeth.com/episode/off-topic-the-achievement-hunter-podcast-2016-i-didn-t-think-it-would-pass-31',
|
'url': 'http://achievementhunter.roosterteeth.com/episode/off-topic-the-achievement-hunter-podcast-2016-i-didn-t-think-it-would-pass-31',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
|
|
@ -130,7 +130,7 @@ def _login(self):
|
||||||
elif username is not None:
|
elif username is not None:
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
'Login using username and password is not currently supported. '
|
'Login using username and password is not currently supported. '
|
||||||
'Use "--user oauth --password <oauth_token>" to login using an oauth token')
|
'Use "--username oauth --password <oauth_token>" to login using an oauth token')
|
||||||
|
|
||||||
r'''
|
r'''
|
||||||
def genDevId():
|
def genDevId():
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
|
str_or_none,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
unsmuggle_url,
|
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
|
unsmuggle_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -25,9 +26,9 @@ def _extract_from_playlist_data(self, value):
|
||||||
'id': voice_id,
|
'id': voice_id,
|
||||||
'title': compat_str(value.get('PlaylistName')),
|
'title': compat_str(value.get('PlaylistName')),
|
||||||
'uploader': value.get('SpeakerName'),
|
'uploader': value.get('SpeakerName'),
|
||||||
'uploader_id': compat_str(value.get('SpeakerId')),
|
'uploader_id': str_or_none(value.get('SpeakerId')),
|
||||||
'channel': value.get('ChannelName'),
|
'channel': value.get('ChannelName'),
|
||||||
'channel_id': compat_str(value.get('ChannelId')),
|
'channel_id': str_or_none(value.get('ChannelId')),
|
||||||
'upload_date': upload_date,
|
'upload_date': upload_date,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -668,7 +668,7 @@ def _dict_from_options_callback(
|
||||||
downloader.add_option(
|
downloader.add_option(
|
||||||
'-N', '--concurrent-fragments',
|
'-N', '--concurrent-fragments',
|
||||||
dest='concurrent_fragment_downloads', metavar='N', default=1, type=int,
|
dest='concurrent_fragment_downloads', metavar='N', default=1, type=int,
|
||||||
help='Number of fragments of a dash/hlsnative video that should be download concurrently (default is %default)')
|
help='Number of fragments of a dash/hlsnative video that should be downloaded concurrently (default is %default)')
|
||||||
downloader.add_option(
|
downloader.add_option(
|
||||||
'-r', '--limit-rate', '--rate-limit',
|
'-r', '--limit-rate', '--rate-limit',
|
||||||
dest='ratelimit', metavar='RATE',
|
dest='ratelimit', metavar='RATE',
|
||||||
|
|
|
@ -99,7 +99,7 @@ def f(info):
|
||||||
class MetadataFromFieldPP(MetadataParserPP):
|
class MetadataFromFieldPP(MetadataParserPP):
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_action(cls, f):
|
def to_action(cls, f):
|
||||||
match = re.match(r'(?P<in>.*?)(?<!\\):(?P<out>.+)$', f)
|
match = re.match(r'(?s)(?P<in>.*?)(?<!\\):(?P<out>.+)$', f)
|
||||||
if match is None:
|
if match is None:
|
||||||
raise ValueError(f'it should be FROM:TO, not {f!r}')
|
raise ValueError(f'it should be FROM:TO, not {f!r}')
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -257,7 +257,7 @@ def update_self(to_screen, verbose, opener):
|
||||||
|
|
||||||
write_string(
|
write_string(
|
||||||
'DeprecationWarning: "yt_dlp.update.update_self" is deprecated and may be removed in a future version. '
|
'DeprecationWarning: "yt_dlp.update.update_self" is deprecated and may be removed in a future version. '
|
||||||
'Use "yt_dlp.update.run_update(ydl)" instead')
|
'Use "yt_dlp.update.run_update(ydl)" instead\n')
|
||||||
|
|
||||||
class FakeYDL():
|
class FakeYDL():
|
||||||
_opener = opener
|
_opener = opener
|
||||||
|
|
|
@ -1862,7 +1862,6 @@ def _windows_write_string(s, out):
|
||||||
False if it has yet to be written out."""
|
False if it has yet to be written out."""
|
||||||
# Adapted from http://stackoverflow.com/a/3259271/35070
|
# Adapted from http://stackoverflow.com/a/3259271/35070
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import ctypes.wintypes
|
import ctypes.wintypes
|
||||||
|
|
||||||
WIN_OUTPUT_IDS = {
|
WIN_OUTPUT_IDS = {
|
||||||
|
@ -3193,30 +3192,29 @@ def parse_codecs(codecs_str):
|
||||||
if codec in ('avc1', 'avc2', 'avc3', 'avc4', 'vp9', 'vp8', 'hev1', 'hev2',
|
if codec in ('avc1', 'avc2', 'avc3', 'avc4', 'vp9', 'vp8', 'hev1', 'hev2',
|
||||||
'h263', 'h264', 'mp4v', 'hvc1', 'av1', 'theora', 'dvh1', 'dvhe'):
|
'h263', 'h264', 'mp4v', 'hvc1', 'av1', 'theora', 'dvh1', 'dvhe'):
|
||||||
if not vcodec:
|
if not vcodec:
|
||||||
vcodec = '.'.join(parts[:4]) if codec in ('vp9', 'av1') else full_codec
|
vcodec = '.'.join(parts[:4]) if codec in ('vp9', 'av1', 'hvc1') else full_codec
|
||||||
if codec in ('dvh1', 'dvhe'):
|
if codec in ('dvh1', 'dvhe'):
|
||||||
hdr = 'DV'
|
hdr = 'DV'
|
||||||
elif codec == 'av1' and len(parts) > 3 and parts[3] == '10':
|
elif codec == 'av1' and len(parts) > 3 and parts[3] == '10':
|
||||||
hdr = 'HDR10'
|
hdr = 'HDR10'
|
||||||
elif full_codec.replace('0', '').startswith('vp9.2'):
|
elif full_codec.replace('0', '').startswith('vp9.2'):
|
||||||
hdr = 'HDR10'
|
hdr = 'HDR10'
|
||||||
elif codec in ('mp4a', 'opus', 'vorbis', 'mp3', 'aac', 'ac-3', 'ec-3', 'eac3', 'dtsc', 'dtse', 'dtsh', 'dtsl'):
|
elif codec in ('flac', 'mp4a', 'opus', 'vorbis', 'mp3', 'aac', 'ac-3', 'ec-3', 'eac3', 'dtsc', 'dtse', 'dtsh', 'dtsl'):
|
||||||
if not acodec:
|
if not acodec:
|
||||||
acodec = full_codec
|
acodec = full_codec
|
||||||
else:
|
else:
|
||||||
write_string('WARNING: Unknown codec %s\n' % full_codec, sys.stderr)
|
write_string('WARNING: Unknown codec %s\n' % full_codec, sys.stderr)
|
||||||
if not vcodec and not acodec:
|
if vcodec or acodec:
|
||||||
if len(split_codecs) == 2:
|
|
||||||
return {
|
|
||||||
'vcodec': split_codecs[0],
|
|
||||||
'acodec': split_codecs[1],
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {
|
return {
|
||||||
'vcodec': vcodec or 'none',
|
'vcodec': vcodec or 'none',
|
||||||
'acodec': acodec or 'none',
|
'acodec': acodec or 'none',
|
||||||
'dynamic_range': hdr,
|
'dynamic_range': hdr,
|
||||||
}
|
}
|
||||||
|
elif len(split_codecs) == 2:
|
||||||
|
return {
|
||||||
|
'vcodec': split_codecs[0],
|
||||||
|
'acodec': split_codecs[1],
|
||||||
|
}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue