Compare commits

...

4 commits

Author SHA1 Message Date
Luna712
7b3c938ba7
Add more custom serializers for kotlinx serialization (#2978)
This adds numerous other serializers to replicate Jackson mapper configuration behavior, that some extensions will need.
2026-07-12 22:38:08 +00:00
Luna712
15632b864a
[skip ci] TraktProvider: migrate to kotlinx serialization (#2872) 2026-07-12 22:18:22 +00:00
Luna712
505722c1b5
[skip ci] Torrent: migrate to kotlinx serialization (#2882) 2026-07-12 22:09:36 +00:00
firelight
70469f55db
[skip ci] Migration guide (#3058) 2026-07-12 21:43:59 +00:00
13 changed files with 1306 additions and 402 deletions

21
COMPOSE.md Normal file
View file

@ -0,0 +1,21 @@
# Migration guide to Compose
### 1. MVI instead of MVVM
The current design of CloudStream loosely uses the MVVM architecture.
This means that the UI invokes the viewmodel with function calls, and it responds with LiveData fields that are observed. While this has worked, it generates a lot of boilerplate and has created some friction.
To make it easier to work with Compose, the new architecture will be based on MVI. In short this means that the viewmodel exposes a singular immutable class that is observed, and receives all UI events with a singular event that is a sealed class. All the UI should be able to be recreated based on this singular state class, and all interactions should be able to be replayed using only the event callback.
For a more detailed overview, see: https://www.youtube.com/watch?v=b2z1jvD4VMQ
This is part of the effort to make CloudStream cross platform, as it allows us to decouple UI and logic.
### 2. KMP-compatible libraries
We plan to leverage Kotlin's KMP project to compile our code to different architectures. However, this requires us to only use KMP-compatible libraries, no Java. Therefore any pull requests must ensure that they use KMP-compatible libraries only.
### 3. UI Changes
While migrating to the new compose UI, you also have the opportunity to change the UI. However, this should only be to freshen up the UI, not completely redesign it. It is also important to stress that this process should not lose any features of the old UI, and be very conservative with adding new features.

View file

@ -1,157 +0,0 @@
package com.lagradost.cloudstream3.utils.serializers
import android.net.Uri
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KeepGeneratedSerializer
import kotlinx.serialization.Serializable
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
@OptIn(ExperimentalSerializationApi::class)
@KeepGeneratedSerializer
@Serializable(with = NonEmptyData.Serializer::class)
data class NonEmptyData(
val title: String = "",
val tags: List<String> = emptyList(),
val meta: Map<String, String> = emptyMap(),
val name: String = "hello",
) {
object Serializer : NonEmptySerializer<NonEmptyData>(NonEmptyData.generatedSerializer())
}
@OptIn(ExperimentalSerializationApi::class)
@KeepGeneratedSerializer
@Serializable(with = WriteOnlyData.Serializer::class)
data class WriteOnlyData(
val fieldA: String = "",
val fieldB: String = "",
) {
object Serializer : WriteOnlySerializer<WriteOnlyData>(
WriteOnlyData.generatedSerializer(),
setOf("fieldB"),
)
}
@OptIn(ExperimentalSerializationApi::class)
@KeepGeneratedSerializer
@Serializable(with = MultiWriteOnly.Serializer::class)
data class MultiWriteOnly(
val fieldA: String = "",
val fieldB: String = "",
val fieldC: String = "",
) {
object Serializer : WriteOnlySerializer<MultiWriteOnly>(
MultiWriteOnly.generatedSerializer(),
setOf("fieldB", "fieldC"),
)
}
@Serializable
data class UriData(
@Serializable(with = UriSerializer::class)
val uri: Uri = Uri.EMPTY,
)
class SerializerTest {
@Test
fun nonEmptySerializerOmitsEmptyStrings() {
val data = NonEmptyData(title = "", name = "hello")
val result = data.toJson()
assertFalse(result.contains("title"))
assertTrue(result.contains("name"))
}
@Test
fun nonEmptySerializerOmitsEmptyLists() {
val data = NonEmptyData(tags = emptyList(), name = "hello")
val result = data.toJson()
assertFalse(result.contains("tags"))
}
@Test
fun nonEmptySerializerOmitsEmptyMaps() {
val data = NonEmptyData(meta = emptyMap(), name = "hello")
val result = data.toJson()
assertFalse(result.contains("meta"))
}
@Test
fun nonEmptySerializerKeepsNonEmptyFields() {
val data = NonEmptyData(title = "hello", tags = listOf("a"), meta = mapOf("k" to "v"))
val result = data.toJson()
assertTrue(result.contains("title"))
assertTrue(result.contains("tags"))
assertTrue(result.contains("meta"))
}
@Test
fun nonEmptySerializerDoesNotAffectDeserialization() {
val input = """{"title":"hello","tags":["a"],"meta":{"k":"v"},"name":"world"}"""
val result = parseJson<NonEmptyData>(input)
assertEquals("hello", result.title)
assertEquals(listOf("a"), result.tags)
assertEquals(mapOf("k" to "v"), result.meta)
assertEquals("world", result.name)
}
@Test
fun writeOnlySerializerOmitsFieldOnSerialize() {
val data = WriteOnlyData(fieldA = "hello", fieldB = "secret")
val result = data.toJson()
assertTrue(result.contains("fieldA"))
assertFalse(result.contains("fieldB"))
}
@Test
fun writeOnlySerializerDeserializesNormally() {
val input = """{"fieldA":"hello","fieldB":"secret"}"""
val result = parseJson<WriteOnlyData>(input)
assertEquals("hello", result.fieldA)
assertEquals("secret", result.fieldB)
}
@Test
fun writeOnlySerializerDeserializesMissingAsDefault() {
val input = """{"fieldA":"hello"}"""
val result = parseJson<WriteOnlyData>(input)
assertEquals("hello", result.fieldA)
assertEquals("", result.fieldB)
}
@Test
fun writeOnlySerializerHandlesMultipleKeys() {
val data = MultiWriteOnly(fieldA = "hello", fieldB = "secret1", fieldC = "secret2")
val result = data.toJson()
assertTrue(result.contains("fieldA"))
assertFalse(result.contains("fieldB"))
assertFalse(result.contains("fieldC"))
}
@Test
fun uriSerializerSerializesUriToString() {
val data = UriData(uri = Uri.parse("https://example.com/path?query=1"))
val result = data.toJson()
assertTrue(result.contains("https://example.com/path?query=1"))
}
@Test
fun uriSerializerDeserializesStringToUri() {
val input = """{"uri":"https://example.com/path?query=1"}"""
val result = parseJson<UriData>(input)
assertEquals(Uri.parse("https://example.com/path?query=1"), result.uri)
}
@Test
fun uriSerializerRoundtripsCorrectly() {
val data = UriData(uri = Uri.parse("https://example.com/path?query=1"))
val encoded = data.toJson()
val decoded = parseJson<UriData>(encoded)
assertEquals(data.uri, decoded.uri)
}
}

View file

@ -0,0 +1,41 @@
package com.lagradost.cloudstream3.utils.serializers
import android.net.Uri
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@Serializable
data class UriData(
@Serializable(with = UriSerializer::class)
@SerialName("uri") val uri: Uri = Uri.EMPTY,
)
class UriSerializerTest {
@Test
fun uriSerializerSerializesUriToString() {
val data = UriData(uri = Uri.parse("https://example.com/path?query=1"))
val result = data.toJson()
assertTrue(result.contains("https://example.com/path?query=1"))
}
@Test
fun uriSerializerDeserializesStringToUri() {
val input = """{"uri":"https://example.com/path?query=1"}"""
val result = parseJson<UriData>(input)
assertEquals(Uri.parse("https://example.com/path?query=1"), result.uri)
}
@Test
fun uriSerializerRoundtripsCorrectly() {
val data = UriData(uri = Uri.parse("https://example.com/path?query=1"))
val encoded = data.toJson()
val decoded = parseJson<UriData>(encoded)
assertEquals(data.uri, decoded.uri)
}
}

View file

@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.newExtractorLink
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import torrServer.TorrServer
import java.io.File
import java.net.ConnectException
@ -39,7 +41,7 @@ object Torrent {
app.get(
"$TORRENT_SERVER_URL/echo",
).text.isNotEmpty()
} catch (e: ConnectException) {
} catch (_: ConnectException) {
// `Failed to connect to /127.0.0.1:8090` if the server is down
false
} catch (t: Throwable) {
@ -164,9 +166,7 @@ object Torrent {
/** Gets all the metadata of a torrent, will throw if that hash does not exists
* https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/web/api/torrents.go#L126 */
@Throws
suspend fun get(
hash: String,
): TorrentStatus {
suspend fun get(hash: String): TorrentStatus {
if (TORRENT_SERVER_URL.isEmpty()) {
throw ErrorLoadingException("Not initialized")
}
@ -278,94 +278,55 @@ object Torrent {
// https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/web/api/torrents.go#L18
// https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/main/web/api/route.go#L7
@Serializable
data class TorrentRequest(
@JsonProperty("action")
val action: String,
@JsonProperty("hash")
val hash: String = "",
@JsonProperty("link")
val link: String = "",
@JsonProperty("title")
val title: String = "",
@JsonProperty("poster")
val poster: String = "",
@JsonProperty("data")
val data: String = "",
@JsonProperty("save_to_db")
val saveToDB: Boolean = false,
@JsonProperty("action") @SerialName("action") val action: String,
@JsonProperty("hash") @SerialName("hash") val hash: String = "",
@JsonProperty("link") @SerialName("link") val link: String = "",
@JsonProperty("title") @SerialName("title") val title: String = "",
@JsonProperty("poster") @SerialName("poster") val poster: String = "",
@JsonProperty("data") @SerialName("data") val data: String = "",
@JsonProperty("save_to_db") @SerialName("save_to_db") val saveToDB: Boolean = false,
)
// https://github.com/Diegopyl1209/torrentserver-aniyomi/blob/c18f58e51b6738f053261bc863177078aa9c1c98/torr/state/state.go#L33
// omitempty = nullable
@Serializable
data class TorrentStatus(
@JsonProperty("title")
var title: String,
@JsonProperty("poster")
var poster: String,
@JsonProperty("data")
var data: String?,
@JsonProperty("timestamp")
var timestamp: Long,
@JsonProperty("name")
var name: String?,
@JsonProperty("hash")
var hash: String?,
@JsonProperty("stat")
var stat: Int,
@JsonProperty("stat_string")
var statString: String,
@JsonProperty("loaded_size")
var loadedSize: Long?,
@JsonProperty("torrent_size")
var torrentSize: Long?,
@JsonProperty("preloaded_bytes")
var preloadedBytes: Long?,
@JsonProperty("preload_size")
var preloadSize: Long?,
@JsonProperty("download_speed")
var downloadSpeed: Double?,
@JsonProperty("upload_speed")
var uploadSpeed: Double?,
@JsonProperty("total_peers")
var totalPeers: Int?,
@JsonProperty("pending_peers")
var pendingPeers: Int?,
@JsonProperty("active_peers")
var activePeers: Int?,
@JsonProperty("connected_seeders")
var connectedSeeders: Int?,
@JsonProperty("half_open_peers")
var halfOpenPeers: Int?,
@JsonProperty("bytes_written")
var bytesWritten: Long?,
@JsonProperty("bytes_written_data")
var bytesWrittenData: Long?,
@JsonProperty("bytes_read")
var bytesRead: Long?,
@JsonProperty("bytes_read_data")
var bytesReadData: Long?,
@JsonProperty("bytes_read_useful_data")
var bytesReadUsefulData: Long?,
@JsonProperty("chunks_written")
var chunksWritten: Long?,
@JsonProperty("chunks_read")
var chunksRead: Long?,
@JsonProperty("chunks_read_useful")
var chunksReadUseful: Long?,
@JsonProperty("chunks_read_wasted")
var chunksReadWasted: Long?,
@JsonProperty("pieces_dirtied_good")
var piecesDirtiedGood: Long?,
@JsonProperty("pieces_dirtied_bad")
var piecesDirtiedBad: Long?,
@JsonProperty("duration_seconds")
var durationSeconds: Double?,
@JsonProperty("bit_rate")
var bitRate: String?,
@JsonProperty("file_stats")
var fileStats: List<TorrentFileStat>?,
@JsonProperty("trackers")
var trackers: List<String>?,
@JsonProperty("title") @SerialName("title") var title: String,
@JsonProperty("poster") @SerialName("poster") var poster: String,
@JsonProperty("data") @SerialName("data") var data: String?,
@JsonProperty("timestamp") @SerialName("timestamp") var timestamp: Long,
@JsonProperty("name") @SerialName("name") var name: String?,
@JsonProperty("hash") @SerialName("hash") var hash: String?,
@JsonProperty("stat") @SerialName("stat") var stat: Int,
@JsonProperty("stat_string") @SerialName("stat_string") var statString: String,
@JsonProperty("loaded_size") @SerialName("loaded_size") var loadedSize: Long?,
@JsonProperty("torrent_size") @SerialName("torrent_size") var torrentSize: Long?,
@JsonProperty("preloaded_bytes") @SerialName("preloaded_bytes") var preloadedBytes: Long?,
@JsonProperty("preload_size") @SerialName("preload_size") var preloadSize: Long?,
@JsonProperty("download_speed") @SerialName("download_speed") var downloadSpeed: Double?,
@JsonProperty("upload_speed") @SerialName("upload_speed") var uploadSpeed: Double?,
@JsonProperty("total_peers") @SerialName("total_peers") var totalPeers: Int?,
@JsonProperty("pending_peers") @SerialName("pending_peers") var pendingPeers: Int?,
@JsonProperty("active_peers") @SerialName("active_peers") var activePeers: Int?,
@JsonProperty("connected_seeders") @SerialName("connected_seeders") var connectedSeeders: Int?,
@JsonProperty("half_open_peers") @SerialName("half_open_peers") var halfOpenPeers: Int?,
@JsonProperty("bytes_written") @SerialName("bytes_written") var bytesWritten: Long?,
@JsonProperty("bytes_written_data") @SerialName("bytes_written_data") var bytesWrittenData: Long?,
@JsonProperty("bytes_read") @SerialName("bytes_read") var bytesRead: Long?,
@JsonProperty("bytes_read_data") @SerialName("bytes_read_data") var bytesReadData: Long?,
@JsonProperty("bytes_read_useful_data") @SerialName("bytes_read_useful_data") var bytesReadUsefulData: Long?,
@JsonProperty("chunks_written") @SerialName("chunks_written") var chunksWritten: Long?,
@JsonProperty("chunks_read") @SerialName("chunks_read") var chunksRead: Long?,
@JsonProperty("chunks_read_useful") @SerialName("chunks_read_useful") var chunksReadUseful: Long?,
@JsonProperty("chunks_read_wasted") @SerialName("chunks_read_wasted") var chunksReadWasted: Long?,
@JsonProperty("pieces_dirtied_good") @SerialName("pieces_dirtied_good") var piecesDirtiedGood: Long?,
@JsonProperty("pieces_dirtied_bad") @SerialName("pieces_dirtied_bad") var piecesDirtiedBad: Long?,
@JsonProperty("duration_seconds") @SerialName("duration_seconds") var durationSeconds: Double?,
@JsonProperty("bit_rate") @SerialName("bit_rate") var bitRate: String?,
@JsonProperty("file_stats") @SerialName("file_stats") var fileStats: List<TorrentFileStat>?,
@JsonProperty("trackers") @SerialName("trackers") var trackers: List<String>?,
) {
fun streamUrl(url: String): String {
val fileName =
@ -381,12 +342,10 @@ object Torrent {
}
}
@Serializable
data class TorrentFileStat(
@JsonProperty("id")
val id: Int?,
@JsonProperty("path")
val path: String?,
@JsonProperty("length")
val length: Long?,
@JsonProperty("id") @SerialName("id") val id: Int?,
@JsonProperty("path") @SerialName("path") val path: String?,
@JsonProperty("length") @SerialName("length") val length: Long?,
)
}

View file

@ -5831,6 +5831,7 @@ public class com/lagradost/cloudstream3/metaproviders/TraktProvider : com/lagrad
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5847,7 +5848,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs {
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Airs$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/lang/Long;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/lang/Long;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5868,7 +5885,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast {
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Cast;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Cast$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data$Companion;
public fun <init> ()V
public fun <init> (Lcom/lagradost/cloudstream3/TvType;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V
public synthetic fun <init> (Lcom/lagradost/cloudstream3/TvType;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5883,7 +5916,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data {
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Data;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Data$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5906,7 +5955,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids {
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Ids$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images$Companion;
public fun <init> ()V
public fun <init> (Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;)V
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5933,7 +5998,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Images$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V
public synthetic fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -5994,10 +6075,26 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkDa
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$LinkData$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component10 ()Ljava/lang/String;
public final fun component11 ()Ljava/lang/String;
@ -6016,7 +6113,7 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD
public final fun component23 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;
public final fun component24 ()Ljava/lang/String;
public final fun component25 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;
public final fun component26 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public final fun component26 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;
public final fun component3 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Ljava/lang/String;
@ -6024,8 +6121,8 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD
public final fun component7 ()Ljava/lang/Integer;
public final fun component8 ()Ljava/lang/String;
public final fun component9 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public fun equals (Ljava/lang/Object;)Z
public final fun getAiredEpisodes ()Ljava/lang/Integer;
public final fun getAirs ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Airs;
@ -6040,7 +6137,7 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD
public final fun getImages ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;
public final fun getLanguage ()Ljava/lang/String;
public final fun getLanguages ()Ljava/util/List;
public final fun getMedia ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public final fun getMedia ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;
public final fun getNetwork ()Ljava/lang/String;
public final fun getOverview ()Ljava/lang/String;
public final fun getRating ()Ljava/lang/Double;
@ -6057,7 +6154,60 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaD
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaDetails$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/Integer;
public final fun component3 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;
public final fun component4 ()Ljava/lang/Double;
public final fun component5 ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;
public final fun copy (Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;
public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;Ljava/lang/String;Ljava/lang/Integer;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Ljava/lang/Double;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILjava/lang/Object;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;
public fun equals (Ljava/lang/Object;)Z
public final fun getIds ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;
public final fun getImages ()Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;
public final fun getRating ()Ljava/lang/Double;
public final fun getTitle ()Ljava/lang/String;
public final fun getYear ()Ljava/lang/Integer;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$MediaSummary$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People$Companion;
public fun <init> ()V
public fun <init> (Ljava/util/List;)V
public synthetic fun <init> (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -6070,7 +6220,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$People$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$People;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$People$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;)V
public synthetic fun <init> (Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -6087,7 +6253,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Person;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Person$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$Companion;
public fun <init> ()V
public fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/util/List;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V
public synthetic fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;Ljava/util/List;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -6124,7 +6306,23 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Season
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$Seasons$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode {
public static final field Companion Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$Companion;
public fun <init> ()V
public fun <init> (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V
public synthetic fun <init> (Ljava/util/List;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Ids;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$Images;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -6165,6 +6363,21 @@ public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktE
public fun toString ()Ljava/lang/String;
}
public final synthetic class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/metaproviders/TraktProvider$TraktEpisode$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class com/lagradost/cloudstream3/mvvm/ArchComponentExtKt {
public static final field DEBUG_EXCEPTION Ljava/lang/String;
public static final field DEBUG_PRINT Ljava/lang/String;

View file

@ -34,6 +34,10 @@ import com.lagradost.cloudstream3.newTvSeriesLoadResponse
import com.lagradost.cloudstream3.newTvSeriesSearchResponse
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
open class TraktProvider : MainAPI() {
override var name = "Trakt"
@ -46,7 +50,6 @@ open class TraktProvider : MainAPI() {
)
private val traktApiUrl = "https://api.trakt.tv"
private val traktClientId: String = BuildConfig.TRAKT_CLIENT_ID
override val mainPage = mainPageOf(
@ -58,16 +61,21 @@ open class TraktProvider : MainAPI() {
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val apiResponse = getApi("${request.data}?extended=full,images&page=$page")
val results = parseJson<List<MediaDetails>>(apiResponse).map { element ->
element.toSearchResponse()
}
return newHomePageResponse(request.name, results)
}
private fun MediaDetails.toSearchResponse(): SearchResponse {
val media = this.media ?: this
val media = this.media ?: MediaSummary(
title = this.title,
year = this.year,
ids = this.ids,
rating = this.rating,
images = this.images,
)
val mediaType = if (media.ids?.tvdb == null) TvType.Movie else TvType.TvSeries
val poster = media.images?.poster?.firstOrNull()
return if (mediaType == TvType.Movie) {
@ -75,7 +83,7 @@ open class TraktProvider : MainAPI() {
name = media.title ?: "",
url = Data(
type = mediaType,
mediaDetails = media,
mediaDetails = this,
).toJson(),
type = TvType.Movie,
) {
@ -87,7 +95,7 @@ open class TraktProvider : MainAPI() {
name = media.title ?: "",
url = Data(
type = mediaType,
mediaDetails = media,
mediaDetails = this,
).toJson(),
type = TvType.TvSeries,
) {
@ -98,9 +106,7 @@ open class TraktProvider : MainAPI() {
}
override suspend fun search(query: String, page: Int): SearchResponseList? {
val apiResponse =
getApi("$traktApiUrl/search/movie,show?extended=full,images&limit=20&page=$page&query=$query")
val apiResponse = getApi("$traktApiUrl/search/movie,show?extended=full,images&limit=20&page=$page&query=$query")
return newSearchResponseList(parseJson<List<MediaDetails>>(apiResponse).map { element ->
element.toSearchResponse()
})
@ -115,9 +121,7 @@ open class TraktProvider : MainAPI() {
val backDropUrl = fixPath(mediaDetails?.images?.fanart?.firstOrNull())
val logoUrl = fixPath(mediaDetails?.images?.logo?.firstOrNull())
val resActor =
getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/people?extended=full,images")
val resActor = getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/people?extended=full,images")
val actors = parseJson<People>(resActor).cast?.map {
ActorData(
Actor(
@ -128,9 +132,7 @@ open class TraktProvider : MainAPI() {
)
}
val resRelated =
getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/related?extended=full,images&limit=20")
val resRelated = getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/related?extended=full,images&limit=20")
val relatedMedia = parseJson<List<MediaDetails>>(resRelated).map { it.toSearchResponse() }
val isCartoon =
@ -142,7 +144,6 @@ open class TraktProvider : MainAPI() {
val uniqueUrl = data.mediaDetails?.ids?.trakt?.toJson() ?: data.toJson()
if (data.type == TvType.Movie) {
val linkData = LinkData(
id = mediaDetails?.ids?.tmdb,
traktId = mediaDetails?.ids?.trakt,
@ -190,7 +191,6 @@ open class TraktProvider : MainAPI() {
addTMDbId(mediaDetails.ids?.tmdb.toString())
}
} else {
val resSeasons =
getApi("$traktApiUrl/shows/${mediaDetails?.ids?.trakt.toString()}/seasons?extended=full,images,episodes")
val episodes = mutableListOf<Episode>()
@ -198,9 +198,7 @@ open class TraktProvider : MainAPI() {
var nextAir: NextAiring? = null
seasons.forEach { season ->
season.episodes?.map { episode ->
val linkData = LinkData(
id = mediaDetails?.ids?.tmdb,
traktId = mediaDetails?.ids?.trakt,
@ -235,7 +233,6 @@ open class TraktProvider : MainAPI() {
this.description = episode.overview
this.runTime = episode.runtime
this.posterUrl = fixPath(episode.images?.screenshot?.firstOrNull())
//this.rating = episode.rating?.times(10)?.roundToInt()
this.score = Score.from10(episode.rating)
this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
@ -307,143 +304,164 @@ open class TraktProvider : MainAPI() {
return "https://$url"
}
@Serializable
data class Data(
val type: TvType? = null,
val mediaDetails: MediaDetails? = null,
@JsonProperty("type") @SerialName("type") val type: TvType? = null,
@JsonProperty("mediaDetails") @SerialName("mediaDetails") val mediaDetails: MediaDetails? = null,
)
@Serializable
data class MediaSummary(
@JsonProperty("title") @SerialName("title") val title: String? = null,
@JsonProperty("year") @SerialName("year") val year: Int? = null,
@JsonProperty("ids") @SerialName("ids") val ids: Ids? = null,
@JsonProperty("rating") @SerialName("rating") val rating: Double? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
)
@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
@Serializable
data class MediaDetails(
@JsonProperty("title") val title: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("tagline") val tagline: String? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("released") val released: String? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("country") val country: String? = null,
@JsonProperty("updatedAt") val updatedAt: String? = null,
@JsonProperty("trailer") val trailer: String? = null,
@JsonProperty("homepage") val homepage: String? = null,
@JsonProperty("status") val status: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("votes") val votes: Long? = null,
@JsonProperty("comment_count") val commentCount: Long? = null,
@JsonProperty("language") val language: String? = null,
@JsonProperty("languages") val languages: List<String>? = null,
@JsonProperty("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("genres") val genres: List<String>? = null,
@JsonProperty("certification") val certification: String? = null,
@JsonProperty("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("airs") val airs: Airs? = null,
@JsonProperty("network") val network: String? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("movie") @JsonAlias("show") val media: MediaDetails? = null
@JsonProperty("title") @SerialName("title") val title: String? = null,
@JsonProperty("year") @SerialName("year") val year: Int? = null,
@JsonProperty("ids") @SerialName("ids") val ids: Ids? = null,
@JsonProperty("tagline") @SerialName("tagline") val tagline: String? = null,
@JsonProperty("overview") @SerialName("overview") val overview: String? = null,
@JsonProperty("released") @SerialName("released") val released: String? = null,
@JsonProperty("runtime") @SerialName("runtime") val runtime: Int? = null,
@JsonProperty("country") @SerialName("country") val country: String? = null,
@JsonProperty("updatedAt") @SerialName("updatedAt") val updatedAt: String? = null,
@JsonProperty("trailer") @SerialName("trailer") val trailer: String? = null,
@JsonProperty("homepage") @SerialName("homepage") val homepage: String? = null,
@JsonProperty("status") @SerialName("status") val status: String? = null,
@JsonProperty("rating") @SerialName("rating") val rating: Double? = null,
@JsonProperty("votes") @SerialName("votes") val votes: Long? = null,
@JsonProperty("comment_count") @SerialName("comment_count") val commentCount: Long? = null,
@JsonProperty("language") @SerialName("language") val language: String? = null,
@JsonProperty("languages") @SerialName("languages") val languages: List<String>? = null,
@JsonProperty("available_translations") @SerialName("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("genres") @SerialName("genres") val genres: List<String>? = null,
@JsonProperty("certification") @SerialName("certification") val certification: String? = null,
@JsonProperty("aired_episodes") @SerialName("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null,
@JsonProperty("airs") @SerialName("airs") val airs: Airs? = null,
@JsonProperty("network") @SerialName("network") val network: String? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
@JsonProperty("media") @JsonAlias("movie", "show") @SerialName("media") @JsonNames("movie", "show") val media: MediaSummary? = null,
)
@Serializable
data class Airs(
@JsonProperty("day") val day: String? = null,
@JsonProperty("time") val time: String? = null,
@JsonProperty("timezone") val timezone: String? = null,
@JsonProperty("day") @SerialName("day") val day: String? = null,
@JsonProperty("time") @SerialName("time") val time: String? = null,
@JsonProperty("timezone") @SerialName("timezone") val timezone: String? = null,
)
@Serializable
data class Ids(
@JsonProperty("trakt") val trakt: Int? = null,
@JsonProperty("slug") val slug: String? = null,
@JsonProperty("tvdb") val tvdb: Int? = null,
@JsonProperty("imdb") val imdb: String? = null,
@JsonProperty("tmdb") val tmdb: Int? = null,
@JsonProperty("tvrage") val tvrage: String? = null,
@JsonProperty("trakt") @SerialName("trakt") val trakt: Int? = null,
@JsonProperty("slug") @SerialName("slug") val slug: String? = null,
@JsonProperty("tvdb") @SerialName("tvdb") val tvdb: Int? = null,
@JsonProperty("imdb") @SerialName("imdb") val imdb: String? = null,
@JsonProperty("tmdb") @SerialName("tmdb") val tmdb: Int? = null,
@JsonProperty("tvrage") @SerialName("tvrage") val tvrage: String? = null,
)
@Serializable
data class Images(
@JsonProperty("poster") val poster: List<String>? = null,
@JsonProperty("fanart") val fanart: List<String>? = null,
@JsonProperty("logo") val logo: List<String>? = null,
@JsonProperty("clearart") val clearArt: List<String>? = null,
@JsonProperty("banner") val banner: List<String>? = null,
@JsonProperty("thumb") val thumb: List<String>? = null,
@JsonProperty("screenshot") val screenshot: List<String>? = null,
@JsonProperty("headshot") val headshot: List<String>? = null,
@JsonProperty("poster") @SerialName("poster") val poster: List<String>? = null,
@JsonProperty("fanart") @SerialName("fanart") val fanart: List<String>? = null,
@JsonProperty("logo") @SerialName("logo") val logo: List<String>? = null,
@JsonProperty("clearart") @SerialName("clearart") val clearArt: List<String>? = null,
@JsonProperty("banner") @SerialName("banner") val banner: List<String>? = null,
@JsonProperty("thumb") @SerialName("thumb") val thumb: List<String>? = null,
@JsonProperty("screenshot") @SerialName("screenshot") val screenshot: List<String>? = null,
@JsonProperty("headshot") @SerialName("headshot") val headshot: List<String>? = null,
)
@Serializable
data class People(
@JsonProperty("cast") val cast: List<Cast>? = null,
@JsonProperty("cast") @SerialName("cast") val cast: List<Cast>? = null,
)
@Serializable
data class Cast(
@JsonProperty("character") val character: String? = null,
@JsonProperty("characters") val characters: List<String>? = null,
@JsonProperty("episode_count") val episodeCount: Long? = null,
@JsonProperty("person") val person: Person? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("character") @SerialName("character") val character: String? = null,
@JsonProperty("characters") @SerialName("characters") val characters: List<String>? = null,
@JsonProperty("episode_count") @SerialName("episode_count") val episodeCount: Long? = null,
@JsonProperty("person") @SerialName("person") val person: Person? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
)
@Serializable
data class Person(
@JsonProperty("name") val name: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("name") @SerialName("name") val name: String? = null,
@JsonProperty("ids") @SerialName("ids") val ids: Ids? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
)
@Serializable
data class Seasons(
@JsonProperty("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("episode_count") val episodeCount: Int? = null,
@JsonProperty("episodes") val episodes: List<TraktEpisode>? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("network") val network: String? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") val votes: Int? = null,
@JsonProperty("aired_episodes") @SerialName("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("episode_count") @SerialName("episode_count") val episodeCount: Int? = null,
@JsonProperty("episodes") @SerialName("episodes") val episodes: List<TraktEpisode>? = null,
@JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null,
@JsonProperty("ids") @SerialName("ids") val ids: Ids? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
@JsonProperty("network") @SerialName("network") val network: String? = null,
@JsonProperty("number") @SerialName("number") val number: Int? = null,
@JsonProperty("overview") @SerialName("overview") val overview: String? = null,
@JsonProperty("rating") @SerialName("rating") val rating: Double? = null,
@JsonProperty("title") @SerialName("title") val title: String? = null,
@JsonProperty("updated_at") @SerialName("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") @SerialName("votes") val votes: Int? = null,
)
@Serializable
data class TraktEpisode(
@JsonProperty("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("comment_count") val commentCount: Int? = null,
@JsonProperty("episode_type") val episodeType: String? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("number_abs") val numberAbs: Int? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") val votes: Int? = null,
@JsonProperty("available_translations") @SerialName("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("comment_count") @SerialName("comment_count") val commentCount: Int? = null,
@JsonProperty("episode_type") @SerialName("episode_type") val episodeType: String? = null,
@JsonProperty("first_aired") @SerialName("first_aired") val firstAired: String? = null,
@JsonProperty("ids") @SerialName("ids") val ids: Ids? = null,
@JsonProperty("images") @SerialName("images") val images: Images? = null,
@JsonProperty("number") @SerialName("number") val number: Int? = null,
@JsonProperty("number_abs") @SerialName("number_abs") val numberAbs: Int? = null,
@JsonProperty("overview") @SerialName("overview") val overview: String? = null,
@JsonProperty("rating") @SerialName("rating") val rating: Double? = null,
@JsonProperty("runtime") @SerialName("runtime") val runtime: Int? = null,
@JsonProperty("season") @SerialName("season") val season: Int? = null,
@JsonProperty("title") @SerialName("title") val title: String? = null,
@JsonProperty("updated_at") @SerialName("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") @SerialName("votes") val votes: Int? = null,
)
@Serializable
data class LinkData(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("trakt_id") val traktId: Int? = null,
@JsonProperty("trakt_slug") val traktSlug: String? = null,
@JsonProperty("tmdb_id") val tmdbId: Int? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("tvdb_id") val tvdbId: Int? = null,
@JsonProperty("tvrage_id") val tvrageId: String? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("episode") val episode: Int? = null,
@JsonProperty("ani_id") val aniId: String? = null,
@JsonProperty("anime_id") val animeId: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("org_title") val orgTitle: String? = null,
@JsonProperty("is_anime") val isAnime: Boolean = false,
@JsonProperty("aired_year") val airedYear: Int? = null,
@JsonProperty("last_season") val lastSeason: Int? = null,
@JsonProperty("eps_title") val epsTitle: String? = null,
@JsonProperty("jp_title") val jpTitle: String? = null,
@JsonProperty("date") val date: String? = null,
@JsonProperty("aired_date") val airedDate: String? = null,
@JsonProperty("is_asian") val isAsian: Boolean = false,
@JsonProperty("is_bollywood") val isBollywood: Boolean = false,
@JsonProperty("is_cartoon") val isCartoon: Boolean = false,
@JsonProperty("id") @SerialName("id") val id: Int? = null,
@JsonProperty("trakt_id") @SerialName("trakt_id") val traktId: Int? = null,
@JsonProperty("trakt_slug") @SerialName("trakt_slug") val traktSlug: String? = null,
@JsonProperty("tmdb_id") @SerialName("tmdb_id") val tmdbId: Int? = null,
@JsonProperty("imdb_id") @SerialName("imdb_id") val imdbId: String? = null,
@JsonProperty("tvdb_id") @SerialName("tvdb_id") val tvdbId: Int? = null,
@JsonProperty("tvrage_id") @SerialName("tvrage_id") val tvrageId: String? = null,
@JsonProperty("type") @SerialName("type") val type: String? = null,
@JsonProperty("season") @SerialName("season") val season: Int? = null,
@JsonProperty("episode") @SerialName("episode") val episode: Int? = null,
@JsonProperty("ani_id") @SerialName("ani_id") val aniId: String? = null,
@JsonProperty("anime_id") @SerialName("anime_id") val animeId: String? = null,
@JsonProperty("title") @SerialName("title") val title: String? = null,
@JsonProperty("year") @SerialName("year") val year: Int? = null,
@JsonProperty("org_title") @SerialName("org_title") val orgTitle: String? = null,
@JsonProperty("is_anime") @SerialName("is_anime") val isAnime: Boolean = false,
@JsonProperty("aired_year") @SerialName("aired_year") val airedYear: Int? = null,
@JsonProperty("last_season") @SerialName("last_season") val lastSeason: Int? = null,
@JsonProperty("eps_title") @SerialName("eps_title") val epsTitle: String? = null,
@JsonProperty("jp_title") @SerialName("jp_title") val jpTitle: String? = null,
@JsonProperty("date") @SerialName("date") val date: String? = null,
@JsonProperty("aired_date") @SerialName("aired_date") val airedDate: String? = null,
@JsonProperty("is_asian") @SerialName("is_asian") val isAsian: Boolean = false,
@JsonProperty("is_bollywood") @SerialName("is_bollywood") val isBollywood: Boolean = false,
@JsonProperty("is_cartoon") @SerialName("is_cartoon") val isCartoon: Boolean = false,
)
}

View file

@ -0,0 +1,42 @@
package com.lagradost.cloudstream3.utils.serializers
import com.lagradost.cloudstream3.Prerelease
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.intOrNull
/**
* Replicates Jackson's ACCEPT_FLOAT_AS_INT behaviour for Int fields.
* A floating-point JSON number is truncated to Int by dropping the
* fractional part, exactly like Jackson's default truncation behaviour.
*
* Usage:
*
* @Serializable
* data class MyData(
* @Serializable(with = FloatAsIntSerializer::class)
* @SerialName("count") val count: Int = 0,
* )
*/
@Prerelease
object FloatAsIntSerializer : KSerializer<Int> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("FloatAsInt", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): Int {
val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeInt()
val element = jsonDecoder.decodeJsonElement()
if (element !is JsonPrimitive) return 0
return element.intOrNull ?: element.doubleOrNull?.toInt() ?: 0
}
override fun serialize(encoder: Encoder, value: Int) = encoder.encodeInt(value)
}

View file

@ -0,0 +1,42 @@
package com.lagradost.cloudstream3.utils.serializers
import com.lagradost.cloudstream3.Prerelease
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.longOrNull
/**
* Replicates Jackson's ACCEPT_FLOAT_AS_INT behaviour for Long fields.
* A floating-point JSON number is truncated to Long by dropping the
* fractional part, exactly like Jackson's default truncation behaviour.
*
* Usage:
*
* @Serializable
* data class MyData(
* @Serializable(with = FloatAsLongSerializer::class)
* @SerialName("timestamp") val timestamp: Long = 0L,
* )
*/
@Prerelease
object FloatAsLongSerializer : KSerializer<Long> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("FloatAsLong", PrimitiveKind.LONG)
override fun deserialize(decoder: Decoder): Long {
val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeLong()
val element = jsonDecoder.decodeJsonElement()
if (element !is JsonPrimitive) return 0L
return element.longOrNull ?: element.doubleOrNull?.toLong() ?: 0L
}
override fun serialize(encoder: Encoder, value: Long) = encoder.encodeLong(value)
}

View file

@ -0,0 +1,75 @@
package com.lagradost.cloudstream3.utils.serializers
import com.lagradost.cloudstream3.Prerelease
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.JsonTransformingSerializer
/**
* Composable deserialize transformer that applies multiple Jackson-equivalent
* behaviours to specific fields by key. Pass only the sets you need; all
* default to empty (no-op).
*
* Behaviours (applied in order per field):
* singleValueAsListKeys ACCEPT_SINGLE_VALUE_AS_ARRAY
* emptyStringAsNullKeys ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
* emptyArrayAsNullKeys ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT
*
* Usage:
*
* @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
* @KeepGeneratedSerializer
* @Serializable(with = MyData.Serializer::class)
* data class MyData(
* @SerialName("title") val title: String? = null,
* @SerialName("tags") val tags: List<String>? = null,
* @SerialName("meta") val meta: MyMeta? = null,
* ) {
* object Serializer : JsonTransformSerializer<MyData>(
* MyData.generatedSerializer(),
* singleValueAsListKeys = setOf("tags"),
* emptyStringAsNullKeys = setOf("title", "tags"),
* emptyArrayAsNullKeys = setOf("tags", "meta"),
* )
* }
*/
@Prerelease
abstract class JsonTransformSerializer<T : Any>(
tSerializer: KSerializer<T>,
private val singleValueAsListKeys: Set<String> = emptySet(),
private val emptyArrayAsNullKeys: Set<String> = emptySet(),
private val emptyStringAsNullKeys: Set<String> = emptySet(),
) : JsonTransformingSerializer<T>(tSerializer) {
override fun transformDeserialize(element: JsonElement): JsonElement {
if (element !is JsonObject) return element
return JsonObject(element.mapValues { (key, value) ->
var result = value
if (key in singleValueAsListKeys) {
result = when (result) {
is JsonArray -> result
JsonNull -> JsonArray(emptyList())
else -> JsonArray(listOf(result))
}
}
if (key in emptyStringAsNullKeys) {
if (result is JsonPrimitive && result.isString && result.content.isEmpty()) {
result = JsonNull
}
}
if (key in emptyArrayAsNullKeys) {
if (result is JsonArray && result.isEmpty()) {
result = JsonNull
}
}
result
})
}
}

View file

@ -17,13 +17,13 @@ import kotlinx.serialization.json.JsonTransformingSerializer
*
* Usage:
*
* @OptIn(ExperimentalSerializationApi::class)
* @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
* @KeepGeneratedSerializer
* @Serializable(with = MyData.Serializer::class)
* data class MyData(
* val tags: List<String> = emptyList(),
* val title: String = "",
* val meta: Map<String, String> = emptyMap(),
* @SerialName("tags") val tags: List<String> = emptyList(),
* @SerialName("title") val title: String = "",
* @SerialName("meta") val meta: Map<String, String> = emptyMap(),
* ) {
* object Serializer : NonEmptySerializer<MyData>(MyData.generatedSerializer())
* }
@ -33,7 +33,6 @@ abstract class NonEmptySerializer<T : Any>(tSerializer: KSerializer<T>) :
override fun transformSerialize(element: JsonElement): JsonElement {
if (element !is JsonObject) return element
return JsonObject(element.filterValues { value ->
when (value) {
is JsonPrimitive -> value.content.isNotEmpty()

View file

@ -0,0 +1,49 @@
package com.lagradost.cloudstream3.utils.serializers
import com.lagradost.cloudstream3.Prerelease
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.decodeFromJsonElement
/**
* Convenience serializer for nullable String fields that combines
* ACCEPT_EMPTY_STRING_AS_NULL_OBJECT with null passthrough.
* An empty JSON string (`""`) or JSON null is decoded as null.
*
* Usage:
*
* @Serializable
* data class MyData(
* @Serializable(with = NullableStringSerializer::class)
* @SerialName("title") val title: String? = null,
* )
*/
@Prerelease
object NullableStringSerializer : KSerializer<String?> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("NullableString", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): String? {
val jsonDecoder = decoder as? JsonDecoder ?: return decoder.decodeString()
return when (val element = jsonDecoder.decodeJsonElement()) {
is JsonPrimitive -> element.content.ifEmpty { null }
JsonNull -> null
else -> jsonDecoder.json.decodeFromJsonElement(String.serializer(), element)
}
}
override fun serialize(encoder: Encoder, value: String?) {
@OptIn(ExperimentalSerializationApi::class) // encodeNull is experimental for now
if (value == null) encoder.encodeNull() else encoder.encodeString(value)
}
}

View file

@ -12,12 +12,12 @@ import kotlinx.serialization.json.JsonTransformingSerializer
*
* Usage:
*
* @OptIn(ExperimentalSerializationApi::class)
* @OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
* @KeepGeneratedSerializer
* @Serializable(with = MyData.Serializer::class)
* data class MyData(
* val fieldA: String = "",
* val fieldB: String = "",
* @SerialName("fieldA") val fieldA: String = "",
* @SerialName("fieldB") val fieldB: String = "",
* ) {
* object Serializer : WriteOnlySerializer<MyData>(
* MyData.generatedSerializer(),

View file

@ -0,0 +1,602 @@
package com.lagradost.cloudstream3.utils.serializers
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KeepGeneratedSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = NonEmptyData.Serializer::class)
data class NonEmptyData(
@SerialName("title") val title: String = "",
@SerialName("tags") val tags: List<String> = emptyList(),
@SerialName("meta") val meta: Map<String, String> = emptyMap(),
@SerialName("name") val name: String = "hello",
) {
object Serializer : NonEmptySerializer<NonEmptyData>(NonEmptyData.generatedSerializer())
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = WriteOnlyData.Serializer::class)
data class WriteOnlyData(
@SerialName("fieldA") val fieldA: String = "",
@SerialName("fieldB") val fieldB: String = "",
) {
object Serializer : WriteOnlySerializer<WriteOnlyData>(
WriteOnlyData.generatedSerializer(),
setOf("fieldB"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = MultiWriteOnly.Serializer::class)
data class MultiWriteOnly(
@SerialName("fieldA") val fieldA: String = "",
@SerialName("fieldB") val fieldB: String = "",
@SerialName("fieldC") val fieldC: String = "",
) {
object Serializer : WriteOnlySerializer<MultiWriteOnly>(
MultiWriteOnly.generatedSerializer(),
setOf("fieldB", "fieldC"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = SingleValueData.Serializer::class)
data class SingleValueData(
@SerialName("tags") val tags: List<String> = emptyList(),
@SerialName("nums") val nums: List<Int> = emptyList(),
) {
object Serializer : JsonTransformSerializer<SingleValueData>(
SingleValueData.generatedSerializer(),
singleValueAsListKeys = setOf("tags", "nums"),
)
}
@Serializable
data class NestedMeta(
@SerialName("key") val key: String = "",
)
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = EmptyArrayData.Serializer::class)
data class EmptyArrayData(
@SerialName("meta") val meta: NestedMeta? = null,
@SerialName("other") val other: String = "hello",
) {
object Serializer : JsonTransformSerializer<EmptyArrayData>(
EmptyArrayData.generatedSerializer(),
emptyArrayAsNullKeys = setOf("meta"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = EmptyStringData.Serializer::class)
data class EmptyStringData(
@SerialName("title") val title: String? = null,
@SerialName("episode") val episode: NestedMeta? = null,
@SerialName("keep") val keep: String? = "hello",
) {
object Serializer : JsonTransformSerializer<EmptyStringData>(
EmptyStringData.generatedSerializer(),
emptyStringAsNullKeys = setOf("title", "episode", "keep"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = SingleValueOrNullData.Serializer::class)
data class SingleValueOrNullData(
@SerialName("tags") val tags: List<String>? = null,
@SerialName("nums") val nums: List<Int>? = null,
@SerialName("other") val other: String = "hello",
) {
object Serializer : JsonTransformSerializer<SingleValueOrNullData>(
SingleValueOrNullData.generatedSerializer(),
singleValueAsListKeys = setOf("tags", "nums"),
emptyArrayAsNullKeys = setOf("tags", "nums"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = NullableFieldsData.Serializer::class)
data class NullableFieldsData(
@SerialName("title") val title: String? = null,
@SerialName("meta") val meta: NestedMeta? = null,
@SerialName("other") val other: String = "hello",
) {
object Serializer : JsonTransformSerializer<NullableFieldsData>(
NullableFieldsData.generatedSerializer(),
emptyStringAsNullKeys = setOf("title"),
emptyArrayAsNullKeys = setOf("meta"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = SingleValueOrEmptyStringData.Serializer::class)
data class SingleValueOrEmptyStringData(
@SerialName("tags") val tags: List<String> = emptyList(),
@SerialName("title") val title: String? = null,
) {
object Serializer : JsonTransformSerializer<SingleValueOrEmptyStringData>(
SingleValueOrEmptyStringData.generatedSerializer(),
singleValueAsListKeys = setOf("tags"),
emptyStringAsNullKeys = setOf("title"),
)
}
@OptIn(ExperimentalSerializationApi::class) // KeepGeneratedSerializer is an experimental annotation for now
@KeepGeneratedSerializer
@Serializable(with = AllTransformsData.Serializer::class)
data class AllTransformsData(
@SerialName("tags") val tags: List<String>? = null,
@SerialName("title") val title: String? = null,
@SerialName("meta") val meta: NestedMeta? = null,
@SerialName("other") val other: String = "hello",
) {
object Serializer : JsonTransformSerializer<AllTransformsData>(
AllTransformsData.generatedSerializer(),
singleValueAsListKeys = setOf("tags"),
emptyArrayAsNullKeys = setOf("tags", "meta"),
emptyStringAsNullKeys = setOf("title", "tags"),
)
}
@Serializable
data class FloatIntData(
@Serializable(with = FloatAsIntSerializer::class)
@SerialName("count") val count: Int = 0,
)
@Serializable
data class FloatLongData(
@Serializable(with = FloatAsLongSerializer::class)
@SerialName("timestamp") val timestamp: Long = 0L,
)
@Serializable
data class NullableStringData(
@Serializable(with = NullableStringSerializer::class)
@SerialName("title") val title: String? = null,
)
class SerializersTest {
@Test
fun nonEmptySerializerOmitsEmptyStrings() {
val data = NonEmptyData(title = "", name = "hello")
val result = data.toJson()
assertFalse(result.contains("title"))
assertTrue(result.contains("name"))
}
@Test
fun nonEmptySerializerOmitsEmptyLists() {
val data = NonEmptyData(tags = emptyList(), name = "hello")
val result = data.toJson()
assertFalse(result.contains("tags"))
}
@Test
fun nonEmptySerializerOmitsEmptyMaps() {
val data = NonEmptyData(meta = emptyMap(), name = "hello")
val result = data.toJson()
assertFalse(result.contains("meta"))
}
@Test
fun nonEmptySerializerKeepsNonEmptyFields() {
val data = NonEmptyData(title = "hello", tags = listOf("a"), meta = mapOf("k" to "v"))
val result = data.toJson()
assertTrue(result.contains("title"))
assertTrue(result.contains("tags"))
assertTrue(result.contains("meta"))
}
@Test
fun nonEmptySerializerDoesNotAffectDeserialization() {
val input = """{"title":"hello","tags":["a"],"meta":{"k":"v"},"name":"world"}"""
val result = parseJson<NonEmptyData>(input)
assertEquals("hello", result.title)
assertEquals(listOf("a"), result.tags)
assertEquals(mapOf("k" to "v"), result.meta)
assertEquals("world", result.name)
}
@Test
fun writeOnlySerializerOmitsFieldOnSerialize() {
val data = WriteOnlyData(fieldA = "hello", fieldB = "secret")
val result = data.toJson()
assertTrue(result.contains("fieldA"))
assertFalse(result.contains("fieldB"))
}
@Test
fun writeOnlySerializerDeserializesNormally() {
val input = """{"fieldA":"hello","fieldB":"secret"}"""
val result = parseJson<WriteOnlyData>(input)
assertEquals("hello", result.fieldA)
assertEquals("secret", result.fieldB)
}
@Test
fun writeOnlySerializerDeserializesMissingAsDefault() {
val input = """{"fieldA":"hello"}"""
val result = parseJson<WriteOnlyData>(input)
assertEquals("hello", result.fieldA)
assertEquals("", result.fieldB)
}
@Test
fun writeOnlySerializerHandlesMultipleKeys() {
val data = MultiWriteOnly(fieldA = "hello", fieldB = "secret1", fieldC = "secret2")
val result = data.toJson()
assertTrue(result.contains("fieldA"))
assertFalse(result.contains("fieldB"))
assertFalse(result.contains("fieldC"))
}
@Test
fun singleValueAsListDecodesArrayNormally() {
val input = """{"tags":["a","b"],"nums":[1,2]}"""
val result = parseJson<SingleValueData>(input)
assertEquals(listOf("a", "b"), result.tags)
assertEquals(listOf(1, 2), result.nums)
}
@Test
fun singleValueAsListWrapsBareStringInList() {
val input = """{"tags":"a","nums":[1]}"""
val result = parseJson<SingleValueData>(input)
assertEquals(listOf("a"), result.tags)
}
@Test
fun singleValueAsListWrapsBareIntInList() {
val input = """{"tags":[],"nums":42}"""
val result = parseJson<SingleValueData>(input)
assertEquals(listOf(42), result.nums)
}
@Test
fun singleValueAsListDecodesNullAsEmptyList() {
val input = """{"tags":null,"nums":[]}"""
val result = parseJson<SingleValueData>(input)
assertEquals(emptyList<String>(), result.tags)
}
@Test
fun singleValueAsListRoundtripsCorrectly() {
val data = SingleValueData(tags = listOf("x", "y"), nums = listOf(1, 2))
val encoded = data.toJson()
val decoded = parseJson<SingleValueData>(encoded)
assertEquals(data, decoded)
}
@Test
fun emptyArrayAsNullDecodesEmptyArrayAsNull() {
val input = """{"meta":[],"other":"hello"}"""
val result = parseJson<EmptyArrayData>(input)
assertNull(result.meta)
}
@Test
fun emptyArrayAsNullDecodesNullAsNull() {
val input = """{"meta":null,"other":"hello"}"""
val result = parseJson<EmptyArrayData>(input)
assertNull(result.meta)
}
@Test
fun emptyArrayAsNullDecodesObjectNormally() {
val input = """{"meta":{"key":"value"},"other":"hello"}"""
val result = parseJson<EmptyArrayData>(input)
assertEquals(NestedMeta("value"), result.meta)
}
@Test
fun emptyArrayAsNullDoesNotAffectOtherFields() {
val input = """{"meta":[],"other":"world"}"""
val result = parseJson<EmptyArrayData>(input)
assertEquals("world", result.other)
}
@Test
fun emptyArrayAsNullRoundtripsCorrectly() {
val data = EmptyArrayData(meta = NestedMeta("test"), other = "hello")
val encoded = data.toJson()
val decoded = parseJson<EmptyArrayData>(encoded)
assertEquals(data, decoded)
}
@Test
fun emptyStringAsNullDecodesEmptyStringAsNull() {
val input = """{"title":"","episode":null,"keep":"hello"}"""
val result = parseJson<EmptyStringData>(input)
assertNull(result.title)
}
@Test
fun emptyStringAsNullDecodesNullAsNull() {
val input = """{"title":null,"episode":null,"keep":"hello"}"""
val result = parseJson<EmptyStringData>(input)
assertNull(result.title)
}
@Test
fun emptyStringAsNullKeepsNonEmptyString() {
val input = """{"title":"hello","episode":null,"keep":"world"}"""
val result = parseJson<EmptyStringData>(input)
assertEquals("hello", result.title)
}
@Test
fun emptyStringAsNullDecodesObjectNormally() {
val input = """{"title":null,"episode":{"key":"value"},"keep":"hello"}"""
val result = parseJson<EmptyStringData>(input)
assertEquals(NestedMeta("value"), result.episode)
}
@Test
fun emptyStringAsNullDoesNotAffectNonEmptyFields() {
val input = """{"title":"","episode":null,"keep":"world"}"""
val result = parseJson<EmptyStringData>(input)
assertEquals("world", result.keep)
}
@Test
fun emptyStringAsNullRoundtripsCorrectly() {
val data = EmptyStringData(title = "hello", episode = NestedMeta("x"), keep = "world")
val encoded = data.toJson()
val decoded = parseJson<EmptyStringData>(encoded)
assertEquals(data, decoded)
}
@Test
fun singleValueOrNullWrapsBareValueInList() {
val input = """{"tags":"a","nums":1,"other":"hello"}"""
val result = parseJson<SingleValueOrNullData>(input)
assertEquals(listOf("a"), result.tags)
assertEquals(listOf(1), result.nums)
}
@Test
fun singleValueOrNullTreatsEmptyArrayAsNull() {
val input = """{"tags":[],"nums":[],"other":"hello"}"""
val result = parseJson<SingleValueOrNullData>(input)
assertNull(result.tags)
assertNull(result.nums)
}
@Test
fun singleValueOrNullDecodesArrayNormally() {
val input = """{"tags":["a","b"],"nums":[1,2],"other":"hello"}"""
val result = parseJson<SingleValueOrNullData>(input)
assertEquals(listOf("a", "b"), result.tags)
assertEquals(listOf(1, 2), result.nums)
}
@Test
fun singleValueOrNullDoesNotAffectOtherFields() {
val input = """{"tags":[],"nums":[],"other":"world"}"""
val result = parseJson<SingleValueOrNullData>(input)
assertEquals("world", result.other)
}
@Test
fun nullableFieldsEmptyStringBecomesNull() {
val input = """{"title":"","meta":{"key":"value"},"other":"hello"}"""
val result = parseJson<NullableFieldsData>(input)
assertNull(result.title)
assertEquals(NestedMeta("value"), result.meta)
}
@Test
fun nullableFieldsEmptyArrayBecomesNull() {
val input = """{"title":"hello","meta":[],"other":"hello"}"""
val result = parseJson<NullableFieldsData>(input)
assertEquals("hello", result.title)
assertNull(result.meta)
}
@Test
fun nullableFieldsBothNullAtOnce() {
val input = """{"title":"","meta":[],"other":"hello"}"""
val result = parseJson<NullableFieldsData>(input)
assertNull(result.title)
assertNull(result.meta)
}
@Test
fun nullableFieldsDoesNotAffectOtherFields() {
val input = """{"title":"","meta":[],"other":"world"}"""
val result = parseJson<NullableFieldsData>(input)
assertEquals("world", result.other)
}
@Test
fun nullableFieldsRoundtripsCorrectly() {
val data = NullableFieldsData(title = "hello", meta = NestedMeta("x"), other = "world")
val encoded = data.toJson()
val decoded = parseJson<NullableFieldsData>(encoded)
assertEquals(data, decoded)
}
@Test
fun singleValueOrEmptyStringWrapsBareString() {
val input = """{"tags":"a","title":"hello"}"""
val result = parseJson<SingleValueOrEmptyStringData>(input)
assertEquals(listOf("a"), result.tags)
assertEquals("hello", result.title)
}
@Test
fun singleValueOrEmptyStringTurnsEmptyTitleToNull() {
val input = """{"tags":["a"],"title":""}"""
val result = parseJson<SingleValueOrEmptyStringData>(input)
assertEquals(listOf("a"), result.tags)
assertNull(result.title)
}
@Test
fun singleValueOrEmptyStringRoundtripsCorrectly() {
val data = SingleValueOrEmptyStringData(tags = listOf("x"), title = "hello")
val encoded = data.toJson()
val decoded = parseJson<SingleValueOrEmptyStringData>(encoded)
assertEquals(data, decoded)
}
@Test
fun allTransformsWrapsBareStringInList() {
val input = """{"tags":"a","title":"hello","meta":{"key":"value"},"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertEquals(listOf("a"), result.tags)
}
@Test
fun allTransformsEmptyArrayTagsBecomesNull() {
val input = """{"tags":[],"title":"hello","meta":{"key":"value"},"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertNull(result.tags)
}
@Test
fun allTransformsEmptyMetaBecomesNull() {
val input = """{"tags":["a"],"title":"hello","meta":[],"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertNull(result.meta)
}
@Test
fun allTransformsEmptyTitleBecomesNull() {
val input = """{"tags":["a"],"title":"","meta":{"key":"value"},"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertNull(result.title)
}
@Test
fun allTransformsAllNullAtOnce() {
val input = """{"tags":[],"title":"","meta":[],"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertNull(result.tags)
assertNull(result.title)
assertNull(result.meta)
}
@Test
fun allTransformsDoesNotAffectOtherFields() {
val input = """{"tags":[],"title":"","meta":[],"other":"world"}"""
val result = parseJson<AllTransformsData>(input)
assertEquals("world", result.other)
}
@Test
fun allTransformsRoundtripsCorrectly() {
val data = AllTransformsData(tags = listOf("a"), title = "hello", meta = NestedMeta("x"), other = "world")
val encoded = data.toJson()
val decoded = parseJson<AllTransformsData>(encoded)
assertEquals(data, decoded)
}
@Test
fun floatAsIntTruncatesFloat() {
val input = """{"count":3.9}"""
val result = parseJson<FloatIntData>(input)
assertEquals(3, result.count)
}
@Test
fun floatAsIntDecodesIntNormally() {
val input = """{"count":42}"""
val result = parseJson<FloatIntData>(input)
assertEquals(42, result.count)
}
@Test
fun floatAsIntHandlesNegativeFloat() {
val input = """{"count":-2.7}"""
val result = parseJson<FloatIntData>(input)
assertEquals(-2, result.count)
}
@Test
fun floatAsIntRoundtripsCorrectly() {
val data = FloatIntData(count = 7)
val encoded = data.toJson()
val decoded = parseJson<FloatIntData>(encoded)
assertEquals(data, decoded)
}
@Test
fun floatAsLongTruncatesFloat() {
val input = """{"timestamp":1234567890.9}"""
val result = parseJson<FloatLongData>(input)
assertEquals(1234567890L, result.timestamp)
}
@Test
fun floatAsLongDecodesLongNormally() {
val input = """{"timestamp":9999999999}"""
val result = parseJson<FloatLongData>(input)
assertEquals(9999999999L, result.timestamp)
}
@Test
fun floatAsLongHandlesNegativeFloat() {
val input = """{"timestamp":-100.6}"""
val result = parseJson<FloatLongData>(input)
assertEquals(-100L, result.timestamp)
}
@Test
fun floatAsLongRoundtripsCorrectly() {
val data = FloatLongData(timestamp = 1700000000L)
val encoded = data.toJson()
val decoded = parseJson<FloatLongData>(encoded)
assertEquals(data, decoded)
}
@Test
fun nullableStringDecodesEmptyStringAsNull() {
val input = """{"title":""}"""
val result = parseJson<NullableStringData>(input)
assertNull(result.title)
}
@Test
fun nullableStringDecodesNullAsNull() {
val input = """{"title":null}"""
val result = parseJson<NullableStringData>(input)
assertNull(result.title)
}
@Test
fun nullableStringKeepsNonEmptyString() {
val input = """{"title":"hello"}"""
val result = parseJson<NullableStringData>(input)
assertEquals("hello", result.title)
}
@Test
fun nullableStringRoundtripsCorrectly() {
val data = NullableStringData(title = "world")
val encoded = data.toJson()
val decoded = parseJson<NullableStringData>(encoded)
assertEquals(data, decoded)
}
}