stream: add a skip-to-next button and /skip route
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
04ea54c03e
commit
a468d78153
2 changed files with 33 additions and 0 deletions
|
|
@ -41,6 +41,16 @@
|
||||||
transition: background .15s;
|
transition: background .15s;
|
||||||
}
|
}
|
||||||
.share button:hover { background: rgba(155,140,255,.3); }
|
.share button:hover { background: rgba(155,140,255,.3); }
|
||||||
|
.actions { display: flex; gap: .5rem; margin-top: .75rem; }
|
||||||
|
.actions button, .actions a {
|
||||||
|
flex: 1; text-align: center; text-decoration: none;
|
||||||
|
padding: .6rem .9rem; font-size: .85rem; font-weight: 600; cursor: pointer;
|
||||||
|
color: #f2f0f7; background: rgba(155,140,255,.18);
|
||||||
|
border: 1px solid rgba(155,140,255,.35); border-radius: 10px;
|
||||||
|
transition: background .15s;
|
||||||
|
}
|
||||||
|
.actions button:hover, .actions a:hover { background: rgba(155,140,255,.3); }
|
||||||
|
.actions button:disabled { opacity: .5; cursor: default; }
|
||||||
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
|
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
|
||||||
background: #4ade80; margin-right: .4rem; vertical-align: middle;
|
background: #4ade80; margin-right: .4rem; vertical-align: middle;
|
||||||
box-shadow: 0 0 0 0 rgba(74,222,128,.6); animation: pulse 2s infinite; }
|
box-shadow: 0 0 0 0 rgba(74,222,128,.6); animation: pulse 2s infinite; }
|
||||||
|
|
@ -58,6 +68,9 @@
|
||||||
<div class="title" id="title">—</div>
|
<div class="title" id="title">—</div>
|
||||||
<div class="artist" id="artist"></div>
|
<div class="artist" id="artist"></div>
|
||||||
<audio id="player" controls autoplay preload="none"></audio>
|
<audio id="player" controls autoplay preload="none"></audio>
|
||||||
|
<div class="actions">
|
||||||
|
<button id="skipBtn" type="button">⏭ Suivant</button>
|
||||||
|
</div>
|
||||||
<div class="share">
|
<div class="share">
|
||||||
<input id="streamUrl" type="text" readonly>
|
<input id="streamUrl" type="text" readonly>
|
||||||
<button id="copyBtn" type="button">Copier</button>
|
<button id="copyBtn" type="button">Copier</button>
|
||||||
|
|
@ -113,6 +126,15 @@
|
||||||
setTimeout(() => { copyBtn.textContent = prev; }, 1500);
|
setTimeout(() => { copyBtn.textContent = prev; }, 1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Passer au morceau suivant.
|
||||||
|
const skipBtn = document.getElementById("skipBtn");
|
||||||
|
skipBtn.addEventListener("click", async () => {
|
||||||
|
skipBtn.disabled = true;
|
||||||
|
try { await fetch("/skip", { method: "POST" }); } catch (e) { /* ignore */ }
|
||||||
|
// Laisser le temps à la bascule, puis rafraîchir l'affichage.
|
||||||
|
setTimeout(() => { skipBtn.disabled = false; poll(); }, 900);
|
||||||
|
});
|
||||||
|
|
||||||
poll();
|
poll();
|
||||||
setInterval(poll, 5000);
|
setInterval(poll, 5000);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -111,3 +111,14 @@ harbor.http.register(
|
||||||
resp.json({title=m["title"], artist=m["artist"]})
|
resp.json({title=m["title"], artist=m["artist"]})
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Passer au morceau suivant : on saute le morceau en cours sur la source
|
||||||
|
# diffusée. request.dynamic a déjà préchargé le suivant, donc l'enchaînement
|
||||||
|
# est immédiat (le prochain /next est demandé au daemon dans la foulée).
|
||||||
|
harbor.http.register(
|
||||||
|
port=8000, method="POST", "/skip",
|
||||||
|
fun(_, resp) -> begin
|
||||||
|
source.skip(radio)
|
||||||
|
resp.json({skipped=true})
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue