diff --git a/Flixy/Info-macOS.plist b/Flixy/Info-macOS.plist index 988b4c7..6d51a6c 100644 --- a/Flixy/Info-macOS.plist +++ b/Flixy/Info-macOS.plist @@ -31,6 +31,8 @@ CFBundleVersion 1 + ITSAppUsesNonExemptEncryption + LSApplicationCategoryType public.app-category.entertainment NSAppTransportSecurity @@ -38,7 +40,22 @@ NSAllowsLocalNetworking - NSHumanReadableCopyright - + UIBackgroundModes + + audio + + UILaunchScreen + + UIColorName + LaunchBackground + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Dark diff --git a/Flixy/Session/PlaybackSettings.swift b/Flixy/Session/PlaybackSettings.swift index b88b1c9..4e01696 100644 --- a/Flixy/Session/PlaybackSettings.swift +++ b/Flixy/Session/PlaybackSettings.swift @@ -1,4 +1,7 @@ import Foundation +#if canImport(FlixyVLC) +import FlixyVLC +#endif import Observation import FlixyPlayback @@ -53,6 +56,21 @@ final class PlaybackSettings { /// Die Geschwindigkeiten, mit denen eine Wiedergabe beginnen kann. static let rateChoices: [Double] = [0.75, 1.0, 1.25, 1.5, 1.75, 2.0] + /// Ob ein zweiter Spieler zur Verfügung steht. + /// + /// VLC steckt in einem eigenen Paket, das nur für iOS gebaut wird — sein + /// Binärpaket hat keinen Teil für Mac Catalyst. Wo es fehlt, übernimmt der + /// Server: MKV und DTS werden dann umgerechnet statt unverändert + /// übertragen. Das kostet ihn Rechenzeit und sieht man nicht, aber es + /// spielt. + static var hasFallbackEngine: Bool { + #if canImport(FlixyVLC) + true + #else + false + #endif + } + private let defaults: UserDefaults var wifiQuality: Quality { didSet { defaults.set(wifiQuality.rawValue, forKey: Key.wifiQuality) } } @@ -129,7 +147,10 @@ final class PlaybackSettings { return PlaybackPolicy( allowsTranscoding: true, - allowsFallbackEngine: true, + // Nur, wenn es ihn auf dieser Plattform gibt. Ohne VLC wählte die + // Entscheidung sonst einen Spieler, den es nicht gibt — und die + // Wiedergabe bliebe schwarz, statt dass der Server umrechnet. + allowsFallbackEngine: Self.hasFallbackEngine, maximumBitrate: quality.bitrateLimit ) } diff --git a/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadActivityPresenter.swift b/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadActivityPresenter.swift index e4397f0..f1534f7 100644 --- a/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadActivityPresenter.swift +++ b/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadActivityPresenter.swift @@ -1,6 +1,9 @@ import Foundation -#if os(iOS) +// Catalyst ausdrücklich ausgenommen: dort gilt `os(iOS)`, aber ActivityKit +// gibt es nicht — die Typen sind zwar da und jeder Aufruf ist als nicht +// verfügbar markiert. +#if os(iOS) && !targetEnvironment(macCatalyst) // `@preconcurrency`, weil `Activity` und `ActivityContent` keine // Sendable-Zusage tragen. Beide bleiben hier ohnehin auf dem Hauptakteur — // die Klasse ist darauf festgelegt, und ActivityKit erwartet seine Aufrufe diff --git a/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadCoordinator.swift b/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadCoordinator.swift index 824fbf3..6a23a31 100644 --- a/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadCoordinator.swift +++ b/Packages/FlixyKit/Sources/FlixyDownloads/Engine/DownloadCoordinator.swift @@ -149,7 +149,9 @@ public final class DownloadCoordinator { return created } - #if os(iOS) + // Catalyst ausdrücklich ausgenommen: dort gilt `os(iOS)`, aber + // ActivityKit gibt es nicht. + #if os(iOS) && !targetEnvironment(macCatalyst) private let activityPresenter = DownloadActivityPresenter() #endif @@ -564,7 +566,10 @@ public final class DownloadCoordinator { /// gleichzeitigen ist das der, der zuerst fertig wird, und damit der, auf /// den man wartet. private func updateActivity() { - #if os(iOS) + // Catalyst ausdrücklich ausgenommen: dort gilt `os(iOS)`, aber + // ActivityKit gibt es nicht — die Typen sind zwar da, und jeder + // Aufruf ist als nicht verfügbar markiert. + #if os(iOS) && !targetEnvironment(macCatalyst) let running = items.filter { $0.state == .downloading } guard let leading = running.max(by: { ($0.progress ?? 0) < ($1.progress ?? 0) }) else { Task { [activityPresenter] in await activityPresenter.update(with: nil) } diff --git a/Packages/FlixyKit/Sources/FlixyDownloads/Model/DownloadActivityAttributes.swift b/Packages/FlixyKit/Sources/FlixyDownloads/Model/DownloadActivityAttributes.swift index c3fa53c..9281876 100644 --- a/Packages/FlixyKit/Sources/FlixyDownloads/Model/DownloadActivityAttributes.swift +++ b/Packages/FlixyKit/Sources/FlixyDownloads/Model/DownloadActivityAttributes.swift @@ -3,7 +3,10 @@ import Foundation // `os(iOS)` statt `canImport(ActivityKit)`: der Import gelingt auch auf // macOS, die Typen darin sind dort aber als nicht verfügbar markiert. Eine // Prüfung auf den Import allein übersetzt deshalb nicht. -#if os(iOS) +// Catalyst ausdrücklich ausgenommen: dort gilt `os(iOS)`, aber ActivityKit +// gibt es nicht — die Typen sind zwar da und jeder Aufruf ist als nicht +// verfügbar markiert. +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit /// Was im Sperrbildschirm und in der Dynamic Island steht. diff --git a/project.yml b/project.yml index f82ea31..7413425 100644 --- a/project.yml +++ b/project.yml @@ -148,25 +148,27 @@ targets: # Die Live Activity für Downloads. Eine eigene Erweiterung, weil iOS die # Darstellung im Sperrbildschirm in einem getrennten Prozess zeichnet — die # App selbst läuft zu dem Zeitpunkt meist gar nicht. - # Dieselben Quellen, eigenes Ziel. + # Dieselben Quellen, dasselbe Aussehen. # - # Ein gemeinsames Target für beide Plattformen wäre weniger Text, aber die - # Info.plist ist voller iOS-Angaben — Startbildschirm, erlaubte - # Ausrichtungen, Live Activities. Die müssten dann alle bedingt werden, und - # jede spätere Änderung an der iOS-Seite ginge über eine Stelle, die auch - # den Mac betrifft. Zwei Ziele sind ein paar Zeilen mehr und ein Sorge - # weniger. + # Mac Catalyst statt einer eigenen Mac-App: die iPad-Oberfläche läuft damit + # unverändert auf dem Mac — gleiche Schriftgrößen, gleiche Seitenleiste, + # gleiche Abstände. Eine native Mac-App war der erste Versuch; sie sah an + # zwei Dutzend Stellen anders aus, weil AppKit dieselben Dinge anders misst, + # und jede davon hätte einzeln nachgezogen werden müssen. # - # Ohne DownloadWidget (Live Activities gibt es auf dem Mac nicht) und ohne - # FlixyVLC: das Binärpaket ist auf iOS festgelegt. Der Player fällt dort - # allein auf den Systemplayer zurück — was er nicht öffnen kann, sagt er. + # Ohne DownloadWidget (Live Activities gibt es hier nicht) und **ohne + # FlixyVLC**: dessen Binärpaket hat keinen Teil für Catalyst. MKV und DTS + # werden dort deshalb vom Server umgerechnet statt unverändert übertragen — + # siehe `PlaybackSettings.hasFallbackEngine`. FlixyMac: type: application - platform: macOS + platform: iOS sources: - path: Flixy excludes: - "AppIcon.icon" + - path: Flixy/AppIcon.icon + type: file dependencies: - package: FlixyKit product: MediaKit @@ -182,25 +184,42 @@ targets: product: FlixyDownloads settings: base: + SUPPORTS_MACCATALYST: YES + # Nur Mac: dieses Ziel ist nicht dazu da, aufs iPhone zu wandern. + SUPPORTED_PLATFORMS: macosx + # „An das iPad angeglichen" statt „für den Mac optimiert" — genau + # darum geht es. + MACCATALYST_OPTIMIZE_FOR_MAC: NO + DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER: NO PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flixy PRODUCT_NAME: Flixy - # Selbst signiert, ohne Entwicklerkonto: ein Build zum Ansehen soll - # nicht jedes Mal nach dem Schlüsselbund-Passwort fragen. + ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon + TARGETED_DEVICE_FAMILY: "2" + # Selbst signiert: ein Build zum Ansehen soll nicht jedes Mal nach dem + # Schlüsselbund-Passwort fragen. CODE_SIGN_STYLE: Manual CODE_SIGN_IDENTITY: "-" DEVELOPMENT_TEAM: "" ENABLE_HARDENED_RUNTIME: NO - COMBINE_HIDPI_IMAGES: YES info: path: Flixy/Info-macOS.plist properties: CFBundleDisplayName: Flixy + UILaunchScreen: + UIColorName: LaunchBackground + UIUserInterfaceStyle: Dark + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UIBackgroundModes: + - audio LSApplicationCategoryType: public.app-category.entertainment - NSHumanReadableCopyright: "" CFBundleURLTypes: - CFBundleURLName: com.scarriffleservices.flixy.auth CFBundleURLSchemes: - com.scarriffleservices.flixy + ITSAppUsesNonExemptEncryption: false NSAppTransportSecurity: NSAllowsLocalNetworking: true