Files
163-music-dl/CLAUDE.md
T
2026-07-22 16:43:50 +08:00

58 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Overview
A single-file Node.js CLI for downloading music from NetEase Cloud Music (网易云音乐), with lyrics, cover art, and ID3 tag embedding. All logic lives in `163_music_downloader.js` (~2600 lines). 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
```bash
# 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 (`API` object 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). Requires `Cookie: os=pc` header.
- **`node-id3`** (npm) — preferred for MP3 tag/lyric/cover embedding. `require`d lazily via `hasNodeID3()`; absent by default.
- **`ffmpeg`** (system) — fallback embedder for non-MP3 (FLAC etc.) and when node-id3 is missing. Detected via `hasFFmpeg()`.
- **Audio players** (`mpv`, `ffplay`, `play`, `cvlc`, `aplay`) — for the preview/play features, detected via `which`. Note: `which` is 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
Everything is one flat module — functions defined top-to-bottom, then a `main()` at the bottom drives either batch mode or an infinite interactive menu loop (`while (true)` + `switch` on single-char menu choices). Key layers:
- **Network helpers** — `fetchJSON`, `downloadFile` (streams with a live progress bar, follows redirects), `downloadBuffer`, all Promise-wrapped around `https.get`.
- **`downloadSong``_downloadSong`** — the core pipeline: `downloadSong` wraps `_downloadSong` with retry/backoff; `_downloadSong` resolves the URL, skips/overwrites based on existing-file size comparison (1% tolerance), writes audio, then lyrics, then embeds cover+tags, then records history.
- **Lyrics** — `fetchLyric` returns `{ lrc, tlyric, ... }`; `mergeLrc` interleaves original + translation by matching `[mm:ss.xx]` timestamps into a `.合并翻译.lrc` file.
- **Cover/tags** — `embedCover` downloads the image to a temp `.cover.jpg`, tries node-id3 (MP3) then ffmpeg (`-map` audio+cover, `-c copy`), always cleans up the temp file in `finally`.
- **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 the `level` quality setting (`getConfig`/`setConfig`).
- **Output layout** — audio + `.lrc` files go to `downloads/`. Deletes move files to `.trash/` (not permanent). ZIP packaging uses `.tmp_zip/`. The `c` menu option cleans these up.
## Conventions & gotchas
- **Quality levels** — `QUALITY_LEVELS` array is the source of truth; `DEFAULT_LEVEL = 'jymaster'`. The chksz API may return a lower `actualLevel` than 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 `case` in the `main()` switch AND a line in both `printMenu()` and the `h` help text. Note the switch currently has a duplicated `case '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** — global `isPaused`/`waitIfPaused()` gate batch loops; toggled by the `p` hotkey (only wired up in interactive mode with a TTY).
- Batch loops sleep ~300500ms between songs to avoid hammering the API — preserve this when editing download loops.