Add kotlinx.serialization to more sources (#7398)
* Replace Gson in Foolslide, Wpmangastream, Gigaviewer and Bilibili. * Replace Gson in Wpmangareader and Cubari. * Replace Gson in MangaAdventure and NaniScans. * Fix missing pages at Wpmangastream (closes #7395). * Replace Gson in Webtoons and WebtoonsTranslate.
This commit is contained in:
parent
d6365b5910
commit
6d144464c7
|
@ -1,20 +1,17 @@
|
|||
package eu.kanade.tachiyomi.extension.pt.silencescan
|
||||
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import com.github.salomonbrys.kotson.array
|
||||
import com.github.salomonbrys.kotson.obj
|
||||
import com.github.salomonbrys.kotson.string
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class SilenceScan : WPMangaStream(
|
||||
"Silence Scan",
|
||||
|
@ -29,31 +26,33 @@ class SilenceScan : WPMangaStream(
|
|||
.addNetworkInterceptor(RateLimitInterceptor(1, 1, TimeUnit.SECONDS))
|
||||
.build()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val infoEl = document.select("div.bigcontent, div.animefull").first()
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
author = infoEl.select("b:contains(Autor) + span").text()
|
||||
artist = infoEl.select("b:contains(Artista) + span").text()
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val infoEl = document.select("div.bigcontent, div.animefull, div.main-info").first()
|
||||
|
||||
author = infoEl.select("div.imptdt:contains(Autor) i").text()
|
||||
artist = infoEl.select("div.imptdt:contains(Artista) + i").text()
|
||||
status = parseStatus(infoEl.select("div.imptdt:contains(Status) i").text())
|
||||
description = infoEl.select("h2:contains(Sinopse) + div p").joinToString("\n") { it.text() }
|
||||
description = infoEl.select("h2:contains(Sinopse) + div p:not([class])").joinToString("\n") { it.text() }
|
||||
thumbnail_url = infoEl.select("div.thumb img").imgAttr()
|
||||
|
||||
val genres = infoEl.select("b:contains(Gêneros) + span a")
|
||||
.map { element -> element.text().toLowerCase() }
|
||||
val genres = infoEl.select("span.mgen a[rel]")
|
||||
.map { element -> element.text() }
|
||||
.toMutableSet()
|
||||
|
||||
// add series type(manga/manhwa/manhua/other) thinggy to genre
|
||||
document.select(seriesTypeSelector).firstOrNull()?.ownText()?.let {
|
||||
if (it.isEmpty().not() && genres.contains(it).not()) {
|
||||
genres.add(it.toLowerCase())
|
||||
genres.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
genre = genres.toList().map { it.capitalize() }.joinToString(", ")
|
||||
genre = genres.toList().joinToString()
|
||||
|
||||
// add alternative name to manga description
|
||||
document.select(altNameSelector).firstOrNull()?.ownText()?.let {
|
||||
if (it.isEmpty().not() && it !="N/A" && it != "-") {
|
||||
if (it.isEmpty().not() && it != "N/A" && it != "-") {
|
||||
description += when {
|
||||
description!!.isEmpty() -> altName + it
|
||||
else -> "\n\n$altName" + it
|
||||
|
@ -63,7 +62,14 @@ class SilenceScan : WPMangaStream(
|
|||
}
|
||||
|
||||
override val seriesTypeSelector = ".imptdt:contains(Tipo) a, a[href*=type\\=]"
|
||||
override val altNameSelector = ".wd-full:contains(Alt) span"
|
||||
override val altName: String = "Nome alternativo: "
|
||||
|
||||
override fun parseStatus(element: String?): Int = when {
|
||||
element == null -> SManga.UNKNOWN
|
||||
element.contains("em andamento", true) -> SManga.ONGOING
|
||||
element.contains("completo", true) -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
name = element.select("span.chapternum").text()
|
||||
|
@ -73,24 +79,6 @@ class SilenceScan : WPMangaStream(
|
|||
setUrlWithoutDomain(element.select("div.eph-num > a").attr("href"))
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val chapterObj = document.select("script:containsData(ts_reader)").first()
|
||||
.data()
|
||||
.substringAfter("run(")
|
||||
.substringBeforeLast(");")
|
||||
.let { JsonParser.parseString(it) }
|
||||
.obj
|
||||
|
||||
if (chapterObj["sources"].array.size() == 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val firstServerAvailable = chapterObj["sources"].array[0].obj
|
||||
|
||||
return firstServerAvailable["images"].array
|
||||
.mapIndexed { i, pageUrl -> Page(i, "", pageUrl.string) }
|
||||
}
|
||||
|
||||
override fun getGenreList(): List<Genre> = listOf(
|
||||
Genre("4-koma", "4-koma"),
|
||||
Genre("Ação", "acao"),
|
||||
|
@ -126,4 +114,8 @@ class SilenceScan : WPMangaStream(
|
|||
Genre("Violência sexual", "violencia-sexual"),
|
||||
Genre("Yuri", "yuri")
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val PORTUGUESE_LOCALE = Locale("pt", "BR")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package eu.kanade.tachiyomi.multisrc.foolslide
|
||||
|
||||
import com.github.salomonbrys.kotson.get
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
@ -10,11 +8,16 @@ import eu.kanade.tachiyomi.source.model.Page
|
|||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import kotlinx.serialization.json.Json
|
||||
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.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
|
@ -33,6 +36,8 @@ abstract class FoolSlide(
|
|||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaSelector() = "div.group"
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
|
@ -166,7 +171,7 @@ abstract class FoolSlide(
|
|||
}
|
||||
|
||||
open fun parseChapterDate(date: String): Long? {
|
||||
val lcDate = date.toLowerCase()
|
||||
val lcDate = date.toLowerCase(Locale.ROOT)
|
||||
if (lcDate.endsWith(" ago"))
|
||||
parseRelativeDate(lcDate)?.let { return it }
|
||||
|
||||
|
@ -272,18 +277,17 @@ abstract class FoolSlide(
|
|||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val doc = document.toString()
|
||||
val jsonstr = doc.substringAfter("var pages = ").substringBefore(";")
|
||||
val json = JsonParser().parse(jsonstr).asJsonArray
|
||||
val pages = mutableListOf<Page>()
|
||||
json.forEach {
|
||||
val jsonStr = doc.substringAfter("var pages = ").substringBefore(";")
|
||||
val pages = json.parseToJsonElement(jsonStr).jsonArray
|
||||
|
||||
return pages.mapIndexed { i, jsonEl ->
|
||||
// Create dummy element to resolve relative URL
|
||||
val absUrl = document.createElement("a")
|
||||
.attr("href", it["url"].asString)
|
||||
.attr("href", jsonEl.jsonObject["url"]!!.jsonPrimitive.content)
|
||||
.absUrl("href")
|
||||
|
||||
pages.add(Page(pages.size, "", absUrl))
|
||||
Page(i, "", absUrl)
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
|
||||
|
|
|
@ -10,7 +10,7 @@ class FoolSlideGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "FoolSlide"
|
||||
|
||||
override val baseVersionCode: Int = 1
|
||||
override val baseVersionCode: Int = 2
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("The Cat Scans", "https://reader2.thecatscans.com/", "en"),
|
||||
|
|
|
@ -4,11 +4,6 @@ import android.graphics.Bitmap
|
|||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import com.github.salomonbrys.kotson.get
|
||||
import com.github.salomonbrys.kotson.obj
|
||||
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.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
|
@ -18,6 +13,10 @@ 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.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Interceptor
|
||||
|
@ -28,6 +27,7 @@ import okhttp3.ResponseBody.Companion.toResponseBody
|
|||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.text.ParseException
|
||||
|
@ -57,6 +57,8 @@ abstract class GigaViewer(
|
|||
.add("Origin", baseUrl)
|
||||
.add("Referer", baseUrl)
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/series", headers)
|
||||
|
||||
override fun popularMangaSelector(): String = "ul.series-list li a"
|
||||
|
@ -143,9 +145,12 @@ abstract class GigaViewer(
|
|||
var result = client.newCall(request).execute()
|
||||
|
||||
while (result.code != 404) {
|
||||
val json = result.asJsonObject()
|
||||
readMoreEndpoint = json["nextUrl"].string
|
||||
val tempDocument = Jsoup.parse(json["html"].string, response.request.url.toString())
|
||||
val jsonResult = json.parseToJsonElement(result.body!!.string()).jsonObject
|
||||
readMoreEndpoint = jsonResult["nextUrl"]!!.jsonPrimitive.content
|
||||
val tempDocument = Jsoup.parse(
|
||||
jsonResult["html"]!!.jsonPrimitive.content,
|
||||
response.request.url.toString()
|
||||
)
|
||||
|
||||
chapters += tempDocument
|
||||
.select("ul.series-episode-list " + chapterListSelector())
|
||||
|
@ -177,16 +182,16 @@ abstract class GigaViewer(
|
|||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val episodeJson = document.select("script#episode-json")
|
||||
val episode = document.select("script#episode-json")
|
||||
.attr("data-value")
|
||||
.let { JsonParser.parseString(it).obj }
|
||||
.let { json.decodeFromString<GigaViewerEpisodeDto>(it) }
|
||||
|
||||
return episodeJson["readableProduct"]["pageStructure"]["pages"].asJsonArray
|
||||
.filter { it["type"].string == "main" }
|
||||
.mapIndexed { i, pageObj ->
|
||||
val imageUrl = pageObj["src"].string.toHttpUrlOrNull()!!.newBuilder()
|
||||
.addQueryParameter("width", pageObj["width"].string)
|
||||
.addQueryParameter("height", pageObj["height"].string)
|
||||
return episode.readableProduct.pageStructure.pages
|
||||
.filter { it.type == "main" }
|
||||
.mapIndexed { i, page ->
|
||||
val imageUrl = page.src.toHttpUrlOrNull()!!.newBuilder()
|
||||
.addQueryParameter("width", page.width.toString())
|
||||
.addQueryParameter("height", page.height.toString())
|
||||
.toString()
|
||||
Page(i, document.location(), imageUrl)
|
||||
}
|
||||
|
@ -280,8 +285,6 @@ abstract class GigaViewer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun Response.asJsonObject(): JsonObject = JsonParser.parseString(body!!.string()).obj
|
||||
|
||||
companion object {
|
||||
private val DATE_PARSER by lazy { SimpleDateFormat("yyyy/MM/dd", Locale.ENGLISH) }
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package eu.kanade.tachiyomi.multisrc.gigaviewer
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GigaViewerEpisodeDto(
|
||||
val readableProduct: GigaViewerReadableProduct
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GigaViewerReadableProduct(
|
||||
val pageStructure: GigaViewerPageStructure
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GigaViewerPageStructure(
|
||||
val pages: List<GigaViewerPage> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GigaViewerPage(
|
||||
val height: Int = 0,
|
||||
val src: String = "",
|
||||
val type: String = "",
|
||||
val width: Int = 0
|
||||
)
|
|
@ -9,7 +9,7 @@ class GigaViewerGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "GigaViewer"
|
||||
|
||||
override val baseVersionCode: Int = 1
|
||||
override val baseVersionCode: Int = 2
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("Comic Gardo", "https://comic-gardo.com", "ja"),
|
||||
|
|
|
@ -12,12 +12,17 @@ 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.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
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.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -55,6 +60,8 @@ abstract class MangAdventure(
|
|||
add("Referer", baseUrl)
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
GET("$apiUrl/releases/", headers)
|
||||
|
||||
|
@ -100,58 +107,57 @@ abstract class MangAdventure(
|
|||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) =
|
||||
JSONArray(response.asString()).run {
|
||||
json.parseToJsonElement(response.asString()).jsonArray.run {
|
||||
MangasPage(
|
||||
(0 until length()).map {
|
||||
val obj = getJSONObject(it)
|
||||
map {
|
||||
val obj = it.jsonObject
|
||||
SManga.create().apply {
|
||||
url = obj.getString("url")
|
||||
title = obj.getString("title")
|
||||
thumbnail_url = obj.getString("cover")
|
||||
// A bit of a hack to sort by date
|
||||
url = obj["url"]!!.jsonPrimitive.content
|
||||
title = obj["title"]!!.jsonPrimitive.content
|
||||
thumbnail_url = obj["cover"]!!.jsonPrimitive.content
|
||||
description = httpDateToTimestamp(
|
||||
obj.getJSONObject("latest_chapter").getString("date")
|
||||
obj["latest_chapter"]!!.jsonObject["date"]!!.jsonPrimitive.content
|
||||
).toString()
|
||||
}
|
||||
}.sortedByDescending(SManga::description),
|
||||
}
|
||||
.sortedByDescending(SManga::description),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response) =
|
||||
JSONObject(response.asString()).getJSONObject("volumes").run {
|
||||
keys().asSequence().flatMap { vol ->
|
||||
val chapters = getJSONObject(vol)
|
||||
chapters.keys().asSequence().map { ch ->
|
||||
json.parseToJsonElement(response.asString()).jsonObject["volumes"]!!.jsonObject.entries
|
||||
.reversed()
|
||||
.flatMap { vol ->
|
||||
vol.value.jsonObject.entries.map { ch ->
|
||||
SChapter.create().fromJSON(
|
||||
chapters.getJSONObject(ch).also {
|
||||
it.put("volume", vol)
|
||||
it.put("chapter", ch)
|
||||
ch.value.jsonObject.toMutableMap().let {
|
||||
it["volume"] = JsonPrimitive(vol.key)
|
||||
it["chapter"] = JsonPrimitive(ch.key)
|
||||
|
||||
JsonObject(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}.toList().reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response) =
|
||||
SManga.create().fromJSON(JSONObject(response.asString()))
|
||||
SManga.create().fromJSON(json.parseToJsonElement(response.asString()).jsonObject)
|
||||
|
||||
override fun pageListParse(response: Response) =
|
||||
JSONObject(response.asString()).run {
|
||||
val url = getString("url")
|
||||
val root = getString("pages_root")
|
||||
val arr = getJSONArray("pages_list")
|
||||
(0 until arr.length()).map {
|
||||
Page(it, "$url${it + 1}", "$root${arr.getString(it)}")
|
||||
json.parseToJsonElement(response.asString()).jsonObject.run {
|
||||
val url = get("url")!!.jsonPrimitive.content
|
||||
val root = get("pages_root")!!.jsonPrimitive.content
|
||||
|
||||
get("pages_list")!!.jsonArray.mapIndexed { i, jsonEl ->
|
||||
Page(i, "$url${i + 1}", "$root${jsonEl.jsonPrimitive.content}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) =
|
||||
JSONArray(response.asString()).run {
|
||||
json.parseToJsonElement(response.asString()).jsonArray.run {
|
||||
MangasPage(
|
||||
(0 until length()).map {
|
||||
SManga.create().fromJSON(getJSONObject(it))
|
||||
}.sortedBy(SManga::title),
|
||||
map { SManga.create().fromJSON(it.jsonObject) },
|
||||
false
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,15 @@ package eu.kanade.tachiyomi.multisrc.mangadventure
|
|||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.intOrNull
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/** Returns the body of a response as a `String`. */
|
||||
|
@ -22,16 +28,14 @@ fun Number.format(fmt: String): String = DecimalFormat(fmt).format(this)
|
|||
/**
|
||||
* Joins each value of a given [field] of the array using [sep].
|
||||
*
|
||||
* @param field The index of a [JSONArray].
|
||||
* When its type is [String], it is treated as the key of a [JSONObject].
|
||||
* @param field The index of a [JsonArray].
|
||||
* When its type is [String], it is treated as the key of a [JsonObject].
|
||||
* @param sep The separator used to join the array.
|
||||
* @return The joined string, or `null` if the array is empty.
|
||||
*/
|
||||
fun JSONArray.joinField(field: Int, sep: String = ", ") =
|
||||
length().takeIf { it != 0 }?.run {
|
||||
(0 until this).joinToString(sep) {
|
||||
getJSONArray(it).getString(field)
|
||||
}
|
||||
fun JsonArray.joinField(field: Int, sep: String = ", ") =
|
||||
size.takeIf { it != 0 }?.run {
|
||||
joinToString(sep) { it.jsonArray[field].jsonPrimitive.content }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,11 +45,9 @@ fun JSONArray.joinField(field: Int, sep: String = ", ") =
|
|||
* @param sep The separator used to join the array.
|
||||
* @return The joined string, or `null` if the array is empty.
|
||||
*/
|
||||
fun JSONArray.joinField(field: String, sep: String = ", ") =
|
||||
length().takeIf { it != 0 }?.run {
|
||||
(0 until this).joinToString(sep) {
|
||||
getJSONObject(it).getString(field)
|
||||
}
|
||||
fun JsonArray.joinField(field: String, sep: String = ", ") =
|
||||
size.takeIf { it != 0 }?.run {
|
||||
joinToString(sep) { it.jsonObject[field]!!.jsonPrimitive.content }
|
||||
}
|
||||
|
||||
/** The slug of a manga. */
|
||||
|
@ -53,19 +55,19 @@ val SManga.slug: String
|
|||
get() = Uri.parse(url).lastPathSegment!!
|
||||
|
||||
/**
|
||||
* Creates a [SManga] by parsing a [JSONObject].
|
||||
* Creates a [SManga] by parsing a [JsonObject].
|
||||
*
|
||||
* @param obj The object containing the manga info.
|
||||
*/
|
||||
fun SManga.fromJSON(obj: JSONObject) = apply {
|
||||
url = obj.getString("url")
|
||||
title = obj.getString("title")
|
||||
description = obj.getString("description")
|
||||
thumbnail_url = obj.getString("cover")
|
||||
author = obj.getJSONArray("authors").joinField(0)
|
||||
artist = obj.getJSONArray("artists").joinField(0)
|
||||
genre = obj.getJSONArray("categories").joinField("name")
|
||||
status = if (obj.getBoolean("completed"))
|
||||
fun SManga.fromJSON(obj: JsonObject) = apply {
|
||||
url = obj["url"]!!.jsonPrimitive.content
|
||||
title = obj["title"]!!.jsonPrimitive.content
|
||||
description = obj["description"]!!.jsonPrimitive.content
|
||||
thumbnail_url = obj["cover"]!!.jsonPrimitive.content
|
||||
author = obj["authors"]!!.jsonArray.joinField(0)
|
||||
artist = obj["artists"]!!.jsonArray.joinField(0)
|
||||
genre = obj["categories"]!!.jsonArray.joinField("name")
|
||||
status = if (obj["completed"]!!.jsonPrimitive.boolean)
|
||||
SManga.COMPLETED else SManga.ONGOING
|
||||
}
|
||||
|
||||
|
@ -78,18 +80,16 @@ val SChapter.path: String
|
|||
*
|
||||
* @param obj The object containing the chapter info.
|
||||
*/
|
||||
fun SChapter.fromJSON(obj: JSONObject) = apply {
|
||||
url = obj.getString("url")
|
||||
chapter_number = obj.optString("chapter", "-1").toFloat()
|
||||
date_upload = MangAdventure.httpDateToTimestamp(obj.getString("date"))
|
||||
scanlator = obj.getJSONArray("groups").joinField("name", " & ")
|
||||
name = obj.optString(
|
||||
"full_title",
|
||||
buildString {
|
||||
obj.optInt("volume").let { if (it != 0) append("Vol. $it, ") }
|
||||
fun SChapter.fromJSON(obj: JsonObject) = apply {
|
||||
url = obj["url"]!!.jsonPrimitive.content
|
||||
chapter_number = obj["chapter"]?.jsonPrimitive?.content?.toFloatOrNull() ?: -1f
|
||||
date_upload = MangAdventure.httpDateToTimestamp(obj["date"]!!.jsonPrimitive.content)
|
||||
scanlator = obj["groups"]!!.jsonArray.joinField("name", " & ")
|
||||
name = obj["full_title"]?.jsonPrimitive?.contentOrNull
|
||||
?: buildString {
|
||||
obj["volume"]?.jsonPrimitive?.intOrNull?.let { if (it != 0) append("Vol. $it, ") }
|
||||
append("Ch. ${chapter_number.format("#.#")}: ")
|
||||
append(obj.getString("title"))
|
||||
append(obj["title"]!!.jsonPrimitive.content)
|
||||
}
|
||||
)
|
||||
if (obj.getBoolean("final")) name += " [END]"
|
||||
if (obj["final"]!!.jsonPrimitive.boolean) name += " [END]"
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class MangAdventureGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "MangAdventure"
|
||||
|
||||
override val baseVersionCode = 1
|
||||
override val baseVersionCode = 2
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("Arc-Relight", "https://arc-relight.com", "en", className = "ArcRelight"),
|
||||
|
|
|
@ -11,6 +11,9 @@ 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.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.Headers
|
||||
|
@ -19,10 +22,10 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
@ -76,6 +79,8 @@ open class Webtoons(
|
|||
}
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaSelector() = "not using"
|
||||
|
||||
override fun latestUpdatesSelector() = "div#dailyList > $day li > a"
|
||||
|
@ -262,14 +267,16 @@ open class Webtoons(
|
|||
|
||||
val docUrl = docUrlRegex.find(docString)!!.destructured.toList()[0]
|
||||
val motiontoonPath = motiontoonPathRegex.find(docString)!!.destructured.toList()[0]
|
||||
val motiontoonResponse = client.newCall(GET(docUrl, headers)).execute()
|
||||
|
||||
val motiontoonJson = JSONObject(client.newCall(GET(docUrl, headers)).execute().body!!.string()).getJSONObject("assets").getJSONObject("image")
|
||||
val motiontoonJson = json.parseToJsonElement(motiontoonResponse.body!!.string()).jsonObject
|
||||
val motiontoonImages = motiontoonJson["assets"]!!.jsonObject["image"]!!.jsonObject
|
||||
|
||||
val keys = motiontoonJson.keys().asSequence().toList().filter { it.contains("layer") }
|
||||
|
||||
return keys.mapIndexed { i, key ->
|
||||
Page(i, "", motiontoonPath + motiontoonJson.getString(key))
|
||||
}
|
||||
return motiontoonImages.entries
|
||||
.filter { it.key.contains("layer") }
|
||||
.mapIndexed { i, entry ->
|
||||
Page(i, "", motiontoonPath + entry.value.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -10,7 +10,7 @@ class WebtoonsGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "Webtoons"
|
||||
|
||||
override val baseVersionCode: Int = 1
|
||||
override val baseVersionCode: Int = 2
|
||||
|
||||
override val sources = listOf(
|
||||
MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 29),
|
||||
|
|
|
@ -7,22 +7,32 @@ import eu.kanade.tachiyomi.source.model.MangasPage
|
|||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.boolean
|
||||
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 kotlinx.serialization.json.long
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import java.util.ArrayList
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
open class WebtoonsTranslate (
|
||||
open class WebtoonsTranslate(
|
||||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
override val lang: String,
|
||||
private val translateLangCode: String
|
||||
) : Webtoons(name, baseUrl, lang) {
|
||||
) : Webtoons(name, baseUrl, lang) {
|
||||
|
||||
// popularMangaRequest already returns manga sorted by latest update
|
||||
override val supportsLatest = false
|
||||
|
||||
|
@ -30,10 +40,9 @@ open class WebtoonsTranslate (
|
|||
private val mobileBaseUrl = "https://m.webtoons.com".toHttpUrlOrNull()!!
|
||||
private val thumbnailBaseUrl = "https://mwebtoon-phinf.pstatic.net"
|
||||
|
||||
private val pageListUrlPattern = "/lineWebtoon/ctrans/translatedEpisodeDetail_jsonp.json?titleNo=%s&episodeNo=%d&languageCode=%s&teamVersion=%d"
|
||||
|
||||
private val pageSize = 24
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
|
||||
.removeAll("Referer")
|
||||
|
@ -57,44 +66,38 @@ open class WebtoonsTranslate (
|
|||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val offset = response.request.url.queryParameter("offset")!!.toInt()
|
||||
var totalCount: Int
|
||||
val mangas = mutableListOf<SManga>()
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
val responseCode = result["code"]!!.jsonPrimitive.content
|
||||
|
||||
JSONObject(response.body!!.string()).let { json ->
|
||||
json.getString("code").let { code ->
|
||||
if (code != "000") throw Exception("Error getting popular manga: error code $code")
|
||||
}
|
||||
|
||||
json.getJSONObject("result").let { results ->
|
||||
totalCount = results.getInt("totalCount")
|
||||
|
||||
results.getJSONArray("titleList").let { array ->
|
||||
for (i in 0 until array.length()) {
|
||||
mangas.add(mangaFromJson(array[i] as JSONObject))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (responseCode != "000") {
|
||||
throw Exception("Error getting popular manga: error code $responseCode")
|
||||
}
|
||||
|
||||
return MangasPage(mangas, totalCount > pageSize + offset)
|
||||
val titles = result["result"]!!.jsonObject
|
||||
val totalCount = titles["totalCount"]!!.jsonPrimitive.int
|
||||
|
||||
val mangaList = titles["titleList"]!!.jsonArray
|
||||
.map { mangaFromJson(it.jsonObject) }
|
||||
|
||||
return MangasPage(mangaList, hasNextPage = totalCount > pageSize + offset)
|
||||
}
|
||||
|
||||
private fun mangaFromJson(json: JSONObject): SManga {
|
||||
val relativeThumnailURL = json.getString("thumbnailIPadUrl")
|
||||
?: json.getString("thumbnailMobileUrl")
|
||||
private fun mangaFromJson(manga: JsonObject): SManga {
|
||||
val relativeThumnailURL = manga["thumbnailIPadUrl"]?.jsonPrimitive?.contentOrNull
|
||||
?: manga["thumbnailMobileUrl"]?.jsonPrimitive?.contentOrNull
|
||||
|
||||
return SManga.create().apply {
|
||||
title = json.getString("representTitle")
|
||||
author = json.getString("writeAuthorName")
|
||||
artist = json.getString("pictureAuthorName") ?: author
|
||||
title = manga["representTitle"]!!.jsonPrimitive.content
|
||||
author = manga["writeAuthorName"]!!.jsonPrimitive.content
|
||||
artist = manga["pictureAuthorName"]?.jsonPrimitive?.contentOrNull ?: author
|
||||
thumbnail_url = if (relativeThumnailURL != null) "$thumbnailBaseUrl$relativeThumnailURL" else null
|
||||
status = SManga.UNKNOWN
|
||||
url = mobileBaseUrl
|
||||
.resolve("/translate/episodeList")!!
|
||||
.newBuilder()
|
||||
.addQueryParameter("titleNo", json.getInt("titleNo").toString())
|
||||
.addQueryParameter("titleNo", manga["titleNo"]!!.jsonPrimitive.int.toString())
|
||||
.addQueryParameter("languageCode", translateLangCode)
|
||||
.addQueryParameter("teamVersion", json.optInt("teamVersion", 0).toString())
|
||||
.addQueryParameter("teamVersion", (manga["teamVersion"]?.jsonPrimitive?.intOrNull ?: 0).toString())
|
||||
.build()
|
||||
.toString()
|
||||
}
|
||||
|
@ -116,24 +119,18 @@ open class WebtoonsTranslate (
|
|||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = mangaRequest(page, 200)
|
||||
|
||||
private fun searchMangaParse(response: Response, query: String): MangasPage {
|
||||
val mangas = mutableListOf<SManga>()
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
val responseCode = result["code"]!!.jsonPrimitive.content
|
||||
|
||||
JSONObject(response.body!!.string()).let { json ->
|
||||
json.getString("code").let { code ->
|
||||
if (code != "000") throw Exception("Error getting manga: error code $code")
|
||||
}
|
||||
|
||||
json.getJSONObject("result").getJSONArray("titleList").let { array ->
|
||||
for (i in 0 until array.length()) {
|
||||
(array[i] as JSONObject).let { jsonManga ->
|
||||
if (jsonManga.getString("representTitle").contains(query, ignoreCase = true))
|
||||
mangas.add(mangaFromJson(jsonManga))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (responseCode != "000") {
|
||||
throw Exception("Error getting manga: error code $responseCode")
|
||||
}
|
||||
|
||||
return MangasPage(mangas, false)
|
||||
val mangaList = result["result"]!!.jsonObject["titleList"]!!.jsonArray
|
||||
.map { mangaFromJson(it.jsonObject) }
|
||||
.filter { it.title.contains(query, ignoreCase = true) }
|
||||
|
||||
return MangasPage(mangaList, false)
|
||||
}
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
|
@ -170,7 +167,7 @@ open class WebtoonsTranslate (
|
|||
override fun chapterListRequest(manga: SManga): Request {
|
||||
val titleNo = manga.url.toHttpUrlOrNull()!!
|
||||
.queryParameter("titleNo")
|
||||
val chapterUrl = apiBaseUrl
|
||||
val chapterListUrl = apiBaseUrl
|
||||
.resolve("/lineWebtoon/ctrans/translatedEpisodes_jsonp.json")!!
|
||||
.newBuilder()
|
||||
.addQueryParameter("titleNo", titleNo)
|
||||
|
@ -178,38 +175,40 @@ open class WebtoonsTranslate (
|
|||
.addQueryParameter("offset", "0")
|
||||
.addQueryParameter("limit", "10000")
|
||||
.toString()
|
||||
return GET(chapterUrl, mobileHeaders)
|
||||
return GET(chapterListUrl, mobileHeaders)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val chapterData = response.body!!.string()
|
||||
val chapterJson = JSONObject(chapterData)
|
||||
val responseCode = chapterJson.getString("code")
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
val responseCode = result["code"]!!.jsonPrimitive.content
|
||||
|
||||
if (responseCode != "000") {
|
||||
val message = chapterJson.optString("message", "error code $responseCode")
|
||||
val message = result["message"]?.jsonPrimitive?.content ?: "error code $responseCode"
|
||||
throw Exception("Error getting chapter list: $message")
|
||||
}
|
||||
val results = chapterJson.getJSONObject("result").getJSONArray("episodes")
|
||||
val ret = ArrayList<SChapter>()
|
||||
for (i in 0 until results.length()) {
|
||||
val result = results.getJSONObject(i)
|
||||
if (result.getBoolean("translateCompleted")) {
|
||||
ret.add(parseChapterJson(result))
|
||||
}
|
||||
}
|
||||
ret.reverse()
|
||||
return ret
|
||||
|
||||
return result["result"]!!.jsonObject["episodes"]!!.jsonArray
|
||||
.filter { it.jsonObject["translateCompleted"]!!.jsonPrimitive.boolean }
|
||||
.map { parseChapterJson(it.jsonObject) }
|
||||
.reversed()
|
||||
}
|
||||
|
||||
private fun parseChapterJson(obj: JSONObject) = SChapter.create().apply {
|
||||
name = obj.getString("title") + " #" + obj.getString("episodeSeq")
|
||||
chapter_number = obj.getInt("episodeSeq").toFloat()
|
||||
date_upload = obj.getLong("updateYmdt")
|
||||
scanlator = obj.getString("teamVersion")
|
||||
if (scanlator == "0") {
|
||||
scanlator = "(wiki)"
|
||||
}
|
||||
url = String.format(pageListUrlPattern, obj.getInt("titleNo"), obj.getInt("episodeNo"), obj.getString("languageCode"), obj.getInt("teamVersion"))
|
||||
private fun parseChapterJson(obj: JsonObject): SChapter = SChapter.create().apply {
|
||||
name = obj["title"]!!.jsonPrimitive.content + " #" + obj["episodeSeq"]!!.jsonPrimitive.int
|
||||
chapter_number = obj["episodeSeq"]!!.jsonPrimitive.int.toFloat()
|
||||
date_upload = obj["updateYmdt"]!!.jsonPrimitive.long
|
||||
scanlator = obj["teamVersion"]!!.jsonPrimitive.int.takeIf { it != 0 }?.toString() ?: "(wiki)"
|
||||
|
||||
val chapterUrl = apiBaseUrl
|
||||
.resolve("/lineWebtoon/ctrans/translatedEpisodeDetail_jsonp.json")!!
|
||||
.newBuilder()
|
||||
.addQueryParameter("titleNo", obj["titleNo"]!!.jsonPrimitive.int.toString())
|
||||
.addQueryParameter("episodeNo", obj["episodeNo"]!!.jsonPrimitive.int.toString())
|
||||
.addQueryParameter("languageCode", obj["languageCode"]!!.jsonPrimitive.content)
|
||||
.addQueryParameter("teamVersion", obj["teamVersion"]!!.jsonPrimitive.int.toString())
|
||||
.toString()
|
||||
|
||||
setUrlWithoutDomain(chapterUrl)
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
|
@ -217,14 +216,12 @@ open class WebtoonsTranslate (
|
|||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val pageJson = JSONObject(response.body!!.string())
|
||||
val results = pageJson.getJSONObject("result").getJSONArray("imageInfo")
|
||||
val ret = ArrayList<Page>()
|
||||
for (i in 0 until results.length()) {
|
||||
val result = results.getJSONObject(i)
|
||||
ret.add(Page(i, "", result.getString("imageUrl")))
|
||||
}
|
||||
return ret
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
|
||||
return result["result"]!!.jsonObject["imageInfo"]!!.jsonArray
|
||||
.mapIndexed { i, jsonEl ->
|
||||
Page(i, "", jsonEl.jsonObject["imageUrl"]!!.jsonPrimitive.content)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFilterList(): FilterList = FilterList()
|
||||
|
|
|
@ -9,7 +9,7 @@ class WebtoonsTranslateGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "WebtoonsTranslation"
|
||||
|
||||
override val baseVersionCode: Int = 1
|
||||
override val baseVersionCode: Int = 2
|
||||
|
||||
override val sources = listOf(
|
||||
MultiLang("Webtoons.com Translations", "https://translate.webtoons.com", listOf("en", "zh-hans", "zh-hant", "th", "id", "fr", "vi", "ru", "ar", "fil", "de", "hi", "it", "ja", "pt-BR", "tr", "ms", "pl", "pt", "bg", "da", "nl", "ro", "mn", "el", "lt", "cs", "sv", "bn", "fa", "uk", "es"), className = "WebtoonsTranslateFactory", pkgName = "webtoonstranslate", overrideVersionCode = 1),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.multisrc.wpmangareader
|
||||
|
||||
import android.util.Log
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
|
@ -11,16 +10,19 @@ 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.jsonPrimitive
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import rx.Single
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -36,6 +38,8 @@ abstract class WPMangaReader(
|
|||
|
||||
override val client: OkHttpClient = network.cloudflareClient
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
// popular
|
||||
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList(OrderByFilter(5)))
|
||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||
|
@ -65,7 +69,7 @@ abstract class WPMangaReader(
|
|||
* @returns An identifier for a manga, or null if none could be found
|
||||
*/
|
||||
protected open fun mangaIdFromUrl(s: String): Single<String?> {
|
||||
var baseMangaUrl = "$baseUrl$mangaUrlDirectory".toHttpUrlOrNull()!!
|
||||
val baseMangaUrl = "$baseUrl$mangaUrlDirectory".toHttpUrlOrNull()!!
|
||||
return s.toHttpUrlOrNull()?.let { url ->
|
||||
fun pathLengthIs(url: HttpUrl, n: Int, strict: Boolean = false) = url.pathSegments.size == n && url.pathSegments[n - 1].isNotEmpty() || (!strict && url.pathSegments.size == n + 1 && url.pathSegments[n].isEmpty())
|
||||
val isMangaUrl = listOf(
|
||||
|
@ -226,7 +230,7 @@ abstract class WPMangaReader(
|
|||
open val pageSelector = "div#readerarea img"
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
var pages = mutableListOf<Page>()
|
||||
val pages = mutableListOf<Page>()
|
||||
document.select(pageSelector)
|
||||
.filterNot { it.attr("src").isNullOrEmpty() }
|
||||
.mapIndexed { i, img -> pages.add(Page(i, "", img.attr("abs:src"))) }
|
||||
|
@ -236,11 +240,12 @@ abstract class WPMangaReader(
|
|||
|
||||
val docString = document.toString()
|
||||
val imageListRegex = Regex("\\\"images.*?:.*?(\\[.*?\\])")
|
||||
val imageListJson = imageListRegex.find(docString)!!.destructured.toList()[0]
|
||||
|
||||
val imageList = JSONArray(imageListRegex.find(docString)!!.destructured.toList()[0])
|
||||
val imageList = json.parseToJsonElement(imageListJson).jsonArray
|
||||
|
||||
for (i in 0 until imageList.length()) {
|
||||
pages.add(Page(i, "", imageList.getString(i)))
|
||||
pages += imageList.mapIndexed { i, jsonEl ->
|
||||
Page(i, "", jsonEl.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
return pages
|
||||
|
|
|
@ -9,7 +9,7 @@ class WPMangaReaderGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "WPMangaReader"
|
||||
|
||||
override val baseVersionCode: Int = 6
|
||||
override val baseVersionCode: Int = 7
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("Kiryuu", "https://kiryuu.id", "id"),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.kanade.tachiyomi.multisrc.wpmangastream
|
||||
|
||||
//import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor // added to override
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
|
@ -12,17 +11,20 @@ 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.jsonPrimitive
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.select.Elements
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
@ -67,17 +69,16 @@ abstract class WPMangaStream(
|
|||
|
||||
private fun getShowThumbnail(): Int = preferences.getInt(SHOW_THUMBNAIL_PREF, 0)
|
||||
|
||||
//private val rateLimitInterceptor = RateLimitInterceptor(4)
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
//.addNetworkInterceptor(rateLimitInterceptor)
|
||||
.build()
|
||||
|
||||
protected fun Element.imgAttr(): String = if (this.hasAttr("data-src")) this.attr("abs:data-src") else this.attr("abs:src")
|
||||
protected fun Elements.imgAttr(): String = this.first().imgAttr()
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
return GET("$baseUrl/manga/?page=$page&order=popular", headers)
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ abstract class WPMangaStream(
|
|||
|
||||
// add alternative name to manga description
|
||||
document.select(altNameSelector).firstOrNull()?.ownText()?.let {
|
||||
if (it.isEmpty().not() && it !="N/A" && it != "-") {
|
||||
if (it.isEmpty().not() && it != "N/A" && it != "-") {
|
||||
description += when {
|
||||
description!!.isEmpty() -> altName + it
|
||||
else -> "\n\n$altName" + it
|
||||
|
@ -182,7 +183,7 @@ abstract class WPMangaStream(
|
|||
open val altNameSelector = ".alternative, .wd-full:contains(Alt) span, .alter, .seriestualt"
|
||||
open val altName = "Alternative Name" + ": "
|
||||
|
||||
protected fun parseStatus(element: String?): Int = when {
|
||||
protected open fun parseStatus(element: String?): Int = when {
|
||||
element == null -> SManga.UNKNOWN
|
||||
listOf("ongoing", "publishing").any { it.contains(element, ignoreCase = true) } -> SManga.ONGOING
|
||||
listOf("completed").any { it.contains(element, ignoreCase = true) } -> SManga.COMPLETED
|
||||
|
@ -265,24 +266,26 @@ abstract class WPMangaStream(
|
|||
open val pageSelector = "div#readerarea img"
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
var pages = mutableListOf<Page>()
|
||||
document.select(pageSelector)
|
||||
val htmlPages = document.select(pageSelector)
|
||||
.filterNot { it.attr("src").isNullOrEmpty() }
|
||||
.mapIndexed { i, img -> pages.add(Page(i, "", img.attr("abs:src"))) }
|
||||
|
||||
// Some wpmangastream sites now load pages via javascript
|
||||
if (pages.isNotEmpty()) { return pages }
|
||||
.mapIndexed { i, img -> Page(i, "", img.attr("abs:src")) }
|
||||
.toMutableList()
|
||||
|
||||
val docString = document.toString()
|
||||
val imageListRegex = Regex("\\\"images.*?:.*?(\\[.*?\\])")
|
||||
val imageListJson = imageListRegex.find(docString)!!.destructured.toList()[0]
|
||||
|
||||
val imageList = JSONArray(imageListRegex.find(docString)!!.destructured.toList()[0])
|
||||
val imageList = json.parseToJsonElement(imageListJson).jsonArray
|
||||
|
||||
for (i in 0 until imageList.length()) {
|
||||
pages.add(Page(i, "", imageList.getString(i)))
|
||||
val scriptPages = imageList.mapIndexed { i, jsonEl ->
|
||||
Page(i, "", jsonEl.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
return pages
|
||||
if (htmlPages.size < scriptPages.size) {
|
||||
htmlPages += scriptPages
|
||||
}
|
||||
|
||||
return htmlPages
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
||||
|
|
|
@ -9,10 +9,10 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
|
|||
|
||||
override val themeClass = "WPMangaStream"
|
||||
|
||||
override val baseVersionCode: Int = 4
|
||||
override val baseVersionCode: Int = 5
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("Asura Scans", "override url", "en", overrideVersionCode = 1),
|
||||
SingleLang("Asura Scans", "https://www.asurascans.com", "en", overrideVersionCode = 1),
|
||||
SingleLang("KlanKomik", "https://klankomik.com", "id"),
|
||||
SingleLang("MasterKomik", "https://masterkomik.com", "id"),
|
||||
SingleLang("Kaisar Komik", "https://kaisarkomik.com", "id"),
|
||||
|
@ -34,7 +34,7 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
|
|||
SingleLang("MangaSwat", "https://mangaswat.com", "ar"),
|
||||
SingleLang("Manga Raw.org", "https://mangaraw.org", "ja", className = "MangaRawOrg", overrideVersionCode = 1),
|
||||
SingleLang("Manga Pro Z", "https://mangaproz.com", "ar"),
|
||||
SingleLang("Silence Scan", "https://silencescan.net", "pt-BR", overrideVersionCode = 1),
|
||||
SingleLang("Silence Scan", "https://silencescan.net", "pt-BR", overrideVersionCode = 2),
|
||||
SingleLang("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en", className = "KumaScans"),
|
||||
SingleLang("Tempest Manga", "https://manga.tempestfansub.com", "tr"),
|
||||
SingleLang("xCaliBR Scans", "https://xcalibrscans.com", "en", overrideVersionCode = 2),
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Cubari'
|
||||
pkgNameSuffix = "all.cubari"
|
||||
extClass = '.CubariFactory'
|
||||
extVersionCode = 5
|
||||
extVersionCode = 6
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -11,21 +11,32 @@ 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.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 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.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
open class Cubari(override val lang: String) : HttpSource() {
|
||||
|
||||
final override val name = "Cubari"
|
||||
|
||||
final override val baseUrl = "https://cubari.moe"
|
||||
|
||||
final override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun headersBuilder() = Headers.Builder().apply {
|
||||
add(
|
||||
"User-Agent",
|
||||
|
@ -50,7 +61,8 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
return parseMangaList(JSONArray(response.body!!.string()), SortType.UNPINNED)
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonArray
|
||||
return parseMangaList(result, SortType.UNPINNED)
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
|
@ -67,7 +79,8 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
return parseMangaList(JSONArray(response.body!!.string()), SortType.PINNED)
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonArray
|
||||
return parseMangaList(result, SortType.PINNED)
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
|
@ -86,7 +99,8 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
}
|
||||
|
||||
private fun mangaDetailsParse(response: Response, manga: SManga): SManga {
|
||||
return parseMangaFromApi(JSONObject(response.body!!.string()), manga)
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
return parseManga(result, manga)
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
|
@ -139,7 +153,7 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
GET("$baseUrl${chapter.url}", headers)
|
||||
}
|
||||
else -> {
|
||||
var url = chapter.url.split("/")
|
||||
val url = chapter.url.split("/")
|
||||
val source = url[2]
|
||||
val slug = url[3]
|
||||
|
||||
|
@ -150,55 +164,49 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
|
||||
private fun directPageListParse(response: Response): List<Page> {
|
||||
val res = response.body!!.string()
|
||||
val pages = JSONArray(res)
|
||||
val pageArray = ArrayList<Page>()
|
||||
val pages = json.parseToJsonElement(res).jsonArray
|
||||
|
||||
for (i in 0 until pages.length()) {
|
||||
val page = if (pages.optJSONObject(i) != null) {
|
||||
pages.getJSONObject(i).getString("src")
|
||||
return pages.mapIndexed { i, jsonEl ->
|
||||
val page = if (jsonEl is JsonObject) {
|
||||
jsonEl.jsonObject["src"]!!.jsonPrimitive.content
|
||||
} else {
|
||||
pages[i]
|
||||
jsonEl.jsonPrimitive.content
|
||||
}
|
||||
pageArray.add(Page(i + 1, "", page.toString()))
|
||||
|
||||
Page(i, "", page)
|
||||
}
|
||||
return pageArray
|
||||
}
|
||||
|
||||
private fun seriesJsonPageListParse(response: Response, chapter: SChapter): List<Page> {
|
||||
val res = response.body!!.string()
|
||||
val json = JSONObject(res)
|
||||
val groups = json.getJSONObject("groups")
|
||||
val groupIter = groups.keys()
|
||||
val groupMap = HashMap<String, String>()
|
||||
val jsonObj = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
val groups = jsonObj["groups"]!!.jsonObject
|
||||
val groupMap = groups.entries
|
||||
.map { Pair(it.value.jsonPrimitive.content, it.key) }
|
||||
.toMap()
|
||||
|
||||
while (groupIter.hasNext()) {
|
||||
val groupKey = groupIter.next()
|
||||
groupMap[groups.getString(groupKey)] = groupKey
|
||||
}
|
||||
val chapters = jsonObj["chapters"]!!.jsonObject
|
||||
|
||||
val chapters = json.getJSONObject("chapters")
|
||||
|
||||
val pages = if (chapters.has(chapter.chapter_number.toString())) {
|
||||
chapters
|
||||
.getJSONObject(chapter.chapter_number.toString())
|
||||
.getJSONObject("groups")
|
||||
.getJSONArray(groupMap[chapter.scanlator])
|
||||
val pages = if (chapters[chapter.chapter_number.toString()] != null) {
|
||||
chapters[chapter.chapter_number.toString()]!!
|
||||
.jsonObject["groups"]!!
|
||||
.jsonObject[groupMap[chapter.scanlator]]!!
|
||||
.jsonArray
|
||||
} else {
|
||||
chapters
|
||||
.getJSONObject(chapter.chapter_number.toInt().toString())
|
||||
.getJSONObject("groups")
|
||||
.getJSONArray(groupMap[chapter.scanlator])
|
||||
chapters[chapter.chapter_number.toInt().toString()]!!
|
||||
.jsonObject["groups"]!!
|
||||
.jsonObject[groupMap[chapter.scanlator]]!!
|
||||
.jsonArray
|
||||
}
|
||||
val pageArray = ArrayList<Page>()
|
||||
for (i in 0 until pages.length()) {
|
||||
val page = if (pages.optJSONObject(i) != null) {
|
||||
pages.getJSONObject(i).getString("src")
|
||||
|
||||
return pages.mapIndexed { i, jsonEl ->
|
||||
val page = if (jsonEl is JsonObject) {
|
||||
jsonEl.jsonObject["src"]!!.jsonPrimitive.content
|
||||
} else {
|
||||
pages[i]
|
||||
jsonEl.jsonPrimitive.content
|
||||
}
|
||||
pageArray.add(Page(i + 1, "", page.toString()))
|
||||
|
||||
Page(i, "", page)
|
||||
}
|
||||
return pageArray
|
||||
}
|
||||
|
||||
// Stub
|
||||
|
@ -241,125 +249,105 @@ open class Cubari(override val lang: String) : HttpSource() {
|
|||
}
|
||||
|
||||
private fun searchMangaParse(response: Response, query: String): MangasPage {
|
||||
return parseSearchList(JSONObject(response.body!!.string()), query)
|
||||
val result = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
return parseSearchList(result, query)
|
||||
}
|
||||
|
||||
// ------------- Helpers and whatnot ---------------
|
||||
|
||||
private fun parseChapterList(payload: String, manga: SManga): List<SChapter> {
|
||||
val json = JSONObject(payload)
|
||||
val groups = json.getJSONObject("groups")
|
||||
val chapters = json.getJSONObject("chapters")
|
||||
val seriesSlug = json.getString("slug")
|
||||
val jsonObj = json.parseToJsonElement(payload).jsonObject
|
||||
val groups = jsonObj["groups"]!!.jsonObject
|
||||
val chapters = jsonObj["chapters"]!!.jsonObject
|
||||
val seriesSlug = jsonObj["slug"]!!.jsonPrimitive.content
|
||||
|
||||
|
||||
val chapterList = ArrayList<SChapter>()
|
||||
|
||||
val iter = chapters.keys()
|
||||
|
||||
val seriesPrefs = Injekt.get<Application>().getSharedPreferences("source_${id}_updateTime:$seriesSlug", 0)
|
||||
val seriesPrefsEditor = seriesPrefs.edit()
|
||||
|
||||
while (iter.hasNext()) {
|
||||
val chapterNum = iter.next()
|
||||
val chapterObj = chapters.getJSONObject(chapterNum)
|
||||
val chapterGroups = chapterObj.getJSONObject("groups")
|
||||
val groupsIter = chapterGroups.keys()
|
||||
val chapterList = chapters.entries.flatMap { chapterEntry ->
|
||||
val chapterNum = chapterEntry.key
|
||||
val chapterObj = chapterEntry.value.jsonObject
|
||||
val chapterGroups = chapterObj["groups"]!!.jsonObject
|
||||
|
||||
while (groupsIter.hasNext()) {
|
||||
val groupNum = groupsIter.next()
|
||||
val chapter = SChapter.create()
|
||||
chapterGroups.entries.map { groupEntry ->
|
||||
val groupNum = groupEntry.key
|
||||
|
||||
chapter.scanlator = groups.getString(groupNum)
|
||||
|
||||
//Api for gist (and some others maybe) doesn't give a "release_date" so we will use the Manga update time.
|
||||
//So when new chapter comes the manga will go on top if sortinf is set to "Last Updated"
|
||||
//Code by ivaniskandar (Implemented on CatManga extension.)
|
||||
|
||||
if (chapterObj.has("release_date")) {
|
||||
chapter.date_upload =
|
||||
chapterObj.getJSONObject("release_date").getLong(groupNum) * 1000
|
||||
} else {
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
if (!seriesPrefs.contains(chapterNum)) {
|
||||
seriesPrefsEditor.putLong(chapterNum, currentTimeMillis)
|
||||
SChapter.create().apply {
|
||||
scanlator = groups[groupNum]!!.jsonPrimitive.content
|
||||
chapter_number = chapterNum.toFloatOrNull() ?: -1f
|
||||
|
||||
date_upload = if (chapterObj["release_date"] != null) {
|
||||
chapterObj["release_date"]!!.jsonObject[groupNum]!!.jsonPrimitive.long * 1000
|
||||
} else {
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
|
||||
if (!seriesPrefs.contains(chapterNum)) {
|
||||
seriesPrefsEditor.putLong(chapterNum, currentTimeMillis)
|
||||
}
|
||||
|
||||
seriesPrefs.getLong(chapterNum, currentTimeMillis)
|
||||
}
|
||||
chapter.date_upload = seriesPrefs.getLong(chapterNum, currentTimeMillis)
|
||||
}
|
||||
chapter.name = if (chapterObj.getString("volume") != "Uncategorized") {
|
||||
|
||||
"Vol. " + chapterObj.getString("volume") + " Ch. " + chapterNum + " - " + chapterObj.getString("title")
|
||||
//Output "Vol. 1 Ch. 1 - Chapter Name"
|
||||
|
||||
} else {
|
||||
|
||||
"Ch. " + chapterNum + " - " + chapterObj.getString("title")
|
||||
//Output "Ch. 1 - Chapter Name"
|
||||
|
||||
}
|
||||
chapter.chapter_number = chapterNum.toFloat()
|
||||
chapter.url =
|
||||
if (chapterGroups.optJSONArray(groupNum) != null) {
|
||||
|
||||
name = if (chapterObj["volume"]!!.jsonPrimitive.content != "Uncategorized") {
|
||||
// Output "Vol. 1 Ch. 1 - Chapter Name"
|
||||
"Vol. " + chapterObj["volume"]!!.jsonPrimitive.content + " Ch. " +
|
||||
chapterNum + " - " + chapterObj["title"]!!.jsonPrimitive.content
|
||||
} else {
|
||||
// Output "Ch. 1 - Chapter Name"
|
||||
"Ch. " + chapterNum + " - " + chapterObj["title"]!!.jsonPrimitive.content
|
||||
}
|
||||
|
||||
url = if (chapterGroups[groupNum] != null) {
|
||||
"${manga.url}/$chapterNum/$groupNum"
|
||||
} else {
|
||||
chapterGroups.getString(groupNum)
|
||||
chapterGroups[groupNum]!!.jsonPrimitive.content
|
||||
}
|
||||
chapterList.add(chapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seriesPrefsEditor.apply()
|
||||
return chapterList.reversed()
|
||||
|
||||
return chapterList.sortedByDescending { it.chapter_number }
|
||||
}
|
||||
|
||||
private fun parseMangaList(payload: JSONArray, sortType: SortType): MangasPage {
|
||||
val mangas = ArrayList<SManga>()
|
||||
|
||||
for (i in 0 until payload.length()) {
|
||||
val json = payload.getJSONObject(i)
|
||||
val pinned = json.getBoolean("pinned")
|
||||
private fun parseMangaList(payload: JsonArray, sortType: SortType): MangasPage {
|
||||
val mangaList = payload.mapNotNull { jsonEl ->
|
||||
val jsonObj = jsonEl.jsonObject
|
||||
val pinned = jsonObj["pinned"]!!.jsonPrimitive.boolean
|
||||
|
||||
if (sortType == SortType.PINNED && pinned) {
|
||||
mangas.add(parseMangaFromRemoteStorage(json))
|
||||
parseManga(jsonObj)
|
||||
} else if (sortType == SortType.UNPINNED && !pinned) {
|
||||
mangas.add(parseMangaFromRemoteStorage(json))
|
||||
parseManga(jsonObj)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return MangasPage(mangas, false)
|
||||
return MangasPage(mangaList, false)
|
||||
}
|
||||
|
||||
private fun parseSearchList(payload: JSONObject, query: String): MangasPage {
|
||||
val mangas = ArrayList<SManga>()
|
||||
val tempManga = SManga.create()
|
||||
tempManga.url = "/read/$query"
|
||||
mangas.add(parseMangaFromApi(payload, tempManga))
|
||||
return MangasPage(mangas, false)
|
||||
private fun parseSearchList(payload: JsonObject, query: String): MangasPage {
|
||||
val tempManga = SManga.create().apply {
|
||||
url = "/read/$query"
|
||||
}
|
||||
|
||||
val mangaList = listOf(parseManga(payload, tempManga))
|
||||
|
||||
return MangasPage(mangaList, false)
|
||||
}
|
||||
|
||||
private fun parseMangaFromRemoteStorage(json: JSONObject): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.title = json.getString("title")
|
||||
manga.artist = json.optString("artist", ARTIST_FALLBACK)
|
||||
manga.author = json.optString("author", AUTHOR_FALLBACK)
|
||||
manga.description = json.optString("description", DESCRIPTION_FALLBACK)
|
||||
manga.url = json.getString("url")
|
||||
manga.thumbnail_url = json.getString("coverUrl")
|
||||
|
||||
return manga
|
||||
}
|
||||
|
||||
private fun parseMangaFromApi(json: JSONObject, mangaReference: SManga): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.title = json.getString("title")
|
||||
manga.artist = json.optString("artist", ARTIST_FALLBACK)
|
||||
manga.author = json.optString("author", AUTHOR_FALLBACK)
|
||||
manga.description = json.optString("description", DESCRIPTION_FALLBACK)
|
||||
manga.url = mangaReference.url
|
||||
manga.thumbnail_url = json.optString("cover", "")
|
||||
|
||||
return manga
|
||||
}
|
||||
private fun parseManga(jsonObj: JsonObject, mangaReference: SManga? = null): SManga =
|
||||
SManga.create().apply {
|
||||
title = jsonObj["title"]!!.jsonPrimitive.content
|
||||
artist = jsonObj["artist"]?.jsonPrimitive?.content ?: ARTIST_FALLBACK
|
||||
author = jsonObj["author"]?.jsonPrimitive?.content ?: AUTHOR_FALLBACK
|
||||
description = jsonObj["description"]?.jsonPrimitive?.content ?: DESCRIPTION_FALLBACK
|
||||
url = mangaReference?.url ?: jsonObj["url"]!!.jsonPrimitive.content
|
||||
thumbnail_url = jsonObj["coverUrl"]?.jsonPrimitive?.content
|
||||
?: jsonObj["cover"]?.jsonPrimitive?.content ?: ""
|
||||
}
|
||||
|
||||
// ----------------- Things we aren't supporting -----------------
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Bilibili Comics'
|
||||
pkgNameSuffix = 'en.bilibilicomics'
|
||||
extClass = '.BilibiliComics'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
libVersion = '1.2'
|
||||
containsNsfw = true
|
||||
}
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
package eu.kanade.tachiyomi.extension.en.bilibilicomics
|
||||
|
||||
import com.github.salomonbrys.kotson.array
|
||||
import com.github.salomonbrys.kotson.bool
|
||||
import com.github.salomonbrys.kotson.float
|
||||
import com.github.salomonbrys.kotson.get
|
||||
import com.github.salomonbrys.kotson.int
|
||||
import com.github.salomonbrys.kotson.jsonArray
|
||||
import com.github.salomonbrys.kotson.jsonObject
|
||||
import com.github.salomonbrys.kotson.obj
|
||||
import com.github.salomonbrys.kotson.string
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.annotations.Nsfw
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
|
@ -21,6 +10,12 @@ 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.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
@ -30,6 +25,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||
import okhttp3.Response
|
||||
import org.jsoup.Jsoup
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
@ -55,14 +51,16 @@ class BilibiliComics : HttpSource() {
|
|||
.add("Origin", baseUrl)
|
||||
.add("Referer", "$baseUrl/")
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request {
|
||||
val requestPayload = jsonObject(
|
||||
"id" to FEATURED_ID,
|
||||
"isAll" to 0,
|
||||
"page_num" to 1,
|
||||
"page_size" to 6
|
||||
)
|
||||
val requestBody = requestPayload.toString().toRequestBody(JSON_CONTENT_TYPE)
|
||||
val requestPayload = buildJsonObject {
|
||||
put("id", FEATURED_ID)
|
||||
put("isAll", 0)
|
||||
put("page_num", 1)
|
||||
put("page_size", 6)
|
||||
}
|
||||
val requestBody = requestPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Content-Length", requestBody.contentLength().toString())
|
||||
|
@ -77,36 +75,36 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<BilibiliFeaturedDto>>(response.body!!.string())
|
||||
|
||||
if (jsonResponse["code"].int != 0) {
|
||||
if (result.code != 0) {
|
||||
return MangasPage(emptyList(), hasNextPage = false)
|
||||
}
|
||||
|
||||
val comicList = jsonResponse["data"]["roll_six_comics"].array
|
||||
val comicList = result.data!!.rollSixComics
|
||||
.map(::popularMangaFromObject)
|
||||
|
||||
return MangasPage(comicList, hasNextPage = false)
|
||||
}
|
||||
|
||||
private fun popularMangaFromObject(obj: JsonElement): SManga = SManga.create().apply {
|
||||
title = obj["title"].string
|
||||
thumbnail_url = obj["vertical_cover"].string
|
||||
url = "/detail/mc" + obj["comic_id"].int
|
||||
private fun popularMangaFromObject(comic: BilibiliComicDto): SManga = SManga.create().apply {
|
||||
title = comic.title
|
||||
thumbnail_url = comic.verticalCover
|
||||
url = "/detail/mc${comic.comicId}"
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val jsonPayload = jsonObject(
|
||||
"area_id" to -1,
|
||||
"is_finish" to -1,
|
||||
"is_free" to 1,
|
||||
"key_word" to query,
|
||||
"order" to 0,
|
||||
"page_num" to page,
|
||||
"page_size" to 9,
|
||||
"style_id" to -1
|
||||
)
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_CONTENT_TYPE)
|
||||
val jsonPayload = buildJsonObject {
|
||||
put("area_id", -1)
|
||||
put("is_finish", -1)
|
||||
put("is_free", 1)
|
||||
put("key_word", query)
|
||||
put("order", 0)
|
||||
put("page_num", page)
|
||||
put("page_size", 9)
|
||||
put("style_id", -1)
|
||||
}
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val refererUrl = "$baseUrl/search".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("keyword", query)
|
||||
|
@ -126,22 +124,22 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<BilibiliSearchDto>>(response.body!!.string())
|
||||
|
||||
if (jsonResponse["code"].int != 0) {
|
||||
if (result.code != 0) {
|
||||
return MangasPage(emptyList(), hasNextPage = false)
|
||||
}
|
||||
|
||||
val comicList = jsonResponse["data"]["list"].array
|
||||
val comicList = result.data!!.list
|
||||
.map(::searchMangaFromObject)
|
||||
|
||||
return MangasPage(comicList, hasNextPage = false)
|
||||
}
|
||||
|
||||
private fun searchMangaFromObject(obj: JsonElement): SManga = SManga.create().apply {
|
||||
title = Jsoup.parse(obj["title"].string).text()
|
||||
thumbnail_url = obj["vertical_cover"].string
|
||||
url = "/detail/mc" + obj["id"].int
|
||||
private fun searchMangaFromObject(comic: BilibiliComicDto): SManga = SManga.create().apply {
|
||||
title = Jsoup.parse(comic.title).text()
|
||||
thumbnail_url = comic.verticalCover
|
||||
url = "/detail/mc${comic.id}"
|
||||
}
|
||||
|
||||
// Workaround to allow "Open in browser" use the real URL.
|
||||
|
@ -156,8 +154,8 @@ class BilibiliComics : HttpSource() {
|
|||
private fun mangaDetailsApiRequest(manga: SManga): Request {
|
||||
val comicId = manga.url.substringAfterLast("/mc").toInt()
|
||||
|
||||
val jsonPayload = jsonObject("comic_id" to comicId)
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_CONTENT_TYPE)
|
||||
val jsonPayload = buildJsonObject { put("comic_id", comicId) }
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Content-Length", requestBody.contentLength().toString())
|
||||
|
@ -173,44 +171,45 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<BilibiliComicDto>>(response.body!!.string())
|
||||
val comic = result.data!!
|
||||
|
||||
title = jsonResponse["data"]["title"].string
|
||||
author = jsonResponse["data"]["author_name"].array.joinToString { it.string }
|
||||
status = if (jsonResponse["data"]["is_finish"].int == 1) SManga.COMPLETED else SManga.ONGOING
|
||||
genre = jsonResponse["data"]["styles"].array.joinToString { it.string }
|
||||
description = jsonResponse["data"]["classic_lines"].string
|
||||
thumbnail_url = jsonResponse["data"]["vertical_cover"].string
|
||||
title = comic.title
|
||||
author = comic.authorName.joinToString()
|
||||
status = if (comic.isFinish == 1) SManga.COMPLETED else SManga.ONGOING
|
||||
genre = comic.styles.joinToString()
|
||||
description = comic.classicLines
|
||||
thumbnail_url = comic.verticalCover
|
||||
}
|
||||
|
||||
// Chapters are available in the same url of the manga details.
|
||||
override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<BilibiliComicDto>>(response.body!!.string())
|
||||
|
||||
if (jsonResponse["code"].int != 0)
|
||||
if (result.code != 0)
|
||||
return emptyList()
|
||||
|
||||
return jsonResponse["data"]["ep_list"].array
|
||||
.filter { ep -> ep["is_locked"].bool.not() }
|
||||
.map { ep -> chapterFromObject(ep, jsonResponse["data"]["id"].int) }
|
||||
return result.data!!.episodeList
|
||||
.filter { episode -> episode.isLocked.not() }
|
||||
.map { ep -> chapterFromObject(ep, result.data.id) }
|
||||
}
|
||||
|
||||
private fun chapterFromObject(obj: JsonElement, comicId: Int): SChapter = SChapter.create().apply {
|
||||
name = "Ep. " + obj["ord"].float.toString().removeSuffix(".0") +
|
||||
" - " + obj["title"].string
|
||||
chapter_number = obj["ord"].float
|
||||
private fun chapterFromObject(episode: BilibiliEpisodeDto, comicId: Int): SChapter = SChapter.create().apply {
|
||||
name = "Ep. " + episode.order.toString().removeSuffix(".0") +
|
||||
" - " + episode.title
|
||||
chapter_number = episode.order
|
||||
scanlator = this@BilibiliComics.name
|
||||
date_upload = obj["pub_time"].string.substringBefore("T").toDate()
|
||||
url = "/mc" + comicId + "/" + obj["id"].int
|
||||
date_upload = episode.publicationTime.substringBefore("T").toDate()
|
||||
url = "/mc$comicId/${episode.id}"
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val chapterId = chapter.url.substringAfterLast("/").toInt()
|
||||
|
||||
val jsonPayload = jsonObject("ep_id" to chapterId)
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_CONTENT_TYPE)
|
||||
val jsonPayload = buildJsonObject { put("ep_id", chapterId) }
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Content-Length", requestBody.contentLength().toString())
|
||||
|
@ -226,19 +225,21 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<BilibiliReader>>(response.body!!.string())
|
||||
|
||||
if (jsonResponse["code"].int != 0) {
|
||||
if (result.code != 0) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return jsonResponse["data"]["images"].array
|
||||
.mapIndexed { i, page -> Page(i, page["path"].string, "") }
|
||||
return result.data!!.images
|
||||
.mapIndexed { i, page -> Page(i, page.path, "") }
|
||||
}
|
||||
|
||||
override fun imageUrlRequest(page: Page): Request {
|
||||
val jsonPayload = jsonObject("urls" to jsonArray(page.url).toString())
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_CONTENT_TYPE)
|
||||
val jsonPayload = buildJsonObject {
|
||||
put("urls", buildJsonArray { add(page.url) }.toString())
|
||||
}
|
||||
val requestBody = jsonPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Content-Length", requestBody.contentLength().toString())
|
||||
|
@ -253,10 +254,10 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String {
|
||||
val jsonResponse = response.asJson().obj
|
||||
val result = json.decodeFromString<BilibiliResultDto<List<BilibiliPageDto>>>(response.body!!.string())
|
||||
val page = result.data!![0]
|
||||
|
||||
return jsonResponse["data"][0]["url"].string
|
||||
.plus("?token=" + jsonResponse["data"][0]["token"].string)
|
||||
return "${page.url}?token=${page.token}"
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used")
|
||||
|
@ -271,14 +272,12 @@ class BilibiliComics : HttpSource() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun Response.asJson(): JsonElement = JsonParser.parseString(body!!.string())
|
||||
|
||||
companion object {
|
||||
private const val BASE_API_ENDPOINT = "twirp/comic.v1.Comic"
|
||||
|
||||
private const val ACCEPT_JSON = "application/json, text/plain, */*"
|
||||
|
||||
private val JSON_CONTENT_TYPE = "application/json;charset=UTF-8".toMediaType()
|
||||
private val JSON_MEDIA_TYPE = "application/json;charset=UTF-8".toMediaType()
|
||||
|
||||
private const val FEATURED_ID = 3
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package eu.kanade.tachiyomi.extension.en.bilibilicomics
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BilibiliResultDto<T>(
|
||||
val code: Int = 0,
|
||||
val data: T? = null,
|
||||
@SerialName("msg") val message: String = ""
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliFeaturedDto(
|
||||
@SerialName("roll_six_comics") val rollSixComics: List<BilibiliComicDto> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliSearchDto(
|
||||
val list: List<BilibiliComicDto> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliComicDto(
|
||||
@SerialName("author_name") val authorName: List<String> = emptyList(),
|
||||
@SerialName("classic_lines") val classicLines: String = "",
|
||||
@SerialName("comic_id") val comicId: Int = 0,
|
||||
@SerialName("ep_list") val episodeList: List<BilibiliEpisodeDto> = emptyList(),
|
||||
val id: Int = 0,
|
||||
@SerialName("is_finish") val isFinish: Int = 0,
|
||||
val styles: List<String> = emptyList(),
|
||||
val title: String,
|
||||
@SerialName("vertical_cover") val verticalCover: String = ""
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliEpisodeDto(
|
||||
val id: Int,
|
||||
@SerialName("is_locked") val isLocked: Boolean,
|
||||
@SerialName("ord") val order: Float,
|
||||
@SerialName("pub_time") val publicationTime: String,
|
||||
val title: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliReader(
|
||||
val images: List<BilibiliImageDto> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliImageDto(
|
||||
val path: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BilibiliPageDto(
|
||||
val token: String,
|
||||
val url: String
|
||||
)
|
|
@ -1,11 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'NANI? Scans'
|
||||
pkgNameSuffix = 'en.naniscans'
|
||||
extClass = '.NaniScans'
|
||||
extVersionCode = 5
|
||||
extVersionCode = 6
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
|
|
@ -8,59 +8,73 @@ 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.int
|
||||
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 rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class NaniScans : HttpSource() {
|
||||
override val baseUrl = "https://naniscans.com"
|
||||
override val lang = "en"
|
||||
|
||||
override val name = "NANI? Scans"
|
||||
|
||||
override val baseUrl = "https://naniscans.com"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val versionId = 2
|
||||
|
||||
private val dateParser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault())
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(page)
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val titlesJson = JSONArray(response.body!!.string())
|
||||
val mangaMap = mutableMapOf<Long, SManga>()
|
||||
val titlesJson = json.parseToJsonElement(response.body!!.string()).jsonArray
|
||||
|
||||
for (i in 0 until titlesJson.length()) {
|
||||
val manga = titlesJson.getJSONObject(i)
|
||||
val mangaList = titlesJson
|
||||
.mapNotNull {
|
||||
val manga = it.jsonObject
|
||||
|
||||
if (manga.getString("type") != "Comic")
|
||||
continue
|
||||
if (manga["type"]!!.jsonPrimitive.content != "Comic") {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
var date = manga.getString("updatedAt")
|
||||
val date = manga["updatedAt"]!!.jsonPrimitive.content.let { datePrimitive ->
|
||||
if (datePrimitive == "null") "2018-04-10T17:38:56" else datePrimitive
|
||||
}
|
||||
|
||||
if (date == "null")
|
||||
date = "2018-04-10T17:38:56"
|
||||
Pair(dateParser.parse(date)!!.time, getBareSManga(manga))
|
||||
}
|
||||
.sortedByDescending { it.first }
|
||||
.map { it.second }
|
||||
|
||||
mangaMap[dateParser.parse(date)!!.time] = getBareSManga(manga)
|
||||
}
|
||||
|
||||
return MangasPage(mangaMap.toSortedMap().values.toList().asReversed(), false)
|
||||
return MangasPage(mangaList, false)
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/api/titles")
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val titlesJson = JSONArray(response.body!!.string())
|
||||
val mangaList = mutableListOf<SManga>()
|
||||
val titlesJson = json.parseToJsonElement(response.body!!.string()).jsonArray
|
||||
|
||||
for (i in 0 until titlesJson.length()) {
|
||||
val manga = titlesJson.getJSONObject(i)
|
||||
val mangaList = titlesJson.mapNotNull {
|
||||
val manga = it.jsonObject
|
||||
|
||||
if (manga.getString("type") != "Comic")
|
||||
continue
|
||||
|
||||
mangaList.add(getBareSManga(manga))
|
||||
if (manga["type"]!!.jsonPrimitive.content == "Comic") {
|
||||
getBareSManga(manga)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return MangasPage(mangaList, false)
|
||||
|
@ -77,65 +91,54 @@ class NaniScans : HttpSource() {
|
|||
override fun mangaDetailsRequest(manga: SManga) = GET("$baseUrl/titles/${manga.url}")
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
val titleJson = JSONObject(response.body!!.string())
|
||||
val titleJson = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
|
||||
if (titleJson.getString("type") != "Comic")
|
||||
if (titleJson["type"]!!.jsonPrimitive.content != "Comic")
|
||||
throw UnsupportedOperationException("Tachiyomi only supports Comics.")
|
||||
|
||||
return SManga.create().apply {
|
||||
title = titleJson.getString("name")
|
||||
artist = titleJson.getString("artist")
|
||||
author = titleJson.getString("author")
|
||||
description = titleJson.getString("synopsis")
|
||||
status = getStatus(titleJson.getString("status"))
|
||||
thumbnail_url = "$baseUrl${titleJson.getString("coverUrl")}"
|
||||
genre = titleJson.getJSONArray("tags").join(", ").replace("\"", "")
|
||||
url = titleJson.getString("id")
|
||||
title = titleJson["name"]!!.jsonPrimitive.content
|
||||
artist = titleJson["artist"]!!.jsonPrimitive.content
|
||||
author = titleJson["author"]!!.jsonPrimitive.content
|
||||
description = titleJson["synopsis"]!!.jsonPrimitive.content
|
||||
status = getStatus(titleJson["status"]!!.jsonPrimitive.content)
|
||||
thumbnail_url = "$baseUrl${titleJson["coverUrl"]!!.jsonPrimitive.content}"
|
||||
genre = titleJson["tags"]!!.jsonArray.joinToString { it.jsonPrimitive.content }
|
||||
url = titleJson["id"]!!.jsonPrimitive.content
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga) = GET("$baseUrl/api/titles/${manga.url}")
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val titleJson = JSONObject(response.body!!.string())
|
||||
val titleJson = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
|
||||
if (titleJson.getString("type") != "Comic")
|
||||
if (titleJson["type"]!!.jsonPrimitive.content != "Comic")
|
||||
throw UnsupportedOperationException("Tachiyomi only supports Comics.")
|
||||
|
||||
val chaptersJson = titleJson.getJSONArray("chapters")
|
||||
val chaptersList = mutableListOf<SChapter>()
|
||||
val chaptersJson = titleJson["chapters"]!!.jsonArray
|
||||
|
||||
for (i in 0 until chaptersJson.length()) {
|
||||
val chapter = chaptersJson.getJSONObject(i)
|
||||
return titleJson["chapters"]!!.jsonArray.map {
|
||||
val chapter = it.jsonObject
|
||||
|
||||
chaptersList.add(
|
||||
SChapter.create().apply {
|
||||
chapter_number = chapter.get("number").toString().toFloat()
|
||||
name = getChapterTitle(chapter)
|
||||
date_upload = dateParser.parse(chapter.getString("releaseDate"))!!.time
|
||||
url = "${titleJson.getString("id")}_${chapter.getString("id")}"
|
||||
}
|
||||
)
|
||||
SChapter.create().apply {
|
||||
chapter_number = chapter["number"]!!.jsonPrimitive.content.toFloatOrNull() ?: -1f
|
||||
name = getChapterTitle(chapter)
|
||||
date_upload = dateParser.parse(chapter["releaseDate"]!!.jsonPrimitive.content)!!.time
|
||||
url = "${titleJson["id"]!!.jsonPrimitive.content} ${chapter["id"]!!.jsonPrimitive.content}"
|
||||
}
|
||||
}
|
||||
|
||||
return chaptersList
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/api/chapters/${chapter.url.substring(37, 73)}")
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val jsonObject = JSONObject(response.body!!.string())
|
||||
val jsonObject = json.parseToJsonElement(response.body!!.string()).jsonObject
|
||||
|
||||
val pagesJson = jsonObject.getJSONArray("pages")
|
||||
val pagesList = mutableListOf<Page>()
|
||||
|
||||
for (i in 0 until pagesJson.length()) {
|
||||
val item = pagesJson.getJSONObject(i)
|
||||
|
||||
pagesList.add(Page(item.getInt("number"), "", "$baseUrl${item.getString("pageUrl")}"))
|
||||
return jsonObject["pages"]!!.jsonArray.map {
|
||||
val item = it.jsonObject
|
||||
Page(item["number"]!!.jsonPrimitive.int, "", "$baseUrl${item["pageUrl"]!!.jsonPrimitive.content}")
|
||||
}
|
||||
|
||||
return pagesList
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not Used.")
|
||||
|
@ -146,23 +149,23 @@ class NaniScans : HttpSource() {
|
|||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
private fun getChapterTitle(chapter: JSONObject): String {
|
||||
private fun getChapterTitle(chapter: JsonObject): String {
|
||||
val chapterName = mutableListOf<String>()
|
||||
|
||||
if (chapter.getString("volume") != "null") {
|
||||
chapterName.add("Vol." + chapter.getString("volume"))
|
||||
if (chapter["volume"]!!.jsonPrimitive.content != "null") {
|
||||
chapterName.add("Vol." + chapter["volume"]!!.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
if (chapter.getString("number") != "null") {
|
||||
chapterName.add("Ch." + chapter.getString("number"))
|
||||
if (chapter["number"]!!.jsonPrimitive.content != "null") {
|
||||
chapterName.add("Ch." + chapter["number"]!!.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
if (chapter.getString("name") != "null") {
|
||||
if (chapter["name"]!!.jsonPrimitive.content != "null") {
|
||||
if (chapterName.isNotEmpty()) {
|
||||
chapterName.add("-")
|
||||
}
|
||||
|
||||
chapterName.add(chapter.getString("name"))
|
||||
chapterName.add(chapter["name"]!!.jsonPrimitive.content)
|
||||
}
|
||||
|
||||
if (chapterName.isEmpty()) {
|
||||
|
@ -172,9 +175,9 @@ class NaniScans : HttpSource() {
|
|||
return chapterName.joinToString(" ")
|
||||
}
|
||||
|
||||
private fun getBareSManga(manga: JSONObject): SManga = SManga.create().apply {
|
||||
title = manga.getString("name")
|
||||
thumbnail_url = "$baseUrl${manga.getString("coverUrl")}"
|
||||
url = manga.getString("id")
|
||||
private fun getBareSManga(manga: JsonObject): SManga = SManga.create().apply {
|
||||
title = manga["name"]!!.jsonPrimitive.content
|
||||
thumbnail_url = "$baseUrl${manga["coverUrl"]!!.jsonPrimitive.content}"
|
||||
url = manga["id"]!!.jsonPrimitive.content
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue