Files
Flaro/Packages/FlixyKit/Sources/FlixyPlayback/Engine/MediaTrack.swift
T
GuidoandClaude Opus 5 1ed64bf166 Phase 0: Projektgerüst, Design-Tokens und Navigation
Legt das Fundament für den Plex-Client an:

- XcodeGen-Projekt nach dem Muster von Onyx (com.scarriffleservices,
  strict concurrency, Projektdatei nicht eingecheckt)
- FlixyKit mit vier Modulen: PlexKit, FlixyDesign, FlixyPlayback,
  FlixyDownloads. macOS steht als Plattform mit drin, damit die
  Logik-Tests ohne Simulator laufen.
- FlixyDesign: dunkle Farb-Tokens, Schriftskala, Poster- und
  Querformat-Kacheln, Aufmacher-Bild. Die Akzentfarbe fließt über die
  Umgebung und wird per WCAG-Rechnung lesbar gehalten.
- PlexKit: Kopfzeilen-Identität, Transport-Protokoll, Fehlerarten und
  die Medienmodelle samt nachsichtigem Decoder.
- FlixyPlayback: VideoPlayerEngine-Protokoll und PlaybackDecider — die
  eine Funktion, an der Phase 2 das Transcoding andockt.
- Ausfahrbare Seitenleiste auf dem iPhone, Split-View auf dem iPad.
- 45 Tests, alle grün.

VLCKit 4.0.0-a24 ist vorab geprüft: baut über SPM unter Swift 6 mit
strict concurrency. Die iOS-Slice ist mit 239 MB allerdings gewichtig,
deshalb bekommt sie in Phase 1 ein eigenes Target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:04:40 +02:00

72 lines
2.3 KiB
Swift

import Foundation
import PlexKit
/// Eine wählbare Ton- oder Untertitelspur.
///
/// Bewusst getrennt von `PlexStream`: VLCKit und AVPlayer nummerieren ihre
/// Spuren selbst, und die Nummern stimmen nicht mit denen von Plex überein.
/// `engineIdentifier` hält deshalb fest, wie der jeweilige Motor die Spur
/// nennt, während `plexStreamID` für den Server gedacht ist.
public struct MediaTrack: Sendable, Equatable, Identifiable {
public enum Kind: Sendable, Equatable { case audio, subtitle }
public let id: String
public let kind: Kind
public let displayName: String
public let languageCode: String?
public let plexStreamID: Int?
/// Die Nummer, unter der der laufende Motor die Spur führt.
public let engineIdentifier: Int?
public let isForced: Bool
public init(
id: String,
kind: Kind,
displayName: String,
languageCode: String? = nil,
plexStreamID: Int? = nil,
engineIdentifier: Int? = nil,
isForced: Bool = false
) {
self.id = id
self.kind = kind
self.displayName = displayName
self.languageCode = languageCode
self.plexStreamID = plexStreamID
self.engineIdentifier = engineIdentifier
self.isForced = isForced
}
}
extension MediaTrack {
/// Baut eine Spur aus dem, was Plex gemeldet hat.
///
/// `displayTitle` ist das, was Plex selbst in seinen Oberflächen zeigt
/// (etwa „Deutsch (AC3 5.1)"). Fehlt es, wird aus Sprache und Codec etwas
/// Brauchbares gebaut, statt „Spur 3" anzuzeigen.
public init?(stream: PlexStream) {
guard let kind = stream.kind else { return nil }
let trackKind: Kind
switch kind {
case .audio: trackKind = .audio
case .subtitle: trackKind = .subtitle
case .video: return nil
}
let name = stream.displayTitle
?? stream.title
?? [stream.language, stream.codec?.uppercased()]
.compactMap(\.self)
.joined(separator: " · ")
self.init(
id: "plex-\(stream.id)",
kind: trackKind,
displayName: name.isEmpty ? "—" : name,
languageCode: stream.languageCode,
plexStreamID: stream.id,
isForced: stream.isForced
)
}
}