added download all option

This commit is contained in:
Schnitzel5 2025-04-28 15:03:45 +02:00
parent 7e9202ceeb
commit 77b912d40e
27 changed files with 697 additions and 300 deletions

View file

@ -120,6 +120,7 @@
"custom_location": "Custom location",
"only_on_wifi": "Only on wifi",
"save_as_cbz_archive": "Save as CBZ archive",
"concurrent_downloads": "Concurrent downloads",
"browse_subtitle": "Sources, global search",
"only_include_pinned_sources": "Only include pinned sources",
"nsfw_sources": "NSFW (+18) sources",
@ -370,10 +371,12 @@
"next_5_chapters": "Next 5 chapters",
"next_10_chapters": "Next 10 chapters",
"next_25_chapters": "Next 25 chapters",
"all_chapters": "All chapters",
"next_episode": "Next episode",
"next_5_episodes": "Next 5 episodes",
"next_10_episodes": "Next 10 episodes",
"next_25_episodes": "Next 25 episodes",
"all_episodes": "All episodes",
"cover_saved": "Cover saved",
"set_as_cover": "Set as cover",
"use_this_as_cover_art": "Use this as cover art?",

View file

@ -837,6 +837,12 @@ abstract class AppLocalizations {
/// **'Save as CBZ archive'**
String get save_as_cbz_archive;
/// No description provided for @concurrent_downloads.
///
/// In en, this message translates to:
/// **'Concurrent downloads'**
String get concurrent_downloads;
/// No description provided for @browse_subtitle.
///
/// In en, this message translates to:
@ -2337,6 +2343,12 @@ abstract class AppLocalizations {
/// **'Next 25 chapters'**
String get next_25_chapters;
/// No description provided for @all_chapters.
///
/// In en, this message translates to:
/// **'All chapters'**
String get all_chapters;
/// No description provided for @next_episode.
///
/// In en, this message translates to:
@ -2361,6 +2373,12 @@ abstract class AppLocalizations {
/// **'Next 25 episodes'**
String get next_25_episodes;
/// No description provided for @all_episodes.
///
/// In en, this message translates to:
/// **'All episodes'**
String get all_episodes;
/// No description provided for @cover_saved.
///
/// In en, this message translates to:

View file

@ -372,6 +372,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get save_as_cbz_archive => 'حفظ كأرشيف CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'المصادر، البحث العام';
@ -1152,6 +1155,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get next_25_chapters => 'الفصول الخمسة والعشرون التالية';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'الحلقة التالية';
@ -1164,6 +1170,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get next_25_episodes => 'الخمسة وعشرون حلقة التالية';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'الغلاف المحفوظ';

View file

@ -372,6 +372,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Als CBZ-Archiv speichern';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Quellen, globale Suche';
@ -1152,6 +1155,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get next_25_chapters => 'Nächsten 25 Kapitel';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Nächste Episode';
@ -1164,6 +1170,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get next_25_episodes => 'Nächsten 25 Episoden';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Titelbild gespeichert';

View file

@ -372,6 +372,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Save as CBZ archive';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Sources, global search';
@ -1152,6 +1155,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get next_25_chapters => 'Next 25 chapters';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Next episode';
@ -1164,6 +1170,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get next_25_episodes => 'Next 25 episodes';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Cover saved';

View file

@ -372,6 +372,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Guardar como archivo CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Fuentes, búsqueda global';
@ -1152,6 +1155,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get next_25_chapters => 'Próximos 25 capítulos';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Siguiente episodio';
@ -1164,6 +1170,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get next_25_episodes => 'Siguientes 25 episodios';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Portada guardada';

View file

@ -372,6 +372,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Enregistrer comme archive CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Sources, extensions, recherche globale';
@ -1152,6 +1155,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get next_25_chapters => '25 chapitres suivants';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Épisode suivant';
@ -1164,6 +1170,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get next_25_episodes => '25 épisodes suivants';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Couverture enregistrée';

View file

@ -372,6 +372,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Simpan sebagai Arsip CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Sumber, Pencarian Umum';
@ -1152,6 +1155,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get next_25_chapters => '25 bab berikutnya';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Episode berikutnya';
@ -1164,6 +1170,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get next_25_episodes => '25 episode berikutnya';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Sampul disimpan';

View file

@ -372,6 +372,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Salva come archivio CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Fonti, ricerca globale';
@ -1152,6 +1155,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get next_25_chapters => 'Prossimi 25 capitoli';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Prossimo episodio';
@ -1164,6 +1170,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get next_25_episodes => 'Prossimi 25 episodi';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Copertina salvata';

View file

@ -372,6 +372,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Salvar como arquivo CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Fontes, pesquisa global';
@ -1152,6 +1155,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get next_25_chapters => 'Próximos 25 capítulos';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Próximo episódio';
@ -1164,6 +1170,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get next_25_episodes => 'Próximos 25 episódios';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Capa salva';

View file

@ -372,6 +372,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get save_as_cbz_archive => 'Сохранить как архив CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Источники, глобальный поиск';
@ -1152,6 +1155,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get next_25_chapters => 'Следующие 25 глав';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Следующий эпизод';
@ -1164,6 +1170,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get next_25_episodes => 'Следующие 25 эпизодов';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Обложка сохранена';

View file

@ -372,6 +372,9 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get save_as_cbz_archive => 'จัดเก็บเป็น CBZ';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Sources, global search';
@ -1152,6 +1155,9 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get next_25_chapters => '25 ตอนถัดไป';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'ตอนถัดไป';
@ -1164,6 +1170,9 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get next_25_episodes => '25 ตอนถัดไป';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'จัดเก็บภาพปกแล้ว';

View file

@ -372,6 +372,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get save_as_cbz_archive => 'CBZ Arşivi Olarak Kaydet';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => 'Kaynaklar, genel arama';
@ -1152,6 +1155,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get next_25_chapters => 'Sonraki 25 bölüm';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => 'Sonraki Bölüm';
@ -1164,6 +1170,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get next_25_episodes => 'Sonraki 25 Bölüm';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => 'Kapak kaydedildi';

View file

@ -372,6 +372,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get save_as_cbz_archive => '保存为CBZ档案';
@override
String get concurrent_downloads => 'Concurrent downloads';
@override
String get browse_subtitle => '来源,全球搜索';
@ -1152,6 +1155,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get next_25_chapters => '下25章';
@override
String get all_chapters => 'All chapters';
@override
String get next_episode => '下一集';
@ -1164,6 +1170,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get next_25_episodes => '接下来的 25 集';
@override
String get all_episodes => 'All episodes';
@override
String get cover_saved => '封面已保存';

View file

@ -82,6 +82,8 @@ class Settings {
bool? saveAsCBZArchive;
int? concurrentDownloads;
String? downloadLocation;
List<FilterScanlator>? filterScanlatorList;
@ -271,6 +273,7 @@ class Settings {
this.pureBlackDarkMode = false,
this.downloadOnlyOnWifi = false,
this.saveAsCBZArchive = false,
this.concurrentDownloads = 2,
this.downloadLocation = "",
this.cropBorders = false,
this.libraryLocalSource,
@ -404,6 +407,7 @@ class Settings {
doubleTapAnimationSpeed = json['doubleTapAnimationSpeed'];
downloadLocation = json['downloadLocation'];
downloadOnlyOnWifi = json['downloadOnlyOnWifi'];
concurrentDownloads = json['concurrentDownloads'];
filterScanlatorList =
(json['filterScanlatorList'] as List?)
?.map((e) => FilterScanlator.fromJson(e))
@ -604,6 +608,7 @@ class Settings {
'doubleTapAnimationSpeed': doubleTapAnimationSpeed,
'downloadLocation': downloadLocation,
'downloadOnlyOnWifi': downloadOnlyOnWifi,
'concurrentDownloads': concurrentDownloads,
'filterScanlatorList': filterScanlatorList,
'flexColorSchemeBlendLevel': flexColorSchemeBlendLevel,
'flexSchemeColorIndex': flexSchemeColorIndex,

File diff suppressed because it is too large Load diff

View file

@ -443,6 +443,14 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
: context.l10n.unwatched,
),
),
PopupMenuItem<int>(
value: 5,
child: Text(
widget.itemType != ItemType.anime
? context.l10n.all_chapters
: context.l10n.all_episodes,
),
),
];
},
onSelected: (value) {
@ -523,6 +531,25 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
);
}
}
} else if (value == 5) {
final allChapters =
isar.chapters
.filter()
.idIsNotNull()
.mangaIdEqualTo(widget.manga!.id!)
.findAllSync();
for (var chapter in allChapters) {
final entry =
isar.downloads
.filter()
.idEqualTo(chapter.id)
.findFirstSync();
if (entry == null || !entry.isDownload!) {
ref.watch(
downloadChapterProvider(chapter: chapter),
);
}
}
}
},
),

View file

@ -40,6 +40,7 @@ Future<void> downloadChapter(
botToast(navigatorKey.currentContext!.l10n.downloads_are_limited_to_wifi);
return;
}
final concurrentDownloads = ref.watch(concurrentDownloadsStateProvider);
final http = MClient.init(
reqcopyWith: {'useDartHttpClient': true, 'followRedirects': false},
);
@ -183,6 +184,7 @@ Future<void> downloadChapter(
headers: videosUrls.first.headers ?? {},
fileName: p.join(mangaMainDirectory!.path, "$chapterName.mp4"),
chapter: chapter,
concurrentDownloads: concurrentDownloads,
);
} else {
pageUrls = [PageUrl(videosUrls.first.url)];
@ -326,7 +328,7 @@ Future<void> downloadChapter(
});
} else {
savePageUrls();
await MDownloader(chapter: chapter, pageUrls: pages).download((progress) {
await MDownloader(chapter: chapter, pageUrls: pages, concurrentDownloads: concurrentDownloads).download((progress) {
setProgress(progress);
});
}

View file

@ -6,7 +6,7 @@ part of 'download_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadChapterHash() => r'd729e45de171814dada724bf6004aeca7ea85a0a';
String _$downloadChapterHash() => r'ab708c05c18d3449efbb8064f3488adb0e68b1f2';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/more/settings/downloads/providers/downloads_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:numberpicker/numberpicker.dart';
class DownloadsScreen extends ConsumerStatefulWidget {
const DownloadsScreen({super.key});
@ -15,6 +17,7 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
Widget build(BuildContext context) {
final saveAsCBZArchiveState = ref.watch(saveAsCBZArchiveStateProvider);
final onlyOnWifiState = ref.watch(onlyOnWifiStateProvider);
final concurrentDownloads = ref.watch(concurrentDownloadsStateProvider);
final l10n = l10nLocalizations(context);
return Scaffold(
appBar: AppBar(title: Text(l10n!.downloads)),
@ -35,6 +38,77 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
ref.read(saveAsCBZArchiveStateProvider.notifier).set(value);
},
),
ListTile(
onTap: () {
int currentIntValue = concurrentDownloads;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(context.l10n.concurrent_downloads),
content: StatefulBuilder(
builder:
(context, setState) => SizedBox(
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
NumberPicker(
value: currentIntValue,
minValue: 1,
maxValue: 255,
step: 1,
haptics: true,
textMapper: (numberText) => numberText,
onChanged:
(value) => setState(
() => currentIntValue = value,
),
),
],
),
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
Navigator.pop(context);
},
child: Text(
context.l10n.cancel,
style: TextStyle(color: context.primaryColor),
),
),
TextButton(
onPressed: () async {
ref
.read(
concurrentDownloadsStateProvider.notifier,
)
.set(currentIntValue);
Navigator.pop(context);
},
child: Text(
context.l10n.ok,
style: TextStyle(color: context.primaryColor),
),
),
],
),
],
);
},
);
},
title: Text(context.l10n.concurrent_downloads),
subtitle: Text(
"$concurrentDownloads",
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
],
),
),

