local comic reading

This commit is contained in:
nyne
2024-10-08 08:30:08 +08:00
parent 601a7cd796
commit b44998663a
4 changed files with 101 additions and 35 deletions

View File

@@ -6,10 +6,11 @@ import 'package:path_provider/path_provider.dart';
import 'package:sqlite3/sqlite3.dart'; import 'package:sqlite3/sqlite3.dart';
import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/comic_type.dart'; import 'package:venera/foundation/comic_type.dart';
import 'package:venera/utils/io.dart';
import 'app.dart'; import 'app.dart';
class LocalComic implements Comic{ class LocalComic implements Comic {
@override @override
final String id; final String id;
@@ -117,12 +118,12 @@ class LocalManager with ChangeNotifier {
PRIMARY KEY (id, comic_type) PRIMARY KEY (id, comic_type)
); );
'''); ''');
if(File('${App.dataPath}/local_path').existsSync()){ if (File('${App.dataPath}/local_path').existsSync()) {
path = File('${App.dataPath}/local_path').readAsStringSync(); path = File('${App.dataPath}/local_path').readAsStringSync();
} else { } else {
if(App.isAndroid) { if (App.isAndroid) {
var external = await getExternalStorageDirectories(); var external = await getExternalStorageDirectories();
if(external != null && external.isNotEmpty){ if (external != null && external.isNotEmpty) {
path = '${external.first.path}/local'; path = '${external.first.path}/local';
} else { } else {
path = '${App.dataPath}/local'; path = '${App.dataPath}/local';
@@ -131,18 +132,18 @@ class LocalManager with ChangeNotifier {
path = '${App.dataPath}/local'; path = '${App.dataPath}/local';
} }
} }
if(!Directory(path).existsSync()) { if (!Directory(path).existsSync()) {
await Directory(path).create(); await Directory(path).create();
} }
} }
String findValidId(ComicType type) { String findValidId(ComicType type) {
final res = _db.select(''' final res = _db.select(
'''
SELECT id FROM comics WHERE comic_type = ? SELECT id FROM comics WHERE comic_type = ?
ORDER BY CAST(id AS INTEGER) DESC ORDER BY CAST(id AS INTEGER) DESC
LIMIT 1; LIMIT 1;
''' '''[type.value],
[type.value],
); );
if (res.isEmpty) { if (res.isEmpty) {
return '1'; return '1';
@@ -229,4 +230,40 @@ class LocalManager with ChangeNotifier {
} }
return LocalComic.fromRow(res.first); return LocalComic.fromRow(res.first);
} }
}
Future<List<String>> getImages(String id, ComicType type, int ep) async {
var comic = find(id, type) ?? (throw "Comic Not Found");
var directory = Directory(FilePath.join(path, comic.directory));
if (comic.chapters != null) {
var cid = comic.chapters!.keys.elementAt(ep - 1);
directory = Directory(FilePath.join(directory.path, cid));
}
var files = <File>[];
await for (var entity in directory.list()) {
if (entity is File) {
if (entity.absolute.path.replaceFirst(path, '').substring(1) ==
comic.cover) {
continue;
}
files.add(entity);
}
}
files.sort((a, b) {
var ai = int.tryParse(a.name.split('.').first);
var bi = int.tryParse(b.name.split('.').first);
if(ai != null && bi != null) {
return ai.compareTo(bi);
}
return a.name.compareTo(b.name);
});
return files.map((e) => "file://${e.path}").toList();
}
Future<bool> isDownloaded(String id, ComicType type, int ep) async {
var comic = find(id, type);
if(comic == null) return false;
if(comic.chapters == null) return true;
var eid = comic.chapters!.keys.elementAt(ep);
return Directory(FilePath.join(path, comic.directory, eid)).exists();
}
}

View File

