Replace org.json in PizzaReader. (#8645)
This commit is contained in:
parent
e5d55d91b5
commit
38f2f4a767
@ -8,23 +8,31 @@ import eu.kanade.tachiyomi.source.model.Page
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.json.JSONObject
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
abstract class PizzaReader(
|
abstract class PizzaReader(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val baseUrl: String,
|
override val baseUrl: String,
|
||||||
override val lang: String,
|
override val lang: String,
|
||||||
private val apiPath: String = "/api"
|
private val apiPath: String = "/api",
|
||||||
|
private val dateParser: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", Locale.ITALY)
|
||||||
) : HttpSource() {
|
) : HttpSource() {
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
open val apiUrl by lazy { "$baseUrl$apiPath" }
|
open val apiUrl by lazy { "$baseUrl$apiPath" }
|
||||||
|
|
||||||
|
protected open val json: Json by injectLazy()
|
||||||
|
|
||||||
override fun headersBuilder() = Headers.Builder().apply {
|
override fun headersBuilder() = Headers.Builder().apply {
|
||||||
add("Referer", baseUrl)
|
add("Referer", baseUrl)
|
||||||
}
|
}
|
||||||
@ -32,72 +40,106 @@ abstract class PizzaReader(
|
|||||||
override fun popularMangaRequest(page: Int) =
|
override fun popularMangaRequest(page: Int) =
|
||||||
GET("$apiUrl/comics", headers)
|
GET("$apiUrl/comics", headers)
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response) =
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
MangasPage(
|
val result = json.decodeFromString<PizzaResultsDto>(response.body!!.string())
|
||||||
JSONObject(response.asString()).run {
|
|
||||||
val arr = getJSONArray("comics")
|
|
||||||
(0 until arr.length()).map {
|
|
||||||
SManga.create().fromJSON(arr.getJSONObject(it))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
val comicList = result.comics
|
||||||
GET("$apiUrl/search/$query", headers)
|
.map(::popularMangaFromObject)
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response) =
|
return MangasPage(comicList, hasNextPage = false)
|
||||||
MangasPage(
|
}
|
||||||
JSONObject(response.asString()).run {
|
|
||||||
val arr = getJSONArray("comics")
|
|
||||||
(0 until arr.length()).map {
|
|
||||||
SManga.create().fromJSON(arr.getJSONObject(it))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO
|
protected open fun popularMangaFromObject(comic: PizzaComicDto): SManga = SManga.create().apply {
|
||||||
override fun latestUpdatesRequest(page: Int): Request =
|
title = comic.title
|
||||||
throw UnsupportedOperationException("Not used")
|
thumbnail_url = comic.thumbnail
|
||||||
|
url = comic.url
|
||||||
|
}
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage =
|
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(page)
|
||||||
throw UnsupportedOperationException("Not used")
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
val result = json.decodeFromString<PizzaResultsDto>(response.body!!.string())
|
||||||
|
|
||||||
|
val comicList = result.comics
|
||||||
|
.filter { comic -> comic.lastChapter != null }
|
||||||
|
.sortedByDescending { comic -> comic.lastChapter!!.publishedOn }
|
||||||
|
.map(::popularMangaFromObject)
|
||||||
|
.take(10)
|
||||||
|
|
||||||
|
return MangasPage(comicList, hasNextPage = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val searchUrl = "$apiUrl/search/".toHttpUrl().newBuilder()
|
||||||
|
.addPathSegment(query)
|
||||||
|
.toString()
|
||||||
|
|
||||||
|
return GET(searchUrl, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
// Workaround to allow "Open in browser" to use the real URL
|
// Workaround to allow "Open in browser" to use the real URL
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||||
client.newCall(chapterListRequest(manga)).asObservableSuccess()
|
client.newCall(chapterListRequest(manga)).asObservableSuccess()
|
||||||
.map { mangaDetailsParse(it).apply { initialized = true } }
|
.map { mangaDetailsParse(it).apply { initialized = true } }
|
||||||
|
|
||||||
// Return the real URL for "Open in browser"
|
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
|
||||||
override fun mangaDetailsRequest(manga: SManga) =
|
val result = json.decodeFromString<PizzaResultDto>(response.body!!.string())
|
||||||
GET("$baseUrl${manga.url}", headers)
|
val comic = result.comic!!
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga =
|
title = comic.title
|
||||||
SManga.create().fromJSON(JSONObject(response.asString()).getJSONObject("comic"))
|
author = comic.author
|
||||||
|
artist = comic.artist
|
||||||
|
description = comic.description
|
||||||
|
genre = comic.genres.joinToString(", ") { it.name }
|
||||||
|
status = comic.status.toStatus()
|
||||||
|
thumbnail_url = comic.thumbnail
|
||||||
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = GET("$apiUrl${manga.url}", headers)
|
override fun chapterListRequest(manga: SManga) = GET(apiUrl + manga.url, headers)
|
||||||
|
|
||||||
override fun chapterListParse(response: Response) =
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
JSONObject(response.asString()).getJSONObject("comic").run {
|
val result = json.decodeFromString<PizzaResultDto>(response.body!!.string())
|
||||||
val arr = getJSONArray("chapters")
|
val comic = result.comic!!
|
||||||
(0 until arr.length()).map {
|
|
||||||
SChapter.create().fromJSON(arr.getJSONObject(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter) =
|
return comic.chapters
|
||||||
GET("$apiUrl${chapter.url}", headers)
|
.map(::chapterFromObject)
|
||||||
|
}
|
||||||
|
|
||||||
override fun pageListParse(response: Response) =
|
protected open fun chapterFromObject(chapter: PizzaChapterDto): SChapter = SChapter.create().apply {
|
||||||
JSONObject(response.asString()).getJSONObject("chapter").run {
|
name = chapter.fullTitle
|
||||||
val arr = getJSONArray("pages")
|
chapter_number = (chapter.chapter ?: -1).toFloat() +
|
||||||
(0 until arr.length()).map {
|
("0." + (chapter.subchapter?.toString() ?: "0")).toFloat()
|
||||||
Page(it, "", arr.getString(it))
|
date_upload = chapter.publishedOn.toDate()
|
||||||
}
|
scanlator = chapter.teams.filterNotNull()
|
||||||
}
|
.joinToString(" & ") { it.name }
|
||||||
|
url = chapter.url
|
||||||
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response): String =
|
override fun pageListRequest(chapter: SChapter) = GET(apiUrl + chapter.url, headers)
|
||||||
throw UnsupportedOperationException("Not used")
|
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val result = json.decodeFromString<PizzaReaderDto>(response.body!!.string())
|
||||||
|
|
||||||
|
return result.chapter!!.pages.mapIndexed { i, page -> Page(i, "", page) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response): String = ""
|
||||||
|
|
||||||
|
protected open fun String.toDate(): Long {
|
||||||
|
return runCatching { dateParser.parse(this)?.time }
|
||||||
|
.getOrNull() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun String.toStatus(): Int = when (substring(0, 7)) {
|
||||||
|
"In cors" -> SManga.ONGOING
|
||||||
|
"On goin" -> SManga.ONGOING
|
||||||
|
"Complet" -> SManga.COMPLETED
|
||||||
|
"Conclus" -> SManga.COMPLETED
|
||||||
|
"Conclud" -> SManga.COMPLETED
|
||||||
|
"Licenzi" -> SManga.LICENSED
|
||||||
|
"License" -> SManga.LICENSED
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package eu.kanade.tachiyomi.multisrc.pizzareader
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaResultsDto(
|
||||||
|
val comics: List<PizzaComicDto> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaResultDto(
|
||||||
|
val comic: PizzaComicDto? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaReaderDto(
|
||||||
|
val chapter: PizzaChapterDto? = null,
|
||||||
|
val comic: PizzaComicDto? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaComicDto(
|
||||||
|
val artist: String = "",
|
||||||
|
val author: String = "",
|
||||||
|
val chapters: List<PizzaChapterDto> = emptyList(),
|
||||||
|
val description: String = "",
|
||||||
|
val genres: List<PizzaGenreDto> = emptyList(),
|
||||||
|
@SerialName("last_chapter") val lastChapter: PizzaChapterDto? = null,
|
||||||
|
val status: String = "",
|
||||||
|
val title: String = "",
|
||||||
|
val thumbnail: String = "",
|
||||||
|
val url: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaGenreDto(
|
||||||
|
val name: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaChapterDto(
|
||||||
|
val chapter: Int? = null,
|
||||||
|
@SerialName("full_title") val fullTitle: String = "",
|
||||||
|
val pages: List<String> = emptyList(),
|
||||||
|
@SerialName("published_on") val publishedOn: String = "",
|
||||||
|
val subchapter: Int? = null,
|
||||||
|
val teams: List<PizzaTeamDto?> = emptyList(),
|
||||||
|
val url: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PizzaTeamDto(
|
||||||
|
val name: String = ""
|
||||||
|
)
|
||||||
|
|
@ -1,94 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.multisrc.pizzareader
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONObject
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
// import java.time.OffsetDateTime
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
/** Returns the body of a response as a `String`. */
|
|
||||||
fun Response.asString(): String = body!!.string()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the number according to [fmt].
|
|
||||||
*
|
|
||||||
* @param fmt A [DecimalFormat] string.
|
|
||||||
* @return A string representation of the number.
|
|
||||||
*/
|
|
||||||
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 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).mapNotNull {
|
|
||||||
val obj = get(it)
|
|
||||||
if (obj != null && obj.toString() != "null") getJSONArray(it).getString(field)
|
|
||||||
else null
|
|
||||||
}.joinToString(sep)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Joins each value of a given [field] of the array using [sep].
|
|
||||||
*
|
|
||||||
* @param field 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: String, sep: String = ", ") =
|
|
||||||
length().takeIf { it != 0 }?.run {
|
|
||||||
(0 until this).mapNotNull {
|
|
||||||
val obj = get(it)
|
|
||||||
if (obj != null && obj.toString() != "null") getJSONObject(it).getString(field)
|
|
||||||
else null
|
|
||||||
}.joinToString(sep)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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("thumbnail")
|
|
||||||
author = obj.getString("author")
|
|
||||||
artist = obj.getString("artist")
|
|
||||||
genre = obj.getJSONArray("genres").joinField("slug")
|
|
||||||
status = when (obj.getString("status").substring(0, 7)) {
|
|
||||||
"In cors" -> SManga.ONGOING
|
|
||||||
"On goin" -> SManga.ONGOING
|
|
||||||
"Complet" -> SManga.COMPLETED
|
|
||||||
"Conclus" -> SManga.COMPLETED
|
|
||||||
"Conclud" -> SManga.COMPLETED
|
|
||||||
"Licenzi" -> SManga.LICENSED
|
|
||||||
"License" -> SManga.LICENSED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a [SChapter] by parsing a [JSONObject].
|
|
||||||
*
|
|
||||||
* @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() + "0.${obj.optInt("subchapter", 0)}".toFloat()
|
|
||||||
// date_upload = OffsetDateTime.parse(obj.getString("published_on")).toEpochSecond() // API 26
|
|
||||||
date_upload = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", Locale.ITALY).parse(obj.getString("published_on"))?.time ?: 0L
|
|
||||||
scanlator = obj.getJSONArray("teams").joinField("name", " & ")
|
|
||||||
name = obj.getString("full_title")
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ class PizzaReaderGenerator : ThemeSourceGenerator {
|
|||||||
|
|
||||||
override val themeClass = "PizzaReader"
|
override val themeClass = "PizzaReader"
|
||||||
|
|
||||||
override val baseVersionCode: Int = 0
|
override val baseVersionCode: Int = 1
|
||||||
|
|
||||||
override val sources = listOf(
|
override val sources = listOf(
|
||||||
SingleLang("Phoenix Scans", "https://www.phoenixscans.com", "it", className = "PhoenixScans", overrideVersionCode = 4),
|
SingleLang("Phoenix Scans", "https://www.phoenixscans.com", "it", className = "PhoenixScans", overrideVersionCode = 4),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user