import 'package:dio/dio.dart'; import 'package:worldhopper/models/opds_feed.dart'; import 'package:worldhopper/services/opds_service.dart'; import 'package:worldhopper/services/url_helper.dart'; import 'package:worldhopper/repositories/server_repository.dart'; /// Repository for managing OPDS feeds class OPDSRepository { final OPDSService _service; final ServerRepository _serverRepository; // Simple in-memory cache final Map _feedCache = {}; final Map _cacheTimestamps = {}; final Duration _cacheDuration = const Duration(minutes: 5); OPDSRepository({ OPDSService? service, ServerRepository? serverRepository, }) : _service = service ?? OPDSService(), _serverRepository = serverRepository ?? ServerRepository(); /// Fetch a feed with caching Future fetchFeed( String serverId, String url, { bool forceRefresh = false, CancelToken? cancelToken, }) async { // Fetch server from database first to resolve URL final server = await _serverRepository.getServer(serverId); if (server == null) { throw Exception('Server not found: $serverId'); } // Resolve relative URLs against server base URL final resolvedUrl = UrlHelper.resolveUrl(server.url, url); final cacheKey = '$serverId:$resolvedUrl'; // Check cache if not forcing refresh if (!forceRefresh && _isCached(cacheKey)) { return _feedCache[cacheKey]!; } // Fetch feed from network final parsedFeed = await _service.fetchFeed( server, resolvedUrl, cancelToken: cancelToken, ); final feed = parsedFeed.feed; // Update cache _feedCache[cacheKey] = feed; _cacheTimestamps[cacheKey] = DateTime.now(); // Update server's last synced timestamp await _serverRepository.updateLastSynced(serverId, DateTime.now()); return feed; } /// Fetch the root feed for a server Future fetchRootFeed( String serverId, { bool forceRefresh = false, CancelToken? cancelToken, }) async { final server = await _serverRepository.getServer(serverId); if (server == null) { throw Exception('Server not found: $serverId'); } return fetchFeed( serverId, server.url, forceRefresh: forceRefresh, cancelToken: cancelToken, ); } /// Test connection to a server Future testConnection(String serverId) async { final server = await _serverRepository.getServer(serverId); if (server == null) { throw Exception('Server not found: $serverId'); } return await _service.testConnection(server); } /// Clear cache for a specific feed void clearFeedCache(String serverId, String url) { final cacheKey = '$serverId:$url'; _feedCache.remove(cacheKey); _cacheTimestamps.remove(cacheKey); } /// Clear cache for a specific server void clearServerCache(String serverId) { _feedCache.removeWhere((key, _) => key.startsWith('$serverId:')); _cacheTimestamps.removeWhere((key, _) => key.startsWith('$serverId:')); } /// Clear all cache void clearAllCache() { _feedCache.clear(); _cacheTimestamps.clear(); } /// Check if a feed is cached and still valid bool _isCached(String cacheKey) { if (!_feedCache.containsKey(cacheKey)) { return false; } final timestamp = _cacheTimestamps[cacheKey]; if (timestamp == null) { return false; } final age = DateTime.now().difference(timestamp); return age < _cacheDuration; } /// Clear service clients (when credentials change) void clearServiceClients() { _service.clearClients(); } /// Clear service client for a specific server void clearServiceClient(String serverId) { _service.clearClient(serverId); } }