Skip to content

Commit 3120073

Browse files
committed
move watch playlist normalization into model
1 parent 7782286 commit 3120073

4 files changed

Lines changed: 18 additions & 51 deletions

File tree

fuo_ytmusic/models.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,17 @@ def v2_model(self) -> SongModelV2:
236236

237237
class YtmusicWatchPlaylistSong(YtmusicSearchSong):
238238
year: str # This field exists in get_watch_playlist API.
239+
length: str # watch playlist uses `length` instead of `duration`.
240+
watch_thumbnail: List[SearchNestedThumbnail] = Field(
241+
default_factory=list, alias="thumbnail"
242+
) # watch playlist uses singular key.
239243

240244
def v2_model(self) -> SongModelV2:
241245
song = super().v2_model()
246+
if song.duration <= 0 and self.length:
247+
song.duration = int(timeparse(self.length) * 1000)
248+
if not song.pic_url and self.watch_thumbnail:
249+
song.pic_url = self.watch_thumbnail[-1].url or ""
242250
song.date = self.year or ""
243251
return song
244252

@@ -418,18 +426,6 @@ def v2_model(self, identifier) -> ArtistModelV2:
418426
# Note that the channelId is different from the identifier.
419427
# Though the channelId also refers to the artist,
420428
# it's songs is a empty list.
421-
album_count = -1
422-
if self.albums and self.albums.browseId is None:
423-
album_count = len(self.albums.results or [])
424-
425-
song_count = -1
426-
if self.songs and self.songs.browseId is None:
427-
song_count = len(self.songs.results or [])
428-
429-
mv_count = -1
430-
if self.videos and self.videos.browseId is None:
431-
mv_count = len(self.videos.results or [])
432-
433429
return ArtistModelV2(
434430
identifier=identifier,
435431
source=self.source,
@@ -441,9 +437,9 @@ def v2_model(self, identifier) -> ArtistModelV2:
441437
for song in (self.songs.results if self.songs else []) or []
442438
],
443439
description=self.description or "",
444-
song_count=song_count,
445-
album_count=album_count,
446-
mv_count=mv_count,
440+
song_count=-1,
441+
album_count=-1,
442+
mv_count=-1,
447443
)
448444

449445

@@ -525,12 +521,6 @@ class Format(BaseModel):
525521
videoDetails: VideoDetails
526522
streamingData: StreamingData
527523

528-
def get_pic_url(self) -> str:
529-
thumbnails = self.videoDetails.thumbnail if self.videoDetails else None
530-
if thumbnails is None:
531-
return ""
532-
return thumbnails.cover or ""
533-
534524
def list_formats(self) -> List[Quality.Audio]:
535525
qualities = set()
536526
if self.streamingData is None:

fuo_ytmusic/provider.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -472,26 +472,12 @@ def song_get_media(
472472
def song_get_web_url(self, song) -> str:
473473
return f"https://music.youtube.com/watch?v={song.identifier}"
474474

475-
@staticmethod
476-
def _normalize_watch_playlist_track(track: dict) -> dict:
477-
normalized = dict(track)
478-
# get_watch_playlist uses `thumbnail`/`length`, while search-like models
479-
# expect `thumbnails`/`duration`.
480-
if "thumbnails" not in normalized and isinstance(normalized.get("thumbnail"), list):
481-
normalized["thumbnails"] = normalized["thumbnail"]
482-
if not normalized.get("duration") and normalized.get("length"):
483-
normalized["duration"] = normalized["length"]
484-
return normalized
485-
486475
def song_get(self, identifier):
487476
# ytmusicapi has not api to get song detail.
488477
# hack(cosven): we use get_watch_playlist to try to get song detail.
489478
# It works for song like '如愿-王菲'.
490479
result = self.service.api.get_watch_playlist(identifier)
491-
songs = [
492-
YtmusicWatchPlaylistSong(**self._normalize_watch_playlist_track(track)).v2_model()
493-
for track in result["tracks"]
494-
]
480+
songs = [YtmusicWatchPlaylistSong(**track).v2_model() for track in result["tracks"]]
495481
for song in songs:
496482
if song.identifier == identifier:
497483
return song
@@ -501,7 +487,7 @@ def song_get(self, identifier):
501487
def song_list_similar(self, song):
502488
result = self.service.api.get_watch_playlist(song.identifier)
503489
songs = [
504-
YtmusicWatchPlaylistSong(**self._normalize_watch_playlist_track(track)).v2_model()
490+
YtmusicWatchPlaylistSong(**track).v2_model()
505491
for track in result["tracks"]
506492
if track["videoId"] != song.identifier
507493
]

tests/test_models_fields.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ def test_artist_info_v2_model_sets_hot_songs():
4949

5050
assert len(model.hot_songs) == len(data["songs"]["results"])
5151
assert model.hot_songs[0].identifier
52-
assert model.album_count == len(data["albums"]["results"])
52+
assert model.song_count == -1
53+
assert model.album_count == -1
54+
assert model.mv_count == -1
5355

5456

5557
def test_song_info_supports_audio_quality_and_media_mapping():
@@ -68,10 +70,3 @@ def test_song_info_supports_audio_quality_and_media_mapping():
6870
if Quality.Audio.sq in audio_qualities:
6971
sq_itag, _, _ = song.get_media(Quality.Audio.sq)
7072
assert sq_itag is not None
71-
72-
73-
def test_song_info_extracts_pic_url_from_video_details():
74-
data = _load_fixture("model_fields_get_song_xqoantj5pny.json")
75-
song = SongInfo(**data)
76-
77-
assert song.get_pic_url() == data["videoDetails"]["thumbnail"]["thumbnails"][-1]["url"]

tests/test_provider_song_get.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from fuo_ytmusic.provider import YtmusicProvider
44

55

6-
def test_song_get_normalizes_watch_playlist_cover_and_duration():
6+
def test_song_get_handles_watch_playlist_thumbnail_and_length_fields():
77
song_id = "vid-1"
88
expected_pic_url = "https://example.com/song-544.jpg"
99

@@ -41,10 +41,6 @@ def get_watch_playlist(identifier):
4141
class _ServiceStub:
4242
api = _ApiStub()
4343

44-
@staticmethod
45-
def song_info(_identifier):
46-
raise AssertionError("song_info fallback should not be used")
47-
4844
provider = YtmusicProvider()
4945
provider.service = _ServiceStub()
5046

@@ -55,7 +51,7 @@ def song_info(_identifier):
5551
assert song.duration > 0
5652

5753

58-
def test_song_list_similar_reuses_watch_playlist_normalization():
54+
def test_song_list_similar_handles_watch_playlist_thumbnail_and_length_fields():
5955
seed_song_id = "seed-song"
6056

6157
class _ApiStub:

0 commit comments

Comments
 (0)