added support for Kotatsu backups

This commit is contained in:
Schnitzel5 2025-03-11 23:34:02 +01:00
parent f443065113
commit 2a08115067
2 changed files with 264 additions and 20 deletions

View file

@ -32,31 +32,66 @@ part 'restore.g.dart';
void doRestore(Ref ref, {required String path, required BuildContext context}) {
final inputStream = InputFileStream(path);
final archive = ZipDecoder().decodeStream(inputStream);
final backup =
jsonDecode(utf8.decode(archive.files.first.content))
as Map<String, dynamic>;
try {
ref.read(restoreBackupProvider(backup));
BotToast.showNotification(
animationDuration: const Duration(milliseconds: 200),
animationReverseDuration: const Duration(milliseconds: 200),
duration: const Duration(seconds: 5),
backButtonBehavior: BackButtonBehavior.none,
leading: (_) => Image.asset('assets/app_icons/icon-red.png', height: 40),
title:
(_) => const Text(
"Backup restored!",
style: TextStyle(fontWeight: FontWeight.bold),
),
enableSlideOff: true,
onlyOne: true,
crossPage: true,
);
final backupType = checkBackupType(path, archive);
switch (backupType) {
case BackupType.mangayomi:
final backup =
jsonDecode(utf8.decode(archive.files.first.content))
as Map<String, dynamic>;
ref.read(restoreBackupProvider(backup));
break;
case BackupType.kotatsu:
ref.read(restoreKotatsuBackupProvider(archive));
break;
default:
}
if (backupType != BackupType.unknown) {
showBotToast("Backup restored!");
} else {
showBotToast("Backup Type not supported!");
}
} catch (e) {
botToast(e.toString());
}
}
void showBotToast(String text) {
BotToast.showNotification(
animationDuration: const Duration(milliseconds: 200),
animationReverseDuration: const Duration(milliseconds: 200),
duration: const Duration(seconds: 5),
backButtonBehavior: BackButtonBehavior.none,
leading: (_) => Image.asset('assets/app_icons/icon-red.png', height: 40),
title: (_) => Text(text, style: TextStyle(fontWeight: FontWeight.bold)),
enableSlideOff: true,
onlyOne: true,
crossPage: true,
);
}
enum BackupType { unknown, mangayomi, tachibk, kotatsu }
BackupType checkBackupType(String path, Archive archive) {
if (path.toLowerCase().contains("mangayomi") &&
archive.files.first.name.endsWith(".backup.db")) {
return BackupType.mangayomi;
} else if (path.toLowerCase().contains("kotatsu") &&
archive.files.where((f) {
switch (f.name) {
case "categories":
case "favourites":
return true;
default:
return false;
}
}).length ==
2) {
return BackupType.kotatsu;
}
return BackupType.unknown;
}
@riverpod
void restoreBackup(Ref ref, Map<String, dynamic> backup, {bool full = true}) {
final version = backup['version'];
@ -246,3 +281,81 @@ ItemType _convertToItemTypeCategory(Map<String, dynamic> backup) {
? ItemType.manga
: ItemType.anime;
}
@riverpod
void restoreKotatsuBackup(Ref ref, Archive archive) {
for (var f in archive.files) {
List<Category> cats = [];
switch (f.name) {
case "categories":
final categories = jsonDecode(utf8.decode(f.content)) as List? ?? [];
isar.writeTxnSync(() {
isar.categorys.clearSync();
for (var category in categories) {
final cat = Category(
id: category["id"],
name: category["title"],
forItemType: ItemType.manga,
hide: !(category["show_in_lib"] ?? true)
);
isar.categorys.putSync(cat);
cats.add(cat);
}
});
case "favourites":
final favourites = jsonDecode(utf8.decode(f.content)) as List? ?? [];
isar.writeTxnSync(() {
isar.mangas.clearSync();
for (var favourite in favourites) {
final tempManga = favourite["manga"];
final manga = Manga(
source: tempManga["source"],
author: tempManga["author"],
artist: null,
genre:
(tempManga["tags"] as List?)
?.map((t) => t["title"] as String)
.toList() ??
[],
imageUrl: tempManga["large_cover_url"],
lang: 'en',
link: tempManga["url"],
name: tempManga["title"],
status: Status.values.firstWhere(
(s) =>
s.name.toLowerCase() ==
(tempManga["state"] as String?)?.toLowerCase(),
orElse: () => Status.unknown,
),
description: null,
categories: [favourite["category_id"]],
itemType: ItemType.manga,
favorite: true
);
isar.mangas.putSync(manga);
}
});
default:
continue;
}
}
isar.writeTxnSync(() {
isar.chapters.clearSync();
isar.downloads.clearSync();
isar.historys.clearSync();
isar.updates.clearSync();
isar.tracks.clearSync();
isar.trackPreferences.clearSync();
ref.read(synchingProvider(syncId: 1).notifier).clearAllChangedParts(false);
ref.invalidate(themeModeStateProvider);
ref.invalidate(blendLevelStateProvider);
ref.invalidate(flexSchemeColorStateProvider);
ref.invalidate(pureBlackDarkModeStateProvider);
ref.invalidate(l10nLocaleStateProvider);
ref.invalidate(navigationOrderStateProvider);
ref.invalidate(hideItemsStateProvider);
ref.invalidate(extensionsRepoStateProvider(ItemType.manga));
ref.invalidate(extensionsRepoStateProvider(ItemType.anime));
ref.invalidate(extensionsRepoStateProvider(ItemType.novel));
});
}

View file

@ -6,7 +6,7 @@ part of 'restore.dart';
// RiverpodGenerator
// **************************************************************************
String _$doRestoreHash() => r'ff5b1cbb192ec7f0da82d79c5ac90e15dd28c1de';
String _$doRestoreHash() => r'355f3ca8fb830aedc6fdc24cb91e2adc25c760f7';
/// Copied from Dart SDK
class _SystemHash {
@ -319,5 +319,136 @@ class _RestoreBackupProviderElement extends AutoDisposeProviderElement<void>
@override
bool get full => (origin as RestoreBackupProvider).full;
}
String _$restoreKotatsuBackupHash() =>
r'a27c30891ba09cc48d4df69270a93a783aeb919f';
/// See also [restoreKotatsuBackup].
@ProviderFor(restoreKotatsuBackup)
const restoreKotatsuBackupProvider = RestoreKotatsuBackupFamily();
/// See also [restoreKotatsuBackup].
class RestoreKotatsuBackupFamily extends Family<void> {
/// See also [restoreKotatsuBackup].
const RestoreKotatsuBackupFamily();
/// See also [restoreKotatsuBackup].
RestoreKotatsuBackupProvider call(
Archive archive,
) {
return RestoreKotatsuBackupProvider(
archive,
);
}
@override
RestoreKotatsuBackupProvider getProviderOverride(
covariant RestoreKotatsuBackupProvider provider,
) {
return call(
provider.archive,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'restoreKotatsuBackupProvider';
}
/// See also [restoreKotatsuBackup].
class RestoreKotatsuBackupProvider extends AutoDisposeProvider<void> {
/// See also [restoreKotatsuBackup].
RestoreKotatsuBackupProvider(
Archive archive,
) : this._internal(
(ref) => restoreKotatsuBackup(
ref as RestoreKotatsuBackupRef,
archive,
),
from: restoreKotatsuBackupProvider,
name: r'restoreKotatsuBackupProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$restoreKotatsuBackupHash,
dependencies: RestoreKotatsuBackupFamily._dependencies,
allTransitiveDependencies:
RestoreKotatsuBackupFamily._allTransitiveDependencies,
archive: archive,
);
RestoreKotatsuBackupProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.archive,
}) : super.internal();
final Archive archive;
@override
Override overrideWith(
void Function(RestoreKotatsuBackupRef provider) create,
) {
return ProviderOverride(
origin: this,
override: RestoreKotatsuBackupProvider._internal(
(ref) => create(ref as RestoreKotatsuBackupRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
archive: archive,
),
);
}
@override
AutoDisposeProviderElement<void> createElement() {
return _RestoreKotatsuBackupProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is RestoreKotatsuBackupProvider && other.archive == archive;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, archive.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin RestoreKotatsuBackupRef on AutoDisposeProviderRef<void> {
/// The parameter `archive` of this provider.
Archive get archive;
}
class _RestoreKotatsuBackupProviderElement
extends AutoDisposeProviderElement<void> with RestoreKotatsuBackupRef {
_RestoreKotatsuBackupProviderElement(super.provider);
@override
Archive get archive => (origin as RestoreKotatsuBackupProvider).archive;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package