Rename all OPDS-prefixed models to generic names (Server, Feed, Entry, Link, StreamLink), expand the ServerSoftware interface to cover all server interactions, and implement a full Kavita REST API client that replaces the OPDS delegation. - Rename OPDS* models to generic names across ~60 files - Add database migrations 13 (opds_id → entry_id) and 14 (unify credentials) - Create OPDSServerSoftware wrapping existing OPDS services - Create KavitaApiClient for direct Kavita REST API calls - Create KavitaFeedMapper to convert Kavita JSON to Feed/Entry models - Rewrite KavitaServerSoftware to use native API (no OPDS delegation) - Unify server credentials (remove softwareUsername/softwarePassword) - Simplify add/edit server UI to single auth section - Add test connection button to server add/edit screens - Add progress indicator to PublicationCard using local and server data - Eliminate softwareType branching in reader screens - Add EntryProgress and fetchEntryProgress to ServerSoftware interface - Fix Entry.acquisitionLink crash on empty links - Add 59 new tests covering models, services, and providers
105 lines
3.6 KiB
Dart
105 lines
3.6 KiB
Dart
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);
|
|
|
|
/// 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.
|
|
Future<File> downloadEpub(
|
|
Server server,
|
|
Entry entry, {
|
|
void Function(double progress)? onProgress,
|
|
});
|
|
|
|
/// 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);
|
|
}
|