[cleanup] Misc (#11216)

- Add Python 3.13 to CI, finalize 3.13 support
- Remove Python 3.8 from CI in preparation for removing 3.8 support
- Document that PyPy3.8 and PyPy3.9 are no longer supported
- Usual documentation fixes and code cleanup

Closes #8248, Closes #11146, Closes #11149, Closes #11211
Authored by: Grub4K, grqz, DTrombett, KarboniteKream, bashonly, mikkovedru, seproDev

Co-authored-by: N/Ame <173015200+grqz@users.noreply.github.com>
Co-authored-by: DTrombett <d@trombett.org>
Co-authored-by: =?UTF-8?q?Klemen=20Ko=C5=A1ir?= <klemen.kosir@kream.io>
Co-authored-by: bashonly <88596187+bashonly@users.noreply.github.com>
Co-authored-by: Mikko Vedru <mikko.vedru@gmail.com>
Co-authored-by: sepro <sepro@sepr0.com>
pull/11311/head
Simon Sawicki 1 month ago committed by GitHub
parent a886cf3e90
commit 67adeb7bab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -36,16 +36,20 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
# CPython 3.8 is in quick-test
python-version: ['3.9', '3.10', '3.11', '3.12', pypy-3.8, pypy-3.10]
# CPython 3.9 is in quick-test
python-version: ['3.10', '3.11', '3.12', '3.13', pypy-3.10]
include:
# atleast one of each CPython/PyPy tests must be in windows
- os: windows-latest
python-version: '3.8'
python-version: '3.9'
- os: windows-latest
python-version: '3.10'
- os: windows-latest
python-version: '3.12'
- os: windows-latest
python-version: pypy-3.9
python-version: '3.13'
- os: windows-latest
python-version: pypy-3.10
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}

@ -28,13 +28,13 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
python-version: ['3.10', '3.11', '3.12', pypy-3.8, pypy-3.10]
python-version: ['3.10', '3.11', '3.12', '3.13', pypy-3.10]
include:
# atleast one of each CPython/PyPy tests must be in windows
- os: windows-latest
python-version: '3.8'
python-version: '3.9'
- os: windows-latest
python-version: pypy-3.9
python-version: pypy-3.10
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}

@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.8
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: '3.8'
python-version: '3.9'
- name: Install test requirements
run: python3 ./devscripts/install_deps.py -o --include test
- name: Run tests
@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.8'
python-version: '3.9'
- name: Install dev dependencies
run: python3 ./devscripts/install_deps.py -o --include static-analysis
- name: Make lazy extractors

