mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
@@ -154,8 +154,8 @@
|
|||||||
"If the directory contains a file named 'cover.*', it will be used as the cover image. Otherwise the first image will be used." : "如果目录包含一个名为'cover.*'的文件,它将被用作封面图片。否则将使用第一张图片。",
|
"If the directory contains a file named 'cover.*', it will be used as the cover image. Otherwise the first image will be used." : "如果目录包含一个名为'cover.*'的文件,它将被用作封面图片。否则将使用第一张图片。",
|
||||||
"The directory name will be used as the comic title. And the name of chapter directories will be used as the chapter titles.\n" : "目录名称将被用作漫画标题。章节目录的名称将被用作章节标题。\n",
|
"The directory name will be used as the comic title. And the name of chapter directories will be used as the chapter titles.\n" : "目录名称将被用作漫画标题。章节目录的名称将被用作章节标题。\n",
|
||||||
"Export as cbz": "导出为cbz",
|
"Export as cbz": "导出为cbz",
|
||||||
"Select a cbz/zip file." : "选择一个cbz/zip文件",
|
"Select an archive file (cbz, zip, 7z, cb7)" : "选择一个归档文件 (cbz, zip, 7z, cb7)",
|
||||||
"A cbz file" : "一个cbz文件",
|
"An archive file" : "一个归档文件",
|
||||||
"Fullscreen": "全屏",
|
"Fullscreen": "全屏",
|
||||||
"Exit": "退出",
|
"Exit": "退出",
|
||||||
"View more": "查看更多",
|
"View more": "查看更多",
|
||||||
@@ -297,8 +297,8 @@
|
|||||||
"End": "末尾",
|
"End": "末尾",
|
||||||
"None": "无",
|
"None": "无",
|
||||||
"View Detail": "查看详情",
|
"View Detail": "查看详情",
|
||||||
"Select a directory which contains multiple cbz/zip files." : "选择一个包含多个cbz/zip文件的目录",
|
"Select a directory which contains multiple archive files." : "选择一个包含多个归档文件的目录",
|
||||||
"Multiple cbz files" : "多个cbz文件",
|
"Multiple archive files" : "多个归档文件",
|
||||||
"No valid comics found" : "未找到有效的漫画",
|
"No valid comics found" : "未找到有效的漫画",
|
||||||
"Enable DNS Overrides": "启用DNS覆写",
|
"Enable DNS Overrides": "启用DNS覆写",
|
||||||
"DNS Overrides": "DNS覆写",
|
"DNS Overrides": "DNS覆写",
|
||||||
@@ -314,7 +314,8 @@
|
|||||||
"Reset": "重置",
|
"Reset": "重置",
|
||||||
"Tags": "标签",
|
"Tags": "标签",
|
||||||
"Authors": "作者",
|
"Authors": "作者",
|
||||||
"Comics": "漫画"
|
"Comics": "漫画",
|
||||||
|
"Imported @a comics": "已导入 @a 本漫画"
|
||||||
},
|
},
|
||||||
"zh_TW": {
|
"zh_TW": {
|
||||||
"Home": "首頁",
|
"Home": "首頁",
|
||||||
@@ -470,8 +471,8 @@
|
|||||||
"If the directory contains a file named 'cover.*', it will be used as the cover image. Otherwise the first image will be used." : "如果目錄包含一個名為'cover.*'的文件,它將被用作封面圖片。否則將使用第一張圖片。",
|
"If the directory contains a file named 'cover.*', it will be used as the cover image. Otherwise the first image will be used." : "如果目錄包含一個名為'cover.*'的文件,它將被用作封面圖片。否則將使用第一張圖片。",
|
||||||
"The directory name will be used as the comic title. And the name of chapter directories will be used as the chapter titles.\n" : "目錄名稱將被用作漫畫標題。章節目錄的名稱將被用作章節標題。\n",
|
"The directory name will be used as the comic title. And the name of chapter directories will be used as the chapter titles.\n" : "目錄名稱將被用作漫畫標題。章節目錄的名稱將被用作章節標題。\n",
|
||||||
"Export as cbz": "匯出為cbz",
|
"Export as cbz": "匯出為cbz",
|
||||||
"Select a cbz/zip file." : "選擇一個cbz/zip文件",
|
"Select an archive file (cbz, zip, 7z, cb7)" : "選擇一個歸檔文件 (cbz, zip, 7z, cb7)",
|
||||||
"A cbz file" : "一個cbz文件",
|
"An archive file" : "一個歸檔文件",
|
||||||
"Fullscreen": "全螢幕",
|
"Fullscreen": "全螢幕",
|
||||||
"Exit": "退出",
|
"Exit": "退出",
|
||||||
"View more": "查看更多",
|
"View more": "查看更多",
|
||||||
@@ -614,8 +615,8 @@
|
|||||||
"End": "末尾",
|
"End": "末尾",
|
||||||
"None": "無",
|
"None": "無",
|
||||||
"View Detail": "查看詳情",
|
"View Detail": "查看詳情",
|
||||||
"Select a directory which contains multiple cbz/zip files." : "選擇一個包含多個cbz/zip文件的目錄",
|
"Select a directory which contains multiple archive files." : "選擇一個包含多個歸檔文件的目錄",
|
||||||
"Multiple cbz files" : "多個cbz文件",
|
"Multiple archive files" : "多個歸檔文件",
|
||||||
"No valid comics found" : "未找到有效的漫畫",
|
"No valid comics found" : "未找到有效的漫畫",
|
||||||
"Enable DNS Overrides": "啟用DNS覆寫",
|
"Enable DNS Overrides": "啟用DNS覆寫",
|
||||||
"DNS Overrides": "DNS覆寫",
|
"DNS Overrides": "DNS覆寫",
|
||||||
@@ -631,6 +632,7 @@
|
|||||||
"Reset": "重置",
|
"Reset": "重置",
|
||||||
"Tags": "標籤",
|
"Tags": "標籤",
|
||||||
"Authors": "作者",
|
"Authors": "作者",
|
||||||
"Comics": "漫畫"
|
"Comics": "漫畫",
|
||||||
|
"Imported @a comics": "已匯入 @a 部漫畫"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -453,15 +453,15 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
|
|||||||
String info = [
|
String info = [
|
||||||
"Select a directory which contains the comic files.".tl,
|
"Select a directory which contains the comic files.".tl,
|
||||||
"Select a directory which contains the comic directories.".tl,
|
"Select a directory which contains the comic directories.".tl,
|
||||||
"Select a cbz/zip file.".tl,
|
"Select an archive file (cbz, zip, 7z, cb7)".tl,
|
||||||
"Select a directory which contains multiple cbz/zip files.".tl,
|
"Select a directory which contains multiple archive files.".tl,
|
||||||
"Select an EhViewer database and a download folder.".tl
|
"Select an EhViewer database and a download folder.".tl
|
||||||
][type];
|
][type];
|
||||||
List<String> importMethods = [
|
List<String> importMethods = [
|
||||||
"Single Comic".tl,
|
"Single Comic".tl,
|
||||||
"Multiple Comics".tl,
|
"Multiple Comics".tl,
|
||||||
"A cbz file".tl,
|
"An archive file".tl,
|
||||||
"Multiple cbz files".tl,
|
"Multiple archive files".tl,
|
||||||
"EhViewer downloads".tl
|
"EhViewer downloads".tl
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -493,7 +493,7 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
if (type != 3)
|
if (type != 4)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("Add to favorites".tl),
|
title: Text("Add to favorites".tl),
|
||||||
trailing: Select(
|
trailing: Select(
|
||||||
@@ -507,7 +507,7 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
).paddingHorizontal(8),
|
).paddingHorizontal(8),
|
||||||
if (!App.isIOS && !App.isMacOS)
|
if (!App.isIOS && !App.isMacOS && type != 2 && type != 3)
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
enabled: true,
|
enabled: true,
|
||||||
title: Text("Copy to app local path".tl),
|
title: Text("Copy to app local path".tl),
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:flutter_7zip/flutter_7zip.dart';
|
||||||
import 'package:venera/foundation/app.dart';
|
import 'package:venera/foundation/app.dart';
|
||||||
import 'package:venera/foundation/comic_type.dart';
|
import 'package:venera/foundation/comic_type.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
import 'package:venera/utils/ext.dart';
|
import 'package:venera/utils/ext.dart';
|
||||||
|
import 'package:venera/utils/file_type.dart';
|
||||||
import 'package:venera/utils/io.dart';
|
import 'package:venera/utils/io.dart';
|
||||||
import 'package:zip_flutter/zip_flutter.dart';
|
import 'package:zip_flutter/zip_flutter.dart';
|
||||||
|
|
||||||
@@ -57,12 +58,33 @@ class ComicChapter {
|
|||||||
ComicChapter({required this.title, required this.start, required this.end});
|
ComicChapter({required this.title, required this.start, required this.end});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Comic Book Archive. Currently supports CBZ, ZIP and 7Z formats.
|
||||||
abstract class CBZ {
|
abstract class CBZ {
|
||||||
|
static Future<FileType> checkType(File file) async {
|
||||||
|
var header = <int>[];
|
||||||
|
await for (var bytes in file.openRead()) {
|
||||||
|
header.addAll(bytes);
|
||||||
|
if (header.length >= 32) break;
|
||||||
|
}
|
||||||
|
return detectFileType(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> extractArchive(File file, Directory out) async {
|
||||||
|
var fileType = await checkType(file);
|
||||||
|
if (fileType.mime == 'application/zip') {
|
||||||
|
await ZipFile.openAndExtractAsync(file.path, out.path, 4);
|
||||||
|
} else if (fileType.mime == "application/x-7z-compressed") {
|
||||||
|
await SZArchive.extractIsolates(file.path, out.path, 4);
|
||||||
|
} else {
|
||||||
|
throw Exception('Unsupported archive type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<LocalComic> import(File file) async {
|
static Future<LocalComic> import(File file) async {
|
||||||
var cache = Directory(FilePath.join(App.cachePath, 'cbz_import'));
|
var cache = Directory(FilePath.join(App.cachePath, 'cbz_import'));
|
||||||
if (cache.existsSync()) cache.deleteSync(recursive: true);
|
if (cache.existsSync()) cache.deleteSync(recursive: true);
|
||||||
cache.createSync();
|
cache.createSync();
|
||||||
await ZipFile.openAndExtractAsync(file.path, cache.path, 4);
|
await extractArchive(file, cache);
|
||||||
var metaDataFile = File(FilePath.join(cache.path, 'metadata.json'));
|
var metaDataFile = File(FilePath.join(cache.path, 'metadata.json'));
|
||||||
ComicMetaData? metaData;
|
ComicMetaData? metaData;
|
||||||
if (metaDataFile.existsSync()) {
|
if (metaDataFile.existsSync()) {
|
||||||
@@ -72,7 +94,7 @@ abstract class CBZ {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
metaData ??= ComicMetaData(
|
metaData ??= ComicMetaData(
|
||||||
title: file.name.replaceLast('.cbz', ''),
|
title: file.name.substring(0, file.name.lastIndexOf('.')),
|
||||||
author: "",
|
author: "",
|
||||||
tags: [],
|
tags: [],
|
||||||
);
|
);
|
||||||
@@ -86,6 +108,7 @@ abstract class CBZ {
|
|||||||
return !['jpg', 'jpeg', 'png', 'webp', 'gif', 'jpe'].contains(ext);
|
return !['jpg', 'jpeg', 'png', 'webp', 'gif', 'jpe'].contains(ext);
|
||||||
});
|
});
|
||||||
if(files.isEmpty) {
|
if(files.isEmpty) {
|
||||||
|
cache.deleteSync(recursive: true);
|
||||||
throw Exception('No images found in the archive');
|
throw Exception('No images found in the archive');
|
||||||
}
|
}
|
||||||
files.sort((a, b) => a.path.compareTo(b.path));
|
files.sort((a, b) => a.path.compareTo(b.path));
|
||||||
|
@@ -21,8 +21,17 @@ class FileType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _resolver = MimeTypeResolver()
|
||||||
|
// zip
|
||||||
|
..addMagicNumber([0x50, 0x4B], 'application/zip')
|
||||||
|
// 7z
|
||||||
|
..addMagicNumber([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C], 'application/x-7z-compressed')
|
||||||
|
// rar
|
||||||
|
..addMagicNumber([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07], 'application/vnd.rar')
|
||||||
|
;
|
||||||
|
|
||||||
FileType detectFileType(List<int> data) {
|
FileType detectFileType(List<int> data) {
|
||||||
var mime = lookupMimeType('no-file', headerBytes: data);
|
var mime = _resolver.lookup('no-file', headerBytes: data);
|
||||||
var ext = mime == null ? '' : extensionFromMime(mime);
|
var ext = mime == null ? '' : extensionFromMime(mime);
|
||||||
if(ext == 'jpe') {
|
if(ext == 'jpe') {
|
||||||
ext = 'jpg';
|
ext = 'jpg';
|
||||||
|
@@ -20,7 +20,7 @@ class ImportComic {
|
|||||||
const ImportComic({this.selectedFolder, this.copyToLocal = true});
|
const ImportComic({this.selectedFolder, this.copyToLocal = true});
|
||||||
|
|
||||||
Future<bool> cbz() async {
|
Future<bool> cbz() async {
|
||||||
var file = await selectFile(ext: ['cbz', 'zip']);
|
var file = await selectFile(ext: ['cbz', 'zip', '7z', 'cb7']);
|
||||||
Map<String?, List<LocalComic>> imported = {};
|
Map<String?, List<LocalComic>> imported = {};
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -42,7 +42,8 @@ class ImportComic {
|
|||||||
var dir = await picker.pickDirectory(directAccess: true);
|
var dir = await picker.pickDirectory(directAccess: true);
|
||||||
if (dir != null) {
|
if (dir != null) {
|
||||||
var files = (await dir.list().toList()).whereType<File>().toList();
|
var files = (await dir.list().toList()).whereType<File>().toList();
|
||||||
files.removeWhere((e) => e.extension != 'cbz' && e.extension != 'zip');
|
const supportedExtensions = ['cbz', 'zip', '7z', 'cb7'];
|
||||||
|
files.removeWhere((e) => !supportedExtensions.contains(e.extension));
|
||||||
Map<String?, List<LocalComic>> imported = {};
|
Map<String?, List<LocalComic>> imported = {};
|
||||||
var controller = showLoadingDialog(App.rootContext, allowCancel: false);
|
var controller = showLoadingDialog(App.rootContext, allowCancel: false);
|
||||||
var comics = <LocalComic>[];
|
var comics = <LocalComic>[];
|
||||||
|
@@ -303,6 +303,15 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_7zip:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: "841ef5f77e5fdfd79e3eb2fa07ece4d46787285b"
|
||||||
|
resolved-ref: "841ef5f77e5fdfd79e3eb2fa07ece4d46787285b"
|
||||||
|
url: "https://github.com/wgh136/flutter_7zip"
|
||||||
|
source: git
|
||||||
|
version: "0.0.1"
|
||||||
flutter_file_dialog:
|
flutter_file_dialog:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@@ -73,6 +73,10 @@ dependencies:
|
|||||||
flutter_memory_info: ^0.0.1
|
flutter_memory_info: ^0.0.1
|
||||||
syntax_highlight: ^0.4.0
|
syntax_highlight: ^0.4.0
|
||||||
text_scroll: ^0.2.0
|
text_scroll: ^0.2.0
|
||||||
|
flutter_7zip:
|
||||||
|
git:
|
||||||
|
url: https://github.com/wgh136/flutter_7zip
|
||||||
|
ref: 841ef5f77e5fdfd79e3eb2fa07ece4d46787285b
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user