Initial commit
This commit is contained in:
commit
98ed7e8839
2263 changed files with 108711 additions and 0 deletions
14
src/fr/voircartoon/build.gradle
Normal file
14
src/fr/voircartoon/build.gradle
Normal file
|
@ -0,0 +1,14 @@
|
|||
ext {
|
||||
extName = 'VoirCartoon'
|
||||
extClass = '.VoirCartoon'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://voircartoon.com'
|
||||
overrideVersionCode = 5
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":lib:playlist-utils"))
|
||||
}
|
BIN
src/fr/voircartoon/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/fr/voircartoon/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
BIN
src/fr/voircartoon/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/fr/voircartoon/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
src/fr/voircartoon/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/fr/voircartoon/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
BIN
src/fr/voircartoon/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/fr/voircartoon/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
src/fr/voircartoon/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/fr/voircartoon/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,138 @@
|
|||
package eu.kanade.tachiyomi.animeextension.fr.voircartoon
|
||||
|
||||
import eu.kanade.tachiyomi.animeextension.fr.voircartoon.extractors.ComedyShowExtractor
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class VoirCartoon : DooPlay(
|
||||
"fr",
|
||||
"VoirCartoon",
|
||||
"https://voircartoon.com",
|
||||
) {
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/tendance/page/$page/", headers)
|
||||
|
||||
override fun popularAnimeSelector() = latestUpdatesSelector()
|
||||
|
||||
override fun popularAnimeNextPageSelector() = "div.pagination a.arrow_pag > i#nextpagination"
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override val supportsLatest = false
|
||||
|
||||
// =============================== Search ===============================
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
return when {
|
||||
query.isBlank() -> {
|
||||
val params = VoirCartoonFilters.getSearchParameters(filters)
|
||||
|
||||
val httpUrl = "$baseUrl/filter/page/$page/".toHttpUrl().newBuilder()
|
||||
.addIfNotBlank("type", params.type)
|
||||
.addIfNotBlank("genre", params.genre)
|
||||
.addIfNotBlank("dtyear", params.year)
|
||||
.addIfNotBlank("status", params.status)
|
||||
.addIfNotBlank("post_tag", params.age)
|
||||
.build()
|
||||
|
||||
GET(httpUrl.toString(), headers)
|
||||
}
|
||||
else -> GET("$baseUrl/page/$page/?s=$query", headers)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
|
||||
|
||||
// ============================== Filters ===============================
|
||||
override val fetchGenres = false
|
||||
|
||||
override fun getFilterList() = VoirCartoonFilters.FILTER_LIST
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
override fun animeDetailsParse(document: Document) =
|
||||
super.animeDetailsParse(document).apply {
|
||||
val statusText = document.selectFirst("div.mvic-info p:contains(Status:) > a[rel]")
|
||||
?.text()
|
||||
.orEmpty()
|
||||
status = parseStatus(statusText)
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String): Int {
|
||||
return when (status) {
|
||||
"Ongoing" -> SAnime.ONGOING
|
||||
"Completed" -> SAnime.COMPLETED
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val doc = response.asJsoup()
|
||||
val episodeList = doc.select(episodeListSelector())
|
||||
return if (episodeList.size < 1) {
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(doc.location())
|
||||
episode_number = 1F
|
||||
name = episodeMovieText
|
||||
}.let(::listOf)
|
||||
} else {
|
||||
episodeList.map(::episodeFromElement).reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
val epNum = element.selectFirst("div.numerando")!!.text()
|
||||
.trim()
|
||||
.let(episodeNumberRegex::find)
|
||||
?.groupValues
|
||||
?.last() ?: "0"
|
||||
val href = element.selectFirst("a[href]")!!
|
||||
val episodeName = href.ownText()
|
||||
episode_number = epNum.toFloatOrNull() ?: 0F
|
||||
name = "Saison" + episodeName.substringAfterLast("Saison")
|
||||
setUrlWithoutDomain(href.attr("href"))
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
private val comedyshowExtractor by lazy { ComedyShowExtractor(client) }
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val doc = response.asJsoup()
|
||||
val id = doc.selectFirst("input[name=idpost]")?.attr("value") ?: return emptyList()
|
||||
|
||||
val players = doc.select("nav.player select > option").toList()
|
||||
.filterNot { it.text().contains("Hydrax") } // Fuck hydrax
|
||||
.map { it.attr("value") }
|
||||
|
||||
val urls = players.map {
|
||||
client.newCall(GET("$baseUrl/ajax-get-link-stream/?server=$it&filmId=$id", headers)).execute()
|
||||
.body.string()
|
||||
}.distinct()
|
||||
|
||||
return urls.flatMap { url ->
|
||||
runCatching {
|
||||
when {
|
||||
url.contains("comedy") -> comedyshowExtractor.videosFromUrl(url)
|
||||
else -> emptyList()
|
||||
}
|
||||
}.onFailure { it.printStackTrace() }.getOrElse { emptyList() }
|
||||
}
|
||||
}
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String): HttpUrl.Builder {
|
||||
if (value.isNotBlank()) {
|
||||
addQueryParameter(query, value)
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package eu.kanade.tachiyomi.animeextension.fr.voircartoon
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
|
||||
object VoirCartoonFilters {
|
||||
open class QueryPartFilter(
|
||||
displayName: String,
|
||||
val vals: Array<Pair<String, String>>,
|
||||
) : AnimeFilter.Select<String>(
|
||||
displayName,
|
||||
vals.map { it.first }.toTypedArray(),
|
||||
) {
|
||||
fun toQueryPart() = vals[state].second
|
||||
}
|
||||
|
||||
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
|
||||
return (first { it is R } as QueryPartFilter).toQueryPart()
|
||||
}
|
||||
|
||||
internal class TypeFilter : QueryPartFilter("Type", VoirCartoonFiltersData.TYPES)
|
||||
internal class GenreFilter : QueryPartFilter("Genre", VoirCartoonFiltersData.GENRES)
|
||||
internal class YearFilter : QueryPartFilter("Year", VoirCartoonFiltersData.YEARS)
|
||||
internal class StatusFilter : QueryPartFilter("Status", VoirCartoonFiltersData.STATUS)
|
||||
internal class AgeFilter : QueryPartFilter("Age", VoirCartoonFiltersData.AGES)
|
||||
|
||||
val FILTER_LIST get() = AnimeFilterList(
|
||||
AnimeFilter.Header("NOTE: Filters are going to be ignored if using search text!"),
|
||||
TypeFilter(),
|
||||
GenreFilter(),
|
||||
YearFilter(),
|
||||
StatusFilter(),
|
||||
AgeFilter(),
|
||||
)
|
||||
|
||||
data class FilterSearchParams(
|
||||
val type: String = "",
|
||||
val genre: String = "",
|
||||
val year: String = "",
|
||||
val status: String = "",
|
||||
val age: String = "",
|
||||
)
|
||||
|
||||
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
||||
if (filters.isEmpty()) return FilterSearchParams()
|
||||
|
||||
return FilterSearchParams(
|
||||
filters.asQueryPart<TypeFilter>(),
|
||||
filters.asQueryPart<GenreFilter>(),
|
||||
filters.asQueryPart<YearFilter>(),
|
||||
filters.asQueryPart<StatusFilter>(),
|
||||
filters.asQueryPart<AgeFilter>(),
|
||||
)
|
||||
}
|
||||
|
||||
private object VoirCartoonFiltersData {
|
||||
private val ANY = Pair("Select", "")
|
||||
|
||||
val TYPES = arrayOf(
|
||||
ANY,
|
||||
Pair("Tv shows", "tvshows"),
|
||||
Pair("Movies", "movies"),
|
||||
)
|
||||
|
||||
val GENRES = arrayOf(
|
||||
ANY,
|
||||
Pair("Action & Adventure", "action-adventure"),
|
||||
Pair("Action", "action"),
|
||||
Pair("Animation", "animation"),
|
||||
Pair("Aventure", "aventure"),
|
||||
Pair("Comédie", "comedie"),
|
||||
Pair("Crime", "crime"),
|
||||
Pair("Documentaire", "documentaire"),
|
||||
Pair("Drame", "drame"),
|
||||
Pair("Familial", "familial"),
|
||||
Pair("Fantastique", "fantastique"),
|
||||
Pair("Guerre", "guerre"),
|
||||
Pair("Histoire", "histoire"),
|
||||
Pair("Horreur", "horreur"),
|
||||
Pair("Kids", "kids"),
|
||||
Pair("Musique", "musique"),
|
||||
Pair("Mystère", "mystere"),
|
||||
Pair("Romance", "romance"),
|
||||
Pair("Science-Fiction & Fantastique", "science-fiction-fantastique"),
|
||||
Pair("Science-Fiction", "science-fiction"),
|
||||
Pair("Sport", "sport"),
|
||||
Pair("Thriller", "thriller"),
|
||||
Pair("Téléfilm", "telefilm"),
|
||||
Pair("War & Politics", "war-politics"),
|
||||
Pair("Western", "western"),
|
||||
)
|
||||
|
||||
val YEARS = arrayOf(
|
||||
ANY,
|
||||
Pair("2024", "2024"),
|
||||
Pair("2023", "2023"),
|
||||
Pair("2022", "2022"),
|
||||
Pair("2021", "2021"),
|
||||
Pair("2020", "2020"),
|
||||
Pair("2019", "2019"),
|
||||
Pair("2018", "2018"),
|
||||
Pair("2017", "2017"),
|
||||
Pair("2016", "2016"),
|
||||
Pair("2015", "2015"),
|
||||
Pair("2014", "2014"),
|
||||
Pair("2013", "2013"),
|
||||
Pair("2012", "2012"),
|
||||
Pair("2011", "2011"),
|
||||
Pair("2010", "2010"),
|
||||
Pair("2009", "2009"),
|
||||
Pair("2008", "2008"),
|
||||
Pair("2007", "2007"),
|
||||
Pair("2006", "2006"),
|
||||
Pair("2005", "2005"),
|
||||
Pair("2004", "2004"),
|
||||
Pair("2003", "2003"),
|
||||
Pair("2002", "2002"),
|
||||
Pair("2001", "2001"),
|
||||
Pair("2000", "2000"),
|
||||
Pair("1999", "1999"),
|
||||
Pair("1998", "1998"),
|
||||
Pair("1997", "1997"),
|
||||
Pair("1996", "1996"),
|
||||
Pair("1995", "1995"),
|
||||
Pair("1994", "1994"),
|
||||
Pair("1993", "1993"),
|
||||
Pair("1992", "1992"),
|
||||
Pair("1991", "1991"),
|
||||
Pair("1990", "1990"),
|
||||
Pair("1989", "1989"),
|
||||
Pair("1988", "1988"),
|
||||
Pair("1987", "1987"),
|
||||
Pair("1986", "1986"),
|
||||
Pair("1985", "1985"),
|
||||
Pair("1984", "1984"),
|
||||
Pair("1983", "1983"),
|
||||
Pair("1982", "1982"),
|
||||
Pair("1981", "1981"),
|
||||
Pair("1980", "1980"),
|
||||
Pair("1979", "1979"),
|
||||
Pair("1978", "1978"),
|
||||
Pair("1977", "1977"),
|
||||
Pair("1976", "1976"),
|
||||
Pair("1975", "1975"),
|
||||
Pair("1974", "1974"),
|
||||
Pair("1973", "1973"),
|
||||
Pair("1972", "1972"),
|
||||
Pair("1971", "1971"),
|
||||
Pair("1970", "1970"),
|
||||
Pair("1969", "1969"),
|
||||
Pair("1968", "1968"),
|
||||
Pair("1967", "1967"),
|
||||
Pair("1965", "1965"),
|
||||
Pair("1964", "1964"),
|
||||
Pair("1963", "1963"),
|
||||
Pair("1962", "1962"),
|
||||
Pair("1961", "1961"),
|
||||
Pair("1960", "1960"),
|
||||
Pair("1959", "1959"),
|
||||
Pair("1958", "1958"),
|
||||
Pair("1957", "1957"),
|
||||
Pair("1956", "1956"),
|
||||
Pair("1955", "1955"),
|
||||
Pair("1953", "1953"),
|
||||
Pair("1951", "1951"),
|
||||
Pair("1950", "1950"),
|
||||
Pair("1949", "1949"),
|
||||
Pair("1948", "1948"),
|
||||
Pair("1947", "1947"),
|
||||
Pair("1946", "1946"),
|
||||
Pair("1944", "1944"),
|
||||
Pair("1942", "1942"),
|
||||
Pair("1941", "1941"),
|
||||
Pair("1940", "1940"),
|
||||
Pair("1939", "1939"),
|
||||
Pair("1937", "1937"),
|
||||
Pair("1930", "1930"),
|
||||
)
|
||||
|
||||
val STATUS = arrayOf(
|
||||
ANY,
|
||||
Pair("Ongoing", "ongoing"),
|
||||
Pair("Completed", "completed"),
|
||||
)
|
||||
|
||||
val AGES = arrayOf(
|
||||
ANY,
|
||||
Pair("14 (14+)", "14"),
|
||||
Pair("G (Tous ages)", "g"),
|
||||
Pair("MA (18+)", "ma"),
|
||||
Pair("PG (10+)", "pg"),
|
||||
Pair("PG-13 (13+)", "pg-13"),
|
||||
Pair("U", "u"),
|
||||
Pair("Y (2 à 6)", "y"),
|
||||
Pair("Y7 (7+)", "y7"),
|
||||
Pair("Y7-FV (9+)", "y7-fv"),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package eu.kanade.tachiyomi.animeextension.fr.voircartoon.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
// Based on EPlayerExtractor (pt/Pobreflix)
|
||||
class ComedyShowExtractor(private val client: OkHttpClient) {
|
||||
private val headers by lazy {
|
||||
Headers.headersOf(
|
||||
"X-Requested-With",
|
||||
"XMLHttpRequest",
|
||||
"Referer",
|
||||
COMEDY_SHOW_HOST,
|
||||
"Origin",
|
||||
COMEDY_SHOW_HOST,
|
||||
)
|
||||
}
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
val id = url.substringAfterLast("/")
|
||||
|
||||
val postUrl = "$COMEDY_SHOW_HOST/player/index.php?data=$id&do=getVideo"
|
||||
val body = FormBody.Builder()
|
||||
.add("hash", id)
|
||||
.add("r", "")
|
||||
.build()
|
||||
|
||||
val masterUrl = client.newCall(POST(postUrl, headers, body = body)).execute()
|
||||
.body.string()
|
||||
.substringAfter("videoSource\":\"")
|
||||
.substringBefore('"')
|
||||
.replace("\\", "")
|
||||
|
||||
return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "ComedyShow - $it" })
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val COMEDY_SHOW_HOST = "https://comedyshow.to"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue