diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 528da1bf..e674dd8c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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() diff --git a/core/util/src/main/kotlin/com/flixclusive/core/util/network/JsonHelper.kt b/core/util/src/main/kotlin/com/flixclusive/core/util/network/JsonHelper.kt index c63262b9..2a0f1a77 100644 --- a/core/util/src/main/kotlin/com/flixclusive/core/util/network/JsonHelper.kt +++ b/core/util/src/main/kotlin/com/flixclusive/core/util/network/JsonHelper.kt @@ -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 /** @@ -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 fromJson(json: String): T { - return Gson().fromJson(json, object : TypeToken() {}.type) + return Gson() + .fromJson(json, object : TypeToken() {}.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 fromJson(json: JsonElement): T { + return Gson() + .fromJson(json, object : TypeToken() {}.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 fromJson( + json: String, + serializer: JsonDeserializer +): T { + return GsonBuilder() + .registerTypeAdapter(T::class.java, serializer) + .create() + .fromJson(json, object : TypeToken() {}.type) } diff --git a/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/VidCloud.kt b/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/VidCloud.kt index 8847c18d..004832e9 100644 --- a/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/VidCloud.kt +++ b/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/VidCloud.kt @@ -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 @@ -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(responseBody) - } catch (e: Exception) { - debugLog("!! Source could be an array !!") - debugLog(responseBody) - - throw Exception("Invalid source type. Possibly an array") - } - - var sources = mutableListOf() - - if (upCloudEmbedData.encrypted) { - client.request(luckyAnimalImage).execute() - .use { keyResponse -> - keyResponse.body?.run { - val key = getKey(byteStream()) - sources = fromJson>( - decryptAes(upCloudEmbedData.sources, key) - ) + throw Exception("Cannot fetch source") + + val upCloudEmbedData = fromJson( + json = responseBody, + serializer = VidCloudEmbedDataCustomDeserializer { + client.request(luckyAnimalImage) + .execute() + .use { keyResponse -> + keyResponse.body?.run { + val key = getKey(byteStream()) + fromJson>( + 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()) + } } - } - ) + ) + } } } \ No newline at end of file diff --git a/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/dto/UpCloudEmbedData.kt b/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/dto/UpCloudEmbedData.kt index 01df4e69..6302dad4 100644 --- a/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/dto/UpCloudEmbedData.kt +++ b/extractor/upcloud/src/main/kotlin/com/flixclusive/extractor/upcloud/dto/UpCloudEmbedData.kt @@ -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, - 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, + val tracks: List, + 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 +): JsonDeserializer { + override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): UpCloudEmbedData { + val obj = json.asJsonObject + val tracks = obj.get("tracks").asJsonArray.map { + fromJson(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(it) + } + } + + return UpCloudEmbedData( + sources = sources, + tracks = tracks, + encrypted = encrypted, + server = server, + ) + } } \ No newline at end of file diff --git a/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt b/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt index 365e60a9..d466916e 100644 --- a/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt +++ b/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt @@ -252,5 +252,4 @@ fun SplashScreen( } } } - } \ No newline at end of file