diff --git a/manga/multisrc/madara/madara-v0.0.35.dart b/manga/multisrc/madara/madara-v0.0.4.dart similarity index 77% rename from manga/multisrc/madara/madara-v0.0.35.dart rename to manga/multisrc/madara/madara-v0.0.4.dart index c9fa5175..bcf84a40 100644 --- a/manga/multisrc/madara/madara-v0.0.35.dart +++ b/manga/multisrc/madara/madara-v0.0.4.dart @@ -67,11 +67,53 @@ class Madara extends MProvider { } @override - Future search(MSource source, String query, int page) async { - final data = { - "url": "${source.baseUrl}/?s=$query&post_type=wp-manga", - "sourceId": source.id - }; + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + + String url = "${source.baseUrl}/?s=$query&post_type=wp-manga"; + + for (var filter in filters) { + if (filter.type == "AuthorFilter") { + if (filter.state.isNotEmpty) { + url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}"; + } + } else if (filter.type == "ArtistFilter") { + if (filter.state.isNotEmpty) { + url += "${ll(url)}artist=${Uri.encodeComponent(filter.state)}"; + } + } else if (filter.type == "YearFilter") { + if (filter.state.isNotEmpty) { + url += "${ll(url)}release=${Uri.encodeComponent(filter.state)}"; + } + } else if (filter.type == "StatusFilter") { + final status = (filter.state as List).where((e) => e.state).toList(); + if (status.isNotEmpty) { + for (var st in status) { + url += "${ll(url)}status[]=${st.value},"; + } + } + } else if (filter.type == "OrderByFilter") { + if (filter.state != 0) { + final order = filter.values[filter.state].value; + url += "${ll(url)}m_orderby=$order"; + } + } else if (filter.type == "AdultContentFilter") { + final ctn = filter.values[filter.state].value; + if (ctn.isNotEmpty) { + url += "${ll(url)}adult=$ctn"; + } + } else if (filter.type == "GenreListFilter") { + final genres = (filter.state as List).where((e) => e.state).toList(); + if (genres.isNotEmpty) { + for (var genre in genres) { + url += "${ll(url)}genre[]=${genre.value},"; + } + } + } + } + + final data = {"url": url, "sourceId": source.id}; final res = await http('GET', json.encode(data)); List mangaList = []; @@ -312,6 +354,41 @@ class Madara extends MProvider { } return pageUrls; } + + List getFilterList() { + return [ + TextFilter("AuthorFilter", "Author"), + TextFilter("ArtistFilter", "Artist"), + TextFilter("YearFilter", "Year of Released"), + GroupFilter("StatusFilter", "Status", [ + CheckBoxFilter("Completed", "end"), + CheckBoxFilter("Ongoing", "on-going"), + CheckBoxFilter("Canceled", "canceled"), + CheckBoxFilter("On Hold", "on-hold"), + ]), + SelectFilter("OrderByFilter", "Order By", 0, [ + SelectFilterOption("Relevance", ""), + SelectFilterOption("Latest", "latest"), + SelectFilterOption("A-Z", "alphabet"), + SelectFilterOption("Rating", "rating"), + SelectFilterOption("Trending", "trending"), + SelectFilterOption("Most Views", "views"), + SelectFilterOption("New", "new-manga"), + ]), + SelectFilter("AdultContentFilter", "Adult Content", 0, [ + SelectFilterOption("All", ""), + SelectFilterOption("None", "0"), + SelectFilterOption("Only", "1"), + ]) + ]; + } + + String ll(String url) { + if (url.contains("?")) { + return "&"; + } + return "?"; + } } Madara main() { diff --git a/manga/multisrc/madara/sources.dart b/manga/multisrc/madara/sources.dart index a068374c..1e8d6532 100644 --- a/manga/multisrc/madara/sources.dart +++ b/manga/multisrc/madara/sources.dart @@ -1,7 +1,7 @@ import '../../../model/source.dart'; import '../../../utils/utils.dart'; -const madaraVersion = "0.0.35"; +const madaraVersion = "0.0.4"; const madaraSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/madara/madara-v$madaraVersion.dart"; const defaultDateFormat = "MMMM dd, yyyy"; diff --git a/manga/multisrc/mangareader/mangareader-v0.0.45.dart b/manga/multisrc/mangareader/mangareader-v0.0.5.dart similarity index 68% rename from manga/multisrc/mangareader/mangareader-v0.0.45.dart rename to manga/multisrc/mangareader/mangareader-v0.0.5.dart index 8450733c..e4e016e0 100644 --- a/manga/multisrc/mangareader/mangareader-v0.0.45.dart +++ b/manga/multisrc/mangareader/mangareader-v0.0.5.dart @@ -25,9 +25,49 @@ class MangaReader extends MProvider { } @override - Future search(MSource source, String query, int page) async { - final url = + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + + String url = "${source.baseUrl}${getMangaUrlDirectory(source.name)}/?&title=$query&page=$page"; + + for (var filter in filters) { + if (filter.type == "AuthorFilter") { + url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}"; + } else if (filter.type == "YearFilter") { + url += "${ll(url)}yearx=${Uri.encodeComponent(filter.state)}"; + } else if (filter.type == "StatusFilter") { + final status = filter.values[filter.state].value; + url += "${ll(url)}status=$status"; + } else if (filter.type == "TypeFilter") { + final type = filter.values[filter.state].value; + url += "${ll(url)}type=$type"; + } else if (filter.type == "OrderByFilter") { + final order = filter.values[filter.state].value; + url += "${ll(url)}order=$order"; + } else if (filter.type == "GenreListFilter") { + 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) { + url += "${ll(url)}genres[]="; + for (var val in included) { + url += "${val.value},"; + } + } + if (excluded.isNotEmpty) { + url += "${ll(url)}genres[]="; + for (var val in excluded) { + url += "-${val.value},"; + } + } + } + } + final data = {"url": url, "sourceId": source.id}; final res = await http('GET', json.encode(data)); @@ -195,6 +235,47 @@ class MangaReader extends MProvider { return MPages(mangaList, true); } + List getFilterList() { + return [ + SeparatorFilter(), + TextFilter("AuthorFilter", "Author"), + TextFilter("YearFilter", "Year"), + SelectFilter("StatusFilter", "Status", 0, [ + SelectFilterOption("All", ""), + SelectFilterOption("Ongoing", "ongoing"), + SelectFilterOption("Completed", "completed"), + SelectFilterOption("Hiatus", "hiatus"), + SelectFilterOption("Dropped", "dropped"), + ]), + SelectFilter("TypeFilter", "Type", 0, [ + SelectFilterOption("All", ""), + SelectFilterOption("Manga", "Manga"), + SelectFilterOption("Manhwa", "Manhwa"), + SelectFilterOption("Manhua", "Manhua"), + SelectFilterOption("Comic", "Comic"), + ]), + SelectFilter("OrderByFilter", "Sort By", 0, [ + SelectFilterOption("Default", ""), + SelectFilterOption("A-Z", "title"), + SelectFilterOption("Z-A", "titlereverse"), + SelectFilterOption("Latest Update", "update"), + SelectFilterOption("Latest Added", "latest"), + SelectFilterOption("Popular", "popular"), + ]), + HeaderFilter("Genre exclusion is not available for all sources"), + GroupFilter("GenreListFilter", "Genre", [ + TriStateFilter("Press reset to attempt to fetch genres", ""), + ]), + ]; + } + + String ll(String url) { + if (url.contains("?")) { + return "&"; + } + return "?"; + } + String getMangaUrlDirectory(String sourceName) { if (sourceName == "Sushi-Scan") { return "/catalogue"; diff --git a/manga/multisrc/mangareader/sources.dart b/manga/multisrc/mangareader/sources.dart index 38111773..6ab6dbe3 100644 --- a/manga/multisrc/mangareader/sources.dart +++ b/manga/multisrc/mangareader/sources.dart @@ -1,7 +1,7 @@ import '../../../model/source.dart'; import '../../../utils/utils.dart'; -const mangareaderVersion = "0.0.45"; +const mangareaderVersion = "0.0.5"; const mangareaderSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mangareader/mangareader-v$mangareaderVersion.dart"; const defaultDateFormat = "MMMM dd, yyyy"; diff --git a/manga/multisrc/mmrcms/mmrcms-v0.0.35.dart b/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart similarity index 50% rename from manga/multisrc/mmrcms/mmrcms-v0.0.35.dart rename to manga/multisrc/mmrcms/mmrcms-v0.0.4.dart index 77899ec6..d0f522cf 100644 --- a/manga/multisrc/mmrcms/mmrcms-v0.0.35.dart +++ b/manga/multisrc/mmrcms/mmrcms-v0.0.4.dart @@ -68,32 +68,74 @@ class MMRCMS extends MProvider { } @override - Future search(MSource source, String query, int page) async { - final url = "${source.baseUrl}/search?query=$query"; + Future search( + MSource source, String query, int page, FilterList filterList) async { + final filters = filterList.filters; + String url = ""; + if (query.isNotEmpty) { + url = "${source.baseUrl}/search?query=$query"; + } else { + url = "${source.baseUrl}/filterList?page=$page"; + for (var filter in filters) { + if (filter.type == "AuthorFilter") { + url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}"; + } else if (filter.type == "SortFilter") { + url += "${ll(url)}sortBy=${filter.values[filter.state.index].value}"; + final asc = filter.state.ascending ? "asc=true" : "asc=false"; + url += "${ll(url)}$asc"; + } else if (filter.type == "CategoryFilter") { + if (filter.state != 0) { + final cat = filter.values[filter.state].value; + url += "${ll(url)}cat=$cat"; + } + } else if (filter.type == "BeginsWithFilter") { + if (filter.state != 0) { + final a = filter.values[filter.state].value; + url += "${ll(url)}alpha=$a"; + } + } + } + } final data = {"url": url, "sourceId": source.id}; final res = await http('GET', json.encode(data)); List mangaList = []; - final jsonList = json.decode(res)["suggestions"]; + List urls = []; List names = []; List images = []; - for (var da in jsonList) { - String value = da["value"]; - String data = da["data"]; - if (source.name == 'Scan VF') { - urls.add('${source.baseUrl}/$data'); - } else if (source.name == 'Manga-FR') { - urls.add('${source.baseUrl}/lecture-en-ligne/$data'); - } else { - urls.add('${source.baseUrl}/manga/$data'); + + if (query.isNotEmpty) { + final jsonList = json.decode(res)["suggestions"]; + for (var da in jsonList) { + String value = da["value"]; + String data = da["data"]; + if (source.name == 'Scan VF') { + urls.add('${source.baseUrl}/$data'); + } else if (source.name == 'Manga-FR') { + urls.add('${source.baseUrl}/lecture-en-ligne/$data'); + } else { + urls.add('${source.baseUrl}/manga/$data'); + } + names.add(value); + if (source.name == "Manga-FR") { + images.add("${source.baseUrl}/uploads/manga/$data.jpg"); + } else { + images.add( + "${source.baseUrl}/uploads/manga/$data/cover/cover_250x350.jpg"); + } } - names.add(value); - if (source.name == "Manga-FR") { - images.add("${source.baseUrl}/uploads/manga/$data.jpg"); - } else { - images.add( - "${source.baseUrl}/uploads/manga/$data/cover/cover_250x350.jpg"); + } else { + urls = xpath(res, '//div/div/div/a/@href'); + names = xpath(res, '//div/div/div/a/text()'); + for (var url in urls) { + String slug = substringAfterLast(url, '/'); + if (source.name == "Manga-FR") { + images.add("${source.baseUrl}/uploads/manga/${slug}.jpg"); + } else { + images.add( + "${source.baseUrl}/uploads/manga/${slug}/cover/cover_250x350.jpg"); + } } } @@ -189,6 +231,91 @@ class MMRCMS extends MProvider { return pagesUrl; } + + List getFilterList() { + return [ + HeaderFilter("NOTE: Ignored if using text search!"), + SeparatorFilter(), + TextFilter("AuthorFilter", "Author"), + SelectFilter("CategoryFilter", "Category", 0, [ + SelectFilterOption("Any", ""), + SelectFilterOption("Action", "Action"), + SelectFilterOption("Adventure", "Adventure"), + SelectFilterOption("Comedy", "Comedy"), + SelectFilterOption("Doujinshi", "Doujinshi"), + SelectFilterOption("Drama", "Drama"), + SelectFilterOption("Ecchi", "Ecchi"), + SelectFilterOption("Fantasy", "Fantasy"), + SelectFilterOption("Gender Bender", "Gender Bender"), + SelectFilterOption("Harem", "Harem"), + SelectFilterOption("Historical", "Historical"), + SelectFilterOption("Horror", "Horror"), + SelectFilterOption("Josei", "Josei"), + SelectFilterOption("Martial Arts", "Martial Arts"), + SelectFilterOption("Mature", "Mature"), + SelectFilterOption("Mecha", "Mecha"), + SelectFilterOption("Mystery", "Mystery"), + SelectFilterOption("One Shot", "One Shot"), + SelectFilterOption("Psychological", "Psychological"), + SelectFilterOption("Romance", "Romance"), + SelectFilterOption("School Life", "School Life"), + SelectFilterOption("Sci-fi", "Sci-fi"), + SelectFilterOption("Seinen", "Seinen"), + SelectFilterOption("Shoujo", "Shoujo"), + SelectFilterOption("Shoujo Ai", "Shoujo Ai"), + SelectFilterOption("Shounen", "Shounen"), + SelectFilterOption("Shounen Ai", "Shounen Ai"), + SelectFilterOption("Slice of Life", "Slice of Life"), + SelectFilterOption("Sports", "Sports"), + SelectFilterOption("Supernatural", "Supernatural"), + SelectFilterOption("Tragedy", "Tragedy"), + SelectFilterOption("Yaoi", "Yaoi"), + SelectFilterOption("Yuri", "Yuri"), + ]), + SelectFilter("BeginsWithFilter", "Begins with", 0, [ + SelectFilterOption("Any", ""), + SelectFilterOption("#", "#"), + SelectFilterOption("A", "A"), + SelectFilterOption("B", "B"), + SelectFilterOption("C", "C"), + SelectFilterOption("D", "D"), + SelectFilterOption("E", "E"), + SelectFilterOption("F", "F"), + SelectFilterOption("G", "G"), + SelectFilterOption("H", "H"), + SelectFilterOption("I", "I"), + SelectFilterOption("J", "J"), + SelectFilterOption("K", "K"), + SelectFilterOption("L", "L"), + SelectFilterOption("M", "M"), + SelectFilterOption("N", "N"), + SelectFilterOption("O", "O"), + SelectFilterOption("P", "P"), + SelectFilterOption("Q", "Q"), + SelectFilterOption("R", "R"), + SelectFilterOption("S", "S"), + SelectFilterOption("T", "T"), + SelectFilterOption("U", "U"), + SelectFilterOption("V", "V"), + SelectFilterOption("W", "W"), + SelectFilterOption("X", "X"), + SelectFilterOption("Y", "Y"), + SelectFilterOption("Z", "Z"), + ]), + SortFilter("SortFilter", "Sort", SortState(0, true), [ + SelectFilterOption("Name", "name"), + SelectFilterOption("Popularity", "views"), + SelectFilterOption("Last update", "last_release"), + ]) + ]; + } + + String ll(String url) { + if (url.contains("?")) { + return "&"; + } + return "?"; + } } MMRCMS main() { diff --git a/manga/multisrc/mmrcms/sources.dart b/manga/multisrc/mmrcms/sources.dart index 7d186739..6b10aa8a 100644 --- a/manga/multisrc/mmrcms/sources.dart +++ b/manga/multisrc/mmrcms/sources.dart @@ -1,7 +1,7 @@ import '../../../model/source.dart'; import '../../../utils/utils.dart'; -const mmrcmsVersion = "0.0.35"; +const mmrcmsVersion = "0.0.4"; const mmrcmsSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/multisrc/mmrcms/mmrcms-v$mmrcmsVersion.dart"; const defaultDateFormat = "d MMM. yyyy"; diff --git a/manga/src/all/comick/comick-v0.0.35.dart b/manga/src/all/comick/comick-v0.0.35.dart deleted file mode 100644 index de315509..00000000 --- a/manga/src/all/comick/comick-v0.0.35.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'package:mangayomi/bridge_lib.dart'; -import 'dart:convert'; - -class ComickFun extends MProvider { - ComickFun(); - - @override - Future getPopular(MSource source, int page) async { - final url = - "${source.apiUrl}/v1.0/search?sort=follow&page=$page&tachiyomi=true"; - final data = {"url": url, "headers": getHeader(source.baseUrl)}; - final res = await http('GET', json.encode(data)); - return mangaRes(res); - } - - @override - Future getLatestUpdates(MSource source, int page) async { - final url = - "${source.apiUrl}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true"; - final data = {"url": url, "headers": getHeader(source.baseUrl)}; - final res = await http('GET', json.encode(data)); - return mangaRes(res); - } - - @override - Future search(MSource source, String query, int page) async { - final url = "${source.apiUrl}/v1.0/search?q=$query&tachiyomi=true"; - final data = {"url": url, "headers": getHeader(source.baseUrl)}; - final res = await http('GET', json.encode(data)); - return mangaRes(res); - } - - @override - Future getDetail(MSource source, String url) async { - final statusList = [ - {"1": 0, "2": 1, "3": 3, "4": 2} - ]; - - final headers = getHeader(source.baseUrl); - - final urll = "${source.apiUrl}${url.replaceAll("#", '')}?tachiyomi=true"; - final data = {"url": urll, "headers": headers}; - final res = await http('GET', json.encode(data)); - MManga manga = MManga(); - manga.author = jsonPathToString(res, r'$.authors[*].name', ''); - manga.genre = jsonPathToString(res, r'$.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=${source.lang}&tachiyomi=true&page=1"; - final dataReq = {"url": chapUrlReq, "headers": headers}; - final request = await http('GET', json.encode(dataReq)); - var total = jsonPathToString(request, r'$.total', ''); - final chapterLimit = int.parse(total); - final newChapUrlReq = - "${source.apiUrl}${url.replaceAll("#", '')}chapters?limit=$chapterLimit&lang=${source.lang}&tachiyomi=true&page=1"; - - final newDataReq = {"url": newChapUrlReq, "headers": headers}; - final newRequest = await http('GET', json.encode(newDataReq)); - - 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(MSource source, String url) async { - final urll = "${source.apiUrl}/chapter/$url?tachiyomi=true"; - final data = {"url": urll, "headers": getHeader(url)}; - final res = await http('GET', json.encode(data)); - 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 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() { - return ComickFun(); -} diff --git a/manga/src/all/comick/comick-v0.0.4.dart b/manga/src/all/comick/comick-v0.0.4.dart new file mode 100644 index 00000000..66e37a98 --- /dev/null +++ b/manga/src/all/comick/comick-v0.0.4.dart @@ -0,0 +1,647 @@ +import 'package:mangayomi/bridge_lib.dart'; +import 'dart:convert'; + +class ComickFun extends MProvider { + ComickFun(); + + @override + Future getPopular(MSource source, int page) async { + final url = + "${source.apiUrl}/v1.0/search?sort=follow&page=$page&tachiyomi=true"; + final data = {"url": url, "headers": getHeader(source.baseUrl)}; + final res = await http('GET', json.encode(data)); + return mangaRes(res); + } + + @override + Future getLatestUpdates(MSource source, int page) async { + final url = + "${source.apiUrl}/v1.0/search?sort=uploaded&page=$page&tachiyomi=true"; + final data = {"url": url, "headers": getHeader(source.baseUrl)}; + final res = await http('GET', json.encode(data)); + return mangaRes(res); + } + + @override + Future search( + MSource source, 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 data = {"url": url, "headers": getHeader(source.baseUrl)}; + final res = await http('GET', json.encode(data)); + return mangaRes(res); + } + + @override + Future getDetail(MSource source, String url) async { + final statusList = [ + {"1": 0, "2": 1, "3": 3, "4": 2} + ]; + + final headers = getHeader(source.baseUrl); + + final urll = "${source.apiUrl}${url.replaceAll("#", '')}?tachiyomi=true"; + final data = {"url": urll, "headers": headers}; + final res = await http('GET', json.encode(data)); + MManga manga = MManga(); + manga.author = jsonPathToString(res, r'$.authors[*].name', ''); + manga.genre = jsonPathToString(res, r'$.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=${source.lang}&tachiyomi=true&page=1"; + final dataReq = {"url": chapUrlReq, "headers": headers}; + final request = await http('GET', json.encode(dataReq)); + var total = jsonPathToString(request, r'$.total', ''); + final chapterLimit = int.parse(total); + final newChapUrlReq = + "${source.apiUrl}${url.replaceAll("#", '')}chapters?limit=$chapterLimit&lang=${source.lang}&tachiyomi=true&page=1"; + + final newDataReq = {"url": newChapUrlReq, "headers": headers}; + final newRequest = await http('GET', json.encode(newDataReq)); + + 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(MSource source, String url) async { + final urll = "${source.apiUrl}/chapter/$url?tachiyomi=true"; + final data = {"url": urll, "headers": getHeader(url)}; + final res = await http('GET', json.encode(data)); + 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 "?"; + } + + 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() { + return ComickFun(); +} diff --git a/manga/src/all/comick/sources.dart b/manga/src/all/comick/sources.dart index 1ed0aa86..ad9493b7 100644 --- a/manga/src/all/comick/sources.dart +++ b/manga/src/all/comick/sources.dart @@ -1,7 +1,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; -const comickVersion = "0.0.35"; +const comickVersion = "0.0.4"; const comickSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/comick/comick-v$comickVersion.dart"; diff --git a/manga/src/all/mangadex/mangadex-v0.0.4.dart b/manga/src/all/mangadex/mangadex-v0.0.45.dart similarity index 98% rename from manga/src/all/mangadex/mangadex-v0.0.4.dart rename to manga/src/all/mangadex/mangadex-v0.0.45.dart index 06e66455..0e686ef8 100644 --- a/manga/src/all/mangadex/mangadex-v0.0.4.dart +++ b/manga/src/all/mangadex/mangadex-v0.0.45.dart @@ -35,7 +35,8 @@ class MangaDex extends MProvider { } @override - Future search(MSource source, String query, int page) async { + Future search( + MSource source, String query, int page, FilterList filterList) async { final url = "https://api.mangadex.org/manga?includes[]=cover_art&offset=0&limit=20&title=$query${getMDXContentRating()}&order[followedCount]=desc&availableTranslatedLanguage[]=${source.lang}"; diff --git a/manga/src/all/mangadex/sources.dart b/manga/src/all/mangadex/sources.dart index 4cda0fe5..917c2fec 100644 --- a/manga/src/all/mangadex/sources.dart +++ b/manga/src/all/mangadex/sources.dart @@ -4,7 +4,7 @@ import '../../../../utils/utils.dart'; const apiUrl = 'https://api.mangadex.org'; const baseUrl = 'https://mangadex.org'; const isNsfw = true; -const mangadexVersion = "0.0.4"; +const mangadexVersion = "0.0.45"; const mangadexSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/all/mangadex/mangadex-v$mangadexVersion.dart"; String _iconUrl = getIconUrl("mangadex", "all"); diff --git a/manga/src/en/mangahere/mangahere-v0.0.35.dart b/manga/src/en/mangahere/mangahere-v0.0.4.dart similarity index 62% rename from manga/src/en/mangahere/mangahere-v0.0.35.dart rename to manga/src/en/mangahere/mangahere-v0.0.4.dart index ffcb8c17..1e31a2ac 100644 --- a/manga/src/en/mangahere/mangahere-v0.0.35.dart +++ b/manga/src/en/mangahere/mangahere-v0.0.4.dart @@ -59,10 +59,55 @@ class MangaHere extends MProvider { } @override - Future search(MSource source, String query, int page) async { + Future search( + MSource source, String query, int page, FilterList filterList) async { final headers = getHeader(source.baseUrl); - final url = "${source.baseUrl}/search?title=$query&page=$page"; + final filters = filterList.filters; + String url = "${source.baseUrl}/search"; + + for (var filter in filters) { + if (filter.type == "TypeList") { + final type = filter.values[filter.state].value; + url += "${ll(url)}type=$type"; + } else if (filter.type == "CompletionList") { + final cmp = filter.values[filter.state].value; + url += "${ll(url)}st=$cmp"; + } else if (filter.type == "RatingList") { + url += "${ll(url)}rating_method=gt"; + final rt = filter.values[filter.state].value; + url += "${ll(url)}rating=$rt"; + } else if (filter.type == "GenreList") { + 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) { + url += "${ll(url)}genres="; + for (var val in included) { + url += "${val.value},"; + } + } + if (excluded.isNotEmpty) { + url += "${ll(url)}nogenres="; + for (var val in excluded) { + url += "${val.value},"; + } + } + } else if (filter.type == "ArtistFilter") { + url += "${ll(url)}artist_method=cw"; + url += "${ll(url)}artist=${Uri.encodeComponent(filter.state)}"; + } else if (filter.type == "AuthorFilter") { + url += "${ll(url)}author_method=cw"; + url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}"; + } else if (filter.type == "YearFilter") { + url += "${ll(url)}released_method=cw"; + url += "${ll(url)}released=${Uri.encodeComponent(filter.state)}"; + } + } + url += "${ll(url)}title=$query&page=$page"; final data = {"url": url, "headers": headers}; final res = await http('POST', json.encode(data)); @@ -205,6 +250,83 @@ class MangaHere extends MProvider { return pageUrls; } + + String ll(String url) { + if (url.contains("?")) { + return "&"; + } + return "?"; + } + + List getFilterList() { + return [ + SelectFilter("TypeList", "Type", 1, [ + SelectFilterOption("American Manga", "5"), + SelectFilterOption("Any", "0"), + SelectFilterOption("Chinese Manhua", "3"), + SelectFilterOption("European Manga", "4"), + SelectFilterOption("Hong Kong Manga", "6"), + SelectFilterOption("Japanese Manga", "1"), + SelectFilterOption("Korean Manhwa", "2"), + SelectFilterOption("Other Manga", "7"), + ]), + TextFilter("ArtistFilter", "Artist"), + TextFilter("AuthorFilter", "Author"), + GroupFilter("GenreList", "Genres", [ + TriStateFilter("Action", "1"), + TriStateFilter("Adventure", "2"), + TriStateFilter("Comedy", "3"), + TriStateFilter("Fantasy", "4"), + TriStateFilter("Historical", "5"), + TriStateFilter("Horror", "6"), + TriStateFilter("Martial Arts", "7"), + TriStateFilter("Mystery", "8"), + TriStateFilter("Romance", "9"), + TriStateFilter("Shounen Ai", "10"), + TriStateFilter("Supernatural", "11"), + TriStateFilter("Drama", "12"), + TriStateFilter("Shounen", "13"), + TriStateFilter("School Life", "14"), + TriStateFilter("Shoujo", "15"), + TriStateFilter("Gender Bender", "16"), + TriStateFilter("Josei", "17"), + TriStateFilter("Psychological", "18"), + TriStateFilter("Seinen", "19"), + TriStateFilter("Slice of Life", "20"), + TriStateFilter("Sci-fi", "21"), + TriStateFilter("Ecchi", "22"), + TriStateFilter("Harem", "23"), + TriStateFilter("Shoujo Ai", "24"), + TriStateFilter("Yuri", "25"), + TriStateFilter("Mature", "26"), + TriStateFilter("Tragedy", "27"), + TriStateFilter("Yaoi", "28"), + TriStateFilter("Doujinshi", "29"), + TriStateFilter("Sports", "30"), + TriStateFilter("Adult", "31"), + TriStateFilter("One Shot", "32"), + TriStateFilter("Smut", "33"), + TriStateFilter("Mecha", "34"), + TriStateFilter("Shotacon", "35"), + TriStateFilter("Lolicon", "36"), + TriStateFilter("Webtoons", "37"), + ]), + SelectFilter("RatingList", "Minimum rating", 0, [ + SelectFilterOption("No Stars", "0"), + SelectFilterOption("1 Star", "1"), + SelectFilterOption("2 Stars", "2"), + SelectFilterOption("3 Stars", "3"), + SelectFilterOption("4 Stars", "4"), + SelectFilterOption("5 Stars", "5"), + ]), + TextFilter("YearFilter", "Year released"), + SelectFilter("CompletionList", "Completed series", 0, [ + SelectFilterOption("Either", "0"), + SelectFilterOption("No", "1"), + SelectFilterOption("Yes", "2"), + ]), + ]; + } } Map getHeader(String url) { diff --git a/manga/src/en/mangahere/source.dart b/manga/src/en/mangahere/source.dart index bd3a825c..ee055b5d 100644 --- a/manga/src/en/mangahere/source.dart +++ b/manga/src/en/mangahere/source.dart @@ -2,7 +2,7 @@ import '../../../../model/source.dart'; import '../../../../utils/utils.dart'; Source get mangahereSource => _mangahereSource; -const mangahereVersion = "0.0.35"; +const mangahereVersion = "0.0.4"; const mangahereSourceCodeUrl = "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/manga/src/en/mangahere/mangahere-v$mangahereVersion.dart"; Source _mangahereSource = Source(