parent
ff790752e0
commit
24aa61ed77
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Sussy Toons'
|
extName = 'Sussy Toons'
|
||||||
extClass = '.SussyToons'
|
extClass = '.SussyToons'
|
||||||
extVersionCode = 48
|
extVersionCode = 49
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,11 @@ package eu.kanade.tachiyomi.extension.pt.sussyscan
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.webkit.WebResourceRequest
|
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import android.webkit.WebSettings
|
|
||||||
import android.webkit.WebView
|
|
||||||
import android.webkit.WebViewClient
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
@ -22,26 +15,18 @@ 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 eu.kanade.tachiyomi.util.asJsoup
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromStream
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Protocol
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.internal.http.HTTP_BAD_GATEWAY
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.select.Elements
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.IOException
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.CountDownLatch
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class SussyToons : HttpSource(), ConfigurableSource {
|
class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
|
@ -67,13 +52,9 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
get() = preferences.getString(API_BASE_URL_PREF, defaultApiUrl)!!
|
get() = preferences.getString(API_BASE_URL_PREF, defaultApiUrl)!!
|
||||||
set(value) = preferences.edit().putString(API_BASE_URL_PREF, value).apply()
|
set(value) = preferences.edit().putString(API_BASE_URL_PREF, value).apply()
|
||||||
|
|
||||||
private var chapterScriptUrl: String
|
private var restoreDefaultEnable: Boolean
|
||||||
get() = preferences.getString(CHAPTER_SCRIPT_URL_PREF, "")!!
|
get() = preferences.getBoolean(DEFAULT_PREF, false)
|
||||||
set(value) = preferences.edit().putString(CHAPTER_SCRIPT_URL_PREF, value).apply()
|
set(value) = preferences.edit().putBoolean(DEFAULT_PREF, value).apply()
|
||||||
|
|
||||||
private var pageScriptUrl: String
|
|
||||||
get() = preferences.getString(PAGE_SCRIPT_URL_PREF, "")!!
|
|
||||||
set(value) = preferences.edit().putString(PAGE_SCRIPT_URL_PREF, value).apply()
|
|
||||||
|
|
||||||
override val baseUrl: String get() = when {
|
override val baseUrl: String get() = when {
|
||||||
isCi -> defaultBaseUrl
|
isCi -> defaultBaseUrl
|
||||||
|
@ -84,14 +65,16 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
private val defaultApiUrl: String = "https://api-dev.sussytoons.site"
|
private val defaultApiUrl: String = "https://api-dev.sussytoons.site"
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder()
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
.rateLimit(2)
|
|
||||||
.addInterceptor(::findApiUrl)
|
|
||||||
.addInterceptor(::findChapterUrl)
|
|
||||||
.addInterceptor(::chapterPages)
|
|
||||||
.addInterceptor(::imageLocation)
|
.addInterceptor(::imageLocation)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (restoreDefaultEnable) {
|
||||||
|
restoreDefaultEnable = false
|
||||||
|
preferences.edit().putString(DEFAULT_BASE_URL_PREF, null).apply()
|
||||||
|
preferences.edit().putString(API_DEFAULT_BASE_URL_PREF, null).apply()
|
||||||
|
}
|
||||||
|
|
||||||
preferences.getString(DEFAULT_BASE_URL_PREF, null).let { domain ->
|
preferences.getString(DEFAULT_BASE_URL_PREF, null).let { domain ->
|
||||||
if (domain != defaultBaseUrl) {
|
if (domain != defaultBaseUrl) {
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
|
@ -187,11 +170,7 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
it.chapterNumber?.let {
|
it.chapterNumber?.let {
|
||||||
chapter_number = it
|
chapter_number = it
|
||||||
}
|
}
|
||||||
val chapterApiUrl = apiUrl.toHttpUrl().newBuilder()
|
setUrlWithoutDomain("$baseUrl/capitulo/${it.id}")
|
||||||
.addEncodedPathSegments(chapterUrl!!)
|
|
||||||
.addPathSegment(it.id.toString())
|
|
||||||
.build()
|
|
||||||
setUrlWithoutDomain(chapterApiUrl.toString())
|
|
||||||
date_upload = it.updateAt.toDate()
|
date_upload = it.updateAt.toDate()
|
||||||
}
|
}
|
||||||
}.sortedBy(SChapter::chapter_number).reversed()
|
}.sortedBy(SChapter::chapter_number).reversed()
|
||||||
|
@ -200,38 +179,13 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
// ============================= Pages ====================================
|
// ============================= Pages ====================================
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
return super.pageListRequest(chapter).let { request ->
|
val request = super.pageListRequest(chapter)
|
||||||
val url = request.url.newBuilder()
|
val chapterPageId = request.url.pathSegments.last()
|
||||||
.fragment("$pageImagePrefix${chapter.url}")
|
return GET("$apiUrl/capitulos/$chapterPageId", headers)
|
||||||
.build()
|
|
||||||
|
|
||||||
request.newBuilder()
|
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var pageUrlSegment: String? = null
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
pageUrlSegment = pageUrlSegment ?: findPageUrlSegment(response)
|
val dto = response.parseAs<WrapperDto<ChapterPageDto>>().results
|
||||||
val chapterPageId = response.request.url.pathSegments.last()
|
|
||||||
|
|
||||||
val chapterUrl = response.request.url.fragment
|
|
||||||
?.substringAfter(pageImagePrefix)
|
|
||||||
?: throw Exception("Não foi possivel carregar as páginas")
|
|
||||||
|
|
||||||
val url = apiUrl.toHttpUrl().newBuilder()
|
|
||||||
.addEncodedPathSegments(pageUrlSegment!!)
|
|
||||||
.addPathSegment(chapterPageId)
|
|
||||||
.fragment(
|
|
||||||
"$chapterPagePrefix${"$baseUrl$chapterUrl"}",
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val res = client.newCall(GET(url, headers)).execute()
|
|
||||||
val dto = res.parseAs<WrapperDto<ChapterPageDto>>().results
|
|
||||||
|
|
||||||
return dto.pages.mapIndexed { index, image ->
|
return dto.pages.mapIndexed { index, image ->
|
||||||
val imageUrl = when {
|
val imageUrl = when {
|
||||||
image.isWordPressContent() -> {
|
image.isWordPressContent() -> {
|
||||||
|
@ -249,28 +203,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the “dynamic” path segment of the chapter page
|
|
||||||
*/
|
|
||||||
private fun findPageUrlSegment(response: Response): String {
|
|
||||||
val scriptUrls = when {
|
|
||||||
pageScriptUrl.isNotBlank() -> listOf(pageScriptUrl to headers)
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val script = loadJsScript(
|
|
||||||
urls = scriptUrls,
|
|
||||||
doRequest = { client.newCall(it).execute() },
|
|
||||||
pattern = pageUrlRegex,
|
|
||||||
fallback = { fetchAllNextJsScriptUrls(response.request) },
|
|
||||||
)
|
|
||||||
|
|
||||||
pageScriptUrl = script.url
|
|
||||||
|
|
||||||
return pageUrlRegex.find(script.body)?.groups?.get(2)?.value?.toPathSegment()
|
|
||||||
?: throw IOException("Não foi encontrar o caminho das páginas")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response): String = ""
|
override fun imageUrlParse(response: Response): String = ""
|
||||||
|
|
||||||
override fun imageUrlRequest(page: Page): Request {
|
override fun imageUrlRequest(page: Page): Request {
|
||||||
|
@ -282,135 +214,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
// ============================= Interceptors =================================
|
// ============================= Interceptors =================================
|
||||||
|
|
||||||
private var chapterPageHeaders: Headers? = null
|
|
||||||
|
|
||||||
private var chapterUrl: String? = null
|
|
||||||
|
|
||||||
private fun findApiUrl(chain: Interceptor.Chain): Response {
|
|
||||||
val request = chain.request()
|
|
||||||
val response: Response = try {
|
|
||||||
chain.proceed(request)
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
chain.createBadGatewayResponse(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.isSuccessful || request.url.toString().contains(apiUrl).not()) {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
response.close()
|
|
||||||
|
|
||||||
fetchApiUrl(chain).forEach { urlCandidate ->
|
|
||||||
val url = request.url.toString()
|
|
||||||
.replace(apiUrl, urlCandidate)
|
|
||||||
.toHttpUrl()
|
|
||||||
|
|
||||||
val newRequest = request.newBuilder()
|
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val localResponse = chain.proceed(newRequest)
|
|
||||||
if (localResponse.isSuccessful.not()) {
|
|
||||||
localResponse.close()
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
apiUrl = urlCandidate
|
|
||||||
return localResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
throw IOException(
|
|
||||||
buildString {
|
|
||||||
append("Não foi possível encontrar a URL da API.")
|
|
||||||
append("Troque manualmente nas configurações da extensão")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Interceptor.Chain.createBadGatewayResponse(request: Request): Response {
|
|
||||||
return Response.Builder()
|
|
||||||
.request(request)
|
|
||||||
.protocol(Protocol.HTTP_1_1)
|
|
||||||
.message("")
|
|
||||||
.code(HTTP_BAD_GATEWAY)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fetchApiUrl(chain: Interceptor.Chain): List<String> {
|
|
||||||
val scripts = chain.proceed(GET(baseUrl, headers)).asJsoup()
|
|
||||||
.select("script[src*=next]:not([nomodule]):not([src*=app])")
|
|
||||||
|
|
||||||
val script = getScriptBodyWithUrls(scripts, chain)
|
|
||||||
?: throw Exception("Não foi possivel localizar a URL da API")
|
|
||||||
|
|
||||||
return apiUrlRegex.findAll(script)
|
|
||||||
.flatMap { stringsRegex.findAll(it.value).map { match -> match.groupValues[1] } }
|
|
||||||
.filter(urlRegex::containsMatchIn)
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getScriptBodyWithUrls(scripts: Elements, chain: Interceptor.Chain): String? {
|
|
||||||
val elements = scripts.toList().reversed()
|
|
||||||
for (element in elements) {
|
|
||||||
val scriptUrl = element.absUrl("src")
|
|
||||||
val script = chain.proceed(GET(scriptUrl, headers)).body.string()
|
|
||||||
if (apiUrlRegex.containsMatchIn(script)) {
|
|
||||||
return script
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the “dynamic” path segment of the chapter list
|
|
||||||
*/
|
|
||||||
private fun findChapterUrl(chain: Interceptor.Chain): Response {
|
|
||||||
val request = chain.request()
|
|
||||||
|
|
||||||
val mangaUrl = request.url.fragment
|
|
||||||
?.takeIf {
|
|
||||||
it.contains(mangaPagePrefix, ignoreCase = true) && chapterUrl.isNullOrBlank()
|
|
||||||
}?.substringAfter(mangaPagePrefix)
|
|
||||||
?: return chain.proceed(request)
|
|
||||||
|
|
||||||
val scriptUrls = when {
|
|
||||||
chapterScriptUrl.isNotBlank() -> listOf(chapterScriptUrl to headers)
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val script = loadJsScript(
|
|
||||||
urls = scriptUrls,
|
|
||||||
doRequest = chain::proceed,
|
|
||||||
pattern = chapterUrlRegex,
|
|
||||||
fallback = { fetchAllNextJsScriptUrls(GET(mangaUrl, headers)) },
|
|
||||||
)
|
|
||||||
|
|
||||||
chapterScriptUrl = script.url
|
|
||||||
|
|
||||||
chapterUrl = chapterUrlRegex.find(script.body)?.groups?.get(1)?.value?.toPathSegment()
|
|
||||||
?: throw IOException("Não foi possivel extrair a URL do capitulo")
|
|
||||||
|
|
||||||
return chain.proceed(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadJsScript(
|
|
||||||
urls: List<Pair<String, Headers>>,
|
|
||||||
doRequest: (request: Request) -> Response,
|
|
||||||
pattern: Regex,
|
|
||||||
fallback: (() -> List<Pair<String, Headers>>)? = null,
|
|
||||||
): Script {
|
|
||||||
val script = urls.map { pair ->
|
|
||||||
val request = GET(pair.first, pair.second)
|
|
||||||
Script(
|
|
||||||
url = request.url.toString(),
|
|
||||||
body = doRequest(request).use { response -> response.body.string() },
|
|
||||||
)
|
|
||||||
}.firstOrNull { pattern.containsMatchIn(it.body) }
|
|
||||||
|
|
||||||
return script ?: fallback?.let { urlList ->
|
|
||||||
loadJsScript(urlList(), doRequest = doRequest, pattern = pattern)
|
|
||||||
} ?: throw IOException("Não foi possivel encontrar a URL do capitulo")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun imageLocation(chain: Interceptor.Chain): Response {
|
private fun imageLocation(chain: Interceptor.Chain): Response {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val response = chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
|
@ -431,146 +234,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the “dynamic” headers of the chapter page
|
|
||||||
*/
|
|
||||||
private fun chapterPages(chain: Interceptor.Chain): Response {
|
|
||||||
val request = chain.request()
|
|
||||||
val chapterUrl = request.url.fragment
|
|
||||||
?.takeIf { it.contains(chapterPagePrefix) }
|
|
||||||
?.substringAfter(chapterPagePrefix)?.toHttpUrl()?.newBuilder()?.fragment(null)
|
|
||||||
?.build()
|
|
||||||
?: return chain.proceed(request)
|
|
||||||
|
|
||||||
val originUrl = request.url.newBuilder()
|
|
||||||
.fragment(null)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val newRequest = request.newBuilder()
|
|
||||||
.url(originUrl)
|
|
||||||
|
|
||||||
chapterPageHeaders?.let { headers ->
|
|
||||||
newRequest.headers(headers)
|
|
||||||
val response = chain.proceed(newRequest.build())
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
response.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
val chapterPageRequest = request.newBuilder()
|
|
||||||
.url(chapterUrl)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return chain.proceed(fetchChapterPagesHeaders(chapterPageRequest, newRequest.build()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
private fun fetchChapterPagesHeaders(baseRequest: Request, originRequest: Request): Request {
|
|
||||||
fun WebResourceRequest.isOriginRequest() =
|
|
||||||
originRequest.url.toString().equals(this.url.toString(), ignoreCase = true)
|
|
||||||
|
|
||||||
chapterPageHeaders = handlingWithWebResourceRequest(
|
|
||||||
baseRequest,
|
|
||||||
initial = headersBuilder(),
|
|
||||||
stopCondition = { _, _, resource ->
|
|
||||||
resource.isOriginRequest() && resource.method.equals("GET", true)
|
|
||||||
},
|
|
||||||
fold = { headers, _, resource ->
|
|
||||||
headers.apply {
|
|
||||||
if (resource.isOriginRequest().not() || resource.method.equals("GET", true).not()) {
|
|
||||||
return@apply
|
|
||||||
}
|
|
||||||
fill(resource.requestHeaders)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
).build()
|
|
||||||
|
|
||||||
return originRequest.newBuilder()
|
|
||||||
.headers(chapterPageHeaders!!)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
private fun fetchAllNextJsScriptUrls(baseRequest: Request): List<Pair<String, Headers>> {
|
|
||||||
fun WebResourceRequest.isNextJSUrl() = this.url.toString().contains("_next", ignoreCase = true) &&
|
|
||||||
this.url.toString().contains(".js", ignoreCase = true)
|
|
||||||
|
|
||||||
return handlingWithWebResourceRequest(
|
|
||||||
baseRequest,
|
|
||||||
initial = mutableListOf(),
|
|
||||||
stopCondition = { urls, _, _ ->
|
|
||||||
val minUrlsAvailable = 24
|
|
||||||
urls.size > minUrlsAvailable
|
|
||||||
},
|
|
||||||
fold = { urls, base, resource ->
|
|
||||||
urls.apply {
|
|
||||||
if (resource.isNextJSUrl().not()) {
|
|
||||||
return@apply
|
|
||||||
}
|
|
||||||
val headers = base.headers.newBuilder().apply {
|
|
||||||
fill(resource.requestHeaders)
|
|
||||||
}
|
|
||||||
add(resource.url.toString() to headers.build())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
private fun <T> handlingWithWebResourceRequest(
|
|
||||||
baseRequest: Request,
|
|
||||||
initial: T,
|
|
||||||
stopCondition: (T, Request, WebResourceRequest) -> Boolean,
|
|
||||||
fold: (T, Request, WebResourceRequest) -> T,
|
|
||||||
): T {
|
|
||||||
val latch = CountDownLatch(1)
|
|
||||||
var webView: WebView? = null
|
|
||||||
val looper = Handler(Looper.getMainLooper())
|
|
||||||
var state = initial
|
|
||||||
looper.post {
|
|
||||||
webView = WebView(Injekt.get<Application>())
|
|
||||||
webView?.let {
|
|
||||||
with(it.settings) {
|
|
||||||
javaScriptEnabled = true
|
|
||||||
domStorageEnabled = true
|
|
||||||
useWideViewPort = true
|
|
||||||
loadWithOverviewMode = true
|
|
||||||
cacheMode = WebSettings.LOAD_DEFAULT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
webView?.webViewClient = object : WebViewClient() {
|
|
||||||
override fun shouldInterceptRequest(
|
|
||||||
view: WebView?,
|
|
||||||
request: WebResourceRequest,
|
|
||||||
): WebResourceResponse? {
|
|
||||||
state = fold(state, baseRequest, request)
|
|
||||||
if (stopCondition(state, baseRequest, request)) {
|
|
||||||
latch.countDown()
|
|
||||||
}
|
|
||||||
return super.shouldInterceptRequest(view, request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
webView?.loadUrl(baseRequest.url.toString(), baseRequest.headers.toMap())
|
|
||||||
}
|
|
||||||
|
|
||||||
latch.await(client.readTimeoutMillis.toLong(), TimeUnit.MILLISECONDS)
|
|
||||||
|
|
||||||
looper.post {
|
|
||||||
webView?.run {
|
|
||||||
stopLoading()
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Headers.Builder.fill(from: Map<String, String>): Headers.Builder {
|
|
||||||
return from.entries.fold(this) { builder, entry ->
|
|
||||||
builder.set(entry.key, entry.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================= Settings ====================================
|
// ============================= Settings ====================================
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
@ -584,10 +247,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
dialogMessage = "URL padrão:\n$defaultBaseUrl"
|
dialogMessage = "URL padrão:\n$defaultBaseUrl"
|
||||||
|
|
||||||
setDefaultValue(defaultBaseUrl)
|
setDefaultValue(defaultBaseUrl)
|
||||||
setOnPreferenceChangeListener { _, _ ->
|
|
||||||
Toast.makeText(screen.context, RESTART_APP_MESSAGE, Toast.LENGTH_LONG).show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
EditTextPreference(screen.context).apply {
|
EditTextPreference(screen.context).apply {
|
||||||
key = API_BASE_URL_PREF
|
key = API_BASE_URL_PREF
|
||||||
|
@ -603,6 +262,19 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
dialogMessage = "URL da API padrão:\n$defaultApiUrl"
|
dialogMessage = "URL da API padrão:\n$defaultApiUrl"
|
||||||
|
|
||||||
setDefaultValue(defaultApiUrl)
|
setDefaultValue(defaultApiUrl)
|
||||||
|
},
|
||||||
|
|
||||||
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = DEFAULT_PREF
|
||||||
|
title = "Redefinir configurações"
|
||||||
|
summary = buildString {
|
||||||
|
append("Habilite para redefinir as configurações padrões no próximo reinicialização da aplicação.")
|
||||||
|
appendLine("Você pode limpar os dados da extensão em Configurações > Avançado:")
|
||||||
|
appendLine("\t - Limpar os cookies")
|
||||||
|
appendLine("\t - Limpar os dados da WebView")
|
||||||
|
appendLine("\t - Limpar o banco de dados (Procure a '$name' e remova os dados)")
|
||||||
|
}
|
||||||
|
setDefaultValue(false)
|
||||||
setOnPreferenceChangeListener { _, _ ->
|
setOnPreferenceChangeListener { _, _ ->
|
||||||
Toast.makeText(screen.context, RESTART_APP_MESSAGE, Toast.LENGTH_LONG).show()
|
Toast.makeText(screen.context, RESTART_APP_MESSAGE, Toast.LENGTH_LONG).show()
|
||||||
true
|
true
|
||||||
|
@ -615,11 +287,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
|
|
||||||
// ============================= Utilities ====================================
|
// ============================= Utilities ====================================
|
||||||
|
|
||||||
class Script(
|
|
||||||
val url: String,
|
|
||||||
val body: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun MangaDto.toSManga(): SManga {
|
private fun MangaDto.toSManga(): SManga {
|
||||||
val sManga = SManga.create().apply {
|
val sManga = SManga.create().apply {
|
||||||
title = name
|
title = name
|
||||||
|
@ -658,8 +325,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
const val CDN_URL = "https://cdn.sussytoons.site"
|
const val CDN_URL = "https://cdn.sussytoons.site"
|
||||||
const val OLDI_URL = "https://oldi.sussytoons.site"
|
const val OLDI_URL = "https://oldi.sussytoons.site"
|
||||||
const val mangaPagePrefix = "mangaPage:"
|
const val mangaPagePrefix = "mangaPage:"
|
||||||
const val chapterPagePrefix = "chapterPage:"
|
|
||||||
const val pageImagePrefix = "pageImage:"
|
|
||||||
|
|
||||||
private const val URL_PREF_SUMMARY = "Para uso temporário, se a extensão for atualizada, a alteração será perdida."
|
private const val URL_PREF_SUMMARY = "Para uso temporário, se a extensão for atualizada, a alteração será perdida."
|
||||||
|
|
||||||
|
@ -672,15 +337,7 @@ class SussyToons : HttpSource(), ConfigurableSource {
|
||||||
private const val API_BASE_URL_PREF_TITLE = "Editar URL da API da fonte"
|
private const val API_BASE_URL_PREF_TITLE = "Editar URL da API da fonte"
|
||||||
private const val API_DEFAULT_BASE_URL_PREF = "defaultApiUrl"
|
private const val API_DEFAULT_BASE_URL_PREF = "defaultApiUrl"
|
||||||
|
|
||||||
private const val CHAPTER_SCRIPT_URL_PREF = "chapterScriptUrl"
|
private const val DEFAULT_PREF = "defaultPref"
|
||||||
private const val PAGE_SCRIPT_URL_PREF = "pageScriptUrl"
|
|
||||||
|
|
||||||
val chapterUrlRegex = """push\("([^"]*capitulo[^"]*)\/?"\.concat""".toRegex()
|
|
||||||
val pageUrlRegex = """\.(get|post)\("([^"]*capitulo[^"]*)\/?"\.concat""".toRegex()
|
|
||||||
|
|
||||||
val apiUrlRegex = """(?<=production",)(.*?)(?=;function)""".toRegex()
|
|
||||||
val urlRegex = """https?://[\w\-]+(\.[\w\-]+)+[/#?]?.*$""".toRegex()
|
|
||||||
val stringsRegex = """"(.*?)"""".toRegex()
|
|
||||||
|
|
||||||
@SuppressLint("SimpleDateFormat")
|
@SuppressLint("SimpleDateFormat")
|
||||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
|
||||||
|
|
Loading…
Reference in New Issue