Fix emit loop zero-delay restart and resume initial-delay checks
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// Maintainer-controlled web client version.
|
// Maintainer-controlled web client version.
|
||||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||||
window.CHGRID_WEB_VERSION = "2026.03.01 R329";
|
window.CHGRID_WEB_VERSION = "2026.03.01 R330";
|
||||||
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
||||||
window.CHGRID_TIME_ZONE = "America/Detroit";
|
window.CHGRID_TIME_ZONE = "America/Detroit";
|
||||||
|
|||||||
@@ -227,19 +227,27 @@ export class ItemEmitRuntime {
|
|||||||
element.playbackRate = initialRates.playbackRate;
|
element.playbackRate = initialRates.playbackRate;
|
||||||
const initialDelaySeconds = resolveEmitInitialDelaySeconds(item);
|
const initialDelaySeconds = resolveEmitInitialDelaySeconds(item);
|
||||||
const loopDelaySeconds = resolveEmitLoopDelaySeconds(item);
|
const loopDelaySeconds = resolveEmitLoopDelaySeconds(item);
|
||||||
|
const resumeState = this.resumeStateByItemId.get(item.id);
|
||||||
|
const matchingResumeState = resumeState && resumeState.soundUrl === soundUrl ? resumeState : null;
|
||||||
const onEnded = () => {
|
const onEnded = () => {
|
||||||
const delaySeconds = this.outputs.get(item.id)?.loopDelaySeconds ?? 0;
|
const current = this.outputs.get(item.id);
|
||||||
|
if (!current || current.element !== element) return;
|
||||||
|
const delaySeconds = current.loopDelaySeconds ?? 0;
|
||||||
|
if (delaySeconds <= 0) {
|
||||||
|
this.nextEmitStartAtMs.delete(item.id);
|
||||||
|
this.tryStartEmitPlayback(item.id, element);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.nextEmitStartAtMs.set(item.id, Date.now() + delaySeconds * 1000);
|
this.nextEmitStartAtMs.set(item.id, Date.now() + delaySeconds * 1000);
|
||||||
};
|
};
|
||||||
element.addEventListener('ended', onEnded);
|
element.addEventListener('ended', onEnded);
|
||||||
const resumeState = this.resumeStateByItemId.get(item.id);
|
if (matchingResumeState) {
|
||||||
if (resumeState && resumeState.soundUrl === soundUrl) {
|
|
||||||
const nowMs = Date.now();
|
const nowMs = Date.now();
|
||||||
const elapsedSeconds = Math.max(0, (nowMs - resumeState.savedAtMs) / 1000);
|
const elapsedSeconds = Math.max(0, (nowMs - matchingResumeState.savedAtMs) / 1000);
|
||||||
const effectiveRate = resumeState.playbackRate > 0 ? resumeState.playbackRate : 1;
|
const effectiveRate = matchingResumeState.playbackRate > 0 ? matchingResumeState.playbackRate : 1;
|
||||||
const durationSeconds = resumeState.durationSeconds;
|
const durationSeconds = matchingResumeState.durationSeconds;
|
||||||
if (durationSeconds && durationSeconds > 0) {
|
if (durationSeconds && durationSeconds > 0) {
|
||||||
const loopDelaySeconds = Math.max(0, resumeState.loopDelaySeconds);
|
const loopDelaySeconds = Math.max(0, matchingResumeState.loopDelaySeconds);
|
||||||
const playWallSeconds = durationSeconds / effectiveRate;
|
const playWallSeconds = durationSeconds / effectiveRate;
|
||||||
const cycleWallSeconds = playWallSeconds + loopDelaySeconds;
|
const cycleWallSeconds = playWallSeconds + loopDelaySeconds;
|
||||||
const seekAndPlayNow = (targetTimeSeconds: number) => {
|
const seekAndPlayNow = (targetTimeSeconds: number) => {
|
||||||
@@ -273,10 +281,13 @@ export class ItemEmitRuntime {
|
|||||||
} else {
|
} else {
|
||||||
seekAndPlayNow(0);
|
seekAndPlayNow(0);
|
||||||
}
|
}
|
||||||
} else if (resumeState.wasPlaying) {
|
} else if (matchingResumeState.wasPlaying) {
|
||||||
const playRemainingWallSeconds = Math.max(0, (durationSeconds - Math.max(0, resumeState.currentTimeSeconds)) / effectiveRate);
|
const playRemainingWallSeconds = Math.max(
|
||||||
|
0,
|
||||||
|
(durationSeconds - Math.max(0, matchingResumeState.currentTimeSeconds)) / effectiveRate,
|
||||||
|
);
|
||||||
if (elapsedSeconds < playRemainingWallSeconds) {
|
if (elapsedSeconds < playRemainingWallSeconds) {
|
||||||
seekAndPlayNow(Math.max(0, resumeState.currentTimeSeconds) + elapsedSeconds * effectiveRate);
|
seekAndPlayNow(Math.max(0, matchingResumeState.currentTimeSeconds) + elapsedSeconds * effectiveRate);
|
||||||
} else {
|
} else {
|
||||||
const afterTrackSeconds = elapsedSeconds - playRemainingWallSeconds;
|
const afterTrackSeconds = elapsedSeconds - playRemainingWallSeconds;
|
||||||
if (cycleWallSeconds > 0) {
|
if (cycleWallSeconds > 0) {
|
||||||
@@ -329,7 +340,7 @@ export class ItemEmitRuntime {
|
|||||||
gain,
|
gain,
|
||||||
panner,
|
panner,
|
||||||
});
|
});
|
||||||
if (!resumeState && !this.nextEmitStartAtMs.has(item.id) && initialDelaySeconds > 0) {
|
if (!matchingResumeState && !this.nextEmitStartAtMs.has(item.id) && initialDelaySeconds > 0) {
|
||||||
this.nextEmitStartAtMs.set(item.id, Date.now() + initialDelaySeconds * 1000);
|
this.nextEmitStartAtMs.set(item.id, Date.now() + initialDelaySeconds * 1000);
|
||||||
}
|
}
|
||||||
this.resumeStateByItemId.delete(item.id);
|
this.resumeStateByItemId.delete(item.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user