diff --git a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt deleted file mode 100644 index 0163cc160..000000000 --- a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt +++ /dev/null @@ -1,50 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.yugenmangas - -import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.lib.randomua.UserAgentType -import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.SChapter -import okhttp3.OkHttpClient -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class YugenMangas : Madara( - "YugenMangas", - "https://yugenmangas.com.br", - "pt-BR", - SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .setRandomUserAgent( - UserAgentType.DESKTOP, - ) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .rateLimit(1, 3, TimeUnit.SECONDS) - .build() - - override fun headersBuilder() = super.headersBuilder() - .add("Origin", baseUrl) - - override val useNewChapterEndpoint: Boolean = true - - override val mangaSubString = "series" - - override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - name = element.selectFirst("p.chapter-manhwa-title")!!.text() - date_upload = parseChapterDate(element.selectFirst("span.chapter-release-date i")?.text()) - - val chapterUrl = element.selectFirst("a")!!.attr("abs:href") - setUrlWithoutDomain( - chapterUrl.substringBefore("?style=paged") + - if (!chapterUrl.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "", - ) - } - - override fun setupPreferenceScreen(screen: PreferenceScreen) { } -} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index eaa78e652..fac8ee845 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -507,7 +507,6 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("YaoiScan", "https://yaoiscan.com", "en", isNsfw = true), SingleLang("YaoiToon", "https://yaoitoon.com", "en", isNsfw = true), SingleLang("YonaBar", "https://yonabar.com", "ar", isNsfw = true, overrideVersionCode = 2), - SingleLang("YugenMangas", "https://yugenmangas.com.br", "pt-BR", overrideVersionCode = 2), SingleLang("Yuri Verso", "https://yuri.live", "pt-BR", overrideVersionCode = 3), SingleLang("Zandy no Fansub", "https://zandynofansub.aishiteru.org", "en"), SingleLang("Zero Scan", "https://zeroscan.com.br", "pt-BR", isNsfw = true), diff --git a/src/pt/yugenmangas/AndroidManifest.xml b/src/pt/yugenmangas/AndroidManifest.xml new file mode 100644 index 000000000..8072ee00d --- /dev/null +++ b/src/pt/yugenmangas/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/src/pt/yugenmangas/build.gradle b/src/pt/yugenmangas/build.gradle new file mode 100644 index 000000000..1d4b531b0 --- /dev/null +++ b/src/pt/yugenmangas/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Yugen Mangás' + pkgNameSuffix = 'pt.yugenmangas' + extClass = '.YugenMangas' + extVersionCode = 34 +} + +apply from: "$rootDir/common.gradle" diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-hdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/mipmap-hdpi/ic_launcher.png rename to src/pt/yugenmangas/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-mdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/mipmap-mdpi/ic_launcher.png rename to src/pt/yugenmangas/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xhdpi/ic_launcher.png rename to src/pt/yugenmangas/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png rename to src/pt/yugenmangas/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png rename to src/pt/yugenmangas/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/yugenmangas/res/web_hi_res_512.png b/src/pt/yugenmangas/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/madara/yugenmangas/res/web_hi_res_512.png rename to src/pt/yugenmangas/res/web_hi_res_512.png diff --git a/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt b/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt new file mode 100644 index 000000000..80d66f43b --- /dev/null +++ b/src/pt/yugenmangas/src/eu/kanade/tachiyomi/extension/pt/yugenmangas/YugenMangas.kt @@ -0,0 +1,159 @@ +package eu.kanade.tachiyomi.extension.pt.yugenmangas + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.FilterList +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 eu.kanade.tachiyomi.source.online.ParsedHttpSource +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +/** + * Changed the name from "YugenMangas" to "Yugen Mangás" when + * the source was updated to handle their CMS changes, so no + * `versionId` change is needed as the ID should be different to + * force users to migrate. + */ +class YugenMangas : ParsedHttpSource() { + + override val name = "Yugen Mangás" + + override val baseUrl = "https://yugenmangas.net.br" + + override val lang = "pt-BR" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + private val json: Json by injectLazy() + + override fun headersBuilder(): Headers.Builder = Headers.Builder() + .add("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int): Request = GET(baseUrl, headers) + + override fun popularMangaSelector(): String = "div.container-popular div.swiper-wrapper a" + + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("h1")!!.text() + thumbnail_url = element.selectFirst("img")!!.absUrl("src") + url = element.attr("href") + } + + override fun popularMangaNextPageSelector(): String? = null + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/updates/?page=$page", headers) + } + + override fun latestUpdatesSelector() = "div.container-update-series div.card-series-updates" + + override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("a.title-serie h1")!!.text() + thumbnail_url = element.selectFirst("img")!!.absUrl("src") + url = element.selectFirst("a")!!.attr("href") + } + + override fun latestUpdatesNextPageSelector() = "div.pagination a:contains(Próxima)" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = "$baseUrl/api/series/list".toHttpUrl().newBuilder() + .addQueryParameter("query", query) + .build() + + return GET(url, headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val result = json.decodeFromString>(response.body.string()) + val matches = result.map { + SManga.create().apply { + title = it.name + url = "/series/${it.slug}" + } + } + + return MangasPage(matches, hasNextPage = false) + } + + override fun searchMangaSelector() = throw UnsupportedOperationException("Not used") + + override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used") + + override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used") + + override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { + val infoElement = document.selectFirst("div.main div.resume > div.sinopse")!! + + title = infoElement.selectFirst("div.title-name h1")!!.text() + author = infoElement.selectFirst("div.author")!!.text() + genre = infoElement.select("div.genero span").joinToString { it.text() } + status = infoElement.selectFirst("div.lancamento p")!!.text().toStatus() + description = infoElement.select("div.sinopse > p").text() + thumbnail_url = document.selectFirst("div.content div.side div.top-side img")!!.absUrl("src") + } + + override fun chapterListSelector() = "#listadecapitulos div.chapter a" + + override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + name = element.selectFirst("span.chapter-title")!!.text() + scanlator = element.selectFirst("div.end-chapter span")?.text() + date_upload = element.selectFirst("span.chapter-lancado")!!.text().toDate() + url = element.attr("href") + } + + override fun pageListParse(document: Document): List { + return document.select("div.chapter-content img.chapter-image") + .mapIndexed { index, element -> + Page(index, document.location(), element.absUrl("src")) + } + } + + override fun imageUrlParse(document: Document) = "" + + override fun imageRequest(page: Page): Request { + val newHeaders = headersBuilder() + .set("Referer", page.url) + .build() + + return GET(page.imageUrl!!, newHeaders) + } + + @Serializable + private data class SearchResultDto(val name: String, val slug: String) + + private fun String.toDate(): Long { + return runCatching { DATE_FORMATTER.parse(trim())?.time } + .getOrNull() ?: 0L + } + + private fun String.toStatus() = when (this) { + "ongoing" -> SManga.ONGOING + "completed", "finished" -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + companion object { + private val DATE_FORMATTER by lazy { + SimpleDateFormat("dd.MM.yyyy", Locale("pt", "BR")) + } + } +}