import 'dart:io'; import 'package:dio/dio.dart'; import 'package:worldhopper/models/entry.dart'; import 'package:worldhopper/models/server.dart'; import 'package:worldhopper/models/stream_link.dart'; import 'package:worldhopper/services/opds_parser.dart' show ParsedFeed; /// Server-reported reading progress for an entry. /// /// This represents progress as reported by the server, not the local database. /// Used to show progress indicators in the feed without requiring a local /// reading history. class EntryProgress { /// The entry ID this progress belongs to. final String entryId; /// Current page (0-indexed). final int currentPage; /// Total pages. final int totalPages; /// Progress as a fraction (0.0 to 1.0). double get percentage => totalPages > 0 ? (currentPage + 1) / totalPages : 0.0; /// Whether the entry has been fully read. bool get isCompleted => totalPages > 0 && currentPage >= totalPages - 1; const EntryProgress({ required this.entryId, required this.currentPage, required this.totalPages, }); } /// Abstract interface for all server interactions. /// /// Each server software type (OPDS, Kavita, etc.) provides its own /// implementation. The UI layer should interact exclusively through this /// interface, never branching on [ServerSoftwareType]. abstract class ServerSoftware { /// Returns true if the server URL matches this software's pattern. bool detectFromUrl(Server server); /// Test if the server is reachable and credentials are valid. /// /// Returns `true` on success. Throws on failure with details about what /// went wrong (network error, auth failure, unexpected response, etc.). Future testConnection(Server server, {CancelToken? cancelToken}); /// Fetch the root catalog/feed for a server. Future fetchRootFeed(Server server, {CancelToken? cancelToken}); /// Fetch a feed/catalog from a specific URL. Future fetchFeed(Server server, String url, {CancelToken? cancelToken}); /// Fetch server-side reading progress for a list of entries. /// /// Returns a map of entry ID → [EntryProgress]. Entries without progress /// on the server are omitted from the result. /// /// For OPDS servers, progress is extracted from [StreamLink.lastRead] which /// is already present in the feed data. For servers with richer APIs (e.g. /// Kavita), this can call dedicated progress endpoints. Future> fetchEntryProgress( Server server, List entries, ); /// Report page progress to the server (for reading tracking). Future reportPageProgress( Server server, StreamLink streamLink, int page); /// Mark an entry (volume/chapter) as fully read. Future markAsRead(Server server, Entry entry); /// Mark an entry (volume/chapter) as unread. Future markAsUnread(Server server, Entry entry); /// Download an EPUB file. Returns the local file. Future downloadEpub( Server server, Entry entry, { void Function(double progress)? onProgress, }); /// Download all pages of an image stream chapter. Returns the directory path. Future downloadImageStreamChapter({ required Server server, required StreamLink streamLink, required String seriesId, required String chapterId, void Function(int downloaded, int total)? onProgress, CancelToken? cancelToken, }); /// Get HTTP auth headers for image/resource requests (CachedNetworkImage, etc). Map getAuthHeaders(Server server); /// Clear any cached state for a server (tokens, HTTP clients, etc). void clearCachedState(String serverId); }