feat: per-extraction OCR language override
Allow users to specify a Tesseract language string (e.g. jpn+jpn_vert)
on a per-extraction basis, overriding the global OCR language setting.
- Add payload column to ai_jobs table (migration) to carry per-call data
- Thread ocrLanguages payload through enqueueJob → processNextJob → extractItemText
- New GET /api/ai-settings/ocr endpoint (requireAuth) returns { ocrMode, ocrLanguages }
- ImageLightbox fetches OCR settings and shows a language input next to the
Extract Text button when mode is hybrid or tesseract (hidden for llm-only)
- MixedView fetches OCR settings and passes them down to EntryTile; kebab
Extract Text on images shows an inline language prompt before dispatching the job
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ interface AiJobRow {
|
||||
started_at: number | null
|
||||
completed_at: number | null
|
||||
item_title: string | null
|
||||
payload: string | null
|
||||
}
|
||||
|
||||
function rowToJob(row: AiJobRow): AiJob {
|
||||
@@ -75,6 +76,7 @@ export function enqueueJob(
|
||||
jobType: AiJobType,
|
||||
libraryId: string,
|
||||
sourceLanguage?: string,
|
||||
payload?: Record<string, string>,
|
||||
): string {
|
||||
const db = getDb()
|
||||
|
||||
@@ -96,9 +98,9 @@ export function enqueueJob(
|
||||
const metadata = jobType === 'translate' && sourceLanguage ? sourceLanguage : null
|
||||
|
||||
db.prepare(
|
||||
`INSERT INTO ai_jobs (id, item_key, library_id, job_type, status, error, attempt, max_retries, created_at, item_title)
|
||||
VALUES (?, ?, ?, ?, 'queued', ?, 0, ?, ?, ?)`
|
||||
).run(id, itemKey, libraryId, jobType, metadata, maxRetries, Date.now(), title)
|
||||
`INSERT INTO ai_jobs (id, item_key, library_id, job_type, status, error, attempt, max_retries, created_at, item_title, payload)
|
||||
VALUES (?, ?, ?, ?, 'queued', ?, 0, ?, ?, ?, ?)`
|
||||
).run(id, itemKey, libraryId, jobType, metadata, maxRetries, Date.now(), title, payload ? JSON.stringify(payload) : null)
|
||||
|
||||
// Wake the processor
|
||||
wakeProcessor()
|
||||
@@ -251,6 +253,8 @@ async function processNextJob(): Promise<boolean> {
|
||||
|
||||
// Extract sourceLanguage for translate jobs (stored in error field at enqueue)
|
||||
const sourceLanguage = row.job_type === 'translate' ? row.error : null
|
||||
// Parse job payload (carries per-call overrides, e.g. ocrLanguages for extract jobs)
|
||||
const jobPayload = row.payload ? (JSON.parse(row.payload) as Record<string, string>) : null
|
||||
|
||||
db.prepare(
|
||||
"UPDATE ai_jobs SET status = 'running', started_at = ?, error = NULL WHERE id = ?"
|
||||
@@ -265,7 +269,7 @@ async function processNextJob(): Promise<boolean> {
|
||||
await generateItemDescription(row.item_key)
|
||||
break
|
||||
case 'extract':
|
||||
await extractItemText(row.item_key)
|
||||
await extractItemText(row.item_key, jobPayload?.ocrLanguages)
|
||||
break
|
||||
case 'translate':
|
||||
await translateItemText(row.item_key, sourceLanguage || undefined)
|
||||
|
||||
Reference in New Issue
Block a user