diff --git a/anime/source_generator.dart b/anime/source_generator.dart index 8ef1722a..f1c9c4d8 100644 --- a/anime/source_generator.dart +++ b/anime/source_generator.dart @@ -17,6 +17,7 @@ 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/sq/filma24/source.dart'; void main() { List _sourcesList = [ @@ -34,7 +35,8 @@ void main() { ...dopeflixSourcesList, animesaturn, uhdmoviesSource, - ...datalifeengineSourcesList + ...datalifeengineSourcesList, + filma24 ]; final List> jsonList = _sourcesList.map((source) => source.toJson()).toList(); diff --git a/anime/src/sq/filma24/filma24.dart b/anime/src/sq/filma24/filma24.dart new file mode 100644 index 00000000..256dda4a --- /dev/null +++ b/anime/src/sq/filma24/filma24.dart @@ -0,0 +1,290 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class Filma24 extends MProvider { + Filma24(); + @override + Future getPopular(MSource source, int page) async { + String pageNu = page == 1 ? "" : "/page/$page/"; + final data = {"url": "${preferenceBaseUrl(source.id)}$pageNu"}; + final res = await http('GET', json.encode(data)); + return animeFromRes(res); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + String pageNu = page == 1 ? "" : "page/$page/"; + final data = {"url": "${preferenceBaseUrl(source.id)}/$pageNu?sort=trendy"}; + final res = await http('GET', json.encode(data)); + return animeFromRes(res); + } + + @override + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + String url = ""; + String pageNu = page == 1 ? "" : "page/$page/"; + if (query.isNotEmpty) { + url += "${preferenceBaseUrl(source.id)}/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 = "${preferenceBaseUrl(source.id)}$url"; + } + + url += pageNu; + + final data = {"url": url}; + final res = await http('GET', json.encode(data)); + return animeFromRes(res); + } + + @override + Future getDetail(MSource source, String url) async { + List? episodesList = []; + if (!url.contains("seriale")) { + MChapter episode = MChapter(); + episode.name = "Filma"; + episode.url = url; + episodesList.add(episode); + } else { + final data = {"url": url}; + final res = await http('GET', json.encode(data)); + 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> getVideoList(MSource source, String url) async { + final data = {"url": url}; + final res = await http('GET', json.encode(data)); + List videos = []; + final serverUrls = xpath(res, '//*[@class="player"]/div[1]/a/@href'); + for (var serverUrl in serverUrls) { + List a = []; + final serVdata = {"url": "$url/$serverUrl"}; + final serVres = await http('GET', json.encode(serVdata)); + List 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 getSourcePreferences(MSource source) { + return [ + EditTextPreference( + key: "pref_domain", + title: "Domeni i përdorur aktualisht", + summary: "", + value: "https://www.filma24.pl", + dialogTitle: "Domeni i përdorur aktualisht", + dialogMessage: "", + text: "https://www.filma24.pl"), + ]; + } + + String preferenceBaseUrl(int sourceId) { + return getPreferenceValue(sourceId, "pref_domain"); + } + + @override + List getFilterList(MSource source) { + return [ + SelectFilter("ReleaseFilter", "Viti", 0, [ + SelectFilterOption("", ""), + 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> vidmolyExtractor(String url) async { + final headers = { + 'Referer': 'https://vidmoly.to', + }; + List videos = []; + final playListUrlResponse = await http('GET', json.encode({"url": url})); + final playlistUrl = + RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ?? + ""; + final masterPlaylistRes = await http( + 'GET', json.encode({"url": playlistUrl, "headers": headers})); + if (masterPlaylistRes != "error") { + 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 = "Vidmoly $quality" + ..headers = headers; + videos.add(video); + } + } + + return videos; + } + + Future> oneuploadExtractor(String url) async { + List videos = []; + final playListUrlResponse = await http('GET', json.encode({"url": url})); + final playlistUrl = + RegExp(r'file:"(\S+?)"').firstMatch(playListUrlResponse)?.group(1) ?? + ""; + final masterPlaylistRes = + await http('GET', json.encode({"url": playlistUrl})); + 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> uqloadExtractor(String url) async { + final res = await http('GET', json.encode({"url": url})); + 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 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() { + return Filma24(); +} diff --git a/anime/src/sq/filma24/icon.png b/anime/src/sq/filma24/icon.png new file mode 100644 index 00000000..159bf41e Binary files /dev/null and b/anime/src/sq/filma24/icon.png differ diff --git a/anime/src/sq/filma24/source.dart b/anime/src/sq/filma24/source.dart new file mode 100644 index 00000000..1aeb44ca --- /dev/null +++ b/anime/src/sq/filma24/source.dart @@ -0,0 +1,16 @@ +import '../../../../model/source.dart'; + +Source get filma24 => _filma24; +const _filma24Version = "0.0.1"; +const _filma24CodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/sq/filma24/filma24.dart"; +Source _filma24 = Source( + name: "Filma24", + baseUrl: "https://www.filma24.pl", + lang: "sq", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/anime/src/sq/filma24/icon.png", + sourceCodeUrl: _filma24CodeUrl, + version: _filma24Version, + isManga: false);