fix(ar/manga.ae): Fix issues with cloudflare (#19473)

* fix: Fix issue with cloudflare

* refactor: Remove unused preference

* refactor: Minor refactoration

* chore: Bump version
This commit is contained in:
Claudemirovsky 2023-12-29 12:18:31 -03:00 committed by GitHub
parent 804c3ef7bc
commit ac4700a760
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 129 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'Manga.ae' extName = 'Manga.ae'
pkgNameSuffix = 'ar.mangaae' pkgNameSuffix = 'ar.mangaae'
extClass = '.MangaAe' extClass = '.MangaAe'
extVersionCode = 10 extVersionCode = 11
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.extension.ar.mangaae package eu.kanade.tachiyomi.extension.ar.mangaae
import android.app.Application import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.BuildConfig import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -14,9 +14,7 @@ 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.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -27,13 +25,9 @@ class MangaAe : ParsedHttpSource(), ConfigurableSource {
override val name = "مانجا العرب" override val name = "مانجا العرب"
private val defaultBaseUrl = "https://manga.ae"
private val defaultUserAgent = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.3"
override val baseUrl by lazy { getPrefBaseUrl() } override val baseUrl by lazy { getPrefBaseUrl() }
private val userAgent by lazy { getPrefUserAgent() }
private val preferences: SharedPreferences by lazy { private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -41,137 +35,110 @@ class MangaAe : ParsedHttpSource(), ConfigurableSource {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client = network.cloudflareClient.newBuilder()
.rateLimit(2) .rateLimit(2)
.build() .build()
override fun headersBuilder() = Headers.Builder().apply { override fun headersBuilder() = super.headersBuilder()
set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7") .set("Referer", "$baseUrl/")
set("Accept-Language", "en-US,en;q=0.9,ar-MA;q=0.8,ar;q=0.7") .set("Origin", baseUrl)
set("Connection", "keep-alive")
set("Sec-Fetch-Dest", "document")
set("Sec-Fetch-Mode", "navigate")
set("Sec-Fetch-Site", "same-origin")
set("Sec-Fetch-User", "?1")
set("Referer", baseUrl)
}
// Popular // ============================== Popular ===============================
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int) = GET("$baseUrl/manga/page:$page", headers)
return GET("$baseUrl/manga/page:$page", headers)
override fun popularMangaSelector() = "div.mangacontainer"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
thumbnail_url = element.selectFirst("img")?.run {
attr("data-pagespeed-lazy-src").ifEmpty { attr("src") }
}
element.selectFirst("div.mangacontainer a.manga")!!.run {
title = text()
setUrlWithoutDomain(absUrl("href"))
}
} }
override fun popularMangaNextPageSelector() = "div.pagination a:last-child:not(.active)" override fun popularMangaNextPageSelector() = "div.pagination a:last-child:not(.active)"
override fun popularMangaSelector() = "div.mangacontainer" // =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { override fun latestUpdatesSelector() = "div.popular-manga-container"
val lazysrc = element.select("img").attr("data-pagespeed-lazy-src")
thumbnail_url = if (lazysrc.isNullOrEmpty()) { override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
element.select("img").attr("src") thumbnail_url = element.selectFirst("img")?.run {
} else { attr("data-pagespeed-lazy-src").ifEmpty { attr("src") }
lazysrc
}
element.select("div.mangacontainer a.manga")[0].let {
title = it.text()
setUrlWithoutDomain(it.attr("abs:href"))
} }
setUrlWithoutDomain(element.selectFirst("a:has(img)")!!.attr("href"))
title = element.selectFirst("a:last-child")!!.text()
} }
// Latest override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesRequest(page: Int): Request {
return GET(baseUrl, headers)
}
override fun latestUpdatesSelector(): String = "div.popular-manga-container" // =============================== Search ===============================
override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
val lazysrc = element.select("img").attr("data-pagespeed-lazy-src")
thumbnail_url = if (lazysrc.isNullOrEmpty()) {
element.select("img").attr("src")
} else {
lazysrc
}
setUrlWithoutDomain(element.select("a:has(img)").attr("href"))
title = element.select("a").last()!!.text()
}
override fun latestUpdatesNextPageSelector(): String? = null
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/manga/search:$query|page:$page" val url = buildString {
filters.forEach { filter -> append("$baseUrl/manga/search:$query|page:$page")
when (filter) { filters.firstOrNull { it is OrderByFilter }
is OrderByFilter -> { ?.takeUnless { it.state == 0 }
if (filter.state != 0) { ?.also {
url += "|order:${filter.toUriPart()}" val filter = it as OrderByFilter
} append("|order:${filter.toUriPart()}")
} }
else -> {} append("|arrange:minus")
}
} }
url += "|arrange:minus" return GET(url.toHttpUrl(), headers)
return GET(url.toHttpUrlOrNull()!!.newBuilder().build().toString(), headers)
} }
override fun searchMangaSelector(): String = popularMangaSelector() override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// Manga summary page // =========================== Manga Details ============================
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val infoElement = document.select("div.indexcontainer").first()!! val infoElement = document.selectFirst("div.indexcontainer")!!
title = infoElement.select("h1.EnglishName").text().removeSurrounding("(", ")") // Essential info, a NPE may be understandable
author = infoElement.select("div.manga-details-author h4")[0].text() with(infoElement) {
artist = author title = selectFirst("h1.EnglishName")!!.text().removeSurrounding("(", ")")
status = parseStatus(infoElement.select("div.manga-details-extended td h4")[0].text().trim()) author = selectFirst("div.manga-details-author h4")?.text()
genre = infoElement.select("div.manga-details-extended a[href*=tag]").joinToString(", ") { it.text() } artist = author
description = infoElement.select("div.manga-details-extended h4[style*=overflow-y]")[0].text() thumbnail_url = selectFirst("img.manga-cover")?.attr("src")
thumbnail_url = infoElement.select("img.manga-cover").attr("src") }
// Additional info
infoElement.selectFirst("div.manga-details-extended")?.run {
status = parseStatus(selectFirst("td h4")?.text().orEmpty())
genre = select("a[href*=tag]").eachText().joinToString()
description = selectFirst("h4[style*=overflow-y]")?.text()
}
} }
private fun parseStatus(status: String?) = when { private fun parseStatus(status: String) = when {
status == null -> SManga.UNKNOWN
status.contains("مستمرة") -> SManga.ONGOING status.contains("مستمرة") -> SManga.ONGOING
status.contains("مكتملة") -> SManga.COMPLETED status.contains("مكتملة") -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
// Chapters // ============================== Chapters ==============================
override fun chapterListSelector() = "ul.new-manga-chapters > li" override fun chapterListSelector() = "ul.new-manga-chapters > li a"
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val chapter = SChapter.create() setUrlWithoutDomain(element.attr("href").removeSuffix("/1/") + "/0/allpages")
element.select("a").let { name = "\u061C" + element.text() // Add unicode ARABIC LETTER MARK to ensure all titles are right to left
// use full pages for easier links
chapter.setUrlWithoutDomain(it.attr("href").removeSuffix("/1/") + "/0/allpages")
chapter.name = "\u061C" + it.text() // Add unicode ARABIC LETTER MARK to ensure all titles are right to left
}
return chapter
} }
// Pages // =============================== Pages ================================
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>() return document.select("div#showchaptercontainer img").mapIndexed { index, item ->
document.select("div#showchaptercontainer img").forEach { Page(index, "", item.attr("src"))
pages.add(Page(pages.size, "", it.attr("src")))
} }
return pages
} }
override fun imageUrlParse(document: Document): String = throw Exception("Not used") override fun imageUrlParse(document: Document): String = throw Exception("Not used")
override fun imageRequest(page: Page): Request { // ============================== Filters ===============================
val imgHeaders = headersBuilder().apply {
set("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
set("Referer", baseUrl)
}.build()
return GET(page.imageUrl!!, imgHeaders)
}
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) : private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) { Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second fun toUriPart() = vals[state].second
@ -188,48 +155,31 @@ class MangaAe : ParsedHttpSource(), ConfigurableSource {
), ),
) )
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(OrderByFilter())
OrderByFilter(),
)
// ============================== Settings ==============================
companion object { companion object {
private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل" private const val RESTART_TACHIYOMI = ".لتطبيق الإعدادات الجديدة Tachiyomi أعد تشغيل"
private const val BASE_URL_PREF_TITLE = "تعديل الرابط" private const val BASE_URL_PREF_TITLE = "تعديل الرابط"
private const val BASE_URL_PREF_DEFAULT = "https://manga.ae"
private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_CODE}" private const val BASE_URL_PREF = "overrideBaseUrl_v${BuildConfig.VERSION_CODE}"
private const val USER_AGENT_PREF_TITLE = "تعديل وكيل المستخدم" private const val BASE_URL_PREF_SUMMARY = ".للاستخدام المؤقت. تحديث التطبيق سيؤدي الى حذف الإعدادات"
private const val USER_AGENT_PREF = "overrideUserAgent_v${BuildConfig.VERSION_CODE}"
private const val PREF_SUMMARY = ".للاستخدام المؤقت. تحديث التطبيق سيؤدي الى حذف الإعدادات"
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val baseUrlPref = androidx.preference.EditTextPreference(screen.context).apply { EditTextPreference(screen.context).apply {
key = BASE_URL_PREF key = BASE_URL_PREF
title = BASE_URL_PREF_TITLE title = BASE_URL_PREF_TITLE
summary = PREF_SUMMARY summary = BASE_URL_PREF_SUMMARY
this.setDefaultValue(defaultBaseUrl) setDefaultValue(BASE_URL_PREF_DEFAULT)
dialogTitle = BASE_URL_PREF_TITLE dialogTitle = BASE_URL_PREF_TITLE
setOnPreferenceChangeListener { _, _ -> setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
true true
} }
} }.also(screen::addPreference)
val userAgentPref = androidx.preference.EditTextPreference(screen.context).apply {
key = USER_AGENT_PREF
title = USER_AGENT_PREF_TITLE
summary = PREF_SUMMARY
this.setDefaultValue(defaultUserAgent)
dialogTitle = USER_AGENT_PREF_TITLE
setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show()
true
}
}
screen.addPreference(baseUrlPref)
screen.addPreference(userAgentPref)
} }
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!! private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, BASE_URL_PREF_DEFAULT)!!
private fun getPrefUserAgent(): String = preferences.getString(USER_AGENT_PREF, defaultUserAgent)!!
} }