Ignore:
Timestamp:
06/30/2026 03:06:32 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
4d1aefb
Parents:
d4d8f9d
Message:

feat(admin-audio): replace an existing track's audio file + Playlists shortcut

  • Track editor: a "Vervang audiobestand" control uploads a new audio file for an existing track. POST /admin/audio/api/:id/replace-audio transcodes it to mp3, swaps the track's media_id + duration, and deletes the old media (file + row). The track id is unchanged, so every [[track:id]] in posts keeps pointing at the same track — now with the new audio.
  • /admin/audio header gets a Playlists button so you can jump to playlist management.
  • src/routes/admin-audio.js — POST /api/:id/replace-audio
  • src/views/partials/track-editor.ejs — replace-audio field + uploader
  • src/views/pages/admin-audio.ejs — Playlists link in the header

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

File:
1 edited

Legend:

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

    rd4d8f9d r98a1990  
    440440
    441441            <div class="te-field">
     442              <span>Audiobestand</span>
     443              <div class="te-cover-btn-row">
     444                <input type="file" id="te-audio-file" accept="audio/*,.mp3,.wav,.m4a,.flac,.ogg,.aac" hidden>
     445                <button type="button" class="te-cover-btn" id="te-audio-pick">${track.stream_url ? '🔁 Vervang audiobestand' : '🎵 Audiobestand kiezen'}</button>
     446                <span class="te-cover-status" id="te-audio-status"></span>
     447              </div>
     448            </div>
     449
     450            <div class="te-field">
    442451              <span>Cover</span>
    443452              <div class="te-cover-row">
     
    636645    }
    637646
     647    // ── Replace the audio file of this track ──────────────────
     648    const aPick = $('#te-audio-pick');
     649    const aFile = $('#te-audio-file');
     650    const aStatus = $('#te-audio-status');
     651    if (aPick && aFile) {
     652      aPick.addEventListener('click', () => aFile.click());
     653      aFile.addEventListener('change', async (e) => {
     654        const f = e.target.files && e.target.files[0];
     655        aFile.value = '';
     656        if (!f) return;
     657        aStatus.textContent = '⏳ Converteren… (kan even duren)'; aStatus.className = 'te-cover-status';
     658        aPick.disabled = true;
     659        try {
     660          const fd = new FormData();
     661          fd.append('audio', f);
     662          const j = await api('POST', '/admin/audio/api/' + encodeURIComponent(id) + '/replace-audio', fd);
     663          if (!j.ok) throw new Error(j.error || 'mislukt');
     664          aStatus.textContent = '✓ Vervangen'; aStatus.className = 'te-cover-status is-ok';
     665          track.stream_url = j.stream_url || track.stream_url;
     666          if (j.duration) { const d = $('#te-duration'); if (d) d.value = j.duration; }
     667        } catch (err) {
     668          aStatus.textContent = 'Mislukt: ' + err.message; aStatus.className = 'te-cover-status is-error';
     669        } finally { aPick.disabled = false; }
     670      });
     671    }
     672
    638673    // ── Insert © symbol into the credit field ─────────────────
    639674    const copyrBtn = $('#te-credit-copyr');
Note: See TracChangeset for help on using the changeset viewer.