parent
dbfe27e7a3
commit
f84ec7c418
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Taiyō'
|
extName = 'Taiyō'
|
||||||
extClass = '.Taiyo'
|
extClass = '.Taiyo'
|
||||||
extVersionCode = 2
|
extVersionCode = 3
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.extension.pt.taiyo
|
package eu.kanade.tachiyomi.extension.pt.taiyo
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.AdditionalInfoDto
|
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.AdditionalInfoDto
|
||||||
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.ChapterListDto
|
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.ChapterListDto
|
||||||
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.MediaChapterDto
|
import eu.kanade.tachiyomi.extension.pt.taiyo.dto.MediaChapterDto
|
||||||
|
@ -25,13 +27,19 @@ import kotlinx.serialization.json.decodeFromStream
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import kotlinx.serialization.json.putJsonObject
|
import kotlinx.serialization.json.putJsonObject
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import okhttp3.internal.http.HTTP_FORBIDDEN
|
||||||
|
import okhttp3.internal.http.HTTP_UNAUTHORIZED
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
import org.jsoup.select.Elements
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -47,41 +55,28 @@ class Taiyo : ParsedHttpSource() {
|
||||||
// The source doesn't show the title on the home page
|
// The source doesn't show the title on the home page
|
||||||
override val supportsLatest = false
|
override val supportsLatest = false
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences =
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
|
||||||
|
private var bearerToken: String = preferences.getString(BEARER_TOKEN_PREF, "").toString()
|
||||||
|
|
||||||
override val client = network.client.newBuilder()
|
override val client = network.client.newBuilder()
|
||||||
.rateLimitHost(baseUrl.toHttpUrl(), 2)
|
.rateLimitHost(baseUrl.toHttpUrl(), 2)
|
||||||
.rateLimitHost(IMG_CDN.toHttpUrl(), 2)
|
.rateLimitHost(IMG_CDN.toHttpUrl(), 2)
|
||||||
|
.addInterceptor(::authorizationInterceptor)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
// ============================== Popular ===============================
|
// ============================== Popular ===============================
|
||||||
var bearerToken = ""
|
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList())
|
||||||
if (bearerToken.isBlank()) {
|
|
||||||
getBearerToken()
|
|
||||||
}
|
|
||||||
return searchMangaRequest(page, "", FilterList())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||||
override fun popularMangaSelector() = throw UnsupportedOperationException()
|
override fun popularMangaSelector() = throw UnsupportedOperationException()
|
||||||
override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException()
|
override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException()
|
||||||
override fun popularMangaNextPageSelector() = null
|
override fun popularMangaNextPageSelector() = null
|
||||||
|
|
||||||
private fun getBearerToken() {
|
|
||||||
val scriptUrl = client.newCall(GET(baseUrl, headers))
|
|
||||||
.execute().asJsoup()
|
|
||||||
.selectFirst("script[src*=ee07d8437723d9f5]")
|
|
||||||
?.attr("src") ?: throw Exception("Não foi possivel localizar o token")
|
|
||||||
|
|
||||||
val script = client.newCall(GET("$baseUrl$scriptUrl", headers))
|
|
||||||
.execute().body.string()
|
|
||||||
|
|
||||||
bearerToken = TOKEN_REGEX.find(script)?.groups?.get("token")?.value
|
|
||||||
?: throw Exception("Não foi possivel extrair o token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================== Latest ===============================
|
// =============================== Latest ===============================
|
||||||
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||||
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
|
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
|
||||||
|
@ -128,13 +123,13 @@ class Taiyo : ParsedHttpSource() {
|
||||||
|
|
||||||
val requestBody = json.encodeToString(jsonObj).toRequestBody(MEDIA_TYPE)
|
val requestBody = json.encodeToString(jsonObj).toRequestBody(MEDIA_TYPE)
|
||||||
|
|
||||||
val apiHeaders = headers.newBuilder()
|
return POST("https://meilisearch.${baseUrl.substringAfterLast("/")}/multi-search", getApiHeaders(), requestBody)
|
||||||
.set("Authorization", "Bearer $bearerToken")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return POST("https://meilisearch.${baseUrl.substringAfterLast("/")}/multi-search", apiHeaders, requestBody)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getApiHeaders() = headers.newBuilder()
|
||||||
|
.set("Authorization", "Bearer $bearerToken")
|
||||||
|
.build()
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
val obj = response.parseAs<SearchResultDto>()
|
val obj = response.parseAs<SearchResultDto>()
|
||||||
val mangas = obj.mangas.map { item ->
|
val mangas = obj.mangas.map { item ->
|
||||||
|
@ -290,10 +285,63 @@ class Taiyo : ParsedHttpSource() {
|
||||||
}.onFailure { it.printStackTrace() }.getOrNull()
|
}.onFailure { it.printStackTrace() }.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================= Authorization ========================
|
||||||
|
|
||||||
|
private fun authorizationInterceptor(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
return when (response.code) {
|
||||||
|
in arrayOf(HTTP_UNAUTHORIZED, HTTP_FORBIDDEN) -> updateTokenAndContinueRequest(request, chain)
|
||||||
|
else -> response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateTokenAndContinueRequest(request: Request, chain: Interceptor.Chain): Response {
|
||||||
|
bearerToken = getToken()
|
||||||
|
val req = request.newBuilder()
|
||||||
|
.headers(getApiHeaders())
|
||||||
|
.build()
|
||||||
|
return chain.proceed(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getToken(): String {
|
||||||
|
return fetchBearerToken().also {
|
||||||
|
preferences.edit()
|
||||||
|
.putString(BEARER_TOKEN_PREF, it)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchBearerToken(): String {
|
||||||
|
val scripts = client.newCall(GET(baseUrl, headers))
|
||||||
|
.execute().asJsoup()
|
||||||
|
.select("script[src*=next]:not([nomodule]):not([src*=app])")
|
||||||
|
|
||||||
|
val script = getScriptContainingToken(scripts)
|
||||||
|
?: throw Exception("Não foi possivel localizar o token")
|
||||||
|
|
||||||
|
return TOKEN_REGEX.find(script)?.groups?.get("token")?.value
|
||||||
|
?: throw Exception("Não foi possivel extrair o token")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getScriptContainingToken(scripts: Elements): String? {
|
||||||
|
val elements = scripts.toList().reversed()
|
||||||
|
for (element in elements) {
|
||||||
|
val scriptUrl = element.attr("src")
|
||||||
|
val script = client.newCall(GET("$baseUrl$scriptUrl", headers))
|
||||||
|
.execute().body.string()
|
||||||
|
if (TOKEN_REGEX.containsMatchIn(script)) {
|
||||||
|
return script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PREFIX_SEARCH = "id:"
|
const val PREFIX_SEARCH = "id:"
|
||||||
val CHAPTER_REGEX = """(?<chapters>\{"chapters".+"totalPages":\d+\})""".toRegex()
|
val CHAPTER_REGEX = """(?<chapters>\{"chapters".+"totalPages":\d+\})""".toRegex()
|
||||||
val TOKEN_REGEX = """NEXT_PUBLIC_MEILISEARCH_PUBLIC_KEY:(\s+)?"(?<token>[^"]+)""".toRegex()
|
val TOKEN_REGEX = """NEXT_PUBLIC_MEILISEARCH_PUBLIC_KEY:(\s+)?"(?<token>[^"]+)""".toRegex()
|
||||||
|
const val BEARER_TOKEN_PREF = "TAIYO_BEARER_TOKEN"
|
||||||
|
|
||||||
private const val IMG_CDN = "https://cdn.taiyo.moe/medias"
|
private const val IMG_CDN = "https://cdn.taiyo.moe/medias"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue