Merge pull request #6 from kodjodevf/add-source-preference

Add source preference
This commit is contained in:
Moustapha Kodjo Amadou
2023-12-05 18:28:25 +01:00
committed by GitHub
31 changed files with 1657 additions and 394 deletions

View File

@@ -6,18 +6,21 @@ class DopeFlix extends MProvider {
@override @override
Future<MPages> getPopular(MSource source, int page) async { Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/movie?page=$page"}; final data = {
"url":
"${preferenceBaseUrl(source.id)}/${getPreferenceValue(source.id, "preferred_popular_page")}?page=$page"
};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
return parseAnimeList(res); return parseAnimeList(res);
} }
@override @override
Future<MPages> getLatestUpdates(MSource source, int page) async { Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/home"}; final data = {"url": "${preferenceBaseUrl(source.id)}/home"};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
List<MManga> animeList = []; List<MManga> animeList = [];
final path = final path =
'//section[contains(text(),"Latest Movies")]/div/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]'; '//section[contains(text(),"${getPreferenceValue(source.id, "preferred_latest_page")}")]/div/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]';
final urls = xpath(res, '$path/a/@href'); final urls = xpath(res, '$path/a/@href');
final names = xpath(res, '$path/a/@title'); final names = xpath(res, '$path/a/@title');
final images = xpath(res, '$path/img/@data-src'); final images = xpath(res, '$path/img/@data-src');
@@ -36,7 +39,7 @@ class DopeFlix extends MProvider {
Future<MPages> search( Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async { MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters; final filters = filterList.filters;
String url = "${source.baseUrl}"; String url = "${preferenceBaseUrl(source.id)}";
if (query.isNotEmpty) { if (query.isNotEmpty) {
url += "/search/${query.replaceAll(" ", "-")}?page=$page"; url += "/search/${query.replaceAll(" ", "-")}?page=$page";
@@ -79,7 +82,7 @@ class DopeFlix extends MProvider {
@override @override
Future<MManga> getDetail(MSource source, String url) async { Future<MManga> getDetail(MSource source, String url) async {
url = Uri.parse(url).path; url = Uri.parse(url).path;
final data = {"url": "${source.baseUrl}$url"}; final data = {"url": "${preferenceBaseUrl(source.id)}$url"};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
MManga anime = MManga(); MManga anime = MManga();
final description = xpath(res, '//div[@class="description"]/text()'); final description = xpath(res, '//div[@class="description"]/text()');
@@ -98,10 +101,12 @@ class DopeFlix extends MProvider {
if (dataType == "1") { if (dataType == "1") {
MChapter episode = MChapter(); MChapter episode = MChapter();
episode.name = "Movie"; episode.name = "Movie";
episode.url = "${source.baseUrl}/ajax/movie/episodes/$id"; episode.url = "${preferenceBaseUrl(source.id)}/ajax/movie/episodes/$id";
episodesList.add(episode); episodesList.add(episode);
} else { } else {
final dataS = {"url": "${source.baseUrl}/ajax/v2/tv/seasons/$id"}; final dataS = {
"url": "${preferenceBaseUrl(source.id)}/ajax/v2/tv/seasons/$id"
};
final resS = await http('GET', json.encode(dataS)); final resS = await http('GET', json.encode(dataS));
final seasonIds = final seasonIds =
@@ -112,7 +117,8 @@ class DopeFlix extends MProvider {
final seasonId = seasonIds[i]; final seasonId = seasonIds[i];
final seasonName = seasonNames[i]; final seasonName = seasonNames[i];
final dataE = { final dataE = {
"url": "${source.baseUrl}/ajax/v2/season/episodes/$seasonId" "url":
"${preferenceBaseUrl(source.id)}/ajax/v2/season/episodes/$seasonId"
}; };
final html = await http('GET', json.encode(dataE)); final html = await http('GET', json.encode(dataE));
final epsHtml = querySelectorAll(html, final epsHtml = querySelectorAll(html,
@@ -120,7 +126,8 @@ class DopeFlix extends MProvider {
typeElement: 2, typeElement: 2,
attributes: "", attributes: "",
typeRegExp: 0); typeRegExp: 0);
print("${source.baseUrl}/ajax/v2/season/episodes/$seasonId"); print(
"${preferenceBaseUrl(source.id)}/ajax/v2/season/episodes/$seasonId");
for (var epHtml in epsHtml) { for (var epHtml in epsHtml) {
final episodeId = final episodeId =
xpath(epHtml, '//div[contains(@class,"eps-item")]/@data-id') xpath(epHtml, '//div[contains(@class,"eps-item")]/@data-id')
@@ -130,7 +137,8 @@ class DopeFlix extends MProvider {
final epName = xpath(epHtml, '//h3[@class="film-name"]/text()').first; final epName = xpath(epHtml, '//h3[@class="film-name"]/text()').first;
MChapter episode = MChapter(); MChapter episode = MChapter();
episode.name = "$seasonName $epNum $epName"; episode.name = "$seasonName $epNum $epName";
episode.url = "${source.baseUrl}/ajax/v2/episode/servers/$episodeId"; episode.url =
"${preferenceBaseUrl(source.id)}/ajax/v2/episode/servers/$episodeId";
episodesList.add(episode); episodesList.add(episode);
} }
} }
@@ -142,8 +150,8 @@ class DopeFlix extends MProvider {
@override @override
Future<List<MVideo>> getVideoList(MSource source, String url) async { Future<List<MVideo>> getVideoList(MSource source, String url) async {
url = Uri.parse(url).path; url = Uri.parse(url).path;
final res = final res = await http(
await http('GET', json.encode({"url": "${source.baseUrl}/$url"})); 'GET', json.encode({"url": "${preferenceBaseUrl(source.id)}/$url"}));
final vidsHtml = querySelectorAll(res, final vidsHtml = querySelectorAll(res,
selector: "ul.fss-list a.btn-play", selector: "ul.fss-list a.btn-play",
typeElement: 2, typeElement: 2,
@@ -154,7 +162,9 @@ class DopeFlix extends MProvider {
final id = xpath(vidHtml, '//a/@data-id').first; final id = xpath(vidHtml, '//a/@data-id').first;
final name = xpath(vidHtml, '//span/text()').first; final name = xpath(vidHtml, '//span/text()').first;
final resSource = await http( final resSource = await http(
'GET', json.encode({"url": "${source.baseUrl}/ajax/sources/$id"})); 'GET',
json.encode(
{"url": "${preferenceBaseUrl(source.id)}/ajax/sources/$id"}));
final vidUrl = final vidUrl =
substringBefore(substringAfter(resSource, "\"link\":\""), "\""); substringBefore(substringAfter(resSource, "\"link\":\""), "\"");
List<MVideo> a = []; List<MVideo> a = [];
@@ -212,6 +222,7 @@ class DopeFlix extends MProvider {
subtitles.add(subtitle); subtitles.add(subtitle);
} catch (_) {} } catch (_) {}
} }
subtitles = sortSubs(subtitles, source.id);
if (type == "hls") { if (type == "hls") {
final masterPlaylistRes = final masterPlaylistRes =
await http('GET', json.encode({"url": masterUrl})); await http('GET', json.encode({"url": masterUrl}));
@@ -248,7 +259,7 @@ class DopeFlix extends MProvider {
videos.addAll(a); videos.addAll(a);
} }
return videos; return sortVideos(videos, source.id);
} }
Future<List<List<int>>> generateIndexPairs() async { Future<List<List<int>>> generateIndexPairs() async {
@@ -261,8 +272,8 @@ class DopeFlix extends MProvider {
script = script.substring(0, script.lastIndexOf(',')); script = script.substring(0, script.lastIndexOf(','));
final list = script final list = script
.split(",") .split(",")
.map((e) { .map((String e) {
String value = substringAfter((e as String), "="); String value = substringAfter(e, "=");
if (value.contains("0x")) { if (value.contains("0x")) {
return int.parse(substringAfter(value, "0x"), radix: 16); return int.parse(substringAfter(value, "0x"), radix: 16);
} else { } else {
@@ -273,7 +284,7 @@ class DopeFlix extends MProvider {
.skip(1) .skip(1)
.toList(); .toList();
return chunked(list, 2) return chunked(list, 2)
.map((list) => (list as List<int>).reversed.toList()) .map((List<int> list) => list.reversed.toList())
.toList(); .toList();
} }
@@ -310,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"),
@@ -405,6 +416,128 @@ class DopeFlix extends MProvider {
]; ];
} }
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
if (source.name == "DopeBox")
ListPreference(
key: "preferred_domain",
title: "Preferred domain",
summary: "",
valueIndex: 0,
entries: ["dopebox.to", "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(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080p", "720p", "480p", "360p"]),
ListPreference(
key: "preferred_subLang",
title: "Preferred sub language",
summary: "",
valueIndex: 1,
entries: [
"Arabic",
"English",
"French",
"German",
"Hungarian",
"Italian",
"Japanese",
"Portuguese",
"Romanian",
"Russian",
"Spanish"
],
entryValues: [
"Arabic",
"English",
"French",
"German",
"Hungarian",
"Italian",
"Japanese",
"Portuguese",
"Romanian",
"Russian",
"Spanish"
]),
ListPreference(
key: "preferred_latest_page",
title: "Preferred latest page",
summary: "",
valueIndex: 0,
entries: ["Movies", "TV Shows"],
entryValues: ["Latest Movies", "Latest TV Shows"]),
ListPreference(
key: "preferred_popular_page",
title: "Preferred popular page",
summary: "",
valueIndex: 0,
entries: ["Movies", "TV Shows"],
entryValues: ["movie", "tv-show"]),
];
}
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<MTrack> sortSubs(List<MTrack> subs, int sourceId) {
String lang = getPreferenceValue(sourceId, "preferred_subLang");
subs.sort((MTrack a, MTrack b) {
int langMatchA = 0;
if (a.label.toLowerCase().contains(lang.toLowerCase())) {
langMatchA = 1;
}
int langMatchB = 0;
if (b.label.toLowerCase().contains(lang.toLowerCase())) {
langMatchB = 1;
}
return langMatchB - langMatchA;
});
return subs;
}
String preferenceBaseUrl(int sourceId) {
return getPreferenceValue(sourceId, "preferred_domain");
}
String ll(String url) { String ll(String url) {
if (url.contains("?")) { if (url.contains("?")) {
return "&"; return "&";

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart'; import '../../../model/source.dart';
import '../../../utils/utils.dart'; import '../../../utils/utils.dart';
const dopeflixVersion = "0.0.15"; const dopeflixVersion = "0.0.2";
const dopeflixSourceCodeUrl = const dopeflixSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/dopeflix/dopeflix-v$dopeflixVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/dopeflix/dopeflix-v$dopeflixVersion.dart";

View File

@@ -1,7 +1,7 @@
import '../../../model/source.dart'; import '../../../model/source.dart';
import '../../../utils/utils.dart'; import '../../../utils/utils.dart';
const zorothemeVersion = "0.0.5"; const zorothemeVersion = "0.0.55";
const zorothemeSourceCodeUrl = const zorothemeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/zorotheme/zorotheme-v$zorothemeVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/multisrc/zorotheme/zorotheme-v$zorothemeVersion.dart";

View File

@@ -223,7 +223,8 @@ class ZoroTheme extends MProvider {
typeRegExp: 0); typeRegExp: 0);
List<MVideo> videos = []; List<MVideo> videos = [];
final hosterSelection = preferenceHosterSelection(source.id);
final typeSelection = preferenceTypeSelection(source.id);
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
final name = names[i]; final name = names[i];
final id = ids[i]; final id = ids[i];
@@ -236,19 +237,22 @@ class ZoroTheme extends MProvider {
final resE = await http('GET', json.encode(datasE)); final resE = await http('GET', json.encode(datasE));
String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\""); String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\"");
print(epUrl);
List<MVideo> a = []; List<MVideo> a = [];
if (name.contains("Vidstreaming")) {
a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub"); if (hosterSelection.contains(name) && typeSelection.contains(subDub)) {
} else if (name.contains("Vidcloud")) { if (name.contains("Vidstreaming")) {
a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub"); a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub");
} else if (name.contains("StreamTape")) { } else if (name.contains("Vidcloud")) {
a = await streamTapeExtractor(epUrl, "StreamTape - $subDub"); a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub");
} else if (name.contains("StreamTape")) {
a = await streamTapeExtractor(epUrl, "StreamTape - $subDub");
}
videos.addAll(a);
} }
videos.addAll(a);
} }
return videos; return sortVideos(videos, source.id);
} }
MPages animeElementM(String res) { MPages animeElementM(String res) {
@@ -288,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", ""),
@@ -421,6 +425,98 @@ class ZoroTheme extends MProvider {
]; ];
} }
@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"]),
ListPreference(
key: "preferred_server",
title: "Preferred server",
summary: "",
valueIndex: 0,
entries: ["Vidstreaming", "VidCloud", "StreamTape"],
entryValues: ["Vidstreaming", "VidCloud", "StreamTape"]),
ListPreference(
key: "preferred_type",
title: "Preferred Type",
summary: "",
valueIndex: 0,
entries: ["Sub", "Dub"],
entryValues: ["sub", "dub"]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: ["Vidstreaming", "VidCloud", "StreamTape"],
entryValues: ["Vidstreaming", "Vidcloud", "StreamTape"],
values: ["Vidstreaming", "Vidcloud", "StreamTape"]),
MultiSelectListPreference(
key: "type_selection",
title: "Enable/Disable Types",
summary: "",
entries: ["Sub", "Dub"],
entryValues: ["sub", "dub"],
values: ["sub", "dub"]),
];
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
String server = getPreferenceValue(sourceId, "preferred_server");
String type = getPreferenceValue(sourceId, "preferred_type");
videos = videos
.where(
(MVideo e) => e.quality.toLowerCase().contains(type.toLowerCase()))
.toList();
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;
});
videos.sort((MVideo a, MVideo b) {
int serverMatchA = 0;
if (a.quality.toLowerCase().contains(server.toLowerCase())) {
serverMatchA = 1;
}
int serverMatchB = 0;
if (b.quality.toLowerCase().contains(server.toLowerCase())) {
serverMatchB = 1;
}
return serverMatchB - serverMatchA;
});
return videos;
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
List<String> preferenceTypeSelection(int sourceId) {
return getPreferenceValue(sourceId, "type_selection");
}
String ll(String url) { String ll(String url) {
if (url.contains("?")) { if (url.contains("?")) {
return "&"; return "&";

View File

@@ -14,6 +14,7 @@ import 'src/fr/otakufr/source.dart';
import 'src/id/nimegami/source.dart'; import 'src/id/nimegami/source.dart';
import 'src/id/oploverz/source.dart'; import 'src/id/oploverz/source.dart';
import 'src/id/otakudesu/source.dart'; import 'src/id/otakudesu/source.dart';
import 'src/it/source.dart';
void main() { void main() {
List<Source> _sourcesList = [ List<Source> _sourcesList = [
@@ -28,7 +29,8 @@ void main() {
nimegami, nimegami,
oploverz, oploverz,
aniwave, aniwave,
...dopeflixSourcesList ...dopeflixSourcesList,
animesaturn
]; ];
final List<Map<String, dynamic>> jsonList = final List<Map<String, dynamic>> jsonList =
_sourcesList.map((source) => source.toJson()).toList(); _sourcesList.map((source) => source.toJson()).toList();

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

@@ -6,7 +6,9 @@ class Aniwave extends MProvider {
@override @override
Future<MPages> getPopular(MSource source, int page) async { Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/filter?sort=trending&page=$page"}; final data = {
"url": "${preferenceBaseUrl(source.id)}/filter?sort=trending&page=$page"
};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
return parseAnimeList(res); return parseAnimeList(res);
} }
@@ -14,7 +16,8 @@ class Aniwave extends MProvider {
@override @override
Future<MPages> getLatestUpdates(MSource source, int page) async { Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = { final data = {
"url": "${source.baseUrl}/filter?sort=recently_updated&page=$page" "url":
"${preferenceBaseUrl(source.id)}/filter?sort=recently_updated&page=$page"
}; };
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
return parseAnimeList(res); return parseAnimeList(res);
@@ -24,7 +27,7 @@ class Aniwave extends MProvider {
Future<MPages> search( Future<MPages> search(
MSource source, String query, int page, FilterList filterList) async { MSource source, String query, int page, FilterList filterList) async {
final filters = filterList.filters; final filters = filterList.filters;
String url = "${source.baseUrl}/filter?keyword=$query"; String url = "${preferenceBaseUrl(source.id)}/filter?keyword=$query";
for (var filter in filters) { for (var filter in filters) {
if (filter.type == "OrderFilter") { if (filter.type == "OrderFilter") {
@@ -98,7 +101,7 @@ class Aniwave extends MProvider {
final statusList = [ final statusList = [
{"Releasing": 0, "Completed": 1} {"Releasing": 0, "Completed": 1}
]; ];
final data = {"url": "${source.baseUrl}${url}"}; final data = {"url": "${preferenceBaseUrl(source.id)}${url}"};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
MManga anime = MManga(); MManga anime = MManga();
final status = xpath(res, '//div[contains(text(),"Status")]/span/text()'); final status = xpath(res, '//div[contains(text(),"Status")]/span/text()');
@@ -124,7 +127,9 @@ class Aniwave extends MProvider {
.first; .first;
final encrypt = vrfEncrypt(id); final encrypt = vrfEncrypt(id);
final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
final dataEp = {"url": "${source.baseUrl}/ajax/episode/list/$id?$vrf"}; final dataEp = {
"url": "${preferenceBaseUrl(source.id)}/ajax/episode/list/$id?$vrf"
};
final resEp = await http('GET', json.encode(dataEp)); final resEp = await http('GET', json.encode(dataEp));
final html = json.decode(resEp)["result"]; final html = json.decode(resEp)["result"];
List<MChapter>? episodesList = []; List<MChapter>? episodesList = [];
@@ -172,8 +177,11 @@ class Aniwave extends MProvider {
final ids = substringBefore(url, "&"); final ids = substringBefore(url, "&");
final encrypt = vrfEncrypt(ids); final encrypt = vrfEncrypt(ids);
final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
final res = await http('GET', final res = await http(
json.encode({"url": "${source.baseUrl}/ajax/server/list/$ids?$vrf"})); 'GET',
json.encode({
"url": "${preferenceBaseUrl(source.id)}/ajax/server/list/$ids?$vrf"
}));
final html = json.decode(res)["result"]; final html = json.decode(res)["result"];
final vidsHtml = querySelectorAll(html, final vidsHtml = querySelectorAll(html,
selector: "div.servers > div", selector: "div.servers > div",
@@ -191,25 +199,34 @@ class Aniwave extends MProvider {
final vrf = "vrf=${Uri.encodeComponent(encrypt)}"; final vrf = "vrf=${Uri.encodeComponent(encrypt)}";
final res = await http( final res = await http(
'GET', 'GET',
json.encode( json.encode({
{"url": "${source.baseUrl}/ajax/server/$serverId?$vrf"})); "url":
"${preferenceBaseUrl(source.id)}/ajax/server/$serverId?$vrf"
}));
final status = json.decode(res)["status"]; final status = json.decode(res)["status"];
if (status == 200) { if (status == 200) {
List<MVideo> a = []; List<MVideo> a = [];
final url = vrfDecrypt(json.decode(res)["result"]["url"]); final url = vrfDecrypt(json.decode(res)["result"]["url"]);
if (url.contains("mp4upload")) { final hosterSelection = preferenceHosterSelection(source.id);
a = await mp4UploadExtractor(url, null, "", type); final typeSelection = preferenceTypeSelection(source.id);
} else if (url.contains("streamtape")) { if (typeSelection.contains(type.toLowerCase())) {
a = await streamTapeExtractor(url, "StreamTape - $type"); if (url.contains("mp4upload") &&
} else if (url.contains("filemoon")) { hosterSelection.contains("mp4upload")) {
a = await filemoonExtractor(url, "", type); a = await mp4UploadExtractor(url, null, "", type);
} else if (url.contains("streamtape") &&
hosterSelection.contains("streamtape")) {
a = await streamTapeExtractor(url, "StreamTape - $type");
} else if (url.contains("filemoon") &&
hosterSelection.contains("filemoon")) {
a = await filemoonExtractor(url, "", type);
}
videos.addAll(a);
} }
videos.addAll(a);
} }
} }
} }
return videos; return sortVideos(videos, source.id);
} }
MPages parseAnimeList(String res) { MPages parseAnimeList(String res) {
@@ -295,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"),
@@ -427,6 +444,147 @@ class Aniwave extends MProvider {
]; ];
} }
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
ListPreference(
key: "preferred_domain",
title: "Preferred domain",
summary: "",
valueIndex: 0,
entries: [
"aniwave.to",
"aniwave.bz",
"aniwave.ws"
],
entryValues: [
"https://aniwave.to",
"https://aniwave.bz",
"https://aniwave.ws"
]),
ListPreference(
key: "preferred_quality",
title: "Preferred Quality",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080", "720", "480", "360"]),
ListPreference(
key: "preferred_language",
title: "Preferred Type",
summary: "",
valueIndex: 0,
entries: ["Sub", "Softsub", "Dub"],
entryValues: ["Sub", "Softsub", "Dub"]),
ListPreference(
key: "preferred_server",
title: "Preferred server",
summary: "",
valueIndex: 0,
entries: [
"VidPlay",
"MyCloud",
"Filemoon",
"StreamTape",
"Mp4Upload"
],
entryValues: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: [
"VidPlay",
"MyCloud",
"Filemoon",
"StreamTape",
"Mp4Upload"
],
entryValues: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
],
values: [
"vidplay",
"mycloud",
"filemoon",
"streamtape",
"mp4upload"
]),
MultiSelectListPreference(
key: "type_selection",
title: "Enable/Disable Type",
summary: "",
entries: ["Sub", "Softsub", "Dub"],
entryValues: ["sub", "softsub", "dub"],
values: ["sub", "softsub", "dub"]),
];
}
String preferenceBaseUrl(int sourceId) {
return getPreferenceValue(sourceId, "preferred_domain");
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
List<String> preferenceTypeSelection(int sourceId) {
return getPreferenceValue(sourceId, "type_selection");
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
String server = getPreferenceValue(sourceId, "preferred_server");
String lang = getPreferenceValue(sourceId, "preferred_language");
videos = videos
.where(
(MVideo e) => e.quality.toLowerCase().contains(lang.toLowerCase()))
.toList();
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;
});
videos.sort((MVideo a, MVideo b) {
int serverMatchA = 0;
if (a.quality.toLowerCase().contains(server.toLowerCase())) {
serverMatchA = 1;
}
int serverMatchB = 0;
if (b.quality.toLowerCase().contains(server.toLowerCase())) {
serverMatchB = 1;
}
return serverMatchB - serverMatchA;
});
return videos;
}
String ll(String url) { String ll(String url) {
if (url.contains("?")) { if (url.contains("?")) {
return "&"; return "&";
@@ -435,14 +593,6 @@ class Aniwave extends MProvider {
} }
} }
Map<String, String> getMirrorPref() {
return {
"aniwave.to": "https://aniwave.to",
"aniwave.bz": "https://aniwave.bz",
"aniwave.ws": "https://aniwave.ws",
};
}
Aniwave main() { Aniwave main() {
return Aniwave(); return Aniwave();
} }

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
Source get aniwave => _aniwave; Source get aniwave => _aniwave;
const aniwaveVersion = "0.0.15"; const aniwaveVersion = "0.0.2";
const aniwaveCodeUrl = const aniwaveCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/aniwave/aniwave-v$aniwaveVersion.dart";
Source _aniwave = Source( Source _aniwave = Source(

View File

@@ -6,7 +6,9 @@ class GogoAnime extends MProvider {
@override @override
Future<MPages> getPopular(MSource source, int page) async { Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/popular.html?page=$page"}; final data = {
"url": "${preferenceBaseUrl(source.id)}/popular.html?page=$page"
};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
List<MManga> animeList = []; List<MManga> animeList = [];
@@ -130,14 +132,15 @@ class GogoAnime extends MProvider {
} }
} }
if (genre.isNotEmpty) { if (genre.isNotEmpty) {
url = "${source.baseUrl}/genre/$genre?page=$page"; url = "${preferenceBaseUrl(source.id)}/genre/$genre?page=$page";
} else if (recent.isNotEmpty) { } else if (recent.isNotEmpty) {
url = url =
"https://ajax.gogo-load.com/ajax/page-recent-release.html?page=$page&type=$recent"; "https://ajax.gogo-load.com/ajax/page-recent-release.html?page=$page&type=$recent";
} else if (season.isNotEmpty) { } else if (season.isNotEmpty) {
url = "${source.baseUrl}/$season?page=$page"; url = "${preferenceBaseUrl(source.id)}/$season?page=$page";
} else { } else {
url = "${source.baseUrl}/filter.html?keyword=$query$filterStr&page=$page"; url =
"${preferenceBaseUrl(source.id)}/filter.html?keyword=$query$filterStr&page=$page";
} }
final data = {"url": url}; final data = {"url": url};
@@ -167,7 +170,7 @@ class GogoAnime extends MProvider {
"Completed": 1, "Completed": 1,
} }
]; ];
final data = {"url": "${source.baseUrl}$url"}; final data = {"url": "${preferenceBaseUrl(source.id)}$url"};
final res = await http('GET', json.encode(data)); final res = await http('GET', json.encode(data));
MManga anime = MManga(); MManga anime = MManga();
final status = xpath( final status = xpath(
@@ -213,7 +216,7 @@ class GogoAnime extends MProvider {
@override @override
Future<List<MVideo>> getVideoList(MSource source, String url) async { Future<List<MVideo>> getVideoList(MSource source, String url) async {
final datas = {"url": "${source.baseUrl}$url"}; final datas = {"url": "${preferenceBaseUrl(source.id)}$url"};
final res = await http('GET', json.encode(datas)); final res = await http('GET', json.encode(datas));
final serverUrls = final serverUrls =
@@ -221,31 +224,34 @@ class GogoAnime extends MProvider {
final serverNames = final serverNames =
xpath(res, '//*[@class="anime_muti_link"]/ul/li/@class'); xpath(res, '//*[@class="anime_muti_link"]/ul/li/@class');
List<MVideo> videos = []; List<MVideo> videos = [];
final hosterSelection = preferenceHosterSelection(source.id);
for (var i = 0; i < serverNames.length; i++) { for (var i = 0; i < serverNames.length; i++) {
final name = serverNames[i]; final name = serverNames[i];
final url = serverUrls[i]; final url = serverUrls[i];
List<MVideo> a = []; List<MVideo> a = [];
if (name.contains("anime")) { if (hosterSelection.contains(name)) {
a = await gogoCdnExtractor(url); if (name.contains("anime")) {
} else if (name.contains("vidcdn")) { a = await gogoCdnExtractor(url);
a = await gogoCdnExtractor(url); } else if (name.contains("vidcdn")) {
} else if (name.contains("doodstream")) { a = await gogoCdnExtractor(url);
a = await doodExtractor(url); } else if (name.contains("doodstream")) {
} else if (name.contains("mp4upload")) { a = await doodExtractor(url);
a = await mp4UploadExtractor(url, null, "", ""); } else if (name.contains("mp4upload")) {
} else if (name.contains("filelions")) { a = await mp4UploadExtractor(url, null, "", "");
a = await streamWishExtractor(url, "FileLions"); } else if (name.contains("filelions")) {
} else if (name.contains("streamwish")) { a = await streamWishExtractor(url, "FileLions");
a = await streamWishExtractor(url, "StreamWish"); } else if (name.contains("streamwish")) {
a = await streamWishExtractor(url, "StreamWish");
}
videos.addAll(a);
} }
videos.addAll(a);
} }
return videos; return sortVideos(videos, source.id);
} }
@override @override
List<dynamic> getFilterList() { List<dynamic> getFilterList(MSource source) {
return [ return [
HeaderFilter("Advanced search"), HeaderFilter("Advanced search"),
GroupFilter("GenreFilter", "Genre", [ GroupFilter("GenreFilter", "Genre", [
@@ -1025,6 +1031,124 @@ class GogoAnime extends MProvider {
]), ]),
]; ];
} }
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
EditTextPreference(
key: "override_baseurl_v${source.id}",
title: "Override BaseUrl",
summary:
"For temporary uses. Updating the extension will erase this setting.",
value: "https://gogoanime3.net",
dialogTitle: "Override BaseUrl",
dialogMessage: "Default: https://gogoanime3.net",
text: "https://gogoanime3.net"),
ListPreference(
key: "preferred_quality",
title: "Preferred quality",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "480p", "360p"],
entryValues: ["1080", "720", "480", "360"]),
ListPreference(
key: "preferred_server",
title: "Preferred server",
summary: "",
valueIndex: 0,
entries: [
"Gogostream",
"Vidstreaming",
"Doodstream",
"StreamWish",
"Mp4upload",
"FileLions"
],
entryValues: [
"Gogostream",
"Vidstreaming",
"Doodstream",
"StreamWish",
"Mp4upload",
"FileLions"
]),
MultiSelectListPreference(
key: "hoster_selection",
title: "Enable/Disable Hosts",
summary: "",
entries: [
"Gogostream",
"Vidstreaming",
"Doodstream",
"StreamWish",
"Mp4upload",
"FileLions"
],
entryValues: [
"vidcdn",
"anime",
"doodstream",
"streamwish",
"mp4upload",
"filelions"
],
values: [
"vidcdn",
"anime",
"doodstream",
"streamwish",
"mp4upload",
"filelions"
]),
];
}
String preferenceBaseUrl(int sourceId) {
return getPreferenceValue(sourceId, "override_baseurl_v$sourceId");
}
List<String> preferenceHosterSelection(int sourceId) {
return getPreferenceValue(sourceId, "hoster_selection");
}
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
String quality = getPreferenceValue(sourceId, "preferred_quality");
String server = getPreferenceValue(sourceId, "preferred_server");
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;
});
videos.sort((MVideo a, MVideo b) {
int serverMatchA = 0;
if (a.quality.contains(server)) {
serverMatchA = 1;
}
int serverMatchB = 0;
if (b.quality.contains(server)) {
serverMatchB = 1;
}
return serverMatchB - serverMatchA;
});
return videos;
}
} }
GogoAnime main() { GogoAnime main() {

View File

@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
Source get gogoanimeSource => _gogoanimeSource; Source get gogoanimeSource => _gogoanimeSource;
const gogoanimeVersion = "0.0.4"; const gogoanimeVersion = "0.0.5";
const gogoanimeSourceCodeUrl = const gogoanimeSourceCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/gogoanime/gogoanime-v$gogoanimeVersion.dart"; "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/en/gogoanime/gogoanime-v$gogoanimeVersion.dart";
Source _gogoanimeSource = Source( Source _gogoanimeSource = Source(

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

@@ -0,0 +1,360 @@
import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';
class AnimeSaturn extends MProvider {
AnimeSaturn();
@override
Future<MPages> getPopular(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/animeincorso?page=$page"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res,
'//*[@class="sebox"]/div[@class="msebox"]/div[@class="headsebox"]/div[@class="tisebox"]/h2/a/@href');
final names = xpath(res,
'//*[@class="sebox"]/div[@class="msebox"]/div[@class="headsebox"]/div[@class="tisebox"]/h2/a/text()');
final images = xpath(res,
'//*[@class="sebox"]/div[@class="msebox"]/div[@class="bigsebox"]/div/img[@class="attachment-post-thumbnail size-post-thumbnail wp-post-image"]/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = formatTitle(names[i]);
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
return MPages(animeList, true);
}
@override
Future<MPages> getLatestUpdates(MSource source, int page) async {
final data = {"url": "${source.baseUrl}/newest?page=$page"};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
final urls = xpath(res, '//*[@class="card mb-4 shadow-sm"]/a/@href');
final names = xpath(res, '///*[@class="card mb-4 shadow-sm"]/a/@title');
final images = xpath(res,
'//*[@class="card mb-4 shadow-sm"]/a/img[@class="new-anime"]/@src');
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = formatTitle(names[i]);
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
return MPages(animeList, true);
}
@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}/animelist?search=$query";
} else {
url = "${source.baseUrl}/filter?";
int variantgenre = 0;
int variantstate = 0;
int variantyear = 0;
for (var filter in filters) {
if (filter.type == "GenreFilter") {
final genre = (filter.state as List).where((e) => e.state).toList();
if (genre.isNotEmpty) {
for (var st in genre) {
url += "&categories%5B${variantgenre}%5D=${st.value}";
variantgenre++;
}
}
} else if (filter.type == "YearList") {
final years = (filter.state as List).where((e) => e.state).toList();
if (years.isNotEmpty) {
for (var st in years) {
url += "&years%5B${variantyear}%5D=${st.value}";
variantyear++;
}
}
} else if (filter.type == "StateList") {
final states = (filter.state as List).where((e) => e.state).toList();
if (states.isNotEmpty) {
for (var st in states) {
url += "&states%5B${variantstate}%5D=${st.value}";
variantstate++;
}
}
} else if (filter.type == "LangList") {
final lang = filter.values[filter.state].value;
if (lang.isNotEmpty) {
url += "&language%5B0%5D=$lang";
}
}
}
url += "&page=$page";
}
final data = {"url": url};
final res = await http('GET', json.encode(data));
List<MManga> animeList = [];
List<String> urls = [];
List<String> names = [];
List<String> images = [];
if (query.isNotEmpty) {
urls = xpath(res,
'//*[@class="list-group"]/li[@class="list-group-item bg-dark-as-box-shadow"]/div[@class="item-archivio"]/div[@class="info-archivio"]/h3/a[@class="badge badge-archivio badge-light"]/@href');
names = xpath(res,
'//*[@class="list-group"]/li[@class="list-group-item bg-dark-as-box-shadow"]/div[@class="item-archivio"]/div[@class="info-archivio"]/h3/a[@class="badge badge-archivio badge-light"]/text()');
images = xpath(res,
'//*[@class="list-group"]/li[@class="list-group-item bg-dark-as-box-shadow"]/div[@class="item-archivio"]/a/img/@src');
} else {
urls = xpath(res, '//*[@class="card mb-4 shadow-sm"]/a/@href');
names = xpath(res, '//*[@class="card mb-4 shadow-sm"]/a/text()');
images = xpath(res,
'//*[@class="card mb-4 shadow-sm"]/a/img[@class="new-anime"]/@src');
}
for (var i = 0; i < names.length; i++) {
MManga anime = MManga();
anime.name = formatTitle(names[i]);
anime.imageUrl = images[i];
anime.link = urls[i];
animeList.add(anime);
}
return MPages(animeList, query.isEmpty);
}
@override
Future<MManga> getDetail(MSource source, String url) async {
final statusList = [
{"In corso": 0, "Finito": 1}
];
final data = {"url": url};
final res = await http('GET', json.encode(data));
MManga anime = MManga();
final details = xpath(res,
'//div[@class="container shadow rounded bg-dark-as-box mb-3 p-3 w-100 text-white"]/text()')
.first;
anime.status = parseStatus(
details.substring(
details.indexOf("Stato:") + 6, details.indexOf("Data di uscita:")),
statusList);
anime.author = details.substring(7, details.indexOf("Stato:"));
final description = xpath(res, '//*[@id="shown-trama"]/text()');
final descriptionFull = xpath(res, '//*[@id="full-trama"]/text()');
if (description.isNotEmpty) {
anime.description = description.first;
} else {
anime.description = "";
}
if (descriptionFull.isNotEmpty) {
if (descriptionFull.first.length > anime.description.length) {
anime.description = descriptionFull.first;
}
}
anime.genre = xpath(res,
'//*[@class="container shadow rounded bg-dark-as-box mb-3 p-3 w-100"]/a/text()');
final epUrls = xpath(res,
'//*[@class="btn-group episodes-button episodi-link-button"]/a/@href');
final titles = xpath(res,
'//*[@class="btn-group episodes-button episodi-link-button"]/a/text()');
List<MChapter>? episodesList = [];
for (var i = 0; i < epUrls.length; i++) {
MChapter episode = MChapter();
episode.name = titles[i];
episode.url = epUrls[i];
episodesList.add(episode);
}
anime.chapters = episodesList.reversed.toList();
return anime;
}
@override
Future<List<MVideo>> getVideoList(MSource source, String url) async {
final res = await http('GET', json.encode({"url": url}));
final urlVid = xpath(res, '//a[contains(@href,"/watch")]/@href').first;
final resVid = await http('GET', json.encode({"url": urlVid}));
String masterUrl = "";
if (resVid.contains("jwplayer(")) {
masterUrl = substringBefore(substringAfter(resVid, "file: \""), "\"");
} else {
masterUrl = querySelectorAll(resVid,
selector: "source",
typeElement: 3,
attributes: "src",
typeRegExp: 0)
.first;
}
List<MVideo> videos = [];
if (masterUrl.endsWith("playlist.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 = quality;
videos.add(video);
}
} else {
MVideo video = MVideo();
video
..url = masterUrl
..originalUrl = masterUrl
..quality = "Qualità predefinita";
videos.add(video);
}
return sortVideos(videos, source.id);
}
String formatTitle(String titlestring) {
return titlestring
.replaceAll("(ITA) ITA", "Dub ITA")
.replaceAll("(ITA)", "Dub ITA")
.replaceAll("Sub ITA", "");
}
@override
List<dynamic> getFilterList(MSource source) {
return [
HeaderFilter("Ricerca per titolo ignora i filtri e viceversa"),
GroupFilter("GenreFilter", "Generi", [
CheckBoxFilter("Arti Marziali", "Arti Marziali"),
CheckBoxFilter("Avventura", "Avventura"),
CheckBoxFilter("Azione", "Azione"),
CheckBoxFilter("Bambini", "Bambini"),
CheckBoxFilter("Commedia", "Commedia"),
CheckBoxFilter("Demenziale", "Demenziale"),
CheckBoxFilter("Demoni", "Demoni"),
CheckBoxFilter("Drammatico", "Drammatico"),
CheckBoxFilter("Ecchi", "Ecchi"),
CheckBoxFilter("Fantasy", "Fantasy"),
CheckBoxFilter("Gioco", "Gioco"),
CheckBoxFilter("Harem", "Harem"),
CheckBoxFilter("Hentai", "Hentai"),
CheckBoxFilter("Horror", "Horror"),
CheckBoxFilter("Josei", "Josei"),
CheckBoxFilter("Magia", "Magia"),
CheckBoxFilter("Mecha", "Mecha"),
CheckBoxFilter("Militari", "Militari"),
CheckBoxFilter("Mistero", "Mistero"),
CheckBoxFilter("Musicale", "Musicale"),
CheckBoxFilter("Parodia", "Parodia"),
CheckBoxFilter("Polizia", "Polizia"),
CheckBoxFilter("Psicologico", "Psicologico"),
CheckBoxFilter("Romantico", "Romantico"),
CheckBoxFilter("Samurai", "Samurai"),
CheckBoxFilter("Sci-Fi", "Sci-Fi"),
CheckBoxFilter("Scolastico", "Scolastico"),
CheckBoxFilter("Seinen", "Seinen"),
CheckBoxFilter("Sentimentale", "Sentimentale"),
CheckBoxFilter("Shoujo Ai", "Shoujo Ai"),
CheckBoxFilter("Shoujo", "Shoujo"),
CheckBoxFilter("Shounen Ai", "Shounen Ai"),
CheckBoxFilter("Shounen", "Shounen"),
CheckBoxFilter("Slice of Life", "Slice of Life"),
CheckBoxFilter("Soprannaturale", "Soprannaturale"),
CheckBoxFilter("Spazio", "Spazio"),
CheckBoxFilter("Sport", "Sport"),
CheckBoxFilter("Storico", "Storico"),
CheckBoxFilter("Superpoteri", "Superpoteri"),
CheckBoxFilter("Thriller", "Thriller"),
CheckBoxFilter("Vampiri", "Vampiri"),
CheckBoxFilter("Veicoli", "Veicoli"),
CheckBoxFilter("Yaoi", "Yaoi"),
CheckBoxFilter("Yuri", "Yuri"),
]),
GroupFilter("YearList", "Anno di Uscita", [
for (var i = 1969; i < 2022; i++)
CheckBoxFilter(i.toString(), i.toString()),
]),
GroupFilter("StateList", "Stato", [
CheckBoxFilter("In corso", "0"),
CheckBoxFilter("Finito", "1"),
CheckBoxFilter("Non rilasciato", "2"),
CheckBoxFilter("Droppato", "3"),
]),
SelectFilter("LangList", "Lingua", 0, [
SelectFilterOption("", ""),
SelectFilterOption("Subbato", "0"),
SelectFilterOption("Doppiato", "1"),
]),
];
}
@override
List<dynamic> getSourcePreferences(MSource source) {
return [
ListPreference(
key: "preferred_quality",
title: "Qualità preferita",
summary: "",
valueIndex: 0,
entries: ["1080p", "720p", "480p", "360p", "240p", "144p"],
entryValues: ["1080", "720", "480", "360", "240", "144"]),
];
}
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;
}
}
AnimeSaturn main() {
return AnimeSaturn();
}

16
anime/src/it/source.dart Normal file
View File

@@ -0,0 +1,16 @@
import '../../../model/source.dart';
import '../../../utils/utils.dart';
Source get animesaturn => _animesaturn;
const animesaturnVersion = "0.0.1";
const animesaturnCodeUrl =
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/anime/src/it/animesaturn/animesaturn-v$animesaturnVersion.dart";
Source _animesaturn = Source(
name: "AnimeSaturn",
baseUrl: "https://www.animesaturn.tv",
lang: "it",
typeSource: "single",
iconUrl: getIconUrl("animesaturn", "it"),
sourceCodeUrl: animesaturnCodeUrl,
version: animesaturnVersion,
isManga: false);

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

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

@@ -308,7 +308,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"),

View File

@@ -47,7 +47,7 @@ class Source {
this.version = "", this.version = "",
this.isManga = true, this.isManga = true,
this.isFullData = false, this.isFullData = false,
this.appMinVerReq = "0.1.1"}); this.appMinVerReq = "0.1.2"});
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {