Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.lagradost.cloudstream3.extractors

import com.google.gson.Gson
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.newSubtitleFile
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.newExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
import io.ktor.http.Url
import io.ktor.http.decodeURLPart
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

class Geodailymotion : Dailymotion() {
override val name = "GeoDailymotion"
Expand All @@ -28,25 +31,25 @@ open class Dailymotion : ExtractorApi() {
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
callback: (ExtractorLink) -> Unit,
) {
val embedUrl = getEmbedUrl(url) ?: return
val id = getVideoId(embedUrl) ?: return
val metaDataUrl = "$baseUrl/player/metadata/video/$id"

val response = app.get(metaDataUrl, referer = embedUrl).text
val gson = Gson()
val meta = gson.fromJson(response, MetaData::class.java)
val metadataUrl = "$baseUrl/player/metadata/video/$id"

val response = app.get(metadataUrl, referer = embedUrl).text
val meta = parseJson<Metadata>(response)
meta.qualities?.get("auto")?.forEach { quality ->
val videoUrl = quality.url
if (!videoUrl.isNullOrEmpty() && videoUrl.contains(".m3u8")) {
callback.invoke(newExtractorLink(
name,
name,
videoUrl,
ExtractorLinkType.M3U8
))
callback.invoke(
newExtractorLink(
name,
name,
videoUrl,
ExtractorLinkType.M3U8,
)
)
}
}

Expand All @@ -55,7 +58,7 @@ open class Dailymotion : ExtractorApi() {
subtitleCallback(
newSubtitleFile(
subData.label,
subUrl
subUrl,
)
)
}
Expand All @@ -68,6 +71,7 @@ open class Dailymotion : ExtractorApi() {
val videoId = url.substringAfter("video=")
return "$baseUrl/embed/video/$videoId"
}

return null
}

Expand All @@ -77,23 +81,27 @@ open class Dailymotion : ExtractorApi() {
return if (id.matches(videoIdRegex)) id else null
}

data class MetaData(
val qualities: Map<String, List<Quality>>?,
val subtitles: SubtitlesWrapper?
@Serializable
data class Metadata(
@JsonProperty("qualities") @SerialName("qualities") val qualities: Map<String, List<Quality>>?,
@JsonProperty("subtitles") @SerialName("subtitles") val subtitles: SubtitlesWrapper?,
)

@Serializable
data class Quality(
val type: String?,
val url: String?
@JsonProperty("type") @SerialName("type") val type: String?,
@JsonProperty("url") @SerialName("url") val url: String?,
)

@Serializable
data class SubtitlesWrapper(
val enable: Boolean,
val data: Map<String, SubtitleData>?
@JsonProperty("enable") @SerialName("enable") val enable: Boolean,
@JsonProperty("data") @SerialName("data") val data: Map<String, SubtitleData>?,
)

@Serializable
data class SubtitleData(
val label: String,
val urls: List<String>
@JsonProperty("label") @SerialName("label") val label: String,
@JsonProperty("urls") @SerialName("urls") val urls: List<String>,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
package com.lagradost.cloudstream3.extractors

import com.google.gson.JsonParser
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.api.Log
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import io.ktor.http.Url
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

class Techinmind: GDMirrorbot() {
override var name = "Techinmind Cloud AIO"
override var mainUrl = "https://stream.techinmind.space"
override var requiresReferer = true
class Techinmind : GDMirrorbot() {
override val name = "Techinmind Cloud AIO"
override val mainUrl = "https://stream.techinmind.space"
override val requiresReferer = true
}

open class GDMirrorbot : ExtractorApi() {
override var name = "GDMirrorbot"
override var mainUrl = "https://gdmirrorbot.nl"
override val name = "GDMirrorbot"
override val mainUrl = "https://gdmirrorbot.nl"
override val requiresReferer = true

override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
callback: (ExtractorLink) -> Unit,
) {
val (sid, host) = if (!url.contains("key=")) {
Pair(url.substringAfterLast("embed/"), getBaseUrl(app.get(url).url))
Expand All @@ -36,63 +39,58 @@ open class GDMirrorbot : ExtractorApi() {
val idType = Regex("""idType\s*=\s*"([^"]+)"""").find(pageText)?.groupValues?.get(1) ?: "imdbid"
val baseUrl = Regex("""let\s+baseUrl\s*=\s*"([^"]+)"""").find(pageText)?.groupValues?.get(1)
val hostUrl = baseUrl?.let { getBaseUrl(it) }

if (finalId != null && myKey != null) {
val apiUrl = if (url.contains("/tv/")) {
val season = Regex("""/tv/\d+/(\d+)/""").find(url)?.groupValues?.get(1) ?: "1"
val episode = Regex("""/tv/\d+/\d+/(\d+)""").find(url)?.groupValues?.get(1) ?: "1"
"$mainUrl/myseriesapi?tmdbid=$finalId&season=$season&epname=$episode&key=$myKey"
} else {
"$mainUrl/mymovieapi?$idType=$finalId&key=$myKey"
}
} else "$mainUrl/mymovieapi?$idType=$finalId&key=$myKey"
pageText = app.get(apiUrl).text
}

val jsonElement = JsonParser.parseString(pageText)
if (!jsonElement.isJsonObject) return
val jsonObject = jsonElement.asJsonObject

val embedData = tryParseJson<EmbedData>(pageText)
val embedId = url.substringAfterLast("/")
val sidValue = jsonObject["data"]?.asJsonArray
?.takeIf { it.size() > 0 }
?.get(0)?.asJsonObject
?.get("fileslug")?.asString
val sidValue = embedData?.data?.firstOrNull()?.fileSlug
?.takeIf { it.isNotBlank() } ?: embedId

Pair(sidValue, hostUrl)
}

val postData = mapOf("sid" to sid)
val responseText = app.post("$host/embedhelper.php", data = postData).text

val rootElement = JsonParser.parseString(responseText)
if (!rootElement.isJsonObject) return
val root = rootElement.asJsonObject

val siteUrls = root["siteUrls"]?.asJsonObject ?: return
val siteFriendlyNames = root["siteFriendlyNames"]?.asJsonObject

val decodedMresult = when {
root["mresult"]?.isJsonObject == true -> root["mresult"]!!.asJsonObject
root["mresult"]?.isJsonPrimitive == true -> try {
base64Decode(root["mresult"]!!.asString)
.let { JsonParser.parseString(it).asJsonObject }
} catch (e: Exception) {
Log.e("GDMirrorbot", "Failed to decode mresult: $e")
return
val root = tryParseJson<EmbedHelper>(responseText) ?: return
val siteUrls = root.siteUrls ?: return
val siteFriendlyNames = root.siteFriendlyNames

// mresult can arrive as a JSON object or a base64-encoded string
val mresult: Map<String, String>? = run {
val raw = responseText
.substringAfter("\"mresult\":")
.trimStart()
when {
raw.startsWith("\"") -> {
// base64-encoded string
tryParseJson<Map<String, String>>(
try { base64Decode(raw.trim('"')) } catch (_: Exception) { return }
)
}
raw.startsWith("{") -> tryParseJson<Map<String, String>>(
raw.substringBefore("\n}").substringBefore(",\n\"").let { "$it" }
.let { responseText.substringAfter("\"mresult\":").trimStart() }
)
else -> null
}
else -> return
}

siteUrls.keySet().intersect(decodedMresult.keySet()).forEach { key ->
val base = siteUrls[key]?.asString?.trimEnd('/') ?: return@forEach
val path = decodedMresult[key]?.asString?.trimStart('/') ?: return@forEach
if (mresult == null) return
siteUrls.keys.intersect(mresult.keys).forEach { key ->
val base = siteUrls[key]?.trimEnd('/') ?: return@forEach
val path = mresult[key]?.trimStart('/') ?: return@forEach
val fullUrl = "$base/$path"
val friendlyName = siteFriendlyNames?.get(key)?.asString ?: key

val friendlyName = siteFriendlyNames?.get(key) ?: key
try {
when (friendlyName) {
"StreamHG","EarnVids" -> VidHidePro().getUrl(fullUrl, referer, subtitleCallback, callback)
"StreamHG", "EarnVids" -> VidHidePro().getUrl(fullUrl, referer, subtitleCallback, callback)
"RpmShare", "UpnShare", "StreamP2p" -> VidStack().getUrl(fullUrl, referer, subtitleCallback, callback)
else -> loadExtractor(fullUrl, referer ?: mainUrl, subtitleCallback, callback)
}
Expand All @@ -105,5 +103,20 @@ open class GDMirrorbot : ExtractorApi() {
private fun getBaseUrl(url: String): String {
return Url(url).let { "${it.protocol.name}://${it.host}" }
}
}

@Serializable
private data class EmbedData(
@JsonProperty("data") @SerialName("data") val data: List<FileSlug>? = null,
)

@Serializable
private data class FileSlug(
@JsonProperty("fileslug") @SerialName("fileslug") val fileSlug: String? = null,
)

@Serializable
private data class EmbedHelper(
@JsonProperty("siteUrls") @SerialName("siteUrls") val siteUrls: Map<String, String>? = null,
@JsonProperty("siteFriendlyNames") @SerialName("siteFriendlyNames") val siteFriendlyNames: Map<String, String>? = null,
)
}
Loading