import 'package:mangayomi/bridge_lib.dart'; import 'dart:convert'; class MangaPark extends MProvider { MangaPark({required this.source}); MSource source; final Client client = Client(source); @override bool get supportsLatest => true; @override Map get headers => {}; @override Future getMangaItems(int page, final method) async { List mangaList = []; final res = await client.post( Uri.parse("${source.apiUrl}"), headers: { "Accept": "*/*", "accept-language": "en-US,en;q=0.9,de;q=0.8,ja;q=0.7,es;q=0.6,nl;q=0.5", "content-type": "application/json", "referer": "https://mangapark.io/", "cookie": "nsfw=${preferenceNsfwContent()}; imgser=${preferenceImgServer()}; wd=2504x735" }, body: jsonEncode({ "query": """ query get_latestReleases(\$select: LatestReleases_Select) { get_latestReleases(select: \$select) { paging { total pages page init size skip limit prev next } items { id data { id dbStatus name origLang tranLang sfw_result urlPath urlCover600 urlCoverOri is_hot is_new follows last_chapterNodes(amount: 1) { id data { id dateCreate dbStatus isFinal dname urlPath is_new } } } sser_follow } } } """, "variables": { "select": { "where": method, "init": 24, "size": 24, "page": page } } })); final json = jsonDecode(res.body); final data = json["data"]["get_latestReleases"]; final items = data["items"]; final bool isNextPage = data["paging"]["pages"] > page; for (var item in items) { final mangaData = item["data"]; MManga manga = MManga(); manga.name = mangaData["name"]; manga.imageUrl = "${source.baseUrl}${mangaData["urlCover600"]}"; manga.link = "${source.baseUrl}${mangaData["urlPath"]}"; mangaList.add(manga); } return MPages(mangaList, isNextPage); } @override Future getPopular(int page) async { return getMangaItems(page, "popular"); } @override Future getLatestUpdates(int page) async { return getMangaItems(page, "release"); } @override Future search(String query, int page, FilterList filterList) async { List mangaList = []; final filters = filterList.filters; String url = ""; String genres = "&genres="; String translFrom = "&orig="; String translTo = "&lang="; for (var filter in filters) { if (filter.type == "SearchFilter") { if (filter.state.toString() != "") { query = filter.state.toString(); } } else if (filter.type == "GenresFilter") { for (int i = 0; i < filter.state.length; i++) { if (filter.state[i].state.toString() == "2") { genres += "|${filter.state[i].value.toString()},"; } else if (filter.state[i].state.toString() == "1") { genres += "${filter.state[i].value.toString()},"; } } } else if (filter.type == "TranslatedFromFilter") { for (int i = 0; i < filter.state.length; i++) { if (filter.state[i].state) { translFrom += "${filter.state[i].value.toString()},"; } } } else if (filter.type == "TranslatedToFilter") { for (int i = 0; i < filter.state.length; i++) { if (filter.state[i].state) { translTo += "${filter.state[i].value.toString()},"; } } } else if (filter.type == "OrigWorkFilter") { url += "&status=${filter.values[filter.state].value.toString()}"; } else if (filter.type == "MparkUplFilter") { url += "&upload=${filter.values[filter.state].value.toString()}"; } else if (filter.type == "ChapCountFilter") { url += "&chapters=${filter.values[filter.state].value.toString()}"; } else if (filter.type == "SortFilter") { url += "&sortby=${filter.values[filter.state].value.toString()}"; } } url += genres + translFrom + translTo; final res = await client.get(Uri.parse("${source.baseUrl}/search?word=${Uri.encodeComponent(query.trim())}&page=$page$url"), headers: { "Accept": "*/*", "accept-language": "en-US,en;q=0.9,de;q=0.8,ja;q=0.7,es;q=0.6,nl;q=0.5", "referer": "https://mangapark.io/", "cookie": "nsfw=${preferenceNsfwContent()}; imgser=${preferenceImgServer()}; wd=2504x735" }); final doc = parseHtml(res.body); final elements = doc.select("div.grid.gap-5.grid-cols-1.border-t.border-t-base-200.pt-5 > div"); if (elements.length == 0) { return MPages(mangaList, false); } for (final element in elements) { final nameElement = element.selectFirst("h3 > a > span"); final imageElement = element.selectFirst("div > div > a > img"); final urlElement = element.selectFirst("div > div > a"); final name = nameElement.text ?? "No Name"; final image = imageElement.attr("src").contains(source.baseUrl) ? imageElement.attr("src") : "${source.baseUrl}${imageElement.attr("src")}"; final url = urlElement.attr("href").contains(source.baseUrl) ? urlElement.attr("href") : "${source.baseUrl}${urlElement.attr("href")}"; MManga manga = MManga(); manga.name = name; manga.imageUrl = image; manga.link = url; mangaList.add(manga); } final lastPageNumber = int.tryParse(doc.selectFirst("div.flex.items-center.flex-wrap.space-x-1.my-10.justify-center > a:last-child")?.text.trim()); bool hasNextPage = (lastPageNumber > page); return MPages(mangaList, hasNextPage); } @override Future getDetail(String url) async { final statusList = [{ "Pending": 0, "Ongoing": 0, "Completed": 1, "Hiatus": 1, "Cancelled": 3, }]; final res = await client.get(Uri.parse("$url"), headers: { "Accept": "*/*", "accept-language": "en-US,en;q=0.9,de;q=0.8,ja;q=0.7,es;q=0.6,nl;q=0.5", "referer": "https://mangapark.io/", "cookie": "nsfw=${preferenceNsfwContent()}; imgser=${preferenceImgServer()}; wd=2504x735" }); final doc = parseHtml(res.body); final statusElement = doc.selectFirst("div:nth-child(2) > div:nth-child(4) > span.font-bold.uppercase"); final authorElements = doc.select("div.flex.flex-col > div.flex > div.grow.pl-3.space-y-2 > div.mt-2.text-sm > a"); final genreList = doc.select("div.flex.items-center.flex-wrap > span > span:nth-child(1)"); final descriptionElement = doc.selectFirst("div.limit-html-p"); final author = authorElements.isNotEmpty ? authorElements.map((e) => e.text).join(" | ") : "Anonymous"; final genres = genreList.map((e) => (e.text as String).replaceAll(",", "").trim()).toList() ?? []; final status = statusElement?.text ?? "Pending"; final description = descriptionElement.text ?? "no Description..."; final chapters = getChapters(doc); MManga manga = MManga(); manga.genre = genres; manga.author = author; manga.chapters = chapters; manga.description = description; manga.status = parseStatus(status, statusList); return manga; } List getChapters(MDocument doc) { List chapters = []; if (doc.selectFirst("div.group.flex.flex-col").outerHtml == null) { throw("Something went wrong. if this is a age-restricted manga, try to turn of the NSFW filter in the settings, then reload the manga."); } MElement chapterList = doc.selectFirst("div.group.flex.flex-col"); for (MElement chapterElement in chapterList.select("div.justify-between")) { var chapter = MChapter(); final chapterName = chapterElement.selectFirst("div > a"); final scanlatorElement = chapterElement.selectFirst("a.link-hover > span"); final name = chapterName.text ?? "No Name Given"; final url = chapterName.attr("href").contains(source.baseUrl) ? chapterName.attr("href") : "${source.baseUrl}${chapterName.attr("href")}"; final uploadDate = chapterElement.selectFirst("time")?.attr("data-time"); final scanlator = scanlatorElement.text ?? "Anonymous"; chapter.name = name; chapter.url = url; chapter.dateUpload = uploadDate; chapter.scanlator = scanlator; chapters.add(chapter); } return chapters; } @override Future>> getPageList(String url) async{ List> images = []; final res = await client.get(Uri.parse(url), headers: { "cookie": "${preferenceImgServer() == "" ? "" : "imgser=${preferenceImgServer()};"} nsfw=${preferenceNsfwContent()}; wd=2504x1362", "referer": url, }); final doc = parseHtml(res.body); final imageObjects = doc.select('div[data-name="image-item"] img'); for (final imageObject in imageObjects) { final imageUrl = imageObject.attr("src"); images.add({ "url": imageUrl.trim(), "headers": { "referer": url, } }); } return images; } @override List getFilterList() { return [ TextFilter("SearchFilter", "Search..."), SelectFilter("SortFilter", "Order By", 0, [ SelectFilterOption("Rating Score", "field_score"), SelectFilterOption("Most Follows", "field_follow"), SelectFilterOption("Most Reviews", "field_review"), SelectFilterOption("Most Comments", "field_comment"), SelectFilterOption("Most Chapters", "field_chapter"), SelectFilterOption("New Chapters", "field_update"), SelectFilterOption("Recently Created", "field_create"), SelectFilterOption("Name A-Z", "field_name"), SelectFilterOption("Most Views: Last 60 Minutes", "views_h001"), SelectFilterOption("Most Views: Last 12 Hours", "views_h012"), SelectFilterOption("Most Views: Last 24 Hours", "views_h024"), SelectFilterOption("Most Views: Last 7 Days", "views_d007"), SelectFilterOption("Most Views: Last 30 Days", "views_d030"), SelectFilterOption("Most Views: Last 90 Days", "views_d090"), SelectFilterOption("Most Views: Last 180 Days", "views_d180"), SelectFilterOption("Most Views: Last 360 Days", "views_d360"), SelectFilterOption("Most Views: All Time", "views_d000"), SelectFilterOption("Emotion: Awesome", "emotion_e1"), SelectFilterOption("Emotion: Funny", "emotion_e2"), SelectFilterOption("Emotion: Love", "emotion_e3"), SelectFilterOption("Emotion: Hot", "emotion_e4"), SelectFilterOption("Emotion: Sweet", "emotion_e5"), SelectFilterOption("Emotion: Cool", "emotion_e6"), SelectFilterOption("Emotion: Scared", "emotion_e7"), SelectFilterOption("Emotion: Angry", "emotion_e8"), SelectFilterOption("Emotion: Sad", "emotion_e9"), ]), SeparatorFilter(), GroupFilter("GenresFilter", "Manga Style", [ TriStateFilter("Artbook", "artbook"), TriStateFilter("Cartoon", "cartoon"), TriStateFilter("Comic", "comic"), TriStateFilter("Doujinshi", "doujinshi"), TriStateFilter("Imageset", "imageset"), TriStateFilter("Manga", "manga"), TriStateFilter("Manhua", "manhua"), TriStateFilter("Manhwa", "manhwa"), TriStateFilter("Webtoon", "webtoon"), TriStateFilter("Western", "western"), TriStateFilter("Oneshot", "oneshot"), TriStateFilter("4-Koma", "_4_koma"), TriStateFilter("Art-by-AI", "ai_art"), TriStateFilter("Story-by-AI", "ai_story"), ]), SeparatorFilter(), GroupFilter("GenresFilter", "General Filters", [ TriStateFilter("Shoujo(G)", "shoujo"), TriStateFilter("Shounen(B)", "shounen"), TriStateFilter("Josei(W)", "josei"), TriStateFilter("Seinen(M)", "seinen"), TriStateFilter("Yuri(GL)", "yuri"), TriStateFilter("Yaoi(BL)", "yaoi"), TriStateFilter("Futa(⚤)", "futa"), TriStateFilter("Bara(ML)", "bara"), TriStateFilter("Kodomo(Kid)", "kodomo"), TriStateFilter("Silver & Golden", "old_people"), TriStateFilter("Shoujo ai", "shoujo_ai"), TriStateFilter("Shounen ai", "shounen_ai"), TriStateFilter("Non-human", "non_human"), ]), GroupFilter("GenresFilter", "Explicit Filters", [ TriStateFilter("Gore", "gore"), TriStateFilter("Bloody", "bloody"), TriStateFilter("Violence", "violence"), TriStateFilter("Ecchi", "ecchi"), TriStateFilter("Adult", "adult"), TriStateFilter("Mature", "mature"), TriStateFilter("Smut", "smut"), TriStateFilter("Hentai", "hentai"), ]), GroupFilter("GenresFilter", "Detailed Filters (A-M)", [ TriStateFilter("Action", "action"), TriStateFilter("Adaptation", "adaptation"), TriStateFilter("Adventure", "adventure"), TriStateFilter("Age Gap", "age_gap"), TriStateFilter("Aliens", "aliens"), TriStateFilter("Animals", "animals"), TriStateFilter("Anthology", "anthology"), TriStateFilter("Beasts", "beasts"), TriStateFilter("Bodyswap", "bodyswap"), TriStateFilter("Blackmail", "blackmail"), TriStateFilter("Brocon/Siscon", "brocon_siscon"), TriStateFilter("Cars", "cars"), TriStateFilter("Cheating/Infidelity", "cheating_infidelity"), TriStateFilter("Childhood Friends", "childhood_friends"), TriStateFilter("College life", "college_life"), TriStateFilter("Comedy", "comedy"), TriStateFilter("Contest winning", "contest_winning"), TriStateFilter("Cooking", "cooking"), TriStateFilter("Crime", "crime"), TriStateFilter("Crossdressing", "crossdressing"), TriStateFilter("Cultivation", "cultivation"), TriStateFilter("Death Game", "death_game"), TriStateFilter("DegenerateMC", "degeneratemc"), TriStateFilter("Delinquents", "delinquents"), TriStateFilter("Dementia", "dementia"), TriStateFilter("Demons", "demons"), TriStateFilter("Drama", "drama"), TriStateFilter("Fantasy", "fantasy"), TriStateFilter("Fan-Colored", "fan_colored"), TriStateFilter("Fetish", "fetish"), TriStateFilter("Full Color", "full_color"), TriStateFilter("Game", "game"), TriStateFilter("Gender Bender", "gender_bender"), TriStateFilter("Genderswap", "genderswap"), TriStateFilter("Ghosts", "ghosts"), TriStateFilter("Gyaru", "gyaru"), TriStateFilter("Harem", "harem"), TriStateFilter("Harlequin", "harlequin"), TriStateFilter("Historical", "historical"), TriStateFilter("Horror", "horror"), TriStateFilter("Incest", "incest"), TriStateFilter("Isekai", "isekai"), TriStateFilter("Kids", "kids"), TriStateFilter("Loli", "loli"), TriStateFilter("Magic", "magic"), TriStateFilter("Magical Girls", "magical_girls"), TriStateFilter("Martial Arts", "martial_arts"), TriStateFilter("Master-Servant", "master_servant"), TriStateFilter("Mecha", "mecha"), TriStateFilter("Medical", "medical"), TriStateFilter("Military", "military"), TriStateFilter("Monster Girls", "monster_girls"), TriStateFilter("Monsters", "monsters"), TriStateFilter("Music", "music"), TriStateFilter("Mystery", "mystery"), ]), GroupFilter("GenresFilter", "Detailed Filters (N-Z)", [ TriStateFilter("Netori", "netori"), TriStateFilter("Netorare/NTR", "netorare"), TriStateFilter("Ninja", "ninja"), TriStateFilter("Office Workers", "office_workers"), TriStateFilter("Omegaverse", "omegaverse"), TriStateFilter("Parody", "parody"), TriStateFilter("Philosophical", "philosophical"), TriStateFilter("Police", "police"), TriStateFilter("Post-Apocalyptic", "post_apocalyptic"), TriStateFilter("Psychological", "psychological"), TriStateFilter("Reincarnation", "reincarnation"), TriStateFilter("Revenge", "revenge"), TriStateFilter("Reverse Harem", "reverse_harem"), TriStateFilter("Romance", "romance"), TriStateFilter("Samurai", "samurai"), TriStateFilter("School Life", "school_life"), TriStateFilter("Sci-Fi", "sci_fi"), TriStateFilter("Shota", "shota"), TriStateFilter("Showbiz", "showbiz"), TriStateFilter("Slice of Life", "slice_of_life"), TriStateFilter("SM/BDSM", "sm_bdsm"), TriStateFilter("Space", "space"), TriStateFilter("Sports", "sports"), TriStateFilter("Spy", "spy"), TriStateFilter("Step Family", "step_family"), TriStateFilter("Super Power", "super_power"), TriStateFilter("Superhero", "superhero"), TriStateFilter("Supernatural", "supernatural"), TriStateFilter("Survival", "survival"), TriStateFilter("Teacher-Student", "teacher_student"), TriStateFilter("Thriller", "thriller"), TriStateFilter("Time Travel", "time_travel"), TriStateFilter("Traditional Games", "traditional_games"), TriStateFilter("Tragedy", "tragedy"), TriStateFilter("Vampires", "vampires"), TriStateFilter("Video Games", "video_games"), TriStateFilter("Villainess", "villainess"), TriStateFilter("Virtual Reality", "virtual_reality"), TriStateFilter("Wuxia", "wuxia"), TriStateFilter("Xianxia", "xianxia"), TriStateFilter("Xuanhuan", "xuanhuan"), TriStateFilter("Zombies", "zombies"), ]), SeparatorFilter(), GroupFilter("TranslatedFromFilter", "Original Work Language", [ CheckBoxFilter("Chinese", "zh"), CheckBoxFilter("English", "en"), CheckBoxFilter("Japanese", "jp"), CheckBoxFilter("Korean", "ko"), ]), GroupFilter("TranslatedToFilter", "Translated Language", [ CheckBoxFilter("Dutch", "nl"), CheckBoxFilter("English", "en"), CheckBoxFilter("German", "de"), CheckBoxFilter("Japanese", "jp"), CheckBoxFilter("Spanish", "es"), CheckBoxFilter("Spanish (LA)", "es_419"), ]), SeparatorFilter(), SelectFilter("OrigWorkFilter", "Original Work Status", 0, [ SelectFilterOption("Any", ""), SelectFilterOption("Pending", "pending"), SelectFilterOption("Ongoing", "ongoing"), SelectFilterOption("Completed", "completed"), SelectFilterOption("Hiatus", "hiatus"), SelectFilterOption("Cancelled", "cancelled"), ]), SelectFilter("MparkUplFilter", "MangaPark Upload Status", 0, [ SelectFilterOption("Any", ""), SelectFilterOption("Pending", "pending"), SelectFilterOption("Ongoing", "ongoing"), SelectFilterOption("Completed", "completed"), SelectFilterOption("Hiatus", "hiatus"), SelectFilterOption("Cancelled", "cancelled"), ]), SeparatorFilter(), SelectFilter("ChapCountFilter", "Number of Chapters", 0, [ SelectFilterOption("Any", ""), SelectFilterOption("0", "0"), SelectFilterOption("1+", "1"), SelectFilterOption("10+", "10"), SelectFilterOption("20+", "20"), SelectFilterOption("30+", "30"), SelectFilterOption("40+", "40"), SelectFilterOption("50+", "50"), SelectFilterOption("60+", "60"), SelectFilterOption("70+", "70"), SelectFilterOption("80+", "80"), SelectFilterOption("90+", "90"), SelectFilterOption("100+", "100"), SelectFilterOption("200+", "200"), SelectFilterOption("300+", "300"), SelectFilterOption("299~200", "200-299"), SelectFilterOption("199~100", "100-199"), SelectFilterOption("99~90", "90-99"), SelectFilterOption("89~80", "80-89"), SelectFilterOption("79~70", "70-79"), SelectFilterOption("69~60", "60-69"), SelectFilterOption("59~50", "50-59"), SelectFilterOption("49~40", "40-49"), SelectFilterOption("39~30", "30-39"), SelectFilterOption("29~20", "20-29"), SelectFilterOption("19~10", "10-19"), SelectFilterOption("9~1", "1-9"), ]), ]; } @override List getSourcePreferences() { return [ ListPreference( key: "NsfwFilter1", title: "Display NSFW content", summary: "", valueIndex: 0, entries: ["False", "True"], entryValues: [0, 2], ), ListPreference( key: "ImgServer", title: "Choose Image Server", summary: "", valueIndex: 0, entries: ["Default", "mpfip.org", "mpizz.org", "mpmok.org", "mpqom.org", "mpqrc.org", "mprnm.org", "mpubn.org", "mpujj.org", "mpvim.org", "mypypl.org"], entryValues: ["", "mpfip.org", "mpizz.org", "mpmok.org", "mpqom.org", "mpqrc.org", "mprnm.org", "mpubn.org", "mpujj.org", "mpvim.org", "mypypl.org"], ), ]; } int preferenceNsfwContent() { return getPreferenceValue(source.id, "NsfwFilter1"); } String preferenceImgServer() { return getPreferenceValue(source.id, "ImgServer"); } } MangaPark main(MSource source) { return MangaPark(source:source); }