Merge branch 'temp/mihon' into feature/mihon

This commit is contained in:
Schnitzel5 2025-08-23 03:53:17 +02:00
commit 604e1b18d3
73 changed files with 3976 additions and 993 deletions

View file

@ -3,10 +3,12 @@ import 'package:mangayomi/models/source.dart';
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, androidProxyServer),
};
}

View file

@ -0,0 +1,85 @@
import 'package:mangayomi/models/manga.dart';
class MangaPages {
List<SManga> list;
bool hasNextPage;
MangaPages({required this.list, this.hasNextPage = false});
factory MangaPages.fromJson(Map<String, dynamic> json, ItemType itemType) {
final name = itemType == ItemType.anime ? "animes" : "mangas";
return MangaPages(
list: json[name] != null
? (json[name] as List).map((e) => SManga.fromJson(e)).toList()
: [],
hasNextPage: json['hasNextPage'],
);
}
Map<String, dynamic> toJson(ItemType itemType) => {
itemType == ItemType.anime ? "animes" : "mangas": list
.map((v) => v.toJson())
.toList(),
'hasNextPage': hasNextPage,
};
}
class SManga {
String? url;
String? title;
String? artist;
String? author;
String? description;
List<String>? genre;
Status? status;
String? thumbnailUrl;
SManga({
this.url,
this.title,
this.artist,
this.author,
this.description,
this.genre,
this.status = Status.unknown,
this.thumbnailUrl,
});
factory SManga.fromJson(Map<String, dynamic> json) {
return SManga(
url: json['url'],
title: json['title'],
artist: json['artist'],
author: json['author'],
description: json['description'],
genre: (json['genres'] as List?)?.map((e) => e.toString()).toList() ?? [],
status: switch (json['status'] as int?) {
1 => Status.ongoing,
2 => Status.completed,
4 => Status.publishingFinished,
5 => Status.canceled,
6 => Status.onHiatus,
_ => Status.unknown,
},
thumbnailUrl: json['thumbnail_url'],
);
}
Map<String, dynamic> toJson() {
return {
'url': url,
'title': title,
'artist': artist,
'author': author,
'description': description,
'genre': genre?.join(", "),
'status': status,
'thumbnail_url': thumbnailUrl,
};
}
}

339
lib/eval/mihon/service.dart Normal file
View file

