mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
parent
7d49cd44a0
commit
11b4998d1d
9 changed files with 137 additions and 141 deletions
|
@ -1,7 +1,7 @@
|
||||||
import org.jetbrains.kotlin.konan.properties.Properties
|
import org.jetbrains.kotlin.konan.properties.Properties
|
||||||
|
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 217
|
version = 218
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|
|
@ -1275,7 +1275,7 @@ object SoraExtractor : SoraStream() {
|
||||||
val selector =
|
val selector =
|
||||||
if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)"
|
if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)"
|
||||||
val server = app.get(
|
val server = app.get(
|
||||||
href ?: return@apmap, interceptor = wpredisInterceptor
|
href ?: return@apmap, interceptor = wpRedisInterceptor
|
||||||
).document.selectFirst("div.entry-content > $selector")
|
).document.selectFirst("div.entry-content > $selector")
|
||||||
?.attr("href") ?: return@apmap
|
?.attr("href") ?: return@apmap
|
||||||
|
|
||||||
|
@ -1717,13 +1717,13 @@ object SoraExtractor : SoraStream() {
|
||||||
"$url&apikey=whXgvN4kVyoubGwqXpw26Oy3PVryl8dm",
|
"$url&apikey=whXgvN4kVyoubGwqXpw26Oy3PVryl8dm",
|
||||||
referer = "https://watcha.movie/"
|
referer = "https://watcha.movie/"
|
||||||
).text
|
).text
|
||||||
val link = Regex("\"file\":\"(http.*?)\"").find(res)?.groupValues?.getOrNull(1) ?: return
|
val link = Regex("\"file\":\"(http.*?)\"").find(res)?.groupValues?.getOrNull(1)
|
||||||
|
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
"RStream",
|
"RStream",
|
||||||
"RStream",
|
"RStream",
|
||||||
link,
|
link ?: return,
|
||||||
"$rStreamAPI/",
|
"$rStreamAPI/",
|
||||||
Qualities.P1080.value,
|
Qualities.P1080.value,
|
||||||
INFER_TYPE
|
INFER_TYPE
|
||||||
|
@ -2053,7 +2053,7 @@ object SoraExtractor : SoraStream() {
|
||||||
"$dahmerMoviesAPI/tvs/${title?.replace(":", " -")}/Season $season/"
|
"$dahmerMoviesAPI/tvs/${title?.replace(":", " -")}/Season $season/"
|
||||||
}
|
}
|
||||||
|
|
||||||
val request = app.get(url, timeout = 120L)
|
val request = app.get(url, interceptor = TimeOutInterceptor())
|
||||||
if (!request.isSuccessful) return
|
if (!request.isSuccessful) return
|
||||||
val paths = request.document.select("a").map {
|
val paths = request.document.select("a").map {
|
||||||
it.text() to it.attr("href")
|
it.text() to it.attr("href")
|
||||||
|
@ -2382,12 +2382,18 @@ object SoraExtractor : SoraStream() {
|
||||||
callback: (ExtractorLink) -> Unit,
|
callback: (ExtractorLink) -> Unit,
|
||||||
referer: String = "https://bflix.gs/"
|
referer: String = "https://bflix.gs/"
|
||||||
) {
|
) {
|
||||||
|
suspend fun String.isSuccess() : Boolean {
|
||||||
|
return app.get(this, referer = referer).isSuccessful
|
||||||
|
}
|
||||||
val slug = getEpisodeSlug(season, episode)
|
val slug = getEpisodeSlug(season, episode)
|
||||||
var url =
|
var url = if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4"
|
||||||
if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4"
|
if (!url.isSuccess()) {
|
||||||
if (!app.get(url, referer = referer).isSuccessful) {
|
url = if (season == null) {
|
||||||
url =
|
val temp = "$nowTvAPI/$imdbId.mp4"
|
||||||
if (season == null) "$nowTvAPI/$imdbId.mp4" else "$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4"
|
if (temp.isSuccess()) temp else "$nowTvAPI/$tmdbId-1.mp4"
|
||||||
|
} else {
|
||||||
|
"$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4"
|
||||||
|
}
|
||||||
if (!app.get(url, referer = referer).isSuccessful) return
|
if (!app.get(url, referer = referer).isSuccessful) return
|
||||||
}
|
}
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
|
|
|
@ -68,7 +68,7 @@ open class SoraStream : TmdbProvider() {
|
||||||
TvType.Anime,
|
TvType.Anime,
|
||||||
)
|
)
|
||||||
|
|
||||||
val wpredisInterceptor by lazy { CloudflareKiller() }
|
val wpRedisInterceptor by lazy { CloudflareKiller() }
|
||||||
val multiInterceptor by lazy { CloudflareKiller() }
|
val multiInterceptor by lazy { CloudflareKiller() }
|
||||||
|
|
||||||
/** AUTHOR : Hexated & Sora */
|
/** AUTHOR : Hexated & Sora */
|
||||||
|
@ -250,8 +250,7 @@ open class SoraStream : TmdbProvider() {
|
||||||
val recommendations =
|
val recommendations =
|
||||||
res.recommendations?.results?.mapNotNull { media -> media.toSearchResponse() }
|
res.recommendations?.results?.mapNotNull { media -> media.toSearchResponse() }
|
||||||
|
|
||||||
val trailer =
|
val trailer = res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" }
|
||||||
res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" }?.randomOrNull()
|
|
||||||
|
|
||||||
return if (type == TvType.TvSeries) {
|
return if (type == TvType.TvSeries) {
|
||||||
val lastSeason = res.last_episode_to_air?.season_number
|
val lastSeason = res.last_episode_to_air?.season_number
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.lagradost.nicehttp.requestCreator
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Interceptor
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
@ -1285,23 +1286,17 @@ private enum class Symbol(val decimalValue: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun request(
|
class TimeOutInterceptor : Interceptor {
|
||||||
url: String,
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
allowRedirects: Boolean = true,
|
val call = chain
|
||||||
timeout: Long = 60L
|
.withConnectTimeout(60, TimeUnit.SECONDS)
|
||||||
): Response {
|
.withReadTimeout(60, TimeUnit.SECONDS)
|
||||||
val client = OkHttpClient().newBuilder()
|
.withWriteTimeout(60, TimeUnit.SECONDS)
|
||||||
.connectTimeout(timeout, TimeUnit.SECONDS)
|
.request()
|
||||||
.readTimeout(timeout, TimeUnit.SECONDS)
|
.newBuilder()
|
||||||
.writeTimeout(timeout, TimeUnit.SECONDS)
|
.build()
|
||||||
.followRedirects(allowRedirects)
|
return chain.proceed(call)
|
||||||
.followSslRedirects(allowRedirects)
|
}
|
||||||
.build()
|
|
||||||
|
|
||||||
val request: Request = Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
return client.newCall(request).await()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// steal from https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt
|
// steal from https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
// use an integer for version numbers
|
import org.jetbrains.kotlin.konan.properties.Properties
|
||||||
version = 12
|
|
||||||
|
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 13
|
||||||
|
|
||||||
|
android {
|
||||||
|
defaultConfig {
|
||||||
|
val properties = Properties()
|
||||||
|
properties.load(project.rootProject.file("local.properties").inputStream())
|
||||||
|
|
||||||
|
buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
language = "en"
|
language = "en"
|
||||||
|
|
|
@ -10,24 +10,23 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
private const val TRACKER_LIST_URL =
|
|
||||||
"https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
|
|
||||||
|
|
||||||
class StremioC : MainAPI() {
|
class StremioC : MainAPI() {
|
||||||
override var mainUrl = "https://stremio.github.io/stremio-static-addon-example"
|
override var mainUrl = "https://stremio.github.io/stremio-static-addon-example"
|
||||||
override var name = "StremioC"
|
override var name = "StremioC"
|
||||||
override val supportedTypes = setOf(TvType.Others)
|
override val supportedTypes = setOf(TvType.Others)
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
private val cinemataUrl = "https://v3-cinemeta.strem.io"
|
|
||||||
|
|
||||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? {
|
companion object {
|
||||||
|
private const val cinemataUrl = "https://v3-cinemeta.strem.io"
|
||||||
|
private const val TRACKER_LIST_URL = "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
mainUrl = mainUrl.fixSourceUrl()
|
mainUrl = mainUrl.fixSourceUrl()
|
||||||
val res = tryParseJson<Manifest>(request("${mainUrl}/manifest.json").body.string()) ?: return null
|
val res = app.get("${mainUrl}/manifest.json").parsedSafe<Manifest>()
|
||||||
val lists = mutableListOf<HomePageList>()
|
val lists = mutableListOf<HomePageList>()
|
||||||
res.catalogs.apmap { catalog ->
|
res?.catalogs?.apmap { catalog ->
|
||||||
catalog.toHomePageList(this).let {
|
catalog.toHomePageList(this).let {
|
||||||
if (it.list.isNotEmpty()) lists.add(it)
|
if (it.list.isNotEmpty()) lists.add(it)
|
||||||
}
|
}
|
||||||
|
@ -38,11 +37,11 @@ class StremioC : MainAPI() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse>? {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
mainUrl = mainUrl.fixSourceUrl()
|
mainUrl = mainUrl.fixSourceUrl()
|
||||||
val res = tryParseJson<Manifest>(request("${mainUrl}/manifest.json").body.string()) ?: return null
|
val res = app.get("${mainUrl}/manifest.json").parsedSafe<Manifest>()
|
||||||
val list = mutableListOf<SearchResponse>()
|
val list = mutableListOf<SearchResponse>()
|
||||||
res.catalogs.apmap { catalog ->
|
res?.catalogs?.apmap { catalog ->
|
||||||
list.addAll(catalog.search(query, this))
|
list.addAll(catalog.search(query, this))
|
||||||
}
|
}
|
||||||
return list.distinct()
|
return list.distinct()
|
||||||
|
@ -64,10 +63,13 @@ class StremioC : MainAPI() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val loadData = parseJson<LoadData>(data)
|
val loadData = parseJson<LoadData>(data)
|
||||||
val request = request("${mainUrl}/stream/${loadData.type}/${loadData.id}.json")
|
val request = app.get(
|
||||||
if (request.code.isSuccessful()) {
|
"${mainUrl}/stream/${loadData.type}/${loadData.id}.json",
|
||||||
val res = tryParseJson<StreamsResponse>(request.body.string()) ?: return false
|
interceptor = interceptor
|
||||||
res.streams.forEach { stream ->
|
)
|
||||||
|
if (request.isSuccessful) {
|
||||||
|
val res = request.parsedSafe<StreamsResponse>()
|
||||||
|
res?.streams?.forEach { stream ->
|
||||||
stream.runCallback(subtitleCallback, callback)
|
stream.runCallback(subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,15 +105,14 @@ class StremioC : MainAPI() {
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
) {
|
) {
|
||||||
val sites =
|
val sites = AcraApplication.getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
|
||||||
AcraApplication.getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
|
?: mutableListOf()
|
||||||
?: mutableListOf()
|
|
||||||
sites.filter { it.parentJavaClass == "StremioX" }.apmap { site ->
|
sites.filter { it.parentJavaClass == "StremioX" }.apmap { site ->
|
||||||
val request = request("${site.url.fixSourceUrl()}/stream/${type}/${id}.json").body.string()
|
val res = app.get(
|
||||||
val res =
|
"${site.url.fixSourceUrl()}/stream/${type}/${id}.json",
|
||||||
tryParseJson<StreamsResponse>(request)
|
interceptor = interceptor
|
||||||
?: return@apmap
|
).parsedSafe<StreamsResponse>()
|
||||||
res.streams.forEach { stream ->
|
res?.streams?.forEach { stream ->
|
||||||
stream.runCallback(subtitleCallback, callback)
|
stream.runCallback(subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,11 +152,11 @@ class StremioC : MainAPI() {
|
||||||
suspend fun search(query: String, provider: StremioC): List<SearchResponse> {
|
suspend fun search(query: String, provider: StremioC): List<SearchResponse> {
|
||||||
val entries = mutableListOf<SearchResponse>()
|
val entries = mutableListOf<SearchResponse>()
|
||||||
types.forEach { type ->
|
types.forEach { type ->
|
||||||
val json = request("${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json").body.string()
|
val res = app.get(
|
||||||
val res =
|
"${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json",
|
||||||
tryParseJson<CatalogResponse>(json)
|
interceptor = interceptor
|
||||||
?: return@forEach
|
).parsedSafe<CatalogResponse>()
|
||||||
res.metas?.forEach { entry ->
|
res?.metas?.forEach { entry ->
|
||||||
entries.add(entry.toSearchResponse(provider))
|
entries.add(entry.toSearchResponse(provider))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,11 +166,11 @@ class StremioC : MainAPI() {
|
||||||
suspend fun toHomePageList(provider: StremioC): HomePageList {
|
suspend fun toHomePageList(provider: StremioC): HomePageList {
|
||||||
val entries = mutableListOf<SearchResponse>()
|
val entries = mutableListOf<SearchResponse>()
|
||||||
types.forEach { type ->
|
types.forEach { type ->
|
||||||
val json = request("${provider.mainUrl}/catalog/${type}/${id}.json").body.string()
|
val res = app.get(
|
||||||
val res =
|
"${provider.mainUrl}/catalog/${type}/${id}.json",
|
||||||
tryParseJson<CatalogResponse>(json)
|
interceptor = interceptor
|
||||||
?: return@forEach
|
).parsedSafe<CatalogResponse>()
|
||||||
res.metas?.forEach { entry ->
|
res?.metas?.forEach { entry ->
|
||||||
entries.add(entry.toSearchResponse(provider))
|
entries.add(entry.toSearchResponse(provider))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,6 +187,7 @@ class StremioC : MainAPI() {
|
||||||
val source: String?,
|
val source: String?,
|
||||||
val type: String?
|
val type: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class CatalogEntry(
|
private data class CatalogEntry(
|
||||||
@JsonProperty("name") val name: String,
|
@JsonProperty("name") val name: String,
|
||||||
@JsonProperty("id") val id: String,
|
@JsonProperty("id") val id: String,
|
||||||
|
@ -226,7 +228,7 @@ class StremioC : MainAPI() {
|
||||||
year = yearNum?.toIntOrNull()
|
year = yearNum?.toIntOrNull()
|
||||||
tags = genre ?: genres
|
tags = genre ?: genres
|
||||||
addActors(cast)
|
addActors(cast)
|
||||||
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }?.randomOrNull())
|
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" })
|
||||||
addImdbId(imdbId)
|
addImdbId(imdbId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,7 +247,8 @@ class StremioC : MainAPI() {
|
||||||
year = yearNum?.toIntOrNull()
|
year = yearNum?.toIntOrNull()
|
||||||
tags = genre ?: genres
|
tags = genre ?: genres
|
||||||
addActors(cast)
|
addActors(cast)
|
||||||
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }?.randomOrNull())
|
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }
|
||||||
|
?.randomOrNull())
|
||||||
addImdbId(imdbId)
|
addImdbId(imdbId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,13 +288,14 @@ class StremioC : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class ProxyHeaders(
|
private data class ProxyHeaders(
|
||||||
val request: Map<String,String>?,
|
val request: Map<String, String>?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class BehaviorHints(
|
private data class BehaviorHints(
|
||||||
val proxyHeaders: ProxyHeaders?,
|
val proxyHeaders: ProxyHeaders?,
|
||||||
val headers: Map<String,String>?,
|
val headers: Map<String, String>?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Stream(
|
private data class Stream(
|
||||||
val name: String?,
|
val name: String?,
|
||||||
val title: String?,
|
val title: String?,
|
||||||
|
@ -312,12 +316,13 @@ class StremioC : MainAPI() {
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
name ?: "",
|
name ?: "",
|
||||||
fixRDSourceName(name, title),
|
fixSourceName(name, title),
|
||||||
url,
|
url,
|
||||||
"",
|
"",
|
||||||
getQualityFromName(description),
|
getQuality(listOf(description,title,name)),
|
||||||
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers ?: mapOf(),
|
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers
|
||||||
isM3u8 = URI(url).path.endsWith(".m3u8")
|
?: mapOf(),
|
||||||
|
type = INFER_TYPE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subtitles.map { sub ->
|
subtitles.map { sub ->
|
||||||
|
|
|
@ -10,26 +10,21 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
import java.net.URI
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||||
|
|
||||||
open class StremioX : TmdbProvider() {
|
class StremioX : TmdbProvider() {
|
||||||
override var mainUrl = "https://torrentio.strem.fun"
|
override var mainUrl = "https://torrentio.strem.fun"
|
||||||
override var name = "StremioX"
|
override var name = "StremioX"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override val hasQuickSearch = true
|
override val hasQuickSearch = true
|
||||||
override val supportedTypes = setOf(
|
override val supportedTypes = setOf(TvType.Others)
|
||||||
TvType.Others,
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TRACKER_LIST_URL =
|
const val TRACKER_LIST_URL = "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
|
||||||
"https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
|
|
||||||
private const val tmdbAPI = "https://api.themoviedb.org/3"
|
private const val tmdbAPI = "https://api.themoviedb.org/3"
|
||||||
private val apiKey =
|
private const val apiKey = BuildConfig.TMDB_API
|
||||||
base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
|
|
||||||
|
|
||||||
fun getType(t: String?): TvType {
|
fun getType(t: String?): TvType {
|
||||||
return when (t) {
|
return when (t) {
|
||||||
|
@ -44,11 +39,6 @@ open class StremioX : TmdbProvider() {
|
||||||
else -> ShowStatus.Completed
|
else -> ShowStatus.Completed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun base64DecodeAPI(api: String): String {
|
|
||||||
return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val mainPage = mainPageOf(
|
override val mainPage = mainPageOf(
|
||||||
|
@ -159,7 +149,7 @@ open class StremioX : TmdbProvider() {
|
||||||
eps.seasonNumber,
|
eps.seasonNumber,
|
||||||
eps.episodeNumber
|
eps.episodeNumber
|
||||||
).toJson(),
|
).toJson(),
|
||||||
name = eps.name + if (isUpcoming(eps.airDate)) " - [UPCOMING]" else "",
|
name = eps.name + if (isUpcoming(eps.airDate)) " • [UPCOMING]" else "",
|
||||||
season = eps.seasonNumber,
|
season = eps.seasonNumber,
|
||||||
episode = eps.episodeNumber,
|
episode = eps.episodeNumber,
|
||||||
posterUrl = getImageUrl(eps.stillPath),
|
posterUrl = getImageUrl(eps.stillPath),
|
||||||
|
@ -177,7 +167,7 @@ open class StremioX : TmdbProvider() {
|
||||||
this.backgroundPosterUrl = bgPoster
|
this.backgroundPosterUrl = bgPoster
|
||||||
this.year = year
|
this.year = year
|
||||||
this.plot = res.overview
|
this.plot = res.overview
|
||||||
this.tags = if (isAnime) keywords else genres
|
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
|
||||||
this.rating = rating
|
this.rating = rating
|
||||||
this.showStatus = getStatus(res.status)
|
this.showStatus = getStatus(res.status)
|
||||||
this.recommendations = recommendations
|
this.recommendations = recommendations
|
||||||
|
@ -200,7 +190,7 @@ open class StremioX : TmdbProvider() {
|
||||||
this.year = year
|
this.year = year
|
||||||
this.plot = res.overview
|
this.plot = res.overview
|
||||||
this.duration = res.runtime
|
this.duration = res.runtime
|
||||||
this.tags = if (isAnime) keywords else genres
|
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
|
||||||
this.rating = rating
|
this.rating = rating
|
||||||
this.recommendations = recommendations
|
this.recommendations = recommendations
|
||||||
this.actors = actors
|
this.actors = actors
|
||||||
|
@ -243,13 +233,13 @@ open class StremioX : TmdbProvider() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
) {
|
) {
|
||||||
val fixMainUrl = mainUrl.fixSourceUrl()
|
val fixMainUrl = mainUrl.fixSourceUrl()
|
||||||
val url = if(season == null) {
|
val url = if (season == null) {
|
||||||
"$fixMainUrl/stream/movie/$imdbId.json"
|
"$fixMainUrl/stream/movie/$imdbId.json"
|
||||||
} else {
|
} else {
|
||||||
"$fixMainUrl/stream/series/$imdbId:$season:$episode.json"
|
"$fixMainUrl/stream/series/$imdbId:$season:$episode.json"
|
||||||
}
|
}
|
||||||
val res = AppUtils.tryParseJson<StreamsResponse>(request(url).body.string()) ?: return
|
val res = app.get(url, interceptor = interceptor).parsedSafe<StreamsResponse>()
|
||||||
res.streams.forEach { stream ->
|
res?.streams?.forEach { stream ->
|
||||||
stream.runCallback(subtitleCallback, callback)
|
stream.runCallback(subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,12 +252,12 @@ open class StremioX : TmdbProvider() {
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class ProxyHeaders(
|
private data class ProxyHeaders(
|
||||||
val request: Map<String,String>?,
|
val request: Map<String, String>?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class BehaviorHints(
|
private data class BehaviorHints(
|
||||||
val proxyHeaders: ProxyHeaders?,
|
val proxyHeaders: ProxyHeaders?,
|
||||||
val headers: Map<String,String>?,
|
val headers: Map<String, String>?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Stream(
|
private data class Stream(
|
||||||
|
@ -290,12 +280,13 @@ open class StremioX : TmdbProvider() {
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
name ?: "",
|
name ?: "",
|
||||||
fixRDSourceName(name, title),
|
fixSourceName(name, title),
|
||||||
url,
|
url,
|
||||||
"",
|
"",
|
||||||
getQualityFromName(description),
|
getQuality(listOf(description,title,name)),
|
||||||
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers ?: mapOf(),
|
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers
|
||||||
isM3u8 = URI(url).path.endsWith(".m3u8")
|
?: mapOf(),
|
||||||
|
type = INFER_TYPE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subtitles.map { sub ->
|
subtitles.map { sub ->
|
||||||
|
|
|
@ -3,10 +3,9 @@ package com.hexated
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.base64Encode
|
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
|
|
||||||
const val openSubAPI = "https://opensubtitles.strem.io/stremio/v1"
|
const val openSubAPI = "https://opensubtitles-v3.strem.io"
|
||||||
const val watchSomuchAPI = "https://watchsomuch.tv"
|
const val watchSomuchAPI = "https://watchsomuch.tv"
|
||||||
|
|
||||||
object SubsExtractors {
|
object SubsExtractors {
|
||||||
|
@ -16,22 +15,20 @@ object SubsExtractors {
|
||||||
episode: Int? = null,
|
episode: Int? = null,
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
) {
|
) {
|
||||||
val id = if(season == null) {
|
val slug = if(season == null) {
|
||||||
imdbId
|
"movie/$imdbId"
|
||||||
} else {
|
} else {
|
||||||
"$imdbId $season $episode"
|
"series/$imdbId:$season:$episode"
|
||||||
}
|
}
|
||||||
val data = base64Encode("""{"id":1,"jsonrpc":"2.0","method":"subtitles.find","params":[null,{"query":{"itemHash":"$id"}}]}""".toByteArray())
|
app.get("${openSubAPI}/subtitles/$slug.json").parsedSafe<OsResult>()?.subtitles?.map { sub ->
|
||||||
app.get("${openSubAPI}/q.json?b=$data").parsedSafe<OsResult>()?.result?.all?.map { sub ->
|
|
||||||
subtitleCallback.invoke(
|
subtitleCallback.invoke(
|
||||||
SubtitleFile(
|
SubtitleFile(
|
||||||
SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang
|
SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang
|
||||||
?: "",
|
?: return@map,
|
||||||
sub.url ?: return@map
|
sub.url ?: return@map
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun invokeWatchsomuch(
|
suspend fun invokeWatchsomuch(
|
||||||
|
@ -81,12 +78,8 @@ object SubsExtractors {
|
||||||
@JsonProperty("lang") val lang: String? = null,
|
@JsonProperty("lang") val lang: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class OsAll(
|
|
||||||
@JsonProperty("all") val all: ArrayList<OsSubtitles>? = arrayListOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class OsResult(
|
data class OsResult(
|
||||||
@JsonProperty("result") val result: OsAll? = null,
|
@JsonProperty("subtitles") val subtitles: ArrayList<OsSubtitles>? = arrayListOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WatchsomuchTorrents(
|
data class WatchsomuchTorrents(
|
||||||
|
|
|
@ -1,53 +1,50 @@
|
||||||
package com.hexated
|
package com.hexated
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.nicehttp.Requests.Companion.await
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
const val defaultTimeOut = 30L
|
val interceptor = TimeOutInterceptor()
|
||||||
suspend fun request(
|
|
||||||
url: String,
|
|
||||||
allowRedirects: Boolean = true,
|
|
||||||
timeout: Long = defaultTimeOut
|
|
||||||
): Response {
|
|
||||||
val client = OkHttpClient().newBuilder()
|
|
||||||
.connectTimeout(timeout, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(timeout, TimeUnit.SECONDS)
|
|
||||||
.writeTimeout(timeout, TimeUnit.SECONDS)
|
|
||||||
.followRedirects(allowRedirects)
|
|
||||||
.followSslRedirects(allowRedirects)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val request: Request = Request.Builder()
|
class TimeOutInterceptor : Interceptor {
|
||||||
.url(url)
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
.build()
|
val call = chain
|
||||||
return client.newCall(request).await()
|
.withConnectTimeout(60, TimeUnit.SECONDS)
|
||||||
}
|
.withReadTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.withWriteTimeout(60, TimeUnit.SECONDS)
|
||||||
fun Int.isSuccessful() : Boolean {
|
.request()
|
||||||
return this in 200..299
|
.newBuilder()
|
||||||
|
.build()
|
||||||
|
return chain.proceed(call)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.fixSourceUrl(): String {
|
fun String.fixSourceUrl(): String {
|
||||||
return this.replace("/manifest.json", "").replace("stremio://", "https://")
|
return this.replace("/manifest.json", "").replace("stremio://", "https://")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fixRDSourceName(name: String?, title: String?): String {
|
fun fixSourceName(name: String?, title: String?): String {
|
||||||
return when {
|
return when {
|
||||||
name?.contains("[RD+]", true) == true -> "[RD+] $title"
|
name?.contains("[RD+]", true) == true -> "[RD+] $title"
|
||||||
name?.contains("[RD download]", true) == true -> "[RD] $title"
|
name?.contains("[RD download]", true) == true -> "[RD download] $title"
|
||||||
!name.isNullOrEmpty() && !title.isNullOrEmpty() -> "$name $title"
|
!name.isNullOrEmpty() && !title.isNullOrEmpty() -> "$name $title"
|
||||||
else -> title ?: name ?: ""
|
else -> title ?: name ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getQuality(qualities: List<String?>): Int {
|
||||||
|
fun String.getQuality(): String? {
|
||||||
|
return Regex("(\\d{3,4}[pP])").find(this)?.groupValues?.getOrNull(1)
|
||||||
|
}
|
||||||
|
val quality = qualities.firstNotNullOfOrNull { it?.getQuality() }
|
||||||
|
return getQualityFromName(quality)
|
||||||
|
}
|
||||||
|
|
||||||
fun getEpisodeSlug(
|
fun getEpisodeSlug(
|
||||||
season: Int? = null,
|
season: Int? = null,
|
||||||
episode: Int? = null,
|
episode: Int? = null,
|
||||||
|
|
Loading…
Reference in a new issue