added support for Mihon extensions via ApkBridge

This commit is contained in:
Schnitzel5 2025-08-20 21:06:26 +02:00
parent 5f9efe957a
commit 9395888c6a
51 changed files with 1105 additions and 140 deletions

View file

@ -5,10 +5,10 @@ import 'dart/service.dart';
import 'javascript/service.dart';
import 'mihon/service.dart';
ExtensionService getExtensionService(Source source) {
ExtensionService getExtensionService(Source source, String androidProxyServer) {
return switch (source.sourceCodeLanguage) {
SourceCodeLanguage.dart => DartExtensionService(source),
SourceCodeLanguage.javascript => JsExtensionService(source),
SourceCodeLanguage.mihon => MihonExtensionService(source, "http://localhost:8080"),
SourceCodeLanguage.mihon => MihonExtensionService(source, androidProxyServer),
};
}

View file

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:http_interceptor/http_interceptor.dart';
import 'package:mangayomi/eval/javascript/http.dart';
import 'package:mangayomi/eval/model/filter.dart';
import 'package:mangayomi/eval/model/m_chapter.dart';
import 'package:mangayomi/eval/model/m_manga.dart';
@ -26,12 +27,14 @@ class MihonExtensionService implements ExtensionService {
@override
Map<String, String> getHeaders() {
return {};
return source.headers != null && source.headers!.isNotEmpty
? (jsonDecode(source.headers!) as Map?)?.toMapStringString ?? {}
: {};
}
@override
bool get supportsLatest {
return true;
return source.supportLatest ?? false;
}
@override
@ -116,6 +119,7 @@ class MihonExtensionService implements ExtensionService {
"method": "getSearch$name",
"page": page + 1,
"search": query,
// "filterList$name": _convertFilters(filters),
"data": source.sourceCode,
}),
);
@ -213,7 +217,7 @@ class MihonExtensionService implements ExtensionService {
}),
);
final data = jsonDecode(res.body) as List;
return data.map((e) => PageUrl(e['url'])).toList();
return data.map((e) => PageUrl(e['imageUrl'])).toList();
}
@override
@ -231,7 +235,7 @@ class MihonExtensionService implements ExtensionService {
final tempHeaders =
e['headers']['namesAndValues\$okhttp'] as List<dynamic>;
final Map<String, String> headers = {};
for (var i = 0; i + 1 < tempHeaders.length; i++) {
for (var i = 0; i + 1 < tempHeaders.length; i += 2) {
headers[tempHeaders[i]] = tempHeaders[i + 1];
}
return Video(
@ -265,6 +269,7 @@ class MihonExtensionService implements ExtensionService {
@override
FilterList getFilterList() {
// return source.getFilterList() ?? FilterList([]);
return FilterList([]);
}
@ -272,4 +277,37 @@ class MihonExtensionService implements ExtensionService {
List<SourcePreference> getSourcePreferences() {
return [];
}
List<dynamic> _convertFilters(List<dynamic> filters) {
return filters.expand((e) sync* {
if (e is TextFilter) {
yield {"name": e.name, "state": e.state};
} else if (e is GroupFilter) {
yield {
"name": e.name,
"state": e.state.expand((e) sync* {
if (e is CheckBoxFilter) {
yield {"name": e.name, "id": e.value, "state": e.state};
} 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,
};
}
}).toList(),
};
} else if (e is SelectFilter) {
yield {"name": e.name, "state": e.state};
} else if (e is SortFilter) {
yield {
"name": e.name,
"state": {"ascending": e.state.ascending, "index": e.state.index},
};
}
}).toList();
}
}

View file

@ -443,6 +443,7 @@
"manga_extensions_repo": "Manga extensions repo",
"anime_extensions_repo": "Anime extensions repo",
"novel_extensions_repo": "Novel extensions repo",
"android_proxy_server": "Android Proxy Server (ApkBridge)",
"undefined": "undefined",
"empty_extensions_repo": "You don't have any repository urls here. Click on the plus button to add one!",
"add_extensions_repo": "Add repo URL",

View file

@ -2733,6 +2733,12 @@ abstract class AppLocalizations {
/// **'Novel extensions repo'**
String get novel_extensions_repo;
/// No description provided for @android_proxy_server.
///
/// In en, this message translates to:
/// **'Android Proxy Server (ApkBridge)'**
String get android_proxy_server;
/// No description provided for @undefined.
///
/// In en, this message translates to:

View file

@ -1402,6 +1402,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get novel_extensions_repo => 'مستودع إضافات الروايات';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'غير محدد';

View file

@ -1404,6 +1404,9 @@ class AppLocalizationsAs extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';

View file

@ -1413,6 +1413,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get novel_extensions_repo => 'Roman-Erweiterungs-Repository';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Nicht definiert';

View file

@ -1403,6 +1403,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';

View file

@ -1417,6 +1417,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositorio de extensiones de novelas';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indefinido';

View file

@ -1420,6 +1420,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get novel_extensions_repo => 'Dépôt d\'extensions de romans';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indéfini';

View file

@ -1405,6 +1405,9 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';

View file

@ -1409,6 +1409,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositori ekstensi novel';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Tidak terdefinisi';

View file

@ -1417,6 +1417,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repository delle estensioni romanzi';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Non definito';

View file

@ -1414,6 +1414,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositório de extensões de romances';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indefinido';

View file

@ -1416,6 +1416,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get novel_extensions_repo => 'Репозиторий расширений новелл';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Не определено';

View file

@ -1403,6 +1403,9 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get novel_extensions_repo => 'ที่เก็บส่วนขยายโนเวล';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'ไม่ได้กำหนด';

View file

@ -1409,6 +1409,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get novel_extensions_repo => 'Roman uzantıları deposu';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Tanımsız';

View file

@ -1377,6 +1377,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get novel_extensions_repo => '小说扩展库';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => '未定义';

View file

@ -1,4 +1,7 @@
import 'dart:convert';
import 'package:isar/isar.dart';
import 'package:mangayomi/eval/model/filter.dart';
import 'package:mangayomi/eval/model/m_source.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
@ -49,6 +52,10 @@ class Source {
String? headers;
bool? supportLatest;
String? filterList;
bool? isManga;
@enumerated
@ -93,6 +100,8 @@ class Source {
this.versionLast = "0.0.1",
this.sourceCode = '',
this.headers = '',
this.supportLatest,
this.filterList,
this.isManga,
this.itemType = ItemType.manga,
this.appMinVerReq = "",
@ -104,6 +113,10 @@ class Source {
this.updatedAt = 0,
});
FilterList? getFilterList() => filterList != null
? FilterList.fromJson(jsonDecode(filterList!) as Map<String, dynamic>)
: null;
Source.fromJson(Map<String, dynamic> json) {
apiUrl = json['apiUrl'];
appMinVerReq = json['appMinVerReq'];
@ -112,6 +125,8 @@ class Source {
dateFormatLocale = json['dateFormatLocale'];
hasCloudflare = json['hasCloudflare'];
headers = json['headers'];
supportLatest = json['supportLatest'];
filterList = json['filterList'];
iconUrl = json['iconUrl'];
id = json['id'];
isActive = json['isActive'];
@ -147,6 +162,8 @@ class Source {
'dateFormatLocale': dateFormatLocale,
'hasCloudflare': hasCloudflare,
'headers': headers,
'supportLatest': supportLatest,
'filterList': filterList,
'iconUrl': iconUrl,
'id': id,
'isActive': isActive,

View file

@ -47,131 +47,141 @@ const SourceSchema = CollectionSchema(
name: r'dateFormatLocale',
type: IsarType.string,
),
r'hasCloudflare': PropertySchema(
r'filterList': PropertySchema(
id: 6,
name: r'filterList',
type: IsarType.string,
),
r'hasCloudflare': PropertySchema(
id: 7,
name: r'hasCloudflare',
type: IsarType.bool,
),
r'headers': PropertySchema(
id: 7,
id: 8,
name: r'headers',
type: IsarType.string,
),
r'iconUrl': PropertySchema(
id: 8,
id: 9,
name: r'iconUrl',
type: IsarType.string,
),
r'isActive': PropertySchema(
id: 9,
id: 10,
name: r'isActive',
type: IsarType.bool,
),
r'isAdded': PropertySchema(
id: 10,
id: 11,
name: r'isAdded',
type: IsarType.bool,
),
r'isFullData': PropertySchema(
id: 11,
id: 12,
name: r'isFullData',
type: IsarType.bool,
),
r'isLocal': PropertySchema(
id: 12,
id: 13,
name: r'isLocal',
type: IsarType.bool,
),
r'isManga': PropertySchema(
id: 13,
id: 14,
name: r'isManga',
type: IsarType.bool,
),
r'isNsfw': PropertySchema(
id: 14,
id: 15,
name: r'isNsfw',
type: IsarType.bool,
),
r'isObsolete': PropertySchema(
id: 15,
id: 16,
name: r'isObsolete',
type: IsarType.bool,
),
r'isPinned': PropertySchema(
id: 16,
id: 17,
name: r'isPinned',
type: IsarType.bool,
),
r'isTorrent': PropertySchema(
id: 17,
id: 18,
name: r'isTorrent',
type: IsarType.bool,
),
r'itemType': PropertySchema(
id: 18,
id: 19,
name: r'itemType',
type: IsarType.byte,
enumMap: _SourceitemTypeEnumValueMap,
),
r'lang': PropertySchema(
id: 19,
id: 20,
name: r'lang',
type: IsarType.string,
),
r'lastUsed': PropertySchema(
id: 20,
id: 21,
name: r'lastUsed',
type: IsarType.bool,
),
r'name': PropertySchema(
id: 21,
id: 22,
name: r'name',
type: IsarType.string,
),
r'notes': PropertySchema(
id: 22,
id: 23,
name: r'notes',
type: IsarType.string,
),
r'repo': PropertySchema(
id: 23,
id: 24,
name: r'repo',
type: IsarType.object,
target: r'Repo',
),
r'sourceCode': PropertySchema(
id: 24,
id: 25,
name: r'sourceCode',
type: IsarType.string,
),
r'sourceCodeLanguage': PropertySchema(
id: 25,
id: 26,
name: r'sourceCodeLanguage',
type: IsarType.byte,
enumMap: _SourcesourceCodeLanguageEnumValueMap,
),
r'sourceCodeUrl': PropertySchema(
id: 26,
id: 27,
name: r'sourceCodeUrl',
type: IsarType.string,
),
r'supportLatest': PropertySchema(
id: 28,
name: r'supportLatest',
type: IsarType.bool,
),
r'typeSource': PropertySchema(
id: 27,
id: 29,
name: r'typeSource',
type: IsarType.string,
),
r'updatedAt': PropertySchema(
id: 28,
id: 30,
name: r'updatedAt',
type: IsarType.long,
),
r'version': PropertySchema(
id: 29,
id: 31,
name: r'version',
type: IsarType.string,
),
r'versionLast': PropertySchema(
id: 30,
id: 32,
name: r'versionLast',
type: IsarType.string,
)
@ -232,6 +242,12 @@ int _sourceEstimateSize(
bytesCount += 3 + value.length * 3;
}
}
{
final value = object.filterList;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
{
final value = object.headers;
if (value != null) {
@ -314,36 +330,38 @@ void _sourceSerialize(
writer.writeString(offsets[3], object.baseUrl);
writer.writeString(offsets[4], object.dateFormat);
writer.writeString(offsets[5], object.dateFormatLocale);
writer.writeBool(offsets[6], object.hasCloudflare);
writer.writeString(offsets[7], object.headers);
writer.writeString(offsets[8], object.iconUrl);
writer.writeBool(offsets[9], object.isActive);
writer.writeBool(offsets[10], object.isAdded);
writer.writeBool(offsets[11], object.isFullData);
writer.writeBool(offsets[12], object.isLocal);
writer.writeBool(offsets[13], object.isManga);
writer.writeBool(offsets[14], object.isNsfw);
writer.writeBool(offsets[15], object.isObsolete);
writer.writeBool(offsets[16], object.isPinned);
writer.writeBool(offsets[17], object.isTorrent);
writer.writeByte(offsets[18], object.itemType.index);
writer.writeString(offsets[19], object.lang);
writer.writeBool(offsets[20], object.lastUsed);
writer.writeString(offsets[21], object.name);
writer.writeString(offsets[22], object.notes);
writer.writeString(offsets[6], object.filterList);
writer.writeBool(offsets[7], object.hasCloudflare);
writer.writeString(offsets[8], object.headers);
writer.writeString(offsets[9], object.iconUrl);
writer.writeBool(offsets[10], object.isActive);
writer.writeBool(offsets[11], object.isAdded);
writer.writeBool(offsets[12], object.isFullData);
writer.writeBool(offsets[13], object.isLocal);
writer.writeBool(offsets[14], object.isManga);
writer.writeBool(offsets[15], object.isNsfw);
writer.writeBool(offsets[16], object.isObsolete);
writer.writeBool(offsets[17], object.isPinned);
writer.writeBool(offsets[18], object.isTorrent);
writer.writeByte(offsets[19], object.itemType.index);
writer.writeString(offsets[20], object.lang);
writer.writeBool(offsets[21], object.lastUsed);
writer.writeString(offsets[22], object.name);
writer.writeString(offsets[23], object.notes);
writer.writeObject<Repo>(
offsets[23],
offsets[24],
allOffsets,
RepoSchema.serialize,
object.repo,
);
writer.writeString(offsets[24], object.sourceCode);
writer.writeByte(offsets[25], object.sourceCodeLanguage.index);
writer.writeString(offsets[26], object.sourceCodeUrl);
writer.writeString(offsets[27], object.typeSource);
writer.writeLong(offsets[28], object.updatedAt);
writer.writeString(offsets[29], object.version);
writer.writeString(offsets[30], object.versionLast);
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);
}
Source _sourceDeserialize(
@ -359,38 +377,40 @@ Source _sourceDeserialize(
baseUrl: reader.readStringOrNull(offsets[3]),
dateFormat: reader.readStringOrNull(offsets[4]),
dateFormatLocale: reader.readStringOrNull(offsets[5]),
hasCloudflare: reader.readBoolOrNull(offsets[6]),
headers: reader.readStringOrNull(offsets[7]),
iconUrl: reader.readStringOrNull(offsets[8]),
filterList: reader.readStringOrNull(offsets[6]),
hasCloudflare: reader.readBoolOrNull(offsets[7]),
headers: reader.readStringOrNull(offsets[8]),
iconUrl: reader.readStringOrNull(offsets[9]),
id: id,
isActive: reader.readBoolOrNull(offsets[9]),
isAdded: reader.readBoolOrNull(offsets[10]),
isFullData: reader.readBoolOrNull(offsets[11]),
isLocal: reader.readBoolOrNull(offsets[12]),
isManga: reader.readBoolOrNull(offsets[13]),
isNsfw: reader.readBoolOrNull(offsets[14]),
isObsolete: reader.readBoolOrNull(offsets[15]),
isPinned: reader.readBoolOrNull(offsets[16]),
itemType: _SourceitemTypeValueEnumMap[reader.readByteOrNull(offsets[18])] ??
isActive: reader.readBoolOrNull(offsets[10]),
isAdded: reader.readBoolOrNull(offsets[11]),
isFullData: reader.readBoolOrNull(offsets[12]),
isLocal: reader.readBoolOrNull(offsets[13]),
isManga: reader.readBoolOrNull(offsets[14]),
isNsfw: reader.readBoolOrNull(offsets[15]),
isObsolete: reader.readBoolOrNull(offsets[16]),
isPinned: reader.readBoolOrNull(offsets[17]),
itemType: _SourceitemTypeValueEnumMap[reader.readByteOrNull(offsets[19])] ??
ItemType.manga,
lang: reader.readStringOrNull(offsets[19]),
lastUsed: reader.readBoolOrNull(offsets[20]),
name: reader.readStringOrNull(offsets[21]),
notes: reader.readStringOrNull(offsets[22]),
lang: reader.readStringOrNull(offsets[20]),
lastUsed: reader.readBoolOrNull(offsets[21]),
name: reader.readStringOrNull(offsets[22]),
notes: reader.readStringOrNull(offsets[23]),
repo: reader.readObjectOrNull<Repo>(
offsets[23],
offsets[24],
RepoSchema.deserialize,
allOffsets,
),
sourceCode: reader.readStringOrNull(offsets[24]),
sourceCodeUrl: reader.readStringOrNull(offsets[26]),
typeSource: reader.readStringOrNull(offsets[27]),
updatedAt: reader.readLongOrNull(offsets[28]),
version: reader.readStringOrNull(offsets[29]),
versionLast: reader.readStringOrNull(offsets[30]),
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]),
);
object.sourceCodeLanguage = _SourcesourceCodeLanguageValueEnumMap[
reader.readByteOrNull(offsets[25])] ??
reader.readByteOrNull(offsets[26])] ??
SourceCodeLanguage.dart;
return object;
}
@ -415,13 +435,13 @@ P _sourceDeserializeProp<P>(
case 5:
return (reader.readStringOrNull(offset)) as P;
case 6:
return (reader.readBoolOrNull(offset)) as P;
case 7:
return (reader.readStringOrNull(offset)) as P;
case 7:
return (reader.readBoolOrNull(offset)) as P;
case 8:
return (reader.readStringOrNull(offset)) as P;
case 9:
return (reader.readBoolOrNull(offset)) as P;
return (reader.readStringOrNull(offset)) as P;
case 10:
return (reader.readBoolOrNull(offset)) as P;
case 11:
@ -437,39 +457,43 @@ P _sourceDeserializeProp<P>(
case 16:
return (reader.readBoolOrNull(offset)) as P;
case 17:
return (reader.readBool(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 18:
return (reader.readBool(offset)) as P;
case 19:
return (_SourceitemTypeValueEnumMap[reader.readByteOrNull(offset)] ??
ItemType.manga) as P;
case 19:
return (reader.readStringOrNull(offset)) as P;
case 20:
return (reader.readBoolOrNull(offset)) as P;
case 21:
return (reader.readStringOrNull(offset)) as P;
case 21:
return (reader.readBoolOrNull(offset)) as P;
case 22:
return (reader.readStringOrNull(offset)) as P;
case 23:
return (reader.readStringOrNull(offset)) as P;
case 24:
return (reader.readObjectOrNull<Repo>(
offset,
RepoSchema.deserialize,
allOffsets,
)) as P;
case 24:
return (reader.readStringOrNull(offset)) as P;
case 25:
return (reader.readStringOrNull(offset)) as P;
case 26:
return (_SourcesourceCodeLanguageValueEnumMap[
reader.readByteOrNull(offset)] ??
SourceCodeLanguage.dart) as P;
case 26:
return (reader.readStringOrNull(offset)) as P;
case 27:
return (reader.readStringOrNull(offset)) as P;
case 28:
return (reader.readLongOrNull(offset)) as P;
return (reader.readBoolOrNull(offset)) as P;
case 29:
return (reader.readStringOrNull(offset)) as P;
case 30:
return (reader.readLongOrNull(offset)) as P;
case 31:
return (reader.readStringOrNull(offset)) as P;
case 32:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
@ -1471,6 +1495,152 @@ extension SourceQueryFilter on QueryBuilder<Source, Source, QFilterCondition> {
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'filterList',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'filterList',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListBetween(
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'filterList',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListContains(
String value,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'filterList',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListMatches(
String pattern,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'filterList',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'filterList',
value: '',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> filterListIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'filterList',
value: '',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> hasCloudflareIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -2954,6 +3124,32 @@ extension SourceQueryFilter on QueryBuilder<Source, Source, QFilterCondition> {
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> supportLatestIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'supportLatest',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> supportLatestIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'supportLatest',
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> supportLatestEqualTo(
bool? value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'supportLatest',
value: value,
));
});
}
QueryBuilder<Source, Source, QAfterFilterCondition> typeSourceIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
@ -3546,6 +3742,18 @@ extension SourceQuerySortBy on QueryBuilder<Source, Source, QSortBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByFilterList() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'filterList', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByFilterListDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'filterList', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByHasCloudflare() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'hasCloudflare', Sort.asc);
@ -3786,6 +3994,18 @@ extension SourceQuerySortBy on QueryBuilder<Source, Source, QSortBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortBySupportLatest() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'supportLatest', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortBySupportLatestDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'supportLatest', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> sortByTypeSource() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'typeSource', Sort.asc);
@ -3908,6 +4128,18 @@ extension SourceQuerySortThenBy on QueryBuilder<Source, Source, QSortThenBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByFilterList() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'filterList', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByFilterListDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'filterList', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByHasCloudflare() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'hasCloudflare', Sort.asc);
@ -4160,6 +4392,18 @@ extension SourceQuerySortThenBy on QueryBuilder<Source, Source, QSortThenBy> {
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenBySupportLatest() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'supportLatest', Sort.asc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenBySupportLatestDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'supportLatest', Sort.desc);
});
}
QueryBuilder<Source, Source, QAfterSortBy> thenByTypeSource() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'typeSource', Sort.asc);
@ -4254,6 +4498,13 @@ extension SourceQueryWhereDistinct on QueryBuilder<Source, Source, QDistinct> {
});
}
QueryBuilder<Source, Source, QDistinct> distinctByFilterList(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'filterList', caseSensitive: caseSensitive);
});
}
QueryBuilder<Source, Source, QDistinct> distinctByHasCloudflare() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'hasCloudflare');
@ -4382,6 +4633,12 @@ extension SourceQueryWhereDistinct on QueryBuilder<Source, Source, QDistinct> {
});
}
QueryBuilder<Source, Source, QDistinct> distinctBySupportLatest() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'supportLatest');
});
}
QueryBuilder<Source, Source, QDistinct> distinctByTypeSource(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
@ -4453,6 +4710,12 @@ extension SourceQueryProperty on QueryBuilder<Source, Source, QQueryProperty> {
});
}
QueryBuilder<Source, String?, QQueryOperations> filterListProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'filterList');
});
}
QueryBuilder<Source, bool?, QQueryOperations> hasCloudflareProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'hasCloudflare');
@ -4580,6 +4843,12 @@ extension SourceQueryProperty on QueryBuilder<Source, Source, QQueryProperty> {
});
}
QueryBuilder<Source, bool?, QQueryOperations> supportLatestProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'supportLatest');
});
}
QueryBuilder<Source, String?, QQueryOperations> typeSourceProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'typeSource');

