Commit 7c3154a4 by PLN (Algolia)

fix(foundry,armada): sound audio streaming — HTTP/1.1, show duration, offer the original

PLN couldn't hear the Loituma stems and wanted the original too. The bytes/format
were fine (PCM_16/44.1k, audio/x-wav, 206 Range all verified) — the problems were
presentation + protocol:

- Both audio servers defaulted to HTTP/1.0, which closes the connection after each
  response. <audio> seeking fires many small Range requests, so HTTP/1.0 makes
  playback/seek flaky in real browsers. Set protocol_version="HTTP/1.1" on the
  Foundry Handler and armada RangeHandler (every response sets an exact
  Content-Length, so keep-alive is safe). Reliable streaming + seeking now.
- The stem players used preload="none", so they showed a misleading "0:00 / 0:00"
  until play — easy to read as "broken". Now preload="metadata": durations load and
  display (verified 164s on all five).
- The ORIGINAL is now offered on separated cards too (an "original" row), not just on
  raw catches — so PLN can A/B the source against each stem.

Verified end to end in Chromium: original + all 4 stems load 164s duration and
currentTime advances on play, no errors.
parent e399a28f
......@@ -56,6 +56,9 @@ def _vibe_rank(qvec, n, drop=-1):
class RangeHandler(SimpleHTTPRequestHandler):
# HTTP/1.1 keep-alive → reliable <audio> seeking (many small Range requests).
protocol_version = "HTTP/1.1"
def do_GET(self):
if self.path.split("?", 1)[0] in ("/vibe", "/similar"):
return self._api()
......
......@@ -85,6 +85,10 @@ def _run_loops(job, workspace, slug, bars, top_n, mode):
class Handler(SimpleHTTPRequestHandler):
workspace: Path = Path()
# HTTP/1.1 keep-alive: <audio> seeking fires many small Range requests; reusing
# the connection makes streaming/seek reliable (HTTP/1.0 closes after each →
# stalls). Safe here because every response sets an exact Content-Length.
protocol_version = "HTTP/1.1"
# ── routing ─────────────────────────────────────────────────────────
def do_GET(self):
......
......@@ -147,7 +147,7 @@ function fillModels(){
function stemRow(slug, st){
const div=document.createElement("div"); div.className="stem "+st.name;
div.innerHTML=`<span class="tag">${st.name}</span>
<audio controls preload="none" src="/media/${slug}/${st.path}"></audio>
<audio controls preload="metadata" src="/media/${slug}/${st.path}"></audio>
<a class="dl" href="/media/${slug}/${st.path}" download>⬇ wav</a>`;
return div;
}
......@@ -161,11 +161,11 @@ function catchCard(c){
<h2>${c.title}</h2>
<div class="meta">${c.slug}${c.source_url?` · <a href="${c.source_url}" target="_blank">source</a>`:""}${c.duration?` · ${Math.round(c.duration)}s`:""}</div>
</div><div class="row">${badge}${c.separated?"":`<button data-sep="${c.slug}">Separate</button>`}</div></div>`;
if(!c.separated){
el.insertAdjacentHTML("beforeend",
`<div class="stem"><span class="tag" style="color:var(--mute)">source</span>
<audio controls preload="none" src="/media/${c.slug}/${c.source_path}"></audio></div>`);
} else {
// the original is always offered — for raw catches AND alongside the stems
el.insertAdjacentHTML("beforeend",
`<div class="stem"><span class="tag" style="color:var(--mute)">original</span>
<audio controls preload="metadata" src="/media/${c.slug}/${c.source_path}"></audio></div>`);
if(c.separated){
STEMS.forEach(name=>{ const s=c.stems.find(x=>x.name===name); if(s) el.appendChild(stemRow(c.slug,s)); });
const lp=document.createElement("div"); lp.className="loops"; lp.id="lp-"+c.slug;
lp.innerHTML=`<div class="loops-head"><span class="lbl">loops</span>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment