feat(ocr): reintroduce batch processing

This commit is contained in:
Elian Doran
2026-04-02 10:08:24 +03:00
parent 8d255d1b89
commit 731fece258

View File

@@ -1,4 +1,8 @@
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import server from "../../../services/server";
import toast from "../../../services/toast";
import { FormTextBoxWithUnit } from "../../react/FormTextBox";
import FormToggle from "../../react/FormToggle";
import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
@@ -92,6 +96,83 @@ function OcrSettings() {
onChange={setOcrMinConfidence}
/>
</OptionsRow>
<BatchProcessing />
</OptionsSection>
);
}
interface BatchProgress {
inProgress: boolean;
total: number;
processed: number;
percentage?: number;
}
function BatchProcessing() {
const [ progress, setProgress ] = useState<BatchProgress | null>(null);
const pollingRef = useRef<ReturnType<typeof setInterval>>(null);
const pollProgress = useCallback(() => {
server.get<BatchProgress>("ocr/batch-progress").then((data) => {
setProgress(data);
if (!data.inProgress && pollingRef.current) {
clearInterval(pollingRef.current);
pollingRef.current = null;
toast.showMessage(t("images.batch_ocr_completed", { processed: data.processed }));
}
});
}, []);
// Clean up polling on unmount.
useEffect(() => {
return () => {
if (pollingRef.current) {
clearInterval(pollingRef.current);
}
};
}, []);
async function startBatch() {
try {
const result = await server.post<{ success: boolean; message?: string }>("ocr/batch-process");
if (result.success) {
toast.showMessage(t("images.batch_ocr_starting"));
pollingRef.current = setInterval(pollProgress, 2000);
pollProgress();
} else {
toast.showError(result.message || t("images.batch_ocr_error", { error: "Unknown" }));
}
} catch {
// Server errors are already shown as toasts by server.ts.
}
}
const isRunning = progress?.inProgress ?? false;
return (
<OptionsRow name="batch-ocr" label={t("images.batch_ocr_title")} description={t("images.batch_ocr_description")}>
{isRunning ? (
<div style={{ width: "100%" }}>
<div className="progress" style={{ height: "24px" }}>
<div
className="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
style={{ width: `${progress?.percentage ?? 0}%` }}
>
{t("images.batch_ocr_progress", { processed: progress?.processed ?? 0, total: progress?.total ?? 0 })}
</div>
</div>
</div>
) : (
<button
type="button"
className="btn btn-secondary"
onClick={startBatch}
>
<span className="bx bx-play" />{" "}{t("images.batch_ocr_start")}
</button>
)}
</OptionsRow>
);
}