mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-07-26 19:22:19 +00:00
New Setting: Mark duplicate chapter as read
When this setting is turned on, the app will automatically mark chapters as read if they have the same chapter number as a chapter you've already read. This is useful for sources where: - multiple scanlators upload the same chapter number - duplicate entries appear due to different naming formats - a series has repeated or alternative releases of the same chapter With the setting enabled, you won’t have to manually mark each duplicate as read.
This commit is contained in:
parent
85f154d738
commit
4a44dd6658
29 changed files with 822 additions and 555 deletions
|
|
@ -537,6 +537,7 @@
|
|||
"video_audio_info": "Preferred languages, pitch correction, audio channels",
|
||||
"player": "Player",
|
||||
"markEpisodeAsSeenSetting": "At what point to mark the episode as seen",
|
||||
"mark_duplicate_chapters_read": "Mark duplicate chapter numbers as read",
|
||||
"default_skip_intro_length": "Default Skip intro length",
|
||||
"default_playback_speed_length": "Default Playback speed length",
|
||||
"updateProgressAfterReading": "Update progress after reading",
|
||||
|
|
|
|||
|
|
@ -2513,6 +2513,12 @@ abstract class AppLocalizations {
|
|||
/// **'At what point to mark the episode as seen'**
|
||||
String get markEpisodeAsSeenSetting;
|
||||
|
||||
/// No description provided for @mark_duplicate_chapters_read.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mark duplicate chapter numbers as read'**
|
||||
String get mark_duplicate_chapters_read;
|
||||
|
||||
/// No description provided for @default_skip_intro_length.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,10 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'في أي نقطة لوضع علامة على الحلقة كمشاهدة';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'طول تخطي المقدمة الافتراضي';
|
||||
|
||||
|
|
|
|||
|
|
@ -1348,6 +1348,10 @@ class AppLocalizationsAs extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'খণ্ডটো কেতিয়া দেখা বুলি চিহ্নিত কৰিব';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'ডিফল্ট ইনট্ৰ\' এৰি দিয়াৰ দৈৰ্ঘ্য';
|
||||
|
||||
|
|
|
|||
|
|
@ -1352,6 +1352,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'Zu welchem Zeitpunkt die Episode als gesehen markieren';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Standardlänge für Intro überspringen';
|
||||
|
|
|
|||
|
|
@ -1347,6 +1347,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'At what point to mark the episode as seen';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'Default Skip intro length';
|
||||
|
||||
|
|
|
|||
|
|
@ -1357,6 +1357,10 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'En qué punto marcar el episodio como visto';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Duración predeterminada para saltar la introducción';
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'À quel moment marquer l\'épisode comme vu';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Longueur par défaut du passage de l\'intro';
|
||||
|
|
|
|||
|
|
@ -1347,6 +1347,10 @@ class AppLocalizationsHi extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'एपिसोड को कब देखा गया के रूप में चिह्नित करना है';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'डिफ़ॉल्ट परिचय छोड़ने की अवधि';
|
||||
|
||||
|
|
|
|||
|
|
@ -1351,6 +1351,10 @@ class AppLocalizationsId extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'Pada titik mana menandai episode sebagai terlihat';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'Panjang lewati intro default';
|
||||
|
||||
|
|
|
|||
|
|
@ -1358,6 +1358,10 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'In quale momento contrassegnare l\'episodio come visto';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Durata predefinita per saltare l\'introduzione';
|
||||
|
|
|
|||
|
|
@ -1332,6 +1332,10 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||
@override
|
||||
String get markEpisodeAsSeenSetting => 'エピソードを視聴済みにするタイミング';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'デフォルトイントロスキップ長';
|
||||
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,10 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'Em que ponto marcar o episódio como visto';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Duração padrão para pular a introdução';
|
||||
|
|
|
|||
|
|
@ -1368,6 +1368,10 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'В какой момент отметить эпизод как просмотренный';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length =>
|
||||
'Стандартная длина пропуска вступления';
|
||||
|
|
|
|||
|
|
@ -1347,6 +1347,10 @@ class AppLocalizationsTh extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'กำหนดจุดที่จะทำเครื่องหมายตอนว่าดูแล้ว';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'ค่าพื้นฐานความยาวการเข้าอินโทร';
|
||||
|
||||
|
|
|
|||
|
|
@ -1349,6 +1349,10 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||
String get markEpisodeAsSeenSetting =>
|
||||
'Bölümün izlendiği olarak işaretleneceği nokta';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => 'Varsayılan Giriş Atla süresi';
|
||||
|
||||
|
|
|
|||
|
|
@ -1316,6 +1316,10 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get markEpisodeAsSeenSetting => '标记剧集为已看的时间点';
|
||||
|
||||
@override
|
||||
String get mark_duplicate_chapters_read =>
|
||||
'Mark duplicate chapter numbers as read';
|
||||
|
||||
@override
|
||||
String get default_skip_intro_length => '默认跳过介绍长度';
|
||||
|
||||
|
|
|
|||
|
|
@ -336,6 +336,8 @@ class Settings {
|
|||
|
||||
bool? showPageGaps;
|
||||
|
||||
bool? autoReadDuplicateChapters;
|
||||
|
||||
bool? invertColors;
|
||||
|
||||
bool? grayscale;
|
||||
|
|
@ -513,6 +515,7 @@ class Settings {
|
|||
this.keepScreenOnReader = true,
|
||||
this.webtoonSidePadding = 0,
|
||||
this.showPageGaps = true,
|
||||
this.autoReadDuplicateChapters = false,
|
||||
this.invertColors = false,
|
||||
this.grayscale = false,
|
||||
this.readerBrightness = 0.0,
|
||||
|
|
@ -796,6 +799,7 @@ class Settings {
|
|||
keepScreenOnReader = json['keepScreenOnReader'];
|
||||
webtoonSidePadding = json['webtoonSidePadding'];
|
||||
showPageGaps = json['showPageGaps'];
|
||||
autoReadDuplicateChapters = json['autoReadDuplicateChapters'];
|
||||
invertColors = json['invertColors'];
|
||||
grayscale = json['grayscale'];
|
||||
readerBrightness = json['readerBrightness']?.toDouble();
|
||||
|
|
@ -985,6 +989,7 @@ class Settings {
|
|||
'keepScreenOnReader': keepScreenOnReader,
|
||||
'webtoonSidePadding': webtoonSidePadding,
|
||||
'showPageGaps': showPageGaps,
|
||||
'autoReadDuplicateChapters': autoReadDuplicateChapters,
|
||||
'invertColors': invertColors,
|
||||
'grayscale': grayscale,
|
||||
'readerBrightness': readerBrightness,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -59,7 +59,7 @@ final class AnimeStreamControllerProvider
|
|||
}
|
||||
|
||||
String _$animeStreamControllerHash() =>
|
||||
r'0a85358dcd4e3d7823c253ffbeccce8cf2612175';
|
||||
r'fd3e6030dccc06be7ccd4b9564476a3587de401f';
|
||||
|
||||
final class AnimeStreamControllerFamily extends $Family
|
||||
with
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ final class UpdateMangaDetailProvider
|
|||
}
|
||||
}
|
||||
|
||||
String _$updateMangaDetailHash() => r'd29f8980f7545dc57a112fd57ea5c045b36b34fe';
|
||||
String _$updateMangaDetailHash() => r'7a5595f307535c2fed5b174c98ce56c0fdedf5d6';
|
||||
|
||||
final class UpdateMangaDetailFamily extends $Family
|
||||
with
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/misc.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
|
||||
import 'package:mangayomi/utils/chapter_recognition.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/mixins/chapter_reader_settings_mixin.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/mixins/chapter_controller_mixin.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
|
|
@ -204,17 +206,45 @@ class ReaderController extends _$ReaderController
|
|||
..chapterId = chapter.id
|
||||
..index = isRead ? 0 : newIndex,
|
||||
);
|
||||
final autoReadDuplChap = ref.read(autoReadDuplicateChaptersStateProvider);
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
// When the chapter is finished, mark every other scanlation of the same
|
||||
// chapter number as read too, in the same transaction.
|
||||
final List<Chapter> siblings = [];
|
||||
if (isRead && autoReadDuplChap) {
|
||||
final manga = chapter.manga.value;
|
||||
if (manga != null) {
|
||||
final chapterNumber = ChapterRecognition().parseChapterNumber(
|
||||
manga.name!,
|
||||
chapter.name!,
|
||||
);
|
||||
for (final c in manga.chapters) {
|
||||
if (c.id == chapter.id || (c.isRead ?? false)) continue;
|
||||
final n = ChapterRecognition().parseChapterNumber(
|
||||
manga.name!,
|
||||
c.name!,
|
||||
);
|
||||
if (n == chapterNumber) {
|
||||
c.isRead = true;
|
||||
c.lastPageRead = '1';
|
||||
c.updatedAt = now;
|
||||
siblings.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final chap = chapter;
|
||||
isar.writeTxnSync(() {
|
||||
isar.settings.putSync(
|
||||
getIsarSetting()
|
||||
..chapterPageIndexList = chapterPageIndexs
|
||||
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
||||
..updatedAt = now,
|
||||
);
|
||||
chap.isRead = isRead;
|
||||
chap.lastPageRead = isRead ? '1' : (newIndex + 1).toString();
|
||||
chap.updatedAt = DateTime.now().millisecondsSinceEpoch;
|
||||
chap.updatedAt = now;
|
||||
isar.chapters.putSync(chap);
|
||||
if (siblings.isNotEmpty) isar.chapters.putAllSync(siblings);
|
||||
});
|
||||
onSettingsMutated();
|
||||
if (isRead) {
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ final class ReaderControllerProvider
|
|||
}
|
||||
}
|
||||
|
||||
String _$readerControllerHash() => r'adab728fa21939302d0f928b11be204e8e8a0527';
|
||||
String _$readerControllerHash() => r'f129a22656f06082d217f2364697f3fc68668b64';
|
||||
|
||||
final class ReaderControllerFamily extends $Family
|
||||
with
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ final class CheckForUpdateProvider
|
|||
}
|
||||
}
|
||||
|
||||
String _$checkForUpdateHash() => r'644316334ac3e95d37f54d7197d744c9de1260b6';
|
||||
String _$checkForUpdateHash() => r'8064f08768f4d98997201a2f07ba160c721a6a3f';
|
||||
|
||||
final class CheckForUpdateFamily extends $Family
|
||||
with
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ final class RestoreTachiBkBackupProvider
|
|||
}
|
||||
|
||||
String _$restoreTachiBkBackupHash() =>
|
||||
r'54434eaa9dc875d03ffb1dcf62ad2d7971649d61';
|
||||
r'0f121d94021eb9a56d0689240dbcefd49f5a2a79';
|
||||
|
||||
final class RestoreTachiBkBackupFamily extends $Family
|
||||
with $FunctionalFamilyOverride<void, (String, BackupType)> {
|
||||
|
|
|
|||
|
|
@ -526,6 +526,26 @@ class ShowPageGapsState extends _$ShowPageGapsState {
|
|||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class AutoReadDuplicateChaptersState extends _$AutoReadDuplicateChaptersState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.autoReadDuplicateChapters ?? false;
|
||||
}
|
||||
|
||||
void set(bool value) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = value;
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(
|
||||
settings!
|
||||
..autoReadDuplicateChapters = value
|
||||
..updatedAt = DateTime.now().millisecondsSinceEpoch,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class InvertColorsState extends _$InvertColorsState {
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -1338,6 +1338,60 @@ abstract class _$ShowPageGapsState extends $Notifier<bool> {
|
|||
}
|
||||
}
|
||||
|
||||
@ProviderFor(AutoReadDuplicateChaptersState)
|
||||
final autoReadDuplicateChaptersStateProvider =
|
||||
AutoReadDuplicateChaptersStateProvider._();
|
||||
|
||||
final class AutoReadDuplicateChaptersStateProvider
|
||||
extends $NotifierProvider<AutoReadDuplicateChaptersState, bool> {
|
||||
AutoReadDuplicateChaptersStateProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'autoReadDuplicateChaptersStateProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$autoReadDuplicateChaptersStateHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AutoReadDuplicateChaptersState create() => AutoReadDuplicateChaptersState();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$autoReadDuplicateChaptersStateHash() =>
|
||||
r'288816e17fefa753c690cc43a40cfbcf5352b9df';
|
||||
|
||||
abstract class _$AutoReadDuplicateChaptersState extends $Notifier<bool> {
|
||||
bool build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(InvertColorsState)
|
||||
final invertColorsStateProvider = InvertColorsStateProvider._();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class ReaderScreen extends ConsumerWidget {
|
|||
|
||||
final cropBorders = ref.watch(cropBordersStateProvider);
|
||||
final keepScreenOn = ref.watch(keepScreenOnReaderStateProvider);
|
||||
final autoReadDuplChap = ref.watch(autoReadDuplicateChaptersStateProvider);
|
||||
final showPageGaps = ref.watch(showPageGapsStateProvider);
|
||||
final webtoonSidePadding = ref.watch(webtoonSidePaddingStateProvider);
|
||||
final navigationLayout = ref.watch(readerNavigationLayoutStateProvider);
|
||||
|
|
@ -411,6 +412,15 @@ class ReaderScreen extends ConsumerWidget {
|
|||
ref.read(showPageGapsStateProvider.notifier).set(value);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
value: autoReadDuplChap,
|
||||
title: Text(context.l10n.mark_duplicate_chapters_read),
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(autoReadDuplicateChaptersStateProvider.notifier)
|
||||
.set(value);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.l10n.webtoon_side_padding),
|
||||
subtitle: Slider(
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ final class NovelReaderControllerProvider
|
|||
}
|
||||
|
||||
String _$novelReaderControllerHash() =>
|
||||
r'9133fe86e1c4bbdd47d9fa38e75310ecb8ede325';
|
||||
r'c9171314d6c24d3f7a88bd7a258ab409f769dbb5';
|
||||
|
||||
final class NovelReaderControllerFamily extends $Family
|
||||
with
|
||||
|
|
|
|||
Loading…
Reference in a new issue