diff --git a/src/all/mangapark/AndroidManifest.xml b/src/all/mangapark/AndroidManifest.xml index 9c5dbd1fb..8072ee00d 100644 --- a/src/all/mangapark/AndroidManifest.xml +++ b/src/all/mangapark/AndroidManifest.xml @@ -1,22 +1,2 @@ - - - - - - - - - - - - - - + diff --git a/src/all/mangapark/build.gradle b/src/all/mangapark/build.gradle index 03046c8c2..b57b5c530 100644 --- a/src/all/mangapark/build.gradle +++ b/src/all/mangapark/build.gradle @@ -3,15 +3,11 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlinx-serialization' ext { - extName = 'MangaPark v3' + extName = 'MangaPark' pkgNameSuffix = 'all.mangapark' extClass = '.MangaParkFactory' - extVersionCode = 18 + extVersionCode = 19 isNsfw = true } apply from: "$rootDir/common.gradle" - -dependencies { - implementation(project(':lib-cryptoaes')) -} \ No newline at end of file diff --git a/src/all/mangapark/res/mipmap-hdpi/ic_launcher.png b/src/all/mangapark/res/mipmap-hdpi/ic_launcher.png index d75a3acbc..aa0ab84be 100644 Binary files a/src/all/mangapark/res/mipmap-hdpi/ic_launcher.png and b/src/all/mangapark/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/mangapark/res/mipmap-mdpi/ic_launcher.png b/src/all/mangapark/res/mipmap-mdpi/ic_launcher.png index bff559a9a..9c1d1e9cb 100644 Binary files a/src/all/mangapark/res/mipmap-mdpi/ic_launcher.png and b/src/all/mangapark/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/mangapark/res/mipmap-xhdpi/ic_launcher.png b/src/all/mangapark/res/mipmap-xhdpi/ic_launcher.png index 5db811547..8119cc79a 100644 Binary files a/src/all/mangapark/res/mipmap-xhdpi/ic_launcher.png and b/src/all/mangapark/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/mangapark/res/mipmap-xxhdpi/ic_launcher.png b/src/all/mangapark/res/mipmap-xxhdpi/ic_launcher.png index 764ff8bcb..caf61063c 100644 Binary files a/src/all/mangapark/res/mipmap-xxhdpi/ic_launcher.png and b/src/all/mangapark/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/mangapark/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/mangapark/res/mipmap-xxxhdpi/ic_launcher.png index b4f2b0ff2..57272a58f 100644 Binary files a/src/all/mangapark/res/mipmap-xxxhdpi/ic_launcher.png and b/src/all/mangapark/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/mangapark/res/web_hi_res_512.png b/src/all/mangapark/res/web_hi_res_512.png index f789273b6..3d37f6a4c 100644 Binary files a/src/all/mangapark/res/web_hi_res_512.png and b/src/all/mangapark/res/web_hi_res_512.png differ diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/CookieInterceptor.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/CookieInterceptor.kt new file mode 100644 index 000000000..09a4691b7 --- /dev/null +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/CookieInterceptor.kt @@ -0,0 +1,46 @@ +package eu.kanade.tachiyomi.extension.all.mangapark + +import android.util.Log +import android.webkit.CookieManager +import okhttp3.Interceptor +import okhttp3.Response + +class CookieInterceptor( + private val domain: String, + private val key: String, + private val value: String, +) : Interceptor { + + init { + val url = "https://$domain/" + val cookie = "$key=$value; Domain=$domain; Path=/" + setCookie(url, cookie) + } + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + if (!request.url.host.endsWith(domain)) return chain.proceed(request) + + val cookie = "$key=$value" + val cookieList = request.header("Cookie")?.split("; ") ?: emptyList() + if (cookie in cookieList) return chain.proceed(request) + + setCookie("https://$domain/", "$cookie; Domain=$domain; Path=/") + val prefix = "$key=" + val newCookie = buildList(cookieList.size + 1) { + cookieList.filterNotTo(this) { it.startsWith(prefix) } + add(cookie) + }.joinToString("; ") + val newRequest = request.newBuilder().header("Cookie", newCookie).build() + return chain.proceed(newRequest) + } + + private fun setCookie(url: String, value: String) { + try { + CookieManager.getInstance().setCookie(url, value) + } catch (e: Exception) { + // Probably running on Tachidesk + Log.e("MangaPark", "failed to set cookie", e) + } + } +} diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt index 108711b15..affd6491d 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt @@ -1,289 +1,251 @@ package eu.kanade.tachiyomi.extension.all.mangapark -import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES -import eu.kanade.tachiyomi.lib.cryptoaes.Deobfuscator +import android.app.Application +import android.widget.Toast +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.OkHttpClient +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import java.util.Calendar -import java.util.Locale -import java.util.concurrent.TimeUnit -open class MangaPark( - final override val lang: String, - private val siteLang: String, -) : ParsedHttpSource() { +class MangaPark( + override val lang: String, + private val siteLang: String = lang, +) : HttpSource(), ConfigurableSource { - override val name: String = "MangaPark v3" - - override val baseUrl: String = "https://mangapark.net" + override val name = "MangaPark" override val supportsLatest = true - override val id: Long = when (lang) { - "zh-Hans" -> 6306867705763005424 - "zh-Hant" -> 4563855043528673539 - else -> super.id - } + override val versionId = 2 + + private val preference = + Injekt.get().getSharedPreferences("source_$id", 0x0000) + + private val domain = + preference.getString(MIRROR_PREF_KEY, MIRROR_PREF_DEFAULT) ?: MIRROR_PREF_DEFAULT + + override val baseUrl = "https://$domain" + + private val apiUrl = "$baseUrl/apo/" private val json: Json by injectLazy() - private val mpFilters = MangaParkFilters() - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client = network.cloudflareClient.newBuilder() + .addInterceptor(CookieInterceptor(domain, "nsfw", "2")) + .rateLimitHost(apiUrl.toHttpUrl(), 1) .build() - // Site Browse Helper - private fun browseMangaSelector(): String = "div#subject-list div.col" + override fun headersBuilder() = super.headersBuilder() + .set("Referer", "$baseUrl/") - private fun browseNextPageSelector(): String = - "div#mainer nav.d-none .pagination .page-item:last-of-type:not(.disabled)" + override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", SortFilter.POPULAR) + override fun popularMangaParse(response: Response) = searchMangaParse(response) - private fun browseMangaFromElement(element: Element): SManga { - return SManga.create().apply { - setUrlWithoutDomain(element.select("a.fw-bold").attr("href")) - title = element.select("a.fw-bold").text() - thumbnail_url = element.select("a.position-relative img").attr("abs:src") - } - } + override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", SortFilter.LATEST) + override fun latestUpdatesParse(response: Response) = searchMangaParse(response) - // Latest - override fun latestUpdatesRequest(page: Int): Request = - GET("$baseUrl/browse?sort=update&page=$page") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val payload = GraphQL( + SearchVariables( + SearchPayload( + page = page, + size = size, + query = query.takeUnless(String::isEmpty), + incGenres = filters.firstInstanceOrNull()?.included, + excGenres = filters.firstInstanceOrNull()?.excluded, + incTLangs = listOf(siteLang), + incOLangs = filters.firstInstanceOrNull()?.checked, + sortby = filters.firstInstanceOrNull()?.selected, + chapCount = filters.firstInstanceOrNull()?.selected, + origStatus = filters.firstInstanceOrNull()?.selected, + siteStatus = filters.firstInstanceOrNull()?.selected, + ), + ), + SEARCH_QUERY, + ).toJsonRequestBody() - override fun latestUpdatesSelector(): String = browseMangaSelector() - - override fun latestUpdatesNextPageSelector(): String = browseNextPageSelector() - - override fun latestUpdatesFromElement(element: Element): SManga = - browseMangaFromElement(element) - - // Popular - override fun popularMangaRequest(page: Int): Request = - GET("$baseUrl/browse?sort=d007&page=$page") - - override fun popularMangaSelector(): String = browseMangaSelector() - - override fun popularMangaNextPageSelector(): String = browseNextPageSelector() - - override fun popularMangaFromElement(element: Element): SManga = - browseMangaFromElement(element) - - // Search - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return when { - query.startsWith(PREFIX_ID_SEARCH) -> fetchSearchIdManga(query) - query.isNotBlank() -> fetchSearchManga(page, query) - else -> fetchGenreSearchManga(page, filters) - } - } - - // Search With Manga ID - private fun fetchSearchIdManga(idWithPrefix: String): Observable { - val id = idWithPrefix.removePrefix(PREFIX_ID_SEARCH) - return client.newCall(GET("$baseUrl/comic/$id", headers)) - .asObservableSuccess() - .map { response -> - MangasPage(listOf(mangaDetailsParse(response.asJsoup())), false) - } - } - - // Search WIth Query - private fun fetchSearchManga(page: Int, query: String): Observable { - return client.newCall(GET("$baseUrl/search?word=$query&page=$page", headers)) - .asObservableSuccess() - .map { response -> - searchMangaParse(response) - } - } - - // Search With Filter - private fun fetchGenreSearchManga(page: Int, filters: FilterList): Observable { - val url = "$baseUrl/browse".toHttpUrl().newBuilder() - .addQueryParameter("page", page.toString()).let { mpFilters.addFiltersToUrl(it, filters) } - - return client.newCall(GET(url, headers)) - .asObservableSuccess() - .map { response -> - searchMangaParse(response) - } - } - - override fun searchMangaSelector(): String = "div#search-list div.col" - - override fun searchMangaNextPageSelector(): String = - "div#mainer nav.d-none .pagination .page-item:last-of-type:not(.disabled)" - - override fun searchMangaFromElement(element: Element): SManga { - return SManga.create().apply { - setUrlWithoutDomain(element.select("a.fw-bold").attr("href")) - title = element.select("a.fw-bold").text() - thumbnail_url = element.select("a.position-relative img").attr("abs:src") - } + return POST(apiUrl, headers, payload) } override fun searchMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - val isBrowse = response.request.url.pathSegments[0] == "browse" - val mangaSelector = if (isBrowse) browseMangaSelector() else searchMangaSelector() - val nextPageSelector = if (isBrowse) browseNextPageSelector() else searchMangaNextPageSelector() + runCatching(::getGenres) - val mangas = document.select(mangaSelector).map { element -> - if (isBrowse) browseMangaFromElement(element) else searchMangaFromElement(element) - } + val result = response.parseAs() - val hasNextPage = document.select(nextPageSelector).first() != null + val entries = result.data.searchComics.items.map { it.data.toSManga() } + val hasNextPage = entries.size == size - return MangasPage(mangas, hasNextPage) + return MangasPage(entries, hasNextPage) } - // Manga Details - override fun mangaDetailsParse(document: Document): SManga { - val infoElement = document.select("div#mainer div.container-fluid") + private var genreCache: List> = emptyList() + private var genreFetchAttempt = 0 - return SManga.create().apply { - setUrlWithoutDomain(infoElement.select("h3.item-title a").attr("href")) + private fun getGenres() { + if (genreCache.isEmpty() && genreFetchAttempt < 3) { + val elements = runCatching { + client.newCall(GET("$baseUrl/search", headers)).execute() + .use { it.asJsoup() } + .select("div.flex-col:contains(Genres) div.whitespace-nowrap") + }.getOrNull().orEmpty() - title = infoElement.select("h3.item-title").text() + genreCache = elements.mapNotNull { + val name = it.selectFirst("span.whitespace-nowrap") + ?.text()?.takeUnless(String::isEmpty) + ?: return@mapNotNull null - description = infoElement.select("div.limit-height-body") - .select("h5.text-muted, div.limit-html") - .joinToString("\n\n") { it.text().trim() } + "\n\nAlt. Titles" + infoElement - .select("div.alias-set").text() - .split("/").joinToString(", ") { it.trim() } + val key = it.attr("q:key") + .takeUnless(String::isEmpty) ?: return@mapNotNull null - author = infoElement.select("div.attr-item:contains(author) a") - .joinToString { it.text().trim() } - - status = infoElement.select("div.attr-item:contains(status) span") - .text().parseStatus() - - thumbnail_url = infoElement.select("div.detail-set div.attr-cover img").attr("abs:src") - - genre = infoElement.select("div.attr-item:contains(genres) span span") - .joinToString { it.text().trim() } + Pair(name, key) + } + genreFetchAttempt++ } } - private fun String?.parseStatus() = if (this == null) { - SManga.UNKNOWN - } else { - when { - this.lowercase(Locale.US).contains("ongoing") -> SManga.ONGOING - this.lowercase(Locale.US).contains("hiatus") -> SManga.ONGOING - this.lowercase(Locale.US).contains("completed") -> SManga.COMPLETED - else -> SManga.UNKNOWN + override fun getFilterList(): FilterList { + val filters = mutableListOf>( + SortFilter(), + OriginalLanguageFilter(), + OriginalStatusFilter(), + UploadStatusFilter(), + ChapterCountFilter(), + ) + + if (genreCache.isEmpty()) { + filters += listOf( + Filter.Separator(), + Filter.Header("Press 'reset' to attempt to load genres"), + ) + } else { + filters.addAll(1, listOf(GenreFilter(genreCache))) } + + return FilterList(filters) + } + + override fun mangaDetailsRequest(manga: SManga): Request { + val payload = GraphQL( + IdVariables(manga.url.substringAfterLast("#")), + DETAILS_QUERY, + ).toJsonRequestBody() + + return POST(apiUrl, headers, payload) + } + + override fun mangaDetailsParse(response: Response): SManga { + val result = response.parseAs() + + return result.data.comic.data.toSManga() + } + + override fun getMangaUrl(manga: SManga) = baseUrl + manga.url.substringBeforeLast("#") + + override fun chapterListRequest(manga: SManga): Request { + val payload = GraphQL( + IdVariables(manga.url.substringAfterLast("#")), + CHAPTERS_QUERY, + ).toJsonRequestBody() + + return POST(apiUrl, headers, payload) } override fun chapterListParse(response: Response): List { - val chapterListHtml = response.asJsoup().select("div.episode-list #chap-index") - return chapterListHtml.flatMap { it.select(chapterListSelector()).map { chapElem -> chapterFromElement(chapElem) } } + val result = response.parseAs() + + return result.data.chapterList.map { it.data.toSChapter() }.reversed() } - override fun chapterListSelector(): String { - return when (lang) { - "en" -> "div.p-2:not(:has(.px-3))" - // To handle both "/comic/1/test/c0-en" and "/comic/1/test/c0-en/" like url - else -> "div.p-2:has(.px-3 a[href\$=\"$siteLang\"]), div.p-2:has(.px-3 a[href\$=\"$siteLang/\"])" + override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url.substringBeforeLast("#") + + override fun pageListRequest(chapter: SChapter): Request { + val payload = GraphQL( + IdVariables(chapter.url.substringAfterLast("#")), + PAGES_QUERY, + ).toJsonRequestBody() + + return POST(apiUrl, headers, payload) + } + + override fun pageListParse(response: Response): List { + val result = response.parseAs() + + return result.data.chapterPages.data.imageFile.urlList.mapIndexed { idx, url -> + Page(idx, "", url) } } - override fun chapterFromElement(element: Element): SChapter { - val urlElement = element.select("a.ms-3") + override fun setupPreferenceScreen(screen: PreferenceScreen) { + ListPreference(screen.context).apply { + key = MIRROR_PREF_KEY + title = "Preferred Mirror" + entries = mirrors + entryValues = mirrors + setDefaultValue(MIRROR_PREF_DEFAULT) + summary = "%s" - return SChapter.create().apply { - name = urlElement.text().removePrefix("Ch").trim() - date_upload = element.select("i.text-nowrap").text().parseChapterDate() - setUrlWithoutDomain(urlElement.attr("href").removeSuffix("/")) - } - } - - private fun String?.parseChapterDate(): Long { - if (this == null) return 0L - val value = this.split(' ')[0].toInt() - - return when (this.split(' ')[1].removeSuffix("s")) { - "sec" -> Calendar.getInstance().apply { - add(Calendar.SECOND, value * -1) - }.timeInMillis - "min" -> Calendar.getInstance().apply { - add(Calendar.MINUTE, value * -1) - }.timeInMillis - "hour" -> Calendar.getInstance().apply { - add(Calendar.HOUR_OF_DAY, value * -1) - }.timeInMillis - "day" -> Calendar.getInstance().apply { - add(Calendar.DATE, value * -1) - }.timeInMillis - "week" -> Calendar.getInstance().apply { - add(Calendar.DATE, value * 7 * -1) - }.timeInMillis - "month" -> Calendar.getInstance().apply { - add(Calendar.MONTH, value * -1) - }.timeInMillis - "year" -> Calendar.getInstance().apply { - add(Calendar.YEAR, value * -1) - }.timeInMillis - else -> { - return 0L + setOnPreferenceChangeListener { _, _ -> + Toast.makeText(screen.context, "Restart Tachiyomi to apply changes", Toast.LENGTH_LONG).show() + true } - } + }.also(screen::addPreference) } - override fun pageListParse(document: Document): List { - if (document.selectFirst("div.wrapper-deleted") != null) { - throw Exception("The chapter content seems to be deleted.\n\nContact the site owner if possible.") - } + private inline fun Response.parseAs(): T = + use { body.string() }.let(json::decodeFromString) - val script = document.selectFirst("script:containsData(imgHttpLis):containsData(amWord):containsData(amPass)")?.html() - ?: throw RuntimeException("Couldn't find script with image data.") + private inline fun List<*>.firstInstanceOrNull(): T? = + filterIsInstance().firstOrNull() - val imgHttpLisString = script.substringAfter("const imgHttpLis =").substringBefore(";").trim() - val imgHttpLis = json.parseToJsonElement(imgHttpLisString).jsonArray.map { it.jsonPrimitive.content } - val amWord = script.substringAfter("const amWord =").substringBefore(";").trim() - val amPass = script.substringAfter("const amPass =").substringBefore(";").trim() + private inline fun T.toJsonRequestBody() = + json.encodeToString(this).toRequestBody(JSON_MEDIA_TYPE) - val evaluatedPass: String = Deobfuscator.deobfuscateJsPassword(amPass) - val imgAccListString = CryptoAES.decrypt(amWord.removeSurrounding("\""), evaluatedPass) - val imgAccList = json.parseToJsonElement(imgAccListString).jsonArray.map { it.jsonPrimitive.content } - - return imgHttpLis.zip(imgAccList).mapIndexed { i, (imgUrl, imgAcc) -> - Page(i, imageUrl = "$imgUrl?$imgAcc") - } + override fun imageUrlParse(response: Response): String { + throw UnsupportedOperationException("Not Used") } - override fun getFilterList() = mpFilters.getFilterList() - - // Unused Stuff - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = - throw UnsupportedOperationException("Not used") - - override fun imageUrlParse(document: Document): String = - throw UnsupportedOperationException("Not used") - companion object { + private const val size = 24 + private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() - const val PREFIX_ID_SEARCH = "id:" + private const val MIRROR_PREF_KEY = "pref_mirror" + private const val MIRROR_PREF_DEFAULT = "mangapark.net" + private val mirrors = arrayOf( + "mangapark.net", + "mangapark.com", + "mangapark.org", + "mangapark.me", + "mangapark.io", + "mangapark.to", + "comicpark.org", + "comicpark.to", + "readpark.org", + "readpark.net", + "parkmanga.com", + "parkmanga.net", + "parkmanga.org", + "mpark.to", + ) } } diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt new file mode 100644 index 000000000..6293244c0 --- /dev/null +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt @@ -0,0 +1,139 @@ +package eu.kanade.tachiyomi.extension.all.mangapark + +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jsoup.Jsoup + +typealias SearchResponse = Data +typealias DetailsResponse = Data +typealias ChapterListResponse = Data +typealias PageListResponse = Data + +@Serializable +data class Data(val data: T) + +@Serializable +data class Items(val items: List) + +@Serializable +data class SearchComics( + @SerialName("get_searchComic") val searchComics: Items>, +) + +@Serializable +data class ComicNode( + @SerialName("get_comicNode") val comic: Data, +) + +@Serializable +data class MangaParkComic( + val id: String, + val name: String, + val altNames: List? = null, + val authors: List? = null, + val artists: List? = null, + val genres: List? = null, + val originalStatus: String? = null, + val uploadStatus: String? = null, + val summary: String? = null, + @SerialName("urlCoverOri") val cover: String? = null, + val urlPath: String, +) { + fun toSManga() = SManga.create().apply { + url = "$urlPath#$id" + title = name + thumbnail_url = cover + author = authors?.joinToString() + artist = artists?.joinToString() + description = buildString { + val desc = summary?.let { Jsoup.parse(it).text() } + val names = altNames?.takeUnless { it.isEmpty() } + ?.joinToString("\n") { "• ${it.trim()}" } + + if (desc.isNullOrEmpty()) { + if (!names.isNullOrEmpty()) { + append("Alternative Names:\n", names) + } + } else { + append(desc) + if (!names.isNullOrEmpty()) { + append("\n\nAlternative Names:\n", names) + } + } + } + genre = genres?.joinToString { it.replace("_", " ").toCamelCase() } + status = when (originalStatus) { + "ongoing" -> SManga.ONGOING + "completed" -> { + if (uploadStatus == "ongoing") { + SManga.PUBLISHING_FINISHED + } else { + SManga.COMPLETED + } + } + "hiatus" -> SManga.ON_HIATUS + "cancelled" -> SManga.CANCELLED + else -> SManga.UNKNOWN + } + initialized = true + } + + companion object { + private fun String.toCamelCase(): String { + val result = StringBuilder(length) + var capitalize = true + for (char in this) { + result.append( + if (capitalize) { + char.uppercase() + } else { + char.lowercase() + }, + ) + capitalize = char.isWhitespace() + } + return result.toString() + } + } +} + +@Serializable +data class ChapterList( + @SerialName("get_comicChapterList") val chapterList: List>, +) + +@Serializable +data class MangaParkChapter( + val id: String, + @SerialName("dname") val displayName: String, + val title: String? = null, + val dateCreate: Long? = null, + val dateModify: Long? = null, + val urlPath: String, +) { + fun toSChapter() = SChapter.create().apply { + url = "$urlPath#$id" + name = buildString { + append(displayName) + title?.let { append(": ", it) } + } + date_upload = dateModify ?: dateCreate ?: 0L + } +} + +@Serializable +data class ChapterPages( + @SerialName("get_chapterNode") val chapterPages: Data, +) + +@Serializable +data class ImageFiles( + val imageFile: UrlList, +) + +@Serializable +data class UrlList( + val urlList: List, +) diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFactory.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFactory.kt index ebf77e545..500bb871a 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFactory.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFactory.kt @@ -1,112 +1,107 @@ package eu.kanade.tachiyomi.extension.all.mangapark -import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory class MangaParkFactory : SourceFactory { - override fun createSources(): List = languages.map { MangaPark(it.lang, it.siteLang) } + override fun createSources() = listOf( + MangaPark("af"), + MangaPark("sq"), + MangaPark("am"), + MangaPark("ar"), + MangaPark("hy"), + MangaPark("az"), + MangaPark("be"), + MangaPark("bn"), + MangaPark("bs"), + MangaPark("bg"), + MangaPark("my"), + MangaPark("km"), + MangaPark("ca"), + MangaPark("ceb"), + MangaPark("zh"), + MangaPark("zh-Hans", "zh_hk"), + MangaPark("zh-Hant", "zh_tw"), + MangaPark("hr"), + MangaPark("cs"), + MangaPark("da"), + MangaPark("nl"), + MangaPark("en"), + MangaPark("eo"), + MangaPark("et"), + MangaPark("fo"), + MangaPark("fil"), + MangaPark("fi"), + MangaPark("fr"), + MangaPark("ka"), + MangaPark("de"), + MangaPark("el"), + MangaPark("gn"), + MangaPark("ht"), + MangaPark("ha"), + MangaPark("he"), + MangaPark("hi"), + MangaPark("hu"), + MangaPark("is"), + MangaPark("ig"), + MangaPark("id"), + MangaPark("ga"), + MangaPark("it"), + MangaPark("ja"), + MangaPark("jv"), + MangaPark("kk"), + MangaPark("ko"), + MangaPark("ku"), + MangaPark("ky"), + MangaPark("lo"), + MangaPark("lv"), + MangaPark("lt"), + MangaPark("lb"), + MangaPark("mk"), + MangaPark("mg"), + MangaPark("ms"), + MangaPark("ml"), + MangaPark("mt"), + MangaPark("mi"), + MangaPark("mo"), + MangaPark("mn"), + MangaPark("ne"), + MangaPark("no"), + MangaPark("ny"), + MangaPark("ps"), + MangaPark("fa"), + MangaPark("pl"), + MangaPark("pt"), + MangaPark("pt-BR", "pt_br"), + MangaPark("ro"), + MangaPark("rm"), + MangaPark("ru"), + MangaPark("sm"), + MangaPark("sr"), + MangaPark("sh"), + MangaPark("st"), + MangaPark("sn"), + MangaPark("sd"), + MangaPark("si"), + MangaPark("sk"), + MangaPark("sl"), + MangaPark("so"), + MangaPark("es"), + MangaPark("es-419", "es_419"), + MangaPark("sw"), + MangaPark("sv"), + MangaPark("tg"), + MangaPark("ta"), + MangaPark("th"), + MangaPark("ti"), + MangaPark("to"), + MangaPark("tr"), + MangaPark("tk"), + MangaPark("uk"), + MangaPark("ur"), + MangaPark("uz"), + MangaPark("vi"), + MangaPark("yo"), + MangaPark("zu"), + MangaPark("other", "_t"), + ) } - -class LanguageOption(val lang: String, val siteLang: String = lang) -private val languages = listOf( - // LanguageOption("",""), - LanguageOption("af"), - LanguageOption("sq"), - LanguageOption("am"), - LanguageOption("ar"), - LanguageOption("hy"), - LanguageOption("az"), - LanguageOption("be"), - LanguageOption("bn"), - LanguageOption("bs"), - LanguageOption("bg"), - LanguageOption("my"), - LanguageOption("km"), - LanguageOption("ca"), - LanguageOption("ceb"), - LanguageOption("zh"), - LanguageOption("zh-Hans", "zh_hk"), - LanguageOption("zh-Hant", "zh_tw"), - LanguageOption("hr"), - LanguageOption("cs"), - LanguageOption("da"), - LanguageOption("nl"), - LanguageOption("en"), - LanguageOption("eo"), - LanguageOption("et"), - LanguageOption("fo"), - LanguageOption("fil"), - LanguageOption("fi"), - LanguageOption("fr"), - LanguageOption("ka"), - LanguageOption("de"), - LanguageOption("el"), - LanguageOption("gn"), - LanguageOption("ht"), - LanguageOption("ha"), - LanguageOption("he"), - LanguageOption("hi"), - LanguageOption("hu"), - LanguageOption("is"), - LanguageOption("ig"), - LanguageOption("id"), - LanguageOption("ga"), - LanguageOption("it"), - LanguageOption("ja"), - LanguageOption("jv"), - LanguageOption("kk"), - LanguageOption("ko"), - LanguageOption("ku"), - LanguageOption("ky"), - LanguageOption("lo"), - LanguageOption("lv"), - LanguageOption("lt"), - LanguageOption("lb"), - LanguageOption("mk"), - LanguageOption("mg"), - LanguageOption("ms"), - LanguageOption("ml"), - LanguageOption("mt"), - LanguageOption("mi"), - LanguageOption("mo"), - LanguageOption("mn"), - LanguageOption("ne"), - LanguageOption("no"), - LanguageOption("ny"), - LanguageOption("ps"), - LanguageOption("fa"), - LanguageOption("pl"), - LanguageOption("pt"), - LanguageOption("pt-BR", "pt_br"), - LanguageOption("ro"), - LanguageOption("rm"), - LanguageOption("ru"), - LanguageOption("sm"), - LanguageOption("sr"), - LanguageOption("sh"), - LanguageOption("st"), - LanguageOption("sn"), - LanguageOption("sd"), - LanguageOption("si"), - LanguageOption("sk"), - LanguageOption("sl"), - LanguageOption("so"), - LanguageOption("es"), - LanguageOption("es-419", "es_419"), - LanguageOption("sw"), - LanguageOption("sv"), - LanguageOption("tg"), - LanguageOption("ta"), - LanguageOption("th"), - LanguageOption("ti"), - LanguageOption("to"), - LanguageOption("tr"), - LanguageOption("tk"), - LanguageOption("uk"), - LanguageOption("ur"), - LanguageOption("uz"), - LanguageOption("vi"), - LanguageOption("yo"), - LanguageOption("zu"), - LanguageOption("other", "_t"), -) diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFilters.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFilters.kt index f78fe81d9..ede975a67 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFilters.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkFilters.kt @@ -2,300 +2,132 @@ package eu.kanade.tachiyomi.extension.all.mangapark import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -import okhttp3.HttpUrl -class MangaParkFilters { - - internal fun getFilterList(): FilterList { - return FilterList( - Filter.Header("NOTE: Ignored if using text search!"), - Filter.Separator(), - SortFilter("Sort By", defaultSort, sortList), - Filter.Separator(), - MinChapterFilter(), - MaxChapterFilter(), - Filter.Separator(), - PublicationFilter("Status", publicationList, 0), - TypeFilter("Type", typeList), - DemographicFilter("Demographic", demographicList), - ContentFilter("Content", contentList), - GenreFilter("Genre", genreList), - ) - } - - internal fun addFiltersToUrl(url: HttpUrl.Builder, filters: FilterList): String { - var sort = "rating.za" - var minChap: Int? = null - var maxChap: Int? = null - var publication: String? = null - val includedGenre = mutableListOf() - val excludedGenre = mutableListOf() - - filters.forEach { filter -> - when (filter) { - is SortFilter -> { - val sortType = sortList[filter.state!!.index].value - val sortDirection = if (filter.state!!.ascending) "az" else "za" - sort = "$sortType.$sortDirection" - } - is MinChapterFilter -> { - try { - minChap = filter.state.toInt() - } catch (_: NumberFormatException) { - // Do Nothing - } - } - is MaxChapterFilter -> { - try { - maxChap = filter.state.toInt() - } catch (_: NumberFormatException) { - // Do Nothing - } - } - is PublicationFilter -> { - if (filter.state != 0) { - publication = publicationList[filter.state].value - } - } - is TypeFilter -> { - includedGenre += filter.state.filter { it.isIncluded() }.map { it.value } - excludedGenre += filter.state.filter { it.isExcluded() }.map { it.value } - } - is DemographicFilter -> { - includedGenre += filter.state.filter { it.isIncluded() }.map { it.value } - excludedGenre += filter.state.filter { it.isExcluded() }.map { it.value } - } - is ContentFilter -> { - includedGenre += filter.state.filter { it.isIncluded() }.map { it.value } - excludedGenre += filter.state.filter { it.isExcluded() }.map { it.value } - } - is GenreFilter -> { - includedGenre += filter.state.filter { it.isIncluded() }.map { it.value } - excludedGenre += filter.state.filter { it.isExcluded() }.map { it.value } - } - else -> {} - } - } - - return url.apply { - if (sort != "rating.za") { - addQueryParameter( - "sort", - sort, - ) - } - if (includedGenre.isNotEmpty() || excludedGenre.isNotEmpty()) { - addQueryParameter( - "genres", - includedGenre.joinToString(",") + "|" + excludedGenre.joinToString(","), - ) - } - if (publication != null) { - addQueryParameter( - "release", - publication, - ) - } - addQueryParameter( - "chapters", - minMaxToChapter(minChap, maxChap), - ) - }.toString() - } - - private fun minMaxToChapter(minChap: Int?, maxChap: Int?): String? { - if (minChap == null && maxChap == null) return null - return when { - minChap != null && maxChap == null -> minChap - minChap == null && maxChap != null -> "0-$maxChap" - else -> "$minChap-$maxChap" - }.toString() - } - - // Sort Filter - class SortItem(val name: String, val value: String) - - private val sortList: List = listOf( - SortItem("Rating", "rating"), - SortItem("Comments", "comments"), - SortItem("Discuss", "discuss"), - SortItem("Update", "update"), - SortItem("Create", "create"), - SortItem("Name", "name"), - SortItem("Total Views", "d000"), - SortItem("Most Views 360 days", "d360"), - SortItem("Most Views 180 days", "d180"), - SortItem("Most Views 90 days", "d090"), - SortItem("Most Views 30 days", "d030"), - SortItem("Most Views 7 days", "d007"), - SortItem("Most Views 24 hours", "h024"), - SortItem("Most Views 12 hours", "h012"), - SortItem("Most Views 6 hours", "h006"), - SortItem("Most Views 60 minutes", "h001"), - ) - - class SortDefault(val defaultSortIndex: Int, val ascending: Boolean) - - private val defaultSort: SortDefault = SortDefault(0, false) - - class SortFilter(name: String, default: SortDefault, sorts: List) : - Filter.Sort( - name, - sorts.map { it.name }.toTypedArray(), - Selection(default.defaultSortIndex, default.ascending), - ) - - // Min - Max Chapter Filter - abstract class TextFilter(name: String) : Filter.Text(name) - - class MinChapterFilter : TextFilter("Min. Chapters") - class MaxChapterFilter : TextFilter("Max. Chapters") - - // Publication Filter - class PublicationItem(val name: String, val value: String) - - private val publicationList: List = listOf( - PublicationItem("All", ""), - PublicationItem("Pending", "pending"), - PublicationItem("Ongoing", "ongoing"), - PublicationItem("Completed", "completed"), - PublicationItem("Hiatus", "hiatus"), - PublicationItem("Cancelled", "cancelled"), - ) - - class PublicationFilter( - name: String, - statusList: List, - defaultStatusIndex: Int, - ) : - Filter.Select( - name, - statusList.map { it.name }.toTypedArray(), - defaultStatusIndex, - ) - - // Type - class TypeItem(name: String, val value: String) : Filter.TriState(name) - - private val typeList: List = listOf( - TypeItem("Cartoon", "cartoon"), - TypeItem("Comic", "comic"), - TypeItem("Doujinshi", "doujinshi"), - TypeItem("Manga", "manga"), - TypeItem("Manhua", "manhua"), - TypeItem("Manhwa", "manhwa"), - TypeItem("Webtoon", "webtoon"), - ) - - class TypeFilter(name: String, typeList: List) : - Filter.Group(name, typeList) - - // Demographic - class DemographicItem(name: String, val value: String) : Filter.TriState(name) - - private val demographicList: List = listOf( - DemographicItem("Shounen", "shounen"), - DemographicItem("Shoujo", "shoujo"), - DemographicItem("Seinen", "seinen"), - DemographicItem("Josei", "josei"), - ) - - class DemographicFilter(name: String, demographicList: List) : - Filter.Group(name, demographicList) - - // Content - class ContentItem(name: String, val value: String) : Filter.TriState(name) - - private val contentList: List = listOf( - ContentItem("Adult", "adult"), - ContentItem("Ecchi", "ecchi"), - ContentItem("Gore", "gore"), - ContentItem("Hentai", "hentai"), - ContentItem("Mature", "mature"), - ContentItem("Smut", "smut"), - ) - - class ContentFilter(name: String, contentList: List) : - Filter.Group(name, contentList) - - // Genre - class GenreItem(name: String, val value: String) : Filter.TriState(name) - - private val genreList: List = listOf( - GenreItem("Action", "action"), - GenreItem("Adaptation", "adaptation"), - GenreItem("Adventure", "adventure"), - GenreItem("Aliens", "aliens"), - GenreItem("Animals", "animals"), - GenreItem("Anthology", "anthology"), - GenreItem("Award Winning", "award_winning"), // This Is Hidden In Web - GenreItem("Cars", "cars"), - GenreItem("Comedy", "comedy"), - GenreItem("Cooking", "cooking"), - GenreItem("Crime", "crime"), - GenreItem("Crossdressing", "crossdressing"), - GenreItem("Delinquents", "delinquents"), - GenreItem("Dementia", "dementia"), - GenreItem("Demons", "demons"), - GenreItem("Drama", "drama"), - GenreItem("Fantasy", "fantasy"), - GenreItem("Full Color", "full_color"), - GenreItem("Game", "game"), - GenreItem("Gender Bender", "gender_bender"), - GenreItem("Genderswap", "genderswap"), - GenreItem("Gyaru", "gyaru"), - GenreItem("Harem", "harem"), - GenreItem("Historical", "historical"), - GenreItem("Horror", "horror"), - GenreItem("Incest", "incest"), - GenreItem("Isekai", "isekai"), - GenreItem("Kids", "kids"), - GenreItem("Loli", "loli"), - GenreItem("Lolicon", "lolicon"), - GenreItem("Magic", "magic"), - GenreItem("Magical Girls", "magical_girls"), - GenreItem("Martial Arts", "martial_arts"), - GenreItem("Mecha", "mecha"), - GenreItem("Medical", "medical"), - GenreItem("Military", "military"), - GenreItem("Monster Girls", "monster_girls"), - GenreItem("Monsters", "monsters"), - GenreItem("Music", "music"), - GenreItem("Mystery", "mystery"), - GenreItem("Office Workers", "office_workers"), - GenreItem("Oneshot", "oneshot"), - GenreItem("Parody", "parody"), - GenreItem("Philosophical", "philosophical"), - GenreItem("Police", "police"), - GenreItem("Post Apocalyptic", "post_apocalyptic"), - GenreItem("Psychological", "psychological"), - GenreItem("Reincarnation", "reincarnation"), - GenreItem("Romance", "romance"), - GenreItem("Samurai", "samurai"), - GenreItem("School Life", "school_life"), - GenreItem("Sci-fi", "sci_fi"), - GenreItem("Shotacon", "shotacon"), - GenreItem("Shounen Ai", "shounen_ai"), - GenreItem("Shoujo Ai", "shoujo_ai"), - GenreItem("Slice of Life", "slice_of_life"), - GenreItem("Space", "space"), - GenreItem("Sports", "sports"), - GenreItem("Super Power", "super_power"), - GenreItem("Superhero", "superhero"), - GenreItem("Supernatural", "supernatural"), - GenreItem("Survival", "survival"), - GenreItem("Thriller", "thriller"), - GenreItem("Traditional Games", "traditional_games"), - GenreItem("Tragedy", "tragedy"), - GenreItem("Vampires", "vampires"), - GenreItem("Video Games", "video_games"), - GenreItem("Virtual Reality", "virtual_reality"), - GenreItem("Wuxia", "wuxia"), - GenreItem("Yaoi", "yaoi"), - GenreItem("Yuri", "yuri"), - GenreItem("Zombies", "zombies"), - ) - - class GenreFilter(name: String, genreList: List) : - Filter.Group(name, genreList) +abstract class SelectFilter( + name: String, + private val options: List>, + defaultValue: String? = null, +) : Filter.Select( + name, + options.map { it.first }.toTypedArray(), + options.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0, +) { + val selected get() = options[state].second.takeUnless { it.isEmpty() } +} + +class CheckBoxFilter(name: String, val value: String) : Filter.CheckBox(name) + +abstract class CheckBoxGroup( + name: String, + options: List>, +) : Filter.Group( + name, + options.map { CheckBoxFilter(it.first, it.second) }, +) { + val checked get() = state.filter { it.state }.map { it.value }.takeUnless { it.isEmpty() } +} + +class TriStateFilter(name: String, val value: String) : Filter.TriState(name) + +abstract class TriStateGroup( + name: String, + private val options: List>, +) : Filter.Group( + name, + options.map { TriStateFilter(it.first, it.second) }, +) { + val included get() = state.filter { it.isIncluded() }.map { it.value }.takeUnless { it.isEmpty() } + val excluded get() = state.filter { it.isExcluded() }.map { it.value }.takeUnless { it.isEmpty() } +} + +class SortFilter(defaultOrder: String? = null) : SelectFilter("Sort By", sort, defaultOrder) { + companion object { + private val sort = listOf( + Pair("Rating Score", "field_score"), + Pair("Most Follows", "field_follow"), + Pair("Most Reviews", "field_review"), + Pair("Most Comments", "field_comment"), + Pair("Most Chapters", "field_chapter"), + Pair("New Chapters", "field_update"), + Pair("Recently Created", "field_create"), + Pair("Name A-Z", "field_name"), + Pair("Total Views", "views_d000"), + Pair("Most Views 360 days", "views_d360"), + Pair("Most Views 180 days", "views_d180"), + Pair("Most Views 90 days", "views_d090"), + Pair("Most Views 30 days", "views_d030"), + Pair("Most Views 7 days", "views_d007"), + Pair("Most Views 24 hours", "views_h024"), + Pair("Most Views 12 hours", "views_h012"), + Pair("Most Views 6 hours", "views_h006"), + Pair("Most Views 60 minutes", "views_h001"), + ) + + val POPULAR = FilterList(SortFilter("field_score")) + val LATEST = FilterList(SortFilter("field_update")) + } +} + +class GenreFilter(genres: List>) : TriStateGroup("Genres", genres) + +abstract class StatusFilter(name: String) : SelectFilter(name, status) { + companion object { + private val status = listOf( + Pair("All", ""), + Pair("Pending", "pending"), + Pair("Ongoing", "ongoing"), + Pair("Completed", "completed"), + Pair("Hiatus", "hiatus"), + Pair("Cancelled", "cancelled"), + ) + } +} + +class OriginalLanguageFilter : CheckBoxGroup("Original Work Language", language) { + companion object { + private val language = listOf( + Pair("Chinese", "zh"), + Pair("English", "en"), + Pair("Japanese", "ja"), + Pair("Korean", "ko"), + ) + } +} + +class OriginalStatusFilter : StatusFilter("Original Work Status") + +class UploadStatusFilter : StatusFilter("Upload Status") + +class ChapterCountFilter : SelectFilter("Number of Chapters", chapters) { + companion object { + private val chapters = listOf( + Pair("", ""), + Pair("0", "0"), + Pair("1+", "1"), + Pair("10+", "10"), + Pair("20+", "20"), + Pair("30+", "30"), + Pair("40+", "40"), + Pair("50+", "50"), + Pair("60+", "60"), + Pair("70+", "70"), + Pair("80+", "80"), + Pair("90+", "90"), + Pair("100+", "100"), + Pair("200+", "200"), + Pair("300+", "300"), + Pair("299~200", "200-299"), + Pair("199~100", "100-199"), + Pair("99~90", "90-99"), + Pair("89~80", "80-89"), + Pair("79~70", "70-79"), + Pair("69~60", "60-69"), + Pair("59~50", "50-59"), + Pair("49~40", "40-49"), + Pair("39~30", "30-39"), + Pair("29~20", "20-29"), + Pair("19~10", "10-19"), + Pair("9~1", "1-9"), + ) + } } diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkPayloadDto.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkPayloadDto.kt new file mode 100644 index 000000000..397f3f44f --- /dev/null +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkPayloadDto.kt @@ -0,0 +1,31 @@ +package eu.kanade.tachiyomi.extension.all.mangapark + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GraphQL( + val variables: T, + val query: String, +) + +@Serializable +data class SearchVariables(val select: SearchPayload) + +@Serializable +data class SearchPayload( + @SerialName("word") val query: String? = null, + val incGenres: List? = null, + val excGenres: List? = null, + val incTLangs: List? = null, + val incOLangs: List? = null, + val sortby: String? = null, + val chapCount: String? = null, + val origStatus: String? = null, + val siteStatus: String? = null, + val page: Int, + val size: Int, +) + +@Serializable +data class IdVariables(val id: String) diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt new file mode 100644 index 000000000..68d2bdb41 --- /dev/null +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt @@ -0,0 +1,100 @@ +package eu.kanade.tachiyomi.extension.all.mangapark + +private fun buildQuery(queryAction: () -> String): String { + return queryAction() + .trimIndent() + .replace("%", "$") +} + +val SEARCH_QUERY = buildQuery { + """ + query ( + %select: SearchComic_Select + ) { + get_searchComic( + select: %select + ) { + items { + data { + id + name + altNames + artists + authors + genres + originalStatus + uploadStatus + summary + urlCoverOri + urlPath + } + } + } + } + """ +} + +val DETAILS_QUERY = buildQuery { + """ + query( + %id: ID! + ) { + get_comicNode( + id: %id + ) { + data { + id + name + altNames + artists + authors + genres + originalStatus + uploadStatus + summary + urlCoverOri + urlPath + } + } + } + """ +} + +val CHAPTERS_QUERY = buildQuery { + """ + query( + %id: ID! + ) { + get_comicChapterList( + comicId: %id + ) { + data { + id + dname + title + dateModify + dateCreate + urlPath + } + } + } + """ +} + +val PAGES_QUERY = buildQuery { + """ + query( + %id: ID! + ) { + get_chapterNode( + id: %id + ) { + data { + imageFile { + urlList + } + } + } + } + """ +} diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkUrlActivity.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkUrlActivity.kt deleted file mode 100644 index 83a3f5e38..000000000 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkUrlActivity.kt +++ /dev/null @@ -1,51 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.mangapark - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.util.Log -import kotlin.system.exitProcess - -class MangaParkUrlActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val host = intent?.data?.host - val pathSegments = intent?.data?.pathSegments - - if (host != null && pathSegments != null) { - val query = fromGuya(pathSegments) - - if (query == null) { - Log.e("MangaParkUrlActivity", "Unable to parse URI from intent $intent") - finish() - exitProcess(1) - } - - val mainIntent = Intent().apply { - action = "eu.kanade.tachiyomi.SEARCH" - putExtra("query", query) - putExtra("filter", packageName) - } - - try { - startActivity(mainIntent) - } catch (e: ActivityNotFoundException) { - Log.e("MangaParkUrlActivity", e.toString()) - } - } - - finish() - exitProcess(0) - } - - private fun fromGuya(pathSegments: MutableList): String? { - return if (pathSegments.size >= 2) { - val id = pathSegments[1] - "id:$id" - } else { - null - } - } -}