import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:worldhopper/models/koreader_progress.dart'; import 'package:worldhopper/models/opds_server.dart'; import 'package:worldhopper/providers/server_provider.dart'; import 'package:worldhopper/services/koreader_sync_service.dart'; /// Provider for creating a KoreaderSyncService for a given server. /// Only returns a service if the server has KOReader sync enabled. final koreaderSyncServiceProvider = Provider.family((ref, server) { if (!server.koreaderSyncEnabled || server.koreaderSyncUrl == null || server.koreaderSyncUrl!.isEmpty) { return null; } return KoreaderSyncService(server); }); /// Provider to get a KoreaderSyncService by server ID. /// Returns null if server not found or KOReader sync is not enabled. final koreaderSyncServiceByIdProvider = FutureProvider.family((ref, serverId) async { final server = await ref.watch(serverProvider(serverId).future); if (server == null) return null; return ref.watch(koreaderSyncServiceProvider(server)); }); /// Push reading progress to KOReader sync server. /// Returns true if sync was successful, false otherwise. /// Fails silently (logs error) to not interrupt the reading experience. Future pushKoreaderProgress({ required KoreaderSyncService syncService, required String documentHash, required double percentage, required String progress, }) async { try { return await syncService.updateProgress( documentHash: documentHash, percentage: percentage, progress: progress, ); } on KoreaderSyncAuthException { rethrow; } catch (e) { debugPrint('Failed to push KOReader progress: $e'); return false; } } /// Pull reading progress from KOReader sync server. /// Returns the remote progress if available, null otherwise. Future pullKoreaderProgress({ required KoreaderSyncService syncService, required String documentHash, }) async { try { return await syncService.getProgress(documentHash); } on KoreaderSyncAuthException { rethrow; } catch (e) { debugPrint('Failed to pull KOReader progress: $e'); return null; } } /// Compute the document hash for a given entry. /// If the EPUB file is available, uses the file's partial MD5 hash (compatible with KOReader). /// Falls back to hashing the OPDS entry ID. Future computeDocumentHash({ required String entryId, File? epubFile, }) async { if (epubFile != null && await epubFile.exists()) { return await KoreaderSyncService.computeFileHash(epubFile); } return KoreaderSyncService.computeStringHash(entryId); }