feat/optimization-and-audio #1

Merged
zhilv merged 4 commits from feat/optimization-and-audio into main 2026-04-26 20:51:23 +08:00
2 changed files with 62 additions and 0 deletions
Showing only changes of commit 0c0d2a0292 - Show all commits

View File

@@ -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);
};

View 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;
};