Update
@@ -1,60 +0,0 @@
|
||||
import '../../model/source.dart';
|
||||
import 'multisrc/datalifeengine/sources.dart';
|
||||
import 'multisrc/dopeflix/sources.dart';
|
||||
import 'multisrc/zorotheme/sources.dart';
|
||||
import 'src/all/animeworldindia/sources.dart';
|
||||
import 'src/all/nyaa/source.dart';
|
||||
import 'src/ar/okanime/source.dart';
|
||||
import 'src/de/animetoast/source.dart';
|
||||
import 'src/en/animepahe/source.dart';
|
||||
import 'src/en/gogoanime/source.dart';
|
||||
import 'src/en/kisskh/source.dart';
|
||||
import 'src/en/nineanimetv/source.dart';
|
||||
import 'src/en/vumeto/source.dart';
|
||||
import 'src/es/animeonlineninja/source.dart';
|
||||
import 'src/fr/animesama/source.dart';
|
||||
import 'src/fr/anizone/source.dart';
|
||||
import 'src/hi/yomovies/source.dart';
|
||||
import 'src/en/uhdmovies/source.dart';
|
||||
import 'src/fr/animesultra/source.dart';
|
||||
import 'src/fr/franime/source.dart';
|
||||
import 'src/fr/otakufr/source.dart';
|
||||
import 'src/id/nimegami/source.dart';
|
||||
import 'src/id/oploverz/source.dart';
|
||||
import 'src/id/otakudesu/source.dart';
|
||||
import 'src/it/animesaturn/source.dart';
|
||||
import 'src/pt/animesvision/source.dart';
|
||||
import 'src/sq/filma24/source.dart';
|
||||
import 'src/tr/diziwatch/source.dart';
|
||||
import 'src/en/donghuastream/source.dart';
|
||||
|
||||
List<Source> dartAnimesourceList = [
|
||||
gogoanimeSource,
|
||||
franimeSource,
|
||||
otakufr,
|
||||
animesultraSource,
|
||||
...zorothemeSourcesList,
|
||||
okanimeSource,
|
||||
otakudesu,
|
||||
nimegami,
|
||||
oploverz,
|
||||
...dopeflixSourcesList,
|
||||
animesaturn,
|
||||
uhdmoviesSource,
|
||||
...datalifeengineSourcesList,
|
||||
filma24,
|
||||
yomoviesSource,
|
||||
animesamaSource,
|
||||
nineanimetv,
|
||||
...animeworldindiaSourcesList,
|
||||
nyaaSource,
|
||||
animepaheSource,
|
||||
animetoast,
|
||||
animesvision,
|
||||
diziwatchSource,
|
||||
aniZoneSource,
|
||||
animeonlineninjaSource,
|
||||
kisskhSource,
|
||||
vumetoSource,
|
||||
donghuastreamSource,
|
||||
];
|
||||
@@ -1,457 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
|
||||
class DataLifeEngine extends MProvider {
|
||||
DataLifeEngine({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl${getPath(source)}page/$page"),
|
||||
)).body;
|
||||
return animeFromElement(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return MPages([], false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String res = "";
|
||||
if (query.isNotEmpty) {
|
||||
if (query.length < 4)
|
||||
throw "La recherche est suspendue! La chaîne de recherche est vide ou contient moins de 4 caractères.";
|
||||
final headers = {
|
||||
"Host": Uri.parse(baseUrl).host,
|
||||
"Origin": baseUrl,
|
||||
"Referer": "$baseUrl/",
|
||||
};
|
||||
final cleanQuery = query.replaceAll(" ", "+");
|
||||
if (page == 1) {
|
||||
res =
|
||||
(await client.post(
|
||||
Uri.parse(
|
||||
"$baseUrl?do=search&subaction=search&story=$cleanQuery",
|
||||
),
|
||||
headers: headers,
|
||||
)).body;
|
||||
} else {
|
||||
res =
|
||||
(await client.post(
|
||||
Uri.parse(
|
||||
"$baseUrl?do=search&subaction=search&search_start=$page&full_search=0&result_from=11&story=$cleanQuery",
|
||||
),
|
||||
headers: headers,
|
||||
)).body;
|
||||
}
|
||||
} else {
|
||||
String url = "";
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "CategoriesFilter") {
|
||||
if (filter.state != 0) {
|
||||
url = "$baseUrl${filter.values[filter.state].value}page/$page/";
|
||||
}
|
||||
} else if (filter.type == "GenresFilter") {
|
||||
if (filter.state != 0) {
|
||||
url = "$baseUrl${filter.values[filter.state].value}page/$page/";
|
||||
}
|
||||
}
|
||||
}
|
||||
res = (await client.get(Uri.parse(url))).body;
|
||||
}
|
||||
|
||||
return animeFromElement(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
String res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl${getUrlWithoutDomain(url)}"),
|
||||
)).body;
|
||||
MManga anime = MManga();
|
||||
final description = xpath(res, '//span[@itemprop="description"]/text()');
|
||||
anime.description = description.isNotEmpty ? description.first : "";
|
||||
anime.genre = xpath(res, '//span[@itemprop="genre"]/a/text()');
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
if (source.name == "French Anime") {
|
||||
final epsData = xpath(res, '//div[@class="eps"]/text()');
|
||||
for (var epData in epsData.first.split('\n')) {
|
||||
final data = epData.split('!');
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Episode ${data.first}";
|
||||
ep.url = data.last;
|
||||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
final doc = parseHtml(res);
|
||||
final elements =
|
||||
doc
|
||||
.select(".hostsblock div:has(a)")
|
||||
.where(
|
||||
(MElement e) => e.outerHtml.contains("loadVideo('https://"),
|
||||
)
|
||||
.toList();
|
||||
if (elements.isNotEmpty) {
|
||||
for (var element in elements) {
|
||||
element = element as MElement;
|
||||
MChapter ep = MChapter();
|
||||
ep.name = element.className
|
||||
.replaceAll("ep", "Episode ")
|
||||
.replaceAll("vs", " VOSTFR")
|
||||
.replaceAll("vf", " VF");
|
||||
ep.url = element
|
||||
.select("a")
|
||||
.map(
|
||||
(MElement e) => substringBefore(
|
||||
substringAfter(e.attr('onclick'), "loadVideo('"),
|
||||
"')",
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
.join(",")
|
||||
.replaceAll("/vd.php?u=", "");
|
||||
ep.scanlator = element.className.contains('vf') ? 'VF' : 'VOSTFR';
|
||||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Film";
|
||||
ep.url = doc
|
||||
.select("a")
|
||||
.where((MElement e) => e.outerHtml.contains("loadVideo('https://"))
|
||||
.map(
|
||||
(MElement e) => substringBefore(
|
||||
substringAfter(e.attr('onclick'), "loadVideo('"),
|
||||
"')",
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
.join(",")
|
||||
.replaceAll("/vd.php?u=", "");
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
List<MVideo> videos = [];
|
||||
final sUrls = url.split(',');
|
||||
for (var sUrl in sUrls) {
|
||||
List<MVideo> a = [];
|
||||
if (sUrl.contains("dood") || sUrl.contains("d000")) {
|
||||
a = await doodExtractor(sUrl, "DoodStream");
|
||||
} else if ([
|
||||
"streamhide",
|
||||
"guccihide",
|
||||
"streamvid",
|
||||
"dhtpre",
|
||||
].any((a) => sUrl.contains(a))) {
|
||||
a = await streamHideExtractor(sUrl);
|
||||
} else if (sUrl.contains("uqload")) {
|
||||
a = await uqloadExtractor(sUrl);
|
||||
} else if (sUrl.contains("upstream")) {
|
||||
a = await upstreamExtractor(sUrl);
|
||||
} else if (sUrl.contains("sibnet")) {
|
||||
a = await sibnetExtractor(sUrl);
|
||||
} else if (sUrl.contains("ok.ru")) {
|
||||
a = await okruExtractor(sUrl);
|
||||
} else if (sUrl.contains("vidmoly")) {
|
||||
a = await vidmolyExtractor(sUrl);
|
||||
} else if (sUrl.contains("streamtape")) {
|
||||
a = await streamTapeExtractor(sUrl, "");
|
||||
} else if (sUrl.contains("voe.sx")) {
|
||||
a = await voeExtractor(sUrl, "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
MPages animeFromElement(String res) {
|
||||
final htmls = parseHtml(res).select("div#dle-content > div.mov");
|
||||
List<MManga> animeList = [];
|
||||
for (var h in htmls) {
|
||||
final html = h.innerHtml;
|
||||
final url = xpath(html, '//a/@href').first;
|
||||
final name = xpath(html, '//a/text()').first;
|
||||
final image = xpath(html, '//div[contains(@class,"mov")]/img/@src').first;
|
||||
final season = xpath(html, '//div/span[@class="block-sai"]/text()');
|
||||
MManga anime = MManga();
|
||||
anime.name =
|
||||
"$name ${season.isNotEmpty ? season.first.replaceAll("\n", " ") : ""}";
|
||||
anime.imageUrl = "$baseUrl$image";
|
||||
anime.link = url;
|
||||
animeList.add(anime);
|
||||
}
|
||||
final hasNextPage = xpath(res, '//span[@class="pnext"]/a/@href').isNotEmpty;
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamHideExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(substringAfter(unpackJs(res), "sources:"), "file:\""),
|
||||
"src:\"",
|
||||
),
|
||||
'"',
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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 = "StreamHideVid - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> upstreamExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "m3u8")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(unpackJs(js.first), "{file:\""),
|
||||
"\"}",
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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>> uqloadExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "sources:")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(js.first, "sources: [\""),
|
||||
'"',
|
||||
);
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Uqload"
|
||||
..headers = {"Referer": "${Uri.parse(url).origin}/"};
|
||||
return [video];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidmolyExtractor(String url) async {
|
||||
final headers = {'Referer': 'https://vidmoly.to'};
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes = await client.get(
|
||||
Uri.parse(playlistUrl),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (masterPlaylistRes.statusCode == 200) {
|
||||
for (var it in substringAfter(
|
||||
masterPlaylistRes.body,
|
||||
"#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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Vidmoly $quality"
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
if (source.name == "French Anime") return "/animes-vostfr/";
|
||||
return "/serie-en-streaming/";
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
if (source.name == "Wiflix")
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://wiflix-hd.vip",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://wiflix-hd.vip",
|
||||
),
|
||||
if (source.name == "French Anime")
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://french-anime.com",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://french-anime.com",
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
HeaderFilter("La recherche de texte ignore les filtres"),
|
||||
if (source.name == "French Anime")
|
||||
SelectFilter("CategoriesFilter", "Catégories", 0, [
|
||||
SelectFilterOption("<Sélectionner>", ""),
|
||||
SelectFilterOption("Action", "/genre/action/"),
|
||||
SelectFilterOption("Aventure", "/genre/aventure/"),
|
||||
SelectFilterOption("Arts martiaux", "/genre/arts-martiaux/"),
|
||||
SelectFilterOption("Combat", "/genre/combat/"),
|
||||
SelectFilterOption("Comédie", "/genre/comedie/"),
|
||||
SelectFilterOption("Drame", "/genre/drame/"),
|
||||
SelectFilterOption("Epouvante", "/genre/epouvante/"),
|
||||
SelectFilterOption("Fantastique", "/genre/fantastique/"),
|
||||
SelectFilterOption("Fantasy", "/genre/fantasy/"),
|
||||
SelectFilterOption("Mystère", "/genre/mystere/"),
|
||||
SelectFilterOption("Romance", "/genre/romance/"),
|
||||
SelectFilterOption("Shonen", "/genre/shonen/"),
|
||||
SelectFilterOption("Surnaturel", "/genre/surnaturel/"),
|
||||
SelectFilterOption("Sci-Fi", "/genre/sci-fi/"),
|
||||
SelectFilterOption("School life", "/genre/school-life/"),
|
||||
SelectFilterOption("Ninja", "/genre/ninja/"),
|
||||
SelectFilterOption("Seinen", "/genre/seinen/"),
|
||||
SelectFilterOption("Horreur", "/genre/horreur/"),
|
||||
SelectFilterOption("Tranche de vie", "/genre/tranchedevie/"),
|
||||
SelectFilterOption("Psychologique", "/genre/psychologique/"),
|
||||
]),
|
||||
if (source.name == "French Anime")
|
||||
SelectFilter("GenresFilter", "Genres", 0, [
|
||||
SelectFilterOption("<Sélectionner>", ""),
|
||||
SelectFilterOption("Animes VF", "/animes-vf/"),
|
||||
SelectFilterOption("Animes VOSTFR", "/animes-vostfr/"),
|
||||
SelectFilterOption("Films VF et VOSTFR", "/films-vf-vostfr/"),
|
||||
]),
|
||||
if (source.name == "Wiflix")
|
||||
SelectFilter("CategoriesFilter", "Catégories", 0, [
|
||||
SelectFilterOption("<Sélectionner>", ""),
|
||||
SelectFilterOption("Séries", "/serie-en-streaming/"),
|
||||
SelectFilterOption("Films", "/film-en-streaming/"),
|
||||
]),
|
||||
if (source.name == "Wiflix")
|
||||
SelectFilter("GenresFilter", "Genres", 0, [
|
||||
SelectFilterOption("<Sélectionner>", ""),
|
||||
SelectFilterOption("Action", "/film-en-streaming/action/"),
|
||||
SelectFilterOption("Animation", "/film-en-streaming/animation/"),
|
||||
SelectFilterOption(
|
||||
"Arts Martiaux",
|
||||
"/film-en-streaming/arts-martiaux/",
|
||||
),
|
||||
SelectFilterOption("Aventure", "/film-en-streaming/aventure/"),
|
||||
SelectFilterOption("Biopic", "/film-en-streaming/biopic/"),
|
||||
SelectFilterOption("Comédie", "/film-en-streaming/comedie/"),
|
||||
SelectFilterOption(
|
||||
"Comédie Dramatique",
|
||||
"/film-en-streaming/comedie-dramatique/",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Épouvante Horreur",
|
||||
"/film-en-streaming/horreur/",
|
||||
),
|
||||
SelectFilterOption("Drame", "/film-en-streaming/drame/"),
|
||||
SelectFilterOption(
|
||||
"Documentaire",
|
||||
"/film-en-streaming/documentaire/",
|
||||
),
|
||||
SelectFilterOption("Espionnage", "/film-en-streaming/espionnage/"),
|
||||
SelectFilterOption("Famille", "/film-en-streaming/famille/"),
|
||||
SelectFilterOption("Fantastique", "/film-en-streaming/fantastique/"),
|
||||
SelectFilterOption("Guerre", "/film-en-streaming/guerre/"),
|
||||
SelectFilterOption("Historique", "/film-en-streaming/historique/"),
|
||||
SelectFilterOption("Musical", "/film-en-streaming/musical/"),
|
||||
SelectFilterOption("Policier", "/film-en-streaming/policier/"),
|
||||
SelectFilterOption("Romance", "/film-en-streaming/romance/"),
|
||||
SelectFilterOption(
|
||||
"Science-Fiction",
|
||||
"/film-en-streaming/science-fiction/",
|
||||
),
|
||||
SelectFilterOption("Spectacles", "/film-en-streaming/spectacles/"),
|
||||
SelectFilterOption("Thriller", "/film-en-streaming/thriller/"),
|
||||
SelectFilterOption("Western", "/film-en-streaming/western/"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
DataLifeEngine main(MSource source) {
|
||||
return DataLifeEngine(source: source);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import '../../../../model/source.dart';
|
||||
import 'src/frenchanime/frenchanime.dart';
|
||||
import 'src/wiflix/wiflix.dart';
|
||||
|
||||
const _datalifeengineVersion = "0.0.65";
|
||||
const _datalifeengineSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/datalifeengine.dart";
|
||||
|
||||
List<Source> get datalifeengineSourcesList => _datalifeengineSourcesList;
|
||||
List<Source> _datalifeengineSourcesList =
|
||||
[
|
||||
//French Anime (FR)
|
||||
frenchanimeSource,
|
||||
//Wiflix (FR)
|
||||
wiflixSource,
|
||||
]
|
||||
.map(
|
||||
(e) =>
|
||||
e
|
||||
..sourceCodeUrl = _datalifeengineSourceCodeUrl
|
||||
..version = _datalifeengineVersion,
|
||||
)
|
||||
.toList();
|
||||
@@ -1,13 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get frenchanimeSource => _frenchanimeSource;
|
||||
|
||||
Source _frenchanimeSource = Source(
|
||||
name: "French Anime",
|
||||
baseUrl: "https://french-anime.com",
|
||||
lang: "fr",
|
||||
typeSource: "datalifeengine",
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/frenchanime/icon.png",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
@@ -1,13 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get wiflixSource => _wiflixSource;
|
||||
|
||||
Source _wiflixSource = Source(
|
||||
name: "Wiflix",
|
||||
baseUrl: "https://wiflix-hd.vip",
|
||||
lang: "fr",
|
||||
typeSource: "datalifeengine",
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/datalifeengine/src/wiflix/icon.png",
|
||||
);
|
||||
@@ -1,577 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class DopeFlix extends MProvider {
|
||||
DopeFlix({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "preferred_domain");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"$baseUrl/${getPreferenceValue(source.id, "preferred_popular_page")}?page=$page",
|
||||
),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl/home"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final path =
|
||||
'//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 names = xpath(res, '$path/a/@title');
|
||||
final images = xpath(res, '$path/img/@data-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);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "$baseUrl";
|
||||
|
||||
if (query.isNotEmpty) {
|
||||
url += "/search/${query.replaceAll(" ", "-")}?page=$page";
|
||||
} else {
|
||||
url += "/filter/?page=$page";
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "TypeFilter") {
|
||||
final type = filter.values[filter.state].value;
|
||||
url += "${ll(url)}type=$type";
|
||||
} else if (filter.type == "QualityFilter") {
|
||||
final quality = filter.values[filter.state].value;
|
||||
url += "${ll(url)}quality=$quality";
|
||||
} else if (filter.type == "ReleaseYearFilter") {
|
||||
final year = filter.values[filter.state].value;
|
||||
url += "${ll(url)}release_year=$year";
|
||||
} else if (filter.type == "GenresFilter") {
|
||||
final genre = (filter.state as List).where((e) => e.state).toList();
|
||||
if (genre.isNotEmpty) {
|
||||
url += "${ll(url)}genre=";
|
||||
for (var st in genre) {
|
||||
url += "${st.value}-";
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "CountriesFilter") {
|
||||
final country = (filter.state as List).where((e) => e.state).toList();
|
||||
if (country.isNotEmpty) {
|
||||
url += "${ll(url)}country=";
|
||||
for (var st in country) {
|
||||
url += "${st.value}-";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
url = getUrlWithoutDomain(url);
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final description = xpath(res, '//div[@class="description"]/text()');
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first.replaceAll("Overview:", "");
|
||||
}
|
||||
final author = xpath(res, '//div[contains(text(),"Production")]/a/text()');
|
||||
if (author.isNotEmpty) {
|
||||
anime.author = author.first;
|
||||
}
|
||||
anime.genre = xpath(res, '//div[contains(text(),"Genre")]/a/text()');
|
||||
List<MChapter> episodesList = [];
|
||||
final id = xpath(res, '//div[@class="detail_page-watch"]/@data-id').first;
|
||||
final dataType =
|
||||
xpath(res, '//div[@class="detail_page-watch"]/@data-type').first;
|
||||
if (dataType == "1") {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Movie";
|
||||
episode.url = "$baseUrl/ajax/movie/episodes/$id";
|
||||
episodesList.add(episode);
|
||||
} else {
|
||||
final resS =
|
||||
(await client.get(Uri.parse("$baseUrl/ajax/v2/tv/seasons/$id"))).body;
|
||||
|
||||
final seasonIds = xpath(
|
||||
resS,
|
||||
'//a[@class="dropdown-item ss-item"]/@data-id',
|
||||
);
|
||||
final seasonNames = xpath(
|
||||
resS,
|
||||
'//a[@class="dropdown-item ss-item"]/text()',
|
||||
);
|
||||
for (int i = 0; i < seasonIds.length; i++) {
|
||||
final seasonId = seasonIds[i];
|
||||
final seasonName = seasonNames[i];
|
||||
|
||||
final html =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/ajax/v2/season/episodes/$seasonId"),
|
||||
)).body;
|
||||
|
||||
final epsHtmls = parseHtml(html).select("div.eps-item");
|
||||
|
||||
for (var epH in epsHtmls) {
|
||||
final epHtml = epH.outerHtml;
|
||||
final episodeId =
|
||||
xpath(
|
||||
epHtml,
|
||||
'//div[contains(@class,"eps-item")]/@data-id',
|
||||
).first;
|
||||
final epNum =
|
||||
xpath(epHtml, '//div[@class="episode-number"]/text()').first;
|
||||
final epName = xpath(epHtml, '//h3[@class="film-name"]/text()').first;
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "$seasonName $epNum $epName";
|
||||
episode.url = "$baseUrl/ajax/v2/episode/servers/$episodeId";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
url = getUrlWithoutDomain(url);
|
||||
final res = (await client.get(Uri.parse("$baseUrl/$url"))).body;
|
||||
|
||||
final vidsHtmls = parseHtml(res).select("ul.fss-list a.btn-play");
|
||||
|
||||
List<MVideo> videos = [];
|
||||
for (var vidH in vidsHtmls) {
|
||||
final vidHtml = vidH.outerHtml;
|
||||
final id = xpath(vidHtml, '//a/@data-id').first;
|
||||
final name = xpath(vidHtml, '//span/text()').first;
|
||||
final resSource =
|
||||
(await client.get(Uri.parse("$baseUrl/ajax/sources/$id"))).body;
|
||||
|
||||
final vidUrl = substringBefore(
|
||||
substringAfter(resSource, "\"link\":\""),
|
||||
"\"",
|
||||
);
|
||||
List<MVideo> a = [];
|
||||
String masterUrl = "";
|
||||
String type = "";
|
||||
if (name.contains("DoodStream")) {
|
||||
a = await doodExtractor(vidUrl, "DoodStream");
|
||||
} else if (["Vidcloud", "UpCloud"].contains(name)) {
|
||||
final id = substringBefore(substringAfter(vidUrl, "/embed-4/"), "?");
|
||||
final serverUrl = substringBefore(vidUrl, "/embed");
|
||||
|
||||
final resServer =
|
||||
(await client.get(
|
||||
Uri.parse("$serverUrl/ajax/embed-4/getSources?id=$id"),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"},
|
||||
)).body;
|
||||
final encrypted = getMapValue(resServer, "encrypted");
|
||||
|
||||
String videoResJson = "";
|
||||
if (encrypted == "true") {
|
||||
final ciphered = getMapValue(resServer, "sources");
|
||||
|
||||
List<List<int>> indexPairs = await generateIndexPairs();
|
||||
|
||||
var password = '';
|
||||
String ciphertext = ciphered;
|
||||
int index = 0;
|
||||
for (List<int> item in json.decode(json.encode(indexPairs))) {
|
||||
int start = item.first + index;
|
||||
int end = start + item.last;
|
||||
String passSubstr = ciphered.substring(start, end);
|
||||
password += passSubstr;
|
||||
ciphertext = ciphertext.replaceFirst(passSubstr, "");
|
||||
index += item.last;
|
||||
}
|
||||
videoResJson = decryptAESCryptoJS(ciphertext, password);
|
||||
masterUrl =
|
||||
((json.decode(videoResJson) as List<Map<String, dynamic>>)
|
||||
.first)['file'];
|
||||
|
||||
type =
|
||||
((json.decode(videoResJson) as List<Map<String, dynamic>>)
|
||||
.first)['type'];
|
||||
} else {
|
||||
masterUrl =
|
||||
((json.decode(resServer)["sources"] as List<Map<String, dynamic>>)
|
||||
.first)['file'];
|
||||
|
||||
type =
|
||||
((json.decode(resServer)["sources"] as List<Map<String, dynamic>>)
|
||||
.first)['type'];
|
||||
}
|
||||
|
||||
final tracks =
|
||||
(json.decode(resServer)['tracks'] as List)
|
||||
.where((e) => e['kind'] == 'captions' ? true : false)
|
||||
.toList();
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in tracks) {
|
||||
try {
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle
|
||||
..label = sub["label"]
|
||||
..file = sub["file"];
|
||||
subtitles.add(subtitle);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
subtitles = sortSubs(subtitles, source.id);
|
||||
if (type == "hls") {
|
||||
final masterPlaylistRes =
|
||||
(await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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 as String).split("/").sublist(0, (masterUrl as String).split("/").length - 1).join("/")}/$videoUrl";
|
||||
}
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$name - $quality"
|
||||
..subtitles = subtitles;
|
||||
a.add(video);
|
||||
}
|
||||
} else {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "$name - Default"
|
||||
..subtitles = subtitles;
|
||||
a.add(video);
|
||||
}
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<List<int>>> generateIndexPairs() async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("https://rabbitstream.net/js/player/prod/e4-player.min.js"),
|
||||
)).body;
|
||||
|
||||
String script = substringBefore(substringAfter(res, "const "), "()");
|
||||
script = script.substring(0, script.lastIndexOf(','));
|
||||
final list =
|
||||
script
|
||||
.split(",")
|
||||
.map((String e) {
|
||||
String value = substringAfter(e, "=");
|
||||
if (value.contains("0x")) {
|
||||
return int.parse(substringAfter(value, "0x"), radix: 16);
|
||||
} else {
|
||||
return int.parse(value);
|
||||
}
|
||||
})
|
||||
.toList()
|
||||
.skip(1)
|
||||
.toList();
|
||||
return chunked(
|
||||
list,
|
||||
2,
|
||||
).map((List<int> list) => list.reversed.toList()).toList();
|
||||
}
|
||||
|
||||
List<List<int>> chunked(List<int> list, int size) {
|
||||
List<List<int>> chunks = [];
|
||||
for (int i = 0; i < list.length; i += size) {
|
||||
int end = list.length;
|
||||
if (i + size < list.length) {
|
||||
end = i + size;
|
||||
}
|
||||
chunks.add(list.sublist(i, end));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final path =
|
||||
'//div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]';
|
||||
final urls = xpath(res, '$path/a/@href');
|
||||
final names = xpath(res, '$path/a/@title');
|
||||
final images = xpath(res, '$path/img/@data-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 pages = xpath(
|
||||
res,
|
||||
'//ul[contains(@class,"pagination")]/li/a[@title="Next"]/@title',
|
||||
);
|
||||
return MPages(animeList, pages.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SelectFilter("TypeFilter", "Type", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("Movies", "movies"),
|
||||
SelectFilterOption("TV Shows", "tv"),
|
||||
]),
|
||||
SelectFilter("QualityFilter", "Quality", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("HD", "HD"),
|
||||
SelectFilterOption("SD", "SD"),
|
||||
SelectFilterOption("CAM", "CAM"),
|
||||
]),
|
||||
SelectFilter("ReleaseYearFilter", "Released at", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("2024", "2024"),
|
||||
SelectFilterOption("2023", "2023"),
|
||||
SelectFilterOption("2022", "2022"),
|
||||
SelectFilterOption("2021", "2021"),
|
||||
SelectFilterOption("2020", "2020"),
|
||||
SelectFilterOption("2019", "2019"),
|
||||
SelectFilterOption("2018", "2018"),
|
||||
SelectFilterOption("Older", "older-2018"),
|
||||
]),
|
||||
SeparatorFilter(),
|
||||
GroupFilter("GenresFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "10"),
|
||||
CheckBoxFilter("Action & Adventure", "24"),
|
||||
CheckBoxFilter("Adventure", "18"),
|
||||
CheckBoxFilter("Animation", "3"),
|
||||
CheckBoxFilter("Biography", "37"),
|
||||
CheckBoxFilter("Comedy", "7"),
|
||||
CheckBoxFilter("Crime", "2"),
|
||||
CheckBoxFilter("Documentary", "11"),
|
||||
CheckBoxFilter("Drama", "4"),
|
||||
CheckBoxFilter("Family", "9"),
|
||||
CheckBoxFilter("Fantasy", "13"),
|
||||
CheckBoxFilter("History", "19"),
|
||||
CheckBoxFilter("Horror", "14"),
|
||||
CheckBoxFilter("Kids", "27"),
|
||||
CheckBoxFilter("Music", "15"),
|
||||
CheckBoxFilter("Mystery", "1"),
|
||||
CheckBoxFilter("News", "34"),
|
||||
CheckBoxFilter("Reality", "22"),
|
||||
CheckBoxFilter("Romance", "12"),
|
||||
CheckBoxFilter("Sci-Fi & Fantasy", "31"),
|
||||
CheckBoxFilter("Science Fiction", "5"),
|
||||
CheckBoxFilter("Soap", "35"),
|
||||
CheckBoxFilter("Talk", "29"),
|
||||
CheckBoxFilter("Thriller", "16"),
|
||||
CheckBoxFilter("TV Movie", "8"),
|
||||
CheckBoxFilter("War", "17"),
|
||||
CheckBoxFilter("War & Politics", "28"),
|
||||
CheckBoxFilter("Western", "6"),
|
||||
]),
|
||||
GroupFilter("CountriesFilter", "Countries", [
|
||||
CheckBoxFilter("Argentina", "11"),
|
||||
CheckBoxFilter("Australia", "151"),
|
||||
CheckBoxFilter("Austria", "4"),
|
||||
CheckBoxFilter("Belgium", "44"),
|
||||
CheckBoxFilter("Brazil", "190"),
|
||||
CheckBoxFilter("Canada", "147"),
|
||||
CheckBoxFilter("China", "101"),
|
||||
CheckBoxFilter("Czech Republic", "231"),
|
||||
CheckBoxFilter("Denmark", "222"),
|
||||
CheckBoxFilter("Finland", "158"),
|
||||
CheckBoxFilter("France", "3"),
|
||||
CheckBoxFilter("Germany", "96"),
|
||||
CheckBoxFilter("Hong Kong", "93"),
|
||||
CheckBoxFilter("Hungary", "72"),
|
||||
CheckBoxFilter("India", "105"),
|
||||
CheckBoxFilter("Ireland", "196"),
|
||||
CheckBoxFilter("Israel", "24"),
|
||||
CheckBoxFilter("Italy", "205"),
|
||||
CheckBoxFilter("Japan", "173"),
|
||||
CheckBoxFilter("Luxembourg", "91"),
|
||||
CheckBoxFilter("Mexico", "40"),
|
||||
CheckBoxFilter("Netherlands", "172"),
|
||||
CheckBoxFilter("New Zealand", "122"),
|
||||
CheckBoxFilter("Norway", "219"),
|
||||
CheckBoxFilter("Poland", "23"),
|
||||
CheckBoxFilter("Romania", "170"),
|
||||
CheckBoxFilter("Russia", "109"),
|
||||
CheckBoxFilter("South Africa", "200"),
|
||||
CheckBoxFilter("South Korea", "135"),
|
||||
CheckBoxFilter("Spain", "62"),
|
||||
CheckBoxFilter("Sweden", "114"),
|
||||
CheckBoxFilter("Switzerland", "41"),
|
||||
CheckBoxFilter("Taiwan", "119"),
|
||||
CheckBoxFilter("Thailand", "57"),
|
||||
CheckBoxFilter("United Kingdom", "180"),
|
||||
CheckBoxFilter("United States of America", "129"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
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 ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
DopeFlix main(MSource source) {
|
||||
return DopeFlix(source: source);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import '../../../../model/source.dart';
|
||||
import 'src/dopebox/dopebox.dart';
|
||||
import 'src/sflix/sflix.dart';
|
||||
|
||||
const _dopeflixVersion = "0.0.6";
|
||||
const _dopeflixSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/dopeflix.dart";
|
||||
|
||||
List<Source> get dopeflixSourcesList => _dopeflixSourcesList;
|
||||
List<Source> _dopeflixSourcesList =
|
||||
[
|
||||
//DopeBox (EN)
|
||||
dopeboxSource,
|
||||
//SFlix (EN)
|
||||
sflixSource,
|
||||
]
|
||||
.map(
|
||||
(e) =>
|
||||
e
|
||||
..sourceCodeUrl = _dopeflixSourceCodeUrl
|
||||
..version = _dopeflixVersion,
|
||||
)
|
||||
.toList();
|
||||
@@ -1,13 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get dopeboxSource => _dopeboxSource;
|
||||
|
||||
Source _dopeboxSource = Source(
|
||||
name: "DopeBox",
|
||||
baseUrl: "https://dopebox.to",
|
||||
lang: "en",
|
||||
typeSource: "dopeflix",
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/dopebox/icon.png",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -1,13 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get sflixSource => _sflixSource;
|
||||
|
||||
Source _sflixSource = Source(
|
||||
name: "SFlix",
|
||||
baseUrl: "https://sflix.to",
|
||||
lang: "en",
|
||||
typeSource: "dopeflix",
|
||||
itemType: ItemType.anime,
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/dopeflix/src/sflix/icon.png",
|
||||
);
|
||||
@@ -1,22 +0,0 @@
|
||||
import '../../../../model/source.dart';
|
||||
import 'src/hianime/hianime.dart';
|
||||
import 'src/kaido/kaido.dart';
|
||||
|
||||
const _zorothemeVersion = "0.1.75";
|
||||
const _zorothemeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/zorotheme.dart";
|
||||
|
||||
List<Source> get zorothemeSourcesList => _zorothemeSourcesList;
|
||||
List<Source> _zorothemeSourcesList =
|
||||
[
|
||||
//AniWatch.to (EN)
|
||||
aniwatchSource,
|
||||
//Kaido.to (EN)
|
||||
kaidoSource,
|
||||
]
|
||||
.map(
|
||||
(e) => e
|
||||
..sourceCodeUrl = _zorothemeSourceCodeUrl
|
||||
..version = _zorothemeVersion,
|
||||
)
|
||||
.toList();
|
||||
@@ -1,14 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get aniwatchSource => _aniwatchSource;
|
||||
|
||||
Source _aniwatchSource = Source(
|
||||
id: 814067600,
|
||||
name: "HiAnime",
|
||||
baseUrl: "https://hianime.to",
|
||||
itemType: ItemType.anime,
|
||||
lang: "en",
|
||||
typeSource: "zorotheme",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/src/hianime/icon.png",
|
||||
);
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,13 +0,0 @@
|
||||
import '../../../../../../model/source.dart';
|
||||
|
||||
Source get kaidoSource => _kaidoSource;
|
||||
|
||||
Source _kaidoSource = Source(
|
||||
name: "Kaido.to",
|
||||
baseUrl: "https://kaido.to",
|
||||
lang: "en",
|
||||
itemType: ItemType.anime,
|
||||
typeSource: "zorotheme",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/src/kaido/icon.png",
|
||||
);
|
||||
@@ -1,710 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class ZoroTheme extends MProvider {
|
||||
ZoroTheme({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse("${source.baseUrl}/most-popular?page=$page"),
|
||||
)).body;
|
||||
|
||||
return animeElementM(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse("${source.baseUrl}/recently-updated?page=$page"),
|
||||
)).body;
|
||||
|
||||
return animeElementM(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "${source.baseUrl}/";
|
||||
|
||||
if (query.isEmpty) {
|
||||
url += "filter?";
|
||||
} else {
|
||||
url += "search?keyword=$query";
|
||||
}
|
||||
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "TypeFilter") {
|
||||
final type = filter.values[filter.state].value;
|
||||
if (type.isNotEmpty) {
|
||||
url += "${ll(url)}type=$type";
|
||||
}
|
||||
} else if (filter.type == "StatusFilter") {
|
||||
final status = filter.values[filter.state].value;
|
||||
if (status.isNotEmpty) {
|
||||
url += "${ll(url)}status=$status";
|
||||
}
|
||||
} else if (filter.type == "RatedFilter") {
|
||||
final rated = filter.values[filter.state].value;
|
||||
if (rated.isNotEmpty) {
|
||||
url += "${ll(url)}rated=$rated";
|
||||
}
|
||||
} else if (filter.type == "ScoreFilter") {
|
||||
final score = filter.values[filter.state].value;
|
||||
if (score.isNotEmpty) {
|
||||
url += "${ll(url)}score=$score";
|
||||
}
|
||||
} else if (filter.type == "SeasonFilter") {
|
||||
final season = filter.values[filter.state].value;
|
||||
if (season.isNotEmpty) {
|
||||
url += "${ll(url)}season=$season";
|
||||
}
|
||||
} else if (filter.type == "LanguageFilter") {
|
||||
final language = filter.values[filter.state].value;
|
||||
if (language.isNotEmpty) {
|
||||
url += "${ll(url)}language=$language";
|
||||
}
|
||||
} else if (filter.type == "SortFilter") {
|
||||
final sort = filter.values[filter.state].value;
|
||||
if (sort.isNotEmpty) {
|
||||
url += "${ll(url)}sort=$sort";
|
||||
}
|
||||
} else if (filter.type == "StartYearFilter") {
|
||||
final sy = filter.values[filter.state].value;
|
||||
if (sy.isNotEmpty) {
|
||||
url += "${ll(url)}sy=$sy";
|
||||
}
|
||||
} else if (filter.type == "StartMonthFilter") {
|
||||
final sm = filter.values[filter.state].value;
|
||||
if (sm.isNotEmpty) {
|
||||
url += "${ll(url)}sm=$sm";
|
||||
}
|
||||
} else if (filter.type == "StartDayFilter") {
|
||||
final sd = filter.values[filter.state].value;
|
||||
if (sd.isNotEmpty) {
|
||||
url += "${ll(url)}sd=$sd";
|
||||
}
|
||||
} else if (filter.type == "EndYearFilter") {
|
||||
final ey = filter.values[filter.state].value;
|
||||
if (ey.isNotEmpty) {
|
||||
url += "${ll(url)}sy=$ey";
|
||||
}
|
||||
} else if (filter.type == "EndMonthFilter") {
|
||||
final em = filter.values[filter.state].value;
|
||||
if (em.isNotEmpty) {
|
||||
url += "${ll(url)}sm=$em";
|
||||
}
|
||||
} else if (filter.type == "EndDayFilter") {
|
||||
final ed = filter.values[filter.state].value;
|
||||
if (ed.isNotEmpty) {
|
||||
url += "${ll(url)}sd=$ed";
|
||||
}
|
||||
} else if (filter.type == "GenreFilter") {
|
||||
final genre = (filter.state as List).where((e) => e.state).toList();
|
||||
if (genre.isNotEmpty) {
|
||||
url += "${ll(url)}genre=";
|
||||
for (var st in genre) {
|
||||
url += "${st.value},";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
url += "${ll(url)}page=$page";
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
return animeElementM(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Currently Airing": 0, "Finished Airing": 1},
|
||||
];
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final status = xpath(
|
||||
res,
|
||||
'//*[@class="anisc-info"]/div[contains(text(),"Status:")]/span[2]/text()',
|
||||
);
|
||||
if (status.isNotEmpty) {
|
||||
anime.status = parseStatus(status.first, statusList);
|
||||
}
|
||||
|
||||
final author = xpath(
|
||||
res,
|
||||
'//*[@class="anisc-info"]/div[contains(text(),"Studios:")]/span/text()',
|
||||
);
|
||||
if (author.isNotEmpty) {
|
||||
anime.author = author.first.replaceAll("Studios:", "");
|
||||
}
|
||||
final description = xpath(
|
||||
res,
|
||||
'//*[@class="anisc-info"]/div[contains(text(),"Overview:")]/text()',
|
||||
);
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first.replaceAll("Overview:", "");
|
||||
}
|
||||
final genre = xpath(
|
||||
res,
|
||||
'//*[@class="anisc-info"]/div[contains(text(),"Genres:")]/a/text()',
|
||||
);
|
||||
|
||||
anime.genre = genre;
|
||||
final id = substringAfterLast(url, '-');
|
||||
|
||||
final urlEp =
|
||||
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/list/$id";
|
||||
|
||||
final resEp = (await client.get(
|
||||
Uri.parse(urlEp),
|
||||
headers: {"referer": url},
|
||||
)).body;
|
||||
|
||||
final html = json.decode(resEp)["html"];
|
||||
final epElements = parseHtml(html).select("a.ep-item");
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
for (var epElement in epElements) {
|
||||
final number = epElement.attr("data-number");
|
||||
final title = epElement.attr("title");
|
||||
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Episode $number: $title";
|
||||
episode.url = epElement.getHref;
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final id = substringAfterLast(url, '?ep=');
|
||||
|
||||
final res = (await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/servers?episodeId=$id",
|
||||
),
|
||||
headers: {"referer": "${source.baseUrl}/$url"},
|
||||
)).body;
|
||||
final html = json.decode(res)["html"];
|
||||
|
||||
final serverElements = parseHtml(html).select("div.server-item");
|
||||
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
final typeSelection = preferenceTypeSelection(source.id);
|
||||
for (var serverElement in serverElements) {
|
||||
final name = serverElement.text;
|
||||
final id = serverElement.attr("data-id");
|
||||
final subDub = serverElement.attr("data-type");
|
||||
|
||||
final resE = (await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/ajax${ajaxRoute('${source.baseUrl}')}/episode/sources?id=$id",
|
||||
),
|
||||
headers: {"referer": "${source.baseUrl}/$url"},
|
||||
)).body;
|
||||
String epUrl = substringBefore(substringAfter(resE, "\"link\":\""), "\"");
|
||||
List<MVideo> a = [];
|
||||
if (hosterSelection.contains(name) && typeSelection.contains(subDub)) {
|
||||
if (name.contains("Vidstreaming")) {
|
||||
a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub", id);
|
||||
} else if (name.contains("Vidcloud")) {
|
||||
a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub", id);
|
||||
} else if (name.contains("StreamTape")) {
|
||||
a = await streamTapeExtractor(epUrl, "StreamTape - $subDub");
|
||||
} else if ([
|
||||
"HD-1",
|
||||
"HD-2",
|
||||
"HD-3",
|
||||
].any((element) => name.contains(element))) {
|
||||
a = await rapidCloudExtractor(epUrl, "$name - $subDub", id);
|
||||
}
|
||||
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> rapidCloudExtractor(
|
||||
String url,
|
||||
String name,
|
||||
String dataID,
|
||||
) async {
|
||||
try {
|
||||
final headers = {'Referer': 'https://megacloud.club/'};
|
||||
final serverUrl = ['https://megacloud.tv', 'https://rapid-cloud.co'];
|
||||
|
||||
final serverType = RegExp(r'https://megacloud\..*').hasMatch(url) ? 0 : 1;
|
||||
final sourceUrl = [
|
||||
'/embed-2/v2/e-1/getSources?id=',
|
||||
'/ajax/embed-6-v2/getSources?id=',
|
||||
];
|
||||
final sourceSpliter = ['/e-1/', '/embed-6-v2/'];
|
||||
final id = url.split(sourceSpliter[serverType]).last.split('?').first;
|
||||
final response = await client.get(
|
||||
Uri.parse('${serverUrl[serverType]}${sourceUrl[serverType]}$id'),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
return [];
|
||||
}
|
||||
final resServer = response.body;
|
||||
|
||||
final encrypted = getMapValue(resServer, "encrypted");
|
||||
List<Map<String, dynamic>> videoResJson = [];
|
||||
List<MVideo> videos = [];
|
||||
if (encrypted == "true") {
|
||||
final key = await getWorkingKey(dataID);
|
||||
if (key == null) {
|
||||
return [];
|
||||
}
|
||||
final sources = await getSource(dataID, key);
|
||||
if (sources == null || sources['sources'] == null) {
|
||||
return [];
|
||||
}
|
||||
videoResJson = sources['sources'];
|
||||
} else {
|
||||
videoResJson = json.decode(resServer)["sources"];
|
||||
}
|
||||
|
||||
String masterUrl = videoResJson[0]['file'];
|
||||
String type = videoResJson[0]['type'];
|
||||
|
||||
final tracks = (json.decode(resServer)['tracks'] as List)
|
||||
.where((e) => e['kind'] == 'captions' ? true : false)
|
||||
.toList();
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in tracks) {
|
||||
try {
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle
|
||||
..label = sub["label"]
|
||||
..file = sub["file"];
|
||||
subtitles.add(subtitle);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (type == "hls") {
|
||||
final masterPlaylistRes = (await client.get(
|
||||
Uri.parse(masterUrl),
|
||||
headers: headers,
|
||||
)).body;
|
||||
|
||||
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 = "$name - $quality"
|
||||
..subtitles = subtitles
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "$name - Default"
|
||||
..subtitles = subtitles
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// credits: https://github.com/50n50/sources/blob/main/hianime/hianime.js
|
||||
Future<String?> getWorkingKey(String dataID) async {
|
||||
try {
|
||||
final res = await client.get(
|
||||
Uri.parse(
|
||||
'https://raw.githubusercontent.com/itzzzme/megacloud-keys/refs/heads/main/key.txt',
|
||||
),
|
||||
);
|
||||
final key = res.body.trim();
|
||||
final sources = await getSource(dataID, key);
|
||||
|
||||
if (sources != null && sources['sources'] != null) return key;
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
final res = await client.get(
|
||||
Uri.parse(
|
||||
'https://justarion.github.io/keys/e1-player/src/data/keys.json',
|
||||
),
|
||||
);
|
||||
final jsonRes = json.decode(res.body);
|
||||
final key = jsonRes['megacloud']['anime']['key'];
|
||||
final sources = await getSource(dataID, key);
|
||||
if (sources != null && sources['sources'] != null) return key;
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
final res = await client.get(
|
||||
Uri.parse(
|
||||
'https://raw.githubusercontent.com/yogesh-hacker/MegacloudKeys/refs/heads/main/keys.json',
|
||||
),
|
||||
);
|
||||
final jsonRes = json.decode(res.body);
|
||||
final key = jsonRes['mega'];
|
||||
final sources = await getSource(dataID, key);
|
||||
if (sources != null && sources['sources'] != null) return key;
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
final res = await client.get(
|
||||
Uri.parse(
|
||||
'https://raw.githubusercontent.com/SpencerDevs/megacloud-key-updater/refs/heads/master/key.txt',
|
||||
),
|
||||
);
|
||||
final key = res.body.trim();
|
||||
final sources = await getSource(dataID, key);
|
||||
if (sources != null && sources['sources'] != null) return key;
|
||||
} catch (e) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// credits: https://github.com/50n50/sources/blob/main/hianime/hianime.js
|
||||
Future<Map<String, dynamic>?> getSource(String sourceId, String key) async {
|
||||
final res1 = await client.get(
|
||||
Uri.parse("${source.baseUrl}/ajax/v2/episode/sources?id=$sourceId"),
|
||||
);
|
||||
final json1 = json.decode(res1.body);
|
||||
final link = json1['link'] ?? "";
|
||||
final idMatch = RegExp(r'/e-1/([^/?]+)').firstMatch(link);
|
||||
final streamId = idMatch?.group(1);
|
||||
|
||||
if (streamId == null) return null;
|
||||
|
||||
final res2 = await client.get(
|
||||
Uri.parse(
|
||||
'https://megacloud.blog/embed-2/v2/e-1/getSources?id=$streamId',
|
||||
),
|
||||
);
|
||||
final json2 = json.decode(res2.body);
|
||||
final encrypted = json2['sources'];
|
||||
|
||||
if (encrypted == null) return null;
|
||||
|
||||
final result = <String, dynamic>{};
|
||||
|
||||
final sources = json.decode(decryptAESCryptoJS(encrypted, key));
|
||||
if (sources == null) return null;
|
||||
|
||||
result['sources'] = sources;
|
||||
return result;
|
||||
}
|
||||
|
||||
MPages animeElementM(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final doc = parseHtml(res);
|
||||
final animeElements = doc.select('.flw-item');
|
||||
for (var element in animeElements) {
|
||||
final linkElement = element.selectFirst('.film-detail h3 a');
|
||||
final imageElement = element.selectFirst('.film-poster img');
|
||||
MManga anime = MManga();
|
||||
anime.name = linkElement.attr('data-jname') ?? linkElement.text;
|
||||
anime.imageUrl = imageElement.getSrc ?? imageElement.attr('data-src');
|
||||
anime.link = linkElement.getHref;
|
||||
animeList.add(anime);
|
||||
}
|
||||
final nextPageElement = doc.selectFirst('li.page-item a[title="Next"]');
|
||||
return MPages(animeList, nextPageElement != null);
|
||||
}
|
||||
|
||||
String ajaxRoute(String baseUrl) {
|
||||
if (baseUrl == "https://kaido.to") {
|
||||
return "";
|
||||
}
|
||||
return "/v2";
|
||||
}
|
||||
|
||||
List<SelectFilterOption> yearList = [
|
||||
for (var i = 1917; i < 2026; i++)
|
||||
SelectFilterOption(i.toString(), i.toString()),
|
||||
SelectFilterOption("All", ""),
|
||||
];
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SelectFilter("TypeFilter", "Type", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("Movie", "1"),
|
||||
SelectFilterOption("TV", "2"),
|
||||
SelectFilterOption("OVA", "3"),
|
||||
SelectFilterOption("ONA", "4"),
|
||||
SelectFilterOption("Special", "5"),
|
||||
SelectFilterOption("Music", "6"),
|
||||
]),
|
||||
SelectFilter("StatusFilter", "Status", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("Finished Airing", "1"),
|
||||
SelectFilterOption("Currently Airing", "2"),
|
||||
SelectFilterOption("Not yet aired", "3"),
|
||||
]),
|
||||
SelectFilter("RatedFilter", "Rated", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("G", "1"),
|
||||
SelectFilterOption("PG", "2"),
|
||||
SelectFilterOption("PG-13", "3"),
|
||||
SelectFilterOption("R", "4"),
|
||||
SelectFilterOption("R+", "5"),
|
||||
SelectFilterOption("Rx", "6"),
|
||||
]),
|
||||
SelectFilter("ScoreFilter", "Score", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("(1) Appalling", "1"),
|
||||
SelectFilterOption("(2) Horrible", "2"),
|
||||
SelectFilterOption("(3) Very Bad", "3"),
|
||||
SelectFilterOption("(4) Bad", "4"),
|
||||
SelectFilterOption("(5) Average", "5"),
|
||||
SelectFilterOption("(6) Fine", "6"),
|
||||
SelectFilterOption("(7) Good", "7"),
|
||||
SelectFilterOption("(8) Very Good", "8"),
|
||||
SelectFilterOption("(9) Great", "9"),
|
||||
SelectFilterOption("(10) Masterpiece", "10"),
|
||||
]),
|
||||
SelectFilter("SeasonFilter", "Season", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("Spring", "1"),
|
||||
SelectFilterOption("Summer", "2"),
|
||||
SelectFilterOption("Fall", "3"),
|
||||
SelectFilterOption("Winter", "4"),
|
||||
]),
|
||||
SelectFilter("LanguageFilter", "Language", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("SUB", "1"),
|
||||
SelectFilterOption("DUB", "2"),
|
||||
SelectFilterOption("SUB & DUB", "3"),
|
||||
]),
|
||||
SelectFilter("SortFilter", "Sort by", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
SelectFilterOption("Default", "default"),
|
||||
SelectFilterOption("Recently Added", "recently_added"),
|
||||
SelectFilterOption("Recently Updated", "recently_updated"),
|
||||
SelectFilterOption("Score", "score"),
|
||||
SelectFilterOption("Name A-Z", "name_az"),
|
||||
SelectFilterOption("Released Date", "released_date"),
|
||||
SelectFilterOption("Most Watched", "most_watched"),
|
||||
]),
|
||||
SelectFilter(
|
||||
"StartYearFilter",
|
||||
"Start year",
|
||||
0,
|
||||
yearList.reversed.toList(),
|
||||
),
|
||||
SelectFilter("StartMonthFilter", "Start month", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
for (var i = 1; i < 13; i++)
|
||||
SelectFilterOption(i.toString(), i.toString()),
|
||||
]),
|
||||
SelectFilter("StartDayFilter", "Start day", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
for (var i = 1; i < 32; i++)
|
||||
SelectFilterOption(i.toString(), i.toString()),
|
||||
]),
|
||||
SelectFilter("EndYearFilter", "End year", 0, yearList.reversed.toList()),
|
||||
SelectFilter("EndmonthFilter", "End month", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
for (var i = 1; i < 32; i++)
|
||||
SelectFilterOption(i.toString(), i.toString()),
|
||||
]),
|
||||
SelectFilter("EndDayFilter", "End day", 0, [
|
||||
SelectFilterOption("All", ""),
|
||||
for (var i = 1; i < 32; i++)
|
||||
SelectFilterOption(i.toString(), i.toString()),
|
||||
]),
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "1"),
|
||||
CheckBoxFilter("Adventure", "2"),
|
||||
CheckBoxFilter("Cars", "3"),
|
||||
CheckBoxFilter("Comedy", "4"),
|
||||
CheckBoxFilter("Dementia", "5"),
|
||||
CheckBoxFilter("Demons", "6"),
|
||||
CheckBoxFilter("Drama", "8"),
|
||||
CheckBoxFilter("Ecchi", "9"),
|
||||
CheckBoxFilter("Fantasy", "10"),
|
||||
CheckBoxFilter("Game", "11"),
|
||||
CheckBoxFilter("Harem", "35"),
|
||||
CheckBoxFilter("Historical", "13"),
|
||||
CheckBoxFilter("Horror", "14"),
|
||||
CheckBoxFilter("Isekai", "44"),
|
||||
CheckBoxFilter("Josei", "43"),
|
||||
CheckBoxFilter("Kids", "15"),
|
||||
CheckBoxFilter("Magic", "16"),
|
||||
CheckBoxFilter("Martial Arts", "17"),
|
||||
CheckBoxFilter("Mecha", "18"),
|
||||
CheckBoxFilter("Military", "38"),
|
||||
CheckBoxFilter("Music", "19"),
|
||||
CheckBoxFilter("Mystery", "7"),
|
||||
CheckBoxFilter("Parody", "20"),
|
||||
CheckBoxFilter("Police", "39"),
|
||||
CheckBoxFilter("Psychological", "40"),
|
||||
CheckBoxFilter("Romance", "22"),
|
||||
CheckBoxFilter("Samurai", "21"),
|
||||
CheckBoxFilter("School", "23"),
|
||||
CheckBoxFilter("Sci-Fi", "24"),
|
||||
CheckBoxFilter("Seinen", "42"),
|
||||
CheckBoxFilter("Shoujo", "25"),
|
||||
CheckBoxFilter("Shoujo Ai", "26"),
|
||||
CheckBoxFilter("Shounen", "27"),
|
||||
CheckBoxFilter("Shounen Ai", "28"),
|
||||
CheckBoxFilter("Slice of Life", "36"),
|
||||
CheckBoxFilter("Space", "29"),
|
||||
CheckBoxFilter("Sports", "30"),
|
||||
CheckBoxFilter("Super Power", "31"),
|
||||
CheckBoxFilter("Supernatural", "37"),
|
||||
CheckBoxFilter("Thriller", "41"),
|
||||
CheckBoxFilter("Vampire", "32"),
|
||||
CheckBoxFilter("Yaoi", "33"),
|
||||
CheckBoxFilter("Yuri", "34"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred Quality",
|
||||
summary: "",
|
||||
valueIndex: 1,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"],
|
||||
),
|
||||
if (source.name == "HiAnime")
|
||||
ListPreference(
|
||||
key: "preferred_server2",
|
||||
title: "Preferred server",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["HD-1", "HD-2", "HD-3", "StreamTape"],
|
||||
entryValues: ["HD-1", "HD-2", "HD-3", "StreamTape"],
|
||||
),
|
||||
if (source.name != "HiAnime")
|
||||
ListPreference(
|
||||
key: "preferred_server2",
|
||||
title: "Preferred server",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Vidstreaming", "VidCloud", "StreamTape"],
|
||||
entryValues: ["Vidstreaming", "VidCloud", "StreamTape"],
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_type1",
|
||||
title: "Preferred Type",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Sub", "Dub"],
|
||||
entryValues: ["sub", "dub"],
|
||||
),
|
||||
if (source.name != "HiAnime")
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selection2",
|
||||
title: "Enable/Disable Hosts",
|
||||
summary: "",
|
||||
entries: ["Vidstreaming", "VidCloud", "StreamTape"],
|
||||
entryValues: ["Vidstreaming", "VidCloud", "StreamTape"],
|
||||
values: ["Vidstreaming", "VidCloud", "StreamTape"],
|
||||
),
|
||||
if (source.name == "HiAnime")
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selection2",
|
||||
title: "Enable/Disable Hosts",
|
||||
summary: "",
|
||||
entries: ["HD-1", "HD-2", "HD-3", "StreamTape"],
|
||||
entryValues: ["HD-1", "HD-2", "HD-3", "StreamTape"],
|
||||
values: ["HD-1", "HD-2", "HD-3", "StreamTape"],
|
||||
),
|
||||
MultiSelectListPreference(
|
||||
key: "type_selection_1",
|
||||
title: "Enable/Disable Types",
|
||||
summary: "",
|
||||
entries: ["Sub", "Dub", "Raw"],
|
||||
entryValues: ["sub", "dub", "raw"],
|
||||
values: ["sub", "dub", "raw"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
String server = getPreferenceValue(sourceId, "preferred_server2");
|
||||
String type = getPreferenceValue(sourceId, "preferred_type1");
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(type.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(type.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
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_selection2");
|
||||
}
|
||||
|
||||
List<String> preferenceTypeSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "type_selection_1");
|
||||
}
|
||||
|
||||
String ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
ZoroTheme main(MSource source) {
|
||||
return ZoroTheme(source: source);
|
||||
}
|
||||
@@ -1,430 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeWorldIndia extends MProvider {
|
||||
AnimeWorldIndia({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/advanced-search/page/$page/?s_lang=${source.lang}&s_orderby=viewed",
|
||||
),
|
||||
)).body;
|
||||
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/advanced-search/page/$page/?s_lang=${source.lang}&s_orderby=update",
|
||||
),
|
||||
)).body;
|
||||
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url =
|
||||
"${source.baseUrl}/advanced-search/page/$page/?s_keyword=$query&s_lang=${source.lang}";
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "TypeFilter") {
|
||||
final type = filter.values[filter.state].value;
|
||||
url += "${ll(url)}s_type=$type";
|
||||
} else if (filter.type == "StatusFilter") {
|
||||
final status = filter.values[filter.state].value;
|
||||
url += "${ll(url)}s_status=$status";
|
||||
} else if (filter.type == "StyleFilter") {
|
||||
final style = filter.values[filter.state].value;
|
||||
url += "${ll(url)}s_sub_type=$style";
|
||||
} else if (filter.type == "YearFilter") {
|
||||
final year = filter.values[filter.state].value;
|
||||
url += "${ll(url)}s_year=$year";
|
||||
} else if (filter.type == "SortFilter") {
|
||||
final sort = filter.values[filter.state].value;
|
||||
url += "${ll(url)}s_orderby=$sort";
|
||||
} else if (filter.type == "GenresFilter") {
|
||||
final genre = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}s_genre=";
|
||||
if (genre.isNotEmpty) {
|
||||
for (var st in genre) {
|
||||
String value = st.value;
|
||||
url += value.toLowerCase().replaceAll(" ", "-");
|
||||
if (genre.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (genre.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final document = parseHtml(res);
|
||||
final isMovie =
|
||||
document.xpath('//li/a[contains(text(),"Movie")]/text()').isNotEmpty;
|
||||
if (isMovie) {
|
||||
anime.status = MStatus.completed;
|
||||
} else {
|
||||
final eps = xpath(
|
||||
res,
|
||||
'//ul/li/a[contains(@href,"${source.baseUrl}/watch")]/text()',
|
||||
);
|
||||
if (eps.isNotEmpty) {
|
||||
final epParts = eps.first
|
||||
.substring(3)
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll("\n", "")
|
||||
.split('/');
|
||||
if (epParts.length == 2) {
|
||||
if (epParts[0].compareTo(epParts[1]) == 0) {
|
||||
anime.status = MStatus.completed;
|
||||
} else {
|
||||
anime.status = MStatus.ongoing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
anime.description = document.selectFirst("div[data-synopsis]")?.text ?? "";
|
||||
anime.author = document
|
||||
.xpath('//li[contains(text(),"Producers:")]/span/a/text()')
|
||||
.join(', ');
|
||||
anime.genre = document.xpath(
|
||||
'//span[@class="leading-6"]/a[contains(@class,"border-opacity-30")]/text()',
|
||||
);
|
||||
final seasonsJson =
|
||||
json.decode(
|
||||
substringBeforeLast(
|
||||
substringBefore(
|
||||
substringAfter(res, "var season_list = "),
|
||||
"var season_label =",
|
||||
),
|
||||
";",
|
||||
),
|
||||
)
|
||||
as List<Map<String, dynamic>>;
|
||||
bool isSingleSeason = seasonsJson.length == 1;
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < seasonsJson.length; i++) {
|
||||
final seasonJson = seasonsJson[i];
|
||||
final seasonName = isSingleSeason ? "" : "Season ${i + 1}";
|
||||
final episodesJson =
|
||||
(seasonJson["episodes"]["all"] as List<Map<String, dynamic>>).reversed
|
||||
.toList();
|
||||
for (var j = 0; j < episodesJson.length; j++) {
|
||||
final episodeJson = episodesJson[j];
|
||||
final episodeTitle = episodeJson["metadata"]["title"] ?? "";
|
||||
String episodeName = "";
|
||||
if (isMovie) {
|
||||
episodeName = "Movie";
|
||||
} else {
|
||||
if (seasonName.isNotEmpty) {
|
||||
episodeName = "$seasonName - ";
|
||||
}
|
||||
episodeName += "Episode ${j + 1} ";
|
||||
if (episodeTitle.isNotEmpty) {
|
||||
episodeName += "- $episodeTitle";
|
||||
}
|
||||
}
|
||||
MChapter episode = MChapter();
|
||||
episode.name = episodeName;
|
||||
|
||||
episode.dateUpload =
|
||||
"${int.parse(episodeJson["metadata"]["released"] ?? "0") * 1000}";
|
||||
episode.url = "/wp-json/kiranime/v1/episode?id=${episodeJson["id"]}";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
var resJson = substringBefore(
|
||||
substringAfterLast(res, "\"players\":"),
|
||||
",\"noplayer\":",
|
||||
);
|
||||
var streams =
|
||||
(json.decode(resJson) as List<Map<String, dynamic>>)
|
||||
.where(
|
||||
(e) =>
|
||||
(e["type"] == "stream" ? true : false) &&
|
||||
(e["url"] as String).isNotEmpty,
|
||||
)
|
||||
.toList()
|
||||
.where(
|
||||
(e) =>
|
||||
language(source.lang).isEmpty ||
|
||||
language(source.lang) == e["language"]
|
||||
? true
|
||||
: false,
|
||||
)
|
||||
.toList();
|
||||
List<MVideo> videos = [];
|
||||
for (var stream in streams) {
|
||||
String videoUrl = stream["url"];
|
||||
final language = stream["language"];
|
||||
final video = await mystreamExtractor(videoUrl, language);
|
||||
videos.addAll(video);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final document = parseHtml(res);
|
||||
|
||||
for (var element in document.select("div.col-span-1")) {
|
||||
MManga anime = MManga();
|
||||
anime.name =
|
||||
element.selectFirst("div.font-medium.line-clamp-2.mb-3").text;
|
||||
anime.link = element.selectFirst("a").getHref;
|
||||
anime.imageUrl =
|
||||
"${source.baseUrl}${getUrlWithoutDomain(element.selectFirst("img").getSrc)}";
|
||||
animeList.add(anime);
|
||||
}
|
||||
final hasNextPage =
|
||||
xpath(
|
||||
res,
|
||||
'//li/span[@class="page-numbers current"]/parent::li//following-sibling::li/a/@href',
|
||||
).isNotEmpty;
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
String language(String lang) {
|
||||
final languages = {
|
||||
"all": "",
|
||||
"bn": "bengali",
|
||||
"en": "english",
|
||||
"hi": "hindi",
|
||||
"ja": "japanese",
|
||||
"ml": "malayalam",
|
||||
"mr": "marathi",
|
||||
"ta": "tamil",
|
||||
"te": "telugu",
|
||||
};
|
||||
return languages[lang] ?? "";
|
||||
}
|
||||
|
||||
Future<List<MVideo>> mystreamExtractor(String url, String language) async {
|
||||
List<MVideo> videos = [];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final streamCode = substringBefore(
|
||||
substringAfter(substringAfter(res, "sniff("), ", \""),
|
||||
'"',
|
||||
);
|
||||
|
||||
final streamUrl =
|
||||
"${substringBefore(url, "/watch")}/m3u8/$streamCode/master.txt?s=1&cache=1";
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(streamUrl))).body;
|
||||
|
||||
List<MTrack> audios = [];
|
||||
for (var it in substringAfter(
|
||||
masterPlaylistRes,
|
||||
"#EXT-X-MEDIA:TYPE=AUDIO",
|
||||
).split("#EXT-X-MEDIA:TYPE=AUDIO")) {
|
||||
final line = substringBefore(
|
||||
substringAfter(it, "#EXT-X-MEDIA:TYPE=AUDIO"),
|
||||
"\n",
|
||||
);
|
||||
final audioUrl = substringBefore(substringAfter(line, "URI=\""), "\"");
|
||||
MTrack audio = MTrack();
|
||||
audio
|
||||
..label = substringBefore(substringAfter(line, "NAME=\""), "\"")
|
||||
..file = audioUrl;
|
||||
audios.add(audio);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "[$language] MyStream - $quality"
|
||||
..audios = audios;
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SelectFilter("TypeFilter", "Type", 0, [
|
||||
SelectFilterOption("Any", "all"),
|
||||
SelectFilterOption("TV", "tv"),
|
||||
SelectFilterOption("Movie", "movies"),
|
||||
]),
|
||||
SelectFilter("StatusFilter", "Status", 0, [
|
||||
SelectFilterOption("Any", "all"),
|
||||
SelectFilterOption("Currently Airing", "airing"),
|
||||
SelectFilterOption("Finished Airing", "completed"),
|
||||
]),
|
||||
SelectFilter("StyleFilter", "Style", 0, [
|
||||
SelectFilterOption("Any", "all"),
|
||||
SelectFilterOption("Anime", "anime"),
|
||||
SelectFilterOption("Cartoon", "cartoon"),
|
||||
]),
|
||||
SelectFilter("YearFilter", "Year", 0, [
|
||||
SelectFilterOption("Any", "all"),
|
||||
SelectFilterOption("2024", "2024"),
|
||||
SelectFilterOption("2023", "2023"),
|
||||
SelectFilterOption("2022", "2022"),
|
||||
SelectFilterOption("2021", "2021"),
|
||||
SelectFilterOption("2020", "2020"),
|
||||
SelectFilterOption("2019", "2019"),
|
||||
SelectFilterOption("2018", "2018"),
|
||||
SelectFilterOption("2017", "2017"),
|
||||
SelectFilterOption("2016", "2016"),
|
||||
SelectFilterOption("2015", "2015"),
|
||||
SelectFilterOption("2014", "2014"),
|
||||
SelectFilterOption("2013", "2013"),
|
||||
SelectFilterOption("2012", "2012"),
|
||||
SelectFilterOption("2011", "2011"),
|
||||
SelectFilterOption("2010", "2010"),
|
||||
SelectFilterOption("2009", "2009"),
|
||||
SelectFilterOption("2008", "2008"),
|
||||
SelectFilterOption("2007", "2007"),
|
||||
SelectFilterOption("2006", "2006"),
|
||||
SelectFilterOption("2005", "2005"),
|
||||
SelectFilterOption("2004", "2004"),
|
||||
SelectFilterOption("2003", "2003"),
|
||||
SelectFilterOption("2002", "2002"),
|
||||
SelectFilterOption("2001", "2001"),
|
||||
SelectFilterOption("2000", "2000"),
|
||||
SelectFilterOption("1999", "1999"),
|
||||
SelectFilterOption("1998", "1998"),
|
||||
SelectFilterOption("1997", "1997"),
|
||||
SelectFilterOption("1996", "1996"),
|
||||
SelectFilterOption("1995", "1995"),
|
||||
SelectFilterOption("1994", "1994"),
|
||||
SelectFilterOption("1993", "1993"),
|
||||
SelectFilterOption("1992", "1992"),
|
||||
SelectFilterOption("1991", "1991"),
|
||||
SelectFilterOption("1990", "1990"),
|
||||
]),
|
||||
SelectFilter("SortFilter", "Sort", 0, [
|
||||
SelectFilterOption("Default", "default"),
|
||||
SelectFilterOption("Ascending", "title_a_z"),
|
||||
SelectFilterOption("Descending", "title_z_a"),
|
||||
SelectFilterOption("Updated", "update"),
|
||||
SelectFilterOption("Published", "date"),
|
||||
SelectFilterOption("Most Viewed", "viewed"),
|
||||
SelectFilterOption("Favourite", "favorite"),
|
||||
]),
|
||||
GroupFilter("GenresFilter", "Genres", [
|
||||
CheckBoxFilter("Action", "Action"),
|
||||
CheckBoxFilter("Adult Cast", "Adult Cast"),
|
||||
CheckBoxFilter("Adventure", "Adventure"),
|
||||
CheckBoxFilter("Animation", "Animation"),
|
||||
CheckBoxFilter("Comedy", "Comedy"),
|
||||
CheckBoxFilter("Detective", "Detective"),
|
||||
CheckBoxFilter("Drama", "Drama"),
|
||||
CheckBoxFilter("Ecchi", "Ecchi"),
|
||||
CheckBoxFilter("Family", "Family"),
|
||||
CheckBoxFilter("Fantasy", "Fantasy"),
|
||||
CheckBoxFilter("Isekai", "Isekai"),
|
||||
CheckBoxFilter("Kids", "Kids"),
|
||||
CheckBoxFilter("Martial Arts", "Martial Arts"),
|
||||
CheckBoxFilter("Mecha", "Mecha"),
|
||||
CheckBoxFilter("Military", "Military"),
|
||||
CheckBoxFilter("Mystery", "Mystery"),
|
||||
CheckBoxFilter("Otaku Culture", "Otaku Culture"),
|
||||
CheckBoxFilter("Reality", "Reality"),
|
||||
CheckBoxFilter("Romance", "Romance"),
|
||||
CheckBoxFilter("School", "School"),
|
||||
CheckBoxFilter("Sci-Fi", "Sci-Fi"),
|
||||
CheckBoxFilter("Seinen", "Seinen"),
|
||||
CheckBoxFilter("Shounen", "Shounen"),
|
||||
CheckBoxFilter("Slice of Life", "Slice of Life"),
|
||||
CheckBoxFilter("Sports", "Sports"),
|
||||
CheckBoxFilter("Super Power", "Super Power"),
|
||||
CheckBoxFilter("SuperHero", "SuperHero"),
|
||||
CheckBoxFilter("Supernatural", "Supernatural"),
|
||||
CheckBoxFilter("TV Movie", "TV Movie"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred Quality",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p", "240p"],
|
||||
entryValues: ["1080", "720", "480", "360", "240"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
String ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
AnimeWorldIndia main(MSource source) {
|
||||
return AnimeWorldIndia(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1,37 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
const _animeworldindiaVersion = "0.0.35";
|
||||
const _animeworldindiaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/animeworldindia/animeworldindia.dart";
|
||||
|
||||
String _iconUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/animeworldindia/icon.png";
|
||||
|
||||
List<String> _languages = [
|
||||
"all",
|
||||
"en",
|
||||
"bn",
|
||||
"hi",
|
||||
"ja",
|
||||
"ml",
|
||||
"mr",
|
||||
"ta",
|
||||
"te",
|
||||
];
|
||||
|
||||
List<Source> get animeworldindiaSourcesList => _animeworldindiaSourcesList;
|
||||
List<Source> _animeworldindiaSourcesList =
|
||||
_languages
|
||||
.map(
|
||||
(e) => Source(
|
||||
name: 'AnimeWorld India',
|
||||
baseUrl: "https://anime-world.in",
|
||||
lang: e,
|
||||
typeSource: "multiple",
|
||||
iconUrl: _iconUrl,
|
||||
version: _animeworldindiaVersion,
|
||||
itemType: ItemType.anime,
|
||||
sourceCodeUrl: _animeworldindiaSourceCodeUrl,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,220 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
|
||||
class Nyaa extends MProvider {
|
||||
Nyaa({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse(
|
||||
"${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=&s=downloads&o=desc&p=$page",
|
||||
),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse(
|
||||
"${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=$page",
|
||||
),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "";
|
||||
url =
|
||||
"${getBaseUrl()}/?f=0&c=${getPreferenceValue(source.id, "preferred_categorie_page")}&q=${query.replaceAll(" ", "+")}&p=$page";
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "SortFilter") {
|
||||
url += "${ll(url)}s=${filter.values[filter.state.index].value}";
|
||||
final asc = filter.state.ascending ? "&o=asc" : "&o=desc";
|
||||
url += "${ll(url)}$asc";
|
||||
}
|
||||
}
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
String extractPanelBody(MDocument document) {
|
||||
final panelBody = document.selectFirst('.panel-body');
|
||||
if (panelBody == null) return "";
|
||||
|
||||
final rows = panelBody.select('.row');
|
||||
|
||||
final Map<String, String> info = {};
|
||||
for (var row in rows) {
|
||||
final labels = row.select('.col-md-1');
|
||||
for (var label in labels) {
|
||||
final key = label.text.replaceAll(":", "").trim();
|
||||
final valueDiv = label.nextElementSibling;
|
||||
if (valueDiv == null) continue;
|
||||
|
||||
final links = valueDiv.select('a');
|
||||
String value;
|
||||
if (links.isNotEmpty) {
|
||||
value = links.map((a) => a.text.trim()).join(' - ');
|
||||
} else {
|
||||
value = valueDiv.text.trim();
|
||||
}
|
||||
|
||||
info[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
final buffer = StringBuffer();
|
||||
buffer.writeln("Torrent Info:\n");
|
||||
info.forEach((k, v) {
|
||||
buffer.writeln("${k.padRight(11)}: $v");
|
||||
});
|
||||
if (getPreferenceValue(source.id, "torrent_description_visible")) {
|
||||
buffer.writeln("\n\n");
|
||||
buffer.writeln("Torrent Description: \n");
|
||||
buffer.writeln(
|
||||
document
|
||||
.select("#torrent-description")
|
||||
.map((e) => e.text.trim())
|
||||
.join("\n\n"),
|
||||
);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga anime = MManga();
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
|
||||
anime.description = extractPanelBody(document);
|
||||
|
||||
List<MChapter> chapters = [];
|
||||
chapters.add(
|
||||
MChapter(
|
||||
name: "Torrent",
|
||||
url: "${getBaseUrl()}/download/${substringAfterLast(url, '/')}.torrent",
|
||||
),
|
||||
);
|
||||
anime.chapters = chapters;
|
||||
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = url
|
||||
..originalUrl = url
|
||||
..quality = "";
|
||||
return [video];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SortFilter("SortFilter", "Sort by", SortState(0, true), [
|
||||
SelectFilterOption("None", ""),
|
||||
SelectFilterOption("Size", "size"),
|
||||
SelectFilterOption("Date", "id"),
|
||||
SelectFilterOption("Seeders", "seeders"),
|
||||
SelectFilterOption("Leechers", "leechers"),
|
||||
SelectFilterOption("Download", "downloads"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_categorie_page",
|
||||
title: "Preferred categorie page",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Anime", "Live Action"],
|
||||
entryValues: ["1_0", "4_0"],
|
||||
),
|
||||
SwitchPreferenceCompat(
|
||||
key: "torrent_description_visible",
|
||||
title: "Display Torrent Description",
|
||||
summary:
|
||||
"Enable to show the full torrent description in the details view.",
|
||||
value: false,
|
||||
),
|
||||
EditTextPreference(
|
||||
key: "domain_url",
|
||||
title: 'Edit URL',
|
||||
summary: "",
|
||||
value: source.baseUrl,
|
||||
dialogTitle: "URL",
|
||||
dialogMessage: "",
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
String getBaseUrl() {
|
||||
final baseUrl = getPreferenceValue(source.id, "domain_url")?.trim();
|
||||
|
||||
if (baseUrl == null || baseUrl.isEmpty) {
|
||||
return source.baseUrl;
|
||||
}
|
||||
|
||||
return baseUrl.endsWith("/")
|
||||
? baseUrl.substring(0, baseUrl.length - 1)
|
||||
: baseUrl;
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final document = parseHtml(res);
|
||||
|
||||
final values = document.select(
|
||||
"body > div > div.table-responsive > table > tbody > tr",
|
||||
);
|
||||
for (var value in values) {
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl =
|
||||
"${getBaseUrl()}${getUrlWithoutDomain(value.selectFirst("td:nth-child(1) > a > img").getSrc)}";
|
||||
MElement firstElement = value
|
||||
.select("td > a")
|
||||
.where(
|
||||
(MElement e) =>
|
||||
e.outerHtml.contains("/view/") &&
|
||||
!e.outerHtml.contains("#comments"),
|
||||
)
|
||||
.toList()
|
||||
.first;
|
||||
anime.link =
|
||||
"${getBaseUrl()}${getUrlWithoutDomain(firstElement.getHref)}";
|
||||
anime.name = firstElement.attr("title");
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
final hasNextPage = xpath(
|
||||
res,
|
||||
'//ul[@class="pagination"]/li[contains(text(),"»")]/a/@href',
|
||||
).isNotEmpty;
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
String ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
Nyaa main(MSource source) {
|
||||
return Nyaa(source: source);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
const _nyaaVersion = "0.0.4";
|
||||
const _nyaaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/nyaa/nyaa.dart";
|
||||
|
||||
String _iconUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/all/nyaa/icon.png";
|
||||
|
||||
Source get nyaaSource => _nyaaSource;
|
||||
Source _nyaaSource = Source(
|
||||
name: 'Nyaa',
|
||||
baseUrl: "https://nyaa.si",
|
||||
lang: "all",
|
||||
typeSource: "torrent",
|
||||
iconUrl: _iconUrl,
|
||||
version: _nyaaVersion,
|
||||
itemType: ItemType.anime,
|
||||
sourceCodeUrl: _nyaaSourceCodeUrl,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,234 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class OkAnime extends MProvider {
|
||||
OkAnime({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).body;
|
||||
List<MManga> animeList = [];
|
||||
String path =
|
||||
'//div[@class="section" and contains(text(),"افضل انميات")]/div[@class="section-content"]/div/div/div[contains(@class,"anime-card")]';
|
||||
final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
|
||||
final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
|
||||
final images = xpath(res, '$path/div[@class="anime-image")]/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);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/espisode-list?page=$page"),
|
||||
)).body;
|
||||
List<MManga> animeList = [];
|
||||
String path = '//*[contains(@class,"anime-card")]';
|
||||
final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
|
||||
final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
|
||||
final images = xpath(res, '$path/div[@class="episode-image")]/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,
|
||||
'//li[@class="page-item"]/a[@rel="next"]/@href',
|
||||
);
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String url = "${source.baseUrl}/search/?s=$query";
|
||||
if (page > 1) {
|
||||
url += "&page=$page";
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
String path = '//*[contains(@class,"anime-card")]';
|
||||
final urls = xpath(res, '$path/div[@class="anime-title")]/h4/a/@href');
|
||||
final names = xpath(res, '$path/div[@class="anime-title")]/h4/a/text()');
|
||||
final images = xpath(res, '$path/div[@class="anime-image")]/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,
|
||||
'//li[@class="page-item"]/a[@rel="next"]/@href',
|
||||
);
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"يعرض الان": 0, "مكتمل": 1},
|
||||
];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final status = xpath(
|
||||
res,
|
||||
'//*[@class="full-list-info" and contains(text(),"حالة الأنمي")]/small/a/text()',
|
||||
);
|
||||
if (status.isNotEmpty) {
|
||||
anime.status = parseStatus(status.first, statusList);
|
||||
}
|
||||
anime.description = xpath(res, '//*[@class="review-content"]/text()').first;
|
||||
|
||||
anime.genre = xpath(res, '//*[@class="review-author-info"]/a/text()');
|
||||
final epUrls =
|
||||
xpath(
|
||||
res,
|
||||
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h5/a/@href',
|
||||
).reversed.toList();
|
||||
final names =
|
||||
xpath(
|
||||
res,
|
||||
'//*[contains(@class,"anime-card")]/div[@class="anime-title")]/h5/a/text()',
|
||||
).reversed.toList();
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < epUrls.length; i++) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = names[i];
|
||||
episode.url = epUrls[i];
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final urls = xpath(res, '//*[@id="streamlinks"]/a/@data-src');
|
||||
final qualities = xpath(res, '//*[@id="streamlinks"]/a/span/text()');
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
List<MVideo> videos = [];
|
||||
for (var i = 0; i < urls.length; i++) {
|
||||
final url = urls[i];
|
||||
final quality = getQuality(qualities[i]);
|
||||
List<MVideo> a = [];
|
||||
|
||||
if (url.contains("https://doo") && hosterSelection.contains("Dood")) {
|
||||
a = await doodExtractor(url, "DoodStream - $quality");
|
||||
} else if (url.contains("mp4upload") &&
|
||||
hosterSelection.contains("Mp4upload")) {
|
||||
a = await mp4UploadExtractor(url, null, "", "");
|
||||
} else if (url.contains("ok.ru") && hosterSelection.contains("Okru")) {
|
||||
a = await okruExtractor(url);
|
||||
} else if (url.contains("voe.sx") && hosterSelection.contains("Voe")) {
|
||||
a = await voeExtractor(url, "VoeSX $quality");
|
||||
} else if (containsVidBom(url) && hosterSelection.contains("VidBom")) {
|
||||
a = await vidBomExtractor(url);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
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;
|
||||
}
|
||||
|
||||
List<String> preferenceHosterSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "hoster_selection");
|
||||
}
|
||||
|
||||
String getQuality(String quality) {
|
||||
quality = quality.replaceAll(" ", "");
|
||||
if (quality == "HD") {
|
||||
return "720p";
|
||||
} else if (quality == "FHD") {
|
||||
return "1080p";
|
||||
} else if (quality == "SD") {
|
||||
return "480p";
|
||||
}
|
||||
return "240p";
|
||||
}
|
||||
|
||||
bool containsVidBom(String url) {
|
||||
url = url;
|
||||
final list = ["vidbam", "vadbam", "vidbom", "vidbm"];
|
||||
for (var n in list) {
|
||||
if (url.contains(n)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OkAnime main(MSource source) {
|
||||
return OkAnime(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get okanimeSource => _okanimeSource;
|
||||
const _okanimeVersion = "0.0.6";
|
||||
const _okanimeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/ar/okanime/okanime.dart";
|
||||
Source _okanimeSource = Source(
|
||||
name: "Okanime",
|
||||
baseUrl: "https://www.okanime.xyz",
|
||||
lang: "ar",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/ar/okanime/icon.png",
|
||||
sourceCodeUrl: _okanimeSourceCodeUrl,
|
||||
version: _okanimeVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,250 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
|
||||
class AnimeToast extends MProvider {
|
||||
AnimeToast({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
String get baseUrl => source.baseUrl;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(baseUrl))).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select("div.row div.col-md-4 div.video-item");
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
MManga anime = MManga();
|
||||
anime.name = element.selectFirst("div.item-thumbnail a").attr("title");
|
||||
anime.link = getUrlWithoutDomain(
|
||||
element.selectFirst("div.item-thumbnail a").attr("href"),
|
||||
);
|
||||
anime.imageUrl = element
|
||||
.selectFirst("div.item-thumbnail a img")
|
||||
.attr("src");
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/page/$page/?s=$query"))).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select("div.item-thumbnail a[href]");
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
MManga anime = MManga();
|
||||
anime.name = element.attr("title");
|
||||
anime.link = getUrlWithoutDomain(element.attr("href"));
|
||||
anime.imageUrl = element.selectFirst("a img").attr("src");
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(
|
||||
animeList,
|
||||
document.selectFirst("li.next a")?.attr("href") != null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga anime = MManga();
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
anime.imageUrl = document.selectFirst(".item-content p img").attr("src");
|
||||
anime.genre =
|
||||
(document.xpathFirst('//p[contains(text(),"Genre:")]/text()') ?? "")
|
||||
.replaceAll("Genre:", "")
|
||||
.split(",");
|
||||
anime.description = document.selectFirst("div.item-content div + p").text;
|
||||
final categoryTag = document.xpath('//*[@rel="category tag"]/text()');
|
||||
if (categoryTag.isNotEmpty) {
|
||||
if (categoryTag.contains("Airing")) {
|
||||
anime.status = MStatus.ongoing;
|
||||
} else {
|
||||
anime.status = MStatus.completed;
|
||||
}
|
||||
}
|
||||
List<MChapter>? episodesList = [];
|
||||
if (categoryTag.contains("Serie")) {
|
||||
List<MElement> elements = [];
|
||||
if (document.selectFirst("#multi_link_tab0")?.attr("id") != null) {
|
||||
elements = document.select("#multi_link_tab0");
|
||||
} else {
|
||||
elements = document.select("#multi_link_tab1");
|
||||
}
|
||||
|
||||
for (var element in elements) {
|
||||
final episodeElement = element.selectFirst("a");
|
||||
final epT = episodeElement.text;
|
||||
if (epT.contains(":") || epT.contains("-")) {
|
||||
final url = episodeElement.attr("href");
|
||||
final document = parseHtml((await client.get(Uri.parse(url))).body);
|
||||
final nUrl = document.selectFirst("#player-embed a").attr("href");
|
||||
final nDoc = parseHtml((await client.get(Uri.parse(nUrl))).body);
|
||||
final nEpEl = nDoc.select("div.tab-pane a");
|
||||
for (var epElement in nEpEl) {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = epElement.text;
|
||||
ep.url = getUrlWithoutDomain(epElement.attr("href"));
|
||||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
final episodeElements = element.select("a");
|
||||
for (var epElement in episodeElements) {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = epElement.text;
|
||||
ep.url = getUrlWithoutDomain(epElement.attr("href"));
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = document.selectFirst("h1.light-title")?.text ?? "Film";
|
||||
ep.url = getUrlWithoutDomain(
|
||||
document.selectFirst("link[rel=canonical]").attr("href"),
|
||||
);
|
||||
episodesList.add(ep);
|
||||
}
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
List<String> preferenceHosterSelection() {
|
||||
return getPreferenceValue(source.id, "hoster_selection");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
final fEp = document.selectFirst("div.tab-pane");
|
||||
List<MVideo> videos = [];
|
||||
List<MElement> ep = [];
|
||||
int epcu = 100;
|
||||
|
||||
if (fEp.text.contains(":") || fEp.text.contains("-")) {
|
||||
final tx = document.select("div.tab-pane");
|
||||
|
||||
for (var e in tx) {
|
||||
final sUrl = e.selectFirst("a").attr("href");
|
||||
final doc = parseHtml((await client.get(Uri.parse(sUrl))).body);
|
||||
final nUrl = doc.selectFirst("#player-embed a").attr("href");
|
||||
final nDoc = parseHtml((await client.get(Uri.parse(nUrl))).body);
|
||||
epcu =
|
||||
int.tryParse(
|
||||
substringAfter(
|
||||
document.selectFirst("div.tab-pane a.current-link")?.text ?? "",
|
||||
"Ep.",
|
||||
),
|
||||
) ??
|
||||
100;
|
||||
ep = nDoc.select("div.tab-pane a");
|
||||
}
|
||||
} else {
|
||||
epcu =
|
||||
int.tryParse(
|
||||
substringAfter(
|
||||
document.selectFirst("div.tab-pane a.current-link")?.text ?? "",
|
||||
"Ep.",
|
||||
),
|
||||
) ??
|
||||
100;
|
||||
ep = document.select("div.tab-pane a");
|
||||
}
|
||||
final hosterSelection = preferenceHosterSelection();
|
||||
for (var e in ep) {
|
||||
if (int.tryParse(substringAfter(e.text, "Ep.")) == epcu) {
|
||||
final epUrl = e.attr("href");
|
||||
final newdoc = parseHtml((await client.get(Uri.parse(epUrl))).body);
|
||||
final elements = newdoc.select("#player-embed");
|
||||
for (var element in elements) {
|
||||
final link = element.selectFirst("a").getHref ?? "";
|
||||
if (link.contains("https://voe.sx") &&
|
||||
hosterSelection.contains("voe")) {
|
||||
videos.addAll(await voeExtractor(link, "Voe"));
|
||||
}
|
||||
}
|
||||
for (var element in elements) {
|
||||
List<MVideo> a = [];
|
||||
final link = element.selectFirst("iframe").getSrc ?? "";
|
||||
if ((link.contains("https://dood") ||
|
||||
link.contains("https://ds2play") ||
|
||||
link.contains("https://d0")) &&
|
||||
hosterSelection.contains("dood")) {
|
||||
a = await doodExtractor(link, "DoodStream");
|
||||
} else if (link.contains("filemoon") &&
|
||||
hosterSelection.contains("filemoon")) {
|
||||
a = await filemoonExtractor(link, "", "");
|
||||
} else if (link.contains("mp4upload") &&
|
||||
hosterSelection.contains("mp4upload")) {
|
||||
a = await mp4UploadExtractor(url, null, "", "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortVideos(videos);
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos) {
|
||||
String server = getPreferenceValue(source.id, "preferred_hoster");
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.toLowerCase().contains(server)) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.toLowerCase().contains(server)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_hoster",
|
||||
title: "Standard-Hoster",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Voe", "DoodStream", "Filemoon", "Mp4upload"],
|
||||
entryValues: ["voe", "doodStream", "filemoon", "mp4upload"],
|
||||
),
|
||||
MultiSelectListPreference(
|
||||
key: "hoster_selection",
|
||||
title: "Hoster auswählen",
|
||||
summary: "",
|
||||
entries: ["Voe", "DoodStream", "Filemoon", "Mp4upload"],
|
||||
entryValues: ["voe", "dood", "filemoon", "mp4upload"],
|
||||
values: ["voe", "dood", "filemoon", "mp4upload"],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
AnimeToast main(MSource source) {
|
||||
return AnimeToast(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animetoast => _animetoast;
|
||||
const _animetoastVersion = "0.0.25";
|
||||
const _animetoastCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/animetoast.dart";
|
||||
Source _animetoast = Source(
|
||||
name: "AnimeToast",
|
||||
baseUrl: "https://animetoast.cc",
|
||||
lang: "de",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/de/animetoast/icon.png",
|
||||
sourceCodeUrl: _animetoastCodeUrl,
|
||||
version: _animetoastVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,415 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
class AnimePahe extends MProvider {
|
||||
AnimePahe(this.source);
|
||||
|
||||
final MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "preferred_domain");
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {'cookie': '__ddg1_=;__ddg2_=;'};
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return await getLatestUpdates(page);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/api?m=airing&page=$page"),
|
||||
headers: headers,
|
||||
)).body;
|
||||
final jsonResult = json.decode(res);
|
||||
final hasNextPage = jsonResult["current_page"] < jsonResult["last_page"];
|
||||
List<MManga> animeList = [];
|
||||
for (var item in jsonResult["data"]) {
|
||||
MManga anime = MManga();
|
||||
anime.name = item["anime_title"];
|
||||
anime.imageUrl = item["snapshot"];
|
||||
anime.link = "/anime/?anime_id=${item["id"]}&name=${item["anime_title"]}";
|
||||
anime.artist = item["fansub"];
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/api?m=search&l=8&q=$query"),
|
||||
headers: headers,
|
||||
)).body;
|
||||
final jsonResult = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
for (var item in jsonResult["data"]) {
|
||||
MManga anime = MManga();
|
||||
anime.name = item["title"];
|
||||
anime.imageUrl = item["poster"];
|
||||
anime.link = "/anime/?anime_id=${item["id"]}&name=${item["title"]}";
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Currently Airing": 0, "Finished Airing": 1},
|
||||
];
|
||||
MManga anime = MManga();
|
||||
final id = substringBefore(substringAfterLast(url, "?anime_id="), "&name=");
|
||||
final name = substringAfterLast(url, "&name=");
|
||||
final session = await getSession(name, id);
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/anime/$session?anime_id=$id"),
|
||||
headers: headers,
|
||||
)).body;
|
||||
final document = parseHtml(res);
|
||||
final status =
|
||||
(document.xpathFirst('//div/p[contains(text(),"Status:")]/text()') ??
|
||||
"")
|
||||
.replaceAll("Status:\n", "")
|
||||
.trim();
|
||||
anime.status = parseStatus(status, statusList);
|
||||
|
||||
anime.name = document.selectFirst("div.title-wrapper > h1 > span").text;
|
||||
anime.author =
|
||||
(document.xpathFirst('//div/p[contains(text(),"Studio:")]/text()') ??
|
||||
"")
|
||||
.replaceAll("Studio:\n", "")
|
||||
.trim();
|
||||
anime.imageUrl = document.selectFirst("div.anime-poster a").attr("href");
|
||||
anime.genre = xpath(
|
||||
res,
|
||||
'//*[contains(@class,"anime-genre")]/ul/li/text()',
|
||||
);
|
||||
final synonyms =
|
||||
(document.xpathFirst('//div/p[contains(text(),"Synonyms:")]/text()') ??
|
||||
"")
|
||||
.replaceAll("Synonyms:\n", "")
|
||||
.trim();
|
||||
anime.description = document.selectFirst("div.anime-summary").text;
|
||||
if (synonyms.isNotEmpty) {
|
||||
anime.description += "\n\n$synonyms";
|
||||
}
|
||||
final epUrl = "$baseUrl/api?m=release&id=$session&sort=episode_desc&page=1";
|
||||
final resEp = (await client.get(Uri.parse(epUrl), headers: headers)).body;
|
||||
final episodes = await recursivePages(epUrl, resEp, session);
|
||||
|
||||
anime.chapters = episodes;
|
||||
return anime;
|
||||
}
|
||||
|
||||
Future<List<MChapter>> recursivePages(
|
||||
String url,
|
||||
String res,
|
||||
String session,
|
||||
) async {
|
||||
final jsonResult = json.decode(res);
|
||||
final page = jsonResult["current_page"];
|
||||
final hasNextPage = page < jsonResult["last_page"];
|
||||
List<MManga> animeList = [];
|
||||
for (var item in jsonResult["data"]) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Episode ${item["episode"]}";
|
||||
episode.url = "/play/$session/${item["session"]}";
|
||||
episode.dateUpload = parseDates(
|
||||
[item["created_at"]],
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
"en",
|
||||
)[0];
|
||||
animeList.add(episode);
|
||||
}
|
||||
if (hasNextPage) {
|
||||
final newUrl = "${substringBeforeLast(url, "&page=")}&page=${page + 1}";
|
||||
final newRes = (await client.get(
|
||||
Uri.parse(newUrl),
|
||||
headers: headers,
|
||||
)).body;
|
||||
animeList.addAll(await recursivePages(newUrl, newRes, session));
|
||||
}
|
||||
return animeList;
|
||||
}
|
||||
|
||||
Future<String> getSession(String title, String animeId) async {
|
||||
final noRedirect = Client(
|
||||
source,
|
||||
json.encode({"followRedirects": false, "useDartHttpClient": true}),
|
||||
);
|
||||
|
||||
final res = await noRedirect.get(
|
||||
Uri.parse("$baseUrl/a/$animeId"),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
final location =
|
||||
"https://${substringAfterLast(getMapValue(json.encode(res.headers), "location"), "https://")}";
|
||||
|
||||
if (location == '$baseUrl/anime') {
|
||||
final res = (await client.get(
|
||||
Uri.parse("$baseUrl/api?m=search&q=$title"),
|
||||
headers: headers,
|
||||
)).body;
|
||||
return substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(res, "\"id\":$animeId"),
|
||||
"\"session\":\"",
|
||||
),
|
||||
"\"",
|
||||
);
|
||||
}
|
||||
return substringAfterLast(location, '/');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
//by default we use rhttp package but it does not support `followRedirects`
|
||||
//so setting `useDartHttpClient` to true allows us to use a Dart http package that supports `followRedirects`
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"), headers: headers));
|
||||
final document = parseHtml(res.body);
|
||||
final downloadLinks = document.select("div#pickDownload > a");
|
||||
final buttons = document.select("div#resolutionMenu > button");
|
||||
List<MVideo> videos = [];
|
||||
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
final btn = buttons[i];
|
||||
final audio = btn.attr(
|
||||
"data-audio",
|
||||
); // Get audio type (jpn/eng). Japanese or Dubbed.
|
||||
final kwikLink = btn.attr("data-src");
|
||||
final quality = btn.text;
|
||||
final paheWinLink = downloadLinks[i].attr("href");
|
||||
|
||||
if (getPreferenceValue(source.id, "preffered_link_type")) {
|
||||
final noRedirectClient = Client(
|
||||
source,
|
||||
json.encode({"followRedirects": false, "useDartHttpClient": true}),
|
||||
);
|
||||
final kwikHeaders = (await noRedirectClient.get(
|
||||
Uri.parse("${paheWinLink}/i"),
|
||||
)).headers;
|
||||
final kwikUrl =
|
||||
"https://${substringAfterLast(getMapValue(json.encode(kwikHeaders), "location"), "https://")}";
|
||||
final reskwik = (await client.get(
|
||||
Uri.parse(kwikUrl),
|
||||
headers: {"Referer": "https://kwik.cx/"},
|
||||
));
|
||||
final matches = RegExp(
|
||||
r'\("(\S+)",\d+,"(\S+)",(\d+),(\d+)',
|
||||
).firstMatch(reskwik.body);
|
||||
final token = decrypt(
|
||||
matches!.group(1)!,
|
||||
matches.group(2)!,
|
||||
matches.group(3)!,
|
||||
int.parse(matches.group(4)!),
|
||||
);
|
||||
final url = RegExp(r'action="([^"]+)"').firstMatch(token)!.group(1)!;
|
||||
final tok = RegExp(r'value="([^"]+)"').firstMatch(token)!.group(1)!;
|
||||
var code = 419;
|
||||
var tries = 0;
|
||||
String location = "";
|
||||
|
||||
while (code != 302 && tries < 20) {
|
||||
String cookie = getMapValue(
|
||||
json.encode(res.request.headers),
|
||||
"cookie",
|
||||
);
|
||||
cookie +=
|
||||
"; ${getMapValue(json.encode(reskwik.headers), "set-cookie").replaceAll("path=/;", "")}";
|
||||
final resNo =
|
||||
await Client(
|
||||
source,
|
||||
json.encode({
|
||||
"followRedirects": false,
|
||||
"useDartHttpClient": true,
|
||||
}),
|
||||
).post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
"referer": reskwik.request.url.toString(),
|
||||
"cookie": cookie,
|
||||
"user-agent": getMapValue(
|
||||
json.encode(res.request.headers),
|
||||
"user-agent",
|
||||
),
|
||||
},
|
||||
body: {"_token": tok},
|
||||
);
|
||||
code = resNo.statusCode;
|
||||
tries++;
|
||||
location = getMapValue(json.encode(resNo.headers), "location");
|
||||
}
|
||||
if (tries > 19) {
|
||||
throw ("Failed to extract the stream uri from kwik.");
|
||||
}
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = location
|
||||
..originalUrl = location
|
||||
..quality = quality;
|
||||
videos.add(video);
|
||||
} else {
|
||||
final ress = (await client.get(
|
||||
Uri.parse(kwikLink),
|
||||
headers: {"Referer": "https://animepahe.com"},
|
||||
));
|
||||
final script = substringAfterLast(
|
||||
xpath(
|
||||
ress.body,
|
||||
'//script[contains(text(),"eval(function")]/text()',
|
||||
).first,
|
||||
"eval(function(",
|
||||
);
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(
|
||||
unpackJsAndCombine("eval(function($script"),
|
||||
"const source=\\'",
|
||||
),
|
||||
"\\';",
|
||||
);
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = quality
|
||||
..headers = {"referer": "https://kwik.cx"};
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
return sortVideos(videos);
|
||||
}
|
||||
|
||||
String getString(String ctn, int sep) {
|
||||
int b = 10;
|
||||
String cm =
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
|
||||
final n = cm.substring(0, b);
|
||||
double mx = 0;
|
||||
for (var index = 0; index < ctn.length; index++) {
|
||||
mx +=
|
||||
(int.tryParse(ctn[ctn.length - index - 1], radix: 10) ?? 0.0)
|
||||
.toInt() *
|
||||
(pow(sep, index));
|
||||
}
|
||||
var m = '';
|
||||
while (mx > 0) {
|
||||
m = n[(mx % b).toInt()] + m;
|
||||
mx = (mx - (mx % b)) / b;
|
||||
}
|
||||
return m.isNotEmpty ? m : '0';
|
||||
}
|
||||
|
||||
String decrypt(String fS, String key, String v1, int v2) {
|
||||
var html = "";
|
||||
var i = 0;
|
||||
final ld = int.parse(v1);
|
||||
while (i < fS.length) {
|
||||
var s = "";
|
||||
while (fS[i] != key[v2]) {
|
||||
s += fS[i];
|
||||
i++;
|
||||
}
|
||||
for (var index = 0; index < key.length; index++) {
|
||||
s = s.replaceAll(key[index], index.toString());
|
||||
}
|
||||
html += String.fromCharCode(int.parse(getString(s, v2)) - ld);
|
||||
i++;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos) {
|
||||
String quality = getPreferenceValue(source.id, "preferred_quality");
|
||||
String preferredAudio = getPreferenceValue(
|
||||
source.id,
|
||||
"preferred_audio",
|
||||
); // get user's audio preference
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
// Prioritize audio first.
|
||||
// Preferred Audio: Videos with matching preferred audio are ranked highest.
|
||||
int audioMatchA = a.quality.contains(preferredAudio) ? 1 : 0;
|
||||
int audioMatchB = b.quality.contains(preferredAudio) ? 1 : 0;
|
||||
if (audioMatchA != audioMatchB) {
|
||||
return audioMatchB - audioMatchA;
|
||||
}
|
||||
|
||||
// quality prioritized next
|
||||
// Preferred Video Quality: If audio matches, videos with preferred video quality are ranked higher.
|
||||
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;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_domain",
|
||||
title: "Preferred domain",
|
||||
summary: "",
|
||||
valueIndex: 1,
|
||||
entries: ["animepahe.com", "animepahe.ru", "animepahe.org"],
|
||||
entryValues: [
|
||||
"https://animepahe.com",
|
||||
"https://animepahe.ru",
|
||||
"https://animepahe.org",
|
||||
],
|
||||
),
|
||||
SwitchPreferenceCompat(
|
||||
key: "preffered_link_type",
|
||||
title: "Use HLS links",
|
||||
summary: "Enable this if you are having Cloudflare issues.",
|
||||
value: false,
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred Quality",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "360p"],
|
||||
entryValues: ["1080", "720", "360"],
|
||||
),
|
||||
|
||||
ListPreference(
|
||||
key: "preferred_audio", // Add new preference for audio
|
||||
title: "Preferred Audio",
|
||||
summary: "Select your preferred audio language (Japanese or English).",
|
||||
valueIndex: 0, // Default to Japanese (or whichever you prefer)
|
||||
entries: ["Japanese", "English"],
|
||||
entryValues: ["jpn", "eng"],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
AnimePahe main(MSource source) {
|
||||
return AnimePahe(source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animepaheSource => _animepaheSource;
|
||||
const _animepaheVersion = "0.0.75";
|
||||
const _animepaheSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/animepahe.dart";
|
||||
Source _animepaheSource = Source(
|
||||
name: "AnimePahe",
|
||||
baseUrl: "https://www.animepahe.ru",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/animepahe/icon.png",
|
||||
sourceCodeUrl: _animepaheSourceCodeUrl,
|
||||
version: _animepaheVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,241 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class DonghuaStream extends MProvider {
|
||||
DonghuaStream({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
bool get supportsLatest => true;
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {};
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}/anime?page=${page}&sub=&order=popular"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//article[@class="bs"]/div/a/@href');
|
||||
final names = xpath(res, '//article[@class="bs"]/div/a/@title');
|
||||
final images = xpath(res, '//article[@class="bs"]/div/a/div/img/@data-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="r"]/@href');
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}/anime?page=${page}&sub=&order=update"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//article[@class="bs"]/div/a/@href');
|
||||
final names = xpath(res, '//article[@class="bs"]/div/a/@title');
|
||||
final images = xpath(res, '//article[@class="bs"]/div/a/div/img/@data-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="r"]/@href');
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}/page/${page}/?s=${query}"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//article[@class="bs"]/div/a/@href');
|
||||
final names = xpath(res, '//article[@class="bs"]/div/a/@title');
|
||||
final images = xpath(res, '//article[@class="bs"]/div/a/div/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-numbers"]/@href');
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
var genre = xpath(res,'//div[@class="genxed"]/a/text()');
|
||||
genre.remove('MY FAVOURITE');
|
||||
anime.genre = genre;
|
||||
anime.description = xpath(res,'//div[@class="entry-content"]/p/text()').join("\n");
|
||||
|
||||
final statusList = [{"Status: Ongoing": 0, "Status: Completed": 1}];
|
||||
final infoContent = xpath(res,'//div[@class="info-content"]/div[@class="spe"]/span/text()');
|
||||
anime.status = parseStatus(infoContent[0], statusList);
|
||||
anime.author = infoContent[1].replaceFirst('Network: ','').replaceFirst('Donghua Stream, ','');
|
||||
anime.artist = infoContent[2].replaceFirst('Studio: ','');
|
||||
final epElements = parseHtml(res).select('div.eplister > ul > li >a');
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
for (var epElement in epElements) {
|
||||
final number = epElement.selectFirst("div.epl-num").text;
|
||||
final title = epElement.selectFirst("div.epl-title").text;
|
||||
final dateString = epElement.selectFirst("div.epl-date").text;
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Episode $number";
|
||||
episode.url = epElement.getHref;
|
||||
episode.dateUpload = parseDates([dateString],"MMMM d, yyyy","en",)[0];
|
||||
episodesList.add(episode);
|
||||
}
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
|
||||
// For anime episode video list
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
var servers = parseHtml(res).select('select.mirror > option[data-index]');
|
||||
if(servers.length==0){
|
||||
final src_data = parseHtml(res).selectFirst('article[id] > script').attr('src').replaceAll ('data:text/javascript;base64,','');
|
||||
final src_function = utf8.decode(base64Url.decode(src_data));
|
||||
final html_data = RegExp(r'"html":"(.*?)","autoplayIndex"').firstMatch(src_function).group(1);
|
||||
servers = parseHtml(html_data.replaceAll(r'\t', '\t').replaceAll(r'\n', '\n').replaceAll(r'\"', '"').replaceAll(r'\/', '/')).select('select.mirror > option[data-index]');
|
||||
}
|
||||
List<MVideo> videos = [];
|
||||
for (var i = 0; i < servers.length; i++) {
|
||||
String name = '${servers[i].attr("data-index")}: ${servers[i].text}';
|
||||
String valueHtml = utf8.decode(base64Url.decode(servers[i].attr('value')));
|
||||
final serverUrlList = xpath(valueHtml,'//iframe/@src');
|
||||
if (serverUrlList.length>0){
|
||||
String serverUrl = serverUrlList[0];
|
||||
if(serverUrl.contains('https://geo.dailymotion.com/player')){
|
||||
String videoId = RegExp(r'[?&]video=([a-zA-Z0-9]+)').firstMatch(serverUrl).group(1);
|
||||
videos.addAll(await dailymotionVideosFetcher(videoId,name));
|
||||
}else if(serverUrl.contains('//play.streamplay.co.in/')){
|
||||
String videoId = serverUrl.split('/embed/')[1];
|
||||
videos.addAll(await streamplayVideosFetcher(videoId,name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> dailymotionVideosFetcher(String videoID, String name) async {
|
||||
String metaDataUrl = 'https://www.dailymotion.com/player/metadata/video/$videoID';
|
||||
final res = (await client.get(Uri.parse(metaDataUrl))).body;
|
||||
final jsonRes = json.decode(res);
|
||||
String masterUrl = jsonRes["qualities"]["auto"][0]["url"];
|
||||
return m3u8extractor(masterUrl, name);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamplayVideosFetcher(String videoID, String name) async {
|
||||
String url = 'https://play.streamplay.co.in/embed/'+videoID;
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final match = RegExp(r"eval\(function\(p,a,c,k,e,d\)\{[\s\S]*?\}\((.*?)\)").firstMatch(res);
|
||||
if (match == null) {
|
||||
return [];
|
||||
}
|
||||
final argsStr = match.group(1);
|
||||
final argsPattern = RegExp(r"'(.*?)',(.*?),(.*?),'(.*?)'\.split");
|
||||
final argsMatches = argsPattern.firstMatch(argsStr);
|
||||
final arg_p = argsMatches.group(1);
|
||||
final arg_a =int.parse(argsMatches.group(2));
|
||||
final arg_c =int.parse(argsMatches.group(3));
|
||||
final arg_k =argsMatches.group(4).split('|').toList();
|
||||
final unpacked_js = unpack(arg_p,arg_a,arg_c,arg_k);
|
||||
final kakenMatch = RegExp(r'window\.kaken\s*=\s*"([^"]+)"').firstMatch(unpacked_js);
|
||||
if (kakenMatch == null) {
|
||||
return [];
|
||||
}
|
||||
final kakenValue = kakenMatch.group(1);
|
||||
final apiUrl = 'https://play.streamplay.co.in/api/?$kakenValue';
|
||||
final apiRes = (await client.get(Uri.parse(apiUrl))).body;
|
||||
final jsonRes = json.decode(apiRes);
|
||||
String masterUrl = jsonRes['sources'][0]['file'];
|
||||
List<MTrack> subtitles = [];
|
||||
for (final track in jsonRes['tracks']){
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle.label = name + ' - ' + track['label'];
|
||||
subtitle.file = track['file'];
|
||||
subtitles.add(subtitle);
|
||||
}
|
||||
List<MVideo> videos = await m3u8extractor(masterUrl, name);
|
||||
if(videos.length>0){
|
||||
videos[0].subtitles = subtitles;
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
String unpack(String p, int a, int c, List<String> k) {
|
||||
for (int i = c - 1; i >= 0; i--) {
|
||||
String word = (i < k.length) ? k[i] : baseN(i, a);
|
||||
String pattern = r'\b' + baseN(i, a) + r'\b';
|
||||
p = p.replaceAll(RegExp(pattern), word);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
String baseN(int num, int base) {
|
||||
const digits = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
if (num == 0) return '0';
|
||||
String result = '';
|
||||
while (num > 0) {
|
||||
result = digits[num % base] + result;
|
||||
num ~/= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> m3u8extractor(String masterUrl, String name) async {
|
||||
List<MVideo> videos = [];
|
||||
List<MTrack> subtitles = [];
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl), headers: headers)).body;
|
||||
final subtitleRegExp = RegExp(r'#EXT-X-MEDIA:TYPE=SUBTITLES.*?NAME="(.*?)".*?URI="(.*?)"', dotAll: true);
|
||||
for (final match in subtitleRegExp.allMatches(masterPlaylistRes)) {
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle.label = name + ' - ' + match.group(1) ?? 'Subtitle';
|
||||
subtitle.file = match.group(2) ?? '';
|
||||
subtitles.add(subtitle);
|
||||
}
|
||||
|
||||
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 = "$name - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
if(videos.length>0){
|
||||
videos[0].subtitles = subtitles;
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
DonghuaStream main(MSource source) {
|
||||
return DonghuaStream(source:source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 81 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get donghuastreamSource => _donghuastreamSource;
|
||||
const _donghuastreamVersion = "0.0.2";
|
||||
const _donghuastreamSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/donghuastream/donghuastream.dart";
|
||||
Source _donghuastreamSource = Source(
|
||||
name: "DonghuaStream",
|
||||
baseUrl: "https://donghuastream.org",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/donghuastream/icon.png",
|
||||
sourceCodeUrl: _donghuastreamSourceCodeUrl,
|
||||
version: _donghuastreamVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get gogoanimeSource => _gogoanimeSource;
|
||||
const _gogoanimeVersion = "0.1.2";
|
||||
const _gogoanimeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/gogoanime.dart";
|
||||
Source _gogoanimeSource = Source(
|
||||
name: "Gogoanime",
|
||||
baseUrl: "https://anitaku.to",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/gogoanime/icon.png",
|
||||
sourceCodeUrl: _gogoanimeSourceCodeUrl,
|
||||
version: _gogoanimeVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,223 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class KissKh extends MProvider {
|
||||
KissKh({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=1&pageSize=40",
|
||||
),
|
||||
)).body;
|
||||
final jsonRes = json.decode(res);
|
||||
final datas = jsonRes["data"];
|
||||
List<MManga> animeList = [];
|
||||
|
||||
for (var data in datas) {
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
int lastPage = jsonRes["totalCount"];
|
||||
int pages = jsonRes["page"];
|
||||
return MPages(animeList, pages < lastPage);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=12&pageSize=40",
|
||||
),
|
||||
)).body;
|
||||
final jsonRes = json.decode(res);
|
||||
final datas = jsonRes["data"];
|
||||
|
||||
List<MManga> animeList = [];
|
||||
|
||||
for (var data in datas) {
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
int lastPage = jsonRes["totalCount"];
|
||||
int pages = jsonRes["page"];
|
||||
return MPages(animeList, pages < lastPage);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/api/DramaList/Search?q=$query&type=0"),
|
||||
)).body;
|
||||
final jsonRes = json.decode(res);
|
||||
List<MManga> animeList = [];
|
||||
for (var data in jsonRes) {
|
||||
var anime = MManga();
|
||||
anime.name = data["title"];
|
||||
anime.imageUrl = data["thumbnail"] ?? "";
|
||||
anime.link =
|
||||
"${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false";
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Ongoing": 0, "Completed": 1},
|
||||
];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
var anime = MManga();
|
||||
final jsonRes = json.decode(res);
|
||||
final status = jsonRes["status"] ?? "";
|
||||
anime.description = jsonRes["description"];
|
||||
anime.status = parseStatus(status, statusList);
|
||||
anime.imageUrl = jsonRes["thumbnail"];
|
||||
var episodes = jsonRes["episodes"];
|
||||
String type = jsonRes["type"];
|
||||
final episodesCount = jsonRes["episodesCount"] as int;
|
||||
final containsAnime = type.contains("Anime");
|
||||
final containsTVSeries = type.contains("TVSeries");
|
||||
final containsHollywood = type.contains("Hollywood");
|
||||
final containsMovie = type.contains("Movie");
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
for (var a in episodes) {
|
||||
MChapter episode = MChapter();
|
||||
String number = (a["number"] as double).toString().replaceAll(".0", "");
|
||||
final id = a["id"];
|
||||
if (containsAnime || containsTVSeries) {
|
||||
episode.name = "Episode $number";
|
||||
} else if (containsHollywood && episodesCount == 1 || containsMovie) {
|
||||
episode.name = "Movie";
|
||||
} else if (containsHollywood && episodesCount > 1) {
|
||||
episode.name = "Episode $number";
|
||||
}
|
||||
episode.url =
|
||||
"${source.baseUrl}/api/DramaList/Episode/$id.png?err=false&ts=&time=";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final id = substringAfter(substringBefore(url, ".png"), "Episode/");
|
||||
final jsonRes = json.decode(res);
|
||||
|
||||
final subRes =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/api/Sub/$id"))).body;
|
||||
var jsonSubRes = json.decode(subRes);
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in jsonSubRes) {
|
||||
final subUrl = sub["src"] as String;
|
||||
final label = sub["label"];
|
||||
if (subUrl.endsWith("txt")) {
|
||||
var subtitle = await getSubtitle(subUrl, label);
|
||||
subtitles.add(subtitle);
|
||||
} else {
|
||||
var subtitle = MTrack();
|
||||
subtitle
|
||||
..label = label
|
||||
..file = subUrl;
|
||||
subtitles.add(subtitle);
|
||||
}
|
||||
}
|
||||
final videoUrl = jsonRes["Video"];
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "kisskh"
|
||||
..subtitles = subtitles
|
||||
..headers = {
|
||||
"referer": "https://kisskh.me/",
|
||||
"origin": "https://kisskh.me",
|
||||
};
|
||||
return [video];
|
||||
}
|
||||
|
||||
Future<MTrack> getSubtitle(String subUrl, String subLang) async {
|
||||
final response = await client.get(
|
||||
Uri.parse(subUrl),
|
||||
headers: {"referer": "https://kisskh.me/", "origin": "https://kisskh.me"},
|
||||
);
|
||||
final subtitleData = response.body;
|
||||
String decrypted = "\n";
|
||||
for (String line in subtitleData.split('\n')) {
|
||||
decrypted += "${decrypt(line.trim())}\n";
|
||||
}
|
||||
var subtitle = MTrack();
|
||||
subtitle
|
||||
..label = subLang
|
||||
..file = decrypted;
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
String decrypt(String data) {
|
||||
final key = utf8.decode([
|
||||
56,
|
||||
48,
|
||||
53,
|
||||
54,
|
||||
52,
|
||||
56,
|
||||
51,
|
||||
54,
|
||||
52,
|
||||
54,
|
||||
51,
|
||||
50,
|
||||
56,
|
||||
55,
|
||||
54,
|
||||
51,
|
||||
]);
|
||||
final iv = utf8.decode([
|
||||
54,
|
||||
56,
|
||||
53,
|
||||
50,
|
||||
54,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
55,
|
||||
48,
|
||||
49,
|
||||
56,
|
||||
53,
|
||||
50,
|
||||
55,
|
||||
51,
|
||||
]);
|
||||
return cryptoHandler(data, iv, key, false);
|
||||
}
|
||||
}
|
||||
|
||||
KissKh main(MSource source) {
|
||||
return KissKh(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get kisskhSource => _kisskhSource;
|
||||
const _kisskhVersion = "0.0.7";
|
||||
const _kisskhSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/kisskh.dart";
|
||||
Source _kisskhSource = Source(
|
||||
name: "KissKH",
|
||||
baseUrl: "https://kisskh.co",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/icon.png",
|
||||
sourceCodeUrl: _kisskhSourceCodeUrl,
|
||||
version: _kisskhVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,580 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class NineAnimeTv extends MProvider {
|
||||
NineAnimeTv({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/filter?sort=all&page=$page"),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/filter?sort=recently_updated&page=$page",
|
||||
),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "${source.baseUrl}/filter?keyword=$query";
|
||||
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "GenreFilter") {
|
||||
final genre = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}genre=";
|
||||
if (genre.isNotEmpty) {
|
||||
for (var st in genre) {
|
||||
url += "${st.value}";
|
||||
if (genre.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (genre.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "SeasonFilter") {
|
||||
final season = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}season=";
|
||||
if (season.isNotEmpty) {
|
||||
for (var st in season) {
|
||||
url += "${st.value}";
|
||||
if (season.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (season.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "YearFilter") {
|
||||
final year = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}year=";
|
||||
if (year.isNotEmpty) {
|
||||
for (var st in year) {
|
||||
url += "${st.value}";
|
||||
if (year.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (year.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "TypeFilter") {
|
||||
final type = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}type=";
|
||||
if (type.isNotEmpty) {
|
||||
for (var st in type) {
|
||||
url += "${st.value}";
|
||||
if (type.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (type.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "StatusFilter") {
|
||||
final status = filter.values[filter.state].value;
|
||||
url += "${ll(url)}status=$status";
|
||||
} else if (filter.type == "LanguageFilter") {
|
||||
final language = (filter.state as List).where((e) => e.state).toList();
|
||||
url += "${ll(url)}language=";
|
||||
if (language.isNotEmpty) {
|
||||
for (var st in language) {
|
||||
url += "${st.value}";
|
||||
if (language.length > 1) {
|
||||
url += "%2C";
|
||||
}
|
||||
}
|
||||
if (language.length > 1) {
|
||||
url = substringBeforeLast(url, '%2C');
|
||||
}
|
||||
}
|
||||
} else if (filter.type == "SortFilter") {
|
||||
final sort = filter.values[filter.state].value;
|
||||
url += "${ll(url)}sort=$sort";
|
||||
}
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse("$url&page=$page"))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Currently Airing": 0, "Finished Airing": 1},
|
||||
];
|
||||
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final document = parseHtml(res);
|
||||
final infoElement = document.selectFirst("div.film-infor");
|
||||
final status =
|
||||
infoElement.xpathFirst(
|
||||
'//div[contains(text(),"Status:")]/following-sibling::div/span/text()',
|
||||
) ??
|
||||
"";
|
||||
anime.status = parseStatus(status, statusList);
|
||||
anime.description =
|
||||
infoElement.selectFirst("div.film-description > p")?.text ?? "";
|
||||
anime.author =
|
||||
infoElement.xpathFirst(
|
||||
'//div[contains(text(),"Studios:")]/following-sibling::div/a/text()',
|
||||
) ??
|
||||
"";
|
||||
|
||||
anime.genre = infoElement.xpath(
|
||||
'//div[contains(text(),"Genre:")]/following-sibling::div/a/text()',
|
||||
);
|
||||
final id = parseHtml(res).selectFirst("div[data-id]").attr("data-id");
|
||||
|
||||
final resEp =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/ajax/episode/list/$id"),
|
||||
)).body;
|
||||
final html = json.decode(resEp)["html"];
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
final epsElements = parseHtml(html).select("a");
|
||||
for (var epElement in epsElements) {
|
||||
final id = epElement.attr('data-id');
|
||||
|
||||
final title = epElement.attr('title') ?? "";
|
||||
|
||||
final epNum = epElement.attr('data-number');
|
||||
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Episode $epNum $title";
|
||||
episode.url = id;
|
||||
episodesList.add(episode);
|
||||
}
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/ajax/episode/servers?episodeId=$url"),
|
||||
)).body;
|
||||
|
||||
final html = json.decode(res)["html"];
|
||||
|
||||
final serverElements = parseHtml(html).select("div.server-item");
|
||||
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
final typeSelection = preferenceTypeSelection(source.id);
|
||||
for (var serverElement in serverElements) {
|
||||
final name = serverElement.text;
|
||||
final id = serverElement.attr("data-id");
|
||||
final subDub = serverElement.attr("data-type");
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/ajax/episode/sources?id=$id"),
|
||||
)).body;
|
||||
final epUrl = json.decode(res)["link"];
|
||||
List<MVideo> a = [];
|
||||
|
||||
if (hosterSelection.contains(name) && typeSelection.contains(subDub)) {
|
||||
if (name.contains("Vidstreaming")) {
|
||||
a = await rapidCloudExtractor(epUrl, "Vidstreaming - $subDub");
|
||||
} else if (name.contains("Vidcloud")) {
|
||||
a = await rapidCloudExtractor(epUrl, "Vidcloud - $subDub");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
final elements = parseHtml(res).select("div.film_list-wrap > div");
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
MManga anime = MManga();
|
||||
anime.name = element.selectFirst("div.film-detail > h3 > a").text;
|
||||
anime.imageUrl = element.selectFirst(" div.film-poster > img").getSrc;
|
||||
anime.link = element.selectFirst("div.film-detail > h3 > a").getHref;
|
||||
animeList.add(anime);
|
||||
}
|
||||
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> rapidCloudExtractor(String url, String name) async {
|
||||
final serverUrl = ['https://megacloud.tv', 'https://rapid-cloud.co'];
|
||||
|
||||
final serverType =
|
||||
url.startsWith('https://megacloud.tv') ||
|
||||
url.startsWith('https://megacloud.club')
|
||||
? 0
|
||||
: 1;
|
||||
final sourceUrl = [
|
||||
'/embed-2/ajax/e-1/getSources?id=',
|
||||
'/ajax/embed-6-v2/getSources?id=',
|
||||
];
|
||||
final sourceSpliter = ['/e-1/', '/embed-6-v2/'];
|
||||
final id = url.split(sourceSpliter[serverType]).last.split('?').first;
|
||||
final resServer =
|
||||
(await client.get(
|
||||
Uri.parse('${serverUrl[serverType]}${sourceUrl[serverType]}$id'),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"},
|
||||
)).body;
|
||||
final encrypted = getMapValue(resServer, "encrypted");
|
||||
String videoResJson = "";
|
||||
List<MVideo> videos = [];
|
||||
if (encrypted == "true") {
|
||||
final ciphered = getMapValue(resServer, "sources");
|
||||
List<List<int>> indexPairs = await generateIndexPairs(serverType);
|
||||
var password = '';
|
||||
String ciphertext = ciphered;
|
||||
int index = 0;
|
||||
for (List<int> item in json.decode(json.encode(indexPairs))) {
|
||||
int start = item.first + index;
|
||||
int end = start + item.last;
|
||||
String passSubstr = ciphered.substring(start, end);
|
||||
password += passSubstr;
|
||||
ciphertext = ciphertext.replaceFirst(passSubstr, "");
|
||||
index += item.last;
|
||||
}
|
||||
videoResJson = decryptAESCryptoJS(ciphertext, password);
|
||||
} else {
|
||||
videoResJson = json.encode(
|
||||
(json.decode(resServer)["sources"] as List<Map<String, dynamic>>),
|
||||
);
|
||||
}
|
||||
|
||||
String masterUrl =
|
||||
((json.decode(videoResJson) as List<Map<String, dynamic>>)
|
||||
.first)['file'];
|
||||
String type =
|
||||
((json.decode(videoResJson) as List<Map<String, dynamic>>)
|
||||
.first)['type'];
|
||||
|
||||
final tracks =
|
||||
(json.decode(resServer)['tracks'] as List)
|
||||
.where((e) => e['kind'] == 'captions' ? true : false)
|
||||
.toList();
|
||||
List<MTrack> subtitles = [];
|
||||
|
||||
for (var sub in tracks) {
|
||||
try {
|
||||
MTrack subtitle = MTrack();
|
||||
subtitle
|
||||
..label = sub["label"]
|
||||
..file = sub["file"];
|
||||
subtitles.add(subtitle);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
if (type == "hls") {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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 = "$name - $quality"
|
||||
..subtitles = subtitles;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "$name - Default"
|
||||
..subtitles = subtitles;
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<List<int>>> generateIndexPairs(int serverType) async {
|
||||
final jsPlayerUrl = [
|
||||
"https://megacloud.tv/js/player/a/prod/e1-player.min.js",
|
||||
"https://rapid-cloud.co/js/player/prod/e6-player-v2.min.js",
|
||||
];
|
||||
final scriptText =
|
||||
(await client.get(Uri.parse(jsPlayerUrl[serverType]))).body;
|
||||
|
||||
final switchCode = scriptText.substring(
|
||||
scriptText.lastIndexOf('switch'),
|
||||
scriptText.indexOf('=partKey'),
|
||||
);
|
||||
|
||||
List<int> indexes = [];
|
||||
for (var variableMatch
|
||||
in RegExp(r'=(\w+)').allMatches(switchCode).toList()) {
|
||||
final regex = RegExp(
|
||||
',${(variableMatch as RegExpMatch).group(1)}=((?:0x)?([0-9a-fA-F]+))',
|
||||
);
|
||||
Match? match = regex.firstMatch(scriptText);
|
||||
|
||||
if (match != null) {
|
||||
String value = match.group(1);
|
||||
if (value.contains("0x")) {
|
||||
indexes.add(int.parse(substringAfter(value, "0x"), radix: 16));
|
||||
} else {
|
||||
indexes.add(int.parse(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chunked(indexes, 2);
|
||||
}
|
||||
|
||||
List<List<int>> chunked(List<int> list, int size) {
|
||||
List<List<int>> chunks = [];
|
||||
for (int i = 0; i < list.length; i += size) {
|
||||
int end = list.length;
|
||||
if (i + size < list.length) {
|
||||
end = i + size;
|
||||
}
|
||||
chunks.add(list.sublist(i, end));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "1"),
|
||||
CheckBoxFilter("Adventure", "2"),
|
||||
CheckBoxFilter("Cars", "3"),
|
||||
CheckBoxFilter("Comedy", "4"),
|
||||
CheckBoxFilter("Dementia", "5"),
|
||||
CheckBoxFilter("Demons", "6"),
|
||||
CheckBoxFilter("Drama", "8"),
|
||||
CheckBoxFilter("Ecchi", "9"),
|
||||
CheckBoxFilter("Fantasy", "10"),
|
||||
CheckBoxFilter("Game", "11"),
|
||||
CheckBoxFilter("Harem", "35"),
|
||||
CheckBoxFilter("Historical", "13"),
|
||||
CheckBoxFilter("Horror", "14"),
|
||||
CheckBoxFilter("Isekai", "44"),
|
||||
CheckBoxFilter("Josei", "43"),
|
||||
CheckBoxFilter("Kids", "15"),
|
||||
CheckBoxFilter("Magic", "16"),
|
||||
CheckBoxFilter("Martial Arts", "17"),
|
||||
CheckBoxFilter("Mecha", "18"),
|
||||
CheckBoxFilter("Military", "38"),
|
||||
CheckBoxFilter("Music", "19"),
|
||||
CheckBoxFilter("Mystery", "7"),
|
||||
CheckBoxFilter("Parody", "20"),
|
||||
CheckBoxFilter("Police", "39"),
|
||||
CheckBoxFilter("Psychological", "40"),
|
||||
CheckBoxFilter("Romance", "22"),
|
||||
CheckBoxFilter("Samurai", "21"),
|
||||
CheckBoxFilter("School", "23"),
|
||||
CheckBoxFilter("Sci-Fi", "24"),
|
||||
CheckBoxFilter("Seinen", "42"),
|
||||
CheckBoxFilter("Shoujo", "25"),
|
||||
CheckBoxFilter("Shoujo Ai", "26"),
|
||||
CheckBoxFilter("Shounen", "27"),
|
||||
CheckBoxFilter("Shounen Ai", "28"),
|
||||
CheckBoxFilter("Slice of Life", "36"),
|
||||
CheckBoxFilter("Space", "29"),
|
||||
CheckBoxFilter("Sports", "30"),
|
||||
CheckBoxFilter("Super Power", "31"),
|
||||
CheckBoxFilter("Supernatural", "37"),
|
||||
CheckBoxFilter("Thriller", "41"),
|
||||
CheckBoxFilter("Vampire", "32"),
|
||||
]),
|
||||
GroupFilter("SeasonFilter", "Season", [
|
||||
CheckBoxFilter("Fall", "3"),
|
||||
CheckBoxFilter("Summer", "2"),
|
||||
CheckBoxFilter("Spring", "1"),
|
||||
CheckBoxFilter("Winter", "4"),
|
||||
]),
|
||||
GroupFilter("YearFilter", "Year", [
|
||||
CheckBoxFilter("2024", "2024"),
|
||||
CheckBoxFilter("2023", "2023"),
|
||||
CheckBoxFilter("2022", "2022"),
|
||||
CheckBoxFilter("2021", "2021"),
|
||||
CheckBoxFilter("2020", "2020"),
|
||||
CheckBoxFilter("2019", "2019"),
|
||||
CheckBoxFilter("2018", "2018"),
|
||||
CheckBoxFilter("2017", "2017"),
|
||||
CheckBoxFilter("2016", "2016"),
|
||||
CheckBoxFilter("2015", "2015"),
|
||||
CheckBoxFilter("2014", "2014"),
|
||||
CheckBoxFilter("2013", "2013"),
|
||||
CheckBoxFilter("2012", "2012"),
|
||||
CheckBoxFilter("2011", "2011"),
|
||||
CheckBoxFilter("2010", "2010"),
|
||||
CheckBoxFilter("2009", "2009"),
|
||||
CheckBoxFilter("2008", "2008"),
|
||||
CheckBoxFilter("2007", "2007"),
|
||||
CheckBoxFilter("2006", "2006"),
|
||||
CheckBoxFilter("2005", "2005"),
|
||||
CheckBoxFilter("2004", "2004"),
|
||||
CheckBoxFilter("2003", "2003"),
|
||||
CheckBoxFilter("2002", "2002"),
|
||||
CheckBoxFilter("2001", "2001"),
|
||||
]),
|
||||
SelectFilter("SortFilter", "Sort by", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("Default", "default"),
|
||||
SelectFilterOption("Recently Added", "recently_added"),
|
||||
SelectFilterOption("Recently Updated", "recently_updated"),
|
||||
SelectFilterOption("Score", "score"),
|
||||
SelectFilterOption("Name A-Z", "name_az"),
|
||||
SelectFilterOption("Released Date", "released_date"),
|
||||
SelectFilterOption("Most Watched", "most_watched"),
|
||||
]),
|
||||
GroupFilter("TypeFilter", "Type", [
|
||||
CheckBoxFilter("Movie", "1"),
|
||||
CheckBoxFilter("TV Series", "2"),
|
||||
CheckBoxFilter("OVA", "3"),
|
||||
CheckBoxFilter("ONA", "4"),
|
||||
CheckBoxFilter("Special", "5"),
|
||||
CheckBoxFilter("Music", "6"),
|
||||
]),
|
||||
SelectFilter("StatusFilter", "Status", 0, [
|
||||
SelectFilterOption("All", "all"),
|
||||
SelectFilterOption("Finished Airing", "1"),
|
||||
SelectFilterOption("Currently Airing", "2"),
|
||||
SelectFilterOption("Not yet aired", "3"),
|
||||
]),
|
||||
GroupFilter("LanguageFilter", "Language", [
|
||||
CheckBoxFilter("Sub", "sub"),
|
||||
CheckBoxFilter("Dub", "dub"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
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"],
|
||||
entryValues: ["Vidstreaming", "VidCloud"],
|
||||
),
|
||||
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"],
|
||||
entryValues: ["Vidstreaming", "Vidcloud"],
|
||||
values: ["Vidstreaming", "Vidcloud"],
|
||||
),
|
||||
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.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(type.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(type.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
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");
|
||||
}
|
||||
|
||||
List<String> preferenceTypeSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "type_selection");
|
||||
}
|
||||
|
||||
String ll(String url) {
|
||||
if (url.contains("?")) {
|
||||
return "&";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
NineAnimeTv main(MSource source) {
|
||||
return NineAnimeTv(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get nineanimetv => _nineanimetv;
|
||||
const _nineanimetvVersion = "0.0.55";
|
||||
const _nineanimetvCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/nineanimetv.dart";
|
||||
Source _nineanimetv = Source(
|
||||
name: "9AnimeTv",
|
||||
baseUrl: "https://9animetv.to",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/nineanimetv/icon.png",
|
||||
sourceCodeUrl: _nineanimetvCodeUrl,
|
||||
version: _nineanimetvVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get uhdmoviesSource => _uhdmoviesSource;
|
||||
const _uhdmoviesVersion = "0.0.5";
|
||||
const _uhdmoviesSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/uhdmovies.dart";
|
||||
Source _uhdmoviesSource = Source(
|
||||
name: "UHD Movies",
|
||||
baseUrl: "https://uhdmovies.fans",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/uhdmovies/icon.png",
|
||||
sourceCodeUrl: _uhdmoviesSourceCodeUrl,
|
||||
version: _uhdmoviesVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,255 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class UHDMovies extends MProvider {
|
||||
UHDMovies({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain_new");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl/page/$page"))).body;
|
||||
return animeFromElement(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return MPages([], false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/page/$page/?s=${query.replaceAll(" ", "+")}"),
|
||||
)).body;
|
||||
return animeFromElement(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
url = getUrlWithoutDomain(url);
|
||||
final res = (await client.get(Uri.parse("$baseUrl${url}"))).body;
|
||||
MManga anime = MManga();
|
||||
final description = xpath(res, '//pre/span/text()');
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first;
|
||||
}
|
||||
anime.status = MStatus.ongoing;
|
||||
final episodesTitles = xpath(
|
||||
res,
|
||||
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/text()',
|
||||
);
|
||||
final episodesUrls = xpath(
|
||||
res,
|
||||
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/a[contains(@class, "maxbutton") or contains(@href, "?sid=")]/@href',
|
||||
);
|
||||
bool isSeries = false;
|
||||
if (episodesTitles.first.contains("Episode") ||
|
||||
episodesTitles.first.contains("Zip") ||
|
||||
episodesTitles.first.contains("Pack")) {
|
||||
isSeries = true;
|
||||
}
|
||||
List<MChapter>? episodesList = [];
|
||||
if (!isSeries) {
|
||||
List<String> moviesTitles = [];
|
||||
moviesTitles = xpath(
|
||||
res,
|
||||
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center")]/text()',
|
||||
);
|
||||
List<String> titles = [];
|
||||
if (moviesTitles.isEmpty) {
|
||||
moviesTitles = xpath(res, '//p[contains(@style, "center")]/text()');
|
||||
}
|
||||
for (var title in moviesTitles) {
|
||||
if (title.isNotEmpty &&
|
||||
!title.contains('Download') &&
|
||||
!title.contains('Note:') &&
|
||||
!title.contains('Copyright')) {
|
||||
titles.add(title.split('[').first.trim());
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < titles.length; i++) {
|
||||
final title = titles[i];
|
||||
final quality = RegExp(r'\d{3,4}p').firstMatch(title)?.group(0) ?? "";
|
||||
final url = episodesUrls[i];
|
||||
MChapter ep = MChapter();
|
||||
ep.name = title;
|
||||
ep.url = url;
|
||||
ep.scanlator = quality;
|
||||
episodesList.add(ep);
|
||||
}
|
||||
} else {
|
||||
List<String> seasonTitles = [];
|
||||
final episodeTitles = xpath(
|
||||
res,
|
||||
'//*[contains(@style, "center") or contains(@class, "maxbutton")]/parent::p//preceding-sibling::p[contains(@style, "center") and not(text()^="Episode")]/text()',
|
||||
);
|
||||
List<String> titles = [];
|
||||
for (var title in episodeTitles) {
|
||||
if (title.isNotEmpty) {
|
||||
titles.add(title.split('[').first.trim());
|
||||
}
|
||||
}
|
||||
int number = 0;
|
||||
for (var i = 0; i < episodesTitles.length; i++) {
|
||||
final episode = episodesTitles[i];
|
||||
final episodeUrl = episodesUrls[i];
|
||||
if (!episode.contains("Zip") || !episode.contains("Pack")) {
|
||||
if (episode == "Episode 1" && seasonTitles.contains("Episode 1")) {
|
||||
number++;
|
||||
} else if (episode == "Episode 1") {
|
||||
seasonTitles.add(episode);
|
||||
}
|
||||
final season =
|
||||
RegExp(r'S(\d{2})').firstMatch(titles[number])?.group(1) ?? "";
|
||||
final quality =
|
||||
RegExp(r'\d{3,4}p').firstMatch(titles[number])?.group(0) ?? "";
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Season $season $episode $quality";
|
||||
ep.url = episodeUrl;
|
||||
ep.scanlator = quality;
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = await getMediaUrl(url);
|
||||
return await extractVideos(res);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "pref_domain_new",
|
||||
title: "Currently used domain",
|
||||
summary: "",
|
||||
value: "https://uhdmovies.fans",
|
||||
dialogTitle: "Currently used domain",
|
||||
dialogMessage: "",
|
||||
text: "https://uhdmovies.fans",
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractVideos(String url) async {
|
||||
List<MVideo> videos = [];
|
||||
for (int type = 1; type < 3; type++) {
|
||||
url = url.replaceAll("/file/", "/wfile/") + "?type=$type";
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final links = xpath(res, '//div[@class="mb-4"]/a/@href');
|
||||
for (int i = 0; i < links.length; i++) {
|
||||
final link = links[i];
|
||||
String decodedLink = link;
|
||||
if (!link.contains("workers.dev")) {
|
||||
decodedLink = utf8.decode(
|
||||
base64Url.decode(substringAfter(link, "download?url=")),
|
||||
);
|
||||
}
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = decodedLink
|
||||
..originalUrl = decodedLink
|
||||
..quality = "CF $type Worker ${i + 1}";
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<String> getMediaUrl(String url) async {
|
||||
String res = "";
|
||||
String host = "";
|
||||
if (url.contains("?sid=")) {
|
||||
final finalUrl = await redirectorBypasser(url);
|
||||
host = Uri.parse(finalUrl).host;
|
||||
res = (await client.get(Uri.parse(finalUrl))).body;
|
||||
} else if (url.contains("r?key=")) {
|
||||
res = (await client.get(Uri.parse(url))).body;
|
||||
host = Uri.parse(url).host;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
final path = substringBefore(substringAfter(res, "replace(\""), "\"");
|
||||
if (path == "/404") return "";
|
||||
return "https://$host$path";
|
||||
}
|
||||
|
||||
Future<String> redirectorBypasser(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
String lastDoc = await recursiveDoc(url, res);
|
||||
final js = xpath(lastDoc, '//script[contains(text(), "/?go=")]/text()');
|
||||
if (js.isEmpty) return "";
|
||||
String script = js.first;
|
||||
String nextUrl = substringBefore(
|
||||
substringAfter(script, "\"href\",\""),
|
||||
'"',
|
||||
);
|
||||
if (!nextUrl.contains("http")) return "";
|
||||
String cookieName = substringAfter(nextUrl, "go=");
|
||||
String cookieValue = substringBefore(
|
||||
substringAfter(script, "'$cookieName', '"),
|
||||
"'",
|
||||
);
|
||||
final response =
|
||||
(await client.get(
|
||||
Uri.parse(nextUrl),
|
||||
headers: {"referer": url, "Cookie": "$cookieName=$cookieValue"},
|
||||
)).body;
|
||||
|
||||
final lastRes = parseHtml(
|
||||
response,
|
||||
).selectFirst("meta[http-equiv]").attr("content");
|
||||
return substringAfter(lastRes, "url=");
|
||||
}
|
||||
|
||||
MPages animeFromElement(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//*[@class="entry-image"]/a/@href');
|
||||
final names = xpath(res, '//*[@class="entry-image"]/a/@title');
|
||||
final images = xpath(res, '//*[@class="entry-image"]/a/img/@src');
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
MManga anime = MManga();
|
||||
anime.name = names[i].replaceAll("Download", "");
|
||||
anime.imageUrl = images[i];
|
||||
anime.link = urls[i];
|
||||
animeList.add(anime);
|
||||
}
|
||||
final nextPage = xpath(res, '//a[@class="next page-numbers"]/@href');
|
||||
return MPages(animeList, nextPage.isNotEmpty);
|
||||
}
|
||||
|
||||
Future<String> recursiveDoc(String url, String html) async {
|
||||
final urlR = xpath(html, '//form[@id="landing"]/@action');
|
||||
if (urlR.isEmpty) return html;
|
||||
final name = xpath(html, '//input/@name').first;
|
||||
final value = xpath(html, '//input/@value').first;
|
||||
final body = {"$name": value};
|
||||
final response =
|
||||
(await client.post(
|
||||
Uri.parse(urlR.first),
|
||||
headers: {"referer": url},
|
||||
body: body,
|
||||
)).body;
|
||||
return recursiveDoc(url, response);
|
||||
}
|
||||
}
|
||||
|
||||
UHDMovies main(MSource source) {
|
||||
return UHDMovies(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 7.4 KiB |
@@ -1,19 +0,0 @@
|
||||
// from https://github.com/MiraiEnoki/anymex-extensions/blob/main/dart/anime/src/en/vumeto
|
||||
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get vumetoSource => _vumetoSource;
|
||||
const _vumetoVersion = "0.0.55";
|
||||
const _vumetoSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/vumeto/vumeto.dart";
|
||||
Source _vumetoSource = Source(
|
||||
name: "Vumeto",
|
||||
baseUrl: "https://vumeto.com",
|
||||
lang: "en",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/vumeto/icon.png",
|
||||
sourceCodeUrl: _vumetoSourceCodeUrl,
|
||||
version: _vumetoVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,336 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class Vumeto extends MProvider {
|
||||
Vumeto({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => true;
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {
|
||||
"Cookie":
|
||||
"_ga=GA1.1.2064759276.1741681027; _ga_5HMNDC3ZE4=GS1.1.1741824276.8.1.1741824749.0.0.0",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
|
||||
"Referer": "https://vumeto.com/",
|
||||
};
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final url = 'https://vumeto.com/most-popular';
|
||||
final resp = await client.get(Uri.parse(url));
|
||||
|
||||
final document = parseHtml(resp.body);
|
||||
|
||||
return MPages(scrapeAnimeList(document), false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final url = 'https://vumeto.com/recently-updated';
|
||||
final resp = await client.get(Uri.parse(url));
|
||||
|
||||
final document = parseHtml(resp.body);
|
||||
|
||||
return MPages(scrapeAnimeList(document), false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final url = 'https://vumeto.com/search?q=$query';
|
||||
final resp = await client.get(Uri.parse(url));
|
||||
|
||||
final document = parseHtml(resp.body);
|
||||
|
||||
return MPages(scrapeAnimeList(document), false);
|
||||
}
|
||||
|
||||
String fixUrl(String encodedUrl) {
|
||||
return encodedUrl
|
||||
.split('url=')
|
||||
.last
|
||||
.split('&w')
|
||||
.first
|
||||
.replaceAll('%3A', ':')
|
||||
.replaceAll('%2F', '/')
|
||||
.replaceAll('%3F', '?')
|
||||
.replaceAll('%3D', '=')
|
||||
.replaceAll('%26', '&');
|
||||
}
|
||||
|
||||
List<MManga> scrapeAnimeList(MDocument document) {
|
||||
List<MElement>? animeElements = document.getElementsByClassName(
|
||||
'relative group border-0',
|
||||
);
|
||||
List<MManga> results = [];
|
||||
|
||||
if (animeElements != null) {
|
||||
for (var anime in animeElements) {
|
||||
String? title = anime.selectFirst('h3')?.text ?? '';
|
||||
String? animeUrl = anime.selectFirst('a')?.attr('href') ?? '';
|
||||
String? imageUrl = anime.selectFirst('img')?.attr('src') ?? '';
|
||||
|
||||
MManga manga = MManga();
|
||||
manga.name = title;
|
||||
manga.link =
|
||||
"https://vumeto.com/watch/" +
|
||||
animeUrl.replaceAll('/info/', '').split('/').first +
|
||||
'?ep=1';
|
||||
manga.imageUrl = fixUrl(imageUrl.split('url=').last);
|
||||
|
||||
results.add(manga);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Releasing": 0, "Finished": 1},
|
||||
];
|
||||
|
||||
final uri = Uri.parse(url);
|
||||
final resp = await client.get(uri, headers);
|
||||
final document = parseHtml(resp.body);
|
||||
|
||||
final description =
|
||||
document.selectFirst("meta[name='description']").attr("content") ?? '';
|
||||
|
||||
MStatus status = MStatus.unknown;
|
||||
final statusStart = resp.body.indexOf(
|
||||
":",
|
||||
resp.body.indexOf("\\\"status\\\""),
|
||||
);
|
||||
final statusEnd = resp.body.indexOf("\\\",", statusStart);
|
||||
if (statusStart != -1 && statusEnd != -1) {
|
||||
final rawStatus = resp.body.substring(statusStart + 1, statusEnd);
|
||||
status = parseStatus(rawStatus.replaceAll("\\\"", ""), statusList);
|
||||
}
|
||||
|
||||
final genresStart = resp.body.indexOf(
|
||||
"[",
|
||||
resp.body.indexOf("\\\"genres\\\":"),
|
||||
);
|
||||
final genresEnd = resp.body.indexOf("]", genresStart);
|
||||
var genres = [];
|
||||
if (genresStart != -1 && genresEnd != -1) {
|
||||
final genreLinks = resp.body
|
||||
.substring(genresStart + 1, genresEnd)
|
||||
.split(",");
|
||||
genres = genreLinks.map((String e) => e.replaceAll("\\\"", "")).toList();
|
||||
}
|
||||
|
||||
List<MChapter> chapters = [];
|
||||
final scripts = document.getElementsByTagName("script");
|
||||
|
||||
String jsonData = "";
|
||||
for (var script in scripts!) {
|
||||
if (script.text!.contains("episodesData")) {
|
||||
final regex = RegExp(
|
||||
r'self\.__next_f\.push\(\[1,".*?",null,(.*?)\]\)',
|
||||
dotAll: true,
|
||||
);
|
||||
final match = regex.firstMatch(script.text!);
|
||||
|
||||
if (match != null && match.groupCount >= 1) {
|
||||
String cleaned = match.group(1)!.replaceAll(r'\', '');
|
||||
|
||||
jsonData = cleaned.substring(0, cleaned.length - 3);
|
||||
break;
|
||||
} else {
|
||||
print("Regex did not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<String, dynamic> parsedData = json.decode(jsonData);
|
||||
List<dynamic> episodesData = parsedData['episodesData'];
|
||||
for (var ep in episodesData) {
|
||||
MChapter ch = MChapter();
|
||||
final number =
|
||||
(ep?['episodeNo'] ?? episodesData.indexOf(ep) + 1).toString();
|
||||
|
||||
ch.name = "Episode $number";
|
||||
|
||||
ch.url = url.split('?').first + '?ep=$number';
|
||||
|
||||
if (!chapters.any((c) => c.name == ch.name)) {
|
||||
chapters.add(ch);
|
||||
}
|
||||
}
|
||||
|
||||
MManga result = MManga();
|
||||
result.description = description;
|
||||
result.status = status;
|
||||
result.genre = genres;
|
||||
result.chapters = chapters.reversed.toList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String stripTags(String htmlString) {
|
||||
final RegExp exp = RegExp(
|
||||
r'<[^>]*>',
|
||||
multiLine: true,
|
||||
caseSensitive: false,
|
||||
);
|
||||
return htmlString.replaceAll(exp, '').trim();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
try {
|
||||
final resp = await client.get(Uri.parse(url), headers);
|
||||
|
||||
final document = parseHtml(resp.body);
|
||||
|
||||
final scripts = document.getElementsByTagName("script");
|
||||
if (scripts.isEmpty) {
|
||||
print("No <script> tags found.");
|
||||
return [];
|
||||
}
|
||||
|
||||
String jsonData = "";
|
||||
for (var script in scripts) {
|
||||
if (script.text.contains("episodesData")) {
|
||||
final regex = RegExp(
|
||||
r'self\.__next_f\.push\(\[1,".*?",null,(.*?)\]\)',
|
||||
dotAll: true,
|
||||
);
|
||||
final match = regex.firstMatch(script.text);
|
||||
if (match != null && match.groupCount >= 1) {
|
||||
String cleaned = match.group(1)!.replaceAll(r'\', '');
|
||||
jsonData = cleaned.substring(0, cleaned.length - 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonData.isEmpty) {
|
||||
print("Could not find video data in any script tag.");
|
||||
return [];
|
||||
}
|
||||
|
||||
final Map<String, dynamic> parsedData = json.decode(jsonData);
|
||||
final List<dynamic> episodesData = parsedData['episodesData'];
|
||||
|
||||
List<Map<String, dynamic>> extractedData = [];
|
||||
|
||||
if (episodesData.isNotEmpty) {
|
||||
final index = int.parse(url.split('ep=').last);
|
||||
final episode = episodesData[index - 1];
|
||||
|
||||
for (var sub in episode['sub']) {
|
||||
for (var source in sub['sources']) {
|
||||
String quality = "AUTO";
|
||||
if (stringContains(sub['serverName'], 'Kiwi')) {
|
||||
quality = '${source['quality'].toString()}P';
|
||||
}
|
||||
final String serverName =
|
||||
"${sub['serverName']}- SUB - ${quality ?? "AUTO"}";
|
||||
final String m3u8Url = source['url'];
|
||||
|
||||
List<Map<String, dynamic>> subtitles = [];
|
||||
for (var track in sub['tracks'] ?? []) {
|
||||
if (track['kind'] == "captions") {
|
||||
final String label =
|
||||
(track['label'] is String)
|
||||
? track['label'].toString()
|
||||
: 'Unknown';
|
||||
subtitles.add({
|
||||
'url': track['file'],
|
||||
'default': track['default'] == true,
|
||||
"label": label,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (source['isProxy'] == true || source['isProxy'] == 'true') {
|
||||
m3u8Url = 'https://proxy.vumeto.com/fetch?url=' + m3u8Url;
|
||||
}
|
||||
|
||||
extractedData.add({
|
||||
'serverName': serverName,
|
||||
'm3u8Url': m3u8Url,
|
||||
'subtitles': subtitles,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (var dub in episode['dub']) {
|
||||
for (var source in dub['sources']) {
|
||||
String quality = 'AUTO';
|
||||
if (stringContains(dub['serverName'], 'Kiwi')) {
|
||||
quality = '${source['quality'].toString()}P';
|
||||
}
|
||||
final String serverName =
|
||||
"${dub['serverName']}- DUB - ${quality ?? "AUTO"}";
|
||||
final String m3u8Url = source['url'];
|
||||
|
||||
List<Map<String, dynamic>> subtitles = [];
|
||||
if (source['isProxy'] == true || source['isProxy'] == 'true') {
|
||||
m3u8Url = 'https://proxy.vumeto.com/fetch?url=' + m3u8Url;
|
||||
}
|
||||
extractedData.add({
|
||||
'serverName': serverName,
|
||||
'm3u8Url': m3u8Url,
|
||||
'subtitles': subtitles,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
List<MVideo> data =
|
||||
extractedData.map((videoData) {
|
||||
MVideo video = MVideo();
|
||||
|
||||
video.url = videoData['m3u8Url'] ?? '';
|
||||
video.url = video.url.replaceAll(
|
||||
RegExp(r'\b[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+\.net\b'),
|
||||
'stormywind74.xyz',
|
||||
);
|
||||
video.quality = videoData['serverName'] ?? '';
|
||||
video.originalUrl = videoData['m3u8Url'] ?? '';
|
||||
|
||||
List<MTrack>? subtitles =
|
||||
(videoData['subtitles'] as List<dynamic>?)?.map((subtitle) {
|
||||
MTrack track = MTrack();
|
||||
return track
|
||||
..file = subtitle['url'] ?? ''
|
||||
..label = subtitle['label'];
|
||||
}).toList();
|
||||
|
||||
video.subtitles = subtitles;
|
||||
|
||||
return video;
|
||||
}).toList();
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
print("Error in getVideoList: $e");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
bool stringContains(String text, String search) {
|
||||
return RegExp(search, caseSensitive: false).hasMatch(text);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
|
||||
Vumeto main(MSource source) {
|
||||
return Vumeto(source: source);
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeOnlineNinja extends MProvider {
|
||||
AnimeOnlineNinja({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/tendencias"))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String pageStr = page == 1 ? "" : "page/$page/";
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/$pageStr?s=${query.replaceAll(" ", "+")}",
|
||||
),
|
||||
)).body;
|
||||
return parseAnimeList(
|
||||
res,
|
||||
selector: "div.result-item div.image a",
|
||||
hasNextPage:
|
||||
parseHtml(res)
|
||||
.selectFirst(
|
||||
"div.pagination > *:last-child:not(span):not(.current)",
|
||||
)
|
||||
?.text !=
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
MManga anime = MManga();
|
||||
final document = parseHtml(res);
|
||||
anime.description = document.selectFirst("div#info").text;
|
||||
anime.genre =
|
||||
document
|
||||
.selectFirst("div.sheader")
|
||||
.select("div.data > div.sgeneros > a")
|
||||
.map((e) => e.text)
|
||||
.toList();
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
final seasonElements = document.select("div#seasons > div");
|
||||
if (seasonElements.isEmpty) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Película";
|
||||
episode.url = getUrlWithoutDomain(url);
|
||||
episodesList.add(episode);
|
||||
} else {
|
||||
for (var seasonElement in seasonElements) {
|
||||
final seasonName = seasonElement.selectFirst("span.se-t").text;
|
||||
for (var epElement in seasonElement.select("ul.episodios > li")) {
|
||||
final href = epElement.selectFirst("a[href]");
|
||||
final epNum = epElement.selectFirst('div.numerando')?.text ?? "0 - 0";
|
||||
MChapter episode = MChapter();
|
||||
episode.name =
|
||||
"Season $seasonName x ${substringAfter(epNum, '- ')} ${href.text}";
|
||||
episode.url = getUrlWithoutDomain(href!.getHref);
|
||||
episodesList.add(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("${source.baseUrl}$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
final players = document.select("ul#playeroptionsul li");
|
||||
List<MVideo> videos = [];
|
||||
for (var player in players) {
|
||||
final name = player.selectFirst("span.title").text;
|
||||
final type = player.attr("data-type");
|
||||
final id = player.attr("data-post");
|
||||
final num = player.attr("data-nume");
|
||||
final resUrl =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/wp-json/dooplayer/v1/post/$id?type=$type&source=$num",
|
||||
),
|
||||
)).body;
|
||||
final url = substringBefore(
|
||||
substringAfter(resUrl, "\"embed_url\":\""),
|
||||
"\",",
|
||||
).replaceAll("\\", "");
|
||||
videos.addAll(await extractVideos(url, name));
|
||||
}
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractVideos(String url, String lang) async {
|
||||
List<MVideo> videos = [];
|
||||
List<MVideo> a = [];
|
||||
if (url.contains("saidochesto.top") || lang == "MULTISERVER") {
|
||||
return await extractFromMulti(url);
|
||||
} else if (["filemoon", "moon", "filemooon"].any((a) => url.contains(a))) {
|
||||
a = await filemoonExtractor(url, "$lang Filemoon - ", "");
|
||||
} else if ([
|
||||
"https://dood",
|
||||
"https://ds2play",
|
||||
"https://d0",
|
||||
].any((a) => url.contains(a))) {
|
||||
a = await doodExtractor(url, "$lang DoodStream");
|
||||
} else if (["streamtape", "stp", "stape"].any((a) => url.contains(a))) {
|
||||
a = await streamTapeExtractor(url, "$lang StreamTape");
|
||||
} else if (url.contains("uqload")) {
|
||||
a = await uqloadExtractor(url, lang);
|
||||
} else if (url.contains("wolfstream")) {
|
||||
final resUrl = (await client.get(Uri.parse(url))).body;
|
||||
final jsData =
|
||||
parseHtml(resUrl).selectFirst("script:contains(sources)").text;
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(jsData, "{file:\""),
|
||||
"\"",
|
||||
);
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang WolfStream";
|
||||
|
||||
a = [video];
|
||||
} else if ([
|
||||
"wishembed",
|
||||
"streamwish",
|
||||
"strwish",
|
||||
"wish",
|
||||
].any((a) => url.contains(a))) {
|
||||
a = await streamWishExtractor(url, "$lang StreamWish");
|
||||
} else if (url.contains("mp4upload")) {
|
||||
a = await mp4UploadExtractor(url, null, "$lang", "");
|
||||
} else if ([
|
||||
"vidhide",
|
||||
"filelions.top",
|
||||
"vid.",
|
||||
].any((a) => url.contains(a))) {
|
||||
a = await streamHideExtractor(url, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamHideExtractor(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(substringAfter(unpackJs(res), "sources:"), "file:\""),
|
||||
"src:\"",
|
||||
),
|
||||
'"',
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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 = "$prefix StreamHideVid - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> uqloadExtractor(String url, String lang) async {
|
||||
final Client client = Client(
|
||||
source,
|
||||
json.encode({"useDartHttpClient": true}),
|
||||
);
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "sources:")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(js.first, "sources: [\""),
|
||||
'"',
|
||||
);
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang Uqload"
|
||||
..headers = {"Referer": "${Uri.parse(url).origin}/"};
|
||||
return [video];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractFromMulti(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
|
||||
final prefLang = getPreferenceValue(source.id, "preferred_lang");
|
||||
String langSelector = "";
|
||||
if (prefLang.isEmpty) {
|
||||
langSelector = "div";
|
||||
} else {
|
||||
langSelector = "div.OD_$prefLang";
|
||||
}
|
||||
List<MVideo> videos = [];
|
||||
for (var element in document.select("div.ODDIV $langSelector > li")) {
|
||||
final hosterUrl = substringBefore(
|
||||
substringAfter(element.attr("onclick"), "('"),
|
||||
"')",
|
||||
);
|
||||
String lang = "";
|
||||
if (langSelector == "div") {
|
||||
lang = substringBefore(
|
||||
substringAfter(element.parent?.attr("class"), "OD_", ""),
|
||||
" ",
|
||||
);
|
||||
} else {
|
||||
lang = prefLang;
|
||||
}
|
||||
videos.addAll(await extractVideos(hosterUrl, lang));
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
MPages parseAnimeList(
|
||||
String res, {
|
||||
String selector = "article.w_item_a > a",
|
||||
bool hasNextPage = false,
|
||||
}) {
|
||||
final elements = parseHtml(res).select(selector);
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
final url = getUrlWithoutDomain(element.getHref);
|
||||
if (!url.startsWith("/episodio/")) {
|
||||
MManga anime = MManga();
|
||||
final img = element.selectFirst("img");
|
||||
anime.name = img.attr("alt");
|
||||
anime.imageUrl =
|
||||
img?.attr("data-src") ??
|
||||
img?.attr("data-lazy-src") ??
|
||||
img?.attr("srcset") ??
|
||||
img?.getSrc;
|
||||
anime.link = url;
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_lang",
|
||||
title: "Preferred language",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["SUB", "All", "ES", "LAT"],
|
||||
entryValues: ["SUB", "", "ES", "LAT"],
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_server1",
|
||||
title: "Preferred server",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: [
|
||||
"Filemoon",
|
||||
"DoodStream",
|
||||
"StreamTape",
|
||||
"Uqload",
|
||||
"WolfStream",
|
||||
"saidochesto.top",
|
||||
"VidHide",
|
||||
"StreamWish",
|
||||
"Mp4Upload",
|
||||
],
|
||||
entryValues: [
|
||||
"Filemoon",
|
||||
"DoodStream",
|
||||
"StreamTape",
|
||||
"Uqload",
|
||||
"WolfStream",
|
||||
"saidochesto.top",
|
||||
"VidHide",
|
||||
"StreamWish",
|
||||
"Mp4Upload",
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String prefLang = getPreferenceValue(source.id, "preferred_lang");
|
||||
String server = getPreferenceValue(sourceId, "preferred_server1");
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
|
||||
if (a.quality.toLowerCase().contains(prefLang.toLowerCase()) &&
|
||||
a.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.toLowerCase().contains(prefLang.toLowerCase()) &&
|
||||
b.quality.toLowerCase().contains(server.toLowerCase())) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
AnimeOnlineNinja main(MSource source) {
|
||||
return AnimeOnlineNinja(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animeonlineninjaSource => _animeonlineninjaSource;
|
||||
const _animeonlineninjaVersion = "0.0.35";
|
||||
const _animeonlineninjaSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/animeonlineninja.dart";
|
||||
Source _animeonlineninjaSource = Source(
|
||||
name: "AnimeOnline.Ninja",
|
||||
baseUrl: "https://ww3.animeonline.ninja",
|
||||
lang: "es",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/es/animeonlineninja/icon.png",
|
||||
sourceCodeUrl: _animeonlineninjaSourceCodeUrl,
|
||||
version: _animeonlineninjaVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,555 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeSama extends MProvider {
|
||||
AnimeSama({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final doc = (await client.get(Uri.parse("${source.baseUrl}/#$page"))).body;
|
||||
final regex = RegExp(
|
||||
r"""^\s*carteClassique\(\s*.*?\s*,\s*"(.*?)".*\)""",
|
||||
multiLine: true,
|
||||
);
|
||||
var matches = regex.allMatches(doc).toList();
|
||||
List<List<RegExpMatch>> chunks = chunked(matches, 5);
|
||||
List<MManga> seasons = [];
|
||||
if (page > 0 && page <= chunks.length) {
|
||||
for (RegExpMatch match in chunks[page - 1]) {
|
||||
seasons.addAll(
|
||||
await fetchAnimeSeasons(
|
||||
"${source.baseUrl}/catalogue/${match.group(1)}",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return MPages(seasons, page < chunks.length);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).body;
|
||||
var document = parseHtml(res);
|
||||
final latest =
|
||||
document
|
||||
.select("h2")
|
||||
.where(
|
||||
(MElement e) => e.outerHtml.toLowerCase().contains(
|
||||
"derniers épisodes ajoutés",
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
final seasonElements =
|
||||
(latest.first.parent.nextElementSibling as MElement)
|
||||
.select("div")
|
||||
.toList();
|
||||
List<MManga> seasons = [];
|
||||
for (var seasonElement in seasonElements) {
|
||||
seasons.addAll(
|
||||
await fetchAnimeSeasons(
|
||||
(seasonElement as MElement).getElementsByTagName("a").first.getHref,
|
||||
),
|
||||
);
|
||||
}
|
||||
return MPages(seasons, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/catalogue/listing_all.php"),
|
||||
)).body;
|
||||
var databaseElements = parseHtml(res).select(".cardListAnime");
|
||||
List<MElement> elements = [];
|
||||
elements =
|
||||
databaseElements
|
||||
.where(
|
||||
(MElement element) => element
|
||||
.select("h1, p")
|
||||
.any(
|
||||
(MElement e) => e.text.toLowerCase().contains(
|
||||
query.toLowerCase().trim(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "TypeFilter") {
|
||||
final types = (filter.state as List).where((e) => e.state).toList();
|
||||
elements =
|
||||
elements
|
||||
.where(
|
||||
(MElement element) =>
|
||||
types.isEmpty ||
|
||||
types.any((p) => element.className.contains(p.value)),
|
||||
)
|
||||
.toList();
|
||||
} else if (filter.type == "LanguageFilter") {
|
||||
final language = (filter.state as List).where((e) => e.state).toList();
|
||||
elements =
|
||||
elements
|
||||
.where(
|
||||
(MElement element) =>
|
||||
language.isEmpty ||
|
||||
language.any((p) => element.className.contains(p.value)),
|
||||
)
|
||||
.toList();
|
||||
} else if (filter.type == "GenreFilter") {
|
||||
final included =
|
||||
(filter.state as List)
|
||||
.where((e) => e.state == 1 ? true : false)
|
||||
.toList();
|
||||
final excluded =
|
||||
(filter.state as List)
|
||||
.where((e) => e.state == 2 ? true : false)
|
||||
.toList();
|
||||
if (included.isNotEmpty) {
|
||||
elements =
|
||||
elements
|
||||
.where(
|
||||
(MElement element) => included.every(
|
||||
(p) => element.className.contains(p.value),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
if (excluded.isNotEmpty) {
|
||||
elements =
|
||||
elements
|
||||
.where(
|
||||
(MElement element) => excluded.every(
|
||||
(p) => element.className.contains(p.value),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
List<List<MElement>> chunks = chunked(elements, 5);
|
||||
if (chunks.isEmpty) return MPages([], false);
|
||||
List<MManga> seasons = [];
|
||||
for (var seasonElement in chunks[page - 1]) {
|
||||
seasons.addAll(
|
||||
await fetchAnimeSeasons(
|
||||
seasonElement.getElementsByTagName("a").first.getHref,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return MPages(seasons, page < chunks.length);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
var animeUrl =
|
||||
"${source.baseUrl}${substringBeforeLast(getUrlWithoutDomain(url), "/")}";
|
||||
var movie = int.tryParse(
|
||||
url.split("#").length >= 2 ? url.split("#")[1] : "",
|
||||
);
|
||||
List<Map<String, dynamic>> playersList = [];
|
||||
for (var lang in ["vostfr", "vf"]) {
|
||||
final players = await fetchPlayers("$animeUrl/$lang");
|
||||
if (players.isNotEmpty) {
|
||||
playersList.add({"players": players, "lang": lang});
|
||||
}
|
||||
}
|
||||
int maxLength = 0;
|
||||
for (var sublist in playersList) {
|
||||
for (var innerList in sublist["players"]) {
|
||||
if (innerList.length > maxLength) {
|
||||
maxLength = innerList.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var episodeNumber = 0; episodeNumber < maxLength; episodeNumber++) {
|
||||
List<String> langs = [];
|
||||
bool isVf = false;
|
||||
int iVostfr = 0;
|
||||
int iVf = 0;
|
||||
List<Map<String, dynamic>> players = [];
|
||||
for (var playerList in playersList) {
|
||||
for (var player in playerList["players"]) {
|
||||
if (player.length > episodeNumber) {
|
||||
isVf = playerList["lang"] == "vf";
|
||||
if ((isVf && iVf < 2) || (!isVf && iVostfr < 2)) {
|
||||
var lang = playerList["lang"];
|
||||
if (!langs.contains(lang)) {
|
||||
langs.add(lang);
|
||||
}
|
||||
players.add({"lang": lang, "player": player[episodeNumber]});
|
||||
isVf ? iVf++ : iVostfr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MChapter episode = MChapter();
|
||||
episode.name = movie == null ? 'Episode ${episodeNumber + 1}' : 'Film';
|
||||
episode.scanlator = langs.toSet().toList().join(', ').toUpperCase();
|
||||
episode.url = json.encode(players);
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
MManga anime = MManga();
|
||||
anime.chapters =
|
||||
movie == null ? episodesList.reversed.toList() : [episodesList[movie]];
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final players = json.decode(url);
|
||||
List<MVideo> videos = [];
|
||||
for (var player in players) {
|
||||
String lang = (player["lang"] as String).toUpperCase();
|
||||
String playerUrl = player["player"];
|
||||
List<MVideo> a = [];
|
||||
if (playerUrl.contains("sendvid")) {
|
||||
a = await sendVidExtractorr(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("vidmoly")) {
|
||||
a = await vidmolyExtractor(playerUrl, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidmolyExtractor(String url, String lang) async {
|
||||
final headers = {'Referer': 'https://vidmoly.to'};
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes = await client.get(
|
||||
Uri.parse(playlistUrl),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (masterPlaylistRes.statusCode == 200) {
|
||||
for (var it in substringAfter(
|
||||
masterPlaylistRes.body,
|
||||
"#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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "$lang Vidmoly $quality"
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
GroupFilter("TypeFilter", "Type", [
|
||||
CheckBoxFilter("Anime", "Anime"),
|
||||
CheckBoxFilter("Film", "Film"),
|
||||
CheckBoxFilter("Autres", "Autres"),
|
||||
]),
|
||||
GroupFilter("LanguageFilter", "Langue", [
|
||||
CheckBoxFilter("VF", "VF"),
|
||||
CheckBoxFilter("VOSTFR", "VOSTFR"),
|
||||
]),
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
TriStateFilter("Action", "Action"),
|
||||
TriStateFilter("Aventure", "Aventure"),
|
||||
TriStateFilter("Combats", "Combats"),
|
||||
TriStateFilter("Comédie", "Comédie"),
|
||||
TriStateFilter("Drame", "Drame"),
|
||||
TriStateFilter("Ecchi", "Ecchi"),
|
||||
TriStateFilter("École", "School-Life"),
|
||||
TriStateFilter("Fantaisie", "Fantasy"),
|
||||
TriStateFilter("Horreur", "Horreur"),
|
||||
TriStateFilter("Isekai", "Isekai"),
|
||||
TriStateFilter("Josei", "Josei"),
|
||||
TriStateFilter("Mystère", "Mystère"),
|
||||
TriStateFilter("Psychologique", "Psychologique"),
|
||||
TriStateFilter("Quotidien", "Slice-of-Life"),
|
||||
TriStateFilter("Romance", "Romance"),
|
||||
TriStateFilter("Seinen", "Seinen"),
|
||||
TriStateFilter("Shônen", "Shônen"),
|
||||
TriStateFilter("Shôjo", "Shôjo"),
|
||||
TriStateFilter("Sports", "Sports"),
|
||||
TriStateFilter("Surnaturel", "Surnaturel"),
|
||||
TriStateFilter("Tournois", "Tournois"),
|
||||
TriStateFilter("Yaoi", "Yaoi"),
|
||||
TriStateFilter("Yuri", "Yuri"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualité préférée",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"],
|
||||
),
|
||||
ListPreference(
|
||||
key: "voices_preference",
|
||||
title: "Préférence des voix",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Préférer VOSTFR", "Préférer VF"],
|
||||
entryValues: ["vostfr", "vf"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<List<MManga>> fetchAnimeSeasons(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
var document = parseHtml(res);
|
||||
String animeName = document.getElementById("titreOeuvre")?.text ?? "";
|
||||
|
||||
var seasonRegex = RegExp(r'panneauAnime\("(.*?)",\s*"(.*?)"\);');
|
||||
var scripts = document
|
||||
.select("h2 + p + div > script, h2 + div > script")
|
||||
.map((MElement element) => element.text)
|
||||
.toList()
|
||||
.join("");
|
||||
List<MManga> animeList = [];
|
||||
List<RegExpMatch> seasonRegexReg = seasonRegex.allMatches(scripts).toList();
|
||||
for (var animeIndex = 0; animeIndex < seasonRegexReg.length; animeIndex++) {
|
||||
final seasonName = seasonRegexReg[animeIndex].group(1);
|
||||
final seasonStem = seasonRegexReg[animeIndex].group(2);
|
||||
if (seasonName != "nom" && seasonStem != "url") {
|
||||
if (seasonStem.toLowerCase().contains("film")) {
|
||||
var moviesUrl = "$url/$seasonStem";
|
||||
var movies = await fetchPlayers(moviesUrl);
|
||||
if (movies.isNotEmpty) {
|
||||
var movieNameRegex = RegExp(
|
||||
"^\\s*newSPF\\(\"(.*)\"\\);",
|
||||
multiLine: true,
|
||||
);
|
||||
var moviesDoc = (await client.get(Uri.parse(moviesUrl))).body;
|
||||
List<RegExpMatch> matches =
|
||||
movieNameRegex.allMatches(moviesDoc).toList();
|
||||
|
||||
for (var i = 0; i < movies.length; i++) {
|
||||
var title = "";
|
||||
if (animeIndex == 0 && movies.length == 1) {
|
||||
title = animeName;
|
||||
} else if (matches.length > i) {
|
||||
title = "$animeName ${(matches[i]).group(1)}";
|
||||
} else if (movies.length == 1) {
|
||||
title = "$animeName Film";
|
||||
} else {
|
||||
title = "$animeName Film ${i + 1}";
|
||||
}
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()',
|
||||
) ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description =
|
||||
document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()',
|
||||
) ??
|
||||
"";
|
||||
|
||||
anime.name = title;
|
||||
anime.link = "$moviesUrl#$i";
|
||||
anime.status = MStatus.completed;
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MManga anime = MManga();
|
||||
anime.imageUrl = document.getElementById("coverOeuvre")?.getSrc;
|
||||
anime.genre = (document.xpathFirst(
|
||||
'//h2[contains(text(),"Genres")]/following-sibling::a/text()',
|
||||
) ??
|
||||
"")
|
||||
.split(",");
|
||||
anime.description =
|
||||
document.xpathFirst(
|
||||
'//h2[contains(text(),"Synopsis")]/following-sibling::p/text()',
|
||||
) ??
|
||||
"";
|
||||
anime.name =
|
||||
'$animeName ${substringBefore(seasonName, ',').replaceAll('"', "")}';
|
||||
anime.link = "$url/$seasonStem";
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
}
|
||||
return animeList;
|
||||
}
|
||||
|
||||
Future<List<List<String>>> fetchPlayers(String url) async {
|
||||
var docUrl = "$url/episodes.js";
|
||||
List<List<String>> players = [];
|
||||
var response = (await client.get(Uri.parse(docUrl))).body;
|
||||
|
||||
if (response == "error") {
|
||||
return [];
|
||||
}
|
||||
|
||||
var sanitizedDoc = sanitizeEpisodesJs(response);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
final numPlayers = getPlayers("eps$i", sanitizedDoc);
|
||||
|
||||
if (numPlayers != null) players.add(numPlayers);
|
||||
}
|
||||
|
||||
final asPlayers = getPlayers("epsAS", sanitizedDoc);
|
||||
if (asPlayers != null) players.add(asPlayers);
|
||||
|
||||
if (players.isEmpty) return [];
|
||||
List<List<String>> finalPlayers = [];
|
||||
for (var i = 0; i <= players[0].length; i++) {
|
||||
for (var playerList in players) {
|
||||
if (playerList.length > i) {
|
||||
finalPlayers.add(playerList);
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalPlayers.toSet().toList();
|
||||
}
|
||||
|
||||
List<String>? getPlayers(String playerName, String doc) {
|
||||
var playerRegex = RegExp('$playerName\\s*=\\s*(\\[.*?\\])', dotAll: true);
|
||||
var match = playerRegex.firstMatch(doc);
|
||||
if (match == null) return null;
|
||||
final regex = RegExp(r"""https?://[^\s\',\[\]]+""");
|
||||
final matches = regex.allMatches(match.group(1));
|
||||
List<String> urls = [];
|
||||
for (var match in matches.toList()) {
|
||||
urls.add((match as RegExpMatch).group(0).toString());
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
String sanitizeEpisodesJs(String doc) {
|
||||
return doc.replaceAll(
|
||||
RegExp(r'(?<=\[|\,)\s*\"\s*(https?://[^\s\"]+)\s*\"\s*(?=\,|\])'),
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
List<List<dynamic>> chunked(List<dynamic> list, int size) {
|
||||
List<List<dynamic>> chunks = [];
|
||||
for (int i = 0; i < list.length; i += size) {
|
||||
int end = list.length;
|
||||
if (i + size < list.length) {
|
||||
end = i + size;
|
||||
}
|
||||
chunks.add(list.sublist(i, end));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
String voice = getPreferenceValue(sourceId, "voices_preference");
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(voice)) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(voice)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
AnimeSama main(MSource source) {
|
||||
return AnimeSama(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.6 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesamaSource => _animesama;
|
||||
const animesamaVersion = "0.0.45";
|
||||
const animesamaCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesama/animesama.dart";
|
||||
Source _animesama = Source(
|
||||
name: "Anime-Sama",
|
||||
baseUrl: "https://anime-sama.fr",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesama/icon.png",
|
||||
sourceCodeUrl: animesamaCodeUrl,
|
||||
version: animesamaVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,247 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimesUltra extends MProvider {
|
||||
AnimesUltra({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => source.baseUrl;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(baseUrl))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(
|
||||
res,
|
||||
'//*[contains(@class,"swiper-slide item-qtip")]/div[@class="item"]/a/@href',
|
||||
);
|
||||
final names = xpath(
|
||||
res,
|
||||
'//*[contains(@class,"swiper-slide item-qtip")]/div[@class="item"]/a/img/@title',
|
||||
);
|
||||
final images = xpath(
|
||||
res,
|
||||
'//*[contains(@class,"swiper-slide item-qtip")]/div[@class="item"]/a/img/@data-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);
|
||||
}
|
||||
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = (await client.get(Uri.parse(baseUrl))).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(
|
||||
res,
|
||||
'//*[@class="block_area block_area_home"]/div[@class="tab-content"]/div[contains(@class,"block_area-content block_area-list")]/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]/a/@href',
|
||||
);
|
||||
final names = xpath(
|
||||
res,
|
||||
'//*[@class="block_area block_area_home"]/div[@class="tab-content"]/div[contains(@class,"block_area-content block_area-list")]/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]/a/@title',
|
||||
);
|
||||
final images = xpath(
|
||||
res,
|
||||
'//*[@class="block_area block_area_home"]/div[@class="tab-content"]/div[contains(@class,"block_area-content block_area-list")]/div[@class="film_list-wrap"]/div[@class="flw-item"]/div[@class="film-poster"]/img/@data-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);
|
||||
}
|
||||
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
query = query.trim().replaceAll(" ", "+");
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"$baseUrl/index.php?do=search&subaction=search&story=$query",
|
||||
),
|
||||
)).body;
|
||||
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//*[@class="film-poster"]/a/@href');
|
||||
final names = xpath(res, '//*[@class="film-poster"]/a/@title');
|
||||
final images = xpath(res, '//*[@class="film-poster"]/img/@data-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);
|
||||
}
|
||||
|
||||
return MPages(animeList.reversed.toList(), false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"En cours": 0, "Terminé": 1},
|
||||
];
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
var anime = MManga();
|
||||
final doc = parseHtml(res);
|
||||
anime.description =
|
||||
xpath(res, '//*[@class="film-description m-hide"]/text()').first;
|
||||
|
||||
final status =
|
||||
xpath(
|
||||
res,
|
||||
'//*[@class="item item-title" and contains(text(),"Status:")]/span[2]/text()',
|
||||
).first;
|
||||
anime.status = parseStatus(status, statusList);
|
||||
anime.genre = xpath(
|
||||
res,
|
||||
'//*[@class="item item-list" and contains(text(),"Genres:")]/a/text()',
|
||||
);
|
||||
anime.author = doc.xpathFirst(
|
||||
'//*[@class="item item-title" and contains(text(),"Studio:")]/span[2]/text()',
|
||||
);
|
||||
final episodesLength = int.parse(
|
||||
substringBefore(
|
||||
doc.xpathFirst('//*[@class="film-stats"]/span[7]/text()'),
|
||||
"/",
|
||||
).replaceAll("Ep", ""),
|
||||
);
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
for (var i = 0; i < episodesLength; i++) {
|
||||
var episode = MChapter();
|
||||
episode.name = "Episode ${i + 1}";
|
||||
episode.url = url.replaceAll('.html', '/episode-${i + 1}.html');
|
||||
episodesList.add(episode);
|
||||
}
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final resHtml = (await client.get(Uri.parse(url))).body;
|
||||
final id = url.split('/')[4].split('-')[0];
|
||||
final resServer =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/engine/ajax/full-story.php?newsId=$id"),
|
||||
)).body;
|
||||
|
||||
final serverIds = xpath(
|
||||
resHtml,
|
||||
'//*[@class="ps__-list"]/div/@data-server-id',
|
||||
);
|
||||
final serverNames = xpath(resHtml, '//*[@class="ps__-list"]/div/a/text()');
|
||||
List<String> serverUrls = [];
|
||||
for (var id in serverIds) {
|
||||
final serversUrls =
|
||||
xpath(
|
||||
jsonDecode(resServer)["html"],
|
||||
'//*[@id="content_player_${id}"]/text()',
|
||||
).first;
|
||||
serverUrls.add(serversUrls);
|
||||
}
|
||||
List<MVideo> videos = [];
|
||||
for (var i = 0; i < serverNames.length; i++) {
|
||||
final name = serverNames[i];
|
||||
final url = serverUrls[i];
|
||||
List<MVideo> a = [];
|
||||
if (name.contains("Sendvid")) {
|
||||
a = await sendVidExtractorr(
|
||||
url.replaceAll("https:////", "https://"),
|
||||
"",
|
||||
);
|
||||
} else if (name.contains("Sibnet")) {
|
||||
a = await sibnetExtractor(
|
||||
"https://video.sibnet.ru/shell.php?videoid=$url",
|
||||
);
|
||||
} else if (name.contains("Mytv")) {
|
||||
a = await myTvExtractor("https://www.myvi.tv/embed/$url");
|
||||
} else if (name.contains("Fmoon")) {
|
||||
a = await filemoonExtractor(url, "", "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AnimesUltra main(MSource source) {
|
||||
return AnimesUltra(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.6 KiB |
@@ -1,18 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesultraSource => _animesultraSource;
|
||||
const _animesultraVersion = "0.0.8";
|
||||
const _animesultraSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesultra/animesultra.dart";
|
||||
Source _animesultraSource = Source(
|
||||
name: "AnimesUltra",
|
||||
baseUrl: "https://w2.animesultra.net",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesultra/icon.png",
|
||||
sourceCodeUrl: _animesultraSourceCodeUrl,
|
||||
version: _animesultraVersion,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: false,
|
||||
);
|
||||
@@ -1,506 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AniZone extends MProvider {
|
||||
AniZone({required this.source});
|
||||
|
||||
final MSource source;
|
||||
final Client client = Client();
|
||||
|
||||
// Constants for the xpath
|
||||
static const String urlXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-poster"]/a/@href';
|
||||
static const String nameXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-detail"]/h3/text()';
|
||||
static const String imageXpath =
|
||||
'//*[contains(@class,"flw-item item-qtip")]/div[@class="film-poster"]/img/@data-src';
|
||||
|
||||
// Methods for fetching the manga list (popular, latest & search)
|
||||
Future<MPages> _getMangaList(String url) async {
|
||||
final doc = (await client.get(Uri.parse(url))).body;
|
||||
List<MManga> animeList = [];
|
||||
|
||||
final urls = xpath(doc, urlXpath);
|
||||
final names = xpath(doc, nameXpath);
|
||||
final images = xpath(doc, imageXpath);
|
||||
|
||||
if (urls.isEmpty || names.isEmpty || images.isEmpty) {
|
||||
return MPages([], false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return MPages(animeList, urls.isNotEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return _getMangaList("${source.baseUrl}/most-popular/?page=$page");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return _getMangaList("${source.baseUrl}/recently-added/?page=$page");
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String baseUrl = "${source.baseUrl}/filter?keyword=$query";
|
||||
|
||||
final filterHandlers = {
|
||||
"TypeFilter": "type",
|
||||
"LanguageFilter": "lang",
|
||||
"SaisonFilter": "season",
|
||||
"StatusFilter": "status",
|
||||
"GenreFilter": "genre",
|
||||
};
|
||||
|
||||
final activeFilterParams = <String, String>{};
|
||||
|
||||
for (var filter in filterList.filters) {
|
||||
final paramKey = filterHandlers[filter.type];
|
||||
if (paramKey != null && filter.state is List) {
|
||||
final selectedValues =
|
||||
(filter.state as List)
|
||||
.where((item) {
|
||||
return item.state == true && item.value != null;
|
||||
})
|
||||
.map((item) => item.value as String)
|
||||
.toList();
|
||||
|
||||
if (selectedValues.isNotEmpty) {
|
||||
activeFilterParams[paramKey] = selectedValues.join("%2C");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activeFilterParams.isNotEmpty) {
|
||||
final queryString = activeFilterParams.entries
|
||||
.map((entry) => '${Uri.encodeComponent(entry.key)}=${entry.value}')
|
||||
.join('&');
|
||||
baseUrl += '&$queryString';
|
||||
}
|
||||
|
||||
return _getMangaList("$baseUrl&page=$page");
|
||||
}
|
||||
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga anime = MManga();
|
||||
final doc = (await client.get(Uri.parse(url))).body;
|
||||
final description = xpath(doc, '//p[contains(@class,"short")]/text()');
|
||||
anime.description = description.isNotEmpty ? description.first : "";
|
||||
|
||||
final statusList = xpath(
|
||||
doc,
|
||||
'//div[contains(@class,"col2")]//div[contains(@class,"item")]//div[contains(@class,"item-content")]/text()',
|
||||
);
|
||||
if (statusList.isNotEmpty) {
|
||||
if (statusList[0] == "Terminer") {
|
||||
anime.status = MStatus.completed;
|
||||
} else if (statusList[0] == "En cours") {
|
||||
anime.status = MStatus.ongoing;
|
||||
} else {
|
||||
anime.status = MStatus.unknown;
|
||||
}
|
||||
} else {
|
||||
anime.status = MStatus.unknown;
|
||||
}
|
||||
|
||||
anime.genre = xpath(
|
||||
doc,
|
||||
'//div[contains(@class,"item")]//div[contains(@class,"item-content")]//a[contains(@href,"genre")]/text()',
|
||||
);
|
||||
|
||||
final regex = RegExp(r'(\d+)$');
|
||||
final match = regex.firstMatch(url);
|
||||
|
||||
if (match == null) {
|
||||
throw Exception('Numéro de l\'épisode non trouvé dans l\'URL.');
|
||||
}
|
||||
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/ajax/episode/list/${match.group(1)}"),
|
||||
)).body;
|
||||
|
||||
List<MChapter> episodesList = [];
|
||||
|
||||
final episodeElements = parseHtml(
|
||||
json.decode(res)["html"],
|
||||
).select(".ep-item");
|
||||
|
||||
// Associer chaque titre à son URL et récupérer les vidéos
|
||||
for (var element in episodeElements) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = element.attr("title");
|
||||
|
||||
String id = substringAfterLast(element.attr("href"), "=");
|
||||
episode.url = "${source.baseUrl}/ajax/episode/servers?episodeId=$id";
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final videoRes =
|
||||
(await client.get(
|
||||
Uri.parse(url),
|
||||
headers: {"Referer": "${source.baseUrl}/"},
|
||||
)).body;
|
||||
|
||||
final lang = xpath(
|
||||
videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/@data-type',
|
||||
);
|
||||
final links = xpath(
|
||||
videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/@data-id',
|
||||
);
|
||||
final playersNames = xpath(
|
||||
videoRes.replaceAll(r'\', ''),
|
||||
'//div[contains(@class,"item server-item")]/text()',
|
||||
);
|
||||
List<Map<String, String>> players = [];
|
||||
for (int j = 0; j < links.length; j++) {
|
||||
// schema of players https://v1.animesz.xyz/ajax/episode/servers?episodeId=(id_episode)
|
||||
// schema or url https://v1.animesz.xyz/ajax/episode/sources?id=(player_id)&epid=(id_episode)
|
||||
if (playersNames.isNotEmpty && playersNames[j] == "sibnet") {
|
||||
final playerUrl =
|
||||
"https://video.sibnet.ru/shell.php?videoid=${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "sendvid") {
|
||||
final playerUrl = "https://sendvid.com/embed/${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "VidCDN") {
|
||||
final playerUrl =
|
||||
"https://r.vidcdn.xyz/v1/api/get_sources/${links[j].replaceFirst(RegExp(r'vidcdn$'), '')}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "Voe") {
|
||||
final playerUrl = "https://voe.sx/e/${links[j]}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
} else if (playersNames.isNotEmpty && playersNames[j] == "Fmoon") {
|
||||
final playerUrl =
|
||||
"https://filemoon.sx/e/${links[j]}&data-realid=${links[j]}&epid=${substringAfter(url, "episodeId=")}";
|
||||
players.add({"lang": lang[j], "player": playerUrl});
|
||||
}
|
||||
}
|
||||
|
||||
List<MVideo> videos = [];
|
||||
for (var player in players) {
|
||||
String lang = (player["lang"] as String).toUpperCase();
|
||||
String playerUrl = player["player"];
|
||||
List<MVideo> a = [];
|
||||
if (playerUrl.contains("sendvid")) {
|
||||
a = await sendVidExtractorr(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("sibnet.ru")) {
|
||||
a = await sibnetExtractor(playerUrl, lang);
|
||||
} else if (playerUrl.contains("voe.sx")) {
|
||||
a = await voeExtractor(playerUrl, "$lang ");
|
||||
} else if (playerUrl.contains("vidcdn")) {
|
||||
a = await vidcdnExtractor(playerUrl, lang);
|
||||
} else if (playerUrl.contains("filemoon")) {
|
||||
a = await filemoonExtractor(playerUrl, "$lang Filemoon - ", "");
|
||||
} else if (playerUrl.contains("vidhide")) {
|
||||
a = await streamHideExtractor(playerUrl, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
Future<List<MVideo>> streamHideExtractor(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(substringAfter(unpackJs(res), "sources:"), "file:\""),
|
||||
"src:\"",
|
||||
),
|
||||
'"',
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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 = "$prefix StreamHideVid - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
GroupFilter("TypeFilter", "Type", [
|
||||
CheckBoxFilter("Film", "1"),
|
||||
CheckBoxFilter("Anime", "2"),
|
||||
CheckBoxFilter("OVA", "3"),
|
||||
CheckBoxFilter("ONA", "4"),
|
||||
CheckBoxFilter("Special", "5"),
|
||||
CheckBoxFilter("Music", "6"),
|
||||
]),
|
||||
GroupFilter("LanguageFilter", "Langue", [
|
||||
CheckBoxFilter("VF", "3"),
|
||||
CheckBoxFilter("VOSTFR", "4"),
|
||||
CheckBoxFilter("Multicc", "2"),
|
||||
CheckBoxFilter("EN", "1"),
|
||||
]),
|
||||
GroupFilter("SaisonFilter", "Saison", [
|
||||
CheckBoxFilter("Printemps", "1"),
|
||||
CheckBoxFilter("Été", "2"),
|
||||
CheckBoxFilter("Automne", "3"),
|
||||
CheckBoxFilter("Hiver", "4"),
|
||||
]),
|
||||
GroupFilter("StatusFilter", "Statut", [
|
||||
CheckBoxFilter("Terminés", "1"),
|
||||
CheckBoxFilter("En cours", "2"),
|
||||
CheckBoxFilter("Pas encore diffusés", "3"),
|
||||
]),
|
||||
GroupFilter("GenreFilter", "Genre", [
|
||||
CheckBoxFilter("Action", "1"),
|
||||
CheckBoxFilter("Aventure", "2"),
|
||||
CheckBoxFilter("Voitures", "3"),
|
||||
CheckBoxFilter("Comédie", "4"),
|
||||
CheckBoxFilter("Démence", "5"),
|
||||
CheckBoxFilter("Démons", "6"),
|
||||
CheckBoxFilter("Drame", "8"),
|
||||
CheckBoxFilter("Ecchi", "9"),
|
||||
CheckBoxFilter("Fantastique", "10"),
|
||||
CheckBoxFilter("Jeu", "11"),
|
||||
CheckBoxFilter("Harem", "35"),
|
||||
CheckBoxFilter("Historique", "13"),
|
||||
CheckBoxFilter("Horreur", "14"),
|
||||
CheckBoxFilter("Isekai", "44"),
|
||||
CheckBoxFilter("Josei", "43"),
|
||||
CheckBoxFilter("Enfants", "25"),
|
||||
CheckBoxFilter("Magie", "16"),
|
||||
CheckBoxFilter("Arts martiaux", "17"),
|
||||
CheckBoxFilter("Mecha", "18"),
|
||||
CheckBoxFilter("Militaire", "38"),
|
||||
CheckBoxFilter("Musique", "19"),
|
||||
CheckBoxFilter("Mystère", "7"),
|
||||
CheckBoxFilter("Parodie", "20"),
|
||||
CheckBoxFilter("Police", "39"),
|
||||
CheckBoxFilter("Psychologique", "40"),
|
||||
CheckBoxFilter("Romance", "22"),
|
||||
CheckBoxFilter("Samouraï", "21"),
|
||||
CheckBoxFilter("École", "23"),
|
||||
CheckBoxFilter("Science-Fiction", "24"),
|
||||
CheckBoxFilter("Seinen", "42"),
|
||||
CheckBoxFilter("Shoujo Ai", "26"),
|
||||
CheckBoxFilter("Shoujo", "25"),
|
||||
CheckBoxFilter("Shounen Ai", "28"),
|
||||
CheckBoxFilter("Tranche de vie", "36"),
|
||||
CheckBoxFilter("Shounen", "27"),
|
||||
CheckBoxFilter("Espace", "29"),
|
||||
CheckBoxFilter("Sports", "30"),
|
||||
CheckBoxFilter("Super Pouvoir", "31"),
|
||||
CheckBoxFilter("Surnaturel", "37"),
|
||||
CheckBoxFilter("Vampire", "32"),
|
||||
CheckBoxFilter("Yaoi", "33"),
|
||||
CheckBoxFilter("Yuri", "34"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualité préférée",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"],
|
||||
),
|
||||
ListPreference(
|
||||
key: "voices_preference",
|
||||
title: "Préférence des voix",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["Préférer VOSTFR", "Préférer VF"],
|
||||
entryValues: ["vostfr", "vf"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
String voice = getPreferenceValue(sourceId, "voices_preference");
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.contains(quality) &&
|
||||
a.quality.toLowerCase().contains(voice)) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality) &&
|
||||
b.quality.toLowerCase().contains(voice)) {
|
||||
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;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
print(masterUrl);
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidcdnExtractor(String url, String prefix) async {
|
||||
final res = await client.get(Uri.parse(url));
|
||||
if (res.statusCode != 200) {
|
||||
print("Erreur lors de la récupération de la page : ${res.statusCode}");
|
||||
return [];
|
||||
}
|
||||
final jsonResponse = jsonDecode(res.body);
|
||||
|
||||
String masterUrl = jsonResponse['sources'][0]['file'] ?? '';
|
||||
final quality = jsonResponse['quality'] ?? '';
|
||||
|
||||
List<MVideo> videos = [];
|
||||
|
||||
final masterPlaylistRes = await client.get(Uri.parse(masterUrl));
|
||||
if (masterPlaylistRes.statusCode != 200) {
|
||||
print(
|
||||
"Error lors de la récupération de la playlist M3U8 : ${masterPlaylistRes.statusCode}",
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
final masterPlaylistBody = masterPlaylistRes.body;
|
||||
|
||||
final playlistLines = masterPlaylistBody.split("\n");
|
||||
|
||||
for (int i = 0; i < playlistLines.length; i++) {
|
||||
final line = playlistLines[i];
|
||||
if (line.startsWith("#EXT-X-STREAM-INF")) {
|
||||
final resolutionLine = line.split("RESOLUTION=").last;
|
||||
final resolution = resolutionLine.split(",").first;
|
||||
final width = int.parse(resolution.split("x").first);
|
||||
final height = int.parse(resolution.split("x").last);
|
||||
|
||||
String videoQuality;
|
||||
if (height >= 1080) {
|
||||
videoQuality = "1080p";
|
||||
} else if (height >= 720) {
|
||||
videoQuality = "720p";
|
||||
} else if (height >= 480) {
|
||||
videoQuality = "480p";
|
||||
} else if (height >= 360) {
|
||||
videoQuality = "360p";
|
||||
} else {
|
||||
videoQuality = "${height}p";
|
||||
}
|
||||
|
||||
String videoUrl = playlistLines[i + 1].trim();
|
||||
|
||||
if (!videoUrl.startsWith("http")) {
|
||||
videoUrl =
|
||||
"${masterUrl.substring(0, masterUrl.lastIndexOf('/'))}/$videoUrl";
|
||||
}
|
||||
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "$prefix VidCDN:$videoQuality";
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AniZone main(MSource source) {
|
||||
return AniZone(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get aniZoneSource => _aniZoneSource;
|
||||
const _aniZoneVersion = "0.0.35";
|
||||
const _aniZoneSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/anizone/anizone.dart";
|
||||
Source _aniZoneSource = Source(
|
||||
name: "AniZone",
|
||||
baseUrl: "https://v1.animesz.xyz",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/anizone/icon.png",
|
||||
sourceCodeUrl: _aniZoneSourceCodeUrl,
|
||||
version: _aniZoneVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,409 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class FrAnime extends MProvider {
|
||||
FrAnime({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = await dataBase();
|
||||
|
||||
return animeResList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res = await dataBase();
|
||||
|
||||
List list = json.decode(res);
|
||||
return animeResList(json.encode(list.reversed.toList()));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res = await dataBase();
|
||||
|
||||
return animeSeachFetch(res, query);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MManga anime = MManga();
|
||||
String language = "vo".toString();
|
||||
if (url.contains("lang=")) {
|
||||
language = substringBefore(substringAfter(url, "lang="), "&");
|
||||
}
|
||||
String stem = substringBefore(substringAfterLast(url, "/"), "?");
|
||||
final res = await dataBase();
|
||||
|
||||
final animeByTitleOJson = databaseAnimeByTitleO(res, stem);
|
||||
final seasons = json.decode(animeByTitleOJson)["saisons"];
|
||||
|
||||
var seasonsJson = seasons.first;
|
||||
|
||||
if (url.contains("s=")) {
|
||||
int seasonNumber = int.parse(
|
||||
substringBefore(substringAfter(url, "s="), "&"),
|
||||
);
|
||||
seasonsJson = seasons[seasonNumber - 1];
|
||||
}
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
|
||||
final episodes = seasonsJson["episodes"];
|
||||
|
||||
for (int i = 0; i < episodes.length; i++) {
|
||||
final ep = episodes[i];
|
||||
|
||||
final lang = ep["lang"];
|
||||
|
||||
final vo = lang["vo"];
|
||||
final vf = lang["vf"];
|
||||
bool hasVostfr = vo["lecteurs"].isNotEmpty;
|
||||
bool hasVf = vf["lecteurs"].isNotEmpty;
|
||||
bool playerIsNotEmpty = false;
|
||||
|
||||
if (language == "vo" && hasVostfr) {
|
||||
playerIsNotEmpty = true;
|
||||
} else if (language == "vf" && hasVf) {
|
||||
playerIsNotEmpty = true;
|
||||
}
|
||||
if (playerIsNotEmpty) {
|
||||
MChapter episode = MChapter();
|
||||
episode.url = "$url&ep=${i + 1}";
|
||||
String title = ep["title"];
|
||||
episode.name = title.replaceAll('"', "");
|
||||
episodesList.add(episode);
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodesList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
String language = "vo";
|
||||
String videoBaseUrl = "https://api.franime.fr/api/anime";
|
||||
if (url.contains("lang=")) {
|
||||
language = substringBefore(substringAfter(url, "lang="), "&");
|
||||
}
|
||||
String stem = substringBefore(substringAfterLast(url, "/"), "?");
|
||||
final res = await dataBase();
|
||||
|
||||
final animeByTitleOJson = databaseAnimeByTitleO(res, stem);
|
||||
final animeId = json.decode(animeByTitleOJson)["id"];
|
||||
final seasons = json.decode(animeByTitleOJson)["saisons"];
|
||||
|
||||
var seasonsJson = seasons.first;
|
||||
|
||||
videoBaseUrl += "/$animeId/";
|
||||
|
||||
if (url.contains("s=")) {
|
||||
int seasonNumber = int.parse(
|
||||
substringBefore(substringAfter(url, "s="), "&"),
|
||||
);
|
||||
videoBaseUrl += "${seasonNumber - 1}/";
|
||||
seasonsJson = seasons[seasonNumber - 1];
|
||||
} else {
|
||||
videoBaseUrl += "0/";
|
||||
}
|
||||
final episodesJson = seasonsJson["episodes"];
|
||||
var episode = episodesJson.first;
|
||||
if (url.contains("ep=")) {
|
||||
int episodeNumber = int.parse(substringAfter(url, "ep="));
|
||||
episode = episodesJson[episodeNumber - 1];
|
||||
videoBaseUrl += "${episodeNumber - 1}";
|
||||
} else {
|
||||
videoBaseUrl += "0";
|
||||
}
|
||||
final lang = episode["lang"];
|
||||
|
||||
final vo = lang["vo"];
|
||||
final vf = lang["vf"];
|
||||
bool hasVostfr = vo["lecteurs"].isNotEmpty;
|
||||
bool hasVf = vf["lecteurs"].isNotEmpty;
|
||||
List<String> vostfrPlayers = vo["lecteurs"];
|
||||
List<String> vfPlayers = vf["lecteurs"];
|
||||
List<String> players = [];
|
||||
if (language == "vo" && hasVostfr) {
|
||||
players = vostfrPlayers;
|
||||
} else if (language == "vf" && hasVf) {
|
||||
players = vfPlayers;
|
||||
}
|
||||
|
||||
List<MVideo> videos = [];
|
||||
for (var i = 0; i < players.length; i++) {
|
||||
String apiUrl = "$videoBaseUrl/$language/$i";
|
||||
String playerName = players[i];
|
||||
|
||||
MVideo video = MVideo();
|
||||
|
||||
final playerUrl =
|
||||
(await client.get(
|
||||
Uri.parse(apiUrl),
|
||||
headers: {"Referer": "https://franime.fr/"},
|
||||
)).body;
|
||||
|
||||
List<MVideo> a = [];
|
||||
print(playerName);
|
||||
if (playerName.contains("vido")) {
|
||||
videos.add(
|
||||
video
|
||||
..url = playerUrl
|
||||
..originalUrl = playerUrl
|
||||
..quality = "FRAnime (Vido)",
|
||||
);
|
||||
} else if (playerName.contains("sendvid")) {
|
||||
a = await sendVidExtractorr(playerUrl, "");
|
||||
} else if (playerName.contains("sibnet")) {
|
||||
a = await sibnetExtractor(playerUrl);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
MPages animeResList(String res) {
|
||||
final statusList = [
|
||||
{"EN COURS": 0, "TERMINÉ": 1},
|
||||
];
|
||||
List<MManga> animeList = [];
|
||||
|
||||
var jsonResList = json.decode(res);
|
||||
|
||||
for (var animeJson in jsonResList) {
|
||||
final seasons = animeJson["saisons"];
|
||||
List<bool> vostfrListName = [];
|
||||
List<bool> vfListName = [];
|
||||
for (var season in seasons) {
|
||||
for (var episode in season["episodes"]) {
|
||||
final lang = episode["lang"];
|
||||
final vo = lang["vo"];
|
||||
final vf = lang["vf"];
|
||||
vostfrListName.add(vo["lecteurs"].isNotEmpty);
|
||||
vfListName.add(vf["lecteurs"].isNotEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
String titleO = animeJson["titleO"];
|
||||
final title = animeJson["title"];
|
||||
final genre = animeJson["themes"];
|
||||
final description = animeJson["description"];
|
||||
final status = parseStatus(animeJson["status"], statusList);
|
||||
final imageUrl = animeJson["affiche"];
|
||||
bool hasVostfr = vostfrListName.contains(true);
|
||||
bool hasVf = vfListName.contains(true);
|
||||
if (hasVostfr || hasVf) {
|
||||
for (int i = 0; i < seasons.length; i++) {
|
||||
MManga anime = MManga();
|
||||
int ind = i + 1;
|
||||
anime.genre = genre;
|
||||
anime.description = description;
|
||||
String seasonTitle = "".toString();
|
||||
String lang = "";
|
||||
if (title.isEmpty) {
|
||||
seasonTitle = titleO;
|
||||
} else {
|
||||
seasonTitle = title;
|
||||
}
|
||||
if (seasons.length > 1) {
|
||||
seasonTitle += " S$ind";
|
||||
}
|
||||
if (hasVf) {
|
||||
seasonTitle += " VF";
|
||||
lang = "vf".toString();
|
||||
}
|
||||
if (hasVostfr) {
|
||||
seasonTitle += " VOSTFR";
|
||||
lang = "vo".toString();
|
||||
}
|
||||
|
||||
anime.status = status;
|
||||
anime.name = seasonTitle;
|
||||
anime.imageUrl = imageUrl;
|
||||
anime.link =
|
||||
"/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind";
|
||||
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
MPages animeSeachFetch(String res, String query) {
|
||||
final statusList = [
|
||||
{"EN COURS": 0, "TERMINÉ": 1},
|
||||
];
|
||||
List<MManga> animeList = [];
|
||||
final jsonResList = json.decode(res);
|
||||
for (var animeJson in jsonResList) {
|
||||
MManga anime = MManga();
|
||||
|
||||
final titleO = getMapValue(json.encode(animeJson), "titleO");
|
||||
final titleAlt = getMapValue(
|
||||
json.encode(animeJson),
|
||||
"titles",
|
||||
encode: true,
|
||||
);
|
||||
final containsEn = getMapValue(
|
||||
titleAlt,
|
||||
"en",
|
||||
).toString().toLowerCase().contains(query.toLowerCase());
|
||||
final containsEnJp = getMapValue(
|
||||
titleAlt,
|
||||
"en_jp",
|
||||
).toString().toLowerCase().contains(query.toLowerCase());
|
||||
final containsJaJp = getMapValue(
|
||||
titleAlt,
|
||||
"ja_jp",
|
||||
).toString().toLowerCase().contains(query.toLowerCase());
|
||||
final containsTitleO = titleO.toLowerCase().contains(query.toLowerCase());
|
||||
|
||||
if (containsEn || containsEnJp || containsJaJp || containsTitleO) {
|
||||
final seasons = animeJson["saisons"];
|
||||
List<bool> vostfrListName = [];
|
||||
List<bool> vfListName = [];
|
||||
for (var season in seasons) {
|
||||
for (var episode in season["episodes"]) {
|
||||
final lang = episode["lang"];
|
||||
final vo = lang["vo"];
|
||||
final vf = lang["vf"];
|
||||
vostfrListName.add(vo["lecteurs"].isNotEmpty);
|
||||
vfListName.add(vf["lecteurs"].isNotEmpty);
|
||||
}
|
||||
}
|
||||
String titleO = animeJson["titleO"];
|
||||
final title = animeJson["title"];
|
||||
final genre = animeJson["themes"];
|
||||
final description = animeJson["description"];
|
||||
final status = parseStatus(animeJson["status"], statusList);
|
||||
final imageUrl = animeJson["affiche"];
|
||||
|
||||
bool hasVostfr = vostfrListName.contains(true);
|
||||
bool hasVf = vfListName.contains(true);
|
||||
if (hasVostfr || hasVf) {
|
||||
for (int i = 0; i < seasons.length; i++) {
|
||||
MManga anime = MManga();
|
||||
int ind = i + 1;
|
||||
anime.genre = genre;
|
||||
anime.description = description;
|
||||
String seasonTitle = "".toString();
|
||||
String lang = "";
|
||||
if (title.isEmpty) {
|
||||
seasonTitle = titleO;
|
||||
} else {
|
||||
seasonTitle = title;
|
||||
}
|
||||
if (seasons.length > 1) {
|
||||
seasonTitle += " S$ind";
|
||||
}
|
||||
if (hasVf) {
|
||||
seasonTitle += " VF";
|
||||
lang = "vf".toString();
|
||||
}
|
||||
if (hasVostfr) {
|
||||
seasonTitle += " VOSTFR";
|
||||
lang = "vo".toString();
|
||||
}
|
||||
|
||||
anime.status = status;
|
||||
anime.name = seasonTitle;
|
||||
anime.imageUrl = imageUrl;
|
||||
anime.link =
|
||||
"/anime/${titleO.replaceAll(RegExp("[^A-Za-z0-9 ]"), "").replaceAll(" ", "-").toLowerCase()}?lang=$lang&s=$ind";
|
||||
|
||||
animeList.add(anime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
Future<String> dataBase() async {
|
||||
return (await client.get(
|
||||
Uri.parse("https://api.franime.fr/api/animes/"),
|
||||
headers: {"Referer": "https://franime.fr/"},
|
||||
)).body;
|
||||
}
|
||||
|
||||
String databaseAnimeByTitleO(String res, String titleO) {
|
||||
final datas = json.decode(res) as List<Map<String, dynamic>>;
|
||||
for (var data in datas) {
|
||||
String title = (data["titleO"] as String).replaceAll(
|
||||
RegExp("[^A-Za-z0-9 ]"),
|
||||
"",
|
||||
);
|
||||
if (title.replaceAll(" ", "-").toLowerCase() == "${titleO}") {
|
||||
return json.encode(data);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Future<List<MVideo>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
FrAnime main(MSource source) {
|
||||
return FrAnime(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,19 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get franimeSource => _franimeSource;
|
||||
const _franimeVersion = "0.0.8";
|
||||
const _franimeSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/franime/franime.dart";
|
||||
Source _franimeSource = Source(
|
||||
name: "FrAnime",
|
||||
baseUrl: "https://franime.fr",
|
||||
apiUrl: "https://api.franime.fr",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/franime/icon.png",
|
||||
sourceCodeUrl: _franimeSourceCodeUrl,
|
||||
version: _franimeVersion,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: true,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,507 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class OtakuFr extends MProvider {
|
||||
OtakuFr({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/en-cours/page/$page"))).body;
|
||||
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(int page) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl/page/$page/"))).body;
|
||||
|
||||
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(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "";
|
||||
if (query.isNotEmpty) {
|
||||
url = "$baseUrl/toute-la-liste-affiches/page/$page/?q=$query";
|
||||
} else {
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "GenreFilter") {
|
||||
if (filter.state != 0) {
|
||||
url = "$baseUrl/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
} else if (filter.type == "SubPageFilter") {
|
||||
if (url.isEmpty) {
|
||||
if (filter.state != 0) {
|
||||
url = "$baseUrl/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
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(String url) async {
|
||||
final statusList = [
|
||||
{"En cours": 0, "Terminé": 1},
|
||||
];
|
||||
String res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl${getUrlWithoutDomain(url)}"),
|
||||
)).body;
|
||||
MManga anime = MManga();
|
||||
final originalUrl = xpath(
|
||||
res,
|
||||
'//*[@class="breadcrumb"]/li[@class="breadcrumb-item"][2]/a/@href',
|
||||
);
|
||||
if (originalUrl.isNotEmpty) {
|
||||
res = (await client.get(Uri.parse(originalUrl.first))).body;
|
||||
}
|
||||
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(String url) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl${getUrlWithoutDomain(url)}"),
|
||||
)).body;
|
||||
|
||||
final servers = parseHtml(res).select("div.tab-content iframe[src]");
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
for (var url in servers) {
|
||||
final urll = url.getSrc != "about:blank" ? url.getSrc : url.getDataSrc;
|
||||
final resServer =
|
||||
(await client.get(
|
||||
Uri.parse(fixUrl(urll)),
|
||||
headers: {"X-Requested-With": "XMLHttpRequest"},
|
||||
)).body;
|
||||
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("https://doo") &&
|
||||
hosterSelection.contains("Doodstream")) {
|
||||
a = await doodExtractor(serverUrl);
|
||||
} else if (containsVidBom(serverUrl) &&
|
||||
hosterSelection.contains("VidBom")) {
|
||||
a = await vidBomExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://ok.ru") &&
|
||||
hosterSelection.contains("Okru")) {
|
||||
a = await okruExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("upstream") &&
|
||||
hosterSelection.contains("Upstream")) {
|
||||
a = await upstreamExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("sendvid") &&
|
||||
hosterSelection.contains("Sendvid")) {
|
||||
a = await sendVidExtractorr(serverUrl, "");
|
||||
} else if (serverUrl.contains("voe.sx") &&
|
||||
hosterSelection.contains("Voe")) {
|
||||
a = await voeExtractor(serverUrl, "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String fixUrl(String url) {
|
||||
return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0);
|
||||
}
|
||||
|
||||
bool containsVidBom(String url) {
|
||||
url = url;
|
||||
final list = ["vidbam", "vadbam", "vidbom", "vidbm"];
|
||||
for (var n in list) {
|
||||
if (url.contains(n)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
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() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Changer l'url de base",
|
||||
summary: "",
|
||||
value: "https://otakufr.cc",
|
||||
dialogTitle: "Changer l'url de base",
|
||||
dialogMessage: "",
|
||||
text: "https://otakufr.cc",
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualité préférée",
|
||||
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",
|
||||
"VidBom",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
"Upstream",
|
||||
],
|
||||
entryValues: [
|
||||
"Streamwish",
|
||||
"Doodstream",
|
||||
"Sendvid",
|
||||
"VidBom",
|
||||
"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>> sendVidExtractorr(String url, String prefix) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final masterUrl = document.selectFirst("source#video_source")?.attr("src");
|
||||
if (masterUrl == null) return [];
|
||||
final masterHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(masterUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains(".m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
|
||||
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";
|
||||
}
|
||||
final videoHeaders = {
|
||||
"Accept": "*/*",
|
||||
"Host": Uri.parse(videoUrl).host,
|
||||
"Origin": "https://${Uri.parse(url).host}",
|
||||
"Referer": "https://${Uri.parse(url).host}/",
|
||||
};
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = prefix + "Sendvid:$quality"
|
||||
..headers = videoHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
} else {
|
||||
var video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = prefix + "Sendvid:default"
|
||||
..headers = masterHeaders;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> upstreamExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "m3u8")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(unpackJs(js.first), "{file:\""),
|
||||
"\"}",
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
OtakuFr main(MSource source) {
|
||||
return OtakuFr(source: source);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get otakufr => _otakufr;
|
||||
const otakufrVersion = "0.1.0";
|
||||
const otakufrCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/otakufr/otakufr.dart";
|
||||
Source _otakufr = Source(
|
||||
name: "OtakuFr",
|
||||
baseUrl: "https://otakufr.cc",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/otakufr/icon.png",
|
||||
sourceCodeUrl: otakufrCodeUrl,
|
||||
version: otakufrVersion,
|
||||
itemType: ItemType.anime,
|
||||
isFullData: false,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get yomoviesSource => _yomoviesSource;
|
||||
const _yomoviesVersion = "0.0.3";
|
||||
const _yomoviesSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/hi/yomovies/yomovies.dart";
|
||||
Source _yomoviesSource = Source(
|
||||
name: "YoMovies",
|
||||
baseUrl: "https://yomovies.boo",
|
||||
lang: "hi",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/hi/yomovies/icon.png",
|
||||
sourceCodeUrl: _yomoviesSourceCodeUrl,
|
||||
version: _yomoviesVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,373 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class YoMovies extends MProvider {
|
||||
YoMovies({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
bool get supportsLatest => false;
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
String pageNu = page == 1 ? "" : "page/$page/";
|
||||
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/most-favorites/$pageNu"))).body;
|
||||
final document = parseHtml(res);
|
||||
return animeFromElement(
|
||||
document.select("div.movies-list > div.ml-item"),
|
||||
document.selectFirst("ul.pagination > li.active + li")?.getHref != null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return MPages([], false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final filters = filterList.filters;
|
||||
String url = "";
|
||||
String pageNu = page == 1 ? "" : "/page/$page";
|
||||
if (query.isNotEmpty) {
|
||||
url = "$baseUrl$pageNu/?s=$query";
|
||||
} else {
|
||||
for (var filter in filters) {
|
||||
if (filter.type.isNotEmpty) {
|
||||
final first = filter.values[filter.state].value;
|
||||
if (first.isNotEmpty) {
|
||||
url = first;
|
||||
}
|
||||
}
|
||||
}
|
||||
url = "$baseUrl$url$pageNu";
|
||||
}
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
return animeFromElement(
|
||||
document.select("div.movies-list > div.ml-item"),
|
||||
document.selectFirst("ul.pagination > li.active + li")?.getHref != null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
url = getUrlWithoutDomain(url);
|
||||
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
MManga anime = MManga();
|
||||
var infoElement = document.selectFirst("div.mvi-content");
|
||||
anime.description = infoElement.selectFirst("p.f-desc")?.text ?? "";
|
||||
|
||||
anime.genre = xpath(
|
||||
res,
|
||||
'//div[@class="mvici-left" and contains(text(),"Genre:")]/p/a/text()',
|
||||
);
|
||||
|
||||
List<MChapter> episodeList = [];
|
||||
final seasonListElements = document.select("div#seasons > div.tvseason");
|
||||
if (seasonListElements.isEmpty) {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "Movie";
|
||||
ep.url = url;
|
||||
episodeList.add(ep);
|
||||
} else {
|
||||
for (var season in seasonListElements) {
|
||||
var seasonText = season.selectFirst("div.les-title").text.trim();
|
||||
for (var episode in season.select("div.les-content > a")) {
|
||||
var epNumber = substringAfter(episode.text.trim(), "pisode ");
|
||||
MChapter ep = MChapter();
|
||||
ep.name = "$seasonText Ep. $epNumber";
|
||||
ep.url = episode.getHref;
|
||||
|
||||
episodeList.add(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anime.chapters = episodeList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
url = getUrlWithoutDomain(url);
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
final serverElements = document.select("div.movieplay > iframe");
|
||||
List<MVideo> videos = [];
|
||||
for (var serverElement in serverElements) {
|
||||
var url = serverElement.getSrc;
|
||||
List<MVideo> a = [];
|
||||
if (url.contains("minoplres")) {
|
||||
a = await minoplresExtractor(url);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Override BaseUrl",
|
||||
summary: "",
|
||||
value: "https://yomovies.boo",
|
||||
dialogTitle: "Override BaseUrl",
|
||||
dialogMessage: "",
|
||||
text: "https://yomovies.boo",
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred quality",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> minoplresExtractor(String url) async {
|
||||
List<MVideo> videos = [];
|
||||
|
||||
final res =
|
||||
(await client.get(Uri.parse(url), headers: {"Referer": url})).body;
|
||||
final script = xpath(res, '//script[contains(text(),"sources:")]/text()');
|
||||
if (script.isEmpty) return [];
|
||||
final masterUrl = substringBefore(
|
||||
substringAfter(script.first, "file:\""),
|
||||
'"',
|
||||
);
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Minoplres - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
MPages animeFromElement(List<MElement> elements, bool hasNextPage) {
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
MManga anime = MManga();
|
||||
anime.name = element.selectFirst("div.qtip-title").text;
|
||||
anime.imageUrl =
|
||||
element.selectFirst("img[data-original]")?.attr("data-original") ??
|
||||
"";
|
||||
anime.link = element.selectFirst("a[href]").getHref;
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
HeaderFilter(
|
||||
"Note: Only one selection at a time works, and it ignores text search",
|
||||
),
|
||||
SeparatorFilter(),
|
||||
SelectFilter("BollywoodFilter", "Bollywood", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Bollywood", "/genre/bollywood"),
|
||||
SelectFilterOption("Trending", "/genre/top-rated"),
|
||||
SelectFilterOption(
|
||||
"Bollywood (2024)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2024&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Bollywood (2023)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2023&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Bollywood (2022)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2022&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Bollywood (2021)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2021&wpas=1",
|
||||
),
|
||||
]),
|
||||
SelectFilter("DualAudioFilter", "Dual Audio", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Dual Audio", "/genre/dual-audio"),
|
||||
SelectFilterOption(
|
||||
"Hollywood Dubbed",
|
||||
"/account/?ptype=post&tax_category%5B%5D=dual-audio&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"South Dubbed",
|
||||
"/account/?ptype=post&tax_category%5B%5D=dual-audio&tax_category%5B%5D=south-special&wpas=1",
|
||||
),
|
||||
]),
|
||||
SelectFilter("HollywoodFilter", "Hollywood", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Hollywood", "/genre/hollywood"),
|
||||
SelectFilterOption(
|
||||
"Hollywood (2023)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2023&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Hollywood (2022)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2022&wpas=1",
|
||||
),
|
||||
SelectFilterOption(
|
||||
"Hollywood (2021)",
|
||||
"/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2021&wpas=1",
|
||||
),
|
||||
]),
|
||||
SelectFilter("EnglishSeriesFilter", "Hindi Series", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("English Series", "/series"),
|
||||
]),
|
||||
SelectFilter("HindiSeriesFilter", "English Series", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Hindi Series", "/genre/web-series"),
|
||||
SelectFilterOption("Netflix", "/director/netflix"),
|
||||
SelectFilterOption("Amazon", "/director/amazon-prime"),
|
||||
SelectFilterOption("Altbalaji", "/director/altbalaji"),
|
||||
SelectFilterOption("Zee5", "/director/zee5"),
|
||||
SelectFilterOption("Voot", "/director/voot-originals"),
|
||||
SelectFilterOption("Mx Player", "/director/mx-player"),
|
||||
SelectFilterOption("Hotstar", "/director/hotstar"),
|
||||
SelectFilterOption("Viu", "/director/viu-originals"),
|
||||
SelectFilterOption("Sony Liv", "/director/sonyliv-original"),
|
||||
]),
|
||||
SelectFilter("GenreFilter", "Genre", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Action", "/genre/action"),
|
||||
SelectFilterOption("Adventure", "/genre/adventure"),
|
||||
SelectFilterOption("Animation", "/genre/animation"),
|
||||
SelectFilterOption("Biography", "/genre/biography"),
|
||||
SelectFilterOption("Comedy", "/genre/comedy"),
|
||||
SelectFilterOption("Crime", "/genre/crime"),
|
||||
SelectFilterOption("Drama", "/genre/drama"),
|
||||
SelectFilterOption("Music", "/genre/music"),
|
||||
SelectFilterOption("Mystery", "/genre/mystery"),
|
||||
SelectFilterOption("Family", "/genre/family"),
|
||||
SelectFilterOption("Fantasy", "/genre/fantasy"),
|
||||
SelectFilterOption("Horror", "/genre/horror"),
|
||||
SelectFilterOption("History", "/genre/history"),
|
||||
SelectFilterOption("Romance", "/genre/romantic"),
|
||||
SelectFilterOption("Science Fiction", "/genre/science-fiction"),
|
||||
SelectFilterOption("Thriller", "/genre/thriller"),
|
||||
SelectFilterOption("War", "/genre/war"),
|
||||
]),
|
||||
SelectFilter("ExtraMoviesFilter", "ExtraMovies", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("ExtraMovies", "/genre/south-special"),
|
||||
SelectFilterOption("Bengali", "/genre/bengali"),
|
||||
SelectFilterOption("Marathi", "/genre/marathi"),
|
||||
SelectFilterOption("Gujarati", "/genre/gujarati"),
|
||||
SelectFilterOption("Punjabi", "/genre/punjabi"),
|
||||
SelectFilterOption("Tamil", "/genre/tamil"),
|
||||
SelectFilterOption("Telugu", "/genre/telugu"),
|
||||
SelectFilterOption("Malayalam", "/genre/malayalam"),
|
||||
SelectFilterOption("Kannada", "/genre/kannada"),
|
||||
SelectFilterOption("Pakistani", "/genre/pakistani"),
|
||||
]),
|
||||
SelectFilter("EroticFilter", "Erotic", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Erotic", "/genre/erotic-movies"),
|
||||
]),
|
||||
SelectFilter("HotSeriesFilter", "Hot Series", 0, [
|
||||
SelectFilterOption("<select>", ""),
|
||||
SelectFilterOption("Hot Series", "/genre/tv-shows"),
|
||||
SelectFilterOption("Uncut", "/?s=uncut"),
|
||||
SelectFilterOption("Fliz Movies", "/director/fliz-movies"),
|
||||
SelectFilterOption("Nuefliks", "/director/nuefliks-exclusive"),
|
||||
SelectFilterOption("Hotshots", "/director/hotshots"),
|
||||
SelectFilterOption("Ullu Originals", "/?s=ullu"),
|
||||
SelectFilterOption("Kooku", "/director/kooku-originals"),
|
||||
SelectFilterOption("Gupchup", "/director/gupchup-exclusive"),
|
||||
SelectFilterOption("Feneomovies", "/director/feneomovies"),
|
||||
SelectFilterOption("Cinemadosti", "/director/cinemadosti"),
|
||||
SelectFilterOption("Primeflix", "/director/primeflix"),
|
||||
SelectFilterOption("Gemplex", "/director/gemplex"),
|
||||
SelectFilterOption("Rabbit", "/director/rabbit-original"),
|
||||
SelectFilterOption("HotMasti", "/director/hotmasti-originals"),
|
||||
SelectFilterOption("BoomMovies", "/director/boommovies-original"),
|
||||
SelectFilterOption("CliffMovies", "/director/cliff-movies"),
|
||||
SelectFilterOption("MastiPrime", "/director/masti-prime-originals"),
|
||||
SelectFilterOption("Ek Night Show", "/director/ek-night-show"),
|
||||
SelectFilterOption("Flixsksmovies", "/director/flixsksmovies"),
|
||||
SelectFilterOption("Lootlo", "/director/lootlo-original"),
|
||||
SelectFilterOption("Hootzy", "/director/hootzy-channel"),
|
||||
SelectFilterOption("Balloons", "/director/balloons-originals"),
|
||||
SelectFilterOption(
|
||||
"Big Movie Zoo",
|
||||
"/director/big-movie-zoo-originals",
|
||||
),
|
||||
SelectFilterOption("Bambooflix", "/director/bambooflix"),
|
||||
SelectFilterOption("Piliflix", "/director/piliflix-originals"),
|
||||
SelectFilterOption("11upmovies", "/director/11upmovies-originals"),
|
||||
SelectFilterOption("Eightshots", "/director/eightshots-originals"),
|
||||
SelectFilterOption(
|
||||
"I-Entertainment",
|
||||
"/director/i-entertainment-exclusive",
|
||||
),
|
||||
SelectFilterOption("Hotprime", "/director/hotprime-originals"),
|
||||
SelectFilterOption("BananaPrime", "/director/banana-prime"),
|
||||
SelectFilterOption("HotHitFilms", "/director/hothitfilms"),
|
||||
SelectFilterOption("Chikooflix", "/director/chikooflix-originals"),
|
||||
SelectFilterOption("Glamheart", "/?s=glamheart"),
|
||||
SelectFilterOption("Worldprime", "/director/worldprime-originals"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
YoMovies main(MSource source) {
|
||||
return YoMovies(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,200 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class NimeGami extends MProvider {
|
||||
NimeGami({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/page/$page"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//div[@class="wrapper-2-a"]/article/a/@href');
|
||||
final names = xpath(res, '//div[@class="wrapper-2-a"]/article/a/@title');
|
||||
final images = xpath(
|
||||
res,
|
||||
'//div[@class="wrapper-2-a"]/article/a/div/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);
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.baseUrl}/page/$page"))).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//div[@class="post-article"]/article/div/a/@href');
|
||||
final names = xpath(
|
||||
res,
|
||||
'//div[@class="post-article"]/article/div/a/@title',
|
||||
);
|
||||
final images = xpath(
|
||||
res,
|
||||
'//div[@class="post-article"]/article/div/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);
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/page/$page/?s=$query&post_type=post"),
|
||||
)).body;
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//div[@class="archive-a"]/article/div/a/@href');
|
||||
final names = xpath(res, '//div[@class="archive-a"]/article/h2/a/@title');
|
||||
final images = xpath(
|
||||
res,
|
||||
'//div[@class="archive-a"]/article/div/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);
|
||||
}
|
||||
return MPages(animeList, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final description = xpath(res, '//*[@id="Sinopsis"]/p/text()');
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first;
|
||||
}
|
||||
|
||||
final author = xpath(res, '//tbody/tr[5]/td[2]/text()');
|
||||
if (author.isNotEmpty) {
|
||||
anime.author = author.first;
|
||||
}
|
||||
anime.genre = xpath(res, '//tr/td[@class="info_a"]/a/text()');
|
||||
final epUrls =
|
||||
xpath(
|
||||
res,
|
||||
'//div[@class="list_eps_stream"]/li/@data',
|
||||
).reversed.toList();
|
||||
final epNums =
|
||||
xpath(res, '//div[@class="list_eps_stream"]/li/@id').reversed.toList();
|
||||
final names =
|
||||
xpath(
|
||||
res,
|
||||
'//div[@class="list_eps_stream"]/li/text()',
|
||||
).reversed.toList();
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < epUrls.length; i++) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = names[i];
|
||||
episode.url = json.encode({
|
||||
"episodeIndex": int.parse(substringAfterLast(epNums[i], '_')),
|
||||
'urls': json.decode(utf8.decode(base64Url.decode(epUrls[i]))),
|
||||
});
|
||||
episodesList.add(episode);
|
||||
}
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final resJson = json.decode(url);
|
||||
final urls = resJson["urls"];
|
||||
List<MVideo> videos = [];
|
||||
List<MVideo> a = [];
|
||||
for (var data in urls) {
|
||||
final quality = data["format"];
|
||||
for (var url in data["url"]) {
|
||||
a = await extractVideos(quality, url);
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractVideos(String quality, String url) async {
|
||||
List<MVideo> videos = [];
|
||||
if (url.contains("video.nimegami.id")) {
|
||||
final realUrl = utf8.decode(
|
||||
base64Url.decode(substringBefore(substringAfter(url, "url="), "&")),
|
||||
);
|
||||
final a = await extractHXFileVideos(realUrl, quality);
|
||||
videos.addAll(a);
|
||||
} else if (url.contains("berkasdrive") || url.contains("drive.nimegami")) {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final source = xpath(res, '//source/@src');
|
||||
if (source.isNotEmpty) {
|
||||
videos.add(toVideo(source.first, "Berkasdrive - $quality"));
|
||||
}
|
||||
} else if (url.contains("hxfile.co")) {
|
||||
final a = await extractHXFileVideos(url, quality);
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> extractHXFileVideos(String url, String quality) async {
|
||||
if (!url.contains("embed-")) {
|
||||
url = url.replaceAll(".co/", ".co/embed-") + ".html";
|
||||
}
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final script = xpath(
|
||||
res,
|
||||
'//script[contains(text(), "eval") and contains(text(), "p,a,c,k,e,d")]/text()',
|
||||
);
|
||||
if (script.isNotEmpty) {
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(
|
||||
substringAfter(unpackJs(script.first), "sources:[", ""),
|
||||
"file\":\"",
|
||||
"",
|
||||
),
|
||||
'"',
|
||||
);
|
||||
if (videoUrl.isNotEmpty) {
|
||||
return [toVideo(videoUrl, "HXFile - $quality")];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
MVideo toVideo(String videoUrl, String quality) {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = quality
|
||||
..subtitles = [];
|
||||
|
||||
return video;
|
||||
}
|
||||
}
|
||||
|
||||
NimeGami main(MSource source) {
|
||||
return NimeGami(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get nimegami => _nimegami;
|
||||
const _nimegamiVersion = "0.0.6";
|
||||
const _nimegamiCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/nimegami/nimegami.dart";
|
||||
Source _nimegami = Source(
|
||||
name: "NimeGami",
|
||||
baseUrl: "https://nimegami.id",
|
||||
lang: "id",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/nimegami/icon.png",
|
||||
sourceCodeUrl: _nimegamiCodeUrl,
|
||||
version: _nimegamiVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,172 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class OploVerz extends MProvider {
|
||||
OploVerz({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/anime-list/page/$page/?order=popular"),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/anime-list/page/$page/?order=latest"),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/anime-list/page/$page/?title=$query"),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"ongoing": 0, "completed": 1},
|
||||
];
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final status = xpath(res, '//*[@class="alternati"]/span[2]/text()');
|
||||
if (status.isNotEmpty) {
|
||||
anime.status = parseStatus(status.first, statusList);
|
||||
}
|
||||
anime.description = xpath(res, '//*[@class="desc"]/div/text()').first;
|
||||
|
||||
anime.genre = xpath(res, '//*[@class="genre-info"]/a/text()');
|
||||
final epUrls = xpath(
|
||||
res,
|
||||
'//div[@class="epsleft")]/span[@class="lchx"]/a/@href',
|
||||
);
|
||||
final names = xpath(
|
||||
res,
|
||||
'//div[@class="epsleft")]/span[@class="lchx"]/a/text()',
|
||||
);
|
||||
final dates = xpath(
|
||||
res,
|
||||
'//div[@class="epsleft")]/span[@class="date"]/text()',
|
||||
);
|
||||
final dateUploads = parseDates(dates, "dd/MM/yyyy", "id");
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < epUrls.length; i++) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = names[i];
|
||||
episode.dateUpload = dateUploads[i];
|
||||
episode.url = epUrls[i];
|
||||
episodesList.add(episode);
|
||||
}
|
||||
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final dataPost =
|
||||
xpath(
|
||||
res,
|
||||
'//*[@id="server"]/ul/li/div[contains(@id,"player-option")]/@data-post',
|
||||
).first;
|
||||
final dataNume =
|
||||
xpath(
|
||||
res,
|
||||
'//*[@id="server"]/ul/li/div[contains(@id,"player-option")]/@data-nume',
|
||||
).first;
|
||||
final dataType =
|
||||
xpath(
|
||||
res,
|
||||
'//*[@id="server"]/ul/li/div[contains(@id,"player-option")]/@data-type',
|
||||
).first;
|
||||
|
||||
final ress =
|
||||
(await client.post(
|
||||
Uri.parse("${source.baseUrl}/wp-admin/admin-ajax.php"),
|
||||
headers: {"_": "_"},
|
||||
body: {
|
||||
"action": "player_ajax",
|
||||
"post": dataPost,
|
||||
"nume": dataNume,
|
||||
"type": dataType,
|
||||
},
|
||||
)).body;
|
||||
|
||||
final playerLink =
|
||||
xpath(ress, '//iframe[@class="playeriframe"]/@src').first;
|
||||
final resPlayer = (await client.get(Uri.parse(playerLink))).body;
|
||||
var resJson = substringBefore(substringAfter(resPlayer, "= "), "<");
|
||||
var streams = json.decode(resJson)["streams"] as List<Map<String, dynamic>>;
|
||||
List<MVideo> videos = [];
|
||||
for (var stream in streams) {
|
||||
String videoUrl = stream["play_url"];
|
||||
final quality = getQuality(stream["format_id"]);
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = quality;
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String getQuality(int formatId) {
|
||||
if (formatId == 18) {
|
||||
return "Google - 360p";
|
||||
} else if (formatId == 22) {
|
||||
return "Google - 720p";
|
||||
}
|
||||
return "Unknown Resolution";
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(res, '//div[@class="relat"]/article/div/div/a/@href');
|
||||
final names = xpath(res, '//div[@class="relat"]/article/div/div/a/@title');
|
||||
final images = xpath(
|
||||
res,
|
||||
'//div[@class="relat"]/article/div/div/a/div/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 pages = xpath(res, '//div[@class="pagination"]/a/@href');
|
||||
final pageNumberCurrent = xpath(
|
||||
res,
|
||||
'//div[@class="pagination"]/span[@class="page-numbers current"]/text()',
|
||||
);
|
||||
|
||||
bool hasNextPage = true;
|
||||
if (pageNumberCurrent.isNotEmpty && pages.isNotEmpty) {
|
||||
hasNextPage = !(pages.length == int.parse(pageNumberCurrent.first));
|
||||
}
|
||||
return MPages(animeList, hasNextPage);
|
||||
}
|
||||
}
|
||||
|
||||
OploVerz main(MSource source) {
|
||||
return OploVerz(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get oploverz => _oploverz;
|
||||
const _oploverzVersion = "0.0.55";
|
||||
const _oploverzCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/oploverz/oploverz.dart";
|
||||
Source _oploverz = Source(
|
||||
name: "Oploverz",
|
||||
baseUrl: "https://oploverz.gold",
|
||||
lang: "id",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/oploverz/icon.png",
|
||||
sourceCodeUrl: _oploverzCodeUrl,
|
||||
version: _oploverzVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1,261 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class OtakuDesu extends MProvider {
|
||||
OtakuDesu({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "overrideBaseUrl");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/complete-anime/page/$page"),
|
||||
)).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/ongoing-anime/page/$page"))).body;
|
||||
return parseAnimeList(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/?s=$query&post_type=anime"),
|
||||
)).body;
|
||||
List<MManga> animeList = [];
|
||||
final images = xpath(res, '//ul[@class="chivsrc"]/li/img/@src');
|
||||
final names = xpath(res, '//ul[@class="chivsrc"]/li/h2/a/text()');
|
||||
final urls = xpath(res, '//ul[@class="chivsrc"]/li/h2/a/@href');
|
||||
|
||||
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);
|
||||
}
|
||||
return MPages(animeList, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Ongoing": 0, "Completed": 1},
|
||||
];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final status = xpath(
|
||||
res,
|
||||
'//*[@class="infozingle"]/p[contains(text(), "Status")]/text()',
|
||||
);
|
||||
if (status.isNotEmpty) {
|
||||
anime.status = parseStatus(status.first.split(':').last, statusList);
|
||||
}
|
||||
final description = xpath(res, '//*[@class="sinopc"]/text()');
|
||||
if (description.isNotEmpty) {
|
||||
anime.description = description.first;
|
||||
}
|
||||
|
||||
final genre = xpath(
|
||||
res,
|
||||
'//*[@class="infozingle"]/p[contains(text(), "Genre")]/text()',
|
||||
);
|
||||
if (genre.isNotEmpty) {
|
||||
anime.genre = genre.first.split(':').last.split(',');
|
||||
}
|
||||
|
||||
final epUrls = xpath(res, '//div[@class="episodelist"]/ul/li/span/a/@href');
|
||||
final names = xpath(res, '//div[@class="episodelist"]/ul/li/span/a/text()');
|
||||
|
||||
final dates = xpath(
|
||||
res,
|
||||
'//div[@class="episodelist"]/ul/li/span[@class="zeebr"]/text()',
|
||||
);
|
||||
final dateUploads = parseDates(dates, "d MMMM,yyyy", "id");
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 1; i < epUrls.length; i++) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = names[i];
|
||||
episode.dateUpload = dateUploads[i];
|
||||
episode.url = epUrls[i];
|
||||
episodesList.add(episode);
|
||||
}
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
List<MVideo> videos = [];
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final script =
|
||||
xpath(res, '//script[contains(text(), "{action:")]/text()').first;
|
||||
final nonceAction = substringBefore(
|
||||
substringAfter(script, "{action:\""),
|
||||
'"',
|
||||
);
|
||||
final action = substringBefore(substringAfter(script, "action:\""), '"');
|
||||
|
||||
final resNonceAction =
|
||||
(await client.post(
|
||||
Uri.parse("$baseUrl/wp-admin/admin-ajax.php"),
|
||||
headers: {"_": "_"},
|
||||
body: {"action": nonceAction},
|
||||
)).body;
|
||||
final nonce = substringBefore(substringAfter(resNonceAction, ":\""), '"');
|
||||
final mirrorstream = xpath(
|
||||
res,
|
||||
'//*[@class="mirrorstream"]/ul/li/a/@data-content',
|
||||
);
|
||||
for (var stream in mirrorstream) {
|
||||
List<MVideo> a = [];
|
||||
final decodedData = json.decode(utf8.decode(base64Url.decode(stream)));
|
||||
final q = decodedData["q"];
|
||||
final id = decodedData["id"];
|
||||
final i = decodedData["i"];
|
||||
|
||||
final res =
|
||||
(await client.post(
|
||||
Uri.parse("$baseUrl/wp-admin/admin-ajax.php"),
|
||||
headers: {"_": "_"},
|
||||
body: {"i": i, "id": id, "q": q, "nonce": nonce, "action": action},
|
||||
)).body;
|
||||
final resJson = json.decode(res);
|
||||
final html = utf8.decode(base64Url.decode(resJson["data"]));
|
||||
String url = xpath(html, '//iframe/@src').first;
|
||||
if (url.contains("yourupload")) {
|
||||
final id = substringBefore(substringAfter(url, "id="), "&");
|
||||
url = "https://yourupload.com/embed/$id";
|
||||
a = await yourUploadExtractor(url, null, "YourUpload - $q", null);
|
||||
} else if (url.contains("filelions")) {
|
||||
a = await streamWishExtractor(url, "FileLions");
|
||||
} else if (url.contains("desustream")) {
|
||||
final response = (await Client().get(Uri.parse(url)));
|
||||
final res = response.body;
|
||||
final script =
|
||||
xpath(res, '//script[contains(text(), "file")]/text()').first;
|
||||
final regex = RegExp(r'file:"(https?://[^"]+)"');
|
||||
final match = regex.firstMatch(script);
|
||||
final videoUrl = match.group(1);
|
||||
if (videoUrl.endsWith(".mp4")) {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "DesuStream - $q";
|
||||
videos.add(video);
|
||||
}
|
||||
} else if (url.contains("mp4upload")) {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final script =
|
||||
xpath(res, '//script[contains(text(), "player.src")]/text()').first;
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(script, "src: \""),
|
||||
'"',
|
||||
);
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Mp4upload - $q";
|
||||
videos.add(video);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos);
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos) {
|
||||
String quality = getPreferenceValue(source.id, "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;
|
||||
}
|
||||
|
||||
MPages parseAnimeList(String res) {
|
||||
List<MManga> animeList = [];
|
||||
final urls = xpath(
|
||||
res,
|
||||
'//div[@class="detpost"]/div[@class="thumb"]/a/@href',
|
||||
);
|
||||
final names = xpath(
|
||||
res,
|
||||
'//div[@class="detpost"]/div[@class="thumb"]/a/div[@class="thumbz"]/h2/text()',
|
||||
);
|
||||
final images = xpath(
|
||||
res,
|
||||
'//div[@class="detpost"]/div[@class="thumb"]/a/div[@class="thumbz"]/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 pages = xpath(
|
||||
res,
|
||||
'//div[@class="pagenavix"]/a[@class="next page-numbers"]/@href',
|
||||
);
|
||||
return MPages(animeList, pages.isNotEmpty);
|
||||
}
|
||||
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "overrideBaseUrl",
|
||||
title: "Override BaseUrl",
|
||||
summary: "",
|
||||
value: "https://otakudesu.cloud",
|
||||
dialogTitle: "Override BaseUrl",
|
||||
dialogMessage: "",
|
||||
text: "https://otakudesu.cloud",
|
||||
),
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Preferred quality",
|
||||
summary: "",
|
||||
valueIndex: 1,
|
||||
entries: ["1080p", "720p", "480p", "360p"],
|
||||
entryValues: ["1080", "720", "480", "360"],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
OtakuDesu main(MSource source) {
|
||||
return OtakuDesu(source: source);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get otakudesu => _otakudesu;
|
||||
const _otakudesuVersion = "0.0.6";
|
||||
const _otakudesuCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/otakudesu/otakudesu.dart";
|
||||
Source _otakudesu = Source(
|
||||
name: "OtakuDesu",
|
||||
baseUrl: "https://otakudesu.cloud",
|
||||
lang: "id",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/id/otakudesu/icon.png",
|
||||
sourceCodeUrl: _otakudesuCodeUrl,
|
||||
version: _otakudesuVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,394 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeSaturn extends MProvider {
|
||||
AnimeSaturn({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/animeincorso?page=$page"),
|
||||
)).body;
|
||||
|
||||
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(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("${source.baseUrl}/newest?page=$page"),
|
||||
)).body;
|
||||
|
||||
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(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 res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
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(String url) async {
|
||||
final statusList = [
|
||||
{"In corso": 0, "Finito": 1},
|
||||
];
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
final detailsList = xpath(
|
||||
res,
|
||||
'//div[@class="container shadow rounded bg-dark-as-box mb-3 p-3 w-100 text-white"]/text()',
|
||||
);
|
||||
if (detailsList.isNotEmpty) {
|
||||
final details = detailsList.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(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
|
||||
final urlVid = xpath(res, '//a[contains(@href,"/watch")]/@href').first;
|
||||
final resVid = (await client.get(Uri.parse(urlVid))).body;
|
||||
String masterUrl = "";
|
||||
if (resVid.contains("jwplayer(")) {
|
||||
masterUrl = substringBefore(substringAfter(resVid, "file: \""), "\"");
|
||||
} else {
|
||||
masterUrl = parseHtml(resVid).selectFirst("source").attr("src");
|
||||
}
|
||||
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.endsWith("playlist.m3u8")) {
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(masterUrl))).body;
|
||||
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() {
|
||||
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() {
|
||||
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(MSource source) {
|
||||
return AnimeSaturn(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesaturn => _animesaturn;
|
||||
const _animesaturnVersion = "0.0.55";
|
||||
const _animesaturnCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/it/animesaturn/animesaturn.dart";
|
||||
Source _animesaturn = Source(
|
||||
name: "AnimeSaturn",
|
||||
baseUrl: "https://www.animesaturn.cx",
|
||||
lang: "it",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/it/animesaturn/icon.png",
|
||||
sourceCodeUrl: _animesaturnCodeUrl,
|
||||
version: _animesaturnVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,308 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:math';
|
||||
|
||||
class AnimesVision extends MProvider {
|
||||
AnimesVision({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
String get baseUrl => source.baseUrl;
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {
|
||||
"Referer": baseUrl,
|
||||
"Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
};
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(baseUrl), headers: headers)).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select(
|
||||
"div#anime-trending div.item > a.film-poster",
|
||||
);
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
var anime = MManga();
|
||||
var img = element.selectFirst("img");
|
||||
anime.name = img.attr("title");
|
||||
anime.link = getUrlWithoutDomain(element.attr("href"));
|
||||
anime.imageUrl = img.attr("src");
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage(document));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/lancamentos?page=$page"),
|
||||
headers: headers,
|
||||
)).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select(
|
||||
"div.container div.screen-items > div.item",
|
||||
);
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
var anime = MManga();
|
||||
anime.name = substringAfter(element.selectFirst("h3").text, "-").trim();
|
||||
anime.link = getUrlWithoutDomain(element.selectFirst("a").attr("href"));
|
||||
anime.imageUrl = element.selectFirst("img")?.attr("src") ?? "";
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage(document));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final res =
|
||||
(await client.get(
|
||||
Uri.parse("$baseUrl/search-anime?nome=$query&page=$page"),
|
||||
)).body;
|
||||
final document = parseHtml(res);
|
||||
final elements = document.select("div.film_list-wrap div.film-poster");
|
||||
List<MManga> animeList = [];
|
||||
for (var element in elements) {
|
||||
var anime = MManga();
|
||||
final elementA = element.selectFirst("a");
|
||||
anime.name = elementA.attr("title");
|
||||
anime.link = getUrlWithoutDomain(elementA.attr("href"));
|
||||
anime.imageUrl = element.selectFirst("img").attr("data-src");
|
||||
animeList.add(anime);
|
||||
}
|
||||
return MPages(animeList, hasNextPage(document));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"Atualmente sendo exibido": 0, "Fim da exibição": 1},
|
||||
];
|
||||
MManga anime = MManga();
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
var document = await getRealDoc(parseHtml(res), "$baseUrl$url");
|
||||
final content = document.selectFirst("div#ani_detail div.anis-content");
|
||||
final detail = content.selectFirst("div.anisc-detail");
|
||||
final infos = content.selectFirst("div.anisc-info");
|
||||
anime.imageUrl = content.selectFirst("img")?.attr("src");
|
||||
anime.name = detail.selectFirst("h2.film-name").text;
|
||||
anime.genre = getInfo(infos, "Gêneros").split(",");
|
||||
anime.author = getInfo(infos, "Produtores");
|
||||
anime.artist = getInfo(infos, "Estúdios");
|
||||
anime.status = parseStatus(getInfo(infos, "Status"), statusList);
|
||||
String description = getInfo(infos, "Sinopse");
|
||||
if (getInfo(infos, "Inglês").isNotEmpty)
|
||||
description += '\n\nTítulo em inglês: ${getInfo(infos, "Inglês")}';
|
||||
anime.description = description;
|
||||
if (getInfo(infos, "Japonês").isNotEmpty)
|
||||
description += '\nTítulo em Japonês: ${getInfo(infos, "Japonês")}';
|
||||
if (getInfo(infos, "Foi ao ar em").isNotEmpty)
|
||||
description += '\nFoi ao ar em: ${getInfo(infos, "Foi ao ar em")}';
|
||||
if (getInfo(infos, "Temporada").isNotEmpty)
|
||||
description += '\nTemporada: ${getInfo(infos, "Temporada")}';
|
||||
if (getInfo(infos, "Duração").isNotEmpty)
|
||||
description += '\nDuração: ${getInfo(infos, "Duração")}';
|
||||
if (getInfo(infos, "Fansub").isNotEmpty)
|
||||
description += '\nFansub: ${getInfo(infos, "Fansub")}';
|
||||
anime.description = description;
|
||||
List<MChapter> episodeList = [];
|
||||
for (var element
|
||||
in document.select("div.container div.screen-items > div.item") ?? []) {
|
||||
episodeList.add(episodeFromElement(element));
|
||||
}
|
||||
|
||||
while (hasNextPage(document)) {
|
||||
if (episodeList.isNotEmpty) {
|
||||
final nextUrl = nextPageElements(
|
||||
document,
|
||||
)[0].selectFirst("a").attr("href");
|
||||
document = parseHtml((await client.get(Uri.parse(nextUrl))).body);
|
||||
}
|
||||
for (var element
|
||||
in document.select("div.container div.screen-items > div.item") ??
|
||||
[]) {
|
||||
episodeList.add(episodeFromElement(element));
|
||||
}
|
||||
}
|
||||
anime.chapters = episodeList.reversed.toList();
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final res = (await client.get(Uri.parse("$baseUrl$url"))).body;
|
||||
final document = parseHtml(res);
|
||||
final encodedScript =
|
||||
document
|
||||
.selectFirst("div.player-frame div#playerglobalapi ~ script")
|
||||
.text;
|
||||
final decodedScript = decodeScriptFromString(encodedScript);
|
||||
List<MVideo> videos = [];
|
||||
for (RegExpMatch match in RegExp(
|
||||
r'"file":"(\S+?)",.*?"label":"(.*?)"',
|
||||
).allMatches(decodedScript)) {
|
||||
final videoUrl = match.group(1)!.replaceAll('\\', '');
|
||||
final qualityName = match.group(2);
|
||||
var video = MVideo();
|
||||
video.url = videoUrl;
|
||||
video.headers = headers;
|
||||
video.quality = 'PlayerVision $qualityName';
|
||||
video.originalUrl = videoUrl;
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
bool hasNextPage(MDocument document) {
|
||||
return nextPageElements(document).isNotEmpty;
|
||||
}
|
||||
|
||||
List<MElement> nextPageElements(MDocument document) {
|
||||
final elements =
|
||||
document
|
||||
.select("ul.pagination li.page-item")
|
||||
.where(
|
||||
(MElement e) =>
|
||||
e.outerHtml.contains("›") &&
|
||||
!e.outerHtml.contains("disabled"),
|
||||
)
|
||||
.toList();
|
||||
return elements;
|
||||
}
|
||||
|
||||
Future<MDocument> getRealDoc(MDocument document, String originalUrl) async {
|
||||
if (["/episodio-", "/filme-"].any((e) => originalUrl.contains(e))) {
|
||||
final url = document.selectFirst("h2.film-name > a").attr("href");
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
return parseHtml(res);
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
String getInfo(MElement element, String key) {
|
||||
final divs =
|
||||
element
|
||||
.select("div.item")
|
||||
.where((MElement e) => e.outerHtml.contains(key))
|
||||
.toList();
|
||||
String text = "";
|
||||
if (divs.isNotEmpty) {
|
||||
MElement div = divs[0];
|
||||
var elementsA = div.select("a[href]");
|
||||
if (elementsA.isEmpty) {
|
||||
String selector =
|
||||
div.outerHtml.contains("w-hide") ? "div.text" : "span.name";
|
||||
text = div.selectFirst(selector).text.trim();
|
||||
} else {
|
||||
text = elementsA.map((MElement e) => e.text.trim()).toList().join(', ');
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
MChapter episodeFromElement(MElement element) {
|
||||
var anime = MChapter();
|
||||
anime.url = getUrlWithoutDomain(element.selectFirst("a").attr("href"));
|
||||
anime.name = element.selectFirst("h3").text.trim();
|
||||
return anime;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Qualidade preferida",
|
||||
summary: "",
|
||||
valueIndex: 1,
|
||||
entries: ["480p", "720p", "1080p", "4K"],
|
||||
entryValues: ["1080", "720", "480", "4K"],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
int convertToNum(String thing, int limit) {
|
||||
int result = 0;
|
||||
int i = 0;
|
||||
for (var n in thing.split('').reversed.toList()) {
|
||||
final a = int.tryParse(n) ?? 0;
|
||||
result += a * pow(limit, i).toInt();
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String decodeScript(
|
||||
String encodedString,
|
||||
String magicStr,
|
||||
int offset,
|
||||
int limit,
|
||||
) {
|
||||
RegExp regex = RegExp('\\w');
|
||||
List<String> parts = encodedString.split(magicStr[limit]);
|
||||
List<String> decodedParts = [];
|
||||
for (String part in parts.sublist(0, parts.length - 1)) {
|
||||
String replaced = part;
|
||||
for (Match match in regex.allMatches(part)) {
|
||||
replaced = replaced.replaceFirst(
|
||||
match.group(0)!,
|
||||
magicStr.indexOf(match.group(0)!).toString(),
|
||||
);
|
||||
}
|
||||
int charInt = convertToNum(replaced, limit) - offset;
|
||||
decodedParts.add(String.fromCharCode(charInt));
|
||||
}
|
||||
return decodedParts.join('');
|
||||
}
|
||||
|
||||
String decodeScriptFromString(String script) {
|
||||
RegExp regex = RegExp(r'\}\("(\w+)",.*?"(\w+)",(\d+),(\d+),.*?\)');
|
||||
Match? match = regex.firstMatch(script);
|
||||
if (match != null) {
|
||||
return decodeScript(
|
||||
match.group(1)!,
|
||||
match.group(2)!,
|
||||
int.tryParse(match.group(3)!) ?? 0,
|
||||
int.tryParse(match.group(4)!) ?? 0,
|
||||
);
|
||||
} else {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimesVision main(MSource source) {
|
||||
return AnimesVision(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesvision => _animesvision;
|
||||
const _animesvisionVersion = "0.0.2";
|
||||
const _animesvisionCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/pt/animesvision/animesvision.dart";
|
||||
Source _animesvision = Source(
|
||||
name: "AnimesVision",
|
||||
baseUrl: "https://animes.vision",
|
||||
lang: "pt-br",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/pt/animesvision/icon.png",
|
||||
sourceCodeUrl: _animesvisionCodeUrl,
|
||||
version: _animesvisionVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,306 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class Filma24 extends MProvider {
|
||||
Filma24({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
@override
|
||||
String get baseUrl => getPreferenceValue(source.id, "pref_domain_new");
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
String pageNu = page == 1 ? "" : "/page/$page/";
|
||||
final res = (await client.get(Uri.parse("$baseUrl$pageNu"))).body;
|
||||
return animeFromRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
String pageNu = page == 1 ? "" : "page/$page/";
|
||||
final res =
|
||||
(await client.get(Uri.parse("$baseUrl/$pageNu?sort=trendy"))).body;
|
||||
return animeFromRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final filters = filterList.filters;
|
||||
String url = "";
|
||||
String pageNu = page == 1 ? "" : "page/$page/";
|
||||
if (query.isNotEmpty) {
|
||||
url += "$baseUrl/search/$query/";
|
||||
} else {
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "ReleaseFilter") {
|
||||
final year = filter.values[filter.state].value;
|
||||
if (year.isNotEmpty) {
|
||||
url = "/released-year/?release=$year/";
|
||||
}
|
||||
} else if (filter.type == "GenreFilter") {
|
||||
final genre = filter.values[filter.state].value;
|
||||
if (genre.isNotEmpty) {
|
||||
url = genre;
|
||||
}
|
||||
}
|
||||
}
|
||||
url = "$baseUrl$url";
|
||||
}
|
||||
|
||||
url += pageNu;
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
return animeFromRes(res);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
List<MChapter>? episodesList = [];
|
||||
if (!url.contains("seriale")) {
|
||||
MChapter episode = MChapter();
|
||||
episode.name = "Filma";
|
||||
episode.url = url;
|
||||
episodesList.add(episode);
|
||||
} else {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final document = parseHtml(res);
|
||||
final resultElements = document.select("div.row");
|
||||
|
||||
for (var result in resultElements) {
|
||||
final elements = result?.select("div.movie-thumb") ?? [];
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
MChapter ep = MChapter();
|
||||
ep.name = elements[i].selectFirst("div > span").text;
|
||||
ep.url = elements[i].selectFirst("a").getHref;
|
||||
episodesList.add(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MManga anime = MManga();
|
||||
anime.chapters = episodesList;
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
List<MVideo> videos = [];
|
||||
final serverUrls = xpath(res, '//*[@class="player"]/div[1]/a/@href');
|
||||
for (var serverUrl in serverUrls) {
|
||||
List<MVideo> a = [];
|
||||
final serVres = (await client.get(Uri.parse("$url/$serverUrl"))).body;
|
||||
List<String> iframe = xpath(serVres, '//*[@id="plx"]/p/iframe/@src');
|
||||
if (iframe.isNotEmpty) {
|
||||
String i = iframe.first;
|
||||
if (i.startsWith("//")) {
|
||||
i = "https:$i";
|
||||
}
|
||||
if (i.contains("vidmoly")) {
|
||||
a = await vidmolyExtractor(i);
|
||||
} else if (i.contains("dood")) {
|
||||
a = await doodExtractor(i, "DoodStream");
|
||||
} else if (i.contains("oneupload")) {
|
||||
a = await oneuploadExtractor(i);
|
||||
} else if (i.contains("uqload")) {
|
||||
a = await uqloadExtractor(i);
|
||||
} else if (i.contains("voe.sx")) {
|
||||
a = await voeExtractor(i, "Voe");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
EditTextPreference(
|
||||
key: "pref_domain_new",
|
||||
title: "Domeni i përdorur aktualisht",
|
||||
summary: "",
|
||||
value: "https://www.filma24.band",
|
||||
dialogTitle: "Domeni i përdorur aktualisht",
|
||||
dialogMessage: "",
|
||||
text: "https://www.filma24.band",
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
SelectFilter("ReleaseFilter", "Viti", 0, [
|
||||
SelectFilterOption("<Select>", ""),
|
||||
SelectFilterOption("2024", "2024"),
|
||||
SelectFilterOption("2023", "2023"),
|
||||
SelectFilterOption("2022", "2022"),
|
||||
SelectFilterOption("2021", "2021"),
|
||||
SelectFilterOption("2020", "2020"),
|
||||
SelectFilterOption("2019", "2019"),
|
||||
SelectFilterOption("2018", "2018"),
|
||||
SelectFilterOption("2017", "2017"),
|
||||
SelectFilterOption("2016", "2016"),
|
||||
SelectFilterOption("2011-2015", "2011-2015"),
|
||||
SelectFilterOption("2006-2010", "2006-2010"),
|
||||
SelectFilterOption("2001-2005", "2001-2005"),
|
||||
SelectFilterOption("1991-2000", "1991-2000"),
|
||||
SelectFilterOption("1900-1990", "1900-1990"),
|
||||
]),
|
||||
SelectFilter("GenreFilter", "Zhanri", 0, [
|
||||
SelectFilterOption("<Select>", ""),
|
||||
SelectFilterOption("SË SHPEJTI", "/se-shpejti/"),
|
||||
SelectFilterOption("Aksion", "/aksion/"),
|
||||
SelectFilterOption("Animuar", "/animuar/"),
|
||||
SelectFilterOption("Aventurë", "/aventure/"),
|
||||
SelectFilterOption("Aziatik", "/aziatik/"),
|
||||
SelectFilterOption("Biografi", "/biografi/"),
|
||||
SelectFilterOption("Nordik", "/nordik/"),
|
||||
SelectFilterOption("Dokumentar", "/dokumentar/"),
|
||||
SelectFilterOption("Dramë", "/drame/"),
|
||||
SelectFilterOption("Erotik +18", "/erotik/"),
|
||||
SelectFilterOption("Familjar", "/familjar/"),
|
||||
SelectFilterOption("Fantashkencë", "/fantashkence/"),
|
||||
SelectFilterOption("Fantazi", "/fantazi/"),
|
||||
SelectFilterOption("Francez", "/francez/"),
|
||||
SelectFilterOption("Gjerman", "/gjerman/"),
|
||||
SelectFilterOption("Hindi", "/hindi/"),
|
||||
SelectFilterOption("Histori", "/histori/"),
|
||||
SelectFilterOption("Horror", "/horror/"),
|
||||
SelectFilterOption("Italian", "/italian/"),
|
||||
SelectFilterOption("Komedi", "/komedi/"),
|
||||
SelectFilterOption("Krim", "/krim/"),
|
||||
SelectFilterOption("Luftë", "/lufte/"),
|
||||
SelectFilterOption("Mister", "/mister/"),
|
||||
SelectFilterOption("Muzikë", "/muzik/"),
|
||||
SelectFilterOption("NETFLIX", "/netflix/"),
|
||||
SelectFilterOption("Romancë", "/romance/"),
|
||||
SelectFilterOption("Rus", "/rus/"),
|
||||
SelectFilterOption("Shqiptar", "/shqiptar/"),
|
||||
SelectFilterOption("Spanjoll", "/spanjoll/"),
|
||||
SelectFilterOption("Sport", "/sport/"),
|
||||
SelectFilterOption("Thriller", "/thriller/"),
|
||||
SelectFilterOption("Turk", "/turk/"),
|
||||
SelectFilterOption("Western", "/western/"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
Future<List<MVideo>> vidmolyExtractor(String url) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final headers = {'Referer': 'https://vidmoly.to'};
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes = await client.get(
|
||||
Uri.parse(playlistUrl),
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (masterPlaylistRes.statusCode == 200) {
|
||||
for (var it in substringAfter(
|
||||
masterPlaylistRes.body,
|
||||
"#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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Vidmoly $quality"
|
||||
..headers = headers;
|
||||
videos.add(video);
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> oneuploadExtractor(String url) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
List<MVideo> videos = [];
|
||||
final playListUrlResponse = (await client.get(Uri.parse(url))).body;
|
||||
final playlistUrl =
|
||||
RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ??
|
||||
"";
|
||||
if (playlistUrl.isEmpty) return [];
|
||||
final masterPlaylistRes = (await client.get(Uri.parse(playlistUrl))).body;
|
||||
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");
|
||||
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "OneUploader $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
Future<List<MVideo>> uqloadExtractor(String url) async {
|
||||
final client = Client(source, json.encode({"useDartHttpClient": true}));
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res, '//script[contains(text(), "sources:")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final videoUrl = substringBefore(
|
||||
substringAfter(js.first, "sources: [\""),
|
||||
'"',
|
||||
);
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = videoUrl
|
||||
..originalUrl = videoUrl
|
||||
..quality = "Uqload"
|
||||
..headers = {"Referer": "${Uri.parse(url).origin}/"};
|
||||
return [video];
|
||||
}
|
||||
|
||||
MPages animeFromRes(String res) {
|
||||
final document = parseHtml(res);
|
||||
final result = document.selectFirst("div.row");
|
||||
final elements = result?.select("div.movie-thumb") ?? [];
|
||||
List<MManga> mangaList = [];
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
MManga manga = MManga();
|
||||
manga.name = elements[i].selectFirst("div > a > h4").text;
|
||||
manga.imageUrl = elements[i].selectFirst("a").getSrc;
|
||||
manga.link = elements[i].selectFirst("a").getHref;
|
||||
mangaList.add(manga);
|
||||
}
|
||||
|
||||
return MPages(
|
||||
mangaList,
|
||||
document.selectFirst("div > a.nextpostslink")?.attr("href") != null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Filma24 main(MSource source) {
|
||||
return Filma24(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.8 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get filma24 => _filma24;
|
||||
const _filma24Version = "0.0.5";
|
||||
const _filma24CodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/sq/filma24/filma24.dart";
|
||||
Source _filma24 = Source(
|
||||
name: "Filma24",
|
||||
baseUrl: "https://www.filma24.band",
|
||||
lang: "sq",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/sq/filma24/icon.png",
|
||||
sourceCodeUrl: _filma24CodeUrl,
|
||||
version: _filma24Version,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||
@@ -1,241 +0,0 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class DiziWatch extends MProvider {
|
||||
DiziWatch({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client();
|
||||
|
||||
@override
|
||||
bool get supportsLatest => true;
|
||||
|
||||
@override
|
||||
Map<String, String> get headers => {};
|
||||
|
||||
Future<MPages> parseMainList(int index) async {
|
||||
MDocument dom = parseHtml(
|
||||
(await client.get(Uri.parse(source.baseUrl))).body,
|
||||
);
|
||||
List<MManga> list = [];
|
||||
MElement containingElement = dom.select("#list-series-hizala2")[index];
|
||||
List<MElement> results = containingElement.select("#list-series-main");
|
||||
for (MElement result in results) {
|
||||
MElement a = result.selectFirst("a");
|
||||
MElement img = a.selectFirst("img");
|
||||
MManga anime = new MManga();
|
||||
anime.name = img.attr("alt");
|
||||
anime.link = a.getHref;
|
||||
anime.imageUrl = img.getSrc;
|
||||
list.add(anime);
|
||||
}
|
||||
|
||||
return MPages(list, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
return parseMainList(1);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> getLatestUpdates(int page) async {
|
||||
return parseMainList(2);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MPages> search(String query, int page, FilterList filterList) async {
|
||||
String orderby = "";
|
||||
String year = "";
|
||||
String imdb = "";
|
||||
String genre = "";
|
||||
for (var filter in filterList.filters) {
|
||||
if (filter.type == "Sort") {
|
||||
orderby = filter.values[filter.state].value;
|
||||
} else if (filter.type == "Year") {
|
||||
year = filter.state;
|
||||
} else if (filter.type == "MinIMDBRating") {
|
||||
imdb = filter.values[filter.state].value;
|
||||
} else if (filter.type == "Genre") {
|
||||
genre = filter.values[filter.state].value;
|
||||
}
|
||||
}
|
||||
MDocument dom = parseHtml(
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/anime-arsivi/page/${page}/?orderby=${orderby}&yil=${year}&imdb=${imdb}&isim=${query}&tur=${genre}",
|
||||
),
|
||||
)).body,
|
||||
);
|
||||
List<MElement> results = dom.select("#list-series");
|
||||
List<MManga> list = [];
|
||||
for (MElement result in results) {
|
||||
MElement a = result.select("a")[1];
|
||||
MElement img = a.selectFirst("img");
|
||||
MManga anime = new MManga();
|
||||
anime.name = result.selectFirst("div.cat-title a").text;
|
||||
anime.link = a.getHref;
|
||||
anime.imageUrl = img.getSrc;
|
||||
list.add(anime);
|
||||
}
|
||||
|
||||
MElement paginateLinksDiv = dom.selectFirst("div.paginate-links");
|
||||
int lastPage = int.parse(
|
||||
paginateLinksDiv.selectFirst("a.next").previousElementSibling.text ?? "1",
|
||||
);
|
||||
|
||||
return MPages(list, lastPage > page);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse(url))).body);
|
||||
|
||||
var anime = new MManga();
|
||||
|
||||
anime.name = dom.selectFirst("h1.title-border").text;
|
||||
anime.link = url;
|
||||
anime.imageUrl = dom.selectFirst("div.category_image img").getSrc;
|
||||
anime.description = dom.selectFirst("div#series-info").text;
|
||||
|
||||
List<String> genres = dom.selectFirst("span.dizi-tur").text.split(", ");
|
||||
genres.remove("Anime"); // not needed
|
||||
anime.genre = genres;
|
||||
|
||||
List<MElement> results = dom.select("div.bolumust");
|
||||
List<MChapter> chapters = [];
|
||||
for (MElement result in results) {
|
||||
MElement a = result.select("a")[1];
|
||||
MChapter chapter = new MChapter();
|
||||
chapter.name =
|
||||
result.selectFirst(".baslik").text +
|
||||
" | " +
|
||||
result.selectFirst("#bolum-ismi").text;
|
||||
chapter.url = a.getHref;
|
||||
chapters.add(chapter);
|
||||
}
|
||||
anime.chapters = chapters.reversed.toList();
|
||||
|
||||
return anime;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MVideo>> getVideoList(String url) async {
|
||||
MDocument dom = parseHtml((await client.get(Uri.parse(url))).body);
|
||||
String id = dom.selectFirst("#takip_et_izledim_Calis").attr("data-ilanid");
|
||||
var json =
|
||||
json.decode(
|
||||
(await client.get(
|
||||
Uri.parse(
|
||||
"${source.baseUrl}/wp-admin/admin-ajax.php?action=playlist&pid=${id}",
|
||||
),
|
||||
)).body,
|
||||
)[0];
|
||||
var sources = json["sources"];
|
||||
List<MVideo> videos = [];
|
||||
for (var source in sources) {
|
||||
MVideo video = new MVideo();
|
||||
video.url = source["file"];
|
||||
video.originalUrl = source["file"];
|
||||
video.quality = source["label"];
|
||||
video.headers = {"Referer": url};
|
||||
videos.add(video);
|
||||
}
|
||||
|
||||
String quality = getPreferenceValue(source.id, "preferred_quality");
|
||||
videos.sort(
|
||||
(MVideo a, MVideo b) =>
|
||||
(b.quality.contains(quality) ? 1 : 0) -
|
||||
(a.quality.contains(quality) ? 1 : 0),
|
||||
);
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
return [
|
||||
HeaderFilter("Filtrele"),
|
||||
SelectFilter("Sort", "Sırala", 0, [
|
||||
SelectFilterOption("IMDb Puanına Göre", "meta_value"),
|
||||
SelectFilterOption("Alfabetik", "name"),
|
||||
SelectFilterOption("Eklenme Tarihine Göre", "ID"),
|
||||
]),
|
||||
TextFilter("Year", "Yapım Yılı"),
|
||||
SelectFilter("Genre", "Tür", 0, [
|
||||
SelectFilterOption("Kategori Seçin", ""),
|
||||
SelectFilterOption("Aksiyon", "aksiyon"),
|
||||
SelectFilterOption("Arabalar", "araba"),
|
||||
SelectFilterOption("Askeri", "askeri"),
|
||||
SelectFilterOption("Bilim Kurgu", "bilim"),
|
||||
SelectFilterOption("Büyü", "buyu"),
|
||||
SelectFilterOption("Doğaüstü Güçler", "doga"),
|
||||
SelectFilterOption("Dövüş Sanatları", "dovus"),
|
||||
SelectFilterOption("Dram", "dram"),
|
||||
SelectFilterOption("Ecchi", "ecchi"),
|
||||
SelectFilterOption("Fantastik", "fantastik"),
|
||||
SelectFilterOption("Gerilim", "gerilim"),
|
||||
SelectFilterOption("Gizem", "gizem"),
|
||||
SelectFilterOption("Harem", "harem"),
|
||||
SelectFilterOption("Isekai", "isekai"),
|
||||
SelectFilterOption("Komedi", "komedi"),
|
||||
SelectFilterOption("Korku", "korku"),
|
||||
SelectFilterOption("Macera", "macera"),
|
||||
SelectFilterOption("Mecha", "mecha"),
|
||||
SelectFilterOption("Müzik", "muzik"),
|
||||
SelectFilterOption("Okul", "okul"),
|
||||
SelectFilterOption("Oyun", "oyun"),
|
||||
SelectFilterOption("Parodi", "parodi"),
|
||||
SelectFilterOption("Polisiye", "polisiye"),
|
||||
SelectFilterOption("Psikolojik", "psikolojik"),
|
||||
SelectFilterOption("Romantizm", "romantizm"),
|
||||
SelectFilterOption("Samuray", "samuray"),
|
||||
SelectFilterOption("Seinen", "seinen"),
|
||||
SelectFilterOption("Shoujo", "shoujo"),
|
||||
SelectFilterOption("Shounen", "shounen"),
|
||||
SelectFilterOption("Spor", "spor"),
|
||||
SelectFilterOption("Suç", "suc"),
|
||||
SelectFilterOption("Süper Güçler", "super"),
|
||||
SelectFilterOption("Şeytanlar", "seytan"),
|
||||
SelectFilterOption("Şizofreni", "sizofreni"),
|
||||
SelectFilterOption("Tarihi", "tarihi"),
|
||||
SelectFilterOption("Uzay", "uzay"),
|
||||
SelectFilterOption("Vampir", "vampir"),
|
||||
SelectFilterOption("Yaşamdan Kesitler", "yasam"),
|
||||
]),
|
||||
SelectFilter("MinIMDBRating", "Min. IMBD Puanı", 0, [
|
||||
SelectFilterOption(
|
||||
"1",
|
||||
"0",
|
||||
), // value 1 looks like buggy so use 0 it wont make any difference.
|
||||
SelectFilterOption("2", "2"),
|
||||
SelectFilterOption("3", "3"),
|
||||
SelectFilterOption("4", "4"),
|
||||
SelectFilterOption("5", "5"),
|
||||
SelectFilterOption("6", "6"),
|
||||
SelectFilterOption("7", "7"),
|
||||
SelectFilterOption("8", "8"),
|
||||
SelectFilterOption("9", "9"),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getSourcePreferences() {
|
||||
return [
|
||||
ListPreference(
|
||||
key: "preferred_quality",
|
||||
title: "Tercih edilen kalite",
|
||||
summary: "",
|
||||
valueIndex: 0,
|
||||
entries: ["1080p", "480p"], // I only saw 1080p and 480p in diziWatch.
|
||||
entryValues: ["1080", "480"],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
DiziWatch main(MSource source) {
|
||||
return DiziWatch(source: source);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,17 +0,0 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get diziwatchSource => _diziwatchSource;
|
||||
const _diziwatchVersion = "0.0.15";
|
||||
const _diziwatchSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/tr/diziwatch/diziwatch.dart";
|
||||
Source _diziwatchSource = Source(
|
||||
name: "diziWatch",
|
||||
baseUrl: "https://diziwatch.net",
|
||||
lang: "tr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/tr/diziwatch/icon.png",
|
||||
sourceCodeUrl: _diziwatchSourceCodeUrl,
|
||||
version: _diziwatchVersion,
|
||||
itemType: ItemType.anime,
|
||||
);
|
||||