From 74cd05142c13d6008a442af9890a2827f505b01c Mon Sep 17 00:00:00 2001 From: Schnitzel5 Date: Sat, 12 Apr 2025 23:17:53 +0200 Subject: [PATCH] added new source: Vumeto --- dart/anime/anime_source_list.dart | 2 + dart/anime/src/en/vumeto/icon.png | Bin 0 -> 7549 bytes dart/anime/src/en/vumeto/source.dart | 19 ++ dart/anime/src/en/vumeto/vumeto.dart | 327 +++++++++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 dart/anime/src/en/vumeto/icon.png create mode 100644 dart/anime/src/en/vumeto/source.dart create mode 100644 dart/anime/src/en/vumeto/vumeto.dart diff --git a/dart/anime/anime_source_list.dart b/dart/anime/anime_source_list.dart index 126f488a..302c95fc 100644 --- a/dart/anime/anime_source_list.dart +++ b/dart/anime/anime_source_list.dart @@ -10,6 +10,7 @@ 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'; @@ -53,4 +54,5 @@ List dartAnimesourceList = [ aniZoneSource, animeonlineninjaSource, kisskhSource, + vumetoSource, ]; diff --git a/dart/anime/src/en/vumeto/icon.png b/dart/anime/src/en/vumeto/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..56be717a4016c671e355a71cb104057a986e82b7 GIT binary patch literal 7549 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 SZ!3y^aOAMXq1*#bq5lKY-6}-@ literal 0 HcmV?d00001 diff --git a/dart/anime/src/en/vumeto/source.dart b/dart/anime/src/en/vumeto/source.dart new file mode 100644 index 00000000..c9472df2 --- /dev/null +++ b/dart/anime/src/en/vumeto/source.dart @@ -0,0 +1,19 @@ +// 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.5"; +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, +); diff --git a/dart/anime/src/en/vumeto/vumeto.dart b/dart/anime/src/en/vumeto/vumeto.dart new file mode 100644 index 00000000..e18e51b9 --- /dev/null +++ b/dart/anime/src/en/vumeto/vumeto.dart @@ -0,0 +1,327 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class Vumeto extends MProvider { + Vumeto({required this.source}); + + MSource source; + + final Client client = Client(source); + + @override + bool get supportsLatest => true; + + @override + Map 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 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 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 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 scrapeAnimeList(MDocument document) { + List? animeElements = document.getElementsByClassName( + 'relative group border-0', + ); + List 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 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 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 parsedData = json.decode(jsonData); + List 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> 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