v0.51.0: the web mix player has previous and next buttons

This commit is contained in:
2026-10-10 23:54:57 -07:00
parent 96fac71075
commit 976d338574
5 changed files with 70 additions and 19 deletions
+2 -1
View File
@@ -659,7 +659,8 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
nobody can answer a prompt. Steps: `ServerCheckWorker` (`ssh test -d`; an
existing folder asks before replacing it), `ExportWorker`, then
`UploadWorker`, all shown in `gui/web_upload_progress.py`. The web
player has a **shuffle** button (and the S key): a shuffled walk that starts at
player has **previous/next** buttons (the arrow keys, wrapping at either
end, through the shuffle order when on) and a **shuffle** button (and the S key): a shuffled walk that starts at
the current song and plays every song once. A mix can carry several
images (`plan.images`): the dialog always keeps one empty row to add
another, and the page shows them in order under the description, a blank
+1 -1
View File
@@ -1,3 +1,3 @@
"""LinTunes — iTunes-style music library manager and player for Linux."""
__version__ = "0.50.0"
__version__ = "0.51.0"
+18 -3
View File
@@ -27,7 +27,7 @@
.audiojs audio { position: absolute; left: -1px; }
.audiojs {
width: 286px;
width: 334px;
height: 36px;
background: #eee;
color: #000;
@@ -40,9 +40,9 @@
}
.audiojs .play-pause {
width: 40px;
width: 36px;
height: 40px;
padding: 4px 6px;
padding: 4px 2px;
margin: 0px;
float: left;
overflow: hidden;
@@ -88,6 +88,21 @@
.audiojs .time em { padding: 0px 2px 0px 0px; color: #000; font-style: normal; }
.audiojs .time strong { padding: 0px 0px 0px 2px; font-weight: normal; }
/* Previous / next, either side of play-pause: plain black glyphs. */
.audiojs .skip {
float: left;
width: 24px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
color: #000;
cursor: pointer;
}
.audiojs .prev { margin-left: 4px; }
.audiojs .skip:hover { color: var(--accent, #8c764a); }
.audiojs .skip:focus-visible { outline: 1px dotted #000; }
/* Shuffle: black like the other glyphs while off, the accent while on
* (the same fill as the progress bar, with the text color picked for it). */
.audiojs .shuffle {
+41 -14
View File
@@ -24,10 +24,20 @@
"use strict";
var WRAPPER =
'<div class="skip prev" role="button" tabindex="0" title="Previous (←)">' +
'<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">' +
'<path d="M6 5h2.5v14H6zM20 5v14L9 12z" fill="currentColor"/>' +
'</svg>' +
'</div>' +
'<div class="play-pause">' +
'<p class="play"></p><p class="pause"></p>' +
'<p class="loading"></p><p class="error"></p>' +
'</div>' +
'<div class="skip next" role="button" tabindex="0" title="Next (→)">' +
'<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">' +
'<path d="M15.5 5H18v14h-2.5zM4 5v14l11-7z" fill="currentColor"/>' +
'</svg>' +
'</div>' +
'<div class="scrubber"><div class="progress"></div><div class="loaded"></div></div>' +
'<div class="time"><em class="played">00:00</em>/<strong class="duration">00:00</strong></div>' +
'<div class="error-message"></div>' +
@@ -62,7 +72,9 @@
played: wrap.querySelector(".played"),
duration: wrap.querySelector(".duration"),
error: wrap.querySelector(".error-message"),
shuffle: wrap.querySelector(".shuffle")
shuffle: wrap.querySelector(".shuffle"),
prev: wrap.querySelector(".prev"),
next: wrap.querySelector(".next")
};
function state(name) {
@@ -127,6 +139,8 @@
return {
element: audio,
shuffleButton: ui.shuffle,
prevButton: ui.prev,
nextButton: ui.next,
load: function (src) {
ui.progress.style.width = "0%";
ui.loaded.style.width = "0%";
@@ -176,16 +190,30 @@
function first() { return (order || items)[0]; }
function last() { var list = order || items; return list[list.length - 1]; }
player.shuffleButton.addEventListener("click", function () {
setShuffle(!order);
});
player.shuffleButton.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
e.stopPropagation();
setShuffle(!order);
}
});
// The previous/next buttons and the arrow keys: wrap around at either
// end, through the shuffled walk when shuffle is on.
function goNext() {
var current = document.querySelector("ol li.playing");
select((current && step(current, 1)) || first(), true);
}
function goPrev() {
var current = document.querySelector("ol li.playing");
select((current && step(current, -1)) || last(), true);
}
function button(el, action) {
el.addEventListener("click", action);
el.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
e.stopPropagation();
action();
}
});
}
button(player.shuffleButton, function () { setShuffle(!order); });
button(player.prevButton, goPrev);
button(player.nextButton, goNext);
function select(li, autoplay) {
items.forEach(function (other) { other.classList.remove("playing"); });
@@ -215,13 +243,12 @@
var tag = (target.tagName || "").toLowerCase();
if (tag === "input" || tag === "textarea" || target.isContentEditable) return;
var current = document.querySelector("ol li.playing");
if (e.key === "ArrowRight") {
e.preventDefault();
select((current && step(current, 1)) || first(), true);
goNext();
} else if (e.key === "ArrowLeft") {
e.preventDefault();
select((current && step(current, -1)) || last(), true);
goPrev();
} else if ((e.key === "s" || e.key === "S") && !e.ctrlKey && !e.metaKey && !e.altKey) {
e.preventDefault();
setShuffle(!order);
+8
View File
@@ -565,3 +565,11 @@ def test_upload_window_shows_the_size(qapp, window):
_wait_for(qapp, lambda: _FakeUpload.runs)
from lintunes.gui import web_upload_progress as wp
assert "KB" in window._web_progress.status(wp.EXPORT)
def test_player_ships_previous_and_next_buttons():
js = (exporter.templates() / "player.js").read_text()
css = (exporter.templates() / "player.css").read_text()
assert 'class="skip prev"' in js and 'class="skip next"' in js
assert "button(player.prevButton, goPrev)" in js
assert ".audiojs .skip" in css