Replace usages of Gson (#10080)

This commit is contained in:
arkon 2021-12-12 16:33:20 -05:00 committed by GitHub
parent 1f6027dd67
commit fa6be0f1c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 572 additions and 575 deletions

View File

@ -123,25 +123,26 @@ dependencies {
#### Additional dependencies #### Additional dependencies
You may find yourself needing additional functionality and wanting to add more dependencies to your `build.gradle` file. Since extensions are run within the main Tachiyomi app, you can make use of [its dependencies](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle). You may find yourself needing additional functionality and wanting to add more dependencies to your `build.gradle` file. Since extensions are run within the main Tachiyomi app, you can make use of [its dependencies](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts).
For example, an extension that needs Gson could add the following: For example, an extension that needs coroutines, it could add the following:
```gradle ```gradle
dependencies { dependencies {
compileOnly 'com.google.code.gson:gson:2.8.2' compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
compileOnly 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
} }
``` ```
(Note that Gson, and several other dependencies, are already exposed to all extensions via `common.gradle`.) (Note that several dependencies are already exposed to all extensions via `common-dependencies.gradle`.)
Notice that we're using `compileOnly` instead of `implementation`, since the app already contains it. You could use `implementation` instead for a new dependency, or you prefer not to rely on whatever the main app has at the expense of app size. Notice that we're using `compileOnly` instead of `implementation`, since the app already contains it. You could use `implementation` instead for a new dependency, or you prefer not to rely on whatever the main app has at the expense of app size.
Note that using `compileOnly` restricts you to versions that must be compatible with those used in [Tachiyomi v0.8.5+](https://github.com/tachiyomiorg/tachiyomi/blob/82141cec6e612885fef4fa70092e29e99d60adbb/app/build.gradle#L104) for proper backwards compatibility. Note that using `compileOnly` restricts you to versions that must be compatible with those used in [Tachiyomi v0.10.12+](https://github.com/tachiyomiorg/tachiyomi/blob/v0.10.12/app/build.gradle.kts) for proper backwards compatibility.
### Extension main class ### Extension main class
The class which is refrenced and defined by `extClass` in `build.gradle`. This class should implement either `SourceFactory` or extend one of the `Source` implementations: `HttpSource` or `ParsedHttpSource`. The class which is referenced and defined by `extClass` in `build.gradle`. This class should implement either `SourceFactory` or extend one of the `Source` implementations: `HttpSource` or `ParsedHttpSource`.
| Class | Description | | Class | Description |
| ----- | ----------- | | ----- | ----------- |

View File

@ -5,13 +5,6 @@ import android.content.SharedPreferences
import androidx.preference.CheckBoxPreference import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.github.salomonbrys.kotson.addProperty
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.set
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
@ -22,6 +15,20 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -29,6 +36,7 @@ import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.Calendar import java.util.Calendar
abstract class Luscious( abstract class Luscious(
@ -44,7 +52,7 @@ abstract class Luscious(
private val apiBaseUrl: String = "$baseUrl/graphql/nobatch/" private val apiBaseUrl: String = "$baseUrl/graphql/nobatch/"
private val gson = Gson() private val json: Json by injectLazy()
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
@ -86,90 +94,91 @@ abstract class Luscious(
val contentTypeFilter = filters.findInstance<ContentTypeSelectFilter>()!! val contentTypeFilter = filters.findInstance<ContentTypeSelectFilter>()!!
val albumSizeFilter = filters.findInstance<AlbumSizeSelectFilter>()!! val albumSizeFilter = filters.findInstance<AlbumSizeSelectFilter>()!!
val restrictGenresFilter = filters.findInstance<RestrictGenresSelectFilter>()!! val restrictGenresFilter = filters.findInstance<RestrictGenresSelectFilter>()!!
return JsonObject().apply { return buildJsonObject {
add( putJsonObject("input") {
"input", put("display", sortByFilter.selected)
JsonObject().apply { put("page", page)
addProperty("display", sortByFilter.selected) putJsonArray("filters") {
addProperty("page", page) if (contentTypeFilter.selected != FILTER_VALUE_IGNORE)
add( add(contentTypeFilter.toJsonObject("content_id"))
"filters",
JsonArray().apply {
if (contentTypeFilter.selected != FILTER_VALUE_IGNORE) if (albumTypeFilter.selected != FILTER_VALUE_IGNORE)
add(contentTypeFilter.toJsonObject("content_id")) add(albumTypeFilter.toJsonObject("album_type"))
if (albumTypeFilter.selected != FILTER_VALUE_IGNORE) if (selectionFilter.selected != FILTER_VALUE_IGNORE)
add(albumTypeFilter.toJsonObject("album_type")) add(selectionFilter.toJsonObject("selection"))
if (selectionFilter.selected != FILTER_VALUE_IGNORE) if (albumSizeFilter.selected != FILTER_VALUE_IGNORE)
add(selectionFilter.toJsonObject("selection")) add(albumSizeFilter.toJsonObject("picture_count_rank"))
if (albumSizeFilter.selected != FILTER_VALUE_IGNORE) if (restrictGenresFilter.selected != FILTER_VALUE_IGNORE)
add(albumSizeFilter.toJsonObject("picture_count_rank")) add(restrictGenresFilter.toJsonObject("restrict_genres"))
if (restrictGenresFilter.selected != FILTER_VALUE_IGNORE) with(interestsFilter) {
add(restrictGenresFilter.toJsonObject("restrict_genres")) if (this.selected.isEmpty()) {
throw Exception("Please select an Interest")
with(interestsFilter) {
if (this.selected.isEmpty()) {
throw Exception("Please select an Interest")
}
add(this.toJsonObject("audience_ids"))
}
if (lusLang != FILTER_VALUE_IGNORE) {
add(
languagesFilter.toJsonObject("language_ids").apply {
set("value", "+$lusLang${get("value").asString}")
}
)
}
if (tagsFilter.state.isNotEmpty()) {
val tags = "+${tagsFilter.state.toLowerCase()}".replace(" ", "_").replace("_,", "+").replace(",_", "+").replace(",", "+").replace("+-", "-").replace("-_", "-").trim()
add(
JsonObject().apply {
addProperty("name", "tagged")
addProperty("value", tags)
}
)
}
if (creatorFilter.state.isNotEmpty()) {
add(
JsonObject().apply {
addProperty("name", "created_by_id")
addProperty("value", creatorFilter.state)
}
)
}
if (favoriteFilter.state.isNotEmpty()) {
add(
JsonObject().apply {
addProperty("name", "favorite_by_user_id")
addProperty("value", favoriteFilter.state)
}
)
}
if (genreFilter.anyNotIgnored()) {
add(genreFilter.toJsonObject("genre_ids"))
}
if (query != "") {
add(
JsonObject().apply {
addProperty("name", "search_query")
addProperty("value", query)
}
)
}
} }
) add(this.toJsonObject("audience_ids"))
}
if (lusLang != FILTER_VALUE_IGNORE) {
val languageIds = languagesFilter.toJsonObject("language_ids")
add(
JsonObject(
languageIds.toMutableMap().apply {
put(
"value",
JsonPrimitive ("+$lusLang${languageIds["value"]!!.jsonPrimitive.content}")
)
}
)
)
}
if (tagsFilter.state.isNotEmpty()) {
val tags = "+${tagsFilter.state.toLowerCase()}".replace(" ", "_")
.replace("_,", "+").replace(",_", "+").replace(",", "+")
.replace("+-", "-").replace("-_", "-").trim()
add(
buildJsonObject {
put("name", "tagged")
put("value", tags)
}
)
}
if (creatorFilter.state.isNotEmpty()) {
add(
buildJsonObject {
put("name", "created_by_id")
put("value", creatorFilter.state)
}
)
}
if (favoriteFilter.state.isNotEmpty()) {
add(
buildJsonObject {
put("name", "favorite_by_user_id")
put("value", favoriteFilter.state)
}
)
}
if (genreFilter.anyNotIgnored()) {
add(genreFilter.toJsonObject("genre_ids"))
}
if (query != "") {
add(
buildJsonObject {
put("name", "search_query")
put("value", query)
}
)
}
} }
) }
} }
} }
@ -184,24 +193,24 @@ abstract class Luscious(
} }
private fun parseAlbumListResponse(response: Response): MangasPage { private fun parseAlbumListResponse(response: Response): MangasPage {
val data = gson.fromJson<JsonObject>(response.body!!.string()) val data = json.decodeFromString<JsonObject>(response.body!!.string())
with(data["data"]["album"]["list"]) { with(data["data"]!!.jsonObject["album"]!!.jsonObject["list"]) {
return MangasPage( return MangasPage(
this["items"].asJsonArray.map { this!!.jsonObject["items"]!!.jsonArray.map {
SManga.create().apply { SManga.create().apply {
url = it["url"].asString url = it.jsonObject["url"]!!.jsonPrimitive.content
title = it["title"].asString title = it.jsonObject["title"]!!.jsonPrimitive.content
thumbnail_url = it["cover"]["url"].asString thumbnail_url = it.jsonObject["cover"]!!.jsonObject["url"]!!.jsonPrimitive.content
} }
}, },
this["info"]["has_next_page"].asBoolean this.jsonObject["info"]!!.jsonObject["has_next_page"]!!.jsonPrimitive.boolean
) )
} }
} }
private fun buildAlbumInfoRequestInput(id: String): JsonObject { private fun buildAlbumInfoRequestInput(id: String): JsonObject {
return JsonObject().apply { return buildJsonObject {
addProperty("id", id) put("id", id)
} }
} }
@ -246,34 +255,34 @@ abstract class Luscious(
var nextPage = true var nextPage = true
var page = 2 var page = 2
val id = response.request.url.queryParameter("variables").toString() val id = response.request.url.queryParameter("variables").toString()
.let { gson.fromJson<JsonObject>(it)["input"]["filters"].asJsonArray } .let { json.decodeFromString<JsonObject>(it)["input"]!!.jsonObject["filters"]!!.jsonArray }
.let { it.first { f -> f["name"].asString == "album_id" } } .let { it.first { f -> f.jsonObject["name"]!!.jsonPrimitive.content == "album_id" } }
.let { it["value"].asString } .let { it.jsonObject["value"]!!.jsonPrimitive.content }
var data = gson.fromJson<JsonObject>(response.body!!.string()) var data = json.decodeFromString<JsonObject>(response.body!!.string())
.let { it["data"]["picture"]["list"].asJsonObject } .let { it.jsonObject["data"]!!.jsonObject["picture"]!!.jsonObject["list"]!!.jsonObject }
while (nextPage) { while (nextPage) {
nextPage = data["info"]["has_next_page"].asBoolean nextPage = data["info"]!!.jsonObject["has_next_page"]!!.jsonPrimitive.boolean
data["items"].asJsonArray.map { data["items"]!!.jsonArray.map {
val chapter = SChapter.create() val chapter = SChapter.create()
val url = when (getResolutionPref()) { val url = when (getResolutionPref()) {
"-1" -> it["url_to_original"].asString "-1" -> it.jsonObject["url_to_original"]!!.jsonPrimitive.content
else -> it["thumbnails"][getResolutionPref()?.toInt()!!]["url"].asString else -> it.jsonObject["thumbnails"]!!.jsonObject[getResolutionPref()?.toInt()!!]!!.jsonObject["url"]!!.jsonPrimitive.content
} }
when { when {
url.startsWith("//") -> chapter.url = "https:$url" url.startsWith("//") -> chapter.url = "https:$url"
else -> chapter.url = url else -> chapter.url = url
} }
chapter.chapter_number = it["position"].asInt.toFloat() chapter.chapter_number = it.jsonObject["position"]!!.jsonPrimitive.int.toFloat()
chapter.name = chapter.chapter_number.toInt().toString() + " - " + it["title"].asString chapter.name = chapter.chapter_number.toInt().toString() + " - " + it.jsonObject["title"]!!.jsonPrimitive.content
chapter.date_upload = "${it["created"].asLong}000".toLong() chapter.date_upload = "${it.jsonObject["created"]!!.jsonPrimitive.long}000".toLong()
chapters.add(chapter) chapters.add(chapter)
} }
if (nextPage) { if (nextPage) {
val newPage = client.newCall(GET(buildAlbumPicturesPageUrl(id, page))).execute() val newPage = client.newCall(GET(buildAlbumPicturesPageUrl(id, page))).execute()
data = gson.fromJson<JsonObject>(newPage.body!!.string()) data = json.decodeFromString<JsonObject>(newPage.body!!.string())
.let { it["data"]["picture"]["list"].asJsonObject } .let { it["data"]!!.jsonObject["picture"]!!.jsonObject["list"]!!.jsonObject }
} }
page++ page++
} }
@ -288,25 +297,17 @@ abstract class Luscious(
// Pages // Pages
private fun buildAlbumPicturesRequestInput(id: String, page: Int): JsonObject { private fun buildAlbumPicturesRequestInput(id: String, page: Int): JsonObject {
return JsonObject().apply { return buildJsonObject {
addProperty( putJsonObject("input") {
"input", putJsonArray("filters") {
JsonObject().apply { add(buildJsonObject {
addProperty( put("name", "album_id")
"filters", put("value", id)
JsonArray().apply { })
add(
JsonObject().apply {
addProperty("name", "album_id")
addProperty("value", id)
}
)
}
)
addProperty("display", getSortPref())
addProperty("page", page)
} }
) put("display", getSortPref())
put("page", page)
}
} }
} }
@ -324,20 +325,20 @@ abstract class Luscious(
var nextPage = true var nextPage = true
var page = 2 var page = 2
val id = response.request.url.queryParameter("variables").toString() val id = response.request.url.queryParameter("variables").toString()
.let { gson.fromJson<JsonObject>(it)["input"]["filters"].asJsonArray } .let { json.decodeFromString<JsonObject>(it)["input"]!!.jsonObject["filters"]!!.jsonArray }
.let { it.first { f -> f["name"].asString == "album_id" } } .let { it.first { f -> f.jsonObject["name"]!!.jsonPrimitive.content == "album_id" } }
.let { it["value"].asString } .let { it.jsonObject["value"]!!.jsonPrimitive.content }
var data = gson.fromJson<JsonObject>(response.body!!.string()) var data = json.decodeFromString<JsonObject>(response.body!!.string())
.let { it["data"]["picture"]["list"].asJsonObject } .let { it["data"]!!.jsonObject["picture"]!!.jsonObject["list"]!!.jsonObject }
while (nextPage) { while (nextPage) {
nextPage = data["info"]["has_next_page"].asBoolean nextPage = data["info"]!!.jsonObject["has_next_page"]!!.jsonPrimitive.boolean
data["items"].asJsonArray.map { data["items"]!!.jsonArray.map {
val index = it["position"].asInt val index = it.jsonObject["position"]!!.jsonPrimitive.int
val url = when (getResolutionPref()) { val url = when (getResolutionPref()) {
"-1" -> it["url_to_original"].asString "-1" -> it.jsonObject["url_to_original"]!!.jsonPrimitive.content
else -> it["thumbnails"][getResolutionPref()?.toInt()!!]["url"].asString else -> it.jsonObject["thumbnails"]!!.jsonObject[getResolutionPref()?.toInt()!!]!!.jsonObject["url"]!!.jsonPrimitive.content
} }
when { when {
url.startsWith("//") -> pages.add(Page(index, "https:$url", "https:$url")) url.startsWith("//") -> pages.add(Page(index, "https:$url", "https:$url"))
@ -346,8 +347,8 @@ abstract class Luscious(
} }
if (nextPage) { if (nextPage) {
val newPage = client.newCall(GET(buildAlbumPicturesPageUrl(id, page))).execute() val newPage = client.newCall(GET(buildAlbumPicturesPageUrl(id, page))).execute()
data = gson.fromJson<JsonObject>(newPage.body!!.string()) data = json.decodeFromString<JsonObject>(newPage.body!!.string())
.let { it["data"]["picture"]["list"].asJsonObject } .let { it["data"]!!.jsonObject["picture"]!!.jsonObject["list"]!!.jsonObject }
} }
page++ page++
} }
@ -381,12 +382,12 @@ abstract class Luscious(
return client.newCall(GET(page.url, headers)) return client.newCall(GET(page.url, headers))
.asObservableSuccess() .asObservableSuccess()
.map { .map {
val data = gson.fromJson<JsonObject>(it.body!!.string()).let { data -> val data = json.decodeFromString<JsonObject>(it.body!!.string()).let { data ->
data["data"]["picture"]["list"].asJsonObject data["data"]!!.jsonObject["picture"]!!.jsonObject["list"]!!.jsonObject
} }
when (getResolutionPref()) { when (getResolutionPref()) {
"-1" -> data["items"].asJsonArray[page.index % 50].asJsonObject["url_to_original"].asString "-1" -> data["items"]!!.jsonArray[page.index % 50].jsonObject["url_to_original"]!!.jsonPrimitive.content
else -> data["items"].asJsonArray[page.index % 50].asJsonObject["thumbnails"][getResolutionPref()?.toInt()!!]["url"].asString else -> data["items"]!!.jsonArray[page.index % 50].jsonObject["thumbnails"]!!.jsonObject[getResolutionPref()?.toInt()!!]!!.jsonObject["url"]!!.jsonPrimitive.content
} }
} }
} }
@ -405,32 +406,32 @@ abstract class Luscious(
} }
private fun detailsParse(response: Response): SManga { private fun detailsParse(response: Response): SManga {
val data = gson.fromJson<JsonObject>(response.body!!.string()) val data = json.decodeFromString<JsonObject>(response.body!!.string())
with(data["data"]["album"]["get"]) { with(data["data"]!!.jsonObject["album"]!!.jsonObject["get"]!!.jsonObject) {
val manga = SManga.create() val manga = SManga.create()
manga.url = this["url"].asString manga.url = this["url"]!!.jsonPrimitive.content
manga.title = this["title"].asString manga.title = this["title"]!!.jsonPrimitive.content
manga.thumbnail_url = this["cover"]["url"].asString manga.thumbnail_url = this["cover"]!!.jsonObject["url"]!!.jsonPrimitive.content
manga.status = 0 manga.status = 0
manga.description = "${this["description"].asString}\n\nPictures: ${this["number_of_pictures"].asString}\nAnimated Pictures: ${this["number_of_animated_pictures"].asString}" manga.description = "${this["description"]!!.jsonPrimitive.content}\n\nPictures: ${this["number_of_pictures"]!!.jsonPrimitive.content}\nAnimated Pictures: ${this["number_of_animated_pictures"]!!.jsonPrimitive.content}"
var genreList = this["language"]["title"].asString var genreList = this["language"]!!.jsonObject["title"]!!.jsonPrimitive.content
for ((i, _) in this["labels"].asJsonArray.withIndex()) { for ((i, _) in this["labels"]!!.jsonArray.withIndex()) {
genreList = "$genreList, ${this["labels"][i].asString}" genreList = "$genreList, ${this["labels"]!!.jsonArray[i].jsonPrimitive.content}"
} }
for ((i, _) in this["genres"].asJsonArray.withIndex()) { for ((i, _) in this["genres"]!!.jsonArray.withIndex()) {
genreList = "$genreList, ${this["genres"][i]["title"].asString}" genreList = "$genreList, ${this["genres"]!!.jsonArray[i].jsonObject["title"]!!.jsonPrimitive.content}"
} }
for ((i, _) in this["audiences"].asJsonArray.withIndex()) { for ((i, _) in this["audiences"]!!.jsonArray.withIndex()) {
genreList = "$genreList, ${this["audiences"][i]["title"].asString}" genreList = "$genreList, ${this["audiences"]!!.jsonArray[i].jsonObject["title"]!!.jsonPrimitive.content}"
} }
for ((i, _) in this["tags"].asJsonArray.withIndex()) { for ((i, _) in this["tags"]!!.jsonArray.withIndex()) {
genreList = "$genreList, ${this["tags"][i]["text"].asString}" genreList = "$genreList, ${this["tags"]!!.jsonArray[i].jsonObject["text"]!!.jsonPrimitive.content}"
if (this["tags"][i]["text"].asString.contains("Artist:")) { if (this["tags"]!!.jsonArray[i].jsonObject["text"]!!.jsonPrimitive.content.contains("Artist:")) {
manga.artist = this["tags"][i]["text"].asString.substringAfter(":").trim() manga.artist = this["tags"]!!.jsonArray[i].jsonObject["text"]!!.jsonPrimitive.content.substringAfter(":").trim()
manga.author = manga.artist manga.author = manga.artist
} }
} }
genreList = "$genreList, ${this["content"]["title"].asString}" genreList = "$genreList, ${this["content"]!!.jsonObject["title"]!!.jsonPrimitive.content}"
manga.genre = genreList manga.genre = genreList
return manga return manga
@ -483,9 +484,9 @@ abstract class Luscious(
private fun Filter<*>.toJsonObject(key: String): JsonObject { private fun Filter<*>.toJsonObject(key: String): JsonObject {
val value = this.toString() val value = this.toString()
return JsonObject().apply { return buildJsonObject {
addProperty("name", key) put("name", key)
addProperty("value", value) put("value", value)
} }
} }

View File

@ -2,13 +2,6 @@ package eu.kanade.tachiyomi.multisrc.mmrcms
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.bool
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
@ -19,11 +12,20 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -33,7 +35,7 @@ abstract class MMRCMS(
override val name: String, override val name: String,
override val baseUrl: String, override val baseUrl: String,
override val lang: String, override val lang: String,
private val sourceInfo: String = "", sourceInfo: String = "",
) : HttpSource() { ) : HttpSource() {
open val jsonData = if (sourceInfo == "") { open val jsonData = if (sourceInfo == "") {
SourceData.giveMetaData(baseUrl) SourceData.giveMetaData(baseUrl)
@ -72,14 +74,15 @@ abstract class MMRCMS(
* *
* *
*/ */
open val jsonObject = JsonParser.parseString(jsonData) as JsonObject private val json: Json by injectLazy()
override val supportsLatest = jsonObject["supports_latest"].bool val jsonObject = json.decodeFromString<JsonObject>(jsonData)
open val itemUrl = jsonObject["item_url"].string override val supportsLatest = jsonObject["supports_latest"]!!.jsonPrimitive.boolean
open val categoryMappings = mapToPairs(jsonObject["categories"].array) open val itemUrl = jsonObject["item_url"]!!.jsonPrimitive.content
open var tagMappings = if (jsonObject["tags"].isJsonArray) { open val categoryMappings = mapToPairs(jsonObject["categories"]!!.jsonArray)
mapToPairs(jsonObject["tags"].asJsonArray) open var tagMappings = if (jsonObject["tags"] is JsonArray) {
mapToPairs(jsonObject["tags"]!!.jsonArray)
} else { } else {
emptyList<Pair<String, String>>() emptyList()
} }
/** /**
@ -95,7 +98,7 @@ abstract class MMRCMS(
open fun mapToPairs(array: JsonArray): List<Pair<String, String>> = array.map { open fun mapToPairs(array: JsonArray): List<Pair<String, String>> = array.map {
it as JsonObject it as JsonObject
it["id"].string to it["name"].string it["id"]!!.jsonPrimitive.content to it["name"]!!.jsonPrimitive.content
} }
private val itemUrlPath = Uri.parse(itemUrl).pathSegments.firstOrNull() private val itemUrlPath = Uri.parse(itemUrl).pathSegments.firstOrNull()
@ -153,16 +156,16 @@ abstract class MMRCMS(
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
return if (listOf("query", "q").any { it in response.request.url.queryParameterNames }) { return if (listOf("query", "q").any { it in response.request.url.queryParameterNames }) {
// If a search query was specified, use search instead! // If a search query was specified, use search instead!
val jsonArray = JsonParser.parseString(response.body!!.string()).let { val jsonArray = json.decodeFromString<JsonObject>(response.body!!.string()).let {
it["suggestions"].array it["suggestions"]!!.jsonArray
} }
MangasPage( MangasPage(
jsonArray jsonArray
.map { .map {
SManga.create().apply { SManga.create().apply {
val segment = it["data"].string val segment = it.jsonObject["data"]!!.jsonPrimitive.content
url = getUrlWithoutBaseUrl(itemUrl + segment) url = getUrlWithoutBaseUrl(itemUrl + segment)
title = it["value"].string title = it.jsonObject["value"]!!.jsonPrimitive.content
// Guess thumbnails // Guess thumbnails
// thumbnail_url = "$baseUrl/uploads/manga/$segment/cover/cover_250x350.jpg" // thumbnail_url = "$baseUrl/uploads/manga/$segment/cover/cover_250x350.jpg"

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
@ -73,7 +73,7 @@ abstract class ToomicsGlobal(
.add("Content-Type", "application/x-www-form-urlencoded") .add("Content-Type", "application/x-www-form-urlencoded")
.build() .build()
val rbody = RequestBody.create(null, "toonData=$query&offset=0&limit=20") val rbody = "toonData=$query&offset=0&limit=20".toRequestBody(null)
return POST("$baseUrl/$siteLang/webtoon/ajax_search", newHeaders, rbody) return POST("$baseUrl/$siteLang/webtoon/ajax_search", newHeaders, rbody)
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'GMANGA' extName = 'GMANGA'

View File

@ -1,13 +1,6 @@
package eu.kanade.tachiyomi.extension.ar.gmanga package eu.kanade.tachiyomi.extension.ar.gmanga
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.toJsonArray
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING
import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING_SHOW_POPULAR import eu.kanade.tachiyomi.extension.ar.gmanga.GmangaPreferences.Companion.PREF_CHAPTER_LISTING_SHOW_POPULAR
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
@ -21,12 +14,24 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.float
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long
import okhttp3.Headers import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class Gmanga : ConfigurableSource, HttpSource() { class Gmanga : ConfigurableSource, HttpSource() {
@ -40,7 +45,7 @@ class Gmanga : ConfigurableSource, HttpSource() {
override val supportsLatest: Boolean = true override val supportsLatest: Boolean = true
private val gson = Gson() private val json: Json by injectLazy()
private val preferences = GmangaPreferences(id) private val preferences = GmangaPreferences(id)
@ -67,18 +72,18 @@ class Gmanga : ConfigurableSource, HttpSource() {
val chapters: List<JsonArray> = buildList { val chapters: List<JsonArray> = buildList {
val allChapters: ArrayList<JsonArray> = ArrayList() val allChapters: ArrayList<JsonArray> = ArrayList()
data["rows"][0]["rows"].asJsonArray.forEach { release -> data["rows"]!!.jsonArray[0].jsonObject["rows"]!!.jsonArray.forEach { release ->
val chapter = data["rows"][2]["rows"].asJsonArray.filter { it[0] == release[4] }.toJsonArray() val chapter = data["rows"]!!.jsonArray[2].jsonObject["rows"]!!.jsonArray.filter { it.jsonArray[0] == release.jsonArray[4] }
chapter.asJsonArray.forEach { release.asJsonArray.addAll(it.asJsonArray) } allChapters.addAll(chapter.map { it.jsonArray })
val team = data["rows"][1]["rows"].asJsonArray.filter { it[0] == release[5] }.toJsonArray() val team = data["rows"]!!.jsonArray[1].jsonObject["rows"]!!.jsonArray.filter { it.jsonArray[0] == release.jsonArray[5] }
team.asJsonArray.forEach { release.asJsonArray.addAll(it.asJsonArray) } allChapters.addAll(team.map { it.jsonArray })
allChapters.add(release.asJsonArray) allChapters.add(release.jsonArray)
} }
when (preferences.getString(PREF_CHAPTER_LISTING)) { when (preferences.getString(PREF_CHAPTER_LISTING)) {
PREF_CHAPTER_LISTING_SHOW_POPULAR -> addAll( PREF_CHAPTER_LISTING_SHOW_POPULAR -> addAll(
allChapters.groupBy { it.asJsonArray[4].asFloat } allChapters.groupBy { it.jsonArray[4].jsonPrimitive.float }
.map { (_: Float, versions: List<JsonArray>) -> versions.maxByOrNull { it[5].asLong }!! } .map { (_: Float, versions: List<JsonArray>) -> versions.maxByOrNull { it[5].jsonPrimitive.float }!! }
) )
else -> addAll(allChapters) else -> addAll(allChapters)
} }
@ -86,14 +91,14 @@ class Gmanga : ConfigurableSource, HttpSource() {
return chapters.map { return chapters.map {
SChapter.create().apply { SChapter.create().apply {
chapter_number = it[8].asFloat chapter_number = it[8].jsonPrimitive.float
val chapterName = it[10].asString.let { if (it.trim() != "") " - $it" else "" } val chapterName = it[10].jsonPrimitive.content.let { if (it.trim() != "") " - $it" else "" }
url = "/r/${it[0]}" url = "/r/${it[0]}"
name = "${chapter_number.let { if (it % 1 > 0) it else it.toInt() }}$chapterName" name = "${chapter_number.let { if (it % 1 > 0) it else it.toInt() }}$chapterName"
date_upload = it[2].asLong * 1000 date_upload = it[2].jsonPrimitive.long * 1000
scanlator = it[13].asString scanlator = it[13].jsonPrimitive.content
} }
} }
} }
@ -101,16 +106,16 @@ class Gmanga : ConfigurableSource, HttpSource() {
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used") override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
val data = gson.fromJson<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html()) val data = json.decodeFromString<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html())
return MangasPage( return MangasPage(
data["mangaDataAction"]["newMangas"].asJsonArray.map { data["mangaDataAction"]!!.jsonObject["newMangas"]!!.jsonArray.map {
SManga.create().apply { SManga.create().apply {
url = "/mangas/${it["id"].asString}" url = "/mangas/${it.jsonObject["id"]!!.jsonPrimitive.content}"
title = it["title"].asString title = it.jsonObject["title"]!!.jsonPrimitive.content
thumbnail_url = it["cover"].nullString?.let { coverFileName -> thumbnail_url = it.jsonObject["cover"]!!.jsonPrimitive.contentOrNull?.let { coverFileName ->
val thumbnail = "medium_${coverFileName.substringBeforeLast(".")}.webp" val thumbnail = "medium_${coverFileName.substringBeforeLast(".")}.webp"
"https://media.$domain/uploads/manga/cover/${it["id"].asString}/$thumbnail" "https://media.$domain/uploads/manga/cover/${it.jsonObject["id"]!!.jsonPrimitive.content}/$thumbnail"
} }
} }
}, },
@ -123,27 +128,27 @@ class Gmanga : ConfigurableSource, HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val data = gson.fromJson<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html()) val data = json.decodeFromString<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html())
val mangaData = data["mangaDataAction"]["mangaData"].asJsonObject val mangaData = data["mangaDataAction"]!!.jsonObject["mangaData"]!!.jsonObject
return SManga.create().apply { return SManga.create().apply {
description = mangaData["summary"].nullString ?: "" description = mangaData["summary"]!!.jsonPrimitive.contentOrNull ?: ""
artist = mangaData["artists"].asJsonArray.joinToString(", ") { it.asJsonObject["name"].asString } artist = mangaData["artists"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content }
author = mangaData["authors"].asJsonArray.joinToString(", ") { it.asJsonObject["name"].asString } author = mangaData["authors"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content }
genre = mangaData["categories"].asJsonArray.joinToString(", ") { it.asJsonObject["name"].asString } genre = mangaData["categories"]!!.jsonArray.joinToString(", ") { it.jsonObject["name"]!!.jsonPrimitive.content }
} }
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val url = response.request.url.toString() val url = response.request.url.toString()
val data = gson.fromJson<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html()) val data = json.decodeFromString<JsonObject>(response.asJsoup().select(".js-react-on-rails-component").html())
val releaseData = data["readerDataAction"]["readerData"]["release"].asJsonObject val releaseData = data["readerDataAction"]!!.jsonObject["readerData"]!!.jsonObject["release"]!!.jsonObject
val hasWebP = releaseData["webp_pages"].asJsonArray.size() > 0 val hasWebP = releaseData["webp_pages"]!!.jsonArray.size > 0
return releaseData[if (hasWebP) "webp_pages" else "pages"].asJsonArray.map { it.asString }.mapIndexed { index, pageUri -> return releaseData[if (hasWebP) "webp_pages" else "pages"]!!.jsonArray.map { it.jsonPrimitive.content }.mapIndexed { index, pageUri ->
Page( Page(
index, index,
"$url#page_$index", "$url#page_$index",
"https://media.$domain/uploads/releases/${releaseData["storage_key"].asString}/hq${if (hasWebP) "_webp" else ""}/$pageUri" "https://media.$domain/uploads/releases/${releaseData["storage_key"]!!.jsonPrimitive.content}/hq${if (hasWebP) "_webp" else ""}/$pageUri"
) )
} }
} }
@ -154,29 +159,29 @@ class Gmanga : ConfigurableSource, HttpSource() {
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
val data = decryptResponse(response) val data = decryptResponse(response)
val mangas = data["mangas"].asJsonArray val mangas = data["mangas"]!!.jsonArray
return MangasPage( return MangasPage(
mangas.asJsonArray.map { mangas.jsonArray.map {
SManga.create().apply { SManga.create().apply {
url = "/mangas/${it["id"].asString}" url = "/mangas/${it.jsonObject["id"]!!.jsonPrimitive.content}"
title = it["title"].asString title = it.jsonObject["title"]!!.jsonPrimitive.content
val thumbnail = "medium_${it["cover"].asString.substringBeforeLast(".")}.webp" val thumbnail = "medium_${it.jsonObject["cover"]!!.jsonPrimitive.content.substringBeforeLast(".")}.webp"
thumbnail_url = "https://media.$domain/uploads/manga/cover/${it["id"].asString}/$thumbnail" thumbnail_url = "https://media.$domain/uploads/manga/cover/${it.jsonObject["id"]!!.jsonPrimitive.content}/$thumbnail"
} }
}, },
mangas.size() == 50 mangas.size == 50
) )
} }
private fun decryptResponse(response: Response): JsonObject { private fun decryptResponse(response: Response): JsonObject {
val encryptedData = gson.fromJson<JsonObject>(response.body!!.string())["data"].asString val encryptedData = json.decodeFromString<JsonObject>(response.body!!.string())["data"]!!.jsonPrimitive.content
val decryptedData = decrypt(encryptedData) val decryptedData = decrypt(encryptedData)
return gson.fromJson(decryptedData) return json.decodeFromString(decryptedData)
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return GmangaFilters.buildSearchPayload(page, query, if (filters.isEmpty()) getFilterList() else filters).let { return GmangaFilters.buildSearchPayload(page, query, if (filters.isEmpty()) getFilterList() else filters).let {
val body = RequestBody.create(MEDIA_TYPE, it.toString()) val body = it.toString().toRequestBody(MEDIA_TYPE)
POST("$baseUrl/api/mangas/search", headers, body) POST("$baseUrl/api/mangas/search", headers, body)
} }
} }

View File

@ -1,14 +1,16 @@
package eu.kanade.tachiyomi.extension.ar.gmanga package eu.kanade.tachiyomi.extension.ar.gmanga
import android.annotation.SuppressLint import android.annotation.SuppressLint
import com.github.salomonbrys.kotson.addAll
import com.github.salomonbrys.kotson.addProperty
import com.google.gson.JsonArray
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import java.lang.Exception import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonObjectBuilder
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import kotlinx.serialization.json.putJsonObject
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -35,139 +37,90 @@ class GmangaFilters() {
val dateRangeFilter = filters.findInstance<DateRangeFilter>()!! val dateRangeFilter = filters.findInstance<DateRangeFilter>()!!
val categoryFilter = filters.findInstance<CategoryFilter>()!! val categoryFilter = filters.findInstance<CategoryFilter>()!!
return JsonObject().apply { return buildJsonObject {
oneShotFilter.state.first().let { oneShotFilter.state.first().let {
when { when {
it.isIncluded() -> addProperty("oneshot", true) it.isIncluded() -> put("oneshot", true)
it.isExcluded() -> addProperty("oneshot", false) it.isExcluded() -> put("oneshot", false)
else -> addProperty("oneshot", JsonNull.INSTANCE) else -> put("oneshot", JsonNull)
} }
} }
addProperty("title", query) put("title", query)
addProperty("page", page) put("page", page)
addProperty( putJsonObject("manga_types") {
"manga_types", putJsonArray("include") {
JsonObject().apply { mangaTypeFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) }
addProperty(
"include",
JsonArray().apply {
addAll(mangaTypeFilter.state.filter { it.isIncluded() }.map { it.id })
}
)
addProperty(
"exclude",
JsonArray().apply {
addAll(mangaTypeFilter.state.filter { it.isExcluded() }.map { it.id })
}
)
} }
)
addProperty(
"story_status",
JsonObject().apply {
addProperty( putJsonArray("exclude") {
"include", mangaTypeFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) }
JsonArray().apply {
addAll(storyStatusFilter.state.filter { it.isIncluded() }.map { it.id })
}
)
addProperty(
"exclude",
JsonArray().apply {
addAll(storyStatusFilter.state.filter { it.isExcluded() }.map { it.id })
}
)
} }
) }
addProperty( putJsonObject("story_status") {
"translation_status", putJsonArray("include") {
JsonObject().apply { storyStatusFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) }
addProperty(
"include",
JsonArray().apply {
addAll(translationStatusFilter.state.filter { it.isIncluded() }.map { it.id })
}
)
addProperty(
"exclude",
JsonArray().apply {
addAll(translationStatusFilter.state.filter { it.isExcluded() }.map { it.id })
}
)
} }
)
addProperty(
"categories",
JsonObject().apply {
addProperty( putJsonArray("exclude") {
"include", storyStatusFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) }
JsonArray().apply {
add(JsonNull.INSTANCE) // always included, maybe to avoid shifting index in the backend
addAll(categoryFilter.state.filter { it.isIncluded() }.map { it.id })
}
)
addProperty(
"exclude",
JsonArray().apply {
addAll(categoryFilter.state.filter { it.isExcluded() }.map { it.id })
}
)
} }
) }
addProperty( putJsonObject("translation_status") {
"chapters", putJsonArray("include") {
JsonObject().apply { translationStatusFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) }
addPropertyFromValidatingTextFilter(
chapterCountFilter.state.first {
it.id == FILTER_ID_MIN_CHAPTER_COUNT
},
"min",
ERROR_INVALID_MIN_CHAPTER_COUNT,
""
)
addPropertyFromValidatingTextFilter(
chapterCountFilter.state.first {
it.id == FILTER_ID_MAX_CHAPTER_COUNT
},
"max",
ERROR_INVALID_MAX_CHAPTER_COUNT,
""
)
} }
)
addProperty(
"dates",
JsonObject().apply {
addPropertyFromValidatingTextFilter( putJsonArray("exclude") {
dateRangeFilter.state.first { translationStatusFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) }
it.id == FILTER_ID_START_DATE
},
"start",
ERROR_INVALID_START_DATE
)
addPropertyFromValidatingTextFilter(
dateRangeFilter.state.first {
it.id == FILTER_ID_END_DATE
},
"end",
ERROR_INVALID_END_DATE
)
} }
) }
putJsonObject("categories") {
putJsonArray("include") {
add(JsonNull) // always included, maybe to avoid shifting index in the backend
categoryFilter.state.filter { it.isIncluded() }.map { it.id }.forEach { add(it) }
}
putJsonArray("exclude") {
categoryFilter.state.filter { it.isExcluded() }.map { it.id }.forEach { add(it) }
}
}
putJsonObject("chapters") {
putFromValidatingTextFilter(
chapterCountFilter.state.first {
it.id == FILTER_ID_MIN_CHAPTER_COUNT
},
"min",
ERROR_INVALID_MIN_CHAPTER_COUNT,
""
)
putFromValidatingTextFilter(
chapterCountFilter.state.first {
it.id == FILTER_ID_MAX_CHAPTER_COUNT
},
"max",
ERROR_INVALID_MAX_CHAPTER_COUNT,
""
)
}
putJsonObject("dates") {
putFromValidatingTextFilter(
dateRangeFilter.state.first {
it.id == FILTER_ID_START_DATE
},
"start",
ERROR_INVALID_START_DATE
)
putFromValidatingTextFilter(
dateRangeFilter.state.first {
it.id == FILTER_ID_END_DATE
},
"end",
ERROR_INVALID_END_DATE
)
}
} }
} }
@ -298,7 +251,7 @@ class GmangaFilters() {
} }
} }
private fun JsonObject.addPropertyFromValidatingTextFilter( private fun JsonObjectBuilder.putFromValidatingTextFilter(
filter: ValidatingTextFilter, filter: ValidatingTextFilter,
property: String, property: String,
invalidErrorMessage: String, invalidErrorMessage: String,
@ -307,9 +260,9 @@ class GmangaFilters() {
filter.let { filter.let {
when { when {
it.state == "" -> if (default == null) { it.state == "" -> if (default == null) {
addProperty(property, JsonNull.INSTANCE) put(property, JsonNull)
} else addProperty(property, default) } else put(property, default)
it.isValid() -> addProperty(property, it.state) it.isValid() -> put(property, it.state)
else -> throw Exception(invalidErrorMessage) else -> throw Exception(invalidErrorMessage)
} }
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'Manga Tube' extName = 'Manga Tube'

View File

@ -1,10 +1,5 @@
package eu.kanade.tachiyomi.extension.de.mangatube package eu.kanade.tachiyomi.extension.de.mangatube
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
@ -14,14 +9,22 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -45,7 +48,7 @@ class MangaTube : ParsedHttpSource() {
private val xhrHeaders: Headers = headersBuilder().add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").build() private val xhrHeaders: Headers = headersBuilder().add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8").build()
private val gson by lazy { Gson() } private val json: Json by injectLazy()
// Popular // Popular
@ -59,21 +62,21 @@ class MangaTube : ParsedHttpSource() {
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
val rbodyContent = "action=load_series_list_entries&parameter%5Bpage%5D=$page&parameter%5Bletter%5D=&parameter%5Bsortby%5D=popularity&parameter%5Border%5D=asc" val rbodyContent = "action=load_series_list_entries&parameter%5Bpage%5D=$page&parameter%5Bletter%5D=&parameter%5Bsortby%5D=popularity&parameter%5Border%5D=asc"
return POST("$baseUrl/ajax", xhrHeaders, RequestBody.create(null, rbodyContent)) return POST("$baseUrl/ajax", xhrHeaders, rbodyContent.toRequestBody(null))
} }
// popular uses "success" as a key, search uses "suggestions" // popular uses "success" as a key, search uses "suggestions"
// for future reference: if adding filters, advanced search might use a different key // for future reference: if adding filters, advanced search might use a different key
private fun parseMangaFromJson(response: Response, hasNextPage: Boolean): MangasPage { private fun parseMangaFromJson(response: Response, hasNextPage: Boolean): MangasPage {
var titleKey = "manga_title" var titleKey = "manga_title"
val mangas = gson.fromJson<JsonObject>(response.body!!.string()) val mangas = json.decodeFromString<JsonObject>(response.body!!.string())
.let { it["success"] ?: it["suggestions"].also { titleKey = "value" } } .let { it["success"] ?: it["suggestions"].also { titleKey = "value" } }!!
.asJsonArray .jsonArray
.map { json -> .map { json ->
SManga.create().apply { SManga.create().apply {
title = json[titleKey].asString title = json.jsonObject[titleKey]!!.jsonPrimitive.content
url = "/series/${json["manga_slug"].asString}" url = "/series/${json.jsonObject["manga_slug"]!!.jsonPrimitive.content}"
thumbnail_url = json["covers"][0]["img_name"].asString thumbnail_url = json.jsonObject["covers"]!!.jsonArray[0].jsonObject["img_name"]!!.jsonPrimitive.content
} }
} }
return MangasPage(mangas, hasNextPage) return MangasPage(mangas, hasNextPage)
@ -109,7 +112,7 @@ class MangaTube : ParsedHttpSource() {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val rbodyContent = "action=search_query&parameter%5Bquery%5D=$query" val rbodyContent = "action=search_query&parameter%5Bquery%5D=$query"
return POST("$baseUrl/ajax", xhrHeaders, RequestBody.create(null, rbodyContent)) return POST("$baseUrl/ajax", xhrHeaders, rbodyContent.toRequestBody(null))
} }
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
@ -173,8 +176,8 @@ class MangaTube : ParsedHttpSource() {
val jsonArray = Regex("""pages: (\[.*]),""").find(script)?.groupValues?.get(1) val jsonArray = Regex("""pages: (\[.*]),""").find(script)?.groupValues?.get(1)
?: throw Exception("Couldn't find JSON array") ?: throw Exception("Couldn't find JSON array")
return gson.fromJson<JsonArray>(jsonArray).mapIndexed { i, json -> return json.decodeFromString<JsonArray>(jsonArray).mapIndexed { i, json ->
Page(i, "", imagePath + json.asJsonObject["file_name"].asString) Page(i, "", imagePath + json.jsonObject["file_name"]!!.jsonPrimitive.content)
} }
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'Mangahub' extName = 'Mangahub'

View File

@ -1,11 +1,5 @@
package eu.kanade.tachiyomi.extension.en.mangahub package eu.kanade.tachiyomi.extension.en.mangahub
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.keys
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
@ -14,12 +8,18 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.net.URL import java.net.URL
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -156,21 +156,26 @@ class Mangahub : ParsedHttpSource() {
val slug = chapter.url.substringAfter("chapter/").substringBefore("/") val slug = chapter.url.substringAfter("chapter/").substringBefore("/")
val number = chapter.url.substringAfter("chapter-").removeSuffix("/") val number = chapter.url.substringAfter("chapter-").removeSuffix("/")
val body = RequestBody.create(null, "{\"query\":\"{chapter(x:m01,slug:\\\"$slug\\\",number:$number){id,title,mangaID,number,slug,date,pages,noAd,manga{id,title,slug,mainSlug,author,isWebtoon,isYaoi,isPorn,isSoftPorn,unauthFile,isLicensed}}}\"}") val body =
"{\"query\":\"{chapter(x:m01,slug:\\\"$slug\\\",number:$number){id,title,mangaID,number,slug,date,pages,noAd,manga{id,title,slug,mainSlug,author,isWebtoon,isYaoi,isPorn,isSoftPorn,unauthFile,isLicensed}}}\"}".toRequestBody(
null
)
return POST("https://api.mghubcdn.com/graphql", jsonHeaders, body) return POST("https://api.mghubcdn.com/graphql", jsonHeaders, body)
} }
private val gson = Gson() private val json: Json by injectLazy()
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val cdn = "https://img.mghubcdn.com/file/imghub" val cdn = "https://img.mghubcdn.com/file/imghub"
return gson.fromJson<JsonObject>(response.body!!.string())["data"]["chapter"]["pages"].string return json.decodeFromString<JsonObject>(response.body!!.string())["data"]!!
.jsonObject["chapter"]!!
.jsonObject["pages"]!!.jsonPrimitive.content
.removeSurrounding("\"").replace("\\", "") .removeSurrounding("\"").replace("\\", "")
.let { cleaned -> .let { cleaned ->
val jsonObject = gson.fromJson<JsonObject>(cleaned) val jsonObject = json.decodeFromString<JsonObject>(cleaned)
jsonObject.keys().map { key -> jsonObject[key].string } jsonObject.keys.map { key -> jsonObject[key]!!.jsonPrimitive.content }
} }
.mapIndexed { i, tail -> Page(i, "", "$cdn/$tail") } .mapIndexed { i, tail -> Page(i, "", "$cdn/$tail") }
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'MangaRock.es' extName = 'MangaRock.es'

View File

@ -1,8 +1,5 @@
package eu.kanade.tachiyomi.extension.en.mangarockes package eu.kanade.tachiyomi.extension.en.mangarockes
import com.github.salomonbrys.kotson.fromJson
import com.google.gson.Gson
import com.google.gson.JsonArray
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -10,6 +7,11 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -18,6 +20,7 @@ import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.ResponseBody.Companion.toResponseBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -160,13 +163,13 @@ class MangaRockEs : ParsedHttpSource() {
// Pages // Pages
private val gson by lazy { Gson() } private val json: Json by injectLazy()
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val responseString = response.body!!.string() val responseString = response.body!!.string()
return Regex("""mangaData = (\[.*]);""", RegexOption.IGNORE_CASE).find(responseString)?.groupValues?.get(1)?.let { array -> return Regex("""mangaData = (\[.*]);""", RegexOption.IGNORE_CASE).find(responseString)?.groupValues?.get(1)?.let { array ->
gson.fromJson<JsonArray>(array) json.decodeFromString<JsonArray>(array)
.mapIndexed { i, jsonElement -> Page(i, "", jsonElement.asJsonObject["url"].asString) } .mapIndexed { i, jsonElement -> Page(i, "", jsonElement.jsonObject["url"]!!.jsonPrimitive.content) }
} }
?: Regex("""getManga\(\d+, '(http.*)',""").findAll(responseString).toList() ?: Regex("""getManga\(\d+, '(http.*)',""").findAll(responseString).toList()
.mapIndexed { i, mr -> Page(i, "", mr.groupValues[1]) } .mapIndexed { i, mr -> Page(i, "", mr.groupValues[1]) }

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -69,7 +69,7 @@ class ManhuaManga : ParsedHttpSource() {
protected fun getXhrChapters(mangaId: String): Document { protected fun getXhrChapters(mangaId: String): Document {
val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8") val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
.build() .build()
val body = RequestBody.create(null, "action=tw_ajax&type=list_chap&id=$mangaId") val body = "action=tw_ajax&type=list_chap&id=$mangaId".toRequestBody(null)
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup() return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup()
} }

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -69,7 +69,7 @@ class ManhwaManga : ParsedHttpSource() {
protected fun getXhrChapters(mangaId: String): Document { protected fun getXhrChapters(mangaId: String): Document {
val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8") val xhrHeaders = headersBuilder().add("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
.build() .build()
val body = RequestBody.create(null, "action=tw_ajax&type=list_chap&id=$mangaId") val body = "action=tw_ajax&type=list_chap&id=$mangaId".toRequestBody(null)
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup() return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, body)).execute().asJsoup()
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'ReadManhwa' extName = 'ReadManhwa'

View File

@ -2,14 +2,6 @@ package eu.kanade.tachiyomi.extension.en.readmanhwa
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
@ -20,6 +12,15 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -28,6 +29,7 @@ import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -54,20 +56,20 @@ class ReadManhwa : ConfigurableSource, HttpSource() {
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
private val gson = Gson() private val json: Json by injectLazy()
private fun parseMangaFromJson(response: Response): MangasPage { private fun parseMangaFromJson(response: Response): MangasPage {
val jsonObject = gson.fromJson<JsonObject>(response.body!!.string()) val jsonObject = json.decodeFromString<JsonObject>(response.body!!.string())
val mangas = jsonObject["data"].asJsonArray.map { json -> val mangas = jsonObject["data"]!!.jsonArray.map { json ->
SManga.create().apply { SManga.create().apply {
title = json["title"].string title = json.jsonObject["title"]!!.jsonPrimitive.content
thumbnail_url = json["image_url"].string thumbnail_url = json.jsonObject["image_url"]!!.jsonPrimitive.content
url = json["slug"].string url = json.jsonObject["slug"]!!.jsonPrimitive.content
} }
} }
return MangasPage(mangas, jsonObject["current_page"].int < jsonObject["last_page"].int) return MangasPage(mangas, jsonObject["current_page"]!!.jsonPrimitive.int < jsonObject["last_page"]!!.jsonPrimitive.int)
} }
private fun getMangaUrl(url: String): String { private fun getMangaUrl(url: String): String {
return url.toHttpUrlOrNull()!!.newBuilder() return url.toHttpUrlOrNull()!!.newBuilder()
@ -159,15 +161,15 @@ class ReadManhwa : ConfigurableSource, HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val jsonObject = gson.fromJson<JsonObject>(response.body!!.string()) val jsonObject = json.decodeFromString<JsonObject>(response.body!!.string())
return SManga.create().apply { return SManga.create().apply {
description = jsonObject["description"].nullString description = jsonObject["description"]!!.jsonPrimitive.contentOrNull
status = jsonObject["status"].nullString.toStatus() status = jsonObject["status"]!!.jsonPrimitive.contentOrNull.toStatus()
thumbnail_url = jsonObject["image_url"].nullString thumbnail_url = jsonObject["image_url"]!!.jsonPrimitive.contentOrNull
genre = try { jsonObject["tags"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } genre = try { jsonObject["tags"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content } } catch (_: Exception) { null }
artist = try { jsonObject["artists"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } artist = try { jsonObject["artists"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content } } catch (_: Exception) { null }
author = try { jsonObject["authors"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } author = try { jsonObject["authors"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content } } catch (_: Exception) { null }
} }
} }
@ -193,11 +195,11 @@ class ReadManhwa : ConfigurableSource, HttpSource() {
} }
private fun chapterListParse(response: Response, titleSlug: String): List<SChapter> { private fun chapterListParse(response: Response, titleSlug: String): List<SChapter> {
return gson.fromJson<JsonArray>(response.body!!.string()).map { json -> return json.decodeFromString<JsonArray>(response.body!!.string()).map { json ->
SChapter.create().apply { SChapter.create().apply {
name = json["name"].string name = json.jsonObject["name"]!!.jsonPrimitive.content
url = "$titleSlug/${json["slug"].string}" url = "$titleSlug/${json.jsonObject["slug"]!!.jsonPrimitive.content}"
date_upload = json["added_at"].string.let { dateString -> date_upload = json.jsonObject["added_at"]!!.jsonPrimitive.content.let { dateString ->
if (dateString.contains("ago")) { if (dateString.contains("ago")) {
val trimmedDate = dateString.substringBefore(" ago").removeSuffix("s").split(" ") val trimmedDate = dateString.substringBefore(" ago").removeSuffix("s").split(" ")
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
@ -225,8 +227,8 @@ class ReadManhwa : ConfigurableSource, HttpSource() {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
return gson.fromJson<JsonObject>(response.body!!.string())["images"].asJsonArray.mapIndexed { i, json -> return json.decodeFromString<JsonObject>(response.body!!.string())["images"]!!.jsonArray.mapIndexed { i, json ->
Page(i, "", json["source_url"].string) Page(i, "", json.jsonObject["source_url"]!!.jsonPrimitive.content)
} }
} }

View File

@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -131,7 +131,7 @@ class DoujinYang : ParsedHttpSource() {
return POST( return POST(
baseUrl + chapter.url, baseUrl + chapter.url,
headersBuilder().add("Content-Type", "application/x-www-form-urlencoded").build(), headersBuilder().add("Content-Type", "application/x-www-form-urlencoded").build(),
RequestBody.create(null, "info") "info".toRequestBody(null)
) )
} }

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.es.kumanga package eu.kanade.tachiyomi.extension.es.kumanga
import android.util.Base64 import android.util.Base64
import com.github.salomonbrys.kotson.jsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'ComX' extName = 'ComX'

View File

@ -1,9 +1,5 @@
package eu.kanade.tachiyomi.extension.ru.comx package eu.kanade.tachiyomi.extension.ru.comx
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.obj
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
@ -15,6 +11,12 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
@ -23,12 +25,16 @@ import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class ComX : ParsedHttpSource() { class ComX : ParsedHttpSource() {
private val json: Json by injectLazy()
override val name = "Com-x" override val name = "Com-x"
override val baseUrl = "https://com-x.life" override val baseUrl = "https://com-x.life"
@ -186,13 +192,13 @@ class ComX : ParsedHttpSource() {
.substringBefore("</script>") .substringBefore("</script>")
.substringBeforeLast(";") .substringBeforeLast(";")
val data = JsonParser.parseString(dataStr).obj val data = json.decodeFromString<JsonObject>(dataStr)
val chaptersList = data["chapters"].nullArray val chaptersList = data["chapters"]?.jsonArray
val chapters: List<SChapter>? = chaptersList?.map { val chapters: List<SChapter>? = chaptersList?.map {
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.name = it["title"].asString chapter.name = it.jsonObject["title"]!!.jsonPrimitive.content
chapter.date_upload = parseDate(it["date"].asString) chapter.date_upload = parseDate(it.jsonObject["date"]!!.jsonPrimitive.content)
chapter.setUrlWithoutDomain("/readcomix/" + data["news_id"] + "/" + it["id"] + ".html") chapter.setUrlWithoutDomain("/readcomix/" + data["news_id"] + "/" + it.jsonObject["id"]!!.jsonPrimitive.content + ".html")
chapter chapter
} }
return chapters ?: emptyList() return chapters ?: emptyList()

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'MangaLib' extName = 'MangaLib'

View File

@ -5,18 +5,6 @@ import android.content.SharedPreferences
import android.widget.Toast import android.widget.Toast
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.nullArray
import com.github.salomonbrys.kotson.nullInt
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.obj
import com.github.salomonbrys.kotson.string
import com.github.salomonbrys.kotson.toMap
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
@ -30,6 +18,17 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -39,12 +38,15 @@ import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class LibManga : ConfigurableSource, HttpSource() { class LibManga : ConfigurableSource, HttpSource() {
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000) Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000)
} }
@ -72,8 +74,6 @@ class LibManga : ConfigurableSource, HttpSource() {
add("Referer", baseUrl) add("Referer", baseUrl)
} }
private val jsonParser = JsonParser()
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers) override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
private val latestUpdatesSelector = "div.updates__item" private val latestUpdatesSelector = "div.updates__item"
@ -135,21 +135,21 @@ class LibManga : ConfigurableSource, HttpSource() {
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val resBody = response.body!!.string() val resBody = response.body!!.string()
val result = jsonParser.parse(resBody).obj val result = json.decodeFromString<JsonObject>(resBody)
val items = result["items"] val items = result["items"]!!.jsonObject
val popularMangas = items["data"].nullArray?.map { popularMangaFromElement(it) } val popularMangas = items["data"]?.jsonArray?.map { popularMangaFromElement(it) }
if (popularMangas != null) { if (popularMangas != null) {
val hasNextPage = items["next_page_url"].nullString != null val hasNextPage = items["next_page_url"]?.jsonPrimitive?.contentOrNull != null
return MangasPage(popularMangas, hasNextPage) return MangasPage(popularMangas, hasNextPage)
} }
return MangasPage(emptyList(), false) return MangasPage(emptyList(), false)
} }
private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply { private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply {
val slug = el["slug"].string val slug = el.jsonObject["slug"]!!.jsonPrimitive.content
val cover = el["cover"].string val cover = el.jsonObject["cover"]!!.jsonPrimitive.content
title = if (titleLanguage.equals("rus")) el["rus_name"].string else el["name"].string title = if (titleLanguage.equals("rus")) el.jsonObject["rus_name"]!!.jsonPrimitive.content else el.jsonObject["name"]!!.jsonPrimitive.content
thumbnail_url = "$COVER_URL/uploads/cover/$slug/cover/${cover}_250x350.jpg" thumbnail_url = "$COVER_URL/uploads/cover/$slug/cover/${cover}_250x350.jpg"
url = "/$slug" url = "/$slug"
} }
@ -232,17 +232,17 @@ class LibManga : ConfigurableSource, HttpSource() {
.substringBefore("window._SITE_COLOR_") .substringBefore("window._SITE_COLOR_")
.substringBeforeLast(";") .substringBeforeLast(";")
val data = jsonParser.parse(dataStr).obj val data = json.decodeFromString<JsonObject>(dataStr)
val chaptersList = data["chapters"]["list"].nullArray val chaptersList = data["chapters"]!!.jsonObject["list"]?.jsonArray
val slug = data["manga"]["slug"].string val slug = data["manga"]!!.jsonObject["slug"]!!.jsonPrimitive.content
val branches = data["chapters"]["branches"].array.reversed() val branches = data["chapters"]!!.jsonObject["branches"]!!.jsonArray.reversed()
val sortingList = preferences.getString(SORTING_PREF, "ms_mixing") val sortingList = preferences.getString(SORTING_PREF, "ms_mixing")
val chapters: List<SChapter>? = if (branches.isNotEmpty() && !sortingList.equals("ms_mixing")) { val chapters: List<SChapter>? = if (branches.isNotEmpty() && !sortingList.equals("ms_mixing")) {
sortChaptersByTranslator(sortingList, chaptersList, slug, branches) sortChaptersByTranslator(sortingList, chaptersList, slug, branches)
} else { } else {
chaptersList chaptersList
?.filter { it["status"].nullInt != 2 } ?.filter { it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
?.map { chapterFromElement(it, sortingList, slug) } ?.map { chapterFromElement(it, sortingList, slug) }
} }
@ -256,9 +256,9 @@ class LibManga : ConfigurableSource, HttpSource() {
"ms_combining" -> { "ms_combining" -> {
val tempChaptersList = mutableListOf<SChapter>() val tempChaptersList = mutableListOf<SChapter>()
for (currentBranch in branches.withIndex()) { for (currentBranch in branches.withIndex()) {
val teamId = branches[currentBranch.index]["id"].int val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
chapters = chaptersList chapters = chaptersList
?.filter { it["branch_id"].nullInt == teamId && it["status"].nullInt != 2 } ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) } ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
chapters?.let { tempChaptersList.addAll(it) } chapters?.let { tempChaptersList.addAll(it) }
} }
@ -267,26 +267,26 @@ class LibManga : ConfigurableSource, HttpSource() {
"ms_largest" -> { "ms_largest" -> {
val sizesChaptersLists = mutableListOf<Int>() val sizesChaptersLists = mutableListOf<Int>()
for (currentBranch in branches.withIndex()) { for (currentBranch in branches.withIndex()) {
val teamId = branches[currentBranch.index]["id"].int val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
val chapterSize = chaptersList val chapterSize = chaptersList
?.filter { it["branch_id"].nullInt == teamId }!!.size ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId }!!.size
sizesChaptersLists.add(chapterSize) sizesChaptersLists.add(chapterSize)
} }
val max = sizesChaptersLists.indexOfFirst { it == sizesChaptersLists.maxOrNull() ?: 0 } val max = sizesChaptersLists.indexOfFirst { it == sizesChaptersLists.maxOrNull() ?: 0 }
val teamId = branches[max]["id"].int val teamId = branches[max].jsonObject["id"]!!.jsonPrimitive.int
chapters = chaptersList chapters = chaptersList
?.filter { it["branch_id"].nullInt == teamId && it["status"].nullInt != 2 } ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) } ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
} }
"ms_active" -> { "ms_active" -> {
for (currentBranch in branches.withIndex()) { for (currentBranch in branches.withIndex()) {
val teams = branches[currentBranch.index]["teams"].array val teams = branches[currentBranch.index].jsonObject["teams"]!!.jsonArray
for (currentTeam in teams.withIndex()) { for (currentTeam in teams.withIndex()) {
if (teams[currentTeam.index]["is_active"].int == 1) { if (teams[currentTeam.index].jsonObject["is_active"]!!.jsonPrimitive.int == 1) {
val teamId = branches[currentBranch.index]["id"].int val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
chapters = chaptersList chapters = chaptersList
?.filter { it["branch_id"].nullInt == teamId && it["status"].nullInt != 2 } ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) } ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
break break
} }
@ -303,23 +303,23 @@ class LibManga : ConfigurableSource, HttpSource() {
(chapterItem: JsonElement, sortingList: String?, slug: String, teamIdParam: Int? = null, branches: List<JsonElement>? = null): SChapter { (chapterItem: JsonElement, sortingList: String?, slug: String, teamIdParam: Int? = null, branches: List<JsonElement>? = null): SChapter {
val chapter = SChapter.create() val chapter = SChapter.create()
val volume = chapterItem["chapter_volume"].int val volume = chapterItem.jsonObject["chapter_volume"]!!.jsonPrimitive.int
val number = chapterItem["chapter_number"].string val number = chapterItem.jsonObject["chapter_number"]!!.jsonPrimitive.content
val teamId = if (teamIdParam != null) "?bid=$teamIdParam" else "" val teamId = if (teamIdParam != null) "?bid=$teamIdParam" else ""
val url = "$baseUrl/$slug/v$volume/c$number$teamId" val url = "$baseUrl/$slug/v$volume/c$number$teamId"
chapter.setUrlWithoutDomain(url) chapter.setUrlWithoutDomain(url)
val nameChapter = chapterItem["chapter_name"].nullString val nameChapter = chapterItem.jsonObject["chapter_name"]?.jsonPrimitive?.contentOrNull
val fullNameChapter = "Том $volume. Глава $number" val fullNameChapter = "Том $volume. Глава $number"
if (!sortingList.equals("ms_mixing")) { if (!sortingList.equals("ms_mixing")) {
chapter.scanlator = branches?.let { getScanlatorTeamName(it, chapterItem) } ?: chapterItem["username"].string chapter.scanlator = branches?.let { getScanlatorTeamName(it, chapterItem) } ?: chapterItem.jsonObject["username"]!!.jsonPrimitive.content
} }
chapter.name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter" chapter.name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter"
chapter.date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.US) chapter.date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.US)
.parse(chapterItem["chapter_created_at"].string.substringBefore(" "))?.time ?: 0L .parse(chapterItem.jsonObject["chapter_created_at"]!!.jsonPrimitive.content.substringBefore(" "))?.time ?: 0L
return chapter return chapter
} }
@ -327,15 +327,15 @@ class LibManga : ConfigurableSource, HttpSource() {
private fun getScanlatorTeamName(branches: List<JsonElement>, chapterItem: JsonElement): String? { private fun getScanlatorTeamName(branches: List<JsonElement>, chapterItem: JsonElement): String? {
var scanlatorData: String? = null var scanlatorData: String? = null
for (currentBranch in branches.withIndex()) { for (currentBranch in branches.withIndex()) {
val branch = branches[currentBranch.index] val branch = branches[currentBranch.index].jsonObject
val teams = branch["teams"].array val teams = branch["teams"]!!.jsonArray
if (chapterItem["branch_id"].int == branch["id"].int) { if (chapterItem.jsonObject["branch_id"]!!.jsonPrimitive.int == branch["id"]!!.jsonPrimitive.int) {
for (currentTeam in teams.withIndex()) { for (currentTeam in teams.withIndex()) {
val team = teams[currentTeam.index] val team = teams[currentTeam.index].jsonObject
val scanlatorId = chapterItem["chapter_scanlator_id"].int val scanlatorId = chapterItem.jsonObject["chapter_scanlator_id"]!!.jsonPrimitive.int
scanlatorData = if ((scanlatorId == team["id"].int) || scanlatorData = if ((scanlatorId == team.jsonObject["id"]!!.jsonPrimitive.int) ||
(scanlatorId == 0 && team["is_active"].int == 1) (scanlatorId == 0 && team["is_active"]!!.jsonPrimitive.int == 1)
) team["name"].string else branch["teams"][0]["name"].string ) team["name"]!!.jsonPrimitive.content else branch["teams"]!!.jsonArray[0].jsonObject["name"]!!.jsonPrimitive.content
} }
} }
} }
@ -371,11 +371,11 @@ class LibManga : ConfigurableSource, HttpSource() {
.split(";") .split(";")
.first() .first()
val chapInfoJson = jsonParser.parse(chapInfo).obj val chapInfoJson = json.decodeFromString<JsonObject>(chapInfo)
val servers = chapInfoJson["servers"].asJsonObject.toMap() val servers = chapInfoJson["servers"]!!.jsonObject.toMap()
val defaultServer: String = chapInfoJson["img"]["server"].string val defaultServer: String = chapInfoJson["img"]!!.jsonObject["server"]!!.jsonPrimitive.content
val autoServer = setOf("secondary", "fourth", defaultServer, "compress") val autoServer = setOf("secondary", "fourth", defaultServer, "compress")
val imgUrl: String = chapInfoJson["img"]["url"].string val imgUrl: String = chapInfoJson["img"]!!.jsonObject["url"]!!.jsonPrimitive.content
val serverToUse = when (this.server) { val serverToUse = when (this.server) {
null -> autoServer null -> autoServer
@ -392,15 +392,15 @@ class LibManga : ConfigurableSource, HttpSource() {
.removePrefix("window.__pg = ") .removePrefix("window.__pg = ")
.removeSuffix(";") .removeSuffix(";")
val pagesJson = jsonParser.parse(pagesArr).array val pagesJson = json.decodeFromString<JsonArray>(pagesArr)
val pages = mutableListOf<Page>() val pages = mutableListOf<Page>()
pagesJson.forEach { page -> pagesJson.forEach { page ->
val keys = servers.keys.filter { serverToUse.indexOf(it) >= 0 }.sortedBy { serverToUse.indexOf(it) } val keys = servers.keys.filter { serverToUse.indexOf(it) >= 0 }.sortedBy { serverToUse.indexOf(it) }
val serversUrls = keys.map { val serversUrls = keys.map {
servers[it]?.string + imgUrl + page["u"].string servers[it]?.jsonPrimitive?.contentOrNull + imgUrl + page.jsonObject["u"]!!.jsonPrimitive.content
}.joinToString(separator = ",,") { it } }.joinToString(separator = ",,") { it }
pages.add(Page(page["p"].int, serversUrls)) pages.add(Page(page.jsonObject["p"]!!.jsonPrimitive.int, serversUrls))
} }
return pages return pages
@ -523,7 +523,7 @@ class LibManga : ConfigurableSource, HttpSource() {
) )
.execute().body!!.string() .execute().body!!.string()
val jsonList = jsonParser.parse(popup).array val jsonList = json.decodeFromString<JsonArray>(popup)
jsonList.forEach { jsonList.forEach {
mangas.add(popularMangaFromElement(it)) mangas.add(popularMangaFromElement(it))
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'Mangaonlinebiz' extName = 'Mangaonlinebiz'

View File

@ -1,10 +1,5 @@
package eu.kanade.tachiyomi.extension.ru.mangaonlinebiz package eu.kanade.tachiyomi.extension.ru.mangaonlinebiz
import com.github.salomonbrys.kotson.float
import com.github.salomonbrys.kotson.forEach
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -13,15 +8,27 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.float
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class MangaOnlineBiz : ParsedHttpSource() { class MangaOnlineBiz : ParsedHttpSource() {
private val json: Json by injectLazy()
override val name = "MangaOnlineBiz" override val name = "MangaOnlineBiz"
override val baseUrl = "https://manga-online.biz" override val baseUrl = "https://manga-online.biz"
@ -68,15 +75,14 @@ class MangaOnlineBiz : ParsedHttpSource() {
return popularMangaParse(response) return popularMangaParse(response)
} }
val jsonData = response.body!!.string() val jsonData = response.body!!.string()
val json = JsonParser().parse(jsonData).asJsonObject val results = json.decodeFromString<JsonObject>(jsonData)["results"]!!.jsonArray
val results = json.getAsJsonArray("results")
val mangas = mutableListOf<SManga>() val mangas = mutableListOf<SManga>()
results.forEach { results.forEach {
val element = it.asJsonObject val element = it.jsonObject
val manga = SManga.create() val manga = SManga.create()
manga.setUrlWithoutDomain(element.get("url").string) manga.setUrlWithoutDomain(element["url"]!!.jsonPrimitive.content)
manga.title = element.get("title").string.split("/").first() manga.title = element["title"]!!.jsonPrimitive.content.split("/").first()
val image = element.get("image").string val image = element["image"]!!.jsonPrimitive.content
if (image.startsWith("http")) { if (image.startsWith("http")) {
manga.thumbnail_url = image manga.thumbnail_url = image
} else { } else {
@ -130,10 +136,9 @@ class MangaOnlineBiz : ParsedHttpSource() {
val jsonData = html.split("App.Collection.MangaChapter(").last().split("]);").first() + "]" val jsonData = html.split("App.Collection.MangaChapter(").last().split("]);").first() + "]"
val mangaName = html.split("mangaName: '").last().split("' });").first() val mangaName = html.split("mangaName: '").last().split("' });").first()
val json = JsonParser().parse(jsonData).asJsonArray
val chapterList = mutableListOf<SChapter>() val chapterList = mutableListOf<SChapter>()
json.forEach { json.decodeFromString<JsonArray>(jsonData).forEach {
chapterList.add(chapterFromElement(mangaName, it.asJsonObject)) chapterList.add(chapterFromElement(mangaName, it.jsonObject))
} }
return chapterList return chapterList
} }
@ -142,22 +147,22 @@ class MangaOnlineBiz : ParsedHttpSource() {
private fun chapterFromElement(mangaName: String, element: JsonObject): SChapter { private fun chapterFromElement(mangaName: String, element: JsonObject): SChapter {
val chapter = SChapter.create() val chapter = SChapter.create()
chapter.setUrlWithoutDomain("/$mangaName/${element.get("volume").string}/${element.get("number").string})/1") chapter.setUrlWithoutDomain("/$mangaName/${element["volume"]!!.jsonPrimitive.content}/${element["number"]!!.jsonPrimitive.content})/1")
chapter.name = "Том ${element.get("volume").string} - Глава ${element.get("number").string} ${element.get("title").string}" chapter.name = "Том ${element["volume"]!!.jsonPrimitive.content} - Глава ${element["number"]!!.jsonPrimitive.content} ${element["title"]!!.jsonPrimitive.content}"
chapter.chapter_number = element.get("number").float chapter.chapter_number = element["number"]!!.jsonPrimitive.float
chapter.date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(element.get("date").string)?.time ?: 0L chapter.date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(element["date"]!!.jsonPrimitive.content)?.time ?: 0L
return chapter return chapter
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val html = response.body!!.string() val html = response.body!!.string()
val jsonData = html.split("new App.Router.Chapter(").last().split("});").first() + "}" val jsonData = html.split("new App.Router.Chapter(").last().split("});").first() + "}"
val json = JsonParser().parse(jsonData).asJsonObject val jsonObj = json.decodeFromString<JsonObject>(jsonData)
val cdnUrl = json.get("srcBaseUrl").string val cdnUrl = jsonObj["srcBaseUrl"]!!.jsonPrimitive.content
val pages = json.get("pages").asJsonObject val pages = jsonObj["pages"]!!.jsonObject
val resPages = mutableListOf<Page>() val resPages = mutableListOf<Page>()
pages.forEach { page, jsonElement -> pages.entries.forEach { (page, jsonElement) ->
resPages.add(Page(page.toInt(), imageUrl = "$cdnUrl/${jsonElement.asJsonObject.get("src").string}")) resPages.add(Page(page.toInt(), imageUrl = "$cdnUrl/${jsonElement.jsonObject["src"]!!.jsonPrimitive.content}"))
} }
return resPages return resPages
} }

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'Risens Team' extName = 'Risens Team'

View File

@ -1,14 +1,5 @@
package eu.kanade.tachiyomi.extension.ru.risensteam package eu.kanade.tachiyomi.extension.ru.risensteam
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.string
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
@ -18,11 +9,22 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -39,7 +41,7 @@ class RisensTeam : HttpSource() {
override val versionId: Int = 2 override val versionId: Int = 2
private val gson by lazy { Gson() } private val json: Json by injectLazy()
// Popular (source only returns manga sorted by latest) // Popular (source only returns manga sorted by latest)
@ -49,16 +51,16 @@ class RisensTeam : HttpSource() {
private fun mangaFromJson(json: JsonElement): SManga { private fun mangaFromJson(json: JsonElement): SManga {
return SManga.create().apply { return SManga.create().apply {
url = "${json["id"].int}/${json["furl"].string}" url = "${json.jsonObject["id"]!!.jsonPrimitive.int}/${json.jsonObject["furl"]!!.jsonPrimitive.content}"
title = json["title"].string title = json.jsonObject["title"]!!.jsonPrimitive.content
thumbnail_url = baseUrl + json["poster"].string thumbnail_url = baseUrl + json.jsonObject["poster"]!!.jsonPrimitive.content
description = json["description"].nullString description = json.jsonObject["description"]!!.jsonPrimitive.contentOrNull
status = try { if (json["active"].int == 1) SManga.ONGOING else SManga.UNKNOWN } catch (_: Exception) { SManga.UNKNOWN } status = try { if (json.jsonObject["active"]!!.jsonPrimitive.int == 1) SManga.ONGOING else SManga.UNKNOWN } catch (_: Exception) { SManga.UNKNOWN }
} }
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val mangas = gson.fromJson<JsonArray>(response.body!!.string()) val mangas = json.decodeFromString<JsonArray>(response.body!!.string())
.map { json -> mangaFromJson(json) } .map { json -> mangaFromJson(json) }
return MangasPage(mangas, false) return MangasPage(mangas, false)
@ -98,7 +100,7 @@ class RisensTeam : HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
return mangaFromJson(gson.fromJson<JsonObject>(response.body!!.string())) return mangaFromJson(json.decodeFromString<JsonObject>(response.body!!.string()))
} }
// Chapters // Chapters
@ -106,11 +108,11 @@ class RisensTeam : HttpSource() {
override fun chapterListRequest(manga: SManga): Request = apiMangaDetailsRequest(manga) override fun chapterListRequest(manga: SManga): Request = apiMangaDetailsRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
return gson.fromJson<JsonObject>(response.body!!.string())["entities"].asJsonArray.map { json -> return json.decodeFromString<JsonObject>(response.body!!.string())["entities"]!!.jsonArray.map { json ->
SChapter.create().apply { SChapter.create().apply {
url = json["id"].int.toString() url = json.jsonObject["id"]!!.jsonPrimitive.int.toString()
name = listOfNotNull(json["label"].nullString, json["name"].nullString).joinToString(" - ") name = listOfNotNull(json.jsonObject["label"]!!.jsonPrimitive.contentOrNull, json.jsonObject["name"]!!.jsonPrimitive.contentOrNull).joinToString(" - ")
date_upload = json["updated_at"].toDate() date_upload = json.jsonObject["updated_at"]!!.toDate()
} }
} }
} }
@ -120,7 +122,7 @@ class RisensTeam : HttpSource() {
} }
private fun JsonElement.toDate(): Long { private fun JsonElement.toDate(): Long {
val date = this.nullString ?: return 0 val date = this.jsonPrimitive.contentOrNull ?: return 0
return try { return try {
simpleDateFormat.parse(date)?.time ?: 0 simpleDateFormat.parse(date)?.time ?: 0
} catch (e: ParseException) { } catch (e: ParseException) {
@ -135,8 +137,8 @@ class RisensTeam : HttpSource() {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
return gson.fromJson<JsonArray>(response.body!!.string()) return json.decodeFromString<JsonArray>(response.body!!.string())
.mapIndexed { i, json -> Page(i, "", json.string) } .mapIndexed { i, json -> Page(i, "", json.jsonPrimitive.content) }
} }
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used") override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")

View File

@ -1,24 +1,24 @@
package eu.kanade.tachiyomi.extension.th.nekopost.model package eu.kanade.tachiyomi.extension.th.nekopost.model
import com.google.gson.annotations.SerializedName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class RawProjectChapter( data class RawProjectChapter(
@SerializedName("cu_displayname") @SerialName("cu_displayname")
val cuDisplayname: String, val cuDisplayname: String,
@SerializedName("nc_chapter_id") @SerialName("nc_chapter_id")
val ncChapterId: String, val ncChapterId: String,
@SerializedName("nc_chapter_name") @SerialName("nc_chapter_name")
val ncChapterName: String, val ncChapterName: String,
@SerializedName("nc_chapter_no") @SerialName("nc_chapter_no")
val ncChapterNo: String, val ncChapterNo: String,
@SerializedName("nc_created_date") @SerialName("nc_created_date")
val ncCreatedDate: String, val ncCreatedDate: String,
@SerializedName("nc_data_file") @SerialName("nc_data_file")
val ncDataFile: String, val ncDataFile: String,
@SerializedName("nc_owner_id") @SerialName("nc_owner_id")
val ncOwnerId: String, val ncOwnerId: String,
@SerializedName("nc_provider") @SerialName("nc_provider")
val ncProvider: String val ncProvider: String
) )

View File

@ -1,38 +1,38 @@
package eu.kanade.tachiyomi.extension.th.nekopost.model package eu.kanade.tachiyomi.extension.th.nekopost.model
import com.google.gson.annotations.SerializedName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class RawProjectInfoData( data class RawProjectInfoData(
@SerializedName("artist_name") @SerialName("artist_name")
val artistName: String, val artistName: String,
@SerializedName("author_name") @SerialName("author_name")
val authorName: String, val authorName: String,
@SerializedName("np_comment") @SerialName("np_comment")
val npComment: String, val npComment: String,
@SerializedName("np_created_date") @SerialName("np_created_date")
val npCreatedDate: String, val npCreatedDate: String,
@SerializedName("np_flag_mature") @SerialName("np_flag_mature")
val npFlagMature: String, val npFlagMature: String,
@SerializedName("np_info") @SerialName("np_info")
val npInfo: String, val npInfo: String,
@SerializedName("np_licenced_by") @SerialName("np_licenced_by")
val npLicencedBy: String, val npLicencedBy: String,
@SerializedName("np_name") @SerialName("np_name")
val npName: String, val npName: String,
@SerializedName("np_name_link") @SerialName("np_name_link")
val npNameLink: String, val npNameLink: String,
@SerializedName("np_project_id") @SerialName("np_project_id")
val npProjectId: String, val npProjectId: String,
@SerializedName("np_status") @SerialName("np_status")
val npStatus: String, val npStatus: String,
@SerializedName("np_type") @SerialName("np_type")
val npType: String, val npType: String,
@SerializedName("np_updated_date") @SerialName("np_updated_date")
val npUpdatedDate: String, val npUpdatedDate: String,
@SerializedName("np_view") @SerialName("np_view")
val npView: String, val npView: String,
@SerializedName("np_web") @SerialName("np_web")
val npWeb: String val npWeb: String
) )