This commit is contained in:
Moustapha Kodjo Amadou
2025-10-01 15:27:46 +01:00
parent a19781234a
commit 6004f1f8d1
129 changed files with 2 additions and 26321 deletions

View File

@@ -4,7 +4,7 @@ This repository contains the available extension catalogues for the Mangayomi ap
## How to add the extensions ## How to add the extensions
Click on one of the buttons below to add the corresponding repository/repositories: Click on one of the buttons below to add the corresponding repository/repositories (Manga & novels only):
<a href="https://intradeus.github.io/http-protocol-redirector?r=mangayomi://add-repo?repo_name=mangayomi-extensions%26repo_url=https://github.com/kodjodevf/mangayomi-extensions%26manga_url=https://kodjodevf.github.io/mangayomi-extensions/index.json%26anime_url=https://kodjodevf.github.io/mangayomi-extensions/anime_index.json%26novel_url=https://kodjodevf.github.io/mangayomi-extensions/novel_index.json"><img alt="Add all repositories" src="images/add-all-repositories.png" height="35"></a> <a href="https://intradeus.github.io/http-protocol-redirector?r=mangayomi://add-repo?repo_name=mangayomi-extensions%26repo_url=https://github.com/kodjodevf/mangayomi-extensions%26manga_url=https://kodjodevf.github.io/mangayomi-extensions/index.json%26anime_url=https://kodjodevf.github.io/mangayomi-extensions/anime_index.json%26novel_url=https://kodjodevf.github.io/mangayomi-extensions/novel_index.json"><img alt="Add all repositories" src="images/add-all-repositories.png" height="35"></a>

View File

@@ -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,
];

View File

@@ -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);
}

View File

@@ -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();

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

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

View File

@@ -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);
}

View File

@@ -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();

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

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

View File

@@ -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();

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

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

View File

@@ -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);
}

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -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();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -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);
}

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -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,
);

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -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,
);

View File

@@ -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);
}

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -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);
}

View File

@@ -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,
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -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);
}

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -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,
);

View File

@@ -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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -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,
);

View File

