added preferences

This commit is contained in:
Schnitzel5 2025-08-22 00:13:24 +02:00
parent a1ada0e03e
commit 03108ae701
8 changed files with 332 additions and 52 deletions

View file

@ -52,6 +52,7 @@ class MihonExtensionService implements ExtensionService {
"method": "getPopular$name",
"page": page + 1,
"search": "",
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -86,6 +87,7 @@ class MihonExtensionService implements ExtensionService {
"method": "getLatest$name",
"page": page + 1,
"search": "",
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -120,7 +122,8 @@ class MihonExtensionService implements ExtensionService {
"method": "getSearch$name",
"page": max(1, page),
"search": query,
// "filterList$name": _convertFilters(filters),
// "filterList": _convertFilters(filters),
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -155,6 +158,7 @@ class MihonExtensionService implements ExtensionService {
"method": "getDetails$name",
if (source.itemType == ItemType.manga) "mangaData": {"url": url},
if (source.itemType == ItemType.anime) "animeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -189,6 +193,7 @@ class MihonExtensionService implements ExtensionService {
: "getChapterList",
if (source.itemType == ItemType.manga) "mangaData": {"url": url},
if (source.itemType == ItemType.anime) "animeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -214,6 +219,7 @@ class MihonExtensionService implements ExtensionService {
body: jsonEncode({
"method": "getPageList",
"chapterData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -228,6 +234,7 @@ class MihonExtensionService implements ExtensionService {
body: jsonEncode({
"method": "getVideoList",
"episodeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
@ -288,37 +295,44 @@ class MihonExtensionService implements ExtensionService {
@override
List<SourcePreference> getSourcePreferences() {
return [];
if (source.preferenceList == null) {
return [];
}
final data = jsonDecode(source.preferenceList!) as List;
return data.map((e) => SourcePreference.fromJson(e)).toList();
}
List<dynamic> _convertFilters(List<dynamic> filters) {
return filters.expand((e) sync* {
if (e is TextFilter) {
yield {"name": e.name, "state": e.state};
yield {"name": e.name, "stateString": e.state, "type": "TextFilter"};
} else if (e is GroupFilter) {
yield {
"name": e.name,
"state": e.state.expand((e) sync* {
"stateList": e.state.expand((e) sync* {
if (e is CheckBoxFilter) {
yield {"name": e.name, "id": e.value, "state": e.state};
yield {
"name": e.name,
"stateBoolean": e.state,
"type": "CheckBoxFilter",
};
} else if (e is TriStateFilter) {
yield {
"name": e.name,
"id": e.value,
"state": e.state,
"included": e.state == 1,
"ignored": e.state == 0,
"excluded": e.state == 2,
"stateInt": e.state,
"type": "TriStateFilter",
};
}
}).toList(),
"type": "GroupFilter",
};
} else if (e is SelectFilter) {
yield {"name": e.name, "state": e.state};
yield {"name": e.name, "stateInt": e.state, "type": "SelectFilter"};
} else if (e is SortFilter) {
yield {
"name": e.name,
"state": {"ascending": e.state.ascending, "index": e.state.index},
"stateSort": {"ascending": e.state.ascending, "index": e.state.index},
"type": "SortFilter",
};
}
}).toList();

View file

@ -52,10 +52,15 @@ class Source {
String? headers;
/// For Mihon ext
bool? supportLatest;
/// For Mihon ext
String? filterList;
/// For Mihon ext
String? preferenceList;
bool? isManga;
@enumerated
@ -102,6 +107,7 @@ class Source {
this.headers = '',
this.supportLatest,
this.filterList,
this.preferenceList,
this.isManga,
this.itemType = ItemType.manga,
this.appMinVerReq = "",
@ -127,6 +133,7 @@ class Source {
headers = json['headers'];
supportLatest = json['supportLatest'];
filterList = json['filterList'];
preferenceList = json['preferenceList'];
iconUrl = json['iconUrl'];
id = json['id'];
isActive = json['isActive'];
@ -164,6 +171,7 @@ class Source {
'headers': headers,
'supportLatest': supportLatest,
'filterList': filterList,
'preferenceList': preferenceList,
'iconUrl': iconUrl,
'id': id,
'isActive': isActive,

View file

@ -138,50 +138,55 @@ const SourceSchema = CollectionSchema(
name: r'notes',
type: IsarType.string,
),
r'repo': PropertySchema(
r'preferenceList': PropertySchema(
id: 24,
name: r'preferenceList',
type: IsarType.string,
),
r'repo': PropertySchema(
id: 25,
name: r'repo',
type: IsarType.object,
target: r'Repo',
),
r'sourceCode': PropertySchema(
id: 25,
id: 26,
name: r'sourceCode',
type: IsarType.string,
),
r'sourceCodeLanguage': PropertySchema(
id: 26,
id: 27,
name: r'sourceCodeLanguage',
type: IsarType.byte,
enumMap: _SourcesourceCodeLanguageEnumValueMap,
),
r'sourceCodeUrl': PropertySchema(
id: 27,
id: 28,
name: r'sourceCodeUrl',
type: IsarType.string,
),
r'supportLatest': PropertySchema(
id: 28,
id: 29,
name: r'supportLatest',
type: IsarType.bool,
),
r'typeSource': PropertySchema(
id: 29,
id: 30,
name: r'typeSource',
type: IsarType.string,
),
r'updatedAt': PropertySchema(
id: 30,
id: 31,
name: r'updatedAt',
type: IsarType.long,
),
r'version': PropertySchema(
id: 31,
id: 32,
name: r'version',
type: IsarType.string,
),
r'versionLast': PropertySchema(
id: 32,
id: 33,
name: r'versionLast',
type: IsarType.string,
)
@ -278,6 +283,12 @@ int _sourceEstimateSize(
bytesCount += 3 + value.length * 3;
}
}
{
final value = object.preferenceList;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
{
final value = object.repo;
if (value != null) {
@ -348,20 +359,21 @@ void _sourceSerialize(
writer.writeBool(offsets[21], object.lastUsed);
writer.writeString(offsets[22], object.name);
writer.writeString(offsets[23], object.notes);
writer.writeString(offsets[24], object.preferenceList);
writer.writeObject<Repo>(
offsets[24],
offsets[25],
allOffsets,
RepoSchema.serialize,
object.repo,
);
writer.writeString(offsets[25], object.sourceCode);
writer.writeByte(offsets[26], object.sourceCodeLanguage.index);
writer.writeString(offsets[27], object.sourceCodeUrl);
writer.writeBool(offsets[28], object.supportLatest);
writer.writeString(offsets[29], object.typeSource);
writer.writeLong(offsets[30], object.updatedAt);
writer.writeString(offsets[31], object.version);
writer.writeString(offsets[32], object.versionLast);
writer.writeString(offsets[26], object.sourceCode);
writer.writeByte(offsets[27], object.sourceCodeLanguage.index);
writer.writeString(offsets[28], object.sourceCodeUrl);
writer.writeBool(offsets[29], object.supportLatest);
writer.writeString(offsets[30], object.typeSource);
writer.writeLong(offsets[31], object.updatedAt);
writer.writeString(offsets[32], object.version);
writer.writeString(offsets[33], object.versionLast);
}
Source _sourceDeserialize(
@ -396,21 +408,22 @@ Source _sourceDeserialize(
lastUsed: reader.readBoolOrNull(offsets[21]),
name: reader.readStringOrNull(offsets[22]),
notes: reader.readStringOrNull(offsets[23]),
preferenceList: reader.readStringOrNull(offsets[24]),
repo: reader.readObjectOrNull<Repo>(
offsets[24],
offsets[25],
RepoSchema.deserialize,
allOffsets,
),
sourceCode: reader.readStringOrNull(offsets[25]),
sourceCodeUrl: reader.readStringOrNull(offsets[27]),
supportLatest: reader.readBoolOrNull(offsets[28]),
typeSource: reader.readStringOrNull(offsets[29]),
updatedAt: reader.readLongOrNull(offsets[30]),
version: reader.readStringOrNull(offsets[31]),
versionLast: reader.readStringOrNull(offsets[32]),
sourceCode: reader.readStringOrNull(offsets[26]),
sourceCodeUrl: reader.readStringOrNull(offsets[28]),
supportLatest: reader.readBoolOrNull(offsets[29]),
typeSource: reader.readStringOrNull(offsets[30]),
updatedAt: reader.readLongOrNull(offsets[31]),
version: reader.readStringOrNull(offsets[32]),
versionLast: reader.readStringOrNull(offsets[33]),
);
object.sourceCodeLanguage = _SourcesourceCodeLanguageValueEnumMap[
reader.readByteOrNull(offsets[26])] ??
reader.readByteOrNull(offsets[27])] ??
SourceCodeLanguage.dart;
return object;
}
@ -472,29 +485,31 @@ P _sourceDeserializeProp<P>(
case 23:
return (reader.readStringOrNull(offset)) as P;
case 24:
return (reader.readStringOrNull(offset)) as P;
case 25:
return (reader.readObjectOrNull<Repo>(
offset,
RepoSchema.deserialize,
allOffsets,
)) as P;
case 25:
return (reader.readStringOrNull(offset)) as P;
case 26:
return (reader.readStringOrNull(offset)) as P;
case 27:
return (_SourcesourceCodeLanguageValueEnumMap[
reader.readByteOrNull(offset)] ??
SourceCodeLanguage.dart) as P;
case 27:
return (reader.readStringOrNull(offset)) as P;
case 28:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readStringOrNull(offset)) as P;
case 29:
return (reader.readStringOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 30:
return (reader.readLongOrNull(offset)) as P;
case 31:
return (reader.readStringOrNull(offset)) as P;
case 31:
return (reader.readLongOrNull(offset)) as P;
case 32:
return (reader.readStringOrNull(offset)) as P;
case 33:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
@ -2760,6 +2775,154 @@ extension SourceQueryFilter on QueryBuilder<Source, Source, QFilterCondition> {
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'preferenceList',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition>
preferenceListIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'preferenceList',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'preferenceList',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListContains(
String value,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'preferenceList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListMatches(
String pattern,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'preferenceList',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> preferenceListIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'preferenceList',
value: '',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition>
preferenceListIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'preferenceList',
value: '',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> repoIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -3958,6 +4121,18 @@ extension SourceQuerySortBy on QueryBuilder<Source, Source, QSortBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByPreferenceList() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'preferenceList', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByPreferenceListDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'preferenceList', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortBySourceCode() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'sourceCode', Sort.asc);
@ -4356,6 +4531,18 @@ extension SourceQuerySortThenBy on QueryBuilder<Source, Source, QSortThenBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByPreferenceList() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'preferenceList', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByPreferenceListDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'preferenceList', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenBySourceCode() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'sourceCode', Sort.asc);
@ -4612,6 +4799,14 @@ extension SourceQueryWhereDistinct on QueryBuilder<Source, Source, QDistinct> {
});
}
QueryBuilder<Source, Source, QDistinct> distinctByPreferenceList(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'preferenceList',
caseSensitive: caseSensitive);
});
}
QueryBuilder<Source, Source, QDistinct> distinctBySourceCode(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -4818,6 +5013,12 @@ extension SourceQueryProperty on QueryBuilder<Source, Source, QQueryProperty> {
});
}
QueryBuilder<Source, String?, QQueryOperations> preferenceListProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'preferenceList');
});
}
QueryBuilder<Source, Repo?, QQueryOperations> repoProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'repo');

View file

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:isar/isar.dart';
import 'package:mangayomi/eval/model/source_preference.dart';
import 'package:mangayomi/main.dart';
@ -11,6 +13,22 @@ void setPreferenceSetting(SourcePreference sourcePreference, Source source) {
.keyEqualTo(sourcePreference.key)
.findFirstSync();
isar.writeTxnSync(() {
if (source.sourceCodeLanguage == SourceCodeLanguage.mihon &&
source.preferenceList != null) {
final prefs = (jsonDecode(source.preferenceList!) as List)
.map((e) => SourcePreference.fromJson(e))
.toList();
final idx = prefs.indexWhere((e) => e.key == sourcePreference.key);
if (idx != -1) {
prefs[idx] = sourcePreference..id = null;
isar.sources.putSync(
source
..preferenceList = jsonEncode(
prefs.map((e) => e.toJson()).toList(),
),
);
}
}
if (sourcePref != null) {
isar.sourcePreferences.putSync(sourcePreference);
} else {

View file

@ -25,7 +25,7 @@ Future<dynamic> updateMangaDetail(
final source = getSource(manga.lang!, manga.source!);
MManga getManga;
getManga = await ref.watch(
getManga = await ref.read(
getDetailProvider(url: manga.link!, source: source!).future,
);
@ -36,9 +36,11 @@ Future<dynamic> updateMangaDetail(
.toSet()
.toList() ??
[];
final tempName = getManga.name?.trim().trimLeft().trimRight();
final tempLink = getManga.link?.trim().trimLeft().trimRight();
manga
..imageUrl = getManga.imageUrl ?? manga.imageUrl
..name = getManga.name?.trim().trimLeft().trimRight() ?? manga.name
..name = tempName != null && tempName.isNotEmpty ? tempName : manga.name
..genre = (genre.isEmpty ? null : genre) ?? manga.genre ?? []
..author =
getManga.author?.trim().trimLeft().trimRight() ?? manga.author ?? ""
@ -51,7 +53,7 @@ Future<dynamic> updateMangaDetail(
getManga.description?.trim().trimLeft().trimRight() ??
manga.description ??
""
..link = getManga.link?.trim().trimLeft().trimRight() ?? manga.link
..link = tempLink != null && tempLink.isNotEmpty ? tempLink : manga.link
..source = manga.source
..lang = manga.lang
..itemType = source.itemType

View file

@ -6,7 +6,7 @@ part of 'update_manga_detail_providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$updateMangaDetailHash() => r'ce51918a48b315c3555b3de4e602bd998e00a992';
String _$updateMangaDetailHash() => r'30185777f73eaf9eac4cce554a7ecbc9e1c0b613';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -158,7 +158,7 @@ class _GetRepoInfosProviderElement
}
String _$androidProxyServerStateHash() =>
r'c16bddb7d686a13e8f34a18dec2d983232f34c65';
r'3ac060f8a61added586dcefc889fa44c71263c5b';
/// See also [AndroidProxyServerState].
@ProviderFor(AndroidProxyServerState)

View file

@ -4,6 +4,7 @@ import 'package:http_interceptor/http_interceptor.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/filter.dart';
import 'package:mangayomi/eval/model/source_preference.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
@ -133,6 +134,7 @@ Future<void> _updateSource(
Map<String, String> headers = {};
bool? supportLatest;
FilterList? filterList;
List<SourcePreference>? preferenceList;
if (source.sourceCodeLanguage == SourceCodeLanguage.mihon) {
headers = await fetchHeadersDalvik(
http,
@ -149,6 +151,11 @@ Future<void> _updateSource(
source..sourceCode = sourceCode,
androidProxyServer,
);
preferenceList = await fetchPreferencesDalvik(
http,
source..sourceCode = sourceCode,
androidProxyServer,
);
} else {
headers = getExtensionService(
source..sourceCode = sourceCode,
@ -160,6 +167,9 @@ Future<void> _updateSource(
..headers = jsonEncode(headers)
..supportLatest = supportLatest
..filterList = filterList != null ? jsonEncode(filterList.toJson()) : null
..preferenceList = preferenceList != null
? jsonEncode(preferenceList.map((e) => e.toJson()).toList())
: null
..isAdded = true
..sourceCode = sourceCode
..sourceCodeUrl = source.sourceCodeUrl
@ -410,3 +420,30 @@ Future<FilterList?> fetchFilterListDalvik(
return null;
}
}
Future<List<SourcePreference>?> fetchPreferencesDalvik(
InterceptedClient client,
Source source,
String androidProxyServer,
) async {
try {
final name = source.itemType == ItemType.anime ? "Anime" : "Manga";
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "preferences$name",
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as List;
return data
.map(
(e) => SourcePreference.fromJson(e)
..id = null
..sourceId = source.id,
)
.toList();
} catch (_) {
return null;
}
}