diff --git a/lib/modules/anime/anime_player_view.dart b/lib/modules/anime/anime_player_view.dart index 6e6afb7e..b7f84b6f 100644 --- a/lib/modules/anime/anime_player_view.dart +++ b/lib/modules/anime/anime_player_view.dart @@ -23,6 +23,7 @@ import 'package:mangayomi/modules/anime/providers/anime_player_controller_provid import 'package:mangayomi/modules/anime/widgets/aniskip_countdown_btn.dart'; import 'package:mangayomi/modules/anime/widgets/desktop.dart'; import 'package:mangayomi/modules/anime/widgets/play_or_pause_button.dart'; +import 'package:mangayomi/modules/library/providers/local_archive.dart'; import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart'; import 'package:mangayomi/modules/anime/widgets/mobile.dart'; import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart'; @@ -2223,7 +2224,8 @@ mp.register_script_message('call_button_${button.id}_long', button${button.id}lo ..updatedAt = DateTime.now() .millisecondsSinceEpoch ..customCoverImage = - imageBytes, + imageBytes + ?.getCoverImage, ); }); if (context.mounted) { diff --git a/lib/modules/library/library_screen.dart b/lib/modules/library/library_screen.dart index cace6626..3f3fbf70 100644 --- a/lib/modules/library/library_screen.dart +++ b/lib/modules/library/library_screen.dart @@ -1131,30 +1131,32 @@ class _LibraryScreenState extends ConsumerState // Downloaded Chapters if (downloadedChapsList.isNotEmpty) { for (var manga in mangasList) { - String mangaDirectory = ""; - if (manga.isLocalArchive ?? false) { - mangaDirectory = _deleteImport( - manga, - mangaDirectory, - ); - // Also remove item from library - // else it has 0 chapters/episodes - // and when opened, shows exception - // "Null check operator" - isar.writeTxnSync(() { - _removeImport(ref, manga); - }); - } else { - mangaDirectory = await _deleteDownload( - manga, - mangaDirectory, - ); - } - if (mangaDirectory.isNotEmpty) { - final path = Directory(mangaDirectory); - if (path.existsSync() && - path.listSync().isEmpty) { - path.deleteSync(recursive: true); + if (!(manga.isLocalArchive ?? false)) { + String mangaDirectory = ""; + if (manga.isLocalArchive ?? false) { + mangaDirectory = _deleteImport( + manga, + mangaDirectory, + ); + // Also remove item from library + // else it has 0 chapters/episodes + // and when opened, shows exception + // "Null check operator" + isar.writeTxnSync(() { + _removeImport(ref, manga); + }); + } else { + mangaDirectory = await _deleteDownload( + manga, + mangaDirectory, + ); + } + if (mangaDirectory.isNotEmpty) { + final path = Directory(mangaDirectory); + if (path.existsSync() && + path.listSync().isEmpty) { + path.deleteSync(recursive: true); + } } } } diff --git a/lib/modules/library/providers/file_scanner.dart b/lib/modules/library/providers/file_scanner.dart index 2b25ae38..c7db7b14 100644 --- a/lib/modules/library/providers/file_scanner.dart +++ b/lib/modules/library/providers/file_scanner.dart @@ -1,9 +1,11 @@ import 'dart:convert'; import 'dart:io'; // For I/O-operations +import 'dart:typed_data'; import 'package:epubx/epubx.dart'; import 'package:isar_community/isar.dart'; // Isar database package for local storage import 'package:mangayomi/main.dart'; // Exposes the global `isar` instance import 'package:mangayomi/models/settings.dart'; +import 'package:mangayomi/modules/library/providers/local_archive.dart'; import 'package:mangayomi/utils/extensions/others.dart'; import 'package:path/path.dart' as p; // For manipulating file system paths import 'package:bot_toast/bot_toast.dart'; // For Exceptions @@ -182,7 +184,7 @@ Future _scanDirectory(Ref ref, Directory? dir) async { final bytes = await File(imageFiles.first.path).readAsBytes(); final byteList = bytes.toList(); if (manga.customCoverImage != byteList) { - manga.customCoverImage = byteList; + manga.customCoverImage = Uint8List.fromList(byteList).getCoverImage; manga.lastUpdate = dateNow; } } catch (e) { @@ -299,7 +301,9 @@ Future _scanDirectory(Ref ref, Directory? dir) async { book.Content!.Images!.containsKey("media/file0.png") ? book.Content!.Images!["media/file0.png"]!.Content : book.Content!.Images!.values.first.Content; - manga.customCoverImage = coverImage; + manga.customCoverImage = coverImage == null + ? null + : Uint8List.fromList(coverImage).getCoverImage; saveManga++; } for (var chapter in book.Chapters ?? []) { diff --git a/lib/modules/library/providers/local_archive.dart b/lib/modules/library/providers/local_archive.dart index 94cffc6f..36b9aa4d 100644 --- a/lib/modules/library/providers/local_archive.dart +++ b/lib/modules/library/providers/local_archive.dart @@ -17,93 +17,108 @@ Future importArchivesFromFile( required ItemType itemType, required bool init, }) async { - FilePickerResult? result = await FilePicker.platform.pickFiles( - allowMultiple: true, - type: FileType.custom, - allowedExtensions: switch (itemType) { - ItemType.manga => ['cbz', 'zip'], - ItemType.anime => ['mp4', 'mov', 'avi', 'flv', 'wmv', 'mpeg', 'mkv'], - ItemType.novel => ['epub'], - }, - ); - if (result != null) { - final dateNow = DateTime.now().millisecondsSinceEpoch; - final manga = - mManga ?? - Manga( - favorite: true, - source: 'archive', - author: '', - itemType: itemType, - genre: [], - imageUrl: '', - lang: '', - link: '', - name: _getName(result.files.first.path!), - dateAdded: dateNow, - lastUpdate: dateNow, - status: Status.unknown, - description: '', - isLocalArchive: true, - artist: '', - updatedAt: dateNow, - sourceId: null, - ); + final keepAlile = ref.keepAlive(); + try { + FilePickerResult? result = await FilePicker.platform.pickFiles( + allowMultiple: true, + type: FileType.custom, + allowedExtensions: switch (itemType) { + ItemType.manga => ['cbz', 'zip'], + ItemType.anime => ['mp4', 'mov', 'avi', 'flv', 'wmv', 'mpeg', 'mkv'], + ItemType.novel => ['epub'], + }, + ); + if (result != null) { + final dateNow = DateTime.now().millisecondsSinceEpoch; + final manga = + mManga ?? + Manga( + favorite: true, + source: 'archive', + author: '', + itemType: itemType, + genre: [], + imageUrl: '', + lang: '', + link: '', + name: _getName(result.files.first.path!), + dateAdded: dateNow, + lastUpdate: dateNow, + status: Status.unknown, + description: '', + isLocalArchive: true, + artist: '', + updatedAt: dateNow, + sourceId: null, + ); - for (var file in result.files.reversed.toList()) { - (String, LocalExtensionType, Uint8List, String)? data = - itemType == ItemType.manga - ? await ref.watch(getArchivesDataFromFileProvider(file.path!).future) - : null; - String name = _getName(file.path!); + for (var file in result.files.reversed.toList()) { + (String, LocalExtensionType, Uint8List, String)? data = + itemType == ItemType.manga + ? await ref.watch( + getArchivesDataFromFileProvider(file.path!).future, + ) + : null; + String name = _getName(file.path!); - if (init) { - manga.customCoverImage = itemType == ItemType.manga ? data!.$3 : null; - } - - await isar.writeTxn(() async { - final mangaId = await isar.mangas.put(manga); - final List chapters = []; - if (itemType == ItemType.novel) { - final bytes = await File(file.path!).readAsBytes(); - final book = await EpubReader.readBook(bytes); - if (book.Content != null && book.Content!.Images != null) { - final coverImage = - book.Content!.Images!.containsKey("media/file0.png") - ? book.Content!.Images!["media/file0.png"]!.Content - : book.Content!.Images!.values.first.Content; - await isar.mangas.put(manga..customCoverImage = coverImage); + if (init) { + if (itemType == ItemType.manga) { + manga.customCoverImage = data!.$3.getCoverImage; } - for (var chapter in book.Chapters ?? []) { + } + await isar.writeTxn(() async { + final mangaId = await isar.mangas.put(manga); + final List chapters = []; + if (itemType == ItemType.novel) { + final bytes = await File(file.path!).readAsBytes(); + final book = await EpubReader.readBook(bytes); + if (book.Content != null && book.Content!.Images != null) { + final coverImage = + book.Content!.Images!.containsKey("media/file0.png") + ? book.Content!.Images!["media/file0.png"]!.Content + : book.Content!.Images!.values.first.Content; + await isar.mangas.put( + manga + ..customCoverImage = coverImage == null + ? null + : Uint8List.fromList(coverImage).getCoverImage, + ); + } + for (var chapter in book.Chapters ?? []) { + chapters.add( + Chapter( + mangaId: mangaId, + name: chapter.Title is String && chapter.Title.isEmpty + ? "Book" + : chapter.Title, + archivePath: file.path, + updatedAt: DateTime.now().millisecondsSinceEpoch, + )..manga.value = manga, + ); + } + } else { chapters.add( Chapter( - mangaId: mangaId, - name: chapter.Title is String && chapter.Title.isEmpty - ? "Book" - : chapter.Title, - archivePath: file.path, + name: itemType == ItemType.manga ? data!.$1 : name, + archivePath: itemType == ItemType.manga ? data!.$4 : file.path, + mangaId: manga.id, updatedAt: DateTime.now().millisecondsSinceEpoch, )..manga.value = manga, ); } - } else { - chapters.add( - Chapter( - name: itemType == ItemType.manga ? data!.$1 : name, - archivePath: itemType == ItemType.manga ? data!.$4 : file.path, - mangaId: manga.id, - updatedAt: DateTime.now().millisecondsSinceEpoch, - )..manga.value = manga, - ); - } - for (final chapter in chapters) { - await isar.chapters.put(chapter); - await chapter.manga.save(); - } - }); + for (final chapter in chapters) { + await isar.chapters.put(chapter); + await chapter.manga.save(); + } + }); + } } + keepAlile.close(); + return ""; + } catch (e) { + keepAlile.close(); + rethrow; } - return ""; } String _getName(String path) { @@ -117,3 +132,13 @@ String _getName(String path) { '', ); } + +extension Uint8ListExtensions on Uint8List { + Uint8List? get getCoverImage { + final length = lengthInBytes / (1024 * 1024); + if (length < 5) { + return this; + } + return null; + } +} diff --git a/lib/modules/library/providers/local_archive.g.dart b/lib/modules/library/providers/local_archive.g.dart index dff6e631..753d42d5 100644 --- a/lib/modules/library/providers/local_archive.g.dart +++ b/lib/modules/library/providers/local_archive.g.dart @@ -65,7 +65,7 @@ final class ImportArchivesFromFileProvider } String _$importArchivesFromFileHash() => - r'784b9d45958695faffdf04ee7c105c9b486122de'; + r'a3fbf9d9ba7eacfa52366bfb10ba9f5f9117585b'; final class ImportArchivesFromFileFamily extends $Family with diff --git a/lib/modules/manga/archive_reader/providers/archive_reader_providers.dart b/lib/modules/manga/archive_reader/providers/archive_reader_providers.dart index 2d7a4b6c..b50dc0a4 100644 --- a/lib/modules/manga/archive_reader/providers/archive_reader_providers.dart +++ b/lib/modules/manga/archive_reader/providers/archive_reader_providers.dart @@ -6,10 +6,20 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:path/path.dart' as p; part 'archive_reader_providers.g.dart'; +// Constants for supported file types +const List _kImageExtensions = [ + '.png', + '.jpg', + '.jpeg', + '.gif', + '.webp', +]; +const List _kArchiveExtensions = ['.cbz', '.zip', '.cbt', '.tar']; + @riverpod Future> getArchivesDataFromDirectory(Ref ref, String path) async { - return compute(_extractOnly, path); + return compute(_extractArchiveMetadataFromDirectory, path); } @riverpod @@ -17,7 +27,7 @@ Future> getArchiveDataFromDirectory( Ref ref, String path, ) async { - return compute(_extract, path); + return compute(_extractArchivesFromDirectory, path); } @riverpod @@ -25,7 +35,7 @@ Future<(String, LocalExtensionType, Uint8List, String)> getArchivesDataFromFile( Ref ref, String path, ) async { - return compute(_extractArchiveOnly, path); + return compute(_extractArchiveMetadata, path); } @riverpod @@ -33,200 +43,315 @@ Future getArchiveDataFromFile(Ref ref, String path) async { return compute(_extractArchive, path); } -Future> _extract(String data) async { - return await _searchForArchive(Directory(data)); -} - -Future> _extractOnly( - String data, +/// Extract full archive data from all archives in a directory (recursive) +Future> _extractArchivesFromDirectory( + String directoryPath, ) async { - return await _searchForArchiveOnly(Directory(data)); -} + final archives = []; -List _list = []; -List<(String, LocalExtensionType, Uint8List, String)> _listOnly = []; -Future> _searchForArchive(Directory dir) async { - List entities = dir.listSync(); - for (FileSystemEntity entity in entities) { - if (entity is Directory) { - _searchForArchive(entity); - } else if (entity is File) { - String path = entity.path; - if (_isArchiveFile(path)) { - final dd = await compute(_extractArchive, path); - _list.add(dd); - } + try { + final dir = Directory(directoryPath); + if (!dir.existsSync()) { + return archives; } + + await _scanDirectoryRecursive( + dir, + onArchiveFound: (path) async { + try { + final archive = await _extractArchive(path); + archives.add(archive); + } catch (e) { + debugPrint('Error extracting archive at $path: $e'); + } + }, + ); + } catch (e) { + debugPrint('Error scanning directory $directoryPath: $e'); } - return _list; + + return archives; } +/// Extract only metadata (cover) from all archives in a directory (recursive) Future> -_searchForArchiveOnly(Directory dir) async { - List entities = dir.listSync(); - for (FileSystemEntity entity in entities) { - if (entity is Directory) { - _searchForArchive(entity); - } else if (entity is File) { - String path = entity.path; - if (_isArchiveFile(path)) { - final dd = await compute(_extractArchiveOnly, path); - _listOnly.add(dd); - } +_extractArchiveMetadataFromDirectory(String directoryPath) async { + final metadata = <(String, LocalExtensionType, Uint8List, String)>[]; + + try { + final dir = Directory(directoryPath); + if (!dir.existsSync()) { + return metadata; } + + await _scanDirectoryRecursive( + dir, + onArchiveFound: (path) async { + try { + final data = await _extractArchiveMetadata(path); + metadata.add(data); + } catch (e) { + debugPrint('Error extracting metadata at $path: $e'); + } + }, + ); + } catch (e) { + debugPrint('Error scanning directory $directoryPath: $e'); } - return _listOnly; + + return metadata; } -bool _isImageFile(String path) { - List imageExtensions = ['.png', '.jpg', '.jpeg']; - String extension = path.toLowerCase(); - for (String imageExtension in imageExtensions) { - if (extension.endsWith(imageExtension)) { - return true; - } - } - return false; -} +/// Recursively scan directory for archive files +Future _scanDirectoryRecursive( + Directory dir, { + required Future Function(String path) onArchiveFound, +}) async { + try { + final entities = dir.listSync(); -bool _isArchiveFile(String path) { - List archiveExtensions = ['.cbz', '.zip', 'cbt', 'tar']; - String extension = path.toLowerCase(); - for (String archiveExtension in archiveExtensions) { - if (extension.endsWith(archiveExtension)) { - return true; - } - } - return false; -} - -LocalArchive _extractArchive(String path) { - // Folder of images? - if (Directory(path).existsSync()) { - final dir = Directory(path); - final pages = - dir.listSync().whereType().where((f) => _isImageFile(f.path)).map( - (f) { - return LocalImage() - ..image = f.readAsBytesSync() - ..name = p.basename(f.path); - }, - ).toList()..sort((a, b) => a.name!.compareTo(b.name!)); - - final localArchive = LocalArchive() - ..path = path - ..extensionType = LocalExtensionType.folder - ..name = p.basename(path) - ..images = pages - ..coverImage = pages.first.image; - - return localArchive; - } - final localArchive = LocalArchive() - ..path = path - ..extensionType = setTypeExtension(p.extension(path).replaceFirst(".", "")) - ..name = p.basenameWithoutExtension(path); - Archive? archive; - final inputStream = InputFileStream(path); - final extensionType = localArchive.extensionType; - if (extensionType == LocalExtensionType.cbt || - extensionType == LocalExtensionType.tar) { - archive = TarDecoder().decodeStream(inputStream); - } else { - archive = ZipDecoder().decodeStream(inputStream); - } - - for (final file in archive.files) { - final filename = file.name; - if (file.isFile) { - if (_isImageFile(filename) && !filename.startsWith('.')) { - final data = file.content; - if (filename.contains("cover")) { - localArchive.coverImage = data; - } else { - localArchive.images!.add( - LocalImage() - ..image = data - ..name = p.basename(filename), - ); + for (final entity in entities) { + if (entity is Directory) { + // Recursive scan + await _scanDirectoryRecursive(entity, onArchiveFound: onArchiveFound); + } else if (entity is File) { + if (_isArchiveFile(entity.path)) { + await onArchiveFound(entity.path); } } } + } catch (e) { + debugPrint('Error scanning directory ${dir.path}: $e'); } - localArchive.images!.sort((a, b) => a.name!.compareTo(b.name!)); - localArchive.coverImage ??= localArchive.images!.first.image; - return localArchive; } -(String, LocalExtensionType, Uint8List, String) _extractArchiveOnly( +/// Check if a file is an image based on extension +bool _isImageFile(String path) { + final extension = p.extension(path).toLowerCase(); + return _kImageExtensions.contains(extension); +} + +/// Check if a file is a supported archive based on extension +bool _isArchiveFile(String path) { + final extension = p.extension(path).toLowerCase(); + return _kArchiveExtensions.any((ext) => extension.endsWith(ext)); +} + +/// Extract full archive with all images +Future _extractArchive(String path) async { + try { + // Handle directory of images + if (Directory(path).existsSync()) { + return await _extractFromImageFolder(path); + } + + // Handle archive file + return _extractFromArchiveFile(path); + } catch (e) { + debugPrint('Error extracting archive from $path: $e'); + rethrow; + } +} + +/// Extract images from a folder +Future _extractFromImageFolder(String path) async { + final dir = Directory(path); + final imageFiles = + await dir + .list() + .where((entity) => entity is File && _isImageFile(entity.path)) + .cast() + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + if (imageFiles.isEmpty) { + throw Exception('No images found in folder: $path'); + } + + final images = imageFiles.map((file) { + return LocalImage() + ..image = file.readAsBytesSync() + ..name = p.basename(file.path); + }).toList(); + + return LocalArchive() + ..path = path + ..extensionType = LocalExtensionType.folder + ..name = p.basename(path) + ..images = images + ..coverImage = images.first.image; +} + +/// Extract images from an archive file +LocalArchive _extractFromArchiveFile(String path) { + final extensionType = _getArchiveType(path); + final localArchive = LocalArchive() + ..path = path + ..extensionType = extensionType + ..name = p.basenameWithoutExtension(path) + ..images = []; + + InputFileStream? inputStream; + + try { + inputStream = InputFileStream(path); + final archive = _decodeArchive(inputStream, extensionType); + + final imageFiles = + archive.files + .where( + (file) => + file.isFile && + _isImageFile(file.name) && + !file.name.startsWith('.'), + ) + .toList() + ..sort((a, b) => a.name.compareTo(b.name)); + + if (imageFiles.isEmpty) { + throw Exception('No images found in archive: $path'); + } + + // Extract images + for (final file in imageFiles) { + final filename = file.name; + final data = file.content; + + if (filename.toLowerCase().contains('cover')) { + localArchive.coverImage = data; + } + + localArchive.images!.add( + LocalImage() + ..image = data + ..name = p.basename(filename), + ); + } + + // Set cover image if not explicitly found + localArchive.coverImage ??= localArchive.images!.first.image; + + return localArchive; + } finally { + inputStream?.close(); + } +} + +/// Extract only metadata (name, type, cover) from archive +Future<(String, LocalExtensionType, Uint8List, String)> _extractArchiveMetadata( + String path, +) async { + try { + // Handle directory of images + if (await Directory(path).exists()) { + return await _extractMetadataFromImageFolder(path); + } + + // Handle archive file + return _extractMetadataFromArchiveFile(path); + } catch (e) { + debugPrint('Error extracting metadata from $path: $e'); + rethrow; + } +} + +/// Extract metadata from image folder +Future<(String, LocalExtensionType, Uint8List, String)> +_extractMetadataFromImageFolder(String path) async { + final dir = Directory(path); + final images = + await dir + .list() + .where((entity) => entity is File && _isImageFile(entity.path)) + .cast() + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + if (images.isEmpty) { + throw Exception('No images found in folder: $path'); + } + + final cover = images.first.readAsBytesSync(); + return (p.basename(path), LocalExtensionType.folder, cover, path); +} + +/// Extract metadata from archive file +(String, LocalExtensionType, Uint8List, String) _extractMetadataFromArchiveFile( String path, ) { - // If it's a directory, just read its images: - if (Directory(path).existsSync()) { - final dir = Directory(path); - final images = - dir - .listSync() - .whereType() - .where((f) => _isImageFile(f.path)) - .toList() - ..sort((a, b) => a.path.compareTo(b.path)); - final cover = images.first.readAsBytesSync(); - return (p.basename(path), LocalExtensionType.folder, cover, path); - } - final extensionType = setTypeExtension( - p.extension(path).replaceFirst('.', ''), - ); + final extensionType = _getArchiveType(path); final name = p.basenameWithoutExtension(path); - Uint8List? coverImage; - Archive? archive; - final inputStream = InputFileStream(path); + InputFileStream? inputStream; - if (extensionType == LocalExtensionType.cbt || - extensionType == LocalExtensionType.tar) { - archive = TarDecoder().decodeStream(inputStream); - } else { - archive = ZipDecoder().decodeStream(inputStream); + try { + inputStream = InputFileStream(path); + final archive = _decodeArchive(inputStream, extensionType); + + // Look for cover image first + final coverFile = archive.files.firstWhere( + (file) => + file.isFile && + _isImageFile(file.name) && + file.name.toLowerCase().contains('cover') && + !file.name.startsWith('.'), + orElse: () { + // If no cover, get first image alphabetically + final imageFiles = + archive.files + .where( + (file) => + file.isFile && + _isImageFile(file.name) && + !file.name.startsWith('.'), + ) + .toList() + ..sort((a, b) => a.name.compareTo(b.name)); + + if (imageFiles.isEmpty) { + throw Exception('No images found in archive: $path'); + } + + return imageFiles.first; + }, + ); + + final coverImage = coverFile.content; + return (name, extensionType, coverImage, path); + } finally { + inputStream?.close(); } +} - final cover = archive.files.where( - (file) => - file.isFile && _isImageFile(file.name) && file.name.contains("cover"), - ); - - if (cover.isNotEmpty) { - coverImage = cover.first.content; - } else { - List lArchive = archive.files - .where( - (file) => - file.isFile && - _isImageFile(file.name) && - !file.name.contains("cover"), - ) - .toList(); - lArchive.sort((a, b) => a.name.compareTo(b.name)); - coverImage = lArchive.first.content; +/// Decode archive based on type +Archive _decodeArchive(InputFileStream stream, LocalExtensionType type) { + switch (type) { + case LocalExtensionType.cbt: + case LocalExtensionType.tar: + return TarDecoder().decodeStream(stream); + case LocalExtensionType.zip: + case LocalExtensionType.cbz: + case LocalExtensionType.folder: + return ZipDecoder().decodeStream(stream); } +} - return (name, extensionType, coverImage, path); +/// Get archive type from file extension +LocalExtensionType _getArchiveType(String path) { + final extension = p.extension(path).toLowerCase().replaceFirst('.', ''); + return setTypeExtension(extension); } String getTypeExtension(LocalExtensionType type) { - return switch (type) { - LocalExtensionType.cbt => type.name, - LocalExtensionType.zip => type.name, - LocalExtensionType.tar => type.name, - _ => type.name, - }; + return type.name; } LocalExtensionType setTypeExtension(String extension) { - return switch (extension) { - "cbt" => LocalExtensionType.cbt, - "zip" => LocalExtensionType.zip, - "tar" => LocalExtensionType.tar, + return switch (extension.toLowerCase()) { + 'cbt' => LocalExtensionType.cbt, + 'zip' => LocalExtensionType.zip, + 'tar' => LocalExtensionType.tar, + 'cbz' => LocalExtensionType.cbz, _ => LocalExtensionType.cbz, }; } diff --git a/lib/modules/manga/archive_reader/providers/archive_reader_providers.g.dart b/lib/modules/manga/archive_reader/providers/archive_reader_providers.g.dart index 06c090d4..3429081b 100644 --- a/lib/modules/manga/archive_reader/providers/archive_reader_providers.g.dart +++ b/lib/modules/manga/archive_reader/providers/archive_reader_providers.g.dart @@ -70,7 +70,7 @@ final class GetArchivesDataFromDirectoryProvider } String _$getArchivesDataFromDirectoryHash() => - r'2a4d1a11e2b028e569ffd8a2700e4a1779bb9264'; + r'2f343dfe03bb479e80e6343f389fce8830998f0e'; final class GetArchivesDataFromDirectoryFamily extends $Family with @@ -154,7 +154,7 @@ final class GetArchiveDataFromDirectoryProvider } String _$getArchiveDataFromDirectoryHash() => - r'49aa47895feafd9fa0c4f20e25d7674a3d54b212'; + r'81705a8d04d4f4d1454a82b35e55eb2e0397ea6f'; final class GetArchiveDataFromDirectoryFamily extends $Family with $FunctionalFamilyOverride>, String> { @@ -232,7 +232,7 @@ final class GetArchivesDataFromFileProvider } String _$getArchivesDataFromFileHash() => - r'79874b548614b4410c19bca5f74978ec761742c5'; + r'04d8ce722c077a7def61dda20ff18b23090fb646'; final class GetArchivesDataFromFileFamily extends $Family with diff --git a/lib/modules/manga/reader/reader_view.dart b/lib/modules/manga/reader/reader_view.dart index 88837108..ea9b1a0b 100644 --- a/lib/modules/manga/reader/reader_view.dart +++ b/lib/modules/manga/reader/reader_view.dart @@ -14,6 +14,7 @@ import 'package:mangayomi/models/chapter.dart'; import 'package:mangayomi/models/manga.dart'; import 'package:mangayomi/models/settings.dart'; import 'package:mangayomi/modules/anime/widgets/desktop.dart'; +import 'package:mangayomi/modules/library/providers/local_archive.dart'; import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart'; import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart'; import 'package:mangayomi/modules/manga/reader/widgets/btn_chapter_list_dialog.dart'; @@ -389,7 +390,7 @@ class _MangaChapterPageGalleryState isar.mangas.putSync( manga ..customCoverImage = - imageBytes + imageBytes.getCoverImage ..updatedAt = DateTime.now() .millisecondsSinceEpoch, ); diff --git a/lib/modules/more/data_and_storage/create_backup.dart b/lib/modules/more/data_and_storage/create_backup.dart index 32582fb4..5075c1b0 100644 --- a/lib/modules/more/data_and_storage/create_backup.dart +++ b/lib/modules/more/data_and_storage/create_backup.dart @@ -176,9 +176,8 @@ class _CreateBackupState extends ConsumerState { result = await FilePicker.platform .getDirectoryPath(); } - if (result != null && context.mounted) { - ref.watch( + ref.read( doBackUpProvider( list: indexList, path: result,