@@ -1,714 +0,0 @@
const mangayomiSources = [
{
"name": "Autoembed",
"lang": "all",
"baseUrl": "https://watch.autoembed.cc",
"apiUrl": "https://tom.autoembed.cc",
"iconUrl":
"https://www.google.com/s2/favicons?sz=64&domain=https://autoembed.cc/",
"typeSource": "multi",
"isManga": false,
"itemType": 1,
"version": "1.2.7",
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "anime/src/all/autoembed.js"
}
];
class DefaultExtension extends MProvider {
decodeBase64 = function (f) {
var g = {},
b = 65,
d = 0,
a,
c = 0,
h,
e = "",
k = String.fromCharCode,
l = f.length;
for (a = ""; 91 > b; ) a += k(b++);
a += a.toLowerCase() + "0123456789+/";
for (b = 0; 64 > b; b++) g[a.charAt(b)] = b;
for (a = 0; a < l; a++)
for (b = g[f.charAt(a)], d = (d << 6) + b, c += 6; 8 <= c; )
((h = (d >>> (c -= 8)) & 255) || a < l - 2) && (e += k(h));
return e;
};
getHeaders(url) {
return {
Referer: url,
Origin: url,
};
}
getPreference(key) {
const preferences = new SharedPreferences();
return preferences.get(key);
}
async tmdbRequest(slug) {
var api = `https://94c8cb9f702d-tmdb-addon.baby-beamup.club/${slug}`;
var response = await new Client().get(api);
var body = JSON.parse(response.body);
return body;
}
async getSearchItems(body) {
var items = [];
var results = body.metas;
for (let i in results) {
var result = results[i];
var id = result.id;
var media_type = result.type;
items.push({
name: result.name,
imageUrl: result.poster,
link: `${media_type}||${id}`,
description: result.description,
genre: result.genre,
});
}
return items;
}
async getSearchInfo(slug) {
var body = await this.tmdbRequest(`catalog/movie/${slug}`);
var popMovie = await this.getSearchItems(body);
body = await this.tmdbRequest(`catalog/series/${slug}`);
var popSeries = await this.getSearchItems(body);
var fullList = [];
var priority = this.getPreference("pref_content_priority");
if (priority === "series") {
fullList = [...popSeries, ...popMovie];
} else {
fullList = [...popMovie, ...popSeries];
}
var hasNextPage = slug.indexOf("search=") > -1 ? false : true;
return {
list: fullList,
hasNextPage,
};
}
async getPopular(page) {
var skip = (page - 1) * 20;
return await this.getSearchInfo(`tmdb.popular/skip=${skip}.json`);
}
get supportsLatest() {
throw new Error("supportsLatest not implemented");
}
async getLatestUpdates(page) {
var trend_window = this.getPreference("pref_latest_time_window");
var skip = (page - 1) * 20;
return await this.getSearchInfo(
`tmdb.trending/genre=${trend_window}&skip=${skip}.json`
);
}
async search(query, page, filters) {
return await this.getSearchInfo(`tmdb.popular/search=${query}.json`);
}
async getDetail(url) {
var baseUrl = this.source.baseUrl;
var linkSlug = `${baseUrl}/title/`;
if (url.includes(linkSlug)) {
url = url.replace(linkSlug, "");
var id = url.replace("t", "");
if (url.includes("t")) {
url = `series||tmdb:${id}`;
} else {
url = `movie||tmdb:${id}`;
}
}
var parts = url.split("||");
var media_type = parts[0];
var id = parts[1];
var body = await this.tmdbRequest(`meta/${media_type}/${id}.json`);
var result = body.meta;
var tmdb_id = id.substring(5);
media_type = media_type == "series" ? "tv" : media_type;
var dateNow = Date.now().valueOf();
var release = result.released
? new Date(result.released).valueOf()
: dateNow;
var chaps = [];
var item = {
name: result.name,
imageUrl: result.poster,
link: `${linkSlug}${linkCode}`,
description: result.description,
genre: result.genre,
};
var link = `${media_type}||${tmdb_id}`;
if (media_type == "tv") {
var videos = result.videos;
for (var i in videos) {
var video = videos[i];
var seasonNum = video.season;
if (!seasonNum) continue;
release = video.released ? new Date(video.released).valueOf() : dateNow;
if (release < dateNow) {
var episodeNum = video.episode;
var name = `S${seasonNum}:E${episodeNum} - ${video.name}`;
var eplink = `${link}||${seasonNum}||${episodeNum}`;
chaps.push({
name: name,
url: eplink,
dateUpload: release.toString(),
});
}
}
} else {
if (release < dateNow) {
chaps.push({
name: "Movie",
url: link,
dateUpload: release.toString(),
});
}
}
item.chapters = chaps;
chaps.reverse();
return item;
}
// Extracts the streams url for different resolutions from a hls stream.
async extractStreams(url, lang = "", hdr = {}, host = "") {
var streams = [
{
url: url,
originalUrl: url,
quality: `${lang} Auto`,
headers: hdr,
},
];
var pref = this.getPreference("autoembed_split_stream_quality");
if (!pref) return streams;
const response = await new Client().get(url, hdr);
const body = response.body;
const lines = body.split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith("#EXT-X-STREAM-INF:")) {
var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
resolution = `${lang} ${resolution}`;
var m3u8Url = lines[i + 1].trim();
m3u8Url = m3u8Url.replace("./", `${url}/`);
if (host.length > 0) {
m3u8Url = `${host}${m3u8Url}`;
}
streams.push({
url: m3u8Url,
originalUrl: m3u8Url,
quality: resolution,
headers: hdr,
});
}
}
return streams;
}
// For some streams, we can form stream url using a default template.
async splitStreams(url, lang = "", hdr = {}) {
var streams = [
{
url: url,
originalUrl: url,
quality: `${lang} - Auto`,
headers: hdr,
},
];
var pref = this.getPreference("autoembed_split_stream_quality");
if (!pref) return streams;
var quality = ["360", "480", "720", "1080"];
for (var q of quality) {
var link = url;
if (q != "auto") {
link = link.replace("index.m3u8", `${q}/index.m3u8`);
q = `${q}p`;
}
streams.push({
url: link,
originalUrl: link,
quality: `${lang} - ${q}`,
headers: hdr,
});
}
return streams;
}
// Sorts streams based on user preference.
async sortStreams(streams) {
var sortedStreams = [];
var copyStreams = streams.slice();
var pref = this.getPreference("pref_video_resolution");
for (var i in streams) {
var stream = streams[i];
if (stream.quality.indexOf(pref) > -1) {
sortedStreams.push(stream);
var index = copyStreams.indexOf(stream);
if (index > -1) {
copyStreams.splice(index, 1);
}
break;
}
}
return [...sortedStreams, ...copyStreams];
}
// Gets subtitles based on TMDB id.
async getSubtitleList(id, s, e) {
var subPref = parseInt(
this.getPreference("autoembed_pref_subtitle_source")
);
var api = `https://sub.wyzie.ru/search?id=${id}`;
var hdr = {};
if (subPref === 2) {
api = `https://sources.hexa.watch/subs/${id}`;
hdr = { "Origin": "https://api.hexa.watch" };
if (s != "0") api = `${api}/${s}/${e}`;
} else {
if (s != "0") api = `${api}&season=${s}&episode=${e}`;
}
var response = await new Client().get(api, hdr);
var body = JSON.parse(response.body);
var subs = [];
for (var sub of body) {
subs.push({
file: sub.url,
label: sub.display,
});
}
return subs;
}
// For anime episode video list
async getVideoList(url) {
var streamAPI = parseInt(this.getPreference("autoembed_stream_source_3"));
var nativeSubs = this.getPreference("autoembed_pref_navtive_subtitle");
var parts = url.split("||");
var media_type = parts[0];
var id = parts[1];
var s = "0";
var e = "0";
if (media_type == "tv") {
s = parts[2];
e = parts[3];
}
var tmdb = id;
var streams = [];
var subtitles = [];
switch (streamAPI) {
case 2: {
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var api = `https://play2.123embed.net/server/3?path=/${media_type}/${id}`;
var response = await new Client().get(api);
if (response.statusCode != 200) {
throw new Error(
"play2.123embed.net unavailable\nPlease choose a different server"
);
}
var body = JSON.parse(response.body);
var link = body.playlist[0].file;
streams.push({
url: link,
originalUrl: link,
quality: "auto",
headers: { "Origin": "https://play2.123embed.net" },
});
break;
}
case 3: {
if (media_type == "tv") {
id = `${id}&s=${s}&e=${e}`;
}
var api = `https://autoembed.cc/embed/player.php?id=${id}`;
var response = await new Client().get(api);
if (response.statusCode != 200) {
throw new Error(
"autoembed.cc unavailable\nPlease choose a different server"
);
}
var body = response.body;
var sKey = '"file": ';
var eKey = "]});";
var start = body.indexOf(sKey);
if (start < 0) {
throw new Error(
"autoembed.cc videos unavailable\nPlease choose a different server"
);
}
start += sKey.length;
var end = body.substring(start).indexOf(eKey) + start - 1;
var strms = JSON.parse(body.substring(start, end) + "]");
for (var strm of strms) {
var link = strm.file;
var lang = strm.title;
var streamSplit = await this.splitStreams(link, lang);
streams = [...streams, ...streamSplit];
}
break;
}
case 4: {
if (media_type == "tv") {
id = `${id}&season=${s}&episode=${e}`;
}
var api = `https://flicky.host/player/desi.php?id=${id}`;
var response = await new Client().get(api, {
"Referer": "https://flicky.host/",
"sec-fetch-dest": "iframe",
});
if (response.statusCode != 200) {
throw new Error(
"flicky.host unavailable\nPlease choose a different server"
);
}
var body = response.body;
var sKey = "streams = ";
var eKey = "];";
var start = body.indexOf(sKey);
if (start < 0) {
throw new Error(
"flicky.host videos unavailable\nPlease choose a different server"
);
}
start += sKey.length;
var end = body.substring(start).indexOf(eKey) + start + 1;
var strms = JSON.parse(body.substring(start, end));
for (var strm of strms) {
var link = strm.url;
var lang = strm.language;
var streamSplit = await this.splitStreams(link, lang);
streams = [...streams, ...streamSplit];
}
break;
}
case 5: {
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var api = `https://vidapi.click/api/video/${media_type}/${id}`;
var response = await new Client().get(api);
if (response.statusCode != 200) {
throw new Error(
"vidapi.click unavailable\nPlease choose a different server"
);
}
var body = JSON.parse(response.body);
var link = body.sources[0].file;
if (nativeSubs) subtitles = body.tracks;
streams = await this.extractStreams(link);
break;
}
case 6: {
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var api = `https://sources.hexa.watch/plsdontscrapemeuwu/${id}`;
var hdr = { "Origin": "https://api.hexa.watch" };
var response = await new Client().get(api, hdr);
if (response.statusCode != 200) {
throw new Error(
"hexa.watch unavailable\nPlease choose a different server"
);
}
var body = JSON.parse(response.body);
var strms = body.streams;
for (var strm of strms) {
var streamLink = strm.url;
if (streamLink.length > 0) {
streams.push({
url: strm.url,
originalUrl: strm.url,
quality: `${strm.label} - Auto`,
headers: strm.headers,
});
}
}
break;
}
case 7: {
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var api = `https://vidsrc.su/embed/${media_type}/${id}`;
var response = await new Client().get(api);
if (response.statusCode != 200) {
throw new Error(
"vidsrc.su unavailable\nPlease choose a different server"
);
}
var body = response.body;
var sKey = "fixedServers = ";
var eKey = "];";
var start = body.indexOf(sKey);
if (start < 0) {
throw new Error(
"vidsrc.su videos unavailable\nPlease choose a different server"
);
}
start += sKey.length;
var end = body.substring(start).indexOf(eKey) + start + 1;
var strms = body.substring(start, end);
// Split the data into lines
var lines = strms.split("\n");
// Regex to match URLs in quotes that start with https://
var regex = /url:\s*'(https:\/\/[^']+)'/;
var availableStreams = [];
// Process each line
lines.forEach((line) => {
var match = line.match(regex);
if (match && match[1]) {
// Extract the label from the line
var labelMatch = line.match(/label:\s*'([^']+)'/);
var label = labelMatch ? labelMatch[1] : "Unknown";
// Add to our results
availableStreams.push({
url: match[1],
label: label,
});
}
});
for (var stream of availableStreams) {
var streamSplit = await this.extractStreams(stream.url, stream.label);
streams = [...streams, ...streamSplit];
}
if (nativeSubs) {
// subtitles
sKey = "const subtitles = ";
eKey = "];";
start = body.indexOf(sKey);
if (start < 0) {
break; // no need for native subtitle if not found.
}
start += sKey.length;
end = body.substring(start).indexOf(eKey) + start + 1;
var natSubs = JSON.parse(body.substring(start, end));
natSubs.forEach((sub) => {
subtitles.push({
file: sub.url,
label: sub.display,
});
});
}
break;
}
case 8: {
function reverse(str) {
return str.split("").reverse().join("");
}
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var baseUrl = "https://embed.su";
var embedUrl = `${baseUrl}/embed/${media_type}/${id}`;
var response = await new Client().get(
embedUrl,
this.getHeaders(baseUrl)
);
var body = response.body;
var sKey = "JSON.parse(atob(`";
var start = body.indexOf(sKey) + sKey.length;
var end = body.substring(start).indexOf("`") + start;
var configHash = body.substring(start, end);
var config = JSON.parse(this.decodeBase64(configHash));
var encodedHash = this.decodeBase64(config.hash);
var decodeHash = reverse(
encodedHash
.split(".")
.map((item) => reverse(item))
.join("")
);
encodedHash = JSON.parse(this.decodeBase64(decodeHash));
var serverHash = encodedHash[0].hash;
var api = `${baseUrl}/api/e/${serverHash}`;
response = await new Client().get(api, this.getHeaders(baseUrl));
var jsonRes = JSON.parse(response.body);
streams = await this.extractStreams(
jsonRes.source,
"",
this.getHeaders(baseUrl),
baseUrl
);
if (nativeSubs) subtitles = jsonRes.subtitles;
break;
}
default: {
if (media_type == "tv") {
id = `${id}/${s}/${e}`;
}
var api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}`;
var response = await new Client().get(
api,
this.getHeaders(this.source.apiUrl)
);
if (response.statusCode != 200) {
throw new Error(
"tom.autoembed.cc unavailable\nPlease choose a different server"
);
}
var body = JSON.parse(response.body);
var link = body.videoSource;
if (nativeSubs) subtitles = body.subtitles;
streams = await this.extractStreams(link);
break;
}
}
if (streams.length < 1) {
throw new Error(
"No streams unavailable\nPlease choose a different server"
);
}
var apiSubs = await this.getSubtitleList(tmdb, s, e);
streams[0].subtitles = [...subtitles, ...apiSubs];
return await this.sortStreams(streams);
}
// For manga chapter pages
async getPageList() {
throw new Error("getPageList not implemented");
}
getFilterList() {
throw new Error("getFilterList not implemented");
}
getSourcePreferences() {
return [
{
key: "pref_latest_time_window",
listPreference: {
title: "Preferred latest trend time window",
summary: "",
valueIndex: 0,
entries: ["Day", "Week"],
entryValues: ["day", "week"],
},
},
{
key: "pref_video_resolution",
listPreference: {
title: "Preferred video resolution",
summary: "",
valueIndex: 0,
entries: ["Auto", "1080p", "720p", "360p"],
entryValues: ["auto", "1080", "720", "360"],
},
},
{
key: "pref_content_priority",
listPreference: {
title: "Preferred content priority",
summary: "Choose which type of content to show first",
valueIndex: 0,
entries: ["Movies", "Series"],
entryValues: ["movies", "series"],
},
},
{
key: "autoembed_split_stream_quality",
"switchPreferenceCompat": {
"title": "Split stream into different quality streams",
"summary": "Split stream Auto into 360p/720p/1080p",
"value": true,
},
},
{
key: "autoembed_stream_source_3",
listPreference: {
title: "Preferred stream source",
summary: "",
valueIndex: 0,
entries: [
"tom.autoembed.cc",
"123embed.net",
"autoembed.cc - Indian languages",
"flicky.host - Indian languages",
"vidapi.click",
"hexa.watch",
"vidsrc.su",
"embed.su",
],
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8"],
},
},
{
key: "autoembed_pref_navtive_subtitle",
"switchPreferenceCompat": {
"title": "Use native subtitles as well",
"summary":
"Use subtitles provided by the source along with subtitle API",
"value": true,
},
},
{
key: "autoembed_pref_subtitle_source",
listPreference: {
title: "Preferred subtitle source",
summary: "",
valueIndex: 0,
entries: ["sub.wyzie.ru", "hexa.watch"],
entryValues: ["1", "2"],
},
},
];
}
}

View File

@@ -1,353 +0,0 @@
const mangayomiSources = [{
"name": "Dramacool",
"lang": "all",
"baseUrl": "https://dramacool.com.tr",
"apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://dramacool.com.tr",
"typeSource": "multi",
"itemType": 1,
"version": "1.0.0",
"pkgPath": "anime/src/all/dramacool.js"
}];
class DefaultExtension extends MProvider {
getHeaders(url) {
return {
'Referer': url,
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6788.76 Safari/537.36"
}
}
getPreference(key) {
return new SharedPreferences().get(key);
}
getBaseUrl() {
return this.source.baseUrl;
}
async request(slug) {
const baseUrl = this.getBaseUrl()
var url = `${baseUrl}${slug}`
var res = await new Client().get(url, this.getHeaders(baseUrl));
var doc = new Document(res.body);
return doc
}
async getList(slug) {
var body = await this.request(slug);
var list = []
var hasNextPage = body.selectFirst("a.next.page-numbers").text.length > 0 ? true : false;
var items = body.select(".switch-block.list-episode-item > li")
items.forEach(item => {
var a = item.selectFirst("a")
var link = a.getHref.replace(this.getBaseUrl(), "")
var imageUrl = a.selectFirst("img").getSrc
var name = a.selectFirst("h3").text
list.push({ name, link, imageUrl })
})
return { list, hasNextPage };
}
async getPopular(page) {
var slug = "/most-popular-drama"
return await this.getList(`${slug}/page/${page}/`)
}
get supportsLatest() {
throw new Error("supportsLatest not implemented");
}
async getLatestUpdates(page) {
var slug = this.getPreference("dramacool_latest_list")
return await this.getList(`/${slug}/page/${page}/`)
}
statusFromString(status) {
return {
"Ongoing": 0,
"Completed": 1,
}[status] ?? 5;
}
async search(query, page, filters) {
var slug = `/page/${page}/?type=movies&s=${query}`
return await this.getList(slug)
}
formatReleaseDate(str) {
var timeSplit = str.split(" ")
var t = parseInt(timeSplit[0])
var unit = timeSplit[1]
var mins = 0
var mons = 0
if (unit.includes('minute')) {
mins = t;
} else if (unit.includes('hour')) {
mins = t * 60;
} else if (unit.includes('day')) {
mins = t * 60 * 24;
} else if (unit.includes('week')) {
mins = t * 60 * 24 * 7;
} else if (unit.includes('month')) {
mons = t;
}
var now = new Date();
now.setMinutes(now.getMinutes() - mins)
now.setMinutes(now.getMonth() - mons)
var pastDate = new Date(now);
return "" + pastDate.valueOf();
}
async getDetail(url) {
if (url.includes("-episode")) {
url = '/series' + url.split("-episode")[0] + "/"
} else if (url.includes("-full-movie")) {
url = '/series' + url.split("-full-movie")[0] + "/"
}
var body = await this.request(url);
var infos = body.select(".info > p")
var name = body.selectFirst("h1").text.trim()
var imageUrl = body.selectFirst(".img").selectFirst("img").getSrc
var isDescription = infos[1].text.includes("Description")
var description = isDescription ? infos[2].text.trim() : ""
var link = `${this.getBaseUrl()}${url}`
var statusIndex = infos.at(-3).text.includes("Status:") ? -3 : -2
var status = this.statusFromString(infos.at(statusIndex).selectFirst("a").text)
var genre = []
infos.at(-1).select("a").forEach(a => genre.push(a.text.trim()))
var chapters = []
var epLists = body.select("ul.list-episode-item-2.all-episode > li")
for (var ep of epLists) {
var a = ep.selectFirst('a')
var epLink = a.getHref.replace(this.getBaseUrl(), "")
var epName = a.selectFirst("h3").text.replace(name + " ", "")
var scanlator = a.selectFirst("span.type").text
var dateUpload = this.formatReleaseDate(a.selectFirst("span.time").text)
chapters.push({
name: epName,
url: epLink,
scanlator,
dateUpload
})
}
return { name, imageUrl, description, link, status, genre, chapters }
}
async splitStreams(streams, server) {
var pref = this.getPreference("dramacool_split_stream_quality");
if (!pref) return streams
var autoStream = streams[0]
var autoStreamUrl = autoStream.url
var hdr = autoStream.headers
var hostUrl = ""
if (server == "Asianload") {
hostUrl = autoStreamUrl.substring(0, autoStreamUrl.indexOf("/media"))
} else {
hostUrl = autoStreamUrl.substring(0, autoStreamUrl.indexOf("master.m3u8"))
}
var response = await new Client().get(autoStreamUrl, hdr)
var body = response.body;
var lines = body.split('\n');
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXT-X-STREAM-INF:')) {
var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
resolution = `${server} - ${resolution}`
var m3u8Url = lines[i + 1].trim();
m3u8Url = hostUrl + m3u8Url
streams.push({
url: m3u8Url,
originalUrl: m3u8Url,
quality: resolution,
headers: hdr
});
}
}
return streams
}
decodeBase64(f) {
var g = {},
b = 65,
d = 0,
a, c = 0,
h, e = "",
k = String.fromCharCode,
l = f.length;
for (a = ""; 91 > b;) a += k(b++);
a += a.toLowerCase() + "0123456789+/";
for (b = 0; 64 > b; b++) g[a.charAt(b)] = b;
for (a = 0; a < l; a++)
for (b = g[f.charAt(a)], d = (d << 6) + b, c += 6; 8 <= c;)((h = d >>> (c -= 8) & 255) || a < l - 2) && (e += k(h));
return e
};
async extractDramacoolEmbed(doc) {
var streams = []
var script = doc.select('script').at(-2)
var unpack = unpackJs(script.text)
var skey = 'hls2":"'
var eKey = '"};jwplayer'
var start = unpack.indexOf(skey) + skey.length
var end = unpack.indexOf(eKey, start)
var track = unpack.substring(start, end)
streams.push({
url: track,
originalUrl: track,
quality: "Dramacool - Auto",
headers: this.getHeaders("https://dramacool.men/")
});
streams = await this.splitStreams(streams, "Dramacool")
return streams
}
async extractAsianLoadEmbed(doc) {
var streams = []
var script = doc.select('script').at(-2)
var unpack = script.text
// tracks
var skey = '|image|'
var eKey = '|'
var start = unpack.indexOf(skey) + skey.length
var end = unpack.indexOf(eKey, start)
var track = unpack.substring(start, end)
var streamUrl = this.decodeBase64(track)
// subs
eKey = "|default|"
var end = unpack.indexOf(eKey)
var subs = []
if (end != -1) {
skey = "|type|"
var start = unpack.indexOf(skey) + skey.length
var subTracks = unpack.substring(start, end).split("|")
subs.push({
file: this.decodeBase64(subTracks[1]),
label: subTracks[0]
})
}
streams.push({
url: streamUrl,
originalUrl: streamUrl,
quality: "Asianload - Auto",
subtitles: subs,
headers: this.getHeaders("https://asianload.cfd/")
});
// Download url
skey = '|_blank|'
eKey = '|'
start = unpack.indexOf(skey) + skey.length
end = unpack.indexOf(eKey, start)
track = unpack.substring(start, end)
var downUrl = this.decodeBase64(track)
streams.push({
url: downUrl,
originalUrl: downUrl,
quality: "Asianload - Direct download",
headers: this.getHeaders("https://asianload.cfd/")
});
streams = await this.splitStreams(streams, "Asianload")
return streams
}
// Sorts streams based on user preference.
async sortStreams(streams) {
var sortedStreams = [];
var copyStreams = streams.slice()
var pref = this.getPreference("dramacool_video_resolution");
for (var i in streams) {
var stream = streams[i];
if (stream.quality.indexOf(pref) > -1) {
sortedStreams.push(stream);
var index = copyStreams.indexOf(stream);
if (index > -1) {
copyStreams.splice(index, 1);
}
break;
}
}
return [...sortedStreams, ...copyStreams]
}
// For anime episode video list
async getVideoList(url) {
var res = await this.request(url)
var iframe = res.selectFirst("iframe").attr("src").trim()
if (iframe == "") {
throw new Error("No iframe found")
}
var streams = []
res = await new Client().get(iframe)
var doc = new Document(res.body);
if (iframe.includes("//dramacool")) {
streams = await this.extractDramacoolEmbed(doc)
} else if (iframe.includes("//asianload")) {
streams = await this.extractAsianLoadEmbed(doc)
}
return this.sortStreams(streams)
}
getSourcePreferences() {
return [
{
key: 'dramacool_latest_list',
listPreference: {
title: 'Preferred latest list',
summary: 'Choose which type of content to be shown "Lastest"',
valueIndex: 0,
entries: ["Drama", "Movie", "KShow"],
entryValues: ["recently-added-drama", "recently-added-movie", "recently-added-kshow"]
}
},
{
key: 'dramacool_split_stream_quality',
switchPreferenceCompat: {
title: 'Split stream into different quality streams',
summary: "Split stream Auto into 360p/720p/1080p",
value: true
}
}, {
key: 'dramacool_video_resolution',
listPreference: {
title: 'Preferred video resolution',
summary: '',
valueIndex: 0,
entries: ["Auto", "Direct download", "720p", "480", "360p"],
entryValues: ["Auto", "download", "720", "480", "360"]
}
},
]
}
}

View File

@@ -1,333 +0,0 @@
const mangayomiSources = [{
"name": "NetMirror",
"id": 446414301,
"lang": "all",
"baseUrl": "https://netfree2.cc",
"apiUrl": "https://netfree2.cc",
"iconUrl": "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/javascript/icon/all.netflixmirror.png",
"typeSource": "single",
"itemType": 1,
"version": "0.3.4",
"pkgPath": "anime/src/all/netflixmirror.js"
}];
class DefaultExtension extends MProvider {
constructor() {
super();
this.client = new Client();
}
getPreference(key) {
const preferences = new SharedPreferences();
return preferences.get(key);
}
getTVBaseUrl() {
return this.getPreference("netmirror_override_tv_base_url");
}
getServiceDetails() {
return this.getPreference("netmirror_pref_service");
}
getPoster(id, service) {
if (service === "nf")
return `https://imgcdn.media/poster/v/${id}.jpg`
if (service === "pv")
return `https://imgcdn.media/pv/480/${id}.jpg`
}
async getCookie(service) {
const preferences = new SharedPreferences();
let cookie = preferences.getString("cookie", "");
var cookie_ts = parseInt(preferences.getString("cookie_ts", "0"));
var now_ts = parseInt(new Date().getTime() / 1000);
// Cookie lasts for 24hrs but still checking for 12hrs
if (now_ts - cookie_ts > 60 * 60 * 12) {
var baseUrl = this.getTVBaseUrl()
const check = await this.client.get(baseUrl + `/mobile/home`, { "cookie": cookie });
const hDocBody = new Document(check.body).selectFirst("body")
const addhash = hDocBody.attr("data-addhash");
const data_time = hDocBody.attr("data-time");
var res = await this.client.post(`${baseUrl}/tv/p.php`, { "cookie": "" }, { "hash": addhash });
cookie = res.headers["set-cookie"];
preferences.setString("cookie", cookie);
preferences.setString("cookie_ts", data_time);
}
service = service ?? this.getServiceDetails();
return `ott=${service}; ${cookie}`;
}
async request(slug, service = null, cookie = null) {
var service = service ?? this.getServiceDetails();
var cookie = cookie ?? await this.getCookie();
var srv = ""
if (service === "pv") srv = "/" + service
var url = this.getTVBaseUrl() + "/tv" + srv + slug
return (await this.client.get(url, { "cookie": cookie })).body;
}
async getHome(body) {
var service = this.getServiceDetails();
var list = []
if (service === "nf") {
var body = await this.request("/home", service)
var elements = new Document(body).select("a.slider-item.boxart-container.open-modal.focusme");
elements.forEach(item => {
var id = item.attr("data-post")
if (id.length > 0) {
var imageUrl = this.getPoster(id, service)
// Having no name breaks the script so having "id" as name
var name = `\n${id}`
list.push({ name, imageUrl, link: id })
}
})
} else {
var body = await this.request("/homepage.php", service)
var elements = JSON.parse(body).post
elements.forEach(item => {
var ids = item.ids
ids.split(",").forEach(id => {
var imageUrl = this.getPoster(id, service)
// Having no name breaks the script so having "id" as name
var name = `\n${id}`
list.push({ name, imageUrl, link: id })
})
})
}
return {
list: list,
hasNextPage: false
}
}
async getPopular(page) {
return await this.getHome()
}
async getLatestUpdates(page) {
return await this.getHome()
}
async search(query, page, filters) {
var service = this.getServiceDetails();
const data = JSON.parse(await this.request(`/search.php?s=${query}`, service));
const list = [];
data.searchResult.map(async (res) => {
const id = res.id;
list.push({ name: res.t, imageUrl: this.getPoster(id, service), link: id });
})
return {
list: list,
hasNextPage: false
}
}
async getDetail(url) {
var service = this.getServiceDetails();
var cookie = await this.getCookie(service);
var linkSlug = "https://netflix.com/title/"
if (service === "pv") linkSlug = `https://www.primevideo.com/detail/`
// Check needed while refreshing existing data
var vidId = url
if (url.includes(linkSlug)) vidId = url.replaceAll(linkSlug, '')
const data = JSON.parse(await this.request(`/post.php?id=${vidId}`));
const name = data.title;
const genre = [data.ua, ...(data.genre || '').split(',').map(g => g.trim())];
const description = data.desc;
let episodes = [];
var seasons = data.season
if (seasons) {
let newEpisodes = [];
await Promise.all(seasons.map(async (season) => {
const eps = await this.getEpisodes(name, vidId, season.id, 1, service, cookie);
newEpisodes.push(...eps);
}));
episodes.push(...newEpisodes);
} else {
// For movies aka if there are no seasons and episodes
episodes.push({
name: `Movie`,
url: vidId
});
}
var link = `${linkSlug}${vidId}`
return {
name, imageUrl: this.getPoster(vidId, service), link, description, status: 1, genre, episodes
};
}
async getEpisodes(name, eid, sid, page, service, cookie) {
const episodes = [];
let pg = page;
while (true) {
try {
const data = JSON.parse(await this.request(`/episodes.php?s=${sid}&series=${eid}&page=${pg}`, service, cookie));
data.episodes?.forEach(ep => {
var season = ep.s.replace('S', 'Season ')
var epNum = ep.ep.replace("E", "")
var epText = `Episode ${epNum}`
var title = ep.t
title = title == epText ? title : `${epText}: ${title}`
episodes.push({
name: `${season} ${title}`,
url: ep.id
});
});
if (data.nextPageShow === 0) break;
pg++;
} catch (_) {
break;
}
}
return episodes.reverse();
}
// Sorts streams based on user preference.
async sortStreams(streams) {
var sortedStreams = [];
var copyStreams = streams.slice()
var pref = this.getPreference("netmirror_pref_video_resolution");
for (var i in streams) {
var stream = streams[i];
if (stream.quality.indexOf(pref) > -1) {
sortedStreams.push(stream);
var index = copyStreams.indexOf(stream);
if (index > -1) {
copyStreams.splice(index, 1);
}
break;
}
}
return [...sortedStreams, ...copyStreams]
}
async getVideoList(url) {
var baseUrl = this.getTVBaseUrl()
var url = `/playlist.php?id=${url}`
const data = JSON.parse(await this.request(url));
let videoList = [];
let subtitles = [];
let audios = [];
var playlist = data[0]
var source = playlist.sources[0]
var link = baseUrl + source.file;
var headers =
{
'Origin': baseUrl,
'Referer': `${baseUrl}/`
};
// Auto
videoList.push({ url: link, quality: "Auto", "originalUrl": link, headers });
var resp = await this.client.get(link, headers);
if (resp.statusCode === 200) {
const masterPlaylist = resp.body;
if (masterPlaylist.indexOf("#EXT-X-STREAM-INF:") > 1) {
masterPlaylist.substringAfter('#EXT-X-MEDIA:').split('#EXT-X-MEDIA:').forEach(it => {
if (it.includes('TYPE=AUDIO')) {
const audioInfo = it.substringAfter('TYPE=AUDIO').substringBefore('\n');
const language = audioInfo.substringAfter('NAME="').substringBefore('"');
const url = audioInfo.substringAfter('URI="').substringBefore('"');
audios.push({ file: url, label: language });
}
});
masterPlaylist.substringAfter('#EXT-X-STREAM-INF:').split('#EXT-X-STREAM-INF:').forEach(it => {
var quality = `${it.substringAfter('RESOLUTION=').substringAfter('x').substringBefore(',')}p`;
let videoUrl = it.substringAfter('\n').substringBefore('\n');
if (!videoUrl.startsWith('http')) {
videoUrl = resp.request.url.substringBeforeLast('/') + `/${videoUrl}`;
}
headers['Host'] = videoUrl.match(/^(?:https?:\/\/)?(?:www\.)?([^\/]+)/)[1]
videoList.push({ url: videoUrl, quality, originalUrl: videoUrl, headers });
});
}
if ("tracks" in playlist) {
await Promise.all(playlist.tracks.map(async (track) => {
if (track.kind == 'captions') {
var subUrl = track.file
subUrl = subUrl.startsWith("//") ? `https:${subUrl}` : subUrl;
var subText = await this.client.get(subUrl)
subtitles.push({
label: track.label,
file: subText.body
});
}
}));
}
}
videoList[0].audios = audios;
videoList[0].subtitles = subtitles;
return this.sortStreams(videoList);
}
getSourcePreferences() {
return [{
key: "netmirror_override_tv_base_url",
editTextPreference: {
title: "Override tv base url",
summary: "",
value: "https://netfree2.cc",
dialogTitle: "Override base url",
dialogMessage: "",
}
}, {
key: 'netmirror_pref_service',
listPreference: {
title: 'Preferred OTT service',
summary: '',
valueIndex: 0,
entries: ["Net mirror", "Prime mirror"],
entryValues: ["nf", "pv",]
}
}, {
key: 'netmirror_pref_video_resolution',
listPreference: {
title: 'Preferred video resolution',
summary: '',
valueIndex: 0,
entries: ["1080p", "720p", "480p"],
entryValues: ["1080", "720", "480"]
}
}
];
}
}

