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 getPopular(int page) async { final res = await dataBase(); return animeResList(res); } @override Future getLatestUpdates(int page) async { final res = await dataBase(); List list = json.decode(res); return animeResList(json.encode(list.reversed.toList())); } @override Future search(String query, int page, FilterList filterList) async { final res = await dataBase(); return animeSeachFetch(res, query); } @override Future 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? 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> 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 vostfrPlayers = vo["lecteurs"]; List vfPlayers = vf["lecteurs"]; List players = []; if (language == "vo" && hasVostfr) { players = vostfrPlayers; } else if (language == "vf" && hasVf) { players = vfPlayers; } List 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 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 animeList = []; var jsonResList = json.decode(res); for (var animeJson in jsonResList) { final seasons = animeJson["saisons"]; List vostfrListName = []; List 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 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 vostfrListName = []; List 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 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>; 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> 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 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); }