import Foundation import MediaKit /// Die Übersetzung der Plex-Drahtmodelle in die neutrale Form. /// /// Eine Datei, eine Richtung. Danach ist unerheblich, von welchem Server ein /// Titel stammt — und genau das war das Ziel: die Oberfläche soll nicht an /// dreißig Stellen wissen müssen, wie Plex seine Felder nennt. /// /// Die Drahtmodelle bleiben, wo sie sind. Sie tragen Eigenheiten, die nur /// Plex hat (dynamische Schlüssel in der Hülle, `grandparent` für die Serie, /// Sekunden seit 1970 als Zahl), und die gehören nicht nach oben. extension PlexItem { public var asMediaItem: MediaItem { MediaItem( id: ratingKey, path: key, kind: kind.map(Self.kind) ?? nil, title: title, sortTitle: titleSort, summary: summary, year: year, duration: duration, contentRating: contentRating, rating: rating, audienceRating: audienceRating, thumb: thumb, art: art, parentThumb: parentThumb, seriesThumb: grandparentThumb, seriesArt: grandparentArt, index: index, parentIndex: parentIndex, parentTitle: parentTitle, parentID: parentRatingKey, seriesTitle: grandparentTitle, seriesID: grandparentRatingKey, playCount: viewCount, resumeMilliseconds: viewOffset, lastPlayedAt: lastViewedAt.map { Date(timeIntervalSince1970: TimeInterval($0)) }, addedAt: addedAt.map { Date(timeIntervalSince1970: TimeInterval($0)) }, // Plex kennt keine Favoriten. Ehrlicher als eine erfundene // Markierung, die sich nirgends setzen ließe. isFavourite: false, episodeCount: leafCount, playedEpisodeCount: viewedLeafCount, childCount: childCount, versions: media.map(\.asMediaVersion), genres: genres.map(\.tag), directors: directors.map(\.tag), writers: writers.map(\.tag), cast: roles.map(\.asMediaPerson), markers: markers.map(\.asMediaMarker), artwork: images.map(\.asMediaArtwork) ) } private static func kind(_ plex: PlexItem.Kind) -> MediaItem.Kind { switch plex { case .movie: .movie case .show: .show case .season: .season case .episode: .episode case .clip: .clip case .collection: .collection } } } extension PlexMedia { public var asMediaVersion: MediaVersion { MediaVersion( id: String(id), container: container, videoCodec: videoCodec, audioCodec: audioCodec, videoResolution: videoResolution, bitrate: bitrate, width: width, height: height, audioChannels: audioChannels, duration: duration, files: parts.map(\.asMediaFile) ) } } extension PlexPart { public var asMediaFile: MediaFile { MediaFile( id: String(id), key: key, container: container, size: size, duration: duration, file: file, streams: streams.compactMap(\.asMediaStream) ) } } extension PlexStream { /// `nil`, wenn Plex eine Spurart meldet, mit der die App nichts anfängt. public var asMediaStream: MediaStream? { let mapped: MediaStream.Kind switch kind { case .video: mapped = .video case .audio: mapped = .audio case .subtitle: mapped = .subtitle case nil: return nil } return MediaStream( id: String(id), kind: mapped, codec: codec, language: language, languageCode: languageCode, displayTitle: displayTitle ?? title, channels: channels, isSelected: isSelected, isForced: isForced ) } } extension PlexTag { public var asMediaPerson: MediaPerson { MediaPerson(id: id, name: tag, role: role, thumb: thumb) } } extension PlexMarker { public var asMediaMarker: MediaMarker { MediaMarker( kind: MediaMarker.Kind(rawValue: type) ?? .unknown, startMilliseconds: startTimeOffset, endMilliseconds: endTimeOffset ) } } extension PlexImage { public var asMediaArtwork: MediaArtwork { MediaArtwork( kind: MediaArtwork.Kind(rawValue: type) ?? .unknown, path: url ) } } extension PlexLibrary { public var asMediaLibrary: MediaLibrary { MediaLibrary( id: key, title: title, kind: mediaKind, thumb: thumb, art: art ) } } extension PlexLibrary { private var mediaKind: MediaLibrary.Kind { switch kind { case .movie: .movie case .show: .show case .artist: .music case .photo: .photo case .livetv: .liveTV case nil: .other } } } extension PlexHub { public var asMediaRowGroup: MediaRowGroup { MediaRowGroup( id: hubIdentifier, title: title, moreKey: more == true ? key : nil, items: items.map(\.asMediaItem) ) } }