import 'dart:io'; import 'package:dio/dio.dart'; import 'package:path/path.dart' as path; import 'package:worldhopper/models/server.dart'; import 'package:worldhopper/models/stream_link.dart'; import 'package:worldhopper/services/auth_service.dart'; import 'package:worldhopper/services/download_storage_service.dart'; /// Downloads all pages of an OPDS-PS chapter to a local directory class ImageStreamDownloadService { final AuthService _authService; final DownloadStorageService _storageService; ImageStreamDownloadService({ AuthService? authService, DownloadStorageService? storageService, }) : _authService = authService ?? AuthService(), _storageService = storageService ?? DownloadStorageService(); /// Derive file extension from MIME type String _extensionForType(String mimeType) { final lower = mimeType.toLowerCase(); if (lower.contains('png')) return '.png'; if (lower.contains('gif')) return '.gif'; if (lower.contains('webp')) return '.webp'; return '.jpg'; // default for image/jpeg and unknown } /// Download all pages of an image stream chapter /// /// Returns the chapter directory path on success. Future downloadChapter({ required Server server, required StreamLink streamLink, required String seriesId, required String chapterId, void Function(int downloaded, int total)? onProgress, CancelToken? cancelToken, }) async { final dio = _authService.createAuthenticatedClient(server); final chapterDir = await _storageService.getChapterDir(seriesId, chapterId); final ext = _extensionForType(streamLink.type); final totalPages = streamLink.pageCount; for (var i = 0; i < totalPages; i++) { if (cancelToken?.isCancelled == true) { throw DioException( requestOptions: RequestOptions(path: ''), type: DioExceptionType.cancel, ); } final pageUrl = streamLink.getPageUrl(i); final filePath = path.join(chapterDir.path, '$i$ext'); // Skip if already downloaded if (await File(filePath).exists()) { onProgress?.call(i + 1, totalPages); continue; } await dio.download( pageUrl, filePath, cancelToken: cancelToken, ); onProgress?.call(i + 1, totalPages); } return chapterDir.path; } }