@ -0,0 +1,339 @@
import 'dart:convert';
import 'dart:math';
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';
import 'package:mangayomi/eval/model/m_pages.dart';
import 'package:mangayomi/eval/model/source_preference.dart';
import 'package:mangayomi/models/page.dart';
import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/video.dart';
import 'package:mangayomi/services/http/m_client.dart';
import '../../models/manga.dart';
import '../interface.dart';
import 'models.dart';
class MihonExtensionService implements ExtensionService {
late String androidProxyServer;
@override
late Source source;
late InterceptedClient client;
MihonExtensionService(this.source, this.androidProxyServer) {
client = MClient.init();
}
@override
Map<String, String> getHeaders() {
return source.headers != null && source.headers!.isNotEmpty
? (jsonDecode(source.headers!) as Map?)?.toMapStringString ?? {}
: {};
}
@override
bool get supportsLatest {
return source.supportLatest ?? false;
}
@override
String get sourceBaseUrl {
return source.baseUrl!;
}
@override
Future<MPages> getPopular(int page) async {
final name = source.itemType == ItemType.anime ? "Anime" : "Manga";
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getPopular$name",
"page": page + 1,
"search": "",
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as Map<String, dynamic>;
final pages = MangaPages.fromJson(data, source.itemType);
return MPages(
list: pages.list
.map(
(e) => MManga(
name: e.title,
link: e.url,
artist: e.artist,
author: e.author,
description: e.description,
genre: e.genre,
status: e.status,
imageUrl: e.thumbnailUrl,
chapters: [],
),
)
.toList(),
hasNextPage: pages.hasNextPage,
);
}
@override
Future<MPages> getLatestUpdates(int page) async {
final name = source.itemType == ItemType.anime ? "Anime" : "Manga";
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getLatest$name",
"page": page + 1,
"search": "",
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as Map<String, dynamic>;
final pages = MangaPages.fromJson(data, source.itemType);
return MPages(
list: pages.list
.map(
(e) => MManga(
name: e.title,
link: e.url,
artist: e.artist,
author: e.author,
description: e.description,
genre: e.genre,
status: e.status,
imageUrl: e.thumbnailUrl,
chapters: [],
),
)
.toList(),
hasNextPage: pages.hasNextPage,
);
}
@override
Future<MPages> search(String query, int page, List<dynamic> filters) async {
final name = source.itemType == ItemType.anime ? "Anime" : "Manga";
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getSearch$name",
"page": max(1, page),
"search": query,
"filterList": _convertFilters(filters),
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as Map<String, dynamic>;
final pages = MangaPages.fromJson(data, source.itemType);
return MPages(
list: pages.list
.map(
(e) => MManga(
name: e.title,
link: e.url,
artist: e.artist,
author: e.author,
description: e.description,
genre: e.genre,
status: e.status,
imageUrl: e.thumbnailUrl,
chapters: [],
),
)
.toList(),
hasNextPage: pages.hasNextPage,
);
}
@override
Future<MManga> getDetail(String url) async {
final name = source.itemType == ItemType.anime ? "Anime" : "Manga";
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getDetails$name",
if (source.itemType == ItemType.manga) "mangaData": {"url": url},
if (source.itemType == ItemType.anime) "animeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as Map<String, dynamic>;
final chapters = await getChapterList(url);
return MManga(
name: data['title'],
link: data['url'],
artist: data['artist'],
author: data['author'],
description: data['description'],
genre: (data['genres'] as List?)?.map((e) => e.toString()).toList() ?? [],
status: switch (data['status'] as int?) {
1 => Status.ongoing,
2 => Status.completed,
4 => Status.publishingFinished,
5 => Status.canceled,
6 => Status.onHiatus,
_ => Status.unknown,
},
imageUrl: data['thumbnail_url'],
chapters: chapters,
);
}
Future<List<MChapter>> getChapterList(String url) async {
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": source.itemType == ItemType.anime
? "getEpisodeList"
: "getChapterList",
if (source.itemType == ItemType.manga) "mangaData": {"url": url},
if (source.itemType == ItemType.anime) "animeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as List;
return data
.map(
(e) => MChapter(
name: e['name'],
url: e['url'],
dateUpload: e['date_upload'] is int
? (e['date_upload'] as int).toString()
: e['date_upload'],
scanlator: e['scanlator'],
),
)
.toList();
}
@override
Future<List<PageUrl>> getPageList(String url) async {
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getPageList",
"chapterData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as List;
return data.map((e) => PageUrl(e['imageUrl'])).toList();
}
@override
Future<List<Video>> getVideoList(String url) async {
final res = await client.post(
Uri.parse("$androidProxyServer/dalvik"),
body: jsonEncode({
"method": "getVideoList",
"episodeData": {"url": url},
"preferences": getSourcePreferences(),
"data": source.sourceCode,
}),
);
final data = jsonDecode(res.body) as List;
return data.map((e) {
final tempHeaders =
e['headers']?['namesAndValues\$okhttp'] as List<dynamic>?;
final Map<String, String> headers = {};
if (tempHeaders != null) {
for (var i = 0; i + 1 < tempHeaders.length; i += 2) {
headers[tempHeaders[i]] = tempHeaders[i + 1];
}
}
return Video(
e['videoUrl'],
e['quality'],
e['url'],
headers: headers,
audios:
(e['audioTracks'] as List?)
?.map(
(e) => Track(
file: e['file'] ?? e['url'],
label: e['label'] ?? e['lang'],
),
)
.toList() ??
[],
subtitles:
(e['subtitleTracks'] as List?)
?.map(
(e) => Track(
file: e['file'] ?? e['url'],
label: e['label'] ?? e['lang'],
),
)
.toList() ??
[],
);
}).toList();
}
@override
Future<String> getHtmlContent(String name, String url) async {
return "";
}
@override
Future<String> cleanHtmlContent(String html) async {
return html;
}
@override
FilterList getFilterList() {
return source.getFilterList() ?? FilterList([]);
}
@override
List<SourcePreference> getSourcePreferences() {
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, "stateString": e.state, "type": "TextFilter"};
} else if (e is GroupFilter) {
yield {
"name": e.name,
"stateList": e.state.expand((e) sync* {
if (e is CheckBoxFilter) {
yield {
"name": e.name,
"stateBoolean": e.state,
"type": "CheckBoxFilter",
};
} else if (e is TriStateFilter) {
yield {
"name": e.name,
"stateInt": e.state,
"type": "TriStateFilter",
};
}
}).toList(),
"type": "GroupFilter",
};
} else if (e is SelectFilter) {
yield {"name": e.name, "stateInt": e.state, "type": "SelectFilter"};
} else if (e is SortFilter) {
yield {
"name": e.name,
"stateSort": {"ascending": e.state.ascending, "index": e.state.index},
"type": "SortFilter",
};
}
}).toList();
}
}

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:isar/isar.dart';
part 'source_preference.g.dart';
@ -139,7 +141,9 @@ class ListPreference {
return ListPreference(
title: json['title'],
summary: json['summary'],
valueIndex: json['valueIndex'],
valueIndex: json['valueIndex'] != null
? max(0, json['valueIndex'])
: null,
entries: json['entries']?.cast<String>(),
entryValues: json['entryValues']?.cast<String>(),
);

View file

@ -446,6 +446,8 @@
"manga_extensions_repo": "Manga extensions repo",
"anime_extensions_repo": "Anime extensions repo",
"novel_extensions_repo": "Novel extensions repo",
"custom_dns": "Custom DNS (leave blank to use system DNS)",
"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

@ -2751,6 +2751,18 @@ abstract class AppLocalizations {
/// **'Novel extensions repo'**
String get novel_extensions_repo;
/// No description provided for @custom_dns.
///
/// In en, this message translates to:
/// **'Custom DNS (leave blank to use system DNS)'**
String get custom_dns;
/// 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:
@ -3279,17 +3291,11 @@ abstract class AppLocalizations {
/// **'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries'**
String get clear_library_input;
/// No description provided for @enable_pip.
/// No description provided for @recommendations_similarity.
///
/// In en, this message translates to:
/// **'Enable Picture-in-Picture (PiP)'**
String get enable_pip;
/// No description provided for @enable_auto_pip.
///
/// In en, this message translates to:
/// **'Enter PiP automatically when moving out of app'**
String get enable_auto_pip;
/// **'Similarity:'**
String get recommendations_similarity;
}
class _AppLocalizationsDelegate

View file

@ -1412,6 +1412,12 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get novel_extensions_repo => 'مستودع إضافات الروايات';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'غير محدد';
@ -1698,9 +1704,5 @@ class AppLocalizationsAr extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1414,6 +1414,12 @@ class AppLocalizationsAs extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';
@ -1700,9 +1706,5 @@ class AppLocalizationsAs extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1423,6 +1423,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get novel_extensions_repo => 'Roman-Erweiterungs-Repository';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Nicht definiert';
@ -1711,9 +1717,5 @@ class AppLocalizationsDe extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1413,6 +1413,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';
@ -1699,9 +1705,5 @@ class AppLocalizationsEn extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1427,6 +1427,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositorio de extensiones de novelas';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indefinido';
@ -1716,11 +1722,7 @@ class AppLocalizationsEs extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}
/// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`).

View file

@ -1430,6 +1430,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get novel_extensions_repo => 'Dépôt d\'extensions de romans';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indéfini';
@ -1717,9 +1723,5 @@ class AppLocalizationsFr extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1415,6 +1415,12 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get novel_extensions_repo => 'Novel extensions repo';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'undefined';
@ -1701,9 +1707,5 @@ class AppLocalizationsHi extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1419,6 +1419,12 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositori ekstensi novel';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Tidak terdefinisi';
@ -1705,9 +1711,5 @@ class AppLocalizationsId extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1427,6 +1427,12 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repository delle estensioni romanzi';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Non definito';
@ -1714,9 +1720,5 @@ class AppLocalizationsIt extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1424,6 +1424,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get novel_extensions_repo => 'Repositório de extensões de romances';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Indefinido';
@ -1713,11 +1719,7 @@ class AppLocalizationsPt extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}
/// The translations for Portuguese, as used in Brazil (`pt_BR`).