View File

@@ -1,279 +0,0 @@
const mangayomiSources = [
{
"name": "Soaper",
"id": 764093578,
"lang": "all",
"baseUrl": "https://soaper.cc",
"apiUrl": "",
"iconUrl":
"https://www.google.com/s2/favicons?sz=128&domain=https://soaper.cc/",
"typeSource": "multi",
"version": "1.0.5",
"itemType": 1,
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "anime/src/all/soaper.js"
}
];
// Authors: - Swakshan, kodjodevf
class DefaultExtension extends MProvider {
getHeaders(url) {
return {
Referer: url,
Origin: url,
};
}
getPreference(key) {
return new SharedPreferences().get(key);
}
getBasueUrl() {
return this.getPreference("soaper_override_base_url");
}
async request(slug) {
const baseUrl = this.getBasueUrl();
var url = `${baseUrl}/${slug}`;
var res = await new Client().get(url, this.getHeaders(baseUrl));
var doc = new Document(res.body);
return doc;
}
async requestJSON(slug, data) {
const baseUrl = this.getBasueUrl();
var url = `${baseUrl}/${slug}`;
var res = await new Client().post(url, this.getHeaders(baseUrl), data);
return JSON.parse(res.body);
}
async formatList(slug, page) {
const baseUrl = this.getPreference("soaper_override_base_url");
slug = parseInt(page) > 1 ? `${slug}?page=${page}` : slug;
var doc = await this.request(slug);
var list = [];
var movies = doc.select(".thumbnail.text-center");
for (var movie of movies) {
var linkSection = movie.selectFirst("div.img-group > a");
var link = linkSection.getHref.substring(1);
var poster = linkSection.selectFirst("img").getSrc;
var imageUrl = `${baseUrl}${poster}`;
var name = movie.selectFirst("h5").selectFirst("a").text;
list.push({ name, imageUrl, link });
}
var hasNextPage = false;
if (slug.indexOf("search.html?") == -1) {
var pagination = doc.select("ul.pagination > li");
var last_page_num = parseInt(pagination[pagination.length - 2].text);
hasNextPage = page < last_page_num ? true : false;
}
return { list, hasNextPage };
}
async filterList(year = "all", genre = "all", sort = "new", page = 1) {
year = year == "all" ? "" : `/year/${year}`;
genre = genre == "all" ? "" : `/cat/${genre}`;
sort = sort == "new" ? "" : `/sort/${sort}`;
var slug = `${sort}${year}${genre}`;
var movieList = await this.formatList(`movielist${slug}`, page);
var seriesList = await this.formatList(`tvlist${slug}`, page);
var list = [];
var priority = this.getPreference("soaper_content_priority");
if (priority === "series") {
list = [...seriesList.list, ...movieList.list];
} else {
list = [...movieList.list, ...seriesList.list];
}
var hasNextPage = seriesList.hasNextPage || movieList.hasNextPage;
return { list, hasNextPage };
}
async getPopular(page) {
return await this.filterList("all", "all", "hot", page);
}
get supportsLatest() {
throw new Error("supportsLatest not implemented");
}
async getLatestUpdates(page) {
return await this.filterList("all", "all", "new", page);
}
async search(query, page, filters) {
var seriesList = [];
var movieList = [];
var list = [];
var res = await this.formatList(`search.html?keyword=${query}`, 1);
var movies = res["list"];
for (var movie of movies) {
var link = movie.link;
if (link.indexOf("tv_") != -1) {
seriesList.push(movie);
} else {
movieList.push(movie);
}
}
var priority = this.getPreference("soaper_content_priority");
if (priority === "series") {
list = [...seriesList, ...movieList];
} else {
list = [...movieList, ...seriesList];
}
return { list, hasNextPage: false };
}
async getDetail(url) {
const baseUrl = this.getPreference("soaper_override_base_url");
var slug = url.replace(`${baseUrl}/`,'')
var doc = await this.request(slug);
var name = doc
.selectFirst(".col-sm-12.col-lg-12.text-center")
.selectFirst("h4")
.text.trim();
var poster = doc
.selectFirst(".thumbnail.text-center")
.selectFirst("img").getSrc;
var imageUrl = `${baseUrl}${poster}`;
var description = doc.selectFirst("p#wrap").text.trim();
var link = `${baseUrl}/${slug}`;
var chapters = [];
if (slug.indexOf("tv_") != -1) {
var seasonList = doc.select(".alert.alert-info-ex.col-sm-12");
var seasonCount = seasonList.length;
for (var season of seasonList) {
var eps = season.select(".col-sm-12.col-md-6.col-lg-4.myp1");
for (var ep of eps) {
var epLinkSection = ep.selectFirst("a");
var epLink = epLinkSection.getHref.substring(1);
var epName = epLinkSection.text;
chapters.push({
name: `S${seasonCount}E${epName}`,
url: epLink,
});
}
seasonCount--;
}
} else {
chapters.push({
name: "Movie",
url: slug,
});
}
return { name, imageUrl, description, link, chapters };
}
// For anime episode video list
async getVideoList(url) {
var body = await this.request(url);
var baseUrl = this.getBasueUrl();
var streams = [];
// Traditional servers
var eId = body.selectFirst("#hId").attr("value");
var hIsW = body.selectFirst("#hIsW").attr("value");
var apiType = url[0].toUpperCase();
var servers = [0, 1];
for (var serverNum of servers) {
var serverName = body.selectFirst(`#server_button_${serverNum}`).text;
if (serverName.length < 1) continue;
var data = {
pass: eId,
param: "",
extra: "1",
e2: hIsW,
server: "" + serverNum,
};
var res = await this.requestJSON(
`home/index/Get${apiType}InfoAjax`,
data
);
var streamUrl = baseUrl + res.val;
var subs = [];
var vidSubs = res.subs;
if (vidSubs != null && vidSubs.length > 0) {
for (var sub of vidSubs) {
subs.push({
file: baseUrl + sub.path,
label: sub.name,
});
}
}
streams.push({
url: streamUrl,
originalUrl: streamUrl,
quality: serverName,
subtitles: subs,
});
}
// Download servers
var modal_footer = body.select(".modal-footer > a");
if (modal_footer.length > 0) {
modal_footer.reverse();
for (var item of modal_footer) {
var dSlug = item.getHref;
var dBody = await this.request(dSlug);
var res = dBody.selectFirst("#res").attr("value");
var mb = dBody.selectFirst("#mb").attr("value");
var streamLink = dBody.selectFirst("#link").attr("value");
streams.push({
url: streamLink,
originalUrl: streamLink,
quality: `Download Server: ${res} [${mb}]`,
});
}
}
return streams;
}
// For manga chapter pages
async getPageList() {
throw new Error("getPageList not implemented");
}
getFilterList() {
throw new Error("getFilterList not implemented");
}
getSourcePreferences() {
return [
{
key: "soaper_override_base_url",
editTextPreference: {
title: "Override base url",
summary: "Default: https://soaper.cc",
value: "https://soaper.cc",
dialogTitle: "Override base url",
dialogMessage: "",
},
},
{
key: "soaper_content_priority",
listPreference: {
title: "Preferred content priority",
summary: "Choose which type of content to show first",
valueIndex: 0,
entries: ["Movies", "Series"],
entryValues: ["movies", "series"],
},
},
];
}
}

