mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-27 17:11:03 +01:00
[cleanup] Misc (#10075)
Closes #10303 Authored by: bashonly, seproDev, jucor, c-basalt Co-authored-by: sepro <4618135+seproDev@users.noreply.github.com> Co-authored-by: Julien Cornebise <julien@cornebise.com> Co-authored-by: c-basalt <117849907+c-basalt@users.noreply.github.com>
This commit is contained in:
parent
d4b99a2333
commit
6aaf96a3d6
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -525,6 +525,10 @@ jobs:
|
|||||||
# make sure SHA sums are also printed to stdout
|
# make sure SHA sums are also printed to stdout
|
||||||
sha256sum -- * | tee ../SHA2-256SUMS
|
sha256sum -- * | tee ../SHA2-256SUMS
|
||||||
sha512sum -- * | tee ../SHA2-512SUMS
|
sha512sum -- * | tee ../SHA2-512SUMS
|
||||||
|
# also print as permanent annotations to the summary page
|
||||||
|
while read -r shasum; do
|
||||||
|
echo "::notice title=${shasum##* }::sha256: ${shasum% *}"
|
||||||
|
done < ../SHA2-256SUMS
|
||||||
|
|
||||||
- name: Make Update spec
|
- name: Make Update spec
|
||||||
run: |
|
run: |
|
||||||
|
@ -127,7 +127,7 @@ ### Are you willing to share account details if needed?
|
|||||||
|
|
||||||
### Is the website primarily used for piracy?
|
### Is the website primarily used for piracy?
|
||||||
|
|
||||||
We follow [youtube-dl's policy](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free) to not support services that is primarily used for infringing copyright. Additionally, it has been decided to not to support porn sites that specialize in fakes. We also cannot support any service that serves only [DRM protected content](https://en.wikipedia.org/wiki/Digital_rights_management).
|
We follow [youtube-dl's policy](https://github.com/ytdl-org/youtube-dl#can-you-add-support-for-this-anime-video-site-or-site-which-shows-current-movies-for-free) to not support services that is primarily used for infringing copyright. Additionally, it has been decided to not to support porn sites that specialize in fakes. We also cannot support any service that serves only [DRM protected content](https://en.wikipedia.org/wiki/Digital_rights_management).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -215,8 +215,8 @@ ## Adding support for a new site
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
class YourExtractorIE(InfoExtractor):
|
class YourExtractorIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?yourextractor\.com/watch/(?P<id>[0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?yourextractor\.com/watch/(?P<id>[0-9]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
@ -244,7 +244,7 @@ ## Adding support for a new site
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
# TODO more code goes here, for example ...
|
# TODO more code goes here, for example ...
|
||||||
title = self._html_search_regex(r'<h1>(.+?)</h1>', webpage, 'title')
|
title = self._html_search_regex(r'<h1>(.+?)</h1>', webpage, 'title')
|
||||||
|
|
||||||
@ -320,7 +320,7 @@ #### Example
|
|||||||
```python
|
```python
|
||||||
meta = self._download_json(url, video_id)
|
meta = self._download_json(url, video_id)
|
||||||
```
|
```
|
||||||
|
|
||||||
Assume at this point `meta`'s layout is:
|
Assume at this point `meta`'s layout is:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -750,7 +750,7 @@ ### Use convenience conversion and parsing functions
|
|||||||
|
|
||||||
Use `traverse_obj` and `try_call` (superseeds `dict_get` and `try_get`) 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.
|
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.
|
||||||
|
|
||||||
Explore [`yt_dlp/utils/`](yt_dlp/utils/) for more useful convenience functions.
|
Explore [`yt_dlp/utils/`](yt_dlp/utils/) for more useful convenience functions.
|
||||||
|
|
||||||
|
@ -61,3 +61,10 @@ ## [Grub4K](https://github.com/Grub4K)
|
|||||||
* Reworked internals like `traverse_obj`, various core refactors and bugs fixes
|
* Reworked internals like `traverse_obj`, various core refactors and bugs fixes
|
||||||
* Implemented proper progress reporting for parallel downloads
|
* Implemented proper progress reporting for parallel downloads
|
||||||
* Improved/fixed/added Bundestag, crunchyroll, pr0gramm, Twitter, WrestleUniverse etc
|
* Improved/fixed/added Bundestag, crunchyroll, pr0gramm, Twitter, WrestleUniverse etc
|
||||||
|
|
||||||
|
|
||||||
|
## [sepro](https://github.com/seproDev)
|
||||||
|
|
||||||
|
* UX improvements: Warn when ffmpeg is missing, warn when double-clicking exe
|
||||||
|
* Code cleanup: Remove dead extractors, mark extractors as broken, enable/apply ruff rules
|
||||||
|
* Improved/fixed/added ArdMediathek, DRTV, Floatplane, MagentaMusik, Naver, Nebula, OnDemandKorea, Vbox7 etc
|
||||||
|
66
README.md
66
README.md
@ -141,7 +141,7 @@ ## UPDATE
|
|||||||
|
|
||||||
If you [installed with pip](https://github.com/yt-dlp/yt-dlp/wiki/Installation#with-pip), simply re-run the same command that was used to install the program
|
If you [installed with pip](https://github.com/yt-dlp/yt-dlp/wiki/Installation#with-pip), simply re-run the same command that was used to install the program
|
||||||
|
|
||||||
For other third-party package managers, see [the wiki](https://github.com/yt-dlp/yt-dlp/wiki/Installation#third-party-package-managers) or refer their documentation
|
For other third-party package managers, see [the wiki](https://github.com/yt-dlp/yt-dlp/wiki/Installation#third-party-package-managers) or refer to their documentation
|
||||||
|
|
||||||
<a id="update-channels"></a>
|
<a id="update-channels"></a>
|
||||||
|
|
||||||
@ -184,10 +184,10 @@ ## DEPENDENCIES
|
|||||||
|
|
||||||
### Strongly recommended
|
### Strongly 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)
|
* [**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)
|
||||||
|
|
||||||
There are bugs in ffmpeg that cause 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 some of 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
|
There are bugs in ffmpeg that cause 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 some of 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
|
||||||
|
|
||||||
**Important**: What you need is ffmpeg *binary*, **NOT** [the Python package of the same name](https://pypi.org/project/ffmpeg)
|
**Important**: What you need is ffmpeg *binary*, **NOT** [the Python package of the same name](https://pypi.org/project/ffmpeg)
|
||||||
|
|
||||||
### Networking
|
### Networking
|
||||||
@ -198,7 +198,7 @@ ### Networking
|
|||||||
|
|
||||||
#### Impersonation
|
#### Impersonation
|
||||||
|
|
||||||
The following provide support for impersonating browser requests. This may be required for some sites that employ TLS fingerprinting.
|
The following provide support for impersonating browser requests. This may be required for some sites that employ TLS fingerprinting.
|
||||||
|
|
||||||
* [**curl_cffi**](https://github.com/yifeikong/curl_cffi) (recommended) - Python binding for [curl-impersonate](https://github.com/lwthiker/curl-impersonate). Provides impersonation targets for Chrome, Edge and Safari. Licensed under [MIT](https://github.com/yifeikong/curl_cffi/blob/main/LICENSE)
|
* [**curl_cffi**](https://github.com/yifeikong/curl_cffi) (recommended) - Python binding for [curl-impersonate](https://github.com/lwthiker/curl-impersonate). Provides impersonation targets for Chrome, Edge and Safari. Licensed under [MIT](https://github.com/yifeikong/curl_cffi/blob/main/LICENSE)
|
||||||
* Can be installed with the `curl-cffi` group, e.g. `pip install "yt-dlp[default,curl-cffi]"`
|
* Can be installed with the `curl-cffi` group, e.g. `pip install "yt-dlp[default,curl-cffi]"`
|
||||||
@ -275,7 +275,7 @@ ### Standalone Py2Exe Builds (Windows)
|
|||||||
### Related scripts
|
### Related scripts
|
||||||
|
|
||||||
* **`devscripts/install_deps.py`** - Install dependencies for yt-dlp.
|
* **`devscripts/install_deps.py`** - Install dependencies for yt-dlp.
|
||||||
* **`devscripts/update-version.py`** - Update the version number based on current date.
|
* **`devscripts/update-version.py`** - Update the version number based on the current date.
|
||||||
* **`devscripts/set-variant.py`** - Set the build variant of the executable.
|
* **`devscripts/set-variant.py`** - Set the build variant of the executable.
|
||||||
* **`devscripts/make_changelog.py`** - Create a markdown changelog using short commit messages and update `CONTRIBUTORS` file.
|
* **`devscripts/make_changelog.py`** - Create a markdown changelog using short commit messages and update `CONTRIBUTORS` file.
|
||||||
* **`devscripts/make_lazy_extractors.py`** - Create lazy extractors. Running this before building the binaries (any variant) will improve their startup performance. Set the environment variable `YTDLP_NO_LAZY_EXTRACTORS=1` if you wish to forcefully disable lazy extractor loading.
|
* **`devscripts/make_lazy_extractors.py`** - Create lazy extractors. Running this before building the binaries (any variant) will improve their startup performance. Set the environment variable `YTDLP_NO_LAZY_EXTRACTORS=1` if you wish to forcefully disable lazy extractor loading.
|
||||||
@ -1125,7 +1125,7 @@ # CONFIGURATION
|
|||||||
* `/etc/yt-dlp/config`
|
* `/etc/yt-dlp/config`
|
||||||
* `/etc/yt-dlp/config.txt`
|
* `/etc/yt-dlp/config.txt`
|
||||||
|
|
||||||
E.g. 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:
|
E.g. 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:
|
||||||
```
|
```
|
||||||
# Lines starting with # are comments
|
# Lines starting with # are comments
|
||||||
|
|
||||||
@ -1142,7 +1142,7 @@ # Save all videos under YouTube directory in your home directory
|
|||||||
-o ~/YouTube/%(title)s.%(ext)s
|
-o ~/YouTube/%(title)s.%(ext)s
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: Options in configuration file are just the same options aka switches used in regular command line calls; thus there **must be no whitespace** after `-` or `--`, e.g. `-o` or `--proxy` but not `- o` or `-- proxy`. They must also be quoted when necessary as-if it were a UNIX shell.
|
**Note**: Options in configuration file are just the same options aka switches used in regular command line calls; thus there **must be no whitespace** after `-` or `--`, e.g. `-o` or `--proxy` but not `- o` or `-- proxy`. They must also be quoted when necessary, as if it were a UNIX shell.
|
||||||
|
|
||||||
You can use `--ignore-config` if you want to disable all configuration files for a particular yt-dlp run. If `--ignore-config` is found inside any configuration file, no further configuration will be loaded. For example, having the option in the portable configuration file prevents loading of home, user, and system configurations. Additionally, (for backward compatibility) if `--ignore-config` is found inside the system configuration file, the user configuration is not loaded.
|
You can use `--ignore-config` if you want to disable all configuration files for a particular yt-dlp run. If `--ignore-config` is found inside any configuration file, no further configuration will be loaded. For example, having the option in the portable configuration file prevents loading of home, user, and system configurations. Additionally, (for backward compatibility) if `--ignore-config` is found inside the system configuration file, the user configuration is not loaded.
|
||||||
|
|
||||||
@ -1154,12 +1154,12 @@ ### Configuration file encoding
|
|||||||
|
|
||||||
### Authentication with netrc
|
### Authentication with netrc
|
||||||
|
|
||||||
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every yt-dlp execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](https://stackoverflow.com/tags/.netrc/info) on a per-extractor basis. For that you will need to create a `.netrc` file in `--netrc-location` and restrict permissions to read/write by only you:
|
You may also want to configure automatic credentials storage for extractors that support authentication (by providing login and password with `--username` and `--password`) in order not to pass credentials as command line arguments on every yt-dlp execution and prevent tracking plain text passwords in the shell command history. You can achieve this using a [`.netrc` file](https://stackoverflow.com/tags/.netrc/info) on a per-extractor basis. For that, you will need to create a `.netrc` file in `--netrc-location` and restrict permissions to read/write by only you:
|
||||||
```
|
```
|
||||||
touch ${HOME}/.netrc
|
touch ${HOME}/.netrc
|
||||||
chmod a-rwx,u+rw ${HOME}/.netrc
|
chmod a-rwx,u+rw ${HOME}/.netrc
|
||||||
```
|
```
|
||||||
After that you can add credentials for an extractor in the following format, where *extractor* is the name of the extractor in lowercase:
|
After that, you can add credentials for an extractor in the following format, where *extractor* is the name of the extractor in lowercase:
|
||||||
```
|
```
|
||||||
machine <extractor> login <username> password <password>
|
machine <extractor> login <username> password <password>
|
||||||
```
|
```
|
||||||
@ -1201,7 +1201,7 @@ # OUTPUT TEMPLATE
|
|||||||
|
|
||||||
The field names themselves (the part inside the parenthesis) can also have some special formatting:
|
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; e.g. `%(tags.0)s`, `%(subtitles.en.-1.ext)s`. You can do Python slicing with colon `:`; E.g. `%(id.3:7:-1)s`, `%(formats.:.format_id)s`. Curly braces `{}` can be used to build dictionaries with only specific keys; e.g. `%(formats.:.{format_id,height})#j`. An empty field name `%()s` refers to the entire infodict; e.g. `%(.{id,title})s`. Note that all the fields that become available using this method are not listed below. Use `-j` to see such fields
|
1. **Object traversal**: The dictionaries and lists available in metadata can be traversed by using a dot `.` separator; e.g. `%(tags.0)s`, `%(subtitles.en.-1.ext)s`. You can do Python slicing with colon `:`; E.g. `%(id.3:7)s`, `%(id.6:2:-1)s`, `%(formats.:.format_id)s`. Curly braces `{}` can be used to build dictionaries with only specific keys; e.g. `%(formats.:.{format_id,height})#j`. An empty field name `%()s` refers to the entire infodict; e.g. `%(.{id,title})s`. Note that all the fields that become available using this method are not listed below. Use `-j` to see such fields
|
||||||
|
|
||||||
1. **Arithmetic**: Simple arithmetic can be done on numeric fields using `+`, `-` and `*`. E.g. `%(playlist_index+10)03d`, `%(n_entries+1-playlist_index)d`
|
1. **Arithmetic**: Simple arithmetic can be done on numeric fields using `+`, `-` and `*`. E.g. `%(playlist_index+10)03d`, `%(n_entries+1-playlist_index)d`
|
||||||
|
|
||||||
@ -1222,7 +1222,7 @@ # OUTPUT TEMPLATE
|
|||||||
%(name[.keys][addition][>strf][,alternate][&replacement][|default])[flags][width][.precision][length]type
|
%(name[.keys][addition][>strf][,alternate][&replacement][|default])[flags][width][.precision][length]type
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally, you can set different output templates for the various metadata files separately from the general output template by specifying the type of file followed by the template separated by a colon `:`. The different file types supported are `subtitle`, `thumbnail`, `description`, `annotation` (deprecated), `infojson`, `link`, `pl_thumbnail`, `pl_description`, `pl_infojson`, `chapter`, `pl_video`. E.g. `-o "%(title)s.%(ext)s" -o "thumbnail:%(title)s\%(title)s.%(ext)s"` will put the thumbnails in a folder with the same name as the video. If any of the templates is empty, that type of file will not be written. E.g. `--write-thumbnail -o "thumbnail:"` will write thumbnails only for playlists and not for video.
|
Additionally, you can set different output templates for the various metadata files separately from the general output template by specifying the type of file followed by the template separated by a colon `:`. The different file types supported are `subtitle`, `thumbnail`, `description`, `annotation` (deprecated), `infojson`, `link`, `pl_thumbnail`, `pl_description`, `pl_infojson`, `chapter`, `pl_video`. E.g. `-o "%(title)s.%(ext)s" -o "thumbnail:%(title)s\%(title)s.%(ext)s"` will put the thumbnails in a folder with the same name as the video. If any of the templates is empty, that type of file will not be written. E.g. `--write-thumbnail -o "thumbnail:"` will write thumbnails only for playlists and not for video.
|
||||||
|
|
||||||
<a id="outtmpl-postprocess-note"></a>
|
<a id="outtmpl-postprocess-note"></a>
|
||||||
|
|
||||||
@ -1282,7 +1282,7 @@ # OUTPUT TEMPLATE
|
|||||||
- `n_entries` (numeric): Total number of extracted items in the playlist
|
- `n_entries` (numeric): Total number of extracted items in the playlist
|
||||||
- `playlist_id` (string): Identifier of the playlist that contains the video
|
- `playlist_id` (string): Identifier of the playlist that contains the video
|
||||||
- `playlist_title` (string): Name of the playlist that contains the video
|
- `playlist_title` (string): Name of the playlist that contains the video
|
||||||
- `playlist` (string): `playlist_id` or `playlist_title`
|
- `playlist` (string): `playlist_title` if available or else `playlist_id`
|
||||||
- `playlist_count` (numeric): Total number of items in the playlist. May not be known if entire playlist is not extracted
|
- `playlist_count` (numeric): Total number of items in the playlist. May not be known if entire playlist is not extracted
|
||||||
- `playlist_index` (numeric): Index of the video in the playlist padded with leading zeros according the final index
|
- `playlist_index` (numeric): Index of the video in the playlist padded with leading zeros according the final index
|
||||||
- `playlist_autonumber` (numeric): Position of the video in the playlist download queue padded with leading zeros according to the total length of the playlist
|
- `playlist_autonumber` (numeric): Position of the video in the playlist download queue padded with leading zeros according to the total length of the playlist
|
||||||
@ -1290,7 +1290,7 @@ # OUTPUT TEMPLATE
|
|||||||
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
||||||
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
||||||
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
||||||
- `webpage_url` (string): A URL to the video webpage which if given to yt-dlp should allow to get the same result again
|
- `webpage_url` (string): A URL to the video webpage which, if given to yt-dlp, should yield the same result again
|
||||||
- `webpage_url_basename` (string): The basename of the webpage URL
|
- `webpage_url_basename` (string): The basename of the webpage URL
|
||||||
- `webpage_url_domain` (string): The domain of the webpage URL
|
- `webpage_url_domain` (string): The domain of the webpage URL
|
||||||
- `original_url` (string): The URL given by the user (or same as `webpage_url` for playlist entries)
|
- `original_url` (string): The URL given by the user (or same as `webpage_url` for playlist entries)
|
||||||
@ -1306,10 +1306,10 @@ # OUTPUT TEMPLATE
|
|||||||
- `chapter_number` (numeric): Number of the chapter the video belongs to
|
- `chapter_number` (numeric): Number of the chapter the video belongs to
|
||||||
- `chapter_id` (string): Id of the chapter the video belongs to
|
- `chapter_id` (string): Id of the chapter the video belongs to
|
||||||
|
|
||||||
Available for the video that is an episode of some series or programme:
|
Available for the video that is an episode of some series or program:
|
||||||
|
|
||||||
- `series` (string): Title of the series or programme the video episode belongs to
|
- `series` (string): Title of the series or program the video episode belongs to
|
||||||
- `series_id` (string): Id of the series or programme the video episode belongs to
|
- `series_id` (string): Id of the series or program the video episode belongs to
|
||||||
- `season` (string): Title of the season the video episode belongs to
|
- `season` (string): Title of the season the video episode belongs to
|
||||||
- `season_number` (numeric): Number of the season the video episode belongs to
|
- `season_number` (numeric): Number of the season the video episode belongs to
|
||||||
- `season_id` (string): Id of the season the video episode belongs to
|
- `season_id` (string): Id of the season the video episode belongs to
|
||||||
@ -1349,9 +1349,9 @@ # OUTPUT TEMPLATE
|
|||||||
- `thumbnails_table` (table): The thumbnail format table as printed by `--list-thumbnails`
|
- `thumbnails_table` (table): The thumbnail format table as printed by `--list-thumbnails`
|
||||||
- `subtitles_table` (table): The subtitle format table as printed by `--list-subs`
|
- `subtitles_table` (table): The subtitle format table as printed by `--list-subs`
|
||||||
- `automatic_captions_table` (table): The automatic subtitle format table as printed by `--list-subs`
|
- `automatic_captions_table` (table): The automatic subtitle format table as printed by `--list-subs`
|
||||||
|
|
||||||
Available only after the video is downloaded (`post_process`/`after_move`):
|
Available only after the video is downloaded (`post_process`/`after_move`):
|
||||||
|
|
||||||
- `filepath`: Actual path of downloaded video file
|
- `filepath`: Actual path of downloaded video file
|
||||||
|
|
||||||
Available only in `--sponsorblock-chapter-title`:
|
Available only in `--sponsorblock-chapter-title`:
|
||||||
@ -1366,7 +1366,7 @@ # OUTPUT TEMPLATE
|
|||||||
|
|
||||||
Each aforementioned sequence when referenced in an output template will be replaced by the actual value corresponding to the sequence name. E.g. for `-o %(title)s-%(id)s.%(ext)s` and an mp4 video with title `yt-dlp test video` and id `BaW_jenozKc`, this will result in a `yt-dlp test video-BaW_jenozKc.mp4` file created in the current directory.
|
Each aforementioned sequence when referenced in an output template will be replaced by the actual value corresponding to the sequence name. E.g. for `-o %(title)s-%(id)s.%(ext)s` and an mp4 video with title `yt-dlp test video` and id `BaW_jenozKc`, this will result in a `yt-dlp test video-BaW_jenozKc.mp4` file created in the current directory.
|
||||||
|
|
||||||
**Note**: Some of the sequences are not guaranteed to be present since they depend on the metadata obtained by a particular extractor. Such sequences will be replaced with placeholder value provided with `--output-na-placeholder` (`NA` by default).
|
**Note**: Some of the sequences are not guaranteed to be present, since they depend on the metadata obtained by a particular extractor. Such sequences will be replaced with placeholder value provided with `--output-na-placeholder` (`NA` by default).
|
||||||
|
|
||||||
**Tip**: Look at the `-j` output to identify which fields are available for the particular URL
|
**Tip**: Look at the `-j` output to identify which fields are available for the particular URL
|
||||||
|
|
||||||
@ -1444,7 +1444,7 @@ # FORMAT SELECTION
|
|||||||
|
|
||||||
- `all`: Select **all formats** separately
|
- `all`: Select **all formats** separately
|
||||||
- `mergeall`: Select and **merge all formats** (Must be used with `--audio-multistreams`, `--video-multistreams` or both)
|
- `mergeall`: Select and **merge all formats** (Must be used with `--audio-multistreams`, `--video-multistreams` or both)
|
||||||
- `b*`, `best*`: Select the best quality format that **contains either** a video or an audio or both (ie; `vcodec!=none or acodec!=none`)
|
- `b*`, `best*`: Select the best quality format that **contains either** a video or an audio or both (i.e.; `vcodec!=none or acodec!=none`)
|
||||||
- `b`, `best`: Select the best quality format that **contains both** video and audio. Equivalent to `best*[vcodec!=none][acodec!=none]`
|
- `b`, `best`: Select the best quality format that **contains both** video and audio. Equivalent to `best*[vcodec!=none][acodec!=none]`
|
||||||
- `bv`, `bestvideo`: Select the best quality **video-only** format. Equivalent to `best*[acodec=none]`
|
- `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]`
|
- `bv*`, `bestvideo*`: Select the best quality format that **contains video**. It may also contain audio. Equivalent to `best*[vcodec!=none]`
|
||||||
@ -1457,7 +1457,7 @@ # FORMAT SELECTION
|
|||||||
- `wa`, `worstaudio`: Select the worst quality audio-only format. Equivalent to `worst*[vcodec=none]`
|
- `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]`
|
- `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 `-S +size` or more rigorously, `-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.
|
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.
|
||||||
|
|
||||||
@ -1507,7 +1507,7 @@ ## Filtering Formats
|
|||||||
|
|
||||||
Any string comparison may be prefixed with negation `!` in order to produce an opposite comparison, e.g. `!*=` (does not contain). The comparand of a string comparison needs to be quoted with either double or single quotes if it contains spaces or special characters other than `._-`.
|
Any string comparison may be prefixed with negation `!` in order to produce an opposite comparison, e.g. `!*=` (does not contain). The comparand of a string comparison needs to be quoted with either double or single quotes if it contains spaces or special characters other than `._-`.
|
||||||
|
|
||||||
**Note**: None of the aforementioned meta fields are guaranteed to be present since this solely depends on the metadata obtained by particular extractor, i.e. the metadata offered by the website. Any other field made available by the extractor can also be used for filtering.
|
**Note**: None of the aforementioned meta fields are guaranteed to be present since this solely depends on the metadata obtained by the particular extractor, i.e. the metadata offered by the website. Any other field made available by the extractor can also be used for filtering.
|
||||||
|
|
||||||
Formats for which the value is not known are excluded unless you put a question mark (`?`) after the operator. You can combine format filters, so `-f "bv[height<=?720][tbr>500]"` selects up to 720p videos (or videos where the height is not known) with a bitrate of at least 500 kbps. You can also use the filters with `all` to download all formats that satisfy the filter, e.g. `-f "all[vcodec=none]"` selects all audio-only formats.
|
Formats for which the value is not known are excluded unless you put a question mark (`?`) after the operator. You can combine format filters, so `-f "bv[height<=?720][tbr>500]"` selects up to 720p videos (or videos where the height is not known) with a bitrate of at least 500 kbps. You can also use the filters with `all` to download all formats that satisfy the filter, e.g. `-f "all[vcodec=none]"` selects all audio-only formats.
|
||||||
|
|
||||||
@ -1546,14 +1546,14 @@ ## Sorting Formats
|
|||||||
- `abr`: Average audio bitrate in [kbps](## "1000 bits/sec")
|
- `abr`: Average audio bitrate in [kbps](## "1000 bits/sec")
|
||||||
- `br`: Average bitrate in [kbps](## "1000 bits/sec"), `tbr`/`vbr`/`abr`
|
- `br`: Average bitrate in [kbps](## "1000 bits/sec"), `tbr`/`vbr`/`abr`
|
||||||
- `asr`: Audio sample rate in Hz
|
- `asr`: Audio sample rate in Hz
|
||||||
|
|
||||||
**Deprecation warning**: Many of these fields have (currently undocumented) aliases, that may be removed in a future version. It is recommended to use only the documented field names.
|
**Deprecation warning**: Many of these fields have (currently undocumented) aliases, that may be removed in a future version. It is recommended to use only the documented field names.
|
||||||
|
|
||||||
All fields, unless specified otherwise, are sorted in descending order. To reverse this, prefix the field with a `+`. E.g. `+res` prefers format with the smallest resolution. Additionally, you can suffix a preferred value for the fields, separated by a `:`. E.g. `res:720` prefers larger videos, but no larger than 720p and the smallest video if there are no videos less than 720p. For `codec` and `ext`, you can provide two preferred values, the first for video and the second for audio. E.g. `+codec:avc:m4a` (equivalent to `+vcodec:avc,+acodec:m4a`) sets the video codec preference to `h264` > `h265` > `vp9` > `vp9.2` > `av01` > `vp8` > `h263` > `theora` and audio codec preference to `mp4a` > `aac` > `vorbis` > `opus` > `mp3` > `ac3` > `dts`. You can also make the sorting prefer the nearest values to the provided by using `~` as the delimiter. E.g. `filesize~1G` prefers the format with filesize closest to 1 GiB.
|
All fields, unless specified otherwise, are sorted in descending order. To reverse this, prefix the field with a `+`. E.g. `+res` prefers format with the smallest resolution. Additionally, you can suffix a preferred value for the fields, separated by a `:`. E.g. `res:720` prefers larger videos, but no larger than 720p and the smallest video if there are no videos less than 720p. For `codec` and `ext`, you can provide two preferred values, the first for video and the second for audio. E.g. `+codec:avc:m4a` (equivalent to `+vcodec:avc,+acodec:m4a`) sets the video codec preference to `h264` > `h265` > `vp9` > `vp9.2` > `av01` > `vp8` > `h263` > `theora` and audio codec preference to `mp4a` > `aac` > `vorbis` > `opus` > `mp3` > `ac3` > `dts`. You can also make the sorting prefer the nearest values to the provided by using `~` as the delimiter. E.g. `filesize~1G` prefers the format with filesize closest to 1 GiB.
|
||||||
|
|
||||||
The fields `hasvid` and `ie_pref` are always given highest priority in sorting, irrespective of the user-defined order. This behaviour can be changed by using `--format-sort-force`. Apart from these, the default order used is: `lang,quality,res,fps,hdr:12,vcodec:vp9.2,channels,acodec,size,br,asr,proto,ext,hasaud,source,id`. The extractors may override this default order, but they cannot override the user-provided order.
|
The fields `hasvid` and `ie_pref` are always given highest priority in sorting, irrespective of the user-defined order. This behavior can be changed by using `--format-sort-force`. Apart from these, the default order used is: `lang,quality,res,fps,hdr:12,vcodec:vp9.2,channels,acodec,size,br,asr,proto,ext,hasaud,source,id`. The extractors may override this default order, but they cannot override the user-provided order.
|
||||||
|
|
||||||
Note that the default has `vcodec:vp9.2`; i.e. `av1` is not preferred. Similarly, the default for hdr is `hdr:12`; i.e. dolby vision is not preferred. These choices are made since DV and AV1 formats are not yet fully compatible with most devices. This may be changed in the future as more devices become capable of smoothly playing back these formats.
|
Note that the default has `vcodec:vp9.2`; i.e. `av1` is not preferred. Similarly, the default for hdr is `hdr:12`; i.e. Dolby Vision is not preferred. These choices are made since DV and AV1 formats are not yet fully compatible with most devices. This may be changed in the future as more devices become capable of smoothly playing back these formats.
|
||||||
|
|
||||||
If your format selector is `worst`, the last item is selected after sorting. This means it will select the format that is worst in all respects. 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`.
|
If your format selector is `worst`, the last item is selected after sorting. This means it will select the format that is worst in all respects. 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`.
|
||||||
|
|
||||||
@ -1765,7 +1765,7 @@ # EXTRACTOR ARGUMENTS
|
|||||||
#### youtube
|
#### youtube
|
||||||
* `lang`: Prefer translated metadata (`title`, `description` etc) of this language code (case-sensitive). By default, the video primary language metadata is preferred, with a fallback to `en` translated. See [youtube.py](https://github.com/yt-dlp/yt-dlp/blob/c26f9b991a0681fd3ea548d535919cec1fbbd430/yt_dlp/extractor/youtube.py#L381-L390) for list of supported content language codes
|
* `lang`: Prefer translated metadata (`title`, `description` etc) of this language code (case-sensitive). By default, the video primary language metadata is preferred, with a fallback to `en` translated. See [youtube.py](https://github.com/yt-dlp/yt-dlp/blob/c26f9b991a0681fd3ea548d535919cec1fbbd430/yt_dlp/extractor/youtube.py#L381-L390) for list of supported content language codes
|
||||||
* `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and [auto-translated subtitles](https://github.com/yt-dlp/yt-dlp/issues/4090#issuecomment-1158102032) respectively
|
* `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and [auto-translated subtitles](https://github.com/yt-dlp/yt-dlp/issues/4090#issuecomment-1158102032) respectively
|
||||||
* `player_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music`, `_embedded`, `_embedscreen`, `_creator` (e.g. `web_embedded`); and `mweb`, `mweb_embedscreen` and `tv_embedded` (agegate bypass) with no variants. By default, `ios,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. The `android` clients will always be given lowest priority since their formats are broken. You can use `all` to use all the clients, and `default` for the default clients.
|
* `player_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music`, `_embedded`, `_embedscreen`, `_creator` (e.g. `web_embedded`); and `mediaconnect`, `mweb`, `mweb_embedscreen` and `tv_embedded` (agegate bypass) with no variants. By default, `ios,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. The `android` clients will always be given lowest priority since their formats are broken. 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
|
* `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
|
||||||
* `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp.
|
* `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp.
|
||||||
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
|
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
|
||||||
@ -1851,7 +1851,7 @@ #### afreecatvlive
|
|||||||
* `cdn`: One or more CDN IDs to use with the API call for stream URLs, e.g. `gcp_cdn`, `gs_cdn_pc_app`, `gs_cdn_mobile_web`, `gs_cdn_pc_web`
|
* `cdn`: One or more CDN IDs to use with the API call for stream URLs, e.g. `gcp_cdn`, `gs_cdn_pc_app`, `gs_cdn_mobile_web`, `gs_cdn_pc_web`
|
||||||
|
|
||||||
#### soundcloud
|
#### soundcloud
|
||||||
* `formats`: Formats to request from the API. Requested values should be in the format of `{protocol}_{extension}` (omitting the bitrate), e.g. `hls_opus,http_aac`. The `*` character functions as a wildcard, e.g. `*_mp3`, and can passed by itself to request all formats. Known protocols include `http`, `hls` and `hls-aes`; known extensions include `aac`, `opus` and `mp3`. Original `download` formats are always extracted. Default is `http_aac,hls_aac,http_opus,hls_opus,http_mp3,hls_mp3`
|
* `formats`: Formats to request from the API. Requested values should be in the format of `{protocol}_{extension}` (omitting the bitrate), e.g. `hls_opus,http_aac`. The `*` character functions as a wildcard, e.g. `*_mp3`, and can be passed by itself to request all formats. Known protocols include `http`, `hls` and `hls-aes`; known extensions include `aac`, `opus` and `mp3`. Original `download` formats are always extracted. Default is `http_aac,hls_aac,http_opus,hls_opus,http_mp3,hls_mp3`
|
||||||
|
|
||||||
#### orfon (orf:on)
|
#### orfon (orf:on)
|
||||||
* `prefer_segments_playlist`: Prefer a playlist of program segments instead of a single complete video when available. If individual segments are desired, use `--concat-playlist never --extractor-args "orfon:prefer_segments_playlist"`
|
* `prefer_segments_playlist`: Prefer a playlist of program segments instead of a single complete video when available. If individual segments are desired, use `--concat-playlist never --extractor-args "orfon:prefer_segments_playlist"`
|
||||||
@ -1868,16 +1868,16 @@ # PLUGINS
|
|||||||
|
|
||||||
Note that **all** plugins are imported even if not invoked, and that **there are no checks** performed on plugin code. **Use plugins at your own risk and only if you trust the code!**
|
Note that **all** plugins are imported even if not invoked, and that **there are no checks** performed on plugin code. **Use plugins at your own risk and only if you trust the code!**
|
||||||
|
|
||||||
Plugins can be of `<type>`s `extractor` or `postprocessor`.
|
Plugins can be of `<type>`s `extractor` or `postprocessor`.
|
||||||
- Extractor plugins do not need to be enabled from the CLI and are automatically invoked when the input URL is suitable for it.
|
- Extractor plugins do not need to be enabled from the CLI and are automatically invoked when the input URL is suitable for it.
|
||||||
- Extractor plugins take priority over builtin extractors.
|
- Extractor plugins take priority over built-in extractors.
|
||||||
- Postprocessor plugins can be invoked using `--use-postprocessor NAME`.
|
- Postprocessor plugins can be invoked using `--use-postprocessor NAME`.
|
||||||
|
|
||||||
|
|
||||||
Plugins are loaded from the namespace packages `yt_dlp_plugins.extractor` and `yt_dlp_plugins.postprocessor`.
|
Plugins are loaded from the namespace packages `yt_dlp_plugins.extractor` and `yt_dlp_plugins.postprocessor`.
|
||||||
|
|
||||||
In other words, the file structure on the disk looks something like:
|
In other words, the file structure on the disk looks something like:
|
||||||
|
|
||||||
yt_dlp_plugins/
|
yt_dlp_plugins/
|
||||||
extractor/
|
extractor/
|
||||||
myplugin.py
|
myplugin.py
|
||||||
@ -1925,7 +1925,7 @@ ## Developing Plugins
|
|||||||
|
|
||||||
See the [yt-dlp-sample-plugins](https://github.com/yt-dlp/yt-dlp-sample-plugins) repo for a template plugin package and the [Plugin Development](https://github.com/yt-dlp/yt-dlp/wiki/Plugin-Development) section of the wiki for a plugin development guide.
|
See the [yt-dlp-sample-plugins](https://github.com/yt-dlp/yt-dlp-sample-plugins) repo for a template plugin package and the [Plugin Development](https://github.com/yt-dlp/yt-dlp/wiki/Plugin-Development) section of the wiki for a plugin development guide.
|
||||||
|
|
||||||
All public classes with a name ending in `IE`/`PP` are imported from each file for extractors and postprocessors repectively. This respects underscore prefix (e.g. `_MyBasePluginIE` is private) and `__all__`. Modules can similarly be excluded by prefixing the module name with an underscore (e.g. `_myplugin.py`).
|
All public classes with a name ending in `IE`/`PP` are imported from each file for extractors and postprocessors respectively. This respects underscore prefix (e.g. `_MyBasePluginIE` is private) and `__all__`. Modules can similarly be excluded by prefixing the module name with an underscore (e.g. `_myplugin.py`).
|
||||||
|
|
||||||
To replace an existing extractor with a subclass of one, set the `plugin_name` class keyword argument (e.g. `class MyPluginIE(ABuiltInIE, plugin_name='myplugin')` will replace `ABuiltInIE` with `MyPluginIE`). Since the extractor replaces the parent, you should exclude the subclass extractor from being imported separately by making it private using one of the methods described above.
|
To replace an existing extractor with a subclass of one, set the `plugin_name` class keyword argument (e.g. `class MyPluginIE(ABuiltInIE, plugin_name='myplugin')` will replace `ABuiltInIE` with `MyPluginIE`). Since the extractor replaces the parent, you should exclude the subclass extractor from being imported separately by making it private using one of the methods described above.
|
||||||
|
|
||||||
@ -1937,7 +1937,7 @@ # EMBEDDING YT-DLP
|
|||||||
|
|
||||||
yt-dlp makes the best effort to be a good command-line program, and thus should be callable from any programming language.
|
yt-dlp makes the best effort to be a good command-line program, and thus should be callable from any programming language.
|
||||||
|
|
||||||
Your program should avoid parsing the normal stdout since they may change in future versions. Instead they should use options such as `-J`, `--print`, `--progress-template`, `--exec` etc to create console output that you can reliably reproduce and parse.
|
Your program should avoid parsing the normal stdout since they may change in future versions. Instead, they should use options such as `-J`, `--print`, `--progress-template`, `--exec` etc to create console output that you can reliably reproduce and parse.
|
||||||
|
|
||||||
From a Python program, you can embed yt-dlp in a more powerful fashion, like this:
|
From a Python program, you can embed yt-dlp in a more powerful fashion, like this:
|
||||||
|
|
||||||
|
@ -169,5 +169,11 @@
|
|||||||
"when": "5c019f6328ad40d66561eac3c4de0b3cd070d0f6",
|
"when": "5c019f6328ad40d66561eac3c4de0b3cd070d0f6",
|
||||||
"short": "[cleanup] Misc (#9765)",
|
"short": "[cleanup] Misc (#9765)",
|
||||||
"authors": ["bashonly", "Grub4K", "seproDev"]
|
"authors": ["bashonly", "Grub4K", "seproDev"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "e6a22834df1776ec4e486526f6df2bf53cb7e06f",
|
||||||
|
"short": "[ie/orf:on] Add `prefer_segments_playlist` extractor-arg (#10314)",
|
||||||
|
"authors": ["seproDev"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
2
devscripts/cli_to_api.py
Normal file → Executable file
2
devscripts/cli_to_api.py
Normal file → Executable file
@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Allow direct execution
|
# Allow direct execution
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -299,7 +299,7 @@ banned-from = [
|
|||||||
"string",
|
"string",
|
||||||
"sys",
|
"sys",
|
||||||
"time",
|
"time",
|
||||||
"urllib",
|
"urllib.parse",
|
||||||
"uuid",
|
"uuid",
|
||||||
"xml",
|
"xml",
|
||||||
]
|
]
|
||||||
|
@ -92,6 +92,7 @@ def test_operators(self):
|
|||||||
self._test('function f(){return 0 && 1 || 2;}', 2)
|
self._test('function f(){return 0 && 1 || 2;}', 2)
|
||||||
self._test('function f(){return 0 ?? 42;}', 0)
|
self._test('function f(){return 0 ?? 42;}', 0)
|
||||||
self._test('function f(){return "life, the universe and everything" < 42;}', False)
|
self._test('function f(){return "life, the universe and everything" < 42;}', False)
|
||||||
|
self._test('function f(){return 0 - 7 * - 6;}', 42)
|
||||||
|
|
||||||
def test_array_access(self):
|
def test_array_access(self):
|
||||||
self._test('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2.0] = 7; return x;}', [5, 2, 7])
|
self._test('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2.0] = 7; return x;}', [5, 2, 7])
|
||||||
|
@ -163,6 +163,10 @@
|
|||||||
'https://www.youtube.com/s/player/b7910ca8/player_ias.vflset/en_US/base.js',
|
'https://www.youtube.com/s/player/b7910ca8/player_ias.vflset/en_US/base.js',
|
||||||
'_hXMCwMt9qE310D', 'LoZMgkkofRMCZQ',
|
'_hXMCwMt9qE310D', 'LoZMgkkofRMCZQ',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/590f65a6/player_ias.vflset/en_US/base.js',
|
||||||
|
'1tm7-g_A9zsI8_Lay_', 'xI4Vem4Put_rOg',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
import errno
|
import errno
|
||||||
import fileinput
|
import fileinput
|
||||||
|
import functools
|
||||||
import http.cookiejar
|
import http.cookiejar
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
@ -24,7 +25,7 @@
|
|||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from .cache import Cache
|
from .cache import Cache
|
||||||
from .compat import functools, urllib # isort: split
|
from .compat import urllib # isort: split
|
||||||
from .compat import compat_os_name, urllib_req_to_req
|
from .compat import compat_os_name, urllib_req_to_req
|
||||||
from .cookies import LenientSimpleCookie, load_cookies
|
from .cookies import LenientSimpleCookie, load_cookies
|
||||||
from .downloader import FFmpegFD, get_suitable_downloader, shorten_protocol_name
|
from .downloader import FFmpegFD, get_suitable_downloader, shorten_protocol_name
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import functools
|
||||||
import glob
|
import glob
|
||||||
|
import hashlib
|
||||||
import http.cookiejar
|
import http.cookiejar
|
||||||
import http.cookies
|
import http.cookies
|
||||||
import io
|
import io
|
||||||
@ -17,14 +19,12 @@
|
|||||||
import time
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from hashlib import pbkdf2_hmac
|
|
||||||
|
|
||||||
from .aes import (
|
from .aes import (
|
||||||
aes_cbc_decrypt_bytes,
|
aes_cbc_decrypt_bytes,
|
||||||
aes_gcm_decrypt_and_verify_bytes,
|
aes_gcm_decrypt_and_verify_bytes,
|
||||||
unpad_pkcs7,
|
unpad_pkcs7,
|
||||||
)
|
)
|
||||||
from .compat import functools # isort: split
|
|
||||||
from .compat import compat_os_name
|
from .compat import compat_os_name
|
||||||
from .dependencies import (
|
from .dependencies import (
|
||||||
_SECRETSTORAGE_UNAVAILABLE_REASON,
|
_SECRETSTORAGE_UNAVAILABLE_REASON,
|
||||||
@ -999,7 +999,7 @@ def _get_windows_v10_key(browser_root, logger):
|
|||||||
|
|
||||||
|
|
||||||
def pbkdf2_sha1(password, salt, iterations, key_length):
|
def pbkdf2_sha1(password, salt, iterations, key_length):
|
||||||
return pbkdf2_hmac('sha1', password, salt, iterations, key_length)
|
return hashlib.pbkdf2_hmac('sha1', password, salt, iterations, key_length)
|
||||||
|
|
||||||
|
|
||||||
def _decrypt_aes_cbc_multi(ciphertext, keys, logger, initialization_vector=b' ' * 16):
|
def _decrypt_aes_cbc_multi(ciphertext, keys, logger, initialization_vector=b' ' * 16):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import enum
|
import enum
|
||||||
|
import functools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -9,7 +10,6 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from .fragment import FragmentFD
|
from .fragment import FragmentFD
|
||||||
from ..compat import functools
|
|
||||||
from ..networking import Request
|
from ..networking import Request
|
||||||
from ..postprocessor.ffmpeg import EXT_TO_OUT_FORMATS, FFmpegPostProcessor
|
from ..postprocessor.ffmpeg import EXT_TO_OUT_FORMATS, FFmpegPostProcessor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
extract_attributes,
|
extract_attributes,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
try_get,
|
try_get,
|
||||||
)
|
)
|
||||||
@ -136,7 +137,7 @@ def _real_extract(self, url):
|
|||||||
else:
|
else:
|
||||||
vbr = int_or_none(s.get('bitrate'))
|
vbr = int_or_none(s.get('bitrate'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': f'{stream_type}-{vbr}' if vbr else stream_type,
|
'format_id': join_nonempty(stream_type, vbr),
|
||||||
'vbr': vbr,
|
'vbr': vbr,
|
||||||
'width': int_or_none(s.get('width')),
|
'width': int_or_none(s.get('width')),
|
||||||
'height': int_or_none(s.get('height')),
|
'height': int_or_none(s.get('height')),
|
||||||
|
@ -131,8 +131,8 @@ def _real_extract(self, url):
|
|||||||
formats.extend(self._extract_f4m_formats(
|
formats.extend(self._extract_f4m_formats(
|
||||||
href, video_id, f4m_id='hds', fatal=False))
|
href, video_id, f4m_id='hds', fatal=False))
|
||||||
elif mime_type == 'application/dash+xml':
|
elif mime_type == 'application/dash+xml':
|
||||||
formats.extend(self._extract_f4m_formats(
|
formats.extend(self._extract_mpd_formats(
|
||||||
href, video_id, f4m_id='hds', fatal=False))
|
href, video_id, mpd_id='dash', fatal=False))
|
||||||
elif mime_type == 'application/vnd.ms-sstr+xml':
|
elif mime_type == 'application/vnd.ms-sstr+xml':
|
||||||
formats.extend(self._extract_ism_formats(
|
formats.extend(self._extract_ism_formats(
|
||||||
href, video_id, ism_id='mss', fatal=False))
|
href, video_id, ism_id='mss', fatal=False))
|
||||||
|
@ -41,7 +41,7 @@ class BandcampIE(InfoExtractor):
|
|||||||
'uploader_id': 'youtube-dl',
|
'uploader_id': 'youtube-dl',
|
||||||
'thumbnail': 'https://f4.bcbits.com/img/a3216802731_5.jpg',
|
'thumbnail': 'https://f4.bcbits.com/img/a3216802731_5.jpg',
|
||||||
},
|
},
|
||||||
'_skip': 'There is a limit of 200 free downloads / month for the test song',
|
'skip': 'There is a limit of 200 free downloads / month for the test song',
|
||||||
}, {
|
}, {
|
||||||
# free download
|
# free download
|
||||||
'url': 'http://benprunty.bandcamp.com/track/lanius-battle',
|
'url': 'http://benprunty.bandcamp.com/track/lanius-battle',
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
fix_xml_ampersands,
|
fix_xml_ampersands,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
@ -538,12 +539,7 @@ def _parse_brightcove_metadata(self, json_data, video_id, headers={}):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def build_format_id(kind):
|
def build_format_id(kind):
|
||||||
format_id = kind
|
return join_nonempty(kind, tbr and f'{int(tbr)}k', height and f'{height}p')
|
||||||
if tbr:
|
|
||||||
format_id += f'-{int(tbr)}k'
|
|
||||||
if height:
|
|
||||||
format_id += f'-{height}p'
|
|
||||||
return format_id
|
|
||||||
|
|
||||||
if src or streaming_src:
|
if src or streaming_src:
|
||||||
f.update({
|
f.update({
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
import urllib.error
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_protocol,
|
determine_protocol,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
try_get,
|
try_get,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
)
|
)
|
||||||
@ -52,7 +53,7 @@ def _real_extract(self, url):
|
|||||||
is_hls = container == 'M2TS'
|
is_hls = container == 'M2TS'
|
||||||
protocol = 'm3u8_native' if is_hls else determine_protocol({'url': rendition_url})
|
protocol = 'm3u8_native' if is_hls else determine_protocol({'url': rendition_url})
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': ('hls' if is_hls else protocol) + (f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty('hls' if is_hls else protocol, tbr),
|
||||||
'url': rendition_url,
|
'url': rendition_url,
|
||||||
'width': int_or_none(rendition.get('frameWidth')),
|
'width': int_or_none(rendition.get('frameWidth')),
|
||||||
'height': int_or_none(rendition.get('frameHeight')),
|
'height': int_or_none(rendition.get('frameHeight')),
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..networking import Request
|
from ..networking import Request
|
||||||
from ..utils import float_or_none, int_or_none, parse_iso8601
|
from ..utils import (
|
||||||
|
float_or_none,
|
||||||
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
|
parse_iso8601,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EitbIE(InfoExtractor):
|
class EitbIE(InfoExtractor):
|
||||||
@ -37,12 +42,9 @@ def _real_extract(self, url):
|
|||||||
if not video_url:
|
if not video_url:
|
||||||
continue
|
continue
|
||||||
tbr = float_or_none(rendition.get('ENCODING_RATE'), 1000)
|
tbr = float_or_none(rendition.get('ENCODING_RATE'), 1000)
|
||||||
format_id = 'http'
|
|
||||||
if tbr:
|
|
||||||
format_id += f'-{int(tbr)}'
|
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': rendition['PMD_URL'],
|
'url': rendition['PMD_URL'],
|
||||||
'format_id': format_id,
|
'format_id': join_nonempty('http', int_or_none(tbr)),
|
||||||
'width': int_or_none(rendition.get('FRAME_WIDTH')),
|
'width': int_or_none(rendition.get('FRAME_WIDTH')),
|
||||||
'height': int_or_none(rendition.get('FRAME_HEIGHT')),
|
'height': int_or_none(rendition.get('FRAME_HEIGHT')),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
|
@ -29,9 +29,6 @@ class EpornerIE(InfoExtractor):
|
|||||||
'view_count': int,
|
'view_count': int,
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'proxy': '127.0.0.1:8118',
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
# New (May 2016) URL layout
|
# New (May 2016) URL layout
|
||||||
'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0/Star-Wars-XXX-Parody/',
|
'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0/Star-Wars-XXX-Parody/',
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
remove_end,
|
remove_end,
|
||||||
remove_start,
|
remove_start,
|
||||||
@ -287,7 +288,7 @@ def _real_extract(self, url):
|
|||||||
if mobj:
|
if mobj:
|
||||||
height = int(mobj.group(2))
|
height = int(mobj.group(2))
|
||||||
f.update({
|
f.update({
|
||||||
'format_id': (f'{format_id}-' if format_id else '') + f'{height}P',
|
'format_id': join_nonempty(format_id, f'{height}P'),
|
||||||
'width': int(mobj.group(1)),
|
'width': int(mobj.group(1)),
|
||||||
'height': height,
|
'height': height,
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
urljoin,
|
urljoin,
|
||||||
xpath_element,
|
xpath_element,
|
||||||
@ -69,7 +70,7 @@ def _extract_info(self, url, display_id):
|
|||||||
height = format_info.get('height')
|
height = format_info.get('height')
|
||||||
fmt = {
|
fmt = {
|
||||||
'url': path,
|
'url': path,
|
||||||
'format_id': 'http{}'.format(f'-{height}p' if height else ''),
|
'format_id': join_nonempty('http'. height and f'{height}p'),
|
||||||
'width': format_info.get('width'),
|
'width': format_info.get('width'),
|
||||||
'height': height,
|
'height': height,
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,6 @@ class HKETVIE(InfoExtractor):
|
|||||||
'duration': 907,
|
'duration': 907,
|
||||||
'subtitles': {},
|
'subtitles': {},
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'geo_verification_proxy': '<HK proxy here>',
|
|
||||||
},
|
|
||||||
'skip': 'Geo restricted to HK',
|
'skip': 'Geo restricted to HK',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ def _real_extract(self, url):
|
|||||||
else:
|
else:
|
||||||
self.report_warning('Main webpage is locked behind the login page. Retrying with embed webpage (some metadata might be missing).')
|
self.report_warning('Main webpage is locked behind the login page. Retrying with embed webpage (some metadata might be missing).')
|
||||||
webpage = self._download_webpage(
|
webpage = self._download_webpage(
|
||||||
f'{url}/embed/', video_id, note='Downloading embed webpage', fatal=False)
|
f'{url}/embed/', video_id, note='Downloading embed webpage', fatal=False) or ''
|
||||||
additional_data = self._search_json(
|
additional_data = self._search_json(
|
||||||
r'window\.__additionalDataLoaded\s*\(\s*[^,]+,', webpage, 'additional data', video_id, fatal=False)
|
r'window\.__additionalDataLoaded\s*\(\s*[^,]+,', webpage, 'additional data', video_id, fatal=False)
|
||||||
if not additional_data and not media:
|
if not additional_data and not media:
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import urllib.error
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
determine_ext,
|
determine_ext,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
@ -498,10 +499,8 @@ def _real_extract(self, url):
|
|||||||
m3u8_id=format_id, fatal=False))
|
m3u8_id=format_id, fatal=False))
|
||||||
continue
|
continue
|
||||||
tbr = int_or_none(va.get('bitrate'), 1000)
|
tbr = int_or_none(va.get('bitrate'), 1000)
|
||||||
if tbr:
|
|
||||||
format_id += f'-{tbr}'
|
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': format_id,
|
'format_id': join_nonempty(format_id, tbr),
|
||||||
'url': public_url,
|
'url': public_url,
|
||||||
'width': int_or_none(va.get('width')),
|
'width': int_or_none(va.get('width')),
|
||||||
'height': int_or_none(va.get('height')),
|
'height': int_or_none(va.get('height')),
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
)
|
)
|
||||||
@ -41,7 +42,7 @@ def _real_extract(self, url):
|
|||||||
else:
|
else:
|
||||||
height = int_or_none(playback.get('height'))
|
height = int_or_none(playback.get('height'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': playback.get('name', 'http' + (f'-{height}p' if height else '')),
|
'format_id': playback.get('name') or join_nonempty('http', height and f'{height}p'),
|
||||||
'url': playback_url,
|
'url': playback_url,
|
||||||
'width': int_or_none(playback.get('width')),
|
'width': int_or_none(playback.get('width')),
|
||||||
'height': height,
|
'height': height,
|
||||||
|
@ -41,7 +41,7 @@ class PelotonIE(InfoExtractor):
|
|||||||
}, 'params': {
|
}, 'params': {
|
||||||
'skip_download': 'm3u8',
|
'skip_download': 'm3u8',
|
||||||
},
|
},
|
||||||
'_skip': 'Account needed',
|
'skip': 'Account needed',
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://members.onepeloton.com/classes/player/26603d53d6bb4de1b340514864a6a6a8',
|
'url': 'https://members.onepeloton.com/classes/player/26603d53d6bb4de1b340514864a6a6a8',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -61,7 +61,7 @@ class PelotonIE(InfoExtractor):
|
|||||||
}, 'params': {
|
}, 'params': {
|
||||||
'skip_download': 'm3u8',
|
'skip_download': 'm3u8',
|
||||||
},
|
},
|
||||||
'_skip': 'Account needed',
|
'skip': 'Account needed',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_MANIFEST_URL_TEMPLATE = '%s?hdnea=%s'
|
_MANIFEST_URL_TEMPLATE = '%s?hdnea=%s'
|
||||||
@ -199,7 +199,7 @@ class PelotonLiveIE(InfoExtractor):
|
|||||||
'params': {
|
'params': {
|
||||||
'skip_download': 'm3u8',
|
'skip_download': 'm3u8',
|
||||||
},
|
},
|
||||||
'_skip': 'Account needed',
|
'skip': 'Account needed',
|
||||||
}
|
}
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..utils import int_or_none, join_nonempty
|
||||||
|
|
||||||
|
|
||||||
class PerformGroupIE(InfoExtractor):
|
class PerformGroupIE(InfoExtractor):
|
||||||
@ -50,11 +50,8 @@ def _real_extract(self, url):
|
|||||||
if not c_url:
|
if not c_url:
|
||||||
continue
|
continue
|
||||||
tbr = int_or_none(c.get('bitrate'), 1000)
|
tbr = int_or_none(c.get('bitrate'), 1000)
|
||||||
format_id = 'http'
|
|
||||||
if tbr:
|
|
||||||
format_id += f'-{tbr}'
|
|
||||||
formats.append({
|
formats.append({
|
||||||
'format_id': format_id,
|
'format_id': join_nonempty('http', tbr),
|
||||||
'url': c_url,
|
'url': c_url,
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'width': int_or_none(c.get('width')),
|
'width': int_or_none(c.get('width')),
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import functools
|
||||||
import json
|
import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import functools
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
determine_ext,
|
determine_ext,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
merge_dicts,
|
merge_dicts,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
)
|
)
|
||||||
@ -147,13 +148,13 @@ def fix_bitrate(bitrate):
|
|||||||
'page_url': 'http://www.prosieben.de',
|
'page_url': 'http://www.prosieben.de',
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'ext': 'flv',
|
'ext': 'flv',
|
||||||
'format_id': 'rtmp{}'.format(f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty('rtmp', tbr),
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': source_url,
|
'url': source_url,
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'format_id': 'http{}'.format(f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty('http', tbr),
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -21,7 +21,7 @@ def _perform_login(self, username, password):
|
|||||||
if not urlh:
|
if not urlh:
|
||||||
return
|
return
|
||||||
|
|
||||||
content, urlh = self._download_webpage_handle(
|
response = self._download_webpage_handle(
|
||||||
urlh.url, None, fatal=False, headers={'referer': urlh.url},
|
urlh.url, None, fatal=False, headers={'referer': urlh.url},
|
||||||
note='logging in', errnote='unable to log in',
|
note='logging in', errnote='unable to log in',
|
||||||
data=urlencode_postdata({
|
data=urlencode_postdata({
|
||||||
@ -30,7 +30,11 @@ def _perform_login(self, username, password):
|
|||||||
'j_username': username,
|
'j_username': username,
|
||||||
'j_password': password,
|
'j_password': password,
|
||||||
}))
|
}))
|
||||||
if not urlh or urlh.url == 'https://tube.tugraz.at/paella/ui/index.html':
|
if not response:
|
||||||
|
return
|
||||||
|
|
||||||
|
content, urlh = response
|
||||||
|
if urlh.url == 'https://tube.tugraz.at/paella/ui/index.html':
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self._html_search_regex(
|
if not self._html_search_regex(
|
||||||
@ -39,7 +43,7 @@ def _perform_login(self, username, password):
|
|||||||
self.report_warning('unable to login: incorrect password')
|
self.report_warning('unable to login: incorrect password')
|
||||||
return
|
return
|
||||||
|
|
||||||
content, urlh = self._download_webpage_handle(
|
urlh = self._request_webpage(
|
||||||
urlh.url, None, fatal=False, headers={'referer': urlh.url},
|
urlh.url, None, fatal=False, headers={'referer': urlh.url},
|
||||||
note='logging in with TFA', errnote='unable to log in with TFA',
|
note='logging in with TFA', errnote='unable to log in with TFA',
|
||||||
data=urlencode_postdata({
|
data=urlencode_postdata({
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
float_or_none,
|
float_or_none,
|
||||||
format_field,
|
format_field,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
make_archive_id,
|
make_archive_id,
|
||||||
remove_end,
|
remove_end,
|
||||||
str_or_none,
|
str_or_none,
|
||||||
@ -107,7 +108,7 @@ def _extract_variant_formats(self, variant, video_id):
|
|||||||
tbr = int_or_none(dict_get(variant, ('bitrate', 'bit_rate')), 1000) or None
|
tbr = int_or_none(dict_get(variant, ('bitrate', 'bit_rate')), 1000) or None
|
||||||
f = {
|
f = {
|
||||||
'url': variant_url,
|
'url': variant_url,
|
||||||
'format_id': 'http' + (f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty('http', tbr),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
}
|
}
|
||||||
self._search_dimensions_in_video_url(f, variant_url)
|
self._search_dimensions_in_video_url(f, variant_url)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
parse_age_limit,
|
parse_age_limit,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
)
|
)
|
||||||
@ -120,7 +121,7 @@ def _real_extract(self, url):
|
|||||||
'height', default=None))
|
'height', default=None))
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': video_asset_url,
|
'url': video_asset_url,
|
||||||
'format_id': 'http{}'.format(f'-{bitrate}' if bitrate else ''),
|
'format_id': join_nonempty('http', bitrate),
|
||||||
'tbr': bitrate,
|
'tbr': bitrate,
|
||||||
'height': height,
|
'height': height,
|
||||||
'vcodec': video_asset.get('codec'),
|
'vcodec': video_asset.get('codec'),
|
||||||
|
@ -52,6 +52,7 @@ def _update_visitor_cookies(self, visitor_url, video_id):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def _weibo_download_json(self, url, video_id, *args, fatal=True, note='Downloading JSON metadata', **kwargs):
|
def _weibo_download_json(self, url, video_id, *args, fatal=True, note='Downloading JSON metadata', **kwargs):
|
||||||
|
# XXX: Always fatal; _download_webpage_handle only returns False (not a tuple) on error
|
||||||
webpage, urlh = self._download_webpage_handle(url, video_id, *args, fatal=fatal, note=note, **kwargs)
|
webpage, urlh = self._download_webpage_handle(url, video_id, *args, fatal=fatal, note=note, **kwargs)
|
||||||
if urllib.parse.urlparse(urlh.url).netloc == 'passport.weibo.com':
|
if urllib.parse.urlparse(urlh.url).netloc == 'passport.weibo.com':
|
||||||
self._update_visitor_cookies(urlh.url, video_id)
|
self._update_visitor_cookies(urlh.url, video_id)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
unified_strdate,
|
unified_strdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ def _real_extract(self, url):
|
|||||||
tbr = int_or_none(v.get('bitrate'))
|
tbr = int_or_none(v.get('bitrate'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': mp4_url,
|
'url': mp4_url,
|
||||||
'format_id': 'http' + (f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty('http', tbr),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'width': int_or_none(v.get('width')),
|
'width': int_or_none(v.get('width')),
|
||||||
'height': int_or_none(v.get('height')),
|
'height': int_or_none(v.get('height')),
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
clean_html,
|
clean_html,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
@ -213,7 +214,7 @@ def _extract_yahoo_video(self, video_id, country):
|
|||||||
tbr = int_or_none(s.get('bitrate'))
|
tbr = int_or_none(s.get('bitrate'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': s_url,
|
'url': s_url,
|
||||||
'format_id': fmt + (f'-{tbr}' if tbr else ''),
|
'format_id': join_nonempty(fmt, tbr),
|
||||||
'width': int_or_none(s.get('width')),
|
'width': int_or_none(s.get('width')),
|
||||||
'height': int_or_none(s.get('height')),
|
'height': int_or_none(s.get('height')),
|
||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
@ -371,12 +372,13 @@ def _extract_formats(self, json_data, content_id):
|
|||||||
url, content_id, 'mp4', 'm3u8_native',
|
url, content_id, 'mp4', 'm3u8_native',
|
||||||
m3u8_id='hls', fatal=False))
|
m3u8_id='hls', fatal=False))
|
||||||
else:
|
else:
|
||||||
|
bitrate = int_or_none(vid.get('bitrate'))
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': url,
|
'url': url,
|
||||||
'format_id': f'http-{vid.get("bitrate")}',
|
'format_id': join_nonempty('http', bitrate),
|
||||||
'height': int_or_none(vid.get('height')),
|
'height': int_or_none(vid.get('height')),
|
||||||
'width': int_or_none(vid.get('width')),
|
'width': int_or_none(vid.get('width')),
|
||||||
'tbr': int_or_none(vid.get('bitrate')),
|
'tbr': bitrate,
|
||||||
})
|
})
|
||||||
self._remove_duplicate_formats(formats)
|
self._remove_duplicate_formats(formats)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
determine_ext,
|
determine_ext,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
join_nonempty,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
try_get,
|
try_get,
|
||||||
urljoin,
|
urljoin,
|
||||||
@ -116,12 +117,9 @@ def call_api(action):
|
|||||||
else:
|
else:
|
||||||
size = video.get('size') or {}
|
size = video.get('size') or {}
|
||||||
height = int_or_none(size.get('height'))
|
height = int_or_none(size.get('height'))
|
||||||
format_id = 'hls'
|
|
||||||
if height:
|
|
||||||
format_id += f'-{height}p'
|
|
||||||
formats.append({
|
formats.append({
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'format_id': format_id,
|
'format_id': join_nonempty('hls', height and f'{height}p'),
|
||||||
'height': height,
|
'height': height,
|
||||||
'protocol': 'm3u8_native',
|
'protocol': 'm3u8_native',
|
||||||
'url': format_url,
|
'url': format_url,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import enum
|
import enum
|
||||||
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
@ -20,7 +21,6 @@
|
|||||||
|
|
||||||
from .common import InfoExtractor, SearchInfoExtractor
|
from .common import InfoExtractor, SearchInfoExtractor
|
||||||
from .openload import PhantomJSwrapper
|
from .openload import PhantomJSwrapper
|
||||||
from ..compat import functools
|
|
||||||
from ..jsinterp import JSInterpreter
|
from ..jsinterp import JSInterpreter
|
||||||
from ..networking.exceptions import HTTPError, network_exceptions
|
from ..networking.exceptions import HTTPError, network_exceptions
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import functools
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
@ -22,7 +23,6 @@
|
|||||||
TransportError,
|
TransportError,
|
||||||
)
|
)
|
||||||
from .websocket import WebSocketRequestHandler, WebSocketResponse
|
from .websocket import WebSocketRequestHandler, WebSocketResponse
|
||||||
from ..compat import functools
|
|
||||||
from ..dependencies import websockets
|
from ..dependencies import websockets
|
||||||
from ..socks import ProxyError as SocksProxyError
|
from ..socks import ProxyError as SocksProxyError
|
||||||
from ..utils import int_or_none
|
from ..utils import int_or_none
|
||||||
|
@ -646,7 +646,7 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs):
|
|||||||
'You can also simply specify a field to match if the field is present, '
|
'You can also simply specify a field to match if the field is present, '
|
||||||
'use "!field" to check if the field is not present, and "&" to check multiple conditions. '
|
'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, '
|
'Use a "\\" to escape "&" or quotes if needed. If used multiple times, '
|
||||||
'the filter matches if atleast one of the conditions are met. E.g. --match-filter '
|
'the filter matches if at least one of the conditions is met. E.g. --match-filter '
|
||||||
'!is_live --match-filter "like_count>?100 & description~=\'(?i)\\bcats \\& dogs\\b\'" '
|
'!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 '
|
'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 '
|
'(or the like field is not available) and also has a description '
|
||||||
@ -1479,7 +1479,7 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs):
|
|||||||
'Optionally, the KEYRING used for decrypting Chromium cookies on Linux, '
|
'Optionally, the KEYRING used for decrypting Chromium cookies on Linux, '
|
||||||
'the name/path of the PROFILE to load cookies from, '
|
'the name/path of the PROFILE to load cookies from, '
|
||||||
'and the CONTAINER name (if Firefox) ("none" for no container) '
|
'and the CONTAINER name (if Firefox) ("none" for no container) '
|
||||||
'can be given with their respective seperators. '
|
'can be given with their respective separators. '
|
||||||
'By default, all containers of the most recently accessed profile are used. '
|
'By default, all containers of the most recently accessed profile are used. '
|
||||||
f'Currently supported keyrings are: {", ".join(map(str.lower, sorted(SUPPORTED_KEYRINGS)))}'))
|
f'Currently supported keyrings are: {", ".join(map(str.lower, sorted(SUPPORTED_KEYRINGS)))}'))
|
||||||
filesystem.add_option(
|
filesystem.add_option(
|
||||||
@ -1781,7 +1781,7 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs):
|
|||||||
'It can be one of "pre_process" (after video extraction), "after_filter" (after video passes filter), '
|
'It can be one of "pre_process" (after video extraction), "after_filter" (after video passes filter), '
|
||||||
'"video" (after --format; before --print/--output), "before_dl" (before each video download), '
|
'"video" (after --format; before --print/--output), "before_dl" (before each video download), '
|
||||||
'"post_process" (after each video download; default), '
|
'"post_process" (after each video download; default), '
|
||||||
'"after_move" (after moving video file to it\'s final locations), '
|
'"after_move" (after moving video file to its final locations), '
|
||||||
'"after_video" (after downloading and processing all formats of a video), '
|
'"after_video" (after downloading and processing all formats of a video), '
|
||||||
'or "playlist" (at end of playlist). '
|
'or "playlist" (at end of playlist). '
|
||||||
'This option can be used multiple times to add different postprocessors'))
|
'This option can be used multiple times to add different postprocessors'))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import collections
|
import collections
|
||||||
import contextvars
|
import contextvars
|
||||||
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -8,7 +9,7 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from .common import PostProcessor
|
from .common import PostProcessor
|
||||||
from ..compat import functools, imghdr
|
from ..compat import imghdr
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
MEDIA_EXTENSIONS,
|
MEDIA_EXTENSIONS,
|
||||||
ISO639Utils,
|
ISO639Utils,
|
||||||
|
Loading…
Reference in New Issue
Block a user