fix(src/es): Pelisplushd and Hackstore fixes (#97)
* VoeExtractor, PelisplusHD & Hackstore fixes * VoeExtractor, PelisplusHD & Hackstore fixes
This commit is contained in:
parent
1bbc02413e
commit
42065686b3
11 changed files with 134 additions and 280 deletions
|
@ -0,0 +1,73 @@
|
||||||
|
package eu.kanade.tachiyomi.lib.voeextractor
|
||||||
|
|
||||||
|
import android.webkit.CookieManager
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
class DdosGuardInterceptor(private val client: OkHttpClient) : Interceptor {
|
||||||
|
|
||||||
|
private val cookieManager by lazy { CookieManager.getInstance() }
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val originalRequest = chain.request()
|
||||||
|
val response = chain.proceed(originalRequest)
|
||||||
|
|
||||||
|
// Check if DDos-GUARD is on
|
||||||
|
if (response.code !in ERROR_CODES || response.header("Server") !in SERVER_CHECK) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
response.close()
|
||||||
|
val cookies = cookieManager.getCookie(originalRequest.url.toString())
|
||||||
|
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
||||||
|
cookies.split(";").mapNotNull { Cookie.parse(originalRequest.url, it) }
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val ddg2Cookie = oldCookie.firstOrNull { it.name == "__ddg2_" }
|
||||||
|
if (!ddg2Cookie?.value.isNullOrEmpty()) {
|
||||||
|
return chain.proceed(originalRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newCookie = getNewCookie(originalRequest.url) ?: return chain.proceed(originalRequest)
|
||||||
|
val newCookieHeader = buildString {
|
||||||
|
(oldCookie + newCookie).forEachIndexed { index, cookie ->
|
||||||
|
if (index > 0) append("; ")
|
||||||
|
append(cookie.name).append('=').append(cookie.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.proceed(originalRequest.newBuilder().addHeader("cookie", newCookieHeader).build())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNewCookie(url: HttpUrl): Cookie? {
|
||||||
|
val cookies = cookieManager.getCookie(url.toString())
|
||||||
|
val oldCookie = if (cookies != null && cookies.isNotEmpty()) {
|
||||||
|
cookies.split(";").mapNotNull { Cookie.parse(url, it) }
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
val ddg2Cookie = oldCookie.firstOrNull { it.name == "__ddg2_" }
|
||||||
|
if (!ddg2Cookie?.value.isNullOrEmpty()) {
|
||||||
|
return ddg2Cookie
|
||||||
|
}
|
||||||
|
val wellKnown = client.newCall(GET("https://check.ddos-guard.net/check.js"))
|
||||||
|
.execute().body.string()
|
||||||
|
.substringAfter("'", "")
|
||||||
|
.substringBefore("'", "")
|
||||||
|
val checkUrl = "${url.scheme}://${url.host + wellKnown}"
|
||||||
|
return client.newCall(GET(checkUrl)).execute().header("set-cookie")?.let {
|
||||||
|
Cookie.parse(url, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val ERROR_CODES = listOf(403)
|
||||||
|
private val SERVER_CHECK = listOf("ddos-guard")
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,9 @@ class VoeExtractor(private val client: OkHttpClient) {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
private val playlistUtils by lazy { PlaylistUtils(client) }
|
private val clientDdos by lazy { client.newBuilder().addInterceptor(DdosGuardInterceptor(client)).build() }
|
||||||
|
|
||||||
|
private val playlistUtils by lazy { PlaylistUtils(clientDdos) }
|
||||||
|
|
||||||
private val linkRegex = "(http|https)://([\\w_-]+(?:\\.[\\w_-]+)+)([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])".toRegex()
|
private val linkRegex = "(http|https)://([\\w_-]+(?:\\.[\\w_-]+)+)([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])".toRegex()
|
||||||
|
|
||||||
|
@ -24,7 +26,16 @@ class VoeExtractor(private val client: OkHttpClient) {
|
||||||
data class VideoLinkDTO(val file: String)
|
data class VideoLinkDTO(val file: String)
|
||||||
|
|
||||||
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
|
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
|
||||||
val document = client.newCall(GET(url)).execute().asJsoup()
|
var document = clientDdos.newCall(GET(url)).execute().asJsoup()
|
||||||
|
|
||||||
|
if (document.selectFirst("script")?.data()?.contains("if (typeof localStorage !== 'undefined')") == true) {
|
||||||
|
val originalUrl = document.selectFirst("script")?.data()
|
||||||
|
?.substringAfter("window.location.href = '")
|
||||||
|
?.substringBefore("';") ?: return emptyList()
|
||||||
|
|
||||||
|
document = clientDdos.newCall(GET(originalUrl)).execute().asJsoup()
|
||||||
|
}
|
||||||
|
|
||||||
val script = document.selectFirst("script:containsData(const sources), script:containsData(var sources), script:containsData(wc0)")
|
val script = document.selectFirst("script:containsData(const sources), script:containsData(var sources), script:containsData(wc0)")
|
||||||
?.data()
|
?.data()
|
||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
|
@ -43,7 +54,7 @@ class VoeExtractor(private val client: OkHttpClient) {
|
||||||
else -> return emptyList()
|
else -> return emptyList()
|
||||||
}
|
}
|
||||||
return playlistUtils.extractFromHls(playlistUrl,
|
return playlistUtils.extractFromHls(playlistUrl,
|
||||||
videoNameGen = { quality -> "${prefix}Voe: $quality" }
|
videoNameGen = { quality -> "${prefix}Voe:$quality" }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Hackstore'
|
extName = 'Hackstore'
|
||||||
extClass = '.Hackstore'
|
extClass = '.Hackstore'
|
||||||
extVersionCode = 10
|
extVersionCode = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -132,8 +132,8 @@ class Hackstore : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||||
|
|
||||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val ismovie = response.request.url.toString().contains("/peliculas/")
|
val isMovie = response.request.url.toString().contains("/peliculas/")
|
||||||
return if (ismovie) {
|
return if (isMovie) {
|
||||||
listOf(
|
listOf(
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
name = "PELÍCULA"
|
name = "PELÍCULA"
|
||||||
|
@ -142,14 +142,14 @@ class Hackstore : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
document.select(".movie-thumbnail").map { thumbnail ->
|
document.select(".movie-thumbnail").mapIndexed { idx, thumbnail ->
|
||||||
val episodeLink = thumbnail.select("a").attr("href")
|
val episodeLink = thumbnail.select("a").attr("href")
|
||||||
val seasonMatch = Regex("-(\\d+)x(\\d+)/$").find(episodeLink)
|
val seasonMatch = Regex("-(\\d+)x(\\d+)/$").find(episodeLink)
|
||||||
val seasonNumber = seasonMatch?.groups?.get(1)?.value?.toInt() ?: 0
|
val seasonNumber = seasonMatch?.groups?.get(1)?.value?.toInt() ?: 0
|
||||||
val episodeNumber = seasonMatch?.groups?.get(2)?.value?.toInt() ?: 0
|
val episodeNumber = seasonMatch?.groups?.get(2)?.value?.toInt() ?: 0
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
name = "T$seasonNumber - E$episodeNumber"
|
name = "T$seasonNumber - E$episodeNumber"
|
||||||
episode_number = episodeNumber.toFloat()
|
episode_number = idx + 1f
|
||||||
setUrlWithoutDomain(episodeLink)
|
setUrlWithoutDomain(episodeLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ class Hackstore : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||||
server.contains("streamtape") || server.contains("stp") || server.contains("stape") -> {
|
server.contains("streamtape") || server.contains("stp") || server.contains("stape") -> {
|
||||||
listOf(streamTapeExtractor.videoFromUrl(url, quality = "$prefix StreamTape")!!)
|
listOf(streamTapeExtractor.videoFromUrl(url, quality = "$prefix StreamTape")!!)
|
||||||
}
|
}
|
||||||
server.contains("voe") -> voeExtractor.videosFromUrl(url, prefix)
|
server.contains("voe") -> voeExtractor.videosFromUrl(url, "$prefix ")
|
||||||
server.contains("filemoon") -> filemoonExtractor.videosFromUrl(url, prefix = "$prefix Filemoon:")
|
server.contains("filemoon") -> filemoonExtractor.videosFromUrl(url, prefix = "$prefix Filemoon:")
|
||||||
server.contains("wishembed") || server.contains("streamwish") || server.contains("strwish") || server.contains("wish") -> {
|
server.contains("wishembed") || server.contains("streamwish") || server.contains("strwish") || server.contains("wish") -> {
|
||||||
streamWishExtractor.videosFromUrl(url, videoNameGen = { "$prefix StreamWish:$it" })
|
streamWishExtractor.videosFromUrl(url, videoNameGen = { "$prefix StreamWish:$it" })
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Pelisplushd'
|
extName = 'Pelisplushd'
|
||||||
extClass = '.PelisplushdFactory'
|
extClass = '.PelisplushdFactory'
|
||||||
extVersionCode = 53
|
extVersionCode = 54
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -22,5 +22,6 @@ dependencies {
|
||||||
implementation(project(':lib:burstcloud-extractor'))
|
implementation(project(':lib:burstcloud-extractor'))
|
||||||
implementation(project(':lib:fastream-extractor'))
|
implementation(project(':lib:fastream-extractor'))
|
||||||
implementation(project(':lib:upstream-extractor'))
|
implementation(project(':lib:upstream-extractor'))
|
||||||
|
implementation(project(':lib:streamhidevid-extractor'))
|
||||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||||
}
|
}
|
|
@ -5,7 +5,6 @@ import android.content.SharedPreferences
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.animeextension.es.pelisplushd.extractors.StreamHideExtractor
|
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
|
@ -19,6 +18,7 @@ import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
|
||||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||||
|
@ -110,32 +110,37 @@ open class Pelisplushd(override val name: String, override val baseUrl: String)
|
||||||
val apiUrl = data?.substringAfter("video[1] = '", "")?.substringBefore("';", "")
|
val apiUrl = data?.substringAfter("video[1] = '", "")?.substringBefore("';", "")
|
||||||
val alternativeServers = document.select("ul.TbVideoNv.nav.nav-tabs li:not(:first-child)")
|
val alternativeServers = document.select("ul.TbVideoNv.nav.nav-tabs li:not(:first-child)")
|
||||||
if (!apiUrl.isNullOrEmpty()) {
|
if (!apiUrl.isNullOrEmpty()) {
|
||||||
val apiResponse = client.newCall(GET(apiUrl)).execute().asJsoup()
|
val apiResponse = client.newCall(GET(apiUrl)).execute()
|
||||||
val regIsUrl = "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex()
|
val docResponse = apiResponse.asJsoup()
|
||||||
val encryptedList = apiResponse.select("#PlayerDisplay div[class*=\"OptionsLangDisp\"] div[class*=\"ODDIV\"] div[class*=\"OD\"] li")
|
if (apiResponse.isSuccessful) {
|
||||||
encryptedList.parallelCatchingFlatMapBlocking {
|
val regIsUrl = "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)".toRegex()
|
||||||
val url = it.attr("onclick")
|
val encryptedList = docResponse.select("#PlayerDisplay div[class*=\"OptionsLangDisp\"] div[class*=\"ODDIV\"] div[class*=\"OD\"] li")
|
||||||
.substringAfter("go_to_player('")
|
encryptedList.flatMap {
|
||||||
.substringAfter("go_to_playerVast('")
|
runCatching {
|
||||||
.substringBefore("?cover_url=")
|
val url = it.attr("onclick")
|
||||||
.substringBefore("')")
|
.substringAfter("go_to_player('")
|
||||||
.substringBefore("',")
|
.substringAfter("go_to_playerVast('")
|
||||||
.substringBefore("?poster")
|
.substringBefore("?cover_url=")
|
||||||
.substringBefore("?c_poster=")
|
.substringBefore("')")
|
||||||
.substringBefore("?thumb=")
|
.substringBefore("',")
|
||||||
.substringBefore("#poster=")
|
.substringBefore("?poster")
|
||||||
|
.substringBefore("?c_poster=")
|
||||||
|
.substringBefore("?thumb=")
|
||||||
|
.substringBefore("#poster=")
|
||||||
|
|
||||||
val realUrl = if (!regIsUrl.containsMatchIn(url)) {
|
val realUrl = if (!regIsUrl.containsMatchIn(url)) {
|
||||||
String(Base64.decode(url, Base64.DEFAULT))
|
String(Base64.decode(url, Base64.DEFAULT))
|
||||||
} else if (url.contains("?data=")) {
|
} else if (url.contains("?data=")) {
|
||||||
val apiPageSoup = client.newCall(GET(url)).execute().asJsoup()
|
val apiPageSoup = client.newCall(GET(url)).execute().asJsoup()
|
||||||
apiPageSoup.selectFirst("iframe")?.attr("src") ?: ""
|
apiPageSoup.selectFirst("iframe")?.attr("src") ?: ""
|
||||||
} else {
|
} else {
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
serverVideoResolver(realUrl)
|
serverVideoResolver(realUrl)
|
||||||
}.also(videoList::addAll)
|
}.getOrNull() ?: emptyList()
|
||||||
|
}.also(videoList::addAll)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifier for old series
|
// verifier for old series
|
||||||
|
@ -210,7 +215,8 @@ open class Pelisplushd(override val name: String, override val baseUrl: String)
|
||||||
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "Fastream:")
|
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "Fastream:")
|
||||||
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url)
|
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url)
|
||||||
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "StreamTape")!!)
|
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "StreamTape")!!)
|
||||||
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") || embedUrl.contains("streamvid") -> StreamHideExtractor(client).videosFromUrl(url, "StreamHide")
|
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") ||
|
||||||
|
embedUrl.contains("streamvid") || embedUrl.contains("vidhide") -> StreamHideVidExtractor(client).videosFromUrl(url)
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}.getOrNull() ?: emptyList()
|
}.getOrNull() ?: emptyList()
|
||||||
|
|
|
@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
|
||||||
|
|
||||||
class PelisplushdFactory : AnimeSourceFactory {
|
class PelisplushdFactory : AnimeSourceFactory {
|
||||||
override fun createSources(): List<AnimeSource> = listOf(
|
override fun createSources(): List<AnimeSource> = listOf(
|
||||||
Pelisplushd("PelisPlusHD", "https://ww1.pelisplushd.nu"),
|
Pelisplushd("PelisPlusHD", "https://pelisplushd.bz"),
|
||||||
Pelisplusto("PelisPlusTo", "https://ww3.pelisplus.to"),
|
Pelisplusto("PelisPlusTo", "https://ww3.pelisplus.to"),
|
||||||
Pelisplusph("PelisPlusPh", "https://www.pelisplushd.ph"),
|
Pelisplusph("PelisPlusPh", "https://www.pelisplushd.ph"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.animeextension.es.pelisplushd
|
||||||
|
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.animeextension.es.pelisplushd.extractors.StreamHideExtractor
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
@ -14,6 +13,7 @@ import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
|
||||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||||
|
@ -174,7 +174,8 @@ class Pelisplusph(override val name: String, override val baseUrl: String) : Pel
|
||||||
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "$prefix Fastream:")
|
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "$prefix Fastream:")
|
||||||
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url, prefix = prefix)
|
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url, prefix = prefix)
|
||||||
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "$prefix StreamTape")!!)
|
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "$prefix StreamTape")!!)
|
||||||
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") || embedUrl.contains("streamvid") -> StreamHideExtractor(client).videosFromUrl(url, "$prefix StreamHide")
|
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") ||
|
||||||
|
embedUrl.contains("streamvid") || embedUrl.contains("vidhide") -> StreamHideVidExtractor(client).videosFromUrl(url, "$prefix ")
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}.getOrNull() ?: emptyList()
|
}.getOrNull() ?: emptyList()
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.animeextension.es.pelisplushd
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.animeextension.es.pelisplushd.extractors.StreamHideExtractor
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
@ -15,6 +14,7 @@ import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor
|
||||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||||
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
|
||||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.streamhidevidextractor.StreamHideVidExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||||
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
|
||||||
|
@ -215,7 +215,8 @@ class Pelisplusto(override val name: String, override val baseUrl: String) : Pel
|
||||||
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "Fastream:")
|
embedUrl.contains("fastream") -> FastreamExtractor(client, headers).videosFromUrl(url, prefix = "Fastream:")
|
||||||
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url)
|
embedUrl.contains("upstream") -> UpstreamExtractor(client).videosFromUrl(url)
|
||||||
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "StreamTape")!!)
|
embedUrl.contains("streamtape") || embedUrl.contains("stp") || embedUrl.contains("stape") -> listOf(StreamTapeExtractor(client).videoFromUrl(url, quality = "StreamTape")!!)
|
||||||
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") || embedUrl.contains("streamvid") -> StreamHideExtractor(client).videosFromUrl(url, "StreamHide")
|
embedUrl.contains("ahvsh") || embedUrl.contains("streamhide") || embedUrl.contains("guccihide") ||
|
||||||
|
embedUrl.contains("streamvid") || embedUrl.contains("vidhide") -> StreamHideVidExtractor(client).videosFromUrl(url)
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}.getOrNull() ?: emptyList()
|
}.getOrNull() ?: emptyList()
|
||||||
|
|
|
@ -1,205 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.animeextension.es.pelisplushd.extractors
|
|
||||||
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
// https://github.com/cylonu87/JsUnpacker
|
|
||||||
class JsUnpacker(packedJS: String?) {
|
|
||||||
private var packedJS: String? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects whether the javascript is P.A.C.K.E.R. coded.
|
|
||||||
*
|
|
||||||
* @return true if it's P.A.C.K.E.R. coded.
|
|
||||||
*/
|
|
||||||
fun detect(): Boolean {
|
|
||||||
val js = packedJS!!.replace(" ", "")
|
|
||||||
val p = Pattern.compile("eval\\(function\\(p,a,c,k,e,[rd]")
|
|
||||||
val m = p.matcher(js)
|
|
||||||
return m.find()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpack the javascript
|
|
||||||
*
|
|
||||||
* @return the javascript unpacked or null.
|
|
||||||
*/
|
|
||||||
fun unpack(): String? {
|
|
||||||
val js = packedJS
|
|
||||||
runCatching {
|
|
||||||
var p =
|
|
||||||
Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL)
|
|
||||||
var m = p.matcher(js)
|
|
||||||
if (m.find() && m.groupCount() == 4) {
|
|
||||||
val payload = m.group(1).replace("\\'", "'")
|
|
||||||
val radixStr = m.group(2)
|
|
||||||
val countStr = m.group(3)
|
|
||||||
val symtab = m.group(4).split("\\|".toRegex()).toTypedArray()
|
|
||||||
val radix = radixStr.toIntOrNull() ?: 36
|
|
||||||
val count = countStr.toIntOrNull() ?: 0
|
|
||||||
if (symtab.size != count) {
|
|
||||||
throw Exception("Unknown p.a.c.k.e.r. encoding")
|
|
||||||
}
|
|
||||||
val unbase = Unbase(radix)
|
|
||||||
p = Pattern.compile("\\b\\w+\\b")
|
|
||||||
m = p.matcher(payload)
|
|
||||||
val decoded = StringBuilder(payload)
|
|
||||||
var replaceOffset = 0
|
|
||||||
while (m.find()) {
|
|
||||||
val word = m.group(0)
|
|
||||||
val x = unbase.unbase(word)
|
|
||||||
var value: String? = null
|
|
||||||
if (x < symtab.size && x >= 0) {
|
|
||||||
value = symtab[x]
|
|
||||||
}
|
|
||||||
if (value != null && value.isNotEmpty()) {
|
|
||||||
decoded.replace(m.start() + replaceOffset, m.end() + replaceOffset, value)
|
|
||||||
replaceOffset += value.length - word.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decoded.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Unbase(private val radix: Int) {
|
|
||||||
private val alphabet62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
private val alphabet95 =
|
|
||||||
" !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
|
||||||
private var alphabet: String? = null
|
|
||||||
private var dictionary: HashMap<String, Int>? = null
|
|
||||||
fun unbase(str: String): Int {
|
|
||||||
var ret = 0
|
|
||||||
if (alphabet == null) {
|
|
||||||
ret = str.toInt(radix)
|
|
||||||
} else {
|
|
||||||
val tmp = StringBuilder(str).reverse().toString()
|
|
||||||
for (i in tmp.indices) {
|
|
||||||
ret += (radix.toDouble().pow(i.toDouble()) * dictionary!![tmp.substring(i, i + 1)]!!).toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (radix > 36) {
|
|
||||||
when {
|
|
||||||
radix < 62 -> {
|
|
||||||
alphabet = alphabet62.substring(0, radix)
|
|
||||||
}
|
|
||||||
radix in 63..94 -> {
|
|
||||||
alphabet = alphabet95.substring(0, radix)
|
|
||||||
}
|
|
||||||
radix == 62 -> {
|
|
||||||
alphabet = alphabet62
|
|
||||||
}
|
|
||||||
radix == 95 -> {
|
|
||||||
alphabet = alphabet95
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dictionary = HashMap(95)
|
|
||||||
for (i in 0 until alphabet!!.length) {
|
|
||||||
dictionary!![alphabet!!.substring(i, i + 1)] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param packedJS javascript P.A.C.K.E.R. coded.
|
|
||||||
*/
|
|
||||||
init {
|
|
||||||
this.packedJS = packedJS
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val C =
|
|
||||||
listOf(
|
|
||||||
0x63,
|
|
||||||
0x6f,
|
|
||||||
0x6d,
|
|
||||||
0x2e,
|
|
||||||
0x67,
|
|
||||||
0x6f,
|
|
||||||
0x6f,
|
|
||||||
0x67,
|
|
||||||
0x6c,
|
|
||||||
0x65,
|
|
||||||
0x2e,
|
|
||||||
0x61,
|
|
||||||
0x6e,
|
|
||||||
0x64,
|
|
||||||
0x72,
|
|
||||||
0x6f,
|
|
||||||
0x69,
|
|
||||||
0x64,
|
|
||||||
0x2e,
|
|
||||||
0x67,
|
|
||||||
0x6d,
|
|
||||||
0x73,
|
|
||||||
0x2e,
|
|
||||||
0x61,
|
|
||||||
0x64,
|
|
||||||
0x73,
|
|
||||||
0x2e,
|
|
||||||
0x4d,
|
|
||||||
0x6f,
|
|
||||||
0x62,
|
|
||||||
0x69,
|
|
||||||
0x6c,
|
|
||||||
0x65,
|
|
||||||
0x41,
|
|
||||||
0x64,
|
|
||||||
0x73,
|
|
||||||
)
|
|
||||||
private val Z =
|
|
||||||
listOf(
|
|
||||||
0x63,
|
|
||||||
0x6f,
|
|
||||||
0x6d,
|
|
||||||
0x2e,
|
|
||||||
0x66,
|
|
||||||
0x61,
|
|
||||||
0x63,
|
|
||||||
0x65,
|
|
||||||
0x62,
|
|
||||||
0x6f,
|
|
||||||
0x6f,
|
|
||||||
0x6b,
|
|
||||||
0x2e,
|
|
||||||
0x61,
|
|
||||||
0x64,
|
|
||||||
0x73,
|
|
||||||
0x2e,
|
|
||||||
0x41,
|
|
||||||
0x64,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun String.load(): String? {
|
|
||||||
return try {
|
|
||||||
var load = this
|
|
||||||
|
|
||||||
for (q in C.indices) {
|
|
||||||
if (C[q % 4] > 270) {
|
|
||||||
load += C[q % 3]
|
|
||||||
} else {
|
|
||||||
load += C[q].toChar()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Class.forName(load.substring(load.length - C.size, load.length)).name
|
|
||||||
} catch (_: Exception) {
|
|
||||||
try {
|
|
||||||
var f = C[2].toChar().toString()
|
|
||||||
for (w in Z.indices) {
|
|
||||||
f += Z[w].toChar()
|
|
||||||
}
|
|
||||||
return Class.forName(f.substring(0b001, f.length)).name
|
|
||||||
} catch (_: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.animeextension.es.pelisplushd.extractors
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.Track
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.Video
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
|
|
||||||
class StreamHideExtractor(private val client: OkHttpClient) {
|
|
||||||
// from nineanime / ask4movie FilemoonExtractor
|
|
||||||
private val subtitleRegex = Regex("""#EXT-X-MEDIA:TYPE=SUBTITLES.*?NAME="(.*?)".*?URI="(.*?)"""")
|
|
||||||
|
|
||||||
fun videosFromUrl(url: String, name: String): List<Video> {
|
|
||||||
val page = client.newCall(GET(url)).execute().body.string()
|
|
||||||
val unpacked = JsUnpacker(page).unpack() ?: return emptyList()
|
|
||||||
val playlistUrl = unpacked.substringAfter("sources:")
|
|
||||||
.substringAfter("file:\"") // StreamHide
|
|
||||||
.substringAfter("src:\"") // StreamVid
|
|
||||||
.substringBefore('"')
|
|
||||||
|
|
||||||
val playlistData = client.newCall(GET(playlistUrl)).execute().body.string()
|
|
||||||
|
|
||||||
val subs = subtitleRegex.findAll(playlistData).map {
|
|
||||||
val urlPart = it.groupValues[2]
|
|
||||||
val subUrl = when {
|
|
||||||
!urlPart.startsWith("https:") ->
|
|
||||||
playlistUrl.substringBeforeLast("/") + "/$urlPart"
|
|
||||||
else -> urlPart
|
|
||||||
}
|
|
||||||
Track(subUrl, it.groupValues[1])
|
|
||||||
}.toList()
|
|
||||||
|
|
||||||
// The playlist usually only have one video quality.
|
|
||||||
return listOf(Video(playlistUrl, name, playlistUrl, subtitleTracks = subs))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue