Fix sources and add Asura Scans TR for wpmangastream (#8983)

* Phoenix Fansub: Fix manga urls and chapter dates

* Mihentai: Fix extension

* AsuraScans: Convert to multilang, adding TR source
This commit is contained in:
Arraiment 2021-09-06 18:19:14 +08:00 committed by GitHub
parent 4598685c2c
commit 0924c076d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 300 additions and 32 deletions

View File

@ -1,28 +0,0 @@
package eu.kanade.tachiyomi.extension.en.asurascans
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
import eu.kanade.tachiyomi.source.model.Page
import okhttp3.OkHttpClient
import org.jsoup.nodes.Document
import java.util.concurrent.TimeUnit
class AsuraScans : WPMangaStream("AsuraScans", "https://www.asurascans.com", "en") {
private val rateLimitInterceptor = RateLimitInterceptor(1, 3, TimeUnit.SECONDS)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(rateLimitInterceptor)
.build()
override val pageSelector = "div.rdminimal img[loading*=lazy]"
// Skip scriptPages
override fun pageListParse(document: Document): List<Page> {
return document.select(pageSelector)
.filterNot { it.attr("abs:src").isNullOrEmpty() }
.mapIndexed { i, img -> Page(i, "", img.attr("abs:src")) }
}
}

View File

@ -0,0 +1,98 @@
package eu.kanade.tachiyomi.extension.all.asurascans
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.OkHttpClient
import org.jsoup.nodes.Document
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class AsuraScansFactory : SourceFactory {
override fun createSources() = listOf(
AsuraScansEn(),
AsuraScansTr()
)
}
abstract class AsuraScans(
override val baseUrl: String,
override val lang: String,
dateFormat: SimpleDateFormat
) : WPMangaStream("Asura Scans", baseUrl, lang, dateFormat) {
private val rateLimitInterceptor = RateLimitInterceptor(1, 3, TimeUnit.SECONDS)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(rateLimitInterceptor)
.build()
}
class AsuraScansEn : AsuraScans("https://asurascans.com/", "en", SimpleDateFormat("MMM d, yyyy", Locale.US)) {
override val pageSelector = "div.rdminimal img[loading*=lazy]"
// Skip scriptPages
override fun pageListParse(document: Document): List<Page> {
return document.select(pageSelector)
.filterNot { it.attr("abs:src").isNullOrEmpty() }
.mapIndexed { i, img -> Page(i, "", img.attr("abs:src")) }
}
}
class AsuraScansTr : AsuraScans("https://tr.asurascans.com/", "tr", SimpleDateFormat("MMM d, yyyy", Locale("tr"))) {
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
document.select("div.bigcontent").firstOrNull()?.let { infoElement ->
status = parseStatus(infoElement.select(".imptdt:contains(Durum) i").firstOrNull()?.ownText())
infoElement.select(".fmed b:contains(Yazar)+span").firstOrNull()?.ownText().let {
if (it.isNullOrBlank().not() && it != "N/A" && it != "-") {
author = it
}
}
infoElement.select(".fmed b:contains(Çizer)+span").firstOrNull()?.ownText().let {
if (it.isNullOrBlank().not() && it != "N/A" && it != "-") {
artist = it
}
}
description = infoElement.select("div.desc p, div.entry-content p").joinToString("\n") { it.text() }
thumbnail_url = infoElement.select("div.thumb img").imgAttr()
val genres = infoElement.select(".mgen a")
.map { element -> element.text().toLowerCase(Locale.ROOT) }
.toMutableSet()
// add series type(manga/manhwa/manhua/other) thinggy to genre
document.select(seriesTypeSelector).firstOrNull()?.ownText()?.let {
if (it.isEmpty().not() && genres.contains(it).not()) {
genres.add(it.toLowerCase(Locale.ROOT))
}
}
genre = genres.toList().map { it.capitalize(Locale.ROOT) }.joinToString(", ")
// add alternative name to manga description
document.select(altNameSelector).firstOrNull()?.ownText()?.let {
if (it.isBlank().not() && it != "N/A" && it != "-") {
description = when {
description.isNullOrBlank() -> altName + it
else -> description + "\n\n$altName" + it
}
}
}
}
}
}
override val seriesTypeSelector = ".imptdt:contains(Tür) a"
override val altName: String = "Alternatif isim: "
override fun parseStatus(element: String?): Int = when {
element == null -> SManga.UNKNOWN
listOf("Devam Ediyor").any { it.contains(element, ignoreCase = true) } -> SManga.ONGOING
listOf("Tamamlandı").any { it.contains(element, ignoreCase = true) } -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}

View File

@ -2,6 +2,180 @@ package eu.kanade.tachiyomi.extension.en.mihentai
import eu.kanade.tachiyomi.annotations.Nsfw
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import org.jsoup.nodes.Document
import uy.kohesive.injekt.injectLazy
import java.util.Locale
@Nsfw
class Mihentai : WPMangaStream("Mihentai", "https://mihentai.com", "en")
class Mihentai : WPMangaStream("Mihentai", "https://mihentai.com", "en") {
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/manga/page/$page/?order=popular", headers)
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/manga/page/$page/?order=update", headers)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/manga/page/$page/".toHttpUrlOrNull()!!.newBuilder()
url.addQueryParameter("title", query)
filters.forEach { filter ->
when (filter) {
is StatusFilter -> url.addQueryParameter("status", filter.toUriPart())
is TypeFilter -> url.addQueryParameter("type", filter.toUriPart())
is SortByFilter -> url.addQueryParameter("order", filter.toUriPart())
is GenreListFilter -> {
filter.state
.filter { it.state != Filter.TriState.STATE_IGNORE }
.forEach { url.addQueryParameter("genre[]", it.id) }
}
}
}
return GET(url.build().toString(), headers)
}
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
document.select("div.bigcontent, div.animefull, div.main-info").firstOrNull()?.let { infoElement ->
status = parseStatus(infoElement.select("span:contains(Status:), .imptdt:contains(Status) i").firstOrNull()?.ownText())
thumbnail_url = infoElement.select("div.thumb img").imgAttr()
val genres = infoElement.select("span:contains(Tag) a")
.map { element -> element.text().toLowerCase(Locale.ROOT) }
.toMutableSet()
// add series type(manga/manhwa/manhua/other) thinggy to genre
document.select("span:contains(Type)").firstOrNull()?.ownText()?.let {
if (it.isEmpty().not() && genres.contains(it).not()) {
genres.add(it.toLowerCase(Locale.ROOT))
}
}
genre = genres.toList().joinToString(", ") { it.capitalize(Locale.ROOT) }
}
}
}
override fun parseStatus(element: String?): Int = when {
element == null -> SManga.UNKNOWN
listOf("ongoing", "publishing").any { it.contains(element, ignoreCase = true) } -> SManga.ONGOING
listOf("finished").any { it.contains(element, ignoreCase = true) } -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
private val json: Json by injectLazy()
override fun pageListParse(document: Document): List<Page> {
val htmlPages = document.select(pageSelector)
.filterNot { it.attr("abs:src").isNullOrEmpty() }
.mapIndexed { i, img ->
val pageUrl = img.attr("abs:src").substringAfter(baseUrl).prependIndent(baseUrl)
Page(i, "", pageUrl)
}
.toMutableList()
val docString = document.toString()
val imageListRegex = Regex("\\\"images.*?:.*?(\\[.*?\\])")
val imageListJson = imageListRegex.find(docString)?.destructured?.toList()?.get(0)
if (imageListJson != null) {
val imageList = json.parseToJsonElement(imageListJson).jsonArray
val baseResolver = baseUrl.toHttpUrl()
val scriptPages = imageList.mapIndexed { i, jsonEl ->
val imageUrl = jsonEl.jsonPrimitive.content
Page(i, "", baseResolver.resolve(imageUrl).toString())
}
if (htmlPages.size < scriptPages.size) {
htmlPages += scriptPages
}
}
countViews(document)
return htmlPages.distinctBy { it.imageUrl }
}
private class StatusFilter : UriPartFilter(
"Status",
arrayOf(
Pair("All", ""),
Pair("Publishing", "publishing"),
Pair("Finished", "finished"),
Pair("Dropped", "drop")
)
)
private class TypeFilter : UriPartFilter(
"Type",
arrayOf(
Pair("Default", ""),
Pair("Manga", "Manga"),
Pair("Manhwa", "Manhwa"),
Pair("Manhua", "Manhua"),
Pair("Webtoon", "webtoon"),
Pair("One-Shot", "One-Shot"),
Pair("Doujin", "doujin")
)
)
override fun getFilterList(): FilterList = FilterList(
listOf(
StatusFilter(),
TypeFilter(),
SortByFilter(),
GenreListFilter(getGenreList())
)
)
override fun getGenreList(): List<Genre> = listOf(
Genre("Adventure", "adventure"),
Genre("Ahego", "ahego"),
Genre("Anal", "anal"),
Genre("Battle", "battle"),
Genre("Big Breasts", "big-breasts"),
Genre("Blowjob", "blowjob"),
Genre("Comic 3D", "comic-3d"),
Genre("Doujin", "doujin"),
Genre("Dragon ball", "dragon-ball"),
Genre("Fingering", "fingering"),
Genre("Full color", "full-color"),
Genre("Futanari", "futanari"),
Genre("Girlfriend", "girlfriend"),
Genre("Grouped", "grouped"),
Genre("Handjob", "handjob"),
Genre("Hijab", "hijab"),
Genre("Incest", "incest"),
Genre("Kissing", "kissing"),
Genre("Mama", "mama"),
Genre("Manga", "manga"),
Genre("Masturbation", "masturbation"),
Genre("Milf", "milf"),
Genre("Mom & Son", "mom-son"),
Genre("Naruto", "naruto"),
Genre("One Piece", "one-piece"),
Genre("Pregnancy", "pregnancy"),
Genre("Rape", "rape"),
Genre("Romance", "romance"),
Genre("School", "school"),
Genre("Scooby-Doo", "scooby-doo"),
Genre("Sister", "sister"),
Genre("Stocking", "stocking"),
Genre("Sub Indo", "sub-indo"),
Genre("Threesome", "threesome"),
Genre("Uncensored", "uncensored"),
Genre("Western", "western"),
Genre("Yuri", "yuri")
)
}

