Merge remote-tracking branch 'OriginHexa/master' into update2

# Conflicts:
#	Movierulzhd/build.gradle.kts
#	Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt
This commit is contained in:
IndusAryan 2024-01-28 15:08:04 +05:30
commit 8215b17940
39 changed files with 350 additions and 548 deletions

View File

@ -54,7 +54,7 @@ jobs:
ZSHOW_API: ${{ secrets.ZSHOW_API }}
SFMOVIES_API: ${{ secrets.SFMOVIES_API }}
CINEMATV_API: ${{ secrets.CINEMATV_API }}
OMOVIES_API: ${{ secrets.OMOVIES_API }}
GHOSTX_API: ${{ secrets.GHOSTX_API }}
SUPERSTREAM_FIRST_API: ${{ secrets.SUPERSTREAM_FIRST_API }}
SUPERSTREAM_SECOND_API: ${{ secrets.SUPERSTREAM_SECOND_API }}
SUPERSTREAM_THIRD_API: ${{ secrets.SUPERSTREAM_THIRD_API }}
@ -73,7 +73,7 @@ jobs:
echo ZSHOW_API=$ZSHOW_API >> local.properties
echo SFMOVIES_API=$SFMOVIES_API >> local.properties
echo CINEMATV_API=$CINEMATV_API >> local.properties
echo OMOVIES_API=$OMOVIES_API >> local.properties
echo GHOSTX_API=$GHOSTX_API >> local.properties
echo SUPERSTREAM_FIRST_API=$SUPERSTREAM_FIRST_API >> local.properties
echo SUPERSTREAM_SECOND_API=$SUPERSTREAM_SECOND_API >> local.properties
echo SUPERSTREAM_THIRD_API=$SUPERSTREAM_THIRD_API >> local.properties

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 9
version = 10
android {
defaultConfig {
@ -38,5 +38,5 @@ cloudstream {
"OVA",
)
iconUrl = "https://media.discordapp.net/attachments/1059306855865782282/1123970193274712096/Anichi.png"
iconUrl = "https://cdn.discordapp.com/attachments/1109266606292488297/1200425504432472176/Anichi.png"
}

View File

@ -1,6 +1,5 @@
package com.hexated
import com.hexated.AnichiExtractors.invokeExternalSources
import com.hexated.AnichiExtractors.invokeInternalSources
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
@ -214,25 +213,12 @@ open class Anichi : MainAPI() {
val loadData = parseJson<AnichiLoadData>(data)
argamap(
{
invokeInternalSources(
loadData.hash,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
},
{
invokeExternalSources(
loadData.idMal,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
}
invokeInternalSources(
loadData.hash,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
return true
@ -245,7 +231,6 @@ open class Anichi : MainAPI() {
const val anilistApi = "https://graphql.anilist.co"
const val jikanApi = "https://api.jikan.moe/v4"
const val marinHost = "https://marin.moe"
private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"

View File

@ -121,67 +121,6 @@ object AnichiExtractors : Anichi() {
}
}
suspend fun invokeExternalSources(
idMal: Int? = null,
dubStatus: String,
episode: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val ids = app.get("https://api.malsync.moe/mal/anime/${idMal ?: return}")
.parsedSafe<MALSyncResponses>()?.sites
if (dubStatus == "sub") invokeMarin(ids?.marin?.keys?.firstOrNull(), episode, callback)
}
private suspend fun invokeMarin(
id: String? = null,
episode: String,
callback: (ExtractorLink) -> Unit
) {
val url = "$marinHost/anime/${id ?: return}/$episode"
val cookies = app.get(
"$marinHost/anime",
headers = mapOf(
"Cookie" to "__ddg1_=;__ddg2_=;"
),
referer = "$marinHost/anime",
).cookies.let {
decode(it["XSRF-TOKEN"].toString()) to decode(it["marin_session"].toString())
}
val json = app.get(
url,
headers = mapOf(
"Accept" to "text/html, application/xhtml+xml",
"Cookie" to "__ddg1=;__ddg2_=;XSRF-TOKEN=${cookies.first};marin_session=${cookies.second};",
"X-XSRF-TOKEN" to cookies.first
),
referer = "$marinHost/anime/$id"
).document.selectFirst("div#app")?.attr("data-page")
tryParseJson<MarinResponses>(json)?.props?.video?.data?.mirror?.map { video ->
callback.invoke(
ExtractorLink(
"Marin",
"Marin",
video.code?.file ?: return@map,
url,
video.code.height ?: Qualities.Unknown.value,
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language" to "en-US,en;q=0.5",
"Cookie" to "__ddg1=;__ddg2_=; XSRF-TOKEN=${cookies.first}; marin_session=${cookies.second};",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
)
)
)
}
}
private suspend fun invokeGogo(
link: String,
subtitleCallback: (SubtitleFile) -> Unit,

View File

@ -240,7 +240,6 @@ data class PageStatus(
@JsonProperty("__typename") val _typename: String? = null
)
data class Recommendations(
@JsonProperty("anyCard") val anyCard: AnyCard? = null,
@JsonProperty("pageStatus") val pageStatus: PageStatus? = PageStatus(),
@ -255,38 +254,4 @@ data class QueryPopular(
data class DataPopular(
@JsonProperty("queryPopular") val queryPopular: QueryPopular? = QueryPopular()
)
data class MALSyncSites(
@JsonProperty("Zoro") val zoro: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
@JsonProperty("Marin") val marin: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
)
data class MALSyncResponses(
@JsonProperty("Sites") val sites: MALSyncSites? = null,
)
data class MarinCode(
@JsonProperty("file") val file: String? = null,
@JsonProperty("height") val height: Int? = null,
)
data class MarinMirror(
@JsonProperty("code") val code: MarinCode? = null,
)
data class MarinData(
@JsonProperty("mirror") val mirror: ArrayList<MarinMirror>? = arrayListOf(),
)
data class MarinVideos(
@JsonProperty("data") val data: MarinData? = null,
)
data class MarinProps(
@JsonProperty("video") val video: MarinVideos? = null,
)
data class MarinResponses(
@JsonProperty("props") val props: MarinProps? = null,
)

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 30
version = 31
android {
defaultConfig {

View File

@ -10,7 +10,7 @@ import org.jsoup.nodes.Element
import java.net.URI
open class Gomov : MainAPI() {
override var mainUrl = "https://gomov.bio"
override var mainUrl = "https://gomov.co"
private var directUrl: String? = null
override var name = "Gomov"
override val hasMainPage = true

View File

@ -3,7 +3,7 @@ package com.hexated
import com.lagradost.cloudstream3.mainPageOf
class Ngefilm : Gomov() {
override var mainUrl = "https://tv3.ngefilm21.homes"
override var mainUrl = "https://tv4.ngefilm21.homes"
override var name = "Ngefilm"
override val mainPage = mainPageOf(
"/page/%d/?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru",

View File

@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.TvSeriesLoadResponse
import com.lagradost.cloudstream3.*
class Pusatfilm : Gomov() {
override var mainUrl = "https://pusatfilm21.vip"
override var mainUrl = "https://pf21.vip"
override var name = "Pusatfilm"
override val mainPage = mainPageOf(
"film-terbaru/page/%d/" to "Film Terbaru",

View File

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

View File

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

View File

@ -8,8 +8,8 @@ import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
class LayarKacaProvider : MainAPI() {
override var mainUrl = "https://tv7.lk21official.wiki"
private var seriesUrl = "https://tv9.nontondrama.click"
override var mainUrl = "https://tv10.lk21official.wiki"
private var seriesUrl = "https://tv11.nontondrama.click"
override var name = "LayarKaca"
override val hasMainPage = true
override var lang = "id"

View File

@ -36,12 +36,16 @@ open class Streampai : ExtractorApi() {
val res = app.get(url, referer = referer).document
val data = res.selectFirst("script:containsData(player =)")?.data() ?: return
val sources = data.substringAfter("sources: [").substringBefore("]").replace("\'", "\"")
val sources = data.substringAfter("sources: [").substringBefore("]")
.addMarks("src")
.addMarks("type")
.addMarks("size")
.replace("\'", "\"")
val tracks = data.substringAfter("tracks: [").substringBefore("]")
.replace("\'", "\"")
tryParseJson<List<Responses>>("[$sources]")?.forEach {
callback.invoke(
ExtractorLink(
@ -56,6 +60,15 @@ open class Streampai : ExtractorApi() {
)
)
}
tryParseJson<List<Responses>>("[$tracks]")?.forEach {
subtitleCallback.invoke(
SubtitleFile(
fixTitle(it.label ?: return@forEach),
fixUrl(it.src)
)
)
}
}
private fun String.addMarks(str: String): String {
@ -65,6 +78,7 @@ open class Streampai : ExtractorApi() {
data class Responses(
@JsonProperty("src") val src: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("size") val size: Int?
)

View File

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

View File

@ -11,7 +11,7 @@ import java.net.URI
open class Movierulzhd : MainAPI() {
override var mainUrl = "https://movierulzhd.dog"
override var mainUrl = "https://movierulzhd.party"
var directUrl = ""
override var name = "Movierulzhd"
override val hasMainPage = true

View File

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

View File

@ -1,12 +1,8 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
open class Mitedrive : ExtractorApi() {
override val name = "Mitedrive"
@ -79,4 +75,21 @@ open class Berkasdrive : ExtractorApi() {
}
}
open class Videogami : ExtractorApi() {
override val name = "Videogami"
override val mainUrl = "https://video.nimegami.id"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val id = base64Decode(url.substringAfter("url=")).substringAfterLast("/")
loadExtractor("https://hxfile.co/embed-$id.html", "$mainUrl/", subtitleCallback, callback)
}
}

View File

@ -81,7 +81,7 @@ class Nimegami : MainAPI() {
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..2) {
val res = app.get("$mainUrl/page/$i/?s=gintama&post_type=post").document.select("div.archive article")
val res = app.get("$mainUrl/page/$i/?s=$query&post_type=post").document.select("div.archive article")
.mapNotNull {
it.toSearchResult()
}

View File

@ -12,5 +12,6 @@ class NimegamiPlugin: Plugin() {
registerMainAPI(Nimegami())
registerExtractorAPI(Mitedrive())
registerExtractorAPI(Berkasdrive())
registerExtractorAPI(Videogami())
}
}

View File

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

View File

@ -27,10 +27,7 @@ open class Qiwi : ExtractorApi() {
"$mainUrl/",
getIndexQuality(title),
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors",
)
)
)
@ -42,39 +39,4 @@ open class Qiwi : ExtractorApi() {
?: Qualities.Unknown.value
}
}
open class Mediafire : ExtractorApi() {
override val name = "Mediafire"
override val mainUrl = "https://www.mediafire.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url, referer = referer).document
val title = res.select("div.dl-btn-label").text()
val video = res.selectFirst("a#downloadButton")?.attr("href")
callback.invoke(
ExtractorLink(
this.name,
this.name,
video ?: return,
"",
getQuality(title),
INFER_TYPE
)
)
}
private fun getQuality(str: String?): Int {
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
}

View File

@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
class OploverzProvider : MainAPI() {
override var mainUrl = "https://oploverz.cool"
override var mainUrl = "https://oploverz.bio"
override var name = "Oploverz"
override val hasMainPage = true
override var lang = "id"

View File

@ -11,6 +11,5 @@ class OploverzProviderPlugin: Plugin() {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(OploverzProvider())
registerExtractorAPI(Qiwi())
registerExtractorAPI(Mediafire())
}
}

View File

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

View File

@ -3,7 +3,7 @@ package com.hexated
import com.lagradost.cloudstream3.TvType
class Cgvindo : RebahinProvider() {
override var mainUrl = "http://198.54.124.245"
override var mainUrl = "http://cgvindo.lol"
override var name = "Cgvindo"
}

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 218
version = 220
android {
defaultConfig {
@ -9,7 +9,7 @@ android {
properties.load(project.rootProject.file("local.properties").inputStream())
buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"")
buildConfigField("String", "OMOVIES_API", "\"${properties.getProperty("OMOVIES_API")}\"")
buildConfigField("String", "GHOSTX_API", "\"${properties.getProperty("GHOSTX_API")}\"")
buildConfigField("String", "CINEMATV_API", "\"${properties.getProperty("CINEMATV_API")}\"")
buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"")
buildConfigField("String", "ZSHOW_API", "\"${properties.getProperty("ZSHOW_API")}\"")

View File

@ -436,9 +436,9 @@ class Streamwish : Filesim() {
override var mainUrl = "https://streamwish.to"
}
class Wishfast : Filesim() {
override val name = "Wishfast"
override var mainUrl = "https://wishfast.top"
class UqloadsXyz : Filesim() {
override val name = "Uqloads"
override var mainUrl = "https://uqloads.xyz"
}
class FilelionsTo : Filesim() {

View File

@ -1,7 +1,6 @@
package com.hexated
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
@ -125,8 +124,8 @@ object SoraExtractor : SoraStream() {
httpsify(srcrcp),
referer = iframedoc
).document.selectFirst("script:containsData(Playerjs)")?.data()
val video = script?.substringAfter("file:\"#2")?.substringBefore("\"")
?.replace(Regex("/.*?=?="), "")?.let { base64Decode(it) }
val video = script?.substringAfter("file:\"#9")?.substringBefore("\"")
?.replace(Regex("/@#@\\S+?=?="), "")?.let { base64Decode(it) }
callback.invoke(
ExtractorLink(
@ -136,66 +135,6 @@ object SoraExtractor : SoraStream() {
)
}
suspend fun invokeDbgo(
id: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val iframeDbgo: String?
val script = if (season == null) {
val doc = app.get("$dbgoAPI/imdb.php?id=$id").document
iframeDbgo = doc.select("div.myvideo iframe").attr("src")
app.get(iframeDbgo, referer = "$dbgoAPI/").document.select("script")
.find { it.data().contains("CDNplayerConfig =") }?.data()
} else {
val doc = app.get("$dbgoAPI/tv-imdb.php?id=$id&s=$season").document
iframeDbgo = doc.select("div.myvideo iframe").attr("src")
val token = app.get(
iframeDbgo,
referer = "$dbgoAPI/"
).document.selectFirst("select#translator-name option")?.attr("data-token")
app.get("https://voidboost.net/serial/$token/iframe?s=$season&e=$episode&h=dbgo.fun").document.select(
"script"
).find { it.data().contains("CDNplayerConfig =") }?.data()
} ?: return
val source =
Regex("['|\"]file['|\"]:\\s['|\"](#\\S+?)['|\"]").find(script)?.groupValues?.get(1)
?: return
val subtitle =
Regex("['|\"]subtitle['|\"]:\\s['|\"](\\S+?)['|\"]").find(script)?.groupValues?.get(1)
val ref = getBaseUrl(iframeDbgo)
decryptStreamUrl(source).split(",").map { links ->
val quality = Regex("\\[(\\d*p.*?)]").find(links)?.groupValues?.getOrNull(1)?.trim()
?: return@map null
links.replace("[$quality]", "").split(" or ").map { it.trim() }.map { link ->
val name = if (link.contains(".m3u8")) "Dbgo (Main)" else "Dbgo (Backup)"
callback.invoke(
ExtractorLink(
name,
name,
link,
"$ref/",
getQuality(quality),
isM3u8 = link.contains(".m3u8"),
headers = mapOf("Origin" to ref)
)
)
}
}
subtitle?.split(",")?.map { sub ->
val language = Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1) ?: return@map null
val link = sub.replace("[$language]", "").trim()
subtitleCallback.invoke(SubtitleFile(getDbgoLanguage(language), link))
}
}
suspend fun invokeDreamfilm(
title: String? = null,
season: Int? = null,
@ -1788,21 +1727,21 @@ object SoraExtractor : SoraStream() {
}
suspend fun invokeSmashyStream(
imdbId: String? = null,
tmdbId: Int? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val url = if (season == null) {
"$smashyStreamAPI/playere.php?imdb=$imdbId"
"$smashyStreamAPI/playere.php?tmdb=$tmdbId"
} else {
"$smashyStreamAPI/playere.php?imdb=$imdbId&season=$season&episode=$episode"
"$smashyStreamAPI/playere.php?tmdb=$tmdbId&season=$season&episode=$episode"
}
app.get(
url,
referer = "https://smashystream.com/"
referer = "https://smashystream.xyz/"
).document.select("div#_default-servers a.server").map {
it.attr("data-url") to it.text()
}.apmap {
@ -1810,9 +1749,8 @@ object SoraExtractor : SoraStream() {
"Player F" -> {
invokeSmashyFfix(it.second, it.first, url, subtitleCallback, callback)
}
"Player D (Hindi)" -> {
invokeSmashyD(it.first, url, callback)
"Player SU" -> {
invokeSmashySu(it.second, it.first, url, callback)
}
else -> return@apmap
@ -1821,6 +1759,52 @@ object SoraExtractor : SoraStream() {
}
suspend fun invokeMoflix(
tmdbId: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit
) {
val id = (if(season == null) {
"tmdb|movie|$tmdbId"
} else {
"tmdb|series|$tmdbId"
}).let { base64Encode(it.toByteArray()) }
val loaderUrl = "$moflixAPI/api/v1/titles/$id?loader=titlePage"
val url = if(season == null) {
loaderUrl
} else {
val mediaId = app.get(loaderUrl, referer = "$moflixAPI/").parsedSafe<MoflixResponse>()?.title?.id
"$moflixAPI/api/v1/titles/$mediaId/seasons/$season/episodes/$episode?loader=episodePage"
}
val res = app.get(url, referer = "$moflixAPI/").parsedSafe<MoflixResponse>()
(res?.episode ?: res?.title)?.videos?.filter { it.category.equals("full", true) }?.apmap { iframe ->
val response = app.get(iframe.src ?: return@apmap, referer = "$moflixAPI/")
val host = getBaseUrl(iframe.src)
val doc = response.document.selectFirst("script:containsData(sources:)")?.data()
val script = if (doc.isNullOrEmpty()) {
getAndUnpack(response.text)
} else {
doc
}
val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return@apmap)?.groupValues?.getOrNull(1)
if(m3u8?.haveDub("$host/") == false) return@apmap
callback.invoke(
ExtractorLink(
"Moflix",
"Moflix [${iframe.name}]",
m3u8 ?: return@apmap,
"$host/",
iframe.quality?.filter { it.isDigit() }?.toIntOrNull() ?: Qualities.Unknown.value,
INFER_TYPE
)
)
}
}
//TODO only subs
suspend fun invokeWatchsomuch(
imdbId: String? = null,
@ -2100,11 +2084,11 @@ object SoraExtractor : SoraStream() {
?: return
val ref = getBaseUrl(framesrc)
val id = framesrc.substringAfter("id=").substringBefore("&")
loadExtractor("https://wishfast.top/e/$id", "$ref/", subtitleCallback, callback)
loadExtractor("https://uqloads.xyz/e/$id", "$ref/", subtitleCallback, callback)
}
suspend fun invokeOmovies(
suspend fun invokeGhostx(
title: String? = null,
year: Int? = null,
season: Int? = null,
@ -2117,8 +2101,8 @@ object SoraExtractor : SoraStream() {
season,
episode,
callback,
BuildConfig.OMOVIES_API,
"Omovies",
BuildConfig.GHOSTX_API,
"Ghostx",
base64Decode("X3NtUWFtQlFzRVRi"),
base64Decode("X3NCV2NxYlRCTWFU")
)
@ -2136,7 +2120,7 @@ object SoraExtractor : SoraStream() {
episodeSelector: String,
) {
fun String.decrypt(key: String): List<GpressSources>? {
return tryParseJson<List<GpressSources>>(base64Decode(this).decodePrimewireXor(key))
return tryParseJson<List<GpressSources>>(base64Decode(this).xorDecrypt(key))
}
val slug = getEpisodeSlug(season, episode)
@ -2148,8 +2132,10 @@ object SoraExtractor : SoraStream() {
val savedCookies = mapOf(
base64Decode("X2lkZW50aXR5Z29tb3ZpZXM3") to base64Decode("NTJmZGM3MGIwMDhjMGIxZDg4MWRhYzBmMDFjY2E4MTllZGQ1MTJkZTAxY2M4YmJjMTIyNGVkNGFhZmI3OGI1MmElM0EyJTNBJTdCaSUzQTAlM0JzJTNBMTglM0ElMjJfaWRlbnRpdHlnb21vdmllczclMjIlM0JpJTNBMSUzQnMlM0E1MiUzQSUyMiU1QjIwNTAzNjYlMkMlMjJIblZSUkFPYlRBU09KRXI0NVl5Q004d2lIb2wwVjFrbyUyMiUyQzI1OTIwMDAlNUQlMjIlM0IlN0Q="),
)
val req = app.get("$api/search/$query")
val doc = req.document
var res = app.get("$api/search/$query")
val cookies = savedCookies + res.cookies
val doc = res.document
val media = doc.select("div.$mediaSelector").map {
Triple(it.attr("data-filmName"), it.attr("data-year"), it.select("a").attr("href"))
}.let { el ->
@ -2172,45 +2158,38 @@ object SoraExtractor : SoraStream() {
val iframe = if (season == null) {
media.third
} else {
val res = app.get(fixUrl(media.third, api))
res.document.selectFirst("div#$episodeSelector a:contains(Episode ${slug.second})")
app.get(fixUrl(media.third, api), cookies = cookies)
.document.selectFirst("div#$episodeSelector a:contains(Episode ${slug.second})")
?.attr("href")
} ?: return
val users = if (season == null) {
media.third.substringAfterLast("/") to "0"
} else {
media.third.substringAfterLast("/") to iframe.substringAfterLast("/")
.substringBefore("-")
}
val res = app.get(fixUrl(iframe, api), verify = false)
delay(2000)
val serverUrl = res.document.selectFirst("script:containsData(pushState)")?.data()?.let {
""",\s*'([^']+)""".toRegex().find(it)?.groupValues?.get(1)
} ?: return
val cookies = savedCookies + res.cookies
res = app.get(fixUrl(iframe ?: return, api), cookies = cookies)
val url = res.document.select("meta[property=og:url]").attr("content")
val headers = mapOf("X-Requested-With" to "XMLHttpRequest")
val qualities = intArrayOf(2160, 1440, 1080, 720, 480, 360)
val (serverId, episodeId) = if (season == null) {
url.substringAfterLast("/") to "0"
} else {
url.substringBeforeLast("/").substringAfterLast("/") to url.substringAfterLast("/")
.substringBefore("-")
}
val serverRes = app.get(
"$api/user/servers/${users.first}?ep=${users.second}",
"$api/user/servers/$serverId?ep=$episodeId",
cookies = cookies,
referer = url,
headers = headers
)
val unpack = getAndUnpack(serverRes.text)
val key = unpack.substringAfter("(key=").substringBefore(")")
val key2 = unpack.substringAfter("<\"").substringBefore("\".")
serverRes.document.select("ul li").amap { el ->
).document
serverRes.select("ul li").apmap { el ->
val server = el.attr("data-value")
val encryptedData = app.get(
"${fixUrl(serverUrl, api)}?server=$server&_=$unixTimeMS",
"$url?server=$server&_=$unixTimeMS",
cookies = cookies,
referer = url,
headers = headers
).text
val links = encryptedData.decrypt(key) ?: encryptedData.decrypt(key2) ?: return@amap
links.forEach { video ->
val links = encryptedData.decrypt(base64Decode("MTEx"))
links?.forEach { video ->
qualities.filter { it <= video.max.toInt() }.forEach {
callback(
ExtractorLink(
@ -2291,43 +2270,23 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val id = imdbId?.removePrefix("tt")
val slug = title.createSlug()
val url = if (season == null) {
"$cinemaTvAPI/movies/play/$id-$slug-$year"
val media = app.get("$cinemaTvAPI/v1/${if (season == null) "movies" else "shows"}?filters[q]=$title")
.parsedSafe<CinemaTvResponse>()?.items?.find {
it.imdb_id?.removePrefix("tt")
.equals(imdbId?.removePrefix("tt")) || (it.title.equals(
title,
true
) && it.year == year)
} ?: return
val mediaId = if (season == null) {
media.id_movie
} else {
"$cinemaTvAPI/shows/play/$id-$slug-$year"
}
app.get("$cinemaTvAPI/v1/shows?expand=episodes&id=${media.id_show}")
.parsedSafe<CinemaTvResponse>()?.episodes?.find { it.episode == episode && it.season == season }?.id
} ?: return
val session =
"PHPSESSID=ngr4cudjrimdnhkth30ssohs0n; _csrf=a6ffd7bb7654083fce6df528225a238d0e85aa1fb885dc7638c1259ec1ba0d5ca%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22_csrf%22%3Bi%3A1%3Bs%3A32%3A%22mTTLiDLjxohs-CpKk0bjRH3HdYMB9uBV%22%3B%7D; _ga=GA1.1.1195498587.1701871187; _ga_VZD7HJ3WK6=GS1.1.$unixTime.4.0.1.$unixTime.0.0.0"
val headers = mapOf(
"Cookie" to session,
"x-requested-with" to "com.wwcinematv",
)
val doc = app.get(url, headers = headers).document
val script = doc.selectFirst("script:containsData(hash:)")?.data()
val hash = Regex("hash:\\s*['\"](\\S+)['\"]").find(script ?: return)?.groupValues?.get(1)
val expires = Regex("expires:\\s*(\\d+)").find(script)?.groupValues?.get(1)
val episodeId = (if (season == null) {
"""id_movie:\s*(\d+)"""
} else {
"""episode:\s*['"]$episode['"],[\n\s]+id_episode:\s*(\d+),[\n\s]+season:\s*['"]$season['"]"""
}).let { it.toRegex().find(script)?.groupValues?.get(1) }
val videoUrl = if (season == null) {
"$cinemaTvAPI/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires"
} else {
"$cinemaTvAPI/api/v1/security/episode-access?id_episode=$episodeId&hash=$hash&expires=$expires"
}
val sources = app.get(
videoUrl,
referer = url,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsedSafe<CinemaTvResponse>()
val sources = app.get("$cinemaTvAPI/v1/${if (season == null) "movies" else "episodes"}/view?expand=streams,subtitles&id=$mediaId").parsedSafe<CinemaTvResponse>()
sources?.streams?.mapKeys { source ->
callback.invoke(
@ -2335,19 +2294,18 @@ object SoraExtractor : SoraStream() {
"CinemaTv",
"CinemaTv",
source.value,
"$cinemaTvAPI/",
"",
getQualityFromName(source.key),
true
)
)
}
sources?.subtitles?.map { sub ->
val file = sub.file.toString()
sources?.subtitles?.map {
subtitleCallback.invoke(
SubtitleFile(
sub.language ?: return@map,
if (file.startsWith("[")) return@map else fixUrl(file, cinemaTvAPI),
it.language ?: return@map,
fixUrl(it.url ?: return@map, cinemaTvAPI)
)
)
}
@ -2410,37 +2368,25 @@ object SoraExtractor : SoraStream() {
suspend fun invokeRidomovies(
tmdbId: Int? = null,
imdbId: String? = null,
title: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val slug = if (season == null) {
app.get("$ridomoviesAPI/core/api/search?q=$imdbId")
.parsedSafe<RidoSearch>()?.data?.items?.find {
it.contentable?.tmdbId == tmdbId || it.contentable?.imdbId == imdbId
}?.slug
} else {
app.get("$ridomoviesAPI/tv/${title.createSlug()}/season-$season/episode-$episode").text.substringAfterLast(
"""postid\":\""""
).substringBefore("""\"""")
} ?: return
val url = if (season == null) {
"$ridomoviesAPI/core/api/movies/$slug/videos"
} else {
"$ridomoviesAPI/core/api/episodes/$slug/videos"
}
val mediaSlug = app.get("$ridomoviesAPI/core/api/search?q=$imdbId").parsedSafe<RidoSearch>()?.data?.items?.find {
it.contentable?.tmdbId == tmdbId || it.contentable?.imdbId == imdbId
}?.slug ?: return
val id = season?.let {
val episodeUrl = "$ridomoviesAPI/tv/$mediaSlug/season-$it/episode-$episode"
app.get(episodeUrl).text.substringAfterLast("""postid\":\"""").substringBefore("""\""")
} ?: mediaSlug
val url = "$ridomoviesAPI/core/api/${if (season == null) "movies" else "episodes"}/$id/videos"
app.get(url).parsedSafe<RidoResponses>()?.data?.apmap { link ->
val iframe = Jsoup.parse(link.url ?: return@apmap).select("iframe").attr("data-src")
if (iframe.startsWith("https://closeload.top")) {
val unpacked =
getAndUnpack(
app.get(
iframe,
referer = "$ridomoviesAPI/"
).text
)
val unpacked = getAndUnpack(app.get(iframe, referer = "$ridomoviesAPI/").text)
val video = Regex("=\"(aHR.*?)\";").find(unpacked)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(

View File

@ -47,6 +47,23 @@ data class AniwaveServer(
}
}
data class MoflixResponse(
@JsonProperty("title") val title: Episode? = null,
@JsonProperty("episode") val episode: Episode? = null,
) {
data class Episode(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("videos") val videos: ArrayList<Videos>? = arrayListOf(),
) {
data class Videos(
@JsonProperty("name") val name: String? = null,
@JsonProperty("category") val category: String? = null,
@JsonProperty("src") val src: String? = null,
@JsonProperty("quality") val quality: String? = null,
)
}
}
data class AniMedia(
@JsonProperty("id") var id: Int? = null,
@JsonProperty("idMal") var idMal: Int? = null
@ -174,15 +191,31 @@ data class JikanResponse(
@JsonProperty("data") val data: JikanData? = null,
)
data class CinemaTvSubtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("file") val file: Any? = null,
)
data class CinemaTvResponse(
@JsonProperty("items") val items: ArrayList<Items>? = arrayListOf(),
@JsonProperty("episodes") val episodes: ArrayList<Episodes>? = arrayListOf(),
@JsonProperty("streams") val streams: HashMap<String, String>? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<CinemaTvSubtitles>? = arrayListOf(),
)
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
) {
data class Items(
@JsonProperty("id_movie") val id_movie: Int? = null,
@JsonProperty("id_show") val id_show: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("imdb_id") val imdb_id: String? = null,
)
data class Episodes(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("episode") val episode: Int? = null,
)
data class Subtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("url") val url: String? = null,
)
}
data class VidsrctoResult(
@JsonProperty("id") val id: String? = null,
@ -427,15 +460,6 @@ data class SmashySources(
@JsonProperty("subtitleUrls") var subtitleUrls: String? = null,
)
data class SmashyDSources(
@JsonProperty("sourceUrls") var sourceUrls: ArrayList<SmashyDSourcesUrls>? = arrayListOf(),
)
data class SmashyDSourcesUrls(
@JsonProperty("file") var file: String? = null,
@JsonProperty("title") var title: String? = null,
)
data class AoneroomResponse(
@JsonProperty("data") val data: Data? = null,
) {

View File

@ -6,7 +6,6 @@ import com.hexated.SoraExtractor.invokeAllMovieland
import com.hexated.SoraExtractor.invokeAnimes
import com.hexated.SoraExtractor.invokeAoneroom
import com.hexated.SoraExtractor.invokeBollyMaza
import com.hexated.SoraExtractor.invokeDbgo
import com.hexated.SoraExtractor.invokeFilmxy
import com.hexated.SoraExtractor.invokeKimcartoon
import com.hexated.SoraExtractor.invokeVidSrc
@ -36,15 +35,14 @@ import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeHdmovies4u
import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokeSFMovies
import com.hexated.SoraExtractor.invokeShowflix
import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVegamovies
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeMMovies
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeMoflix
import com.hexated.SoraExtractor.invokeGhostx
import com.hexated.SoraExtractor.invokeWatchCartoon
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZshow
@ -119,6 +117,7 @@ open class SoraStream : TmdbProvider() {
const val aoneroomAPI = "https://api3.aoneroom.com"
const val mMoviesAPI = "https://multimovies.uno"
const val watchCartoonAPI = "https://www1.watchcartoononline.bz"
const val moflixAPI = "https://moflix-stream.xyz"
const val fdMoviesAPI = "https://freedrivemovie.com"
const val uhdmoviesAPI = "https://uhdmovies.zip"
@ -384,9 +383,6 @@ open class SoraStream : TmdbProvider() {
{
invokeVidSrc(res.id, res.season, res.episode, callback)
},
{
invokeDbgo(res.imdbId, res.season, res.episode, subtitleCallback, callback)
},
{
if (!res.isAnime) invokeAoneroom(
res.title, res.airedYear
@ -466,7 +462,7 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeOmovies(
if (!res.isAnime) invokeGhostx(
res.title,
res.year,
res.season,
@ -547,7 +543,7 @@ open class SoraStream : TmdbProvider() {
},
{
if (!res.isAnime) invokeSmashyStream(
res.imdbId,
res.id,
res.season,
res.episode,
subtitleCallback,
@ -587,7 +583,6 @@ open class SoraStream : TmdbProvider() {
if (!res.isAnime) invokeRidomovies(
res.id,
res.imdbId,
res.title,
res.season,
res.episode,
subtitleCallback,
@ -716,13 +711,7 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeSFMovies(
res.id, res.title, res.airedYear
?: res.year, res.season, res.episode, callback
)
},
{
invokeMMovies(res.title, res.season, res.episode, subtitleCallback, callback)
if (!res.isAnime) invokeMoflix(res.id, res.season, res.episode, callback)
},
)

View File

@ -4,7 +4,6 @@ import com.hexated.SoraExtractor.invoke2embed
import com.hexated.SoraExtractor.invokeAllMovieland
import com.hexated.SoraExtractor.invokeAnimes
import com.hexated.SoraExtractor.invokeAoneroom
import com.hexated.SoraExtractor.invokeDbgo
import com.hexated.SoraExtractor.invokeDoomovies
import com.hexated.SoraExtractor.invokeDramaday
import com.hexated.SoraExtractor.invokeDreamfilm
@ -24,13 +23,12 @@ import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokeSFMovies
import com.hexated.SoraExtractor.invokeShowflix
import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeMMovies
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeMoflix
import com.hexated.SoraExtractor.invokeGhostx
import com.hexated.SoraExtractor.invokeWatchCartoon
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZshow
@ -52,6 +50,9 @@ class SoraStreamLite : SoraStream() {
val res = AppUtils.parseJson<LinkData>(data)
argamap(
{
if (!res.isAnime) invokeMoflix(res.id, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeWatchsomuch(
res.imdbId,
@ -93,9 +94,6 @@ class SoraStreamLite : SoraStream() {
{
invokeVidSrc(res.id, res.season, res.episode, callback)
},
{
invokeDbgo(res.imdbId, res.season, res.episode, subtitleCallback, callback)
},
{
if (!res.isAnime && res.isCartoon) invokeWatchCartoon(
res.title,
@ -137,7 +135,7 @@ class SoraStreamLite : SoraStream() {
)
},
{
if (!res.isAnime) invokeOmovies(
if (!res.isAnime) invokeGhostx(
res.title,
res.year,
res.season,
@ -156,7 +154,7 @@ class SoraStreamLite : SoraStream() {
},
{
if (!res.isAnime) invokeSmashyStream(
res.imdbId,
res.id,
res.season,
res.episode,
subtitleCallback,
@ -226,7 +224,6 @@ class SoraStreamLite : SoraStream() {
if (!res.isAnime) invokeRidomovies(
res.id,
res.imdbId,
res.title,
res.season,
res.episode,
subtitleCallback,
@ -322,15 +319,6 @@ class SoraStreamLite : SoraStream() {
callback
)
},
{
if (!res.isAnime) invokeSFMovies(
res.id, res.title, res.airedYear
?: res.year, res.season, res.episode, callback
)
},
{
invokeMMovies(res.title, res.season, res.episode, subtitleCallback, callback)
},
)
return true

View File

@ -25,7 +25,7 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Streamwish())
registerExtractorAPI(FilelionsTo())
registerExtractorAPI(Embedwish())
registerExtractorAPI(Wishfast())
registerExtractorAPI(UqloadsXyz())
registerExtractorAPI(Uploadever())
registerExtractorAPI(Netembed())
registerExtractorAPI(Flaswish())

View File

@ -18,17 +18,12 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.requestCreator
import kotlinx.coroutines.delay
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import java.math.BigInteger
import java.net.*
@ -38,7 +33,6 @@ import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.IvParameterSpec
@ -427,21 +421,28 @@ suspend fun invokeSmashyFfix(
}
suspend fun invokeSmashyD(
suspend fun invokeSmashySu(
name: String,
url: String,
ref: String,
callback: (ExtractorLink) -> Unit,
) {
val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest"))
.parsedSafe<SmashyDSources>()
json?.sourceUrls?.apmap {
M3u8Helper.generateM3u8(
"Smashy [Player D ${it.title}]",
it.file ?: return@apmap,
""
).forEach(callback)
.parsedSafe<SmashySources>()
json?.sourceUrls?.firstOrNull()?.removeSuffix(",")?.split(",")?.forEach { links ->
val quality = Regex("\\[(\\S+)]").find(links)?.groupValues?.getOrNull(1) ?: return@forEach
val trimmedLink = links.removePrefix("[$quality]").trim()
callback.invoke(
ExtractorLink(
"Smashy [$name]",
"Smashy [$name]",
trimmedLink,
"",
getQualityFromName(quality),
INFER_TYPE
)
)
}
}
suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<String?, Int?> {
@ -800,6 +801,10 @@ suspend fun getCrunchyrollIdFromMalSync(aniId: String?): String? {
?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1)
}
suspend fun String.haveDub(referer: String) : Boolean {
return app.get(this,referer=referer).text.contains("TYPE=AUDIO")
}
suspend fun convertTmdbToAnimeId(
title: String?,
date: String?,
@ -1034,7 +1039,7 @@ fun decodeIndexJson(json: String): String {
return base64Decode(slug.substring(0, slug.length - 20))
}
fun String.decodePrimewireXor(key: String): String {
fun String.xorDecrypt(key: String): String {
val sb = StringBuilder()
var i = 0
while (i < this.length) {
@ -1060,9 +1065,9 @@ fun vidsrctoDecrypt(text: String): String {
}
fun String?.createSlug(): String? {
return this?.replace(Regex("[^\\w\\s-]"), "")
?.replace(" ", "-")
?.replace(Regex("( )|( -)|(- )|(--)"), "-")
return this?.filter { it.isWhitespace() || it.isLetterOrDigit() }
?.trim()
?.replace("\\s+".toRegex(), "-")
?.lowercase()
}
@ -1148,14 +1153,6 @@ fun getVipLanguage(str: String): String {
}
}
fun getDbgoLanguage(str: String): String {
return when (str) {
"Русский" -> "Russian"
"Українська" -> "Ukrainian"
else -> str
}
}
fun fixCrunchyrollLang(language: String?): String? {
return SubtitleHelper.fromTwoLettersToLanguage(language ?: return null)
?: SubtitleHelper.fromTwoLettersToLanguage(language.substringBefore("-"))
@ -1212,37 +1209,6 @@ fun base64DecodeAPI(api: String): String {
return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("")
}
fun decryptStreamUrl(data: String): String {
fun getTrash(arr: List<String>, item: Int): List<String> {
val trash = ArrayList<List<String>>()
for (i in 1..item) {
trash.add(arr)
}
return trash.reduce { acc, list ->
val temp = ArrayList<String>()
acc.forEach { ac ->
list.forEach { li ->
temp.add(ac.plus(li))
}
}
return@reduce temp
}
}
val trashList = listOf("@", "#", "!", "^", "$")
val trashSet = getTrash(trashList, 2) + getTrash(trashList, 3)
var trashString = data.replace("#2", "").split("//_//").joinToString("")
trashSet.forEach {
val temp = base64Encode(it.toByteArray())
trashString = trashString.replace(temp, "")
}
return base64Decode(trashString)
}
fun fixUrl(url: String, domain: String): String {
if (url.startsWith("http")) {
return url

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 1
version = 3
android {
defaultConfig {

View File

@ -1,7 +1,9 @@
package com.hexated
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.*
import java.net.URL
object Extractors : Superstream() {
@ -66,45 +68,44 @@ object Extractors : Superstream() {
callback: (ExtractorLink) -> Unit,
) {
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
val shareKey = app.get(
"$fourthAPI/index/share_link?id=${mediaId}&type=$type"
).parsedSafe<ExternalResponse>()?.data?.link?.substringAfterLast("/")
val shareKey = app.get("$fourthAPI/index/share_link?id=${mediaId}&type=$type")
.parsedSafe<ExternalResponse>()?.data?.link?.substringAfterLast("/") ?: return
val headers = mapOf("Accept-Language" to "en")
val shareRes = app.get(
"$thirdAPI/file/file_share_list?share_key=${shareKey ?: return}",
headers = headers
).parsedSafe<ExternalResponse>()?.data
val shareRes = app.get("$thirdAPI/file/file_share_list?share_key=$shareKey", headers = headers)
.parsedSafe<ExternalResponse>()?.data ?: return
val fids = if (season == null) {
shareRes?.file_list
shareRes.file_list
} else {
val parentId =
shareRes?.file_list?.find { it.file_name.equals("season $season", true) }?.fid
app.get(
"$thirdAPI/file/file_share_list?share_key=$shareKey&parent_id=$parentId&page=1",
headers = headers
).parsedSafe<ExternalResponse>()?.data?.file_list?.filter {
it.file_name?.contains(
"s${seasonSlug}e${episodeSlug}",
true
) == true
}
}
val parentId = shareRes.file_list?.find { it.file_name.equals("season $season", true) }?.fid
app.get("$thirdAPI/file/file_share_list?share_key=$shareKey&parent_id=$parentId&page=1", headers = headers)
.parsedSafe<ExternalResponse>()?.data?.file_list?.filter {
it.file_name?.contains("s${seasonSlug}e${episodeSlug}", true) == true
}
} ?: return
fids?.apmapIndexed { index, fileList ->
fids.apmapIndexed { index, fileList ->
val player = app.get("$thirdAPI/file/player?fid=${fileList.fid}&share_key=$shareKey").text
val video = """"(https.*?m3u8.*?)"""".toRegex().find(player)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(
"External",
"External [Server ${index + 1}]",
video?.replace("\\/", "/") ?: return@apmapIndexed,
"$thirdAPI/",
getIndexQuality(fileList.file_name),
isM3u8 = true
)
)
val sources = "sources\\s*=\\s*(.*);".toRegex().find(player)?.groupValues?.get(1)
val qualities = "quality_list\\s*=\\s*(.*);".toRegex().find(player)?.groupValues?.get(1)
listOf(sources, qualities).forEach {
AppUtils.tryParseJson<ArrayList<ExternalSources>>(it)?.forEach org@{ source ->
val format = if (source.type == "video/mp4") ExtractorLinkType.VIDEO else ExtractorLinkType.M3U8
val label = if (format == ExtractorLinkType.M3U8) "Hls" else "Mp4"
if(!(source.label == "AUTO" || format == ExtractorLinkType.VIDEO)) return@org
callback.invoke(
ExtractorLink(
"External",
"External $label [Server ${index + 1}]",
(source.m3u8_url ?: source.file)?.replace("\\/", "/") ?: return@org,
"",
getIndexQuality(if (format == ExtractorLinkType.M3U8) fileList.file_name else source.label),
type = format,
)
)
}
}
}
}

View File

@ -623,7 +623,6 @@ open class Superstream : MainAPI() {
}
}
private data class LinkData(
val id: Int,
val type: Int,
@ -633,7 +632,6 @@ open class Superstream : MainAPI() {
val imdbId: String?,
)
data class LinkDataProp(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("msg") val msg: String? = null,
@ -776,6 +774,13 @@ open class Superstream : MainAPI() {
}
}
data class ExternalSources(
@JsonProperty("m3u8_url") val m3u8_url: String? = null,
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
@JsonProperty("type") val type: String? = null,
)
data class WatchsomuchTorrents(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("movieId") val movieId: Int? = null,

View File

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

View File

@ -4,7 +4,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
import java.net.URI
@ -14,22 +14,22 @@ class TimefourTv : MainAPI() {
override val hasDownloadSupport = false
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Live
TvType.Live
)
private val homePoster =
"https://cdn.discordapp.com/attachments/1109266606292488297/1193060449193840681/Screenshot_2024-01-06_at_12-14-16_Logo_Maker_Used_By_2.3_Million_Startups.png"
"https://cdn.discordapp.com/attachments/1109266606292488297/1193060449193840681/Screenshot_2024-01-06_at_12-14-16_Logo_Maker_Used_By_2.3_Million_Startups.png"
private val detailPoster =
"https://cdn.discordapp.com/attachments/1109266606292488297/1193060448929595454/Screenshot_2024-01-06_at_12-13-02_Logo_Maker_Used_By_2.3_Million_Startups.png"
"https://cdn.discordapp.com/attachments/1109266606292488297/1193060448929595454/Screenshot_2024-01-06_at_12-13-02_Logo_Maker_Used_By_2.3_Million_Startups.png"
override val mainPage = mainPageOf(
"$mainUrl/24-7-channels.php" to "24/7 Channels",
"$mainUrl/schedule/schedule-generated.json" to "Schedule Channels"
"$mainUrl/24-7-channels.php" to "24/7 Channels",
"$mainUrl/schedule/schedule-generated.json" to "Schedule Channels"
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
page: Int,
request: MainPageRequest
): HomePageResponse {
val items = mutableListOf<HomePageList>()
if (request.name == "24/7 Channels") {
@ -46,11 +46,11 @@ class TimefourTv : MainAPI() {
val header = tag.key
val channels = tag.value.mapNotNull {
LiveSearchResponse(
it.key,
Item(it.key, items = it.value.toJson()).toJson(),
this@TimefourTv.name,
TvType.Live,
posterUrl = homePoster,
it.key,
Item(it.key, items = it.value.toJson()).toJson(),
this@TimefourTv.name,
TvType.Live,
posterUrl = homePoster,
)
}
if (channels.isNotEmpty()) items.add(HomePageList(header, channels, true))
@ -64,11 +64,11 @@ class TimefourTv : MainAPI() {
val title = this.select("strong").text()
val href = fixUrl(this.select("a").attr("href"))
return LiveSearchResponse(
title,
Item(title, href).toJson(),
this@TimefourTv.name,
TvType.Live,
posterUrl = homePoster,
title,
Item(title, href).toJson(),
this@TimefourTv.name,
TvType.Live,
posterUrl = homePoster,
)
}
@ -88,18 +88,18 @@ class TimefourTv : MainAPI() {
val items = AppUtils.parseJson<ArrayList<Items>>(data.items)
items.mapNotNull { eps ->
Episode(
data = eps.channels?.toJson() ?: return@mapNotNull null,
name = "${eps.event}${eps.time}",
description = eps.channels.map { it.channel_name }.joinToString(""),
posterUrl = detailPoster,
data = eps.channels?.toJson() ?: return@mapNotNull null,
name = "${eps.event}${eps.time}",
description = eps.channels.map { it.channel_name }.joinToString(""),
posterUrl = detailPoster,
)
}
}
return newTvSeriesLoadResponse(
data.title ?: "",
url,
TvType.TvSeries,
episodes = episodes
data.title ?: "",
url,
TvType.TvSeries,
episodes = episodes
) {
posterUrl = homePoster
}
@ -107,29 +107,34 @@ class TimefourTv : MainAPI() {
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val json = AppUtils.parseJson<ArrayList<Channels>>(data)
json.apmap {
val iframe = app.get(
fixChannelUrl(
it.channel_id ?: return@apmap
)
fixChannelUrl(
it.channel_id ?: return@apmap
)
).document.selectFirst("iframe#thatframe")?.attr("src")
?: throw ErrorLoadingException("No Iframe Found")
?: throw ErrorLoadingException("No Iframe Found")
val host = getBaseUrl(iframe)
val video = extractVideo(iframe)
M3u8Helper.generateM3u8(
callback.invoke(
ExtractorLink(
this.name,
it.channel_name ?: return@apmap,
video ?: return@apmap,
"$host/",
).forEach(callback)
Qualities.Unknown.value,
isM3u8 = true,
)
)
}
return true
@ -138,13 +143,13 @@ class TimefourTv : MainAPI() {
private suspend fun extractVideo(url: String): String? {
val res = app.get(url, referer = mainUrl)
return Regex("""source:['"](\S+.m3u8)['"],""").find(res.text)?.groupValues?.getOrNull(
1
1
) ?: run {
val scriptData =
res.document.selectFirst("div#player")?.nextElementSibling()?.data()
?.substringAfterLast("return(")?.substringBefore(".join")
res.document.selectFirst("div#player")?.nextElementSibling()?.data()
?.substringAfterLast("return(")?.substringBefore(".join")
scriptData?.removeSurrounding("[", "]")?.replace("\"", "")?.split(",")
?.joinToString("")
?.joinToString("")
}
}
@ -163,20 +168,20 @@ class TimefourTv : MainAPI() {
}
data class Item(
val title: String? = null,
val url: String? = null,
val items: String? = null,
val title: String? = null,
val url: String? = null,
val items: String? = null,
)
data class Items(
val time: String? = null,
val event: String? = null,
val channels: ArrayList<Channels>? = arrayListOf(),
val time: String? = null,
val event: String? = null,
val channels: ArrayList<Channels>? = arrayListOf(),
)
data class Channels(
val channel_name: String? = null,
val channel_id: String? = null,
val channel_name: String? = null,
val channel_id: String? = null,
)
}

View File

@ -77,7 +77,7 @@ subprojects {
// https://github.com/recloudstream/cloudstream/blob/master/app/build.gradle
implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc
implementation("com.github.Blatzar:NiceHttp:0.4.4") // http library
implementation("com.github.Blatzar:NiceHttp:0.4.5") // http library
implementation("org.jsoup:jsoup:1.17.2") // html parser
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1")
implementation("io.karn:khttp-android:0.1.2")