Changeset e6888ca in Klonkt


Ignore:
Timestamp:
06/15/2026 01:29:13 AM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
851239c
Parents:
bd7f10d
Message:

feat: audio player persists + resumes across page navigations (session persistence)

An <audio> doesn't survive a full reload, and cross-context navigation (e.g. to
the headerless hub overview) is intentionally full-nav -> the playing player
disappeared. Now the player saves queue+position+playstate to sessionStorage
(pagehide/play/pause + 5s interval) and restores+resumes on the next page;
restore wins over the page seed. Position is applied on loadedmetadata.
audio-player.js v=10->v=11.

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

Location:
src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/audio-player.js

    rbd7f10d re6888ca  
    694694
    695695  // ============================================================
    696   // 10. Site-level pre-seed (window.PCMS_SITE_TRACKS)
    697   // ============================================================
    698   if (Array.isArray(window.PCMS_SITE_TRACKS) && window.PCMS_SITE_TRACKS.length) {
    699     queue = window.PCMS_SITE_TRACKS.map(t => ({
    700       url:    t.media_url || t.url,
    701       title:  t.title  || 'Untitled',
    702       artist: t.artist || '',
    703       cover:  t.cover_url || t.cover || '',
    704     }));
    705     if (queue.length) loadTrack(0, false, true);  // metadata only — fetch on first play
     696  // 10. Sessie-persistentie — speler "blijft" over page-navigaties heen
     697  // ============================================================
     698  // Een <audio> overleeft geen volledige page-load (en cross-context navigatie
     699  // — bv. naar de headerloze hub-overview — is bewust full-nav). We bewaren de
     700  // sessie in sessionStorage en herstellen + hervatten 'm op de volgende pagina:
     701  // de speler staat er weer met dezelfde track op dezelfde positie. In Chrome
     702  // (hoge media-engagement) speelt 'ie meteen door; staat de browser autoplay
     703  // niet toe, dan staat 'ie klaar op die plek (één tik = verder).
     704  const PLAYER_STATE_KEY = 'pcms-player-state';
     705  let pendingSeek = 0;
     706  function savePlayerState() {
     707    try {
     708      if (!queue.length) { sessionStorage.removeItem(PLAYER_STATE_KEY); return; }
     709      sessionStorage.setItem(PLAYER_STATE_KEY, JSON.stringify({
     710        queue, currentIndex, albumName,
     711        time: audio.currentTime || 0,
     712        playing: !!audio.src && !audio.paused,
     713      }));
     714    } catch (e) {}
     715  }
     716  window.addEventListener('pagehide', savePlayerState);
     717  window.addEventListener('beforeunload', savePlayerState);
     718  audio.addEventListener('play',  savePlayerState);
     719  audio.addEventListener('pause', savePlayerState);
     720  audio.addEventListener('ended', savePlayerState);
     721  setInterval(() => { if (audio.src && !audio.paused) savePlayerState(); }, 5000);
     722  // Herstelde positie toepassen zodra de track-metadata binnen is.
     723  audio.addEventListener('loadedmetadata', () => {
     724    if (pendingSeek > 0 && isFinite(audio.duration) && audio.duration > 0) {
     725      try { audio.currentTime = Math.min(pendingSeek, audio.duration - 0.25); } catch (e) {}
     726      pendingSeek = 0;
     727    }
     728  });
     729  function restorePlayerState() {
     730    let s = null;
     731    try { s = JSON.parse(sessionStorage.getItem(PLAYER_STATE_KEY) || 'null'); } catch (e) { return false; }
     732    if (!s || !Array.isArray(s.queue) || !s.queue.length) return false;
     733    queue = s.queue;
     734    albumName = s.albumName || '';
     735    pendingSeek = s.time || 0;
     736    const idx = Math.max(0, Math.min(s.currentIndex || 0, queue.length - 1));
     737    // playing -> fetch + (poging tot) hervatten; gepauzeerd -> alleen metadata.
     738    loadTrack(idx, !!s.playing, !s.playing);
     739    return true;
     740  }
     741
     742  // ============================================================
     743  // 11. Site-level pre-seed (window.PCMS_SITE_TRACKS)
     744  // ============================================================
     745  // Een actieve sessie (restore) wint van de pagina-seed, zodat lopende muziek
     746  // doorgaat i.p.v. vervangen te worden door de tracks van de nieuwe pagina.
     747  if (!restorePlayerState()) {
     748    if (Array.isArray(window.PCMS_SITE_TRACKS) && window.PCMS_SITE_TRACKS.length) {
     749      queue = window.PCMS_SITE_TRACKS.map(t => ({
     750        url:    t.media_url || t.url,
     751        title:  t.title  || 'Untitled',
     752        artist: t.artist || '',
     753        cover:  t.cover_url || t.cover || '',
     754      }));
     755      if (queue.length) loadTrack(0, false, true);  // metadata only — fetch on first play
     756    }
    706757  }
    707758})();
  • src/views/shell.ejs

    rbd7f10d re6888ca  
    309309     ?v=N — cache-buster: bump bij elke audio-player.js wijziging zodat
    310310     Cloudflare (max-age=1y) niet de oude versie blijft serveren. -->
    311 <script src="/assets/js/audio-player.js?v=10"></script>
     311<script src="/assets/js/audio-player.js?v=11"></script>
    312312<% if (site && site.enable_audio_player && audioTracks && audioTracks.length > 0) { %>
    313313  <script>window.PCMS_SITE_TRACKS = <%- JSON.stringify(audioTracks) %>;</script>
Note: See TracChangeset for help on using the changeset viewer.