Skip to content

Commit 7e8eec8

Browse files
Add is_short method (#580)
Co-authored-by: Joost Lekkerkerker <[email protected]>
1 parent dd13e58 commit 7e8eec8

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Changelog = "https://github.com/joostlek/python-youtube/releases"
5353

5454
[tool.coverage.report]
5555
show_missing = true
56+
fail_under = 80
5657

5758
[tool.coverage.run]
5859
plugins = ["covdefaults"]

src/youtubeaio/youtube.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(
7777
],
7878
] = {
7979
"get": self._api_get_request,
80+
"head": self._api_head_request,
8081
}
8182

8283
async def _check_request_return(self, response: ClientResponse) -> ClientResponse:
@@ -115,6 +116,18 @@ async def _api_get_request(
115116
response = await session.get(url, headers=headers, json=data)
116117
return await self._check_request_return(response)
117118

119+
async def _api_head_request(
120+
self,
121+
session: ClientSession,
122+
url: str,
123+
data: dict[str, Any] | None = None,
124+
) -> ClientResponse:
125+
"""Make HEAD request with authorization."""
126+
headers = {"Authorization": f"Bearer {self._user_auth_token}"}
127+
self.logger.debug("making HEAD request to %s", url)
128+
response = await session.head(url, headers=headers, json=data)
129+
return await self._check_request_return(response)
130+
118131
async def _build_generator(
119132
self,
120133
req: str,
@@ -284,6 +297,20 @@ async def get_playlist_items(
284297
):
285298
yield item # type: ignore[misc]
286299

300+
async def is_short(self, video_id: str) -> bool:
301+
"""Return True if the video ID corresponds to a YouTube Short."""
302+
_url = "https://www.youtube.com/shorts/" + video_id
303+
if not self.session:
304+
self.session = ClientSession()
305+
self._close_session = True
306+
async with asyncio.timeout(self.session_timeout):
307+
response = await self._api_head_request(self.session, _url)
308+
if response.status == 200:
309+
return True
310+
if response.status == 303:
311+
return False
312+
raise YouTubeAPIError
313+
287314
async def close(self) -> None:
288315
"""Close open client session."""
289316
if self.session and self._close_session:

tests/test_short.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Tests for the YouTube client."""
2+
3+
import aiohttp
4+
from aresponses import ResponsesMockServer
5+
6+
from youtubeaio.youtube import YouTube
7+
8+
9+
async def test_is_short(
10+
aresponses: ResponsesMockServer,
11+
) -> None:
12+
"""Test YouTube video id that is a short."""
13+
aresponses.add(
14+
"www.youtube.com",
15+
"/shorts/2YUPfsi8PF4",
16+
"HEAD",
17+
aresponses.Response(
18+
status=200,
19+
headers={"Content-Type": "text/html"},
20+
),
21+
)
22+
async with aiohttp.ClientSession() as session:
23+
youtube = YouTube(session=session)
24+
response = await youtube.is_short("2YUPfsi8PF4")
25+
assert response
26+
await youtube.close()
27+
28+
29+
async def test_is_not_short(
30+
aresponses: ResponsesMockServer,
31+
) -> None:
32+
"""Test YouTube video id that is not a short."""
33+
aresponses.add(
34+
"www.youtube.com",
35+
"/shorts/KXaMtA6kWXU",
36+
"HEAD",
37+
aresponses.Response(
38+
status=303,
39+
headers={
40+
"Content-Type": "application/binary",
41+
"Location": "https://www.youtube.com/watch?v=KXaMtA6kWXU",
42+
},
43+
),
44+
)
45+
async with aiohttp.ClientSession() as session:
46+
youtube = YouTube(session=session)
47+
response = await youtube.is_short("KXaMtA6kWXU")
48+
assert not response
49+
await youtube.close()

0 commit comments

Comments
 (0)