View file

@ -65,3 +65,19 @@ class DownloadLocationState extends _$DownloadLocationState {
);
}
}
@riverpod
class ConcurrentDownloadsState extends _$ConcurrentDownloadsState {
@override
int build() {
return isar.settings.getSync(227)!.concurrentDownloads ?? 2;
}
void set(int value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..concurrentDownloads = value),
);
}
}

View file

@ -56,5 +56,22 @@ final downloadLocationStateProvider = AutoDisposeNotifierProvider<
);
typedef _$DownloadLocationState = AutoDisposeNotifier<(String, String)>;
String _$concurrentDownloadsStateHash() =>
r'665ec25af7d72e3345ce1ca96319419f11f121e6';
/// See also [ConcurrentDownloadsState].
@ProviderFor(ConcurrentDownloadsState)
final concurrentDownloadsStateProvider =
AutoDisposeNotifierProvider<ConcurrentDownloadsState, int>.internal(
ConcurrentDownloadsState.new,
name: r'concurrentDownloadsStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$concurrentDownloadsStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ConcurrentDownloadsState = AutoDisposeNotifier<int>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View file

@ -6,7 +6,7 @@ part of 'aniskip.dart';
// RiverpodGenerator
// **************************************************************************
String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c';
String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421';
/// See also [AniSkip].
@ProviderFor(AniSkip)

View file

@ -39,7 +39,7 @@ class M3u8Downloader {
required this.fileName,
this.headers,
required this.chapter,
this.concurrentDownloads = 15,
this.concurrentDownloads = 2,
});
void _log(String message) {

View file

@ -29,7 +29,7 @@ class MDownloader {
MDownloader({
required this.chapter,
required this.pageUrls,
this.concurrentDownloads = 15,
this.concurrentDownloads = 2,
});
void _log(String message) {

View file

@ -6,7 +6,7 @@ part of 'anilist.dart';
// RiverpodGenerator
// **************************************************************************
String _$anilistHash() => r'ddd07acc8d28d2aa95c942566109e9393ca9e5ed';
String _$anilistHash() => r'70e8cd537270a9054a1ef72de117fc7ad5545218';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -7,6 +7,8 @@ PODS:
- FlutterMacOS
- device_info_plus (0.0.1):
- FlutterMacOS
- file_picker (0.0.1):
- FlutterMacOS
- flutter_inappwebview_macos (0.0.1):
- FlutterMacOS
- OrderedSet (~> 6.0.3)
@ -63,6 +65,7 @@ DEPENDENCIES:
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
- flutter_qjs (from `Flutter/ephemeral/.symlinks/plugins/flutter_qjs/macos`)
- flutter_web_auth_2 (from `Flutter/ephemeral/.symlinks/plugins/flutter_web_auth_2/macos`)
@ -99,6 +102,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
device_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
file_picker:
:path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos
flutter_inappwebview_macos:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
flutter_qjs:
@ -149,6 +154,7 @@ SPEC CHECKSUMS:
audio_session: eaca2512cf2b39212d724f35d11f46180ad3a33e
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
flutter_qjs: cb2d0cba9deade1d03b89f6c432eac126f39482a
flutter_web_auth_2: 62b08da29f15a20fa63f144234622a1488d45b65