Replace org.json with kotlinx.serialization in some sources (#9569)
* Replace org.json in MeDocTruyenTranh. * Replace org.json in Kuaikanmanhua. * Replace org.json in QiXiManhua. * Replace org.json in Desu.
This commit is contained in:
parent
d0e8f185ab
commit
fd927c254d
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Desu'
|
||||
pkgNameSuffix = 'ru.desu'
|
||||
extClass = '.Desu'
|
||||
extVersionCode = 12
|
||||
extVersionCode = 13
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -9,12 +9,20 @@ import eu.kanade.tachiyomi.source.model.Page
|
|||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.float
|
||||
import kotlinx.serialization.json.floatOrNull
|
||||
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 okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.ArrayList
|
||||
|
||||
class Desu : HttpSource() {
|
||||
|
@ -26,31 +34,34 @@ class Desu : HttpSource() {
|
|||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun headersBuilder() = Headers.Builder().apply {
|
||||
add("User-Agent", "Tachiyomi")
|
||||
add("Referer", baseUrl)
|
||||
}
|
||||
|
||||
private fun mangaPageFromJSON(json: String, next: Boolean): MangasPage {
|
||||
val arr = JSONArray(json)
|
||||
val ret = ArrayList<SManga>(arr.length())
|
||||
for (i in 0 until arr.length()) {
|
||||
val obj = arr.getJSONObject(i)
|
||||
ret.add(
|
||||
private fun mangaPageFromJSON(jsonStr: String, next: Boolean): MangasPage {
|
||||
val mangaList = json.parseToJsonElement(jsonStr).jsonArray
|
||||
.map {
|
||||
SManga.create().apply {
|
||||
mangaFromJSON(obj, false)
|
||||
mangaFromJSON(it.jsonObject, false)
|
||||
}
|
||||
)
|
||||
}
|
||||
return MangasPage(ret, next)
|
||||
}
|
||||
|
||||
return MangasPage(mangaList, next)
|
||||
}
|
||||
|
||||
private fun SManga.mangaFromJSON(obj: JSONObject, chapter: Boolean) {
|
||||
val id = obj.getInt("id")
|
||||
private fun SManga.mangaFromJSON(obj: JsonObject, chapter: Boolean) {
|
||||
val id = obj["id"]!!.jsonPrimitive.int
|
||||
|
||||
url = "/$id"
|
||||
title = obj.getString("name").split(" / ").first()
|
||||
thumbnail_url = obj.getJSONObject("image").getString("original")
|
||||
val ratingValue = obj.getString("score").toFloat()
|
||||
title = obj["name"]!!.jsonPrimitive.content
|
||||
.split(" / ")
|
||||
.first()
|
||||
thumbnail_url = obj["image"]!!.jsonObject["original"]!!.jsonPrimitive.content
|
||||
|
||||
val ratingValue = obj["score"]!!.jsonPrimitive.floatOrNull ?: 0F
|
||||
val ratingStar = when {
|
||||
ratingValue > 9.5 -> "★★★★★"
|
||||
ratingValue > 8.5 -> "★★★★✬"
|
||||
|
@ -64,14 +75,13 @@ class Desu : HttpSource() {
|
|||
ratingValue > 0.5 -> "✬☆☆☆☆"
|
||||
else -> "☆☆☆☆☆"
|
||||
}
|
||||
val rawAgeValue = obj.getString("adult")
|
||||
val rawAgeStop = when (rawAgeValue) {
|
||||
"1" -> "18+"
|
||||
|
||||
val rawAgeStop = when (obj["adult"]!!.jsonPrimitive.int) {
|
||||
1 -> "18+"
|
||||
else -> "0+"
|
||||
}
|
||||
|
||||
val rawTypeValue = obj.getString("kind")
|
||||
val rawTypeStr = when (rawTypeValue) {
|
||||
val rawTypeStr = when (obj["kind"]!!.jsonPrimitive.content) {
|
||||
"manga" -> "Манга"
|
||||
"manhwa" -> "Манхва"
|
||||
"manhua" -> "Маньхуа"
|
||||
|
@ -81,21 +91,32 @@ class Desu : HttpSource() {
|
|||
}
|
||||
|
||||
var altName = ""
|
||||
if (obj.getString("synonyms").isNotEmpty() && obj.getString("synonyms") != "null") {
|
||||
altName = "Альтернативные названия:\n" + obj.getString("synonyms").replace("|", " / ") + "\n\n"
|
||||
|
||||
if (obj["synonyms"]?.jsonPrimitive?.content.orEmpty().isNotEmpty()) {
|
||||
altName = "Альтернативные названия:\n" +
|
||||
obj["synonyms"]!!.jsonPrimitive.content
|
||||
.replace("|", " / ") +
|
||||
"\n\n"
|
||||
}
|
||||
description = obj.getString("russian") + "\n" + ratingStar + " " + ratingValue + " (голосов: " + obj.getString("score_users") + ")\n" + altName + obj.getString("description")
|
||||
|
||||
description = obj["russian"]!!.jsonPrimitive.content + "\n" +
|
||||
ratingStar + " " + ratingValue +
|
||||
" (голосов: " +
|
||||
obj["score_users"]!!.jsonPrimitive.int +
|
||||
")\n" + altName +
|
||||
obj["description"]!!.jsonPrimitive.content
|
||||
|
||||
genre = if (chapter) {
|
||||
val jsonArray = obj.getJSONArray("genres")
|
||||
val genreList = mutableListOf<String>()
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
genreList.add(jsonArray.getJSONObject(i).getString("russian"))
|
||||
}
|
||||
genreList.plusElement(rawTypeStr).plusElement(rawAgeStop).joinToString()
|
||||
obj["genres"]!!.jsonArray
|
||||
.map { it.jsonObject["russian"]!!.jsonPrimitive.content }
|
||||
.plusElement(rawTypeStr)
|
||||
.plusElement(rawAgeStop)
|
||||
.joinToString()
|
||||
} else {
|
||||
obj.getString("genres") + ", " + rawTypeStr + ", " + rawAgeStop
|
||||
obj["genres"]!!.jsonPrimitive.content + ", " + rawTypeStr + ", " + rawAgeStop
|
||||
}
|
||||
status = when (obj.getString("status")) {
|
||||
|
||||
status = when (obj["status"]!!.jsonPrimitive.content) {
|
||||
"ongoing" -> SManga.ONGOING
|
||||
"released" -> SManga.COMPLETED
|
||||
"copyright" -> SManga.LICENSED
|
||||
|
@ -103,11 +124,13 @@ class Desu : HttpSource() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl$API_URL/?limit=50&order=popular&page=$page")
|
||||
override fun popularMangaRequest(page: Int) =
|
||||
GET("$baseUrl$API_URL/?limit=50&order=popular&page=$page")
|
||||
|
||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl$API_URL/?limit=50&order=updated&page=$page")
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
GET("$baseUrl$API_URL/?limit=50&order=updated&page=$page")
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
|
||||
|
||||
|
@ -136,12 +159,13 @@ class Desu : HttpSource() {
|
|||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val res = response.body!!.string()
|
||||
val obj = JSONObject(res).getJSONArray("response")
|
||||
val nav = JSONObject(res).getJSONObject("pageNavParams")
|
||||
val count = nav.getInt("count")
|
||||
val limit = nav.getInt("limit")
|
||||
val page = nav.getInt("page")
|
||||
val res = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
val obj = res["response"]!!.jsonArray
|
||||
val nav = res["pageNavParams"]!!.jsonObject
|
||||
val count = nav["count"]!!.jsonPrimitive.int
|
||||
val limit = nav["limit"]!!.jsonPrimitive.int
|
||||
val page = nav["page"]!!.jsonPrimitive.int
|
||||
|
||||
return mangaPageFromJSON(obj.toString(), count > page * limit)
|
||||
}
|
||||
|
||||
|
@ -162,53 +186,56 @@ class Desu : HttpSource() {
|
|||
return GET(baseUrl + "/manga" + manga.url, headers)
|
||||
}
|
||||
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
|
||||
val obj = JSONObject(response.body!!.string()).getJSONObject("response")
|
||||
val obj = json.parseToJsonElement(response.body!!.string())
|
||||
.jsonObject["response"]!!
|
||||
.jsonObject
|
||||
|
||||
mangaFromJSON(obj, true)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val obj = JSONObject(response.body!!.string()).getJSONObject("response")
|
||||
val ret = ArrayList<SChapter>()
|
||||
val obj = json.parseToJsonElement(response.body!!.string())
|
||||
.jsonObject["response"]!!
|
||||
.jsonObject
|
||||
|
||||
val cid = obj.getInt("id")
|
||||
val cid = obj["id"]!!.jsonPrimitive.int
|
||||
|
||||
val arr = obj.getJSONObject("chapters").getJSONArray("list")
|
||||
for (i in 0 until arr.length()) {
|
||||
val obj2 = arr.getJSONObject(i)
|
||||
ret.add(
|
||||
SChapter.create().apply {
|
||||
val ch = obj2.getString("ch")
|
||||
val fullnumstr = obj2.getString("vol") + ". " + "Глава " + ch
|
||||
val title = if (obj2.getString("title") == "null") "" else obj2.getString("title")
|
||||
name = if (title.isEmpty()) {
|
||||
fullnumstr
|
||||
} else {
|
||||
"$fullnumstr: $title"
|
||||
}
|
||||
val id = obj2.getString("id")
|
||||
url = "/$cid/chapter/$id"
|
||||
chapter_number = ch.toFloat()
|
||||
date_upload = obj2.getLong("date") * 1000
|
||||
return obj["chapters"]!!.jsonObject["list"]!!.jsonArray.map {
|
||||
val chapterObj = it.jsonObject
|
||||
val ch = chapterObj["ch"]!!.jsonPrimitive.float
|
||||
val fullNumStr = "${chapterObj["vol"]!!.jsonPrimitive.int} . Глава $ch"
|
||||
val title = chapterObj["title"]?.jsonPrimitive?.content.orEmpty()
|
||||
|
||||
SChapter.create().apply {
|
||||
name = if (title.isEmpty()) {
|
||||
fullNumStr
|
||||
} else {
|
||||
"$fullNumStr: $title"
|
||||
}
|
||||
)
|
||||
url = "/$cid/chapter/${chapterObj["id"]!!.jsonPrimitive.int}"
|
||||
chapter_number = ch
|
||||
date_upload = chapterObj["date"]!!.jsonPrimitive.long * 1000L
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
return GET(baseUrl + API_URL + manga.url, headers)
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET(baseUrl + API_URL + chapter.url, headers)
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val obj = JSONObject(response.body!!.string()).getJSONObject("response")
|
||||
val pages = obj.getJSONObject("pages")
|
||||
val list = pages.getJSONArray("list")
|
||||
val ret = ArrayList<Page>(list.length())
|
||||
for (i in 0 until list.length()) {
|
||||
ret.add(Page(i, "", list.getJSONObject(i).getString("img")))
|
||||
}
|
||||
return ret
|
||||
val obj = json.parseToJsonElement(response.body!!.string())
|
||||
.jsonObject["response"]!!
|
||||
.jsonObject
|
||||
|
||||
return obj["pages"]!!.jsonObject["list"]!!.jsonArray
|
||||
.mapIndexed { i, jsonEl ->
|
||||
Page(i, "", jsonEl.jsonObject["img"]!!.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response) =
|
||||
|
@ -236,10 +263,6 @@ class Desu : HttpSource() {
|
|||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||
private const val API_URL = "/manga/api"
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка",
|
||||
|
@ -247,10 +270,13 @@ class Desu : HttpSource() {
|
|||
)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанр", genres)
|
||||
|
||||
private class TypeList(types: List<Type>) : Filter.Group<Type>("Тип", types)
|
||||
|
||||
private class Type(name: String, val id: String) : Filter.CheckBox(name)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.CheckBox(name)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
TypeList(getTypeList()),
|
||||
|
@ -312,4 +338,10 @@ class Desu : HttpSource() {
|
|||
Genre("Юри", "Yuri"),
|
||||
Genre("Яой", "Yaoi")
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||
|
||||
private const val API_URL = "/manga/api"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'MeDocTruyenTranh'
|
||||
pkgNameSuffix = 'vi.medoctruyentranh'
|
||||
extClass = '.MeDocTruyenTranh'
|
||||
extVersionCode = 4
|
||||
extVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -8,12 +8,15 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -29,6 +32,8 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
|
||||
override val client = network.cloudflareClient
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaSelector() = "div.classifyList a"
|
||||
|
||||
override fun searchMangaSelector() = ".listCon a"
|
||||
|
@ -37,37 +42,24 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
return GET("$baseUrl/tim-truyen/toan-bo" + if (page > 1) "/$page" else "", headers)
|
||||
}
|
||||
|
||||
private inline fun <reified T, R> JSONArray.mapJSONArray(transform: (Int, T) -> R): List<R> {
|
||||
val list = mutableListOf<R>()
|
||||
for (i in 0 until this.length()) {
|
||||
list.add(transform(i, this[i] as T))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
private fun JSONArray.flatten(): JSONArray {
|
||||
val list = mutableListOf<Any>()
|
||||
for (i in 0 until this.length()) {
|
||||
val childArray = this[i] as JSONArray
|
||||
for (j in 0 until childArray.length()) {
|
||||
list.add(childArray[j])
|
||||
}
|
||||
}
|
||||
return JSONArray(list)
|
||||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
// trying to build URLs from this JSONObject could cause issues but we need it to get thumbnails
|
||||
val titleCoverMap = JSONObject(document.select("script#__NEXT_DATA__").first().data())
|
||||
.getJSONObject("props")
|
||||
.getJSONObject("pageProps")
|
||||
.getJSONObject("initialState")
|
||||
.getJSONObject("classify")
|
||||
.getJSONArray("comics")
|
||||
.mapJSONArray { _, jsonObject: JSONObject ->
|
||||
Pair(jsonObject.getString("title"), jsonObject.getString("coverimg"))
|
||||
val nextData = document.select("script#__NEXT_DATA__").first()
|
||||
.let { json.parseToJsonElement(it.data()).jsonObject }
|
||||
|
||||
val titleCoverMap = nextData["props"]!!
|
||||
.jsonObject["pageProps"]!!
|
||||
.jsonObject["initialState"]!!
|
||||
.jsonObject["classify"]!!
|
||||
.jsonObject["comics"]!!
|
||||
.jsonArray
|
||||
.map {
|
||||
Pair(
|
||||
it.jsonObject["title"]!!.jsonPrimitive.content,
|
||||
it.jsonObject["coverimg"]!!.jsonPrimitive.content
|
||||
)
|
||||
}
|
||||
.toMap()
|
||||
|
||||
|
@ -79,6 +71,7 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
|
||||
return MangasPage(mangas, document.select(popularMangaNextPageSelector()) != null)
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
return GET("$baseUrl/search/$query", headers)
|
||||
}
|
||||
|
@ -108,26 +101,24 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val manga = SManga.create()
|
||||
val jsonData = JSONObject(document.select("#__NEXT_DATA__").first()!!.data())
|
||||
val mangaDetail = jsonData
|
||||
.getJSONObject("props")
|
||||
.getJSONObject("pageProps")
|
||||
.getJSONObject("initialState")
|
||||
.getJSONObject("detail")
|
||||
.getJSONObject("story_item")
|
||||
manga.title = mangaDetail.getString("title")
|
||||
manga.author = mangaDetail.getJSONArray("author_list").getString(0)
|
||||
val genres = mutableListOf<String>()
|
||||
for (i in 0 until mangaDetail.getJSONArray("category_list").length()) {
|
||||
genres.add(mangaDetail.getJSONArray("category_list").getString(i))
|
||||
}
|
||||
manga.genre = genres.joinToString(", ")
|
||||
manga.description = mangaDetail.getString("summary")
|
||||
manga.status = parseStatus(mangaDetail.getString("is_updating"))
|
||||
manga.thumbnail_url = mangaDetail.getString("coverimg")
|
||||
return manga
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
|
||||
val nextData = document.select("script#__NEXT_DATA__").first()
|
||||
.let { json.parseToJsonElement(it.data()).jsonObject }
|
||||
|
||||
val mangaDetail = nextData["props"]!!
|
||||
.jsonObject["pageProps"]!!
|
||||
.jsonObject["initialState"]!!
|
||||
.jsonObject["detail"]!!
|
||||
.jsonObject["story_item"]!!
|
||||
.jsonObject
|
||||
|
||||
title = mangaDetail["title"]!!.jsonPrimitive.content
|
||||
author = mangaDetail["author_list"]!!.jsonArray.joinToString { it.jsonPrimitive.content }
|
||||
genre = mangaDetail["category_list"]!!.jsonArray.joinToString { it.jsonPrimitive.content }
|
||||
description = mangaDetail["summary"]!!.jsonPrimitive.content
|
||||
status = parseStatus(mangaDetail["is_updating"]!!.jsonPrimitive.content)
|
||||
thumbnail_url = mangaDetail["coverimg"]!!.jsonPrimitive.content
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String) = when {
|
||||
|
@ -139,18 +130,23 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
override fun chapterListSelector() = "div.chapters a"
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
return JSONObject(response.asJsoup().select("script#__NEXT_DATA__").first().data())
|
||||
.getJSONObject("props")
|
||||
.getJSONObject("pageProps")
|
||||
.getJSONObject("initialState")
|
||||
.getJSONObject("detail")
|
||||
.getJSONArray("story_chapters")
|
||||
.flatten()
|
||||
.mapJSONArray { _, jsonObject: JSONObject ->
|
||||
val nextData = response.asJsoup().select("script#__NEXT_DATA__").first()
|
||||
.let { json.parseToJsonElement(it.data()).jsonObject }
|
||||
|
||||
return nextData["props"]!!
|
||||
.jsonObject["pageProps"]!!
|
||||
.jsonObject["initialState"]!!
|
||||
.jsonObject["detail"]!!
|
||||
.jsonObject["story_chapters"]!!
|
||||
.jsonArray
|
||||
.flatMap { it.jsonArray }
|
||||
.map {
|
||||
val chapterObj = it.jsonObject
|
||||
|
||||
SChapter.create().apply {
|
||||
name = jsonObject.getString("title")
|
||||
setUrlWithoutDomain("${response.request.url}/${jsonObject.getString("chapter_index")}")
|
||||
date_upload = parseChapterDate(jsonObject.getString("time"))
|
||||
name = chapterObj["title"]!!.jsonPrimitive.content
|
||||
date_upload = parseChapterDate(chapterObj["time"]!!.jsonPrimitive.content)
|
||||
setUrlWithoutDomain("${response.request.url}/${chapterObj["chapter_index"]!!.jsonPrimitive.content}")
|
||||
}
|
||||
}
|
||||
.reversed()
|
||||
|
@ -166,15 +162,19 @@ class MeDocTruyenTranh : ParsedHttpSource() {
|
|||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return JSONObject(document.select("#__NEXT_DATA__").first()?.data() ?: "{}")
|
||||
.getJSONObject("props")
|
||||
.getJSONObject("pageProps")
|
||||
.getJSONObject("initialState")
|
||||
.getJSONObject("read")
|
||||
.getJSONObject("detail_item")
|
||||
.getJSONArray("elements")
|
||||
.mapJSONArray { i, jsonObject: JSONObject ->
|
||||
Page(i, "", jsonObject.getString("content"))
|
||||
val nextData = document.select("script#__NEXT_DATA__").firstOrNull()
|
||||
?.let { json.parseToJsonElement(it.data()).jsonObject }
|
||||
?: return emptyList()
|
||||
|
||||
return nextData["props"]!!
|
||||
.jsonObject["pageProps"]!!
|
||||
.jsonObject["initialState"]!!
|
||||
.jsonObject["read"]!!
|
||||
.jsonObject["detail_item"]!!
|
||||
.jsonObject["elements"]!!
|
||||
.jsonArray
|
||||
.mapIndexed { i, jsonEl ->
|
||||
Page(i, "", jsonEl.jsonObject["content"]!!.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Kuaikanmanhua'
|
||||
pkgNameSuffix = 'zh.kuaikanmanhua'
|
||||
extClass = '.Kuaikanmanhua'
|
||||
extVersionCode = 5
|
||||
extVersionCode = 6
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -10,14 +10,20 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class Kuaikanmanhua : HttpSource() {
|
||||
|
@ -34,6 +40,8 @@ class Kuaikanmanhua : HttpSource() {
|
|||
|
||||
private val apiUrl = "https://api.kkmh.com"
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
// Popular
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
|
@ -42,25 +50,25 @@ class Kuaikanmanhua : HttpSource() {
|
|||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val body = response.body!!.string()
|
||||
val jsonList = JSONObject(body).getJSONObject("data").getJSONArray("topics")
|
||||
val jsonList = json.parseToJsonElement(body).jsonObject["data"]!!
|
||||
.jsonObject["topics"]!!
|
||||
.jsonArray
|
||||
return parseMangaJsonArray(jsonList)
|
||||
}
|
||||
|
||||
private fun parseMangaJsonArray(jsonList: JSONArray, isSearch: Boolean = false): MangasPage {
|
||||
val mangaList = mutableListOf<SManga>()
|
||||
private fun parseMangaJsonArray(jsonList: JsonArray, isSearch: Boolean = false): MangasPage {
|
||||
val mangaList = jsonList.map {
|
||||
val mangaObj = it.jsonObject
|
||||
|
||||
for (i in 0 until jsonList.length()) {
|
||||
val obj = jsonList.getJSONObject(i)
|
||||
mangaList.add(
|
||||
SManga.create().apply {
|
||||
title = obj.getString("title")
|
||||
thumbnail_url = obj.getString("vertical_image_url")
|
||||
url = "/web/topic/" + obj.getInt("id")
|
||||
}
|
||||
)
|
||||
SManga.create().apply {
|
||||
title = mangaObj["title"]!!.jsonPrimitive.content
|
||||
thumbnail_url = mangaObj["vertical_image_url"]!!.jsonPrimitive.content
|
||||
url = "/web/topic/" + mangaObj["id"]!!.jsonPrimitive.int
|
||||
}
|
||||
}
|
||||
|
||||
// KKMH does not have pages when you search
|
||||
return MangasPage(mangaList, mangaList.size > 9 && !isSearch)
|
||||
return MangasPage(mangaList, hasNextPage = mangaList.size > 9 && !isSearch)
|
||||
}
|
||||
|
||||
// Latest
|
||||
|
@ -110,12 +118,12 @@ class Kuaikanmanhua : HttpSource() {
|
|||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val body = response.body!!.string()
|
||||
val jsonObj = JSONObject(body).getJSONObject("data")
|
||||
if (jsonObj.has("hit")) {
|
||||
return parseMangaJsonArray(jsonObj.getJSONArray("hit"), true)
|
||||
val jsonObj = json.parseToJsonElement(body).jsonObject["data"]!!.jsonObject
|
||||
if (jsonObj["hit"] != null) {
|
||||
return parseMangaJsonArray(jsonObj["hit"]!!.jsonArray, true)
|
||||
}
|
||||
|
||||
return parseMangaJsonArray(jsonObj.getJSONArray("topics"), false)
|
||||
return parseMangaJsonArray(jsonObj["topics"]!!.jsonArray, false)
|
||||
}
|
||||
|
||||
// Details
|
||||
|
@ -128,32 +136,37 @@ class Kuaikanmanhua : HttpSource() {
|
|||
return Observable.just(sManga)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
val data = JSONObject(response.body!!.string()).getJSONObject("data")
|
||||
val manga = SManga.create()
|
||||
manga.title = data.getString("title")
|
||||
manga.thumbnail_url = data.getString("vertical_image_url")
|
||||
manga.author = data.getJSONObject("user").getString("nickname")
|
||||
manga.description = data.getString("description")
|
||||
manga.status = data.getInt("update_status_code")
|
||||
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
|
||||
val data = json.parseToJsonElement(response.body!!.string())
|
||||
.jsonObject["data"]!!
|
||||
.jsonObject
|
||||
|
||||
return manga
|
||||
title = data["title"]!!.jsonPrimitive.content
|
||||
thumbnail_url = data["vertical_image_url"]!!.jsonPrimitive.content
|
||||
author = data["user"]!!.jsonObject["nickname"]!!.jsonPrimitive.content
|
||||
description = data["description"]!!.jsonPrimitive.content
|
||||
status = data["update_status_code"]!!.jsonPrimitive.int
|
||||
}
|
||||
|
||||
// Chapters & Pages
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
val chapters = mutableListOf<SChapter>()
|
||||
val script = document.select("script:containsData(comics)").first().data()
|
||||
val comics = JSONArray(script.substringAfter("comics:").substringBefore(",first_comic_id"))
|
||||
val variable = script.substringAfter("(function(").substringBefore("){").split(",")
|
||||
val value = script.substringAfterLast("}}(").substringBefore("));").split(",")
|
||||
val comics = script.substringAfter("comics:")
|
||||
.substringBefore(",first_comic_id")
|
||||
.let { json.parseToJsonElement(it).jsonArray }
|
||||
val variable = script.substringAfter("(function(")
|
||||
.substringBefore("){")
|
||||
.split(",")
|
||||
val value = script.substringAfterLast("}}(")
|
||||
.substringBefore("));")
|
||||
.split(",")
|
||||
|
||||
document.select("div.TopicItem").forEachIndexed { index, element ->
|
||||
chapters.add(
|
||||
return document.select("div.TopicItem")
|
||||
.mapIndexed { index, element ->
|
||||
SChapter.create().apply {
|
||||
val idVar = comics.getJSONObject(index).getString("id")
|
||||
val idVar = comics[index].jsonObject["id"]!!.jsonPrimitive.content
|
||||
val id = value[variable.indexOf(idVar)]
|
||||
url = "/web/comic/$id"
|
||||
name = element.select("div.title > a").text()
|
||||
|
@ -167,11 +180,10 @@ class Kuaikanmanhua : HttpSource() {
|
|||
} else {
|
||||
"20$dateStr"
|
||||
}
|
||||
date_upload = SimpleDateFormat("yyyy-MM-dd").parse(dateStr).time
|
||||
date_upload = runCatching { DATE_FORMAT.parse(dateStr)?.time }
|
||||
.getOrNull() ?: 0L
|
||||
}
|
||||
)
|
||||
}
|
||||
return chapters
|
||||
}
|
||||
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||
|
@ -187,18 +199,26 @@ class Kuaikanmanhua : HttpSource() {
|
|||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val pages = ArrayList<Page>()
|
||||
val document = response.asJsoup()
|
||||
val script = document.selectFirst("script:containsData(comicImages)").data()
|
||||
val images = JSONArray(script.substringAfter("comicImages:").substringBefore("},nextComicInfo"))
|
||||
val variable = script.substringAfter("(function(").substringBefore("){").split(",")
|
||||
val value = script.substringAfterLast("}}(").substringBefore("));").split(",")
|
||||
for (i in 0 until images.length()) {
|
||||
val urlVar = images.getJSONObject(i).getString("url")
|
||||
val url = value[variable.indexOf(urlVar)].replace("\\u002F", "/").replace("\"", "")
|
||||
pages.add(Page(i, "", url))
|
||||
val images = script.substringAfter("comicImages:")
|
||||
.substringBefore("},nextComicInfo")
|
||||
.let { json.parseToJsonElement(it).jsonArray }
|
||||
val variable = script.substringAfter("(function(")
|
||||
.substringBefore("){")
|
||||
.split(",")
|
||||
val value = script.substringAfterLast("}}(")
|
||||
.substringBefore("));")
|
||||
.split(",")
|
||||
|
||||
return images.mapIndexed { index, jsonEl ->
|
||||
val urlVar = jsonEl.jsonObject["url"]!!.jsonPrimitive.content
|
||||
val imageUrl = value[variable.indexOf(urlVar)]
|
||||
.replace("\\u002F", "/")
|
||||
.replace("\"", "")
|
||||
|
||||
Page(index, "", imageUrl)
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
// Filters
|
||||
|
@ -256,5 +276,9 @@ class Kuaikanmanhua : HttpSource() {
|
|||
|
||||
companion object {
|
||||
const val TOPIC_ID_SEARCH_PREFIX = "topic:"
|
||||
|
||||
private val DATE_FORMAT by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'QiXiManhua'
|
||||
pkgNameSuffix = 'zh.qiximh'
|
||||
extClass = '.Qiximh'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -11,19 +11,30 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Qiximh : HttpSource() {
|
||||
|
||||
override val lang = "zh"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val name = "七夕漫画"
|
||||
|
||||
override val baseUrl = "http://qiximh1.com"
|
||||
|
||||
// This is hard limit by API
|
||||
val maxPage = 5
|
||||
private val maxPage = 5
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
// Used in Rank API
|
||||
private enum class RANKTYPE(val rankVal: Int) {
|
||||
|
@ -84,20 +95,19 @@ class Qiximh : HttpSource() {
|
|||
}
|
||||
|
||||
private fun commonDataProcess(origRequest: Request, responseBody: String): MangasPage {
|
||||
val jsonData = JSONArray(responseBody)
|
||||
val jsonData = json.parseToJsonElement(responseBody).jsonArray
|
||||
|
||||
val mangaArr = mutableListOf<SManga>()
|
||||
val mangaArr = jsonData.map {
|
||||
val targetObj = it.jsonObject
|
||||
|
||||
for (i in 0 until jsonData.length()) {
|
||||
val targetObj = jsonData.getJSONObject(i)
|
||||
mangaArr.add(
|
||||
SManga.create().apply {
|
||||
title = targetObj.get("name") as String
|
||||
status = SManga.UNKNOWN
|
||||
thumbnail_url = targetObj.get("imgurl") as String
|
||||
url = "$baseUrl/${targetObj.get("id")}/"
|
||||
}
|
||||
)
|
||||
SManga.create().apply {
|
||||
title = targetObj["name"]!!.jsonPrimitive.content
|
||||
status = SManga.UNKNOWN
|
||||
thumbnail_url = targetObj["imgurl"]!!.jsonPrimitive.content
|
||||
// Extension is wrongly adding the baseURL to the SManga.
|
||||
// I kept it as it is to avoid user migrations.
|
||||
url = "$baseUrl/${targetObj["id"]!!.jsonPrimitive.int}"
|
||||
}
|
||||
}
|
||||
|
||||
val requestBody = origRequest.body as FormBody
|
||||
|
@ -153,36 +163,37 @@ class Qiximh : HttpSource() {
|
|||
}
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val responseBody = response.body
|
||||
val mangaArr = mutableListOf<SManga>()
|
||||
?: return MangasPage(emptyList(), false)
|
||||
|
||||
if (responseBody != null) {
|
||||
val responseString = responseBody.string()
|
||||
if (!responseString.isNullOrEmpty()) {
|
||||
if (responseString.startsWith("[")) {
|
||||
// This is to process filter
|
||||
return commonDataProcess(response.request, responseString)
|
||||
} else {
|
||||
val jsonData = JSONObject(responseString)
|
||||
if (jsonData.get("msg") == "success") {
|
||||
val jsonArr = jsonData.getJSONArray("search_data")
|
||||
val responseString = responseBody.string()
|
||||
|
||||
for (i in 0 until jsonArr.length()) {
|
||||
val targetObj = jsonArr.getJSONObject(i)
|
||||
mangaArr.add(
|
||||
SManga.create().apply {
|
||||
title = targetObj.get("name") as String
|
||||
thumbnail_url = targetObj.get("imgs") as String
|
||||
url = "$baseUrl/${targetObj.get("id")}/"
|
||||
}
|
||||
)
|
||||
if (responseString.isNotEmpty()) {
|
||||
if (responseString.startsWith("[")) {
|
||||
// This is to process filter
|
||||
return commonDataProcess(response.request, responseString)
|
||||
} else {
|
||||
val jsonData = json.parseToJsonElement(responseString).jsonObject
|
||||
|
||||
if (jsonData["msg"]!!.jsonPrimitive.content == "success") {
|
||||
val mangaArr = jsonData["search_data"]!!.jsonArray.map {
|
||||
val targetObj = it.jsonObject
|
||||
|
||||
SManga.create().apply {
|
||||
title = targetObj["name"]!!.jsonPrimitive.content
|
||||
thumbnail_url = targetObj["imgs"]!!.jsonPrimitive.content
|
||||
// Extension is wrongly adding the baseURL to the SManga.
|
||||
// I kept it as it is to avoid user migrations.
|
||||
url = "$baseUrl/${targetObj["id"]!!.jsonPrimitive.int}"
|
||||
}
|
||||
}
|
||||
|
||||
return MangasPage(mangaArr, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search does not have pagination
|
||||
return MangasPage(mangaArr, false)
|
||||
return MangasPage(emptyList(), false)
|
||||
}
|
||||
|
||||
// Filter
|
||||
|
@ -247,13 +258,16 @@ class Qiximh : HttpSource() {
|
|||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
// API does not allow retrieve full chapter list, hence the need to parse the chapters from both HTML and API
|
||||
val htmlChapters = document.select(".catalog_list.row_catalog_list a").map {
|
||||
SChapter.create().apply {
|
||||
name = it.text()
|
||||
url = "$baseUrl${it.attr("href")}"
|
||||
// API does not allow retrieve full chapter list, hence the need to parse
|
||||
// the chapters from both HTML and API
|
||||
val chapterList = document.select(".catalog_list.row_catalog_list a")
|
||||
.map {
|
||||
SChapter.create().apply {
|
||||
name = it.text()
|
||||
url = "$baseUrl${it.attr("href")}"
|
||||
}
|
||||
}
|
||||
}
|
||||
.toMutableList()
|
||||
|
||||
val mangaUrl = response.request.url.toString()
|
||||
|
||||
|
@ -267,22 +281,18 @@ class Qiximh : HttpSource() {
|
|||
)
|
||||
|
||||
val inlineResponse = client.newCall(request).execute()
|
||||
val jsonData = JSONArray(inlineResponse.body!!.string())
|
||||
val jsonData = json.parseToJsonElement(inlineResponse.body!!.string()).jsonArray
|
||||
|
||||
val chapterArr = mutableListOf<SChapter>()
|
||||
chapterArr.addAll(htmlChapters)
|
||||
chapterList += jsonData.map {
|
||||
val targetObj = it.jsonObject
|
||||
|
||||
for (i in 0 until jsonData.length()) {
|
||||
val targetObj = jsonData.getJSONObject(i)
|
||||
chapterArr.add(
|
||||
SChapter.create().apply {
|
||||
name = targetObj.get("chaptername") as String
|
||||
url = "$mangaUrl${targetObj.get("chapterid")}.html"
|
||||
}
|
||||
)
|
||||
SChapter.create().apply {
|
||||
name = targetObj["chaptername"]!!.jsonPrimitive.content
|
||||
url = "$mangaUrl${targetObj["chapterid"]!!.jsonPrimitive.int}.html"
|
||||
}
|
||||
}
|
||||
|
||||
return chapterArr
|
||||
return chapterList
|
||||
}
|
||||
|
||||
// Page
|
||||
|
|
Loading…
Reference in New Issue