View file

@ -9,6 +9,7 @@ import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/manga/home/widget/filter_widget.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/app_font_family.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/services/get_detail.dart';
import 'package:mangayomi/services/get_filter_list.dart';
@ -314,6 +315,7 @@ class _CodeEditorPageState extends ConsumerState<CodeEditorPage> {
if (source != null) {
final service = getExtensionService(
source!,
ref.read(androidProxyServerStateProvider),
);
try {

View file

@ -119,7 +119,7 @@ class _GlobalSearchScreenState extends ConsumerState<GlobalSearchScreen> {
}
}
class SourceSearchScreen extends StatefulWidget {
class SourceSearchScreen extends ConsumerStatefulWidget {
final String query;
final Source source;
@ -130,10 +130,10 @@ class SourceSearchScreen extends StatefulWidget {
});
@override
State<SourceSearchScreen> createState() => _SourceSearchScreenState();
ConsumerState<SourceSearchScreen> createState() => _SourceSearchScreenState();
}
class _SourceSearchScreenState extends State<SourceSearchScreen> {
class _SourceSearchScreenState extends ConsumerState<SourceSearchScreen> {
@override
void initState() {
super.initState();
@ -146,11 +146,13 @@ class _SourceSearchScreenState extends State<SourceSearchScreen> {
_init() async {
try {
_errorMessage = "";
pages = await search(
source: widget.source,
page: 1,
query: widget.query,
filterList: [],
pages = await ref.read(
searchProvider(
source: widget.source,
page: 1,
query: widget.query,
filterList: [],
).future,
);
if (mounted) {
setState(() {

View file

@ -140,7 +140,7 @@ class _MigrationScreenScreenState extends ConsumerState<MigrationScreen> {
}
}
class MigrationSourceSearchScreen extends StatefulWidget {
class MigrationSourceSearchScreen extends ConsumerStatefulWidget {
final String query;
final Manga manga;
final TrackSearch? trackSearch;
@ -155,12 +155,12 @@ class MigrationSourceSearchScreen extends StatefulWidget {
});
@override
State<MigrationSourceSearchScreen> createState() =>
ConsumerState<MigrationSourceSearchScreen> createState() =>
_MigrationSourceSearchScreenState();
}
class _MigrationSourceSearchScreenState
extends State<MigrationSourceSearchScreen> {
extends ConsumerState<MigrationSourceSearchScreen> {
@override
void initState() {
super.initState();
@ -173,11 +173,13 @@ class _MigrationSourceSearchScreenState
_init() async {
try {
_errorMessage = "";
pages = await search(
source: widget.source,
page: 1,
query: widget.query,
filterList: [],
pages = await ref.read(
searchProvider(
source: widget.source,
page: 1,
query: widget.query,
filterList: [],
).future,
);
if (mounted) {
setState(() {

View file

@ -20,6 +20,7 @@ class BrowseSScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final androidProxyServer = ref.watch(androidProxyServerStateProvider);
final onlyIncludePinnedSource = ref.watch(
onlyIncludePinnedSourceStateProvider,
);
@ -51,6 +52,21 @@ class BrowseSScreen extends ConsumerWidget {
],
),
),
ListTile(
onTap: () => _showAndroidProxyServerDialog(
context,
ref,
androidProxyServer,
),
title: Text(l10n.android_proxy_server),
subtitle: Text(
androidProxyServer,
style: TextStyle(
fontSize: 11,
color: context.secondaryColor,
),
),
),
ListTile(
onTap: () {
context.push(
@ -242,6 +258,82 @@ void _showClearAllSourcesDialog(BuildContext context, dynamic l10n) {
);
}
void _showAndroidProxyServerDialog(
BuildContext context,
WidgetRef ref,
String proxyServer,
) {
final serverController = TextEditingController(text: proxyServer);
String server = proxyServer;
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(
context.l10n.android_proxy_server,
style: const TextStyle(fontSize: 30),
),
content: SizedBox(
width: context.width(0.8),
height: context.height(0.3),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: TextFormField(
controller: serverController,
autofocus: true,
onChanged: (value) => setState(() {
server = value;
}),
decoration: InputDecoration(
hintText:
"Server IP (e.g., 10.0.0.5 or https://example.com)",
filled: false,
contentPadding: const EdgeInsets.all(12),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(width: 0.4),
borderRadius: BorderRadius.circular(5),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(),
borderRadius: BorderRadius.circular(5),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide: const BorderSide(),
),
),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: SizedBox(
width: context.width(1),
child: ElevatedButton(
onPressed: () {
ref
.read(androidProxyServerStateProvider.notifier)
.set(server);
Navigator.pop(context);
},
child: Text(context.l10n.dialog_confirm),
),
),
),
],
),
),
);
},
),
);
}
void _showCleanNonLibraryDialog(BuildContext context, dynamic l10n) {
showDialog(
context: context,

View file

@ -10,6 +10,39 @@ import 'package:mangayomi/services/http/m_client.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'browse_state_provider.g.dart';
@riverpod
class AndroidProxyServerState extends _$AndroidProxyServerState {
@override
String build() {
String proxyServer =
isar.settings.getSync(227)!.androidProxyServer ??
"http://127.0.0.1:8080";
if (!proxyServer.startsWith("http")) {
proxyServer = "http://$proxyServer";
}
if ((proxyServer.contains("localhost") ||
RegExp(
r'^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$',
).hasMatch(proxyServer.replaceAll("://", ":").split(":")[1])) &&
proxyServer.split(":").length < 3) {
proxyServer = "$proxyServer:8080";
}
return proxyServer;
}
void set(String value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(
settings!
..androidProxyServer = value
..updatedAt = DateTime.now().millisecondsSinceEpoch,
),
);
}
}
@riverpod
class OnlyIncludePinnedSourceState extends _$OnlyIncludePinnedSourceState {
@override

View file

@ -157,6 +157,23 @@ class _GetRepoInfosProviderElement
String get jsonUrl => (origin as GetRepoInfosProvider).jsonUrl;
}
String _$androidProxyServerStateHash() =>
r'c16bddb7d686a13e8f34a18dec2d983232f34c65';
/// See also [AndroidProxyServerState].
@ProviderFor(AndroidProxyServerState)
final androidProxyServerStateProvider =
AutoDisposeNotifierProvider<AndroidProxyServerState, String>.internal(
AndroidProxyServerState.new,
name: r'androidProxyServerStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$androidProxyServerStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AndroidProxyServerState = AutoDisposeNotifier<String>;
String _$onlyIncludePinnedSourceStateHash() =>
r'b9f707348d5d0f7abfa8e615c1d2b35c6dbd57f3';

View file

@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
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/main.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
@ -67,7 +69,8 @@ Future<void> fetchSourcesList({
? ItemType.anime
: ItemType.manga
..iconUrl = "$repoUrl/icon/${e['pkg']}.png"
..notes = "Requires Android Proxy Server!";
..notes =
"Requires Android Proxy Server (ApkBridge) for installing and using the extensions!";
src.id = 'mihon-${source['id']}'.hashCode;
yield src;
}
@ -126,12 +129,37 @@ Future<void> _updateSource(
final sourceCode = source.sourceCodeLanguage == SourceCodeLanguage.mihon
? base64.encode(req.bodyBytes)
: req.body;
final headers = getExtensionService(
source..sourceCode = sourceCode,
).getHeaders();
final androidProxyServer = ref.read(androidProxyServerStateProvider);
Map<String, String> headers = {};
bool? supportLatest;
FilterList? filterList;
if (source.sourceCodeLanguage == SourceCodeLanguage.mihon) {
headers = await fetchHeadersDalvik(
http,
source..sourceCode = sourceCode,
androidProxyServer,
);
supportLatest = await fetchSupportLatestDalvik(
http,
source..sourceCode = sourceCode,
androidProxyServer,
);
filterList = await fetchFilterListDalvik(
http,
source..sourceCode = sourceCode,
androidProxyServer,
);
} else {
headers = getExtensionService(
source..sourceCode = sourceCode,
androidProxyServer,
).getHeaders();
}
final updatedSource = Source()
..headers = jsonEncode(headers)
..supportLatest = supportLatest
..filterList = filterList != null ? jsonEncode(filterList.toJson()) : null
..isAdded = true
..sourceCode = sourceCode
..sourceCodeUrl = source.sourceCodeUrl
@ -252,3 +280,133 @@ int compareVersions(String version1, String version2) {
return v1Parts.length.compareTo(v2Parts.length);
}
Future<Map<String, String>> fetchHeadersDalvik(
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": "headers$name", "data": source.sourceCode}),
);
final data = jsonDecode(res.body) as List;
final Map<String, String> headers = {};
for (var i = 0; i + 1 < data.length; i += 2) {
headers[data[i]] = data[i + 1];
}
return headers;
} catch (_) {
return {};
}
}
Future<bool> fetchSupportLatestDalvik(
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": "supportLatest$name",
"data": source.sourceCode,
}),
);
return res.body.trim() == "true";
} catch (_) {
return false;
}
}
Future<FilterList?> fetchFilterListDalvik(
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": "filters$name", "data": source.sourceCode}),
);
final data = jsonDecode(res.body) as List;
final filters = data.expand((e) sync* {
if (e['name'] is String &&
e['state'] is Map<String, dynamic> &&
e['values'] is List) {
yield SortFilter(
"${e['name']}Filter",
e['name'],
SortState(e['state']['index'], e['state']['ascending'], null),
(e['values'] as List)
.map((e) => SelectFilterOption(e, e, null))
.toList(),
null,
);
} else if (e['name'] is String &&
e['state'] is int &&
(e['values'] is List || e['vals'] is List)) {
yield SelectFilter(
"${e['name']}Filter",
e['name'],
e['state'],
e['vals'] is List
? (e['vals'] as List)
.map(
(e) => SelectFilterOption(e['first'], e['second'], null),
)
.toList()
: e['values'] is List
? (e['values'] as List)
.map((e) => SelectFilterOption(e, e, null))
.toList()
: [],
"SelectFilter",
);
} else if (e['name'] is String && e['state'] is List) {
yield GroupFilter(
"${e['name']}Filter",
e['name'],
(e['state'] as List).map((e) {
if (e['included'] is bool &&
e['ignored'] is bool &&
e['excluded'] is bool) {
return TriStateFilter(
null,
e['name'],
e['id'] ?? e['name'],
null,
state: e['state'],
);
}
return CheckBoxFilter(
null,
e['name'],
e['id'] ?? e['name'],
null,
state: e['state'],
);
}).toList(),
"GroupFilter",
);
} else if (e['name'] is String && e['state'] is String) {
yield TextFilter(
"${e['name']}Filter",
e['name'],
null,
state: e['state'],
);
} else if (e['name'] is String && e['state'] is int) {
yield HeaderFilter(e['name'], "${e['name']}Filter");
}
}).toList();
return FilterList(filters);
} catch (_) {
return null;
}
}

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:path/path.dart' as p;
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/javascript/http.dart';
@ -74,7 +75,10 @@ Future<GetChapterPagesModel> getChapterPages(
pageUrls.add(PageUrl(isarPageUrls.urls![i], headers: headers));
}
} else {
pageUrls = await getExtensionService(source).getPageList(chapter.url!);
pageUrls = await getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).getPageList(chapter.url!);
}
}

View file

@ -6,7 +6,7 @@ part of 'get_chapter_pages.dart';
// RiverpodGenerator
// **************************************************************************
String _$getChapterPagesHash() => r'08f56022f03c4834c69c50d0020007fa8b26c091';
String _$getChapterPagesHash() => r'8f6d2d661593fc5537f4dda83abea8d46d65b027';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,7 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/m_manga.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'get_detail.g.dart';
@ -11,5 +12,8 @@ Future<MManga> getDetail(
required String url,
required Source source,
}) async {
return getExtensionService(source).getDetail(url);
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).getDetail(url);
}

View file

@ -6,7 +6,7 @@ part of 'get_detail.dart';
// RiverpodGenerator
// **************************************************************************
String _$getDetailHash() => r'84cc79aa0fd35a2d8efa95f75b85978f521c5daa';
String _$getDetailHash() => r'6b758b79281cb00a7df2fe1903d4a67068052bca';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,12 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/models/source.dart';
List<dynamic> getFilterList({required Source source}) {
return getExtensionService(source).getFilterList().filters;
List<dynamic> getFilterList({
required Source source,
String androidProxyServer = "",
}) {
return getExtensionService(
source,
androidProxyServer,
).getFilterList().filters;
}

View file

@ -4,6 +4,7 @@ import 'package:epubx/epubx.dart';
import 'package:html/parser.dart';
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/utils/utils.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@ -48,11 +49,16 @@ Future<(String, EpubBook?)> getHtmlContent(
chapter.manga.value!.source!,
);
String? html;
final proxyServer = ref.read(androidProxyServerStateProvider);
if (htmlContent != null) {
html = await getExtensionService(source!).cleanHtmlContent(htmlContent);
html = await getExtensionService(
source!,
proxyServer,
).cleanHtmlContent(htmlContent);
} else {
html = await getExtensionService(
source!,
proxyServer,
).getHtmlContent(chapter.manga.value!.name!, chapter.url!);
}
return (_buildHtml(html.substring(1, html.length - 1)), null);

View file

@ -6,7 +6,7 @@ part of 'get_html_content.dart';
// RiverpodGenerator
// **************************************************************************
String _$getHtmlContentHash() => r'19e6959d8fceb065b19c6c6d38cd1b5132a8ba94';
String _$getHtmlContentHash() => r'c32670ed25b093761c867f5cf1cb5dfe063edc84';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,7 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/m_pages.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'get_latest_updates.g.dart';
@ -11,5 +12,8 @@ Future<MPages?> getLatestUpdates(
required Source source,
required int page,
}) async {
return getExtensionService(source).getLatestUpdates(page);
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).getLatestUpdates(page);
}

View file

@ -6,7 +6,7 @@ part of 'get_latest_updates.dart';
// RiverpodGenerator
// **************************************************************************
String _$getLatestUpdatesHash() => r'93e1ba376d14006110e9a6c06d191ffd12b1fdfb';
String _$getLatestUpdatesHash() => r'fd4ece1d796e079a469e5f80f456ee821ff0bc03';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,7 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/m_pages.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'get_popular.g.dart';
@ -11,5 +12,8 @@ Future<MPages?> getPopular(
required Source source,
required int page,
}) async {
return getExtensionService(source).getPopular(page);
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).getPopular(page);
}

View file

@ -6,7 +6,7 @@ part of 'get_popular.dart';
// RiverpodGenerator
// **************************************************************************
String _$getPopularHash() => r'02291ff9c3eba594b2344b853c34b2cea7be491b';
String _$getPopularHash() => r'5fd933ce7e2b9c2dd113b7642ed54c1a1196f638';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,11 +1,15 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_source_baseurl.g.dart';
@riverpod
String sourceBaseUrl(Ref ref, {required Source source}) {
return getExtensionService(source).sourceBaseUrl;
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).sourceBaseUrl;
}

View file

@ -6,7 +6,7 @@ part of 'get_source_baseurl.dart';
// RiverpodGenerator
// **************************************************************************
String _$sourceBaseUrlHash() => r'2eaf2f441085cec9e2f035763ef2ec64aa00f838';
String _$sourceBaseUrlHash() => r'ead3cca719e2530502d97613e3168e0031eecde7';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -2,6 +2,9 @@ import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/source_preference.dart';
import 'package:mangayomi/models/source.dart';
List<SourcePreference> getSourcePreference({required Source source}) {
return getExtensionService(source).getSourcePreferences();
List<SourcePreference> getSourcePreference({
required Source source,
String androidProxyServer = "",
}) {
return getExtensionService(source, androidProxyServer).getSourcePreferences();
}

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/services/torrent_server.dart';
import 'package:mangayomi/utils/utils.dart';
@ -58,6 +59,7 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
episode.manga.value!.lang!,
episode.manga.value!.source!,
);
final proxyServer = ref.read(androidProxyServerStateProvider);
if (source?.isTorrent ?? false || episode.manga.value!.source == "torrent") {
List<Video> list = [];
@ -72,7 +74,10 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
}
try {
list = await getExtensionService(source!).getVideoList(episode.url!);
list = await getExtensionService(
source!,
proxyServer,
).getVideoList(episode.url!);
} catch (e) {
list = [Video(episode.url!, episode.name!, episode.url!)];
}
@ -96,6 +101,7 @@ Future<(List<Video>, bool, List<String>)> getVideoList(
List<Video> list = await getExtensionService(
source!,
proxyServer,
).getVideoList(episode.url!);
List<Video> videos = [];

View file

@ -6,7 +6,7 @@ part of 'get_video_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$getVideoListHash() => r'aeed8a24962e960a374d6bc7294e798ad3d0c05e';
String _$getVideoListHash() => r'd39e325f21e68830c0692101e6efd95b2e04bcef';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,6 +1,7 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/m_pages.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'search.g.dart';
@ -13,5 +14,8 @@ Future<MPages?> search(
required int page,
required List<dynamic> filterList,
}) async {
return getExtensionService(source).search(query, page, filterList);
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).search(query, page, filterList);
}

