diff --git a/lib-multisrc/vercomics/build.gradle.kts b/lib-multisrc/vercomics/build.gradle.kts new file mode 100644 index 000000000..dc076cc37 --- /dev/null +++ b/lib-multisrc/vercomics/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("lib-multisrc") +} + +baseVersionCode = 1 diff --git a/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMP.kt b/lib-multisrc/vercomics/src/eu/kanade/tachiyomi/multisrc/vercomics/VerComics.kt similarity index 69% rename from src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMP.kt rename to lib-multisrc/vercomics/src/eu/kanade/tachiyomi/multisrc/vercomics/VerComics.kt index 5c805faf6..afc1a9c3d 100644 --- a/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMP.kt +++ b/lib-multisrc/vercomics/src/eu/kanade/tachiyomi/multisrc/vercomics/VerComics.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.extension.es.vcpvmp +package eu.kanade.tachiyomi.multisrc.vercomics import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.model.Filter @@ -8,45 +8,79 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.online.ParsedHttpSource -import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -open class VCPVMP(override val name: String, override val baseUrl: String) : ParsedHttpSource() { - - override val lang = "es" +abstract class VerComics( + override val name: String, + override val baseUrl: String, + override val lang: String, +) : ParsedHttpSource() { override val supportsLatest: Boolean = false - override fun headersBuilder(): Headers.Builder { - return Headers.Builder() - .add("Referer", "$baseUrl/") - } + protected open val urlSuffix = "" + protected open val genreSuffix = "" + protected open val useSuffixOnSearch = true - override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() - - override fun latestUpdatesSelector() = throw UnsupportedOperationException() - - override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException() - - override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException() + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int) = GET("$baseUrl/$urlSuffix/page/$page", headers) - override fun popularMangaSelector() = "div.blog-list-items > div.entry" + override fun popularMangaSelector() = "header:has(h1) ~ * .entry" + + override fun popularMangaNextPageSelector() = "div.wp-pagenavi > span.current + a" override fun popularMangaFromElement(element: Element) = SManga.create().apply { element.select("a.popimg").first()!!.let { setUrlWithoutDomain(it.attr("href")) title = it.select("img").attr("alt") - thumbnail_url = it.select("img:not(noscript img)").attr("abs:data-src") + thumbnail_url = it.selectFirst("img:not(noscript img)")?.imgAttr() } } - override fun popularMangaNextPageSelector() = "div.wp-pagenavi > span.current + a" + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + var url = baseUrl.toHttpUrl().newBuilder() + + if (query.isNotBlank()) { + url = baseUrl.toHttpUrl().newBuilder() + if (useSuffixOnSearch) { + url.addPathSegments(urlSuffix) + } + url.addPathSegments("page") + url.addPathSegments(page.toString()) + url.addQueryParameter("s", query) + + return GET(url.build(), headers) + } + + filters.forEach { filter -> + when (filter) { + is Genre -> { + if (filter.toUriPart().isNotEmpty()) { + url.addPathSegments(genreSuffix) + url.addPathSegments(filter.toUriPart()) + + url.addPathSegments("page") + url.addPathSegments(page.toString()) + } + } + else -> {} + } + } + + return GET(url.build(), headers) + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) override fun mangaDetailsParse(document: Document) = SManga.create().apply { document.select("div.tax_post").let { @@ -81,50 +115,13 @@ open class VCPVMP(override val name: String, override val baseUrl: String) : Par override fun chapterListSelector() = throw UnsupportedOperationException() override fun chapterFromElement(element: Element) = throw UnsupportedOperationException() - protected open val pageListSelector = "div.wp-content p > img:not(noscript img)" + protected open val pageListSelector = + "div.wp-content p > img:not(noscript img), " + + "div.wp-content div#lector > img:not(noscript img), " + + "div.wp-content > figure img:not(noscript img)" + override fun pageListParse(document: Document): List = document.select(pageListSelector) - .mapIndexed { i, img -> Page(i, "", img.attr("abs:data-src")) } - - override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() - - protected open val urlSuffix = "" - protected open val genreSuffix = "" - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - var url = baseUrl.toHttpUrl().newBuilder() - - if (query.isNotBlank()) { - url = "$baseUrl/$urlSuffix".toHttpUrl().newBuilder() - url.addPathSegments("page") - url.addPathSegments(page.toString()) - url.addQueryParameter("s", query) - - return GET(url.build(), headers) - } - - filters.forEach { filter -> - when (filter) { - is Genre -> { - if (filter.toUriPart().isNotEmpty()) { - url.addPathSegments(genreSuffix) - url.addPathSegments(filter.toUriPart()) - - url.addPathSegments("page") - url.addPathSegments(page.toString()) - } - } - else -> {} - } - } - - return GET(url.build(), headers) - } - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + .mapIndexed { i, img -> Page(i, imageUrl = img.imgAttr()) } protected open var genres = arrayOf(Pair("Ver todos", "")) @@ -138,11 +135,45 @@ open class VCPVMP(override val name: String, override val baseUrl: String) : Par return FilterList(filters) } - // Array.from(document.querySelectorAll('div.tagcloud a.tag-cloud-link')).map(a => `Pair("${a.innerText}", "${a.href.replace('https://vercomicsporno.com/etiquetas/', '')}")`).join(',\n') - // from https://vercomicsporno.com/ + protected open fun Element.imgAttr(): String? { + return when { + this.hasAttr("data-src") -> this.attr("abs:data-src") + this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src") + this.hasAttr("srcset") -> this.attr("abs:srcset").getSrcSetImage() + this.hasAttr("data-cfsrc") -> this.attr("abs:data-cfsrc") + else -> this.attr("abs:src") + } + } - private class Genre(genres: Array>) : UriPartFilter( + private fun String.getSrcSetImage(): String? { + return this.split(" ") + .filter(URL_REGEX::matches) + .maxOfOrNull(String::toString) + } + + // Replace the baseUrl and genreSuffix in the following string + // Array.from(document.querySelectorAll('div.tagcloud a.tag-cloud-link')).map(a => `Pair("${a.innerText}", "${a.href.replace('$baseUrl/genreSuffix/', '')}")`).join(',\n') + class Genre(genres: Array>) : UriPartFilter( "Filtrar por género", genres, ) + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesSelector() = throw UnsupportedOperationException() + + override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException() + + override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException() + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() + + companion object { + val URL_REGEX = """^(https?://[^\s/$.?#].[^\s]*)${'$'}""".toRegex() + } + + open class UriPartFilter(displayName: String, private val vals: Array>) : + Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { + fun toUriPart() = vals[state].second + } } diff --git a/src/es/chochox/build.gradle b/src/es/chochox/build.gradle new file mode 100644 index 000000000..73afd9323 --- /dev/null +++ b/src/es/chochox/build.gradle @@ -0,0 +1,9 @@ +ext { + extName = 'ChoChoX' + extClass = '.Chochox' + themePkg = 'vercomics' + overrideVersionCode = 0 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/es/chochox/res/mipmap-hdpi/ic_launcher.png b/src/es/chochox/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..478448c07 Binary files /dev/null and b/src/es/chochox/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/es/chochox/res/mipmap-mdpi/ic_launcher.png b/src/es/chochox/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..85775aca2 Binary files /dev/null and b/src/es/chochox/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/es/chochox/res/mipmap-xhdpi/ic_launcher.png b/src/es/chochox/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..426d8eff3 Binary files /dev/null and b/src/es/chochox/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/es/chochox/res/mipmap-xxhdpi/ic_launcher.png b/src/es/chochox/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d65405e6d Binary files /dev/null and b/src/es/chochox/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/es/chochox/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/chochox/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..faf6e38fe Binary files /dev/null and b/src/es/chochox/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/es/chochox/src/eu/kanade/tachiyomi/extension/es/chochox/Chochox.kt b/src/es/chochox/src/eu/kanade/tachiyomi/extension/es/chochox/Chochox.kt new file mode 100644 index 000000000..e335a2b7c --- /dev/null +++ b/src/es/chochox/src/eu/kanade/tachiyomi/extension/es/chochox/Chochox.kt @@ -0,0 +1,35 @@ +package eu.kanade.tachiyomi.extension.es.chochox + +import eu.kanade.tachiyomi.multisrc.vercomics.VerComics + +class Chochox : VerComics("ChoChoX", "https://chochox.com", "es") { + + override val urlSuffix = "porno" + override val genreSuffix = "tag" + override val useSuffixOnSearch = false + + override var genres = + arrayOf( + Pair("Ver todos", ""), + Pair("Anal", "anal-xxx-comics"), + Pair("Comics Porno 3D", "comics-3d"), + Pair("Culonas", "culonas-comicsporno-xxx"), + Pair("Dragon Ball", "dragon-ball-porno"), + Pair("Full Color", "full-color"), + Pair("Furry Hentai", "furry-hentai-comics"), + Pair("Futanari", "futanari-comics"), + Pair("Hinata XXX", "hinata-xxx"), + Pair("Lesbianas", "lesbianas"), + Pair("Mamadas", "mamadas-comics-porno"), + Pair("Milfs", "milfs-porno-comics"), + Pair("My Hero Academia XXX", "my-hero-academia-xxx"), + Pair("Naruto Hentai XXX", "naruto-hentai-xxx"), + Pair("Parodia Porno", "parodia-porno"), + Pair("Parodias Porno", "parodias-porno-comics-porno"), + Pair("Series TV Porno", "series-tv-xxx-comics-porno"), + Pair("Sonic", "sonic"), + Pair("Steven Universe", "steven-universe-xxx"), + Pair("Tetonas", "tetonas-comics"), + Pair("Vaginal", "vaginal-comics-porno"), + ) +} diff --git a/src/es/vcpvmp/build.gradle b/src/es/vcpvmp/build.gradle index 911b72855..df9b8e1a9 100644 --- a/src/es/vcpvmp/build.gradle +++ b/src/es/vcpvmp/build.gradle @@ -1,7 +1,8 @@ ext { extName = 'VCPVMP' extClass = '.VCPVMPFactory' - extVersionCode = 9 + themePkg = 'vercomics' + overrideVersionCode = 9 isNsfw = true } diff --git a/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMPFactory.kt b/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMPFactory.kt index b79db8770..b3e2f5db5 100644 --- a/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMPFactory.kt +++ b/src/es/vcpvmp/src/eu/kanade/tachiyomi/extension/es/vcpvmp/VCPVMPFactory.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.extension.es.vcpvmp +import eu.kanade.tachiyomi.multisrc.vercomics.VerComics import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory -import eu.kanade.tachiyomi.source.model.Filter class VCPVMPFactory : SourceFactory { override fun createSources(): List = listOf( @@ -11,68 +11,63 @@ class VCPVMPFactory : SourceFactory { ) } -class VCP : VCPVMP("VCP", "https://vercomicsporno.com") { +class VCP : VerComics("VCP", "https://vercomicsporno.com", "es") { override val urlSuffix = "comics-porno" override val genreSuffix = "etiquetas" override var genres = arrayOf( Pair("Ver todos", ""), - Pair("Anales", "anales"), - Pair("Anime", "anime"), - Pair("Aprobado por c1b3r3y3", "aprobado-por-c1b3r3y3"), - Pair("Comics Incesto", "incesto-xxx"), + Pair("Anal", "anal"), + Pair("Big Ass", "big-ass"), + Pair("Big Breasts", "big-breasts"), + Pair("Big Cock", "big-cock"), + Pair("Big Penis", "big-penis"), + Pair("Big Tits", "big-tits"), + Pair("Blowjob", "blowjob"), Pair("Culonas", "culonas"), - Pair("Furry", "furry-3"), - Pair("Futanari", "futanari-2"), - Pair("Lesbianas", "lesbianas"), - Pair("Madre Hijo", "madre-hijo"), + Pair("Cum", "cum"), + Pair("Dark Skin", "dark-skin"), + Pair("Furry", "furry"), + Pair("Hot Girls", "hot-girls"), + Pair("Incest", "incest"), Pair("Mamadas", "mamadas"), - Pair("Manga Hentai", "manga-hentai-3"), - Pair("Masturbaciones", "madre-hijo"), - Pair("Milfs", "milfs-xxx"), - Pair("Orgias", "orgias"), - Pair("Parodias Porno", "parodias-porno-xxx"), - Pair("Rubias", "rubias"), + Pair("Milf", "milf"), + Pair("Muscle", "muscle"), + Pair("Nakadashi", "nakadashi"), + Pair("Sole Female", "sole-female"), + Pair("Sole Male", "sole-male"), Pair("Tetonas", "tetonas"), - Pair("Trios", "trios"), - Pair("Videojuegos", "videojuegos-2"), - Pair("Yuri", "yuri-xxx"), ) } -class VMP : VCPVMP("VMP", "https://vermangasporno.com") { +class VMP : VerComics("VMP", "https://vermangasporno.com", "es") { override val urlSuffix = "xxx" - override val genreSuffix = "genero" + override val genreSuffix = "tag" override var genres = arrayOf( Pair("Ver todos", ""), Pair("Ahegao", "ahegao"), + Pair("Anal", "anal"), Pair("Big Ass", "big-ass"), Pair("Big Breasts", "big-breasts"), - Pair("Blowjob", "blowjob"), - Pair("Cheating", "cheating"), + Pair("Big Penis", "big-penis"), + Pair("BlowJob", "blowjob"), Pair("Creampie", "creampie"), Pair("Cum", "cum"), - Pair("Group", "group"), Pair("Hairy", "hairy"), - Pair("Kissing", "kissing"), + Pair("Incest", "incest"), + Pair("Manga Hentai", "manga-hentai"), Pair("Milf", "milf"), Pair("Mosaic Censorship", "mosaic-censorship"), Pair("Nakadashi", "nakadashi"), + Pair("Paizuri", "paizuri"), Pair("Schoolgirl Uniform", "schoolgirl-uniform"), Pair("Sin Censura", "sin-censura"), - Pair("Sole Female", "sole-female"), - Pair("Sole Male", "sole-male"), Pair("Squirting", "squirting"), - Pair("Stockings", "stockings"), + Pair("Student", "student"), Pair("Unusual Pupils", "unusual-pupils"), ) } - -open class UriPartFilter(displayName: String, private val vals: Array>) : - Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { - fun toUriPart() = vals[state].second -}