Added PizzaReader multisrc CMS (#7196)
* Added PizzaReader multisrc CMS * Added PhoenixScans * bump versionId
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 471 KiB |
@ -1,5 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.it.phoenixscans
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.foolslide.FoolSlide
|
|
||||||
|
|
||||||
class PhoenixScans : FoolSlide("The Phoenix Scans", "https://www.phoenixscans.com", "it", "/reader")
|
|
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 56 KiB |
BIN
multisrc/overrides/pizzareader/default/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 193 KiB |
@ -0,0 +1,7 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.it.phoenixscans
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.multisrc.pizzareader.PizzaReader
|
||||||
|
|
||||||
|
class PhoenixScans : PizzaReader("Phoenix Scans", "https://www.phoenixscans.com", "it") {
|
||||||
|
override val versionId = 2
|
||||||
|
}
|
@ -35,7 +35,6 @@ class FoolSlideGenerator : ThemeSourceGenerator {
|
|||||||
SingleLang("Baixar Hentai", "https://leitura.baixarhentai.net", "pt-BR", isNsfw = true, overrideVersionCode = 3),
|
SingleLang("Baixar Hentai", "https://leitura.baixarhentai.net", "pt-BR", isNsfw = true, overrideVersionCode = 3),
|
||||||
MultiLang("HNI-Scantrad", "https://hni-scantrad.com", listOf("fr", "en"), className = "HNIScantradFactory", pkgName = "hniscantrad", overrideVersionCode = 1),
|
MultiLang("HNI-Scantrad", "https://hni-scantrad.com", listOf("fr", "en"), className = "HNIScantradFactory", pkgName = "hniscantrad", overrideVersionCode = 1),
|
||||||
SingleLang("QuegnaReader", "http://pignaquegna.altervista.org", "it"),
|
SingleLang("QuegnaReader", "http://pignaquegna.altervista.org", "it"),
|
||||||
SingleLang("The Phoenix Scans", "https://www.phoenixscans.com", "it", className = "PhoenixScans"),
|
|
||||||
SingleLang("GTO The Great Site", "https://www.gtothegreatsite.net", "it", className = "GTO"),
|
SingleLang("GTO The Great Site", "https://www.gtothegreatsite.net", "it", className = "GTO"),
|
||||||
SingleLang("NIFTeam", "http://read-nifteam.info", "it"),
|
SingleLang("NIFTeam", "http://read-nifteam.info", "it"),
|
||||||
SingleLang("TuttoAnimeManga", "https://tuttoanimemanga.net", "it"),
|
SingleLang("TuttoAnimeManga", "https://tuttoanimemanga.net", "it"),
|
||||||
@ -44,7 +43,7 @@ class FoolSlideGenerator : ThemeSourceGenerator {
|
|||||||
SingleLang("Mabushimajo", "http://mabushimajo.com", "tr"),
|
SingleLang("Mabushimajo", "http://mabushimajo.com", "tr"),
|
||||||
SingleLang("Hyakuro", "https://hyakuro.com", "en"),
|
SingleLang("Hyakuro", "https://hyakuro.com", "en"),
|
||||||
SingleLang("Le Cercle du Scan", "https://lel.lecercleduscan.com", "fr", className = "LeCercleDuScan", overrideVersionCode = 1),
|
SingleLang("Le Cercle du Scan", "https://lel.lecercleduscan.com", "fr", className = "LeCercleDuScan", overrideVersionCode = 1),
|
||||||
SingleLang("LetItGo Scans", "https://reader.letitgo.scans.today", "en", overrideVersionCode = 1)
|
SingleLang("LetItGo Scans", "https://reader.letitgo.scans.today", "en", overrideVersionCode = 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package eu.kanade.tachiyomi.multisrc.pizzareader
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
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 eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.json.JSONObject
|
||||||
|
import rx.Observable
|
||||||
|
|
||||||
|
abstract class PizzaReader(
|
||||||
|
override val name: String,
|
||||||
|
override val baseUrl: String,
|
||||||
|
override val lang: String,
|
||||||
|
private val apiPath: String = "/api"
|
||||||
|
) : HttpSource() {
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
open val apiUrl by lazy { "$baseUrl$apiPath" }
|
||||||
|
|
||||||
|
override fun headersBuilder() = Headers.Builder().apply {
|
||||||
|
add("Referer", baseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int) =
|
||||||
|
GET("$apiUrl/comics", headers)
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response) =
|
||||||
|
MangasPage(
|
||||||
|
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) =
|
||||||
|
GET("$apiUrl/search/$query", headers)
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) =
|
||||||
|
MangasPage(
|
||||||
|
JSONObject(response.asString()).run {
|
||||||
|
val arr = getJSONArray("comics")
|
||||||
|
(0 until arr.length()).map {
|
||||||
|
SManga.create().fromJSON(arr.getJSONObject(it))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request =
|
||||||
|
throw UnsupportedOperationException("Not used")
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage =
|
||||||
|
throw UnsupportedOperationException("Not used")
|
||||||
|
|
||||||
|
// Workaround to allow "Open in browser" to use the real URL
|
||||||
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
|
||||||
|
client.newCall(chapterListRequest(manga)).asObservableSuccess()
|
||||||
|
.map { mangaDetailsParse(it).apply { initialized = true } }
|
||||||
|
|
||||||
|
// Return the real URL for "Open in browser"
|
||||||
|
override fun mangaDetailsRequest(manga: SManga) =
|
||||||
|
GET("$baseUrl${manga.url}", headers)
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga =
|
||||||
|
SManga.create().fromJSON(JSONObject(response.asString()).getJSONObject("comic"))
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga) = GET("$apiUrl${manga.url}", headers)
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response) =
|
||||||
|
JSONObject(response.asString()).getJSONObject("comic").run {
|
||||||
|
val arr = getJSONArray("chapters")
|
||||||
|
(0 until arr.length()).map {
|
||||||
|
SChapter.create().fromJSON(arr.getJSONObject(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter) =
|
||||||
|
GET("$apiUrl${chapter.url}", headers)
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response) =
|
||||||
|
JSONObject(response.asString()).getJSONObject("chapter").run {
|
||||||
|
val arr = getJSONArray("pages")
|
||||||
|
(0 until arr.length()).map {
|
||||||
|
Page(it, "", arr.getString(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response): String =
|
||||||
|
throw UnsupportedOperationException("Not used")
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
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")
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package eu.kanade.tachiyomi.multisrc.pizzareader
|
||||||
|
|
||||||
|
// import generator.ThemeSourceData.MultiLang
|
||||||
|
import generator.ThemeSourceData.SingleLang
|
||||||
|
import generator.ThemeSourceGenerator
|
||||||
|
|
||||||
|
class PizzaReaderGenerator : ThemeSourceGenerator {
|
||||||
|
|
||||||
|
override val themePkg = "pizzareader"
|
||||||
|
|
||||||
|
override val themeClass = "PizzaReader"
|
||||||
|
|
||||||
|
override val baseVersionCode: Int = 0
|
||||||
|
|
||||||
|
override val sources = listOf(
|
||||||
|
SingleLang("Phoenix Scans", "https://www.phoenixscans.com", "it", className = "PhoenixScans", overrideVersionCode = 4),
|
||||||
|
// Current migrating to this CMS:
|
||||||
|
// SingleLang("GTO The Great Site", "https://reader.gtothegreatsite.net", "it", className = "GTO", overrideVersionCode = 4),
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
PizzaReaderGenerator().createAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|