forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathocrService.ts
More file actions
79 lines (73 loc) · 2.96 KB
/
Copy pathocrService.ts
File metadata and controls
79 lines (73 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { readFileSync } from 'fs'
import { helperProcess } from '../ocr/helperProcess'
import { unindexedRewindFrames } from '../ipc/db'
import { persistFrameOcr } from './ocrPersist'
const BACKFILL_INTERVAL_MS = 4000
const BATCH = 5
let timer: NodeJS.Timeout | null = null
let running = false
// Whether an un-OCR'd frame MAY exist. The capture hot path OCRs almost every
// frame inline (marking it indexed=1); a frame only reaches this backlog sweep
// when the hot path skipped or failed it (see captureService.refreshCurrentScreen,
// which calls signalRewindOcrPending in exactly those cases). While this is false
// there is demonstrably nothing to do, so the 4s tick skips the DB read entirely —
// the mirror of embeddingService's queue-empty tick gate. Starts true so a
// pre-existing backlog from a previous session is drained on launch.
let pending = true
/** Wake the backlog sweep: an un-OCR'd frame may now exist. Cheap + idempotent;
* safe to call from the capture hot path. */
export function signalRewindOcrPending(): void {
pending = true
}
async function backfill(): Promise<void> {
if (running || !pending) return
running = true
// Clear optimistically BEFORE the query: a frame captured mid-sweep re-arms
// `pending` via signalRewindOcrPending, so work is never lost by clearing early.
pending = false
try {
const frames = unindexedRewindFrames(BATCH)
// A full page means more may remain — keep sweeping on the next tick. A short
// page drained the backlog, so stay gated until capture signals again.
if (frames.length === BATCH) pending = true
for (const f of frames) {
if (f.id == null) continue
// The app context stored on the frame at capture time; it is embedded with
// the OCR text (see ocrPersist.FrameContext), so this sweep and the capture
// hot path produce byte-identical content for the same screen.
const context = { app: f.app, windowTitle: f.windowTitle }
let jpeg: Buffer
try {
jpeg = readFileSync(f.imagePath)
} catch {
persistFrameOcr(f.id, '', context) // image gone; mark indexed so we stop retrying
continue
}
const result = await helperProcess.ocr(jpeg)
// Persists the text + per-line boxes AND queues it for semantic indexing —
// see ocrPersist.ts for why those two are deliberately fused.
persistFrameOcr(
f.id,
result.ok ? result.fullText : '',
context,
result.ok ? result.lines : null
)
}
} finally {
running = false
}
}
export function startRewindOcr(): void {
// Drain any backlog left by a previous session on (re)start.
pending = true
if (timer) clearInterval(timer)
timer = setInterval(() => void backfill(), BACKFILL_INTERVAL_MS)
}
/** Test seam: run one sweep synchronously and drive/inspect the pending latch. */
export const __rewindOcrTestHooks = {
backfill,
setPending: (v: boolean): void => {
pending = v
},
getPending: (): boolean => pending
}