Phase 1 Week 2: Format detection and progress conversion engine
- Add internal/sync package with format detection - FormatGroup types: reflowable, fixed_layout, comic_archive - DetectFormatGroup() function based on mimetype and file extension - MimeType mappings for common ebook formats - Progress conversion engine with: - ConvertProgress() between format groups - Extract percentage from various progress formats - PageToPercentage / PercentageToPage helpers - CharacterToPercentage / PercentageToCharacter helpers - MergeProgress() with 'max progress wins' strategy - FormatProgressForDisplay() for UI rendering - Add sqlc queries for format detection and progress updates - BulkUpdateFormatGroups query for auto-format detection - GetUniversalProgress query with all location references - UpdateUniversalProgress query with device sync metadata - ReadingHistory queries for session tracking
This commit is contained in:
@@ -484,4 +484,141 @@ UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = $1 AND revoked_at I
|
||||
-- name: CleanupExpiredRefreshTokens :exec
|
||||
DELETE FROM refresh_tokens WHERE expires_at < NOW() OR (revoked_at IS NOT NULL AND revoked_at < NOW() - INTERVAL '7 days');
|
||||
|
||||
-- ============================================
|
||||
-- PHASE 1: FORMAT DETECTION & PROGRESS (Week 2)
|
||||
-- ============================================
|
||||
|
||||
-- Update media item format group information
|
||||
-- name: UpdateMediaItemFormatGroup :exec
|
||||
UPDATE media_items
|
||||
SET
|
||||
format_group = $2,
|
||||
format_mimetype = $3,
|
||||
is_reflowable = $4,
|
||||
has_fixed_layout = $5,
|
||||
total_characters = $6,
|
||||
chapter_count = $7,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1;
|
||||
|
||||
-- Bulk update format group for all media items
|
||||
-- name: BulkUpdateFormatGroups :exec
|
||||
UPDATE media_items m
|
||||
SET
|
||||
format_group = detect_format_group(m.mime_type, m.file_path),
|
||||
format_mimetype = m.mime_type,
|
||||
is_reflowable = (detect_format_group(m.mime_type, m.file_path) = 'reflowable'),
|
||||
has_fixed_layout = (detect_format_group(m.mime_type, m.file_path) IN ('fixed_layout', 'comic_archive')),
|
||||
updated_at = NOW()
|
||||
WHERE m.format_group IS NULL OR m.format_group = 'unknown';
|
||||
|
||||
-- Get universal progress for a book
|
||||
-- name: GetUniversalProgress :one
|
||||
SELECT
|
||||
rp.id,
|
||||
rp.media_item_id,
|
||||
rp.user_id,
|
||||
rp.current_page,
|
||||
rp.total_pages,
|
||||
rp.last_read_at,
|
||||
rp.percentage,
|
||||
rp.character_offset,
|
||||
rp.epubcfi,
|
||||
rp.chapter,
|
||||
rp.chapter_progress,
|
||||
rp.viewport_x,
|
||||
rp.viewport_y,
|
||||
rp.zoom_level,
|
||||
rp.scroll_position_x,
|
||||
rp.scroll_position_y,
|
||||
rp.panel_number,
|
||||
rp.reading_mode,
|
||||
rp.last_sync_device,
|
||||
rp.last_sync_source,
|
||||
rp.last_sync_timestamp,
|
||||
rp.conflict_detected,
|
||||
rp.conflict_resolved,
|
||||
mi.format_group,
|
||||
mi.format_mimetype,
|
||||
mi.is_reflowable,
|
||||
mi.has_fixed_layout,
|
||||
mi.total_characters,
|
||||
mi.chapter_count
|
||||
FROM reading_progress rp
|
||||
JOIN media_items mi ON rp.media_item_id = mi.id
|
||||
WHERE rp.media_item_id = $1 AND rp.user_id = $2;
|
||||
|
||||
-- Update universal progress
|
||||
-- name: UpdateUniversalProgress :one
|
||||
INSERT INTO reading_progress (
|
||||
media_item_id,
|
||||
user_id,
|
||||
percentage,
|
||||
character_offset,
|
||||
epubcfi,
|
||||
chapter,
|
||||
chapter_progress,
|
||||
viewport_x,
|
||||
viewport_y,
|
||||
zoom_level,
|
||||
scroll_position_x,
|
||||
scroll_position_y,
|
||||
panel_number,
|
||||
reading_mode,
|
||||
last_sync_device,
|
||||
last_sync_source,
|
||||
last_sync_timestamp,
|
||||
current_page,
|
||||
total_pages,
|
||||
last_read_at
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NOW(), $17, $18, NOW()
|
||||
)
|
||||
ON CONFLICT (media_item_id, user_id)
|
||||
DO UPDATE SET
|
||||
percentage = EXCLUDED.percentage,
|
||||
character_offset = EXCLUDED.character_offset,
|
||||
epubcfi = EXCLUDED.epubcfi,
|
||||
chapter = EXCLUDED.chapter,
|
||||
chapter_progress = EXCLUDED.chapter_progress,
|
||||
viewport_x = EXCLUDED.viewport_x,
|
||||
viewport_y = EXCLUDED.viewport_y,
|
||||
zoom_level = EXCLUDED.zoom_level,
|
||||
scroll_position_x = EXCLUDED.scroll_position_x,
|
||||
scroll_position_y = EXCLUDED.scroll_position_y,
|
||||
panel_number = EXCLUDED.panel_number,
|
||||
reading_mode = EXCLUDED.reading_mode,
|
||||
last_sync_device = EXCLUDED.last_sync_device,
|
||||
last_sync_source = EXCLUDED.last_sync_source,
|
||||
last_sync_timestamp = EXCLUDED.last_sync_timestamp,
|
||||
current_page = EXCLUDED.current_page,
|
||||
total_pages = EXCLUDED.total_pages,
|
||||
last_read_at = NOW()
|
||||
RETURNING *;
|
||||
|
||||
-- Get reading history for a user and book
|
||||
-- name: GetReadingHistory :many
|
||||
SELECT *
|
||||
FROM reading_history
|
||||
WHERE user_id = $1 AND media_item_id = $2
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $3;
|
||||
|
||||
-- Create reading history entry
|
||||
-- name: CreateReadingHistory :one
|
||||
INSERT INTO reading_history (
|
||||
user_id,
|
||||
media_item_id,
|
||||
device_id,
|
||||
progress_percentage,
|
||||
reading_session_start,
|
||||
reading_session_end,
|
||||
pages_read,
|
||||
time_spent_seconds,
|
||||
device_metadata
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING *;
|
||||
|
||||
-- Media Items Admin Operations
|
||||
Reference in New Issue
Block a user