worldhopper/lib/services/server_software/server_software.dart
Felipe M. b5debe2117
All checks were successful
Release / build (push) Successful in 9m40s
feat: sync EPUB reading progress to Kavita with debounced updates
2026-04-17 12:09:44 +02:00

117 lines
4 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<bool> testConnection(Server server, {CancelToken? cancelToken});
/// Fetch the root catalog/feed for a server.
Future<ParsedFeed> fetchRootFeed(Server server, {CancelToken? cancelToken});
/// Fetch a feed/catalog from a specific URL.
Future<ParsedFeed> 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<Map<String, EntryProgress>> fetchEntryProgress(
Server server,
List<Entry> entries,
);
/// Report page progress to the server (for reading tracking).
Future<void> reportPageProgress(
Server server, StreamLink streamLink, int page);
/// Report EPUB reading progress as a fractional percentage (0.0–1.0).
/// Default is a no-op — servers without rich progress APIs can ignore it.
Future<void> reportEpubProgress(
Server server,
Entry entry,
double progress,
) async {}
/// Mark an entry (volume/chapter) as fully read.
Future<void> markAsRead(Server server, Entry entry);
/// Mark an entry (volume/chapter) as unread.
Future<void> markAsUnread(Server server, Entry entry);
/// Download an EPUB file. Returns the local file.
/// If [savePath] is provided, writes directly to that path; otherwise uses a
/// temporary/cache location.
Future<File> downloadEpub(
Server server,
Entry entry, {
String? savePath,
void Function(double progress)? onProgress,
CancelToken? cancelToken,
});
/// Download all pages of an image stream chapter. Returns the directory path.
Future<String> 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<String, String> getAuthHeaders(Server server);
/// Clear any cached state for a server (tokens, HTTP clients, etc).
void clearCachedState(String serverId);
}