View File

@ -0,0 +1,23 @@
package eu.kanade.tachiyomi.extension.es.phoenixfansub
import eu.kanade.tachiyomi.multisrc.wpmangastream.WPMangaStream
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class PhoenixFansub : WPMangaStream(
"Phoenix Fansub",
"https://phoenixfansub.com",
"es",
SimpleDateFormat("MMM d, yyyy", Locale("es"))
) {
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
thumbnail_url = element.select("div.limit img").imgAttr()
element.select("div.bsx > a").first().let {
url = "/manga/${it.attr("href")}"
title = it.attr("title")
}
}
override val altName: String = "Nombre alternativo: "
}

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.multisrc.wpmangastream
import generator.ThemeSourceData.MultiLang
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
@ -12,7 +13,7 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
override val baseVersionCode: Int = 11
override val sources = listOf(
SingleLang("Asura Scans", "https://www.asurascans.com", "en", overrideVersionCode = 5),
MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 6),
SingleLang("KlanKomik", "https://klankomik.com", "id", overrideVersionCode = 1),
SingleLang("MasterKomik", "https://masterkomik.com", "id", overrideVersionCode = 1),
SingleLang("Kaisar Komik", "https://kaisarkomik.com", "id", overrideVersionCode = 1),
@ -35,7 +36,7 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
SingleLang("MangaSwat", "https://mangaswat.com", "ar", overrideVersionCode = 3),
SingleLang("Manga Raw.org", "https://mangaraw.org", "ja", className = "MangaRawOrg", overrideVersionCode = 1),
SingleLang("Manga Pro Z", "https://mangaproz.com", "ar"),
SingleLang("Mihentai", "https://mihentai.com", "en", isNsfw = true),
SingleLang("Mihentai", "https://mihentai.com", "en", isNsfw = true, overrideVersionCode = 1),
SingleLang("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en", className = "KumaScans", overrideVersionCode = 1),
SingleLang("Tempest Manga", "https://manga.tempestfansub.com", "tr"),
SingleLang("xCaliBR Scans", "https://xcalibrscans.com", "en", overrideVersionCode = 2),
@ -44,7 +45,7 @@ class WPMangaStreamGenerator : ThemeSourceGenerator {
SingleLang("The Apollo Team", "https://theapollo.team", "en"),
SingleLang("Sekte Doujin", "https://sektedoujin.xyz", "id", isNsfw = true, overrideVersionCode = 2),
SingleLang("Lemon Juice Scan", "https://lemonjuicescan.com", "pt-BR", isNsfw = true, overrideVersionCode = 1),
SingleLang("Phoenix Fansub", "https://phoenixfansub.com", "es"),
SingleLang("Phoenix Fansub", "https://phoenixfansub.com", "es", overrideVersionCode = 1),
SingleLang("Geass Scanlator", "https://geassscan.xyz", "pt-BR", overrideVersionCode = 2),
SingleLang("Imagine Scan", "https://imaginescan.com.br", "pt-BR", isNsfw = true),
SingleLang("Vapo Scan", "https://vaposcans.com", "pt-BR", overrideVersionCode = 2),