feat: split backend into lib/ modules + add squid.wtf (Qobuz) download backend
- backend/lib/config.js — CONFIG from env (adds DOWNLOAD_BACKEND, SQUID_QUALITY) - backend/lib/jobs.js — jobs Map, updateJob(), log() - backend/lib/metadata.js — iTunes, artwork, lyrics, sanitize helpers - backend/lib/ytdlp.js — yt-dlp download cascade + shared embedMetadata() - backend/lib/squid.js — ALTCHA PoW solver, Qobuz search/download via squid.wtf - backend/lib/downloader.js — backend selector: ytdlp | squid | auto (squid-first + fallback) - novasonic-server.js thinned to Express routes only, imports from lib/ - docker-compose: mount full backend/ dir, install deps outside read-only volume - .env.example: document DOWNLOAD_BACKEND and SQUID_QUALITY Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
2d71fdce6a
commit
ac8d26bf9d
9 files changed
+858
-557
No files matched your search
@@ -0,0 +1,60 @@
|
||||
const CONFIG = require('./config');
|
||||
const { log, updateJob } = require('./jobs');
|
||||
const ytdlp = require('./ytdlp');
|
||||
const squid = require('./squid');
|
||||
|
||||
// Route a single track download to the correct backend, with optional fallback.
|
||||
async function downloadTrack(opts) {
|
||||
const backend = CONFIG.downloadBackend;
|
||||
|
||||
if (backend === 'ytdlp') return ytdlp.downloadTrack(opts);
|
||||
if (backend === 'squid') return squid.downloadTrack(opts);
|
||||
|
||||
// 'auto': squid first, yt-dlp as fallback
|
||||
try {
|
||||
const result = await squid.downloadTrack(opts);
|
||||
if (result !== null) return result;
|
||||
// null = not found on Qobuz — fall through to yt-dlp
|
||||
log(opts.jobId, '⚠️ squid.wtf: nicht gefunden — Fallback zu yt-dlp');
|
||||
} catch (e) {
|
||||
log(opts.jobId, `⚠️ squid.wtf Fehler — Fallback zu yt-dlp: ${e.message}`);
|
||||
}
|
||||
return ytdlp.downloadTrack(opts);
|
||||
}
|
||||
|
||||
// Download all tracks of an album sequentially, updating job progress.
|
||||
async function downloadAlbumTracks({ jobId, artist, albumTitle, tracks, artworkUrl }) {
|
||||
const total = tracks.length;
|
||||
log(jobId, `📀 Album "${albumTitle}" — ${total} Tracks`);
|
||||
let done = 0, skipped = 0;
|
||||
|
||||
for (const track of tracks) {
|
||||
try {
|
||||
const result = await downloadTrack({
|
||||
jobId,
|
||||
artist: track.artistName || artist,
|
||||
albumArtist: artist,
|
||||
title: track.trackName || track.title,
|
||||
album: albumTitle,
|
||||
year: track.releaseDate ? new Date(track.releaseDate).getFullYear() : undefined,
|
||||
trackNum: track.trackNumber || undefined,
|
||||
totalTracks: total,
|
||||
discNum: track.discNumber || undefined,
|
||||
discTotal: track.discCount || undefined,
|
||||
compilation: track.collectionType === 'Compilation' || undefined,
|
||||
genre: track.primaryGenreName || undefined,
|
||||
artworkUrl: artworkUrl || track.artworkUrl100,
|
||||
explicit: track.trackExplicitness === 'explicit',
|
||||
outputDir: CONFIG.musicPath,
|
||||
durationMs: track.trackTimeMillis || undefined,
|
||||
});
|
||||
if (result?.skipped) skipped++; else done++;
|
||||
updateJob(jobId, { progress: Math.round(((done + skipped) / total) * 90) });
|
||||
} catch (e) {
|
||||
log(jobId, `⚠️ Fehler bei "${track.trackName || track.title}": ${e.message}`);
|
||||
}
|
||||
}
|
||||
return { done, skipped };
|
||||
}
|
||||
|
||||
module.exports = { downloadTrack, downloadAlbumTracks };
|
||||
Reference in new issue
Block a user