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:
Alessandro Jean 2021-11-01 08:32:12 -03:00 committed by GitHub
parent d0e8f185ab
commit fd927c254d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 320 additions and 250 deletions

View File

@ -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"

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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"

View File

@ -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)
}
}
}

View File

@ -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"

View File

@ -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