Changeset 3e86f1c in Klonkt for src/views/partials


Ignore:
Timestamp:
06/15/2026 03:23:29 AM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
7f46817d
Parents:
4c9f29a
Message:

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@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/partials/track-editor.ejs

    r4c9f29a r3e86f1c  
    380380
    381381            <label class="te-field">
    382               <span>Duur <small>(seconden — bv. 142 voor 2:22)</small></span>
     382              <span>Duur <small>(seconden — automatisch bepaald, hier te overschrijven)</small></span>
    383383              <input type="number" id="te-duration" min="0" step="1"
    384384                     inputmode="numeric" pattern="[0-9]*"
    385                      value="${track.duration || ''}" placeholder="0">
     385                     value="${track.duration || ''}" placeholder="auto">
    386386            </label>
    387387
     
    619619    });
    620620
     621    // ── Auto-duur ───────────────────────────────────────────
     622    // De server bepaalt de duur al automatisch bij upload. Dit is de vangnet/
     623    // UX-laag: opent een admin een bestaande track zónder duur, dan lezen we 'm
     624    // hier uit de audio-metadata en vullen het veld — zodat je nooit seconden
     625    // hoeft te typen. Bestaande waarde wordt nooit overschreven. We halen de
     626    // bytes via dezelfde header-gate als de speler (X-Audio-Player).
     627    (async function autoDuration() {
     628      const durEl = $('#te-duration');
     629      if (!durEl || !track.stream_url) return;
     630      if (durEl.value && Number(durEl.value) > 0) return;  // al ingevuld → met rust laten
     631      let objUrl = null;
     632      try {
     633        const r = await fetch(track.stream_url, { credentials: 'same-origin', headers: { 'X-Audio-Player': '1' } });
     634        if (!r.ok) return;
     635        objUrl = URL.createObjectURL(await r.blob());
     636        const probe = new Audio();
     637        probe.preload = 'metadata';
     638        probe.addEventListener('loadedmetadata', () => {
     639          if (isFinite(probe.duration) && probe.duration > 0 && !(durEl.value && Number(durEl.value) > 0)) {
     640            durEl.value = Math.round(probe.duration);
     641          }
     642          if (objUrl) URL.revokeObjectURL(objUrl);
     643        });
     644        probe.addEventListener('error', () => { if (objUrl) URL.revokeObjectURL(objUrl); });
     645        probe.src = objUrl;
     646      } catch (e) { if (objUrl) URL.revokeObjectURL(objUrl); }
     647    })();
     648
    621649    // Focus title for fast typing
    622650    setTimeout(() => $('#te-title').focus(), 60);
Note: See TracChangeset for help on using the changeset viewer.