From f7599ecad54f678d7027a96c990cb77610935516 Mon Sep 17 00:00:00 2001 From: DevHugo Date: Mon, 16 Mar 2026 21:49:06 +0000 Subject: [PATCH 1/5] fix(is_short): use curl User-Agent to avoid GDPR consent redirects --- src/youtubeaio/youtube.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/youtubeaio/youtube.py b/src/youtubeaio/youtube.py index 0870725..d0e488f 100644 --- a/src/youtubeaio/youtube.py +++ b/src/youtubeaio/youtube.py @@ -304,7 +304,11 @@ async def is_short(self, video_id: str) -> bool: self.session = ClientSession() self._close_session = True async with asyncio.timeout(self.session_timeout): - response = await self._api_head_request(self.session, _url) + response = await self.session.head( + _url, + allow_redirects=False, + headers={"User-Agent": "curl/x.x"}, + ) if response.status == 200: return True if response.status in {303, 302, 301}: From c98feb775b3528a2e214e7a382c70066e62821b9 Mon Sep 17 00:00:00 2001 From: DevHugo Date: Wed, 18 Mar 2026 08:00:12 +0000 Subject: [PATCH 2/5] test(youtubeaio): update is_short tests for 302 and curl User-Agent --- tests/test_short.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_short.py b/tests/test_short.py index c20e5c9..6ecb9d1 100644 --- a/tests/test_short.py +++ b/tests/test_short.py @@ -1,6 +1,7 @@ """Tests for the YouTube client.""" import aiohttp +import aiohttp.web from aresponses import ResponsesMockServer from youtubeaio.youtube import YouTube @@ -35,7 +36,7 @@ async def test_is_not_short( "/shorts/KXaMtA6kWXU", "HEAD", aresponses.Response( - status=303, + status=302, headers={ "Content-Type": "application/binary", "Location": "https://www.youtube.com/watch?v=KXaMtA6kWXU", @@ -47,3 +48,23 @@ async def test_is_not_short( response = await youtube.is_short("KXaMtA6kWXU") assert not response await youtube.close() + + +async def test_is_short_sends_curl_user_agent( + aresponses: ResponsesMockServer, +) -> None: + """Test that is_short sends a curl User-Agent to avoid GDPR consent redirects.""" + + async def _assert_user_agent( + request: aiohttp.web.Request, + ) -> aiohttp.web.Response: + assert request.headers.get("User-Agent") == "curl/x.x" + return aresponses.Response(status=200) + + aresponses.add( + "www.youtube.com", "/shorts/2YUPfsi8PF4", "HEAD", _assert_user_agent + ) + async with aiohttp.ClientSession() as session: + youtube = YouTube(session=session) + await youtube.is_short("2YUPfsi8PF4") + await youtube.close() \ No newline at end of file From 5b15f9ea495df5fc6da2b80af1e282d50ada285a Mon Sep 17 00:00:00 2001 From: DevHugo Date: Mon, 23 Mar 2026 22:08:43 +0000 Subject: [PATCH 3/5] Format: ruff reported issue --- tests/test_short.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_short.py b/tests/test_short.py index 6ecb9d1..95377ab 100644 --- a/tests/test_short.py +++ b/tests/test_short.py @@ -54,17 +54,16 @@ async def test_is_short_sends_curl_user_agent( aresponses: ResponsesMockServer, ) -> None: """Test that is_short sends a curl User-Agent to avoid GDPR consent redirects.""" - async def _assert_user_agent( request: aiohttp.web.Request, ) -> aiohttp.web.Response: assert request.headers.get("User-Agent") == "curl/x.x" return aresponses.Response(status=200) - + aresponses.add( "www.youtube.com", "/shorts/2YUPfsi8PF4", "HEAD", _assert_user_agent ) async with aiohttp.ClientSession() as session: youtube = YouTube(session=session) await youtube.is_short("2YUPfsi8PF4") - await youtube.close() \ No newline at end of file + await youtube.close() From a68df8bf38dc925c308d7a20b8529c3aa417d830 Mon Sep 17 00:00:00 2001 From: DevHugo Date: Mon, 23 Mar 2026 22:17:49 +0000 Subject: [PATCH 4/5] FIx: mypy errors --- tests/test_short.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_short.py b/tests/test_short.py index 95377ab..617646f 100644 --- a/tests/test_short.py +++ b/tests/test_short.py @@ -1,5 +1,7 @@ """Tests for the YouTube client.""" +from typing import cast + import aiohttp import aiohttp.web from aresponses import ResponsesMockServer @@ -58,7 +60,7 @@ async def _assert_user_agent( request: aiohttp.web.Request, ) -> aiohttp.web.Response: assert request.headers.get("User-Agent") == "curl/x.x" - return aresponses.Response(status=200) + return cast("aiohttp.web.Response", aresponses.Response(status=200)) aresponses.add( "www.youtube.com", "/shorts/2YUPfsi8PF4", "HEAD", _assert_user_agent From 58d951b10e710b1a8413a1d7f61eecd669b0c446 Mon Sep 17 00:00:00 2001 From: DevHugo Date: Tue, 24 Mar 2026 09:31:00 +0000 Subject: [PATCH 5/5] Fix ruff formating --- tests/test_short.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_short.py b/tests/test_short.py index 617646f..e157cd0 100644 --- a/tests/test_short.py +++ b/tests/test_short.py @@ -56,15 +56,14 @@ async def test_is_short_sends_curl_user_agent( aresponses: ResponsesMockServer, ) -> None: """Test that is_short sends a curl User-Agent to avoid GDPR consent redirects.""" + async def _assert_user_agent( request: aiohttp.web.Request, ) -> aiohttp.web.Response: assert request.headers.get("User-Agent") == "curl/x.x" return cast("aiohttp.web.Response", aresponses.Response(status=200)) - aresponses.add( - "www.youtube.com", "/shorts/2YUPfsi8PF4", "HEAD", _assert_user_agent - ) + aresponses.add("www.youtube.com", "/shorts/2YUPfsi8PF4", "HEAD", _assert_user_agent) async with aiohttp.ClientSession() as session: youtube = YouTube(session=session) await youtube.is_short("2YUPfsi8PF4")