View file

@ -1426,6 +1426,12 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get novel_extensions_repo => 'Репозиторий расширений новелл';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Не определено';
@ -1715,9 +1721,5 @@ class AppLocalizationsRu extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1413,6 +1413,12 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get novel_extensions_repo => 'ที่เก็บส่วนขยายโนเวล';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'ไม่ได้กำหนด';
@ -1699,9 +1705,5 @@ class AppLocalizationsTh extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1419,6 +1419,12 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get novel_extensions_repo => 'Roman uzantıları deposu';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => 'Tanımsız';
@ -1705,9 +1711,5 @@ class AppLocalizationsTr extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -1387,6 +1387,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get novel_extensions_repo => '小说扩展库';
@override
String get custom_dns => 'Custom DNS (leave blank to use system DNS)';
@override
String get android_proxy_server => 'Android Proxy Server (ApkBridge)';
@override
String get undefined => '未定义';
@ -1670,9 +1676,5 @@ class AppLocalizationsZh extends AppLocalizations {
'Type \'manga\', \'anime\' and/or \'novel\' (separated by a comma) to remove all related entries';
@override
String get enable_pip => 'Enable Picture-in-Picture (PiP)';
@override
String get enable_auto_pip =>
'Enter PiP automatically when moving out of app';
String get recommendations_similarity => 'Similarity:';
}

View file

