Changeset 8681239 in Klonkt


Ignore:
Timestamp:
05/07/2026 10:38:43 PM (4 months ago)
Author:
Robin <robin@…>
Branches:
main
Children:
76e9cfd
Parents:
00550c0
Message:

audio v3: robust continuous playback — pause+load reset, error auto-skip, autoplay policy hint

  • loadTrack: pause + audio.load() for a clean reset (prevents state corruption after multiple src changes, root cause of 'stop after 3-4 tracks')
  • play() rejection NotAllowedError -> visual pulse hint on the play button so the user knows 'tap to continue' (iOS autoplay policy)
  • audio.error event: auto-skip to next track, max 3 errors in a row before giving up (anti-infinite-loop)
  • Reset error counter on successful play
  • Cache buster ?v=3 on shell.ejs
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • .beads/issues.jsonl

    r00550c0 r8681239  
     1{"_type":"issue","id":"prutfolio-src-3fl","title":"Audio: continuous playback breekt na 3-4 tracks (browser-policy / state corruption)","description":"Na fix v1 (next() unconditional play) werkt 1-\u003e2 en 2-\u003e3, maar stopt bij 3-\u003e4. Verdacht: iOS Safari autoplay-policy revoke OF audio element state-corruption na meerdere src changes. Fix: pause()+load()+play() pattern in loadTrack, error-event auto-skip naar volgende, NotAllowedError visuele hint (pulse op play-btn).","status":"open","priority":1,"issue_type":"bug","owner":"robin@roboburr.com","created_at":"2026-05-07T22:38:42Z","created_by":"Robin","updated_at":"2026-05-07T22:38:42Z","dependency_count":0,"dependent_count":0,"comment_count":0}
    12{"_type":"issue","id":"prutfolio-src-0tg","title":"Audio: continuous playback fix — next() altijd play() ipv conditional","description":"Bug: next() checkte 'if (isPlaying) play()' maar isPlaying was al false door de pause-event die de browser firet vóór ended. Resultaat: track stopt na 1 keer. Fix: drop de conditional, roep altijd play() aan. Zelfde voor prev(). Bestand: src/assets/js/audio-player.js regels 221-230.","status":"closed","priority":1,"issue_type":"bug","assignee":"Robin","owner":"robin@roboburr.com","created_at":"2026-05-06T22:18:40Z","created_by":"Robin","updated_at":"2026-05-06T22:28:20Z","started_at":"2026-05-06T22:26:37Z","closed_at":"2026-05-06T22:28:20Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
    23{"_type":"issue","id":"prutfolio-src-e44","title":"Asset cache-busting via commit-hash ipv handmatig bumpen","description":"Nu staan ?v=N hardcoded in shell.ejs. Beter: middleware zet res.locals.assetVersion = git-commit-sha (build-time of runtime) zodat elke deploy auto-cache-bust krijgt. Behandel alle static script/css tags in shell.ejs (htmx.min.js, audio-player.js, install-app.js).","status":"open","priority":3,"issue_type":"task","owner":"robin@roboburr.com","created_at":"2026-05-07T20:03:17Z","created_by":"Robin","updated_at":"2026-05-07T20:03:17Z","dependency_count":0,"dependent_count":0,"comment_count":0}
  • src/assets/css/audio.css

    r00550c0 r8681239  
    111111  opacity: 0;
    112112  pointer-events: none;
     113}
     114
     115/* Browser-autoplay-policy heeft de auto-advance gestopt — typisch
     116   na 3-4 tracks op iOS Safari. Visuele hint dat user op play moet
     117   tappen om door te gaan (PCMS v10.1, audio-player.js). */
     118.audio-player.audio-needs-tap .audio-btn-play {
     119  animation: audio-pulse-tap 1.2s ease-in-out infinite;
     120  background: rgba(255, 165, 0, 0.18);
     121  border-color: orange;
     122  color: orange;
     123}
     124@keyframes audio-pulse-tap {
     125  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 165, 0, 0.5); }
     126  50%      { box-shadow: 0 0 0 8px rgba(255, 165, 0, 0); }
    113127}
    114128.audio-player-inner {
  • src/assets/js/audio-player.js

    r00550c0 r8681239  
    189189    }
    190190    console.log('[pcms-audio] loading', t.title, t.url);
     191    // Schone overgang: pause + reset voorkomt state-corruption van het
     192    // audio-element na meerdere src-changes (bug die continuous playback
     193    // brak na 3-4 tracks). audio.load() forceert reset van internal state.
     194    try { audio.pause(); } catch (e) {}
    191195    audio.src = t.url;
     196    try { audio.load(); } catch (e) {}
    192197    titleEl.textContent  = t.title  || 'Untitled';
    193198    artistEl.textContent = t.artist || '';
     
    214219    const p = audio.play();
    215220    if (p && typeof p.catch === 'function') {
    216       p.catch((err) => console.warn('[pcms-audio] play() rejected', err));
     221      p.catch((err) => {
     222        console.warn('[pcms-audio] play() rejected:', err.name, err.message);
     223        // Browser-autoplay-policy heeft 't gestopt (typisch na 3-4
     224        // automatische plays op iOS Safari, of als tab tijdelijk inactive
     225        // was). Visuele hint dat user op play moet tappen.
     226        if (err && err.name === 'NotAllowedError') {
     227          root.classList.add('audio-needs-tap');
     228          isPlaying = false;
     229          root.classList.remove('is-playing');
     230        }
     231      });
    217232    }
    218233  }
     
    268283  // 5. Audio element events → UI sync
    269284  // ============================================================
    270   audio.addEventListener('play',  () => { isPlaying = true;  root.classList.add('is-playing'); });
     285  // Error-counter voorkomt infinite-loop als ALLE tracks broken zijn.
     286  let consecutiveErrors = 0;
     287
     288  audio.addEventListener('play',  () => {
     289    isPlaying = true;
     290    consecutiveErrors = 0;  // reset bij succesvolle play
     291    root.classList.add('is-playing');
     292    root.classList.remove('audio-needs-tap');  // verstop tap-hint
     293  });
    271294  audio.addEventListener('pause', () => { isPlaying = false; root.classList.remove('is-playing'); });
    272295  audio.addEventListener('ended', next);
     
    274297    const code = audio.error ? audio.error.code : '?';
    275298    console.error('[pcms-audio] playback error', code, audio.src, e);
     299    consecutiveErrors++;
     300    // Bij netwerk/decode-fout: skip naar volgende track ipv stilstaan.
     301    // Max 3 fouten op rij voordat we opgeven (anders infinite loop).
     302    if (consecutiveErrors < 3 && queue.length > 1) {
     303      console.warn('[pcms-audio] auto-skip naar volgende na error', consecutiveErrors);
     304      setTimeout(next, 400);
     305    }
    276306  });
    277   audio.addEventListener('stalled', () => console.warn('[pcms-audio] stalled'));
     307  audio.addEventListener('stalled', () => console.warn('[pcms-audio] stalled at', audio.currentTime));
    278308  audio.addEventListener('volumechange', () => { root.classList.toggle('is-muted', audio.muted || audio.volume === 0); });
    279309  audio.addEventListener('timeupdate', () => {
  • src/views/shell.ejs

    r00550c0 r8681239  
    277277     ?v=N — cache-buster: bump bij elke audio-player.js wijziging zodat
    278278     Cloudflare (max-age=1y) niet de oude versie blijft serveren. -->
    279 <script src="/assets/js/audio-player.js?v=2"></script>
     279<script src="/assets/js/audio-player.js?v=3"></script>
    280280<% if (site && site.enable_audio_player && audioTracks && audioTracks.length > 0) { %>
    281281  <script>window.PCMS_SITE_TRACKS = <%- JSON.stringify(audioTracks) %>;</script>
Note: See TracChangeset for help on using the changeset viewer.