mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-05-24 08:32:13 +00:00
Merge pull request #687 from Schnitzel5/feature/mass-migration
added mass migration
This commit is contained in:
commit
aff7f28476
29 changed files with 5881 additions and 115 deletions
|
|
@ -33,11 +33,12 @@ class JsExtensionService implements ExtensionService {
|
|||
JsUtils(runtime).init();
|
||||
JsVideosExtractors(runtime).init();
|
||||
JsPreferences(runtime, source).init();
|
||||
final sourceJson = jsonEncode(source.toMSource().toJson());
|
||||
|
||||
runtime.evaluate('''
|
||||
class MProvider {
|
||||
get source() {
|
||||
return JSON.parse('${jsonEncode(source.toMSource().toJson())}');
|
||||
return $sourceJson;
|
||||
}
|
||||
get supportsLatest() {
|
||||
throw new Error("supportsLatest not implemented");
|
||||
|
|
@ -96,7 +97,7 @@ var extention = new DefaultExtension();
|
|||
@override
|
||||
Map<String, String> getHeaders() {
|
||||
return _extensionCall<Map>(
|
||||
'getHeaders(`${source.baseUrl ?? ''}`)',
|
||||
'getHeaders(${jsonEncode(source.baseUrl ?? '')})',
|
||||
{},
|
||||
).toMapStringString!;
|
||||
}
|
||||
|
|
@ -127,14 +128,16 @@ var extention = new DefaultExtension();
|
|||
Future<MPages> search(String query, int page, List<dynamic> filters) async {
|
||||
return MPages.fromJson(
|
||||
await _extensionCallAsync(
|
||||
'search("$query",$page,${jsonEncode(filterValuesListToJson(filters))})',
|
||||
'search(${jsonEncode(query)},$page,${jsonEncode(filterValuesListToJson(filters))})',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
return MManga.fromJson(await _extensionCallAsync('getDetail(`$url`)'));
|
||||
return MManga.fromJson(
|
||||
await _extensionCallAsync('getDetail(${jsonEncode(url)})'),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -144,7 +147,9 @@ var extention = new DefaultExtension();
|
|||
hashCode: (p) => p.url.hashCode,
|
||||
);
|
||||
|
||||
for (final e in await _extensionCallAsync<List>('getPageList(`$url`)')) {
|
||||
for (final e in await _extensionCallAsync<List>(
|
||||
'getPageList(${jsonEncode(url)})',
|
||||
)) {
|
||||
if (e != null) {
|
||||
final page = e is String
|
||||
? PageUrl(e.trim())
|
||||
|
|
@ -164,7 +169,7 @@ var extention = new DefaultExtension();
|
|||
);
|
||||
|
||||
for (final element in await _extensionCallAsync<List>(
|
||||
'getVideoList(`$url`)',
|
||||
'getVideoList(${jsonEncode(url)})',
|
||||
)) {
|
||||
if (element['url'] != null && element['originalUrl'] != null) {
|
||||
videos.add(Video.fromJson(element));
|
||||
|
|
@ -178,7 +183,7 @@ var extention = new DefaultExtension();
|
|||
_init();
|
||||
final res = (await runtime.handlePromise(
|
||||
await runtime.evaluateAsync(
|
||||
'jsonStringify(() => extention.getHtmlContent(`$name`, `$url`))',
|
||||
'jsonStringify(() => extention.getHtmlContent(${jsonEncode(name)}, ${jsonEncode(url)}))',
|
||||
),
|
||||
)).stringResult;
|
||||
return res;
|
||||
|
|
@ -189,7 +194,7 @@ var extention = new DefaultExtension();
|
|||
_init();
|
||||
final res = (await runtime.handlePromise(
|
||||
await runtime.evaluateAsync(
|
||||
'jsonStringify(() => extention.cleanHtmlContent(`$html`))',
|
||||
'jsonStringify(() => extention.cleanHtmlContent(${jsonEncode(html)}))',
|
||||
),
|
||||
)).stringResult;
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -154,7 +154,10 @@ const extension = exports.default;
|
|||
@override
|
||||
Future<MPages> search(String query, int page, List<dynamic> filters) async {
|
||||
final items =
|
||||
((await _extensionCallAsync('searchNovels("$query",$page)', [])))
|
||||
((await _extensionCallAsync(
|
||||
'searchNovels(${jsonEncode(query)},$page)',
|
||||
[],
|
||||
)))
|
||||
.map((e) => NovelItem.fromJson(e))
|
||||
.map(
|
||||
(e) => MManga(
|
||||
|
|
@ -171,10 +174,13 @@ const extension = exports.default;
|
|||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final item = SourceNovel.fromJson(
|
||||
await _extensionCallAsync('parseNovel(`$url`)', {}),
|
||||
await _extensionCallAsync('parseNovel(${jsonEncode(url)})', {}),
|
||||
);
|
||||
final chapters = SourcePage.fromJson(
|
||||
await _extensionCallAsync('parsePage(`${item.path}`, `1`)', {}),
|
||||
await _extensionCallAsync(
|
||||
'parsePage(${jsonEncode(item.path)}, ${jsonEncode('1')})',
|
||||
{},
|
||||
),
|
||||
);
|
||||
final chaps =
|
||||
((chapters.chapters.isNotEmpty ? chapters.chapters : item.chapters)
|
||||
|
|
@ -225,7 +231,7 @@ const extension = exports.default;
|
|||
_init();
|
||||
final res = (await runtime.handlePromise(
|
||||
await runtime.evaluateAsync(
|
||||
'jsonStringify(() => extension.parseChapter(`$url`))',
|
||||
'jsonStringify(() => extension.parseChapter(${jsonEncode(url)}))',
|
||||
),
|
||||
)).stringResult;
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -71,6 +71,188 @@
|
|||
"latest": "Latest",
|
||||
"extensions": "Extensions",
|
||||
"migrate": "Migrate",
|
||||
"mass_migration_title": "Mass migration",
|
||||
"mass_migration_preview_items": "Preview items",
|
||||
"mass_migration_destination_source": "Destination source",
|
||||
"mass_migration_no_library_items": "No library items are available for mass migration.",
|
||||
"mass_migration_no_destination_sources": "No installed destination sources are available.",
|
||||
"mass_migration_installed": "Installed",
|
||||
"mass_migration_items_ready_for_review": "{count, plural, =1{1 item ready for review} other{{count} items ready for review}}",
|
||||
"@mass_migration_items_ready_for_review": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_item_count": "{count, plural, =1{1 item} other{{count} items}}",
|
||||
"@mass_migration_item_count": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_select_destination_source": "Select destination source",
|
||||
"mass_migration_finding_matches": "Finding matches in {source} • {language}",
|
||||
"@mass_migration_finding_matches": {
|
||||
"placeholders": {
|
||||
"source": {},
|
||||
"language": {}
|
||||
}
|
||||
},
|
||||
"mass_migration_processing_item": "Processing item {current} of {total}",
|
||||
"@mass_migration_processing_item": {
|
||||
"placeholders": {
|
||||
"current": {
|
||||
"type": "int"
|
||||
},
|
||||
"total": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_waiting_next_item": "Waiting 2 seconds before the next item...",
|
||||
"mass_migration_waiting_next_migration": "Waiting 2 seconds before the next migration...",
|
||||
"mass_migration_matched_so_far": "Matched so far: {count}",
|
||||
"@mass_migration_matched_so_far": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_no_match_count": "No match: {count}",
|
||||
"@mass_migration_no_match_count": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_review_matches": "Review matches for {source}",
|
||||
"@mass_migration_review_matches": {
|
||||
"placeholders": {
|
||||
"source": {}
|
||||
}
|
||||
},
|
||||
"mass_migration_found_matches": "Found matches: {count}",
|
||||
"@mass_migration_found_matches": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_no_matches": "No matches: {count}",
|
||||
"@mass_migration_no_matches": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_selected_to_migrate": "Selected to migrate: {count}",
|
||||
"@mass_migration_selected_to_migrate": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_finish_review": "Finish review",
|
||||
"mass_migration_migrate_selected": "Migrate selected items ({count})",
|
||||
"@mass_migration_migrate_selected": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_migrating_selected": "Migrating selected items to {source}",
|
||||
"@mass_migration_migrating_selected": {
|
||||
"placeholders": {
|
||||
"source": {}
|
||||
}
|
||||
},
|
||||
"mass_migration_no_items_selected": "No items selected for migration.",
|
||||
"mass_migration_migrating_item": "Migrating item {current} of {total}",
|
||||
"@mass_migration_migrating_item": {
|
||||
"placeholders": {
|
||||
"current": {
|
||||
"type": "int"
|
||||
},
|
||||
"total": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_complete": "Mass migration complete",
|
||||
"mass_migration_complete_success_message": "All selected items were processed successfully.",
|
||||
"mass_migration_complete_partial_message": "Migration finished with a few items that still need manual attention.",
|
||||
"mass_migration_route_summary": "{source} → {destination}",
|
||||
"@mass_migration_route_summary": {
|
||||
"placeholders": {
|
||||
"source": {},
|
||||
"destination": {}
|
||||
}
|
||||
},
|
||||
"mass_migration_processed": "Processed",
|
||||
"mass_migration_matched": "Matched",
|
||||
"mass_migration_migrated": "Migrated",
|
||||
"mass_migration_skipped": "Skipped",
|
||||
"mass_migration_failed": "Failed",
|
||||
"mass_migration_failed_items": "Failed Items",
|
||||
"mass_migration_exit": "Exit Mass Migration",
|
||||
"mass_migration_no_destination_match": "No destination match found",
|
||||
"mass_migration_query": "Query: {query}",
|
||||
"@mass_migration_query": {
|
||||
"placeholders": {
|
||||
"query": {}
|
||||
}
|
||||
},
|
||||
"mass_migration_skip": "Skip",
|
||||
"mass_migration_loading": "Loading...",
|
||||
"mass_migration_choose_another_result": "Choose another result",
|
||||
"mass_migration_source_chapters": "Source chapters",
|
||||
"mass_migration_destination_chapters": "Destination chapters",
|
||||
"mass_migration_chapter_count": "{count, plural, =1{1 chapter} other{{count} chapters}}",
|
||||
"@mass_migration_chapter_count": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_source_chapter_count": "{count, plural, =1{1 source chapter} other{{count} source chapters}}",
|
||||
"@mass_migration_source_chapter_count": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_destination_chapter_count": "{count, plural, =1{1 destination chapter} other{{count} destination chapters}}",
|
||||
"@mass_migration_destination_chapter_count": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_no_chapters_found": "No chapters found.",
|
||||
"mass_migration_and_more_chapters": "And {count} more...",
|
||||
"@mass_migration_and_more_chapters": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mass_migration_unknown_title": "Unknown title",
|
||||
"mass_migration_unknown_match": "Unknown match",
|
||||
"mass_migration_unknown_source": "Unknown source",
|
||||
"mass_migration_unknown_chapter": "Unknown chapter",
|
||||
"migrate_confirm": "Migrate to another source",
|
||||
"clean_database": "Clean database",
|
||||
"cleaned_database": "Database cleaned! {x} entries removed",
|
||||
|
|
|
|||
|
|
@ -503,6 +503,312 @@ abstract class AppLocalizations {
|
|||
/// **'Migrate'**
|
||||
String get migrate;
|
||||
|
||||
/// No description provided for @mass_migration_title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mass migration'**
|
||||
String get mass_migration_title;
|
||||
|
||||
/// No description provided for @mass_migration_preview_items.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Preview items'**
|
||||
String get mass_migration_preview_items;
|
||||
|
||||
/// No description provided for @mass_migration_destination_source.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Destination source'**
|
||||
String get mass_migration_destination_source;
|
||||
|
||||
/// No description provided for @mass_migration_no_library_items.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No library items are available for mass migration.'**
|
||||
String get mass_migration_no_library_items;
|
||||
|
||||
/// No description provided for @mass_migration_no_destination_sources.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No installed destination sources are available.'**
|
||||
String get mass_migration_no_destination_sources;
|
||||
|
||||
/// No description provided for @mass_migration_installed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Installed'**
|
||||
String get mass_migration_installed;
|
||||
|
||||
/// No description provided for @mass_migration_items_ready_for_review.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 item ready for review} other{{count} items ready for review}}'**
|
||||
String mass_migration_items_ready_for_review(int count);
|
||||
|
||||
/// No description provided for @mass_migration_item_count.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 item} other{{count} items}}'**
|
||||
String mass_migration_item_count(int count);
|
||||
|
||||
/// No description provided for @mass_migration_select_destination_source.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select destination source'**
|
||||
String get mass_migration_select_destination_source;
|
||||
|
||||
/// No description provided for @mass_migration_finding_matches.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Finding matches in {source} • {language}'**
|
||||
String mass_migration_finding_matches(Object source, Object language);
|
||||
|
||||
/// No description provided for @mass_migration_processing_item.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Processing item {current} of {total}'**
|
||||
String mass_migration_processing_item(int current, int total);
|
||||
|
||||
/// No description provided for @mass_migration_waiting_next_item.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Waiting 2 seconds before the next item...'**
|
||||
String get mass_migration_waiting_next_item;
|
||||
|
||||
/// No description provided for @mass_migration_waiting_next_migration.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Waiting 2 seconds before the next migration...'**
|
||||
String get mass_migration_waiting_next_migration;
|
||||
|
||||
/// No description provided for @mass_migration_matched_so_far.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Matched so far: {count}'**
|
||||
String mass_migration_matched_so_far(int count);
|
||||
|
||||
/// No description provided for @mass_migration_no_match_count.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No match: {count}'**
|
||||
String mass_migration_no_match_count(int count);
|
||||
|
||||
/// No description provided for @mass_migration_review_matches.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Review matches for {source}'**
|
||||
String mass_migration_review_matches(Object source);
|
||||
|
||||
/// No description provided for @mass_migration_found_matches.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Found matches: {count}'**
|
||||
String mass_migration_found_matches(int count);
|
||||
|
||||
/// No description provided for @mass_migration_no_matches.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No matches: {count}'**
|
||||
String mass_migration_no_matches(int count);
|
||||
|
||||
/// No description provided for @mass_migration_selected_to_migrate.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Selected to migrate: {count}'**
|
||||
String mass_migration_selected_to_migrate(int count);
|
||||
|
||||
/// No description provided for @mass_migration_finish_review.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Finish review'**
|
||||
String get mass_migration_finish_review;
|
||||
|
||||
/// No description provided for @mass_migration_migrate_selected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Migrate selected items ({count})'**
|
||||
String mass_migration_migrate_selected(int count);
|
||||
|
||||
/// No description provided for @mass_migration_migrating_selected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Migrating selected items to {source}'**
|
||||
String mass_migration_migrating_selected(Object source);
|
||||
|
||||
/// No description provided for @mass_migration_no_items_selected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No items selected for migration.'**
|
||||
String get mass_migration_no_items_selected;
|
||||
|
||||
/// No description provided for @mass_migration_migrating_item.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Migrating item {current} of {total}'**
|
||||
String mass_migration_migrating_item(int current, int total);
|
||||
|
||||
/// No description provided for @mass_migration_complete.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mass migration complete'**
|
||||
String get mass_migration_complete;
|
||||
|
||||
/// No description provided for @mass_migration_complete_success_message.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All selected items were processed successfully.'**
|
||||
String get mass_migration_complete_success_message;
|
||||
|
||||
/// No description provided for @mass_migration_complete_partial_message.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Migration finished with a few items that still need manual attention.'**
|
||||
String get mass_migration_complete_partial_message;
|
||||
|
||||
/// No description provided for @mass_migration_route_summary.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{source} → {destination}'**
|
||||
String mass_migration_route_summary(Object source, Object destination);
|
||||
|
||||
/// No description provided for @mass_migration_processed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Processed'**
|
||||
String get mass_migration_processed;
|
||||
|
||||
/// No description provided for @mass_migration_matched.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Matched'**
|
||||
String get mass_migration_matched;
|
||||
|
||||
/// No description provided for @mass_migration_migrated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Migrated'**
|
||||
String get mass_migration_migrated;
|
||||
|
||||
/// No description provided for @mass_migration_skipped.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Skipped'**
|
||||
String get mass_migration_skipped;
|
||||
|
||||
/// No description provided for @mass_migration_failed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed'**
|
||||
String get mass_migration_failed;
|
||||
|
||||
/// No description provided for @mass_migration_failed_items.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed Items'**
|
||||
String get mass_migration_failed_items;
|
||||
|
||||
/// No description provided for @mass_migration_exit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Exit Mass Migration'**
|
||||
String get mass_migration_exit;
|
||||
|
||||
/// No description provided for @mass_migration_no_destination_match.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No destination match found'**
|
||||
String get mass_migration_no_destination_match;
|
||||
|
||||
/// No description provided for @mass_migration_query.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Query: {query}'**
|
||||
String mass_migration_query(Object query);
|
||||
|
||||
/// No description provided for @mass_migration_skip.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Skip'**
|
||||
String get mass_migration_skip;
|
||||
|
||||
/// No description provided for @mass_migration_loading.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Loading...'**
|
||||
String get mass_migration_loading;
|
||||
|
||||
/// No description provided for @mass_migration_choose_another_result.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose another result'**
|
||||
String get mass_migration_choose_another_result;
|
||||
|
||||
/// No description provided for @mass_migration_source_chapters.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Source chapters'**
|
||||
String get mass_migration_source_chapters;
|
||||
|
||||
/// No description provided for @mass_migration_destination_chapters.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Destination chapters'**
|
||||
String get mass_migration_destination_chapters;
|
||||
|
||||
/// No description provided for @mass_migration_chapter_count.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 chapter} other{{count} chapters}}'**
|
||||
String mass_migration_chapter_count(int count);
|
||||
|
||||
/// No description provided for @mass_migration_source_chapter_count.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 source chapter} other{{count} source chapters}}'**
|
||||
String mass_migration_source_chapter_count(int count);
|
||||
|
||||
/// No description provided for @mass_migration_destination_chapter_count.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =1{1 destination chapter} other{{count} destination chapters}}'**
|
||||
String mass_migration_destination_chapter_count(int count);
|
||||
|
||||
/// No description provided for @mass_migration_no_chapters_found.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No chapters found.'**
|
||||
String get mass_migration_no_chapters_found;
|
||||
|
||||
/// No description provided for @mass_migration_and_more_chapters.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'And {count} more...'**
|
||||
String mass_migration_and_more_chapters(int count);
|
||||
|
||||
/// No description provided for @mass_migration_unknown_title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown title'**
|
||||
String get mass_migration_unknown_title;
|
||||
|
||||
/// No description provided for @mass_migration_unknown_match.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown match'**
|
||||
String get mass_migration_unknown_match;
|
||||
|
||||
/// No description provided for @mass_migration_unknown_source.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown source'**
|
||||
String get mass_migration_unknown_source;
|
||||
|
||||
/// No description provided for @mass_migration_unknown_chapter.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown chapter'**
|
||||
String get mass_migration_unknown_chapter;
|
||||
|
||||
/// No description provided for @migrate_confirm.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -210,6 +210,236 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'ترحيل';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'الانتقال إلى مصدر آخر';
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,236 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'স্থানান্তৰ';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'অন্য উৎসলৈ স্থানান্তৰ কৰক';
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,236 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrieren';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Zu einer anderen Erweiterung migrieren';
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,236 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrate';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrate to another source';
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,236 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrar';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrar a otra fuente';
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,236 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrer';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrer vers une autre source';
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,236 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'स्थानांतरण';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'दूसरे स्रोत में माइग्रेट करें';
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,236 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrasi';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrasi ke sumber lain';
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,236 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migra';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrare a un\'altra fonte';
|
||||
|
||||
|
|
|
|||
|
|
@ -207,6 +207,236 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => '移行';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => '別のソースに移行';
|
||||
|
||||
|
|
|
|||
|
|
@ -214,6 +214,236 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Migrar';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Migrar para outra fonte';
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,236 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Перенести';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Перенести на другой источник';
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,236 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'ผนวก';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'ย้ายไปยังแหล่งอื่น';
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,236 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => 'Taşı';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => 'Başka bir kaynağa geç';
|
||||
|
||||
|
|
|
|||
|
|
@ -207,6 +207,236 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get migrate => '迁移';
|
||||
|
||||
@override
|
||||
String get mass_migration_title => 'Mass migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_preview_items => 'Preview items';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_source => 'Destination source';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_library_items =>
|
||||
'No library items are available for mass migration.';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_sources =>
|
||||
'No installed destination sources are available.';
|
||||
|
||||
@override
|
||||
String get mass_migration_installed => 'Installed';
|
||||
|
||||
@override
|
||||
String mass_migration_items_ready_for_review(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items ready for review',
|
||||
one: '1 item ready for review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_item_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count items',
|
||||
one: '1 item',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_select_destination_source =>
|
||||
'Select destination source';
|
||||
|
||||
@override
|
||||
String mass_migration_finding_matches(Object source, Object language) {
|
||||
return 'Finding matches in $source • $language';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_processing_item(int current, int total) {
|
||||
return 'Processing item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_item =>
|
||||
'Waiting 2 seconds before the next item...';
|
||||
|
||||
@override
|
||||
String get mass_migration_waiting_next_migration =>
|
||||
'Waiting 2 seconds before the next migration...';
|
||||
|
||||
@override
|
||||
String mass_migration_matched_so_far(int count) {
|
||||
return 'Matched so far: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_match_count(int count) {
|
||||
return 'No match: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_review_matches(Object source) {
|
||||
return 'Review matches for $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_found_matches(int count) {
|
||||
return 'Found matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_no_matches(int count) {
|
||||
return 'No matches: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_selected_to_migrate(int count) {
|
||||
return 'Selected to migrate: $count';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_finish_review => 'Finish review';
|
||||
|
||||
@override
|
||||
String mass_migration_migrate_selected(int count) {
|
||||
return 'Migrate selected items ($count)';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_selected(Object source) {
|
||||
return 'Migrating selected items to $source';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_items_selected =>
|
||||
'No items selected for migration.';
|
||||
|
||||
@override
|
||||
String mass_migration_migrating_item(int current, int total) {
|
||||
return 'Migrating item $current of $total';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_complete => 'Mass migration complete';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_success_message =>
|
||||
'All selected items were processed successfully.';
|
||||
|
||||
@override
|
||||
String get mass_migration_complete_partial_message =>
|
||||
'Migration finished with a few items that still need manual attention.';
|
||||
|
||||
@override
|
||||
String mass_migration_route_summary(Object source, Object destination) {
|
||||
return '$source → $destination';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_processed => 'Processed';
|
||||
|
||||
@override
|
||||
String get mass_migration_matched => 'Matched';
|
||||
|
||||
@override
|
||||
String get mass_migration_migrated => 'Migrated';
|
||||
|
||||
@override
|
||||
String get mass_migration_skipped => 'Skipped';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed => 'Failed';
|
||||
|
||||
@override
|
||||
String get mass_migration_failed_items => 'Failed Items';
|
||||
|
||||
@override
|
||||
String get mass_migration_exit => 'Exit Mass Migration';
|
||||
|
||||
@override
|
||||
String get mass_migration_no_destination_match =>
|
||||
'No destination match found';
|
||||
|
||||
@override
|
||||
String mass_migration_query(Object query) {
|
||||
return 'Query: $query';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_skip => 'Skip';
|
||||
|
||||
@override
|
||||
String get mass_migration_loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get mass_migration_choose_another_result => 'Choose another result';
|
||||
|
||||
@override
|
||||
String get mass_migration_source_chapters => 'Source chapters';
|
||||
|
||||
@override
|
||||
String get mass_migration_destination_chapters => 'Destination chapters';
|
||||
|
||||
@override
|
||||
String mass_migration_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count chapters',
|
||||
one: '1 chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_source_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count source chapters',
|
||||
one: '1 source chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String mass_migration_destination_chapter_count(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count destination chapters',
|
||||
one: '1 destination chapter',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_no_chapters_found => 'No chapters found.';
|
||||
|
||||
@override
|
||||
String mass_migration_and_more_chapters(int count) {
|
||||
return 'And $count more...';
|
||||
}
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_title => 'Unknown title';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_match => 'Unknown match';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_source => 'Unknown source';
|
||||
|
||||
@override
|
||||
String get mass_migration_unknown_chapter => 'Unknown chapter';
|
||||
|
||||
@override
|
||||
String get migrate_confirm => '迁移到另一个来源';
|
||||
|
||||
|
|
|
|||
|
|
@ -631,6 +631,10 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
value: 3,
|
||||
child: Text(l10n.migrate),
|
||||
),
|
||||
PopupMenuItem<int>(
|
||||
value: 6,
|
||||
child: const Text('Mass migration'),
|
||||
),
|
||||
if (!isLocalArchive)
|
||||
PopupMenuItem<int>(
|
||||
value: 4,
|
||||
|
|
@ -742,6 +746,12 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
botToast("Failed to export metadata: $e");
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
context.push(
|
||||
"/massMigration",
|
||||
extra: widget.manga,
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,19 +7,13 @@ import 'package:mangayomi/eval/model/m_manga.dart';
|
|||
import 'package:mangayomi/eval/model/m_pages.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/category.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/track_search.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/modules/library/widgets/search_text_form_field.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/services/mass_migration_service.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/update_manga_detail_providers.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
|
||||
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/services/get_detail.dart';
|
||||
|
|
@ -629,103 +623,13 @@ class _MigrationMangaGlobalImageCardState
|
|||
}
|
||||
|
||||
Future<void> _migrateManga(MManga preview) async {
|
||||
String? historyChapter;
|
||||
String? historyDate;
|
||||
List<Chapter> chaptersProgress = [];
|
||||
isar.writeTxnSync(() {
|
||||
final histories = isar.historys
|
||||
.filter()
|
||||
.mangaIdEqualTo(widget.oldManga.id)
|
||||
.sortByDate()
|
||||
.findAllSync();
|
||||
historyChapter = _extractChapterNumber(
|
||||
histories.lastOrNull?.chapter.value?.name ?? "",
|
||||
);
|
||||
historyDate = histories.lastOrNull?.date;
|
||||
for (var history in histories) {
|
||||
isar.historys.deleteSync(history.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.removeHistory, history.id, "{}", false);
|
||||
}
|
||||
for (var chapter in widget.oldManga.chapters) {
|
||||
chaptersProgress.add(chapter);
|
||||
isar.updates
|
||||
.filter()
|
||||
.mangaIdEqualTo(chapter.mangaId)
|
||||
.chapterNameEqualTo(chapter.name)
|
||||
.deleteAllSync();
|
||||
isar.chapters.deleteSync(chapter.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.removeChapter, chapter.id, "{}", false);
|
||||
}
|
||||
widget.oldManga.name = widget.manga.name;
|
||||
widget.oldManga.link = widget.manga.link;
|
||||
widget.oldManga.imageUrl = widget.manga.imageUrl;
|
||||
widget.oldManga.lang = widget.source.lang;
|
||||
widget.oldManga.source = widget.source.name;
|
||||
widget.oldManga.sourceId = widget.source.id;
|
||||
widget.oldManga.artist = preview.artist;
|
||||
widget.oldManga.author = preview.author;
|
||||
widget.oldManga.status = preview.status ?? widget.oldManga.status;
|
||||
widget.oldManga.description = preview.description;
|
||||
widget.oldManga.genre = preview.genre;
|
||||
widget.oldManga.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(widget.oldManga);
|
||||
});
|
||||
await ref.read(
|
||||
updateMangaDetailProvider(
|
||||
mangaId: widget.oldManga.id,
|
||||
isInit: false,
|
||||
).future,
|
||||
await migrateLibraryItem(
|
||||
ref: ref,
|
||||
oldManga: widget.oldManga,
|
||||
selectedManga: widget.manga,
|
||||
preview: preview,
|
||||
destinationSource: widget.source,
|
||||
);
|
||||
isar.writeTxnSync(() {
|
||||
for (var oldChapter in chaptersProgress) {
|
||||
final chapter = isar.chapters
|
||||
.filter()
|
||||
.mangaIdEqualTo(widget.oldManga.id)
|
||||
.nameContains(
|
||||
_extractChapterNumber(oldChapter.name ?? "") ?? ".....",
|
||||
caseSensitive: false,
|
||||
)
|
||||
.findFirstSync();
|
||||
if (chapter != null) {
|
||||
chapter.isBookmarked = oldChapter.isBookmarked;
|
||||
chapter.lastPageRead = oldChapter.lastPageRead;
|
||||
chapter.isRead = oldChapter.isRead;
|
||||
isar.chapters.putSync(chapter);
|
||||
}
|
||||
}
|
||||
final chapter = isar.chapters
|
||||
.filter()
|
||||
.mangaIdEqualTo(widget.oldManga.id)
|
||||
.nameContains(historyChapter ?? ".....", caseSensitive: false)
|
||||
.findFirstSync();
|
||||
if (chapter != null) {
|
||||
isar.historys.putSync(
|
||||
History(
|
||||
mangaId: widget.oldManga.id,
|
||||
date:
|
||||
historyDate ?? DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
itemType: widget.oldManga.itemType,
|
||||
chapterId: chapter.id,
|
||||
)..chapter.value = chapter,
|
||||
);
|
||||
}
|
||||
});
|
||||
ref.invalidate(getMangaDetailStreamProvider(mangaId: widget.oldManga.id!));
|
||||
}
|
||||
|
||||
String? _extractChapterNumber(String chapterName) {
|
||||
return RegExp(
|
||||
r'\s*(\d+\.\d+)\s*',
|
||||
multiLine: true,
|
||||
).firstMatch(chapterName)?.group(0) ??
|
||||
RegExp(
|
||||
r'\s*(\d+)\s*',
|
||||
multiLine: true,
|
||||
).firstMatch(chapterName)?.group(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/mass_migration_runner_screen.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/models/mass_migration_models.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/widgets/mass_migration_widgets.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
class MassMigrationDestinationScreen extends StatelessWidget {
|
||||
const MassMigrationDestinationScreen({required this.sourceGroup, super.key});
|
||||
|
||||
final MassMigrationSourceGroup sourceGroup;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final sources = buildMassMigrationDestinationSources(
|
||||
sourceGroup: sourceGroup,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.mass_migration_destination_source)),
|
||||
body: sources.isEmpty
|
||||
? Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(l10n.mass_migration_no_destination_sources),
|
||||
),
|
||||
)
|
||||
: ListView.separated(
|
||||
itemCount: sources.length,
|
||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final source = sources[index];
|
||||
return _DestinationSourceTile(
|
||||
source: source,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
createRoute(
|
||||
page: MassMigrationRunnerScreen(
|
||||
sourceGroup: sourceGroup,
|
||||
destinationSource: source,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DestinationSourceTile extends StatelessWidget {
|
||||
const _DestinationSourceTile({required this.source, required this.onTap});
|
||||
|
||||
final Source source;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return ListTile(
|
||||
leading: MassMigrationSourceIcon(source: source),
|
||||
title: Text(source.name ?? l10n.mass_migration_unknown_source),
|
||||
subtitle: Text(
|
||||
[
|
||||
if ((source.lang ?? '').isNotEmpty)
|
||||
completeLanguageName(source.lang!),
|
||||
l10n.mass_migration_installed,
|
||||
].join(' • '),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
134
lib/modules/mass_migration/mass_migration_preview_screen.dart
Normal file
134
lib/modules/mass_migration/mass_migration_preview_screen.dart
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/mass_migration_destination_screen.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/models/mass_migration_models.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/widgets/mass_migration_widgets.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
class MassMigrationPreviewScreen extends StatelessWidget {
|
||||
const MassMigrationPreviewScreen({required this.sourceGroup, super.key});
|
||||
|
||||
final MassMigrationSourceGroup sourceGroup;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.mass_migration_preview_items)),
|
||||
body: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: MassMigrationSourceIcon(source: sourceGroup.source),
|
||||
title: Text(sourceGroup.sourceName),
|
||||
subtitle: Text(
|
||||
[
|
||||
if ((sourceGroup.lang ?? '').isNotEmpty)
|
||||
completeLanguageName(sourceGroup.lang!),
|
||||
l10n.mass_migration_items_ready_for_review(sourceGroup.count),
|
||||
].join(' • '),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: sourceGroup.items.length,
|
||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final manga = sourceGroup.items[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('${index + 1}'),
|
||||
const SizedBox(width: 12),
|
||||
MassMigrationCover(
|
||||
libraryItem: manga,
|
||||
source: sourceGroup.source,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manga.name ??
|
||||
l10n.mass_migration_unknown_title,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
[
|
||||
if ((manga.author ?? '')
|
||||
.trim()
|
||||
.isNotEmpty)
|
||||
manga.author!,
|
||||
if ((manga.artist ?? '')
|
||||
.trim()
|
||||
.isNotEmpty)
|
||||
manga.artist!,
|
||||
l10n.mass_migration_chapter_count(
|
||||
manga.chapters.length,
|
||||
),
|
||||
].join(' • '),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
MassMigrationChapterSection(
|
||||
title: l10n.mass_migration_source_chapters,
|
||||
chapters: manga.chapters
|
||||
.map(
|
||||
(chapter) =>
|
||||
chapter.name ??
|
||||
l10n.mass_migration_unknown_chapter,
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
createRoute(
|
||||
page: MassMigrationDestinationScreen(
|
||||
sourceGroup: sourceGroup,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.mass_migration_select_destination_source),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
783
lib/modules/mass_migration/mass_migration_runner_screen.dart
Normal file
783
lib/modules/mass_migration/mass_migration_runner_screen.dart
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/models/mass_migration_models.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/services/mass_migration_service.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/widgets/mass_migration_widgets.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/services/get_detail.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
enum _MassMigrationPhase { matching, review, applying, summary }
|
||||
|
||||
class MassMigrationRunnerScreen extends ConsumerStatefulWidget {
|
||||
const MassMigrationRunnerScreen({
|
||||
required this.sourceGroup,
|
||||
required this.destinationSource,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final MassMigrationSourceGroup sourceGroup;
|
||||
final Source destinationSource;
|
||||
|
||||
@override
|
||||
ConsumerState<MassMigrationRunnerScreen> createState() =>
|
||||
_MassMigrationRunnerScreenState();
|
||||
}
|
||||
|
||||
class _MassMigrationRunnerScreenState
|
||||
extends ConsumerState<MassMigrationRunnerScreen> {
|
||||
_MassMigrationPhase _phase = _MassMigrationPhase.matching;
|
||||
int _currentIndex = 0;
|
||||
int _migratedCount = 0;
|
||||
int _skippedCount = 0;
|
||||
final List<String> _failedItems = [];
|
||||
final Set<int> _loadingCandidateIndexes = {};
|
||||
final List<MassMigrationResolvedItem> _resolvedItems = [];
|
||||
bool _isWaitingForNextItem = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
unawaited(_processAllItems());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.mass_migration_title)),
|
||||
body: switch (_phase) {
|
||||
_MassMigrationPhase.matching => _buildMatchingPhase(context),
|
||||
_MassMigrationPhase.review => _buildReviewPhase(context),
|
||||
_MassMigrationPhase.applying => _buildApplyingPhase(context),
|
||||
_MassMigrationPhase.summary => _buildSummaryPhase(context),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMatchingPhase(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final total = widget.sourceGroup.items.length;
|
||||
final currentItem = widget.sourceGroup.items[_currentIndex];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
MassMigrationSourceIcon(source: widget.destinationSource),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
l10n.mass_migration_finding_matches(
|
||||
widget.destinationSource.name ?? '',
|
||||
completeLanguageName(widget.destinationSource.lang ?? ''),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
LinearProgressIndicator(value: (_currentIndex + 1) / total),
|
||||
const SizedBox(height: 8),
|
||||
Text(l10n.mass_migration_processing_item(_currentIndex + 1, total)),
|
||||
const SizedBox(height: 16),
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MassMigrationCover(
|
||||
libraryItem: currentItem,
|
||||
source: widget.sourceGroup.source,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
currentItem.name ?? l10n.mass_migration_unknown_title,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
[
|
||||
widget.sourceGroup.sourceName,
|
||||
l10n.mass_migration_chapter_count(
|
||||
currentItem.chapters.length,
|
||||
),
|
||||
].join(' • '),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_isWaitingForNextItem) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(l10n.mass_migration_waiting_next_item),
|
||||
],
|
||||
if (_resolvedItems.isNotEmpty) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
l10n.mass_migration_matched_so_far(
|
||||
_resolvedItems.where((item) => item.hasMatch).length,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
l10n.mass_migration_no_match_count(
|
||||
_resolvedItems.where((item) => !item.hasMatch).length,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReviewPhase(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final matchedCount = _resolvedItems.where((item) => item.hasMatch).length;
|
||||
final selectedCount = _resolvedItems
|
||||
.where((item) => item.shouldMigrate)
|
||||
.length;
|
||||
final noMatchCount = _resolvedItems.where((item) => !item.hasMatch).length;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
MassMigrationSourceIcon(source: widget.destinationSource),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
l10n.mass_migration_review_matches(
|
||||
widget.destinationSource.name ?? '',
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(l10n.mass_migration_found_matches(matchedCount)),
|
||||
Text(l10n.mass_migration_no_matches(noMatchCount)),
|
||||
Text(l10n.mass_migration_selected_to_migrate(selectedCount)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
itemCount: _resolvedItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _resolvedItems[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 6,
|
||||
),
|
||||
child: _buildResolvedItemCard(context, item, index),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: _startMigration,
|
||||
child: Text(
|
||||
selectedCount == 0
|
||||
? l10n.mass_migration_finish_review
|
||||
: l10n.mass_migration_migrate_selected(selectedCount),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildApplyingPhase(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final selectedItems = _resolvedItems
|
||||
.where((item) => item.shouldMigrate)
|
||||
.toList();
|
||||
final total = selectedItems.isEmpty ? 1 : selectedItems.length;
|
||||
final currentItem = selectedItems.isEmpty
|
||||
? null
|
||||
: selectedItems[_currentIndex.clamp(0, selectedItems.length - 1)];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.mass_migration_migrating_selected(
|
||||
widget.destinationSource.name ?? '',
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
LinearProgressIndicator(
|
||||
value: selectedItems.isEmpty ? 1 : (_currentIndex + 1) / total,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
selectedItems.isEmpty
|
||||
? l10n.mass_migration_no_items_selected
|
||||
: l10n.mass_migration_migrating_item(_currentIndex + 1, total),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (currentItem != null)
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MassMigrationCover(
|
||||
libraryItem: currentItem.sourceItem,
|
||||
source: widget.sourceGroup.source,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Icon(Icons.arrow_forward_rounded),
|
||||
const SizedBox(width: 12),
|
||||
MassMigrationCover(
|
||||
remoteItem: currentItem.selectedCandidate,
|
||||
source: widget.destinationSource,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
currentItem.sourceItem.name ??
|
||||
l10n.mass_migration_unknown_title,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
currentItem.selectedCandidate?.name ??
|
||||
l10n.mass_migration_unknown_match,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_isWaitingForNextItem) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(l10n.mass_migration_waiting_next_migration),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryPhase(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final total = widget.sourceGroup.items.length;
|
||||
final matchedCount = _resolvedItems.where((item) => item.hasMatch).length;
|
||||
final successTone = _failedItems.isEmpty ? Colors.green : Colors.orange;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).cardColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).shadowColor.withValues(alpha: 0.08),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: successTone.withValues(alpha: 0.14),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Icon(
|
||||
_failedItems.isEmpty
|
||||
? Icons.task_alt_rounded
|
||||
: Icons.done_all_rounded,
|
||||
color: successTone,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.mass_migration_complete,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_failedItems.isEmpty
|
||||
? l10n.mass_migration_complete_success_message
|
||||
: l10n.mass_migration_complete_partial_message,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
l10n.mass_migration_route_summary(
|
||||
widget.sourceGroup.sourceName,
|
||||
widget.destinationSource.name ?? '',
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
_SummaryStatCard(
|
||||
label: l10n.mass_migration_processed,
|
||||
value: '$total',
|
||||
),
|
||||
_SummaryStatCard(
|
||||
label: l10n.mass_migration_matched,
|
||||
value: '$matchedCount',
|
||||
),
|
||||
_SummaryStatCard(
|
||||
label: l10n.mass_migration_migrated,
|
||||
value: '$_migratedCount',
|
||||
),
|
||||
_SummaryStatCard(
|
||||
label: l10n.mass_migration_skipped,
|
||||
value: '$_skippedCount',
|
||||
),
|
||||
_SummaryStatCard(
|
||||
label: l10n.mass_migration_failed,
|
||||
value: '${_failedItems.length}',
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_failedItems.isNotEmpty) ...[
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.mass_migration_failed_items,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
for (final item in _failedItems)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error_outline_rounded,
|
||||
size: 18,
|
||||
color: Colors.orange,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(item)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
FilledButton.icon(
|
||||
onPressed: _exitMassMigrationFlow,
|
||||
icon: const Icon(Icons.exit_to_app_rounded),
|
||||
label: Text(l10n.mass_migration_exit),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildResolvedItemCard(
|
||||
BuildContext context,
|
||||
MassMigrationResolvedItem item,
|
||||
int index,
|
||||
) {
|
||||
final l10n = context.l10n;
|
||||
final destinationChapterNames =
|
||||
item.destinationPreview?.chapters
|
||||
?.map(
|
||||
(chapter) => chapter.name ?? l10n.mass_migration_unknown_chapter,
|
||||
)
|
||||
.toList() ??
|
||||
const <String>[];
|
||||
final sourceChapterNames = item.sourceItem.chapters
|
||||
.map((chapter) => chapter.name ?? l10n.mass_migration_unknown_chapter)
|
||||
.toList();
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MassMigrationCover(
|
||||
libraryItem: item.sourceItem,
|
||||
source: widget.sourceGroup.source,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Icon(Icons.arrow_forward_rounded),
|
||||
const SizedBox(width: 12),
|
||||
MassMigrationCover(
|
||||
remoteItem: item.selectedCandidate,
|
||||
source: widget.destinationSource,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.sourceItem.name ??
|
||||
l10n.mass_migration_unknown_title,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
item.hasMatch
|
||||
? (item.selectedCandidate?.name ??
|
||||
l10n.mass_migration_unknown_match)
|
||||
: l10n.mass_migration_no_destination_match,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
[
|
||||
l10n.mass_migration_source_chapter_count(
|
||||
item.sourceItem.chapters.length,
|
||||
),
|
||||
l10n.mass_migration_destination_chapter_count(
|
||||
destinationChapterNames.length,
|
||||
),
|
||||
if ((item.searchResult.usedQuery ?? '').isNotEmpty)
|
||||
l10n.mass_migration_query(
|
||||
item.searchResult.usedQuery!,
|
||||
),
|
||||
].join(' • '),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if ((item.errorMessage ?? '').isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(item.errorMessage!),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
ChoiceChip(
|
||||
label: Text(l10n.mass_migration_skip),
|
||||
selected: !item.shouldMigrate,
|
||||
onSelected: (_) => _updateShouldMigrate(index, false),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ChoiceChip(
|
||||
label: Text(l10n.migrate),
|
||||
selected: item.shouldMigrate,
|
||||
onSelected: item.canMigrate
|
||||
? (_) => _updateShouldMigrate(index, true)
|
||||
: null,
|
||||
),
|
||||
const Spacer(),
|
||||
if (item.searchResult.candidates.length > 1)
|
||||
TextButton(
|
||||
onPressed: _loadingCandidateIndexes.contains(index)
|
||||
? null
|
||||
: () => _pickAnotherCandidate(index),
|
||||
child: Text(
|
||||
_loadingCandidateIndexes.contains(index)
|
||||
? l10n.mass_migration_loading
|
||||
: l10n.mass_migration_choose_another_result,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
MassMigrationChapterSection(
|
||||
title: l10n.mass_migration_source_chapters,
|
||||
chapters: sourceChapterNames,
|
||||
),
|
||||
MassMigrationChapterSection(
|
||||
title: l10n.mass_migration_destination_chapters,
|
||||
chapters: destinationChapterNames,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _processAllItems() async {
|
||||
for (var i = 0; i < widget.sourceGroup.items.length; i++) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_currentIndex = i;
|
||||
});
|
||||
final resolvedItem = await resolveMassMigrationItem(
|
||||
ref: ref,
|
||||
manga: widget.sourceGroup.items[i],
|
||||
destinationSource: widget.destinationSource,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_resolvedItems.add(resolvedItem);
|
||||
});
|
||||
if (i < widget.sourceGroup.items.length - 1) {
|
||||
setState(() {
|
||||
_isWaitingForNextItem = true;
|
||||
});
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isWaitingForNextItem = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_phase = _MassMigrationPhase.review;
|
||||
_currentIndex = 0;
|
||||
});
|
||||
}
|
||||
|
||||
void _updateShouldMigrate(int index, bool shouldMigrate) {
|
||||
setState(() {
|
||||
_resolvedItems[index] = _resolvedItems[index].copyWith(
|
||||
shouldMigrate: shouldMigrate && _resolvedItems[index].canMigrate,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _pickAnotherCandidate(int index) async {
|
||||
final item = _resolvedItems[index];
|
||||
final selected = await showModalBottomSheet<MManga>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return SafeArea(
|
||||
child: ListView.separated(
|
||||
itemCount: item.searchResult.candidates.length,
|
||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||
itemBuilder: (context, candidateIndex) {
|
||||
final l10n = context.l10n;
|
||||
final candidate = item.searchResult.candidates[candidateIndex];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
candidate.name ?? l10n.mass_migration_unknown_title,
|
||||
),
|
||||
subtitle: Text(
|
||||
[
|
||||
if ((candidate.author ?? '').trim().isNotEmpty)
|
||||
candidate.author!,
|
||||
if ((candidate.artist ?? '').trim().isNotEmpty)
|
||||
candidate.artist!,
|
||||
].join(' • '),
|
||||
),
|
||||
onTap: () => Navigator.pop(context, candidate),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (selected == null || !mounted) return;
|
||||
|
||||
setState(() {
|
||||
_loadingCandidateIndexes.add(index);
|
||||
});
|
||||
|
||||
try {
|
||||
final preview = await ref.read(
|
||||
getDetailProvider(
|
||||
url: selected.link!,
|
||||
source: widget.destinationSource,
|
||||
).future,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_resolvedItems[index] = item.copyWith(
|
||||
selectedCandidate: selected,
|
||||
destinationPreview: preview,
|
||||
errorMessage: null,
|
||||
shouldMigrate: true,
|
||||
keepErrorMessage: false,
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_resolvedItems[index] = item.copyWith(
|
||||
selectedCandidate: selected,
|
||||
destinationPreview: null,
|
||||
errorMessage: error.toString(),
|
||||
shouldMigrate: false,
|
||||
keepDestinationPreview: false,
|
||||
keepErrorMessage: false,
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loadingCandidateIndexes.remove(index);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startMigration() async {
|
||||
final selectedItems = _resolvedItems
|
||||
.where((item) => item.shouldMigrate)
|
||||
.toList();
|
||||
if (selectedItems.isEmpty) {
|
||||
setState(() {
|
||||
_skippedCount = widget.sourceGroup.items.length;
|
||||
_phase = _MassMigrationPhase.summary;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_phase = _MassMigrationPhase.applying;
|
||||
_migratedCount = 0;
|
||||
_skippedCount = widget.sourceGroup.items.length - selectedItems.length;
|
||||
_failedItems.clear();
|
||||
_currentIndex = 0;
|
||||
});
|
||||
|
||||
for (var i = 0; i < selectedItems.length; i++) {
|
||||
final item = selectedItems[i];
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_currentIndex = i;
|
||||
});
|
||||
|
||||
try {
|
||||
await migrateLibraryItem(
|
||||
ref: ref,
|
||||
oldManga: item.sourceItem,
|
||||
selectedManga: item.selectedCandidate!,
|
||||
preview: item.destinationPreview!,
|
||||
destinationSource: widget.destinationSource,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_migratedCount += 1;
|
||||
});
|
||||
} catch (error) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_failedItems.add(
|
||||
item.sourceItem.name ?? context.l10n.mass_migration_unknown_title,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_phase = _MassMigrationPhase.summary;
|
||||
});
|
||||
}
|
||||
|
||||
void _exitMassMigrationFlow() {
|
||||
final navigator = Navigator.of(context);
|
||||
var pops = 0;
|
||||
while (navigator.canPop() && pops < 4) {
|
||||
navigator.pop();
|
||||
pops++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _SummaryStatCard extends StatelessWidget {
|
||||
const _SummaryStatCard({required this.label, required this.value});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 156,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(value, style: Theme.of(context).textTheme.headlineSmall),
|
||||
const SizedBox(height: 6),
|
||||
Text(label, style: Theme.of(context).textTheme.bodyMedium),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/mass_migration_preview_screen.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/models/mass_migration_models.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/widgets/mass_migration_widgets.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/utils/language.dart';
|
||||
|
||||
class MassMigrationSourceSelectionScreen extends StatelessWidget {
|
||||
const MassMigrationSourceSelectionScreen({
|
||||
required this.initialManga,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Manga initialManga;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final sourceGroups = buildMassMigrationSourceGroups(
|
||||
itemType: initialManga.itemType,
|
||||
prioritizedManga: initialManga,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.mass_migration_title)),
|
||||
body: sourceGroups.isEmpty
|
||||
? Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(l10n.mass_migration_no_library_items),
|
||||
),
|
||||
)
|
||||
: ListView.separated(
|
||||
itemCount: sourceGroups.length,
|
||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||
itemBuilder: (context, index) {
|
||||
final sourceGroup = sourceGroups[index];
|
||||
return ListTile(
|
||||
leading: MassMigrationSourceIcon(source: sourceGroup.source),
|
||||
title: Text(sourceGroup.sourceName),
|
||||
subtitle: Text(
|
||||
[
|
||||
if ((sourceGroup.lang ?? '').isNotEmpty)
|
||||
completeLanguageName(sourceGroup.lang!),
|
||||
l10n.mass_migration_item_count(sourceGroup.count),
|
||||
].join(' • '),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
createRoute(
|
||||
page: MassMigrationPreviewScreen(
|
||||
sourceGroup: sourceGroup,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
177
lib/modules/mass_migration/models/mass_migration_models.dart
Normal file
177
lib/modules/mass_migration/models/mass_migration_models.dart
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import 'package:isar_community/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
|
||||
class MassMigrationSourceGroup {
|
||||
const MassMigrationSourceGroup({
|
||||
required this.sourceName,
|
||||
required this.itemType,
|
||||
required this.items,
|
||||
this.source,
|
||||
this.lang,
|
||||
this.sourceId,
|
||||
});
|
||||
|
||||
final String sourceName;
|
||||
final Source? source;
|
||||
final String? lang;
|
||||
final int? sourceId;
|
||||
final ItemType itemType;
|
||||
final List<Manga> items;
|
||||
|
||||
int get count => items.length;
|
||||
}
|
||||
|
||||
class MassMigrationSearchResult {
|
||||
const MassMigrationSearchResult({
|
||||
required this.queries,
|
||||
required this.candidates,
|
||||
this.selected,
|
||||
this.usedQuery,
|
||||
});
|
||||
|
||||
final List<String> queries;
|
||||
final List<MManga> candidates;
|
||||
final MManga? selected;
|
||||
final String? usedQuery;
|
||||
|
||||
bool get hasMatch => selected != null;
|
||||
}
|
||||
|
||||
class MassMigrationResolvedItem {
|
||||
const MassMigrationResolvedItem({
|
||||
required this.sourceItem,
|
||||
required this.searchResult,
|
||||
this.selectedCandidate,
|
||||
this.destinationPreview,
|
||||
this.errorMessage,
|
||||
this.shouldMigrate = false,
|
||||
});
|
||||
|
||||
final Manga sourceItem;
|
||||
final MassMigrationSearchResult searchResult;
|
||||
final MManga? selectedCandidate;
|
||||
final MManga? destinationPreview;
|
||||
final String? errorMessage;
|
||||
final bool shouldMigrate;
|
||||
|
||||
bool get hasMatch => selectedCandidate != null;
|
||||
bool get canMigrate =>
|
||||
selectedCandidate != null &&
|
||||
destinationPreview != null &&
|
||||
(errorMessage == null || errorMessage!.isEmpty);
|
||||
|
||||
MassMigrationResolvedItem copyWith({
|
||||
MManga? selectedCandidate,
|
||||
MManga? destinationPreview,
|
||||
String? errorMessage,
|
||||
bool? shouldMigrate,
|
||||
bool keepSelectedCandidate = true,
|
||||
bool keepDestinationPreview = true,
|
||||
bool keepErrorMessage = true,
|
||||
}) {
|
||||
return MassMigrationResolvedItem(
|
||||
sourceItem: sourceItem,
|
||||
searchResult: searchResult,
|
||||
selectedCandidate: keepSelectedCandidate
|
||||
? selectedCandidate ?? this.selectedCandidate
|
||||
: selectedCandidate,
|
||||
destinationPreview: keepDestinationPreview
|
||||
? destinationPreview ?? this.destinationPreview
|
||||
: destinationPreview,
|
||||
errorMessage: keepErrorMessage
|
||||
? errorMessage ?? this.errorMessage
|
||||
: errorMessage,
|
||||
shouldMigrate: shouldMigrate ?? this.shouldMigrate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<MassMigrationSourceGroup> buildMassMigrationSourceGroups({
|
||||
required ItemType itemType,
|
||||
Manga? prioritizedManga,
|
||||
}) {
|
||||
final libraryItems = isar.mangas
|
||||
.filter()
|
||||
.favoriteEqualTo(true)
|
||||
.itemTypeEqualTo(itemType)
|
||||
.findAllSync();
|
||||
|
||||
final grouped = <String, List<Manga>>{};
|
||||
for (final manga in libraryItems) {
|
||||
final sourceName = (manga.source ?? '').trim();
|
||||
if (sourceName.isEmpty) continue;
|
||||
grouped.putIfAbsent(sourceName, () => []).add(manga);
|
||||
}
|
||||
|
||||
final groups = grouped.entries.map((entry) {
|
||||
final items = [...entry.value]
|
||||
..sort(
|
||||
(left, right) => (left.name ?? '').toLowerCase().compareTo(
|
||||
(right.name ?? '').toLowerCase(),
|
||||
),
|
||||
);
|
||||
final first = items.first;
|
||||
final source = first.sourceId != null
|
||||
? isar.sources.getSync(first.sourceId!)
|
||||
: isar.sources
|
||||
.filter()
|
||||
.nameEqualTo(first.source)
|
||||
.langEqualTo(first.lang)
|
||||
.findFirstSync();
|
||||
return MassMigrationSourceGroup(
|
||||
sourceName: entry.key,
|
||||
source: source,
|
||||
lang: first.lang,
|
||||
sourceId: first.sourceId,
|
||||
itemType: itemType,
|
||||
items: items,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
groups.sort((left, right) {
|
||||
final prioritizedSource = prioritizedManga?.source?.trim().toLowerCase();
|
||||
final leftPriority = left.sourceName.toLowerCase() == prioritizedSource;
|
||||
final rightPriority = right.sourceName.toLowerCase() == prioritizedSource;
|
||||
if (leftPriority != rightPriority) {
|
||||
return leftPriority ? -1 : 1;
|
||||
}
|
||||
final nameCompare = left.sourceName.toLowerCase().compareTo(
|
||||
right.sourceName.toLowerCase(),
|
||||
);
|
||||
if (nameCompare != 0) return nameCompare;
|
||||
return right.count.compareTo(left.count);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
List<Source> buildMassMigrationDestinationSources({
|
||||
required MassMigrationSourceGroup sourceGroup,
|
||||
}) {
|
||||
final sources = isar.sources
|
||||
.filter()
|
||||
.isAddedEqualTo(true)
|
||||
.itemTypeEqualTo(sourceGroup.itemType)
|
||||
.findAllSync()
|
||||
.where(
|
||||
(source) =>
|
||||
source.sourceCode != null &&
|
||||
!(source.name == sourceGroup.sourceName &&
|
||||
source.lang == sourceGroup.lang),
|
||||
)
|
||||
.toList();
|
||||
|
||||
sources.sort((left, right) {
|
||||
final nameCompare = (left.name ?? '').toLowerCase().compareTo(
|
||||
(right.name ?? '').toLowerCase(),
|
||||
);
|
||||
if (nameCompare != 0) return nameCompare;
|
||||
return (left.lang ?? '').toLowerCase().compareTo(
|
||||
(right.lang ?? '').toLowerCase(),
|
||||
);
|
||||
});
|
||||
return sources;
|
||||
}
|
||||
488
lib/modules/mass_migration/services/mass_migration_service.dart
Normal file
488
lib/modules/mass_migration/services/mass_migration_service.dart
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/models/track.dart';
|
||||
import 'package:mangayomi/models/update.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/models/mass_migration_models.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/providers/isar_providers.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/services/get_detail.dart';
|
||||
import 'package:mangayomi/services/search_.dart';
|
||||
import 'package:mangayomi/utils/extensions/string_extensions.dart';
|
||||
|
||||
Future<void> migrateLibraryItem({
|
||||
required WidgetRef ref,
|
||||
required Manga oldManga,
|
||||
required MManga selectedManga,
|
||||
required MManga preview,
|
||||
required Source destinationSource,
|
||||
}) async {
|
||||
final migrationSnapshot = _captureMigrationSnapshot(
|
||||
ref: ref,
|
||||
oldManga: oldManga,
|
||||
);
|
||||
_rewriteMigratedItemMetadata(
|
||||
oldManga: oldManga,
|
||||
selectedManga: selectedManga,
|
||||
preview: preview,
|
||||
destinationSource: destinationSource,
|
||||
);
|
||||
_syncMigratedMangaFromPreview(
|
||||
oldManga: oldManga,
|
||||
preview: preview,
|
||||
destinationSource: destinationSource,
|
||||
);
|
||||
_restoreMigrationProgress(oldManga: oldManga, snapshot: migrationSnapshot);
|
||||
ref.invalidate(getMangaDetailStreamProvider(mangaId: oldManga.id!));
|
||||
}
|
||||
|
||||
class _MigrationSnapshot {
|
||||
const _MigrationSnapshot({
|
||||
required this.chaptersProgress,
|
||||
this.historyChapter,
|
||||
this.historyDate,
|
||||
});
|
||||
|
||||
final List<Chapter> chaptersProgress;
|
||||
final String? historyChapter;
|
||||
final String? historyDate;
|
||||
}
|
||||
|
||||
_MigrationSnapshot _captureMigrationSnapshot({
|
||||
required WidgetRef ref,
|
||||
required Manga oldManga,
|
||||
}) {
|
||||
String? historyChapter;
|
||||
String? historyDate;
|
||||
final chaptersProgress = <Chapter>[];
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
final histories = isar.historys
|
||||
.filter()
|
||||
.mangaIdEqualTo(oldManga.id)
|
||||
.sortByDate()
|
||||
.findAllSync();
|
||||
historyChapter = extractMigrationChapterNumber(
|
||||
histories.lastOrNull?.chapter.value?.name ?? '',
|
||||
);
|
||||
historyDate = histories.lastOrNull?.date;
|
||||
for (final history in histories) {
|
||||
isar.historys.deleteSync(history.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.removeHistory, history.id, '{}', false);
|
||||
}
|
||||
for (final chapter in oldManga.chapters) {
|
||||
chaptersProgress.add(chapter);
|
||||
isar.updates
|
||||
.filter()
|
||||
.mangaIdEqualTo(chapter.mangaId)
|
||||
.chapterNameEqualTo(chapter.name)
|
||||
.deleteAllSync();
|
||||
isar.chapters.deleteSync(chapter.id!);
|
||||
ref
|
||||
.read(synchingProvider(syncId: 1).notifier)
|
||||
.addChangedPart(ActionType.removeChapter, chapter.id, '{}', false);
|
||||
}
|
||||
});
|
||||
|
||||
return _MigrationSnapshot(
|
||||
chaptersProgress: chaptersProgress,
|
||||
historyChapter: historyChapter,
|
||||
historyDate: historyDate,
|
||||
);
|
||||
}
|
||||
|
||||
void _rewriteMigratedItemMetadata({
|
||||
required Manga oldManga,
|
||||
required MManga selectedManga,
|
||||
required MManga preview,
|
||||
required Source destinationSource,
|
||||
}) {
|
||||
isar.writeTxnSync(() {
|
||||
oldManga.name = selectedManga.name;
|
||||
oldManga.link = selectedManga.link;
|
||||
oldManga.imageUrl = selectedManga.imageUrl;
|
||||
oldManga.lang = destinationSource.lang;
|
||||
oldManga.source = destinationSource.name;
|
||||
oldManga.sourceId = destinationSource.id;
|
||||
oldManga.artist = preview.artist;
|
||||
oldManga.author = preview.author;
|
||||
oldManga.status = preview.status ?? oldManga.status;
|
||||
oldManga.description = preview.description;
|
||||
oldManga.genre = preview.genre;
|
||||
oldManga.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
isar.mangas.putSync(oldManga);
|
||||
});
|
||||
}
|
||||
|
||||
void _syncMigratedMangaFromPreview({
|
||||
required Manga oldManga,
|
||||
required MManga preview,
|
||||
required Source destinationSource,
|
||||
}) {
|
||||
final genre =
|
||||
preview.genre
|
||||
?.map((entry) => entry.toString().trim())
|
||||
.where((entry) => entry.isNotEmpty)
|
||||
.toSet()
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
final previewImageUrl = _trimmedOrDefault(
|
||||
preview.imageUrl,
|
||||
oldManga.imageUrl,
|
||||
);
|
||||
oldManga
|
||||
..imageUrl = previewImageUrl == null
|
||||
? null
|
||||
: previewImageUrl.startsWith('http')
|
||||
? previewImageUrl
|
||||
: '${destinationSource.baseUrl ?? ''}/${previewImageUrl.getUrlWithoutDomain}'
|
||||
..name = _trimmedOrDefault(preview.name, oldManga.name)
|
||||
..genre = genre.isEmpty ? oldManga.genre ?? [] : genre
|
||||
..author = _trimmedOrDefault(preview.author, oldManga.author) ?? ''
|
||||
..artist = _trimmedOrDefault(preview.artist, oldManga.artist) ?? ''
|
||||
..status = preview.status == Status.unknown
|
||||
? oldManga.status
|
||||
: preview.status ?? Status.unknown
|
||||
..description =
|
||||
_trimmedOrDefault(preview.description, oldManga.description) ?? ''
|
||||
..link = _trimmedOrDefault(preview.link, oldManga.link)
|
||||
..source = destinationSource.name
|
||||
..lang = destinationSource.lang
|
||||
..itemType = destinationSource.itemType
|
||||
..lastUpdate = DateTime.now().millisecondsSinceEpoch
|
||||
..updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
isar.writeTxnSync(() {
|
||||
final mangaId = isar.mangas.putSync(oldManga);
|
||||
final previewChapters = preview.chapters ?? const [];
|
||||
final chapters = previewChapters
|
||||
.map(
|
||||
(previewChapter) => Chapter(
|
||||
name: previewChapter.name ?? '',
|
||||
url: previewChapter.url?.trim() ?? '',
|
||||
dateUpload: previewChapter.dateUpload == null
|
||||
? DateTime.now().millisecondsSinceEpoch.toString()
|
||||
: previewChapter.dateUpload.toString(),
|
||||
scanlator: previewChapter.scanlator ?? '',
|
||||
mangaId: mangaId,
|
||||
updatedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
isFiller: previewChapter.isFiller,
|
||||
thumbnailUrl: previewChapter.thumbnailUrl,
|
||||
description: previewChapter.description,
|
||||
downloadSize: previewChapter.downloadSize,
|
||||
duration: previewChapter.duration,
|
||||
)..manga.value = oldManga,
|
||||
)
|
||||
.toList();
|
||||
for (final chapter in chapters.reversed) {
|
||||
isar.chapters.putSync(chapter);
|
||||
chapter.manga.saveSync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _restoreMigrationProgress({
|
||||
required Manga oldManga,
|
||||
required _MigrationSnapshot snapshot,
|
||||
}) {
|
||||
isar.writeTxnSync(() {
|
||||
for (final oldChapter in snapshot.chaptersProgress) {
|
||||
final chapter = isar.chapters
|
||||
.filter()
|
||||
.mangaIdEqualTo(oldManga.id)
|
||||
.nameContains(
|
||||
extractMigrationChapterNumber(oldChapter.name ?? '') ?? '.....',
|
||||
caseSensitive: false,
|
||||
)
|
||||
.findFirstSync();
|
||||
if (chapter != null) {
|
||||
chapter.isBookmarked = oldChapter.isBookmarked;
|
||||
chapter.lastPageRead = oldChapter.lastPageRead;
|
||||
chapter.isRead = oldChapter.isRead;
|
||||
isar.chapters.putSync(chapter);
|
||||
}
|
||||
}
|
||||
|
||||
final historyChapter = isar.chapters
|
||||
.filter()
|
||||
.mangaIdEqualTo(oldManga.id)
|
||||
.nameContains(snapshot.historyChapter ?? '.....', caseSensitive: false)
|
||||
.findFirstSync();
|
||||
if (historyChapter != null) {
|
||||
isar.historys.putSync(
|
||||
History(
|
||||
mangaId: oldManga.id,
|
||||
date:
|
||||
snapshot.historyDate ??
|
||||
DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
itemType: oldManga.itemType,
|
||||
chapterId: historyChapter.id,
|
||||
)..chapter.value = historyChapter,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<MassMigrationSearchResult> findBestMassMigrationMatch({
|
||||
required WidgetRef ref,
|
||||
required Manga manga,
|
||||
required Source destinationSource,
|
||||
}) async {
|
||||
final queries = buildMassMigrationQueries(manga);
|
||||
|
||||
for (final query in queries) {
|
||||
final pages = await ref.read(
|
||||
searchProvider(
|
||||
source: destinationSource,
|
||||
page: 1,
|
||||
query: query,
|
||||
filterList: const [],
|
||||
).future,
|
||||
);
|
||||
final candidates = pages?.list ?? const <MManga>[];
|
||||
if (candidates.isEmpty) continue;
|
||||
return MassMigrationSearchResult(
|
||||
queries: queries,
|
||||
usedQuery: query,
|
||||
candidates: candidates,
|
||||
selected: _selectBestCandidate(
|
||||
manga: manga,
|
||||
queries: queries,
|
||||
candidates: candidates,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return MassMigrationSearchResult(queries: queries, candidates: const []);
|
||||
}
|
||||
|
||||
Future<MassMigrationResolvedItem> resolveMassMigrationItem({
|
||||
required WidgetRef ref,
|
||||
required Manga manga,
|
||||
required Source destinationSource,
|
||||
}) async {
|
||||
try {
|
||||
final searchResult = await _resolveSearchResult(
|
||||
ref: ref,
|
||||
manga: manga,
|
||||
destinationSource: destinationSource,
|
||||
);
|
||||
return await _resolveMatchedPreview(
|
||||
ref: ref,
|
||||
manga: manga,
|
||||
destinationSource: destinationSource,
|
||||
searchResult: searchResult,
|
||||
);
|
||||
} catch (error) {
|
||||
return _buildErroredResolvedItem(
|
||||
sourceItem: manga,
|
||||
errorMessage: error.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<MassMigrationSearchResult> _resolveSearchResult({
|
||||
required WidgetRef ref,
|
||||
required Manga manga,
|
||||
required Source destinationSource,
|
||||
}) {
|
||||
return findBestMassMigrationMatch(
|
||||
ref: ref,
|
||||
manga: manga,
|
||||
destinationSource: destinationSource,
|
||||
);
|
||||
}
|
||||
|
||||
Future<MassMigrationResolvedItem> _resolveMatchedPreview({
|
||||
required WidgetRef ref,
|
||||
required Manga manga,
|
||||
required Source destinationSource,
|
||||
required MassMigrationSearchResult searchResult,
|
||||
}) async {
|
||||
final selectedCandidate = searchResult.selected;
|
||||
if (selectedCandidate == null) {
|
||||
return MassMigrationResolvedItem(
|
||||
sourceItem: manga,
|
||||
searchResult: searchResult,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final preview = await ref.read(
|
||||
getDetailProvider(
|
||||
url: selectedCandidate.link!,
|
||||
source: destinationSource,
|
||||
).future,
|
||||
);
|
||||
return MassMigrationResolvedItem(
|
||||
sourceItem: manga,
|
||||
searchResult: searchResult,
|
||||
selectedCandidate: selectedCandidate,
|
||||
destinationPreview: preview,
|
||||
shouldMigrate: true,
|
||||
);
|
||||
} catch (error) {
|
||||
return MassMigrationResolvedItem(
|
||||
sourceItem: manga,
|
||||
searchResult: searchResult,
|
||||
selectedCandidate: selectedCandidate,
|
||||
errorMessage: error.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MassMigrationResolvedItem _buildErroredResolvedItem({
|
||||
required Manga sourceItem,
|
||||
required String errorMessage,
|
||||
}) {
|
||||
return MassMigrationResolvedItem(
|
||||
sourceItem: sourceItem,
|
||||
searchResult: MassMigrationSearchResult(
|
||||
queries: buildMassMigrationQueries(sourceItem),
|
||||
candidates: const [],
|
||||
),
|
||||
errorMessage: errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
List<String> buildMassMigrationQueries(Manga manga) {
|
||||
final queries = <String>{};
|
||||
|
||||
void addQuery(String? value) {
|
||||
final cleaned = value?.trim();
|
||||
if (cleaned == null || cleaned.isEmpty) return;
|
||||
queries.add(cleaned);
|
||||
}
|
||||
|
||||
addQuery(manga.name);
|
||||
for (final track
|
||||
in isar.tracks.filter().mangaIdEqualTo(manga.id).findAllSync()) {
|
||||
addQuery(track.title);
|
||||
}
|
||||
|
||||
final name = manga.name?.trim();
|
||||
if (name != null && name.isNotEmpty) {
|
||||
addQuery(name.split(RegExp(r'\s*[:\-|/]\s*')).first);
|
||||
final beforeParenthesis = name.split('(').first.trim();
|
||||
if (beforeParenthesis.isNotEmpty && beforeParenthesis != name) {
|
||||
addQuery(beforeParenthesis);
|
||||
}
|
||||
final matches = RegExp(r'\(([^)]+)\)').allMatches(name);
|
||||
for (final match in matches) {
|
||||
addQuery(match.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
return queries.toList();
|
||||
}
|
||||
|
||||
String? extractMigrationChapterNumber(String chapterName) {
|
||||
return RegExp(
|
||||
r'\s*(\d+\.\d+)\s*',
|
||||
multiLine: true,
|
||||
).firstMatch(chapterName)?.group(0) ??
|
||||
RegExp(r'\s*(\d+)\s*', multiLine: true).firstMatch(chapterName)?.group(0);
|
||||
}
|
||||
|
||||
MManga _selectBestCandidate({
|
||||
required Manga manga,
|
||||
required List<String> queries,
|
||||
required List<MManga> candidates,
|
||||
}) {
|
||||
candidates.sort((left, right) {
|
||||
final leftScore = _scoreCandidate(
|
||||
manga: manga,
|
||||
queries: queries,
|
||||
candidate: left,
|
||||
);
|
||||
final rightScore = _scoreCandidate(
|
||||
manga: manga,
|
||||
queries: queries,
|
||||
candidate: right,
|
||||
);
|
||||
return rightScore.compareTo(leftScore);
|
||||
});
|
||||
return candidates.first;
|
||||
}
|
||||
|
||||
double _scoreCandidate({
|
||||
required Manga manga,
|
||||
required List<String> queries,
|
||||
required MManga candidate,
|
||||
}) {
|
||||
final candidateName = _normalizeTitle(candidate.name);
|
||||
if (candidateName.isEmpty) return 0;
|
||||
|
||||
var score = 0.0;
|
||||
for (final query in queries) {
|
||||
final normalizedQuery = _normalizeTitle(query);
|
||||
if (normalizedQuery.isEmpty) continue;
|
||||
if (normalizedQuery == candidateName) {
|
||||
score = score < 100 ? 100 : score;
|
||||
continue;
|
||||
}
|
||||
if (candidateName.contains(normalizedQuery) ||
|
||||
normalizedQuery.contains(candidateName)) {
|
||||
final ratio = normalizedQuery.length < candidateName.length
|
||||
? normalizedQuery.length / candidateName.length
|
||||
: candidateName.length / normalizedQuery.length;
|
||||
score = score < (80 * ratio) ? 80 * ratio : score;
|
||||
}
|
||||
final tokenScore = _tokenOverlapScore(normalizedQuery, candidateName);
|
||||
score = score < tokenScore ? tokenScore : score;
|
||||
}
|
||||
|
||||
final sourceAuthor = _normalizeTitle(manga.author);
|
||||
final sourceArtist = _normalizeTitle(manga.artist);
|
||||
final candidateAuthor = _normalizeTitle(candidate.author);
|
||||
final candidateArtist = _normalizeTitle(candidate.artist);
|
||||
if (sourceAuthor.isNotEmpty &&
|
||||
(sourceAuthor == candidateAuthor || sourceAuthor == candidateArtist)) {
|
||||
score += 15;
|
||||
}
|
||||
if (sourceArtist.isNotEmpty &&
|
||||
(sourceArtist == candidateArtist || sourceArtist == candidateAuthor)) {
|
||||
score += 10;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
double _tokenOverlapScore(String left, String right) {
|
||||
final leftTokens = left.split(' ').where((token) => token.isNotEmpty).toSet();
|
||||
final rightTokens = right
|
||||
.split(' ')
|
||||
.where((token) => token.isNotEmpty)
|
||||
.toSet();
|
||||
if (leftTokens.isEmpty || rightTokens.isEmpty) return 0;
|
||||
final overlap = leftTokens.intersection(rightTokens).length;
|
||||
return (overlap / leftTokens.union(rightTokens).length) * 70;
|
||||
}
|
||||
|
||||
String _normalizeTitle(String? value) {
|
||||
return value
|
||||
?.toLowerCase()
|
||||
.replaceAll(RegExp(r'[^a-z0-9\s]'), ' ')
|
||||
.replaceAll(RegExp(r'\b(the|a|an)\b'), ' ')
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim() ??
|
||||
'';
|
||||
}
|
||||
|
||||
String? _trimmedOrDefault(String? value, String? defaultValue) {
|
||||
if (value?.trim().isNotEmpty ?? false) {
|
||||
return value!.trim();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
170
lib/modules/mass_migration/widgets/mass_migration_widgets.dart
Normal file
170
lib/modules/mass_migration/widgets/mass_migration_widgets.dart
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/eval/model/m_manga.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/cached_network.dart';
|
||||
import 'package:mangayomi/utils/constant.dart';
|
||||
import 'package:mangayomi/utils/headers.dart';
|
||||
|
||||
class MassMigrationSourceIcon extends StatelessWidget {
|
||||
const MassMigrationSourceIcon({required this.source, super.key});
|
||||
|
||||
final Source? source;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconUrl = source?.iconUrl ?? '';
|
||||
return Container(
|
||||
height: 37,
|
||||
width: 37,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: iconUrl.isEmpty
|
||||
? const Icon(Icons.extension_rounded)
|
||||
: cachedNetworkImage(
|
||||
imageUrl: iconUrl,
|
||||
fit: BoxFit.contain,
|
||||
width: 37,
|
||||
height: 37,
|
||||
errorWidget: const SizedBox(
|
||||
width: 37,
|
||||
height: 37,
|
||||
child: Center(child: Icon(Icons.extension_rounded)),
|
||||
),
|
||||
useCustomNetworkImage: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MassMigrationCover extends ConsumerWidget {
|
||||
const MassMigrationCover({
|
||||
this.libraryItem,
|
||||
this.remoteItem,
|
||||
this.source,
|
||||
this.width = 72,
|
||||
this.height = 104,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Manga? libraryItem;
|
||||
final MManga? remoteItem;
|
||||
final Source? source;
|
||||
final double width;
|
||||
final double height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final customCover = libraryItem?.customCoverImage;
|
||||
if (customCover != null && customCover.isNotEmpty) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.memory(
|
||||
Uint8List.fromList(customCover),
|
||||
width: width,
|
||||
height: height,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final imageUrl = toImgUrl(
|
||||
remoteItem?.imageUrl ??
|
||||
libraryItem?.customCoverFromTracker ??
|
||||
libraryItem?.imageUrl ??
|
||||
'',
|
||||
);
|
||||
final headers =
|
||||
source == null ||
|
||||
(source!.name?.isEmpty ?? true) ||
|
||||
(source!.lang?.isEmpty ?? true)
|
||||
? null
|
||||
: ref.watch(
|
||||
headersProvider(
|
||||
source: source!.name!,
|
||||
lang: source!.lang!,
|
||||
sourceId: source!.id,
|
||||
),
|
||||
);
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: cachedNetworkImage(
|
||||
headers: headers,
|
||||
imageUrl: imageUrl,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: BoxFit.cover,
|
||||
errorWidget: Container(
|
||||
width: width,
|
||||
height: height,
|
||||
color: Theme.of(context).secondaryHeaderColor.withValues(alpha: 0.4),
|
||||
child: const Icon(Icons.image_not_supported_outlined),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MassMigrationChapterSection extends StatelessWidget {
|
||||
const MassMigrationChapterSection({
|
||||
required this.title,
|
||||
required this.chapters,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final List<String> chapters;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return ExpansionTile(
|
||||
tilePadding: EdgeInsets.zero,
|
||||
childrenPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
title: Text('$title (${chapters.length})'),
|
||||
children: [
|
||||
if (chapters.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(l10n.mass_migration_no_chapters_found),
|
||||
),
|
||||
)
|
||||
else
|
||||
...chapters
|
||||
.take(12)
|
||||
.map(
|
||||
(chapter) => ListTile(
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
chapter,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (chapters.length > 12)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
l10n.mass_migration_and_more_chapters(chapters.length - 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import 'package:mangayomi/modules/browse/extension/widgets/create_extension.dart
|
|||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/calendar/calendar_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/migrate_screen.dart';
|
||||
import 'package:mangayomi/modules/mass_migration/mass_migration_source_selection_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/recommendation_screen.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/watch_order_screen.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/create_backup.dart';
|
||||
|
|
@ -254,6 +255,11 @@ class RouterNotifier extends ChangeNotifier {
|
|||
name: "migrate",
|
||||
builder: (manga) => MigrationScreen(manga: manga),
|
||||
),
|
||||
_genericRoute<Manga>(
|
||||
name: "massMigration",
|
||||
builder: (manga) =>
|
||||
MassMigrationSourceSelectionScreen(initialManga: manga),
|
||||
),
|
||||
_genericRoute<(Manga, TrackSearch)>(
|
||||
name: "migrate/tracker",
|
||||
builder: (data) => MigrationScreen(manga: data.$1, trackSearch: data.$2),
|
||||
|
|
|
|||
Loading…
Reference in a new issue