@ -21,6 +21,7 @@ import 'package:mangayomi/models/source.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/providers/storage_provider.dart';
import 'package:mangayomi/router/router.dart';
@ -42,6 +43,7 @@ import 'package:flutter/services.dart' show rootBundle;
late Isar isar;
DiscordRPC? discordRpc;
WebViewEnvironment? webViewEnvironment;
String? customDns;
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isLinux && runWebViewTitleBarWidget(args)) return;
@ -102,6 +104,7 @@ class _MyAppState extends ConsumerState<MyApp> {
unawaited(ref.read(scanLocalLibraryProvider.future));
WidgetsBinding.instance.addPostFrameCallback((_) {
customDns = ref.read(customDnsStateProvider);
if (ref.read(clearChapterCacheOnAppLaunchStateProvider)) {
ref
.read(totalChapterCacheSizeStateProvider.notifier)

View file

@ -169,6 +169,8 @@ class Settings {
int? aniSkipTimeoutLength;
String? customDns;
String? btServerAddress;
int? btServerPort;
@ -201,6 +203,8 @@ class Settings {
List<Repo>? novelExtensionsRepo;
String? androidProxyServer;
@enumerated
late SectionType disableSectionType;
@ -277,7 +281,7 @@ class Settings {
int? volumeBoostCap;
bool? downloadedOnlyMode;
late AlgorithmWeights? algorithmWeights;
Settings({
@ -354,6 +358,7 @@ class Settings {
this.enableAniSkip,
this.enableAutoSkip,
this.aniSkipTimeoutLength,
this.customDns = "",
this.btServerAddress = "127.0.0.1",
this.btServerPort,
this.fullScreenReader = true,
@ -388,6 +393,7 @@ class Settings {
this.mangaExtensionsRepo,
this.animeExtensionsRepo,
this.novelExtensionsRepo,
this.androidProxyServer,
this.lastTrackerLibraryLocation,
this.mergeLibraryNavMobile = false,
this.enableDiscordRpc = true,
@ -548,6 +554,7 @@ class Settings {
enableAniSkip = json['enableAniSkip'];
enableAutoSkip = json['enableAutoSkip'];
aniSkipTimeoutLength = json['aniSkipTimeoutLength'];
customDns = json['customDns'];
btServerAddress = json['btServerAddress'];
btServerPort = json['btServerPort'];
customColorFilter = json['customColorFilter'] != null
@ -619,6 +626,7 @@ class Settings {
.map((e) => Repo.fromJson(e))
.toList();
}
androidProxyServer = json['androidProxyServer'];
lastTrackerLibraryLocation = json['lastTrackerLibraryLocation'];
mergeLibraryNavMobile = json['mergeLibraryNavMobile'];
enableDiscordRpc = json['enableDiscordRpc'];
@ -734,6 +742,7 @@ class Settings {
'enableAniSkip': enableAniSkip,
'enableAutoSkip': enableAutoSkip,
'aniSkipTimeoutLength': aniSkipTimeoutLength,
'customDns': customDns,
'btServerAddress': btServerAddress,
'btServerPort': btServerPort,
'fullScreenReader': fullScreenReader,
@ -771,6 +780,7 @@ class Settings {
'mangaExtensionsRepo': mangaExtensionsRepo?.map((e) => e.toJson()).toList(),
'animeExtensionsRepo': animeExtensionsRepo?.map((e) => e.toJson()).toList(),
'novelExtensionsRepo': novelExtensionsRepo?.map((e) => e.toJson()).toList(),
'androidProxyServer': androidProxyServer,
'lastTrackerLibraryLocation': lastTrackerLibraryLocation,
'mergeLibraryNavMobile': mergeLibraryNavMobile,
'enableDiscordRpc': enableDiscordRpc,
@ -989,20 +999,34 @@ class Repo {
String? name;
String? website;
String? jsonUrl;
bool? hidden;
Repo({this.name, this.website, this.jsonUrl});
Repo({this.name, this.website, this.jsonUrl, this.hidden});
Repo.fromJson(Map<String, dynamic> json) {
name = json['name'];
website = json['website'];
name = json['meta']?['name'] ?? json['name'];
website = json['meta']?['website'] ?? json['website'];
jsonUrl = json['jsonUrl'];
hidden = json['hidden'];
}
Map<String, dynamic> toJson() => {
'name': name,
'website': website,
'jsonUrl': jsonUrl,
'hidden': hidden,
};
@override
bool operator ==(Object other) {
return other is Repo &&
name == other.name &&
website == other.website &&
jsonUrl == other.jsonUrl;
}
@override
int get hashCode => Object.hash(name, website, jsonUrl);
}
@embedded

File diff suppressed because it is too large Load diff

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,15 @@ class Source {
String? headers;
/// For Mihon ext
bool? supportLatest;
/// For Mihon ext
String? filterList;
/// For Mihon ext
String? preferenceList;
bool? isManga;
@enumerated
@ -93,6 +105,9 @@ class Source {
this.versionLast = "0.0.1",
this.sourceCode = '',
this.headers = '',
this.supportLatest,
this.filterList,
this.preferenceList,
this.isManga,
this.itemType = ItemType.manga,
this.appMinVerReq = "",
@ -104,6 +119,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 +131,9 @@ class Source {
dateFormatLocale = json['dateFormatLocale'];
hasCloudflare = json['hasCloudflare'];
headers = json['headers'];
supportLatest = json['supportLatest'];
filterList = json['filterList'];
preferenceList = json['preferenceList'];
iconUrl = json['iconUrl'];
id = json['id'];
isActive = json['isActive'];
@ -147,6 +169,9 @@ class Source {
'dateFormatLocale': dateFormatLocale,
'hasCloudflare': hasCloudflare,
'headers': headers,
'supportLatest': supportLatest,
'filterList': filterList,
'preferenceList': preferenceList,
'iconUrl': iconUrl,
'id': id,
'isActive': isActive,
@ -191,4 +216,4 @@ class Source {
}
}
enum SourceCodeLanguage { dart, javascript }
enum SourceCodeLanguage { dart, javascript, mihon }

View file

@ -47,131 +47,146 @@ 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'preferenceList': PropertySchema(
id: 24,
name: r'preferenceList',
type: IsarType.string,
),
r'repo': PropertySchema(
id: 23,
id: 25,
name: r'repo',
type: IsarType.object,
target: r'Repo',
),
r'sourceCode': PropertySchema(
id: 24,
id: 26,
name: r'sourceCode',
type: IsarType.string,
),
r'sourceCodeLanguage': PropertySchema(
id: 25,
id: 27,
name: r'sourceCodeLanguage',
type: IsarType.byte,
enumMap: _SourcesourceCodeLanguageEnumValueMap,
),
r'sourceCodeUrl': PropertySchema(
id: 26,
id: 28,
name: r'sourceCodeUrl',
type: IsarType.string,
),
r'supportLatest': PropertySchema(
id: 29,
name: r'supportLatest',
type: IsarType.bool,
),
r'typeSource': PropertySchema(
id: 27,
id: 30,
name: r'typeSource',
type: IsarType.string,
),
r'updatedAt': PropertySchema(
id: 28,
id: 31,
name: r'updatedAt',
type: IsarType.long,
),
r'version': PropertySchema(
id: 29,
id: 32,
name: r'version',
type: IsarType.string,
),
r'versionLast': PropertySchema(
id: 30,
id: 33,
name: r'versionLast',
type: IsarType.string,
)
@ -232,6 +247,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) {
@ -262,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) {
@ -314,36 +341,39 @@ 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.writeString(offsets[24], object.preferenceList);
writer.writeObject<Repo>(
offsets[23],
offsets[25],
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[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(
@ -359,38 +389,41 @@ 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]),
preferenceList: reader.readStringOrNull(offsets[24]),
repo: reader.readObjectOrNull<Repo>(
offsets[23],
offsets[25],
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[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[25])] ??
reader.readByteOrNull(offsets[27])] ??
SourceCodeLanguage.dart;
return object;
}
@ -415,13 +448,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,40 +470,46 @@ 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.readStringOrNull(offset)) as P;
case 25:
return (reader.readObjectOrNull<Repo>(
offset,
RepoSchema.deserialize,
allOffsets,
)) as P;
case 24:
return (reader.readStringOrNull(offset)) as P;
case 25:
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;
return (_SourcesourceCodeLanguageValueEnumMap[
reader.readByteOrNull(offset)] ??
SourceCodeLanguage.dart) as P;
case 28:
return (reader.readLongOrNull(offset)) as P;
case 29:
return (reader.readStringOrNull(offset)) as P;
case 29:
return (reader.readBoolOrNull(offset)) as P;
case 30:
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');
}
@ -489,10 +528,12 @@ const _SourceitemTypeValueEnumMap = {
const _SourcesourceCodeLanguageEnumValueMap = {
'dart': 0,
'javascript': 1,
'mihon': 2,
};
const _SourcesourceCodeLanguageValueEnumMap = {
0: SourceCodeLanguage.dart,
1: SourceCodeLanguage.javascript,
2: SourceCodeLanguage.mihon,
};
Id _sourceGetId(Source object) {
@ -1469,6 +1510,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(
@ -2588,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(
@ -2952,6 +3287,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(
@ -3544,6 +3905,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);
@ -3748,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);
@ -3784,6 +4169,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);
@ -3906,6 +4303,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);
@ -4122,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);
@ -4158,6 +4579,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);
@ -4252,6 +4685,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');
@ -4359,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) {
@ -4380,6 +4828,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) {
@ -4451,6 +4905,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');
@ -4553,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');
@ -4578,6 +5044,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

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_qjs/quickjs/ffi.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/modules/widgets/custom_sliver_grouped_list_view.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/source.dart';
@ -66,6 +68,9 @@ class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
final streamExtensions = ref.watch(
getExtensionsStreamProvider(widget.itemType),
);
final repositories = ref.watch(
extensionsRepoStateProvider(widget.itemType),
);
final l10n = l10nLocalizations(context)!;
@ -92,6 +97,12 @@ class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
final notInstalledEntries = <Source>[];
for (var element in filteredData) {
if (repositories
.firstWhereOrNull((e) => e == element.repo)
?.hidden ??
false) {
continue;
}
final isLatestVersion = element.version == element.versionLast;
if (compareVersions(

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

@ -1,6 +1,7 @@
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/models/source.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -12,6 +13,7 @@ Stream<List<Source>> getExtensionsStream(Ref ref, ItemType itemType) async* {
.filter()
.idIsNotNull()
.and()
.repo((q) => q.hiddenIsNull().or().hiddenEqualTo(false))
.isActiveEqualTo(true)
.itemTypeEqualTo(itemType)
.watch(fireImmediately: true);

View file

@ -7,7 +7,7 @@ part of 'extensions_provider.dart';
// **************************************************************************
String _$getExtensionsStreamHash() =>
r'3c5d6625c40c222f25fc8141df078dd46bcc762f';
r'af34092ebf31c784010110af746e3ee2731297bd';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -45,15 +45,15 @@ class _SourcePreferenceWidgetState extends State<SourcePreferenceWidget> {
showDialog(
context: context,
builder: (context) => EditTextDialogWidget(
text: pref.value!,
text: pref.value ?? "",
onChanged: (value) {
setState(() {
pref.value = value;
});
setPreferenceSetting(preference, widget.source);
},
dialogTitle: pref.dialogTitle!,
dialogMessage: pref.dialogMessage!,
dialogTitle: pref.dialogTitle ?? "",
dialogMessage: pref.dialogMessage ?? "",
),
);
},

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

@ -28,7 +28,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,
);
@ -39,9 +39,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 ?? ""
@ -54,7 +56,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'85660b206c2bce558760118936758a261519cad8';
String _$updateMangaDetailHash() => r'05605c26d058a9176a351ba5a79f811987d4ba1a';
/// Copied from Dart SDK
class _SystemHash {

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

@ -21,6 +21,7 @@ class BrowseSScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final androidProxyServer = ref.watch(androidProxyServerStateProvider);
final onlyIncludePinnedSource = ref.watch(
onlyIncludePinnedSourceStateProvider,
);
@ -52,6 +53,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(
@ -254,6 +270,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
@ -43,6 +76,16 @@ class ExtensionsRepoState extends _$ExtensionsRepoState {
[];
}
void setVisibility(Repo repo, bool hidden) {
final value = state.map((e) {
if (e == repo) {
e.hidden = hidden;
}
return e;
}).toList();
set(value);
}
void set(List<Repo> value) {
final settings = isar.settings.getSync(227)!;
state = value;

View file

@ -157,6 +157,23 @@ class _GetRepoInfosProviderElement
String get jsonUrl => (origin as GetRepoInfosProvider).jsonUrl;
}
String _$androidProxyServerStateHash() =>
r'3ac060f8a61added586dcefc889fa44c71263c5b';
/// 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';
@ -175,7 +192,7 @@ final onlyIncludePinnedSourceStateProvider =
typedef _$OnlyIncludePinnedSourceState = AutoDisposeNotifier<bool>;
String _$extensionsRepoStateHash() =>
r'5c23b8b7ecf83b253b76a2663a71c0c752e53a40';
r'86edc9a3f78d72acda4b20a058031c345ee406eb';
abstract class _$ExtensionsRepoState
extends BuildlessAutoDisposeNotifier<List<Repo>> {

View file

@ -7,6 +7,8 @@ import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/services/fetch_item_sources.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:url_launcher/url_launcher.dart';
@ -19,8 +21,13 @@ class SourceRepositories extends ConsumerStatefulWidget {
}
class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
final urlRegex = RegExp(
r'^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$',
);
List<Repo> _entries = [];
String urlInput = "";
bool isRefreshing = false;
Future<void> _launchInBrowser(Uri url) async {
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
throw 'Could not launch $url';
@ -41,6 +48,43 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
ItemType.anime => Text(l10n.manage_anime_repo_urls),
_ => Text(l10n.manage_novel_repo_urls),
},
actions: [
isRefreshing
? const Padding(
padding: EdgeInsets.all(20.0),
child: SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 3),
),
)
: Padding(
padding: EdgeInsets.all(8.0),
child: IconButton(
splashRadius: 20,
onPressed: () async {
setState(() {
isRefreshing = true;
});
final result = await ref.refresh(
fetchItemSourcesListProvider(
id: null,
reFresh: true,
itemType: widget.itemType,
).future,
);
setState(() {
isRefreshing = false;
});
return result;
},
icon: Icon(
Icons.refresh,
color: Theme.of(context).hintColor,
),
),
),
],
),
body: data.when(
data: (data) {
@ -62,6 +106,12 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
itemCount: _entries.length,
itemBuilder: (context, index) {
final repo = _entries[index];
final isHidden = repo.hidden ?? false;
final repoAvatar = urlRegex
.firstMatch(repo.jsonUrl ?? "")
?.group(4)
?.split("/")
.elementAtOrNull(1);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Card(
@ -69,25 +119,53 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 15,
Opacity(
opacity: isHidden ? 0.3 : 1,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (repoAvatar != null)
Padding(
padding: EdgeInsets.all(8.0),
child: cachedNetworkImage(
imageUrl:
"https://github.com/$repoAvatar.png?size=64",
fit: BoxFit.contain,
width: 64,
height: 64,
errorWidget: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 15,
),
child: Icon(Icons.label_outline_rounded),
),
useCustomNetworkImage: false,
),
),
if (repoAvatar == null)
const Padding(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 15,
),
child: Icon(Icons.label_outline_rounded),
),
const SizedBox(width: 10),
Expanded(
child: Text(
repo.name ??
repo.jsonUrl ??
"Invalid source - remove it",
style: TextStyle(
decoration: isHidden
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
),
child: const Icon(Icons.label_outline_rounded),
),
const SizedBox(width: 10),
Expanded(
child: Text(
repo.name ??
repo.jsonUrl ??
"Invalid source - remove it",
),
),
],
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -112,72 +190,40 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
),
SizedBox(width: 10),
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(
l10n.remove_extensions_repo,
),
content: Text(
l10n.remove_extensions_repo,
),
actions: [
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(l10n.cancel),
),
const SizedBox(width: 15),
TextButton(
onPressed: () {
final mangaRepos = ref
.read(
extensionsRepoStateProvider(
widget.itemType,
),
)
.toList();
mangaRepos.removeWhere(
(url) =>
url ==
_entries[index],
);
ref
.read(
extensionsRepoStateProvider(
widget.itemType,
).notifier,
)
.set(mangaRepos);
ref.watch(
extensionsRepoStateProvider(
widget.itemType,
),
);
if (context.mounted) {
Navigator.pop(context);
}
},
child: Text(l10n.ok),
),
],
onPressed: () => ref
.read(
extensionsRepoStateProvider(
widget.itemType,
).notifier,
)
.setVisibility(repo, !isHidden),
icon: Stack(
children: [
const Icon(Icons.remove_red_eye_outlined),
if (!isHidden)
Positioned(
right: 8,
child: Transform.scale(
scaleX: 2.5,
child: const Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
'\\',
style: TextStyle(fontSize: 17),
),
],
);
},
);
},
);
},
),
),
),
],
),
),
SizedBox(width: 10),
IconButton(
onPressed: () =>
_showRemoveRepoDialog(context, index),
icon: const Icon(Icons.delete_outlined),
),
],
@ -207,144 +253,7 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
},
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
bool isLoading = false;
final controller = TextEditingController();
showDialog(
context: context,
builder: (context) {
return SizedBox(
child: StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(l10n.add_extensions_repo),
content: TextFormField(
controller: controller,
autofocus: true,
keyboardType: TextInputType.url,
onChanged: (value) => setState(() {}),
validator: (value) {
if (value == null || value.isEmpty) {
return l10n.url_cannot_be_empty;
}
if (!value.endsWith('.json')) {
return l10n.url_must_end_with_dot_json;
}
try {
final uri = Uri.parse(value);
if (!uri.isAbsolute) {
return l10n.invalid_url_format;
}
return null;
} catch (e) {
return l10n.invalid_url_format;
}
},
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(
hintText: l10n.url_must_end_with_dot_json,
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(),
),
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(l10n.cancel),
),
const SizedBox(width: 15),
StatefulBuilder(
builder: (context, setState) {
return TextButton(
onPressed:
controller.text.isEmpty ||
!controller.text.endsWith(".json")
? null
: () async {
setState(() => isLoading = true);
try {
final mangaRepos = ref
.read(
extensionsRepoStateProvider(
widget.itemType,
),
)
.toList();
final repo = await ref.read(
getRepoInfosProvider(
jsonUrl: controller.text,
).future,
);
if (repo == null) {
botToast(l10n.unsupported_repo);
return;
}
mangaRepos.add(repo);
ref
.read(
extensionsRepoStateProvider(
widget.itemType,
).notifier,
)
.set(mangaRepos);
} catch (e, s) {
setState(() => isLoading = false);
botToast('$e\n$s');
}
if (context.mounted) {
Navigator.pop(context);
}
},
child: isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(),
)
: Text(
l10n.add,
style: TextStyle(
color:
controller.text.isEmpty ||
!controller.text.endsWith(
".json",
)
? Theme.of(context).primaryColor
.withValues(alpha: 0.2)
: null,
),
),
);
},
),
],
),
],
);
},
),
);
},
);
},
onPressed: () => _showAddRepoDialog(context),
label: Row(
children: [
const Icon(Icons.add),
@ -355,4 +264,193 @@ class _SourceRepositoriesState extends ConsumerState<SourceRepositories> {
),
);
}
_showRemoveRepoDialog(BuildContext context, int index) {
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
final l10n = context.l10n;
return AlertDialog(
title: Text(l10n.remove_extensions_repo),
content: Text(l10n.remove_extensions_repo),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(l10n.cancel),
),
const SizedBox(width: 15),
TextButton(
onPressed: () {
final mangaRepos = ref
.read(extensionsRepoStateProvider(widget.itemType))
.toList();
mangaRepos.removeWhere((url) => url == _entries[index]);
ref
.read(
extensionsRepoStateProvider(
widget.itemType,
).notifier,
)
.set(mangaRepos);
ref.watch(extensionsRepoStateProvider(widget.itemType));
if (context.mounted) {
Navigator.pop(context);
}
},
child: Text(l10n.ok),
),
],
),
],
);
},
);
},
);
}
_showAddRepoDialog(BuildContext context) {
bool isLoading = false;
final controller = TextEditingController();
showDialog(
context: context,
builder: (context) {
return SizedBox(
child: StatefulBuilder(
builder: (context, setState) {
final l10n = context.l10n;
return AlertDialog(
title: Text(l10n.add_extensions_repo),
content: TextFormField(
controller: controller,
autofocus: true,
keyboardType: TextInputType.url,
onChanged: (value) => setState(() {}),
validator: (value) {
if (value == null || value.isEmpty) {
return l10n.url_cannot_be_empty;
}
if (!value.endsWith('.json')) {
return l10n.url_must_end_with_dot_json;
}
try {
final uri = Uri.parse(value);
if (!uri.isAbsolute) {
return l10n.invalid_url_format;
}
return null;
} catch (e) {
return l10n.invalid_url_format;
}
},
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(
hintText: l10n.url_must_end_with_dot_json,
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(),
),
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(l10n.cancel),
),
const SizedBox(width: 15),
StatefulBuilder(
builder: (context, setState) {
return TextButton(
onPressed:
controller.text.isEmpty ||
!controller.text.endsWith(".json")
? null
: () async {
setState(() => isLoading = true);
try {
final mangaRepos = ref
.read(
extensionsRepoStateProvider(
widget.itemType,
),
)
.toList();
final repo = await ref.read(
getRepoInfosProvider(
jsonUrl: controller.text,
).future,
);
if (repo == null) {
botToast(l10n.unsupported_repo);
return;
}
mangaRepos.add(repo);
ref
.read(
extensionsRepoStateProvider(
widget.itemType,
).notifier,
)
.set(mangaRepos);
} catch (e, s) {
setState(() => isLoading = false);
botToast('$e\n$s');
}
if (context.mounted) {
Navigator.pop(context);
}
},
child: isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(),
)
: Text(
l10n.add,
style: TextStyle(
color:
controller.text.isEmpty ||
!controller.text.endsWith(".json")
? Theme.of(context).primaryColor
.withValues(alpha: 0.2)
: null,
),
),
);
},
),
],
),
],
);
},
),
);
},
);
}
}

