Changeset 4fbe2c1 in Klonkt


Ignore:
Timestamp:
08/22/2026 10:35:33 AM (2 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
c0e463b
Parents:
8bb3b09
Message:

Een bandje loopt niet rond: het stopt aan het eind, en aan het begin

Robins eis (22-8). Drie omlopen zaten in de weg, en de stilste was de keten:
na het laatste nummer hing die nummer een er weer achter, en omdat de wachtrij
EEN doorlopende tijdlijn is merk je dat niet eens als een trackwissel -- de
band gaat gewoon door alsof er meer op stond.

  • De keten haakt in bandmodus niets meer aan voorbij het laatste nummer.
  • next() pauzeert daar in plaats van om te lopen. ended roept next() aan, dus dit is wat de band echt laat stoppen.
  • prev() loopt aan het begin niet naar achteren om: terugspoelen voorbij de kop levert de kop op, niet het laatste nummer.
  • Vooruit prefetchen stopt ook aan het eind.

De drie modulo's blijven staan voor een gewone wachtrij; die hoort wel te
blijven rondlopen. Daar staat een test op, anders sneuvelt dat gedrag stil.

TERUGSPOELEN IS NU ECHT SPOELEN, ook over de nummergrenzen. Op de MSE-motor
ging dat al vanzelf (de band is een tijdlijn). Op de blob-motor landde het in
de KOP van het vorige nummer, en dan kom je nooit ergens in het midden uit; nu
zet hij pendingSeek zodat je aan het EIND van dat nummer uitkomt.

EN EEN FOUT DIE IK ER ZELF MEE INTRODUCEERDE, gevonden door het te draaien en
niet door te lezen: bij een MediaSource vuurt ended pas als de stream dicht
is, en die ging alleen dicht bij een wachtrij van een. Sinds de keten aan het
eind niets meer aanhaakt bleef de band dus hangen op de laatste seconde --
teller stil, isPlaying() waar, spoelen draaiend. Gemeten: 125,6 van 125,7.
De stream sluit nu ook aan het eind van een bandje.

Spoelen stopt vanzelf bij de kop en de staart: beweegt de teller twee rondes
niet, dan is er geen band meer. Anders bleven de spoelen draaien terwijl er
niets gebeurde.

Nagemeten op dev: band loopt af -> laatste nummer, gestopt, teller op 125,7 van
125,7, spoelen stil, knop terug op Afspelen. next() op het laatste nummer
blijft staan; prev() op het eerste spoelt naar de kop.

audio-player v37, MOD_V 54.

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

Files:
4 edited

Legend:

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

    r8bb3b09 r4fbe2c1  
    279279  function preloadNext() {
    280280    if (queue.length < 2) return;
     281    if (tapeMode && currentIndex >= queue.length - 1) return;   // einde band
    281282    const ni = (currentIndex + 1) % queue.length;
    282283    const t = queue[ni];
     
    361362    const start = chain.length ? chain[chain.length - 1].end : (buffered.length ? buffered.start(0) : 0);
    362363    chain.push({ qIndex, start, end: chainEnd });
    363     if (queue.length === 1 && ms && ms.readyState === 'open') {
     364    // DE STREAM MOET DICHT ALS ER NIETS MEER KOMT, anders vuurt `ended` nooit.
     365    // Bij een wachtrij van een was dat al zo. Een BANDJE heeft nu hetzelfde
     366    // nodig: sinds hij niet meer rondloopt haakt de keten na het laatste nummer
     367    // niets meer aan, en dan bleef de band aan het eind hangen -- de teller
     368    // stilstaand op de laatste seconde, isPlaying() waar, en de spoelen
     369    // draaiend. Gemeten op dev (22-8): 125,6 van 125,7 en daar bleef hij.
     370    const laatsteVanDeBand = tapeMode && qIndex >= queue.length - 1;
     371    if ((queue.length === 1 || laatsteVanDeBand) && ms && ms.readyState === 'open') {
    364372      // Single-track queue: close the stream so `ended` fires (which replays
    365       // it, matching the old engine's behaviour).
     373      // it, matching the old engine's behaviour). Bij een bandje stopt `ended`
     374      // hem juist, want next() pauzeert daar aan het eind.
    366375      try { ms.endOfStream(); } catch (e) {}
    367376    }
     
    374383    const seg = currentSegment();
    375384    if (!seg || chain.length - 1 - chain.indexOf(seg) >= 1) return;  // already one ahead
    376     const nextIdx = (chain[chain.length - 1].qIndex + 1) % queue.length;
     385    // EEN BANDJE LOOPT NIET ROND (Robins eis, 22-8). Deze modulo is precies wat
     386    // een cassette eindeloos maakte: na het laatste nummer hing hij nummer een
     387    // er weer achter, en omdat het een doorlopende keten is merk je dat niet
     388    // eens als een trackwissel -- de band gaat gewoon door.
     389    const volgendeInRij = chain[chain.length - 1].qIndex + 1;
     390    if (tapeMode && volgendeInRij > queue.length - 1) return;   // einde band
     391    const nextIdx = volgendeInRij % queue.length;
    377392    const t = queue[nextIdx];
    378393    if (!t || !t.url) return;
     
    642657  function next() {
    643658    if (!queue.length) return;
     659    // Aan het eind van een bandje: stoppen. `ended` roept deze functie aan, dus
     660    // zonder deze tak begint de band na het laatste nummer weer vooraan.
     661    if (tapeMode && currentIndex >= queue.length - 1) { pause(); return; }
    644662    loadTrack((currentIndex + 1) % queue.length, true);
    645663  }
    646664  function prev() {
    647665    if (!queue.length) return;
     666    // En aan het begin ook niet omlopen. Terugspoelen voorbij het begin levert
     667    // de kop van de band op, niet het laatste nummer.
     668    if (tapeMode && currentIndex === 0) {
     669      try { audio.currentTime = 0; } catch (e) { /* nog niets geladen */ }
     670      return;
     671    }
    648672    loadTrack(currentIndex === 0 ? queue.length - 1 : currentIndex - 1, true);
    649673  }
     
    11571181    const duur = audio.duration || 0;
    11581182    const nu = audio.currentTime || 0;
    1159     if (duur && nu + d >= duur) { next(); return; }
    1160     if (nu + d < 0) { prev(); return; }
     1183    if (duur && nu + d >= duur) {
     1184      // Vooruit voorbij het eind. In bandmodus stopt next() aan het eind van de
     1185      // band; daarbuiten loopt hij door naar het volgende nummer.
     1186      next();
     1187      return;
     1188    }
     1189    if (nu + d < 0) {
     1190      // TERUGSPOELEN VOORBIJ HET BEGIN moet in het vorige nummer landen aan het
     1191      // EIND, niet aan het begin -- dat is wat terugspoelen doet. Zonder dit
     1192      // sprong je bij elke druk naar de kop van het vorige nummer en kwam je
     1193      // nooit ergens in het midden uit.
     1194      if (!(tapeMode && currentIndex === 0)) pendingSeek = Number.MAX_SAFE_INTEGER;
     1195      prev();
     1196      return;
     1197    }
    11611198    try { audio.currentTime = Math.max(0, nu + d); } catch (e) {}
    11621199  }
  • src/assets/js/mod/tape.js

    r8bb3b09 r4fbe2c1  
    109109  stopWinden(tape);
    110110  let snelheid = START_SNELHEID;
     111  let vorigePositie = null;
     112  let stil = 0;
    111113  tape.classList.add('is-winding');
    112114  if (richting < 0) tape.classList.add('is-winding-back');
     
    115117    p.seekBy(richting * snelheid * (STAP_MS / 1000));
    116118    tekenBand(tape);
     119    // AAN HET EIND VAN DE BAND STOPPEN MET SPOELEN. De speler klemt de positie
     120    // af, dus daar gebeurt niets meer -- maar de spoelen bleven wel doordraaien
     121    // en de knop bleef oplichten, alsof er nog band was. Beweegt de teller twee
     122    // rondes niet, dan zijn we bij de kop of de staart.
     123    const nu = p.tapeTijden ? p.tapeTijden().cur : null;
     124    if (nu !== null && vorigePositie !== null && Math.abs(nu - vorigePositie) < 0.05) {
     125      if (++stil >= 2) { stopWinden(tape); return; }
     126    } else stil = 0;
     127    vorigePositie = nu;
    117128  }, STAP_MS);
    118129  winder = { timer, tape };
  • src/views/shell.ejs

    r8bb3b09 r4fbe2c1  
    406406     ?v=N — cache-buster: bump bij elke audio-player.js wijziging zodat
    407407     Cloudflare (max-age=1y) niet de oude versie blijft serveren. -->
    408 <script src="/assets/js/audio-player.js?v=35"></script>
     408<script src="/assets/js/audio-player.js?v=37"></script>
    409409<!-- Eigen custom media-embeds (YouTube/SoundCloud/Spotify) via de echte
    410410     player-API's + gedeelde mutual-exclusion registry met de site-speler. -->
     
    505505  // Eén nummer voor de hele map. Te vaak bumpen kost één download; te weinig
    506506  // bumpen kost een bugfix die nooit aankomt.
    507   var MOD_V = 53;
     507  var MOD_V = 54;
    508508
    509509  // name -> 1 (aan het laden) of de module-namespace (geladen). Een module
  • test/mixtape-ui.test.js

    r8bb3b09 r4fbe2c1  
    166166  assert.doesNotMatch(mod, /p\.next\(\); else p\.prev\(\)/, 'de oude sprong per nummer');
    167167});
     168
     169test('een bandje loopt niet rond, aan geen van beide kanten', () => {
     170  const speler = fs.readFileSync('src/assets/js/audio-player.js', 'utf8');
     171
     172  // De keten mag na het laatste nummer niet nummer een er weer achter hangen.
     173  // Dat was de stilste van de drie: omdat het EEN doorlopende tijdlijn is merk
     174  // je die omloop niet eens als een trackwissel, de band gaat gewoon door.
     175  assert.match(speler, /if \(tapeMode && volgendeInRij > queue\.length - 1\) return;/,
     176    'de keten hoort te stoppen aan het eind van de band');
     177
     178  // ended roept next() aan, dus zonder deze tak begint de band opnieuw.
     179  assert.match(speler, /if \(tapeMode && currentIndex >= queue\.length - 1\) \{ pause\(\); return; \}/,
     180    'aan het eind stoppen in plaats van omlopen');
     181
     182  // En terugspoelen voorbij het begin levert de kop van de band op, niet het
     183  // laatste nummer.
     184  assert.match(speler, /if \(tapeMode && currentIndex === 0\) \{/,
     185    'aan het begin niet naar achteren omlopen');
     186
     187  // Alle drie de modulo's zijn nog aanwezig voor de NIET-bandmodus: een gewone
     188  // playlist hoort wel te blijven rondlopen.
     189  assert.equal((speler.match(/% queue\.length/g) || []).length, 3,
     190    'de omloop van een gewone wachtrij mag niet gesneuveld zijn');
     191});
     192
     193test('terugspoelen komt in het vorige nummer uit aan het EIND', () => {
     194  const speler = fs.readFileSync('src/assets/js/audio-player.js', 'utf8');
     195  // Zonder dit sprong terugspoelen naar de kop van het vorige nummer, en kwam
     196  // je nooit ergens in het midden uit -- dat is geen terugspoelen maar
     197  // terugspringen. Geldt voor de blob-motor; op MSE is de band een tijdlijn en
     198  // gaat het vanzelf goed.
     199  assert.match(speler, /pendingSeek = Number\.MAX_SAFE_INTEGER;/);
     200});
     201
     202test('aan het eind van de band gaat de stream dicht', () => {
     203  const speler = fs.readFileSync('src/assets/js/audio-player.js', 'utf8');
     204  // ZONDER DIT HANGT DE BAND. Bij een MediaSource vuurt `ended` pas als de
     205  // stream gesloten is. Sinds een bandje niet meer rondloopt haakt de keten na
     206  // het laatste nummer niets meer aan, en dan bleef hij op de laatste seconde
     207  // staan: teller stil, isPlaying() waar, spoelen draaiend. Gemeten op dev
     208  // (22-8): 125,6 van 125,7 en daar bleef hij staan.
     209  assert.match(speler, /const laatsteVanDeBand = tapeMode && qIndex >= queue\.length - 1;/);
     210  assert.match(speler, /if \(\(queue\.length === 1 \|\| laatsteVanDeBand\) && ms && ms\.readyState === 'open'\)/);
     211});
Note: See TracChangeset for help on using the changeset viewer.