Compare commits

..

53 commits

Author SHA1 Message Date
Mikel Olasagasti Uranga
8ba6ef58d2 Update to 2026.08.19 - Closes rhbz#2497100 rhbz#2505367 rhbz#2491888 rhbz#2491889 rhbz#2491890 rhbz#2499189 2026-08-20 16:36:38 +02:00
Fedora Release Engineering
c82ae71264 Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild 2026-07-17 09:36:02 +00:00
Maxwell G
de7ea94edf
Update to 2026.06.09. Fixes rhbz#2487407.
- Mitigates CVE-2026-50019, CVE-2026-50023, CVE-2026-50574
2026-06-18 21:24:05 -05:00
Maxwell G
5bb0cafe50
Add Python 3.15 support patches 2026-06-05 16:52:53 -05:00
Python Maint
a2500a4c99 Rebuilt for Python 3.15 2026-06-04 10:04:51 +02:00
Mikel Olasagasti Uranga
2afd4b2674 Update to 2026.03.17. Fixes rhbz#2448523. 2026-03-18 10:06:44 +01:00
Mikel Olasagasti Uranga
4ccc324af3 Update to 2026.03.13. Fixes rhbz#2447293. 2026-03-13 15:29:57 +01:00
Mikel Olasagasti Uranga
720f2742d0 Update to 2026.03.03. Fixes rhbz#2444129. 2026-03-05 11:59:09 +01:00
Maxwell G
61c2655717
Update to 2026.02.21. Fixes rhbz#2441709.
- Mitigates CVE-2026-26331 / GHSA-g3gw-q23r-pgqm (rhbz#2442244)
2026-02-24 12:17:20 -06:00
Dominik 'Rathann' Mierzejewski
dedd353fe7 fix FTBFS with python 3.14.3
This fixes a failing cookie test:
E           http.cookies.CookieError: Control characters are not allowed in cookies 'keebler' 'E=mc2; L="Loves"; fudge=\n;' '"E=mc2; L=\\"Loves\\"; fudge=\\012;"'

following python 3.14.3 change:
https://github.com/python/cpython/issues/143919
2026-02-21 20:23:23 +01:00
Maxwell G
4e4c19a028
Update to 2026.02.04. Fixes rhbz#2435740. 2026-02-09 17:36:57 -06:00
Maxwell G
13a1b07f32 Update to 2026.01.29. Fixes rhbz#2435358. 2026-01-29 21:30:10 -06:00
Fedora Release Engineering
682a4e8001 Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild 2026-01-17 21:03:29 +00:00
Maxwell G
f950e7eac2
Exclude optional yt-dlp-ejs dependency. Users need to handle it manually.
- https://github.com/yt-dlp/yt-dlp/wiki/EJS
- Remove python -s shebang flag so users can install yt-dlp-ejs or other
  optional deps or plugins using pip
2025-12-11 16:50:08 -06:00
Maxwell G
fab98c1246
Update to 2025.12.08. Fixes rhbz#2414329. 2025-12-08 19:38:32 -06:00
Maxwell G
fc98c68b40
Remove F41 patches
[skip changelog]
2025-12-08 19:38:32 -06:00
Maxwell G
f879056646
Add Provides for Obsoleted yt-dlp-*-completion packages
- Fixes: rhbz#2415381
2025-11-17 08:03:38 -06:00
Maxwell G
8fd3b0dce3
Update to 2025.10.22. Fixes rhbz#2405950. 2025-10-24 23:54:20 -05:00
Maxwell G
243086924c
Reformat specfile and remove separate spec license file
[skip changelog]
2025-10-24 23:48:26 -05:00
Maxwell G
2405ab343a
Remove separate shell completions packages
Separate shell completions packages are clunky, not typical in Fedora,
and not needed now that the filesystem package owns all the completions
directories.
2025-10-24 23:48:23 -05:00
Mikel Olasagasti Uranga
4ca3855bcf Update to 2025.09.26. Fixes rhbz#2397582 2025-09-28 21:59:41 +02:00
Python Maint
4884749aca Rebuilt for Python 3.14.0rc3 bytecode 2025-09-19 15:04:25 +02:00
Mikel Olasagasti Uranga
0296333d57 Update to 2025.09.05. Fixes rhbz#2393609 2025-09-06 23:16:20 +02:00
Mikel Olasagasti Uranga
01e9c71207 Update to 2025.08.27. Fixes rhbz#2391409. 2025-08-28 10:22:28 +02:00
Mikel Olasagasti Uranga
712d69fdd3 Update to 2025.08.22. Fixes rhbz#2389762 rhbz#2390971. 2025-08-26 13:31:07 +02:00
Python Maint
593bf58c62 Rebuilt for Python 3.14.0rc2 bytecode 2025-08-15 15:23:43 +02:00
Mikel Olasagasti Uranga
f134e3c17b Update to 2025.08.11. Fixes rhbz#2382547. 2025-08-11 12:19:16 +02:00
Mikel Olasagasti Uranga
892b44222a Update to 2025.07.21. Fixes rhbz#2382547. 2025-07-24 00:08:34 +02:00
Mikel Olasagasti Uranga
b72de101a6 Update to 2025.06.30. Fixes rhbz#2382272. 2025-07-23 20:11:30 +02:00
Maxwell G
5b33382a1e
Update to 2025.06.25. Fixes rhbz#2374869. 2025-06-26 16:46:24 -05:00
Maxwell G
45ad1b7f49
Fix build with Python 3.14 (rhbz#2345522) 2025-06-18 08:50:36 -05:00
Maxwell G
3567b90b25
Replace %{name} with yt-dlp
This makes the spec more readable.

[skip changelog]
2025-06-18 08:47:24 -05:00
Maxwell G
f9a69f22bc
Update to 2025.06.09. Fixes rhbz#2371359. 2025-06-11 12:39:14 -05:00
Python Maint
c2f2d74820 Rebuilt for Python 3.14 2025-06-03 16:10:00 +02:00
Maxwell G
2cf62665fb
Remove unneeded patch. Fedora 40 is EOL.
[skip changelog]
2025-05-23 22:27:21 -05:00
Maxwell G
8d6c81d86e
Update to 2025.05.22. Fixes rhbz#2363227. 2025-05-23 22:26:42 -05:00
Maxwell G
1b7ac92bf3
Update to 2025.04.30. 2025-04-30 19:45:57 -05:00
Maxwell G
a0bc985168
Update to 2025.03.31. Fixes rhbz#2355632. 2025-03-31 22:15:26 -05:00
Maxwell G
ce00940224
Update to 2025.03.26. Fixes rhbz#2354242. 2025-03-26 23:13:32 -05:00
Maxwell G
4ce1cf0f3f
Update to 2025.02.19. Fixes rhbz#2346539. 2025-02-22 15:48:36 -06:00
Mikel Olasagasti Uranga
1915cbb3ee Update to 2025.01.26 - Closes rhbz#2342116 2025-01-26 15:29:47 +01:00
Mikel Olasagasti Uranga
4f0432daaf Update to 2025.01.15 - Closes rhbz#2338387 2025-01-16 15:23:32 +01:00
Mikel Olasagasti Uranga
aef2688045 Update to 2025.01.12 - Closes rhbz#2337284 2025-01-13 09:44:08 +01:00
Maxwell G
b8d3a22583 Preserve compatibility with requests 2.31 for Fedora 40
[skip changelog]
2025-01-06 14:55:15 -06:00
Maxwell G
eacda9a5dd Properly handle yt-dlp extras subpackages 2025-01-06 14:55:15 -06:00
Maxwell G
6308364583 Recreate websockets 12 compat patch
Recreate the websockets 12 compat patch so it can be rebased and applied
in the normal way instead needing to use patch -R.

Also, remove the tomcli pyproject.toml version constraints command.
Brute forcing the version constraints in the pyproject.toml won't work,
as they are now also checked at runtime and the code itself needs to be
patched as well.

[skip changelog]
2025-01-06 14:55:15 -06:00
Mikel Olasagasti Uranga
473142dd7d Update to 2024.12.23 - Closes rhbz#2330257 2025-01-04 21:05:47 +01:00
Mikel Olasagasti Uranga
4b45f4c7c9 Convert to %autorelease and %autochangelog
[skip changelog]
2025-01-04 21:00:09 +01:00
Yaakov Selkowitz
d77c484ebe Fix EPEL build
Undefined macros result in a parser error unless they are ?-prefixed, and
%fedora is not guaranteed to be defined (as in EPEL builds).
2024-11-27 11:50:20 -05:00
Mikel Olasagasti Uranga
b43afeaccb Update to 2024.11.04. Fixes rhbz#2323783 2024-11-18 13:41:57 +01:00
Mikel Olasagasti Uranga
5d846b7f84 Update to 2024.11.04. Fixes rhbz#2323783 2024-11-05 12:23:03 +01:00
Mikel Olasagasti Uranga
0b309bc750 Update to 2024.10.22. Fixes rhbz#2321221 2024-10-23 09:57:35 +02:00
Maxwell G
1fa052bd17
Update to 2024.10.07. 2024-10-08 10:36:12 -05:00
8 changed files with 156 additions and 564 deletions

21
.gitignore vendored
View file

@ -53,3 +53,24 @@
/yt-dlp-2025.02.19.tar.gz
/yt-dlp-2025.03.26.tar.gz
/yt-dlp-2025.03.31.tar.gz
/yt-dlp-2025.04.30.tar.gz
/yt-dlp-2025.05.22.tar.gz
/yt-dlp-2025.06.09.tar.gz
/yt-dlp-2025.06.25.tar.gz
/yt-dlp-2025.06.30.tar.gz
/yt-dlp-2025.07.21.tar.gz
/yt-dlp-2025.08.11.tar.gz
/yt-dlp-2025.08.22.tar.gz
/yt-dlp-2025.08.27.tar.gz
/yt-dlp-2025.09.05.tar.gz
/yt-dlp-2025.09.26.tar.gz
/yt-dlp-2025.10.22.tar.gz
/yt-dlp-2025.12.08.tar.gz
/yt-dlp-2026.01.29.tar.gz
/yt-dlp-2026.02.04.tar.gz
/yt-dlp-2026.02.21.tar.gz
/yt-dlp-2026.03.03.tar.gz
/yt-dlp-2026.03.13.tar.gz
/yt-dlp-2026.03.17.tar.gz
/yt-dlp-2026.06.09.tar.gz
/yt-dlp-2026.08.19.tar.gz

View file

@ -1,119 +0,0 @@
From fbc486f4fac78bfd49011c1b60362e656c679c71 Mon Sep 17 00:00:00 2001
From: Maxwell G <maxwell@gtmx.me>
Date: Mon, 6 Jan 2025 14:37:58 -0600
Subject: [PATCH 1/2] Revert "[rh:websockets] Upgrade websockets to 13.0
(#10815)"
This reverts commit 6f9e6537434562d513d0c9b68ced8a61ade94a64.
---
.github/workflows/core.yml | 1 -
.github/workflows/quick-test.yml | 1 -
pyproject.toml | 2 +-
test/test_websockets.py | 8 ++++----
yt_dlp/networking/_websockets.py | 5 ++++-
5 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml
index 9a4342a58..bed3af94b 100644
--- a/.github/workflows/core.yml
+++ b/.github/workflows/core.yml
@@ -59,7 +59,6 @@ jobs:
- name: Install test requirements
run: python3 ./devscripts/install_deps.py --include test --include curl-cffi
- name: Run tests
- timeout-minutes: 15
continue-on-error: False
run: |
python3 -m yt_dlp -v || true # Print debug head
diff --git a/.github/workflows/quick-test.yml b/.github/workflows/quick-test.yml
index 1a32bbfe3..235a2c64d 100644
--- a/.github/workflows/quick-test.yml
+++ b/.github/workflows/quick-test.yml
@@ -17,7 +17,6 @@ jobs:
- name: Install test requirements
run: python3 ./devscripts/install_deps.py -o --include test
- name: Run tests
- timeout-minutes: 15
run: |
python3 -m yt_dlp -v || true
python3 ./devscripts/run_tests.py --pytest-args '--reruns 2 --reruns-delay 3.0' core
diff --git a/pyproject.toml b/pyproject.toml
index 5e987a6fd..6810415d1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,7 +52,7 @@ default = [
"pycryptodomex",
"requests>=2.32.2,<3",
"urllib3>=1.26.17,<3",
- "websockets>=13.0",
+ "websockets>=12.0",
]
curl-cffi = [
"curl-cffi>=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.11; implementation_name=='cpython'",
diff --git a/test/test_websockets.py b/test/test_websockets.py
index dead5fe5c..5e0ce48cc 100644
--- a/test/test_websockets.py
+++ b/test/test_websockets.py
@@ -88,7 +88,7 @@ def create_wss_websocket_server():
certfn = os.path.join(TEST_DIR, 'testcert.pem')
sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
sslctx.load_cert_chain(certfn, None)
- return create_websocket_server(ssl=sslctx)
+ return create_websocket_server(ssl_context=sslctx)
MTLS_CERT_DIR = os.path.join(TEST_DIR, 'testdata', 'certificate')
@@ -103,7 +103,7 @@ def create_mtls_wss_websocket_server():
sslctx.load_verify_locations(cafile=cacertfn)
sslctx.load_cert_chain(certfn, None)
- return create_websocket_server(ssl=sslctx)
+ return create_websocket_server(ssl_context=sslctx)
def create_legacy_wss_websocket_server():
@@ -112,7 +112,7 @@ def create_legacy_wss_websocket_server():
sslctx.maximum_version = ssl.TLSVersion.TLSv1_2
sslctx.set_ciphers('SHA1:AESCCM:aDSS:eNULL:aNULL')
sslctx.load_cert_chain(certfn, None)
- return create_websocket_server(ssl=sslctx)
+ return create_websocket_server(ssl_context=sslctx)
def ws_validate_and_send(rh, req):
@@ -139,7 +139,7 @@ def setup_class(cls):
cls.wss_thread, cls.wss_port = create_wss_websocket_server()
cls.wss_base_url = f'wss://127.0.0.1:{cls.wss_port}'
- cls.bad_wss_thread, cls.bad_wss_port = create_websocket_server(ssl=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER))
+ cls.bad_wss_thread, cls.bad_wss_port = create_websocket_server(ssl_context=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER))
cls.bad_wss_host = f'wss://127.0.0.1:{cls.bad_wss_port}'
cls.mtls_wss_thread, cls.mtls_wss_port = create_mtls_wss_websocket_server()
diff --git a/yt_dlp/networking/_websockets.py b/yt_dlp/networking/_websockets.py
index d29f8e45a..a2bdb9c82 100644
--- a/yt_dlp/networking/_websockets.py
+++ b/yt_dlp/networking/_websockets.py
@@ -48,7 +48,10 @@
# 2: "AttributeError: 'ClientConnection' object has no attribute 'recv_events_exc'. Did you mean: 'recv_events'?"
import websockets.sync.connection # isort: split
with contextlib.suppress(Exception):
+ # > 12.0
websockets.sync.connection.Connection.recv_exc = None
+ # 12.0
+ websockets.sync.connection.Connection.recv_events_exc = None
class WebsocketsResponseAdapter(WebSocketResponse):
@@ -162,7 +165,7 @@ def _send(self, request):
additional_headers=headers,
open_timeout=timeout,
user_agent_header=None,
- ssl=ssl_ctx if wsuri.secure else None,
+ ssl_context=ssl_ctx if wsuri.secure else None,
close_timeout=0, # not ideal, but prevents yt-dlp hanging
)
return WebsocketsResponseAdapter(conn, url=request.url)
--
2.48.1

View file

@ -0,0 +1,46 @@
From b958f102848b73edeec7bb92fff8d4d002ae4b23 Mon Sep 17 00:00:00 2001
From: Maxwell G <maxwell@gtmx.me>
Date: Thu, 18 Jun 2026 21:19:45 -0500
Subject: [PATCH 2/2] Restore compatibility with pytest < 9
---
pyproject.toml | 4 +---
test/test_downloader_external.py | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 8128fb3..8bb3631 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -467,14 +467,12 @@ select = [
]
exclude = "*/extractor/lazy_extractors.py,*venv*,*/test/testdata/sigs/player-*.js,.idea,.vscode"
-[tool.pytest]
+[tool.pytest.ini_options]
addopts = [
"-ra", # summary: all except passed
"--tb=short",
]
verbosity_test_cases = "1"
-strict_config = true
-strict_markers = true
markers = [
"download",
]
diff --git a/test/test_downloader_external.py b/test/test_downloader_external.py
index 0b51c96..d318d3c 100644
--- a/test/test_downloader_external.py
+++ b/test/test_downloader_external.py
@@ -132,7 +132,7 @@ class TestDownloaderCookieBehavior:
pytest.param(WgetFD, marks=pytest.mark.skipif(not WgetFD.available(), reason='wget unavailable')),
pytest.param(Aria2cFD, marks=pytest.mark.skipif(not Aria2cFD.available(), reason='aria2c unavailable')),
])
- def test_cookie_behavior(self, /, downloader_cls):
+ def test_cookie_behavior(self, downloader_cls):
with FakeYDL() as ydl:
downloader = downloader_cls(ydl, {})
--
2.54.0

