Yeet Neox for good. (#12695)
This commit is contained in:
parent
3ff93b4d09
commit
69aec03657
|
@ -35,7 +35,7 @@ jobs:
|
|||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": ".*(mangago|mangafox|hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|heroscan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|read\\s*comic\\s*online|coco\\s*manhua|hitomi\\.la|copymanga).*",
|
||||
"regex": ".*(mangago|mangafox|hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|heroscan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|read\\s*comic\\s*online|coco\\s*manhua|hitomi\\.la|copymanga|neox).*",
|
||||
"ignoreCase": true,
|
||||
"message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information"
|
||||
},
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
|
@ -1,290 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.pt.neoxscanlator
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.AppInfo
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.asObservable
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
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 kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.Call
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.Exception
|
||||
|
||||
class NeoxScanlator :
|
||||
Madara(
|
||||
"Neox Scanlator",
|
||||
DEFAULT_BASE_URL,
|
||||
"pt-BR",
|
||||
SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR"))
|
||||
),
|
||||
ConfigurableSource {
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.connectTimeout(1, TimeUnit.MINUTES)
|
||||
.readTimeout(1, TimeUnit.MINUTES)
|
||||
.addInterceptor(::titleCollectionIntercept)
|
||||
.addInterceptor(::obsoleteCheckIntercept)
|
||||
.rateLimit(1, 2, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
override val altNameSelector = ".post-content_item:contains(Alternativo) .summary-content"
|
||||
|
||||
override val chapterUrlSelector = "a:not(:has(img.thumb))"
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override val baseUrl: String by lazy {
|
||||
preferences.getString(BASE_URL_PREF_KEY, DEFAULT_BASE_URL)!!
|
||||
}
|
||||
|
||||
private var titleCollectionPath: String? = null
|
||||
|
||||
private var extIsObsolete: Boolean? = null
|
||||
|
||||
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
|
||||
.add("Accept", ACCEPT)
|
||||
.add("Accept-Language", ACCEPT_LANGUAGE)
|
||||
.add("Referer", REFERER)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val popularPage = super.popularMangaParse(response)
|
||||
|
||||
titleCollectionPath = popularPage.mangas.firstOrNull()?.url
|
||||
?.removePrefix("/")
|
||||
?.removeSuffix("/")
|
||||
?.substringBeforeLast("/")
|
||||
|
||||
return popularPage
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val latestPage = super.latestUpdatesParse(response)
|
||||
|
||||
titleCollectionPath = latestPage.mangas.firstOrNull()?.url
|
||||
?.removePrefix("/")
|
||||
?.removeSuffix("/")
|
||||
?.substringBeforeLast("/")
|
||||
|
||||
return latestPage
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val searchPage = super.searchMangaParse(response)
|
||||
|
||||
titleCollectionPath = searchPage.mangas.firstOrNull()?.url
|
||||
?.removePrefix("/")
|
||||
?.removeSuffix("/")
|
||||
?.substringBeforeLast("/")
|
||||
|
||||
return searchPage
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
return client.newCall(mangaDetailsRequest(manga))
|
||||
.asCustomObservable()
|
||||
.map { response ->
|
||||
mangaDetailsParse(response).apply { initialized = true }
|
||||
}
|
||||
}
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
val titleSlug = manga.url
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
val fixedPath = titleCollectionPath ?: TITLE_PATH_PLACEHOLDER
|
||||
|
||||
return GET("$baseUrl/$fixedPath/$titleSlug", headers)
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
return client.newCall(chapterListRequest(manga))
|
||||
.asCustomObservable()
|
||||
.map(::chapterListParse)
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga)
|
||||
|
||||
override fun xhrChaptersRequest(mangaUrl: String): Request {
|
||||
val xhrHeaders = headersBuilder()
|
||||
.add("Referer", baseUrl)
|
||||
.add("X-Requested-With", "XMLHttpRequest")
|
||||
.build()
|
||||
|
||||
val titleSlug = mangaUrl
|
||||
.substringAfter(baseUrl)
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
val fixedPath = titleCollectionPath ?: TITLE_PATH_PLACEHOLDER
|
||||
|
||||
return POST("$baseUrl/$fixedPath/$titleSlug/ajax/chapters", xhrHeaders)
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val chapterUrl = chapter.url.toHttpUrlOrNull()
|
||||
?: return super.pageListRequest(chapter)
|
||||
|
||||
val chapterSlug = chapterUrl.pathSegments
|
||||
.takeLast(3)
|
||||
.joinToString("/")
|
||||
val fixedPath = titleCollectionPath ?: TITLE_PATH_PLACEHOLDER
|
||||
|
||||
return GET("$baseUrl/$fixedPath/$chapterSlug", headers)
|
||||
}
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val newHeaders = headersBuilder()
|
||||
.set("Accept", ACCEPT_IMAGE)
|
||||
.set("Referer", page.url)
|
||||
.build()
|
||||
|
||||
return GET(page.imageUrl!!, newHeaders)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val baseUrlPref = EditTextPreference(screen.context).apply {
|
||||
key = BASE_URL_PREF_KEY
|
||||
title = BASE_URL_PREF_TITLE
|
||||
summary = BASE_URL_PREF_SUMMARY
|
||||
setDefaultValue(DEFAULT_BASE_URL)
|
||||
dialogTitle = BASE_URL_PREF_TITLE
|
||||
dialogMessage = "Padrão: $DEFAULT_BASE_URL"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val res = preferences.edit()
|
||||
.putString(BASE_URL_PREF_KEY, newValue as String)
|
||||
.commit()
|
||||
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
|
||||
res
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(baseUrlPref)
|
||||
}
|
||||
|
||||
private fun titleCollectionIntercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
if (!request.url.toString().contains(TITLE_PATH_PLACEHOLDER)) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
val titlePathResult = runCatching {
|
||||
val popularResponse = chain.proceed(popularMangaRequest(1))
|
||||
val popularPage = popularMangaParse(popularResponse)
|
||||
|
||||
popularPage.mangas.firstOrNull()?.url
|
||||
?.removePrefix("/")
|
||||
?.removeSuffix("/")
|
||||
?.substringBeforeLast("/")
|
||||
}
|
||||
|
||||
titleCollectionPath = titlePathResult.getOrNull()
|
||||
|
||||
val fixedUrl = request.url.toString()
|
||||
.replace(TITLE_PATH_PLACEHOLDER, titleCollectionPath ?: "manga")
|
||||
|
||||
val fixedRequest = request.newBuilder()
|
||||
.url(fixedUrl)
|
||||
.build()
|
||||
|
||||
return chain.proceed(fixedRequest)
|
||||
}
|
||||
|
||||
private fun obsoleteCheckIntercept(chain: Interceptor.Chain): Response {
|
||||
if (extIsObsolete == null) {
|
||||
val repoRequest = GET(EXT_REPO_JSON)
|
||||
val repoResponse = chain.proceed(repoRequest)
|
||||
val repoJson = json.parseToJsonElement(repoResponse.body!!.string()).jsonArray
|
||||
|
||||
val extDetails = repoJson.firstOrNull { it.jsonObject["pkg"]!!.jsonPrimitive.content == EXT_PKG }
|
||||
extIsObsolete = extDetails == null
|
||||
|
||||
repoResponse.close()
|
||||
}
|
||||
|
||||
if (extIsObsolete == true) {
|
||||
throw IOException(OBSOLETE_ERROR)
|
||||
}
|
||||
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
private fun Call.asCustomObservable(): Observable<Response> {
|
||||
return asObservable().doOnNext { response ->
|
||||
if (!response.isSuccessful) {
|
||||
response.close()
|
||||
val message = if (response.code == 404 || response.code == 403)
|
||||
MIGRATION_MESSAGE else "HTTP error ${response.code}"
|
||||
throw Exception(message)
|
||||
}
|
||||
|
||||
if (response.request.url.toString() == "$baseUrl/" && response.code == 200) {
|
||||
response.close()
|
||||
throw Exception(MIGRATION_MESSAGE)
|
||||
}
|
||||
|
||||
if (!response.headers["Content-Type"]!!.contains("text/html")) {
|
||||
response.close()
|
||||
throw Exception(MIGRATION_MESSAGE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MIGRATION_MESSAGE = "A URL deste título mudou. " +
|
||||
"Faça a migração da Neox para a Neox para atualizar a URL."
|
||||
|
||||
private const val ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9," +
|
||||
"image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
private const val ACCEPT_IMAGE = "image/webp,image/apng,image/*,*/*;q=0.8"
|
||||
private const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,gl;q=0.5"
|
||||
private const val REFERER = "https://google.com/"
|
||||
|
||||
private const val DEFAULT_BASE_URL = "https://neoxscans.net"
|
||||
private val BASE_URL_PREF_KEY = "base_url_${AppInfo.getVersionName()}"
|
||||
private const val BASE_URL_PREF_TITLE = "URL da fonte"
|
||||
private const val BASE_URL_PREF_SUMMARY = "Para uso temporário. Quando você atualizar a " +
|
||||
"extensão, esta configuração será apagada."
|
||||
|
||||
private const val RESTART_TACHIYOMI = "Reinicie o Tachiyomi para aplicar as configurações."
|
||||
|
||||
private val TITLE_PATH_PLACEHOLDER = UUID.randomUUID().toString()
|
||||
|
||||
private const val EXT_REPO_JSON = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/index.json"
|
||||
private const val EXT_PKG = "eu.kanade.tachiyomi.extension.pt.neoxscanlator"
|
||||
private const val OBSOLETE_ERROR = "Extensão obsoleta. Migre para fontes melhores."
|
||||
}
|
||||
}
|
|
@ -367,7 +367,6 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("NeatManga", "https://neatmanga.com", "en", overrideVersionCode = 2),
|
||||
SingleLang("NekoBreaker Scan", "https://nekobreakerscan.com", "pt-BR", overrideVersionCode = 1),
|
||||
SingleLang("NekoScan", "https://nekoscan.com", "en", overrideVersionCode = 2),
|
||||
SingleLang("Neox Scanlator", "https://neoxscans.net", "pt-BR", overrideVersionCode = 14),
|
||||
SingleLang("Night Comic", "https://www.nightcomic.com", "en", overrideVersionCode = 1),
|
||||
SingleLang("Niji Translations", "https://niji-translations.com", "ar", overrideVersionCode = 1),
|
||||
SingleLang("Ninja Scan", "https://ninjascan.xyz", "pt-BR", overrideVersionCode = 1),
|
||||
|
|
Loading…
Reference in New Issue