feat: add silent audio playback to prevent browser tab throttling during study
Play a nearly inaudible Web Audio API signal when study starts, stop it when study completes, is stopped, or fails. This prevents browsers from throttling timers and network requests in background tabs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
type ExamListItem,
|
||||
} from "~/service/wk";
|
||||
import { setUnauthorizedHandler } from "~/service/http";
|
||||
import { startSilentAudio, stopSilentAudio } from "~/service/silentAudio";
|
||||
import { accountStore, type AccountItem } from "~/store/account";
|
||||
import { getMergedHosts, settingsStore } from "~/store/settings";
|
||||
import type { CourseType } from "~/types/Course";
|
||||
@@ -722,6 +723,7 @@ const Account = () => {
|
||||
try {
|
||||
accountStore.getState().setAccountRunningStudy(account.id, true);
|
||||
touchStudyHeartbeat(account.id);
|
||||
startSilentAudio();
|
||||
appendStudyLog(`开始刷课:${course.name}`, account.id);
|
||||
await runStudyQueue({
|
||||
accountId: account.id,
|
||||
@@ -734,6 +736,7 @@ const Account = () => {
|
||||
setIsRunningStudy: () => {
|
||||
accountStore.getState().setAccountRunningStudy(account.id, false);
|
||||
clearStudyHeartbeat(account.id);
|
||||
stopSilentAudio();
|
||||
},
|
||||
onLog: (message: string, accoundID: string) => {
|
||||
touchStudyHeartbeat(accoundID);
|
||||
@@ -751,6 +754,7 @@ const Account = () => {
|
||||
} finally {
|
||||
accountStore.getState().setAccountRunningStudy(account.id, false);
|
||||
clearStudyHeartbeat(account.id);
|
||||
stopSilentAudio();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -762,6 +766,7 @@ const Account = () => {
|
||||
|
||||
accountStore.getState().setAccountRunningStudy(account.id, false);
|
||||
clearStudyHeartbeat(account.id);
|
||||
stopSilentAudio();
|
||||
appendStudyLog(`已发送停止刷课指令:${account.user.name}`, account.id);
|
||||
};
|
||||
|
||||
|
||||
57
src/service/silentAudio.ts
Normal file
57
src/service/silentAudio.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Silent audio playback to prevent browser tab throttling during long-running tasks.
|
||||
*
|
||||
* Uses the Web Audio API to produce a nearly inaudible signal that keeps the
|
||||
* browser from suspending the tab's timers and network requests.
|
||||
*/
|
||||
|
||||
let audioContext: AudioContext | null = null;
|
||||
let oscillatorNode: OscillatorNode | null = null;
|
||||
let gainNode: GainNode | null = null;
|
||||
|
||||
/**
|
||||
* Start playing silent audio. Safe to call multiple times — duplicate calls
|
||||
* are ignored if audio is already playing.
|
||||
*/
|
||||
export const startSilentAudio = () => {
|
||||
if (oscillatorNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
audioContext = new AudioContext();
|
||||
gainNode = audioContext.createGain();
|
||||
gainNode.gain.value = 0.001; // Nearly silent
|
||||
|
||||
oscillatorNode = audioContext.createOscillator();
|
||||
oscillatorNode.type = "sine";
|
||||
oscillatorNode.frequency.value = 1; // Sub-bass, inaudible
|
||||
oscillatorNode.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
oscillatorNode.start();
|
||||
} catch {
|
||||
// AudioContext may be unavailable in some environments; degrade silently.
|
||||
oscillatorNode = null;
|
||||
gainNode = null;
|
||||
audioContext = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop playing silent audio and release resources.
|
||||
*/
|
||||
export const stopSilentAudio = () => {
|
||||
try {
|
||||
oscillatorNode?.stop();
|
||||
} catch {
|
||||
// Already stopped or never started
|
||||
}
|
||||
|
||||
oscillatorNode?.disconnect();
|
||||
gainNode?.disconnect();
|
||||
audioContext?.close();
|
||||
|
||||
oscillatorNode = null;
|
||||
gainNode = null;
|
||||
audioContext = null;
|
||||
};
|
||||
Reference in New Issue
Block a user