From 7966d45be79257ff0ead92eb79bd436f08968b24 Mon Sep 17 00:00:00 2001 From: Moustapha Kodjo Amadou <107993382+kodjodevf@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:07:33 +0100 Subject: [PATCH] + --- .github/workflows/gen_index.yml | 8 +- dart/anime/anime_source_list.dart | 2 + dart/anime/src/en/kisskh/icon.png | Bin 0 -> 3012 bytes dart/anime/src/en/kisskh/kisskh.dart | 223 +++++++++++++++++++++++++++ dart/anime/src/en/kisskh/source.dart | 17 ++ 5 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 dart/anime/src/en/kisskh/icon.png create mode 100644 dart/anime/src/en/kisskh/kisskh.dart create mode 100644 dart/anime/src/en/kisskh/source.dart diff --git a/.github/workflows/gen_index.yml b/.github/workflows/gen_index.yml index 01c92b8b..a0a30b0d 100644 --- a/.github/workflows/gen_index.yml +++ b/.github/workflows/gen_index.yml @@ -28,5 +28,9 @@ jobs: git add index.json git add anime_index.json git add novel_index.json - git commit -m "Update extensions" - git push origin main --force \ No newline at end of file + if git diff --cached --quiet; then + echo "No changes detected, skipping commit." + else + git commit -m "Update extensions" + git push origin main --force + fi \ No newline at end of file diff --git a/dart/anime/anime_source_list.dart b/dart/anime/anime_source_list.dart index be1af11e..126f488a 100644 --- a/dart/anime/anime_source_list.dart +++ b/dart/anime/anime_source_list.dart @@ -8,6 +8,7 @@ import 'src/ar/okanime/source.dart'; import 'src/de/animetoast/source.dart'; 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/es/animeonlineninja/source.dart'; import 'src/fr/animesama/source.dart'; @@ -51,4 +52,5 @@ List dartAnimesourceList = [ diziwatchSource, aniZoneSource, animeonlineninjaSource, + kisskhSource, ]; diff --git a/dart/anime/src/en/kisskh/icon.png b/dart/anime/src/en/kisskh/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c440d80beb67f246591a19b6f9998f557522039 GIT binary patch literal 3012 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 getPopular(int page) async { + final res = + (await client.get( + Uri.parse( + "${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=1&pageSize=40", + ), + )).body; + final jsonRes = json.decode(res); + final datas = jsonRes["data"]; + List animeList = []; + + for (var data in datas) { + var anime = MManga(); + anime.name = data["title"]; + anime.imageUrl = data["thumbnail"] ?? ""; + anime.link = + "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; + animeList.add(anime); + } + + int lastPage = jsonRes["totalCount"]; + int pages = jsonRes["page"]; + return MPages(animeList, pages < lastPage); + } + + @override + Future getLatestUpdates(int page) async { + final res = + (await client.get( + Uri.parse( + "${source.baseUrl}/api/DramaList/List?page=$page&type=0&sub=0&country=0&status=0&order=12&pageSize=40", + ), + )).body; + final jsonRes = json.decode(res); + final datas = jsonRes["data"]; + + List animeList = []; + + for (var data in datas) { + var anime = MManga(); + anime.name = data["title"]; + anime.imageUrl = data["thumbnail"] ?? ""; + anime.link = + "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; + animeList.add(anime); + } + + int lastPage = jsonRes["totalCount"]; + int pages = jsonRes["page"]; + return MPages(animeList, pages < lastPage); + } + + @override + Future search(String query, int page, FilterList filterList) async { + final res = + (await client.get( + Uri.parse("${source.baseUrl}/api/DramaList/Search?q=$query&type=0"), + )).body; + final jsonRes = json.decode(res); + List animeList = []; + for (var data in jsonRes) { + var anime = MManga(); + anime.name = data["title"]; + anime.imageUrl = data["thumbnail"] ?? ""; + anime.link = + "${source.baseUrl}/api/DramaList/Drama/${data["id"]}?isq=false"; + animeList.add(anime); + } + return MPages(animeList, false); + } + + @override + Future getDetail(String url) async { + final statusList = [ + {"Ongoing": 0, "Completed": 1}, + ]; + final res = (await client.get(Uri.parse(url))).body; + var anime = MManga(); + final jsonRes = json.decode(res); + final status = jsonRes["status"] ?? ""; + anime.description = jsonRes["description"]; + anime.status = parseStatus(status, statusList); + anime.imageUrl = jsonRes["thumbnail"]; + var episodes = jsonRes["episodes"]; + String type = jsonRes["type"]; + final episodesCount = jsonRes["episodesCount"] as int; + final containsAnime = type.contains("Anime"); + final containsTVSeries = type.contains("TVSeries"); + final containsHollywood = type.contains("Hollywood"); + final containsMovie = type.contains("Movie"); + List? episodesList = []; + + for (var a in episodes) { + MChapter episode = MChapter(); + String number = (a["number"] as double).toString().replaceAll(".0", ""); + final id = a["id"]; + if (containsAnime || containsTVSeries) { + episode.name = "Episode $number"; + } else if (containsHollywood && episodesCount == 1 || containsMovie) { + episode.name = "Movie"; + } else if (containsHollywood && episodesCount > 1) { + episode.name = "Episode $number"; + } + episode.url = + "${source.baseUrl}/api/DramaList/Episode/$id.png?err=false&ts=&time="; + episodesList.add(episode); + } + + anime.chapters = episodesList; + return anime; + } + + @override + Future> getVideoList(String url) async { + final res = (await client.get(Uri.parse(url))).body; + final id = substringAfter(substringBefore(url, ".png"), "Episode/"); + final jsonRes = json.decode(res); + + final subRes = + (await client.get(Uri.parse("${source.baseUrl}/api/Sub/$id"))).body; + var jsonSubRes = json.decode(subRes); + List subtitles = []; + + for (var sub in jsonSubRes) { + final subUrl = sub["src"] as String; + final label = sub["label"]; + if (subUrl.endsWith("txt")) { + var subtitle = await getSubtitle(subUrl, label); + subtitles.add(subtitle); + } else { + var subtitle = MTrack(); + subtitle + ..label = label + ..file = subUrl; + subtitles.add(subtitle); + } + } + final videoUrl = jsonRes["Video"]; + var video = MVideo(); + video + ..url = videoUrl + ..originalUrl = videoUrl + ..quality = "kisskh" + ..subtitles = subtitles + ..headers = { + "referer": "https://kisskh.me/", + "origin": "https://kisskh.me", + }; + return [video]; + } + + Future getSubtitle(String subUrl, String subLang) async { + final response = await client.get( + Uri.parse(subUrl), + headers: {"referer": "https://kisskh.me/", "origin": "https://kisskh.me"}, + ); + final subtitleData = response.body; + String decrypted = "\n"; + for (String line in subtitleData.split('\n')) { + decrypted += "${decrypt(line.trim())}\n"; + } + var subtitle = MTrack(); + subtitle + ..label = subLang + ..file = decrypted; + return subtitle; + } + + String decrypt(String data) { + final key = utf8.decode([ + 56, + 48, + 53, + 54, + 52, + 56, + 51, + 54, + 52, + 54, + 51, + 50, + 56, + 55, + 54, + 51, + ]); + final iv = utf8.decode([ + 54, + 56, + 53, + 50, + 54, + 49, + 50, + 51, + 55, + 48, + 49, + 56, + 53, + 50, + 55, + 51, + ]); + return cryptoHandler(data, iv, key, false); + } +} + +KissKh main(MSource source) { + return KissKh(source: source); +} diff --git a/dart/anime/src/en/kisskh/source.dart b/dart/anime/src/en/kisskh/source.dart new file mode 100644 index 00000000..725dd762 --- /dev/null +++ b/dart/anime/src/en/kisskh/source.dart @@ -0,0 +1,17 @@ +import '../../../../../model/source.dart'; + +Source get kisskhSource => _kisskhSource; +const _kisskhVersion = "0.0.65"; +const _kisskhSourceCodeUrl = + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/kisskh.dart"; +Source _kisskhSource = Source( + name: "KissKH", + baseUrl: "https://kisskh.co", + lang: "en", + typeSource: "single", + iconUrl: + "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/src/en/kisskh/icon.png", + sourceCodeUrl: _kisskhSourceCodeUrl, + version: _kisskhVersion, + itemType: ItemType.anime, +);