View file

@ -34,6 +34,7 @@ class _GeneralStateScreen extends ConsumerState<GeneralScreen> {
@override
Widget build(BuildContext context) {
final l10n = l10nLocalizations(context);
final customDns = ref.watch(customDnsStateProvider);
final enableDiscordRpc = ref.watch(enableDiscordRpcStateProvider);
final hideDiscordRpcInIncognito = ref.watch(
hideDiscordRpcInIncognitoStateProvider,
@ -48,6 +49,14 @@ class _GeneralStateScreen extends ConsumerState<GeneralScreen> {
body: SingleChildScrollView(
child: Column(
children: [
ListTile(
onTap: () => _showCustomDnsDialog(context, ref, customDns),
title: Text(l10n.custom_dns),
subtitle: Text(
customDns,
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
Container(
margin: const EdgeInsets.all(20.0),
padding: const EdgeInsets.all(10.0),
@ -290,4 +299,77 @@ class _GeneralStateScreen extends ConsumerState<GeneralScreen> {
),
);
}
void _showCustomDnsDialog(
BuildContext context,
WidgetRef ref,
String customDns,
) {
final dnsController = TextEditingController(text: customDns);
String dns = customDns;
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(
context.l10n.custom_dns,
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: dnsController,
autofocus: true,
onChanged: (value) => setState(() {
dns = value;
}),
decoration: InputDecoration(
hintText: "8.8.8.8",
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(customDnsStateProvider.notifier).set(dns);
Navigator.pop(context);
},
child: Text(context.l10n.dialog_confirm),
),
),
),
],
),
),
);
},
),
);
}
}

