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;
|
||||
}
|
||||
.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%;
|
||||
background: #4ade80; margin-right: .4rem; vertical-align: middle;
|
||||
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="artist" id="artist"></div>
|
||||
<audio id="player" controls autoplay preload="none"></audio>
|
||||
<div class="actions">
|
||||
<button id="skipBtn" type="button">⏭ Suivant</button>
|
||||
</div>
|
||||
<div class="share">
|
||||
<input id="streamUrl" type="text" readonly>
|
||||
<button id="copyBtn" type="button">Copier</button>
|
||||
|
|
@ -113,6 +126,15 @@
|
|||
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();
|
||||
setInterval(poll, 5000);
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -111,3 +111,14 @@ harbor.http.register(
|
|||
resp.json({title=m["title"], artist=m["artist"]})
|
||||
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