mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
io utils; single favorite folder exporting and importing
This commit is contained in:
@@ -567,6 +567,19 @@ class SliverGridComics extends StatefulWidget {
|
||||
class _SliverGridComicsState extends State<SliverGridComics> {
|
||||
List<Comic> comics = [];
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant SliverGridComics oldWidget) {
|
||||
if (oldWidget.comics != widget.comics) {
|
||||
comics.clear();
|
||||
for (var comic in widget.comics) {
|
||||
if (isBlocked(comic) == null) {
|
||||
comics.add(comic);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
for (var comic in widget.comics) {
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
import 'package:venera/foundation/appdata.dart';
|
||||
import 'package:venera/foundation/log.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'app.dart';
|
||||
@@ -92,7 +95,39 @@ class FavoriteItem implements Comic {
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
throw UnimplementedError();
|
||||
return {
|
||||
"name": name,
|
||||
"author": author,
|
||||
"type": type.value,
|
||||
"tags": tags,
|
||||
"id": id,
|
||||
"coverPath": coverPath,
|
||||
};
|
||||
}
|
||||
|
||||
static FavoriteItem fromJson(Map<String, dynamic> json) {
|
||||
var type = json["type"] as int;
|
||||
if(type == 0 && json['coverPath'].toString().startsWith('http')) {
|
||||
type = 'picacg'.hashCode;
|
||||
} else if(type == 1) {
|
||||
type = 'ehentai'.hashCode;
|
||||
} else if(type == 2) {
|
||||
type = 'jm'.hashCode;
|
||||
} else if(type == 3) {
|
||||
type = 'hitomi'.hashCode;
|
||||
} else if(type == 4) {
|
||||
type = 'wnacg'.hashCode;
|
||||
} else if(type == 6) {
|
||||
type = 'nhentai'.hashCode;
|
||||
}
|
||||
return FavoriteItem(
|
||||
id: json["id"] ?? json['target'],
|
||||
name: json["name"],
|
||||
author: json["author"],
|
||||
coverPath: json["coverPath"],
|
||||
type: ComicType(type),
|
||||
tags: List<String>.from(json["tags"] ?? []),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,4 +560,39 @@ class LocalFavoritesManager {
|
||||
comic.type.value
|
||||
]);
|
||||
}
|
||||
|
||||
String folderToJson(String folder) {
|
||||
var res = _db.select("""
|
||||
select * from "$folder";
|
||||
""");
|
||||
return jsonEncode({
|
||||
"info": "Generated by Venera",
|
||||
"name": folder,
|
||||
"comics": res.map((e) => FavoriteItem.fromRow(e).toJson()).toList(),
|
||||
});
|
||||
}
|
||||
|
||||
void fromJson(String json) {
|
||||
var data = jsonDecode(json);
|
||||
var folder = data["name"];
|
||||
if(folder == null || folder is! String) {
|
||||
throw "Invalid data";
|
||||
}
|
||||
if (folderNames.contains(folder)) {
|
||||
int i = 0;
|
||||
while (folderNames.contains("$folder($i)")) {
|
||||
i++;
|
||||
}
|
||||
folder = "$folder($i)";
|
||||
}
|
||||
createFolder(folder);
|
||||
for (var comic in data["comics"]) {
|
||||
try {
|
||||
addComic(folder, FavoriteItem.fromJson(comic));
|
||||
}
|
||||
catch(e) {
|
||||
Log.error("Import Data", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ class ImageDownloader {
|
||||
var configs = <String, dynamic>{};
|
||||
if (sourceKey != null) {
|
||||
var comicSource = ComicSource.find(sourceKey);
|
||||
configs = comicSource!.getThumbnailLoadingConfig?.call(url) ?? {};
|
||||
configs = comicSource?.getThumbnailLoadingConfig?.call(url) ?? {};
|
||||
}
|
||||
configs['headers'] ??= {};
|
||||
if(configs['headers']['user-agent'] == null
|
||||
|
@@ -1145,7 +1145,7 @@ class _FavoritePanelState extends State<_FavoritePanel> {
|
||||
title: Text("Favorite".tl),
|
||||
),
|
||||
body: DefaultTabController(
|
||||
length: comicSource.favoriteData == null ? 1 : 2,
|
||||
length: hasNetwork ? 2 : 1,
|
||||
child: Column(
|
||||
children: [
|
||||
TabBar(tabs: [
|
||||
|
@@ -1,7 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:venera/components/components.dart';
|
||||
@@ -11,6 +8,7 @@ import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||
import 'package:venera/foundation/log.dart';
|
||||
import 'package:venera/network/app_dio.dart';
|
||||
import 'package:venera/utils/ext.dart';
|
||||
import 'package:venera/utils/io.dart';
|
||||
import 'package:venera/utils/translations.dart';
|
||||
|
||||
class ComicSourcePage extends StatefulWidget {
|
||||
@@ -328,7 +326,7 @@ class _BodyState extends State<_Body> {
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: selectFile, child: Text("Select file".tl))
|
||||
onPressed: _selectFile, child: Text("Select file".tl))
|
||||
.paddingLeft(8),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
@@ -350,16 +348,12 @@ class _BodyState extends State<_Body> {
|
||||
);
|
||||
}
|
||||
|
||||
void selectFile() async {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['js'],
|
||||
);
|
||||
final file = result?.files.first;
|
||||
void _selectFile() async {
|
||||
final file = await selectFile(ext: ["js"]);
|
||||
if (file == null) return;
|
||||
try {
|
||||
var fileName = file.name;
|
||||
var bytes = await File(file.path!).readAsBytes();
|
||||
var bytes = await file.readAsBytes();
|
||||
var content = utf8.decode(bytes);
|
||||
await addSource(content, fileName);
|
||||
} catch (e, s) {
|
||||
|
@@ -27,10 +27,26 @@ Future<void> newFolder() async {
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
).paddingHorizontal(16),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text("Import from file".tl),
|
||||
onPressed: () async {
|
||||
var file = await selectFile(ext: ['json']);
|
||||
if(file == null) return;
|
||||
var data = await file.readAsBytes();
|
||||
try {
|
||||
LocalFavoritesManager().fromJson(utf8.decode(data));
|
||||
}
|
||||
catch(e) {
|
||||
context.showMessage(message: "Failed to import".tl);
|
||||
return;
|
||||
}
|
||||
context.pop();
|
||||
},
|
||||
).paddingRight(4),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
var e = validateFolderName(controller.text);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -9,6 +10,7 @@ import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||
import 'package:venera/foundation/comic_type.dart';
|
||||
import 'package:venera/foundation/favorites.dart';
|
||||
import 'package:venera/foundation/res.dart';
|
||||
import 'package:venera/utils/io.dart';
|
||||
import 'package:venera/utils/translations.dart';
|
||||
|
||||
part 'favorite_actions.dart';
|
||||
@@ -134,7 +136,12 @@ class _FavoritesPageState extends State<FavoritesPage> {
|
||||
)
|
||||
: null,
|
||||
),
|
||||
title: Text("Unselected".tl),
|
||||
title: GestureDetector(
|
||||
onTap: context.width < _kTwoPanelChangeWidth
|
||||
? showFolderSelector
|
||||
: null,
|
||||
child: Text("Unselected".tl),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@@ -15,8 +15,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
||||
late List<FavoriteItem> comics;
|
||||
|
||||
void updateComics() {
|
||||
print(comics.length);
|
||||
setState(() {
|
||||
comics = LocalFavoritesManager().getAllComics(widget.folder);
|
||||
print(comics.length);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +66,6 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
||||
favPage.setFolder(false, null);
|
||||
LocalFavoritesManager().deleteFolder(widget.folder);
|
||||
favPage.folderList?.updateFolders();
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}),
|
||||
@@ -110,6 +111,18 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
||||
},
|
||||
);
|
||||
}),
|
||||
MenuEntry(
|
||||
icon: Icons.upload_file,
|
||||
text: "Export".tl,
|
||||
onClick: () {
|
||||
var json = LocalFavoritesManager().folderToJson(
|
||||
widget.folder,
|
||||
);
|
||||
saveFile(
|
||||
data: utf8.encode(json),
|
||||
filename: "${widget.folder}.json",
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@@ -211,11 +211,13 @@ class _LeftBarState extends State<_LeftBar> implements FolderList {
|
||||
|
||||
@override
|
||||
void update() {
|
||||
if(!mounted) return;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void updateFolders() {
|
||||
if(!mounted) return;
|
||||
setState(() {
|
||||
folders = LocalFavoritesManager().folderNames;
|
||||
networkFolders = ComicSource.all()
|
||||
|
@@ -25,8 +25,11 @@ class _AppSettingsState extends State<AppSettings> {
|
||||
title: "Set New Storage Path".tl,
|
||||
actionTitle: "Set".tl,
|
||||
callback: () async {
|
||||
var picker = FilePicker.platform;
|
||||
var result = await picker.getDirectoryPath();
|
||||
if(App.isIOS) {
|
||||
context.showMessage(message: "Not supported on iOS".tl);
|
||||
return;
|
||||
}
|
||||
var result = await selectDirectory();
|
||||
if (result == null) return;
|
||||
var loadingDialog = showLoadingDialog(
|
||||
App.rootContext,
|
||||
|
@@ -1,6 +1,4 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@@ -1,12 +1,13 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||
import 'package:venera/foundation/app.dart';
|
||||
import 'package:venera/utils/ext.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:share_plus/share_plus.dart' as s;
|
||||
import 'package:file_selector/file_selector.dart' as file_selector;
|
||||
|
||||
export 'dart:io';
|
||||
export 'dart:typed_data';
|
||||
@@ -128,7 +129,7 @@ class DirectoryPicker {
|
||||
|
||||
Future<Directory?> pickDirectory() async {
|
||||
if (App.isWindows || App.isLinux) {
|
||||
var d = await FilePicker.platform.getDirectoryPath();
|
||||
var d = await file_selector.getDirectoryPath();
|
||||
_directory = d;
|
||||
return d == null ? null : Directory(d);
|
||||
} else if (App.isAndroid) {
|
||||
@@ -156,15 +157,46 @@ class DirectoryPicker {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveFile(
|
||||
{required Uint8List data, required String filename}) async {
|
||||
var res = await FilePicker.platform.saveFile(
|
||||
bytes: data,
|
||||
fileName: filename,
|
||||
lockParentWindow: true,
|
||||
Future<file_selector.XFile?> selectFile({required List<String> ext}) async {
|
||||
file_selector.XTypeGroup typeGroup = file_selector.XTypeGroup(
|
||||
label: 'files',
|
||||
extensions: ext,
|
||||
);
|
||||
if (App.isDesktop && res != null) {
|
||||
await File(res).writeAsBytes(data);
|
||||
final file_selector.XFile? file = await file_selector.openFile(
|
||||
acceptedTypeGroups: <file_selector.XTypeGroup>[typeGroup],
|
||||
);
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<String?> selectDirectory() async {
|
||||
var path = await file_selector.getDirectoryPath();
|
||||
return path;
|
||||
}
|
||||
|
||||
Future<void> saveFile(
|
||||
{Uint8List? data, required String filename, File? file}) async {
|
||||
if(data == null && file == null) {
|
||||
throw Exception("data and file cannot be null at the same time");
|
||||
}
|
||||
if(data != null) {
|
||||
var cache = FilePath.join(App.cachePath, filename);
|
||||
if(File(cache).existsSync()) {
|
||||
File(cache).deleteSync();
|
||||
}
|
||||
await File(cache).writeAsBytes(data);
|
||||
file = File(cache);
|
||||
}
|
||||
if(App.isMobile) {
|
||||
final params = SaveFileDialogParams(sourceFilePath: file!.path);
|
||||
await FlutterFileDialog.saveFile(params: params);
|
||||
} else {
|
||||
final result = await file_selector.getSaveLocation(
|
||||
suggestedName: filename,
|
||||
);
|
||||
if (result != null) {
|
||||
var xFile = file_selector.XFile(file!.path);
|
||||
await xFile.saveTo(result.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user