feature: crop borders (experimental)

This commit is contained in:
kodjomoustapha 2023-06-09 23:10:34 +01:00
parent c4346d8a06
commit 380310be55
14 changed files with 633 additions and 245 deletions

View file

@ -83,6 +83,8 @@ class Settings {
List<FilterScanlator>? filterScanlatorList;
bool? cropBorders;
Settings(
{this.id = 227,
this.displayType = DisplayType.compactGrid,
@ -118,7 +120,8 @@ class Settings {
this.pureBlackDarkMode = false,
this.downloadOnlyOnWifi = false,
this.saveAsCBZArchive = false,
this.downloadLocation = ""});
this.downloadLocation = "",
this.cropBorders = false});
}
enum DisplayType {

View file

@ -58,159 +58,164 @@ const SettingsSchema = CollectionSchema(
type: IsarType.objectList,
target: r'Cookie',
),
r'dateFormat': PropertySchema(
r'cropBorders': PropertySchema(
id: 7,
name: r'cropBorders',
type: IsarType.bool,
),
r'dateFormat': PropertySchema(
id: 8,
name: r'dateFormat',
type: IsarType.string,
),
r'defaultReaderMode': PropertySchema(
id: 8,
id: 9,
name: r'defaultReaderMode',
type: IsarType.byte,
enumMap: _SettingsdefaultReaderModeEnumValueMap,
),
r'displayType': PropertySchema(
id: 9,
id: 10,
name: r'displayType',
type: IsarType.byte,
enumMap: _SettingsdisplayTypeEnumValueMap,
),
r'doubleTapAnimationSpeed': PropertySchema(
id: 10,
id: 11,
name: r'doubleTapAnimationSpeed',
type: IsarType.long,
),
r'downloadLocation': PropertySchema(
id: 11,
id: 12,
name: r'downloadLocation',
type: IsarType.string,
),
r'downloadOnlyOnWifi': PropertySchema(
id: 12,
id: 13,
name: r'downloadOnlyOnWifi',
type: IsarType.bool,
),
r'filterScanlatorList': PropertySchema(
id: 13,
id: 14,
name: r'filterScanlatorList',
type: IsarType.objectList,
target: r'FilterScanlator',
),
r'flexColorSchemeBlendLevel': PropertySchema(
id: 14,
id: 15,
name: r'flexColorSchemeBlendLevel',
type: IsarType.double,
),
r'flexSchemeColorIndex': PropertySchema(
id: 15,
id: 16,
name: r'flexSchemeColorIndex',
type: IsarType.long,
),
r'incognitoMode': PropertySchema(
id: 16,
id: 17,
name: r'incognitoMode',
type: IsarType.bool,
),
r'libraryDownloadedChapters': PropertySchema(
id: 17,
id: 18,
name: r'libraryDownloadedChapters',
type: IsarType.bool,
),
r'libraryFilterMangasBookMarkedType': PropertySchema(
id: 18,
id: 19,
name: r'libraryFilterMangasBookMarkedType',
type: IsarType.long,
),
r'libraryFilterMangasDownloadType': PropertySchema(
id: 19,
id: 20,
name: r'libraryFilterMangasDownloadType',
type: IsarType.long,
),
r'libraryFilterMangasStartedType': PropertySchema(
id: 20,
id: 21,
name: r'libraryFilterMangasStartedType',
type: IsarType.long,
),
r'libraryFilterMangasUnreadType': PropertySchema(
id: 21,
id: 22,
name: r'libraryFilterMangasUnreadType',
type: IsarType.long,
),
r'libraryShowCategoryTabs': PropertySchema(
id: 22,
id: 23,
name: r'libraryShowCategoryTabs',
type: IsarType.bool,
),
r'libraryShowContinueReadingButton': PropertySchema(
id: 23,
id: 24,
name: r'libraryShowContinueReadingButton',
type: IsarType.bool,
),
r'libraryShowLanguage': PropertySchema(
id: 24,
id: 25,
name: r'libraryShowLanguage',
type: IsarType.bool,
),
r'libraryShowNumbersOfItems': PropertySchema(
id: 25,
id: 26,
name: r'libraryShowNumbersOfItems',
type: IsarType.bool,
),
r'onlyIncludePinnedSources': PropertySchema(
id: 26,
id: 27,
name: r'onlyIncludePinnedSources',
type: IsarType.bool,
),
r'personalReaderModeList': PropertySchema(
id: 27,
id: 28,
name: r'personalReaderModeList',
type: IsarType.objectList,
target: r'PersonalReaderMode',
),
r'pureBlackDarkMode': PropertySchema(
id: 28,
id: 29,
name: r'pureBlackDarkMode',
type: IsarType.bool,
),
r'relativeTimesTamps': PropertySchema(
id: 29,
id: 30,
name: r'relativeTimesTamps',
type: IsarType.long,
),
r'saveAsCBZArchive': PropertySchema(
id: 30,
id: 31,
name: r'saveAsCBZArchive',
type: IsarType.bool,
),
r'showNSFW': PropertySchema(
id: 31,
id: 32,
name: r'showNSFW',
type: IsarType.bool,
),
r'showPagesNumber': PropertySchema(
id: 32,
id: 33,
name: r'showPagesNumber',
type: IsarType.bool,
),
r'sortChapterList': PropertySchema(
id: 33,
id: 34,
name: r'sortChapterList',
type: IsarType.objectList,
target: r'SortChapter',
),
r'sortLibraryManga': PropertySchema(
id: 34,
id: 35,
name: r'sortLibraryManga',
type: IsarType.object,
target: r'SortLibraryManga',
),
r'themeIsDark': PropertySchema(
id: 35,
id: 36,
name: r'themeIsDark',
type: IsarType.bool,
),
r'userAgent': PropertySchema(
id: 36,
id: 37,
name: r'userAgent',
type: IsarType.string,
)
@ -443,56 +448,57 @@ void _settingsSerialize(
CookieSchema.serialize,
object.cookiesList,
);
writer.writeString(offsets[7], object.dateFormat);
writer.writeByte(offsets[8], object.defaultReaderMode.index);
writer.writeByte(offsets[9], object.displayType.index);
writer.writeLong(offsets[10], object.doubleTapAnimationSpeed);
writer.writeString(offsets[11], object.downloadLocation);
writer.writeBool(offsets[12], object.downloadOnlyOnWifi);
writer.writeBool(offsets[7], object.cropBorders);
writer.writeString(offsets[8], object.dateFormat);
writer.writeByte(offsets[9], object.defaultReaderMode.index);
writer.writeByte(offsets[10], object.displayType.index);
writer.writeLong(offsets[11], object.doubleTapAnimationSpeed);
writer.writeString(offsets[12], object.downloadLocation);
writer.writeBool(offsets[13], object.downloadOnlyOnWifi);
writer.writeObjectList<FilterScanlator>(
offsets[13],
offsets[14],
allOffsets,
FilterScanlatorSchema.serialize,
object.filterScanlatorList,
);
writer.writeDouble(offsets[14], object.flexColorSchemeBlendLevel);
writer.writeLong(offsets[15], object.flexSchemeColorIndex);
writer.writeBool(offsets[16], object.incognitoMode);
writer.writeBool(offsets[17], object.libraryDownloadedChapters);
writer.writeLong(offsets[18], object.libraryFilterMangasBookMarkedType);
writer.writeLong(offsets[19], object.libraryFilterMangasDownloadType);
writer.writeLong(offsets[20], object.libraryFilterMangasStartedType);
writer.writeLong(offsets[21], object.libraryFilterMangasUnreadType);
writer.writeBool(offsets[22], object.libraryShowCategoryTabs);
writer.writeBool(offsets[23], object.libraryShowContinueReadingButton);
writer.writeBool(offsets[24], object.libraryShowLanguage);
writer.writeBool(offsets[25], object.libraryShowNumbersOfItems);
writer.writeBool(offsets[26], object.onlyIncludePinnedSources);
writer.writeDouble(offsets[15], object.flexColorSchemeBlendLevel);
writer.writeLong(offsets[16], object.flexSchemeColorIndex);
writer.writeBool(offsets[17], object.incognitoMode);
writer.writeBool(offsets[18], object.libraryDownloadedChapters);
writer.writeLong(offsets[19], object.libraryFilterMangasBookMarkedType);
writer.writeLong(offsets[20], object.libraryFilterMangasDownloadType);
writer.writeLong(offsets[21], object.libraryFilterMangasStartedType);
writer.writeLong(offsets[22], object.libraryFilterMangasUnreadType);
writer.writeBool(offsets[23], object.libraryShowCategoryTabs);
writer.writeBool(offsets[24], object.libraryShowContinueReadingButton);
writer.writeBool(offsets[25], object.libraryShowLanguage);
writer.writeBool(offsets[26], object.libraryShowNumbersOfItems);
writer.writeBool(offsets[27], object.onlyIncludePinnedSources);
writer.writeObjectList<PersonalReaderMode>(
offsets[27],
offsets[28],
allOffsets,
PersonalReaderModeSchema.serialize,
object.personalReaderModeList,
);
writer.writeBool(offsets[28], object.pureBlackDarkMode);
writer.writeLong(offsets[29], object.relativeTimesTamps);
writer.writeBool(offsets[30], object.saveAsCBZArchive);
writer.writeBool(offsets[31], object.showNSFW);
writer.writeBool(offsets[32], object.showPagesNumber);
writer.writeBool(offsets[29], object.pureBlackDarkMode);
writer.writeLong(offsets[30], object.relativeTimesTamps);
writer.writeBool(offsets[31], object.saveAsCBZArchive);
writer.writeBool(offsets[32], object.showNSFW);
writer.writeBool(offsets[33], object.showPagesNumber);
writer.writeObjectList<SortChapter>(
offsets[33],
offsets[34],
allOffsets,
SortChapterSchema.serialize,
object.sortChapterList,
);
writer.writeObject<SortLibraryManga>(
offsets[34],
offsets[35],
allOffsets,
SortLibraryMangaSchema.serialize,
object.sortLibraryManga,
);
writer.writeBool(offsets[35], object.themeIsDark);
writer.writeString(offsets[36], object.userAgent);
writer.writeBool(offsets[36], object.themeIsDark);
writer.writeString(offsets[37], object.userAgent);
}
Settings _settingsDeserialize(
@ -527,54 +533,55 @@ Settings _settingsDeserialize(
allOffsets,
Cookie(),
),
dateFormat: reader.readStringOrNull(offsets[7]),
cropBorders: reader.readBoolOrNull(offsets[7]),
dateFormat: reader.readStringOrNull(offsets[8]),
defaultReaderMode: _SettingsdefaultReaderModeValueEnumMap[
reader.readByteOrNull(offsets[8])] ??
reader.readByteOrNull(offsets[9])] ??
ReaderMode.vertical,
displayType:
_SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offsets[9])] ??
_SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offsets[10])] ??
DisplayType.compactGrid,
doubleTapAnimationSpeed: reader.readLongOrNull(offsets[10]),
downloadLocation: reader.readStringOrNull(offsets[11]),
downloadOnlyOnWifi: reader.readBoolOrNull(offsets[12]),
flexColorSchemeBlendLevel: reader.readDoubleOrNull(offsets[14]),
flexSchemeColorIndex: reader.readLongOrNull(offsets[15]),
doubleTapAnimationSpeed: reader.readLongOrNull(offsets[11]),
downloadLocation: reader.readStringOrNull(offsets[12]),
downloadOnlyOnWifi: reader.readBoolOrNull(offsets[13]),
flexColorSchemeBlendLevel: reader.readDoubleOrNull(offsets[15]),
flexSchemeColorIndex: reader.readLongOrNull(offsets[16]),
id: id,
incognitoMode: reader.readBoolOrNull(offsets[16]),
libraryDownloadedChapters: reader.readBoolOrNull(offsets[17]),
libraryFilterMangasBookMarkedType: reader.readLongOrNull(offsets[18]),
libraryFilterMangasDownloadType: reader.readLongOrNull(offsets[19]),
libraryFilterMangasStartedType: reader.readLongOrNull(offsets[20]),
libraryFilterMangasUnreadType: reader.readLongOrNull(offsets[21]),
libraryShowCategoryTabs: reader.readBoolOrNull(offsets[22]),
libraryShowContinueReadingButton: reader.readBoolOrNull(offsets[23]),
libraryShowLanguage: reader.readBoolOrNull(offsets[24]),
libraryShowNumbersOfItems: reader.readBoolOrNull(offsets[25]),
onlyIncludePinnedSources: reader.readBoolOrNull(offsets[26]),
incognitoMode: reader.readBoolOrNull(offsets[17]),
libraryDownloadedChapters: reader.readBoolOrNull(offsets[18]),
libraryFilterMangasBookMarkedType: reader.readLongOrNull(offsets[19]),
libraryFilterMangasDownloadType: reader.readLongOrNull(offsets[20]),
libraryFilterMangasStartedType: reader.readLongOrNull(offsets[21]),
libraryFilterMangasUnreadType: reader.readLongOrNull(offsets[22]),
libraryShowCategoryTabs: reader.readBoolOrNull(offsets[23]),
libraryShowContinueReadingButton: reader.readBoolOrNull(offsets[24]),
libraryShowLanguage: reader.readBoolOrNull(offsets[25]),
libraryShowNumbersOfItems: reader.readBoolOrNull(offsets[26]),
onlyIncludePinnedSources: reader.readBoolOrNull(offsets[27]),
personalReaderModeList: reader.readObjectList<PersonalReaderMode>(
offsets[27],
offsets[28],
PersonalReaderModeSchema.deserialize,
allOffsets,
PersonalReaderMode(),
),
pureBlackDarkMode: reader.readBoolOrNull(offsets[28]),
relativeTimesTamps: reader.readLongOrNull(offsets[29]),
saveAsCBZArchive: reader.readBoolOrNull(offsets[30]),
showNSFW: reader.readBoolOrNull(offsets[31]),
showPagesNumber: reader.readBoolOrNull(offsets[32]),
pureBlackDarkMode: reader.readBoolOrNull(offsets[29]),
relativeTimesTamps: reader.readLongOrNull(offsets[30]),
saveAsCBZArchive: reader.readBoolOrNull(offsets[31]),
showNSFW: reader.readBoolOrNull(offsets[32]),
showPagesNumber: reader.readBoolOrNull(offsets[33]),
sortChapterList: reader.readObjectList<SortChapter>(
offsets[33],
offsets[34],
SortChapterSchema.deserialize,
allOffsets,
SortChapter(),
),
sortLibraryManga: reader.readObjectOrNull<SortLibraryManga>(
offsets[34],
offsets[35],
SortLibraryMangaSchema.deserialize,
allOffsets,
),
themeIsDark: reader.readBoolOrNull(offsets[35]),
userAgent: reader.readStringOrNull(offsets[36]),
themeIsDark: reader.readBoolOrNull(offsets[36]),
userAgent: reader.readStringOrNull(offsets[37]),
);
object.chapterFilterBookmarkedList =
reader.readObjectList<ChapterFilterBookmarked>(
@ -590,7 +597,7 @@ Settings _settingsDeserialize(
ChapterFilterUnread(),
);
object.filterScanlatorList = reader.readObjectList<FilterScanlator>(
offsets[13],
offsets[14],
FilterScanlatorSchema.deserialize,
allOffsets,
FilterScanlator(),
@ -650,37 +657,37 @@ P _settingsDeserializeProp<P>(
Cookie(),
)) as P;
case 7:
return (reader.readStringOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 8:
return (reader.readStringOrNull(offset)) as P;
case 9:
return (_SettingsdefaultReaderModeValueEnumMap[
reader.readByteOrNull(offset)] ??
ReaderMode.vertical) as P;
case 9:
case 10:
return (_SettingsdisplayTypeValueEnumMap[reader.readByteOrNull(offset)] ??
DisplayType.compactGrid) as P;
case 10:
return (reader.readLongOrNull(offset)) as P;
case 11:
return (reader.readStringOrNull(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 12:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readStringOrNull(offset)) as P;
case 13:
return (reader.readBoolOrNull(offset)) as P;
case 14:
return (reader.readObjectList<FilterScanlator>(
offset,
FilterScanlatorSchema.deserialize,
allOffsets,
FilterScanlator(),
)) as P;
case 14:
return (reader.readDoubleOrNull(offset)) as P;
case 15:
return (reader.readLongOrNull(offset)) as P;
return (reader.readDoubleOrNull(offset)) as P;
case 16:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 17:
return (reader.readBoolOrNull(offset)) as P;
case 18:
return (reader.readLongOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 19:
return (reader.readLongOrNull(offset)) as P;
case 20:
@ -688,7 +695,7 @@ P _settingsDeserializeProp<P>(
case 21:
return (reader.readLongOrNull(offset)) as P;
case 22:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readLongOrNull(offset)) as P;
case 23:
return (reader.readBoolOrNull(offset)) as P;
case 24:
@ -698,38 +705,40 @@ P _settingsDeserializeProp<P>(
case 26:
return (reader.readBoolOrNull(offset)) as P;
case 27:
return (reader.readBoolOrNull(offset)) as P;
case 28:
return (reader.readObjectList<PersonalReaderMode>(
offset,
PersonalReaderModeSchema.deserialize,
allOffsets,
PersonalReaderMode(),
)) as P;
case 28:
return (reader.readBoolOrNull(offset)) as P;
case 29:
return (reader.readLongOrNull(offset)) as P;
case 30:
return (reader.readBoolOrNull(offset)) as P;
case 30:
return (reader.readLongOrNull(offset)) as P;
case 31:
return (reader.readBoolOrNull(offset)) as P;
case 32:
return (reader.readBoolOrNull(offset)) as P;
case 33:
return (reader.readBoolOrNull(offset)) as P;
case 34:
return (reader.readObjectList<SortChapter>(
offset,
SortChapterSchema.deserialize,
allOffsets,
SortChapter(),
)) as P;
case 34:
case 35:
return (reader.readObjectOrNull<SortLibraryManga>(
offset,
SortLibraryMangaSchema.deserialize,
allOffsets,
)) as P;
case 35:
return (reader.readBoolOrNull(offset)) as P;
case 36:
return (reader.readBoolOrNull(offset)) as P;
case 37:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -1520,6 +1529,33 @@ extension SettingsQueryFilter
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> cropBordersIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'cropBorders',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition>
cropBordersIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'cropBorders',
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> cropBordersEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'cropBorders',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> dateFormatIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -3543,6 +3579,18 @@ extension SettingsQuerySortBy on QueryBuilder<Settings, Settings, QSortBy> {
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByCropBorders() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'cropBorders', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByCropBordersDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'cropBorders', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByDateFormat() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'dateFormat', Sort.asc);
@ -3898,6 +3946,18 @@ extension SettingsQuerySortThenBy
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByCropBorders() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'cropBorders', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByCropBordersDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'cropBorders', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByDateFormat() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'dateFormat', Sort.asc);
@ -4258,6 +4318,12 @@ extension SettingsQueryWhereDistinct
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByCropBorders() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'cropBorders');
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByDateFormat(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -4487,6 +4553,12 @@ extension SettingsQueryProperty
});
}
QueryBuilder<Settings, bool?, QQueryOperations> cropBordersProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'cropBorders');
});
}
QueryBuilder<Settings, String?, QQueryOperations> dateFormatProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'dateFormat');

