Skip to content

Commit e68f1fd

Browse files
authored
Add method to get highest quality thumbnail (#56)
1 parent 3219c18 commit e68f1fd

File tree

4 files changed

+137
-5
lines changed

4 files changed

+137
-5
lines changed

src/youtubeaio/models.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,18 @@ class YouTubeVideoThumbnails(BaseModel):
3333
"""Model representing video thumbnails."""
3434

3535
default: YouTubeThumbnail = Field(...)
36-
medium: YouTubeThumbnail = Field(...)
37-
high: YouTubeThumbnail = Field(...)
38-
standard: YouTubeThumbnail = Field(...)
36+
medium: YouTubeThumbnail | None = Field(None)
37+
high: YouTubeThumbnail | None = Field(None)
38+
standard: YouTubeThumbnail | None = Field(None)
3939
maxres: YouTubeThumbnail | None = Field(None)
4040

41+
def get_highest_quality(self) -> YouTubeThumbnail:
42+
"""Return the highest quality thumbnail."""
43+
for size in (self.maxres, self.standard, self.high, self.medium):
44+
if size is not None:
45+
return size
46+
return self.default
47+
4148

4249
class YouTubeVideoSnippet(BaseModel):
4350
"""Model representing video snippet."""
@@ -68,8 +75,15 @@ class YouTubeChannelThumbnails(BaseModel):
6875
"""Model representing channel thumbnails."""
6976

7077
default: YouTubeThumbnail = Field(...)
71-
medium: YouTubeThumbnail = Field(...)
72-
high: YouTubeThumbnail = Field(...)
78+
medium: YouTubeThumbnail | None = Field(None)
79+
high: YouTubeThumbnail | None = Field(None)
80+
81+
def get_highest_quality(self) -> YouTubeThumbnail:
82+
"""Return the highest quality thumbnail."""
83+
for size in (self.high, self.medium):
84+
if size is not None:
85+
return size
86+
return self.default
7387

7488

7589
class YouTubeChannelRelatedPlaylists(BaseModel):

tests/helper.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Test helpers."""
2+
from youtubeaio.models import YouTubeThumbnail
3+
4+
5+
def get_thumbnail(resolution: str) -> YouTubeThumbnail:
6+
"""Return mock thumbnail with resolution as url."""
7+
return YouTubeThumbnail(
8+
url=resolution,
9+
width=100,
10+
height=100,
11+
)

tests/test_channel.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import pytest
66
from aresponses import ResponsesMockServer
77

8+
from youtubeaio.models import YouTubeChannelThumbnails
89
from youtubeaio.youtube import YouTube
910

1011
from . import load_fixture
1112
from .const import YOUTUBE_URL
13+
from .helper import get_thumbnail
1214

1315

1416
async def test_fetch_channel(
@@ -82,3 +84,40 @@ async def test_fetch_own_channel(
8284
with pytest.raises(StopAsyncIteration):
8385
await channel_generator.__anext__()
8486
await youtube.close()
87+
88+
89+
@pytest.mark.parametrize(
90+
("thumbnails", "result_url"),
91+
[
92+
(
93+
YouTubeChannelThumbnails(
94+
high=get_thumbnail("high"),
95+
medium=get_thumbnail("medium"),
96+
default=get_thumbnail("default"),
97+
),
98+
"high",
99+
),
100+
(
101+
YouTubeChannelThumbnails(
102+
high=None,
103+
medium=get_thumbnail("medium"),
104+
default=get_thumbnail("default"),
105+
),
106+
"medium",
107+
),
108+
(
109+
YouTubeChannelThumbnails(
110+
high=None,
111+
medium=None,
112+
default=get_thumbnail("default"),
113+
),
114+
"default",
115+
),
116+
],
117+
)
118+
async def test_get_hq_thumbnail(
119+
thumbnails: YouTubeChannelThumbnails,
120+
result_url: str,
121+
) -> None:
122+
"""Check if the highest quality thumbnail is returned."""
123+
assert thumbnails.get_highest_quality().url == result_url

tests/test_video.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
from aresponses import Response, ResponsesMockServer
88

99
from youtubeaio.helper import first
10+
from youtubeaio.models import YouTubeVideoThumbnails
1011
from youtubeaio.youtube import YouTube
1112

1213
from . import load_fixture
1314
from .const import YOUTUBE_URL
15+
from .helper import get_thumbnail
1416

1517

1618
async def test_fetch_video(
@@ -74,18 +76,21 @@ async def test_fetch_video(
7476
)
7577
assert video.snippet.thumbnails.default.width == 120
7678
assert video.snippet.thumbnails.default.height == 90
79+
assert video.snippet.thumbnails.medium
7780
assert (
7881
video.snippet.thumbnails.medium.url
7982
== "https://i.ytimg.com/vi/Ks-_Mh1QhMc/mqdefault.jpg"
8083
)
8184
assert video.snippet.thumbnails.medium.width == 320
8285
assert video.snippet.thumbnails.medium.height == 180
86+
assert video.snippet.thumbnails.high
8387
assert (
8488
video.snippet.thumbnails.high.url
8589
== "https://i.ytimg.com/vi/Ks-_Mh1QhMc/hqdefault.jpg"
8690
)
8791
assert video.snippet.thumbnails.high.width == 480
8892
assert video.snippet.thumbnails.high.height == 360
93+
assert video.snippet.thumbnails.standard
8994
assert (
9095
video.snippet.thumbnails.standard.url
9196
== "https://i.ytimg.com/vi/Ks-_Mh1QhMc/sddefault.jpg"
@@ -173,3 +178,66 @@ async def test_fetch_no_videos() -> None:
173178
youtube = YouTube()
174179
with pytest.raises(ValueError):
175180
await first(youtube.get_videos(video_ids=[]))
181+
182+
183+
@pytest.mark.parametrize(
184+
("thumbnails", "result_url"),
185+
[
186+
(
187+
YouTubeVideoThumbnails(
188+
maxres=get_thumbnail("maxres"),
189+
standard=get_thumbnail("standard"),
190+
high=get_thumbnail("high"),
191+
medium=get_thumbnail("medium"),
192+
default=get_thumbnail("default"),
193+
),
194+
"maxres",
195+
),
196+
(
197+
YouTubeVideoThumbnails(
198+
maxres=None,
199+
standard=get_thumbnail("standard"),
200+
high=get_thumbnail("high"),
201+
medium=get_thumbnail("medium"),
202+
default=get_thumbnail("default"),
203+
),
204+
"standard",
205+
),
206+
(
207+
YouTubeVideoThumbnails(
208+
maxres=None,
209+
standard=None,
210+
high=get_thumbnail("high"),
211+
medium=get_thumbnail("medium"),
212+
default=get_thumbnail("default"),
213+
),
214+
"high",
215+
),
216+
(
217+
YouTubeVideoThumbnails(
218+
maxres=None,
219+
standard=None,
220+
high=None,
221+
medium=get_thumbnail("medium"),
222+
default=get_thumbnail("default"),
223+
),
224+
"medium",
225+
),
226+
(
227+
YouTubeVideoThumbnails(
228+
maxres=None,
229+
standard=None,
230+
high=None,
231+
medium=None,
232+
default=get_thumbnail("default"),
233+
),
234+
"default",
235+
),
236+
],
237+
)
238+
async def test_get_hq_thumbnail(
239+
thumbnails: YouTubeVideoThumbnails,
240+
result_url: str,
241+
) -> None:
242+
"""Check if the highest quality thumbnail is returned."""
243+
assert thumbnails.get_highest_quality().url == result_url

0 commit comments

Comments
 (0)