forked from AlmightyHak/extensions-source
Merge branch 'Kohi-den:main' into main
This commit is contained in:
commit
930fd156e0
89 changed files with 736 additions and 86 deletions
lib
chillx-extractor/src/main/java/eu/kanade/tachiyomi/lib/chillxextractor
voe-extractor/src/main/java/eu/kanade/tachiyomi/lib/voeextractor
src
all
chineseanime
hikari
javguru
lmanime
supjav
ar
anime4up
animerco
arabseed
asia2tv
egydead
okanime
de
animebase
animeloads
animetoast
aniworld
cineclix
cinemathek
einfach
filmpalast
kinoking
kool
moflixstream
movie4k
serienstream
en
allanimechi
animegg
build.gradle
res
mipmap-hdpi
mipmap-mdpi
mipmap-xhdpi
mipmap-xxhdpi
mipmap-xxxhdpi
src/eu/kanade/tachiyomi/animeextension/en/animegg
animekhor
animenosub
asiaflix
asianload
dramacool
gogoanime
multimovies
tokuzilla
es
animefenix
animeflv
animeid
animelatinohd
animemovil
asialiveaction
cine24h
build.gradle
res
mipmap-hdpi
mipmap-mdpi
mipmap-xhdpi
mipmap-xxhdpi
mipmap-xxxhdpi
src/eu/kanade/tachiyomi/animeextension/es/cine24h
cinecalidad
cineplus123
cuevana
doramasflix
ennovelas
gnula
hackstore
hentaila
hentaitk
jkanime
lacartoons
legionanime
metroseries
mundodonghua
pelisforte
pelisplushd
tioanimeh
tiodonghua
veranimes
fr
hi/animesaga
id/otakudesu
it/toonitalia
pt
tr
|
@ -5,6 +5,8 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
|||
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES.decryptWithSalt
|
||||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
@ -27,6 +29,7 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
|
||||
// matches "[language]https://...,"
|
||||
private val REGEX_SUBS by lazy { Regex("""\[(.*?)\](.*?)"?\,""") }
|
||||
private const val KEY_SOURCE = "https://rowdy-avocado.github.io/multi-keys/"
|
||||
}
|
||||
|
||||
fun videoFromUrl(url: String, referer: String, prefix: String = "Chillx - "): List<Video> {
|
||||
|
@ -39,7 +42,7 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
|
||||
val master = REGEX_MASTER_JS.find(body)?.groupValues?.get(1) ?: return emptyList()
|
||||
val aesJson = json.decodeFromString<CryptoInfo>(master)
|
||||
val key = getKey(body)
|
||||
val key = fetchKey()
|
||||
val decryptedScript = decryptWithSalt(aesJson.ciphertext, aesJson.salt, key)
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\", "")
|
||||
|
@ -83,6 +86,11 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private fun fetchKey(): String {
|
||||
return client.newCall(GET(KEY_SOURCE)).execute().parseAs<KeysData>().keys.get(0)
|
||||
}
|
||||
|
||||
private fun getKey(body: String): String {
|
||||
val (encrypted, pass, offset, index) = REGEX_EVAL_KEY.find(body)!!.groupValues.drop(1)
|
||||
val decrypted = decryptScript(encrypted, pass, offset.toInt(), index.toInt())
|
||||
|
@ -114,4 +122,10 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
|
|||
val label: String = "",
|
||||
val file: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class KeysData(
|
||||
@SerialName("chillx")
|
||||
val keys: List<String>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ class VoeExtractor(private val client: OkHttpClient) {
|
|||
|
||||
private val base64Regex = Regex("'.*'")
|
||||
|
||||
private val scriptBase64Regex = "(let|var)\\s+\\w+\\s*=\\s*'(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)';".toRegex()
|
||||
|
||||
@Serializable
|
||||
data class VideoLinkDTO(val file: String)
|
||||
|
||||
|
@ -36,8 +38,9 @@ class VoeExtractor(private val client: OkHttpClient) {
|
|||
document = clientDdos.newCall(GET(originalUrl)).execute().asJsoup()
|
||||
}
|
||||
|
||||
val script = document.selectFirst("script:containsData(const sources), script:containsData(var sources), script:containsData(wc0)")
|
||||
?.data()
|
||||
val alternativeScript = document.select("script").find { scriptBase64Regex.containsMatchIn(it.data()) }?.data()
|
||||
val script = document.selectFirst("script:containsData(const sources), script:containsData(var sources), script:containsData(wc0)")?.data()
|
||||
?: alternativeScript
|
||||
?: return emptyList()
|
||||
val playlistUrl = when {
|
||||
// Layout 1
|
||||
|
@ -46,10 +49,11 @@ class VoeExtractor(private val client: OkHttpClient) {
|
|||
if (linkRegex.matches(link)) link else String(Base64.decode(link, Base64.DEFAULT))
|
||||
}
|
||||
// Layout 2
|
||||
script.contains("wc0") -> {
|
||||
script.contains("wc0") || alternativeScript != null -> {
|
||||
val base64 = base64Regex.find(script)!!.value
|
||||
val decoded = Base64.decode(base64, Base64.DEFAULT).let(::String)
|
||||
json.decodeFromString<VideoLinkDTO>(decoded).file
|
||||
|
||||
json.decodeFromString<VideoLinkDTO>(if (alternativeScript != null) decoded.reversed() else decoded).file
|
||||
}
|
||||
else -> return emptyList()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.ChineseAnime'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://www.chineseanime.vip'
|
||||
overrideVersionCode = 8
|
||||
overrideVersionCode = 9
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Hikari'
|
||||
extClass = '.Hikari'
|
||||
extVersionCode = 5
|
||||
extVersionCode = 6
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.animeextension.all.hikari
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
|
@ -233,24 +234,69 @@ class Hikari : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val html = response.parseAs<HtmlResponseDto>().toHtml(baseUrl)
|
||||
Log.d("Hikari", html.toString())
|
||||
|
||||
val headers = headersBuilder()
|
||||
.set("Referer", response.request.url.toString())
|
||||
.build()
|
||||
|
||||
val embedUrls = html.select(videoListSelector()).flatMap {
|
||||
val name = it.text()
|
||||
val onClick = it.selectFirst("a")!!.attr("onclick")
|
||||
val match = embedRegex.find(onClick)!!.groupValues
|
||||
val subEmbedUrls = html.select(".servers-sub div.item.server-item").flatMap { item ->
|
||||
val name = item.text().trim() + " (Sub)"
|
||||
val onClick = item.selectFirst("a")?.attr("onclick")
|
||||
if (onClick == null) {
|
||||
Log.e("Hikari", "onClick attribute is null for item: $item")
|
||||
return@flatMap emptyList<Pair<String, String>>()
|
||||
}
|
||||
|
||||
val match = embedRegex.find(onClick)?.groupValues
|
||||
if (match == null) {
|
||||
Log.e("Hikari", "No match found for onClick: $onClick")
|
||||
return@flatMap emptyList<Pair<String, String>>()
|
||||
}
|
||||
|
||||
val url = "$baseUrl/ajax/embed/${match[1]}/${match[2]}/${match[3]}"
|
||||
val iframeList = client.newCall(
|
||||
GET(url, headers),
|
||||
).execute().parseAs<List<String>>()
|
||||
|
||||
iframeList.map {
|
||||
Pair(Jsoup.parseBodyFragment(it).selectFirst("iframe")!!.attr("src"), name)
|
||||
}
|
||||
val iframeSrc = Jsoup.parseBodyFragment(it).selectFirst("iframe")?.attr("src")
|
||||
if (iframeSrc == null) {
|
||||
Log.e("Hikari", "iframe src is null for URL: $url")
|
||||
return@map Pair("", "")
|
||||
}
|
||||
Pair(iframeSrc, name)
|
||||
}.filter { it.first.isNotEmpty() }
|
||||
}
|
||||
val dubEmbedUrls = html.select(".servers-dub div.item.server-item").flatMap { item ->
|
||||
val name = item.text().trim() + " (Dub)"
|
||||
val onClick = item.selectFirst("a")?.attr("onclick")
|
||||
if (onClick == null) {
|
||||
Log.e("Hikari", "onClick attribute is null for item: $item")
|
||||
return@flatMap emptyList<Pair<String, String>>()
|
||||
}
|
||||
|
||||
val match = embedRegex.find(onClick)?.groupValues
|
||||
if (match == null) {
|
||||
Log.e("Hikari", "No match found for onClick: $onClick")
|
||||
return@flatMap emptyList<Pair<String, String>>()
|
||||
}
|
||||
|
||||
val url = "$baseUrl/ajax/embed/${match[1]}/${match[2]}/${match[3]}"
|
||||
val iframeList = client.newCall(
|
||||
GET(url, headers),
|
||||
).execute().parseAs<List<String>>()
|
||||
|
||||
iframeList.map {
|
||||
val iframeSrc = Jsoup.parseBodyFragment(it).selectFirst("iframe")?.attr("src")
|
||||
if (iframeSrc == null) {
|
||||
Log.e("Hikari", "iframe src is null for URL: $url")
|
||||
return@map Pair("", "")
|
||||
}
|
||||
Pair(iframeSrc, name)
|
||||
}.filter { it.first.isNotEmpty() }
|
||||
}
|
||||
val embedUrls = subEmbedUrls + dubEmbedUrls
|
||||
|
||||
return embedUrls.parallelCatchingFlatMapBlocking {
|
||||
getVideosFromEmbed(it.first, it.second)
|
||||
|
@ -258,7 +304,7 @@ class Hikari : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
}
|
||||
|
||||
private fun getVideosFromEmbed(embedUrl: String, name: String): List<Video> = when {
|
||||
name.contains("vidhide", true) -> vidHideExtractor.videosFromUrl(embedUrl)
|
||||
name.contains("vidhide", true) -> vidHideExtractor.videosFromUrl(embedUrl, videoNameGen = { s -> "$name - $s" })
|
||||
embedUrl.contains("filemoon", true) -> {
|
||||
filemoonExtractor.videosFromUrl(embedUrl, prefix = "$name - ", headers = headers)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Jav Guru'
|
||||
extClass = '.JavGuru'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.LMAnime'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://lmanime.com'
|
||||
overrideVersionCode = 6
|
||||
overrideVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'SupJav'
|
||||
extClass = '.SupJavFactory'
|
||||
extVersionCode = 9
|
||||
extVersionCode = 10
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime4up'
|
||||
extClass = '.Anime4Up'
|
||||
extVersionCode = 57
|
||||
extVersionCode = 58
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animerco'
|
||||
extClass = '.Animerco'
|
||||
extVersionCode = 37
|
||||
extVersionCode = 38
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Arab Seed'
|
||||
extClass = '.ArabSeed'
|
||||
extVersionCode = 13
|
||||
extVersionCode = 14
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'asia2tv'
|
||||
extClass = '.Asia2TV'
|
||||
extVersionCode = 18
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Egy Dead'
|
||||
extClass = '.EgyDead'
|
||||
extVersionCode = 12
|
||||
extVersionCode = 13
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Okanime'
|
||||
extClass = '.Okanime'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime-Base'
|
||||
extClass = '.AnimeBase'
|
||||
extVersionCode = 24
|
||||
extVersionCode = 25
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime-Loads'
|
||||
extClass = '.AnimeLoads'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeToast'
|
||||
extClass = '.AnimeToast'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AniWorld'
|
||||
extClass = '.AniWorld'
|
||||
extVersionCode = 23
|
||||
extVersionCode = 24
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'CineClix'
|
||||
extClass = '.CineClix'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Cinemathek'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://cinemathek.net'
|
||||
overrideVersionCode = 20
|
||||
overrideVersionCode = 21
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Einfach'
|
||||
extClass = '.Einfach'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'FilmPalast'
|
||||
extClass = '.FilmPalast'
|
||||
extVersionCode = 17
|
||||
extVersionCode = 18
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Kinoking'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://kinoking.cc'
|
||||
overrideVersionCode = 21
|
||||
overrideVersionCode = 22
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Kool'
|
||||
extClass = '.Kool'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Moflix-Stream'
|
||||
extClass = '.MoflixStream'
|
||||
extVersionCode = 8
|
||||
extVersionCode = 9
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Movie4k'
|
||||
extClass = '.Movie4k'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Serienstream'
|
||||
extClass = '.Serienstream'
|
||||
extVersionCode = 18
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AllAnimeChi'
|
||||
extClass = '.AllAnimeChi'
|
||||
extVersionCode = 7
|
||||
extVersionCode = 8
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
7
src/en/animegg/build.gradle
Normal file
7
src/en/animegg/build.gradle
Normal file
|
@ -0,0 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeGG'
|
||||
extClass = '.AnimeGG'
|
||||
extVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/en/animegg/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/en/animegg/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 893 B |
BIN
src/en/animegg/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/en/animegg/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 586 B |
BIN
src/en/animegg/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/en/animegg/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1 KiB |
BIN
src/en/animegg/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/en/animegg/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.5 KiB |
BIN
src/en/animegg/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/en/animegg/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.1 KiB |
|
@ -0,0 +1,279 @@
|
|||
package eu.kanade.tachiyomi.animeextension.en.animegg
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
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.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Locale
|
||||
|
||||
class AnimeGG : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "AnimeGG"
|
||||
|
||||
override val baseUrl = "https://www.animegg.org"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_LANGUAGE_KEY = "preferred_language"
|
||||
private const val PREF_LANGUAGE_DEFAULT = "[SUBBED]"
|
||||
private val LANGUAGE_LIST = arrayOf("[SUBBED]", "[DUBBED]", "[RAW]")
|
||||
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
|
||||
|
||||
private const val PREF_SERVER_KEY = "preferred_server"
|
||||
private const val PREF_SERVER_DEFAULT = "AnimeGG"
|
||||
private val SERVER_LIST = arrayOf("AnimeGG")
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
val animeDetails = SAnime.create().apply {
|
||||
title = document.selectFirst(".media-body h1")?.text()?.trim() ?: ""
|
||||
status = if (document.location().contains("/series/")) SAnime.UNKNOWN else SAnime.COMPLETED
|
||||
description = document.selectFirst(".ptext")?.text()
|
||||
genre = document.select(".tagscat a").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst(".media .media-object")?.attr("abs:src")
|
||||
document.select(".infoami span").map { it.text() }.map { textContent ->
|
||||
when {
|
||||
"Status" in textContent -> status = parseStatus(textContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return animeDetails
|
||||
}
|
||||
|
||||
private fun parseStatus(span: String): Int {
|
||||
val status = span.substringAfter("Status:").trim()
|
||||
return when {
|
||||
"Completed" in status -> SAnime.COMPLETED
|
||||
"Ongoing" in status -> SAnime.ONGOING
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/popular-series?sortBy=hits&sortDirection=DESC&ongoing&limit=50&start=0", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".fea")
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
title = element.selectFirst(".rightpop a")?.text()?.trim() ?: ""
|
||||
thumbnail_url = element.selectFirst("img")?.attr("abs:src")
|
||||
setUrlWithoutDomain(element.select(".rightpop a").attr("abs:href"))
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, false)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/popular-series?sortBy=createdAt&sortDirection=DESC&ongoing&limit=50&start=0", headers)
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/search/?q=$query", headers)
|
||||
genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}/page/$page", headers)
|
||||
else -> popularAnimeRequest(page)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".mse")
|
||||
val animeList = elements.map { element ->
|
||||
SAnime.create().apply {
|
||||
title = element.selectFirst(".first h2")?.text()?.trim() ?: ""
|
||||
thumbnail_url = element.selectFirst("img")?.attr("abs:src")
|
||||
setUrlWithoutDomain(element.attr("abs:href"))
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, false)
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return document.select(".newmanga li div").mapIndexed { idx, episode ->
|
||||
val episodeNumber = episode.selectFirst(".anm_det_pop strong")?.getEpNumber() ?: (idx + 1F)
|
||||
val title = episode.select(".anititle").text()
|
||||
SEpisode.create().apply {
|
||||
episode_number = episodeNumber
|
||||
name = when {
|
||||
episodeNumber.formatEp() in title -> episode.select(".anititle").text()
|
||||
else -> "Episode ${episodeNumber.formatEp()} - ${episode.select(".anititle").text()}"
|
||||
}
|
||||
scanlator = episode.select(".btn-xs").joinToString { it.text() }
|
||||
setUrlWithoutDomain(episode.select(".anm_det_pop").attr("abs:href"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
return document.select("iframe").parallelCatchingFlatMapBlocking {
|
||||
val mode = when (it.closest(".tab-pane")?.attr("id")) {
|
||||
"subbed-Animegg" -> "[SUBBED]"
|
||||
"dubbed-Animegg" -> "[DUBBED]"
|
||||
"raw-Animegg" -> "[RAW]"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
val link = it.attr("abs:src")
|
||||
val embedPlayer = client.newCall(GET(link)).execute().asJsoup()
|
||||
val scriptData = embedPlayer.selectFirst("script:containsData(var videoSources =)")?.data() ?: return@parallelCatchingFlatMapBlocking emptyList()
|
||||
val host = link.toHttpUrl().host
|
||||
val videoHeaders = headers.newBuilder().add("referer", "https://$host").build()
|
||||
val jsonString = fixJsonString(scriptData.substringAfter("var videoSources = ").substringBefore(";"))
|
||||
json.decodeFromString<Array<GgVideo>>(jsonString).map { video ->
|
||||
val videoUrl = "https://$host${video.file}"
|
||||
Video(videoUrl, "$mode AnimeGG:${video.label}", videoUrl, headers = videoHeaders)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixJsonString(jsonString: String): String {
|
||||
return jsonString.replace(Regex("""(\w+):"""), """"$1":""")
|
||||
.replace(Regex("""(:\s)([^{\[}\]":\s,]+)"""), """$1"$2"""")
|
||||
.replace(Regex("""(:\s)("[^"]*")"""), """$1$2""")
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
|
||||
val lang = preferences.getString(PREF_LANGUAGE_KEY, PREF_LANGUAGE_DEFAULT)!!
|
||||
return this.sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(lang) },
|
||||
{ it.quality.contains(server, true) },
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
|
||||
GenreFilter(),
|
||||
)
|
||||
|
||||
private class GenreFilter : UriPartFilter(
|
||||
"Genre",
|
||||
arrayOf(
|
||||
Pair("<Seleccionar>", ""),
|
||||
Pair("Películas", "peliculas"),
|
||||
Pair("Series", "series"),
|
||||
),
|
||||
)
|
||||
|
||||
private fun Element?.getEpNumber(): Float? {
|
||||
val input = this?.text() ?: return null
|
||||
val regex = Regex("""(\d+(\.\d+)?)(?:-\d+(\.\d+)?)?$""")
|
||||
val matchResult = regex.find(input)
|
||||
return matchResult?.groupValues?.get(1)?.toFloatOrNull()
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun Float.formatEp(): String {
|
||||
return if (this % 1 == 0.0f) {
|
||||
String.format(Locale.US, "%.0f", this)
|
||||
} else {
|
||||
String.format(Locale.US, "%.1f", this)
|
||||
}
|
||||
}
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GgVideo(
|
||||
val file: String,
|
||||
val label: String,
|
||||
)
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_LANGUAGE_KEY
|
||||
title = "Preferred language"
|
||||
entries = LANGUAGE_LIST
|
||||
entryValues = LANGUAGE_LIST
|
||||
setDefaultValue(PREF_LANGUAGE_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
entries = QUALITY_LIST
|
||||
entryValues = QUALITY_LIST
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.AnimeKhor'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://animekhor.xyz'
|
||||
overrideVersionCode = 3
|
||||
overrideVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Animenosub'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://animenosub.com'
|
||||
overrideVersionCode = 5
|
||||
overrideVersionCode = 6
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AsiaFlix'
|
||||
extClass = '.AsiaFlix'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AsianLoad'
|
||||
extClass = '.AsianLoad'
|
||||
extVersionCode = 39
|
||||
extVersionCode = 40
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'DramaCool'
|
||||
extClass = '.DramaCool'
|
||||
extVersionCode = 48
|
||||
extVersionCode = 49
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Gogoanime'
|
||||
extClass = '.GogoAnime'
|
||||
extVersionCode = 83
|
||||
extVersionCode = 84
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Multimovies'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://multimovies.art'
|
||||
overrideVersionCode = 17
|
||||
overrideVersionCode = 18
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Tokuzilla'
|
||||
extClass = '.Tokuzilla'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animefenix'
|
||||
extClass = '.Animefenix'
|
||||
extVersionCode = 38
|
||||
extVersionCode = 39
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeFLV'
|
||||
extClass = '.AnimeFlv'
|
||||
extVersionCode = 55
|
||||
extVersionCode = 56
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -8,4 +8,5 @@ apply from: "$rootDir/common.gradle"
|
|||
|
||||
dependencies {
|
||||
implementation(project(':lib:streamtape-extractor'))
|
||||
implementation(project(':lib:streamwish-extractor'))
|
||||
}
|
||||
|
|
|
@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
|
|||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
|
@ -117,15 +117,22 @@ class AnimeID : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
|
||||
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
|
||||
|
||||
// ============================ Video Links =============================
|
||||
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
|
||||
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
val videoList = mutableListOf<Video>()
|
||||
document.select("#partes div.container li.subtab div.parte").forEach { script ->
|
||||
val jsonString = script.attr("data")
|
||||
val jsonUnescape = unescapeJava(jsonString)!!.replace("\\", "")
|
||||
val url = jsonUnescape.substringAfter("src=\"").substringBefore("\"").replace("\\\\", "\\")
|
||||
if (url.contains("streamtape")) {
|
||||
StreamTapeExtractor(client).videoFromUrl(url)?.let { videoList.add(it) }
|
||||
val url = fetchUrls(jsonUnescape).firstOrNull()?.replace("\\\\", "\\") ?: ""
|
||||
if (url.contains("streamtape") || url.contains("tape") || url.contains("stp")) {
|
||||
streamtapeExtractor.videosFromUrl(url).also(videoList::addAll)
|
||||
}
|
||||
if (url.contains("wish") || url.contains("fviplions") || url.contains("obeywish")) {
|
||||
streamwishExtractor.videosFromUrl(url, videoNameGen = { "StreamWish:$it" }).also(videoList::addAll)
|
||||
}
|
||||
}
|
||||
return videoList
|
||||
|
@ -147,6 +154,12 @@ class AnimeID : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
return processed
|
||||
}
|
||||
|
||||
private fun fetchUrls(text: String?): List<String> {
|
||||
if (text.isNullOrEmpty()) return listOf()
|
||||
val linkRegex = "(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:\\/~+#-]*[\\w@?^=%&\\/~+#-])".toRegex()
|
||||
return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList()
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
val anime = SAnime.create()
|
||||
anime.thumbnail_url = document.selectFirst("#anime figure img.cover")!!.attr("abs:src")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeLatinoHD'
|
||||
extClass = '.AnimeLatinoHD'
|
||||
extVersionCode = 32
|
||||
extVersionCode = 33
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimeMovil'
|
||||
extClass = '.AnimeMovil'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AsiaLiveAction'
|
||||
extClass = '.AsiaLiveAction'
|
||||
extVersionCode = 30
|
||||
extVersionCode = 31
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
14
src/es/cine24h/build.gradle
Normal file
14
src/es/cine24h/build.gradle
Normal file
|
@ -0,0 +1,14 @@
|
|||
ext {
|
||||
extName = 'Cine24h'
|
||||
extClass = '.Cine24h'
|
||||
extVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:fastream-extractor'))
|
||||
implementation(project(':lib:dood-extractor'))
|
||||
implementation(project(':lib:filemoon-extractor'))
|
||||
implementation(project(':lib:voe-extractor'))
|
||||
}
|
BIN
src/es/cine24h/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/es/cine24h/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 5.2 KiB |
BIN
src/es/cine24h/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/es/cine24h/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.5 KiB |
BIN
src/es/cine24h/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/es/cine24h/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 7.7 KiB |
BIN
src/es/cine24h/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/es/cine24h/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 16 KiB |
BIN
src/es/cine24h/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/es/cine24h/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 26 KiB |
|
@ -0,0 +1,272 @@
|
|||
package eu.kanade.tachiyomi.animeextension.es.cine24h
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
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.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
||||
import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
|
||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Calendar
|
||||
|
||||
open class Cine24h : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "Cine24h"
|
||||
|
||||
override val baseUrl = "https://cine24h.online"
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
|
||||
|
||||
private const val PREF_SERVER_KEY = "preferred_server"
|
||||
private const val PREF_SERVER_DEFAULT = "Voe"
|
||||
private val SERVER_LIST = arrayOf("Voe", "Fastream", "Filemoon", "Doodstream")
|
||||
}
|
||||
|
||||
override fun animeDetailsParse(response: Response): SAnime {
|
||||
val document = response.asJsoup()
|
||||
val animeDetails = SAnime.create().apply {
|
||||
description = document.selectFirst(".Single .Description")?.text()
|
||||
genre = document.select(".Single .InfoList a").joinToString { it.text() }
|
||||
thumbnail_url = document.selectFirst(".Single .Image img")?.getImageUrl()?.replace("/w185/", "/w500/")
|
||||
if (document.location().contains("/movie/")) {
|
||||
status = SAnime.COMPLETED
|
||||
} else {
|
||||
val statusText = document.select(".InfoList .AAIco-adjust").map { it.text() }
|
||||
.find { "En Producción:" in it }?.substringAfter("En Producción:")?.trim()
|
||||
status = when (statusText) { "Sí" -> SAnime.ONGOING else -> SAnime.COMPLETED }
|
||||
}
|
||||
}
|
||||
return animeDetails
|
||||
}
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/peliculas-mas-vistas/page/$page", headers)
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val elements = document.select(".TPost a:not([target])")
|
||||
val nextPage = document.select(".wp-pagenavi .next").any()
|
||||
val animeList = elements.map { element ->
|
||||
val lang = element.select(".Langu .sprite").attr("class").lowercase()
|
||||
val titleItem = element.selectFirst(".Title")?.text()?.trim() ?: ""
|
||||
val link = element.attr("abs:href")
|
||||
|
||||
val prefix = when {
|
||||
lang.contains("sub") || titleItem.lowercase().contains("(sub)") || link.contains("-sub") -> "\uD83C\uDDFA\uD83C\uDDF8 "
|
||||
lang.contains("lat") || titleItem.lowercase().contains("(lat)") || link.contains("-lat") -> "\uD83C\uDDF2\uD83C\uDDFD "
|
||||
lang.contains("esp") || titleItem.lowercase().contains("(es)") || link.contains("-es") -> "\uD83C\uDDEA\uD83C\uDDF8 "
|
||||
else -> ""
|
||||
}
|
||||
SAnime.create().apply {
|
||||
title = prefix + titleItem
|
||||
thumbnail_url = element.selectFirst(".Image img")?.getImageUrl()?.replace("/w185/", "/w300/")
|
||||
setUrlWithoutDomain(link)
|
||||
}
|
||||
}
|
||||
return AnimesPage(animeList, nextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
val currentYear = Calendar.getInstance().get(Calendar.YEAR)
|
||||
return GET("$baseUrl/page/$page/?s=trfilter&trfilter=1&years%5B0%5D=$currentYear#038;trfilter=1&years%5B0%5D=$currentYear", headers)
|
||||
}
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/page/$page/?s=$query", headers)
|
||||
genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}/page/$page", headers)
|
||||
else -> popularAnimeRequest(page)
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
return if (document.location().contains("/movie/")) {
|
||||
listOf(
|
||||
SEpisode.create().apply {
|
||||
episode_number = 1f
|
||||
name = "PELÍCULA"
|
||||
scanlator = document.select(".AAIco-date_range").text().trim()
|
||||
setUrlWithoutDomain(document.location())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
var episodeCounter = 1F
|
||||
document.select(".AABox").flatMap { season ->
|
||||
val noSeason = season.select(".Title").text().substringAfter("Temporada").trim()
|
||||
season.select(".AABox .TPTblCn tr").map { ep ->
|
||||
SEpisode.create().apply {
|
||||
episode_number = episodeCounter++
|
||||
name = "T$noSeason - E${ep.select(".Num").text().trim()} - ${ep.select(".MvTbTtl a").text().trim()}"
|
||||
scanlator = ep.select(".MvTbTtl span").text()
|
||||
setUrlWithoutDomain(ep.select(".MvTbTtl a").attr("abs:href"))
|
||||
}
|
||||
}
|
||||
}.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
return document.select(".TPlayerTb").map { org.jsoup.nodes.Entities.unescape(it.html()) }.parallelCatchingFlatMapBlocking {
|
||||
val urlEmbed = it.substringAfter("src=\"").substringBefore("\"").replace("&", "&")
|
||||
val link = client.newCall(GET(urlEmbed)).execute().asJsoup().selectFirst("iframe")?.attr("src") ?: ""
|
||||
serverVideoResolver(link)
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------Video extractors------------------------------------*/
|
||||
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
|
||||
private val doodExtractor by lazy { DoodExtractor(client) }
|
||||
private val voeExtractor by lazy { VoeExtractor(client) }
|
||||
|
||||
private fun serverVideoResolver(url: String): List<Video> {
|
||||
val embedUrl = url.lowercase()
|
||||
return when {
|
||||
embedUrl.contains("fastream") -> {
|
||||
val link = if (url.contains("emb.html")) "https://fastream.to/embed-${url.split("/").last()}.html" else url
|
||||
FastreamExtractor(client, headers).videosFromUrl(link)
|
||||
}
|
||||
embedUrl.contains("filemoon") || embedUrl.contains("moonplayer") -> filemoonExtractor.videosFromUrl(url, prefix = "Filemoon:")
|
||||
embedUrl.contains("voe") -> voeExtractor.videosFromUrl(url)
|
||||
embedUrl.contains("dood") -> doodExtractor.videosFromUrl(url)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
|
||||
return this.sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(server, true) },
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
AnimeFilter.Header("La busqueda por texto ignora el filtro"),
|
||||
GenreFilter(),
|
||||
)
|
||||
|
||||
private class GenreFilter : UriPartFilter(
|
||||
"Género",
|
||||
arrayOf(
|
||||
Pair("<Seleccionar>", ""),
|
||||
Pair("Películas", "peliculas"),
|
||||
Pair("Series", "series"),
|
||||
Pair("Acción", "category/accion"),
|
||||
Pair("Animación", "category/animacion"),
|
||||
Pair("Anime", "category/anime"),
|
||||
Pair("Aventura", "category/aventura"),
|
||||
Pair("Bélica", "category/belica"),
|
||||
Pair("Ciencia ficción", "category/ciencia-ficcion"),
|
||||
Pair("Comedia", "category/comedia"),
|
||||
Pair("Crimen", "category/crimen"),
|
||||
Pair("Documental", "category/documental"),
|
||||
Pair("Drama", "category/drama"),
|
||||
Pair("Familia", "category/familia"),
|
||||
Pair("Fantasía", "category/fantasia"),
|
||||
Pair("Gerra", "category/gerra"),
|
||||
Pair("Historia", "category/historia"),
|
||||
Pair("Misterio", "category/misterio"),
|
||||
Pair("Música", "category/musica"),
|
||||
Pair("Navidad", "category/navidad"),
|
||||
Pair("Película de TV", "category/pelicula-de-tv"),
|
||||
Pair("Romance", "category/romance"),
|
||||
Pair("Suspenso", "category/suspense"),
|
||||
Pair("Terror", "category/terror"),
|
||||
Pair("Western", "category/western"),
|
||||
),
|
||||
)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
protected open fun org.jsoup.nodes.Element.getImageUrl(): String? {
|
||||
return when {
|
||||
isValidUrl("data-src") -> attr("abs:data-src")
|
||||
isValidUrl("data-lazy-src") -> attr("abs:data-lazy-src")
|
||||
isValidUrl("srcset") -> attr("abs:srcset").substringBefore(" ")
|
||||
isValidUrl("src") -> attr("abs:src")
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun org.jsoup.nodes.Element.isValidUrl(attrName: String): Boolean {
|
||||
if (!hasAttr(attrName)) return false
|
||||
return !attr(attrName).contains("data:image/")
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_SERVER_KEY
|
||||
title = "Preferred server"
|
||||
entries = SERVER_LIST
|
||||
entryValues = SERVER_LIST
|
||||
setDefaultValue(PREF_SERVER_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
entries = QUALITY_LIST
|
||||
entryValues = QUALITY_LIST
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'CineCalidad'
|
||||
extClass = '.CineCalidad'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Cineplus123'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://cineplus123.org'
|
||||
overrideVersionCode = 0
|
||||
overrideVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Cuevana'
|
||||
extClass = '.CuevanaFactory'
|
||||
extVersionCode = 32
|
||||
extVersionCode = 33
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Doramasflix'
|
||||
extClass = '.Doramasflix'
|
||||
extVersionCode = 21
|
||||
extVersionCode = 22
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'EnNovelas'
|
||||
extClass = '.EnNovelas'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Gnula'
|
||||
extClass = '.Gnula'
|
||||
extVersionCode = 18
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Hackstore'
|
||||
extClass = '.Hackstore'
|
||||
extVersionCode = 11
|
||||
extVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'HentaiLA'
|
||||
extClass = '.Hentaila'
|
||||
extVersionCode = 24
|
||||
extVersionCode = 25
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'HentaiTk'
|
||||
extClass = '.Hentaitk'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Jkanime'
|
||||
extClass = '.Jkanime'
|
||||
extVersionCode = 23
|
||||
extVersionCode = 24
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'LACartoons'
|
||||
extClass = '.Lacartoons'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'LegionAnime'
|
||||
extClass = '.LegionAnime'
|
||||
extVersionCode = 31
|
||||
extVersionCode = 32
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'MetroSeries'
|
||||
extClass = '.MetroSeries'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'MundoDonghua'
|
||||
extClass = '.MundoDonghua'
|
||||
extVersionCode = 22
|
||||
extVersionCode = 23
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'PelisForte'
|
||||
extClass = '.PelisForte'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Pelisplushd'
|
||||
extClass = '.PelisplushdFactory'
|
||||
extVersionCode = 56
|
||||
extVersionCode = 57
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'TioanimeH'
|
||||
extClass = '.TioanimeHFactory'
|
||||
extVersionCode = 17
|
||||
extVersionCode = 18
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Tiodonghua'
|
||||
themePkg = 'animestream'
|
||||
baseUrl = 'https://anime.tiodonghua.com'
|
||||
overrideVersionCode = 3
|
||||
overrideVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'VerAnimes'
|
||||
extClass = '.VerAnimes'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AniSama'
|
||||
extClass = '.AniSama'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'EmpireStreaming'
|
||||
extClass = '.EmpireStreaming'
|
||||
extVersionCode = 16
|
||||
extVersionCode = 17
|
||||
}
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'OtakuFR'
|
||||
extClass = '.OtakuFR'
|
||||
extVersionCode = 12
|
||||
extVersionCode = 13
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Wiflix'
|
||||
themePkg = 'datalifeengine'
|
||||
baseUrl = 'https://wiflix.voto'
|
||||
overrideVersionCode = 6
|
||||
overrideVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.AnimeSAGA'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://www.animesaga.in'
|
||||
overrideVersionCode = 9
|
||||
overrideVersionCode = 10
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'OtakuDesu'
|
||||
extClass = '.OtakuDesu'
|
||||
extVersionCode = 25
|
||||
extVersionCode = 26
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Toonitalia'
|
||||
extClass = '.Toonitalia'
|
||||
extVersionCode = 21
|
||||
extVersionCode = 22
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Bakashi'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://bakashi.tv'
|
||||
overrideVersionCode = 11
|
||||
overrideVersionCode = 12
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -3,7 +3,7 @@ ext {
|
|||
extClass = '.Pobreflix'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://pobreflix1.art'
|
||||
overrideVersionCode = 9
|
||||
overrideVersionCode = 10
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animeler'
|
||||
extClass = '.Animeler'
|
||||
extVersionCode = 13
|
||||
extVersionCode = 14
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anizm'
|
||||
extClass = '.Anizm'
|
||||
extVersionCode = 21
|
||||
extVersionCode = 22
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'TR Anime Izle'
|
||||
extClass = '.TRAnimeIzle'
|
||||
extVersionCode = 17
|
||||
extVersionCode = 18
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Türk Anime TV'
|
||||
extClass = '.TurkAnime'
|
||||
extVersionCode = 24
|
||||
extVersionCode = 25
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue