Fix overlapping audio when explaining multi-line PDF selections.
Use exclusive playback sessions so popup and pane do not play simultaneously, and stop standalone audio when the lecture pane is closed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -136,6 +136,14 @@ export async function playSelectionExplainStandalone(
|
|||||||
await playSelectionExplainExclusive(result);
|
await playSelectionExplainExclusive(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pauseStandalonePlayback(): void {
|
||||||
|
standaloneAudio?.pause();
|
||||||
|
if (standaloneAudio) {
|
||||||
|
standaloneAudio.onended = null;
|
||||||
|
standaloneAudio.removeAttribute("src");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function stopSelectionExplainStandalone(): void {
|
export function stopSelectionExplainStandalone(): void {
|
||||||
stopAllLecturePlayback();
|
stopAllLecturePlayback();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
beginSelectionExplainSession,
|
beginSelectionExplainSession,
|
||||||
isSelectionExplainSessionActive,
|
isSelectionExplainSessionActive,
|
||||||
playSelectionExplainAudio,
|
|
||||||
playSelectionExplainExclusive,
|
playSelectionExplainExclusive,
|
||||||
registerLecturePlaybackHandle,
|
registerLecturePlaybackHandle,
|
||||||
|
pauseStandalonePlayback,
|
||||||
stopAllLecturePlayback,
|
stopAllLecturePlayback,
|
||||||
} from "../application/playSelectionExplain";
|
} from "../application/playSelectionExplain";
|
||||||
import { getString } from "../../../utils/locale";
|
import { getString } from "../../../utils/locale";
|
||||||
@@ -264,10 +264,23 @@ export class LecturePaneView {
|
|||||||
|
|
||||||
this.updateActionButton();
|
this.updateActionButton();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
this.unregisterPlayback = registerLecturePlaybackHandle({
|
||||||
|
pause: () => {
|
||||||
|
this.paragraphPlayer?.pause();
|
||||||
|
this.player?.pause();
|
||||||
|
this.audioEl?.pause();
|
||||||
|
},
|
||||||
|
});
|
||||||
await this.refreshFromStore();
|
await this.refreshFromStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
this.unregisterPlayback?.();
|
||||||
|
this.unregisterPlayback = undefined;
|
||||||
|
this.paragraphPlayer?.pause();
|
||||||
|
this.player?.pause();
|
||||||
|
this.audioEl?.pause();
|
||||||
|
pauseStandalonePlayback();
|
||||||
for (const off of this.unsubscribers) off();
|
for (const off of this.unsubscribers) off();
|
||||||
this.unsubscribers = [];
|
this.unsubscribers = [];
|
||||||
this.player?.destroy();
|
this.player?.destroy();
|
||||||
@@ -313,7 +326,7 @@ export class LecturePaneView {
|
|||||||
}),
|
}),
|
||||||
lectureEvents.on("lecture:selection_explain", (d) => {
|
lectureEvents.on("lecture:selection_explain", (d) => {
|
||||||
if (String(getRegularItem(this.item).id) !== d.itemId) return;
|
if (String(getRegularItem(this.item).id) !== d.itemId) return;
|
||||||
void this.applySelectionExplainResult(d.result);
|
void this.applySelectionExplainResult(d.result, { play: false });
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -828,6 +841,8 @@ export class LecturePaneView {
|
|||||||
|
|
||||||
this.explainingSelection = true;
|
this.explainingSelection = true;
|
||||||
this.explainSelectionBtn.disabled = true;
|
this.explainSelectionBtn.disabled = true;
|
||||||
|
this.explainSessionId = beginSelectionExplainSession();
|
||||||
|
const sessionId = this.explainSessionId;
|
||||||
this.setStatus("summarizing", getString("lecture-selection-explaining"));
|
this.setStatus("summarizing", getString("lecture-selection-explaining"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -836,7 +851,9 @@ export class LecturePaneView {
|
|||||||
selectedText,
|
selectedText,
|
||||||
onProgress: (message) => this.setStatus("summarizing", message),
|
onProgress: (message) => this.setStatus("summarizing", message),
|
||||||
});
|
});
|
||||||
await this.applySelectionExplainResult(result);
|
if (!isSelectionExplainSessionActive(sessionId)) return;
|
||||||
|
await this.applySelectionExplainResult(result, { play: true });
|
||||||
|
if (!isSelectionExplainSessionActive(sessionId)) return;
|
||||||
this.setStatus("ready", getString("lecture-selection-done"));
|
this.setStatus("ready", getString("lecture-selection-done"));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
@@ -849,12 +866,17 @@ export class LecturePaneView {
|
|||||||
|
|
||||||
private async applySelectionExplainResult(
|
private async applySelectionExplainResult(
|
||||||
result: SelectionExplainResult,
|
result: SelectionExplainResult,
|
||||||
|
options?: { play?: boolean },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.lastSelectionResult = result;
|
this.lastSelectionResult = result;
|
||||||
this.renderSelectionExplain(result);
|
this.renderSelectionExplain(result);
|
||||||
|
|
||||||
await this.syncPdfHighlight(result.original, result.page);
|
await this.syncPdfHighlight(result.original, result.page);
|
||||||
|
|
||||||
|
if (options?.play === false) return;
|
||||||
|
|
||||||
|
stopAllLecturePlayback();
|
||||||
|
|
||||||
if (result.fromPrepared && result.matched) {
|
if (result.fromPrepared && result.matched) {
|
||||||
if (!this.paragraphPlayer) {
|
if (!this.paragraphPlayer) {
|
||||||
await this.refreshPlayer();
|
await this.refreshPlayer();
|
||||||
@@ -881,12 +903,10 @@ export class LecturePaneView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.audioEl) return;
|
if (!this.audioEl) return;
|
||||||
this.paragraphPlayer?.pause();
|
|
||||||
this.player?.pause();
|
|
||||||
this.playBtn.hidden = true;
|
this.playBtn.hidden = true;
|
||||||
this.pauseBtn.hidden = false;
|
this.pauseBtn.hidden = false;
|
||||||
try {
|
try {
|
||||||
await playSelectionExplainAudio(this.audioEl, result);
|
await playSelectionExplainExclusive(result, this.audioEl);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ztoolkit.log("[ChatPapers:Lecture] selection audio failed", e);
|
ztoolkit.log("[ChatPapers:Lecture] selection audio failed", e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -938,8 +958,8 @@ export class LecturePaneView {
|
|||||||
this.doc.createTextNode(getString("lecture-selection-replay")),
|
this.doc.createTextNode(getString("lecture-selection-replay")),
|
||||||
);
|
);
|
||||||
replayBtn.addEventListener("click", () => {
|
replayBtn.addEventListener("click", () => {
|
||||||
if (!this.audioEl || !this.lastSelectionResult) return;
|
if (!this.lastSelectionResult) return;
|
||||||
void playSelectionExplainAudio(this.audioEl, this.lastSelectionResult);
|
void playSelectionExplainExclusive(this.lastSelectionResult, this.audioEl);
|
||||||
});
|
});
|
||||||
this.selectionExplainEl.append(replayBtn);
|
this.selectionExplainEl.append(replayBtn);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user