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}: diff --git a/tests/test_short.py b/tests/test_short.py index c20e5c9..e157cd0 100644 --- a/tests/test_short.py +++ b/tests/test_short.py @@ -1,6 +1,9 @@ """Tests for the YouTube client.""" +from typing import cast + import aiohttp +import aiohttp.web from aresponses import ResponsesMockServer from youtubeaio.youtube import YouTube @@ -35,7 +38,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 +50,21 @@ 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 cast("aiohttp.web.Response", 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()