sora: added netflix

This commit is contained in:
hexated 2023-09-15 01:39:07 +07:00
parent 4077d1275d
commit 36e77332c5
12 changed files with 167 additions and 28 deletions

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 17 version = 18
cloudstream { cloudstream {

View file

@ -59,7 +59,7 @@ class NontonAnimeIDProvider : MainAPI() {
document.select("aside#sidebar_right > div.side").forEach { block -> document.select("aside#sidebar_right > div.side").forEach { block ->
val header = block.selectFirst("h3")!!.ownText().trim() val header = block.selectFirst("h3")!!.ownText().trim()
val animes = block.select("ul li.fullwdth").mapNotNull { val animes = block.select("div.bor").mapNotNull {
it.toSearchResultPopular() it.toSearchResultPopular()
} }
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes)) if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
@ -71,7 +71,7 @@ class NontonAnimeIDProvider : MainAPI() {
private fun Element.toSearchResult(): AnimeSearchResponse? { private fun Element.toSearchResult(): AnimeSearchResponse? {
val href = fixUrl(this.selectFirst("a")!!.attr("href")) val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val title = this.selectFirst("h3.title")?.text() ?: return null val title = this.selectFirst("h3.title")?.text() ?: return null
val posterUrl = fixUrl(this.select("img").attr("data-src")) val posterUrl = fixUrl(this.select("img").attr("src"))
return newAnimeSearchResponse(title, href, TvType.Anime) { return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
@ -83,7 +83,7 @@ class NontonAnimeIDProvider : MainAPI() {
private fun Element.toSearchResultPopular(): AnimeSearchResponse? { private fun Element.toSearchResultPopular(): AnimeSearchResponse? {
val href = fixUrl(this.selectFirst("a")!!.attr("href")) val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val title = this.selectFirst("h4")?.text()?.trim() ?: return null val title = this.selectFirst("h4")?.text()?.trim() ?: return null
val posterUrl = fixUrl(this.select("img").attr("data-src")) val posterUrl = fixUrl(this.select("img").attr("src"))
return newAnimeSearchResponse(title, href, TvType.Anime) { return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl this.posterUrl = posterUrl

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 15 version = 16
cloudstream { cloudstream {

View file

@ -4,9 +4,13 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class Samehadaku : MainAPI() { class Samehadaku : MainAPI() {
@ -15,13 +19,26 @@ class Samehadaku : MainAPI() {
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
override val hasDownloadSupport = true override val hasDownloadSupport = true
private val cloudflareKiller by lazy { CloudflareKiller() }
private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) }
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Anime, TvType.Anime,
TvType.AnimeMovie, TvType.AnimeMovie,
TvType.OVA TvType.OVA
) )
class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val doc = Jsoup.parse(response.peekBody(1024 * 1024).string())
if (doc.select("title").text() == "Just a moment...") {
return cloudflareKiller.intercept(chain)
}
return response
}
}
companion object { companion object {
const val acefile = "https://acefile.co" const val acefile = "https://acefile.co"
@ -52,7 +69,7 @@ class Samehadaku : MainAPI() {
val items = mutableListOf<HomePageList>() val items = mutableListOf<HomePageList>()
if (request.name != "Episode Terbaru" && page <= 1) { if (request.name != "Episode Terbaru" && page <= 1) {
val doc = app.get(request.data).document val doc = app.get(request.data, interceptor = interceptor).document
doc.select("div.widget_senction:not(:contains(Baca Komik))").forEach { block -> doc.select("div.widget_senction:not(:contains(Baca Komik))").forEach { block ->
val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach
val home = block.select("div.animepost").mapNotNull { val home = block.select("div.animepost").mapNotNull {
@ -64,7 +81,7 @@ class Samehadaku : MainAPI() {
if (request.name == "Episode Terbaru") { if (request.name == "Episode Terbaru") {
val home = val home =
app.get(request.data + page).document.selectFirst("div.post-show")?.select("ul li") app.get(request.data + page, interceptor = interceptor).document.selectFirst("div.post-show")?.select("ul li")
?.mapNotNull { ?.mapNotNull {
it.toSearchResult() it.toSearchResult()
} ?: throw ErrorLoadingException("No Media Found") } ?: throw ErrorLoadingException("No Media Found")
@ -84,12 +101,13 @@ class Samehadaku : MainAPI() {
return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) { return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(epNum) addSub(epNum)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val document = app.get("$mainUrl/?s=$query").document val document = app.get("$mainUrl/?s=$query", interceptor = interceptor).document
return document.select("main#main div.animepost").mapNotNull { return document.select("main#main div.animepost").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
@ -99,10 +117,10 @@ class Samehadaku : MainAPI() {
val fixUrl = if (url.contains("/anime/")) { val fixUrl = if (url.contains("/anime/")) {
url url
} else { } else {
app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href") app.get(url, interceptor = interceptor).document.selectFirst("div.nvs.nvsc a")?.attr("href")
} }
val document = app.get(fixUrl ?: return null).document val document = app.get(fixUrl ?: return null, interceptor = interceptor).document
val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null
val poster = document.selectFirst("div.thumb > img")?.attr("src") val poster = document.selectFirst("div.thumb > img")?.attr("src")
val tags = document.select("div.genre-info > a").map { it.text() } val tags = document.select("div.genre-info > a").map { it.text() }
@ -147,6 +165,7 @@ class Samehadaku : MainAPI() {
this.recommendations = recommendations this.recommendations = recommendations
addMalId(tracker?.malId) addMalId(tracker?.malId)
addAniListId(tracker?.aniId?.toIntOrNull()) addAniListId(tracker?.aniId?.toIntOrNull())
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -158,7 +177,7 @@ class Samehadaku : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data).document val document = app.get(data, interceptor = interceptor).document
argamap( argamap(
{ {
@ -176,7 +195,8 @@ class Samehadaku : MainAPI() {
"type" to dataType "type" to dataType
), ),
referer = data, referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest") headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
interceptor = interceptor
).document.select("iframe").attr("src") ).document.select("iframe").attr("src")
loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback) loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback)

View file

@ -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 = 168 version = 169
android { android {
defaultConfig { defaultConfig {

View file

@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.extractors.Pixeldrain import com.lagradost.cloudstream3.extractors.Pixeldrain
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import java.math.BigInteger import java.math.BigInteger
import java.net.URI
import java.security.MessageDigest import java.security.MessageDigest
open class Playm4u : ExtractorApi() { open class Playm4u : ExtractorApi() {
@ -150,7 +151,7 @@ open class VCloud : ExtractorApi() {
val changedLink = doc.selectFirst("script:containsData(url =)")?.data()?.let { val changedLink = doc.selectFirst("script:containsData(url =)")?.data()?.let {
"""url\s*=\s*['"](.*)['"];""".toRegex().find(it)?.groupValues?.get(1) """url\s*=\s*['"](.*)['"];""".toRegex().find(it)?.groupValues?.get(1)
?.substringAfter("r=") ?.substringAfter("r=")
} } ?: doc.selectFirst("div.div.vd.d-none a")?.attr("href")
val header = doc.selectFirst("div.card-header")?.text() val header = doc.selectFirst("div.card-header")?.text()
app.get( app.get(
base64Decode(changedLink ?: return), cookies = res.cookies, headers = mapOf( base64Decode(changedLink ?: return), cookies = res.cookies, headers = mapOf(
@ -158,7 +159,8 @@ open class VCloud : ExtractorApi() {
) )
).document.select("p.text-success ~ a").apmap { ).document.select("p.text-success ~ a").apmap {
val link = it.attr("href") val link = it.attr("href")
if (it.text().contains(Regex("Server : 1|2"))) { val uri = URI(link)
if (uri.path.contains("workers.dev")) {
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
this.name, this.name,
@ -184,6 +186,16 @@ open class VCloud : ExtractorApi() {
} }
class HubcloudLol : VCloud() {
override val name = "Hubcloud"
override val mainUrl = "https://hubcloud.lol"
}
class Hubcloud : VCloud() {
override val name = "Hubcloud"
override val mainUrl = "https://hubcloud.in"
}
class Pixeldra : Pixeldrain() { class Pixeldra : Pixeldrain() {
override val mainUrl = "https://pixeldra.in" override val mainUrl = "https://pixeldra.in"
} }

View file

@ -5,12 +5,7 @@ import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.Session import com.lagradost.nicehttp.Session
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.GMPlayer
import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe
import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler
import com.lagradost.cloudstream3.extractors.helper.GogoHelper
import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -319,7 +314,6 @@ object SoraExtractor : SoraStream() {
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
fixIframe: Boolean = false, fixIframe: Boolean = false,
encrypt: Boolean = false, encrypt: Boolean = false,
key: String? = null,
) { ) {
fun String.fixBloat() : String { fun String.fixBloat() : String {
return this.replace("\"", "").replace("\\", "") return this.replace("\"", "").replace("\\", "")
@ -572,8 +566,8 @@ object SoraExtractor : SoraStream() {
"${link.name} ${it.second}", "${link.name} ${it.second}",
link.url, link.url,
link.referer, link.referer,
when { when (link.type) {
link.type == ExtractorLinkType.M3U8 -> link.quality ExtractorLinkType.M3U8 -> link.quality
else -> getQualityFromName(it.first) else -> getQualityFromName(it.first)
}, },
link.type, link.type,
@ -900,8 +894,8 @@ object SoraExtractor : SoraStream() {
"${link.name} [${it.second}]", "${link.name} [${it.second}]",
link.url, link.url,
link.referer, link.referer,
when { when (link.type) {
link.type == ExtractorLinkType.M3U8 -> link.quality ExtractorLinkType.M3U8 -> link.quality
else -> it.third else -> it.third
}, },
link.type, link.type,
@ -2756,6 +2750,52 @@ object SoraExtractor : SoraStream() {
) )
} }
suspend fun invokeNetflix(
imdbId: String? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val headers = mapOf("X-Requested-With" to "XMLHttpRequest", "Cookie" to "hd=on")
val netflixId = imdbToNetflixId(imdbId, season)
val (title, id) = app.get(
"$netflixAPI/post.php?id=${netflixId ?: return}&t=${APIHolder.unixTime}",
headers = headers
).parsedSafe<NetflixResponse>().let { media ->
if (season == null) {
media?.title to netflixId
} else {
val seasonId = media?.season?.find { it.s == "$season" }?.id
val episodeId =
app.get(
"$netflixAPI/episodes.php?s=${seasonId}&series=$netflixId&t=${APIHolder.unixTime}",
headers = headers
)
.parsedSafe<NetflixResponse>()?.episodes?.find { it.ep == "E$episode" }?.id
media?.title to episodeId
}
}
app.get(
"$netflixAPI/playlist.php?id=${id ?: return}&t=${title ?: return}&tm=${APIHolder.unixTime}",
headers = headers
).text.let {
tryParseJson<ArrayList<NetflixResponse>>(it)
}?.firstOrNull()?.sources?.map {
callback.invoke(
ExtractorLink(
"Netflix",
"Netflix",
fixUrl(it.file ?: return@map, netflixAPI),
"$netflixAPI/",
getQualityFromName(it.file.substringAfter("q=")),
INFER_TYPE,
headers = mapOf("Cookie" to "hd=on")
)
)
}
}
} }

View file

@ -428,3 +428,36 @@ data class EMovieTraks(
data class FourCartoonSources( data class FourCartoonSources(
@JsonProperty("videoSource") val videoSource: String? = null, @JsonProperty("videoSource") val videoSource: String? = null,
) )
data class WatchhubStream(
@JsonProperty("name") val name: String? = null,
@JsonProperty("externalUrl") val externalUrl: String? = null,
)
data class WatchhubResponse(
@JsonProperty("streams") val streams: ArrayList<WatchhubStream>? = arrayListOf(),
)
data class NetflixSources(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
)
data class NetflixEpisodes(
@JsonProperty("id") val id: String? = null,
@JsonProperty("t") val t: String? = null,
@JsonProperty("s") val s: String? = null,
@JsonProperty("ep") val ep: String? = null,
)
data class NetflixSeason(
@JsonProperty("s") val s: String? = null,
@JsonProperty("id") val id: String? = null,
)
data class NetflixResponse(
@JsonProperty("title") val title: String? = null,
@JsonProperty("season") val season: ArrayList<NetflixSeason>? = arrayListOf(),
@JsonProperty("episodes") val episodes: ArrayList<NetflixEpisodes>? = arrayListOf(),
@JsonProperty("sources") val sources: ArrayList<NetflixSources>? = arrayListOf(),
)

View file

@ -41,6 +41,7 @@ import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeJump1 import com.hexated.SoraExtractor.invokeJump1
import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetflix
import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokePobmovies import com.hexated.SoraExtractor.invokePobmovies
import com.hexated.SoraExtractor.invokePrimewire import com.hexated.SoraExtractor.invokePrimewire
@ -79,6 +80,7 @@ open class SoraStream : TmdbProvider() {
const val malsyncAPI = "https://api.malsync.moe" const val malsyncAPI = "https://api.malsync.moe"
const val consumetHelper = "https://api.consumet.org/anime/9anime/helper" const val consumetHelper = "https://api.consumet.org/anime/9anime/helper"
const val jikanAPI = "https://api.jikan.moe/v4" const val jikanAPI = "https://api.jikan.moe/v4"
const val watchhubApi = "https://watchhub.strem.io"
private val apiKey = private val apiKey =
base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
@ -131,6 +133,7 @@ open class SoraStream : TmdbProvider() {
const val susflixAPI = "https://susflix.tv" const val susflixAPI = "https://susflix.tv"
const val jump1API = "https://ca.jump1.net" const val jump1API = "https://ca.jump1.net"
const val vegaMoviesAPI = "https://vegamovies.im" const val vegaMoviesAPI = "https://vegamovies.im"
const val netflixAPI = "https://m.netflixmirror.com"
// INDEX SITE // INDEX SITE
const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev" const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev"
@ -738,6 +741,14 @@ open class SoraStream : TmdbProvider() {
callback callback
) )
}, },
{
if (!res.isAnime) invokeNetflix(
res.imdbId,
res.season,
res.episode,
callback
)
}
) )
return true return true

View file

@ -28,6 +28,7 @@ import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeJump1 import com.hexated.SoraExtractor.invokeJump1
import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetflix
import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokePrimewire import com.hexated.SoraExtractor.invokePrimewire
import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidSrc
@ -327,6 +328,14 @@ class SoraStreamLite : SoraStream() {
callback callback
) )
}, },
{
if (!res.isAnime) invokeNetflix(
res.imdbId,
res.season,
res.episode,
callback
)
}
) )
return true return true

View file

@ -20,5 +20,7 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Playm4u()) registerExtractorAPI(Playm4u())
registerExtractorAPI(VCloud()) registerExtractorAPI(VCloud())
registerExtractorAPI(Pixeldra()) registerExtractorAPI(Pixeldra())
registerExtractorAPI(Hubcloud())
registerExtractorAPI(HubcloudLol())
} }
} }

View file

@ -13,6 +13,7 @@ import com.hexated.SoraStream.Companion.malsyncAPI
import com.hexated.SoraStream.Companion.smashyStreamAPI import com.hexated.SoraStream.Companion.smashyStreamAPI
import com.hexated.SoraStream.Companion.tvMoviesAPI import com.hexated.SoraStream.Companion.tvMoviesAPI
import com.hexated.SoraStream.Companion.watchOnlineAPI import com.hexated.SoraStream.Companion.watchOnlineAPI
import com.hexated.SoraStream.Companion.watchhubApi
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.APIHolder.unixTimeMS
@ -1139,6 +1140,17 @@ suspend fun tmdbToAnimeId(title: String?, year: Int?, season: String?, type: TvT
} }
suspend fun imdbToNetflixId(imdbId: String?, season: Int?): String? {
val url = if (season == null) {
"$watchhubApi/stream/movie/$imdbId.json"
} else {
"$watchhubApi/stream/series/$imdbId:1:1.json"
}
return app.get(url)
.parsedSafe<WatchhubResponse>()?.streams?.find { it.name == "Netflix" }?.externalUrl
?.substringAfterLast("/")
}
suspend fun loadCustomExtractor( suspend fun loadCustomExtractor(
name: String? = null, name: String? = null,
url: String, url: String,
@ -1154,8 +1166,8 @@ suspend fun loadCustomExtractor(
name ?: link.name, name ?: link.name,
link.url, link.url,
link.referer, link.referer,
when { when (link.type) {
link.type == ExtractorLinkType.M3U8 -> link.quality ExtractorLinkType.M3U8 -> link.quality
else -> quality ?: link.quality else -> quality ?: link.quality
}, },
link.type, link.type,