Musicdl APIs
This document describes the main public interfaces of MusicClient, MusicClientCMD, and BaseMusicClient based on the latest code version.
musicdl.musicdl.MusicClient
MusicClient is the high-level entry point for end users. It manages multiple source-specific clients, sends search and download requests to the correct source, and provides both programmatic and interactive workflows.
Constructor:
musicdl.musicdl.MusicClient(
music_sources: list = [],
init_music_clients_cfg: dict = {},
clients_threadings: dict = {},
requests_overrides: dict = {},
search_rules: dict = {},
)
Arguments:
music_sourcesA list of source client names to enable. You can pass a list such as
["NeteaseMusicClient", "QQMusicClient"]. You can also pass a single string, it will be converted into a one-item list internally. If empty, the default sources are:["MiguMusicClient", "NeteaseMusicClient", "QQMusicClient", "KuwoMusicClient", "QianqianMusicClient"]
init_music_clients_cfgPer-source initialization settings. The key is the source name, and the value is a config dictionary used when building that source client.
Example:
{ "NeteaseMusicClient": {"work_dir": "outputs", "search_size_per_source": 10, "maintain_session": True}, "QQMusicClient": {"search_size_per_source": 5, "default_search_cookies": {"foo": "bar"}}, }
Common fields include:
search_size_per_sourceauto_set_proxiesrandom_update_uamax_retriesmaintain_sessiondisable_printwork_dirdefault_search_cookiesdefault_download_cookiesdefault_parse_cookiessearch_size_per_pagestrict_limit_search_size_per_pagequark_parser_configfreeproxy_settingsenable_search_curl_cffienable_download_curl_cffienable_parse_curl_cffi
Notes:
logger_handleis injected automatically byMusicClient.typeis also set automatically to the current source name.Some sources use a smaller default
search_size_per_sourceinternally.
clients_threadingsPer-source thread counts used during search and download.
Example:
{"NeteaseMusicClient": 8, "QQMusicClient": 4}
If a source is not provided here,
MusicClientuses its internal default thread count.requests_overridesPer-source request overrides forwarded to the underlying source client.
Typical values include:
headerscookiesproxiestimeoutverify
Example:
{"NeteaseMusicClient": {"timeout": (10, 30), "headers": {"User-Agent": "..."},}}
search_rulesPer-source search rules passed into each source client’s
search()implementation.Example:
{"FiveSingMusicClient": {"sort": 1, "filter": 0, "type": 0}}
Use this when a source supports custom search filters or paging rules.
MusicClient.search(keyword) -> dict[str, list[SongInfo]]
Searches all enabled music sources in parallel.
What it does:
Starts one search task per enabled source.
Uses the per-source thread count from
clients_threadings.Passes
requests_overrides[source]andsearch_rules[source]to each source client.Returns a dictionary keyed by source name.
Example:
music_client = MusicClient(
music_sources=["NeteaseMusicClient", "QQMusicClient"]
)
results = music_client.search("Jay Chou")
Typical return shape:
{
"NeteaseMusicClient": [SongInfo(...), SongInfo(...)],
"QQMusicClient": [SongInfo(...)]
}
MusicClient.download(song_infos) -> list[SongInfo]
Downloads songs by routing them to the correct underlying source client.
Accepted input:
a flat list of
SongInfoor a dictionary like
dict[str, list[SongInfo]]
What it does:
Groups songs by
song_info.sourceCalls the matching source client’s
download()methodMerges all successful downloads into one list
Example with a flat list:
results = music_client.search("Faded")
selected = results["NeteaseMusicClient"][:2]
downloaded = music_client.download(selected)
Example with the full search result dictionary:
results = music_client.search("Faded")
downloaded = music_client.download(results)
Return value:
list[SongInfo]
Only successfully downloaded items are returned.
MusicClient.parseplaylist(playlist_url: str) -> list[SongInfo]
Tries to parse a playlist URL by asking enabled source clients one by one.
What it does:
Iterates through all initialized clients
Calls each client’s
parseplaylist()Stops at the first client that returns a non-empty result
Example:
songs = music_client.parseplaylist(
"https://music.163.com/#/playlist?id=7583298906"
)
downloaded = music_client.download(songs)
This is useful when the caller does not want to manually determine which source owns the playlist URL.
MusicClient.startcmdui()
Starts the interactive terminal UI.
What it does:
Prints basic usage information (version, save paths, etc..).
Prompts the user to input keywords for music search.
Calls
MusicClient.search()to retrieve search results from all configured music sources.Displays a formatted table of candidate songs with IDs.
Opens a cursor-based selection UI where the user can choose one or multiple songs:
Use “↑/↓” to move the cursor
Press “Space” to toggle selection
Press “a” to select all, “i” to invert selection
Press “Enter” to confirm and start downloading
Press “Esc” or “q” to cancel selection
Collects the corresponding song info entries and calls
MusicClient.download()to download them.
Special inputs at the main prompt:
q: quit the programr: restart the interactive flow
This method runs in a loop until the user exits.
musicdl.musicdl.MusicClientCMD
MusicClientCMD is the Click-based command-line entry point.
It supports three modes:
Interactive mode
If bothkeywordandplaylist_urlare empty, it launchesstartcmdui().Playlist mode
Ifplaylist_urlis provided, it parses the playlist and downloads all parsed tracks.Keyword mode
Ifkeywordis provided, it searches by keyword, opens the selection UI, and downloads the chosen tracks.
Signature:
musicdl.musicdl.MusicClientCMD(
keyword: str,
playlist_url: str,
music_sources: str,
init_music_clients_cfg: str,
requests_overrides: str,
clients_threadings: str,
search_rules: str,
)
CLI Options:
-k, --keywordSearch keyword. If omitted and no playlist URL is given, the program enters interactive mode.
Example:
musicdl -k "Taylor Swift"
-p, --playlist-url, --playlist_urlPlaylist URL to parse and download.
Example:
musicdl -p "https://music.163.com/#/playlist?id=7583298906"
-m, --music-sources, --music_sourcesComma-separated list of source names.
Example:
musicdl -k "Adele" -m "NeteaseMusicClient,QQMusicClient"
-i, --init-music-clients-cfg, --init_music_clients_cfgJSON string for per-source client initialization config.
Example:
musicdl -k "Adele" -i '{"NeteaseMusicClient": {"work_dir": "outputs", "search_size_per_source": 10}}'
-r, --requests-overrides, --requests_overridesJSON string for per-source request overrides.
Example:
musicdl -k "Adele" -r '{"NeteaseMusicClient": {"timeout": [10, 30]}}'
-c, --clients-threadings, --clients_threadingsJSON string for per-source thread counts.
Example:
musicdl -k "Adele" -c '{"NeteaseMusicClient": 8, "QQMusicClient": 4}'
-s, --search-rules, --search_rulesJSON string for per-source search rules.
Example:
musicdl -k "Adele" -s '{"FiveSingMusicClient": {"sort": 1, "type": 0}}'
Important Behavior:
keywordandplaylist_urlcannot be set at the same time.The JSON-like CLI strings are parsed with
json_repair.loads, which is more tolerant than strictjson.loads.music_sourcesis split by commas after removing extra spaces.
musicdl.modules.sources.BaseMusicClient
BaseMusicClient is the common base class for source-specific music clients.
End users usually do not create this class directly. Instead, they use subclasses such as,
musicdl.modules.sources.AppleMusicClientmusicdl.modules.sources.BilibiliMusicClientmusicdl.modules.sources.BodianMusicClientmusicdl.modules.sources.DeezerMusicClientmusicdl.modules.sources.FiveSingMusicClientmusicdl.modules.sources.FMAMusicClientmusicdl.modules.sources.JamendoMusicClientmusicdl.modules.sources.JooxMusicClientmusicdl.modules.sources.JioSaavnMusicClientmusicdl.modules.sources.KugouMusicClientmusicdl.modules.sources.KuwoMusicClientmusicdl.modules.sources.MiguMusicClientmusicdl.modules.sources.MOOVMusicClientmusicdl.modules.sources.NeteaseMusicClientmusicdl.modules.sources.OpenGameArtMusicClientmusicdl.modules.sources.QianqianMusicClientmusicdl.modules.sources.QQMusicClientmusicdl.modules.sources.QobuzMusicClientmusicdl.modules.sources.SodaMusicClientmusicdl.modules.sources.StreetVoiceMusicClientmusicdl.modules.sources.SoundCloudMusicClientmusicdl.modules.sources.SpotifyMusicClientmusicdl.modules.sources.SunoMusicClientmusicdl.modules.sources.TIDALMusicClientmusicdl.modules.sources.YouTubeMusicClientmusicdl.modules.thirdpartysites.AlgerMusicClientmusicdl.modules.thirdpartysites.BuguyyMusicClientmusicdl.modules.thirdpartysites.FiveSongMusicClientmusicdl.modules.thirdpartysites.FangpiMusicClientmusicdl.modules.thirdpartysites.FLMP3MusicClientmusicdl.modules.thirdpartysites.GequbaoMusicClientmusicdl.modules.thirdpartysites.GequhaiMusicClientmusicdl.modules.thirdpartysites.HTQYYMusicClientmusicdl.modules.thirdpartysites.JCPOOMusicClientmusicdl.modules.thirdpartysites.KKWSMusicClientmusicdl.modules.thirdpartysites.LivePOOMusicClientmusicdl.modules.thirdpartysites.MituMusicClientmusicdl.modules.thirdpartysites.TwoT58MusicClientmusicdl.modules.thirdpartysites.YinyuedaoMusicClientmusicdl.modules.thirdpartysites.ZhuolinMusicClientmusicdl.modules.common.GDStudioMusicClientmusicdl.modules.common.JBSouMusicClientmusicdl.modules.common.MP3JuiceMusicClientmusicdl.modules.common.MyFreeMP3MusicClientmusicdl.modules.common.TuneHubMusicClientmusicdl.modules.common.WJHEMusicClientmusicdl.modules.audiobooks.ITunesMusicClientmusicdl.modules.audiobooks.LizhiMusicClientmusicdl.modules.audiobooks.LRTSMusicClientmusicdl.modules.audiobooks.QingtingMusicClientmusicdl.modules.audiobooks.XimalayaMusicClient
For developers, this class provides the shared workflow for:
search URL construction
concurrent searching
deduplication
work directory creation
progress display
downloading with HTTP or HLS
playlist parsing hooks
HTTP session / retry handling
Constructor:
musicdl.modules.sources.BaseMusicClient(
search_size_per_source: int = 5,
auto_set_proxies: bool = False,
random_update_ua: bool = False,
enable_search_curl_cffi: bool = False,
enable_parse_curl_cffi: bool = False,
enable_download_curl_cffi: bool = False,
maintain_session: bool = False,
logger_handle: LoggerHandle = None,
disable_print: bool = False,
work_dir: str = "musicdl_outputs",
max_retries: int = 3,
freeproxy_settings: dict = None,
default_search_cookies: dict | str = None,
default_download_cookies: dict | str = None,
default_parse_cookies: dict | str = None,
strict_limit_search_size_per_page: bool = True,
search_size_per_page: int = 10,
quark_parser_config: dict = None,
)
Arguments:
search_size_per_source: maximum number of results to keep for this sourcesearch_size_per_page: page size used when building paginated search requestsstrict_limit_search_size_per_page: whether to strictly cap returned resultsmaintain_session: reuse one session across requestsmax_retries: retry count forget()andpost()random_update_ua: randomly refreshUser-Agentenable_search_curl_cffienable_parse_curl_cffienable_download_curl_cffiauto_set_proxies: auto-fetch proxies throughfreeproxyfreeproxy_settings: config for the proxy clientdefault_search_cookiesdefault_download_cookiesdefault_parse_cookieswork_dir: root output directorylogger_handle: logger instancedisable_print: disable supported logger printsquark_parser_config: configuration used when a source needs Quark Netdisk cookies or related settings
Methods Subclasses Usually Override:
_constructsearchurls(keyword, rule=None, request_overrides=None) -> listMust be implemented by each subclass. This method should build the list of search URLs or search request targets for the given keyword.
Example idea:
def _constructsearchurls(self, keyword, rule=None, request_overrides=None): return [f"https://example.com/search?q={keyword}&page=1"]
_search(keyword="", search_url="", request_overrides=None, song_infos=[], progress=None, progress_id=0)Must be implemented by each subclass. This method should:
fetch data from one search URL
parse the source response
create
SongInfoobjectsappend valid results into the provided
song_infoslistupdate progress text if needed
parseplaylist(playlist_url, request_overrides=None)Optional for subclasses. The base implementation raises
NotImplementedError. A subclass should override it if the source supports albums, playlists, episode collections, or other grouped URLs.
BaseMusicClient.search(keyword, num_threadings=5, request_overrides=None, rule=None, main_process_context=None, main_progress_id=None, main_progress_lock=None) -> list[SongInfo]
Searches this source only.
What it does:
Builds search URLs using
_constructsearchurls()Searches those URLs concurrently with
_search()Merges results from all worker threads
Removes duplicates by
song_info.identifierCreates a unique work directory for this search
Assigns
work_dirto returned songsSaves serialized results to
search_results.pkl
Example:
client = SomeMusicClient(search_size_per_source=10)
songs = client.search("Imagine Dragons")
Return value:
list[SongInfo]
Only valid downloadable items are kept in the final result.
BaseMusicClient.download(song_infos, num_threadings=5, request_overrides=None, auto_supplement_song=True) -> list[SongInfo]
Downloads songs from this source only.
What it does:
filters invalid items before downloading
creates an overall progress bar and per-song progress bars
downloads with multiple threads
supports different download paths depending on protocol
saves serialized results to
download_results.pkl
Example:
songs = client.search("Imagine Dragons")
downloaded = client.download(songs[:3])
Return value:
list[SongInfo]
Only successful downloads are returned.
When auto_supplement_song=True, each successful download is post-processed through:
SongInfoUtils.supplsonginfothensavelyricsthenwritetags(...)
This may supplement metadata, save lyrics, and write tags.
BaseMusicClient.get(url, **kwargs)
A retry-aware wrapper around session.get().
Features:
applies default cookies when not explicitly provided
optionally uses auto proxies
optionally uses
curl_cffiimpersonationoptionally refreshes
User-Agentretries up to
max_retries
BaseMusicClient.post(url, **kwargs)
Same idea as get(), but for POST requests.