feat(shop): storefront release page gets ▶ Listen too (Apple preview / YouTube / Beatport)

Same audio affordance as the kiosk on release.html — Apple 30s preview (instant audio),
else YouTube embed, else Beatport/Bandcamp embed, + country-scoped Apple Music link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-06-23 23:22:46 +10:00
parent aa56e874ad
commit d6e1e15982

View File

@ -71,6 +71,7 @@ async function render(){
${show('title')?`<h1>${esc(r.title||'')}</h1>`:''}${show('artist')?`<div class="artist">${aHtml}</div>`:''}
<div class="meta">${meta.map(m=>`<span class="pill">${esc(String(m))}</span>`).join('')}</div>
<div class="meta">${tags.map(t=>`<a class="pill" href="/${t.k}/${encodeURIComponent(t.n)}">${esc(t.n)}</a>`).join('')}</div>
<div id="listen" style="margin:4px 0 10px"></div>
${show('cart')||show('price')?`<div class="copies">${(d.copies||[]).map(c=>`<div class="copy">
<div class="cd"><div class="pr">${money(c.price)}</div><div class="muted">${esc(c.condition||'')}${c.sleeve_cond?' / '+esc(c.sleeve_cond):''} · ${esc(c.sku)}</div></div>${buy(c)}</div>`).join('')||'<div class="muted">no copies in stock</div>'}</div>`:''}
<div style="margin-top:10px"><a href="/wantlist?artist=${encodeURIComponent(r.artist||'')}&title=${encodeURIComponent(r.title||'')}&format=${encodeURIComponent((r.format||'').split(',')[0]||'')}&release_id=${r.id}" class="muted" style="font-size:13px;text-decoration:underline">${(d.copies||[]).length?"Want a different copy? Request it":"Not in stock — request this record"} →</a></div>
@ -78,7 +79,26 @@ async function render(){
</div>
${show('tracklist')&&d.tracks.length?`<div class="sec"><h3>Tracklist</h3><table>${d.tracks.map(t=>`<tr><td class="muted" style="width:40px">${esc(t.position||'')}</td><td>${esc(t.title||'')}</td><td class="muted" style="width:50px;text-align:right">${esc(t.duration||'')}</td></tr>`).join('')}</table></div>`:''}
${show('description')&&r.notes?`<div class="sec"><h3>Notes</h3><div class="desc">${esc(r.notes)}</div></div>`:''}`;
loadAudio(r.id);
}
function ytId(uri){ const m=String(uri||'').match(/(?:v=|youtu\.be\/|embed\/)([\w-]{11})/); return m?m[1]:(/^[\w-]{11}$/.test(uri||'')?uri:null); }
async function loadAudio(rid){
const z=$('#listen'); if(!z) return; z.innerHTML='';
const d=await fetch('/shop/audio/'+rid).then(r=>r.json()).catch(()=>({})), v=d.videos||[];
let html='';
if(d.apple_preview){ html=`<button class="btn" id="lbtn" onclick="togglePreview('${esc(d.apple_preview)}')">▶ Listen <span style="opacity:.7;font-weight:500">· 30s</span></button><audio id="aud" onended="resetLbtn()"></audio>`; }
else { const yt=v.map(x=>ytId(x.uri)).find(Boolean);
if(yt) html=`<button class="btn" onclick="playYT('${yt}')">▶ Watch / listen</button>`;
else if(d.beatport_embed) html=d.beatport_embed;
else if(d.bandcamp_embed) html=d.bandcamp_embed;
}
if(d.apple && d.apple.url) html+=` <a href="${esc(d.apple.url)}" target="_blank" class="muted" style="font-size:13px;margin-left:12px">Apple Music ↗</a>`;
z.innerHTML=html;
}
function resetLbtn(){ const b=$('#lbtn'); if(b) b.innerHTML='▶ Listen <span style="opacity:.7;font-weight:500">· 30s</span>'; }
function togglePreview(url){ const au=$('#aud'), b=$('#lbtn'); if(!au) return;
if(au.paused){ if(au.src!==url) au.src=url; au.play(); b.textContent='⏸ Pause'; } else { au.pause(); resetLbtn(); } }
function playYT(id){ $('#listen').innerHTML=`<iframe style="width:100%;max-width:560px;height:300px;border:0;border-radius:12px" src="https://www.youtube.com/embed/${id}?autoplay=1&rel=0" allow="autoplay" allowfullscreen></iframe>`; }
boot();
</script>
</body>