184 lines
7.2 KiB
Python
184 lines
7.2 KiB
Python
import unittest
|
|
from types import SimpleNamespace
|
|
import inspect
|
|
|
|
|
|
class FakeResponse:
|
|
def __init__(self, payload):
|
|
self.payload = payload
|
|
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return self.payload
|
|
|
|
|
|
class DeferredSongInfoTests(unittest.TestCase):
|
|
def test_deferred_module_avoids_py39_str_prefix_suffix_methods(self):
|
|
import musicdl.catalogsync.deferred as deferred
|
|
|
|
source = inspect.getsource(deferred)
|
|
self.assertNotIn(".removesuffix(", source)
|
|
self.assertNotIn(".removeprefix(", source)
|
|
|
|
def test_extract_playlist_id_from_html_path(self):
|
|
from musicdl.catalogsync.deferred import _extract_playlist_id_from_url
|
|
|
|
playlist_id = _extract_playlist_id_from_url("https://www.kuwo.cn/playlist_detail/3671258656.html")
|
|
|
|
self.assertEqual("3671258656", playlist_id)
|
|
|
|
def test_build_kuwo_raw_track_song_infos_strips_music_prefix(self):
|
|
from musicdl.catalogsync.deferred import build_kuwo_raw_track_song_infos
|
|
|
|
client = SimpleNamespace(
|
|
source="KuwoMusicClient",
|
|
_constructuniqueworkdir=lambda keyword: f"/tmp/{keyword}",
|
|
_removeduplicates=lambda song_infos: list(song_infos),
|
|
)
|
|
song_infos = build_kuwo_raw_track_song_infos(
|
|
client,
|
|
raw_tracks=[
|
|
{
|
|
"MUSICRID": "MUSIC_123456",
|
|
"SONGNAME": "Song A",
|
|
"ARTIST": "Singer A",
|
|
"ALBUM": "Album A",
|
|
"DURATION": 180,
|
|
}
|
|
],
|
|
playlist_name="Kuwo Playlist",
|
|
)
|
|
|
|
self.assertEqual(1, len(song_infos))
|
|
self.assertEqual("123456", song_infos[0].identifier)
|
|
|
|
def test_build_deferred_song_info_marks_snapshot_as_deferred(self):
|
|
from musicdl.catalogsync.deferred import build_deferred_song_info
|
|
|
|
song_info = build_deferred_song_info(
|
|
source="NeteaseMusicClient",
|
|
raw_search_result={"id": 101, "name": "Song A"},
|
|
identifier="101",
|
|
song_name="Song A",
|
|
singers="Singer A",
|
|
album="Album A",
|
|
duration_s=215,
|
|
cover_url="https://example.com/a.jpg",
|
|
ext="flac",
|
|
)
|
|
|
|
self.assertEqual("NeteaseMusicClient", song_info.source)
|
|
self.assertEqual("101", song_info.identifier)
|
|
self.assertEqual("Song A", song_info.song_name)
|
|
self.assertEqual("Singer A", song_info.singers)
|
|
self.assertEqual("Album A", song_info.album)
|
|
self.assertEqual("flac", song_info.ext)
|
|
self.assertFalse(song_info.with_valid_download_url)
|
|
self.assertTrue(song_info.raw_data["deferred_search"])
|
|
self.assertEqual(101, song_info.raw_data["search"]["id"])
|
|
|
|
def test_build_netease_playlist_song_infos_fetches_missing_track_details(self):
|
|
from musicdl.catalogsync.deferred import build_netease_playlist_song_infos
|
|
|
|
class FakeClient:
|
|
source = "NeteaseMusicClient"
|
|
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def post(self, url, data=None, **kwargs):
|
|
self.calls.append((url, data))
|
|
if url.endswith("/api/v6/playlist/detail"):
|
|
return FakeResponse(
|
|
{
|
|
"playlist": {
|
|
"name": "Test Playlist",
|
|
"trackIds": [{"id": 101}, {"id": 102}],
|
|
"tracks": [
|
|
{
|
|
"id": 101,
|
|
"name": "Song A",
|
|
"dt": 215000,
|
|
"ar": [{"name": "Singer A"}],
|
|
"al": {"name": "Album A", "picUrl": "https://example.com/a.jpg"},
|
|
"sq": {"size": 1},
|
|
}
|
|
],
|
|
}
|
|
}
|
|
)
|
|
if url.endswith("/api/v3/song/detail"):
|
|
return FakeResponse(
|
|
{
|
|
"songs": [
|
|
{
|
|
"id": 102,
|
|
"name": "Song B",
|
|
"dt": 186000,
|
|
"ar": [{"name": "Singer B"}],
|
|
"al": {"name": "Album B", "picUrl": "https://example.com/b.jpg"},
|
|
"h": {"size": 1},
|
|
}
|
|
]
|
|
}
|
|
)
|
|
raise AssertionError(f"Unexpected URL: {url}")
|
|
|
|
def _constructuniqueworkdir(self, keyword):
|
|
return f"/tmp/{keyword}"
|
|
|
|
def _removeduplicates(self, song_infos):
|
|
return list(song_infos)
|
|
|
|
client = FakeClient()
|
|
song_infos = build_netease_playlist_song_infos(client, "https://music.163.com/#/playlist?id=999")
|
|
|
|
self.assertEqual(2, len(song_infos))
|
|
self.assertEqual(["101", "102"], [song_info.identifier for song_info in song_infos])
|
|
self.assertEqual(["Song A", "Song B"], [song_info.song_name for song_info in song_infos])
|
|
self.assertTrue(all(song_info.raw_data["deferred_search"] for song_info in song_infos))
|
|
self.assertEqual("flac", song_infos[0].ext)
|
|
self.assertEqual("mp3", song_infos[1].ext)
|
|
self.assertTrue(any(url.endswith("/api/v3/song/detail") for url, _ in client.calls))
|
|
|
|
def test_build_qq_raw_track_song_infos_keeps_tracks_without_direct_download_urls(self):
|
|
from musicdl.catalogsync.deferred import build_qq_raw_track_song_infos
|
|
|
|
client = SimpleNamespace(
|
|
source="QQMusicClient",
|
|
_constructuniqueworkdir=lambda keyword: f"/tmp/{keyword}",
|
|
_removeduplicates=lambda song_infos: list(song_infos),
|
|
)
|
|
song_infos = build_qq_raw_track_song_infos(
|
|
client,
|
|
raw_tracks=[
|
|
{
|
|
"songmid": "mid-a",
|
|
"songname": "Song A",
|
|
"interval": 210,
|
|
"singer": [{"name": "Singer A"}],
|
|
"albumname": "Album A",
|
|
"albummid": "album-a",
|
|
"sizeflac": 1024,
|
|
},
|
|
{
|
|
"songmid": "mid-b",
|
|
"songname": "Song B",
|
|
"interval": 180,
|
|
"singer": [{"name": "Singer B"}],
|
|
"albumname": "Album B",
|
|
"albummid": "album-b",
|
|
"size320": 512,
|
|
},
|
|
],
|
|
playlist_name="QQ Playlist",
|
|
)
|
|
|
|
self.assertEqual(2, len(song_infos))
|
|
self.assertEqual(["mid-a", "mid-b"], [song_info.identifier for song_info in song_infos])
|
|
self.assertEqual(["Song A", "Song B"], [song_info.song_name for song_info in song_infos])
|
|
self.assertEqual(["flac", "mp3"], [song_info.ext for song_info in song_infos])
|
|
self.assertTrue(all(song_info.raw_data["deferred_search"] for song_info in song_infos))
|