View file

@ -6,7 +6,7 @@ part of 'search.dart';
// RiverpodGenerator
// **************************************************************************
String _$searchHash() => r'8ef361b28d7a569b9f5babd69eb83efd9d9814d7';
String _$searchHash() => r'b08d5a4b6e7d285830af7e5388b06fa61f175ede';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,12 +1,21 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/eval/model/m_pages.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'search_.g.dart';
Future<MPages?> search({
@riverpod
Future<MPages?> search(
Ref ref, {
required Source source,
required String query,
required int page,
required List<dynamic> filterList,
}) async {
return getExtensionService(source).search(query, page, filterList);
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).search(query, page, filterList);
}

208
lib/services/search_.g.dart Normal file
View file

@ -0,0 +1,208 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'search_.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$searchHash() => r'b08d5a4b6e7d285830af7e5388b06fa61f175ede';
/// 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));
}
}
/// See also [search].
@ProviderFor(search)
const searchProvider = SearchFamily();
/// See also [search].
class SearchFamily extends Family<AsyncValue<MPages?>> {
/// See also [search].
const SearchFamily();
/// See also [search].
SearchProvider call({
required Source source,
required String query,
required int page,
required List<dynamic> filterList,
}) {
return SearchProvider(
source: source,
query: query,
page: page,
filterList: filterList,
);
}
@override
SearchProvider getProviderOverride(
covariant SearchProvider provider,
) {
return call(
source: provider.source,
query: provider.query,
page: provider.page,
filterList: provider.filterList,
);
}
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'searchProvider';
}
/// See also [search].
class SearchProvider extends AutoDisposeFutureProvider<MPages?> {
/// See also [search].
SearchProvider({
required Source source,
required String query,
required int page,
required List<dynamic> filterList,
}) : this._internal(
(ref) => search(
ref as SearchRef,
source: source,
query: query,
page: page,
filterList: filterList,
),
from: searchProvider,
name: r'searchProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$searchHash,
dependencies: SearchFamily._dependencies,
allTransitiveDependencies: SearchFamily._allTransitiveDependencies,
source: source,
query: query,
page: page,
filterList: filterList,
);
SearchProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.source,
required this.query,
required this.page,
required this.filterList,
}) : super.internal();
final Source source;
final String query;
final int page;
final List<dynamic> filterList;
@override
Override overrideWith(
FutureOr<MPages?> Function(SearchRef provider) create,
) {
return ProviderOverride(
origin: this,
override: SearchProvider._internal(
(ref) => create(ref as SearchRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
source: source,
query: query,
page: page,
filterList: filterList,
),
);
}
@override
AutoDisposeFutureProviderElement<MPages?> createElement() {
return _SearchProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is SearchProvider &&
other.source == source &&
other.query == query &&
other.page == page &&
other.filterList == filterList;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
hash = _SystemHash.combine(hash, query.hashCode);
hash = _SystemHash.combine(hash, page.hashCode);
hash = _SystemHash.combine(hash, filterList.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin SearchRef on AutoDisposeFutureProviderRef<MPages?> {
/// The parameter `source` of this provider.
Source get source;
/// The parameter `query` of this provider.
String get query;
/// The parameter `page` of this provider.
int get page;
/// The parameter `filterList` of this provider.
List<dynamic> get filterList;
}
class _SearchProviderElement extends AutoDisposeFutureProviderElement<MPages?>
with SearchRef {
_SearchProviderElement(super.provider);
@override
Source get source => (origin as SearchProvider).source;
@override
String get query => (origin as SearchProvider).query;
@override
int get page => (origin as SearchProvider).page;
@override
List<dynamic> get filterList => (origin as SearchProvider).filterList;
}
// 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

@ -1,10 +1,14 @@
import 'package:mangayomi/eval/lib.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'supports_latest.g.dart';
@riverpod
bool supportsLatest(Ref ref, {required Source source}) {
return getExtensionService(source).supportsLatest;
return getExtensionService(
source,
ref.read(androidProxyServerStateProvider),
).supportsLatest;
}

View file

@ -6,7 +6,7 @@ part of 'supports_latest.dart';
// RiverpodGenerator
// **************************************************************************
String _$supportsLatestHash() => r'71f77f99b86f2f597ec728add2483a5623f4984a';
String _$supportsLatestHash() => r'e2d9b73adde86f78f1ab1c97d91ea2d3a59dc78d';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -14,6 +14,7 @@ Map<String, String> headers(
Ref ref, {
required String source,
required String lang,
String androidProxyServer = "",
}) {
final mSource = getSource(lang, source);
@ -26,7 +27,9 @@ Map<String, String> headers(
headers.addAll((jsonDecode(fromSource) as Map).toMapStringString!);
}
headers.addAll(getExtensionService(mSource).getHeaders());
headers.addAll(
getExtensionService(mSource, androidProxyServer).getHeaders(),
);
headers.addAll(MClient.getCookiesPref(mSource.baseUrl!));
}

View file

@ -6,7 +6,7 @@ part of 'headers.dart';
// RiverpodGenerator
// **************************************************************************
String _$headersHash() => r'47499544c2d972da6a0c20f41fe3fc46d1d7c1a0';
String _$headersHash() => r'a33ccbf1971e6b5da84f25a852c675a4d8821ea2';
/// Copied from Dart SDK
class _SystemHash {
@ -42,10 +42,12 @@ class HeadersFamily extends Family<Map<String, String>> {
HeadersProvider call({
required String source,
required String lang,
String androidProxyServer = "",
}) {
return HeadersProvider(
source: source,
lang: lang,
androidProxyServer: androidProxyServer,
);
}
@ -56,6 +58,7 @@ class HeadersFamily extends Family<Map<String, String>> {
return call(
source: provider.source,
lang: provider.lang,
androidProxyServer: provider.androidProxyServer,
);
}
@ -80,11 +83,13 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
HeadersProvider({
required String source,
required String lang,
String androidProxyServer = "",
}) : this._internal(
(ref) => headers(
ref as HeadersRef,
source: source,
lang: lang,
androidProxyServer: androidProxyServer,
),
from: headersProvider,
name: r'headersProvider',
@ -96,6 +101,7 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
allTransitiveDependencies: HeadersFamily._allTransitiveDependencies,
source: source,
lang: lang,
androidProxyServer: androidProxyServer,
);
HeadersProvider._internal(
@ -107,10 +113,12 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
required super.from,
required this.source,
required this.lang,
required this.androidProxyServer,
}) : super.internal();
final String source;
final String lang;
final String androidProxyServer;
@override
Override overrideWith(
@ -127,6 +135,7 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
debugGetCreateSourceHash: null,
source: source,
lang: lang,
androidProxyServer: androidProxyServer,
),
);
}
@ -140,7 +149,8 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
bool operator ==(Object other) {
return other is HeadersProvider &&
other.source == source &&
other.lang == lang;
other.lang == lang &&
other.androidProxyServer == androidProxyServer;
}
@override
@ -148,6 +158,7 @@ class HeadersProvider extends AutoDisposeProvider<Map<String, String>> {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
hash = _SystemHash.combine(hash, lang.hashCode);
hash = _SystemHash.combine(hash, androidProxyServer.hashCode);
return _SystemHash.finish(hash);
}
@ -161,6 +172,9 @@ mixin HeadersRef on AutoDisposeProviderRef<Map<String, String>> {
/// The parameter `lang` of this provider.
String get lang;
/// The parameter `androidProxyServer` of this provider.
String get androidProxyServer;
}
class _HeadersProviderElement
@ -171,6 +185,9 @@ class _HeadersProviderElement
String get source => (origin as HeadersProvider).source;
@override
String get lang => (origin as HeadersProvider).lang;
@override
String get androidProxyServer =>
(origin as HeadersProvider).androidProxyServer;
}
// 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