- parsePDF function restored in import.go (body was orphaned outside function) - ParseImportFile() called at upload time with 3-min timeout; chapters stored as JSON in MinIO - runner.go: prefer ChaptersKey path (read pre-parsed JSON) over BookImport.Import() - ImportChapterStore interface added; store wired in runner/main.go - HeartbeatTask and ReapStaleTasks now include import_tasks collection - parseImportTask now returns ChaptersKey in domain.ImportTask - asynq_runner.go handleImportTask passes ChaptersKey - pb-init-v3.sh: chapters_key field added to import_tasks schema
58 lines
2.4 KiB
Go
58 lines
2.4 KiB
Go
// Package asynqqueue provides Asynq-backed implementations of the
|
|
// taskqueue.Producer and taskqueue.Consumer interfaces.
|
|
//
|
|
// Architecture:
|
|
// - Producer: dual-writes — creates a PocketBase record for audit/UI, then
|
|
// enqueues an Asynq job so the runner picks it up immediately (sub-ms).
|
|
// - Consumer: thin wrapper used only for result write-back (FinishAudioTask,
|
|
// FinishScrapeTask, FailTask). ClaimNext*/Heartbeat/Reap are no-ops because
|
|
// Asynq owns those responsibilities.
|
|
// - Handlers: asynq.HandlerFunc wrappers that decode job payloads and invoke
|
|
// the existing runner logic (runScrapeTask / runAudioTask).
|
|
//
|
|
// Fallback: when REDIS_ADDR is empty the caller should use the plain
|
|
// storage.Store (PocketBase-polling) implementation unchanged.
|
|
package asynqqueue
|
|
|
|
// Queue names — keep all jobs on the default queue for now.
|
|
// Add separate queues (e.g. "audio", "scrape") later if you need priority.
|
|
const QueueDefault = "default"
|
|
|
|
// Task type constants used for Asynq routing.
|
|
const (
|
|
TypeAudioGenerate = "audio:generate"
|
|
TypeScrapeBook = "scrape:book"
|
|
TypeScrapeCatalogue = "scrape:catalogue"
|
|
TypeImportBook = "import:book"
|
|
)
|
|
|
|
// AudioPayload is the Asynq job payload for audio generation tasks.
|
|
type AudioPayload struct {
|
|
// PBTaskID is the PocketBase record ID created before enqueueing.
|
|
// The handler uses it to write results back via Consumer.FinishAudioTask.
|
|
PBTaskID string `json:"pb_task_id"`
|
|
Slug string `json:"slug"`
|
|
Chapter int `json:"chapter"`
|
|
Voice string `json:"voice"`
|
|
}
|
|
|
|
// ScrapePayload is the Asynq job payload for scrape tasks.
|
|
type ScrapePayload struct {
|
|
// PBTaskID is the PocketBase record ID created before enqueueing.
|
|
PBTaskID string `json:"pb_task_id"`
|
|
Kind string `json:"kind"` // "catalogue", "book", or "book_range"
|
|
TargetURL string `json:"target_url"` // empty for catalogue tasks
|
|
FromChapter int `json:"from_chapter"` // 0 unless Kind=="book_range"
|
|
ToChapter int `json:"to_chapter"` // 0 unless Kind=="book_range"
|
|
}
|
|
|
|
// ImportPayload is the Asynq job payload for PDF/EPUB import tasks.
|
|
type ImportPayload struct {
|
|
PBTaskID string `json:"pb_task_id"`
|
|
Slug string `json:"slug"`
|
|
Title string `json:"title"`
|
|
FileType string `json:"file_type"` // "pdf" or "epub"
|
|
ObjectKey string `json:"object_key"` // MinIO path to uploaded file
|
|
ChaptersKey string `json:"chapters_key"` // MinIO path to pre-parsed chapters JSON
|
|
}
|