This commit is contained in:
1 parent
c20f241583
commit
94f4feeac1
1 file changed
+43
-6
+43
-6
@@ -80,6 +80,9 @@ const avrTrack = {
|
||||
progress: ui.avrProgress,
|
||||
// Only a Zidoo ever fills this card's now-playing in, and it can always seek.
|
||||
canSeek: () => true,
|
||||
// A film paused on the Zidoo reports its own play_state; nothing else
|
||||
// ever lands here with a position and duration to tick.
|
||||
playing: () => avrNowPlaying?.play_state === 'play',
|
||||
seek: null,
|
||||
};
|
||||
// Same reasoning as a room's own cover: drop one that will not load rather
|
||||
@@ -152,6 +155,7 @@ els('.room').forEach((node) => {
|
||||
// HEOS cannot seek, so only a stream Spotify can be asked to seek in: the
|
||||
// same one the transport buttons show for (see paintRoom).
|
||||
canSeek: () => room.onSpotify && room.spotifyAccount && !room.grouped,
|
||||
playing: () => room.playState === 'play',
|
||||
seek: null, // {ms, stage, ...} from a drag of the cursor until the player catches up
|
||||
};
|
||||
rooms[key] = room;
|
||||
@@ -248,27 +252,42 @@ function renderTrack(track, refs) {
|
||||
{position_ms, duration_ms} the poll hands back -- absent for anything
|
||||
HEOS never sends a progress event for (an AVR input, an internet radio
|
||||
stream with no fixed length), in which case the line stays plain. Around
|
||||
a seek, the position seekPosition() picks shows instead of the one reported. */
|
||||
a seek, the position seekPosition() picks shows instead of the one
|
||||
reported. Also leaves refs.tick pointing at this position and the moment
|
||||
it was true, for tickProgress() to run forward from until the next call
|
||||
here -- a poll, a drag, or a seek landing -- corrects it and moves the
|
||||
mark again. */
|
||||
function renderProgress(track, refs) {
|
||||
const { progress } = refs;
|
||||
progress.hidden = !track;
|
||||
refs.shown = null;
|
||||
refs.tick = null;
|
||||
if (!track) return;
|
||||
const { duration_ms: duration } = track;
|
||||
const position = seekPosition(track, refs);
|
||||
const known = typeof duration === 'number' && duration > 0 && typeof position === 'number';
|
||||
const percent = known ? Math.min(100, Math.max(0, (position / duration) * 100)) : 0;
|
||||
if (known) {
|
||||
refs.shown = track;
|
||||
refs.shownMs = position;
|
||||
refs.tick = { ms: position, at: Date.now(), duration };
|
||||
}
|
||||
paintProgress(refs, known, position, duration);
|
||||
}
|
||||
|
||||
/* Just the painting, shared with tickProgress() below so a between-polls
|
||||
nudge repaints exactly the same way a real sync does. */
|
||||
function paintProgress(refs, known, position, duration) {
|
||||
const { progress } = refs;
|
||||
// Whole seconds on both sides, so elapsed and remaining always add up to
|
||||
// the total shown at the end of the line -- and the knob is placed off
|
||||
// these same whole seconds, not the raw fractional position, so it never
|
||||
// creeps past where the digits still say it is.
|
||||
const total = known ? Math.floor(duration / 1000) : 0;
|
||||
const elapsed = known ? Math.min(total, Math.max(0, Math.floor(position / 1000))) : 0;
|
||||
const percent = known ? Math.min(100, Math.max(0, (elapsed / total) * 100)) : 0;
|
||||
progress.classList.toggle('known', known);
|
||||
progress.classList.toggle('seekable', known && Boolean(refs.canSeek()));
|
||||
progress.style.setProperty('--progress', percent);
|
||||
// Whole seconds on both sides, so elapsed and remaining always add up to
|
||||
// the total shown at the end of the line.
|
||||
const total = known ? Math.floor(duration / 1000) : 0;
|
||||
const elapsed = known ? Math.min(total, Math.max(0, Math.floor(position / 1000))) : 0;
|
||||
const labels = {
|
||||
elapsed: known ? '+' + clock(elapsed) : '',
|
||||
remaining: known ? '−' + clock(total - elapsed) : '',
|
||||
@@ -282,6 +301,24 @@ function renderProgress(track, refs) {
|
||||
el('[data-role="progress-total"]', progress).textContent = known ? clock(total) : '';
|
||||
}
|
||||
|
||||
/* Between polls the cursor otherwise sits still for POLL_MS at a time and
|
||||
then jumps -- so this nudges it along at real speed from the last known
|
||||
position, purely cosmetic until the next renderProgress() (a poll, most
|
||||
often) corrects it and moves the mark again. Skipped while a seek owns
|
||||
the display (seekPosition already covers that) or nothing is actually
|
||||
playing, so a paused cursor stays put. */
|
||||
function tickProgress(refs) {
|
||||
if (!refs.tick || !refs.shown || refs.seek || !refs.playing()) return;
|
||||
const { ms, at, duration } = refs.tick;
|
||||
const position = Math.min(duration, ms + (Date.now() - at));
|
||||
paintProgress(refs, true, position, duration);
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
tickProgress(avrTrack);
|
||||
Object.values(rooms).forEach(tickProgress);
|
||||
}, 250);
|
||||
|
||||
/* Drag the cursor, or either time riding with it, to jump backwards or
|
||||
forwards. Like the volume knob, it moves by how far the finger travels
|
||||
rather than jumping to where it lands, so grabbing a time off-centre never
|
||||
|
||||
Reference in new issue
Block a user