source: Klonkt/scripts/backfill-durations.mjs@ 3e86f1c

main
Last change on this file since 3e86f1c was 3e86f1c, checked in by roboburr <roboburr@…>, 3 months ago

feat: automatic track duration — no more manually entering seconds

The duration of an audio track is now determined automatically instead of manually:

  • On upload: read from ffmpeg's codecData event during the existing transcode (no extra ffprobe binary). The upload INSERT never set duration before -> every track started as NULL ('--:--'); that is now fixed.
  • In the track editor: a hidden <audio preload=metadata> reads the duration and fills the field if it's still empty (manual override still possible).
  • Backfill: npm run backfill:durations fills existing tracks without duration via ffmpeg (probeDuration reads only codecData and stops immediately -> fast).

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 2.0 KB
Line 
1#!/usr/bin/env node
2/**
3 * Backfill audio_tracks.duration voor bestaande tracks die nog géén duur hebben.
4 * Leest de duur uit het mp3-bestand via ffmpeg (probeDuration — geen aparte
5 * ffprobe-binary nodig). Idempotent: pakt alleen rijen met duration NULL of 0,
6 * dus veilig herhaalbaar.
7 *
8 * npm run backfill:durations
9 *
10 * Respecteert AUDIO_PATH (env) net als de upload-route.
11 */
12import path from 'path';
13import fs from 'fs';
14import { fileURLToPath } from 'url';
15import db from '../src/config/database.js';
16import { probeDuration } from '../src/services/AudioTranscoder.js';
17
18const __dirname = path.dirname(fileURLToPath(import.meta.url));
19const AUDIO_DIR = path.resolve(
20 process.env.AUDIO_PATH || path.join(__dirname, '..', 'storage', 'audio')
21);
22
23// storage_path is absoluut (opgeslagen bij upload); val terug op AUDIO_DIR/filename.
24function resolveFile(t) {
25 const candidates = [t.storage_path, t.filename ? path.join(AUDIO_DIR, t.filename) : null].filter(Boolean);
26 for (const c of candidates) {
27 try { if (fs.statSync(c).isFile()) return c; } catch { /* volgende kandidaat */ }
28 }
29 return null;
30}
31
32const rows = db.prepare(`
33 SELECT t.id, m.filename, m.storage_path
34 FROM audio_tracks t
35 JOIN media m ON m.id = t.media_id
36 WHERE (t.duration IS NULL OR t.duration = 0) AND m.filename IS NOT NULL
37`).all();
38
39console.log(`[backfill-durations] ${rows.length} track(s) zonder duur`);
40const update = db.prepare('UPDATE audio_tracks SET duration = ? WHERE id = ?');
41
42let ok = 0, miss = 0, fail = 0;
43for (const t of rows) {
44 const file = resolveFile(t);
45 if (!file) { console.warn(` - ${t.id}: bestand niet gevonden (${t.filename})`); miss++; continue; }
46 try {
47 const sec = await probeDuration(file);
48 if (sec && sec > 0) { update.run(sec, t.id); ok++; console.log(` ✓ ${t.id}: ${sec}s`); }
49 else { console.warn(` - ${t.id}: geen duur uit ffmpeg`); fail++; }
50 } catch (e) { console.warn(` - ${t.id}: ${e.message}`); fail++; }
51}
52console.log(`[backfill-durations] klaar — ${ok} bijgewerkt, ${miss} bestand-mist, ${fail} mislukt`);
53process.exit(0);
Note: See TracBrowser for help on using the repository browser.