VCPVMP: Move to multisrc and add ChoChoX (#6356)

* move to multisrc and add chochox

* lint

* apply suggestions
This commit is contained in:
bapeey 2024-11-28 08:40:04 -05:00 committed by Draff
parent bcf51e8138
commit aa5804b858
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
11 changed files with 175 additions and 99 deletions

View File

@ -0,0 +1,5 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 1

View File

@ -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.network.GET
import eu.kanade.tachiyomi.source.model.Filter 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.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
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.toHttpUrl
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
import rx.Observable import rx.Observable
open class VCPVMP(override val name: String, override val baseUrl: String) : ParsedHttpSource() { abstract class VerComics(
override val name: String,
override val lang = "es" override val baseUrl: String,
override val lang: String,
) : ParsedHttpSource() {
override val supportsLatest: Boolean = false override val supportsLatest: Boolean = false
override fun headersBuilder(): Headers.Builder { protected open val urlSuffix = ""
return Headers.Builder() protected open val genreSuffix = ""
.add("Referer", "$baseUrl/") protected open val useSuffixOnSearch = true
}
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
override fun popularMangaRequest(page: Int) = GET("$baseUrl/$urlSuffix/page/$page", headers) 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 { override fun popularMangaFromElement(element: Element) = SManga.create().apply {
element.select("a.popimg").first()!!.let { element.select("a.popimg").first()!!.let {
setUrlWithoutDomain(it.attr("href")) setUrlWithoutDomain(it.attr("href"))
title = it.select("img").attr("alt") 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 { override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.select("div.tax_post").let { 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 chapterListSelector() = throw UnsupportedOperationException()
override fun chapterFromElement(element: Element) = 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<Page> = document.select(pageListSelector) override fun pageListParse(document: Document): List<Page> = document.select(pageListSelector)
.mapIndexed { i, img -> Page(i, "", img.attr("abs:data-src")) } .mapIndexed { i, img -> Page(i, imageUrl = img.imgAttr()) }
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()
protected open var genres = arrayOf(Pair("Ver todos", "")) 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) 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') protected open fun Element.imgAttr(): String? {
// from https://vercomicsporno.com/ 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<Pair<String, String>>) : 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<Pair<String, String>>) : UriPartFilter(
"Filtrar por género", "Filtrar por género",
genres, 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<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
} }

View File

@ -0,0 +1,9 @@
ext {
extName = 'ChoChoX'
extClass = '.Chochox'
themePkg = 'vercomics'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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"),
)
}

View File

@ -1,7 +1,8 @@
ext { ext {
extName = 'VCPVMP' extName = 'VCPVMP'
extClass = '.VCPVMPFactory' extClass = '.VCPVMPFactory'
extVersionCode = 9 themePkg = 'vercomics'
overrideVersionCode = 9
isNsfw = true isNsfw = true
} }

View File

@ -1,8 +1,8 @@
package eu.kanade.tachiyomi.extension.es.vcpvmp 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.Source
import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.Filter
class VCPVMPFactory : SourceFactory { class VCPVMPFactory : SourceFactory {
override fun createSources(): List<Source> = listOf( override fun createSources(): List<Source> = 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 urlSuffix = "comics-porno"
override val genreSuffix = "etiquetas" override val genreSuffix = "etiquetas"
override var genres = override var genres =
arrayOf( arrayOf(
Pair("Ver todos", ""), Pair("Ver todos", ""),
Pair("Anales", "anales"), Pair("Anal", "anal"),
Pair("Anime", "anime"), Pair("Big Ass", "big-ass"),
Pair("Aprobado por c1b3r3y3", "aprobado-por-c1b3r3y3"), Pair("Big Breasts", "big-breasts"),
Pair("Comics Incesto", "incesto-xxx"), Pair("Big Cock", "big-cock"),
Pair("Big Penis", "big-penis"),
Pair("Big Tits", "big-tits"),
Pair("Blowjob", "blowjob"),
Pair("Culonas", "culonas"), Pair("Culonas", "culonas"),
Pair("Furry", "furry-3"), Pair("Cum", "cum"),
Pair("Futanari", "futanari-2"), Pair("Dark Skin", "dark-skin"),
Pair("Lesbianas", "lesbianas"), Pair("Furry", "furry"),
Pair("Madre Hijo", "madre-hijo"), Pair("Hot Girls", "hot-girls"),
Pair("Incest", "incest"),
Pair("Mamadas", "mamadas"), Pair("Mamadas", "mamadas"),
Pair("Manga Hentai", "manga-hentai-3"), Pair("Milf", "milf"),
Pair("Masturbaciones", "madre-hijo"), Pair("Muscle", "muscle"),
Pair("Milfs", "milfs-xxx"), Pair("Nakadashi", "nakadashi"),
Pair("Orgias", "orgias"), Pair("Sole Female", "sole-female"),
Pair("Parodias Porno", "parodias-porno-xxx"), Pair("Sole Male", "sole-male"),
Pair("Rubias", "rubias"),
Pair("Tetonas", "tetonas"), 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 urlSuffix = "xxx"
override val genreSuffix = "genero" override val genreSuffix = "tag"
override var genres = override var genres =
arrayOf( arrayOf(
Pair("Ver todos", ""), Pair("Ver todos", ""),
Pair("Ahegao", "ahegao"), Pair("Ahegao", "ahegao"),
Pair("Anal", "anal"),
Pair("Big Ass", "big-ass"), Pair("Big Ass", "big-ass"),
Pair("Big Breasts", "big-breasts"), Pair("Big Breasts", "big-breasts"),
Pair("Blowjob", "blowjob"), Pair("Big Penis", "big-penis"),
Pair("Cheating", "cheating"), Pair("BlowJob", "blowjob"),
Pair("Creampie", "creampie"), Pair("Creampie", "creampie"),
Pair("Cum", "cum"), Pair("Cum", "cum"),
Pair("Group", "group"),
Pair("Hairy", "hairy"), Pair("Hairy", "hairy"),
Pair("Kissing", "kissing"), Pair("Incest", "incest"),
Pair("Manga Hentai", "manga-hentai"),
Pair("Milf", "milf"), Pair("Milf", "milf"),
Pair("Mosaic Censorship", "mosaic-censorship"), Pair("Mosaic Censorship", "mosaic-censorship"),
Pair("Nakadashi", "nakadashi"), Pair("Nakadashi", "nakadashi"),
Pair("Paizuri", "paizuri"),
Pair("Schoolgirl Uniform", "schoolgirl-uniform"), Pair("Schoolgirl Uniform", "schoolgirl-uniform"),
Pair("Sin Censura", "sin-censura"), Pair("Sin Censura", "sin-censura"),
Pair("Sole Female", "sole-female"),
Pair("Sole Male", "sole-male"),
Pair("Squirting", "squirting"), Pair("Squirting", "squirting"),
Pair("Stockings", "stockings"), Pair("Student", "student"),
Pair("Unusual Pupils", "unusual-pupils"), Pair("Unusual Pupils", "unusual-pupils"),
) )
} }
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}