@@ -462,7 +462,7 @@ abstract mixin class _ComicPageActions {
void read([int? ep, int? page]) { void read([int? ep, int? page]) {
App.rootContext.to( App.rootContext.to(
() => Reader( () => Reader(
source: comicSource, type: comic.comicType,
cid: comic.id, cid: comic.id,
name: comic.title, name: comic.title,
chapters: comic.chapters, chapters: comic.chapters,

View File

@@ -21,23 +21,43 @@ class _ReaderImagesState extends State<_ReaderImages> {
void load() async { void load() async {
if (inProgress) return; if (inProgress) return;
inProgress = true; inProgress = true;
var res = await context.reader.widget.source.loadComicPages!( if (context.reader.type == ComicType.local ||
context.reader.widget.cid, (await LocalManager().isDownloaded(
context.reader.widget.chapters?.keys context.reader.cid, context.reader.type, context.reader.chapter))) {
.elementAt(context.reader.chapter - 1), try {
); var images = await LocalManager().getImages(
if (res.error) { context.reader.cid, context.reader.type, context.reader.chapter);
setState(() { setState(() {
error = res.errorMessage; context.reader.images = images;
context.reader.isLoading = false; context.reader.isLoading = false;
inProgress = false; inProgress = false;
}); });
} catch (e) {
setState(() {
error = e.toString();
context.reader.isLoading = false;
inProgress = false;
});
}
} else { } else {
setState(() { var res = await context.reader.type.comicSource!.loadComicPages!(
context.reader.images = res.data; context.reader.widget.cid,
context.reader.isLoading = false; context.reader.widget.chapters?.keys
inProgress = false; .elementAt(context.reader.chapter - 1),
}); );
if (res.error) {
setState(() {
error = res.errorMessage;
context.reader.isLoading = false;
inProgress = false;
});
} else {
setState(() {
context.reader.images = res.data;
context.reader.isLoading = false;
inProgress = false;
});
}
} }
context.readerScaffold.update(); context.readerScaffold.update();
} }
@@ -196,12 +216,17 @@ class _GalleryModeState extends State<_GalleryMode>
} }
ImageProvider _createImageProvider(int page, BuildContext context) { ImageProvider _createImageProvider(int page, BuildContext context) {
return ReaderImageProvider( var imageKey = context.reader.images![page-1];
context.reader.images![page - 1], if(imageKey.startsWith('file://')) {
context.reader.widget.source.key, return FileImage(File(imageKey.replaceFirst("file://", '')));
context.reader.cid, } else {
context.reader.eid, return ReaderImageProvider(
); imageKey,
context.reader.type.comicSource!.key,
context.reader.cid,
context.reader.eid,
);
}
} }
void _precacheImage(int page, BuildContext context) { void _precacheImage(int page, BuildContext context) {

View File

@@ -10,9 +10,11 @@ import 'package:photo_view/photo_view_gallery.dart';
import 'package:venera/components/components.dart'; import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart'; import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/comic_type.dart';
import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/image_provider/reader_image.dart'; import 'package:venera/foundation/image_provider/reader_image.dart';
import 'package:venera/foundation/local.dart';
import 'package:venera/utils/io.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@@ -32,7 +34,7 @@ extension _ReaderContext on BuildContext {
class Reader extends StatefulWidget { class Reader extends StatefulWidget {
const Reader({ const Reader({
super.key, super.key,
required this.source, required this.type,
required this.cid, required this.cid,
required this.name, required this.name,
required this.chapters, required this.chapters,
@@ -41,7 +43,7 @@ class Reader extends StatefulWidget {
this.initialChapter, this.initialChapter,
}); });
final ComicSource source; final ComicType type;
final String cid; final String cid;
@@ -72,6 +74,8 @@ class _ReaderState extends State<Reader> with _ReaderLocation, _ReaderWindow {
@override @override
int get maxPage => images?.length ?? 1; int get maxPage => images?.length ?? 1;
ComicType get type => widget.type;
String get cid => widget.cid; String get cid => widget.cid;
String get eid => widget.chapters?.keys.elementAt(chapter - 1) ?? '0'; String get eid => widget.chapters?.keys.elementAt(chapter - 1) ?? '0';