diff --git a/assets/translation.json b/assets/translation.json index 721d07f..92816e6 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -201,7 +201,10 @@ "Select All": "全选", "Deselect": "取消选择", "Invert Selection": "反选", - "Select in range": "区间选择" + "Select in range": "区间选择", + "Finished": "已完成", + "Updating": "更新中", + "Update Comics Info": "更新漫画信息" }, "zh_TW": { "Home": "首頁", @@ -405,6 +408,9 @@ "Select All": "全選", "Deselect": "取消選擇", "Invert Selection": "反選", - "Select in range": "區間選擇" + "Select in range": "區間選擇", + "Finished": "已完成", + "Updating": "更新中", + "Update Comics Info": "更新漫畫信息" } } \ No newline at end of file diff --git a/lib/pages/favorites/favorite_actions.dart b/lib/pages/favorites/favorite_actions.dart index 2b625d6..21d1634 100644 --- a/lib/pages/favorites/favorite_actions.dart +++ b/lib/pages/favorites/favorite_actions.dart @@ -34,12 +34,11 @@ Future newFolder() async { child: Text("Import from file".tl), onPressed: () async { var file = await selectFile(ext: ['json']); - if(file == null) return; + if (file == null) return; var data = await file.readAsBytes(); try { LocalFavoritesManager().fromJson(utf8.decode(data)); - } - catch(e) { + } catch (e) { context.showMessage(message: "Failed to import".tl); return; } @@ -113,7 +112,9 @@ void addFavorite(Comic comic) { name: comic.title, coverPath: comic.cover, author: comic.subtitle ?? '', - type: ComicType((comic.sourceKey == 'local' ? 0 : comic.sourceKey.hashCode)), + type: ComicType((comic.sourceKey == 'local' + ? 0 + : comic.sourceKey.hashCode)), tags: comic.tags ?? [], ), ); @@ -128,3 +129,114 @@ void addFavorite(Comic comic) { }, ); } + +Future> updateComicsInfo(String folder) async { + var comics = LocalFavoritesManager().getAllComics(folder); + + Future updateSingleComic(int index) async { + int retry = 3; + + while (true) { + try { + var c = comics[index]; + var comicSource = c.type.comicSource; + if (comicSource == null) return; + + var newInfo = (await comicSource.loadComicInfo!(c.id)).data; + + comics[index] = FavoriteItem( + id: c.id, + name: newInfo.title, + coverPath: newInfo.cover, + author: newInfo.subTitle ?? + newInfo.tags['author']?.firstOrNull ?? + c.author, + type: c.type, + tags: c.tags, + ); + + LocalFavoritesManager().updateInfo(folder, comics[index]); + return; + } catch (e) { + retry--; + if(retry == 0) { + rethrow; + } + continue; + } + } + } + + var finished = ValueNotifier(0); + + var errors = 0; + + var index = 0; + + bool isCanceled = false; + + showDialog( + context: App.rootContext, + builder: (context) { + return ValueListenableBuilder( + valueListenable: finished, + builder: (context, value, child) { + var isFinished = value == comics.length; + return ContentDialog( + title: isFinished ? "Finished".tl : "Updating".tl, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + LinearProgressIndicator( + value: value / comics.length, + ), + const SizedBox(height: 4), + Text("$value/${comics.length}"), + const SizedBox(height: 4), + if (errors > 0) Text("Errors: $errors"), + ], + ).paddingHorizontal(16), + actions: [ + Button.filled( + color: isFinished ? null : context.colorScheme.error, + onPressed: () { + isCanceled = true; + context.pop(); + }, + child: isFinished ?Text("OK".tl) : Text("Cancel".tl), + ), + ], + ); + }, + ); + }, + ).then((_) { + isCanceled = true; + }); + + while(index < comics.length) { + var futures = []; + const maxConcurrency = 4; + + if(isCanceled) { + return comics; + } + + for (var i = 0; i < maxConcurrency; i++) { + if (index+i >= comics.length) break; + futures.add(updateSingleComic(index + i).then((v) { + finished.value++; + }, onError: (_) { + errors++; + finished.value++; + })); + } + + await Future.wait(futures); + index += maxConcurrency; + } + + return comics; +} diff --git a/lib/pages/favorites/local_favorites_page.dart b/lib/pages/favorites/local_favorites_page.dart index 7d87726..d3bb263 100644 --- a/lib/pages/favorites/local_favorites_page.dart +++ b/lib/pages/favorites/local_favorites_page.dart @@ -123,6 +123,18 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> { filename: "${widget.folder}.json", ); }), + MenuEntry( + icon: Icons.update, + text: "Update Comics Info".tl, + onClick: () { + updateComicsInfo(widget.folder).then((newComics) { + if(mounted) { + setState(() { + comics = newComics; + }); + } + }); + }), ], ), ],