// Package domain contains the core value types shared across all packages // in this module. It has zero internal imports — only the standard library. // Every other package imports domain; domain imports nothing from this module. package domain import "time" // ── Book types ──────────────────────────────────────────────────────────────── // Visibility values for BookMeta.Visibility. const ( VisibilityPublic = "public" // visible to all users VisibilityAdminOnly = "admin_only" // visible only to admin users (e.g. scraped content) ) // BookMeta carries all bibliographic information about a novel. type BookMeta struct { Slug string `json:"slug"` Title string `json:"title"` Author string `json:"author"` Cover string `json:"cover,omitempty"` Status string `json:"status,omitempty"` Genres []string `json:"genres,omitempty"` Summary string `json:"summary,omitempty"` TotalChapters int `json:"total_chapters,omitempty"` SourceURL string `json:"source_url"` Ranking int `json:"ranking,omitempty"` Rating float64 `json:"rating,omitempty"` // MetaUpdated is the Unix timestamp (seconds) when the book record was last // updated in PocketBase. Populated on read; not sent on write (PocketBase // manages its own updated field). MetaUpdated int64 `json:"meta_updated,omitempty"` // Archived is true when the book has been soft-deleted by an admin. // Archived books are excluded from all public search and catalogue responses. Archived bool `json:"archived,omitempty"` // Visibility controls who can see this book. // "public" = all users; "admin_only" = admin only (default for scraped content). Visibility string `json:"visibility,omitempty"` // SubmittedBy is the app_users ID of the author who submitted this book, // or empty for scraped books. SubmittedBy string `json:"submitted_by,omitempty"` } // CatalogueEntry is a lightweight book reference returned by catalogue pages. type CatalogueEntry struct { Slug string `json:"slug"` Title string `json:"title"` URL string `json:"url"` } // ChapterRef is a reference to a single chapter returned by chapter-list pages. type ChapterRef struct { Number int `json:"number"` Title string `json:"title"` URL string `json:"url"` Volume int `json:"volume,omitempty"` } // Chapter contains the fully-extracted text of a single chapter. type Chapter struct { Ref ChapterRef `json:"ref"` Text string `json:"text"` } // RankingItem represents a single entry in the novel ranking list. type RankingItem struct { Rank int `json:"rank"` Slug string `json:"slug"` Title string `json:"title"` Author string `json:"author,omitempty"` Cover string `json:"cover,omitempty"` Status string `json:"status,omitempty"` Genres []string `json:"genres,omitempty"` SourceURL string `json:"source_url,omitempty"` Updated time.Time `json:"updated,omitempty"` } // ── Voice types ─────────────────────────────────────────────────────────────── // Voice describes a single text-to-speech voice available in the system. type Voice struct { // ID is the voice identifier passed to TTS clients (e.g. "af_bella", "alba"). ID string `json:"id"` // Engine is "kokoro" or "pocket-tts". Engine string `json:"engine"` // Lang is the primary language tag (e.g. "en-us", "en-gb", "en", "es", "fr"). Lang string `json:"lang"` // Gender is "f" or "m". Gender string `json:"gender"` } // ── Storage record types ────────────────────────────────────────────────────── // ChapterInfo is a lightweight chapter descriptor stored in the index. type ChapterInfo struct { Number int `json:"number"` Title string `json:"title"` Date string `json:"date,omitempty"` } // ReadingProgress holds a single user's reading position for one book. type ReadingProgress struct { Slug string `json:"slug"` Chapter int `json:"chapter"` UpdatedAt time.Time `json:"updated_at"` } // ── Task record types ───────────────────────────────────────────────────────── // TaskStatus enumerates the lifecycle states of any task. type TaskStatus string const ( TaskStatusPending TaskStatus = "pending" TaskStatusRunning TaskStatus = "running" TaskStatusDone TaskStatus = "done" TaskStatusFailed TaskStatus = "failed" TaskStatusCancelled TaskStatus = "cancelled" ) // ScrapeTask represents a book-scraping job stored in PocketBase. type ScrapeTask struct { ID string `json:"id"` Kind string `json:"kind"` // "catalogue" | "book" | "book_range" TargetURL string `json:"target_url"` // non-empty for single-book tasks FromChapter int `json:"from_chapter,omitempty"` ToChapter int `json:"to_chapter,omitempty"` WorkerID string `json:"worker_id,omitempty"` Status TaskStatus `json:"status"` BooksFound int `json:"books_found"` ChaptersScraped int `json:"chapters_scraped"` ChaptersSkipped int `json:"chapters_skipped"` Errors int `json:"errors"` Started time.Time `json:"started"` Finished time.Time `json:"finished,omitempty"` ErrorMessage string `json:"error_message,omitempty"` } // ScrapeResult is the outcome reported by the runner after finishing a ScrapeTask. type ScrapeResult struct { // Slug is the book slug that was scraped. Empty for catalogue tasks. Slug string `json:"slug,omitempty"` BooksFound int `json:"books_found"` ChaptersScraped int `json:"chapters_scraped"` ChaptersSkipped int `json:"chapters_skipped"` Errors int `json:"errors"` ErrorMessage string `json:"error_message,omitempty"` } // AudioTask represents an audio-generation job stored in PocketBase. type AudioTask struct { ID string `json:"id"` CacheKey string `json:"cache_key"` // "slug/chapter/voice" Slug string `json:"slug"` Chapter int `json:"chapter"` Voice string `json:"voice"` WorkerID string `json:"worker_id,omitempty"` Status TaskStatus `json:"status"` ErrorMessage string `json:"error_message,omitempty"` Started time.Time `json:"started"` Finished time.Time `json:"finished,omitempty"` } // AudioResult is the outcome reported by the runner after finishing an AudioTask. type AudioResult struct { ObjectKey string `json:"object_key,omitempty"` ErrorMessage string `json:"error_message,omitempty"` } // TranslationTask represents a machine-translation job stored in PocketBase. type TranslationTask struct { ID string `json:"id"` CacheKey string `json:"cache_key"` // "{slug}/{chapter}/{lang}" Slug string `json:"slug"` Chapter int `json:"chapter"` Lang string `json:"lang"` WorkerID string `json:"worker_id,omitempty"` Status TaskStatus `json:"status"` ErrorMessage string `json:"error_message,omitempty"` Started time.Time `json:"started"` Finished time.Time `json:"finished,omitempty"` } // TranslationResult is the outcome reported by the runner after finishing a TranslationTask. type TranslationResult struct { ObjectKey string `json:"object_key,omitempty"` ErrorMessage string `json:"error_message,omitempty"` } // ImportTask represents a PDF/EPUB import job stored in PocketBase. type ImportTask struct { ID string `json:"id"` Slug string `json:"slug"` // derived from filename Title string `json:"title"` FileName string `json:"file_name"` FileType string `json:"file_type"` // "pdf" or "epub" ObjectKey string `json:"object_key,omitempty"` // MinIO path to uploaded file ChaptersKey string `json:"chapters_key,omitempty"` // MinIO path to pre-parsed chapters JSON Author string `json:"author,omitempty"` CoverURL string `json:"cover_url,omitempty"` Genres []string `json:"genres,omitempty"` Summary string `json:"summary,omitempty"` BookStatus string `json:"book_status,omitempty"` // "ongoing" | "completed" | "hiatus" WorkerID string `json:"worker_id,omitempty"` InitiatorUserID string `json:"initiator_user_id,omitempty"` // PocketBase user ID who submitted the import Status TaskStatus `json:"status"` ChaptersDone int `json:"chapters_done"` ChaptersTotal int `json:"chapters_total"` ErrorMessage string `json:"error_message,omitempty"` Started time.Time `json:"started"` Finished time.Time `json:"finished,omitempty"` } // ImportResult is the outcome reported by the runner after finishing an ImportTask. type ImportResult struct { Slug string `json:"slug,omitempty"` ChaptersImported int `json:"chapters_imported"` ErrorMessage string `json:"error_message,omitempty"` } // AIJob represents an AI generation task tracked in PocketBase (ai_jobs collection). type AIJob struct { ID string `json:"id"` // Kind is one of: "chapter-names", "batch-covers", "chapter-covers", "refresh-metadata". Kind string `json:"kind"` // Slug is the book slug for per-book jobs; empty for catalogue-wide jobs. Slug string `json:"slug"` Status TaskStatus `json:"status"` // FromItem is the first item to process (chapter number, or 0-based book index). // 0 = start from the beginning. FromItem int `json:"from_item"` // ToItem is the last item to process (inclusive). 0 = process all. ToItem int `json:"to_item"` // ItemsDone is the cumulative count of successfully processed items. ItemsDone int `json:"items_done"` // ItemsTotal is the total number of items in this job. ItemsTotal int `json:"items_total"` Model string `json:"model"` // Payload is a JSON-encoded string with job-specific parameters // (e.g. naming pattern for chapter-names, num_steps for batch-covers). Payload string `json:"payload"` ErrorMessage string `json:"error_message,omitempty"` Started time.Time `json:"started,omitempty"` Finished time.Time `json:"finished,omitempty"` HeartbeatAt time.Time `json:"heartbeat_at,omitempty"` }