View File

@@ -1,660 +0,0 @@
const mangayomiSources = [{
"name": "Torrentio (Torrent)",
"lang": "all",
"baseUrl": "https://torrentio.strem.fun",
"apiUrl": "",
"iconUrl": "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/javascript/icon/all.torrentio.png",
"typeSource": "torrent",
"isManga": false,
"itemType": 1,
"version": "0.0.25",
"pkgPath": "anime/src/all/torrentio.js"
}];
class DefaultExtension extends MProvider {
constructor() {
super();
this.client = new Client();
}
justWatchQuery() {
return `
query GetPopularTitles(
$country: Country!,
$first: Int!,
$language: Language!,
$offset: Int,
$searchQuery: String,
$packages: [String!]!,
$objectTypes: [ObjectType!]!,
$popularTitlesSortBy: PopularTitlesSorting!,
$releaseYear: IntFilter
) {
popularTitles(
country: $country
first: $first
offset: $offset
sortBy: $popularTitlesSortBy
filter: {
objectTypes: $objectTypes,
searchQuery: $searchQuery,
packages: $packages,
genres: [],
excludeGenres: [],
releaseYear: $releaseYear
}
) {
edges {
node {
id
objectType
content(country: $country, language: $language) {
fullPath
title
shortDescription
externalIds {
imdbId
}
posterUrl
genres {
translation(language: $language)
}
credits {
name
role
}
}
}
}
pageInfo {
hasPreviousPage
hasNextPage
}
}
}
`.trim();
}
async makeGraphQLRequest(query, variables) {
const res = await this.client.post("https://apis.justwatch.com/graphql", { "Content-Type": "application/json" },
{
query: query,
variables
});
return res;
}
async searchAnimeRequest(page, query) {
const preferences = new SharedPreferences();
const country = preferences.get("jw_region1");
const language = preferences.get("jw_lang");
const perPage = 40;
const year = 0;
const searchQueryRegex = /[^a-zA-Z0-9 ]/g;
const sanitizedQuery = query.replace(searchQueryRegex, "").trim();
const variables = {
first: perPage,
offset: (page - 1) * perPage,
platform: "WEB",
country: country,
language: language,
searchQuery: sanitizedQuery,
packages: [],
objectTypes: [],
popularTitlesSortBy: "TRENDING",
releaseYear: {
min: year,
max: year
}
};
return await this.makeGraphQLRequest(this.justWatchQuery(), variables);
}
parseSearchJson(jsonLine) {
const popularTitlesResponse = JSON.parse(jsonLine);
const edges = popularTitlesResponse?.data?.popularTitles?.edges || [];
const hasNextPage = popularTitlesResponse?.data?.popularTitles?.pageInfo?.hasNextPage || false;
const animeList = edges
.map(edge => {
const node = edge?.node;
const content = node?.content;
if (!node || !content) return null;
return {
link: `${content.externalIds?.imdbId || ""},${node.objectType || ""},${content.fullPath || ""}`,
name: content.title || "",
imageUrl: `https://images.justwatch.com${content.posterUrl?.replace("{profile}", "s276")?.replace("{format}", "webp")}`,
description: content.shortDescription || "",
genre: content.genres?.map(genre => genre.translation).filter(Boolean) || [],
author: (content.credits?.filter(credit => credit.role === "DIRECTOR").map(credit => credit.name) || []).join(", "),
artist: (content.credits?.filter(credit => credit.role === "ACTOR").slice(0, 4).map(credit => credit.name) || []).join(", "),
};
})
.filter(Boolean);
return { "list": animeList, hasNextPage };
}
get supportsLatest() {
return false;
}
async getPopular(page) {
return this.parseSearchJson((await this.searchAnimeRequest(page, "")).body);
}
async getLatestUpdates(page) {
}
async search(query, page, filters) {
return this.parseSearchJson((await this.searchAnimeRequest(page, query)).body);
}
async getDetail(url) {
const anime = {};
const parts = url.split(",");
const type = parts[1].toLowerCase();
const imdbId = parts[0];
const response = await this.client.get(`https://cinemeta-live.strem.io/meta/${type}/${imdbId}.json`);
const meta = JSON.parse(response.body).meta;
if (!meta) return anime;
anime.episodes = (() => {
switch (meta.type) {
case "show":
const videos = meta.videos || [];
return videos
.filter(video => (video.firstAired ? new Date(video.firstAired) : Date.now()) < Date.now())
.map(video => {
const firstAired = video.firstAired ? new Date(video.firstAired) : Date.now();
return {
url: `/stream/series/${video.id}.json`,
dateUpload: firstAired.valueOf().toString(),
name: `S${(video.season || "").toString().trim()}:E${(video.number || "").toString()} - ${video.name || ""}`,
};
})
.sort((a, b) => {
const seasonA = parseInt(a.name.substringAfter("S").substringBefore(":"), 10);
const seasonB = parseInt(b.name.substringAfter("S").substringBefore(":"), 10);
const episodeA = parseInt(a.name.substringAfter("E").substringBefore(" -"), 10);
const episodeB = parseInt(b.name.substringAfter("E").substringBefore(" -"), 10);
return seasonA - seasonB || episodeA - episodeB;
})
.reverse();
case "movie":
return [
{
url: `/stream/movie/${meta.id}.json`,
name: "Movie"
}
].reverse();
default:
return [];
}
})();
return anime;
}
appendQueryParam(key, values) {
let url = "";
if (values && values.length > 0) {
const filteredValues = Array.from(values).filter(value => value.trim() !== "").join(",");
url += `${key}=${filteredValues}|`;
}
return url;
};
async getVideoList(url) {
const preferences = new SharedPreferences();
let mainURL = `${this.source.baseUrl}/`;
mainURL += this.appendQueryParam("providers", preferences.get("provider_selection1"));
mainURL += this.appendQueryParam("language", preferences.get("lang_selection"));
mainURL += this.appendQueryParam("qualityfilter", preferences.get("quality_selection"));
mainURL += this.appendQueryParam("sort", new Set([preferences.get("sorting_link")]));
mainURL += url;
mainURL = mainURL.replace(/\|$/, "");
const responseEpisodes = await this.client.get(mainURL);
const streamList = JSON.parse(responseEpisodes.body);
const animeTrackers = `
http://nyaa.tracker.wf:7777/announce,
http://anidex.moe:6969/announce,http://tracker.anirena.com:80/announce,
udp://tracker.uw0.xyz:6969/announce,
http://share.camoe.cn:8080/announce,
http://t.nyaatracker.com:80/announce,
udp://47.ip-51-68-199.eu:6969/announce,
udp://9.rarbg.me:2940,
udp://9.rarbg.to:2820,
udp://exodus.desync.com:6969/announce,
udp://explodie.org:6969/announce,
udp://ipv4.tracker.harry.lu:80/announce,
udp://open.stealth.si:80/announce,
udp://opentor.org:2710/announce,
udp://opentracker.i2p.rocks:6969/announce,
udp://retracker.lanta-net.ru:2710/announce,
udp://tracker.cyberia.is:6969/announce,
udp://tracker.dler.org:6969/announce,
udp://tracker.ds.is:6969/announce,
udp://tracker.internetwarriors.net:1337,
udp://tracker.openbittorrent.com:6969/announce,
udp://tracker.opentrackr.org:1337/announce,
udp://tracker.tiny-vps.com:6969/announce,
udp://tracker.torrent.eu.org:451/announce,
udp://valakas.rollo.dnsabr.com:2710/announce,
udp://www.torrent.eu.org:451/announce
`.split(",").map(tracker => tracker.trim()).filter(tracker => tracker);
const videos = this.sortVideos((streamList.streams || []).map(stream => {
const hash = `magnet:?xt=urn:btih:${stream.infoHash}&dn=${stream.infoHash}&tr=${animeTrackers.join("&tr=")}&index=${stream.fileIdx}`;
const videoTitle = `${(stream.name || "").replace("Torrentio\n", "")}\n${stream.title || ""}`.trim();
return {
url: hash,
originalUrl: hash,
quality: videoTitle,
};
}));
const numberOfLinks = preferences.get("number_of_links");
if (numberOfLinks == "all") {
return videos;
}
return videos.slice(0, parseInt(numberOfLinks))
}
sortVideos(videos) {
const preferences = new SharedPreferences();
const isDub = preferences.get("dubbed");
const isEfficient = preferences.get("efficient");
return videos.sort((a, b) => {
const regexMatchA = /\[(.+?) download\]/.test(a.quality);
const regexMatchB = /\[(.+?) download\]/.test(b.quality);
const isDubA = isDub && !a.quality.toLowerCase().includes("dubbed");
const isDubB = isDub && !b.quality.toLowerCase().includes("dubbed");
const isEfficientA = isEfficient && !["hevc", "265", "av1"].some(q => a.quality.toLowerCase().includes(q));
const isEfficientB = isEfficient && !["hevc", "265", "av1"].some(q => b.quality.toLowerCase().includes(q));
return (
regexMatchA - regexMatchB ||
isDubA - isDubB ||
isEfficientA - isEfficientB
);
});
}
getSourcePreferences() {
return [
{
"key": "number_of_links",
"listPreference": {
"title": "Number of links to load for video list",
"summary": "⚠️ Increasing the number of links will increase the loading time of the video list",
"valueIndex": 1,
"entries": [
"2",
"4",
"8",
"12",
"all"],
"entryValues": [
"2",
"4",
"8",
"12",
"all"],
}
},
{
"key": "provider_selection1",
"multiSelectListPreference": {
"title": "Enable/Disable Providers",
"summary": "",
"entries": [
"YTS",
"EZTV",
"RARBG",
"1337x",
"ThePirateBay",
"KickassTorrents",
"TorrentGalaxy",
"MagnetDL",
"HorribleSubs",
"NyaaSi",
"TokyoTosho",
"AniDex",
"🇷🇺 Rutor",
"🇷🇺 Rutracker",
"🇵🇹 Comando",
"🇵🇹 BluDV",
"🇫🇷 Torrent9",
"🇪🇸 MejorTorrent",
"🇲🇽 Cinecalidad"],
"entryValues": [
"yts",
"eztv",
"rarbg",
"1337x",
"thepiratebay",
"kickasstorrents",
"torrentgalaxy",
"magnetdl",
"horriblesubs",
"nyaasi",
"tokyotosho",
"anidex",
"rutor",
"rutracker",
"comando",
"bludv",
"torrent9",
"mejortorrent",
"cinecalidad"],
"values": [
"yts",
"eztv",
"rarbg",
"1337x",
"thepiratebay",
"kickasstorrents",
"torrentgalaxy",
"magnetdl",
"horriblesubs",
"nyaasi",
"tokyotosho",
"anidex",
"rutor",
"rutracker",
"comando",
"bludv",
"torrent9",
"mejortorrent",
"cinecalidad"]
}
},
{
"key": "quality_selection",
"multiSelectListPreference": {
"title": "Exclude Qualities/Resolutions",
"summary": "",
"entries": [
"BluRay REMUX",
"HDR/HDR10+/Dolby Vision",
"Dolby Vision",
"4k",
"1080p",
"720p",
"480p",
"Other (DVDRip/HDRip/BDRip...)",
"Screener",
"Cam",
"Unknown"],
"entryValues": [
"brremux",
"hdrall",
"dolbyvision",
"4k",
"1080p",
"720p",
"480p",
"other",
"scr",
"cam",
"unknown"],
"values": [
"720p",
"480p",
"other",
"scr",
"cam",
"unknown"]
}
},
{
"key": "lang_selection",
"multiSelectListPreference": {
"title": "Priority foreign language",
"summary": "",
"entries": [
"🇯🇵 Japanese",
"🇷🇺 Russian",
"🇮🇹 Italian",
"🇵🇹 Portuguese",
"🇪🇸 Spanish",
"🇲🇽 Latino",
"🇰🇷 Korean",
"🇨🇳 Chinese",
"🇹🇼 Taiwanese",
"🇫🇷 French",
"🇩🇪 German",
"🇳🇱 Dutch",
"🇮🇳 Hindi",
"🇮🇳 Telugu",
"🇮🇳 Tamil",
"🇵🇱 Polish",
"🇱🇹 Lithuanian",
"🇱🇻 Latvian",
"🇪🇪 Estonian",
"🇨🇿 Czech",
"🇸🇰 Slovakian",
"🇸🇮 Slovenian",
"🇭🇺 Hungarian",
"🇷🇴 Romanian",
"🇧🇬 Bulgarian",
"🇷🇸 Serbian",
"🇭🇷 Croatian",
"🇺🇦 Ukrainian",
"🇬🇷 Greek",
"🇩🇰 Danish",
"🇫🇮 Finnish",
"🇸🇪 Swedish",
"🇳🇴 Norwegian",
"🇹🇷 Turkish",
"🇸🇦 Arabic",
"🇮🇷 Persian",
"🇮🇱 Hebrew",
"🇻🇳 Vietnamese",
"🇮🇩 Indonesian",
"🇲🇾 Malay",
"🇹🇭 Thai",],
"entryValues": [
"japanese",
"russian",
"italian",
"portuguese",
"spanish",
"latino",
"korean",
"chinese",
"taiwanese",
"french",
"german",
"dutch",
"hindi",
"telugu",
"tamil",
"polish",
"lithuanian",
"latvian",
"estonian",
"czech",
"slovakian",
"slovenian",
"hungarian",
"romanian",
"bulgarian",
"serbian",
"croatian",
"ukrainian",
"greek",
"danish",
"finnish",
"swedish",
"norwegian",
"turkish",
"arabic",
"persian",
"hebrew",
"vietnamese",
"indonesian",
"malay",
"thai"],
"values": []
}
},
{
"key": "sorting_link",
"listPreference": {
"title": "Sorting",
"summary": "",
"valueIndex": 0,
"entries": [
"By quality then seeders",
"By quality then size",
"By seeders",
"By size"],
"entryValues": [
"quality",
"qualitysize",
"seeders",
"size"],
}
},
{
"key": "dubbed",
"switchPreferenceCompat": {
"title": "Dubbed Video Priority",
"summary": "",
"value": false
}
},
{
"key": "efficient",
"switchPreferenceCompat": {
"title": "Efficient Video Priority",
"summary": "Codec: (HEVC / x265) & AV1. High-quality video with less data usage.",
"value": false
}
},
{
"key": "jw_region1",
"listPreference": {
"title": "Catalogue Region",
"summary": "Region based catalogue recommendation.",
"valueIndex": 132,
"entries": [
"Albania", "Algeria", "Androrra", "Angola", "Antigua and Barbuda", "Argentina", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Barbados", "Belarus", "Belgium", "Belize", "Bermuda", "Bolivia", "Bosnia and Herzegovina", "Brazil", "Bulgaria", "Burkina Faso", "Cameroon", "Canada", "Cape Verde", "Chad", "Chile", "Colombia", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", "DR Congo", "Denmark", "Dominican Republic", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Estonia", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", "Germany", "Ghana", "Gibraltar", "Greece", "Guatemala", "Guernsey", "Guyana", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iraq", "Ireland", "Israel", "Italy", "Ivory Coast", "Jamaica", "Japan", "Jordan", "Kenya", "Kosovo", "Kuwait", "Latvia", "Lebanon", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macedonia", "Madagascar", "Malawi", "Malaysia", "Mali", "Malta", "Mauritius", "Mexico", "Moldova", "Monaco", "Montenegro", "Morocco", "Mozambique", "Netherlands", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Poland", "Portugal", "Qatar", "Romania", "Russia", "Saint Lucia", "San Marino", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea", "Spain", "Sweden", "Switzerland", "Taiwan", "Tanzania", "Thailand", "Trinidad and Tobago", "Tunisia", "Turkey", "Turks and Caicos Islands", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", "United States", "Uruguay", "Vatican City", "Venezuela", "Yemen", "Zambia", "Zimbabwe"],
"entryValues": [
"AL", "DZ", "AD", "AO", "AG", "AR", "AU", "AT", "AZ", "BS", "BH", "BB", "BY", "BE", "BZ", "BM", "BO", "BA", "BR", "BG", "BF", "CM", "CA", "CV", "TD", "CL", "CO", "CR", "HR", "CU", "CY", "CZ", "CD", "DK", "DO", "EC", "EG", "SV", "GQ", "EE", "FJ", "FI", "FR", "GF", "PF", "DE", "GH", "GI", "GR", "GT", "GG", "GY", "HN", "HK", "HU", "IS", "IN", "ID", "IQ", "IE", "IL", "IT", "CI", "JM", "JP", "JO", "KE", "XK", "KW", "LV", "LB", "LY", "LI", "LT", "LU", "MK", "MG", "MW", "MY", "ML", "MT", "MU", "MX", "MD", "MC", "ME", "MA", "MZ", "NL", "NZ", "NI", "NE", "NG", "NO", "OM", "PK", "PS", "PA", "PG", "PY", "PE", "PH", "PL", "PT", "QA", "RO", "RU", "LC", "SM", "SA", "SN", "RS", "SC", "SG", "SK", "SI", "ZA", "KR", "ES", "SE", "CH", "TW", "TZ", "TH", "TT", "TN", "TR", "TC", "UG", "UA", "AE", "UK", "US", "UY", "VA", "VE", "YE", "ZM", "ZW"],
}
},
{
"key": "jw_lang",
"listPreference": {
"title": "Poster and Titles Language",
"summary": "",
"valueIndex": 9,
"entries": [
"Arabic",
"Azerbaijani",
"Belarusian",
"Bulgarian",
"Bosnian",
"Catalan",
"Czech",
"German",
"Greek",
"English",
"English (U.S.A.)",
"Spanish",
"Spanish (Spain)",
"Spanish (Latinamerican)",
"Estonian",
"Finnish",
"French",
"French (Canada)",
"Hebrew",
"Croatian",
"Hungarian",
"Icelandic",
"Italian",
"Japanese",
"Korean",
"Lithuanian",
"Latvian",
"Macedonian",
"Maltese",
"Polish",
"Portuguese",
"Portuguese (Portugal)",
"Portuguese (Brazil)",
"Romanian",
"Russian",
"Slovakian",
"Slovenian",
"Albanian",
"Serbian",
"Swedish",
"Swahili",
"Turkish",
"Ukrainian",
"Urdu",
"Chinese"],
"entryValues": [
"ar",
"az",
"be",
"bg",
"bs",
"ca",
"cs",
"de",
"el",
"en",
"en-US",
"es",
"es-ES",
"es-LA",
"et",
"fi",
"fr",
"fr-CA",
"he",
"hr",
"hu",
"is",
"it",
"ja",
"ko",
"lt",
"lv",
"mk",
"mt",
"pl",
"pt",
"pt-PT",
"pt-BR",
"ro",
"ru",
"sk",
"sl",
"sq",
"sr",
"sv",
"sw",
"tr",
"uk",
"ur",
"zh"],
}
},
];
}
}

Some files were not shown because too many files have changed in this diff Show More