diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 2fdf0ee6d..266b1ec4c 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -43,7 +43,7 @@ jobs: }, { "type": "both", - "regex": ".*(team\\s*x|tqneplus|manga\\s*disk|komiktap|gourmet\\s*scans|manga\\s*crimson|mangawow|voidscans|hikari\\s*scans|mangagegecesi|piedpiperfb|knightnoscanlations|ahstudios|mangagecesi|nartag|xxx\\s*yaoi|yaoi\\s*fan\\s*clube|luminous|dragontea|manhwaid\\.org|hunters\\s*scan|mnhaestate|swat\\s*manga|manga\\s*swat|reset(?:\\s*|-)scan|aresmanga).*", + "regex": ".*(team\\s*x|tqneplus|manga\\s*disk|komiktap|gourmet\\s*scans|manga\\s*crimson|mangawow|voidscans|hikari\\s*scans|mangagegecesi|piedpiperfb|knightnoscanlations|ahstudios|mangagecesi|nartag|xxx\\s*yaoi|yaoi\\s*fan\\s*clube|luminous|dragontea|manhwaid\\.org|hunters\\s*scan|mnhaestate|swat\\s*manga|manga\\s*swat|reset(?:\\s*|-)scan|aresmanga|manga-flix\\.com).*", "ignoreCase": true, "message": "{match} will not be added back as the Scanlator team has requested it to be removed. Read #3475 for more information" } diff --git a/multisrc/overrides/madara/mangaflix/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaflix/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 7b0509318..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaflix/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 040b76338..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaflix/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 6aa48ac89..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaflix/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a66087900..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaflix/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 5873d1c75..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaflix/res/web_hi_res_512.png deleted file mode 100644 index f4ff55089..000000000 Binary files a/multisrc/overrides/madara/mangaflix/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangaflix/src/MangaFlix.kt b/multisrc/overrides/madara/mangaflix/src/MangaFlix.kt deleted file mode 100644 index 10bcdce1a..000000000 --- a/multisrc/overrides/madara/mangaflix/src/MangaFlix.kt +++ /dev/null @@ -1,31 +0,0 @@ -package eu.kanade.tachiyomi.extension.ar.mangaflix - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.source.model.SChapter -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale - -class MangaFlix : Madara( - "مانجا فليكس", - "https://www.manga-flix.com", - "ar", - dateFormat = SimpleDateFormat("dd/MM/yyy", Locale.ROOT) -) { - override val useNewChapterEndpoint = true - - override val mangaDetailsSelectorStatus = "div.summarys" - - override fun chapterFromElement(element: Element): SChapter { - val chapter = super.chapterFromElement(element) - - with(element) { - selectFirst(chapterUrlSelector)?.let { urlElement -> - // Using .text() on the tag is too broad - chapter.name = urlElement.selectFirst("> p")!!.text() - } - } - - return chapter - } -} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index 3d0210b0c..953cb5848 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -493,7 +493,6 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("مانجا العاشق", "https://3asq.org", "ar", className = "Manga3asq", overrideVersionCode = 2), SingleLang("مانجا العرب Manga Alarab", "https://manga-alarab.com", "ar", className = "MangAlarab", overrideVersionCode = 1), SingleLang("مانجا عرب تيم Manga Arab Team", "https://mangaarbteam.com", "ar", className = "MangaArabTeam", overrideVersionCode = 1), - SingleLang("مانجا فليكس", "https://www.manga-flix.com", "ar", className = "MangaFlix", overrideVersionCode = 1), SingleLang("مانجا ليك", "https://mangalek.com", "ar", className = "Mangalek", overrideVersionCode = 1), SingleLang("مانجا لينك", "https://mangalink.io", "ar", className = "MangaLinkio", overrideVersionCode = 2), ) diff --git a/src/en/mangamutiny/AndroidManifest.xml b/src/en/mangamutiny/AndroidManifest.xml deleted file mode 100644 index 5bbc61078..000000000 --- a/src/en/mangamutiny/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/en/mangamutiny/build.gradle b/src/en/mangamutiny/build.gradle deleted file mode 100644 index f4c95bd87..000000000 --- a/src/en/mangamutiny/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' - -ext { - extName = 'Manga Mutiny' - pkgNameSuffix = "en.mangamutiny" - extClass = '.MangaMutiny' - extVersionCode = 9 - isNsfw = true -} - -apply from: "$rootDir/common.gradle" diff --git a/src/en/mangamutiny/res/mipmap-hdpi/ic_launcher.png b/src/en/mangamutiny/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 994f93478..000000000 Binary files a/src/en/mangamutiny/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangamutiny/res/mipmap-mdpi/ic_launcher.png b/src/en/mangamutiny/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 257699262..000000000 Binary files a/src/en/mangamutiny/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangamutiny/res/mipmap-xhdpi/ic_launcher.png b/src/en/mangamutiny/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index d17be79a4..000000000 Binary files a/src/en/mangamutiny/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangamutiny/res/mipmap-xxhdpi/ic_launcher.png b/src/en/mangamutiny/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9e7f7e2e8..000000000 Binary files a/src/en/mangamutiny/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangamutiny/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/mangamutiny/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index edf0fe931..000000000 Binary files a/src/en/mangamutiny/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangamutiny/res/web_hi_res_512.png b/src/en/mangamutiny/res/web_hi_res_512.png deleted file mode 100644 index c160cb691..000000000 Binary files a/src/en/mangamutiny/res/web_hi_res_512.png and /dev/null differ diff --git a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutiny.kt b/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutiny.kt deleted file mode 100644 index 27ff5feed..000000000 --- a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutiny.kt +++ /dev/null @@ -1,441 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangamutiny - -import android.net.Uri -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess -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.HttpSource -import kotlinx.serialization.json.Json -import okhttp3.Headers -import okhttp3.Request -import okhttp3.Response -import rx.Observable -import uy.kohesive.injekt.injectLazy - -class MangaMutiny : HttpSource() { - - override val name = "Manga Mutiny" - override val baseUrl = "https://mangamutiny.org" - - override val supportsLatest = true - - override val lang = "en" - - private val json: Json by injectLazy() - - private val baseUrlAPI = "https://api.mangamutiny.org" - - private val webViewSingleMangaPath = "title/" - private val webViewMultipleMangaPath = "titles/" - - override fun headersBuilder(): Headers.Builder { - return super.headersBuilder().apply { - add("Accept", "application/json") - add("Origin", "https://mangamutiny.org") - } - } - - private val apiMangaUrlPath = "v1/public/manga" - private val apiChapterUrlPath = "v1/public/chapter" - - private val fetchAmount = 21 - - companion object { - const val PREFIX_ID_SEARCH = "slug:" - } - - // Popular manga - override fun popularMangaRequest(page: Int): Request = mangaRequest(page) - - override fun popularMangaParse(response: Response): MangasPage = mangaParse(response) - - // Chapters - override fun chapterListRequest(manga: SManga): Request = - mangaDetailsRequestCommon(manga, false) - - override fun chapterListParse(response: Response): List { - val responseBody = response.body - - return responseBody?.use { - json.decodeFromString(ListChapterDS, it.string()).also { - responseBody.close() - } - } ?: listOf() - } - - // latest - override fun latestUpdatesRequest(page: Int): Request = - mangaRequest(page, filters = FilterList(SortFilter().apply { this.state = 1 })) - - override fun latestUpdatesParse(response: Response): MangasPage = mangaParse(response) - - // browse + latest + search - override fun mangaDetailsRequest(manga: SManga): Request = mangaDetailsRequestCommon(manga) - - private fun mangaDetailsRequestCommon(manga: SManga, lite: Boolean = true): Request { - val uri = if (isForWebView()) { - Uri.parse(baseUrl).buildUpon() - .appendEncodedPath(webViewSingleMangaPath) - .appendPath(manga.url) - } else { - Uri.parse(baseUrlAPI).buildUpon() - .appendEncodedPath(apiMangaUrlPath) - .appendPath(manga.url).let { - if (lite) it.appendQueryParameter("lite", "1") else it - } - } - - return GET(uri.build().toString(), headers) - } - - override fun mangaDetailsParse(response: Response): SManga { - val responseBody = response.body - - if (responseBody != null) { - return responseBody.use { - json.decodeFromString(SMangaDS, it.string()) - } - } else { - throw IllegalStateException("Response code ${response.code}") - } - } - - override fun pageListRequest(chapter: SChapter): Request { - val uri = Uri.parse(baseUrlAPI).buildUpon() - .appendEncodedPath(apiChapterUrlPath) - .appendEncodedPath(chapter.url) - - return GET(uri.build().toString(), headers) - } - - override fun pageListParse(response: Response): List { - val responseBody = response.body - - return responseBody?.use { - json.decodeFromString(ListPageDS, it.string()) - } ?: listOf() - } - - // Search - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = - mangaRequest(page, query, filters) - - override fun searchMangaParse(response: Response): MangasPage = mangaParse(response) - - // commonly used functions - private fun mangaParse(response: Response): MangasPage { - val responseBody = response.body - - return if (responseBody != null) { - val deserializationResult = json.decodeFromString(PageInfoDS, responseBody.string()) - val totalObjects = deserializationResult.second - val skipped = response.request.url.queryParameter("skip")?.toInt() ?: 0 - val moreElementsToSkip = skipped + fetchAmount < totalObjects - val pageSizeEqualsFetchAmount = deserializationResult.first.size == fetchAmount - val hasMorePages = pageSizeEqualsFetchAmount && moreElementsToSkip - - MangasPage(deserializationResult.first, hasMorePages) - } else { - MangasPage(listOf(), false) - } - } - - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - - return if (query.startsWith(PREFIX_ID_SEARCH)) { - val realQuery = query.removePrefix(PREFIX_ID_SEARCH) - - val tempManga = SManga.create().apply { - url = realQuery - } - - client.newCall(mangaDetailsRequestCommon(tempManga, true)) - .asObservableSuccess() - .map { response -> - val details = mangaDetailsParse(response) - MangasPage(listOf(details), false) - } - } else { - client.newCall(searchMangaRequest(page, query, filters)) - .asObservableSuccess() - .map { response -> - searchMangaParse(response) - } - } - } - - private fun mangaRequest(page: Int, query: String? = null, filters: FilterList? = null): Request { - val forWebView = isForWebView() - - val uri = if (forWebView) { - Uri.parse(baseUrl).buildUpon().apply { - appendEncodedPath(webViewMultipleMangaPath) - } - } else { - Uri.parse(baseUrlAPI).buildUpon().apply { - appendEncodedPath(apiMangaUrlPath) - } - } - - if (query?.isNotBlank() == true) { - uri.appendQueryParameter("text", query) - } - - val applicableFilters = if (filters != null && filters.isNotEmpty()) { - filters - } else { - FilterList(SortFilter()) - } - - val uriParameterMap = mutableMapOf() - - for (singleFilter in applicableFilters) { - if (singleFilter is UriFilter) { - singleFilter.addParameter(uriParameterMap) - } - } - - for (uriParameter in uriParameterMap) { - uri.appendQueryParameter(uriParameter.key, uriParameter.value) - } - - if (!forWebView) { - uri.appendQueryParameter("limit", fetchAmount.toString()) - if (page != 1) { - uri.appendQueryParameter("skip", ((page - 1) * fetchAmount).toString()) - } - } - - return GET(uri.build().toString(), headers) - } - - // Filter - override fun getFilterList(): FilterList { - return FilterList( - StatusFilter(), - CategoryFilter(), - GenresFilter(), - FormatsFilter(), - SortFilter(), - AuthorFilter() - // ScanlatorFilter() - ) - } - - override fun imageUrlParse(response: Response): String { - throw Exception("Not used") - } - - private interface UriFilter { - val uriParam: () -> String - val shouldAdd: () -> Boolean - val getParameter: () -> String - - fun addParameter(parameterMap: MutableMap) { - if (shouldAdd()) { - val newParameterValueBuilder = StringBuilder() - if (parameterMap[uriParam()] != null) { - newParameterValueBuilder.append(parameterMap[uriParam()] + " ") - } - newParameterValueBuilder.append(getParameter()) - - parameterMap[uriParam()] = newParameterValueBuilder.toString() - } - } - } - - private abstract class UriSelectFilter( - displayName: String, - override val uriParam: () -> String, - val vals: Array>, - val defaultValue: Int = 0 - ) : - Filter.Select(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter { - - override val shouldAdd = fun() = - this.state != defaultValue - - override val getParameter = fun() = vals[state].first - } - - private class StatusFilter : UriSelectFilter( - "Status", - fun() = "status", - arrayOf( - Pair("", "All"), - Pair("completed", "Completed"), - Pair("ongoing", "Ongoing") - ) - ) - - private class CategoryFilter : UriSelectFilter( - "Category", - fun() = "tags", - arrayOf( - Pair("", "All"), - Pair("josei", "Josei"), - Pair("seinen", "Seinen"), - Pair("shoujo", "Shoujo"), - Pair("shounen", "Shounen") - ) - ) - - // A single filter: either a genre or a format filter - private class GenreOrFormatFilter(val uriParam: String, displayName: String) : - Filter.CheckBox(displayName) - - // A collection of genre or format filters - private abstract class GenreOrFormatFilterList(name: String, specificUriParam: String, elementList: List) : Filter.Group(name, elementList), UriFilter { - - override val shouldAdd = fun() = state.any { it.state } - - override val getParameter = fun() = - state.filter { it.state }.joinToString(" ") { it.uriParam } - - override val uriParam = fun() = if (isForWebView()) specificUriParam else "tags" - } - - // Generes filter list - private class GenresFilter : GenreOrFormatFilterList( - "Genres", - "genres", - listOf( - GenreOrFormatFilter("action", "action"), - GenreOrFormatFilter("adult", "adult"), - GenreOrFormatFilter("adventure", "adventure"), - GenreOrFormatFilter("aliens", "aliens"), - GenreOrFormatFilter("animals", "animals"), - GenreOrFormatFilter("comedy", "comedy"), - GenreOrFormatFilter("cooking", "cooking"), - GenreOrFormatFilter("crossdressing", "crossdressing"), - GenreOrFormatFilter("delinquents", "delinquents"), - GenreOrFormatFilter("demons", "demons"), - GenreOrFormatFilter("drama", "drama"), - GenreOrFormatFilter("ecchi", "ecchi"), - GenreOrFormatFilter("fantasy", "fantasy"), - GenreOrFormatFilter("gender_bender", "gender bender"), - GenreOrFormatFilter("genderswap", "genderswap"), - GenreOrFormatFilter("ghosts", "ghosts"), - GenreOrFormatFilter("gore", "gore"), - GenreOrFormatFilter("gyaru", "gyaru"), - GenreOrFormatFilter("harem", "harem"), - GenreOrFormatFilter("historical", "historical"), - GenreOrFormatFilter("horror", "horror"), - GenreOrFormatFilter("incest", "incest"), - GenreOrFormatFilter("isekai", "isekai"), - GenreOrFormatFilter("loli", "loli"), - GenreOrFormatFilter("magic", "magic"), - GenreOrFormatFilter("magical_girls", "magical girls"), - GenreOrFormatFilter("mangamutiny", "mangamutiny"), - GenreOrFormatFilter("martial_arts", "martial arts"), - GenreOrFormatFilter("mature", "mature"), - GenreOrFormatFilter("mecha", "mecha"), - GenreOrFormatFilter("medical", "medical"), - GenreOrFormatFilter("military", "military"), - GenreOrFormatFilter("monster_girls", "monster girls"), - GenreOrFormatFilter("monsters", "monsters"), - GenreOrFormatFilter("mystery", "mystery"), - GenreOrFormatFilter("ninja", "ninja"), - GenreOrFormatFilter("office_workers", "office workers"), - GenreOrFormatFilter("philosophical", "philosophical"), - GenreOrFormatFilter("psychological", "psychological"), - GenreOrFormatFilter("reincarnation", "reincarnation"), - GenreOrFormatFilter("reverse_harem", "reverse harem"), - GenreOrFormatFilter("romance", "romance"), - GenreOrFormatFilter("school_life", "school life"), - GenreOrFormatFilter("sci_fi", "sci fi"), - GenreOrFormatFilter("sci-fi", "sci-fi"), - GenreOrFormatFilter("sexual_violence", "sexual violence"), - GenreOrFormatFilter("shota", "shota"), - GenreOrFormatFilter("shoujo_ai", "shoujo ai"), - GenreOrFormatFilter("shounen_ai", "shounen ai"), - GenreOrFormatFilter("slice_of_life", "slice of life"), - GenreOrFormatFilter("smut", "smut"), - GenreOrFormatFilter("sports", "sports"), - GenreOrFormatFilter("superhero", "superhero"), - GenreOrFormatFilter("supernatural", "supernatural"), - GenreOrFormatFilter("survival", "survival"), - GenreOrFormatFilter("time_travel", "time travel"), - GenreOrFormatFilter("tragedy", "tragedy"), - GenreOrFormatFilter("video_games", "video games"), - GenreOrFormatFilter("virtual_reality", "virtual reality"), - GenreOrFormatFilter("webtoons", "webtoons"), - GenreOrFormatFilter("wuxia", "wuxia"), - GenreOrFormatFilter("zombies", "zombies") - ) - ) - - // Actual format filter List - private class FormatsFilter : GenreOrFormatFilterList( - "Formats", - "formats", - listOf( - GenreOrFormatFilter("4-koma", "4-koma"), - GenreOrFormatFilter("adaptation", "adaptation"), - GenreOrFormatFilter("anthology", "anthology"), - GenreOrFormatFilter("award_winning", "award winning"), - GenreOrFormatFilter("doujinshi", "doujinshi"), - GenreOrFormatFilter("fan_colored", "fan colored"), - GenreOrFormatFilter("full_color", "full color"), - GenreOrFormatFilter("long_strip", "long strip"), - GenreOrFormatFilter("official_colored", "official colored"), - GenreOrFormatFilter("oneshot", "oneshot"), - GenreOrFormatFilter("web_comic", "web comic") - ), - - ) - - private class SortFilter : UriSelectFilter( - "Sort", - fun() = "sort", - arrayOf( - Pair("title", "Name"), - Pair("-lastReleasedAt", "Last update"), - Pair("-createdAt", "Newest"), - Pair("-rating -ratingCount", "Popular") - ), - defaultValue = 3 - ) { - override val shouldAdd = fun() = if (isForWebView()) state != defaultValue else true - - override val getParameter = fun(): String { - return if (isForWebView()) { - this.state.toString() - } else { - this.vals[this.state].first - } - } - } - - private class AuthorFilter : Filter.Text("Manga Author & Artist"), UriFilter { - override val uriParam = fun() = "creator" - - override val shouldAdd = fun() = state.isNotEmpty() - - override val getParameter = fun(): String = state - } - - /**The scanlator filter exists on the mangamutiny website, however it doesn't work. - This should stay disabled in the extension until it's properly implemented on the website, - otherwise users may be confused by searches that return no results.**/ - /* - private class ScanlatorFilter : Filter.Text("Scanlator Name"), UriFilter { - override val uriParam = fun() = "scanlator" - - override val shouldAdd = fun() = state.isNotEmpty() - - override val getParameter = fun(): String = state - } - */ -} - -private fun isForWebView(): Boolean = - Thread.currentThread().stackTrace.map { it.methodName } - .firstOrNull { - it.contains("WebView", true) && !it.contains("isForWebView") - } != null diff --git a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinySerialization.kt b/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinySerialization.kt deleted file mode 100644 index ad0262ccd..000000000 --- a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinySerialization.kt +++ /dev/null @@ -1,183 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangamutiny - -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.json.JsonArray -import kotlinx.serialization.json.JsonDecoder -import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.JsonNull -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.float -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonPrimitive -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone - -private fun JsonElement?.primitiveContent(): String? { - if (this is JsonNull) return null - return this?.jsonPrimitive?.content -} -private fun JsonElement?.primitiveInt(): Int? { - if (this is JsonNull) return null - return this?.jsonPrimitive?.int -} -private fun JsonElement?.primitiveFloat(): Float? { - if (this is JsonNull) return null - return this?.jsonPrimitive?.float -} - -private val jsonObjectToMapSerializer = MapSerializer(String.serializer(), JsonElement.serializer()) - -object PageInfoDS : DeserializationStrategy, Int>> { - override val descriptor: SerialDescriptor = jsonObjectToMapSerializer.descriptor - - override fun deserialize(decoder: Decoder): Pair, Int> { - require(decoder is JsonDecoder) - val json = decoder.json - val jsonElement = decoder.decodeJsonElement() - require(jsonElement is JsonObject) - val items = (jsonElement["items"] as JsonArray).map { json.decodeFromJsonElement(SMangaDS, it) } - val total = jsonElement["total"]?.jsonPrimitive?.int - - require(total != null) - return Pair(items, total) - } -} - -object SMangaDS : DeserializationStrategy { - override val descriptor: SerialDescriptor = jsonObjectToMapSerializer.descriptor - - override fun deserialize(decoder: Decoder): SManga { - require(decoder is JsonDecoder) - val jsonElement = decoder.decodeJsonElement() - require(jsonElement is JsonObject) - val title = jsonElement["title"].primitiveContent() - val slug = jsonElement["slug"].primitiveContent() - val thumbnail = jsonElement["thumbnail"].primitiveContent() - - val status: Int = jsonElement["status"].primitiveContent()?.let { - when (it) { - "ongoing" -> SManga.ONGOING - "completed" -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - } ?: SManga.UNKNOWN - - val summary: String? = jsonElement["summary"].primitiveContent() - val artists: String? = jsonElement["artists"].primitiveContent() - val authors: String? = jsonElement["authors"].primitiveContent() - val tags: String? = - jsonElement["tags"]?.jsonArray?.mapNotNull { it.primitiveContent() }?.joinToString() - - require(title != null && slug != null) - return SManga.create().apply { - this.title = title - this.url = slug - this.thumbnail_url = thumbnail - - this.status = status - this.description = summary - this.artist = artists - this.author = authors - this.genre = tags - } - } -} - -object ListChapterDS : DeserializationStrategy> { - override val descriptor: SerialDescriptor = jsonObjectToMapSerializer.descriptor - - override fun deserialize(decoder: Decoder): List { - require(decoder is JsonDecoder) - val json = decoder.json - val jsonElement = decoder.decodeJsonElement() - require(jsonElement is JsonObject) - - val jsonElementChapters = jsonElement["chapters"]?.jsonArray - require(jsonElementChapters != null) - - return jsonElementChapters.map { chapter -> - json.decodeFromJsonElement(SChapterDS, chapter) - }.apply { - if (this.size == 1) this.first().chapter_number = 1F - } - } -} - -private object SChapterDS : DeserializationStrategy { - - override val descriptor: SerialDescriptor = jsonObjectToMapSerializer.descriptor - - override fun deserialize(decoder: Decoder): SChapter { - require(decoder is JsonDecoder) - val jsonElement = decoder.decodeJsonElement() - require(jsonElement is JsonObject) - val volume: Int? = jsonElement["volume"].primitiveInt() - val chapter: Float? = jsonElement["chapter"].primitiveFloat() - val title: String? = jsonElement["title"].primitiveContent() - val slug: String? = jsonElement["slug"].primitiveContent() - val releasedAt: String? = jsonElement["releasedAt"].primitiveContent() - - require(slug != null && releasedAt != null) - return SChapter.create().apply { - if (chapter != null) this.chapter_number = chapter - this.name = chapterTitleBuilder(volume, title, chapter) - this.url = slug - this.date_upload = dateFormatter.parse(releasedAt)?.time ?: 0 - } - } - - private val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) - .apply { timeZone = TimeZone.getTimeZone("UTC") } - - /** - * Converts this Float into a String, removing any trailing .0 - */ - private fun Float.toStringWithoutDotZero(): String = when (this % 1) { - 0F -> this.toInt().toString() - else -> this.toString() - } - - private fun chapterTitleBuilder(volume: Int?, title: String?, chapter: Float?): String { - val chapterTitle = StringBuilder() - if (volume != null) { - chapterTitle.append("Vol. $volume ") - } - if (chapter != null) { - chapterTitle.append("Chapter ${chapter.toStringWithoutDotZero()}") - } - if (title != null && title != "") { - if (chapterTitle.isNotEmpty()) chapterTitle.append(": ") - chapterTitle.append(title) - } - return chapterTitle.toString() - } -} - -object ListPageDS : DeserializationStrategy> { - override val descriptor: SerialDescriptor = jsonObjectToMapSerializer.descriptor - - override fun deserialize(decoder: Decoder): List { - require(decoder is JsonDecoder) - val jsonElement = decoder.decodeJsonElement() - require(jsonElement is JsonObject) - - val storage: String? = jsonElement["storage"].primitiveContent() - val manga: String? = jsonElement["manga"].primitiveContent() - val id: String? = jsonElement["id"].primitiveContent() - val images: List? = - jsonElement["images"]?.jsonArray?.mapNotNull { it.primitiveContent() } - - require(storage != null && manga != null && id != null && images != null) - val chapterUrl = "$storage/$manga/$id/" - return images.mapIndexed { index, pageSuffix -> Page(index, "", chapterUrl + pageSuffix) } - } -} diff --git a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinyUrlActivity.kt b/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinyUrlActivity.kt deleted file mode 100644 index cc358b29d..000000000 --- a/src/en/mangamutiny/src/eu/kanade/tachiyomi/extension/en/mangamutiny/MangaMutinyUrlActivity.kt +++ /dev/null @@ -1,41 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangamutiny - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.util.Log -import kotlin.system.exitProcess - -/** - * Springboard that accepts https://mangamutiny.org/title/xxx intents and redirects them to - * the main tachiyomi process. The idea is to not install the intent filter unless - * you have this extension installed, but still let the main tachiyomi app control - * things. - */ -class MangaMutinyUrlActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val pathSegments = intent?.data?.pathSegments - if (pathSegments != null && pathSegments.size > 1) { - val slug = pathSegments[1] - val mainIntent = Intent().apply { - action = "eu.kanade.tachiyomi.SEARCH" - putExtra("query", "${MangaMutiny.PREFIX_ID_SEARCH}$slug") - putExtra("filter", packageName) - } - - try { - startActivity(mainIntent) - } catch (e: ActivityNotFoundException) { - Log.e("MangaMutinyUrlActivity", e.toString()) - } - } else { - Log.e("MangaMutinyUrlActivity", "could not parse uri from intent $intent") - } - - finish() - exitProcess(0) - } -}