View file

@ -3,6 +3,26 @@ import 'package:mangayomi/models/settings.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'general_state_provider.g.dart';
@riverpod
class CustomDnsState extends _$CustomDnsState {
@override
String build() {
return isar.settings.getSync(227)!.customDns ?? "";
}
void set(String value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(
settings!
..customDns = value
..updatedAt = DateTime.now().millisecondsSinceEpoch,
),
);
}
}
@riverpod
class EnableDiscordRpcState extends _$EnableDiscordRpcState {
@override

View file

@ -6,6 +6,22 @@ part of 'general_state_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$customDnsStateHash() => r'6061c64d742b3f873e54c1b9ef724b7c0b6350a2';
/// See also [CustomDnsState].
@ProviderFor(CustomDnsState)
final customDnsStateProvider =
AutoDisposeNotifierProvider<CustomDnsState, String>.internal(
CustomDnsState.new,
name: r'customDnsStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$customDnsStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$CustomDnsState = AutoDisposeNotifier<String>;
String _$enableDiscordRpcStateHash() =>
r'ab8ce3b29f5d94aedbc88dcb87c7c834648270f5';

View file

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

View file

@ -1,7 +1,10 @@
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/eval/model/source_preference.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/models/settings.dart';
@ -25,7 +28,57 @@ Future<void> fetchSourcesList({
final info = await PackageInfo.fromPlatform();
final sourceList = (jsonDecode(req.body) as List)
.map((e) => Source.fromJson(e))
.expand((e) sync* {
if (e['name'] != null &&
e['pkg'] != null &&
e['version'] != null &&
e['code'] != null &&
e['lang'] != null &&
e['nsfw'] != null &&
e['sources'] != null &&
e['apk'] != null) {
final repoUrl = url.replaceAll("/index.min.json", "");
final sources = e['sources'] as List;
for (final source in sources) {
final src = Source.fromJson(e)
..apiUrl = ''
..appMinVerReq = ''
..dateFormat = ''
..dateFormatLocale = ''
..hasCloudflare = false
..headers = ''
..isActive = true
..isAdded = false
..isFullData = false
..isNsfw = e['nsfw'] == 1
..isPinned = false
..lastUsed = false
..sourceCode = ''
..typeSource = ''
..versionLast = '0.0.1'
..isObsolete = false
..isLocal = false
..name = source['name']
..lang = source['lang']
..baseUrl = source['baseUrl']
..sourceCodeUrl = "$repoUrl/apk/${e['apk']}"
..sourceCodeLanguage = SourceCodeLanguage.mihon
..itemType =
(e['pkg'] as String).startsWith(
"eu.kanade.tachiyomi.animeextension",
)
? ItemType.anime
: ItemType.manga
..iconUrl = "$repoUrl/icon/${e['pkg']}.png"
..notes =
"Requires Android Proxy Server (ApkBridge) for installing and using the extensions!";
src.id = 'mihon-${source['id']}'.hashCode;
yield src;
}
} else {
yield Source.fromJson(e);
}
})
.where(
(source) =>
source.itemType == itemType &&
@ -74,14 +127,51 @@ Future<void> _updateSource(
) async {
final http = MClient.init(reqcopyWith: {'useDartHttpClient': true});
final req = await http.get(Uri.parse(source.sourceCodeUrl!));
final headers = getExtensionService(
source..sourceCode = req.body,
).getHeaders();
final sourceCode = source.sourceCodeLanguage == SourceCodeLanguage.mihon
? base64.encode(req.bodyBytes)
: req.body;
final androidProxyServer = ref.read(androidProxyServerStateProvider);
Map<String, String> headers = {};
bool? supportLatest;
FilterList? filterList;
List<SourcePreference>? preferenceList;
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,
);
preferenceList = await fetchPreferencesDalvik(
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
..preferenceList = preferenceList != null
? jsonEncode(preferenceList.map((e) => e.toJson()).toList())
: null
..isAdded = true
..sourceCode = req.body
..sourceCode = sourceCode
..sourceCodeUrl = source.sourceCodeUrl
..id = source.id
..apiUrl = source.apiUrl
@ -200,3 +290,160 @@ 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;
}
}
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;
}
}

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:mangayomi/modules/manga/reader/u_chap_data_preload.dart';
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';
@ -10,6 +11,8 @@ import 'package:mangayomi/utils/extensions/string_extensions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path/path.dart' as p;
import '../models/source.dart';
part 'get_video_list.g.dart';
@riverpod
@ -60,8 +63,14 @@ Future<(List<Video>, bool, List<String>, Directory?)> getVideoList(
episode.manga.value!.lang!,
episode.manga.value!.source!,
);
final proxyServer = ref.read(androidProxyServerStateProvider);
if (source?.isTorrent ?? false || episode.manga.value!.source == "torrent") {
final isMihonTorrent =
source?.sourceCodeLanguage == SourceCodeLanguage.mihon &&
source!.name!.contains("(Torrent");
if ((source?.isTorrent ?? false) ||
episode.manga.value!.source == "torrent" ||
isMihonTorrent) {
List<Video> list = [];
List<Video> torrentList = [];
@ -74,7 +83,10 @@ Future<(List<Video>, bool, List<String>, Directory?)> 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!)];
}
@ -98,6 +110,7 @@ Future<(List<Video>, bool, List<String>, Directory?)> 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'140ac1ca572d6220b7791c4350a0b32e275535a4';
String _$getVideoListHash() => r'1fe7493f84a661cb7a3a2f1ce1e0b62e53801096';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -54,8 +54,20 @@ class MClient {
rhttp.ClientSettings? settings,
bool showCloudFlareError = true,
}) {
final clientSettings = customDns == null || customDns!.trim().isEmpty
? settings
: settings?.copyWith(
dnsSettings: DnsSettings.dynamic(
resolver: (host) async => [customDns!],
),
) ??
ClientSettings(
dnsSettings: DnsSettings.dynamic(
resolver: (host) async => [customDns!],
),
);
return InterceptedClient.build(
client: httpClient(settings: settings, reqcopyWith: reqcopyWith),
client: httpClient(settings: clientSettings, reqcopyWith: reqcopyWith),
retryPolicy: ResolveCloudFlareChallenge(showCloudFlareError),
interceptors: [
MCookieManager(reqcopyWith),

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

@ -6,7 +6,7 @@ part of 'sync_server.dart';
// RiverpodGenerator
// **************************************************************************
String _$syncServerHash() => r'141ba3be28182e05480e06fbf3f1de68f868cb8e';
String _$syncServerHash() => r'08225f80e9c249dc62e8e918acecbd593f54541f';
/// Copied from Dart SDK
class _SystemHash {

View file

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

View file

@ -6,7 +6,7 @@ part of 'kitsu.dart';
// RiverpodGenerator
// **************************************************************************
String _$kitsuHash() => r'e24e9b57cfea974110d1f7c704c306c3b58e3529';
String _$kitsuHash() => r'd46b955c92bc4d7382d32e17827da2e2b3a8434f';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -6,7 +6,7 @@ part of 'myanimelist.dart';
// RiverpodGenerator
// **************************************************************************
String _$myAnimeListHash() => r'4391ad9446d14b1fb1ffdfbc5323ef04db5140f7';
String _$myAnimeListHash() => r'739c836ddbfc7c2c2b7593304f481e8d35074391';
/// 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