feat: add reader infrastructure - Phase 0 database schema and queries
Implement Phase 0 prerequisites for reader functionality including database schema, SQL queries, and frontend dependencies. ## Database Schema (5 New Tables + 1 Column Addition) ### New Tables Added: 1. **panel_data** - Comic/manga panel detection results - Stores detected panel boundaries (x, y, width, height) - Supports grid, ML, and manual detection methods - JSONB storage for flexible panel structures 2. **reading_speed** - User reading speed statistics - Tracks pages per minute and total reading time - Per-user per-media-item tracking - Enables progress estimation and analytics 3. **dictionary_cache** - Offline dictionary word definitions - Caches external dictionary lookups - Reduces API calls and improves performance - Supports offline reading functionality 4. **reader_settings** - User reader preferences (per-user) - Stores typography, theme, and display settings - JSONB storage for flexible configuration - Per-user customization (fonts, margins, themes) 5. **media_bookmarks** - Enhanced bookmarks with chapter/CFI support - Unified bookmarking for ebooks, comics, manga, PDFs - Supports page_number, chapter_number, and epubcfi_position - Includes notes field for annotations - Unique constraint on (media_item_id, user_id, title) ### Column Addition: - **media_items.chapter_metadata** (JSONB) - Caches detected chapter structure - Stores TOC/chapter detection results - Prevents re-parsing files on every read - Populated by ReaderService.DetectChapters() ## Database Queries (12 New Queries) Added queries for all reader functionality: - Panel data: GetPanelData, UpsertPanelData - Reading speed: GetReadingSpeed, CreateReadingSpeed, UpdateReadingSpeed - Dictionary: GetDictionaryEntry, CreateDictionaryEntry, UpdateDictionaryAccessed - Settings: GetReaderSettings, UpsertReaderSettings - Bookmarks: GetMediaBookmarks, CreateMediaBookmark, DeleteMediaBookmark, UpdateMediaBookmark ## Frontend Dependencies Added to package.json: - jszip@^3.10.1 - EPUB/comic archive parsing (client-side) - pdfjs-dist@^3.11.174 - PDF rendering library (Mozilla PDF.js) ## Generated Code Ran `sqlc generate` to regenerate: - models.go - Go structs for new tables (55 lines added) - querier.go - Database query methods (14 lines added) - queries.sql.go - Compiled SQL queries (504 lines added) ## Implementation Status Phase 0 prerequisites now complete: ✅ Database schema (5 tables + 1 column) ✅ SQL queries (12 queries) ✅ Frontend dependencies (2 packages) ✅ Generated Go code (sqlc) ✅ Database recreated with new schema Ready for Phase 1: Infrastructure & Basic Reader implementation. Related to: Universal web reader for ebooks, comics, manga, PDFs
This commit is contained in:
@@ -81,6 +81,17 @@ type Devices struct {
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type DictionaryCache struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
Word string `db:"word" json:"word"`
|
||||
Definition string `db:"definition" json:"definition"`
|
||||
PartOfSpeech pgtype.Text `db:"part_of_speech" json:"part_of_speech"`
|
||||
Example pgtype.Text `db:"example" json:"example"`
|
||||
Etymology pgtype.Text `db:"etymology" json:"etymology"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
AccessedAt pgtype.Timestamptz `db:"accessed_at" json:"accessed_at"`
|
||||
}
|
||||
|
||||
type KoboEntitlements struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
DeviceID pgtype.UUID `db:"device_id" json:"device_id"`
|
||||
@@ -143,6 +154,19 @@ type LibraryVisibility struct {
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type MediaBookmarks struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
PageNumber pgtype.Int4 `db:"page_number" json:"page_number"`
|
||||
ChapterNumber pgtype.Int4 `db:"chapter_number" json:"chapter_number"`
|
||||
CfiPosition pgtype.Text `db:"cfi_position" json:"cfi_position"`
|
||||
Title string `db:"title" json:"title"`
|
||||
Position pgtype.Text `db:"position" json:"position"`
|
||||
Notes pgtype.Text `db:"notes" json:"notes"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type MediaHighlights struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
@@ -254,6 +278,7 @@ type MediaItems struct {
|
||||
ScanInformation pgtype.Text `db:"scan_information" json:"scan_information"`
|
||||
// Summary from ComicInfo.xml (may be merged with description from Calibre)
|
||||
Summary pgtype.Text `db:"summary" json:"summary"`
|
||||
ChapterMetadata []byte `db:"chapter_metadata" json:"chapter_metadata"`
|
||||
TagsSearch []string `db:"tags_search" json:"tags_search"`
|
||||
ContributorsSearch []string `db:"contributors_search" json:"contributors_search"`
|
||||
FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"`
|
||||
@@ -298,6 +323,24 @@ type OpdsTokens struct {
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type PanelData struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
PageNumber int32 `db:"page_number" json:"page_number"`
|
||||
DetectionMethod string `db:"detection_method" json:"detection_method"`
|
||||
Panels []byte `db:"panels" json:"panels"`
|
||||
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type ReaderSettings struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
SettingKey string `db:"setting_key" json:"setting_key"`
|
||||
SettingValue []byte `db:"setting_value" json:"setting_value"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type ReadingHistory struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
@@ -338,6 +381,18 @@ type ReadingProgress struct {
|
||||
ConflictResolved pgtype.Bool `db:"conflict_resolved" json:"conflict_resolved"`
|
||||
}
|
||||
|
||||
type ReadingSpeed struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
MediaItemID pgtype.UUID `db:"media_item_id" json:"media_item_id"`
|
||||
WordsPerMinute pgtype.Numeric `db:"words_per_minute" json:"words_per_minute"`
|
||||
PagesPerMinute pgtype.Numeric `db:"pages_per_minute" json:"pages_per_minute"`
|
||||
PagesRead pgtype.Int4 `db:"pages_read" json:"pages_read"`
|
||||
TotalReadingMinutes pgtype.Numeric `db:"total_reading_minutes" json:"total_reading_minutes"`
|
||||
LastReadAt pgtype.Timestamptz `db:"last_read_at" json:"last_read_at"`
|
||||
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type RefreshTokens struct {
|
||||
ID pgtype.UUID `db:"id" json:"id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
|
||||
Reference in New Issue
Block a user