diff --git a/anime/source_generator.dart b/anime/source_generator.dart index f1c9c4d8..ca5fe23f 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -7,6 +7,7 @@ import 'multisrc/dopeflix/sources.dart'; import 'multisrc/zorotheme/sources.dart'; import 'src/ar/okanime/source.dart'; import 'src/en/aniwave/source.dart'; +import 'src/en/dramacool/source.dart'; import 'src/en/gogoanime/source.dart'; import 'src/en/kisskh/source.dart'; import 'src/en/uhdmovies/source.dart'; @@ -36,7 +37,8 @@ void main() { animesaturn, uhdmoviesSource, ...datalifeengineSourcesList, - filma24 + filma24, + dramacoolSource ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/en/dramacool/dramacool.dart b/anime/src/en/dramacool/dramacool.dart new file mode 100644 index 00000000..9ebc27ec --- /dev/null +++ b/anime/src/en/dramacool/dramacool.dart @@ -0,0 +1,213 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class DramaCool extends MProvider { + DramaCool(); + @override + Future getPopular(MSource source, int page) async { + final data = { + "url": "${preferenceBaseUrl(source.id)}/most-popular-drama?page=$page" + }; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + return animeFromElement(document.select("ul.list-episode-item li a"), + document.selectFirst("li.next a")?.attr("href") != null); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + final data = { + "url": "${preferenceBaseUrl(source.id)}/recently-added?page=$page" + }; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + return animeFromElement(document.select("ul.switch-block a"), + document.selectFirst("li.next a")?.attr("href") != null); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final data = { + "url": "${preferenceBaseUrl(source.id)}/search?keyword=$query&page=$page" + }; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + return animeFromElement(document.select("ul.list-episode-item li a"), + document.selectFirst("li.next a")?.attr("href") != null); + } + + @override + Future getDetail(MSource source, String url) async { + final statusList = [ + {"Ongoing": 0, "Completed": 1} + ]; + url = Uri.parse(url).path; + if (url.contains("-episode-") && url.endsWith(".html")) { + final data = {"url": "${preferenceBaseUrl(source.id)}$url"}; + final res = await http('GET', json.encode(data)); + url = parseHtml(res).selectFirst("div.category a").attr("href"); + } + url = Uri.parse(url).path; + + final data = {"url": "${preferenceBaseUrl(source.id)}$url"}; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + MManga anime = MManga(); + anime.description = document + .selectFirst("div.info") + .select("p") + .map((MElement e) { + if (!e.outerHtml.contains(" episodesList = []; + final episodeListElements = document.select("ul.all-episode li a"); + + for (var element in episodeListElements) { + var epNum = + substringAfterLast(element.selectFirst("h3").text, "Episode "); + var type = element.selectFirst("span.type")?.text ?? "RAW"; + var date = element.selectFirst("span.time")?.text ?? ""; + MChapter ep = MChapter(); + ep.name = "$type: Episode $epNum".trim(); + ep.url = element.getHref; + if (date.isNotEmpty) + ep.dateUpload = parseDates([element.selectFirst("span.time")?.text], + "yyyy-MM-dd HH:mm:ss", "en") + .first; + episodesList.add(ep); + } + + anime.chapters = episodesList; + return anime; + } + + @override + Future> getVideoList(MSource source, String url) async { + url = Uri.parse(url).path; + final data = {"url": "${preferenceBaseUrl(source.id)}$url"}; + final res = await http('GET', json.encode(data)); + final document = parseHtml(res); + String iframeUrl = document.selectFirst("iframe")?.getSrc ?? ""; + if (iframeUrl.isEmpty) return []; + if (iframeUrl.startsWith("//")) { + iframeUrl = "https:$iframeUrl"; + } + var iframeDoc = + parseHtml(await http('GET', json.encode({"url": iframeUrl}))); + final serverElements = iframeDoc.select("ul.list-server-items li"); + List videos = []; + for (var serverElement in serverElements) { + var url = serverElement.attr("data-video"); + List a = []; + if (url.contains("dood")) { + a = await doodExtractor(url, "DoodStream"); + } else if (url.contains("dwish")) { + a = await streamWishExtractor(url, "StreamWish"); + } else if (url.contains("streamtape")) { + a = await streamTapeExtractor(url, "StreamTape"); + } + videos.addAll(a); + } + return sortVideos(videos, source.id); + } + + @override + List getSourcePreferences(MSource source) { + return [ + EditTextPreference( + key: "overrideBaseUrl", + title: "Override BaseUrl", + summary: "", + value: "https://dramacool.pa", + dialogTitle: "Override BaseUrl", + dialogMessage: "", + text: "https://dramacool.pa"), + ListPreference( + key: "preferred_quality", + title: "Preferred quality", + summary: "", + valueIndex: 0, + entries: [ + "1080p", + "720p", + "480p", + "360p", + "Doodstream", + "StreamTape" + ], + entryValues: [ + "1080", + "720", + "480", + "360", + "Doodstream", + "StreamTape" + ]) + ]; + } + + String preferenceBaseUrl(int sourceId) { + return getPreferenceValue(sourceId, "overrideBaseUrl"); + } + + MPages animeFromElement(List elements, bool hasNextPage) { + List animeList = []; + for (var element in elements) { + MManga anime = MManga(); + anime.name = element.selectFirst("h3")?.text ?? "Serie"; + anime.imageUrl = (element.selectFirst("img")?.attr("data-original") ?? "") + .replaceAll(" ", "%20") ?? + ""; + anime.link = element.getHref; + animeList.add(anime); + } + return MPages(animeList, hasNextPage); + } + + List sortVideos(List 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; + } +} + +DramaCool main() { + return DramaCool(); +} diff --git a/anime/src/en/dramacool/icon.png b/anime/src/en/dramacool/icon.png new file mode 100644 index 00000000..ceb49c5d Binary files /dev/null and b/anime/src/en/dramacool/icon.png differ diff --git a/anime/src/en/dramacool/source.dart b/anime/src/en/dramacool/source.dart new file mode 100644 index 00000000..499d46f5 --- /dev/null +++ b/anime/src/en/dramacool/source.dart @@ -0,0 +1,16 @@ +import '../../../../model/source.dart'; + +Source get dramacoolSource => _dramacoolSource; +const _dramacoolVersion = "0.0.1"; +const _dramacoolSourceCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/en/dramacool/dramacool.dart"; +Source _dramacoolSource = Source( + name: "DramaCool", + baseUrl: "https://dramacool.pa", + lang: "en", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/en/dramacool/icon.png", + sourceCodeUrl: _dramacoolSourceCodeUrl, + version: _dramacoolVersion, + isManga: false);