151 lines
4.7 KiB
Dart
151 lines
4.7 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/auth_service.dart';
|
|
import 'package:worldhopper/services/epub_download_service.dart';
|
|
import 'package:worldhopper/services/image_stream_download_service.dart';
|
|
import 'package:worldhopper/services/opds_parser.dart' show ParsedFeed;
|
|
import 'package:worldhopper/services/opds_service.dart';
|
|
import 'package:worldhopper/services/server_software/server_software.dart';
|
|
|
|
/// OPDS implementation of [ServerSoftware].
|
|
///
|
|
/// Wraps the existing OPDS service layer (fetching XML feeds, PSE page
|
|
/// streaming, HTTP Basic Auth) to implement the full server interface.
|
|
/// This is the default/fallback implementation for generic OPDS servers.
|
|
class OPDSServerSoftware implements ServerSoftware {
|
|
final OPDSService _opdsService;
|
|
final AuthService _authService;
|
|
final EpubDownloadService _epubDownloadService;
|
|
final ImageStreamDownloadService _imageStreamDownloadService;
|
|
|
|
OPDSServerSoftware({
|
|
OPDSService? opdsService,
|
|
AuthService? authService,
|
|
EpubDownloadService? epubDownloadService,
|
|
ImageStreamDownloadService? imageStreamDownloadService,
|
|
}) : _opdsService = opdsService ?? OPDSService(),
|
|
_authService = authService ?? AuthService(),
|
|
_epubDownloadService = epubDownloadService ?? EpubDownloadService(),
|
|
_imageStreamDownloadService =
|
|
imageStreamDownloadService ?? ImageStreamDownloadService();
|
|
|
|
@override
|
|
bool detectFromUrl(Server server) => true; // OPDS is the fallback
|
|
|
|
@override
|
|
Future<bool> testConnection(Server server, {CancelToken? cancelToken}) async {
|
|
return _opdsService.testConnection(server, cancelToken: cancelToken);
|
|
}
|
|
|
|
@override
|
|
Future<ParsedFeed> fetchRootFeed(Server server,
|
|
{CancelToken? cancelToken}) async {
|
|
return _opdsService.fetchRootFeed(server, cancelToken: cancelToken);
|
|
}
|
|
|
|
@override
|
|
Future<ParsedFeed> fetchFeed(Server server, String url,
|
|
{CancelToken? cancelToken}) async {
|
|
return _opdsService.fetchFeed(server, url, cancelToken: cancelToken);
|
|
}
|
|
|
|
@override
|
|
Future<Map<String, EntryProgress>> fetchEntryProgress(
|
|
Server server,
|
|
List<Entry> entries,
|
|
) async {
|
|
// For OPDS, progress is already embedded in StreamLink.lastRead from the
|
|
// feed response. Extract it from the entries directly.
|
|
final result = <String, EntryProgress>{};
|
|
for (final entry in entries) {
|
|
if (entry.hasStreamLink &&
|
|
entry.streamLink!.lastRead != null &&
|
|
entry.streamLink!.lastRead! > 0) {
|
|
result[entry.id] = EntryProgress(
|
|
entryId: entry.id,
|
|
currentPage: entry.streamLink!.lastRead!,
|
|
totalPages: entry.streamLink!.pageCount,
|
|
);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
Future<void> reportPageProgress(
|
|
Server server, StreamLink streamLink, int page) async {
|
|
await _opdsService.reportPageProgress(server, streamLink, page);
|
|
}
|
|
|
|
@override
|
|
Future<void> reportEpubProgress(
|
|
Server server,
|
|
Entry entry,
|
|
double progress,
|
|
) async {
|
|
// Generic OPDS has no standard EPUB progress endpoint.
|
|
}
|
|
|
|
@override
|
|
Future<void> markAsRead(Server server, Entry entry) async {
|
|
// PSE-based: fetch the last page URL to trigger server-side progress
|
|
if (entry.hasStreamLink) {
|
|
await _opdsService.markAsRead(server, entry.streamLink!);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> markAsUnread(Server server, Entry entry) async {
|
|
// No-op for generic OPDS — PSE has no unread concept
|
|
}
|
|
|
|
@override
|
|
Future<File> downloadEpub(
|
|
Server server,
|
|
Entry entry, {
|
|
String? savePath,
|
|
void Function(double progress)? onProgress,
|
|
CancelToken? cancelToken,
|
|
}) async {
|
|
return _epubDownloadService.downloadEpub(
|
|
entry,
|
|
server,
|
|
savePath: savePath,
|
|
onProgress: onProgress,
|
|
cancelToken: cancelToken,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<String> downloadImageStreamChapter({
|
|
required Server server,
|
|
required StreamLink streamLink,
|
|
required String seriesId,
|
|
required String chapterId,
|
|
void Function(int downloaded, int total)? onProgress,
|
|
CancelToken? cancelToken,
|
|
}) async {
|
|
return _imageStreamDownloadService.downloadChapter(
|
|
server: server,
|
|
streamLink: streamLink,
|
|
seriesId: seriesId,
|
|
chapterId: chapterId,
|
|
onProgress: onProgress,
|
|
cancelToken: cancelToken,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Map<String, String> getAuthHeaders(Server server) {
|
|
return _authService.getAuthHeaders(server);
|
|
}
|
|
|
|
@override
|
|
void clearCachedState(String serverId) {
|
|
_opdsService.clearClient(serverId);
|
|
}
|
|
}
|