Phase 3 Week 7: Add KOReader bulk sync function to database schema
- Add bulk_update_progress_from_koreader() function for batch processing - Handles progress, annotations, and conflict detection - Returns success/failure status for each book - Supports device matching by UUID, file path, or title/author - Implements automatic conflict detection for concurrent syncs - Part of Phase 3 KOReader Integration implementation
This commit is contained in:
+120
-1
@@ -596,4 +596,123 @@ BEGIN
|
|||||||
'merge_timestamp', NOW()
|
'merge_timestamp', NOW()
|
||||||
);
|
);
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- KOREADER SYNC FUNCTIONS (Phase 3)
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- Bulk update progress from KOReader sync data
|
||||||
|
CREATE OR REPLACE FUNCTION bulk_update_progress_from_koreader(
|
||||||
|
p_user_id UUID,
|
||||||
|
p_sync_data JSONB
|
||||||
|
) RETURNS TABLE (
|
||||||
|
media_item_id UUID,
|
||||||
|
success BOOLEAN,
|
||||||
|
message TEXT
|
||||||
|
) AS $$
|
||||||
|
DECLARE
|
||||||
|
book_record JSONB;
|
||||||
|
media_uuid UUID;
|
||||||
|
existing_progress reading_progress%ROWTYPE;
|
||||||
|
conflict_detected BOOLEAN;
|
||||||
|
BEGIN
|
||||||
|
FOR book_record IN SELECT * FROM jsonb_array_elements(p_sync_data->'books')
|
||||||
|
LOOP
|
||||||
|
-- Try to find media item by UUID
|
||||||
|
media_uuid := NULL;
|
||||||
|
SELECT id INTO media_uuid FROM media_items WHERE id = (book_record->>'uuid')::uuid;
|
||||||
|
|
||||||
|
-- If not found by UUID, try to match by file path
|
||||||
|
IF media_uuid IS NULL AND book_record ? 'file_path' THEN
|
||||||
|
SELECT id INTO media_uuid FROM media_items WHERE file_path = book_record->>'file_path';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- If still not found, try to match by title + author
|
||||||
|
IF media_uuid IS NULL THEN
|
||||||
|
SELECT id INTO media_uuid FROM media_items
|
||||||
|
WHERE title = book_record->>'title'
|
||||||
|
AND author = book_record->>'author'
|
||||||
|
LIMIT 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- If no match found, return failure
|
||||||
|
IF media_uuid IS NULL THEN
|
||||||
|
RETURN QUERY SELECT NULL::uuid, FALSE, 'media item not found';
|
||||||
|
CONTINUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Check for conflicts
|
||||||
|
conflict_detected := detect_conflict(media_uuid, book_record, 'koreader');
|
||||||
|
|
||||||
|
-- Check if progress exists
|
||||||
|
SELECT * INTO existing_progress FROM reading_progress
|
||||||
|
WHERE media_item_id = media_uuid AND user_id = p_user_id;
|
||||||
|
|
||||||
|
-- Update or insert progress
|
||||||
|
IF existing_progress.media_item_id IS NOT NULL THEN
|
||||||
|
UPDATE reading_progress SET
|
||||||
|
percentage = (book_record->>'percentage')::FLOAT,
|
||||||
|
character_offset = CASE WHEN book_record ? 'character' THEN (book_record->>'character')::BIGINT ELSE existing_progress.character_offset END,
|
||||||
|
epubcfi = CASE WHEN book_record ? 'epubcfi' THEN (book_record->>'epubcfi')::TEXT ELSE existing_progress.epubcfi END,
|
||||||
|
chapter = CASE WHEN book_record ? 'chapter' THEN (book_record->>'chapter')::INTEGER ELSE existing_progress.chapter END,
|
||||||
|
chapter_progress = (book_record->>'percentage')::FLOAT,
|
||||||
|
last_sync_device = 'koreader',
|
||||||
|
last_sync_source = 'koreader',
|
||||||
|
last_sync_timestamp = NOW(),
|
||||||
|
conflict_detected = conflict_detected,
|
||||||
|
conflict_resolved = NOT conflict_detected,
|
||||||
|
last_read_at = CASE WHEN book_record ? 'last_read' THEN (book_record->>'last_read')::TIMESTAMP WITH TIME ZONE ELSE NOW() END,
|
||||||
|
current_page = CASE WHEN book_record ? 'page' THEN (book_record->>'page')::INTEGER ELSE existing_progress.current_page END,
|
||||||
|
total_pages = CASE WHEN book_record ? 'total_pages' THEN (book_record->>'total_pages')::INTEGER ELSE existing_progress.total_pages END
|
||||||
|
WHERE media_item_id = media_uuid AND user_id = p_user_id;
|
||||||
|
ELSE
|
||||||
|
INSERT INTO reading_progress (
|
||||||
|
media_item_id,
|
||||||
|
user_id,
|
||||||
|
percentage,
|
||||||
|
character_offset,
|
||||||
|
epubcfi,
|
||||||
|
chapter,
|
||||||
|
chapter_progress,
|
||||||
|
last_sync_device,
|
||||||
|
last_sync_source,
|
||||||
|
last_sync_timestamp,
|
||||||
|
conflict_detected,
|
||||||
|
conflict_resolved,
|
||||||
|
last_read_at,
|
||||||
|
current_page,
|
||||||
|
total_pages
|
||||||
|
) VALUES (
|
||||||
|
media_uuid,
|
||||||
|
p_user_id,
|
||||||
|
(book_record->>'percentage')::FLOAT,
|
||||||
|
CASE WHEN book_record ? 'character' THEN (book_record->>'character')::BIGINT ELSE NULL END,
|
||||||
|
CASE WHEN book_record ? 'epubcfi' THEN (book_record->>'epubcfi')::TEXT ELSE NULL END,
|
||||||
|
CASE WHEN book_record ? 'chapter' THEN (book_record->>'chapter')::INTEGER ELSE NULL END,
|
||||||
|
(book_record->>'percentage')::FLOAT,
|
||||||
|
'koreader',
|
||||||
|
'koreader',
|
||||||
|
NOW(),
|
||||||
|
conflict_detected,
|
||||||
|
NOT conflict_detected,
|
||||||
|
CASE WHEN book_record ? 'last_read' THEN (book_record->>'last_read')::TIMESTAMP WITH TIME ZONE ELSE NOW() END,
|
||||||
|
CASE WHEN book_record ? 'page' THEN (book_record->>'page')::INTEGER ELSE NULL END,
|
||||||
|
CASE WHEN book_record ? 'total_pages' THEN (book_record->>'total_pages')::INTEGER ELSE NULL END
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Create conflict record if detected
|
||||||
|
IF conflict_detected THEN
|
||||||
|
INSERT INTO sync_conflicts (media_item_id, user_id, conflict_type, conflict_data)
|
||||||
|
VALUES (media_uuid, p_user_id, 'progress', jsonb_build_object(
|
||||||
|
'koreader', book_record,
|
||||||
|
'timestamp', NOW()
|
||||||
|
));
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN QUERY SELECT media_uuid, TRUE,
|
||||||
|
CASE WHEN conflict_detected THEN 'conflict detected' ELSE 'success' END;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|||||||
Reference in New Issue
Block a user