mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 10:51:17 +00:00
Reorganize folders
This commit is contained in:
409
dart/anime/src/fr/animesama/animesama.dart
Normal file
409
dart/anime/src/fr/animesama/animesama.dart
Normal file
@@ -0,0 +1,409 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimeSama extends MProvider {
|
||||
AnimeSama({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@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 ajouts"))
|
||||
.toList();
|
||||
final seasonElements = (latest.first.nextElementSibling as MElement)
|
||||
.select(".scrollBarStyled > 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 = [];
|
||||
List<Map<String, dynamic>> players = [];
|
||||
for (var playerListt in playersList) {
|
||||
for (var player in playerListt["players"]) {
|
||||
if (player.length > episodeNumber) {
|
||||
langs.add(playerListt["lang"]);
|
||||
players.add(
|
||||
{"lang": playerListt["lang"], "player": player[episodeNumber]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 sendVidExtractor(playerUrl, null, lang);
|
||||
} else if (playerUrl.contains("anime-sama.fr")) {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = playerUrl
|
||||
..originalUrl = playerUrl
|
||||
..quality = "${lang} - AS Player";
|
||||
a = [video];
|
||||
} else if (playerUrl.contains("sibnet.ru")) {
|
||||
a = await sibnetExtractor(playerUrl, lang);
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return sortVideos(videos, source.id);
|
||||
}
|
||||
|
||||
@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("^\\s*panneauAnime\\(\"(.*)\", \"(.*)\"\\)", multiLine: true);
|
||||
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 (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);
|
||||
}
|
||||
BIN
dart/anime/src/fr/animesama/icon.png
Normal file
BIN
dart/anime/src/fr/animesama/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
16
dart/anime/src/fr/animesama/source.dart
Normal file
16
dart/anime/src/fr/animesama/source.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesamaSource => _animesama;
|
||||
const animesamaVersion = "0.0.25";
|
||||
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,
|
||||
isManga: false);
|
||||
158
dart/anime/src/fr/animesultra/animesultra.dart
Normal file
158
dart/anime/src/fr/animesultra/animesultra.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class AnimesUltra extends MProvider {
|
||||
AnimesUltra({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res = (await client.get(Uri.parse(source.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(source.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 {
|
||||
final res = (await client.get(Uri.parse(source.baseUrl))).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, false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MManga> getDetail(String url) async {
|
||||
final statusList = [
|
||||
{"En cours": 0, "Terminé": 1}
|
||||
];
|
||||
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
MManga anime = MManga();
|
||||
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 = xpath(res,
|
||||
'//*[@class="item item-title" and contains(text(),"Studio:")]/span[2]/text()')
|
||||
.first;
|
||||
final urlEp = url.replaceAll('.html', '/episode-1.html');
|
||||
final resEpWebview =
|
||||
await getHtmlViaWebview(urlEp, '//*[@class="ss-list"]/a/@href');
|
||||
final epUrls =
|
||||
xpath(resEpWebview, '//*[@class="ss-list"]/a/@href').reversed.toList();
|
||||
final names = xpath(resEpWebview,
|
||||
'//*[@class="ss-list"]/a/div[@class="ssli-detail"]/div/text()')
|
||||
.reversed
|
||||
.toList();
|
||||
|
||||
List<MChapter>? episodesList = [];
|
||||
for (var i = 0; i < names.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 resWebview = await getHtmlViaWebview(
|
||||
url, '//*[@class="ps__-list"]/div/@data-server-id');
|
||||
|
||||
final serverIds =
|
||||
xpath(resWebview, '//*[@class="ps__-list"]/div/@data-server-id');
|
||||
final serverNames =
|
||||
xpath(resWebview, '//*[@class="ps__-list"]/div/a/text()');
|
||||
List<String> serverUrls = [];
|
||||
for (var id in serverIds) {
|
||||
final serversUrls =
|
||||
xpath(resWebview, '//*[@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 sendVidExtractor(url.replaceAll("https:////", "https://"),
|
||||
json.encode({"Referer": "${source.baseUrl}/"}), "");
|
||||
} 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");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
AnimesUltra main(MSource source) {
|
||||
return AnimesUltra(source: source);
|
||||
}
|
||||
BIN
dart/anime/src/fr/animesultra/icon.png
Normal file
BIN
dart/anime/src/fr/animesultra/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
17
dart/anime/src/fr/animesultra/source.dart
Normal file
17
dart/anime/src/fr/animesultra/source.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get animesultraSource => _animesultraSource;
|
||||
const _animesultraVersion = "0.0.65";
|
||||
const _animesultraSourceCodeUrl =
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/animesultra/animesultra.dart";
|
||||
Source _animesultraSource = Source(
|
||||
name: "AnimesUltra",
|
||||
baseUrl: "https://ww.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,
|
||||
isManga: false,
|
||||
isFullData: false);
|
||||
342
dart/anime/src/fr/franime/franime.dart
Normal file
342
dart/anime/src/fr/franime/franime.dart
Normal file
@@ -0,0 +1,342 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class FrAnime extends MProvider {
|
||||
FrAnime({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@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 = [];
|
||||
if (playerName.contains("vido")) {
|
||||
videos.add(video
|
||||
..url = playerUrl
|
||||
..originalUrl = playerUrl
|
||||
..quality = "FRAnime (Vido)");
|
||||
} else if (playerName.contains("sendvid")) {
|
||||
a = await sendVidExtractor(
|
||||
playerUrl, json.encode({"Referer": "https://franime.fr/"}), "");
|
||||
} 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 "";
|
||||
}
|
||||
}
|
||||
|
||||
FrAnime main(MSource source) {
|
||||
return FrAnime(source: source);
|
||||
}
|
||||
BIN
dart/anime/src/fr/franime/icon.png
Normal file
BIN
dart/anime/src/fr/franime/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
18
dart/anime/src/fr/franime/source.dart
Normal file
18
dart/anime/src/fr/franime/source.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get franimeSource => _franimeSource;
|
||||
const _franimeVersion = "0.0.7";
|
||||
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,
|
||||
isManga: false,
|
||||
isFullData: true);
|
||||
BIN
dart/anime/src/fr/otakufr/icon.png
Normal file
BIN
dart/anime/src/fr/otakufr/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
442
dart/anime/src/fr/otakufr/otakufr.dart
Normal file
442
dart/anime/src/fr/otakufr/otakufr.dart
Normal file
@@ -0,0 +1,442 @@
|
||||
import 'package:mangayomi/bridge_lib.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
class OtakuFr extends MProvider {
|
||||
OtakuFr({required this.source});
|
||||
|
||||
MSource source;
|
||||
|
||||
final Client client = Client(source);
|
||||
|
||||
@override
|
||||
Future<MPages> getPopular(int page) async {
|
||||
final res =
|
||||
(await client.get(Uri.parse("${source.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("${source.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 = "${source.baseUrl}/toute-la-liste-affiches/page/$page/?q=$query";
|
||||
} else {
|
||||
for (var filter in filters) {
|
||||
if (filter.type == "GenreFilter") {
|
||||
if (filter.state != 0) {
|
||||
url =
|
||||
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
} else if (filter.type == "SubPageFilter") {
|
||||
if (url.isEmpty) {
|
||||
if (filter.state != 0) {
|
||||
url =
|
||||
"${source.baseUrl}/${filter.values[filter.state].value}page/$page";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final 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(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(url))).body;
|
||||
|
||||
final servers = xpath(res, '//*[@id="nav-tabContent"]/div/iframe/@src');
|
||||
List<MVideo> videos = [];
|
||||
final hosterSelection = preferenceHosterSelection(source.id);
|
||||
for (var url in servers) {
|
||||
final resServer = (await client.get(Uri.parse(fixUrl(url)),
|
||||
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("sibnet") &&
|
||||
hosterSelection.contains("Sibnet")) {
|
||||
a = await sibnetExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://doo") &&
|
||||
hosterSelection.contains("Doodstream")) {
|
||||
a = await doodExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("https://voe.sx") &&
|
||||
hosterSelection.contains("Voe")) {
|
||||
a = await voeExtractor(serverUrl, null);
|
||||
} else if (serverUrl.contains("https://ok.ru") &&
|
||||
hosterSelection.contains("Okru")) {
|
||||
a = await okruExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("vadbam") &&
|
||||
hosterSelection.contains("Vidbm")) {
|
||||
a = await vidbmExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("upstream") &&
|
||||
hosterSelection.contains("Upstream")) {
|
||||
a = await upstreamExtractor(serverUrl);
|
||||
} else if (serverUrl.contains("sendvid") &&
|
||||
hosterSelection.contains("Sendvid")) {
|
||||
a = await sendVidExtractor(serverUrl, null, "");
|
||||
}
|
||||
videos.addAll(a);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
String fixUrl(String url) {
|
||||
return regExp(url, r"^(?:(?:https?:)?//|www\.)", 'https://', 0, 0);
|
||||
}
|
||||
|
||||
@override
|
||||
List<dynamic> getFilterList() {
|
||||
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 [
|
||||
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",
|
||||
"Vidbm",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
"Upstream"
|
||||
],
|
||||
entryValues: [
|
||||
"Streamwish",
|
||||
"Doodstream",
|
||||
"Sendvid",
|
||||
"Vidbm",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
"Upstream"
|
||||
],
|
||||
values: [
|
||||
"Streamwish",
|
||||
"Doodstream",
|
||||
"Sendvid",
|
||||
"Vidbm",
|
||||
"Okru",
|
||||
"Voe",
|
||||
"Sibnet",
|
||||
"Upstream"
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
List<MVideo> sortVideos(List<MVideo> videos, int sourceId) {
|
||||
String quality = getPreferenceValue(sourceId, "preferred_quality");
|
||||
|
||||
videos.sort((MVideo a, MVideo b) {
|
||||
int qualityMatchA = 0;
|
||||
if (a.quality.contains(quality)) {
|
||||
qualityMatchA = 1;
|
||||
}
|
||||
int qualityMatchB = 0;
|
||||
if (b.quality.contains(quality)) {
|
||||
qualityMatchB = 1;
|
||||
}
|
||||
if (qualityMatchA != qualityMatchB) {
|
||||
return qualityMatchB - qualityMatchA;
|
||||
}
|
||||
|
||||
final regex = RegExp(r'(\d+)p');
|
||||
final matchA = regex.firstMatch(a.quality);
|
||||
final matchB = regex.firstMatch(b.quality);
|
||||
final int qualityNumA = int.tryParse(matchA?.group(1) ?? '0') ?? 0;
|
||||
final int qualityNumB = int.tryParse(matchB?.group(1) ?? '0') ?? 0;
|
||||
return qualityNumB - qualityNumA;
|
||||
});
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
List<String> preferenceHosterSelection(int sourceId) {
|
||||
return getPreferenceValue(sourceId, "hoster_selection");
|
||||
}
|
||||
|
||||
Future<List<MVideo>> upstreamExtractor(String url) async {
|
||||
final res = (await 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>> vidbmExtractor(String url) async {
|
||||
final res = (await client.get(Uri.parse(url))).body;
|
||||
final js = xpath(res,
|
||||
'//script[contains(text(), "m3u8") or contains(text(), "mp4")]/text()');
|
||||
if (js.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
final masterUrl = substringBefore(substringAfter(js.first, "source"), "\"");
|
||||
final quality = substringBefore(
|
||||
substringAfter(
|
||||
substringBefore(
|
||||
substringAfter(substringAfter(js.first, "source"), "file"),
|
||||
"]"),
|
||||
"label:\""),
|
||||
"\"");
|
||||
List<MVideo> videos = [];
|
||||
if (masterUrl.contains("m3u8")) {
|
||||
final masterPlaylistRes = (await 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 = "Vidbm - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
} else {
|
||||
MVideo video = MVideo();
|
||||
video
|
||||
..url = masterUrl
|
||||
..originalUrl = masterUrl
|
||||
..quality = "Vidbm - $quality";
|
||||
videos.add(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
|
||||
OtakuFr main(MSource source) {
|
||||
return OtakuFr(source: source);
|
||||
}
|
||||
17
dart/anime/src/fr/otakufr/source.dart
Normal file
17
dart/anime/src/fr/otakufr/source.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import '../../../../../model/source.dart';
|
||||
|
||||
Source get otakufr => _otakufr;
|
||||
const otakufrVersion = "0.0.8";
|
||||
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.co",
|
||||
lang: "fr",
|
||||
typeSource: "single",
|
||||
iconUrl:
|
||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/fr/otakufr/icon.png",
|
||||
sourceCodeUrl: otakufrCodeUrl,
|
||||
version: otakufrVersion,
|
||||
isManga: false,
|
||||
isFullData: false);
|
||||
Reference in New Issue
Block a user