Fix whisper should abort when cancel (#692)

* add whisper abort

* abort whisper when exit meida page

* add abort controller for youtubedr
This commit is contained in:
an-lee
2024-06-20 14:23:11 +08:00
committed by GitHub
parent 814be8369d
commit c50e5abf7b
6 changed files with 66 additions and 17 deletions

View File

@@ -22,6 +22,7 @@ class Whipser {
private binMain: string;
private bundledModelsDir: string;
public config: WhisperConfigType;
private abortController: AbortController;
constructor() {
const customWhisperPath = path.join(
@@ -87,6 +88,9 @@ class Whipser {
}
async check() {
this.abortController?.abort();
this.abortController = new AbortController();
const model = this.currentModel();
logger.debug(`Checking whisper model: ${model.savePath}`);
@@ -107,6 +111,7 @@ class Whipser {
commands.join(" "),
{
timeout: PROCESS_TIMEOUT,
signal: this.abortController.signal,
},
(error, stdout, stderr) => {
if (error) {
@@ -149,6 +154,9 @@ class Whipser {
): Promise<Partial<WhisperOutputType>> {
logger.debug("transcribing from local");
this.abortController?.abort();
this.abortController = new AbortController();
const { blob } = params;
let { file } = params;
@@ -199,6 +207,7 @@ class Whipser {
const command = spawn(this.binMain, commandArguments, {
timeout: PROCESS_TIMEOUT,
signal: this.abortController.signal,
});
return new Promise((resolve, reject) => {
@@ -224,8 +233,8 @@ class Whipser {
reject(err);
});
command.on("close", () => {
if (fs.pathExistsSync(outputFile)) {
command.on("close", (code) => {
if (code === 0 && fs.pathExistsSync(outputFile)) {
resolve(fs.readJson(outputFile));
} else {
reject(new Error("Transcription failed"));
@@ -234,6 +243,10 @@ class Whipser {
});
}
abort() {
this.abortController?.abort();
}
registerIpcHandlers() {
ipcMain.handle("whisper-config", async () => {
return this.config;
@@ -305,6 +318,10 @@ class Whipser {
});
}
});
ipcMain.handle("whisper-abort", async (_event) => {
return await this.abort();
});
}
}

View File

@@ -48,9 +48,11 @@ const validPathDomains =
/^https?:\/\/(youtu\.be\/|(www\.)?youtube\.com\/(embed|v|shorts)\/)/;
const ONE_MINUTE = 1000 * 60; // 1 minute
const TEN_MINUTES = 1000 * 60 * 10; // 10 minutes
class Youtubedr {
private binFile: string;
private abortController: AbortController | null = null;
constructor() {
this.binFile = path.join(
@@ -96,6 +98,9 @@ class Youtubedr {
webContents?: Electron.WebContents;
} = {}
): Promise<string> {
this.abortController?.abort();
this.abortController = new AbortController();
const {
quality,
filename = this.getYtVideoId(url) + ".mp4",
@@ -116,13 +121,20 @@ class Youtubedr {
let currentSpeed = "";
return new Promise((resolve, reject) => {
const proc = spawn(this.binFile, [
"download",
url,
`--quality=${quality || "medium"}`,
`--filename=${filename}`,
`--directory=${directory}`,
]);
const proc = spawn(
this.binFile,
[
"download",
url,
`--quality=${quality || "medium"}`,
`--filename=${filename}`,
`--directory=${directory}`,
],
{
timeout: TEN_MINUTES,
signal: this.abortController.signal,
}
);
proc.stdout.on("data", (data) => {
const output = data.toString();
@@ -239,6 +251,10 @@ class Youtubedr {
return false;
}
};
abortDownload() {
this.abortController?.abort();
}
}
export default new Youtubedr();

View File

@@ -463,6 +463,9 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
onProgress: (
callback: (event: IpcRendererEvent, progress: number) => void
) => ipcRenderer.on("whisper-on-progress", callback),
abort: () => {
return ipcRenderer.invoke("whisper-abort");
},
removeProgressListeners: () => {
ipcRenderer.removeAllListeners("whisper-on-progress");
},

View File

@@ -178,6 +178,7 @@ export const MediaPlayerProvider = ({
generateTranscription,
transcribing,
transcribingProgress,
abortGenerateTranscription,
} = useTranscriptions(media);
const {
@@ -559,6 +560,18 @@ export const MediaPlayerProvider = ({
};
}, [media?.src, ref, mediaProvider, layout?.playerHeight]);
/* cache last segment index */
useEffect(() => {
if (!media) return;
if (!currentSegmentIndex) return;
setCachedSegmentIndex(currentSegmentIndex);
}, [currentSegmentIndex]);
/*
* Update layout when window is resized
* Abort transcription when component is unmounted
*/
useEffect(() => {
calculateHeight();
@@ -572,17 +585,10 @@ export const MediaPlayerProvider = ({
return () => {
EnjoyApp.window.removeListeners();
abortGenerateTranscription();
};
}, []);
/* cache last segment index */
useEffect(() => {
if (!media) return;
if (!currentSegmentIndex) return;
setCachedSegmentIndex(currentSegmentIndex);
}, [currentSegmentIndex]);
return (
<>
<MediaPlayerProviderContext.Provider

View File

@@ -254,10 +254,16 @@ export const useTranscriptions = (media: AudioType | VideoType) => {
};
}, [transcription, media]);
const abortGenerateTranscription = () => {
EnjoyApp.whisper.abort();
setTranscribing(false);
};
return {
transcription,
transcribingProgress,
transcribing,
generateTranscription,
abortGenerateTranscription,
};
};

View File

@@ -269,6 +269,7 @@ type EnjoyAppType = {
}
) => Promise<Partial<WhisperOutputType>>;
onProgress: (callback: (event, progress: number) => void) => void;
abort: () => Promise<void>;
removeProgressListeners: () => Promise<void>;
};
ffmpeg: {