Files
NovaSonic/CLAUDE.md
T
HoneyandClaude Opus 4.8 4c85fec4ca docs: add CLAUDE.md with project conventions and download-pipeline knowledge
Captures the rules and hard-won details from recent work: no mocked frontend
data, two-part versioning, yt-dlp-only download cascade + Mittelweg rule, file
naming, the YTM-search limitation for obscure tracks, and edit/delete file
resolution. Loaded automatically by Claude Code at session start.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:48:29 +02:00

3.6 KiB

CLAUDE.md

Guidance for Claude Code when working in this repository.

What this is

NovaSonic — a self-hosted music streaming PWA (Vite + vanilla ES modules) and Android app (Jetpack Compose + Media3/ExoPlayer) for Navidrome / Subsonic servers, plus a Node.js/Express backend that downloads tracks via yt-dlp and proxies APIs.

Critical rules

  • Never hardcode or mock frontend data. All library data comes from the Navidrome (Subsonic) API. No placeholder songs/albums/artists, ever.
  • Don't commit or push unless asked. When asked, push to the Gitea remote.
  • Two-part versioning MAJOR.MINOR (e.g. 0.8) — no patch component. Each release bumps the minor; features and fixes go in the same release. See CHANGELOG.md.
  • Keep the CHANGELOG.md Platform Parity Matrix accurate when adding cross-platform features.

Layout

  • src/ — web PWA (vanilla ES modules). State in src/state.js (the S object); Subsonic/iTunes API in src/api.js; views in src/views/, UI in src/ui/.
  • backend/novasonic-server.js — Express API (downloads, metadata edit/delete, events/ABS proxy).
  • backend/lib/ — ytdlp.js (download + cascade + ffmpeg tagging), downloader.js, metadata.js (iTunes lookup, file resolution helpers), config.js, jobs.js, romaji.js.
  • android/ — native Kotlin app (separate; backend-dependent web features like the metadata editor are not in the Android app).

Commands

npm run dev      # Vite dev server (HMR)
npm run build    # production build → dist/
npm run preview  # preview the production build

Backend runs in Docker (see docker-compose.yml); on the deploy host it lives at /opt/navidrome/music. Restart after backend changes: docker compose restart novasonic-backend (this also re-pulls the latest yt-dlp).

Download pipeline (hard-won knowledge)

  • Downloads are yt-dlp only (the old squid.wtf/Qobuz backend was removed).
  • File naming: {NN - }Artist - Title.ext with the release suffix stripped from the title (no - Single/- EP). The album folder keeps the full album name. The cleaned title (cleanTitle/searchTitle) drives both the filename and the embedded title tag.
  • Search cascade (ytdlp.js): YTM search first (web_music player client, Songs filter param), then regular YouTube; each candidate filtered by duration (±10%, min 10s), title keyword, artist channel, and variant exclusions (remix/cover/sped/…).
  • "Mittelweg" rule: when the duration is known but nothing matches the duration window, relax only the duration constraint while still requiring the artist channel. If nothing matches, download nothing and fail cleanly with "Nicht eindeutig gefunden — bitte Direktlink im Importer nutzen". Never fall back to a blind first-result grab (it pulled same-titled videos by other uploaders).
  • Known limitation: yt-dlp's YTM/YouTube search can't surface very obscure Art Tracks by ambiguously-named artists (e.g. "MINA") — the browser finds them, yt-dlp's API extraction doesn't (confirmed across params/clients/deno). For those, the direct link in the importer (sourceUrl path) is the reliable route.
  • File resolution for edit/delete: Navidrome's stored path may use a different mount root or a stale stem. metadata.js rebases the path and falls back to recursive/stem matching (tolerant of release-suffix mismatch). A ghost Navidrome entry needs a full library rescan.

UI / references

When referencing files in chat, use clickable markdown links, not backticks: [ytdlp.js](backend/lib/ytdlp.js).