refactor: remove Phase X terminology from source code comments

Remove planning document phase references from code comments:

app_test.go:
- Remove Phase 5 references from 8 test function comments

querier.go & queries.sql.go:
- Remove Phase 1, 2, 3, 4, 6 references from section headers
- Clean up week numbers (Weeks 5-6, Week 3-4, etc.)

queries.sql:
- Remove Phase 4 references from Kobo queries

kobo.go:
- Remove Phase 6 references from ContentId mapping comments

progress.go:
- Remove Phase 1 reference from route comment

media_scanner.go & media_scanner_library_type_test.go:
- Remove Phase 2 references from library type scanning comments

schema.sql:
- Remove Phase 1, 2, 3, 4, 5, 7 references from table/section comments
- Clean up: Format Detection, Progress Tracking, Device Registry,
  Sync Queue, Conflict Resolution, Reading History, Indexes, etc.

test_helpers.go:
- Remove Phase 6 reference from handler setup comment

These phase numbers were from internal planning documents and have no
meaning in the codebase. Removing them makes the code self-documenting.
This commit is contained in:
2026-02-13 21:50:29 -05:00
parent 80dcdfdd71
commit 2706ae52c1
9 changed files with 96 additions and 96 deletions
+23 -23
View File
@@ -119,14 +119,14 @@ CREATE TABLE IF NOT EXISTS media_items (
added_by_admin_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Universal Sync Format Detection (Phase 1)
-- Universal Sync Format Detection
format_group VARCHAR(20) NOT NULL DEFAULT 'reflowable',
format_mimetype VARCHAR(100),
is_reflowable BOOLEAN DEFAULT TRUE,
has_fixed_layout BOOLEAN DEFAULT FALSE,
total_characters BIGINT,
chapter_count INTEGER,
-- Kobo-Specific Metadata (Phase 4)
-- Kobo-Specific Metadata
entitlement_id VARCHAR(255) UNIQUE,
revision_number INTEGER DEFAULT 1,
kobo_content_id VARCHAR(255),
@@ -154,7 +154,7 @@ CREATE TABLE IF NOT EXISTS reading_progress (
total_pages INTEGER,
last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(media_item_id, user_id),
-- Universal Progress Tracking (Phase 1)
-- Universal Progress Tracking
percentage FLOAT CHECK (percentage >= 0 AND percentage <= 1),
character_offset BIGINT,
epubcfi TEXT,
@@ -167,7 +167,7 @@ CREATE TABLE IF NOT EXISTS reading_progress (
scroll_position_y FLOAT DEFAULT 0,
panel_number INTEGER,
reading_mode VARCHAR(20),
-- Device Sync Metadata (Phase 1)
-- Device Sync Metadata
last_sync_device VARCHAR(50),
last_sync_source VARCHAR(20),
last_sync_timestamp TIMESTAMP WITH TIME ZONE,
@@ -195,7 +195,7 @@ CREATE TABLE IF NOT EXISTS media_notes (
position VARCHAR(100), -- optional position (page:offset or CFI) for standalone notes
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Location Enhancements (Phase 1)
-- Location Enhancements
percentage_location FLOAT,
character_start INTEGER,
character_end INTEGER,
@@ -217,7 +217,7 @@ CREATE TABLE IF NOT EXISTS media_highlights (
note_id UUID REFERENCES media_notes(id) ON DELETE SET NULL, -- optional associated note
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Location Enhancements (Phase 1)
-- Location Enhancements
percentage_start FLOAT,
percentage_end FLOAT,
character_start INTEGER,
@@ -232,7 +232,7 @@ CREATE TABLE IF NOT EXISTS media_highlights (
);
-- ============================================
-- DEVICE REGISTRY (Phase 1)
-- DEVICE REGISTRY
-- ============================================
CREATE TABLE IF NOT EXISTS devices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -252,7 +252,7 @@ CREATE TABLE IF NOT EXISTS devices (
);
-- ============================================
-- SYNC QUEUE FOR OFFLINE SUPPORT (Phase 1)
-- SYNC QUEUE FOR OFFLINE SUPPORT
-- ============================================
CREATE TABLE IF NOT EXISTS sync_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -270,7 +270,7 @@ CREATE TABLE IF NOT EXISTS sync_queue (
);
-- ============================================
-- CONFLICT RESOLUTION (Phase 1)
-- CONFLICT RESOLUTION
-- ============================================
CREATE TABLE IF NOT EXISTS sync_conflicts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -286,7 +286,7 @@ CREATE TABLE IF NOT EXISTS sync_conflicts (
);
-- ============================================
-- KOBO SHELF MANAGEMENT (Phase 4)
-- KOBO SHELF MANAGEMENT
-- ============================================
CREATE TABLE IF NOT EXISTS kobo_shelves (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -304,7 +304,7 @@ CREATE INDEX IF NOT EXISTS idx_kobo_shelves_media_item_id ON kobo_shelves(media_
CREATE INDEX IF NOT EXISTS idx_kobo_shelves_shelf_name ON kobo_shelves(shelf_name);
-- ============================================
-- KOBO ENTITLEMENTS TRACKING (Phase 4)
-- KOBO ENTITLEMENTS TRACKING
-- ============================================
CREATE TABLE IF NOT EXISTS kobo_entitlements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -329,7 +329,7 @@ CREATE INDEX IF NOT EXISTS idx_kobo_entitlements_entitlement_id ON kobo_entitlem
CREATE INDEX IF NOT EXISTS idx_kobo_entitlements_content_id ON kobo_entitlements(content_id);
-- ============================================
-- READING HISTORY (Phase 1)
-- READING HISTORY
-- ============================================
CREATE TABLE IF NOT EXISTS reading_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -386,7 +386,7 @@ CREATE INDEX IF NOT EXISTS idx_media_highlights_user_id ON media_highlights(user
CREATE INDEX IF NOT EXISTS idx_media_highlights_note_id ON media_highlights(note_id);
-- ============================================
-- NEW TABLE INDEXES (Phase 1)
-- NEW TABLE INDEXES
-- ============================================
-- Device registry indexes
@@ -404,22 +404,22 @@ CREATE INDEX IF NOT EXISTS idx_sync_conflicts_media_item_id ON sync_conflicts(me
CREATE INDEX IF NOT EXISTS idx_sync_conflicts_user_id ON sync_conflicts(user_id);
CREATE INDEX IF NOT EXISTS idx_sync_conflicts_status ON sync_conflicts(resolution_status);
-- Composite indexes for sync queue performance (Phase 7)
-- Composite indexes for sync queue performance
CREATE INDEX IF NOT EXISTS idx_sync_queue_device_status_priority ON sync_queue(device_id, status, priority ASC, created_at ASC);
CREATE INDEX IF NOT EXISTS idx_sync_queue_status_priority_created ON sync_queue(status, priority ASC, created_at ASC);
CREATE INDEX IF NOT EXISTS idx_sync_queue_device_created_at ON sync_queue(device_id, created_at DESC);
-- Composite indexes for reading progress performance (Phase 7)
-- Composite indexes for reading progress performance
CREATE INDEX IF NOT EXISTS idx_reading_progress_user_last_sync ON reading_progress(user_id, last_sync_timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_reading_progress_media_last_sync ON reading_progress(media_item_id, last_sync_timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_reading_progress_user_sync_source ON reading_progress(user_id, last_sync_source, last_sync_timestamp DESC);
-- Composite indexes for devices (Phase 7)
-- Composite indexes for devices
CREATE INDEX IF NOT EXISTS idx_devices_user_sync_enabled ON devices(user_id, sync_enabled, auto_sync);
CREATE INDEX IF NOT EXISTS idx_devices_user_type ON devices(user_id, device_type);
CREATE INDEX IF NOT EXISTS idx_devices_last_seen ON devices(last_seen DESC) WHERE sync_enabled = true;
-- Composite indexes for annotations (Phase 7)
-- Composite indexes for annotations
CREATE INDEX IF NOT EXISTS idx_media_notes_user_media ON media_notes(user_id, media_item_id);
CREATE INDEX IF NOT EXISTS idx_media_highlights_user_media ON media_highlights(user_id, media_item_id);
@@ -430,7 +430,7 @@ CREATE INDEX IF NOT EXISTS idx_reading_history_created_at ON reading_history(cre
CREATE INDEX IF NOT EXISTS idx_reading_history_user_device_created ON reading_history(user_id, device_id, created_at DESC);
-- ============================================
-- TRIGGER FUNCTIONS (Phase 1)
-- TRIGGER FUNCTIONS
-- ============================================
-- Update updated_at timestamp for devices
@@ -476,7 +476,7 @@ COMMENT ON COLUMN media_items.google_books_id IS 'Google Books identifier for in
-- - Highlights can have associated notes for detailed annotations
-- ============================================
-- PERFORMANCE OPTIMIZATION (Phase 7)
-- PERFORMANCE OPTIMIZATION
-- ============================================
-- NOTE: ALTER SYSTEM commands are commented out for sqlc compatibility.
-- These should be run manually by DBA or via init script:
@@ -513,7 +513,7 @@ COMMENT ON COLUMN media_items.google_books_id IS 'Google Books identifier for in
-- REINDEX TABLE CONCURRENTLY reading_progress;
-- ============================================
-- SYNC HELPER FUNCTIONS (Phase 1)
-- SYNC HELPER FUNCTIONS
-- ============================================
-- Detect format group based on mimetype and file path
@@ -680,7 +680,7 @@ END;
$$ LANGUAGE plpgsql;
-- ============================================
-- KOREADER SYNC FUNCTIONS (Phase 3)
-- KOREADER SYNC FUNCTIONS
-- ============================================
-- Bulk update progress from KOReader sync data
@@ -799,7 +799,7 @@ END;
$$ LANGUAGE plpgsql;
-- ============================================
-- PHASE 1: UNIVERSAL BOOK IDENTIFIERS (Week 1)
--: UNIVERSAL BOOK IDENTIFIERS
-- ============================================
-- Add universal identifier columns to media_items table
@@ -937,7 +937,7 @@ ALTER TABLE kobo_shelves ADD COLUMN IF NOT EXISTS collection_id UUID REFERENCES
ALTER TABLE kobo_shelves ADD COLUMN IF NOT EXISTS position_in_collection INTEGER;
-- ============================================
-- PHASE 6: UNLINKED BOOKS TRACKING (Week 3-4)
--: UNLINKED BOOKS TRACKING
-- ============================================
-- Create unlinked_books table to track books that couldn't be auto-matched
+12 -12
View File
@@ -48,7 +48,7 @@ func (m *mockHandler) reset() {
m.stopSchedulerCalled = false
}
// TestApp_New tests Phase 5: App constructor
// TestApp_New tests App constructor
func TestApp_New(t *testing.T) {
e := echo.New()
handler := &mockHandler{}
@@ -62,7 +62,7 @@ func TestApp_New(t *testing.T) {
assert.NotNil(t, app.shutdownDone, "Shutdown done channel should be initialized")
}
// TestApp_SetShutdownTimeout tests Phase 5: configurable shutdown timeout
// TestApp_SetShutdownTimeout tests configurable shutdown timeout
func TestApp_SetShutdownTimeout(t *testing.T) {
e := echo.New()
handler := &mockHandler{}
@@ -74,7 +74,7 @@ func TestApp_SetShutdownTimeout(t *testing.T) {
assert.Equal(t, customTimeout, app.shutdownTimeout, "Shutdown timeout should be updated")
}
// TestApp_ShutdownDone tests Phase 5: shutdown done channel
// TestApp_ShutdownDone tests shutdown done channel
func TestApp_ShutdownDone(t *testing.T) {
e := echo.New()
handler := &mockHandler{}
@@ -92,7 +92,7 @@ func TestApp_ShutdownDone(t *testing.T) {
}
}
// TestApp_Start_BackgroundServices tests Phase 1 & 5: background service startup
// TestApp_Start_BackgroundServices tests background service startup
func TestApp_Start_BackgroundServices(t *testing.T) {
e := echo.New()
handler := &mockHandler{
@@ -135,7 +135,7 @@ func TestApp_Start_BackgroundServices(t *testing.T) {
<-done2
}
// TestApp_Shutdown_GracefulShutdown tests Phase 5: graceful shutdown sequence
// TestApp_Shutdown_GracefulShutdown tests graceful shutdown sequence
func TestApp_Shutdown_GracefulShutdown(t *testing.T) {
e := echo.New()
handler := &mockHandler{
@@ -150,7 +150,7 @@ func TestApp_Shutdown_GracefulShutdown(t *testing.T) {
assert.True(t, handler.stopSchedulerCalled, "StopScheduler should be called")
}
// TestApp_Shutdown_ThreadSafety tests Phase 5: thread-safe shutdown
// TestApp_Shutdown_ThreadSafety tests thread-safe shutdown
func TestApp_Shutdown_ThreadSafety(t *testing.T) {
e := echo.New()
handler := &mockHandler{}
@@ -175,7 +175,7 @@ func TestApp_Shutdown_ThreadSafety(t *testing.T) {
assert.True(t, handler.stopSchedulerCalled, "StopScheduler should be called at least once")
}
// TestApp_Shutdown_Timeout tests Phase 5: shutdown timeout handling
// TestApp_Shutdown_Timeout tests shutdown timeout handling
func TestApp_Shutdown_Timeout(t *testing.T) {
e := echo.New()
handler := &mockHandler{
@@ -191,7 +191,7 @@ func TestApp_Shutdown_Timeout(t *testing.T) {
assert.True(t, handler.stopSchedulerCalled, "StopScheduler should still be called")
}
// TestApp_Shutdown_ClosesEchoServer tests Phase 5: HTTP server shutdown
// TestApp_Shutdown_ClosesEchoServer tests HTTP server shutdown
func TestApp_Shutdown_ClosesEchoServer(t *testing.T) {
e := echo.New()
handler := &mockHandler{}
@@ -213,7 +213,7 @@ func TestApp_Shutdown_ClosesEchoServer(t *testing.T) {
assert.True(t, handler.stopSchedulerCalled, "StopScheduler should be called")
}
// TestApp_SignalHandling tests Phase 5: signal handling (SIGINT, SIGTERM, SIGQUIT)
// TestApp_SignalHandling tests signal handling (SIGINT, SIGTERM, SIGQUIT)
func TestApp_SignalHandling(t *testing.T) {
tests := []struct {
name string
@@ -259,7 +259,7 @@ func TestApp_SignalHandling(t *testing.T) {
}
}
// TestApp_Integration_StartupSequence tests Phase 1 & 5: complete startup sequence
// TestApp_Integration_StartupSequence tests complete startup sequence
func TestApp_Integration_StartupSequence(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
@@ -320,8 +320,8 @@ func TestApp_HandlerInterface(t *testing.T) {
var _ Handler = &mockHandler{}
}
// TestPhase1_AutoStartVerification tests Phase 1: auto-start functionality
func TestPhase1_AutoStartVerification(t *testing.T) {
// TestApp_AutoStartVerification tests auto-start functionality
func TestApp_AutoStartVerification(t *testing.T) {
t.Run("Auto-start runs asynchronously", func(t *testing.T) {
e := echo.New()
handler := &mockHandler{
+7 -7
View File
@@ -15,7 +15,7 @@ type Querier interface {
// Add book to collection
AddBookToCollection(ctx context.Context, arg AddBookToCollectionParams) (CollectionItems, error)
// ============================================
// KOBO SHELF MANAGEMENT QUERIES (Phase 4)
// KOBO SHELF MANAGEMENT QUERIES
// ============================================
AddBookToKoboShelf(ctx context.Context, arg AddBookToKoboShelfParams) (KoboShelves, error)
// Library Folders queries
@@ -36,7 +36,7 @@ type Querier interface {
// Create collection
CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collections, error)
// ============================================
// PHASE 2: DEVICE MANAGEMENT & AUTH (Weeks 5-6)
// DEVICE MANAGEMENT & AUTH
// ============================================
// Device Registration & Management
CreateDevice(ctx context.Context, arg CreateDeviceParams) (Devices, error)
@@ -65,7 +65,7 @@ type Querier interface {
// Create OPDS token
CreateOpdsToken(ctx context.Context, arg CreateOpdsTokenParams) (OpdsTokens, error)
// ============================================
// KOBO ENTITLEMENT QUERIES (Phase 4)
// KOBO ENTITLEMENT QUERIES
// ============================================
CreateOrUpdateKoboEntitlement(ctx context.Context, arg CreateOrUpdateKoboEntitlementParams) (KoboEntitlements, error)
// Create reading history entry
@@ -78,7 +78,7 @@ type Querier interface {
// Sync Queue Management
CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueItemParams) (SyncQueue, error)
// ============================================
// PHASE 6: ENHANCED KOBO SYNC (Week 3-4)
// ENHANCED KOBO SYNC
// ============================================
// Create unlinked book entry
CreateUnlinkedBook(ctx context.Context, arg CreateUnlinkedBookParams) (UnlinkedBooks, error)
@@ -166,7 +166,7 @@ type Querier interface {
GetMediaItem(ctx context.Context, id pgtype.UUID) (MediaItems, error)
GetMediaItemByFilePath(ctx context.Context, filePath string) (MediaItems, error)
// ============================================
// PHASE 3: KOREADER SYNC PROTOCOL (Weeks 7-9)
// KOREADER SYNC PROTOCOL
// ============================================
GetMediaItemByFilePathForSync(ctx context.Context, filePath string) (MediaItems, error)
GetMediaItemByKoboContentId(ctx context.Context, koboContentID pgtype.Text) (MediaItems, error)
@@ -293,13 +293,13 @@ type Querier interface {
// Update media item format
UpdateMediaItemFormat(ctx context.Context, arg UpdateMediaItemFormatParams) (MediaItemFormats, error)
// ============================================
// PHASE 1: FORMAT DETECTION & PROGRESS (Week 2)
// FORMAT DETECTION & PROGRESS
// ============================================
// Update media item format group information
UpdateMediaItemFormatGroup(ctx context.Context, arg UpdateMediaItemFormatGroupParams) error
// Media Items Admin Operations
// ============================================
// PHASE 1: UNIVERSAL BOOK IDENTIFIERS (Week 1)
// UNIVERSAL BOOK IDENTIFIERS
// ============================================
// Update media item with universal identifiers
UpdateMediaItemIdentifiers(ctx context.Context, arg UpdateMediaItemIdentifiersParams) (MediaItems, error)
+7 -7
View File
@@ -60,7 +60,7 @@ type AddBookToKoboShelfParams struct {
}
// ============================================
// KOBO SHELF MANAGEMENT QUERIES (Phase 4)
// KOBO SHELF MANAGEMENT QUERIES
// ============================================
func (q *Queries) AddBookToKoboShelf(ctx context.Context, arg AddBookToKoboShelfParams) (KoboShelves, error) {
row := q.db.QueryRow(ctx, AddBookToKoboShelf,
@@ -292,7 +292,7 @@ type CreateDeviceParams struct {
}
// ============================================
// PHASE 2: DEVICE MANAGEMENT & AUTH (Weeks 5-6)
// DEVICE MANAGEMENT & AUTH
// ============================================
// Device Registration & Management
func (q *Queries) CreateDevice(ctx context.Context, arg CreateDeviceParams) (Devices, error) {
@@ -837,7 +837,7 @@ type CreateOrUpdateKoboEntitlementParams struct {
}
// ============================================
// KOBO ENTITLEMENT QUERIES (Phase 4)
// KOBO ENTITLEMENT QUERIES
// ============================================
func (q *Queries) CreateOrUpdateKoboEntitlement(ctx context.Context, arg CreateOrUpdateKoboEntitlementParams) (KoboEntitlements, error) {
row := q.db.QueryRow(ctx, CreateOrUpdateKoboEntitlement,
@@ -1084,7 +1084,7 @@ type CreateUnlinkedBookParams struct {
}
// ============================================
// PHASE 6: ENHANCED KOBO SYNC (Week 3-4)
// ENHANCED KOBO SYNC
// ============================================
// Create unlinked book entry
func (q *Queries) CreateUnlinkedBook(ctx context.Context, arg CreateUnlinkedBookParams) (UnlinkedBooks, error) {
@@ -2902,7 +2902,7 @@ SELECT id, library_id, title, author, isbn, description, file_path, file_size, m
`
// ============================================
// PHASE 3: KOREADER SYNC PROTOCOL (Weeks 7-9)
// KOREADER SYNC PROTOCOL
// ============================================
func (q *Queries) GetMediaItemByFilePathForSync(ctx context.Context, filePath string) (MediaItems, error) {
row := q.db.QueryRow(ctx, GetMediaItemByFilePathForSync, filePath)
@@ -7175,7 +7175,7 @@ type UpdateMediaItemFormatGroupParams struct {
}
// ============================================
// PHASE 1: FORMAT DETECTION & PROGRESS (Week 2)
// FORMAT DETECTION & PROGRESS
// ============================================
// Update media item format group information
func (q *Queries) UpdateMediaItemFormatGroup(ctx context.Context, arg UpdateMediaItemFormatGroupParams) error {
@@ -7215,7 +7215,7 @@ type UpdateMediaItemIdentifiersParams struct {
// Media Items Admin Operations
// ============================================
// PHASE 1: UNIVERSAL BOOK IDENTIFIERS (Week 1)
// UNIVERSAL BOOK IDENTIFIERS
// ============================================
// Update media item with universal identifiers
func (q *Queries) UpdateMediaItemIdentifiers(ctx context.Context, arg UpdateMediaItemIdentifiersParams) (MediaItems, error) {
+7 -7
View File
@@ -520,7 +520,7 @@ UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = $1 AND revoked_at I
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)
-- FORMAT DETECTION & PROGRESS
-- ============================================
-- Update media item format group information
@@ -716,7 +716,7 @@ SELECT
LIMIT $2;
-- ============================================
-- PHASE 2: DEVICE MANAGEMENT & AUTH (Weeks 5-6)
-- DEVICE MANAGEMENT & AUTH
-- ============================================
-- Device Registration & Management
@@ -929,7 +929,7 @@ DELETE FROM sync_conflicts WHERE id = $1;
ORDER BY sc.created_at DESC;
-- ============================================
-- PHASE 3: KOREADER SYNC PROTOCOL (Weeks 7-9)
-- KOREADER SYNC PROTOCOL
-- ============================================
-- name: GetMediaItemByFilePathForSync :one
@@ -1040,7 +1040,7 @@ WHERE media_item_id = $1
AND last_sync_source != $3;
-- ============================================
-- KOBO SHELF MANAGEMENT QUERIES (Phase 4)
-- KOBO SHELF MANAGEMENT QUERIES
-- ============================================
-- name: AddBookToKoboShelf :one
@@ -1093,7 +1093,7 @@ FROM kobo_shelves
WHERE device_id = $1;
-- ============================================
-- KOBO ENTITLEMENT QUERIES (Phase 4)
-- KOBO ENTITLEMENT QUERIES
-- ============================================
-- name: CreateOrUpdateKoboEntitlement :one
@@ -1161,7 +1161,7 @@ SELECT * FROM media_items WHERE kobo_content_id = $1;
-- Media Items Admin Operations
-- ============================================
-- PHASE 1: UNIVERSAL BOOK IDENTIFIERS (Week 1)
-- UNIVERSAL BOOK IDENTIFIERS
-- ============================================
-- Update media item with universal identifiers
@@ -1522,7 +1522,7 @@ WHERE ks.device_id = $1 AND ks.collection_id = $2
ORDER BY ks.position_in_collection ASC, ks.shelf_position ASC;
-- ============================================
-- PHASE 6: ENHANCED KOBO SYNC (Week 3-4)
-- ENHANCED KOBO SYNC
-- ============================================
-- Create unlinked book entry
+8 -8
View File
@@ -27,7 +27,7 @@ func NewKoboHandler(db *database.Queries, connManager *wsync.ConnectionManager)
}
// mapContentIdToBookhoardUUID maps Kobo ContentId to Bookhoard UUID with multiple fallback strategies
// Phase 6: Enhanced Kobo Sync - ContentId Mapping Logic
// Enhanced Kobo Sync - ContentId Mapping Logic
func (h *KoboHandler) mapContentIdToBookhoardUUID(ctx echo.Context, contentId string, deviceID uuid.UUID) (uuid.UUID, error, string) {
// Step 1: Try direct ContentId lookup in device_catalogs table
catalog, err := h.db.GetDeviceCatalogByKoboContentId(ctx.Request().Context(), contentId)
@@ -320,7 +320,7 @@ func (h *KoboHandler) Initialization(c echo.Context) error {
author = item.Author.String
}
// Phase 6: Use ContentId mapping instead of direct UUID
// Use ContentId mapping instead of direct UUID
koboContentId, err := h.mapBookhoardUUIDToKoboContentId(c, bookhoardUUID, deviceUUID)
if err != nil {
// Fallback to entitlement_id or generate new one
@@ -351,7 +351,7 @@ func (h *KoboHandler) Initialization(c echo.Context) error {
contentType = "5"
}
// Phase 6: Get collection metadata for this book
// Get collection metadata for this book
collections, _ := h.getCollectionMetadataForBook(c, bookhoardUUID, deviceUUID)
librarySync = append(librarySync, KoboLibraryBook{
@@ -403,7 +403,7 @@ func (h *KoboHandler) Markup(c echo.Context) error {
unlinkedBooks := 0
for _, readingSync := range req.ReadingSync {
// Phase 6: Use ContentId mapping with fallback logic
// Use ContentId mapping with fallback logic
bookhoardUUID, err, _ := h.mapContentIdToBookhoardUUID(c, readingSync.ContentId, deviceUUID)
if err != nil || bookhoardUUID == uuid.Nil {
// Unlinked book detected
@@ -440,7 +440,7 @@ func (h *KoboHandler) Markup(c echo.Context) error {
}
for _, bookmarkSync := range req.BookmarkSync {
// Phase 6: Use ContentId mapping with fallback logic
// Use ContentId mapping with fallback logic
bookhoardUUID, err, _ := h.mapContentIdToBookhoardUUID(c, bookmarkSync.ContentId, deviceUUID)
if err != nil || bookhoardUUID == uuid.Nil {
// Unlinked book - skip
@@ -541,7 +541,7 @@ func (h *KoboHandler) Bookmark(c echo.Context) error {
bookmarksSynced := 0
for _, bookmarkSync := range req.BookmarkSync {
// Phase 6: Use ContentId mapping with fallback logic
// Use ContentId mapping with fallback logic
bookhoardUUID, err, _ := h.mapContentIdToBookhoardUUID(c, bookmarkSync.ContentId, deviceUUID)
if err != nil || bookhoardUUID == uuid.Nil {
// Unlinked book - skip
@@ -606,7 +606,7 @@ func (h *KoboHandler) AnalyticsGettests(c echo.Context) error {
}
for _, test := range req {
// Phase 6: Use ContentId mapping with fallback logic
// Use ContentId mapping with fallback logic
bookhoardUUID, err, _ := h.mapContentIdToBookhoardUUID(c, test.ContentId, deviceUUID)
if err != nil || bookhoardUUID == uuid.Nil {
// Unlinked book - skip
@@ -683,7 +683,7 @@ func (h *KoboHandler) SyncFromServer(c echo.Context) error {
highlightsSent := 0
for _, syncData := range req {
// Phase 6: Use ContentId mapping with fallback logic
// Use ContentId mapping with fallback logic
bookhoardUUID, err, _ := h.mapContentIdToBookhoardUUID(c, syncData.ContentId, deviceUUID)
if err != nil || bookhoardUUID == uuid.Nil {
// Unlinked book - skip
+1 -1
View File
@@ -11,7 +11,7 @@ func registerProgressRoutes(cfg *Config, scannerHandler *handlers.Handler) {
jwtMiddleware := createJWTMiddleware(cfg)
protected := e.Group("/api", jwtMiddleware)
// Universal Progress routes (Phase 1)
// Universal Progress routes
protected.GET("/progress/:id", scannerHandler.GetUniversalProgress)
protected.POST("/progress/:id", scannerHandler.UpdateUniversalProgress)
protected.GET("/progress/:id/history", scannerHandler.GetProgressHistory)
+14 -14
View File
@@ -48,8 +48,8 @@ type MediaMetadata struct {
ASIN string
Tags []string
Phase1HashInfo *HashInfo
Phase1FormatFormats []*FormatInfo
FileHashInfo *HashInfo
FileFormats []*FormatInfo
}
type HashInfo struct {
@@ -338,15 +338,15 @@ func (s *MediaScanner) processMediaFile(ctx context.Context, path string) error
metadata = &MediaMetadata{}
}
// Extract hash information (Phase 2)
// Extract hash information during metadata extraction
hashInfo, formatInfo, err := s.extractHashInfo(path)
if err != nil {
fmt.Printf("Warning: failed to extract hash info from %s: %v\n", path, err)
hashInfo = &HashInfo{}
formatInfo = &FormatInfo{}
} else {
metadata.Phase1HashInfo = hashInfo
metadata.Phase1FormatFormats = []*FormatInfo{formatInfo}
metadata.FileHashInfo = hashInfo
metadata.FileFormats = []*FormatInfo{formatInfo}
fmt.Printf("Hash info for %s: SHA256=%s, OPF_ID=%s, OPF_UUID=%s, Confidence=%s\n",
path, hashInfo.FileSHA256, hashInfo.OPFIdentifier, hashInfo.OPFUUID, hashInfo.HashConfidence)
}
@@ -479,22 +479,22 @@ func (s *MediaScanner) processMediaFile(ctx context.Context, path string) error
return fmt.Errorf("failed to create media item: %v", err)
}
// Update hash information (Phase 2)
if metadata.Phase1HashInfo != nil && metadata.Phase1HashInfo.FileSHA256 != "" {
// Update hash information before database storage
if metadata.FileHashInfo != nil && metadata.FileHashInfo.FileSHA256 != "" {
_, err = s.db.UpdateMediaItemIdentifiers(ctx, database.UpdateMediaItemIdentifiersParams{
ID: createdItem.ID,
FileSha256: pgtype.Text{String: metadata.Phase1HashInfo.FileSHA256, Valid: true},
OpfIdentifier: pgtype.Text{String: metadata.Phase1HashInfo.OPFIdentifier, Valid: metadata.Phase1HashInfo.OPFIdentifier != ""},
OpfUuid: pgtype.Text{String: metadata.Phase1HashInfo.OPFUUID, Valid: metadata.Phase1HashInfo.OPFUUID != ""},
HashConfidence: pgtype.Text{String: metadata.Phase1HashInfo.HashConfidence, Valid: true},
FileSha256: pgtype.Text{String: metadata.FileHashInfo.FileSHA256, Valid: true},
OpfIdentifier: pgtype.Text{String: metadata.FileHashInfo.OPFIdentifier, Valid: metadata.FileHashInfo.OPFIdentifier != ""},
OpfUuid: pgtype.Text{String: metadata.FileHashInfo.OPFUUID, Valid: metadata.FileHashInfo.OPFUUID != ""},
HashConfidence: pgtype.Text{String: metadata.FileHashInfo.HashConfidence, Valid: true},
})
if err != nil {
fmt.Printf("Warning: failed to update hash identifiers for %s: %v\n", path, err)
}
}
// Store format information (Phase 2)
for _, format := range metadata.Phase1FormatFormats {
// Store format information in the database
for _, format := range metadata.FileFormats {
_, err = s.db.CreateMediaItemFormat(ctx, database.CreateMediaItemFormatParams{
MediaItemID: createdItem.ID,
FormatType: format.FormatType,
@@ -1021,7 +1021,7 @@ func (s *MediaScanner) Close() error {
}
// ============================================
// PHASE 2: SCANNER ENHANCEMENTS (Week 1-2)
// SCANNER ENHANCEMENTS
// ============================================
// calculateFileSHA256 calculates SHA-256 hash using streaming to avoid loading entire file into memory
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
)
// TestMediaScanner_LibraryTypeAwareScanning tests Phase 2: library-type-aware scanning
// TestMediaScanner_LibraryTypeAwareScanning tests library-type-aware scanning
func TestMediaScanner_LibraryTypeAwareScanning(t *testing.T) {
t.Run("isScannableFile checks library type restrictions", func(t *testing.T) {
scanner := &MediaScanner{
@@ -121,7 +121,7 @@ func TestMediaScanner_LibraryTypeAwareScanning(t *testing.T) {
})
}
// TestMediaScanner_SetFolders_BuildsLibraryTypeCache tests Phase 2: SetFolders builds cache
// TestMediaScanner_SetFolders_BuildsLibraryTypeCache tests SetFolders builds cache
func TestMediaScanner_SetFolders_BuildsLibraryTypeCache(t *testing.T) {
// This is a unit test that verifies SetFolders properly initializes the libraryTypes cache
// Full integration testing would require a mock database
@@ -150,7 +150,7 @@ func TestMediaScanner_SetFolders_BuildsLibraryTypeCache(t *testing.T) {
})
}
// TestMediaScanner_LibraryTypeCrossContamination tests Phase 2: prevents cross-contamination
// TestMediaScanner_LibraryTypeCrossContamination tests prevents cross-contamination
func TestMediaScanner_LibraryTypeCrossContamination(t *testing.T) {
t.Run("Ebook library rejects comic formats", func(t *testing.T) {
scanner := &MediaScanner{
@@ -185,7 +185,7 @@ func TestMediaScanner_LibraryTypeCrossContamination(t *testing.T) {
})
}
// TestMediaScanner_MultipleLibraryTypes tests Phase 2: multiple libraries with different types
// TestMediaScanner_MultipleLibraryTypes tests multiple libraries with different types
func TestMediaScanner_MultipleLibraryTypes(t *testing.T) {
scanner := &MediaScanner{
folders: []string{