View file

@ -57,70 +57,75 @@ Future<List<String>> downloadChapter(
});
if (pageUrls.isNotEmpty) {
for (var index = 0; index < pageUrls.length; index++) {
final path2 = Directory("${path1.path}downloads/");
final path4 = Directory(
"${path2.path}${manga.source} (${manga.lang!.toUpperCase()})/");
final path3 =
Directory("${path4.path}${manga.name!.replaceAll(regExp, '_')}/");
if (!(await path1.exists())) {
path1.create();
}
if (Platform.isAndroid) {
if (!(await File("${path1.path}" ".nomedia").exists())) {
File("${path1.path}" ".nomedia").create();
}
}
if (!(await path2.exists())) {
path2.create();
}
if (!(await path4.exists())) {
path4.create();
}
if (!(await path3.exists())) {
path3.create();
}
if ((await path.exists())) {
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: pageUrls[index],
headers: ref.watch(headersProvider(source: manga.source!)),
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory: Platform.isAndroid
? BaseDirectory.temporary
: BaseDirectory.applicationDocuments,
directory: 'Mangayomi/$finalPath',
updates: Updates.statusAndProgress,
allowPause: true,
requiresWiFi: onlyOnWifi));
}
} else {
path.create();
if (await File("${path.path}" "${padIndex(index + 1)}.jpg").exists()) {
} else {
tasks.add(DownloadTask(
taskId: pageUrls[index],
headers: ref.watch(headersProvider(source: manga.source!)),
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory: Platform.isAndroid
? BaseDirectory.temporary
: BaseDirectory.applicationDocuments,
directory: 'Mangayomi/$finalPath',
updates: Updates.statusAndProgress,
allowPause: true,
requiresWiFi: onlyOnWifi));
}
}
}
bool cbzFileExist =
await File("${mangaDir!.path}${chapter.name}.cbz").exists() &&
ref.watch(saveAsCBZArchiveStateProvider);
if (tasks.isEmpty && pageUrls.isNotEmpty || cbzFileExist) {
if (!cbzFileExist) {
for (var index = 0; index < pageUrls.length; index++) {
final path2 = Directory("${path1.path}downloads/");
final path4 = Directory(
"${path2.path}${manga.source} (${manga.lang!.toUpperCase()})/");
final path3 =
Directory("${path4.path}${manga.name!.replaceAll(regExp, '_')}/");
if (!(await path1.exists())) {
path1.create();
}
if (Platform.isAndroid) {
if (!(await File("${path1.path}" ".nomedia").exists())) {
File("${path1.path}" ".nomedia").create();
}
}
if (!(await path2.exists())) {
path2.create();
}
if (!(await path4.exists())) {
path4.create();
}
if (!(await path3.exists())) {
path3.create();
}
if ((await path.exists())) {
if (await File("${path.path}" "${padIndex(index + 1)}.jpg")
.exists()) {
} else {
tasks.add(DownloadTask(
taskId: pageUrls[index],
headers: ref.watch(headersProvider(source: manga.source!)),
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory: Platform.isAndroid
? BaseDirectory.temporary
: BaseDirectory.applicationDocuments,
directory: 'Mangayomi/$finalPath',
updates: Updates.statusAndProgress,
allowPause: true,
requiresWiFi: onlyOnWifi));
}
} else {
path.create();
if (await File("${path.path}" "${padIndex(index + 1)}.jpg")
.exists()) {
} else {
tasks.add(DownloadTask(
taskId: pageUrls[index],
headers: ref.watch(headersProvider(source: manga.source!)),
url: pageUrls[index],
filename: "${padIndex(index + 1)}.jpg",
baseDirectory: Platform.isAndroid
? BaseDirectory.temporary
: BaseDirectory.applicationDocuments,
directory: 'Mangayomi/$finalPath',
updates: Updates.statusAndProgress,
allowPause: true,
requiresWiFi: onlyOnWifi));
}
}
}
}
if (tasks.isEmpty && pageUrls.isNotEmpty) {
final model = Download(
succeeded: 0,
failed: 0,
@ -130,12 +135,6 @@ Future<List<String>> downloadChapter(
isStartDownload: false,
chapterId: chapter.id);
if (!cbzFileExist) {
await ref.watch(convertToCBZProvider(
path.path, mangaDir.path, chapter.name!, pageUrls)
.future);
}
isar.writeTxnSync(() {
isar.downloads.putSync(model..chapter.value = chapter);
});

View file

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

View file

@ -39,25 +39,18 @@ class ImageViewCenter extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return isLocale
? archiveImage != null
? ExtendedImage.memory(
archiveImage!,
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
mode: ExtendedImageMode.gesture,
initGestureConfigHandler: initGestureConfigHandler,
onDoubleTap: onDoubleTap,
loadStateChanged: loadStateChanged,
)
: ExtendedImage.file(
File("${path.path}" "${padIndex(index + 1)}.jpg"),
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
mode: ExtendedImageMode.gesture,
initGestureConfigHandler: initGestureConfigHandler,
onDoubleTap: onDoubleTap,
loadStateChanged: loadStateChanged,
)
? ExtendedImage.memory(
archiveImage != null
? archiveImage!
: File("${path.path}" "${padIndex(index + 1)}.jpg")
.readAsBytesSync(),
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
mode: ExtendedImageMode.gesture,
initGestureConfigHandler: initGestureConfigHandler,
onDoubleTap: onDoubleTap,
loadStateChanged: loadStateChanged,
)
: ExtendedImage.network(
url,
headers: ref.watch(headersProvider(source: source)),

View file

@ -45,18 +45,15 @@ class ImageViewVertical extends ConsumerWidget {
height: MediaQuery.of(context).padding.top,
),
isLocale
? archiveImage != null
? ExtendedImage.memory(
archiveImage!,
fit: BoxFit.contain,
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
)
: ExtendedImage.file(
fit: BoxFit.contain,
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
File('${path.path}${padIndex(index + 1)}.jpg'))
? ExtendedImage.memory(
archiveImage != null
? archiveImage!
: File('${path.path}${padIndex(index + 1)}.jpg')
.readAsBytesSync(),
fit: BoxFit.contain,
clearMemoryCacheWhenDispose: true,
enableMemoryCache: false,
)
: ExtendedImage.network(url,
headers: ref.watch(headersProvider(source: source)),
handleLoadingProgress: true,

View file

@ -15,6 +15,8 @@ import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/history.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/manga/reader/providers/auto_crop_image_provider.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/sources/utils/utils.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
@ -27,6 +29,7 @@ import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provi
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/utils/reg_exp_matcher.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
@ -319,6 +322,48 @@ class _MangaChapterPageGalleryState
}
}
Future _cropImageFuture(Uint8List? image, String? url) async {
final cropImage = await ref.watch(autoCropImageProvider(
url,
image,
).future);
if (cropImage != null) {
cropImagesList.add(cropImage);
} else {
cropImagesList.add(null);
}
}
List<Uint8List?> cropImagesList = [];
bool isOk = false;
_cropImage() async {
List<Future> futures = [];
if (!isOk) {
isOk = true;
if (widget.archiveImages.isNotEmpty) {
for (var image in widget.archiveImages) {
futures.add(_cropImageFuture(image, null));
}
} else if (widget.isLocaleList.contains(true)) {
for (var i = 0; i < widget.isLocaleList.length; i++) {
if (widget.isLocaleList[i] == true) {
Uint8List? image = File('${widget.path.path}${padIndex(i + 1)}.jpg')
.readAsBytesSync();
futures.add(_cropImageFuture(image, null));
} else {
futures.add(_cropImageFuture(null, null));
}
}
} else {
for (var url in widget.url) {
futures.add(_cropImageFuture(null, url));
}
}
await Future.wait(futures);
setState(() {});
}
}
ReaderMode? _selectedValue;
bool _isView = false;
Alignment _scalePosition = Alignment.center;
@ -427,7 +472,7 @@ class _MangaChapterPageGalleryState
Color _backgroundColor(BuildContext context) =>
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.9);
Widget _showMore() {
Widget _showMore(bool cropBorders) {
bool isNotFirstChapter = widget.readerController.getChapterIndex() + 1 !=
widget.readerController.getChaptersLength();
bool isNotLastChapter = widget.readerController.getChapterIndex() != 0;
@ -707,7 +752,11 @@ class _MangaChapterPageGalleryState
],
),
IconButton(
onPressed: () {},
onPressed: () {
ref
.read(cropBordersStateProvider.notifier)
.set(!cropBorders);
},
icon: const Icon(
Icons.screen_rotation,
),
@ -906,6 +955,8 @@ class _MangaChapterPageGalleryState
double _imageDetailY = 0;
@override
Widget build(BuildContext context) {
final cropBorders = ref.watch(cropBordersStateProvider);
_cropImage();
return WillPopScope(
onWillPop: () async {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
@ -971,9 +1022,7 @@ class _MangaChapterPageGalleryState
scrollDirection: _scrollDirection,
reverse: _isReversHorizontal,
physics: const ClampingScrollPhysics(),
preloadPagesCount: _isZoom
? 0
: widget.readerController.getPageLength(widget.url),
preloadPagesCount: _isZoom ? 0 : 6,
canScrollPage: (GestureDetails? gestureDetails) {
return gestureDetails != null
? !(gestureDetails.totalScale! > 1.0)
@ -981,9 +1030,12 @@ class _MangaChapterPageGalleryState
},
itemBuilder: (BuildContext context, int index) {
return ImageViewCenter(
archiveImage: widget.archiveImages.isNotEmpty
? widget.archiveImages[index]
: null,
archiveImage:
cropImagesList.isNotEmpty && cropBorders == true
? cropImagesList[index]
: widget.archiveImages.isNotEmpty
? widget.archiveImages[index]
: null,
titleManga: widget.readerController.getMangaName(),
source: widget.readerController
.getSourceName()
@ -1129,9 +1181,12 @@ class _MangaChapterPageGalleryState
_doubleClickAnimationController.forward();
},
isLocale: _isReversHorizontal
? widget.isLocaleList.reversed.toList()[index]
: widget.isLocaleList[index],
isLocale: cropImagesList.isNotEmpty &&
cropBorders == true
? true
: _isReversHorizontal
? widget.isLocaleList.reversed.toList()[index]
: widget.isLocaleList[index],
);
},
itemCount:
@ -1139,7 +1194,7 @@ class _MangaChapterPageGalleryState
onPageChanged: _onPageChanged)),
_gestureRightLeft(),
_gestureTopBottom(),
_showMore(),
_showMore(cropBorders),
_showPage(),
],
),

View file

@ -0,0 +1,115 @@
import 'dart:io';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'auto_crop_image_provider.g.dart';
@riverpod
Future<Uint8List?> autoCropImage(
AutoCropImageRef ref, String? url, Uint8List? data) async {
Uint8List? oldImage;
Uint8List? newImage;
String path = "";
File? cachedImage;
if (url != null) {
cachedImage = await getCachedImageFile(url);
}
if (cachedImage != null) {
path = cachedImage.path;
}
if (path.isNotEmpty) {
oldImage = File(path).readAsBytesSync();
} else if (data != null) {
oldImage = data;
}
if (oldImage != null) {
newImage = await compute(autocropImageIsolate, oldImage);
}
return newImage;
}
Future<Uint8List?> autocropImageIsolate(List<int> data) async {
Image? croppedImage;
Image? image = decodeImage(data);
final old = image;
image = copyCrop(image!, 0, 0, image.width, image.height);
int left = 0;
int top = 0;
int right = image.width - 1;
int bottom = image.height - 1;
// Find left coordinate
for (int x = 0; x < image.width; x++) {
bool stop = false;
for (int y = 0; y < image.height; y++) {
if (image.getPixel(x, y) != getColor(255, 255, 255, 255)) {
stop = true;
break;
}
}
if (stop) {
left = x;
break;
}
}
// Find top coordinate
for (int y = 0; y < image.height; y++) {
bool stop = false;
for (int x = 0; x < image.width; x++) {
if (image.getPixel(x, y) != getColor(255, 255, 255, 255)) {
stop = true;
break;
}
}
if (stop) {
top = y;
break;
}
}
// Find right coordinate
for (int x = image.width - 1; x >= 0; x--) {
bool stop = false;
for (int y = 0; y < image.height; y++) {
if (image.getPixel(x, y) != getColor(255, 255, 255, 255)) {
stop = true;
break;
}
}
if (stop) {
right = x;
break;
}
}
// Find bottom coordinate
for (int y = image.height - 1; y >= 0; y--) {
bool stop = false;
for (int x = 0; x < image.width; x++) {
if (image.getPixel(x, y) != getColor(255, 255, 255, 255)) {
stop = true;
break;
}
}
if (stop) {
bottom = y;
break;
}
}
// Crop the image
croppedImage = copyCrop(
image,
left,
top,
right - left + 1,
bottom - top + 1,
);
if (old != croppedImage) {
return encodeJpg(croppedImage) as Uint8List;
}
return null;
}

View file

@ -0,0 +1,121 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'auto_crop_image_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$autoCropImageHash() => r'aae86e74203def1027400fa81e06e8e10344487d';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef AutoCropImageRef = AutoDisposeFutureProviderRef<Uint8List?>;
/// See also [autoCropImage].
@ProviderFor(autoCropImage)
const autoCropImageProvider = AutoCropImageFamily();
/// See also [autoCropImage].
class AutoCropImageFamily extends Family<AsyncValue<Uint8List?>> {
/// See also [autoCropImage].
const AutoCropImageFamily();
/// See also [autoCropImage].
AutoCropImageProvider call(
String? url,
Uint8List? data,
) {
return AutoCropImageProvider(
url,
data,
);
}
@override
AutoCropImageProvider getProviderOverride(
covariant AutoCropImageProvider provider,
) {
return call(
provider.url,
provider.data,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'autoCropImageProvider';
}
/// See also [autoCropImage].
class AutoCropImageProvider extends AutoDisposeFutureProvider<Uint8List?> {
/// See also [autoCropImage].
AutoCropImageProvider(
this.url,
this.data,
) : super.internal(
(ref) => autoCropImage(
ref,
url,
data,
),
from: autoCropImageProvider,
name: r'autoCropImageProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$autoCropImageHash,
dependencies: AutoCropImageFamily._dependencies,
allTransitiveDependencies:
AutoCropImageFamily._allTransitiveDependencies,
);
final String? url;
final Uint8List? data;
@override
bool operator ==(Object other) {
return other is AutoCropImageProvider &&
other.url == url &&
other.data == data;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, url.hashCode);
hash = _SystemHash.combine(hash, data.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View file

@ -47,3 +47,18 @@ class DoubleTapAnimationSpeedState extends _$DoubleTapAnimationSpeedState {
isar.settings.putSync(settings!..doubleTapAnimationSpeed = value));
}
}
@riverpod
class CropBordersState extends _$CropBordersState {
@override
bool build() {
return isar.settings.getSync(227)!.cropBorders ?? false;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..cropBorders = value));
}
}

View file

@ -57,4 +57,20 @@ final doubleTapAnimationSpeedStateProvider =
);
typedef _$DoubleTapAnimationSpeedState = AutoDisposeNotifier<int>;
String _$cropBordersStateHash() => r'65d636df7bebd9fb15b3915c1044c61f3479e806';
/// See also [CropBordersState].
@ProviderFor(CropBordersState)
final cropBordersStateProvider =
AutoDisposeNotifierProvider<CropBordersState, bool>.internal(
CropBordersState.new,
name: r'cropBordersStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$cropBordersStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$CropBordersState = AutoDisposeNotifier<bool>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View file

@ -13,6 +13,7 @@ class ReaderScreen extends ConsumerWidget {
final defaultReadingMode = ref.watch(defaultReadingModeStateProvider);
final animatePageTransitions =
ref.watch(animatePageTransitionsStateProvider);
final cropBorders = ref.watch(cropBordersStateProvider);
final doubleTapAnimationSpeed =
ref.watch(doubleTapAnimationSpeedStateProvider);
return Scaffold(
@ -150,6 +151,14 @@ class ReaderScreen extends ConsumerWidget {
.read(animatePageTransitionsStateProvider.notifier)
.set(value);
}),
SwitchListTile(
value: cropBorders,
title: const Text("Crop borders"),
onChanged: (value) {
ref
.read(cropBordersStateProvider.notifier)
.set(value);
}),
],
),
);