View file

@ -1,369 +0,0 @@
From 86b38cb4301ee26dbea2c3ef1865d463aaf67721 Mon Sep 17 00:00:00 2001
From: Maxwell G <maxwell@gtmx.me>
Date: Mon, 6 Jan 2025 14:48:08 -0600
Subject: [PATCH 2/2] Revert "[rh:requests] Bump minimum `requests` version to
2.32.2 (#10079)"
This reverts commit db50f19d76c6870a5a13d0cab9287d684fd7449a.
---
pyproject.toml | 2 +-
yt_dlp/networking/_requests.py | 163 ++++++++++++++++++++++-----------
2 files changed, 109 insertions(+), 56 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 6810415d1..2c1d485d4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -50,7 +50,7 @@ default = [
"certifi",
"mutagen",
"pycryptodomex",
- "requests>=2.32.2,<3",
+ "requests>=2.31.0,<3",
"urllib3>=1.26.17,<3",
"websockets>=12.0",
]
diff --git a/yt_dlp/networking/_requests.py b/yt_dlp/networking/_requests.py
index 5b6b264a6..98c755d57 100644
--- a/yt_dlp/networking/_requests.py
+++ b/yt_dlp/networking/_requests.py
@@ -13,20 +13,21 @@
from ..utils.networking import normalize_url
if requests is None:
- raise ImportError('requests module is not installed')
+ raise ImportError("requests module is not installed")
if urllib3 is None:
- raise ImportError('urllib3 module is not installed')
+ raise ImportError("urllib3 module is not installed")
-urllib3_version = tuple(int_or_none(x, default=0) for x in urllib3.__version__.split('.'))
+urllib3_version = tuple(
+ int_or_none(x, default=0) for x in urllib3.__version__.split(".")
+)
if urllib3_version < (1, 26, 17):
- urllib3._yt_dlp__version = f'{urllib3.__version__} (unsupported)'
- raise ImportError('Only urllib3 >= 1.26.17 is supported')
+ urllib3._yt_dlp__version = f"{urllib3.__version__} (unsupported)"
+ raise ImportError("Only urllib3 >= 1.26.17 is supported")
-if requests.__build__ < 0x023202:
- requests._yt_dlp__version = f'{requests.__version__} (unsupported)'
- raise ImportError('Only requests >= 2.32.2 is supported')
+if requests.__build__ < 0x023100:
+ raise ImportError("Only requests >= 2.31.0 is supported")
import requests.adapters
import requests.utils
@@ -62,13 +63,14 @@
from ..socks import ProxyError as SocksProxyError
SUPPORTED_ENCODINGS = [
- 'gzip', 'deflate',
+ "gzip",
+ "deflate",
]
if brotli is not None:
- SUPPORTED_ENCODINGS.append('br')
+ SUPPORTED_ENCODINGS.append("br")
-'''
+"""
Override urllib3's behavior to not convert lower-case percent-encoded characters
to upper-case during url normalization process.
@@ -83,7 +85,7 @@
1: https://tools.ietf.org/html/rfc3986#section-2.1
2: https://github.com/streamlink/streamlink/pull/4003
-'''
+"""
class Urllib3PercentREOverride:
@@ -102,14 +104,19 @@ def subn(self, repl, string, *args, **kwargs):
# https://github.com/urllib3/urllib3/commit/a2697e7c6b275f05879b60f593c5854a816489f0
import urllib3.util.url
-if hasattr(urllib3.util.url, 'PERCENT_RE'):
+if hasattr(urllib3.util.url, "PERCENT_RE"):
urllib3.util.url.PERCENT_RE = Urllib3PercentREOverride(urllib3.util.url.PERCENT_RE)
-elif hasattr(urllib3.util.url, '_PERCENT_RE'): # urllib3 >= 2.0.0
- urllib3.util.url._PERCENT_RE = Urllib3PercentREOverride(urllib3.util.url._PERCENT_RE)
+elif hasattr(urllib3.util.url, "_PERCENT_RE"): # urllib3 >= 2.0.0
+ urllib3.util.url._PERCENT_RE = Urllib3PercentREOverride(
+ urllib3.util.url._PERCENT_RE
+ )
else:
- warnings.warn('Failed to patch PERCENT_RE in urllib3 (does the attribute exist?)' + bug_reports_message())
+ warnings.warn(
+ "Failed to patch PERCENT_RE in urllib3 (does the attribute exist?)"
+ + bug_reports_message()
+ )
-'''
+"""
Workaround for issue in urllib.util.ssl_.py: ssl_wrap_context does not pass
server_hostname to SSLContext.wrap_socket if server_hostname is an IP,
however this is an issue because we set check_hostname to True in our SSLContext.
@@ -118,7 +125,7 @@ def subn(self, repl, string, *args, **kwargs):
This has been fixed in urllib3 2.0+.
See: https://github.com/urllib3/urllib3/issues/517
-'''
+"""
if urllib3_version < (2, 0, 0):
with contextlib.suppress(Exception):
@@ -134,8 +141,12 @@ def subn(self, repl, string, *args, **kwargs):
class RequestsResponseAdapter(Response):
def __init__(self, res: requests.models.Response):
super().__init__(
- fp=res.raw, headers=res.headers, url=res.url,
- status=res.status_code, reason=res.reason)
+ fp=res.raw,
+ headers=res.headers,
+ url=res.url,
+ status=res.status_code,
+ reason=res.reason,
+ )
self._requests_response = res
@@ -152,12 +163,21 @@ def read(self, amt: int | None = None):
# IncompleteRead is always contained within ProtocolError
# See urllib3.response.HTTPResponse._error_catcher()
ir_err = next(
- (err for err in (e.__context__, e.__cause__, *variadic(e.args))
- if isinstance(err, http.client.IncompleteRead)), None)
+ (
+ err
+ for err in (e.__context__, e.__cause__, *variadic(e.args))
+ if isinstance(err, http.client.IncompleteRead)
+ ),
+ None,
+ )
if ir_err is not None:
# `urllib3.exceptions.IncompleteRead` is subclass of `http.client.IncompleteRead`
# but uses an `int` for its `partial` property.
- partial = ir_err.partial if isinstance(ir_err.partial, int) else len(ir_err.partial)
+ partial = (
+ ir_err.partial
+ if isinstance(ir_err.partial, int)
+ else len(ir_err.partial)
+ )
raise IncompleteRead(partial=partial, expected=ir_err.expected) from e
raise TransportError(cause=e) from e
@@ -167,12 +187,14 @@ def read(self, amt: int | None = None):
class RequestsHTTPAdapter(requests.adapters.HTTPAdapter):
- def __init__(self, ssl_context=None, proxy_ssl_context=None, source_address=None, **kwargs):
+ def __init__(
+ self, ssl_context=None, proxy_ssl_context=None, source_address=None, **kwargs
+ ):
self._pm_args = {}
if ssl_context:
- self._pm_args['ssl_context'] = ssl_context
+ self._pm_args["ssl_context"] = ssl_context
if source_address:
- self._pm_args['source_address'] = (source_address, 0)
+ self._pm_args["source_address"] = (source_address, 0)
self._proxy_ssl_context = proxy_ssl_context or ssl_context
super().__init__(**kwargs)
@@ -181,14 +203,21 @@ def init_poolmanager(self, *args, **kwargs):
def proxy_manager_for(self, proxy, **proxy_kwargs):
extra_kwargs = {}
- if not proxy.lower().startswith('socks') and self._proxy_ssl_context:
- extra_kwargs['proxy_ssl_context'] = self._proxy_ssl_context
- return super().proxy_manager_for(proxy, **proxy_kwargs, **self._pm_args, **extra_kwargs)
+ if not proxy.lower().startswith("socks") and self._proxy_ssl_context:
+ extra_kwargs["proxy_ssl_context"] = self._proxy_ssl_context
+ return super().proxy_manager_for(
+ proxy, **proxy_kwargs, **self._pm_args, **extra_kwargs
+ )
# Skip `requests` internal verification; we use our own SSLContext
+ # requests 2.31.0+
def cert_verify(*args, **kwargs):
pass
+ # requests 2.31.0-2.32.1
+ def _get_connection(self, request, *_, proxies=None, **__):
+ return self.get_connection(request.url, proxies)
+
# requests 2.32.2+: Reimplementation without `_urllib3_request_context`
def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
url = urllib3.util.parse_url(request.url).url
@@ -222,7 +251,7 @@ def rebuild_method(self, prepared_request, response):
def rebuild_auth(self, prepared_request, response):
# HACK: undo status code change from rebuild_method, if applicable.
# rebuild_auth runs after requests would remove headers/body based on status code
- if hasattr(response, '_real_status_code'):
+ if hasattr(response, "_real_status_code"):
response.status_code = response._real_status_code
del response._real_status_code
return super().rebuild_auth(prepared_request, response)
@@ -256,23 +285,30 @@ def emit(self, record):
@register_rh
class RequestsRH(RequestHandler, InstanceStoreMixin):
-
"""Requests RequestHandler
https://github.com/psf/requests
"""
- _SUPPORTED_URL_SCHEMES = ('http', 'https')
+
+ _SUPPORTED_URL_SCHEMES = ("http", "https")
_SUPPORTED_ENCODINGS = tuple(SUPPORTED_ENCODINGS)
- _SUPPORTED_PROXY_SCHEMES = ('http', 'https', 'socks4', 'socks4a', 'socks5', 'socks5h')
+ _SUPPORTED_PROXY_SCHEMES = (
+ "http",
+ "https",
+ "socks4",
+ "socks4a",
+ "socks5",
+ "socks5h",
+ )
_SUPPORTED_FEATURES = (Features.NO_PROXY, Features.ALL_PROXY)
- RH_NAME = 'requests'
+ RH_NAME = "requests"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Forward urllib3 debug messages to our logger
- logger = logging.getLogger('urllib3')
+ logger = logging.getLogger("urllib3")
self.__logging_handler = Urllib3LoggingHandler(logger=self._logger)
- self.__logging_handler.setFormatter(logging.Formatter('requests: %(message)s'))
+ self.__logging_handler.setFormatter(logging.Formatter("requests: %(message)s"))
self.__logging_handler.addFilter(Urllib3LoggingFilter())
logger.addHandler(self.__logging_handler)
# TODO: Use a logger filter to suppress pool reuse warning instead
@@ -291,14 +327,14 @@ def close(self):
self._clear_instances()
# Remove the logging handler that contains a reference to our logger
# See: https://github.com/yt-dlp/yt-dlp/issues/8922
- logging.getLogger('urllib3').removeHandler(self.__logging_handler)
+ logging.getLogger("urllib3").removeHandler(self.__logging_handler)
def _check_extensions(self, extensions):
super()._check_extensions(extensions)
- extensions.pop('cookiejar', None)
- extensions.pop('timeout', None)
- extensions.pop('legacy_ssl', None)
- extensions.pop('keep_header_casing', None)
+ extensions.pop("cookiejar", None)
+ extensions.pop("timeout", None)
+ extensions.pop("legacy_ssl", None)
+ extensions.pop("keep_header_casing", None)
def _create_instance(self, cookiejar, legacy_ssl_support=None):
session = RequestsSession()
@@ -308,9 +344,11 @@ def _create_instance(self, cookiejar, legacy_ssl_support=None):
max_retries=urllib3.util.retry.Retry(False),
)
session.adapters.clear()
- session.headers = requests.models.CaseInsensitiveDict({'Connection': 'keep-alive'})
- session.mount('https://', http_adapter)
- session.mount('http://', http_adapter)
+ session.headers = requests.models.CaseInsensitiveDict(
+ {"Connection": "keep-alive"}
+ )
+ session.mount("https://", http_adapter)
+ session.mount("http://", http_adapter)
session.cookies = cookiejar
session.trust_env = False # no need, we already load proxies from env
return session
@@ -325,7 +363,7 @@ def _send(self, request):
session = self._get_instance(
cookiejar=self._get_cookiejar(request),
- legacy_ssl_support=request.extensions.get('legacy_ssl'),
+ legacy_ssl_support=request.extensions.get("legacy_ssl"),
)
try:
@@ -345,7 +383,7 @@ def _send(self, request):
requests_res = e.response
except requests.exceptions.SSLError as e:
- if 'CERTIFICATE_VERIFY_FAILED' in str(e):
+ if "CERTIFICATE_VERIFY_FAILED" in str(e):
raise CertificateVerifyError(cause=e) from e
raise SSLError(cause=e) from e
@@ -378,26 +416,33 @@ def requests_preference(rh, request):
# Use our socks proxy implementation with requests to avoid an extra dependency.
class SocksHTTPConnection(urllib3.connection.HTTPConnection):
- def __init__(self, _socks_options, *args, **kwargs): # must use _socks_options to pass PoolKey checks
+ def __init__(
+ self, _socks_options, *args, **kwargs
+ ): # must use _socks_options to pass PoolKey checks
self._proxy_args = _socks_options
super().__init__(*args, **kwargs)
def _new_conn(self):
try:
return create_connection(
- address=(self._proxy_args['addr'], self._proxy_args['port']),
+ address=(self._proxy_args["addr"], self._proxy_args["port"]),
timeout=self.timeout,
source_address=self.source_address,
_create_socket_func=functools.partial(
- create_socks_proxy_socket, (self.host, self.port), self._proxy_args))
+ create_socks_proxy_socket, (self.host, self.port), self._proxy_args
+ ),
+ )
except (socket.timeout, TimeoutError) as e:
raise urllib3.exceptions.ConnectTimeoutError(
- self, f'Connection to {self.host} timed out. (connect timeout={self.timeout})') from e
+ self,
+ f"Connection to {self.host} timed out. (connect timeout={self.timeout})",
+ ) from e
except SocksProxyError as e:
raise urllib3.exceptions.ProxyError(str(e), e) from e
except OSError as e:
raise urllib3.exceptions.NewConnectionError(
- self, f'Failed to establish a new connection: {e}') from e
+ self, f"Failed to establish a new connection: {e}"
+ ) from e
class SocksHTTPSConnection(SocksHTTPConnection, urllib3.connection.HTTPSConnection):
@@ -414,12 +459,20 @@ class SocksHTTPSConnectionPool(urllib3.HTTPSConnectionPool):
class SocksProxyManager(urllib3.PoolManager):
- def __init__(self, socks_proxy, username=None, password=None, num_pools=10, headers=None, **connection_pool_kw):
- connection_pool_kw['_socks_options'] = make_socks_proxy_opts(socks_proxy)
+ def __init__(
+ self,
+ socks_proxy,
+ username=None,
+ password=None,
+ num_pools=10,
+ headers=None,
+ **connection_pool_kw,
+ ):
+ connection_pool_kw["_socks_options"] = make_socks_proxy_opts(socks_proxy)
super().__init__(num_pools, headers, **connection_pool_kw)
self.pool_classes_by_scheme = {
- 'http': SocksHTTPConnectionPool,
- 'https': SocksHTTPSConnectionPool,
+ "http": SocksHTTPConnectionPool,
+ "https": SocksHTTPSConnectionPool,
}
--
2.48.1

45
17491.patch Normal file
View file

@ -0,0 +1,45 @@
From b6ef4e25fe882a82c02c12ca716f19fb3c1b89f3 Mon Sep 17 00:00:00 2001
From: Mikel Olasagasti Uranga <mikel@olasagasti.info>
Date: Thu, 20 Aug 2026 16:23:27 +0200
Subject: [PATCH] tests: accept newer OpenSSL handshake error wording
Newer Python/OpenSSL combinations may report TLS handshake failures as
"TLS_ALERT_HANDSHAKE_FAILURE" / "tls alert handshake failure" instead
of the older "sslv3 alert handshake failure" wording expected by the
test suite.
Relax the test regexes to accept both forms without changing the
behavior being tested.
Signed-off-by: Mikel Olasagasti Uranga <mikel@olasagasti.info>
---
test/test_networking.py | 2 +-
test/test_websockets.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/test_networking.py b/test/test_networking.py
index 9dbe263b6fcd..57a7a21bd14b 100644
--- a/test/test_networking.py
+++ b/test/test_networking.py
@@ -338,7 +338,7 @@ def test_ssl_error(self, handler):
https_server_thread.start()
with handler(verify=False) as rh:
- with pytest.raises(SSLError, match=r'(?i)ssl(?:v3|/tls).alert.handshake.failure') as exc_info:
+ with pytest.raises(SSLError, match=r'(?i)(?:TLS_ALERT_HANDSHAKE_FAILURE|(?:sslv3|ssl/tls|tls).alert.handshake.failure)') as exc_info:
validate_and_send(rh, Request(f'https://127.0.0.1:{https_port}/headers'))
assert not issubclass(exc_info.type, CertificateVerifyError)
diff --git a/test/test_websockets.py b/test/test_websockets.py
index caa026f44f3a..3b3814018eeb 100644
--- a/test/test_websockets.py
+++ b/test/test_websockets.py
@@ -186,7 +186,7 @@ def test_verify_cert(self, handler):
def test_ssl_error(self, handler):
with handler(verify=False) as rh:
- with pytest.raises(SSLError, match=r'ssl(?:v3|/tls) alert handshake failure') as exc_info:
+ with pytest.raises(SSLError, match=r'(?:TLS_ALERT_HANDSHAKE_FAILURE|(?:sslv3|ssl/tls|tls) alert handshake failure)') as exc_info:
ws_validate_and_send(rh, Request(self.bad_wss_host))
assert not issubclass(exc_info.type, CertificateVerifyError)

View file

@ -1 +1 @@
SHA512 (yt-dlp-2025.03.31.tar.gz) = 3ac5e5d13e3d1d4965ec398901bea158f451e0aed3600dace706e9e1a8f89580349fe1249c76fe097966c8254eb1cc526075138fc16ea29cf933ab0019cfaf27
SHA512 (yt-dlp-2026.08.19.tar.gz) = 04f2dae28fe2b087fed3df38fc6787eac8f85a351bd72f3b010a84fb48bccf9bfde3a50ca4c005afb384f1219ee66a4cab24e5740eb1e5b8eb1a0d8ab773bafd

View file

@ -2,35 +2,30 @@
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: Fedora Project Authors
# SPDX-FileCopyrightText: 2022 Maxwell G <gotmax@e.email>
# See %%{name}.spec.license for the full license text.
# License text: https://spdx.org/licenses/MIT.html
%bcond_without tests
Name: yt-dlp
Version: 2025.03.31
Version: 2026.08.19
Release: %autorelease
Summary: A command-line program to download videos from online video platforms
License: Unlicense
URL: https://github.com/%{name}/%{name}
Source: %{url}/archive/%{version}/%{name}-%{version}.tar.gz
# License of the specfile
Source: yt-dlp.spec.license
URL: https://github.com/yt-dlp/yt-dlp
Source: %{url}/archive/%{version}/yt-dlp-%{version}.tar.gz
# https://github.com/yt-dlp/yt-dlp/commit/6f9e6537434562d513d0c9b68ced8a61ade94a64
# [rh:websockets] Upgrade websockets to 13.0 (#10815)
# Revert this patch for compatibility with older Fedora versions
# This patch is applied conditionally to Fedora <= 41
Patch1000: 0001-Revert-rh-websockets-Upgrade-websockets-to-13.0-1081.patch
# https://github.com/yt-dlp/yt-dlp/pull/17491
# Relax handshake error regexp
Patch: 17491.patch
# Revert "[rh:requests] Bump minimum `requests` version to 2.32.2 (#10079)"
# Revert this patch for compatibility with older Fedora versions
# This patch is applied conditionally to Fedora <= 40
Patch1001: 0002-Revert-rh-requests-Bump-minimum-requests-version-to-.patch
# Needed for compatibility with Fedora <= 44
Patch: 0002-Restore-compatibility-with-pytest-9.patch
BuildArch: noarch
BuildRequires: python3-devel
BuildRequires: tomcli
%if %{with tests}
# Needed for %%check
@ -49,58 +44,42 @@ Recommends: /usr/bin/ffprobe
Suggests: python3dist(keyring)
%global _description %{expand:
# START: Remove in Fedora 46
Provides: yt-dlp-bash-completion = %{version}-%{release}
Obsoletes: yt-dlp-bash-completion < 2025.09.26-2
Provides: yt-dlp-fish-completion = %{version}-%{release}
Obsoletes: yt-dlp-fish-completion < 2025.09.26-2
Provides: yt-dlp-zsh-completion = %{version}-%{release}
Obsoletes: yt-dlp-zsh-completion < 2025.09.26-2
# END: Remove in Fedora 46
%description
yt-dlp is a command-line program to download videos from many different online
video platforms, such as youtube.com. The project is a fork of youtube-dl with
additional features and fixes.}
additional features and fixes.
%description %{_description}
%package bash-completion
Summary: Bash completion for %{name}
Requires: %{name} = %{version}-%{release}
Requires: bash-completion
Supplements: (%{name} and bash-completion)
%description bash-completion
Bash command line completion support for %{name}.
%package zsh-completion
Summary: Zsh completion for %{name}
Requires: %{name} = %{version}-%{release}
Requires: zsh
Supplements: (%{name} and zsh)
%description zsh-completion
Zsh command line completion support for %{name}.
%package fish-completion
Summary: Fish completion for %{name}
Requires: %{name} = %{version}-%{release}
Requires: fish
Supplements: (%{name} and fish)
%description fish-completion
Fish command line completion support for %{name}.
%prep
%autosetup -N
%autopatch -M 999 -p1
%if %{defined fedora} && 0%{?fedora} <= 41
# Revert patch for compatibility with older websockets
%autopatch 1000 -p1
%endif
%if %{defined fedora} && 0%{?fedora} <= 40
# Revert patch for compatibility with older requests
%autopatch 1001 -p1
%endif
%autosetup -p1
# TODO: Figure out https://bugzilla.redhat.com/show_bug.cgi?id=2405578
# Users can configure JS challenges manually for now.
tomcli set pyproject.toml arrays delitem \
project.optional-dependencies.default 'yt-dlp-ejs.*'
# Remove -s from shebang so users can install extra deps or plugins (or yt-dlp-ejs) using pip.
%undefine _py3_shebang_s
# Remove unnecessary shebangs
find -type f ! -executable -name '*.py' -print -exec sed -i -e '1{\@^#!.*@d}' '{}' +
%generate_buildrequires
%pyproject_buildrequires -x default,secretstorage
%build
# Docs and shell completions
make yt-dlp.1 completion-bash completion-zsh completion-fish
@ -108,32 +87,30 @@ make yt-dlp.1 completion-bash completion-zsh completion-fish
# Docs and shell completions are also included in the wheel.
%pyproject_wheel
%install
%pyproject_install
%pyproject_save_files yt_dlp
%check
%if %{with tests}
# See https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/run_tests.sh
%pytest -k "not download and not test_verify_cert[Websockets]"
%endif
%files -f %{pyproject_files}
%{_bindir}/%{name}
%{_mandir}/man1/%{name}.1*
%doc README.md
%license LICENSE
%{_bindir}/yt-dlp
%{_mandir}/man1/yt-dlp.1*
%{bash_completions_dir}/yt-dlp
%{fish_completions_dir}/yt-dlp.fish
%{zsh_completions_dir}/_yt-dlp
%files bash-completion
%{bash_completions_dir}/%{name}
%files zsh-completion
%{zsh_completions_dir}/_%{name}
%files fish-completion
%{fish_completions_dir}/%{name}.fish
%pyproject_extras_subpkg -n yt-dlp default secretstorage
%changelog
%autochangelog

View file

@ -1,9 +0,0 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.