import Foundation import MediaKit /// Ein Eintrag, wie Jellyfin ihn über die Leitung schickt. /// /// Die Felder heißen groß und die Zeiten sind Ticks zu hundert Nanosekunden — /// beides bleibt hier unten. Nach oben geht nur `MediaItem`. public struct JellyfinItem: Decodable, Sendable { public let id: String public let name: String? public let type: String? public let overview: String? public let productionYear: Int? /// Laufzeit in Ticks zu 100 ns. public let runTimeTicks: Int64? public let indexNumber: Int? public let parentIndexNumber: Int? public let seriesName: String? public let seriesID: String? public let seasonID: String? public let seasonName: String? public let officialRating: String? public let communityRating: Double? public let criticRating: Double? public let dateCreated: Date? public let childCount: Int? public let recursiveItemCount: Int? public let genres: [String]? public let people: [Person]? public let userData: UserData? public let imageTags: [String: String]? public let backdropImageTags: [String]? public let parentBackdropItemID: String? public let seriesPrimaryImageTag: String? public let mediaSources: [JellyfinMediaSource]? public let collectionType: String? public struct Person: Decodable, Sendable { public let id: String? public let name: String? public let role: String? public let type: String? public let primaryImageTag: String? private enum CodingKeys: String, CodingKey { case id = "Id", name = "Name", role = "Role", type = "Type" case primaryImageTag = "PrimaryImageTag" } } public struct UserData: Decodable, Sendable { public let playbackPositionTicks: Int64? public let playCount: Int? public let played: Bool? public let isFavorite: Bool? public let unplayedItemCount: Int? private enum CodingKeys: String, CodingKey { case playbackPositionTicks = "PlaybackPositionTicks" case playCount = "PlayCount" case played = "Played" case isFavorite = "IsFavorite" case unplayedItemCount = "UnplayedItemCount" } } private enum CodingKeys: String, CodingKey { case id = "Id", name = "Name", type = "Type", overview = "Overview" case productionYear = "ProductionYear", runTimeTicks = "RunTimeTicks" case indexNumber = "IndexNumber", parentIndexNumber = "ParentIndexNumber" case seriesName = "SeriesName", seriesID = "SeriesId" case seasonID = "SeasonId", seasonName = "SeasonName" case officialRating = "OfficialRating", communityRating = "CommunityRating" case criticRating = "CriticRating", dateCreated = "DateCreated" case childCount = "ChildCount", recursiveItemCount = "RecursiveItemCount" case genres = "Genres", people = "People", userData = "UserData" case imageTags = "ImageTags", backdropImageTags = "BackdropImageTags" case parentBackdropItemID = "ParentBackdropItemId" case seriesPrimaryImageTag = "SeriesPrimaryImageTag" case mediaSources = "MediaSources", collectionType = "CollectionType" } } /// Eine Fassung, wie Jellyfin sie meldet. public struct JellyfinMediaSource: Decodable, Sendable { public let id: String? public let container: String? public let size: Int64? public let bitrate: Int? public let runTimeTicks: Int64? public let mediaStreams: [JellyfinMediaStream]? public let supportsDirectPlay: Bool? public let supportsDirectStream: Bool? public let supportsTranscoding: Bool? public let transcodingURL: String? private enum CodingKeys: String, CodingKey { case id = "Id", container = "Container", size = "Size", bitrate = "Bitrate" case runTimeTicks = "RunTimeTicks", mediaStreams = "MediaStreams" case supportsDirectPlay = "SupportsDirectPlay" case supportsDirectStream = "SupportsDirectStream" case supportsTranscoding = "SupportsTranscoding" case transcodingURL = "TranscodingUrl" } } /// Eine Spur, wie Jellyfin sie meldet. public struct JellyfinMediaStream: Decodable, Sendable { public let index: Int? public let type: String? public let codec: String? public let language: String? public let displayTitle: String? public let channels: Int? public let isDefault: Bool? public let isForced: Bool? public let height: Int? public let width: Int? private enum CodingKeys: String, CodingKey { case index = "Index", type = "Type", codec = "Codec", language = "Language" case displayTitle = "DisplayTitle", channels = "Channels" case isDefault = "IsDefault", isForced = "IsForced" case height = "Height", width = "Width" } } /// Die Hülle, in der Jellyfin Listen schickt. public struct JellyfinItemPage: Decodable, Sendable { public let items: [JellyfinItem] public let totalRecordCount: Int? public let startIndex: Int? private enum CodingKeys: String, CodingKey { case items = "Items" case totalRecordCount = "TotalRecordCount" case startIndex = "StartIndex" } } extension Int64 { /// Ticks zu hundert Nanosekunden in Millisekunden. /// /// Jellyfins Einheit. Zehntausend Ticks sind eine Millisekunde — eine /// Zahl, die man beim Lesen zweimal prüft und deshalb genau einmal /// aufschreibt. var millisecondsFromTicks: Int { Int(self / 10_000) } } extension Int { var ticksFromMilliseconds: Int64 { Int64(self) * 10_000 } }