View file

@ -438,14 +438,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
@ -590,13 +582,13 @@ packages:
source: hosted
version: "4.0.2"
image:
dependency: transitive
dependency: "direct main"
description:
name: image
sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227"
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
url: "https://pub.dev"
source: hosted
version: "4.0.15"
version: "3.3.0"
intl:
dependency: "direct main"
description:

View file

@ -63,6 +63,7 @@ dependencies:
archive: ^3.3.7
file_picker: ^5.3.0
path_provider: ^2.0.15
image: ^3.3.0
# The following adds the Cupertino Icons font to your application.
@ -74,22 +75,22 @@ dev_dependencies:
sdk: flutter
build_runner: ^2.4.5
riverpod_generator: ^2.2.3
flutter_launcher_icons: ^0.13.1
# flutter_launcher_icons: ^0.13.1
isar_generator: 3.1.0+1
flutter_lints: ^2.0.1
flutter_launcher_icons:
android: "launcher_icon"
ios: true
remove_alpha_ios: true
image_path: "assets/mangayomi_logo.png"
min_sdk_android: 24
windows:
generate: true
image_path: "assets/mangayomi_logo.png"
macos:
generate: true
image_path: "assets/mangayomi_logo.png"
# flutter_launcher_icons:
# android: "launcher_icon"
# ios: true
# remove_alpha_ios: true
# image_path: "assets/mangayomi_logo.png"
# min_sdk_android: 24
# windows:
# generate: true
# image_path: "assets/mangayomi_logo.png"
# macos:
# generate: true
# image_path: "assets/mangayomi_logo.png"
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is