Skip to content

Commit

Permalink
fix: FlixHQ sometimes decrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
rhenwinch committed Feb 25, 2024
1 parent d7c2bc8 commit 59652f7
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 96 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ plugins {
val versionMajor = 1
val versionMinor = 4
val versionPatch = 0
val versionBuild = 0
val versionBuild = 1
val applicationName: String = libs.versions.applicationName.get()
val _applicationId: String = libs.versions.applicationId.get()
val _versionName = "${versionMajor}.${versionMinor}.${versionPatch}-beta" // TODO: Remove beta
val _versionName = "${versionMajor}.${versionMinor}.${versionPatch}-beta${versionBuild}" // TODO: Remove beta

fun Project.getCommitVersion(): String {
val byteOut = ByteArrayOutputStream()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.flixclusive.core.util.network

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.reflect.TypeToken

/**
Expand All @@ -11,5 +14,37 @@ import com.google.gson.reflect.TypeToken
* @throws JsonSyntaxException if the JSON is not well-formed or cannot be parsed.
*/
inline fun <reified T> fromJson(json: String): T {
return Gson().fromJson(json, object : TypeToken<T>() {}.type)
return Gson()
.fromJson(json, object : TypeToken<T>() {}.type)
}

/**
* Parses the specified [JsonElement] into an object of type [T] using Gson library.
*
* @param json The [JsonElement] to parse.
* @return The parsed object of type [T].
* @throws JsonSyntaxException if the JSON is not well-formed or cannot be parsed.
*/
inline fun <reified T> fromJson(json: JsonElement): T {
return Gson()
.fromJson(json, object : TypeToken<T>() {}.type)
}

/**
* Parses the specified JSON string into an object of type [T] using Gson library.
* With additional parameter to add your custom deserializer.
*
* @param json The JSON string to parse.
* @param serializer The JSON deserializer to use for parsing.
* @return The parsed object of type [T].
* @throws JsonSyntaxException if the JSON is not well-formed or cannot be parsed.
*/
inline fun <reified T> fromJson(
json: String,
serializer: JsonDeserializer<T>
): T {
return GsonBuilder()
.registerTypeAdapter(T::class.java, serializer)
.create()
.fromJson(json, object : TypeToken<T>() {}.type)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package com.flixclusive.extractor.upcloud
import com.flixclusive.core.util.coroutines.asyncCalls
import com.flixclusive.core.util.coroutines.mapAsync
import com.flixclusive.core.util.coroutines.mapIndexedAsync
import com.flixclusive.core.util.log.debugLog
import com.flixclusive.core.util.network.CryptographyUtil.decryptAes
import com.flixclusive.core.util.network.fromJson
import com.flixclusive.core.util.network.request
import com.flixclusive.extractor.upcloud.dto.DecryptedSource
import com.flixclusive.extractor.upcloud.dto.UpCloudEmbedData
import com.flixclusive.extractor.upcloud.dto.UpCloudEmbedData.Companion.toSubtitle
import com.flixclusive.extractor.upcloud.dto.VidCloudEmbedDataCustomDeserializer
import com.flixclusive.extractor.upcloud.util.getKey
import com.flixclusive.model.provider.SourceLink
import com.flixclusive.model.provider.Subtitle
Expand Down Expand Up @@ -58,82 +58,75 @@ class VidCloud(

val responseBody = response.body
?.string()
?: throw Exception("Cannot fetch sources")
?: throw Exception("Cannot fetch source")


if(responseBody.isBlank())
throw Exception("Cannot fetch sources")

val upCloudEmbedData: UpCloudEmbedData

try {
upCloudEmbedData = fromJson<UpCloudEmbedData>(responseBody)
} catch (e: Exception) {
debugLog("!! Source could be an array !!")
debugLog(responseBody)

throw Exception("Invalid source type. Possibly an array")
}

var sources = mutableListOf<DecryptedSource>()

if (upCloudEmbedData.encrypted) {
client.request(luckyAnimalImage).execute()
.use { keyResponse ->
keyResponse.body?.run {
val key = getKey(byteStream())
sources = fromJson<MutableList<DecryptedSource>>(
decryptAes(upCloudEmbedData.sources, key)
)
throw Exception("Cannot fetch source")

val upCloudEmbedData = fromJson<UpCloudEmbedData>(
json = responseBody,
serializer = VidCloudEmbedDataCustomDeserializer {
client.request(luckyAnimalImage)
.execute()
.use { keyResponse ->
keyResponse.body?.run {
val key = getKey(byteStream())
fromJson<List<DecryptedSource>>(
decryptAes(it, key)
)
} ?: emptyList()
}
}
}
}
)


check(sources.isNotEmpty())
onLinkLoaded(
SourceLink(
url = sources[0].url,
name = "${getHost(isAlternative)}: " + "Auto"
upCloudEmbedData.run {
check(sources.isNotEmpty())
onLinkLoaded(
SourceLink(
url = sources[0].url,
name = "${getHost(isAlternative)}: " + "Auto"
)
)
)

asyncCalls(
{
sources.mapAsync { source ->
client.request(
url = source.url,
headers = options
).execute().body
?.string()
?.let { data ->
val urls = data
.split('\n')
.filter { line -> line.contains(".m3u8") }

val qualities = data
.split('\n')
.filter { line -> line.contains("RESOLUTION=") }

qualities.mapIndexedAsync { i, s ->
val qualityTag = "${getHost(isAlternative)}: ${s.split('x')[1]}p"
val dataUrl = urls[i]

onLinkLoaded(
SourceLink(
name = qualityTag,
url = dataUrl
asyncCalls(
{
sources.mapAsync { source ->
client.request(
url = source.url,
headers = options
).execute().body
?.string()
?.let { data ->
val urls = data
.split('\n')
.filter { line -> line.contains(".m3u8") }

val qualities = data
.split('\n')
.filter { line -> line.contains("RESOLUTION=") }

qualities.mapIndexedAsync { i, s ->
val qualityTag = "${getHost(isAlternative)}: ${s.split('x')[1]}p"
val dataUrl = urls[i]

onLinkLoaded(
SourceLink(
name = qualityTag,
url = dataUrl
)
)
)
}
}
}
}
},
{
upCloudEmbedData.tracks.mapAsync {
onSubtitleLoaded(it.toSubtitle())
}
},
{
upCloudEmbedData.tracks.mapAsync {
onSubtitleLoaded(it.toSubtitle())
}
}
}
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,59 @@
package com.flixclusive.extractor.upcloud.dto

import com.flixclusive.model.provider.Subtitle
import com.flixclusive.model.provider.SubtitleSource
import com.google.gson.annotations.SerializedName

data class UpCloudEmbedData(
val sources: String,
val tracks: List<UpCloudEmbedSubtitleData>,
val encrypted: Boolean,
val server: Int
) {
data class UpCloudEmbedSubtitleData(
@SerializedName("file") val url: String,
@SerializedName("label") val lang: String,
val kind: String
)

companion object {
fun UpCloudEmbedSubtitleData.toSubtitle() = Subtitle(
url = url,
language = lang,
type = SubtitleSource.ONLINE
)
}
package com.flixclusive.extractor.upcloud.dto

import com.flixclusive.core.util.network.fromJson
import com.flixclusive.model.provider.Subtitle
import com.flixclusive.model.provider.SubtitleSource
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.annotations.SerializedName
import java.lang.reflect.Type

data class UpCloudEmbedData(
val sources: List<DecryptedSource>,
val tracks: List<UpCloudEmbedSubtitleData>,
val encrypted: Boolean,
val server: Int
) {
data class UpCloudEmbedSubtitleData(
@SerializedName("file") val url: String,
@SerializedName("label") val lang: String,
val kind: String
)

companion object {
fun UpCloudEmbedSubtitleData.toSubtitle() = Subtitle(
url = url,
language = lang,
type = SubtitleSource.ONLINE
)
}
}

internal class VidCloudEmbedDataCustomDeserializer(
private val decryptSource: (String) -> List<DecryptedSource>
): JsonDeserializer<UpCloudEmbedData> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): UpCloudEmbedData {
val obj = json.asJsonObject
val tracks = obj.get("tracks").asJsonArray.map {
fromJson<UpCloudEmbedData.UpCloudEmbedSubtitleData>(it)
}
val encrypted = obj.get("encrypted").asBoolean
val server = obj.get("server").asInt

val sources = if (encrypted) {
decryptSource(obj.get("sources").asString)
} else {
obj.get("sources").asJsonArray.map {
fromJson<DecryptedSource>(it)
}
}

return UpCloudEmbedData(
sources = sources,
tracks = tracks,
encrypted = encrypted,
server = server,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,5 +252,4 @@ fun SplashScreen(
}
}
}

}

0 comments on commit 59652f7

Please sign in to comment.