Implement latest updates in Guya (#8355)

This commit is contained in:
ObserverOfTime 2021-08-02 18:01:05 +03:00 committed by GitHub
parent 28234597b3
commit 0a2a26e48c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 119 deletions

View File

@ -1,13 +1,12 @@
package eu.kanade.tachiyomi.extension.all.magicaltranslators
import eu.kanade.tachiyomi.multisrc.guya.Guya
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.MangasPage
import okhttp3.Response
class MagicalTranslatorsFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
override fun createSources() = listOf(
MagicalTranslatorsEN(),
MagicalTranslatorsPL(),
)
@ -15,18 +14,23 @@ class MagicalTranslatorsFactory : SourceFactory {
abstract class MagicalTranslatorsCommon(lang: String) :
Guya("Magical Translators", "https://mahoushoujobu.com", lang) {
protected abstract fun filterMangasPage(mangasPage: MangasPage): MangasPage
override fun popularMangaParse(response: Response): MangasPage =
filterMangasPage(super.popularMangaParse(response))
override fun proxySearchMangaParse(response: Response, slug: String): MangasPage =
filterMangasPage(super.proxySearchMangaParse(response, slug))
override fun latestUpdatesParse(response: Response): MangasPage =
filterMangasPage(super.latestUpdatesParse(response))
override fun proxySearchMangaParse(response: Response, query: String): MangasPage =
filterMangasPage(super.proxySearchMangaParse(response, query))
override fun searchMangaParseWithSlug(response: Response, slug: String): MangasPage =
filterMangasPage(super.searchMangaParseWithSlug(response, slug))
override fun searchMangaParse(response: Response, slug: String): MangasPage =
filterMangasPage(super.searchMangaParse(response, slug))
override fun searchMangaParse(response: Response, query: String): MangasPage =
filterMangasPage(super.searchMangaParse(response, query))
}
class MagicalTranslatorsEN : MagicalTranslatorsCommon("en") {

View File

@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.multisrc.guya
import android.app.Application
import android.content.SharedPreferences
import android.os.Build
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable
@ -23,26 +25,23 @@ import rx.Observable
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.HashMap
abstract class Guya(
override val name: String,
override val baseUrl: String,
override val lang: String) : ConfigurableSource, HttpSource() {
override val lang: String
) : ConfigurableSource, HttpSource() {
override val supportsLatest = false
override val supportsLatest = true
private val scanlatorCacheUrl = "$baseUrl/api/get_all_groups/"
private val scanlatorCacheUrl by lazy { "$baseUrl/api/get_all_groups/" }
override fun headersBuilder() = Headers.Builder().apply {
add(
"User-Agent",
"(Android ${Build.VERSION.RELEASE}; " +
"${Build.MANUFACTURER} ${Build.MODEL}) " +
"Tachiyomi/${BuildConfig.VERSION_NAME} " +
Build.ID
)
}
override fun headersBuilder() = Headers.Builder().add(
"User-Agent",
"(Android ${Build.VERSION.RELEASE}; " +
"${Build.MANUFACTURER} ${Build.MODEL}) " +
"Tachiyomi/${BuildConfig.VERSION_NAME} ${Build.ID}"
)
private val scanlators: ScanlatorStore = ScanlatorStore()
@ -50,6 +49,7 @@ abstract class Guya(
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val scanlatorPreference = "SCANLATOR_PREFERENCE"
// Request builder for the "browse" page of the manga
@ -63,6 +63,23 @@ abstract class Guya(
return parseManga(JSONObject(res))
}
override fun latestUpdatesRequest(page: Int): Request {
return popularMangaRequest(page)
}
override fun latestUpdatesParse(response: Response): MangasPage {
val payload = JSONObject(response.body!!.string())
val mangas = sortedMapOf<Long, SManga>()
for (series in payload.keys()) {
val json = payload.getJSONObject(series)
val timestamp = json.getLong("last_updated")
mangas[timestamp] = parseMangaFromJson(json, "", series)
}
return MangasPage(mangas.values.reversed(), false)
}
// Overridden to use our overload
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return when {
@ -91,11 +108,6 @@ abstract class Guya(
}
}
// Stub
override fun mangaDetailsParse(response: Response): SManga {
throw Exception("Unused")
}
private fun mangaDetailsParse(response: Response, manga: SManga): SManga {
val res = response.body!!.string()
return parseMangaFromJson(JSONObject(res), "", manga.title)
@ -125,14 +137,9 @@ abstract class Guya(
return GET("$baseUrl/api/series/${manga.url}/", headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
throw Exception("Unused")
}
// Called after the request
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
val res = response.body!!.string()
return parseChapterList(res, manga)
return parseChapterList(response.body!!.string(), manga)
}
// Overridden fetch so that we use our overloaded method instead
@ -159,11 +166,6 @@ abstract class Guya(
return GET("$baseUrl/api/series/${chapter.url.split("/")[0]}/", headers)
}
// Stub
override fun pageListParse(response: Response): List<Page> {
throw Exception("Unused")
}
private fun pageListParse(response: Response, chapter: SChapter): List<Page> {
val res = response.body!!.string()
@ -175,7 +177,7 @@ abstract class Guya(
val metadata = JSONObject()
metadata.put("chapter", chapterNum)
metadata.put("scanlator", scanlators.getKeyFromValue(chapter.scanlator.toString()))
metadata.put("scanlator", scanlators.getKeyFromValue(chapter.scanlator ?: ""))
metadata.put("slug", json.getString("slug"))
metadata.put(
"folder",
@ -198,7 +200,7 @@ abstract class Guya(
}
}
query.startsWith(PROXY_PREFIX) && query.contains("/") -> {
client.newCall(proxySearchMangaRequest(page, query, filters))
client.newCall(proxySearchMangaRequest(query))
.asObservableSuccess()
.map { response ->
proxySearchMangaParse(response, query)
@ -218,17 +220,11 @@ abstract class Guya(
return GET("$baseUrl/api/get_all_series/", headers)
}
override fun searchMangaParse(response: Response): MangasPage {
throw Exception("Unused.")
}
protected open fun searchMangaParseWithSlug(response: Response, slug: String): MangasPage {
val results = JSONObject(response.body!!.string())
val mangaIter = results.keys()
val truncatedJSON = JSONObject()
while (mangaIter.hasNext()) {
val mangaTitle = mangaIter.next()
for (mangaTitle in results.keys()) {
val mangaDetails = results.getJSONObject(mangaTitle)
if (mangaDetails.get("slug") == slug) {
@ -244,11 +240,8 @@ abstract class Guya(
val json = JSONObject(res)
val truncatedJSON = JSONObject()
val iter = json.keys()
while (iter.hasNext()) {
val candidate = iter.next()
if (candidate.contains(query.toRegex(RegexOption.IGNORE_CASE))) {
for (candidate in json.keys()) {
if (candidate.contains(query, ignoreCase = true)) {
truncatedJSON.put(candidate, json.get(candidate))
}
}
@ -256,8 +249,8 @@ abstract class Guya(
return parseManga(truncatedJSON)
}
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
val preference = androidx.preference.ListPreference(screen.context).apply {
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val preference = ListPreference(screen.context).apply {
key = "preferred_scanlator"
title = "Preferred scanlator"
entries = arrayOf<String>()
@ -271,7 +264,7 @@ abstract class Guya(
"on chapter refresh/update. It will get the next available if " +
"your preferred scanlator isn't an option (yet)."
this.setDefaultValue("1")
setDefaultValue("1")
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue.toString()
@ -332,27 +325,23 @@ abstract class Guya(
.getJSONObject("groups")
.getJSONArray(groupNum)
}
val pageArray = ArrayList<Page>()
for (i in 0 until pages.length()) {
val page = if (pages.optJSONObject(i) != null) {
pages.getJSONObject(i).getString("src")
} else {
pages[i]
}
pageArray.add(Page(i + 1, "", page.toString()))
return List(pages.length()) {
Page(
it + 1,
"",
pages.optJSONObject(it)?.getString("src")
?: pages[it].toString()
)
}
return pageArray
}
private fun proxySearchMangaRequest(page: Int, query: String, filters: FilterList): Request {
private fun proxySearchMangaRequest(query: String): Request {
return proxySeriesRequest(query)
}
protected open fun proxySearchMangaParse(response: Response, query: String): MangasPage {
return MangasPage(
arrayListOf(parseMangaFromJson(JSONObject(response.body!!.string()), query)),
false
)
val json = JSONObject(response.body!!.string())
return MangasPage(listOf(parseMangaFromJson(json, query)), false)
}
// ------------- Helpers and whatnot ---------------
@ -363,7 +352,7 @@ abstract class Guya(
val chapters = response.getJSONObject("chapters")
val mapping = response.getJSONObject("groups")
val chapterList = ArrayList<SChapter>()
val chapterList = mutableListOf<SChapter>()
val iter = chapters.keys()
@ -408,11 +397,9 @@ abstract class Guya(
chapter.chapter_number = chapterNum.toFloat()
chapter.url =
if (groups.optJSONArray(groupNum) != null) {
val mangaUrl = manga.url
"$mangaUrl/$chapterNum/$groupNum"
"${manga.url}/$chapterNum/$groupNum"
} else {
val url = groups.getString(groupNum)
"$PROXY_PREFIX$url"
"$PROXY_PREFIX${groups.getString(groupNum)}"
}
chapterList.add(chapter)
}
@ -425,15 +412,11 @@ abstract class Guya(
// Helper function to get all the listings
private fun parseManga(payload: JSONObject): MangasPage {
val mangas = ArrayList<SManga>()
val mangas = mutableListOf<SManga>()
val iter = payload.keys()
while (iter.hasNext()) {
val series = iter.next()
for (series in payload.keys()) {
val json = payload.getJSONObject(series)
val manga = parseMangaFromJson(json, "", series)
mangas.add(manga)
mangas += parseMangaFromJson(json, "", series)
}
return MangasPage(mangas, false)
@ -442,14 +425,19 @@ abstract class Guya(
// Takes a json of the manga to parse
private fun parseMangaFromJson(json: JSONObject, slug: String, title: String = ""): SManga {
val manga = SManga.create()
manga.title = if (title.isNotEmpty()) title else json.getString("title")
manga.artist = json.optString("artist", "Unknown")
manga.author = json.optString("author", "Unknown")
manga.description = json.optString("description", "None")
manga.title = title.ifEmpty { json.getString("title") }
manga.artist = json.optString("artist")
manga.author = json.optString("author")
manga.description = json.optString("description")
manga.url = if (slug.startsWith(PROXY_PREFIX)) slug else json.getString("slug")
val cover = json.optString("cover", "")
manga.thumbnail_url = if (cover.startsWith("http")) cover else "$baseUrl/$cover"
val cover = json.optString("cover")
manga.thumbnail_url = when {
cover.startsWith("http") -> cover
cover.isNotEmpty() -> "$baseUrl/$cover"
else -> null
}
return manga
}
@ -469,23 +457,18 @@ abstract class Guya(
private fun parsePageFromJson(json: JSONObject, metadata: JSONObject): List<Page> {
val pages = json.getJSONArray(metadata.getString("scanlator"))
val pageArray = ArrayList<Page>()
for (i in 0 until pages.length()) {
val page = Page(
i + 1,
return List(pages.length()) {
Page(
it + 1,
"",
pageBuilder(
metadata.getString("slug"),
metadata.getString("folder"),
pages[i].toString(),
pages[it].toString(),
metadata.getString("scanlator")
)
)
pageArray.add(page)
}
return pageArray
}
private fun getBestScanlator(json: JSONObject, sort: JSONArray): String {
@ -509,8 +492,8 @@ abstract class Guya(
}
inner class ScanlatorStore {
private val scanlatorMap = HashMap<String, String>()
private val TOTAL_RETRIES = 10
private val scanlatorMap = mutableMapOf<String, String>()
private val totalRetries = 10
private var retryCount = 0
init {
@ -519,13 +502,10 @@ abstract class Guya(
fun getKeyFromValue(value: String): String {
update()
for (key in scanlatorMap.keys) {
if (scanlatorMap[key].equals(value)) {
return key
}
}
// Fall back to value as key if endpoint fails
return value
return scanlatorMap.keys.firstOrNull {
scanlatorMap[it].equals(value)
} ?: value
}
fun getValueFromKey(key: String): String {
@ -545,30 +525,28 @@ abstract class Guya(
retryCount++
} else {
val json = JSONObject(response.body!!.string())
val iter = json.keys()
while (iter.hasNext()) {
val scanId = iter.next()
for (scanId in json.keys()) {
scanlatorMap[scanId] = json.getString(scanId)
}
}
}
private fun onError(error: Throwable) {
// Do nothing for now
error.printStackTrace()
}
private fun update(blocking: Boolean = true) {
if (scanlatorMap.isEmpty() && retryCount < TOTAL_RETRIES) {
if (scanlatorMap.isEmpty() && retryCount < totalRetries) {
try {
val call = client.newCall(GET(scanlatorCacheUrl, headers))
.asObservable()
if (blocking) {
call.toBlocking()
.subscribe({ res -> onResponse(res) }, { err -> onError(err) })
.subscribe(::onResponse, ::onError)
} else {
call.subscribeOn(Schedulers.io())
.subscribe({ res -> onResponse(res) }, { err -> onError(err) })
.subscribe(::onResponse, ::onError)
}
} catch (e: Exception) {
// Prevent the extension from failing to load
@ -579,16 +557,24 @@ abstract class Guya(
// ----------------- Things we aren't supporting -----------------
override fun mangaDetailsParse(response: Response): SManga {
throw UnsupportedOperationException("Unused")
}
override fun chapterListParse(response: Response): List<SChapter> {
throw UnsupportedOperationException("Unused")
}
override fun pageListParse(response: Response): List<Page> {
throw UnsupportedOperationException("Unused")
}
override fun searchMangaParse(response: Response): MangasPage {
throw UnsupportedOperationException("Unused.")
}
override fun imageUrlParse(response: Response): String {
throw Exception("imageUrlParse not supported.")
}
override fun latestUpdatesRequest(page: Int): Request {
throw Exception("Latest updates not supported.")
}
override fun latestUpdatesParse(response: Response): MangasPage {
throw Exception("Latest updates not supported.")
throw UnsupportedOperationException("Unused.")
}
companion object {

View File

@ -10,14 +10,14 @@ class GuyaGenerator : ThemeSourceGenerator {
override val themeClass = "Guya"
override val baseVersionCode: Int = 2
override val baseVersionCode = 3
override val sources = listOf(
SingleLang("Guya", "https://guya.moe", "en", overrideVersionCode = 18),
SingleLang("Danke fürs Lesen", "https://danke.moe", "en", className = "DankeFursLesen"),
SingleLang("Colored Council", "https://coloredcouncil.moe", "en"),
SingleLang("Hachirumi", "https://hachirumi.com", "en", isNsfw = true),
MultiLang("Magical Translators", "https://mahoushoujobu.com", listOf("en", "pl"), className = "MagicalTranslatorsFactory"),
MultiLang("Magical Translators", "https://mahoushoujobu.com", listOf("en", "pl")),
)
companion object {
@JvmStatic