add source preference

This commit is contained in:
kodjomoustapha
2023-12-04 15:23:34 +01:00
parent e0c2be06a3
commit a2abfc537f
22 changed files with 722 additions and 325 deletions

View File

@@ -321,7 +321,7 @@ class DopeFlix extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SelectFilter("TypeFilter", "Type", 0, [ SelectFilter("TypeFilter", "Type", 0, [
SelectFilterOption("All", "all"), SelectFilterOption("All", "all"),
@@ -419,6 +419,7 @@ class DopeFlix extends MProvider {
@override @override
List<dynamic> getSourcePreferences(MSource source) { List<dynamic> getSourcePreferences(MSource source) {
return [ return [
if (source.name == "DopeBox")
ListPreference( ListPreference(
key: "preferred_domain", key: "preferred_domain",
title: "Preferred domain", title: "Preferred domain",
@@ -426,6 +427,14 @@ class DopeFlix extends MProvider {
valueIndex: 0, valueIndex: 0,
entries: ["dopebox.to", "dopebox.se"], entries: ["dopebox.to", "dopebox.se"],
entryValues: ["https://dopebox.to", "https://dopebox.se"]), entryValues: ["https://dopebox.to", "https://dopebox.se"]),
if (source.name == "SFlix")
ListPreference(
key: "preferred_domain",
title: "Preferred domain",
summary: "",
valueIndex: 0,
entries: ["sflix.to", "sflix.se"],
entryValues: ["https://sflix.to", "https://sflix.se"]),
ListPreference( ListPreference(
key: "preferred_quality", key: "preferred_quality",
title: "Preferred Quality", title: "Preferred Quality",

View File

@@ -292,7 +292,7 @@ class ZoroTheme extends MProvider {
]; ];
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SelectFilter("TypeFilter", "Type", 0, [ SelectFilter("TypeFilter", "Type", 0, [
SelectFilterOption("All", ""), SelectFilterOption("All", ""),

View File

@@ -10,12 +10,11 @@ class OkAnime extends MProvider {
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
List<MManga> animeList = []; List<MManga> animeList = [];
final urls = xpath(res, String path =
'//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); '//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]';
final names = xpath(res, final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
'//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
final images = xpath(res, final images = xpath(res, '$path/div[@class="anime-image")]/img/@src');
'//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]/div[@class="anime-image")]/img/@src');
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
MManga anime = MManga(); MManga anime = MManga();
@@ -33,12 +32,10 @@ class OkAnime extends MProvider {
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
List<MManga> animeList = []; List<MManga> animeList = [];
final urls = xpath(res, String path = '//*[contains(@class,"anime-card")]';
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
final names = xpath(res, final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); final images = xpath(res, '$path/div[@class="episode-image")]/img/@src');
final images = xpath(res,
'//*[contains(@class,"anime-card")]/div[@class="episode-image")]/img/@src');
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
MManga anime = MManga(); MManga anime = MManga();
@@ -63,12 +60,10 @@ class OkAnime extends MProvider {
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
List<MManga> animeList = []; List<MManga> animeList = [];
final urls = xpath(res, String path = '//*[contains(@class,"anime-card")]';
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/@href'); final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
final names = xpath(res, final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h4/a/text()'); final images = xpath(res, '$path/div[@class="anime-image")]/img/@src');
final images = xpath(res,
'//*[contains(@class,"anime-card")]/div[@class="anime-image")]/img/@src');
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
MManga anime = MManga(); MManga anime = MManga();
@@ -125,29 +120,81 @@ class OkAnime extends MProvider {
final urls = xpath(res, '//*[@id="streamlinks"]/a/@data-src'); final urls = xpath(res, '//*[@id="streamlinks"]/a/@data-src');
final qualities = xpath(res, '//*[@id="streamlinks"]/a/span/text()'); final qualities = xpath(res, '//*[@id="streamlinks"]/a/span/text()');
final hosterSelection = preferenceHosterSelection(source.id);
List<MVideo> videos = []; List<MVideo> videos = [];
for (var i = 0; i < urls.length; i++) { for (var i = 0; i < urls.length; i++) {
final url = urls[i]; final url = urls[i];
final quality = getQuality(qualities[i]); final quality = getQuality(qualities[i]);
List<MVideo> a = []; List<MVideo> a = [];
if (url.contains("https://doo")) { if (url.contains("https://doo") && hosterSelection.contains("Dood")) {
a = await doodExtractor(url, "DoodStream - $quality"); a = await doodExtractor(url, "DoodStream - $quality");
} else if (url.contains("mp4upload")) { } else if (url.contains("mp4upload") &&
hosterSelection.contains("Mp4upload")) {
a = await mp4UploadExtractor(url, null, "", ""); a = await mp4UploadExtractor(url, null, "", "");
} else if (url.contains("ok.ru")) { } else if (url.contains("ok.ru") && hosterSelection.contains("Okru")) {
a = await okruExtractor(url); a = await okruExtractor(url);
} else if (url.contains("voe.sx")) { } else if (url.contains("voe.sx") && hosterSelection.contains("Voe")) {
a = await voeExtractor(url, "VoeSX $quality"); a = await voeExtractor(url, "VoeSX $quality");
} else if (containsVidBom(url)) { } else if (containsVidBom(url) && hosterSelection.contains("VidBom")) {
a = await vidBomExtractor(url); a = await vidBomExtractor(url);
} }
videos.addAll(a); videos.addAll(a);
} }
return sortVideos(videos, source.id);
}
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
ListPreference(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 1,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080", "720", "480", "360"]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"],
entryValues: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"],
values: ["Dood", "Voe", "Mp4upload", "VidBom", "Okru"]),
];
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
videos.sort((MVideo a, MVideo b) {
int qualityMatchA = 0;
if (a.quality.contains(quality)) {
qualityMatchA = 1;
}
int qualityMatchB = 0;
if (b.quality.contains(quality)) {
qualityMatchB = 1;
}
if (qualityMatchA != qualityMatchB) {
return qualityMatchB - qualityMatchA;
}
final regex = RegExp(r'(\d+)p');
final matchA = regex.firstMatch(a.quality);
final matchB = regex.firstMatch(b.quality);
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
return qualityNumB - qualityNumA;
});
return videos; return videos;
} }
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
String getQuality(String quality) { String getQuality(String quality) {
quality = quality.replaceAll(" ", ""); quality = quality.replaceAll(" ", "");
if (quality == "HD") { if (quality == "HD") {

View File

@@ -312,7 +312,7 @@ class Aniwave extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SelectFilter("OrderFilter", "Sort order", 0, [ SelectFilter("OrderFilter", "Sort order", 0, [
SelectFilterOption("Most relevance", "most_relevance"), SelectFilterOption("Most relevance", "most_relevance"),

View File

@@ -251,7 +251,7 @@ class GogoAnime extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
HeaderFilter("Advanced search"), HeaderFilter("Advanced search"),
GroupFilter("GenreFilter", "Genre", [ GroupFilter("GenreFilter", "Genre", [

View File

@@ -136,6 +136,7 @@ class FrAnime extends MProvider {
} else if (language == "vf" && hasVf) { } else if (language == "vf" && hasVf) {
players = vfPlayers; players = vfPlayers;
} }
print(players);
List<MVideo> videos = []; List<MVideo> videos = [];
for (var i = 0; i < players.length; i++) { for (var i = 0; i < players.length; i++) {
String apiUrl = "$videoBaseUrl/$language/$i"; String apiUrl = "$videoBaseUrl/$language/$i";
@@ -155,8 +156,6 @@ class FrAnime extends MProvider {
..url = playerUrl ..url = playerUrl
..originalUrl = playerUrl ..originalUrl = playerUrl
..quality = "FRAnime (Vido)"); ..quality = "FRAnime (Vido)");
} else if (playerName.contains("myvi")) {
a = await myTvExtractor(playerUrl);
} else if (playerName.contains("sendvid")) { } else if (playerName.contains("sendvid")) {
a = await sendVidExtractor( a = await sendVidExtractor(
playerUrl, json.encode({"Referer": "https://franime.fr/"}), ""); playerUrl, json.encode({"Referer": "https://franime.fr/"}), "");
@@ -191,7 +190,7 @@ class FrAnime extends MProvider {
} }
} }
final titleO = animeJson["titleO"]; String titleO = animeJson["titleO"];
final title = animeJson["title"]; final title = animeJson["title"];
final genre = animeJson["themes"]; final genre = animeJson["themes"];
final description = animeJson["description"]; final description = animeJson["description"];
@@ -228,7 +227,7 @@ class FrAnime extends MProvider {
anime.name = seasonTitle; anime.name = seasonTitle;
anime.imageUrl = imageUrl; anime.imageUrl = imageUrl;
anime.link = anime.link =
"/anime/${regExp(titleO, "[^A-Za-z0-9 ]", "", 0, 0).replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; "/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind";
animeList.add(anime); animeList.add(anime);
} }
@@ -276,7 +275,7 @@ class FrAnime extends MProvider {
vfListName.add(vf["lecteurs"].isNotEmpty); vfListName.add(vf["lecteurs"].isNotEmpty);
} }
} }
final titleO = animeJson["titleO"]; String titleO = animeJson["titleO"];
final title = animeJson["title"]; final title = animeJson["title"];
final genre = animeJson["themes"]; final genre = animeJson["themes"];
final description = animeJson["description"]; final description = animeJson["description"];
@@ -314,7 +313,7 @@ class FrAnime extends MProvider {
anime.name = seasonTitle; anime.name = seasonTitle;
anime.imageUrl = imageUrl; anime.imageUrl = imageUrl;
anime.link = anime.link =
"/anime/${regExp(titleO, "[^A-Za-z0-9 ]", "", 0, 0).replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind"; "/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind";
animeList.add(anime); animeList.add(anime);
} }
@@ -335,12 +334,11 @@ class FrAnime extends MProvider {
String databaseAnimeByTitleO(String res, String titleO) { String databaseAnimeByTitleO(String res, String titleO) {
print(titleO); print(titleO);
final datas = json.decode(res) as List; final datas = json.decode(res) as List<Map<String, dynamic>>;
for (var data in datas) { for (var data in datas) {
if (regExp(data["titleO"], "[^A-Za-z0-9 ]", "", 0, 0) String title =
.replaceAll(" ", "-") (data["titleO"] as String).replaceAll(RegExp("[^A-Za-z0-9 ]"), "");
.toLowerCase() == if (title.replaceAll(" ", "-").toLowerCase() == "${titleO}") {
"${titleO}") {
return json.encode(data); return json.encode(data);
} }
} }

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
Source get franimeSource => _franimeSource; Source get franimeSource => _franimeSource;
const franimeVersion = "0.0.4"; const franimeVersion = "0.0.45";
const franimeSourceCodeUrl = const franimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/franime/franime-v$franimeVersion.dart";
Source _franimeSource = Source( Source _franimeSource = Source(

View File

@@ -1,197 +0,0 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class OtakuFr extends MProvider {
OtakuFr();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=."
};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href');
final names =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title');
final images =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/page/$page/"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="episode"]/div/a/@href');
final namess = xpath(res, '//*[@class="episode"]/div/a/text()');
List<String> names = [];
for (var name in namess) {
names.add(regExp(
name,
r'(?<=\bS\d\s*|)\d{2}\s*(?=\b(Vostfr|vostfr|VF|Vf|vf|\(VF\)|\(vf\)|\(Vf\)|\(Vostfr\)\b))?',
'',
0,
0)
.replaceAll(' vostfr', '')
.replaceAll(' Vostfr', '')
.replaceAll(' VF', '')
.replaceAll(' Vf', '')
.replaceAll(' vf', '')
.replaceAll(' (VF)', '')
.replaceAll(' (vf)', '')
.replaceAll(' (vf)', '')
.replaceAll(' (Vf)', '')
.replaceAll(' (Vostfr)', ''));
}
final images = xpath(res, '//*[@class="episode"]/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final data = {
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query"
};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href');
final names =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title');
final images =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{
"En cours": 0,
"Terminé": 1,
}
];
final data = {"url": url};
String res = await http('GET', json.encode(data));
MManga anime = MManga();
final originalUrl = xpath(res,
'//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href')
.first;
if (originalUrl.isNotEmpty) {
final newData = {"url": originalUrl};
res = await http('GET', json.encode(newData));
}
anime.description = xpath(res, '//*[@class="episode fz-sm synop"]/p/text()')
.first
.replaceAll("Synopsis:", "");
final status = xpath(res,
'//*[@class="list-unstyled"]/li[contains(text(),"Statut")]/text()')
.first
.replaceAll("Statut: ", "");
anime.status = parseStatus(status, statusList);
anime.genre = xpath(res,
'//*[@class="list-unstyled"]/li[contains(text(),"Genre")]/ul/li/a/text()');
final epUrls = xpath(res, '//*[@class="list-episodes list-group"]/a/@href');
final dates =
xpath(res, '//*[@class="list-episodes list-group"]/a/span/text()');
final names = xpath(res, '//*[@class="list-episodes list-group"]/a/text()');
List<String> episodes = [];
for (var i = 0; i < names.length; i++) {
final date = dates[i];
final name = names[i];
episodes.add(
"Episode ${regExp(name.replaceAll(date, ""), r".* (\d*) [VvfF]{1,1}", '', 1, 1)}");
}
final dateUploads = parseDates(dates, "dd MMMM yyyy", "fr");
List<MChapter>? episodesList = [];
for (var i = 0; i < episodes.length; i++) {
MChapter episode = MChapter();
episode.name = episodes[i];
episode.url = epUrls[i];
episode.dateUpload = dateUploads[i];
episodesList.add(episode);
}
anime.chapters = episodesList;
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final res = await http('GET', json.encode({"url": url}));
final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src');
List<MVideo> videos = [];
for (var url in servers) {
final datasServer = {
"url": fixUrl(url),
"headers": {"X-Requested-With": "XMLHttpRequest"}
};
final resServer = await http('GET', json.encode(datasServer));
final serverUrl =
fixUrl(regExp(resServer, r"data-url='([^']+)'", '', 1, 1));
List<MVideo> a = [];
if (serverUrl.contains("https://streamwish")) {
a = await streamWishExtractor(serverUrl, "StreamWish");
} else if (serverUrl.contains("sibnet")) {
a = await sibnetExtractor(serverUrl);
} else if (serverUrl.contains("https://doo")) {
a = await doodExtractor(serverUrl);
} else if (serverUrl.contains("https://voe.sx")) {
a = await voeExtractor(serverUrl, null);
} else if (serverUrl.contains("https://ok.ru")) {
a = await okruExtractor(serverUrl);
}
videos.addAll(a);
}
return videos;
}
String fixUrl(String url) {
return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0);
}
}
OtakuFr main() {
return OtakuFr();
}

View File

@@ -0,0 +1,448 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class OtakuFr extends MProvider {
OtakuFr();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {
"url": "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=."
};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href');
final names =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title');
final images =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/page/$page/"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="episode"]/div/a/@href');
final namess = xpath(res, '//*[@class="episode"]/div/a/text()');
List<String> names = [];
for (var name in namess) {
names.add(regExp(
name,
r'(?<=\bS\d\s*|)\d{2}\s*(?=\b(Vostfr|vostfr|VF|Vf|vf|\(VF\)|\(vf\)|\(Vf\)|\(Vostfr\)\b))?',
'',
0,
0)
.replaceAll(' vostfr', '')
.replaceAll(' Vostfr', '')
.replaceAll(' VF', '')
.replaceAll(' Vf', '')
.replaceAll(' vf', '')
.replaceAll(' (VF)', '')
.replaceAll(' (vf)', '')
.replaceAll(' (vf)', '')
.replaceAll(' (Vf)', '')
.replaceAll(' (Vostfr)', ''));
}
final images = xpath(res, '//*[@class="episode"]/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters;
String url = "";
if (query.isNotEmpty) {
url = "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query";
} else {
for (var filter in filters) {
if (filter.type == "GenreFilter") {
if (filter.state != 0) {
url =
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
}
} else if (filter.type == "SubPageFilter") {
if (url.isEmpty) {
if (filter.state != 0) {
url =
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
}
}
}
}
}
final data = {"url": url};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/@href');
final names =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@title');
final images =
xpath(res, '//*[@class="list"]/article/div/div/figure/a/img/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = names[i];
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
final nextPage = xpath(res, '//a[@class="next page-link"]/@href');
return MPages(animeList, nextPage.isNotEmpty);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"En cours": 0, "Terminé": 1}
];
final data = {"url": url};
String res = await http('GET', json.encode(data));
MManga anime = MManga();
final originalUrl = xpath(res,
'//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href');
if (originalUrl.isNotEmpty) {
final newData = {"url": originalUrl.first};
res = await http('GET', json.encode(newData));
}
final description =
xpath(res, '//*[@class="episode fz-sm synop"]/p/text()');
if (description.isNotEmpty) {
anime.description = description.first.replaceAll("Synopsis:", "");
}
final status = xpath(res,
'//*[@class="list-unstyled"]/li[contains(text(),"Statut")]/text()');
if (status.isNotEmpty) {
anime.status =
parseStatus(status.first.replaceAll("Statut: ", ""), statusList);
}
anime.genre = xpath(res,
'//*[@class="list-unstyled"]/li[contains(text(),"Genre")]/ul/li/a/text()');
final epUrls = xpath(res, '//*[@class="list-episodes list-group"]/a/@href');
final dates =
xpath(res, '//*[@class="list-episodes list-group"]/a/span/text()');
final names = xpath(res, '//*[@class="list-episodes list-group"]/a/text()');
List<String> episodes = [];
for (var i = 0; i < names.length; i++) {
final date = dates[i];
final name = names[i];
episodes.add(
"Episode ${regExp(name.replaceAll(date, ""), r".* (\d*) [VvfF]{1,1}", '', 1, 1)}");
}
final dateUploads = parseDates(dates, "dd MMMM yyyy", "fr");
List<MChapter>? episodesList = [];
for (var i = 0; i < episodes.length; i++) {
MChapter episode = MChapter();
episode.name = episodes[i];
episode.url = epUrls[i];
episode.dateUpload = dateUploads[i];
episodesList.add(episode);
}
anime.chapters = episodesList;
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final res = await http('GET', json.encode({"url": url}));
final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src');
List<MVideo> videos = [];
final hosterSelection = preferenceHosterSelection(source.id);
for (var url in servers) {
final datasServer = {
"url": fixUrl(url),
"headers": {"X-Requested-With": "XMLHttpRequest"}
};
final resServer = await http('GET', json.encode(datasServer));
final serverUrl =
fixUrl(regExp(resServer, r"data-url='([^']+)'", '', 1, 1));
List<MVideo> a = [];
if (serverUrl.contains("https://streamwish") &&
hosterSelection.contains("Streamwish")) {
a = await streamWishExtractor(serverUrl, "StreamWish");
} else if (serverUrl.contains("sibnet") &&
hosterSelection.contains("Sibnet")) {
a = await sibnetExtractor(serverUrl);
} else if (serverUrl.contains("https://doo") &&
hosterSelection.contains("Doodstream")) {
a = await doodExtractor(serverUrl);
} else if (serverUrl.contains("https://voe.sx") &&
hosterSelection.contains("Voe")) {
a = await voeExtractor(serverUrl, null);
} else if (serverUrl.contains("https://ok.ru") &&
hosterSelection.contains("Okru")) {
a = await okruExtractor(serverUrl);
} else if (serverUrl.contains("vadbam") &&
hosterSelection.contains("Vidbm")) {
a = await vidbmExtractor(serverUrl);
} else if (serverUrl.contains("upstream") &&
hosterSelection.contains("Upstream")) {
a = await upstreamExtractor(serverUrl);
} else if (serverUrl.contains("sendvid") &&
hosterSelection.contains("Sendvid")) {
a = await sendVidExtractor(serverUrl, null, "");
}
videos.addAll(a);
}
return videos;
}
String fixUrl(String url) {
return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0);
}
@override
List<dynamic> getFilterList(MSource source) {
return [
HeaderFilter("La recherche de texte ignore les filtres"),
SelectFilter("GenreFilter", "Genre", 0, [
SelectFilterOption("<Selectionner>", ""),
SelectFilterOption("Action", "/genre/action/"),
SelectFilterOption("Aventure", "/genre/aventure/"),
SelectFilterOption("Comedie", "/genre/comedie/"),
SelectFilterOption("Crime", "/genre/crime/"),
SelectFilterOption("Démons", "/genre/demons/"),
SelectFilterOption("Drame", "/genre/drame/"),
SelectFilterOption("Ecchi", "/genre/ecchi/"),
SelectFilterOption("Espace", "/genre/espace/"),
SelectFilterOption("Fantastique", "/genre/fantastique/"),
SelectFilterOption("Gore", "/genre/gore/"),
SelectFilterOption("Harem", "/genre/harem/"),
SelectFilterOption("Historique", "/genre/historique/"),
SelectFilterOption("Horreur", "/genre/horreur/"),
SelectFilterOption("Isekai", "/genre/isekai/"),
SelectFilterOption("Jeux", "/genre/jeu/"),
SelectFilterOption("L'école", "/genre/lecole/"),
SelectFilterOption("Magical girls", "/genre/magical-girls/"),
SelectFilterOption("Magie", "/genre/magie/"),
SelectFilterOption("Martial Arts", "/genre/martial-arts/"),
SelectFilterOption("Mecha", "/genre/mecha/"),
SelectFilterOption("Militaire", "/genre/militaire/"),
SelectFilterOption("Musique", "/genre/musique/"),
SelectFilterOption("Mysterieux", "/genre/mysterieux/"),
SelectFilterOption("Parodie", "/genre/Parodie/"),
SelectFilterOption("Police", "/genre/police/"),
SelectFilterOption("Psychologique", "/genre/psychologique/"),
SelectFilterOption("Romance", "/genre/romance/"),
SelectFilterOption("Samurai", "/genre/samurai/"),
SelectFilterOption("Sci-Fi", "/genre/sci-fi/"),
SelectFilterOption("Seinen", "/genre/seinen/"),
SelectFilterOption("Shoujo", "/genre/shoujo/"),
SelectFilterOption("Shoujo Ai", "/genre/shoujo-ai/"),
SelectFilterOption("Shounen", "/genre/shounen/"),
SelectFilterOption("Shounen Ai", "/genre/shounen-ai/"),
SelectFilterOption("Sport", "/genre/sport/"),
SelectFilterOption("Super Power", "/genre/super-power/"),
SelectFilterOption("Surnaturel", "/genre/surnaturel/"),
SelectFilterOption("Suspense", "/genre/suspense/"),
SelectFilterOption("Thriller", "/genre/thriller/"),
SelectFilterOption("Tranche de vie", "/genre/tranche-de-vie/"),
SelectFilterOption("Vampire", "/genre/vampire/")
]),
SelectFilter("SubPageFilter", "Sous page", 0, [
SelectFilterOption("<Selectionner>", ""),
SelectFilterOption("Terminé", "/termine/"),
SelectFilterOption("Film", "/film/"),
])
];
}
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
ListPreference(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 1,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080", "720", "480", "360"]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: [
"Streamwish",
"Doodstream",
"Sendvid",
"Vidbm",
"Okru",
"Voe",
"Sibnet",
"Upstream"
],
entryValues: [
"Streamwish",
"Doodstream",
"Sendvid",
"Vidbm",
"Okru",
"Voe",
"Sibnet",
"Upstream"
],
values: [
"Streamwish",
"Doodstream",
"Sendvid",
"Vidbm",
"Okru",
"Voe",
"Sibnet",
"Upstream"
]),
];
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
videos.sort((MVideo a, MVideo b) {
int qualityMatchA = 0;
if (a.quality.contains(quality)) {
qualityMatchA = 1;
}
int qualityMatchB = 0;
if (b.quality.contains(quality)) {
qualityMatchB = 1;
}
if (qualityMatchA != qualityMatchB) {
return qualityMatchB - qualityMatchA;
}
final regex = RegExp(r'(\d+)p');
final matchA = regex.firstMatch(a.quality);
final matchB = regex.firstMatch(b.quality);
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
return qualityNumB - qualityNumA;
});
return videos;
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
Future<List<MVideo>> upstreamExtractor(String url) async {
final res = await http('GET', json.encode({"url": url}));
final js = xpath(res, '//script[contains(text(), "m3u8")]/text()');
if (js.isEmpty) {
return [];
}
final masterUrl =
substringBefore(substringAfter(evalJs(js.first), "{file:\""), "\"}");
final masterPlaylistRes =
await http('GET', json.encode({"url": masterUrl}));
List<MVideo> videos = [];
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:")) {
final quality =
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
if (!videoUrl.startsWith("http")) {
videoUrl =
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
}
MVideo video = MVideo();
video
..url = videoUrl
..originalUrl = videoUrl
..quality = "Upstream - $quality";
videos.add(video);
}
return videos;
}
Future<List<MVideo>> vidbmExtractor(String url) async {
final res = await http('GET', json.encode({"url": url}));
final js = xpath(res,
'//script[contains(text(), "m3u8") or contains(text(), "mp4")]/text()');
if (js.isEmpty) {
return [];
}
final masterUrl = substringBefore(substringAfter(js.first, "source"), "\"");
final quality = substringBefore(
substringAfter(
substringBefore(
substringAfter(substringAfter(js.first, "source"), "file"),
"]"),
"label:\""),
"\"");
List<MVideo> videos = [];
if (masterUrl.contains("m3u8")) {
final masterPlaylistRes =
await http('GET', json.encode({"url": masterUrl}));
for (var it in substringAfter(masterPlaylistRes, "#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:")) {
final quality =
"${substringBefore(substringBefore(substringAfter(substringAfter(it, "RESOLUTION="), "x"), ","), "\n")}p";
String videoUrl = substringBefore(substringAfter(it, "\n"), "\n");
if (!videoUrl.startsWith("http")) {
videoUrl =
"${masterUrl.split("/").sublist(0, masterUrl.split("/").length - 1).join("/")}/$videoUrl";
}
MVideo video = MVideo();
video
..url = videoUrl
..originalUrl = videoUrl
..quality = "Vidbm - $quality";
videos.add(video);
}
return videos;
} else {
MVideo video = MVideo();
video
..url = masterUrl
..originalUrl = masterUrl
..quality = "Vidbm - $quality";
videos.add(video);
}
return videos;
}
}
OtakuFr main() {
return OtakuFr();
}

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
Source get otakufr => _otakufr; Source get otakufr => _otakufr;
const otakufrVersion = "0.0.4"; const otakufrVersion = "0.0.45";
const otakufrCodeUrl = const otakufrCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/fr/otakufr/otakufr-v$otakufrVersion.dart";
Source _otakufr = Source( Source _otakufr = Source(

View File

@@ -98,12 +98,11 @@ class OploVerz extends MProvider {
xpath(ress, '//iframe[@class="playeriframe"]/@src').first; xpath(ress, '//iframe[@class="playeriframe"]/@src').first;
final resPlayer = await http('GET', json.encode({"url": playerLink})); final resPlayer = await http('GET', json.encode({"url": playerLink}));
var resJson = substringBefore(substringAfter(resPlayer, "= "), "<"); var resJson = substringBefore(substringAfter(resPlayer, "= "), "<");
var streams = var streams = json.decode(resJson)["streams"] as List<Map<String, dynamic>>;
json.decode(getMapValue(resJson, "streams", encode: true)) as List;
List<MVideo> videos = []; List<MVideo> videos = [];
for (var stream in streams) { for (var stream in streams) {
final videoUrl = getMapValue(stream, "play_url"); String videoUrl = stream["play_url"];
final quality = getQuality(getMapValue(stream, "format_id")); final quality = getQuality(stream["format_id"]);
MVideo video = MVideo(); MVideo video = MVideo();
video video

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
Source get oploverz => _oploverz; Source get oploverz => _oploverz;
const oploverzVersion = "0.0.15"; const oploverzVersion = "0.0.2";
const oploverzCodeUrl = const oploverzCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/id/oploverz/oploverz-v$oploverzVersion.dart";
Source _oploverz = Source( Source _oploverz = Source(

View File

@@ -356,7 +356,7 @@ class Madara extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
TextFilter("AuthorFilter", "Author"), TextFilter("AuthorFilter", "Author"),
TextFilter("ArtistFilter", "Artist"), TextFilter("ArtistFilter", "Artist"),

View File

@@ -236,7 +236,7 @@ class MangaReader extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SeparatorFilter(), SeparatorFilter(),
TextFilter("AuthorFilter", "Author"), TextFilter("AuthorFilter", "Author"),

View File

@@ -232,7 +232,7 @@ class MMRCMS extends MProvider {
return pagesUrl; return pagesUrl;
} }
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
HeaderFilter("NOTE: Ignored if using text search!"), HeaderFilter("NOTE: Ignored if using text search!"),
SeparatorFilter(), SeparatorFilter(),

View File

@@ -10,9 +10,15 @@ class NepNep extends MProvider {
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
final directory = directoryFromDocument(res); final directory = directoryFromDocument(res);
final resSort = sortMapList(json.decode(directory), "vm", 1); final resSort = (json.decode(directory) as List<Map<String, dynamic>>);
resSort.sort(
(Map<String, dynamic> a, Map<String, dynamic> b) =>
(a["vm"] as String).compareTo(b["vm"] as String),
);
return parseDirectory(resSort); // sortMapList(json.decode(directory), "vm", 1);
return parseDirectory(json.encode(resSort));
} }
@override @override
@@ -308,7 +314,7 @@ class NepNep extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
TextFilter("YearFilter", "Years"), TextFilter("YearFilter", "Years"),
TextFilter("AuthorFilter", "Author"), TextFilter("AuthorFilter", "Author"),

View File

@@ -7,7 +7,7 @@ class Batoto extends MProvider {
@override @override
Future<MPages> getPopular(MSource source, int page) async { Future<MPages> getPopular(MSource source, int page) async {
final url = final url =
"${source.baseUrl}/browse?${lang(source.lang)}&sort=views_a&page=$page"; "${preferenceMirror(source.id)}/browse?${lang(source.lang)}&sort=views_a&page=$page";
final data = {"url": url}; final data = {"url": url};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
return mangaElementM(res, source); return mangaElementM(res, source);
@@ -16,7 +16,7 @@ class Batoto extends MProvider {
@override @override
Future<MPages> getLatestUpdates(MSource source, int page) async { Future<MPages> getLatestUpdates(MSource source, int page) async {
final url = final url =
"${source.baseUrl}/browse?${lang(source.lang)}&sort=update&page=$page"; "${preferenceMirror(source.id)}/browse?${lang(source.lang)}&sort=update&page=$page";
final data = {"url": url}; final data = {"url": url};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
return mangaElementM(res, source); return mangaElementM(res, source);
@@ -30,7 +30,7 @@ class Batoto extends MProvider {
String min = ""; String min = "";
String max = ""; String max = "";
if (query.isNotEmpty) { if (query.isNotEmpty) {
url = "${source.baseUrl}/search?word=$query&page=$page"; url = "${preferenceMirror(source.id)}/search?word=$query&page=$page";
for (var filter in filters) { for (var filter in filters) {
if (filter.type == "LetterFilter") { if (filter.type == "LetterFilter") {
if (filter.state == 1) { if (filter.state == 1) {
@@ -39,7 +39,7 @@ class Batoto extends MProvider {
} }
} }
} else { } else {
url = "${source.baseUrl}/browse"; url = "${preferenceMirror(source.id)}/browse";
for (var filter in filters) { for (var filter in filters) {
if (filter.type == "LangGroupFilter") { if (filter.type == "LangGroupFilter") {
final langs = (filter.state as List).where((e) => e.state).toList(); final langs = (filter.state as List).where((e) => e.state).toList();
@@ -111,7 +111,7 @@ class Batoto extends MProvider {
{"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2} {"Ongoing": 0, "Completed": 1, "Cancelled": 3, "Hiatus": 2}
]; ];
final data = {"url": "${source.baseUrl}$url"}; final data = {"url": "${preferenceMirror(source.id)}$url"};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
MManga manga = MManga(); MManga manga = MManga();
final workStatus = xpath(res, final workStatus = xpath(res,
@@ -171,7 +171,7 @@ class Batoto extends MProvider {
@override @override
Future<List<String>> getPageList(MSource source, String url) async { Future<List<String>> getPageList(MSource source, String url) async {
final datas = {"url": "${source.baseUrl}$url"}; final datas = {"url": "${preferenceMirror(source.id)}$url"};
final res = await http('GET', json.encode(datas)); final res = await http('GET', json.encode(datas));
final script = xpath(res, final script = xpath(res,
@@ -273,7 +273,7 @@ class Batoto extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SelectFilter("LetterFilter", "Letter matching mode (Slow)", 0, [ SelectFilter("LetterFilter", "Letter matching mode (Slow)", 0, [
SelectFilterOption("Disabled", "disabled"), SelectFilterOption("Disabled", "disabled"),
@@ -1663,36 +1663,51 @@ class Batoto extends MProvider {
SeparatorFilter(), SeparatorFilter(),
]; ];
} }
}
Map<String, String> getMirrorPref() { @override
return { List<dynamic> getSourcePreferences(MSource source) {
"bato.to": "https://bato.to", return [
"batocomic.com": "https://batocomic.com", ListPreference(
"batocomic.net": "https://batocomic.net", key: "mirror",
"batocomic.org": "https://batocomic.org", title: "Mirror",
"batotoo.com": "https://batotoo.com", summary: "",
"batotwo.com": "https://batotwo.com", valueIndex: 0,
"battwo.com": "https://battwo.com", entries: mirrorEntries,
"comiko.net": "https://comiko.net", entryValues: mirrorEntries.map((e) => "https://$e").toList()),
"comiko.org": "https://comiko.org", ];
"mangatoto.com": "https://mangatoto.com", }
"mangatoto.net": "https://mangatoto.net",
"mangatoto.org": "https://mangatoto.org", List<String> mirrorEntries = [
"readtoto.com": "https://readtoto.com", "bato.to",
"readtoto.net": "https://readtoto.net", "batocomic.com",
"readtoto.org": "https://readtoto.org", "batocomic.net",
"dto.to": "https://dto.to", "batocomic.org",
"hto.to": "https://hto.to", "batotoo.com",
"mto.to": "https://mto.to", "batotwo.com",
"wto.to": "https://wto.to", "battwo.com",
"xbato.com": "https://xbato.com", "comiko.net",
"xbato.net": "https://xbato.net", "comiko.org",
"xbato.org": "https://xbato.org", "mangatoto.com",
"zbato.com": "https://zbato.com", "mangatoto.net",
"zbato.net": "https://zbato.net", "mangatoto.org",
"zbato.org": "https://zbato.org", "readtoto.com",
}; "readtoto.net",
"readtoto.org",
"dto.to",
"hto.to",
"mto.to",
"wto.to",
"xbato.com",
"xbato.net",
"xbato.org",
"zbato.com",
"zbato.net",
"zbato.org",
];
String preferenceMirror(int sourceId) {
return getPreferenceValue(sourceId, "mirror");
}
} }
Batoto main() { Batoto main() {

View File

@@ -1,7 +1,7 @@
import '../../../../model/source.dart'; import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
const batotoVersion = "0.0.4"; const batotoVersion = "0.0.45";
const batotoSourceCodeUrl = const batotoSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/batoto/batoto-v$batotoVersion.dart";

View File

@@ -215,7 +215,7 @@ class ComickFun extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
HeaderFilter("The filter is ignored when using text search."), HeaderFilter("The filter is ignored when using text search."),
GroupFilter("GenreFilter", "Genre", [ GroupFilter("GenreFilter", "Genre", [

View File

@@ -8,7 +8,7 @@ class MangaDex extends MProvider {
Future<MPages> getPopular(MSource source, int page) async { Future<MPages> getPopular(MSource source, int page) async {
page = (20 * (page - 1)); page = (20 * (page - 1));
final url = final url =
"https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=${source.lang}&includes[]=cover_art${getMDXContentRating()}&order[followedCount]=desc"; "https://api.mangadex.org/manga?limit=20&offset=$page&availableTranslatedLanguage[]=${source.lang}&includes[]=cover_art${preferenceContentRating(source.id)}${preferenceOriginalLanguages(source.id)}&order[followedCount]=desc";
final datas = {"url": url}; final datas = {"url": url};
final res = await http('GET', json.encode(datas)); final res = await http('GET', json.encode(datas));
return mangaRes(res, source); return mangaRes(res, source);
@@ -24,12 +24,12 @@ class MangaDex extends MProvider {
final mangaIds = final mangaIds =
jsonPathToString(ress, r'$.data[*].relationships[*].id', '.--') jsonPathToString(ress, r'$.data[*].relationships[*].id', '.--')
.split('.--'); .split('.--');
String mangaIdss = "".toString(); String mangaIdss = "";
for (var id in mangaIds) { for (var id in mangaIds) {
mangaIdss += "&ids[]=$id"; mangaIdss += "&ids[]=$id";
} }
final newUrl = final newUrl =
"https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${getMDXContentRating()}$mangaIdss"; "https://api.mangadex.org/manga?includes[]=cover_art&limit=${mangaIds.length}${preferenceContentRating(source.id)}${preferenceOriginalLanguages(source.id)}$mangaIdss";
final res = await http('GET', json.encode({"url": newUrl})); final res = await http('GET', json.encode({"url": newUrl}));
return mangaRes(res, source); return mangaRes(res, source);
} }
@@ -188,7 +188,7 @@ class MangaDex extends MProvider {
final mangaId = url.split('/').last; final mangaId = url.split('/').last;
final paginatedChapterList = final paginatedChapterList =
await paginatedChapterListRequest(mangaId, 0, source.lang); await paginatedChapterListRequest(mangaId, 0, source.lang, source.id);
final chapterList = final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.').split('_.'); jsonPathToString(paginatedChapterList, r'$.data[*]', '_.').split('_.');
int limit = int limit =
@@ -206,8 +206,8 @@ class MangaDex extends MProvider {
var hasMoreResults = (limit + offset) < total; var hasMoreResults = (limit + offset) < total;
while (hasMoreResults) { while (hasMoreResults) {
offset += limit; offset += limit;
var newRequest = var newRequest = await paginatedChapterListRequest(
await paginatedChapterListRequest(mangaId, offset, source.lang); mangaId, offset, source.lang, source.id);
int total = int.parse(jsonPathToString(newRequest, r'$.total', '')); int total = int.parse(jsonPathToString(newRequest, r'$.total', ''));
final chapterList = final chapterList =
jsonPathToString(paginatedChapterList, r'$.data[*]', '_.') jsonPathToString(paginatedChapterList, r'$.data[*]', '_.')
@@ -244,7 +244,7 @@ class MangaDex extends MProvider {
for (var e in resJson) { for (var e in resJson) {
MManga manga = MManga(); MManga manga = MManga();
manga.name = findTitle(json.encode(e), source.lang); manga.name = findTitle(json.encode(e), source.lang);
manga.imageUrl = getCover(json.encode(e)); manga.imageUrl = getCover(json.encode(e), source.id);
manga.link = "/manga/${getMapValue(json.encode(e), "id")}"; manga.link = "/manga/${getMapValue(json.encode(e), "id")}";
mangaList.add(manga); mangaList.add(manga);
} }
@@ -253,28 +253,28 @@ class MangaDex extends MProvider {
List<MChapter> getChapters(int length, String paginatedChapterListA) { List<MChapter> getChapters(int length, String paginatedChapterListA) {
List<MChapter> chaptersList = []; List<MChapter> chaptersList = [];
String paginatedChapterList = paginatedChapterListA.toString(); String paginatedChapterList = paginatedChapterListA;
final dataList = jsonPathToList(paginatedChapterList, r'$.data[*]', 0); final dataList = jsonPathToList(paginatedChapterList, r'$.data[*]', 0);
for (var res in dataList) { for (var res in dataList) {
String scan = "".toString(); String scan = "";
final groups = jsonPathToList(res, final groups = jsonPathToList(res,
r'$.relationships[?@.id!="00e03853-1b96-4f41-9542-c71b8692033b"]', 0); r'$.relationships[?@.id!="00e03853-1b96-4f41-9542-c71b8692033b"]', 0);
String chapName = "".toString(); String chapName = "";
for (var element in groups) { for (var element in groups) {
final data = getMapValue(element, "attributes", encode: true); final data = getMapValue(element, "attributes", encode: true);
if (data.isNotEmpty) { if (data.isNotEmpty) {
final name = getMapValue(data, "name"); final name = getMapValue(data, "name");
scan += "$name".toString(); scan += "$name";
final username = getMapValue(data, "username"); final username = getMapValue(data, "username");
if (username.isNotEmpty) { if (username.isNotEmpty) {
if (scan.isEmpty) { if (scan.isEmpty) {
scan += "Uploaded by $username".toString(); scan += "Uploaded by $username";
} }
} }
} }
} }
if (scan.isEmpty) { if (scan.isEmpty) {
scan = "No Group".toString(); scan = "No Group";
} }
final dataRes = getMapValue(res, "attributes", encode: true); final dataRes = getMapValue(res, "attributes", encode: true);
if (dataRes.isNotEmpty) { if (dataRes.isNotEmpty) {
@@ -282,26 +282,26 @@ class MangaDex extends MProvider {
final volume = getMapValue(data, "volume"); final volume = getMapValue(data, "volume");
if (volume.isNotEmpty) { if (volume.isNotEmpty) {
if (volume != "null") { if (volume != "null") {
chapName = "Vol.$volume ".toString(); chapName = "Vol.$volume ";
} }
} }
final chapter = getMapValue(data, "chapter"); final chapter = getMapValue(data, "chapter");
if (chapter.isNotEmpty) { if (chapter.isNotEmpty) {
if (chapter != "null") { if (chapter != "null") {
chapName += "Ch.$chapter ".toString(); chapName += "Ch.$chapter ";
} }
} }
final title = getMapValue(data, "title"); final title = getMapValue(data, "title");
if (title.isNotEmpty) { if (title.isNotEmpty) {
if (title != "null") { if (title != "null") {
if (chapName.isNotEmpty) { if (chapName.isNotEmpty) {
chapName += "- ".toString(); chapName += "- ";
} }
chapName += "$title".toString(); chapName += "$title";
} }
} }
if (chapName.isEmpty) { if (chapName.isEmpty) {
chapName += "Oneshot".toString(); chapName += "Oneshot";
} }
final date = getMapValue(data, "publishAt"); final date = getMapValue(data, "publishAt");
final id = getMapValue(res, "id"); final id = getMapValue(res, "id");
@@ -319,19 +319,13 @@ class MangaDex extends MProvider {
} }
Future<String> paginatedChapterListRequest( Future<String> paginatedChapterListRequest(
String mangaId, int offset, String lang) async { String mangaId, int offset, String lang, int sourceId) async {
final url = final url =
'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${getMDXContentRating()}'; 'https://api.mangadex.org/manga/$mangaId/feed?limit=500&offset=$offset&includes[]=user&includes[]=scanlation_group&order[volume]=desc&order[chapter]=desc&translatedLanguage[]=$lang&includeFuturePublishAt=0&includeEmptyPages=0${preferenceContentRating(sourceId)}';
final res = await http('GET', json.encode({"url": url})); final res = await http('GET', json.encode({"url": url}));
return res; return res;
} }
String getMDXContentRating() {
String ctnRating =
"&contentRating[]=suggestive&contentRating[]=safe&contentRating[]=erotica&contentRating[]=pornographic";
return ctnRating;
}
String findTitle(String dataRes, String lang) { String findTitle(String dataRes, String lang) {
final attributes = getMapValue(dataRes, "attributes", encode: true); final attributes = getMapValue(dataRes, "attributes", encode: true);
final altTitlesJ = final altTitlesJ =
@@ -349,7 +343,8 @@ class MangaDex extends MProvider {
return title; return title;
} }
String getCover(String dataRes) { String getCover(String dataRes, int sourceId) {
final coverQuality = getPreferenceValue(sourceId, "cover_quality");
final relationships = json final relationships = json
.decode(getMapValue(dataRes, "relationships", encode: true)) as List; .decode(getMapValue(dataRes, "relationships", encode: true)) as List;
String coverFileName = "".toString(); String coverFileName = "".toString();
@@ -360,7 +355,7 @@ class MangaDex extends MProvider {
final attributes = final attributes =
getMapValue(json.encode(a), "attributes", encode: true); getMapValue(json.encode(a), "attributes", encode: true);
coverFileName = coverFileName =
"https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}"; "https://uploads.mangadex.org/covers/${getMapValue(dataRes, "id")}/${getMapValue(attributes, "fileName")}$coverQuality";
} }
} }
} }
@@ -368,7 +363,7 @@ class MangaDex extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
CheckBoxFilter( CheckBoxFilter(
"Has available chapters", "", "HasAvailableChaptersFilter"), "Has available chapters", "", "HasAvailableChaptersFilter"),
@@ -510,6 +505,83 @@ class MangaDex extends MProvider {
]; ];
} }
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
ListPreference(
key: "cover_quality",
title: "Cover quality",
summary: "",
valueIndex: 0,
entries: ["Original", "Medium", "Low"],
entryValues: ["", ".512.jpg", ".256.jpg"]),
MultiSelectListPreference(
key: "content_rating",
title: "Default content rating",
summary: "Show content with the selected rating by default",
valueIndex: 0,
entries: [
"safe",
"suggestive",
"erotica",
"pornographic"
],
entryValues: [
"contentRating[]=safe",
"contentRating[]=suggestive",
"contentRating[]=erotica",
"contentRating[]=pornographic"
],
values: [
"contentRating[]=safe",
"contentRating[]=suggestive"
]),
MultiSelectListPreference(
key: "original_languages",
title: "Filter original languages",
summary:
"Only show content that was originaly published in the selected languages in both latest and browse",
valueIndex: 0,
entries: [
"Japanese",
"Chinese",
"Korean"
],
entryValues: [
"originalLanguage[]=ja",
"originalLanguage[]=zh&originalLanguage[]=zh-hk",
"originalLanguage[]=ko"
],
values: []),
];
}
String preferenceContentRating(int sourceId) {
final contentRating =
getPreferenceValue(sourceId, "content_rating") as List<String>;
String contentRatingStr = "";
if (contentRating.isNotEmpty) {
contentRatingStr = "&";
for (var ctn in contentRating) {
contentRatingStr += "&$ctn";
}
}
return contentRatingStr;
}
String preferenceOriginalLanguages(int sourceId) {
final originalLanguages =
getPreferenceValue(sourceId, "original_languages") as List<String>;
String originalLanguagesStr = "";
if (originalLanguages.isNotEmpty) {
originalLanguagesStr = "&";
for (var language in originalLanguages) {
originalLanguagesStr += "&$language";
}
}
return originalLanguagesStr;
}
String ll(String url) { String ll(String url) {
if (url.contains("?")) { if (url.contains("?")) {
return "&"; return "&";

View File

@@ -4,7 +4,7 @@ import '../../../../utils/utils.dart';
const apiUrl = 'https://api.mangadex.org'; const apiUrl = 'https://api.mangadex.org';
const baseUrl = 'https://mangadex.org'; const baseUrl = 'https://mangadex.org';
const isNsfw = true; const isNsfw = true;
const mangadexVersion = "0.0.45"; const mangadexVersion = "0.0.5";
const mangadexSourceCodeUrl = const mangadexSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart";
String _iconUrl = getIconUrl("mangadex", "all"); String _iconUrl = getIconUrl("mangadex", "all");

View File

@@ -259,7 +259,7 @@ class MangaHere extends MProvider {
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
SelectFilter("TypeList", "Type", 1, [ SelectFilter("TypeList", "Type", 1, [
SelectFilterOption("American Manga", "5"), SelectFilterOption("American Manga", "5"),