@ -233,7 +233,7 @@ After you have ensured this site is distributing its content legally, you can fo
# * MD5 checksum; start the string with 'md5:', e.g.
# 'description': 'md5:098f6bcd4621d373cade4e832627b4f6',
# * A regular expression; start the string with 're:', e.g.
# 'thumbnail': r're:^https?://.*\.jpg$',
# 'thumbnail': r're:https?://.*\.jpg$',
# * A count of elements in a list; start the string with 'count:', e.g.
# 'tags': 'count:10',
# * Any Python type, e.g.
@ -268,7 +268,7 @@ After you have ensured this site is distributing its content legally, you can fo
You can use `hatch fmt` to automatically fix problems. Rules that the linter/formatter enforces should not be disabled with `# noqa` unless a maintainer requests it. The only exception allowed is for old/printf-style string formatting in GraphQL query templates (use `# noqa: UP031`).
1. Make sure your code works under all [Python](https://www.python.org/) versions supported by yt-dlp, namely CPython and PyPy for Python 3.8 and above. Backward compatibility is not required for even older versions of Python.
1. Make sure your code works under all [Python](https://www.python.org/) versions supported by yt-dlp, namely CPython >=3.8 and PyPy >=3.10. Backward compatibility is not required for even older versions of Python.
1. When the tests pass, [add](https://git-scm.com/docs/git-add) the new files, [commit](https://git-scm.com/docs/git-commit) them and [push](https://git-scm.com/docs/git-push) the result, like this:
```shell
@ -302,10 +302,9 @@ Extractors are very fragile by nature since they depend on the layout of the sou
For extraction to work yt-dlp relies on metadata your extractor extracts and provides to yt-dlp expressed by an [information dictionary](yt_dlp/extractor/common.py#L119-L440) or simply *info dict*. Only the following meta fields in the *info dict* are considered mandatory for a successful extraction process by yt-dlp:
- `id` (media identifier)
- `title` (media title)
- `url` (media download URL) or `formats`
The aforementioned metafields are the critical data that the extraction does not make any sense without and if any of them fail to be extracted then the extractor is considered completely broken. While all extractors must return a `title`, they must also allow it's extraction to be non-fatal.
The aforementioned metadata fields are the critical data without which extraction does not make any sense. If any of them fail to be extracted, then the extractor is considered broken. All other metadata extraction should be completely non-fatal.
For pornographic sites, appropriate `age_limit` must also be returned.

@ -4,7 +4,7 @@
[![YT-DLP](https://raw.githubusercontent.com/yt-dlp/yt-dlp/master/.github/banner.svg)](#readme)
[![Release version](https://img.shields.io/github/v/release/yt-dlp/yt-dlp?color=brightgreen&label=Download&style=for-the-badge)](#installation "Installation")
[![PyPi](https://img.shields.io/badge/-PyPi-blue.svg?logo=pypi&labelColor=555555&style=for-the-badge)](https://pypi.org/project/yt-dlp "PyPi")
[![PyPI](https://img.shields.io/badge/-PyPI-blue.svg?logo=pypi&labelColor=555555&style=for-the-badge)](https://pypi.org/project/yt-dlp "PyPI")
[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)](Collaborators.md#collaborators "Donate")
[![Matrix](https://img.shields.io/matrix/yt-dlp:matrix.org?color=brightgreen&labelColor=555555&label=&logo=element&style=for-the-badge)](https://matrix.to/#/#yt-dlp:matrix.org "Matrix")
[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)](https://discord.gg/H5MNcFW63r "Discord")
@ -81,7 +81,7 @@ yt-dlp is a feature-rich command-line audio/video downloader with support for [t
[![Windows](https://img.shields.io/badge/-Windows_x64-blue.svg?style=for-the-badge&logo=windows)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe)
[![Unix](https://img.shields.io/badge/-Linux/BSD-red.svg?style=for-the-badge&logo=linux)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp)
[![MacOS](https://img.shields.io/badge/-MacOS-lightblue.svg?style=for-the-badge&logo=apple)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos)
[![PyPi](https://img.shields.io/badge/-PyPi-blue.svg?logo=pypi&labelColor=555555&style=for-the-badge)](https://pypi.org/project/yt-dlp)
[![PyPI](https://img.shields.io/badge/-PyPI-blue.svg?logo=pypi&labelColor=555555&style=for-the-badge)](https://pypi.org/project/yt-dlp)
[![Source Tarball](https://img.shields.io/badge/-Source_tar-green.svg?style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.tar.gz)
[![Other variants](https://img.shields.io/badge/-Other-grey.svg?style=for-the-badge)](#release-files)
[![All versions](https://img.shields.io/badge/-All_Versions-lightgrey.svg?style=for-the-badge)](https://github.com/yt-dlp/yt-dlp/releases)
@ -172,11 +172,11 @@ python3 -m pip install -U --pre "yt-dlp[default]"
```
## DEPENDENCIES
Python versions 3.8+ (CPython and PyPy) are supported. Other versions and implementations may or may not work correctly.
Python versions 3.8+ (CPython) and 3.10+ (PyPy) are supported. Other versions and implementations may or may not work correctly.
<!-- Python 3.5+ uses VC++14 and it is already embedded in the binary created
<!x-- https://www.microsoft.com/en-us/download/details.aspx?id=26999 --x>
On windows, [Microsoft Visual C++ 2010 SP1 Redistributable Package (x86)](https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe) is also necessary to run yt-dlp. You probably already have this, but if the executable throws an error due to missing `MSVCR100.dll` you need to install it manually.
On Windows, [Microsoft Visual C++ 2010 SP1 Redistributable Package (x86)](https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe) is also necessary to run yt-dlp. You probably already have this, but if the executable throws an error due to missing `MSVCR100.dll` you need to install it manually.
-->
While all the other dependencies are optional, `ffmpeg` and `ffprobe` are highly recommended
@ -438,10 +438,10 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
E.g. "--date today-2weeks" downloads only
videos uploaded on the same day two weeks ago
--datebefore DATE Download only videos uploaded on or before
this date. The date formats accepted is the
this date. The date formats accepted are the
same as --date
--dateafter DATE Download only videos uploaded on or after
this date. The date formats accepted is the
this date. The date formats accepted are the
same as --date
--match-filters FILTER Generic video filter. Any "OUTPUT TEMPLATE"
field can be compared with a number or a
@ -726,16 +726,16 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
used. This option can be used multiple times
--print-to-file [WHEN:]TEMPLATE FILE
Append given template to the file. The
values of WHEN and TEMPLATE are same as that
of --print. FILE uses the same syntax as the
output template. This option can be used
multiple times
values of WHEN and TEMPLATE are the same as
that of --print. FILE uses the same syntax
as the output template. This option can be
used multiple times
-j, --dump-json Quiet, but print JSON information for each
video. Simulate unless --no-simulate is
used. See "OUTPUT TEMPLATE" for a
description of available keys
-J, --dump-single-json Quiet, but print JSON information for each
url or infojson passed. Simulate unless
URL or infojson passed. Simulate unless
--no-simulate is used. If the URL refers to
a playlist, the whole playlist information
is dumped in a single line
@ -810,9 +810,9 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
--no-audio-multistreams Only one audio stream is downloaded for each
output file (default)
--prefer-free-formats Prefer video formats with free containers
over non-free ones of same quality. Use with
"-S ext" to strictly prefer free containers
irrespective of quality
over non-free ones of the same quality. Use
with "-S ext" to strictly prefer free
containers irrespective of quality
--no-prefer-free-formats Don't give any special preference to free
containers (default)
--check-formats Make sure formats are selected only from
@ -837,15 +837,17 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
(default) (Alias: --no-write-automatic-subs)
--list-subs List available subtitles of each video.
Simulate unless --no-simulate is used
--sub-format FORMAT Subtitle format; accepts formats preference,
e.g. "srt" or "ass/srt/best"
--sub-format FORMAT Subtitle format; accepts formats preference
separated by "/", e.g. "srt" or "ass/srt/best"
--sub-langs LANGS Languages of the subtitles to download (can
be regex) or "all" separated by commas, e.g.
--sub-langs "en.*,ja". You can prefix the
language code with a "-" to exclude it from
the requested languages, e.g. --sub-langs
all,-live_chat. Use --list-subs for a list
of available language tags
--sub-langs "en.*,ja" (where "en.*" is a
regex pattern that matches "en" followed by
0 or more of any character). You can prefix
the language code with a "-" to exclude it
from the requested languages, e.g. --sub-
langs all,-live_chat. Use --list-subs for a
list of available language tags
## Authentication Options:
-u, --username USERNAME Login with this account ID
@ -893,9 +895,9 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
necessary (currently supported: avi, flv,
gif, mkv, mov, mp4, webm, aac, aiff, alac,
flac, m4a, mka, mp3, ogg, opus, vorbis,
wav). If target container does not support
the video/audio codec, remuxing will fail.
You can specify multiple rules; e.g.
wav). If the target container does not
support the video/audio codec, remuxing will
fail. You can specify multiple rules; e.g.
"aac>m4a/mov>mp4/mkv" will remux aac to m4a,
mov to mp4 and anything else to mkv
--recode-video FORMAT Re-encode the video into another format if
@ -963,29 +965,29 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
are the same as that of --use-postprocessor
(default: pre_process)
--xattrs Write metadata to the video file's xattrs
(using dublin core and xdg standards)
(using Dublin Core and XDG standards)
--concat-playlist POLICY Concatenate videos in a playlist. One of
"never", "always", or "multi_video"
(default; only when the videos form a single
show). All the video files must have same
codecs and number of streams to be
concatable. The "pl_video:" prefix can be
show). All the video files must have the
same codecs and number of streams to be
concatenable. The "pl_video:" prefix can be
used with "--paths" and "--output" to set
the output filename for the concatenated
files. See "OUTPUT TEMPLATE" for details
--fixup POLICY Automatically correct known faults of the
file. One of never (do nothing), warn (only
emit a warning), detect_or_warn (the
default; fix file if we can, warn
otherwise), force (try fixing even if file
already exists)
default; fix the file if we can, warn
otherwise), force (try fixing even if the
file already exists)
--ffmpeg-location PATH Location of the ffmpeg binary; either the
path to the binary or its containing directory
--exec [WHEN:]CMD Execute a command, optionally prefixed with
when to execute it, separated by a ":".
Supported values of "WHEN" are the same as
that of --use-postprocessor (default:
after_move). Same syntax as the output
after_move). The same syntax as the output
template can be used to pass any field as
arguments to the command. If no fields are
passed, %(filepath,_filename|)q is appended
@ -1023,7 +1025,7 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
--no-force-keyframes-at-cuts Do not force keyframes around the chapters
when cutting/splitting (default)
--use-postprocessor NAME[:ARGS]
The (case sensitive) name of plugin
The (case-sensitive) name of plugin
postprocessors to be enabled, and
(optionally) arguments to be passed to it,
separated by a colon ":". ARGS are a
@ -1036,8 +1038,8 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
--print/--output), "before_dl" (before each
video download), "post_process" (after each
video download; default), "after_move"
(after moving video file to its final
locations), "after_video" (after downloading
(after moving the video file to its final
location), "after_video" (after downloading
and processing all formats of a video), or
"playlist" (at end of playlist). This option
can be used multiple times to add different
@ -1055,7 +1057,7 @@ Make chapter entries for, or remove various segments (sponsor,
music_offtopic, poi_highlight, chapter, all
and default (=all). You can prefix the
category with a "-" to exclude it. See [1]
for description of the categories. E.g.
for descriptions of the categories. E.g.
--sponsorblock-mark all,-preview
[1] https://wiki.sponsor.ajay.app/w/Segment_Categories
--sponsorblock-remove CATS SponsorBlock categories to be removed from
@ -1087,7 +1089,7 @@ Make chapter entries for, or remove various segments (sponsor,
(Alias: --no-allow-dynamic-mpd)
--hls-split-discontinuity Split HLS playlists to different formats at
discontinuities such as ad breaks
--no-hls-split-discontinuity Do not split HLS playlists to different
--no-hls-split-discontinuity Do not split HLS playlists into different
formats at discontinuities such as ad breaks
(default)
--extractor-args IE_KEY:ARGS Pass ARGS arguments to the IE_KEY extractor.
@ -1097,7 +1099,7 @@ Make chapter entries for, or remove various segments (sponsor,
# CONFIGURATION
You can configure yt-dlp by placing any supported command line option to a configuration file. The configuration is loaded from the following locations:
You can configure yt-dlp by placing any supported command line option in a configuration file. The configuration is loaded from the following locations:
1. **Main Configuration**:
* The file given to `--config-location`
@ -1142,7 +1144,7 @@ E.g. with the following configuration file, yt-dlp will always extract the audio
-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 a 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.
@ -1176,13 +1178,13 @@ As an alternative to using the `.netrc` file, which has the disadvantage of keep
E.g. To use an encrypted `.netrc` file stored as `.authinfo.gpg`
```
yt-dlp --netrc-cmd 'gpg --decrypt ~/.authinfo.gpg' https://www.youtube.com/watch?v=BaW_jenozKc
yt-dlp --netrc-cmd 'gpg --decrypt ~/.authinfo.gpg' 'https://www.youtube.com/watch?v=BaW_jenozKc'
```
### Notes about environment variables
* Environment variables are normally specified as `${VARIABLE}`/`$VARIABLE` on UNIX and `%VARIABLE%` on Windows; but is always shown as `${VARIABLE}` in this documentation
* yt-dlp also allow using UNIX-style variables on Windows for path-like options; e.g. `--output`, `--config-location`
* yt-dlp also allows using UNIX-style variables on Windows for path-like options; e.g. `--output`, `--config-location`
* If unset, `${XDG_CONFIG_HOME}` defaults to `~/.config` and `${XDG_CACHE_HOME}` to `~/.cache`
* On Windows, `~` points to `${HOME}` if present; or, `${USERPROFILE}` or `${HOMEDRIVE}${HOMEPATH}` otherwise
* On Windows, `${USERPROFILE}` generally points to `C:\Users\<user name>` and `${APPDATA}` to `${USERPROFILE}\AppData\Roaming`
@ -1263,7 +1265,7 @@ The available fields are:
- `like_count` (numeric): Number of positive ratings of the video
- `dislike_count` (numeric): Number of negative ratings of the video
- `repost_count` (numeric): Number of reposts of the video
- `average_rating` (numeric): Average rating give by users, the scale used depends on the webpage
- `average_rating` (numeric): Average rating given by users, the scale used depends on the webpage
- `comment_count` (numeric): Number of comments on the video (For some extractors, comments are only downloaded at the end, and so this field cannot be used)
- `age_limit` (numeric): Age restriction for the video (years)
- `live_status` (string): One of "not_live", "is_live", "is_upcoming", "was_live", "post_live" (was live, but VOD is not yet processed)
@ -1293,7 +1295,7 @@ The available fields are:
- `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_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 the same as `webpage_url` for playlist entries)
- `categories` (list): List of categories the video belongs to
- `tags` (list): List of tags assigned to the video
- `cast` (list): List of cast members
@ -1370,7 +1372,7 @@ Each aforementioned sequence when referenced in an output template will be repla
**Tip**: Look at the `-j` output to identify which fields are available for the particular URL
For numeric sequences you can use [numeric related formatting](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting); e.g. `%(view_count)05d` will result in a string with view count padded with zeros up to 5 characters, like in `00042`.
For numeric sequences, you can use [numeric related formatting](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting); e.g. `%(view_count)05d` will result in a string with view count padded with zeros up to 5 characters, like in `00042`.
Output templates can also contain arbitrary hierarchical path, e.g. `-o "%(playlist)s/%(playlist_index)s - %(title)s.%(ext)s"` which will result in downloading each video in a directory corresponding to this path template. Any missing directory will be automatically created for you.
@ -1412,7 +1414,7 @@ $ yt-dlp -P "C:/MyVideos" -o "%(series)s/%(season_number)s - %(season)s/%(episod
# Download video as "C:\MyVideos\uploader\title.ext", subtitles as "C:\MyVideos\subs\uploader\title.ext"
# and put all temporary files in "C:\MyVideos\tmp"
$ yt-dlp -P "C:/MyVideos" -P "temp:tmp" -P "subtitle:subs" -o "%(uploader)s/%(title)s.%(ext)s" BaW_jenoz --write-subs
$ yt-dlp -P "C:/MyVideos" -P "temp:tmp" -P "subtitle:subs" -o "%(uploader)s/%(title)s.%(ext)s" BaW_jenozKc --write-subs
# Download video as "C:\MyVideos\uploader\title.ext" and subtitles as "C:\MyVideos\uploader\subs\title.ext"
$ yt-dlp -P "C:/MyVideos" -o "%(uploader)s/%(title)s.%(ext)s" -o "subtitle:%(uploader)s/subs/%(title)s.%(ext)s" BaW_jenozKc --write-subs
@ -1630,11 +1632,11 @@ $ yt-dlp -S "res:480"
# or the worst video (that also has audio) if there is no video under 50 MB
$ yt-dlp -f "b[filesize<50M] / w"
# Download largest video (that also has audio) but no bigger than 50 MB,
# Download the largest video (that also has audio) but no bigger than 50 MB,
# or the smallest video (that also has audio) if there is no video under 50 MB
$ yt-dlp -f "b" -S "filesize:50M"
# Download best video (that also has audio) that is closest in size to 50 MB
# Download the best video (that also has audio) that is closest in size to 50 MB
$ yt-dlp -f "b" -S "filesize~50M"
@ -1765,7 +1767,7 @@ The following extractors use this feature:
#### 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
* `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` and `_creator` (e.g. `ios_creator`); and `mediaconnect`, `mweb`, `android_producer`, `android_testsuite`, `android_vr`, `web_safari`, `web_embedded`, `tv` and `tv_embedded` with no variants. By default, `ios,mweb` is used, and `tv_embedded`, `web_creator` and `mediaconnect` are added as required for age-gated videos. Similarly, the music variants are added for `music.youtube.com` urls. Most `android` clients will be given lowest priority since their formats are broken. You can use `all` to use all the clients, and `default` for the default clients. You can prefix a client with `-` to exclude it, e.g. `youtube:player_client=all,-web`
* `player_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music` and `_creator` (e.g. `ios_creator`); and `mediaconnect`, `mweb`, `android_producer`, `android_testsuite`, `android_vr`, `web_safari`, `web_embedded`, `tv` and `tv_embedded` with no variants. By default, `ios,mweb` is used, and `tv_embedded`, `web_creator` and `mediaconnect` are added as required for age-gated videos. Similarly, the music variants are added for `music.youtube.com` URLs. Most `android` clients will be given lowest priority since their formats are broken. You can use `all` to use all the clients, and `default` for the default clients. You can prefix a client with `-` to exclude it, e.g. `youtube:player_client=all,-web`
* `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.
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
@ -2198,7 +2200,7 @@ Features marked with a **\*** have been back-ported to youtube-dl
Some of yt-dlp's default options are different from that of youtube-dl and youtube-dlc:
* yt-dlp supports only [Python 3.8+](## "Windows 7"), and *may* remove support for more versions as they [become EOL](https://devguide.python.org/versions/#python-release-cycle); while [youtube-dl still supports Python 2.6+ and 3.2+](https://github.com/ytdl-org/youtube-dl/issues/30568#issue-1118238743)
* yt-dlp supports only [Python 3.8+](## "Windows 7"), and will remove support for more versions as they [become EOL](https://devguide.python.org/versions/#python-release-cycle); while [youtube-dl still supports Python 2.6+ and 3.2+](https://github.com/ytdl-org/youtube-dl/issues/30568#issue-1118238743)
* The options `--auto-number` (`-A`), `--title` (`-t`) and `--literal` (`-l`), no longer work. See [removed options](#Removed) for details
* `avconv` is not supported as an alternative to `ffmpeg`
* yt-dlp stores config files in slightly different locations to youtube-dl. See [CONFIGURATION](#configuration) for a list of correct locations
@ -2274,8 +2276,8 @@ While these options are redundant, they are still expected to be used due to the
--min-views COUNT --match-filters "view_count >=? COUNT"
--max-views COUNT --match-filters "view_count <=? COUNT"
--break-on-reject Use --break-match-filters
--user-agent UA --add-header "User-Agent:UA"
--referer URL --add-header "Referer:URL"
--user-agent UA --add-headers "User-Agent:UA"
--referer URL --add-headers "Referer:URL"
--playlist-start NUMBER -I NUMBER:
--playlist-end NUMBER -I :NUMBER
--playlist-reverse -I ::-1

@ -196,5 +196,15 @@
"when": "b31b81d85f00601710d4fac590c3e4efb4133283",
"short": "[ci] Rerun failed tests (#11143)",
"authors": ["Grub4K"]
},
{
"action": "add",
"when": "a886cf3e900f4a2ec00af705f883539269545609",
"short": "[priority] **py2exe is no longer supported**\nThis release's `yt-dlp_min.exe` will be the last, and it's actually a PyInstaller-bundled executable so that yt-dlp users updating their py2exe build with `-U` will be automatically migrated. [Read more](https://github.com/yt-dlp/yt-dlp/issues/10087)"
},
{
"action": "add",
"when": "a886cf3e900f4a2ec00af705f883539269545609",
"short": "[priority] **Following this release, yt-dlp's Python dependencies *must* be installed using the `default` group**\nIf you're installing yt-dlp with pip/pipx or requiring yt-dlp in your own Python project, you'll need to specify `yt-dlp[default]` if you want to also install yt-dlp's optional dependencies (which were previously included by default). [Read more](https://github.com/yt-dlp/yt-dlp/pull/11255)"
}
]

@ -34,6 +34,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
@ -169,13 +170,11 @@ run-cov = "echo Code coverage not implemented && exit 1"
[[tool.hatch.envs.hatch-test.matrix]]
python = [
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
"pypy3.8",
"pypy3.9",
"3.13",
"pypy3.10",
]

@ -16,7 +16,7 @@ remove-unused-variables = true
[tox:tox]
skipsdist = true
envlist = py{38,39,310,311,312},pypy{38,39,310}
envlist = py{39,310,311,312,313},pypy310
skip_missing_interpreters = true
[testenv] # tox

@ -154,7 +154,6 @@ from .utils import (
try_get,
url_basename,
variadic,
version_tuple,
windows_enable_vt_mode,
write_json_file,
write_string,
@ -251,7 +250,7 @@ class YoutubeDL:
format_sort_force: Force the given format_sort. see "Sorting Formats"
for more details.
prefer_free_formats: Whether to prefer video formats with free containers
over non-free ones of same quality.
over non-free ones of the same quality.
allow_multiple_video_streams: Allow multiple video streams to be merged
into a single file
allow_multiple_audio_streams: Allow multiple audio streams to be merged
@ -285,7 +284,7 @@ class YoutubeDL:
rejecttitle: Reject downloads for matching titles.
logger: Log messages to a logging.Logger instance.
logtostderr: Print everything to stderr instead of stdout.
consoletitle: Display progress in console window's titlebar.
consoletitle: Display progress in the console window's titlebar.
writedescription: Write the video description to a .description file
writeinfojson: Write the video description to a .info.json file
clean_infojson: Remove internal metadata from the infojson
@ -513,7 +512,7 @@ class YoutubeDL:
The following options are used by the extractors:
extractor_retries: Number of times to retry for known errors (default: 3)
dynamic_mpd: Whether to process dynamic DASH manifests (default: True)
hls_split_discontinuity: Split HLS playlists to different formats at
hls_split_discontinuity: Split HLS playlists into different formats at
discontinuities such as ad breaks (default: False)
extractor_args: A dictionary of arguments to be passed to the extractors.
See "EXTRACTOR ARGUMENTS" for details.
@ -553,7 +552,7 @@ class YoutubeDL:
include_ads: - Doesn't work
Download ads as well
call_home: - Not implemented
Boolean, true iff we are allowed to contact the
Boolean, true if we are allowed to contact the
yt-dlp servers for debugging.
post_hooks: - Register a custom postprocessor
A list of functions that get called as the final step
@ -4089,17 +4088,6 @@ class YoutubeDL:
if plugin_dirs:
write_debug(f'Plugin directories: {plugin_dirs}')
# Not implemented
if False and self.params.get('call_home'):
ipaddr = self.urlopen('https://yt-dl.org/ip').read().decode()
write_debug(f'Public IP address: {ipaddr}')
latest_version = self.urlopen(
'https://yt-dl.org/latest/version').read().decode()
if version_tuple(latest_version) > version_tuple(__version__):
self.report_warning(
f'You are using an outdated version (newest version: {latest_version})! '
'See https://yt-dl.org/update if you need help updating.')
@functools.cached_property
def proxies(self):
"""Global proxy configuration"""

@ -333,7 +333,7 @@ class InfoExtractor:
like_count: Number of positive ratings of the video
dislike_count: Number of negative ratings of the video
repost_count: Number of reposts of the video
average_rating: Average rating give by users, the scale used depends on the webpage
average_rating: Average rating given by users, the scale used depends on the webpage
comment_count: Number of comments on the video
comments: A list of comments, each with one or more of the following
properties (all but one of text or html optional):
@ -520,7 +520,7 @@ class InfoExtractor:
or _extract_from_webpage as necessary. While these are normally classmethods,
_extract_from_webpage is allowed to be an instance method.
_extract_from_webpage may raise self.StopExtraction() to stop further
_extract_from_webpage may raise self.StopExtraction to stop further
processing of the webpage and obtain exclusive rights to it. This is useful
when the extractor cannot reliably be matched using just the URL,
e.g. invidious/peertube instances

@ -371,7 +371,7 @@ class NexxIE(InfoExtractor):
# not all videos work via arc, e.g. nexx:741:1269984
if not video:
# Reverse engineered from JS code (see getDeviceID function)
device_id = f'{random.randint(1, 4)}:{int(time.time())}:{random.randint(1e4, 99999)}{random.randint(1, 9)}'
device_id = f'{random.randint(1, 4)}:{int(time.time())}:{random.randint(10000, 99999)}{random.randint(1, 9)}'
result = self._call_api(domain_id, 'session/init', video_id, data={
'nxp_devh': device_id,

@ -236,7 +236,7 @@ class TubeTuGrazSeriesIE(TubeTuGrazBaseIE):
},
},
],
'min_playlist_count': 4,
'playlist_mincount': 4,
}]
def _real_extract(self, url):

@ -73,7 +73,7 @@ class UstreamIE(InfoExtractor):
def num_to_hex(n):
return hex(n)[2:]
rnd = random.randrange
rnd = lambda x: random.randrange(int(x))
if not extra_note:
extra_note = ''

@ -8,7 +8,8 @@ from ..utils import (
int_or_none,
parse_duration,
qualities,
try_get,
remove_start,
strip_or_none,
)
@ -108,7 +109,7 @@ class VeohIE(InfoExtractor):
categories = metadata.get('categoryPath')
if not categories:
category = try_get(video, lambda x: x['category'].strip().removeprefix('category_'))
category = remove_start(strip_or_none(video.get('category')), 'category_')
categories = [category] if category else None
tags = video.get('tags')

@ -7792,9 +7792,9 @@ class YoutubeTruncatedURLIE(YoutubeBaseInfoExtractor):
raise ExtractorError(
'Did you forget to quote the URL? Remember that & is a meta '
'character in most shells, so you want to put the URL in quotes, '
'like youtube-dl '
'like yt-dlp '
'"https://www.youtube.com/watch?feature=foo&v=BaW_jenozKc" '
' or simply youtube-dl BaW_jenozKc .',
' or simply yt-dlp BaW_jenozKc .',
expected=True)

@ -631,13 +631,13 @@ def create_parser():
metavar='DATE', dest='datebefore', default=None,
help=(
'Download only videos uploaded on or before this date. '
'The date formats accepted is the same as --date'))
'The date formats accepted are the same as --date'))
selection.add_option(
'--dateafter',
metavar='DATE', dest='dateafter', default=None,
help=(
'Download only videos uploaded on or after this date. '
'The date formats accepted is the same as --date'))
'The date formats accepted are the same as --date'))
selection.add_option(
'--min-views',
metavar='COUNT', dest='min_views', default=None, type=int,
@ -833,7 +833,7 @@ def create_parser():
'--prefer-free-formats',
action='store_true', dest='prefer_free_formats', default=False,
help=(
'Prefer video formats with free containers over non-free ones of same quality. '
'Prefer video formats with free containers over non-free ones of the same quality. '
'Use with "-S ext" to strictly prefer free containers irrespective of quality'))
video_format.add_option(
'--no-prefer-free-formats',
@ -907,13 +907,14 @@ def create_parser():
subtitles.add_option(
'--sub-format',
action='store', dest='subtitlesformat', metavar='FORMAT', default='best',
help='Subtitle format; accepts formats preference, e.g. "srt" or "ass/srt/best"')
help='Subtitle format; accepts formats preference separated by "/", e.g. "srt" or "ass/srt/best"')
subtitles.add_option(
'--sub-langs', '--srt-langs',
action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
default=[], callback=_list_from_options_callback,
help=(
'Languages of the subtitles to download (can be regex) or "all" separated by commas, e.g. --sub-langs "en.*,ja". '
'Languages of the subtitles to download (can be regex) or "all" separated by commas, e.g. --sub-langs "en.*,ja" '
'(where "en.*" is a regex pattern that matches "en" followed by 0 or more of any character). '
'You can prefix the language code with a "-" to exclude it from the requested languages, e.g. --sub-langs all,-live_chat. '
'Use --list-subs for a list of available language tags'))
@ -1182,7 +1183,7 @@ def create_parser():
'--print-to-file',
metavar='[WHEN:]TEMPLATE FILE', dest='print_to_file', nargs=2, **when_prefix('video'),
help=(
'Append given template to the file. The values of WHEN and TEMPLATE are same as that of --print. '
'Append given template to the file. The values of WHEN and TEMPLATE are the same as that of --print. '
'FILE uses the same syntax as the output template. This option can be used multiple times'))
verbosity.add_option(
'-g', '--get-url',
@ -1226,7 +1227,7 @@ def create_parser():
'-J', '--dump-single-json',
action='store_true', dest='dump_single_json', default=False,
help=(
'Quiet, but print JSON information for each url or infojson passed. Simulate unless --no-simulate is used. '
'Quiet, but print JSON information for each URL or infojson passed. Simulate unless --no-simulate is used. '
'If the URL refers to a playlist, the whole playlist information is dumped in a single line'))
verbosity.add_option(
'--print-json',
@ -1570,7 +1571,7 @@ def create_parser():
help=(
'Remux the video into another container if necessary '
f'(currently supported: {", ".join(FFmpegVideoRemuxerPP.SUPPORTED_EXTS)}). '
'If target container does not support the video/audio codec, remuxing will fail. You can specify multiple rules; '
'If the target container does not support the video/audio codec, remuxing will fail. You can specify multiple rules; '
'e.g. "aac>m4a/mov>mp4/mkv" will remux aac to m4a, mov to mp4 and anything else to mkv'))
postproc.add_option(
'--recode-video',
@ -1676,7 +1677,7 @@ def create_parser():
postproc.add_option(
'--xattrs', '--xattr',
action='store_true', dest='xattrs', default=False,
help='Write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
help='Write metadata to the video file\'s xattrs (using Dublin Core and XDG standards)')
postproc.add_option(
'--concat-playlist',
metavar='POLICY', dest='concat_playlist', default='multi_video',
@ -1684,7 +1685,7 @@ def create_parser():
help=(
'Concatenate videos in a playlist. One of "never", "always", or '
'"multi_video" (default; only when the videos form a single show). '
'All the video files must have same codecs and number of streams to be concatable. '
'All the video files must have the same codecs and number of streams to be concatenable. '
'The "pl_video:" prefix can be used with "--paths" and "--output" to '
'set the output filename for the concatenated files. See "OUTPUT TEMPLATE" for details'))
postproc.add_option(
@ -1694,8 +1695,8 @@ def create_parser():
help=(
'Automatically correct known faults of the file. '
'One of never (do nothing), warn (only emit a warning), '
'detect_or_warn (the default; fix file if we can, warn otherwise), '
'force (try fixing even if file already exists)'))
'detect_or_warn (the default; fix the file if we can, warn otherwise), '
'force (try fixing even if the file already exists)'))
postproc.add_option(
'--prefer-avconv', '--no-prefer-ffmpeg',
action='store_false', dest='prefer_ffmpeg',
@ -1714,7 +1715,7 @@ def create_parser():
help=(
'Execute a command, optionally prefixed with when to execute it, separated by a ":". '
'Supported values of "WHEN" are the same as that of --use-postprocessor (default: after_move). '
'Same syntax as the output template can be used to pass any field as arguments to the command. '
'The same syntax as the output template can be used to pass any field as arguments to the command. '
'If no fields are passed, %(filepath,_filename|)q is appended to the end of the command. '
'This option can be used multiple times'))
postproc.add_option(
@ -1785,14 +1786,14 @@ def create_parser():
'delim': None,
'process': lambda val: dict(_postprocessor_opts_parser(*val.split(':', 1))),
}, help=(
'The (case sensitive) name of plugin postprocessors to be enabled, '
'The (case-sensitive) name of plugin postprocessors to be enabled, '
'and (optionally) arguments to be passed to it, separated by a colon ":". '
'ARGS are a semicolon ";" delimited list of NAME=VALUE. '
'The "when" argument determines when the postprocessor is invoked. '
'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), '
'"post_process" (after each video download; default), '
'"after_move" (after moving video file to its final locations), '
'"after_move" (after moving the video file to its final location), '
'"after_video" (after downloading and processing all formats of a video), '
'or "playlist" (at end of playlist). '
'This option can be used multiple times to add different postprocessors'))
@ -1809,7 +1810,7 @@ def create_parser():
}, help=(
'SponsorBlock categories to create chapters for, separated by commas. '
f'Available categories are {", ".join(SponsorBlockPP.CATEGORIES.keys())}, all and default (=all). '
'You can prefix the category with a "-" to exclude it. See [1] for description of the categories. '
'You can prefix the category with a "-" to exclude it. See [1] for descriptions of the categories. '
'E.g. --sponsorblock-mark all,-preview [1] https://wiki.sponsor.ajay.app/w/Segment_Categories'))
sponsorblock.add_option(
'--sponsorblock-remove', metavar='CATS',
@ -1895,7 +1896,7 @@ def create_parser():
extractor.add_option(
'--no-hls-split-discontinuity',
dest='hls_split_discontinuity', action='store_false',
help='Do not split HLS playlists to different formats at discontinuities such as ad breaks (default)')
help='Do not split HLS playlists into different formats at discontinuities such as ad breaks (default)')
_extractor_arg_parser = lambda key, vals='': (key.strip().lower().replace('-', '_'), [
val.replace(r'\,', ',').strip() for val in re.split(r'(?<!\\),', vals)])
extractor.add_option(

@ -55,7 +55,7 @@ def traverse_obj(
The keys in the path can be one of:
- `None`: Return the current object.
- `set`: Requires the only item in the set to be a type or function,
like `{type}`/`{type, type, ...}/`{func}`. If a `type`, return only
like `{type}`/`{type, type, ...}`/`{func}`. If a `type`, return only
values of this type. If a function, returns `func(obj)`.
- `str`/`int`: Return `obj[key]`. For `re.Match`, return `obj.group(key)`.
- `slice`: Branch out and return all values in `obj[key]`.
@ -75,7 +75,7 @@ def traverse_obj(
`tuple`, `list`, and `dict` all support nested paths and branches.
@params paths Paths which to traverse by.
@params paths Paths by which to traverse.
@param default Value to return if the paths do not match.
If the last key in the path is a `dict`, it will apply to each value inside
the dict instead, depth first. Try to avoid if using nested `dict` keys.

Loading…
Cancel
Save