4.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
A modular CommonJS Node.js CLI for downloading music from NetEase Cloud Music (网易云音乐), with lyrics, cover art, and ID3 tag embedding. 163_music_downloader.js owns CLI argument handling and the interactive menu; reusable logic lives under lib/. There is no package.json — the script runs on Node built-ins only (https, http, fs, path, readline, child_process). The UI, comments, and console output are entirely in Chinese.
Running
# Interactive menu (TTY required for pause hotkey)
node 163_music_downloader.js
# Command-line batch mode (bypasses the menu entirely)
node 163_music_downloader.js <songId> <songId> ...
node 163_music_downloader.js --playlist=<playlistId>
# Flags (batch mode only)
--level=lossless # override quality; see QUALITY_LEVELS
--retries=5 # download retry count (default 3)
--no-lyric # skip .lrc download
--no-cover # skip cover/tag embedding
There are no tests, linter, or build step. Verify changes by running the script.
External dependencies (all optional / runtime-detected)
api.chksz.top— third-party API (APIobject at top of file) for song URLs, search, lyrics, and playlists. This is the core download source.music.163.com— queried directly (fetchNeteaseSongDetail,fetchNeteaseAlbumDetail) for rich metadata (album, publish date, popularity). RequiresCookie: os=pcheader.node-id3(npm) — preferred for MP3 tag/lyric/cover embedding.required lazily viahasNodeID3(); absent by default.ffmpeg(system) — fallback embedder for non-MP3 (FLAC etc.) and when node-id3 is missing. Detected viahasFFmpeg().- Audio players (
mpv,ffplay,play,cvlc,aplay) — for the preview/play features, detected viawhich. Note:whichis Unix-only, so preview won't find players on Windows even if installed.
If a dependency is missing the relevant feature degrades gracefully (skips embedding, prints the URL, etc.).
Architecture
163_music_downloader.js drives batch mode or the interactive menu loop, while lib/ contains feature modules:
lib/network.js—fetchJSON,downloadFile(streams with a live progress bar, follows redirects), anddownloadBuffer.lib/downloader.js— the core retry/download pipeline: resolves the URL, handles existing files, writes audio and lyrics, embeds cover/tags, then records history.lib/lyrics.js— lyric fetching, translation merging, credit parsing, and standalone.lrcsaving.lib/metadata.js— NetEase song/album metadata, album directory naming, andcover.jpgsaving.lib/tagging.js— node-id3/ffmpeg detection and audio tag embedding.lib/config.js,history.js,pause.js,quality.js,catalog.js,utils.js— configuration, persistence, batch state, quality selection, search, and shared helpers.- Persistence (all JSON in the script's
__dirname, not the download dir):download_history.json— capped at 500 entries, newest-first (addHistory).downloader_config.json— key/value config, currently only thelevelquality setting (getConfig/setConfig).
- Output layout — audio +
.lrcfiles go todownloads/. Deletes move files to.trash/(not permanent). ZIP packaging uses.tmp_zip/. Thecmenu option cleans these up.
Conventions & gotchas
- Quality levels —
QUALITY_LEVELSarray is the source of truth;DEFAULT_LEVEL = 'jymaster'. The chksz API may return a loweractualLevelthan requested. - Filenames — always
sanitize(${artist} - ${name})(strips/\:*?"<>|). Lyric/rename logic depends on this exact"artist - name"shape when parsing filenames back out. - Menu dispatch — adding a feature means adding a
casein themain()switch AND a line in bothprintMenu()and thehhelp text. Note the switch currently has a duplicatedcase 'p'/'P'block (the second is unreachable) and the keypress listener is registered twice — mirror the existing style rather than assuming it's clean. - Pause —
lib/pause.jsowns pause state;waitIfPaused()gates batch loops and thephotkey toggles it in interactive TTY mode. - Batch loops sleep ~300–500ms between songs to avoid hammering the API — preserve this when editing download loops.