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,
|
progress: ui.avrProgress,
|
||||||
// Only a Zidoo ever fills this card's now-playing in, and it can always seek.
|
// Only a Zidoo ever fills this card's now-playing in, and it can always seek.
|
||||||
canSeek: () => true,
|
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,
|
seek: null,
|
||||||
};
|
};
|
||||||
// Same reasoning as a room's own cover: drop one that will not load rather
|
// 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
|
// HEOS cannot seek, so only a stream Spotify can be asked to seek in: the
|
||||||
// same one the transport buttons show for (see paintRoom).
|
// same one the transport buttons show for (see paintRoom).
|
||||||
canSeek: () => room.onSpotify && room.spotifyAccount && !room.grouped,
|
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
|
seek: null, // {ms, stage, ...} from a drag of the cursor until the player catches up
|
||||||
};
|
};
|
||||||
rooms[key] = room;
|
rooms[key] = room;
|
||||||
@@ -248,27 +252,42 @@ function renderTrack(track, refs) {
|
|||||||
{position_ms, duration_ms} the poll hands back -- absent for anything
|
{position_ms, duration_ms} the poll hands back -- absent for anything
|
||||||
HEOS never sends a progress event for (an AVR input, an internet radio
|
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
|
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) {
|
function renderProgress(track, refs) {
|
||||||
const { progress } = refs;
|
const { progress } = refs;
|
||||||
progress.hidden = !track;
|
progress.hidden = !track;
|
||||||
refs.shown = null;
|
refs.shown = null;
|
||||||
|
refs.tick = null;
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
const { duration_ms: duration } = track;
|
const { duration_ms: duration } = track;
|
||||||
const position = seekPosition(track, refs);
|
const position = seekPosition(track, refs);
|
||||||
const known = typeof duration === 'number' && duration > 0 && typeof position === 'number';
|
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) {
|
if (known) {
|
||||||
refs.shown = track;
|
refs.shown = track;
|
||||||
refs.shownMs = position;
|
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('known', known);
|
||||||
progress.classList.toggle('seekable', known && Boolean(refs.canSeek()));
|
progress.classList.toggle('seekable', known && Boolean(refs.canSeek()));
|
||||||
progress.style.setProperty('--progress', percent);
|
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 = {
|
const labels = {
|
||||||
elapsed: known ? '+' + clock(elapsed) : '',
|
elapsed: known ? '+' + clock(elapsed) : '',
|
||||||
remaining: known ? '−' + clock(total - elapsed) : '',
|
remaining: known ? '−' + clock(total - elapsed) : '',
|
||||||
@@ -282,6 +301,24 @@ function renderProgress(track, refs) {
|
|||||||
el('[data-role="progress-total"]', progress).textContent = known ? clock(total) : '';
|
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
|
/* 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
|
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
|
rather than jumping to where it lands, so grabbing a time off-centre never
|
||||||
|
|||||||
Reference in new issue
Block a user