Initial import: Music_Server, MusicFree, catalog-sync

This commit is contained in:
2026-05-23 16:51:14 +08:00
commit 069af30dba
847 changed files with 179878 additions and 0 deletions
@@ -0,0 +1,183 @@
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))