From d972abbe87c5fb30fc29f593c6ccb8ed04a89701 Mon Sep 17 00:00:00 2001 From: kodjomoustapha <107993382+kodjodevf@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:34:13 +0100 Subject: [PATCH] rewrite Comick source code in JS --- dart/manga/src/all/comick/comick.dart | 658 ------------------ dart/manga/src/all/comick/sources.dart | 72 -- .../icon/all.comick.png | Bin javascript/manga/src/all/comick.js | 424 +++++++++++ source_generator.dart | 13 +- 5 files changed, 433 insertions(+), 734 deletions(-) delete mode 100644 dart/manga/src/all/comick/comick.dart delete mode 100644 dart/manga/src/all/comick/sources.dart rename dart/manga/src/all/comick/icon.png => javascript/icon/all.comick.png (100%) create mode 100644 javascript/manga/src/all/comick.js diff --git a/dart/manga/src/all/comick/comick.dart b/dart/manga/src/all/comick/comick.dart deleted file mode 100644 index 8a51c7c..0000000 --- a/dart/manga/src/all/comick/comick.dart +++ /dev/null @@ -1,658 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; - -class ComickFun extends MProvider { - ComickFun({required this.source}); - - MSource source; - - final Client client = Client(source); - - @override - Future getPopular(int page) async { - final res = (await client.get( - Uri.parse( - "${source.apiUrl}/v1.0/search?sort=follow&page=$page&tachiyomi=true"), - headers: getHeader(source.baseUrl))) - .body; - return mangaRes(res); - } - - @override - Future getLatestUpdates(int page) async { - final res = (await client.get( - Uri.parse( - "${source.apiUrl}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true"), - headers: getHeader(source.baseUrl))) - .body; - return mangaRes(res); - } - - @override - Future search(String query, int page, FilterList filterList) async { - final filters = filterList.filters; - String url = ""; - if (query.isNotEmpty) { - url = "${source.apiUrl}/v1.0/search?q=$query&tachiyomi=true"; - } else { - url = "${source.apiUrl}/v1.0/search"; - for (var filter in filters) { - if (filter.type == "CompletedFilter") { - if (filter.state) { - url += "${ll(url)}completed=true"; - } - } else if (filter.type == "GenreFilter") { - final included = (filter.state as List) - .where((e) => e.state == 1 ? true : false) - .toList(); - final excluded = (filter.state as List) - .where((e) => e.state == 2 ? true : false) - .toList(); - if (included.isNotEmpty) { - for (var val in included) { - url += "${ll(url)}genres=${val.value}"; - } - } - if (excluded.isNotEmpty) { - for (var val in excluded) { - url += "${ll(url)}excludes=${val.value}"; - } - } - } else if (filter.type == "DemographicFilter") { - final included = (filter.state as List) - .where((e) => e.state == 1 ? true : false) - .toList(); - if (included.isNotEmpty) { - for (var val in included) { - url += "${ll(url)}demographic=${val.value}"; - } - } - } else if (filter.type == "TypeFilter") { - final country = (filter.state as List).where((e) => e.state).toList(); - if (country.isNotEmpty) { - for (var coun in country) { - url += "${ll(url)}country=${coun.value}"; - } - } - } else if (filter.type == "SortFilter") { - url += "${ll(url)}sort=${filter.values[filter.state].value}"; - } else if (filter.type == "StatusFilter") { - url += "${ll(url)}status=${filter.values[filter.state].value}"; - } else if (filter.type == "CreatedAtFilter") { - if (filter.state > 0) { - url += "${ll(url)}time=${filter.values[filter.state].value}"; - } - } else if (filter.type == "MinimumFilter") { - if (filter.state.isNotEmpty) { - url += "${ll(url)}minimum=${filter.state}"; - } - } else if (filter.type == "FromYearFilter") { - if (filter.state.isNotEmpty) { - url += "${ll(url)}from=${filter.state}"; - } - } else if (filter.type == "ToYearFilter") { - if (filter.state.isNotEmpty) { - url += "${ll(url)}to=${filter.state}"; - } - } else if (filter.type == "TagFilter") { - if (filter.state.isNotEmpty) { - final tags = (filter.state as String).split(","); - for (var tag in tags) { - url += "${ll(url)}tags=$tag"; - } - } - } - } - url += "${ll(url)}page=$page&tachiyomi=true"; - } - - final res = - (await client.get(Uri.parse(url), headers: getHeader(source.baseUrl))) - .body; - return mangaRes(res); - } - - @override - Future getDetail(String url) async { - final statusList = [ - {"1": 0, "2": 1, "3": 3, "4": 2} - ]; - final headers = getHeader(source.baseUrl); - final res = (await client.get( - Uri.parse( - "${source.apiUrl}${url.replaceAll("#", '')}?tachiyomi=true"), - headers: headers)) - .body; - final lang = "${source.lang != "all" ? "&lang=${source.lang}" : ""}"; - MManga manga = MManga(); - manga.author = jsonPathToString(res, r'$.authors[*].name', ''); - manga.genre = jsonPathToString( - res, r'$.comic.md_comic_md_genres[*].md_genres.name', "_.") - .split("_."); - manga.description = jsonPathToString(res, r'$..desc', ''); - manga.status = - parseStatus(jsonPathToString(res, r'$..comic.status', ''), statusList); - final chapUrlReq = - "${source.apiUrl}${url.replaceAll("#", '')}chapters?${lang}&tachiyomi=true&page=1"; - final request = - (await client.get(Uri.parse(chapUrlReq), headers: headers)).body; - var total = jsonPathToString(request, r'$.total', ''); - final chapterLimit = int.parse(total); - final newChapUrlReq = - "${source.apiUrl}${url.replaceAll("#", '')}chapters?limit=$chapterLimit${lang}&tachiyomi=true&page=1"; - - final newRequest = - (await client.get(Uri.parse(newChapUrlReq), headers: headers)).body; - - final chapsUrls = - jsonPathToString(newRequest, r'$.chapters[*].hid', "_.").split("_."); - final chapDate = - jsonPathToString(newRequest, r'$.chapters[*].created_at', "_.") - .split("_."); - final chaptersVolumes = - jsonPathToString(newRequest, r'$.chapters[*].vol', "_.").split("_."); - final chaptersScanlators = - jsonPathToString(newRequest, r'$.chapters[*].group_name', "_.") - .split("_."); - final chapsNames = - jsonPathToString(newRequest, r'$.chapters[*].title', "_.").split("_."); - final chaptersChaps = - jsonPathToString(newRequest, r'$.chapters[*].chap', "_.").split("_."); - - var dateUploads = - parseDates(chapDate, source.dateFormat, source.dateFormatLocale); - List? chaptersList = []; - for (var i = 0; i < chapsNames.length; i++) { - String title = ""; - String scanlator = ""; - if (chaptersChaps.isNotEmpty && chaptersVolumes.isNotEmpty) { - title = beautifyChapterName( - chaptersVolumes[i], chaptersChaps[i], chapsNames[i], source.lang); - } else { - title = chapsNames[i]; - } - if (chaptersScanlators.isNotEmpty) { - scanlator = chaptersScanlators[i] - .toString() - .replaceAll(']', "") - .replaceAll("[", ""); - } - MChapter chapter = MChapter(); - chapter.name = title; - chapter.url = chapsUrls[i]; - chapter.scanlator = scanlator == "null" ? "" : scanlator; - chapter.dateUpload = dateUploads[i]; - chaptersList.add(chapter); - } - manga.chapters = chaptersList; - return manga; - } - - @override - Future> getPageList(String url) async { - final res = (await client.get( - Uri.parse("${source.apiUrl}/chapter/$url?tachiyomi=true"), - headers: getHeader(url))) - .body; - return jsonPathToString(res, r'$.chapter.images[*].url', '_.').split('_.'); - } - - MPages mangaRes(String res) async { - final names = jsonPathToList(res, r'$.title', 0); - List ids = jsonPathToList(res, r'$.hid', 0); - List mangaUrls = []; - for (var id in ids) { - mangaUrls.add("/comic/$id/#"); - } - final urls = mangaUrls; - final images = jsonPathToList(res, r'$.cover_url', 0); - List mangaList = []; - for (var i = 0; i < urls.length; i++) { - MManga manga = MManga(); - manga.name = names[i]; - manga.imageUrl = images[i]; - manga.link = urls[i]; - mangaList.add(manga); - } - - return MPages(mangaList, true); - } - - String ll(String url) { - if (url.contains("?")) { - return "&"; - } - return "?"; - } - - @override - List getFilterList() { - return [ - HeaderFilter("The filter is ignored when using text search."), - GroupFilter("GenreFilter", "Genre", [ - { - "type": "TriState", - "filter": {"name": "4-Koma", "value": "4-koma"} - }, - { - "type": "TriState", - "filter": {"name": "Action", "value": "action"} - }, - { - "type": "TriState", - "filter": {"name": "Adaptation", "value": "adaptation"} - }, - { - "type": "TriState", - "filter": {"name": "Adult", "value": "adult"} - }, - { - "type": "TriState", - "filter": {"name": "Adventure", "value": "adventure"} - }, - { - "type": "TriState", - "filter": {"name": "Aliens", "value": "aliens"} - }, - { - "type": "TriState", - "filter": {"name": "Animals", "value": "animals"} - }, - { - "type": "TriState", - "filter": {"name": "Anthology", "value": "anthology"} - }, - { - "type": "TriState", - "filter": {"name": "Award Winning", "value": "award-winning"} - }, - { - "type": "TriState", - "filter": {"name": "Comedy", "value": "comedy"} - }, - { - "type": "TriState", - "filter": {"name": "Cooking", "value": "cooking"} - }, - { - "type": "TriState", - "filter": {"name": "Crime", "value": "crime"} - }, - { - "type": "TriState", - "filter": {"name": "Crossdressing", "value": "crossdressing"} - }, - { - "type": "TriState", - "filter": {"name": "Delinquents", "value": "delinquents"} - }, - { - "type": "TriState", - "filter": {"name": "Demons", "value": "demons"} - }, - { - "type": "TriState", - "filter": {"name": "Doujinshi", "value": "doujinshi"} - }, - { - "type": "TriState", - "filter": {"name": "Drama", "value": "drama"} - }, - { - "type": "TriState", - "filter": {"name": "Ecchi", "value": "ecchi"} - }, - { - "type": "TriState", - "filter": {"name": "Fan Colored", "value": "fan-colored"} - }, - { - "type": "TriState", - "filter": {"name": "Fantasy", "value": "fantasy"} - }, - { - "type": "TriState", - "filter": {"name": "Full Color", "value": "full-color"} - }, - { - "type": "TriState", - "filter": {"name": "Gender Bender", "value": "gender-bender"} - }, - { - "type": "TriState", - "filter": {"name": "Genderswap", "value": "genderswap"} - }, - { - "type": "TriState", - "filter": {"name": "Ghosts", "value": "ghosts"} - }, - { - "type": "TriState", - "filter": {"name": "Gore", "value": "gore"} - }, - { - "type": "TriState", - "filter": {"name": "Gyaru", "value": "gyaru"} - }, - { - "type": "TriState", - "filter": {"name": "Harem", "value": "harem"} - }, - { - "type": "TriState", - "filter": {"name": "Historical", "value": "historical"} - }, - { - "type": "TriState", - "filter": {"name": "Horror", "value": "horror"} - }, - { - "type": "TriState", - "filter": {"name": "Incest", "value": "incest"} - }, - { - "type": "TriState", - "filter": {"name": "Isekai", "value": "isekai"} - }, - { - "type": "TriState", - "filter": {"name": "Loli", "value": "loli"} - }, - { - "type": "TriState", - "filter": {"name": "Long Strip", "value": "long-strip"} - }, - { - "type": "TriState", - "filter": {"name": "Mafia", "value": "mafia"} - }, - { - "type": "TriState", - "filter": {"name": "Magic", "value": "magic"} - }, - { - "type": "TriState", - "filter": {"name": "Magical Girls", "value": "magical-girls"} - }, - { - "type": "TriState", - "filter": {"name": "Martial Arts", "value": "martial-arts"} - }, - { - "type": "TriState", - "filter": {"name": "Mature", "value": "mature"} - }, - { - "type": "TriState", - "filter": {"name": "Mecha", "value": "mecha"} - }, - { - "type": "TriState", - "filter": {"name": "Medical", "value": "medical"} - }, - { - "type": "TriState", - "filter": {"name": "Military", "value": "military"} - }, - { - "type": "TriState", - "filter": {"name": "Monster Girls", "value": "monster-girls"} - }, - { - "type": "TriState", - "filter": {"name": "Monsters", "value": "monsters"} - }, - { - "type": "TriState", - "filter": {"name": "Music", "value": "music"} - }, - { - "type": "TriState", - "filter": {"name": "Mystery", "value": "mystery"} - }, - { - "type": "TriState", - "filter": {"name": "Ninja", "value": "ninja"} - }, - { - "type": "TriState", - "filter": {"name": "Office Workers", "value": "office-workers"} - }, - { - "type": "TriState", - "filter": {"name": "Official Colored", "value": "official-colored"} - }, - { - "type": "TriState", - "filter": {"name": "Oneshot", "value": "oneshot"} - }, - { - "type": "TriState", - "filter": {"name": "Philosophical", "value": "philosophical"} - }, - { - "type": "TriState", - "filter": {"name": "Police", "value": "police"} - }, - { - "type": "TriState", - "filter": {"name": "Post-Apocalyptic", "value": "post-apocalyptic"} - }, - { - "type": "TriState", - "filter": {"name": "Psychological", "value": "psychological"} - }, - { - "type": "TriState", - "filter": {"name": "Reincarnation", "value": "reincarnation"} - }, - { - "type": "TriState", - "filter": {"name": "Reverse Harem", "value": "reverse-harem"} - }, - { - "type": "TriState", - "filter": {"name": "Romance", "value": "romance"} - }, - { - "type": "TriState", - "filter": {"name": "Samurai", "value": "samurai"} - }, - { - "type": "TriState", - "filter": {"name": "School Life", "value": "school-life"} - }, - { - "type": "TriState", - "filter": {"name": "Sci-Fi", "value": "sci-fi"} - }, - { - "type": "TriState", - "filter": {"name": "Sexual Violence", "value": "sexual-violence"} - }, - { - "type": "TriState", - "filter": {"name": "Shota", "value": "shota"} - }, - { - "type": "TriState", - "filter": {"name": "Shoujo Ai", "value": "shoujo-ai"} - }, - { - "type": "TriState", - "filter": {"name": "Shounen Ai", "value": "shounen-ai"} - }, - { - "type": "TriState", - "filter": {"name": "Slice of Life", "value": "slice-of-life"} - }, - { - "type": "TriState", - "filter": {"name": "Smut", "value": "smut"} - }, - { - "type": "TriState", - "filter": {"name": "Sports", "value": "sports"} - }, - { - "type": "TriState", - "filter": {"name": "Superhero", "value": "superhero"} - }, - { - "type": "TriState", - "filter": {"name": "Supernatural", "value": "supernatural"} - }, - { - "type": "TriState", - "filter": {"name": "Survival", "value": "survival"} - }, - { - "type": "TriState", - "filter": {"name": "Thriller", "value": "thriller"} - }, - { - "type": "TriState", - "filter": {"name": "Time Travel", "value": "time-travel"} - }, - { - "type": "TriState", - "filter": {"name": "Traditional Games", "value": "traditional-games"} - }, - { - "type": "TriState", - "filter": {"name": "Tragedy", "value": "tragedy"} - }, - { - "type": "TriState", - "filter": {"name": "User Created", "value": "user-created"} - }, - { - "type": "TriState", - "filter": {"name": "Vampires", "value": "vampires"} - }, - { - "type": "TriState", - "filter": {"name": "Video Games", "value": "video-games"} - }, - { - "type": "TriState", - "filter": {"name": "Villainess", "value": "villainess"} - }, - { - "type": "TriState", - "filter": {"name": "Virtual Reality", "value": "virtual-reality"} - }, - { - "type": "TriState", - "filter": {"name": "Web Comic", "value": "web-comic"} - }, - { - "type": "TriState", - "filter": {"name": "Wuxia", "value": "wuxia"} - }, - { - "type": "TriState", - "filter": {"name": "Yaoi", "value": "yaoi"} - }, - { - "type": "TriState", - "filter": {"name": "Yuri", "value": "yuri"} - }, - { - "type": "TriState", - "filter": {"name": "Zombies", "value": "zombies"} - } - ]), - GroupFilter("DemographicFilter", "Demographic", [ - TriStateFilter("Shounen", "1"), - TriStateFilter("Shoujo", "2"), - TriStateFilter("Seinen", "3"), - TriStateFilter("Josei", "4"), - ]), - GroupFilter("TypeFilter", "Type", [ - CheckBoxFilter("Manga", "jp"), - CheckBoxFilter("Manhwa", "kr"), - CheckBoxFilter("Manhua", "cn"), - ]), - SelectFilter("SortFilter", "Sort", 0, [ - SelectFilterOption("Most popular", "follow"), - SelectFilterOption("Most follows", "user_follow_count"), - SelectFilterOption("Most views", "view"), - SelectFilterOption("High rating", "rating"), - SelectFilterOption("Last updated", "uploaded"), - SelectFilterOption("Newest", "created_at"), - ]), - SelectFilter("StatusFilter", "Status", 0, [ - SelectFilterOption("All", "0"), - SelectFilterOption("Ongoing", "1"), - SelectFilterOption("Completed", "2"), - SelectFilterOption("Cancelled", "3"), - SelectFilterOption("Hiatus", "4"), - ]), - CheckBoxFilter("Completely Scanlated?", "", "CompletedFilter"), - SelectFilter("CreatedAtFilter", "Created at", 0, [ - SelectFilterOption("", ""), - SelectFilterOption("3 days", "3"), - SelectFilterOption("7 days", "7"), - SelectFilterOption("30 days", "30"), - SelectFilterOption("3 months", "90"), - SelectFilterOption("6 months", "180"), - SelectFilterOption("1 year", "365"), - ]), - TextFilter("MinimumFilter", "Minimum Chapters"), - HeaderFilter("From Year, ex: 2010"), - TextFilter("FromYearFilter", "From"), - HeaderFilter("To Year, ex: 2021"), - TextFilter("ToYearFilter", "To"), - HeaderFilter("Separate tags with commas"), - TextFilter("TagFilter", "Tags") - ]; - } - - String beautifyChapterName( - String vol, String chap, String title, String lang) { - String result = ""; - - if (vol != "null" && vol.isNotEmpty) { - if (chap != "null" && chap.isEmpty) { - result += "Volume $vol "; - } else { - result += "Vol. $vol "; - } - } - - if (chap != "null" && chap.isNotEmpty) { - if (vol != "null" && vol.isEmpty) { - if (lang != "null" && lang == "fr") { - result += "Chapitre $chap"; - } else { - result += "Chapter $chap"; - } - } else { - result += "Ch. $chap "; - } - } - - if (title != "null" && title.isNotEmpty) { - if (chap != "null" && chap.isEmpty) { - result += title; - } else { - result += " : $title"; - } - } - - return result; - } -} - -Map getHeader(String url) { - final headers = { - "Referer": "$url/", - 'User-Agent': - "Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0" - }; - return headers; -} - -ComickFun main(MSource source) { - return ComickFun(source: source); -} diff --git a/dart/manga/src/all/comick/sources.dart b/dart/manga/src/all/comick/sources.dart deleted file mode 100644 index dbfb1b6..0000000 --- a/dart/manga/src/all/comick/sources.dart +++ /dev/null @@ -1,72 +0,0 @@ -import '../../../../../model/source.dart'; - -const _comickVersion = "0.0.75"; -const _comickSourceCodeUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/src/all/comick/comick.dart"; - -String _iconUrl = - "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/manga/src/all/comick/icon.png"; -const _apiUrl = 'https://api.comick.fun'; -const _baseUrl = 'https://comick.app'; -const _isNsfw = true; - -List _languages = [ - "all", - "en", - "pt-br", - "ru", - "fr", - "es-419", - "pl", - "tr", - "it", - "es", - "id", - "hu", - "vi", - "zh-hk", - "ar", - "de", - "zh", - "ca", - "bg", - "th", - "fa", - "uk", - "mn", - "ro", - "he", - "ms", - "tl", - "ja", - "hi", - "my", - "ko", - "cs", - "pt", - "nl", - "sv", - "bn", - "no", - "lt", - "el", - "sr", - "da", -]; - -List get comickSourcesList => _comickSourcesList; -List _comickSourcesList = _languages - .map((e) => Source( - name: 'Comick', - apiUrl: _apiUrl, - baseUrl: _baseUrl, - lang: e, - typeSource: "comick", - iconUrl: _iconUrl, - dateFormat: "yyyy-MM-dd'T'HH:mm:ss'Z'", - isNsfw: _isNsfw, - dateFormatLocale: "en", - version: _comickVersion, - itemType: ItemType.manga, - sourceCodeUrl: _comickSourceCodeUrl)) - .toList(); diff --git a/dart/manga/src/all/comick/icon.png b/javascript/icon/all.comick.png similarity index 100% rename from dart/manga/src/all/comick/icon.png rename to javascript/icon/all.comick.png diff --git a/javascript/manga/src/all/comick.js b/javascript/manga/src/all/comick.js new file mode 100644 index 0000000..924efef --- /dev/null +++ b/javascript/manga/src/all/comick.js @@ -0,0 +1,424 @@ +const mangayomiSources = [ + { + "name": "Comick test", + "langs": ["all", "en", "pt-br", "ru", "fr", "es-419", "pl", "tr", "it", "es", "id", "hu", "vi", "zh-hk", "ar", "de", "zh", "ca", "bg", "th", "fa", "uk", "mn", "ro", "he", "ms", "tl", "ja", "hi", "my", "ko", "cs", "pt", "nl", "sv", "bn", "no", "lt", "el", "sr", "da"], + "ids": { + "all": 370890607, + "en": 955190069, + "pt-br": 494197461, + "ru": 1050814052, + "fr": 380505196, + "es-419": 296390197, + "pl": 242913014, + "tr": 507059585, + "it": 851891714, + "es": 115169439, + "id": 719269008, + "hu": 719759654, + "vi": 301477894, + "zh-hk": 113594984, + "ar": 602472856, + "de": 401493183, + "zh": 752155292, + "ca": 1069764002, + "bg": 678531099, + "th": 311480598, + "fa": 141560456, + "uk": 8261465, + "mn": 565474938, + "ro": 533803532, + "he": 459976450, + "ms": 375702775, + "tl": 737984097, + "ja": 796489006, + "hi": 683471552, + "my": 778623467, + "ko": 1065236294, + "cs": 422767524, + "pt": 678647945, + "nl": 698202010, + "sv": 359879447, + "bn": 532878423, + "no": 481504622, + "lt": 112887841, + "el": 824905526, + "sr": 373675453, + "da": 574420905 + }, + "baseUrl": "https://comick.io", + "apiUrl": "https://api.comick.fun", + "iconUrl": "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/javascript/icon/all.comick.png", + "typeSource": "single", + "itemType": 0, + "version": "0.0.8", + "pkgPath": "manga/src/all/comick.js" + }]; + +class DefaultExtension extends MProvider { + constructor() { + super(); + this.client = new Client(); + } + getHeaders(url) { + return { + "Referer": `${this.source.baseUrl}/`, + 'User-Agent': + "Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0" + }; + } + + async getPopular(page) { + const url = `${this.source.apiUrl}/v1.0/search?sort=follow&page=${page}&tachiyomi=true`; + const res = await this.client.get(url, this.getHeaders()); + return this.mangaRes(res.body); + } + + async getLatestUpdates(page) { + const url = `${this.source.apiUrl}/v1.0/search?sort=uploaded&page=${page}&tachiyomi=true`; + const res = await this.client.get(url, this.getHeaders()); + return this.mangaRes(res.body); + } + async search(query, page, filterList) { + let url = `${this.source.apiUrl}/v1.0/search`; + + if (query) { + url += `?q=${encodeURIComponent(query)}&tachiyomi=true`; + } else { + filterList.forEach(filter => { + if (filter.type === "CompletedFilter" && filter.state) { + url += `${this.ll(url)}completed=true`; + } else if (filter.type === "GenreFilter") { + const included = filter.state.filter(e => e.state === 1); + const excluded = filter.state.filter(e => e.state === 2); + included.forEach(val => url += `${this.ll(url)}genres=${val.value}`); + excluded.forEach(val => url += `${this.ll(url)}excludes=${val.value}`); + } else if (filter.type === "DemographicFilter") { + const included = filter.state.filter(e => e.state === 1); + included.forEach(val => url += `${this.ll(url)}demographic=${val.value}`); + } else if (filter.type === "TypeFilter") { + const country = filter.state.filter(e => e.state); + country.forEach(coun => url += `${this.ll(url)}country=${coun.value}`); + } else if (filter.type === "SortFilter") { + url += `${this.ll(url)}sort=${filter.values[filter.state].value}`; + } else if (filter.type === "StatusFilter") { + url += `${this.ll(url)}status=${filter.values[filter.state].value}`; + } else if (filter.type === "CreatedAtFilter" && filter.state > 0) { + url += `${this.ll(url)}time=${filter.values[filter.state].value}`; + } else if (filter.type === "MinimumFilter" && filter.state) { + url += `${this.ll(url)}minimum=${filter.state}`; + } else if (filter.type === "FromYearFilter" && filter.state) { + url += `${this.ll(url)}from=${filter.state}`; + } else if (filter.type === "ToYearFilter" && filter.state) { + url += `${this.ll(url)}to=${filter.state}`; + } else if (filter.type === "TagFilter" && filter.state) { + const tags = filter.state.split(","); + tags.forEach(tag => url += `${this.ll(url)}tags=${tag}`); + } + }); + url += `&page=${page}&tachiyomi=true`; + } + const res = await this.client.get(url, this.getHeaders()); + return this.mangaRes(res.body); + } + + async getDetail(url) { + const apiUrl = `${this.source.apiUrl}${url.replace("#", "")}?tachiyomi=true`; + const res = await this.client.get(apiUrl, this.getHeaders()); + const data = JSON.parse(res.body); + const lang = this.source.lang != "all" ? `&lang=${this.source.lang}` : ""; + const chapUrlReq = + `${this.source.apiUrl}${url.replaceAll("#", '')}chapters?${lang}&tachiyomi=true&page=1`; + const total = JSON.parse((await this.client.get(chapUrlReq, this.getHeaders())).body).total; + const newChapUrlReq = + `${this.source.apiUrl}${url.replaceAll("#", '')}chapters?limit=${parseInt(total, 10)}${lang}&tachiyomi=true&page=1`; + const newRes = await this.client.get(newChapUrlReq, this.getHeaders()); + const chapters = JSON.parse(newRes.body).chapters.map(chapter => { + let title = ""; + let scanlator = ""; + + if (chapter.chap !== "null" && chapter.vol !== "null") { + title = this.beautifyChapterName( + chapter.vol, + chapter.chap, + chapter.title + ); + } else { + title = chapter.title; + } + + if (chapter.group_name !== "null") { + scanlator = chapter.group_name + .toString() + .replace(/]/g, "") + .replace(/\[/g, ""); + } + + return { + name: title, + url: chapter.hid, + scanlator: scanlator ?? "", + dateUpload: new Date(chapter.created_at).valueOf().toString(), + }; + }); + + return { + author: data.authors?.map(author => author.name).join(', '), + description: data.comic.desc, + genres: Array.from(data.comic.md_comic_md_genres.map(g => g.md_genres.name)), + status: { "1": 0, "2": 1, "3": 3, "4": 2 }[data.comic.status], + chapters + }; + } + + async getPageList(url) { + const apiUrl = `${this.source.apiUrl}/chapter/${url}?tachiyomi=true`; + const res = await this.client.get(apiUrl, this.getHeaders()); + const data = JSON.parse(res.body); + return data.chapter.images.map(image => ({ + url: image.url + })); + } + getFilterList() { + return [ + { + type_name: "HeaderFilter", + name: "The filter is ignored when using text search.", + }, + { + type_name: "GroupFilter", + type: "GenreFilter", + name: "Genre", + state: [ + ["4-Koma", "4-koma"], + ["Action", "action"], + ["Adaptation", "adaptation"], + ["Adult", "adult"], + ["Adventure", "adventure"], + ["Aliens", "aliens"], + ["Animals", "animals"], + ["Anthology", "anthology"], + ["Award Winning", "award-winning"], + ["Comedy", "comedy"], + ["Cooking", "cooking"], + ["Crime", "crime"], + ["Crossdressing", "crossdressing"], + ["Delinquents", "delinquents"], + ["Demons", "demons"], + ["Doujinshi", "doujinshi"], + ["Drama", "drama"], + ["Ecchi", "ecchi"], + ["Fan Colored", "fan-colored"], + ["Fantasy", "fantasy"], + ["Full Color", "full-color"], + ["Gender Bender", "gender-bender"], + ["Genderswap", "genderswap"], + ["Ghosts", "ghosts"], + ["Gore", "gore"], + ["Gyaru", "gyaru"], + ["Harem", "harem"], + ["Historical", "historical"], + ["Horror", "horror"], + ["Incest", "incest"], + ["Isekai", "isekai"], + ["Loli", "loli"], + ["Long Strip", "long-strip"], + ["Mafia", "mafia"], + ["Magic", "magic"], + ["Magical Girls", "magical-girls"], + ["Martial Arts", "martial-arts"], + ["Mature", "mature"], + ["Mecha", "mecha"], + ["Medical", "medical"], + ["Military", "military"], + ["Monster Girls", "monster-girls"], + ["Monsters", "monsters"], + ["Music", "music"], + ["Mystery", "mystery"], + ["Ninja", "ninja"], + ["Office Workers", "office-workers"], + ["Official Colored", "official-colored"], + ["Oneshot", "oneshot"], + ["Philosophical", "philosophical"], + ["Police", "police"], + ["Post-Apocalyptic", "post-apocalyptic"], + ["Psychological", "psychological"], + ["Reincarnation", "reincarnation"], + ["Reverse Harem", "reverse-harem"], + ["Romance", "romance"], + ["Samurai", "samurai"], + ["School Life", "school-life"], + ["Sci-Fi", "sci-fi"], + ["Sexual Violence", "sexual-violence"], + ["Shota", "shota"], + ["Shoujo Ai", "shoujo-ai"], + ["Shounen Ai", "shounen-ai"], + ["Slice of Life", "slice-of-life"], + ["Smut", "smut"], + ["Sports", "sports"], + ["Superhero", "superhero"], + ["Supernatural", "supernatural"], + ["Survival", "survival"], + ["Thriller", "thriller"], + ["Time Travel", "time-travel"], + ["Traditional Games", "traditional-games"], + ["Tragedy", "tragedy"], + ["User Created", "user-created"], + ["Vampires", "vampires"], + ["Video Games", "video-games"], + ["Villainess", "villainess"], + ["Virtual Reality", "virtual-reality"], + ["Web Comic", "web-comic"], + ["Wuxia", "wuxia"], + ["Yaoi", "yaoi"], + ["Yuri", "yuri"], + ["Zombies", "zombies"] + ].map(x => ({ type_name: 'TriState', name: x[0], value: x[1] })) + }, + { + type_name: "GroupFilter", + type: "DemographicFilter", + name: "Demographic", + state: [ + ["Shounen", "1"], + ["Shoujo", "2"], + ["Seinen", "3"], + ["Josei", "4"] + ].map(x => ({ type_name: 'TriState', name: x[0], value: x[1] })) + }, + { + type_name: "GroupFilter", + type: "TypeFilter", + name: "Type", + state: [ + ["Manga", "jp"], + ["Manhwa", "kr"], + ["Manhua", "cn"] + ].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) + }, + { + type_name: "SelectFilter", + type: "SortFilter", + name: "Sort", + state: 0, + values: [ + ["Most popular", "follow"], + ["Most follows", "user_follow_count"], + ["Most views", "view"], + ["High rating", "rating"], + ["Last updated", "uploaded"], + ["Newest", "created_at"] + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, + { + type_name: "SelectFilter", + type: "StatusFilter", + name: "Status", + state: 0, + values: [ + ["All", "0"], + ["Ongoing", "1"], + ["Completed", "2"], + ["Cancelled", "3"], + ["Hiatus", "4"] + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, + { + type_name: 'CheckBox', + type: "CompletedFilter", + name: "Completely Scanlated?", + value: "" + }, + { + type_name: "SelectFilter", + type: "CreatedAtFilter", + name: "Created at", + state: 0, + values: [ + ["", ""], + ["3 days", "3"], + ["7 days", "7"], + ["30 days", "30"], + ["3 months", "90"], + ["6 months", "180"], + ["1 year", "365"] + ].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) + }, + { + type_name: "TextFilter", + type: "MinimumFilter", + name: "Minimum Chapters", + }, + { + type_name: "HeaderFilter", + name: "From Year, ex: 2010", + }, + { + type_name: "TextFilter", + type: "FromYearFilter", + name: "From", + }, + { + type_name: "HeaderFilter", + name: "To Year, ex: 2021", + }, + { + type_name: "TextFilter", + type: "ToYearFilter", + name: "To", + }, + { + type_name: "HeaderFilter", + name: "Separate tags with commas", + }, + { + type_name: "TextFilter", + type: "TagFilter", + name: "Tags", + }, + ]; + } + mangaRes(body) { + body = JSON.parse(body); + return { + list: body.map(manga => ({ + name: manga.title, + imageUrl: manga.cover_url, + link: `/comic/${manga.hid}/#` + })), + hasNextPage: body.hasNextPage || false + }; + } + beautifyChapterName(vol, chap, title) { + let result = ""; + + if (vol && vol.trim() !== "") { + if (chap && chap.trim() === "") { + result += `Volume ${vol} `; + } else { + result += `Vol. ${vol} `; + } + } + + if (chap && chap.trim() !== "") { + if (vol && vol.trim() === "") { + result += `Chapter ${chap}`; + } else { + result += `Ch. ${chap} `; + } + } + + if (title && title.trim() !== "") { + if (chap && chap.trim() === "") { + result += title; + } else { + result += ` : ${title}`; + } + } + + return result; + } + + ll(url) { + return url.includes("?") ? "&" : "?"; + } +} diff --git a/source_generator.dart b/source_generator.dart index 354e805..e55ac4a 100644 --- a/source_generator.dart +++ b/source_generator.dart @@ -8,9 +8,12 @@ import 'model/source.dart'; void main() { final jsSources = _searchJsSources(Directory("javascript")); - genManga(jsSources.where((element) => element.itemType!.name == "manga").toList()); - genAnime(jsSources.where((element) => element.itemType!.name == "anime").toList()); - genNovel(jsSources.where((element) => element.itemType!.name == "novel").toList()); + genManga( + jsSources.where((element) => element.itemType!.name == "manga").toList()); + genAnime( + jsSources.where((element) => element.itemType!.name == "anime").toList()); + genNovel( + jsSources.where((element) => element.itemType!.name == "novel").toList()); } void genManga(List jsMangasourceList) { @@ -83,9 +86,11 @@ List _searchJsSources(Directory dir) { } if (langs?.isNotEmpty ?? false) { for (var lang in langs!) { + final id = sourceJson["ids"]?[lang] as int?; sourceList.add(Source.fromJson(source.toJson()) ..lang = lang - ..id = 'mangayomi-js-"$lang"."${source.name}"'.hashCode); + ..id = + id ?? 'mangayomi-js-"$lang"."${source.name}"'.hashCode); } } else { sourceList.add(source);