Add Next/Previous clip buttons
Skip forward/back through the current folder queue -- the player already keeps the folder as an ordered queue with an index, so next/previous just move within it (no-op at the ends). New PlayerStateManager.next/previous/skip, the /api/control/next|previous routes, and compact ⏮/⏭ buttons in the transport row. Lets you skip clips completely without a resume/history feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
99b5c27e65
commit
a6400d56f5
5 changed files with 57 additions and 2 deletions
|
|
@ -194,6 +194,25 @@ class PlayerStateManager:
|
|||
if pid is not None:
|
||||
self._kodi.call("Player.PlayPause", playerid=pid)
|
||||
|
||||
def skip(self, delta):
|
||||
"""Jump to another clip in the current folder queue (+1 next, -1
|
||||
previous). No-op at the ends, or when nothing is queued."""
|
||||
with self._lock:
|
||||
if not self._queue_files:
|
||||
return
|
||||
new_index = self._queue_index + delta
|
||||
if new_index < 0 or new_index >= len(self._queue_files):
|
||||
return
|
||||
self._queue_index = new_index
|
||||
target = self._queue_files[new_index]
|
||||
self._open(target)
|
||||
|
||||
def next(self):
|
||||
self.skip(1)
|
||||
|
||||
def previous(self):
|
||||
self.skip(-1)
|
||||
|
||||
def seek(self, offset_seconds):
|
||||
pid = self._active_player_id()
|
||||
if pid is not None:
|
||||
|
|
|
|||
|
|
@ -57,6 +57,24 @@ def playpause():
|
|||
return jsonify({"ok": True})
|
||||
|
||||
|
||||
@bp.route("/control/next", methods=["POST"])
|
||||
def next_clip():
|
||||
try:
|
||||
current_app.player.next()
|
||||
except KodiError as exc:
|
||||
return jsonify({"error": f"player unavailable: {exc}"}), 503
|
||||
return jsonify({"ok": True})
|
||||
|
||||
|
||||
@bp.route("/control/previous", methods=["POST"])
|
||||
def previous_clip():
|
||||
try:
|
||||
current_app.player.previous()
|
||||
except KodiError as exc:
|
||||
return jsonify({"error": f"player unavailable: {exc}"}), 503
|
||||
return jsonify({"ok": True})
|
||||
|
||||
|
||||
@bp.route("/control/seek", methods=["POST"])
|
||||
def seek():
|
||||
body = request.get_json(force=True, silent=True) or {}
|
||||
|
|
|
|||
|
|
@ -135,18 +135,26 @@ html, body {
|
|||
|
||||
.transport-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
gap: 0.4rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.transport-row button {
|
||||
flex: 1;
|
||||
padding: 0.8rem;
|
||||
padding: 0.8rem 0.3rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #444;
|
||||
background: #1c1c1c;
|
||||
color: #eee;
|
||||
font-size: 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Prev/Next are compact icon buttons; the middle three carry the labels. */
|
||||
#btn-prev, #btn-next {
|
||||
flex: 0 0 auto;
|
||||
min-width: 2.6rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.volume-row {
|
||||
|
|
|
|||
|
|
@ -120,6 +120,14 @@
|
|||
api("/api/control/playpause", { method: "POST" });
|
||||
});
|
||||
|
||||
el("btn-prev").addEventListener("click", () => {
|
||||
api("/api/control/previous", { method: "POST" });
|
||||
});
|
||||
|
||||
el("btn-next").addEventListener("click", () => {
|
||||
api("/api/control/next", { method: "POST" });
|
||||
});
|
||||
|
||||
el("btn-back30").addEventListener("click", () => {
|
||||
api("/api/control/seek", { method: "POST", body: JSON.stringify({ offset: -30 }) });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@
|
|||
<span id="time-dur">0:00</span>
|
||||
</div>
|
||||
<div class="transport-row">
|
||||
<button id="btn-prev" title="Previous clip">⏮</button>
|
||||
<button id="btn-back30">« 30</button>
|
||||
<button id="btn-playpause">Play/Pause</button>
|
||||
<button id="btn-fwd30">30 »</button>
|
||||
<button id="btn-next" title="Next clip">⏭</button>
|
||||
</div>
|
||||
<div class="volume-row">
|
||||
<span>Vol</span>
|
||||
|
|
|
|||
Loading…
Reference in a new issue