fix: SC metadata fields, albumartist & separator, importer clear, folder rename on album edit
- SC tracks: use artworkUrl/album/genre fields (not iTunes artworkUrl100/collectionName/primaryGenreName) - Playlist handler: same SC field fallbacks for artwork, album, genre, year - ytdlp.js: convert & to , in album_artist tag so Navidrome albumartist.Split works - Importer: clear both previews on any URL input change, not just on clear - edit-metadata: rename album folder on disk when album name changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
62fefbb5e7
commit
d785a4c09a
3 files changed
+28
-16
No files matched your search
@@ -312,7 +312,7 @@ async function embedMetadata({ jobId, audioFile, dir, trackPrefix, safeTitle, ar
|
|||||||
'-id3v2_version', '3',
|
'-id3v2_version', '3',
|
||||||
'-metadata', `title=${title}`,
|
'-metadata', `title=${title}`,
|
||||||
'-metadata', `artist=${navidromeArtist(artist)}`,
|
'-metadata', `artist=${navidromeArtist(artist)}`,
|
||||||
'-metadata', `album_artist=${mainArtist}`,
|
'-metadata', `album_artist=${mainArtist.replace(/\s+&\s+/g, ', ')}`,
|
||||||
);
|
);
|
||||||
if (album) ffArgs.push('-metadata', `album=${album}`);
|
if (album) ffArgs.push('-metadata', `album=${album}`);
|
||||||
if (year) ffArgs.push('-metadata', `date=${String(year)}`);
|
if (year) ffArgs.push('-metadata', `date=${String(year)}`);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const crypto = require('crypto');
|
|||||||
|
|
||||||
const CONFIG = require('./lib/config');
|
const CONFIG = require('./lib/config');
|
||||||
const { jobs, updateJob, log } = require('./lib/jobs');
|
const { jobs, updateJob, log } = require('./lib/jobs');
|
||||||
const { lookupItunesTrackMeta, fetchItunesTracks, searchItunesAlbumTracks } = require('./lib/metadata');
|
const { sanitize, lookupItunesTrackMeta, fetchItunesTracks, searchItunesAlbumTracks } = require('./lib/metadata');
|
||||||
const { downloadTrack, downloadAlbumTracks } = require('./lib/downloader');
|
const { downloadTrack, downloadAlbumTracks } = require('./lib/downloader');
|
||||||
const { buildRomajiIndex, clearRomajiCache, isTokenizerReady, fetchNdSongs } = require('./lib/romaji');
|
const { buildRomajiIndex, clearRomajiCache, isTokenizerReady, fetchNdSongs } = require('./lib/romaji');
|
||||||
|
|
||||||
@@ -243,15 +243,16 @@ app.post('/api/download', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
if (type === 'track') {
|
if (type === 'track') {
|
||||||
const sp = req.body.tracks?.[0] || {};
|
const sp = req.body.tracks?.[0] || {};
|
||||||
const spAlbum = sp.collectionName || album || '';
|
const spAlbum = sp.collectionName || sp.album || album || '';
|
||||||
const spArt = sp.artworkUrl100 || artworkUrl || '';
|
const spArt = sp.artworkUrl100 || sp.artworkUrl || artworkUrl || '';
|
||||||
const spYear = sp.releaseDate ? new Date(sp.releaseDate).getFullYear() : undefined;
|
const spYear = sp.releaseDate ? new Date(sp.releaseDate).getFullYear() : (sp.year || undefined);
|
||||||
let meta = {
|
let meta = {
|
||||||
album: spAlbum, year: spYear,
|
album: spAlbum, year: spYear,
|
||||||
trackNum: sp.trackNumber || bodyTrackNum || undefined,
|
trackNum: sp.trackNumber || bodyTrackNum || undefined,
|
||||||
totalTracks: sp.trackCount || bodyTotalTracks || undefined,
|
totalTracks: sp.trackCount || bodyTotalTracks || undefined,
|
||||||
discNum: sp.discNumber || bodyDiscNum || undefined,
|
discNum: sp.discNumber || bodyDiscNum || undefined,
|
||||||
discTotal: sp.discCount || bodyDiscTotal || undefined,
|
discTotal: sp.discCount || bodyDiscTotal || undefined,
|
||||||
|
genre: sp.primaryGenreName || sp.genre || undefined,
|
||||||
artworkUrl: spArt, explicit: sp.trackExplicitness === 'explicit' || !!explicit,
|
artworkUrl: spArt, explicit: sp.trackExplicitness === 'explicit' || !!explicit,
|
||||||
durationMs: sp.durationMs || undefined,
|
durationMs: sp.durationMs || undefined,
|
||||||
};
|
};
|
||||||
@@ -260,7 +261,7 @@ app.post('/api/download', async (req, res) => {
|
|||||||
log(jobId, '📋 Suche Album-Metadaten von iTunes…');
|
log(jobId, '📋 Suche Album-Metadaten von iTunes…');
|
||||||
const extra = await lookupItunesTrackMeta(searchArtist, title);
|
const extra = await lookupItunesTrackMeta(searchArtist, title);
|
||||||
if (extra.album) log(jobId, `📋 Album: "${extra.album}" (${extra.year || '?'})`);
|
if (extra.album) log(jobId, `📋 Album: "${extra.album}" (${extra.year || '?'})`);
|
||||||
meta = { ...meta, ...extra, artworkUrl: spArt || extra.artworkUrl || '' };
|
meta = { ...meta, ...extra, artworkUrl: spArt || extra.artworkUrl || '', genre: meta.genre || extra.genre || undefined };
|
||||||
} else {
|
} else {
|
||||||
log(jobId, `📋 Album aus Spotify: "${meta.album}"`);
|
log(jobId, `📋 Album aus Spotify: "${meta.album}"`);
|
||||||
}
|
}
|
||||||
@@ -343,12 +344,12 @@ app.post('/api/download', async (req, res) => {
|
|||||||
artist: trackArtist,
|
artist: trackArtist,
|
||||||
albumArtist: trackArtist,
|
albumArtist: trackArtist,
|
||||||
title: trackTitle,
|
title: trackTitle,
|
||||||
album: itunes.album || track.collectionName || '',
|
album: itunes.album || track.collectionName || track.album || '',
|
||||||
year: itunes.year || (track.releaseDate ? new Date(track.releaseDate).getFullYear() : undefined),
|
year: itunes.year || (track.releaseDate ? new Date(track.releaseDate).getFullYear() : undefined) || track.year,
|
||||||
trackNum: itunes.trackNum || track.trackNumber || undefined,
|
trackNum: itunes.trackNum || track.trackNumber || undefined,
|
||||||
totalTracks: itunes.totalTracks || undefined,
|
totalTracks: itunes.totalTracks || undefined,
|
||||||
genre: itunes.genre || track.primaryGenreName || undefined,
|
genre: itunes.genre || track.primaryGenreName || track.genre || undefined,
|
||||||
artworkUrl: itunes.artworkUrl || track.artworkUrl100 || artworkUrl || '',
|
artworkUrl: itunes.artworkUrl || track.artworkUrl100 || track.artworkUrl || artworkUrl || '',
|
||||||
explicit: track.trackExplicitness === 'explicit',
|
explicit: track.trackExplicitness === 'explicit',
|
||||||
durationMs: itunes.durationMs || track.durationMs || undefined,
|
durationMs: itunes.durationMs || track.durationMs || undefined,
|
||||||
sourceUrl: track.sourceUrl || undefined,
|
sourceUrl: track.sourceUrl || undefined,
|
||||||
@@ -578,6 +579,18 @@ app.post('/api/edit-metadata', async (req, res) => {
|
|||||||
fs.renameSync(tmpFile, filePath);
|
fs.renameSync(tmpFile, filePath);
|
||||||
if (artFile) try { fs.unlinkSync(artFile); } catch {}
|
if (artFile) try { fs.unlinkSync(artFile); } catch {}
|
||||||
|
|
||||||
|
// Rename album folder if album changed and folder name matches old album dir
|
||||||
|
if (album) {
|
||||||
|
const dir = path.dirname(filePath);
|
||||||
|
const newDirName = sanitize(album);
|
||||||
|
if (path.basename(dir) !== newDirName) {
|
||||||
|
const newDir = path.join(path.dirname(dir), newDirName);
|
||||||
|
if (!fs.existsSync(newDir)) {
|
||||||
|
try { fs.renameSync(dir, newDir); } catch (e) { console.error('[edit-metadata] folder rename:', e.message); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger Navidrome rescan
|
// Trigger Navidrome rescan
|
||||||
try {
|
try {
|
||||||
const salt2 = Math.random().toString(36).slice(2, 10);
|
const salt2 = Math.random().toString(36).slice(2, 10);
|
||||||
|
|||||||
+5
-6
@@ -7,6 +7,11 @@ import { openDlModal } from './ui.js';
|
|||||||
|
|
||||||
// ─── UNIFIED IMPORTER INPUT ───
|
// ─── UNIFIED IMPORTER INPUT ───
|
||||||
export function onImporterInput(val) {
|
export function onImporterInput(val) {
|
||||||
|
// Always clear results when URL changes
|
||||||
|
document.getElementById('spotify-preview').style.display = 'none';
|
||||||
|
document.getElementById('sc-preview').style.display = 'none';
|
||||||
|
document.getElementById('importer-hint').style.display = val ? 'none' : '';
|
||||||
|
|
||||||
const badge = document.getElementById('importer-badge');
|
const badge = document.getElementById('importer-badge');
|
||||||
const btn = document.getElementById('importer-btn');
|
const btn = document.getElementById('importer-btn');
|
||||||
if (!badge || !btn) return;
|
if (!badge || !btn) return;
|
||||||
@@ -32,12 +37,6 @@ export function onImporterInput(val) {
|
|||||||
btn.style.color = '';
|
btn.style.color = '';
|
||||||
btn.style.borderColor = '';
|
btn.style.borderColor = '';
|
||||||
}
|
}
|
||||||
// Hide results when input is cleared
|
|
||||||
if (!val) {
|
|
||||||
document.getElementById('spotify-preview').style.display = 'none';
|
|
||||||
document.getElementById('sc-preview').style.display = 'none';
|
|
||||||
document.getElementById('importer-hint').style.display = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveImporterLink() {
|
export async function resolveImporterLink() {
|
||||||
|
|||||||
Reference in new issue
Block a user