Perf: Vollständiger Norm-Cache-Sync beim Start + Auto-Cache bei Upload/Import
- syncNormCache() beim Serverstart: normalisiert ALLE Sounds (nicht nur Top 50) und räumt verwaiste Cache-Dateien automatisch auf - DM-Upload: neue Datei wird sofort im Hintergrund normalisiert - URL-Import: Datei wird vor dem Abspielen normalisiert → direkt aus Cache - Detailliertes Logging: neu/vorhanden/fehlgeschlagen/verwaist + Dauer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
83b8f1acac
commit
68414ac257
1 changed files with 59 additions and 28 deletions
|
|
@ -181,7 +181,7 @@ function getNormCachePath(filePath: string): string | null {
|
|||
function normalizeToCache(filePath: string): Promise<string> {
|
||||
const cacheFile = path.join(NORM_CACHE_DIR, normCacheKey(filePath));
|
||||
return new Promise((resolve, reject) => {
|
||||
const ffArgs = ['-hide_banner', '-loglevel', 'error', '-i', filePath,
|
||||
const ffArgs = ['-hide_banner', '-loglevel', 'error', '-y', '-i', filePath,
|
||||
'-af', `loudnorm=I=${NORMALIZE_I}:LRA=${NORMALIZE_LRA}:TP=${NORMALIZE_TP}`,
|
||||
'-f', 's16le', '-ar', '48000', '-ac', '2', cacheFile];
|
||||
const ff = child_process.spawn('ffmpeg', ffArgs);
|
||||
|
|
@ -193,6 +193,54 @@ function normalizeToCache(filePath: string): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Vollständige Cache-Synchronisation:
|
||||
* 1. Alle Sound-Dateien normalisieren, die noch nicht im Cache sind
|
||||
* 2. Verwaiste Cache-Dateien löschen (Sound wurde gelöscht/umbenannt)
|
||||
* Läuft im Hintergrund, blockiert nicht den Server.
|
||||
*/
|
||||
async function syncNormCache(): Promise<void> {
|
||||
if (!NORMALIZE_ENABLE) return;
|
||||
const t0 = Date.now();
|
||||
const allSounds = listAllSounds();
|
||||
|
||||
// Set aller erwarteten Cache-Keys
|
||||
const expectedKeys = new Set<string>();
|
||||
let created = 0;
|
||||
let skipped = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const s of allSounds) {
|
||||
const fp = path.join(SOUNDS_DIR, s.relativePath);
|
||||
const key = normCacheKey(fp);
|
||||
expectedKeys.add(key);
|
||||
if (!fs.existsSync(fp)) continue;
|
||||
if (getNormCachePath(fp)) { skipped++; continue; } // bereits gecacht & gültig
|
||||
try {
|
||||
await normalizeToCache(fp);
|
||||
created++;
|
||||
} catch (e) {
|
||||
failed++;
|
||||
console.warn(`Norm-cache failed: ${s.relativePath}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Verwaiste Cache-Dateien aufräumen
|
||||
let cleaned = 0;
|
||||
try {
|
||||
for (const f of fs.readdirSync(NORM_CACHE_DIR)) {
|
||||
if (!expectedKeys.has(f)) {
|
||||
try { fs.unlinkSync(path.join(NORM_CACHE_DIR, f)); cleaned++; } catch {}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const dt = ((Date.now() - t0) / 1000).toFixed(1);
|
||||
console.log(
|
||||
`Norm-Cache sync (${dt}s): ${created} neu, ${skipped} vorhanden, ${failed} fehlgeschlagen, ${cleaned} verwaist entfernt (${allSounds.length} Sounds gesamt)`
|
||||
);
|
||||
}
|
||||
|
||||
// --- Voice Abhängigkeiten prüfen ---
|
||||
await sodium.ready;
|
||||
// init nacl to ensure it loads
|
||||
|
|
@ -622,6 +670,10 @@ client.on(Events.MessageCreate, async (message: Message) => {
|
|||
if (!res.ok) throw new Error(`Download fehlgeschlagen: ${attachment.url}`);
|
||||
const arrayBuffer = await res.arrayBuffer();
|
||||
fs.writeFileSync(targetPath, Buffer.from(arrayBuffer));
|
||||
// Sofort normalisieren für instant Play
|
||||
if (NORMALIZE_ENABLE) {
|
||||
normalizeToCache(targetPath).catch((e) => console.warn('Norm after upload failed:', e));
|
||||
}
|
||||
await message.author.send?.(`Sound gespeichert: ${path.basename(targetPath)}`);
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -1328,6 +1380,10 @@ app.post('/api/play-url', async (req: Request, res: Response) => {
|
|||
if (!r.ok) return res.status(400).json({ error: 'Download fehlgeschlagen' });
|
||||
const buf = Buffer.from(await r.arrayBuffer());
|
||||
fs.writeFileSync(dest, buf);
|
||||
// Vor dem Abspielen normalisieren → sofort aus Cache
|
||||
if (NORMALIZE_ENABLE) {
|
||||
try { await normalizeToCache(dest); } catch (e) { console.warn('Norm after URL import failed:', e); }
|
||||
}
|
||||
try {
|
||||
await playFilePath(guildId, channelId, dest, volume, path.basename(dest));
|
||||
} catch {
|
||||
|
|
@ -1352,33 +1408,8 @@ if (fs.existsSync(webDistPath)) {
|
|||
app.listen(PORT, () => {
|
||||
console.log(`Server läuft auf http://0.0.0.0:${PORT}`);
|
||||
|
||||
// Hintergrund-Warmup: häufigste Sounds vorab normalisieren, damit der erste Play sofort schnell ist
|
||||
if (NORMALIZE_ENABLE) {
|
||||
(async () => {
|
||||
try {
|
||||
const allSounds = listAllSounds();
|
||||
// Sortiere nach Play-Count (häufigste zuerst), maximal 50 vorab cachen
|
||||
const plays = persistedState.plays ?? {};
|
||||
const sorted = [...allSounds].sort((a, b) => ((plays[b.relativePath] ?? 0) as number) - ((plays[a.relativePath] ?? 0) as number));
|
||||
const toWarm = sorted.slice(0, 50);
|
||||
let cached = 0;
|
||||
for (const s of toWarm) {
|
||||
const fp = path.join(SOUNDS_DIR, s.relativePath);
|
||||
if (!fs.existsSync(fp)) continue;
|
||||
if (getNormCachePath(fp)) continue; // schon gecacht
|
||||
try {
|
||||
await normalizeToCache(fp);
|
||||
cached++;
|
||||
} catch (e) {
|
||||
console.warn(`Warmup failed for ${s.relativePath}:`, e);
|
||||
}
|
||||
}
|
||||
if (cached > 0) console.log(`Loudnorm-Warmup: ${cached} Sounds vorab normalisiert`);
|
||||
} catch (e) {
|
||||
console.warn('Loudnorm-Warmup error:', e);
|
||||
}
|
||||
})();
|
||||
}
|
||||
// Vollständige Cache-Synchronisation beim Start (Hintergrund)
|
||||
syncNormCache();
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue