- EmbyApiModels: 8个嵌套类型添加@Immutable(EmbyUserData/EmbyPerson/EmbyNamedReference/EmbyMediaStream/EmbyItemCounts/EmbyResourceStats/EmbyChapter/EmbyMediaSource) - LumaComponents: 新增rememberImageRequest()辅助函数避免ImageRequest.Builder重复分配 - HomeScreen/HomeLibraryAllScreen/FavoritesAllScreen/SearchScreen/SearchAllScreen/RecordsScreen/LibraryScreen: 17处ImageRequest.Builder替换为rememberImageRequest() - MediaDetailScreen: episodes/genres/qualities/streams LazyRow添加stable key - LibraryScreen: servers LazyVerticalGrid添加stable key
221 lines
9.7 KiB
Kotlin
221 lines
9.7 KiB
Kotlin
package com.example.myapplication.data
|
|
|
|
import androidx.compose.runtime.Immutable
|
|
import com.google.gson.annotations.SerializedName
|
|
|
|
/* ---------- Auth ---------- */
|
|
|
|
data class EmbyAuthRequest(
|
|
val Username: String,
|
|
val Pw: String,
|
|
)
|
|
|
|
data class EmbyAuthResponse(
|
|
@SerializedName("User") val user: EmbyUser?,
|
|
@SerializedName("AccessToken") val accessToken: String?,
|
|
@SerializedName("ServerId") val serverId: String?,
|
|
)
|
|
|
|
data class EmbyUser(
|
|
@SerializedName("Id") val id: String,
|
|
@SerializedName("Name") val name: String,
|
|
)
|
|
|
|
/* ---------- Libraries ---------- */
|
|
|
|
data class EmbyMediaFoldersResponse(
|
|
@SerializedName("Items") val items: List<EmbyMediaFolder> = emptyList(),
|
|
@SerializedName("TotalRecordCount") val totalRecordCount: Int = 0,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyMediaFolder(
|
|
@SerializedName("Id") val id: String,
|
|
@SerializedName("Name") val name: String,
|
|
@SerializedName("CollectionType") val collectionType: String? = null,
|
|
@SerializedName("PrimaryImageAspectRatio") val primaryImageAspectRatio: Double? = null,
|
|
)
|
|
|
|
/* ---------- Media Items ---------- */
|
|
|
|
data class EmbyItemsResponse(
|
|
@SerializedName("Items") val items: List<EmbyItem> = emptyList(),
|
|
@SerializedName("TotalRecordCount") val totalRecordCount: Int = 0,
|
|
@SerializedName("StartIndex") val startIndex: Int = 0,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyItem(
|
|
@SerializedName("Id") val id: String,
|
|
@SerializedName("Name") val name: String,
|
|
@SerializedName("Type") val type: String? = null,
|
|
@SerializedName("CollectionType") val collectionType: String? = null,
|
|
@SerializedName("OfficialRating") val officialRating: String? = null,
|
|
@SerializedName("CommunityRating") val communityRating: Double? = null,
|
|
@SerializedName("ProductionYear") val productionYear: Int? = null,
|
|
@SerializedName("Overview") val overview: String? = null,
|
|
@SerializedName("Genres") val _genres: List<String>? = emptyList(),
|
|
@SerializedName("RunTimeTicks") val runTimeTicks: Long? = null,
|
|
@SerializedName("PremiereDate") val premiereDate: String? = null,
|
|
@SerializedName("DateCreated") val dateCreated: String? = null,
|
|
@SerializedName("ImageTags") val imageTags: Map<String, String>? = null,
|
|
@SerializedName("BackdropImageTags") val _backdropImageTags: List<String>? = emptyList(),
|
|
@SerializedName("SeriesName") val seriesName: String? = null,
|
|
@SerializedName("SeriesId") val seriesId: String? = null,
|
|
@SerializedName("SeasonId") val seasonId: String? = null,
|
|
@SerializedName("ParentId") val parentId: String? = null,
|
|
@SerializedName("IndexNumber") val indexNumber: Int? = null,
|
|
@SerializedName("ParentIndexNumber") val parentIndexNumber: Int? = null,
|
|
@SerializedName("OriginalTitle") val originalTitle: String? = null,
|
|
@SerializedName("Taglines") val _taglines: List<String>? = emptyList(),
|
|
@SerializedName("People") val _people: List<EmbyPerson>? = emptyList(),
|
|
@SerializedName("Studios") val _studios: List<EmbyNamedReference>? = emptyList(),
|
|
@SerializedName("ProviderIds") val _providerIds: Map<String, String>? = emptyMap(),
|
|
@SerializedName("MediaStreams") val _mediaStreams: List<EmbyMediaStream>? = emptyList(),
|
|
@SerializedName("MediaSources") val _mediaSources: List<EmbyMediaSource>? = emptyList(),
|
|
@SerializedName("Size") val size: Long? = null,
|
|
@SerializedName("Bitrate") val bitrate: Int? = null,
|
|
@SerializedName("Chapters") val _chapters: List<EmbyChapter>? = emptyList(),
|
|
@SerializedName("UserData") val userData: EmbyUserData? = null,
|
|
@SerializedName("RecursiveChildCount") val recursiveChildCount: Int? = null,
|
|
@SerializedName("ChildCount") val childCount: Int? = null,
|
|
) {
|
|
val genres: List<String> get() = _genres.orEmpty()
|
|
val backdropImageTags: List<String> get() = _backdropImageTags.orEmpty()
|
|
val taglines: List<String> get() = _taglines.orEmpty()
|
|
val people: List<EmbyPerson> get() = _people.orEmpty()
|
|
val studios: List<EmbyNamedReference> get() = _studios.orEmpty()
|
|
val providerIds: Map<String, String> get() = _providerIds.orEmpty()
|
|
val mediaStreams: List<EmbyMediaStream> get() = _mediaStreams.orEmpty()
|
|
val mediaSources: List<EmbyMediaSource> get() = _mediaSources.orEmpty()
|
|
val chapters: List<EmbyChapter> get() = _chapters.orEmpty()
|
|
val playbackPositionMs: Long
|
|
get() = (userData?.playbackPositionTicks ?: 0L).coerceAtLeast(0L) / 10_000L
|
|
|
|
val primaryImageTag: String?
|
|
get() = imageTags?.get("Primary")
|
|
|
|
val logoImageTag: String?
|
|
get() = imageTags?.get("Logo")
|
|
|
|
val posterUrlPath: String?
|
|
get() {
|
|
if (primaryImageTag.isNullOrBlank()) return null
|
|
return "/emby/Items/$id/Images/Primary?tag=$primaryImageTag&maxWidth=400&quality=80"
|
|
}
|
|
|
|
fun getLogoUrl(serverUrl: String, maxWidth: Int = 600): String? {
|
|
val tag = logoImageTag ?: return null
|
|
return "$serverUrl/emby/Items/$id/Images/Logo?tag=$tag&maxWidth=$maxWidth&quality=90"
|
|
}
|
|
}
|
|
|
|
@Immutable
|
|
data class EmbyUserData(
|
|
@SerializedName("PlaybackPositionTicks") val playbackPositionTicks: Long = 0L,
|
|
@SerializedName("Played") val played: Boolean = false,
|
|
@SerializedName("LastPlayedDate") val lastPlayedDate: String? = null,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyPerson(
|
|
@SerializedName("Id") val id: String = "",
|
|
@SerializedName("Name") val name: String = "",
|
|
@SerializedName("Role") val role: String? = null,
|
|
@SerializedName("Type") val type: String? = null,
|
|
@SerializedName("PrimaryImageTag") val primaryImageTag: String? = null,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyNamedReference(
|
|
@SerializedName("Name") val name: String = "",
|
|
)
|
|
|
|
/* ---------- Detail ---------- */
|
|
|
|
@Immutable
|
|
data class EmbyMediaStream(
|
|
@SerializedName("DisplayTitle") val displayTitle: String? = null,
|
|
@SerializedName("DisplayLanguage") val displayLanguage: String? = null,
|
|
@SerializedName("Title") val title: String? = null,
|
|
@SerializedName("Type") val type: String? = null,
|
|
@SerializedName("Codec") val codec: String? = null,
|
|
@SerializedName("Language") val language: String? = null,
|
|
@SerializedName("Height") val height: Int? = null,
|
|
@SerializedName("Width") val width: Int? = null,
|
|
@SerializedName("BitRate") val bitRate: Int? = null,
|
|
@SerializedName("SampleRate") val sampleRate: Int? = null,
|
|
@SerializedName("Channels") val channels: Int? = null,
|
|
@SerializedName("ChannelLayout") val channelLayout: String? = null,
|
|
@SerializedName("RealFrameRate") val realFrameRate: Float? = null,
|
|
@SerializedName("AverageFrameRate") val averageFrameRate: Float? = null,
|
|
@SerializedName("VideoRange") val videoRange: String? = null,
|
|
@SerializedName("VideoRangeType") val videoRangeType: String? = null,
|
|
@SerializedName("IsInterlaced") val isInterlaced: Boolean = false,
|
|
@SerializedName("ColorPrimaries") val colorPrimaries: String? = null,
|
|
@SerializedName("ColorSpace") val colorSpace: String? = null,
|
|
@SerializedName("ColorTransfer") val colorTransfer: String? = null,
|
|
@SerializedName("Profile") val profile: String? = null,
|
|
@SerializedName("Level") val level: Double? = null,
|
|
@SerializedName("AspectRatio") val aspectRatio: String? = null,
|
|
@SerializedName("BitDepth") val bitDepth: Int? = null,
|
|
@SerializedName("PixelFormat") val pixelFormat: String? = null,
|
|
@SerializedName("IsExternal") val isExternal: Boolean = false,
|
|
@SerializedName("IsDefault") val isDefault: Boolean = false,
|
|
@SerializedName("IsForced") val isForced: Boolean = false,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyItemCounts(
|
|
@SerializedName("MovieCount") val movieCount: Int = 0,
|
|
@SerializedName("SeriesCount") val seriesCount: Int = 0,
|
|
@SerializedName("EpisodeCount") val episodeCount: Int = 0,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyResourceStats(
|
|
val withVersions: EmbyItemCounts = EmbyItemCounts(),
|
|
val withoutVersions: EmbyItemCounts = EmbyItemCounts(),
|
|
val versionsAvailable: Boolean = false,
|
|
val groupedAvailable: Boolean = false,
|
|
) {
|
|
fun updatedWith(latest: EmbyResourceStats): EmbyResourceStats = EmbyResourceStats(
|
|
withVersions = if (latest.versionsAvailable) latest.withVersions else withVersions,
|
|
withoutVersions = if (latest.groupedAvailable) latest.withoutVersions else withoutVersions,
|
|
versionsAvailable = versionsAvailable || latest.versionsAvailable,
|
|
groupedAvailable = groupedAvailable || latest.groupedAvailable,
|
|
)
|
|
}
|
|
|
|
@Immutable
|
|
data class EmbyChapter(
|
|
@SerializedName("StartPositionTicks") val startPositionTicks: Long = 0L,
|
|
@SerializedName("Name") val name: String? = null,
|
|
@SerializedName("MarkerType") val markerType: String? = null,
|
|
) {
|
|
val startPositionMs: Long get() = startPositionTicks.coerceAtLeast(0L) / 10_000L
|
|
}
|
|
|
|
/* ---------- Playback Info ---------- */
|
|
|
|
data class EmbyPlaybackInfoResponse(
|
|
@SerializedName("MediaSources") val mediaSources: List<EmbyMediaSource>? = null,
|
|
@SerializedName("PlaySessionId") val playSessionId: String? = null,
|
|
)
|
|
|
|
@Immutable
|
|
data class EmbyMediaSource(
|
|
@SerializedName("Id") val id: String,
|
|
@SerializedName("PlaySessionId") val playSessionId: String? = null,
|
|
@SerializedName("Path") val path: String? = null,
|
|
@SerializedName("Container") val container: String? = null,
|
|
@SerializedName("SupportsDirectPlay") val supportsDirectPlay: Boolean = false,
|
|
@SerializedName("SupportsDirectStream") val supportsDirectStream: Boolean = false,
|
|
@SerializedName("SupportsTranscoding") val supportsTranscoding: Boolean = false,
|
|
@SerializedName("TranscodingUrl") val transcodingUrl: String? = null,
|
|
@SerializedName("DirectStreamUrl") val directStreamUrl: String? = null,
|
|
@SerializedName("Size") val size: Long? = null,
|
|
@SerializedName("Bitrate") val bitrate: Int? = null,
|
|
@SerializedName("MediaStreams") val mediaStreams: List<EmbyMediaStream>? = null,
|
|
)
|