60 lines
4.4 KiB
Markdown
Executable File
60 lines
4.4 KiB
Markdown
Executable File
# AGENTS.md
|
||
|
||
This file provides guidance to Codex (Codex.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
|
||
|
||
```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
|
||
|
||
`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), and `downloadBuffer`.
|
||
- **`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 `.lrc` saving.
|
||
- **`lib/metadata.js`** — NetEase song/album metadata, album directory naming, and `cover.jpg` saving.
|
||
- **`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 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** — `lib/pause.js` owns pause state; `waitIfPaused()` gates batch loops and the `p` hotkey toggles it in interactive TTY mode.
|
||
- Batch loops sleep ~300–500ms between songs to avoid hammering the API — preserve this when editing download loops.
|