From a7cf6c5def07b8f72872559d0b791e4c54808e74 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sat, 3 Oct 2026 19:38:27 +0200 Subject: [PATCH] =?UTF-8?q?Halo=20heisst=20ab=20hier=20Flaro=20=E2=80=94?= =?UTF-8?q?=20die=20Marke=20stand=20im=20Weg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Upload zu App Store Connect scheiterte am Namen. „Halo" ist eine eingetragene Marke von Microsoft; im App Store heisst zwar keine App exakt so, aber reservierte und markenrechtlich belegte Namen tauchen in keiner Suche auf. Der einzige verlässliche Test war die Eingabe dort, und die ging daneben. **Der Name wurde diesmal vorher geprüft.** Zwanzig Kandidaten gegen den deutschen und amerikanischen App Store, auf exakte Treffer und auf ähnliche Namen. „Kavo" wäre dieselbe Wand gewesen — exakt belegt in beiden Stores, in Deutschland von KaVo Dental GmbH. „Flare" ist mit 42 ähnlichen Apps je Store der am dichtesten besetzte Name der ganzen Runde. **Flaro** ist in beiden frei und liest sich als eigenes Wort, nicht als Flare mit Tippfehler. Das bleibt eine Store-Suche und keine Markenrecherche — reservierte Namen ohne veröffentlichte App sieht sie nicht. Aber es ist mehr Prüfung, als beim letzten Mal stattfand. --- 2160 Textvorkommen in 273 Dateien, 49 Pfade. Betroffen ist diesmal auch der Server: `Flaro.Api`, `Flaro.Core`, `Flaro.Data`, `Flaro.Media`, `Flaro.Metadata`, `FlaroContext`, `FlaroPaths`, `flaro.db`, die Variablen `FLARO_DATA`, `FLARO_CACHE`, `FLARO_TRANSCODE`, `FLARO_WRITE_SIDECARS`, und das Image. **Vor dem Ersetzen geprüft**, ob „halo" irgendwo als Teil eines anderen Wortes steckt — und ob es im TMDB-Token vorkommt. Tat es nicht; ein blindes Ersetzen hätte den sonst zerlegt. `docs/superpowers/specs/` bleibt wie beim letzten Mal unangetastet: datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen. Das Server-Dokument hat stattdessen einen Nachtrag bekommen, damit es niemanden in die Irre führt. **Die Bundle-ID wandert mit**, zum zweiten Mal an einem Tag: `com.scarriffleservices.flaro`. Wer die vorige Fassung installiert hat, hat jetzt zwei Karteileichen auf dem Gerät — beide löschen, dann ist Ruhe. 308 Server-Tests grün, 368 im Swift-Paket, null Warnungen. iOS und tvOS bauen, das Image baut, und ein frischer Container läuft durch Einrichtung und Anmeldung. Co-Authored-By: Claude Opus 5 --- .gitignore | 6 +- DownloadWidget/DownloadLiveActivity.swift | 4 +- DownloadWidget/Info.plist | 2 +- .../README.md | 4 +- .../contents.xcworkspacedata | 2 +- {Halo => Flaro}/AppIcon.icon/Assets/play.svg | 0 {Halo => Flaro}/AppIcon.icon/icon.json | 0 {Halo => Flaro}/Branding.swift | 8 +- Halo/HaloApp.swift => Flaro/FlaroApp.swift | 20 +-- {Halo => Flaro}/Info.plist | 4 +- .../Resources/Assets.xcassets/Contents.json | 0 .../LaunchBackground.colorset/Contents.json | 0 .../Resources/Localizable.xcstrings | 4 +- .../Session/DownloadRefiller.swift | 2 +- {Halo => Flaro}/Session/DownloadService.swift | 4 +- .../Session/FlaroSession.swift | 30 ++-- {Halo => Flaro}/Session/HeroArtwork.swift | 6 +- {Halo => Flaro}/Session/HomeFeed.swift | 0 .../Session/HomeLayoutSettings.swift | 18 +-- .../Session/JellyfinSignInModel.swift | 4 +- .../Session/LanguagePreferences.swift | 16 +- {Halo => Flaro}/Session/LibraryFeed.swift | 0 .../Session/MediaItem+Presentation.swift | 2 +- .../Session/NavigationLayoutSettings.swift | 4 +- {Halo => Flaro}/Session/NowPlaying.swift | 0 {Halo => Flaro}/Session/OrientationLock.swift | 0 .../Session/PlaybackCoordinator.swift | 0 .../Session/PlaybackDiagnostics.swift | 2 +- .../Session/PlaybackSettings.swift | 28 ++-- {Halo => Flaro}/Session/QuickPlay.swift | 0 {Halo => Flaro}/Session/ScreenSafeArea.swift | 0 {Halo => Flaro}/Session/SearchFeed.swift | 2 +- .../Session/SubtitleAppearance.swift | 6 +- .../Session/SubtitleStyle+Labels.swift | 2 +- {Halo => Flaro}/Session/TopShelfWriter.swift | 4 +- .../Views/Components/ItemActionEntries.swift | 2 +- .../Views/Components/QuickPlayTile.swift | 18 +-- .../Views/Detail/ItemActionsMenu.swift | 4 +- .../Views/Detail/MediaDetailView.swift | 10 +- .../Views/Detail/ParentLinks.swift | 16 +- .../Views/Detail/PlayableDetailView.swift | 80 +++++----- .../Views/Detail/SeriesDetailView.swift | 82 +++++------ .../Views/Detail/TechnicalDetails.swift | 36 ++--- .../Views/Downloads/DownloadButton.swift | 20 +-- .../Downloads/DownloadQualityChoice.swift | 2 +- .../Views/Downloads/DownloadsView.swift | 80 +++++----- {Halo => Flaro}/Views/Home/HeroCarousel.swift | 20 +-- {Halo => Flaro}/Views/Home/HeroSection.swift | 20 +-- {Halo => Flaro}/Views/Home/HomeView.swift | 18 +-- {Halo => Flaro}/Views/Home/MediaRow.swift | 16 +- .../Views/Library/LibraryBrowser.swift | 26 ++-- .../Views/Library/LibraryFilterSheet.swift | 14 +- .../Views/Library/LibraryView.swift | 74 +++++----- .../Views/LiveTV/ChannelSelectionSheet.swift | 24 +-- {Halo => Flaro}/Views/LiveTV/LiveTVView.swift | 92 ++++++------ .../Views/LiveTV/ProgramGuide.swift | 98 ++++++------- .../Views/LiveTV/RecordingsView.swift | 52 +++---- {Halo => Flaro}/Views/LoadState.swift | 2 +- .../Views/Navigation/AccountAvatar.swift | 12 +- .../Views/Navigation/AccountBar.swift | 32 ++-- .../Views/Navigation/FlaroNavigationBar.swift | 138 +++++++++--------- .../Views/Navigation/MediaStack.swift | 6 +- .../Views/Navigation/RootView.swift | 56 +++---- .../Views/Navigation/SidebarDestination.swift | 0 .../Views/Navigation/TabChrome.swift | 0 .../Views/Navigation/TopChrome.swift | 2 +- .../Views/Placeholder/PlaceholderScreen.swift | 6 +- .../Views/Player/AirPlayButton.swift | 0 .../Views/Player/MacFullScreen.swift | 0 {Halo => Flaro}/Views/Player/MiniPlayer.swift | 14 +- .../Views/Player/PlaybackScrubber.swift | 4 +- .../Views/Player/PlayerModel.swift | 8 +- {Halo => Flaro}/Views/Player/PlayerView.swift | 94 ++++++------ .../Views/Player/QualitySheet.swift | 16 +- .../Views/Player/TrackPickerSheet.swift | 18 +-- .../Views/Player/VideoSurface.swift | 2 +- {Halo => Flaro}/Views/Search/SearchView.swift | 24 +-- .../Views/Settings/AppearanceView.swift | 56 +++---- .../Views/Settings/AudioLanguagePicker.swift | 18 +-- .../Views/Settings/DiagnosticsView.swift | 32 ++-- .../Views/Settings/DownloadSettingsView.swift | 12 +- .../Views/Settings/HomeLayoutView.swift | 34 ++--- .../Views/Settings/LanguagesView.swift | 20 +-- .../Views/Settings/PlaybackSettingsView.swift | 18 +-- .../Views/Settings/ServerSettingsView.swift | 22 +-- .../Views/Settings/SettingsChrome.swift | 16 +- .../Views/Settings/SettingsView.swift | 18 +-- .../Settings/SubtitleAppearanceView.swift | 16 +- .../Views/SignIn/ConnectionStateViews.swift | 14 +- .../Views/SignIn/JellyfinSignInView.swift | 120 +++++++-------- {Halo => Flaro}/Views/SignIn/SignInView.swift | 32 ++-- .../Flaro.entitlements | 2 +- .../FlaroTVApp.swift | 18 +-- {HaloTV => FlaroTV}/Info.plist | 8 +- .../Content.imageset/Contents.json | 0 .../Content.imageset/back.png | Bin .../Back.imagestacklayer/Contents.json | 0 .../Contents.json | 0 .../Content.imageset/Contents.json | 0 .../Content.imageset/front.png | Bin .../Front.imagestacklayer/Contents.json | 0 .../Content.imageset/Contents.json | 0 .../Content.imageset/back.png | Bin .../Content.imageset/back@2x.png | Bin .../Back.imagestacklayer/Contents.json | 0 .../App Icon.imagestack/Contents.json | 0 .../Content.imageset/Contents.json | 0 .../Content.imageset/front.png | Bin .../Content.imageset/front@2x.png | Bin .../Front.imagestacklayer/Contents.json | 0 .../Contents.json | 0 .../Contents.json | 0 .../topshelfwide.png | Bin .../topshelfwide@2x.png | Bin .../Top Shelf Image.imageset/Contents.json | 0 .../Top Shelf Image.imageset/topshelf.png | Bin .../Top Shelf Image.imageset/topshelf@2x.png | Bin .../Resources/TVAssets.xcassets/Contents.json | 0 .../Views/Detail/TVDetailView.swift | 8 +- .../Views/Home/TVHeroSection.swift | 8 +- .../Views/Home/TVHomeView.swift | 28 ++-- .../Views/Home/TVMediaRow.swift | 18 +-- .../Views/Library/TVLibraryView.swift | 26 ++-- .../Views/Player/TVPlayerCoordinator.swift | 2 +- .../Views/Player/TVPlayerHost.swift | 2 +- .../Views/Player/TVPlayerScreen.swift | 6 +- .../Views/Player/TVTrackMenuBuilder.swift | 2 +- .../Views/Search/TVSearchView.swift | 24 +-- .../Views/SignIn/TVSignInView.swift | 48 +++--- {HaloTV => FlaroTV}/Views/TVDeepLink.swift | 6 +- {HaloTV => FlaroTV}/Views/TVRootView.swift | 16 +- {HaloTV => FlaroTV}/Views/TVShell.swift | 4 +- .../FlaroTopShelf.entitlements | 2 +- {HaloTopShelf => FlaroTopShelf}/Info.plist | 2 +- .../TopShelfIdentity.swift | 4 +- .../TopShelfProvider.swift | 0 .../LayoutSnapshots.swift | 14 +- .../PlayerSheets.swift | 2 +- LICENSES.md | 2 +- Packages/{HaloKit => FlaroKit}/Package.swift | 24 +-- .../Color/AccentEnvironment.swift | 14 +- .../FlaroDesign}/Color/AccentOption.swift | 10 +- .../Sources/FlaroDesign}/Color/Contrast.swift | 2 +- .../FlaroDesign/Color/FlaroColor.swift} | 26 ++-- .../Sources/FlaroDesign}/Color/HSB.swift | 0 .../Sources/FlaroDesign}/Color/RGB.swift | 0 .../Components/ArtworkLoader.swift | 0 .../Components/FlaroArtwork.swift} | 8 +- .../FlaroDesign}/Components/HeroBanner.swift | 32 ++-- .../Components/LandscapeCard.swift | 14 +- .../Components/PlaceholderCard.swift | 10 +- .../FlaroDesign}/Components/PosterCard.swift | 16 +- .../Components/ShimmerPlaceholder.swift | 2 +- .../FlaroDesign}/Components/TVCards.swift | 18 +-- .../Components/WatchProgressBar.swift | 2 +- .../Sources/FlaroDesign/FlaroFont.swift} | 46 +++--- .../Sources/FlaroDesign/FlaroTheme.swift} | 4 +- .../Sources/FlaroDesign}/GuideLayout.swift | 0 .../FlaroDesign}/Localizable.xcstrings | 0 .../FlaroDesign}/MiniPlayerPlacement.swift | 2 +- .../Sources/FlaroDesign}/PlatformShims.swift | 28 ++-- .../FlaroDownloads}/DownloadRate.swift | 0 .../FlaroDownloads}/DownloadState.swift | 0 .../Engine/DownloadActivityPresenter.swift | 0 .../Engine/DownloadCoordinator.swift | 12 +- .../Engine/DownloadFileStore.swift | 0 .../Engine/DownloadRules.swift | 0 .../FlaroDownloads}/Localizable.xcstrings | 0 .../Model/DownloadActivityAttributes.swift | 2 +- .../FlaroDownloads}/Model/DownloadItem.swift | 0 .../FlaroDownloads}/TransferHold.swift | 0 .../Decision/AudioTrackChooser.swift | 0 .../Decision/DeviceCapabilities.swift | 0 .../Decision/DowngradeRule.swift | 0 .../Decision/LanguageCode.swift | 0 .../Decision/NetworkConditions.swift | 2 +- .../Decision/PlaybackDecider.swift | 0 .../Decision/PlaybackDecision.swift | 0 .../Decision/PlaybackPolicy.swift | 0 .../Decision/PlayerQuality.swift | 0 .../FlaroPlayback}/Decision/StallLedger.swift | 0 .../Decision/StreamHealth.swift | 0 .../FlaroPlayback}/Decision/TrackMenu.swift | 0 .../FlaroPlayback}/Decision/UpgradeRule.swift | 0 .../Engine/AVPlayerEngine.swift | 0 .../FlaroPlayback}/Engine/MediaTrack.swift | 0 .../FlaroPlayback}/Engine/PlaybackState.swift | 0 .../Engine/SubtitleStyle+AVFoundation.swift | 0 .../FlaroPlayback}/Engine/SubtitleStyle.swift | 0 .../Engine/VideoPlayerEngine.swift | 0 .../Prefetch/LocalStreamServer.swift | 2 +- .../Prefetch/MediaPlaylist.swift | 0 .../FlaroPlayback}/Prefetch/StreamCache.swift | 4 +- .../Sources/FlaroProbe}/Leiter.swift | 8 +- .../Sources/FlaroProbe}/main.swift | 2 +- .../Sources/JellyfinKit/JellyfinAddress.swift | 0 .../JellyfinKit/JellyfinAuthenticator.swift | 2 +- .../JellyfinKit/JellyfinBackend+LiveTV.swift | 0 .../JellyfinBackend+Playback.swift | 0 .../JellyfinBackend+Recordings.swift | 0 .../JellyfinBackend+Segments.swift | 0 .../Sources/JellyfinKit/JellyfinBackend.swift | 2 +- .../Sources/JellyfinKit/JellyfinClient.swift | 0 .../JellyfinIdentity+Current.swift | 2 +- .../JellyfinKit/JellyfinItem+Media.swift | 0 .../Sources/JellyfinKit/JellyfinItem.swift | 0 .../JellyfinKit/JellyfinLibraryMemo.swift | 0 .../Sources/JellyfinKit/Localizable.xcstrings | 0 .../Sources/MediaKit/Arrangement.swift | 0 .../Sources/MediaKit/Duration+Units.swift | 0 .../Sources/MediaKit/EpisodeSelection.swift | 0 .../Sources/MediaKit/HeroSelection.swift | 0 .../Sources/MediaKit/LibraryCache.swift | 2 +- .../Sources/MediaKit/LiveTV.swift | 0 .../Sources/MediaKit/MediaBackend.swift | 0 .../Sources/MediaKit/MediaBackendError.swift | 0 .../Sources/MediaKit/MediaItem.swift | 0 .../Sources/MediaKit/MediaLibrary.swift | 0 .../Sources/MediaKit/MediaPlayback.swift | 0 .../Sources/MediaKit/MediaProfile.swift | 0 .../Sources/MediaKit/MediaQuery.swift | 0 .../Sources/MediaKit/MediaVersion.swift | 0 .../Sources/MediaKit/PagingWindow.swift | 0 .../Sources/MediaKit/TokenStore.swift | 0 .../Sources/MediaKit/TopShelfSnapshot.swift | 0 .../Sources/MediaKit/VideoResolution.swift | 0 .../FlaroDesignTests}/ContrastTests.swift | 12 +- .../FlaroPlatformTests.swift} | 6 +- .../FlaroDesignTests}/GuideLayoutTests.swift | 2 +- .../MiniPlayerPlacementTests.swift | 2 +- .../DownloadCoordinatorTests.swift | 6 +- .../DownloadRateTests.swift | 2 +- .../DownloadRulesTests.swift | 2 +- .../DownloadStateTests.swift | 2 +- .../TransferHoldTests.swift | 2 +- .../AudioTrackChooserTests.swift | 2 +- .../DowngradeRuleTests.swift | 2 +- .../LocalStreamServerTests.swift | 2 +- .../MediaPlaylistTests.swift | 2 +- .../PlaybackDeciderTests.swift | 2 +- .../QualityMemoryTests.swift | 2 +- .../StallLedgerTests.swift | 2 +- .../StreamHealthTests.swift | 2 +- .../SubtitleStyleTests.swift | 2 +- .../FlaroPlaybackTests}/TrackMenuTests.swift | 2 +- .../UpgradeRuleTests.swift | 2 +- .../JellyfinKitTests/AdaptiveURLTests.swift | 0 .../Tests/JellyfinKitTests/AddressTests.swift | 0 .../JellyfinKitTests/Fixtures/episode.json | 0 .../JellyfinKitTests/Fixtures/series.json | 0 .../JellyfinKitTests/PlaybackTests.swift | 8 +- .../JellyfinKitTests/TranslationTests.swift | 0 .../Tests/MediaKitTests/.keep | 0 .../MediaKitTests/ArrangementTests.swift | 0 .../MediaKitTests/DurationUnitsTests.swift | 0 .../MediaKitTests/EpisodeSelectionTests.swift | 0 .../MediaKitTests/HeroSelectionTests.swift | 0 .../MediaKitTests/LibraryCacheTests.swift | 2 +- .../MediaKitTests/PagingWindowTests.swift | 0 .../MediaKitTests/TopShelfSnapshotTests.swift | 2 +- .../MediaKitTests/VideoResolutionTests.swift | 0 README.md | 26 ++-- Tools/JellyfinStub/server.py | 2 +- Tools/TVBrandAssets.py | 2 +- Tools/WindowShot/main.swift | 2 +- .../specs/2026-10-03-halo-server-design.md | 9 ++ project.yml | 136 ++++++++--------- server/Dockerfile | 14 +- server/Flaro.slnx | 15 ++ server/Halo.slnx | 15 -- server/README.md | 58 ++++---- server/docker-compose.example.yml | 18 +-- .../Auth/TokenAuthentication.cs | 16 +- .../Endpoints/AuthEndpoints.cs | 32 ++-- .../Endpoints/Contracts.cs | 2 +- .../Endpoints/DeliveryEndpoints.cs | 14 +- .../Endpoints/ImageEndpoints.cs | 4 +- .../Endpoints/LibraryContentEndpoints.cs | 16 +- .../Endpoints/ManagementEndpoints.cs | 26 ++-- .../Endpoints/MatchEndpoints.cs | 24 +-- .../Endpoints/SettingsEndpoints.cs | 18 +-- .../Endpoints/SystemEndpoints.cs | 12 +- .../Flaro.Api.csproj} | 8 +- .../HaloPaths.cs => Flaro.Api/FlaroPaths.cs} | 22 +-- .../{Halo.Api => Flaro.Api}/MatchApplier.cs | 10 +- .../MetadataSettings.cs | 10 +- .../PackageCoordinator.cs | 16 +- server/src/{Halo.Api => Flaro.Api}/Program.cs | 34 ++--- .../Properties/launchSettings.json | 0 .../ScanCoordinator.cs | 4 +- .../appsettings.Development.json | 0 .../{Halo.Api => Flaro.Api}/appsettings.json | 0 .../Auth/AuthCode.cs | 2 +- .../Auth/DeviceToken.cs | 2 +- .../{Halo.Core => Flaro.Core}/Auth/Secrets.cs | 2 +- .../Delivery/DeliveryDecider.cs | 4 +- .../Delivery/DeviceProfile.cs | 2 +- .../Flaro.Core.csproj} | 0 .../Libraries/Library.cs | 4 +- .../Library/FileNames.cs | 2 +- .../Library/IMediaProbe.cs | 2 +- .../Library/ISidecarStore.cs | 2 +- .../Library/LibraryWalker.cs | 2 +- .../Library/MediaEntities.cs | 2 +- .../Library/MediaExtensions.cs | 2 +- .../Library/MediaStreams.cs | 2 +- .../{Halo.Core => Flaro.Core}/Library/Nfo.cs | 4 +- .../Library/ProviderIds.cs | 6 +- .../Library/SidecarNames.cs | 6 +- .../Library/SortTitle.cs | 2 +- .../Metadata/IMetadataProvider.cs | 2 +- .../src/{Halo.Core => Flaro.Core}/Settings.cs | 2 +- .../Users/PasswordHasher.cs | 2 +- .../{Halo.Core => Flaro.Core}/Users/User.cs | 2 +- .../{Halo.Data => Flaro.Data}/Converters.cs | 2 +- .../Flaro.Data.csproj} | 2 +- .../FlaroContext.cs} | 14 +- .../LibraryScanner.cs | 8 +- .../MetadataEnricher.cs | 8 +- .../20261003095500_Initial.Designer.cs | 20 +-- .../Migrations/20261003095500_Initial.cs | 2 +- .../20261003115256_Mediathek.Designer.cs | 44 +++--- .../Migrations/20261003115256_Mediathek.cs | 2 +- .../20261003120100_Metadaten.Designer.cs | 44 +++--- .../Migrations/20261003120100_Metadaten.cs | 2 +- .../20261003123234_GeparsterTitel.Designer.cs | 44 +++--- .../20261003123234_GeparsterTitel.cs | 2 +- .../20261003150124_Spuren.Designer.cs | 50 +++---- .../Migrations/20261003150124_Spuren.cs | 2 +- .../20261003165210_Zuordnung.Designer.cs | 50 +++---- .../Migrations/20261003165210_Zuordnung.cs | 2 +- .../20261003165918_GeparstesJahr.Designer.cs | 50 +++---- .../20261003165918_GeparstesJahr.cs | 2 +- .../Migrations/FlaroContextModelSnapshot.cs} | 52 +++---- .../FfmpegOptions.cs | 6 +- .../FfmpegRemuxer.cs | 2 +- .../FfprobeRunner.cs | 4 +- .../Flaro.Media.csproj} | 2 +- .../RemuxArguments.cs | 6 +- .../SidecarWriter.cs | 16 +- .../Attribution.cs | 2 +- .../Flaro.Metadata.csproj} | 2 +- .../ImageCache.cs | 6 +- .../TmdbClient.cs | 4 +- .../TmdbSettings.cs | 8 +- .../AbgeschalteterAnbieter.cs | 4 +- .../CodeSignInTests.cs | 20 +-- .../DeliveryTests.cs | 10 +- .../Flaro.Api.Tests.csproj} | 6 +- .../FlaroPathsTests.cs} | 22 +-- .../FlaroServer.cs} | 20 +-- .../ManagementTests.cs | 16 +- .../MatchCorrectionTests.cs | 22 +-- .../MetadataTests.cs | 32 ++-- .../ScanTests.cs | 30 ++-- .../SetupTests.cs | 14 +- .../SignInTests.cs | 22 +-- .../AuthCodeTests.cs | 4 +- .../DeliveryDeciderTests.cs | 6 +- .../FileNameTests.cs | 4 +- .../Flaro.Core.Tests.csproj} | 2 +- .../LibraryWalkerTests.cs | 6 +- .../PasswordHasherTests.cs | 4 +- .../SidecarTests.cs | 12 +- .../FfprobeRunnerTests.cs | 6 +- .../Flaro.Media.Tests.csproj} | 4 +- .../RemuxArgumentsTests.cs | 6 +- .../RemuxTests.cs | 8 +- .../SidecarWriterTests.cs | 10 +- .../Testdateien.cs | 6 +- .../Attrappe.cs | 2 +- .../Flaro.Metadata.Tests.csproj} | 4 +- .../ImageCacheTests.cs | 10 +- .../TmdbClientTests.cs | 4 +- .../ZugangTests.cs | 4 +- web/README.md | 6 +- web/app/index.html | 2 +- web/app/package-lock.json | 4 +- web/app/package.json | 2 +- web/app/src/api.ts | 20 +-- web/app/src/contrast.ts | 2 +- web/app/src/main.ts | 2 +- web/app/src/prefs.ts | 4 +- web/app/src/style.css | 4 +- web/app/src/views/login.ts | 2 +- web/app/src/views/player.ts | 4 +- web/app/vite.config.ts | 2 +- web/frontend/index.html | 74 +++++----- web/jellyfin-theme/{halo.css => flaro.css} | 138 +++++++++--------- web/server/index.js | 10 +- 390 files changed, 2078 insertions(+), 2069 deletions(-) rename {Halo.xcworkspace => Flaro.xcworkspace}/README.md (83%) rename {Halo.xcworkspace => Flaro.xcworkspace}/contents.xcworkspacedata (57%) rename {Halo => Flaro}/AppIcon.icon/Assets/play.svg (100%) rename {Halo => Flaro}/AppIcon.icon/icon.json (100%) rename {Halo => Flaro}/Branding.swift (90%) rename Halo/HaloApp.swift => Flaro/FlaroApp.swift (95%) rename {Halo => Flaro}/Info.plist (91%) rename {Halo => Flaro}/Resources/Assets.xcassets/Contents.json (100%) rename {Halo => Flaro}/Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json (100%) rename {Halo => Flaro}/Resources/Localizable.xcstrings (99%) rename {Halo => Flaro}/Session/DownloadRefiller.swift (99%) rename {Halo => Flaro}/Session/DownloadService.swift (99%) rename Halo/Session/HaloSession.swift => Flaro/Session/FlaroSession.swift (95%) rename {Halo => Flaro}/Session/HeroArtwork.swift (96%) rename {Halo => Flaro}/Session/HomeFeed.swift (100%) rename {Halo => Flaro}/Session/HomeLayoutSettings.swift (92%) rename {Halo => Flaro}/Session/JellyfinSignInModel.swift (98%) rename {Halo => Flaro}/Session/LanguagePreferences.swift (93%) rename {Halo => Flaro}/Session/LibraryFeed.swift (100%) rename {Halo => Flaro}/Session/MediaItem+Presentation.swift (98%) rename {Halo => Flaro}/Session/NavigationLayoutSettings.swift (95%) rename {Halo => Flaro}/Session/NowPlaying.swift (100%) rename {Halo => Flaro}/Session/OrientationLock.swift (100%) rename {Halo => Flaro}/Session/PlaybackCoordinator.swift (100%) rename {Halo => Flaro}/Session/PlaybackDiagnostics.swift (99%) rename {Halo => Flaro}/Session/PlaybackSettings.swift (87%) rename {Halo => Flaro}/Session/QuickPlay.swift (100%) rename {Halo => Flaro}/Session/ScreenSafeArea.swift (100%) rename {Halo => Flaro}/Session/SearchFeed.swift (98%) rename {Halo => Flaro}/Session/SubtitleAppearance.swift (91%) rename {Halo => Flaro}/Session/SubtitleStyle+Labels.swift (98%) rename {Halo => Flaro}/Session/TopShelfWriter.swift (97%) rename {Halo => Flaro}/Views/Components/ItemActionEntries.swift (98%) rename {Halo => Flaro}/Views/Components/QuickPlayTile.swift (96%) rename {Halo => Flaro}/Views/Detail/ItemActionsMenu.swift (97%) rename {Halo => Flaro}/Views/Detail/MediaDetailView.swift (89%) rename {Halo => Flaro}/Views/Detail/ParentLinks.swift (85%) rename {Halo => Flaro}/Views/Detail/PlayableDetailView.swift (80%) rename {Halo => Flaro}/Views/Detail/SeriesDetailView.swift (88%) rename {Halo => Flaro}/Views/Detail/TechnicalDetails.swift (84%) rename {Halo => Flaro}/Views/Downloads/DownloadButton.swift (96%) rename {Halo => Flaro}/Views/Downloads/DownloadQualityChoice.swift (99%) rename {Halo => Flaro}/Views/Downloads/DownloadsView.swift (87%) rename {Halo => Flaro}/Views/Home/HeroCarousel.swift (97%) rename {Halo => Flaro}/Views/Home/HeroSection.swift (89%) rename {Halo => Flaro}/Views/Home/HomeView.swift (87%) rename {Halo => Flaro}/Views/Home/MediaRow.swift (85%) rename {Halo => Flaro}/Views/Library/LibraryBrowser.swift (89%) rename {Halo => Flaro}/Views/Library/LibraryFilterSheet.swift (95%) rename {Halo => Flaro}/Views/Library/LibraryView.swift (88%) rename {Halo => Flaro}/Views/LiveTV/ChannelSelectionSheet.swift (88%) rename {Halo => Flaro}/Views/LiveTV/LiveTVView.swift (87%) rename {Halo => Flaro}/Views/LiveTV/ProgramGuide.swift (82%) rename {Halo => Flaro}/Views/LiveTV/RecordingsView.swift (87%) rename {Halo => Flaro}/Views/LoadState.swift (98%) rename {Halo => Flaro}/Views/Navigation/AccountAvatar.swift (88%) rename {Halo => Flaro}/Views/Navigation/AccountBar.swift (83%) rename Halo/Views/Navigation/HaloNavigationBar.swift => Flaro/Views/Navigation/FlaroNavigationBar.swift (79%) rename {Halo => Flaro}/Views/Navigation/MediaStack.swift (91%) rename {Halo => Flaro}/Views/Navigation/RootView.swift (95%) rename {Halo => Flaro}/Views/Navigation/SidebarDestination.swift (100%) rename {Halo => Flaro}/Views/Navigation/TabChrome.swift (100%) rename {Halo => Flaro}/Views/Navigation/TopChrome.swift (98%) rename {Halo => Flaro}/Views/Placeholder/PlaceholderScreen.swift (86%) rename {Halo => Flaro}/Views/Player/AirPlayButton.swift (100%) rename {Halo => Flaro}/Views/Player/MacFullScreen.swift (100%) rename {Halo => Flaro}/Views/Player/MiniPlayer.swift (96%) rename {Halo => Flaro}/Views/Player/PlaybackScrubber.swift (98%) rename {Halo => Flaro}/Views/Player/PlayerModel.swift (99%) rename {Halo => Flaro}/Views/Player/PlayerView.swift (95%) rename {Halo => Flaro}/Views/Player/QualitySheet.swift (96%) rename {Halo => Flaro}/Views/Player/TrackPickerSheet.swift (94%) rename {Halo => Flaro}/Views/Player/VideoSurface.swift (99%) rename {Halo => Flaro}/Views/Search/SearchView.swift (82%) rename {Halo => Flaro}/Views/Settings/AppearanceView.swift (78%) rename {Halo => Flaro}/Views/Settings/AudioLanguagePicker.swift (86%) rename {Halo => Flaro}/Views/Settings/DiagnosticsView.swift (83%) rename {Halo => Flaro}/Views/Settings/DownloadSettingsView.swift (95%) rename {Halo => Flaro}/Views/Settings/HomeLayoutView.swift (90%) rename {Halo => Flaro}/Views/Settings/LanguagesView.swift (90%) rename {Halo => Flaro}/Views/Settings/PlaybackSettingsView.swift (93%) rename {Halo => Flaro}/Views/Settings/ServerSettingsView.swift (90%) rename {Halo => Flaro}/Views/Settings/SettingsChrome.swift (84%) rename {Halo => Flaro}/Views/Settings/SettingsView.swift (92%) rename {Halo => Flaro}/Views/Settings/SubtitleAppearanceView.swift (92%) rename {Halo => Flaro}/Views/SignIn/ConnectionStateViews.swift (75%) rename {Halo => Flaro}/Views/SignIn/JellyfinSignInView.swift (71%) rename {Halo => Flaro}/Views/SignIn/SignInView.swift (67%) rename HaloTV/Halo.entitlements => FlaroTV/Flaro.entitlements (82%) rename HaloTV/HaloTVApp.swift => FlaroTV/FlaroTVApp.swift (87%) rename {HaloTV => FlaroTV}/Info.plist (84%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/back.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/front.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back@2x.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front@2x.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide@2x.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf@2x.png (100%) rename {HaloTV => FlaroTV}/Resources/TVAssets.xcassets/Contents.json (100%) rename {HaloTV => FlaroTV}/Views/Detail/TVDetailView.swift (87%) rename {HaloTV => FlaroTV}/Views/Home/TVHeroSection.swift (92%) rename {HaloTV => FlaroTV}/Views/Home/TVHomeView.swift (87%) rename {HaloTV => FlaroTV}/Views/Home/TVMediaRow.swift (83%) rename {HaloTV => FlaroTV}/Views/Library/TVLibraryView.swift (85%) rename {HaloTV => FlaroTV}/Views/Player/TVPlayerCoordinator.swift (99%) rename {HaloTV => FlaroTV}/Views/Player/TVPlayerHost.swift (99%) rename {HaloTV => FlaroTV}/Views/Player/TVPlayerScreen.swift (97%) rename {HaloTV => FlaroTV}/Views/Player/TVTrackMenuBuilder.swift (99%) rename {HaloTV => FlaroTV}/Views/Search/TVSearchView.swift (83%) rename {HaloTV => FlaroTV}/Views/SignIn/TVSignInView.swift (77%) rename {HaloTV => FlaroTV}/Views/TVDeepLink.swift (88%) rename {HaloTV => FlaroTV}/Views/TVRootView.swift (88%) rename {HaloTV => FlaroTV}/Views/TVShell.swift (96%) rename HaloTopShelf/HaloTopShelf.entitlements => FlaroTopShelf/FlaroTopShelf.entitlements (82%) rename {HaloTopShelf => FlaroTopShelf}/Info.plist (97%) rename {HaloTopShelf => FlaroTopShelf}/TopShelfIdentity.swift (89%) rename {HaloTopShelf => FlaroTopShelf}/TopShelfProvider.swift (100%) rename {HaloUITests => FlaroUITests}/LayoutSnapshots.swift (94%) rename {HaloUITests => FlaroUITests}/PlayerSheets.swift (95%) rename Packages/{HaloKit => FlaroKit}/Package.swift (73%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Color/AccentEnvironment.swift (65%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Color/AccentOption.swift (92%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Color/Contrast.swift (97%) rename Packages/{HaloKit/Sources/HaloDesign/Color/HaloColor.swift => FlaroKit/Sources/FlaroDesign/Color/FlaroColor.swift} (59%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Color/HSB.swift (100%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Color/RGB.swift (100%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/ArtworkLoader.swift (100%) rename Packages/{HaloKit/Sources/HaloDesign/Components/HaloArtwork.swift => FlaroKit/Sources/FlaroDesign/Components/FlaroArtwork.swift} (96%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/HeroBanner.swift (91%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/LandscapeCard.swift (73%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/PlaceholderCard.swift (89%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/PosterCard.swift (74%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/ShimmerPlaceholder.swift (97%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/TVCards.swift (90%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Components/WatchProgressBar.swift (94%) rename Packages/{HaloKit/Sources/HaloDesign/HaloFont.swift => FlaroKit/Sources/FlaroDesign/FlaroFont.swift} (82%) rename Packages/{HaloKit/Sources/HaloDesign/HaloTheme.swift => FlaroKit/Sources/FlaroDesign/FlaroTheme.swift} (93%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/GuideLayout.swift (100%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/Localizable.xcstrings (100%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/MiniPlayerPlacement.swift (97%) rename Packages/{HaloKit/Sources/HaloDesign => FlaroKit/Sources/FlaroDesign}/PlatformShims.swift (89%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/DownloadRate.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/DownloadState.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Engine/DownloadActivityPresenter.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Engine/DownloadCoordinator.swift (98%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Engine/DownloadFileStore.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Engine/DownloadRules.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Localizable.xcstrings (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Model/DownloadActivityAttributes.swift (96%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/Model/DownloadItem.swift (100%) rename Packages/{HaloKit/Sources/HaloDownloads => FlaroKit/Sources/FlaroDownloads}/TransferHold.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/AudioTrackChooser.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/DeviceCapabilities.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/DowngradeRule.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/LanguageCode.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/NetworkConditions.swift (94%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/PlaybackDecider.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/PlaybackDecision.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/PlaybackPolicy.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/PlayerQuality.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/StallLedger.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/StreamHealth.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/TrackMenu.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Decision/UpgradeRule.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/AVPlayerEngine.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/MediaTrack.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/PlaybackState.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/SubtitleStyle+AVFoundation.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/SubtitleStyle.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Engine/VideoPlayerEngine.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Prefetch/LocalStreamServer.swift (99%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Prefetch/MediaPlaylist.swift (100%) rename Packages/{HaloKit/Sources/HaloPlayback => FlaroKit/Sources/FlaroPlayback}/Prefetch/StreamCache.swift (98%) rename Packages/{HaloKit/Sources/HaloProbe => FlaroKit/Sources/FlaroProbe}/Leiter.swift (99%) rename Packages/{HaloKit/Sources/HaloProbe => FlaroKit/Sources/FlaroProbe}/main.swift (96%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinAddress.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinAuthenticator.swift (98%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinBackend+LiveTV.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinBackend+Playback.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinBackend+Recordings.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinBackend+Segments.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinBackend.swift (99%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinClient.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinIdentity+Current.swift (95%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinItem+Media.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinItem.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/JellyfinLibraryMemo.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/JellyfinKit/Localizable.xcstrings (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/Arrangement.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/Duration+Units.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/EpisodeSelection.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/HeroSelection.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/LibraryCache.swift (98%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/LiveTV.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaBackend.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaBackendError.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaItem.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaLibrary.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaPlayback.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaProfile.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaQuery.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/MediaVersion.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/PagingWindow.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/TokenStore.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/TopShelfSnapshot.swift (100%) rename Packages/{HaloKit => FlaroKit}/Sources/MediaKit/VideoResolution.swift (100%) rename Packages/{HaloKit/Tests/HaloDesignTests => FlaroKit/Tests/FlaroDesignTests}/ContrastTests.swift (96%) rename Packages/{HaloKit/Tests/HaloDesignTests/HaloPlatformTests.swift => FlaroKit/Tests/FlaroDesignTests/FlaroPlatformTests.swift} (87%) rename Packages/{HaloKit/Tests/HaloDesignTests => FlaroKit/Tests/FlaroDesignTests}/GuideLayoutTests.swift (99%) rename Packages/{HaloKit/Tests/HaloDesignTests => FlaroKit/Tests/FlaroDesignTests}/MiniPlayerPlacementTests.swift (99%) rename Packages/{HaloKit/Tests/HaloDownloadsTests => FlaroKit/Tests/FlaroDownloadsTests}/DownloadCoordinatorTests.swift (98%) rename Packages/{HaloKit/Tests/HaloDownloadsTests => FlaroKit/Tests/FlaroDownloadsTests}/DownloadRateTests.swift (99%) rename Packages/{HaloKit/Tests/HaloDownloadsTests => FlaroKit/Tests/FlaroDownloadsTests}/DownloadRulesTests.swift (99%) rename Packages/{HaloKit/Tests/HaloDownloadsTests => FlaroKit/Tests/FlaroDownloadsTests}/DownloadStateTests.swift (99%) rename Packages/{HaloKit/Tests/HaloDownloadsTests => FlaroKit/Tests/FlaroDownloadsTests}/TransferHoldTests.swift (97%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/AudioTrackChooserTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/DowngradeRuleTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/LocalStreamServerTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/MediaPlaylistTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/PlaybackDeciderTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/QualityMemoryTests.swift (98%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/StallLedgerTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/StreamHealthTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/SubtitleStyleTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/TrackMenuTests.swift (99%) rename Packages/{HaloKit/Tests/HaloPlaybackTests => FlaroKit/Tests/FlaroPlaybackTests}/UpgradeRuleTests.swift (99%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/AdaptiveURLTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/AddressTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/Fixtures/episode.json (100%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/Fixtures/series.json (100%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/PlaybackTests.swift (98%) rename Packages/{HaloKit => FlaroKit}/Tests/JellyfinKitTests/TranslationTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/.keep (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/ArrangementTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/DurationUnitsTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/EpisodeSelectionTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/HeroSelectionTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/LibraryCacheTests.swift (99%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/PagingWindowTests.swift (100%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/TopShelfSnapshotTests.swift (97%) rename Packages/{HaloKit => FlaroKit}/Tests/MediaKitTests/VideoResolutionTests.swift (100%) create mode 100644 server/Flaro.slnx delete mode 100644 server/Halo.slnx rename server/src/{Halo.Api => Flaro.Api}/Auth/TokenAuthentication.cs (91%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/AuthEndpoints.cs (89%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/Contracts.cs (99%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/DeliveryEndpoints.cs (93%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/ImageEndpoints.cs (95%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/LibraryContentEndpoints.cs (94%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/ManagementEndpoints.cs (88%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/MatchEndpoints.cs (84%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/SettingsEndpoints.cs (84%) rename server/src/{Halo.Api => Flaro.Api}/Endpoints/SystemEndpoints.cs (93%) rename server/src/{Halo.Api/Halo.Api.csproj => Flaro.Api/Flaro.Api.csproj} (65%) rename server/src/{Halo.Api/HaloPaths.cs => Flaro.Api/FlaroPaths.cs} (84%) rename server/src/{Halo.Api => Flaro.Api}/MatchApplier.cs (98%) rename server/src/{Halo.Api => Flaro.Api}/MetadataSettings.cs (82%) rename server/src/{Halo.Api => Flaro.Api}/PackageCoordinator.cs (96%) rename server/src/{Halo.Api => Flaro.Api}/Program.cs (84%) rename server/src/{Halo.Api => Flaro.Api}/Properties/launchSettings.json (100%) rename server/src/{Halo.Api => Flaro.Api}/ScanCoordinator.cs (98%) rename server/src/{Halo.Api => Flaro.Api}/appsettings.Development.json (100%) rename server/src/{Halo.Api => Flaro.Api}/appsettings.json (100%) rename server/src/{Halo.Core => Flaro.Core}/Auth/AuthCode.cs (98%) rename server/src/{Halo.Core => Flaro.Core}/Auth/DeviceToken.cs (97%) rename server/src/{Halo.Core => Flaro.Core}/Auth/Secrets.cs (97%) rename server/src/{Halo.Core => Flaro.Core}/Delivery/DeliveryDecider.cs (98%) rename server/src/{Halo.Core => Flaro.Core}/Delivery/DeviceProfile.cs (98%) rename server/src/{Halo.Core/Halo.Core.csproj => Flaro.Core/Flaro.Core.csproj} (100%) rename server/src/{Halo.Core => Flaro.Core}/Libraries/Library.cs (85%) rename server/src/{Halo.Core => Flaro.Core}/Library/FileNames.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Library/IMediaProbe.cs (96%) rename server/src/{Halo.Core => Flaro.Core}/Library/ISidecarStore.cs (97%) rename server/src/{Halo.Core => Flaro.Core}/Library/LibraryWalker.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Library/MediaEntities.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Library/MediaExtensions.cs (98%) rename server/src/{Halo.Core => Flaro.Core}/Library/MediaStreams.cs (98%) rename server/src/{Halo.Core => Flaro.Core}/Library/Nfo.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Library/ProviderIds.cs (91%) rename server/src/{Halo.Core => Flaro.Core}/Library/SidecarNames.cs (94%) rename server/src/{Halo.Core => Flaro.Core}/Library/SortTitle.cs (97%) rename server/src/{Halo.Core => Flaro.Core}/Metadata/IMetadataProvider.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Settings.cs (95%) rename server/src/{Halo.Core => Flaro.Core}/Users/PasswordHasher.cs (99%) rename server/src/{Halo.Core => Flaro.Core}/Users/User.cs (96%) rename server/src/{Halo.Data => Flaro.Data}/Converters.cs (99%) rename server/src/{Halo.Data/Halo.Data.csproj => Flaro.Data/Flaro.Data.csproj} (90%) rename server/src/{Halo.Data/HaloContext.cs => Flaro.Data/FlaroContext.cs} (96%) rename server/src/{Halo.Data => Flaro.Data}/LibraryScanner.cs (98%) rename server/src/{Halo.Data => Flaro.Data}/MetadataEnricher.cs (99%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003095500_Initial.Designer.cs (91%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003095500_Initial.cs (99%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003115256_Mediathek.Designer.cs (89%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003115256_Mediathek.cs (99%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003120100_Metadaten.Designer.cs (90%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003120100_Metadaten.cs (98%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003123234_GeparsterTitel.Designer.cs (90%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003123234_GeparsterTitel.cs (97%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003150124_Spuren.Designer.cs (90%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003150124_Spuren.cs (99%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003165210_Zuordnung.Designer.cs (90%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003165210_Zuordnung.cs (98%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003165918_GeparstesJahr.Designer.cs (90%) rename server/src/{Halo.Data => Flaro.Data}/Migrations/20261003165918_GeparstesJahr.cs (96%) rename server/src/{Halo.Data/Migrations/HaloContextModelSnapshot.cs => Flaro.Data/Migrations/FlaroContextModelSnapshot.cs} (90%) rename server/src/{Halo.Media => Flaro.Media}/FfmpegOptions.cs (81%) rename server/src/{Halo.Media => Flaro.Media}/FfmpegRemuxer.cs (99%) rename server/src/{Halo.Media => Flaro.Media}/FfprobeRunner.cs (99%) rename server/src/{Halo.Media/Halo.Media.csproj => Flaro.Media/Flaro.Media.csproj} (84%) rename server/src/{Halo.Media => Flaro.Media}/RemuxArguments.cs (98%) rename server/src/{Halo.Media => Flaro.Media}/SidecarWriter.cs (91%) rename server/src/{Halo.Metadata => Flaro.Metadata}/Attribution.cs (97%) rename server/src/{Halo.Metadata/Halo.Metadata.csproj => Flaro.Metadata/Flaro.Metadata.csproj} (83%) rename server/src/{Halo.Metadata => Flaro.Metadata}/ImageCache.cs (98%) rename server/src/{Halo.Metadata => Flaro.Metadata}/TmdbClient.cs (99%) rename server/src/{Halo.Metadata => Flaro.Metadata}/TmdbSettings.cs (90%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/AbgeschalteterAnbieter.cs (97%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/CodeSignInTests.cs (92%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/DeliveryTests.cs (96%) rename server/tests/{Halo.Api.Tests/Halo.Api.Tests.csproj => Flaro.Api.Tests/Flaro.Api.Tests.csproj} (79%) rename server/tests/{Halo.Api.Tests/HaloPathsTests.cs => Flaro.Api.Tests/FlaroPathsTests.cs} (83%) rename server/tests/{Halo.Api.Tests/HaloServer.cs => Flaro.Api.Tests/FlaroServer.cs} (85%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/ManagementTests.cs (92%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/MatchCorrectionTests.cs (94%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/MetadataTests.cs (94%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/ScanTests.cs (93%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/SetupTests.cs (89%) rename server/tests/{Halo.Api.Tests => Flaro.Api.Tests}/SignInTests.cs (91%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/AuthCodeTests.cs (98%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/DeliveryDeciderTests.cs (98%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/FileNameTests.cs (99%) rename server/tests/{Halo.Core.Tests/Halo.Core.Tests.csproj => Flaro.Core.Tests/Flaro.Core.Tests.csproj} (91%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/LibraryWalkerTests.cs (97%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/PasswordHasherTests.cs (98%) rename server/tests/{Halo.Core.Tests => Flaro.Core.Tests}/SidecarTests.cs (95%) rename server/tests/{Halo.Media.Tests => Flaro.Media.Tests}/FfprobeRunnerTests.cs (97%) rename server/tests/{Halo.Media.Tests/Halo.Media.Tests.csproj => Flaro.Media.Tests/Flaro.Media.Tests.csproj} (84%) rename server/tests/{Halo.Media.Tests => Flaro.Media.Tests}/RemuxArgumentsTests.cs (98%) rename server/tests/{Halo.Media.Tests => Flaro.Media.Tests}/RemuxTests.cs (97%) rename server/tests/{Halo.Media.Tests => Flaro.Media.Tests}/SidecarWriterTests.cs (94%) rename server/tests/{Halo.Media.Tests => Flaro.Media.Tests}/Testdateien.cs (96%) rename server/tests/{Halo.Metadata.Tests => Flaro.Metadata.Tests}/Attrappe.cs (98%) rename server/tests/{Halo.Metadata.Tests/Halo.Metadata.Tests.csproj => Flaro.Metadata.Tests/Flaro.Metadata.Tests.csproj} (82%) rename server/tests/{Halo.Metadata.Tests => Flaro.Metadata.Tests}/ImageCacheTests.cs (95%) rename server/tests/{Halo.Metadata.Tests => Flaro.Metadata.Tests}/TmdbClientTests.cs (99%) rename server/tests/{Halo.Metadata.Tests => Flaro.Metadata.Tests}/ZugangTests.cs (98%) rename web/jellyfin-theme/{halo.css => flaro.css} (69%) diff --git a/.gitignore b/.gitignore index 7c0035e..7aef122 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,10 @@ xcuserdata/ # Von XcodeGen aus project.yml erzeugt — nicht eingecheckt, damit es keine # Konflikte in der pbxproj gibt. Nach dem Klonen: xcodegen generate # -# Halo.xcworkspace daneben ist dagegen von Hand geschrieben und gehört dazu: +# Flaro.xcworkspace daneben ist dagegen von Hand geschrieben und gehört dazu: # er verweist nur auf das Projekt und überlebt jedes Neuerzeugen. Warum das -# nötig ist, steht in Halo.xcworkspace/README.md. -Halo.xcodeproj/ +# nötig ist, steht in Flaro.xcworkspace/README.md. +Flaro.xcodeproj/ node_modules/ # .NET diff --git a/DownloadWidget/DownloadLiveActivity.swift b/DownloadWidget/DownloadLiveActivity.swift index e9b34b7..3c510eb 100644 --- a/DownloadWidget/DownloadLiveActivity.swift +++ b/DownloadWidget/DownloadLiveActivity.swift @@ -1,8 +1,8 @@ import ActivityKit import SwiftUI import WidgetKit -import HaloDesign -import HaloDownloads +import FlaroDesign +import FlaroDownloads /// Der Download-Fortschritt im Sperrbildschirm und in der Dynamic Island. struct DownloadLiveActivity: Widget { diff --git a/DownloadWidget/Info.plist b/DownloadWidget/Info.plist index 49e5165..8b79382 100644 --- a/DownloadWidget/Info.plist +++ b/DownloadWidget/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Halo Downloads + Flaro Downloads CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/Halo.xcworkspace/README.md b/Flaro.xcworkspace/README.md similarity index 83% rename from Halo.xcworkspace/README.md rename to Flaro.xcworkspace/README.md index efd8ddd..40f7acf 100644 --- a/Halo.xcworkspace/README.md +++ b/Flaro.xcworkspace/README.md @@ -1,9 +1,9 @@ # Diesen Arbeitsbereich öffnen, nicht das Projekt -`Halo.xcodeproj` wird von XcodeGen aus `project.yml` erzeugt — und dabei +`Flaro.xcodeproj` wird von XcodeGen aus `project.yml` erzeugt — und dabei **ersetzt**, nicht bearbeitet: jede Datei darin bekommt einen neuen Inode, auch die, deren Inhalt gleich bleibt. Hat Xcode den Arbeitsbereich **im** -Projekt offen (`Halo.xcodeproj/project.xcworkspace`), meldet es danach +Projekt offen (`Flaro.xcodeproj/project.xcworkspace`), meldet es danach > The workspace file … has disappeared. Do you want to re-save the container, > or close it? diff --git a/Halo.xcworkspace/contents.xcworkspacedata b/Flaro.xcworkspace/contents.xcworkspacedata similarity index 57% rename from Halo.xcworkspace/contents.xcworkspacedata rename to Flaro.xcworkspace/contents.xcworkspacedata index 7416b61..33fcc87 100644 --- a/Halo.xcworkspace/contents.xcworkspacedata +++ b/Flaro.xcworkspace/contents.xcworkspacedata @@ -1,4 +1,4 @@ - + diff --git a/Halo/AppIcon.icon/Assets/play.svg b/Flaro/AppIcon.icon/Assets/play.svg similarity index 100% rename from Halo/AppIcon.icon/Assets/play.svg rename to Flaro/AppIcon.icon/Assets/play.svg diff --git a/Halo/AppIcon.icon/icon.json b/Flaro/AppIcon.icon/icon.json similarity index 100% rename from Halo/AppIcon.icon/icon.json rename to Flaro/AppIcon.icon/icon.json diff --git a/Halo/Branding.swift b/Flaro/Branding.swift similarity index 90% rename from Halo/Branding.swift rename to Flaro/Branding.swift index 91a3608..5ecdce0 100644 --- a/Halo/Branding.swift +++ b/Flaro/Branding.swift @@ -9,21 +9,21 @@ import Foundation /// /// Der Name steht noch nicht fest. Damit ein späterer Wechsel nicht zur Suche /// quer durch den Quelltext wird, greift die App nirgends auf ein hart -/// geschriebenes „Halo" zu, sondern immer hierauf. Zu ändern sind dann genau +/// geschriebenes „Flaro" zu, sondern immer hierauf. Zu ändern sind dann genau /// zwei Dinge: diese Datei und `project.yml` (Produktname, Bundle-Kennung, /// Anzeigename). /// -/// Die Typen aus HaloKit behalten ihre Namen — sie sind Bauteile, keine Marke. +/// Die Typen aus FlaroKit behalten ihre Namen — sie sind Bauteile, keine Marke. enum Branding { /// Wie die App sich selbst nennt. - static let appName = "Halo" + static let appName = "Flaro" /// Unter welchem Namen die Tokens im Schlüsselbund liegen. /// /// An die Bundle-Kennung gebunden statt an den Anzeigenamen: eine spätere /// Umbenennung der App soll niemanden abmelden. static var keychainService: String { - Bundle.main.bundleIdentifier ?? "com.scarriffleservices.halo" + Bundle.main.bundleIdentifier ?? "com.scarriffleservices.flaro" } /// Wann dieser Build entstanden ist. diff --git a/Halo/HaloApp.swift b/Flaro/FlaroApp.swift similarity index 95% rename from Halo/HaloApp.swift rename to Flaro/FlaroApp.swift index f0c5f6d..fca3768 100644 --- a/Halo/HaloApp.swift +++ b/Flaro/FlaroApp.swift @@ -3,14 +3,14 @@ import SwiftUI #if os(iOS) import UIKit #endif -import HaloDesign -import HaloDownloads -import HaloPlayback +import FlaroDesign +import FlaroDownloads +import FlaroPlayback import JellyfinKit import MediaKit @main -struct HaloApp: App { +struct FlaroApp: App { #if os(iOS) /// Nimmt das Signal entgegen, mit dem iOS die App zum Abschluss von /// Hintergrund-Downloads aufweckt. Dafür gibt es in SwiftUI keinen @@ -23,8 +23,8 @@ struct HaloApp: App { /// Leben hier, nicht in einer View: sie gelten für die gesamte App, und /// ein Neuaufbau der Wurzelansicht darf weder die Anmeldung noch laufende /// Downloads zurücksetzen. - @State private var theme = HaloTheme() - @State private var session: HaloSession + @State private var theme = FlaroTheme() + @State private var session: FlaroSession @State private var playback = PlaybackCoordinator() @State private var playbackSettings = PlaybackSettings() @State private var languages = LanguagePreferences() @@ -40,7 +40,7 @@ struct HaloApp: App { private let modelContainer: ModelContainer init() { - _session = State(initialValue: HaloSession( + _session = State(initialValue: FlaroSession( jellyfinIdentity: JellyfinIdentity.current( client: Branding.appName, version: Branding.version @@ -74,8 +74,8 @@ struct HaloApp: App { // Notizzettelgröße, und darin fällt der Aufmacher in sich // zusammen, weil seine Abstände dehnbar sind. .frame( - minWidth: HaloPlatform.isMac ? 900 : nil, - minHeight: HaloPlatform.isMac ? 620 : nil + minWidth: FlaroPlatform.isMac ? 900 : nil, + minHeight: FlaroPlatform.isMac ? 620 : nil ) // `defaultSize` gibt es hier nicht; die Größe hängt an der // Szene. @@ -133,7 +133,7 @@ struct HaloApp: App { } #if os(iOS) -extension HaloApp { +extension FlaroApp { /// Gibt dem Fenster eine vernünftige Mindestgröße. /// /// Auf dem Mac öffnet das System sonst ein Fenster von der Größe eines diff --git a/Halo/Info.plist b/Flaro/Info.plist similarity index 91% rename from Halo/Info.plist rename to Flaro/Info.plist index 6d76d2a..6ad3fbc 100644 --- a/Halo/Info.plist +++ b/Flaro/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Halo + Flaro CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -28,7 +28,7 @@ NSLocalNetworkUsageDescription - Halo benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen und Medien direkt von ihm zu laden. + Flaro benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen und Medien direkt von ihm zu laden. NSSupportsLiveActivities UIBackgroundModes diff --git a/Halo/Resources/Assets.xcassets/Contents.json b/Flaro/Resources/Assets.xcassets/Contents.json similarity index 100% rename from Halo/Resources/Assets.xcassets/Contents.json rename to Flaro/Resources/Assets.xcassets/Contents.json diff --git a/Halo/Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json b/Flaro/Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json similarity index 100% rename from Halo/Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json rename to Flaro/Resources/Assets.xcassets/LaunchBackground.colorset/Contents.json diff --git a/Halo/Resources/Localizable.xcstrings b/Flaro/Resources/Localizable.xcstrings similarity index 99% rename from Halo/Resources/Localizable.xcstrings rename to Flaro/Resources/Localizable.xcstrings index 91d8409..e2d6299 100644 --- a/Halo/Resources/Localizable.xcstrings +++ b/Flaro/Resources/Localizable.xcstrings @@ -5647,13 +5647,13 @@ "de": { "stringUnit": { "state": "translated", - "value": "Die Obergrenze, nicht das Ziel. Im eigenen Netz überträgt der Server das Original; unterwegs beginnt Halo kleiner und tastet sich hinauf, solange die Verbindung es trägt — höchstens bis zu dieser Stufe. Auflösung und Bitrate gehen dabei zusammen." + "value": "Die Obergrenze, nicht das Ziel. Im eigenen Netz überträgt der Server das Original; unterwegs beginnt Flaro kleiner und tastet sich hinauf, solange die Verbindung es trägt — höchstens bis zu dieser Stufe. Auflösung und Bitrate gehen dabei zusammen." } }, "en": { "stringUnit": { "state": "translated", - "value": "An upper limit, not a target. On your own network the server sends the original; away from home Halo starts lower and works its way up as long as the connection holds — never above this step. Resolution and bitrate move together." + "value": "An upper limit, not a target. On your own network the server sends the original; away from home Flaro starts lower and works its way up as long as the connection holds — never above this step. Resolution and bitrate move together." } } } diff --git a/Halo/Session/DownloadRefiller.swift b/Flaro/Session/DownloadRefiller.swift similarity index 99% rename from Halo/Session/DownloadRefiller.swift rename to Flaro/Session/DownloadRefiller.swift index c7d03b4..535e728 100644 --- a/Halo/Session/DownloadRefiller.swift +++ b/Flaro/Session/DownloadRefiller.swift @@ -1,5 +1,5 @@ import Foundation -import HaloDownloads +import FlaroDownloads import MediaKit /// Füllt den Vorrat an ungesehenen Folgen wieder auf. diff --git a/Halo/Session/DownloadService.swift b/Flaro/Session/DownloadService.swift similarity index 99% rename from Halo/Session/DownloadService.swift rename to Flaro/Session/DownloadService.swift index 06394c5..84075be 100644 --- a/Halo/Session/DownloadService.swift +++ b/Flaro/Session/DownloadService.swift @@ -1,6 +1,6 @@ import Foundation -import HaloDownloads -import HaloPlayback +import FlaroDownloads +import FlaroPlayback import MediaKit /// Übersetzt Titel vom Server in Download-Aufträge. diff --git a/Halo/Session/HaloSession.swift b/Flaro/Session/FlaroSession.swift similarity index 95% rename from Halo/Session/HaloSession.swift rename to Flaro/Session/FlaroSession.swift index 4fc1e90..f062875 100644 --- a/Halo/Session/HaloSession.swift +++ b/Flaro/Session/FlaroSession.swift @@ -15,7 +15,7 @@ import Observation /// Umschalten ja, nebeneinander nein. @MainActor @Observable -final class HaloSession { +final class FlaroSession { enum Phase: Equatable { /// Beim Start: liegt ein Token im Schlüsselbund? case launching @@ -32,11 +32,11 @@ final class HaloSession { } private enum Key { - static let jellyfinAddress = "halo.jellyfin.address" - static let jellyfinUserID = "halo.jellyfin.userID" - static let jellyfinUserName = "halo.jellyfin.userName" - static let jellyfinServerName = "halo.jellyfin.serverName" - static let jellyfinIsAdministrator = "halo.jellyfin.isAdministrator" + static let jellyfinAddress = "flaro.jellyfin.address" + static let jellyfinUserID = "flaro.jellyfin.userID" + static let jellyfinUserName = "flaro.jellyfin.userName" + static let jellyfinServerName = "flaro.jellyfin.serverName" + static let jellyfinIsAdministrator = "flaro.jellyfin.isAdministrator" } /// Unter welchem Namen das Server-Token im Schlüsselbund liegt. @@ -55,14 +55,14 @@ final class HaloSession { /// könnte. Deshalb wird einmalig aufgeräumt, bevor die Namen endgültig /// verschwinden. private enum Altlast { - static let merker = "halo.migration.plexPurged" + static let merker = "flaro.migration.plexPurged" static let tokenKonten = ["plex.account", "plex.server"] static let schlüssel = [ - "halo.backend.kind", - "halo.server.clientIdentifier", - "halo.server.connectionURI", - "halo.server.name", - "halo.server.connectionIsLocal", + "flaro.backend.kind", + "flaro.server.clientIdentifier", + "flaro.server.connectionURI", + "flaro.server.name", + "flaro.server.connectionIsLocal", ] } @@ -93,7 +93,7 @@ final class HaloSession { /// Wer angemeldet ist — sofern der Server einen Namen dafür kennt. var accountName: String? { jellyfinAccount?.userName } - /// Die Mediatheken, die Halo darstellen kann — Musik und Fotos + /// Die Mediatheken, die Flaro darstellen kann — Musik und Fotos /// erscheinen gar nicht erst. private(set) var libraries: [MediaLibrary] = [] @@ -233,14 +233,14 @@ final class HaloSession { /// Meldet sich an der Attrappe aus `Tools/JellyfinStub` an. /// /// Nur in der Entwicklung und nur auf ausdrückliches Kommando: - /// `-HaloStubServer http://127.0.0.1:8096` als Startargument. Der Weg + /// `-FlaroStubServer http://127.0.0.1:8096` als Startargument. Der Weg /// führt bewusst **am Schlüsselbund vorbei** und schreibt nichts fort — /// so lässt sich die Oberfläche ansehen, prüfen und fotografieren, ohne /// dass je ein echter Server, ein echtes Konto oder ein echter Token im /// Spiel wäre. Genau daran hat es bisher gefehlt: Aussagen über das /// Aussehen waren Vermutungen, weil niemand hinsehen konnte. private func adoptStubServerIfRequested() async -> Bool { - guard let raw = defaults.string(forKey: "HaloStubServer"), + guard let raw = defaults.string(forKey: "FlaroStubServer"), let baseURL = URL(string: raw) else { return false } diff --git a/Halo/Session/HeroArtwork.swift b/Flaro/Session/HeroArtwork.swift similarity index 96% rename from Halo/Session/HeroArtwork.swift rename to Flaro/Session/HeroArtwork.swift index 1d1eed0..0f75e99 100644 --- a/Halo/Session/HeroArtwork.swift +++ b/Flaro/Session/HeroArtwork.swift @@ -1,5 +1,5 @@ import Foundation -import HaloDesign +import FlaroDesign import MediaKit /// Woher der Aufmacher seine Bilder nimmt. @@ -64,8 +64,8 @@ enum HeroArtwork { } static func aspectRatio(_ placement: Placement = .carousel, compact: Bool) -> CGFloat { - if isPortrait(placement, compact: compact) { return HaloAspectRatio.poster } - return compact ? HaloAspectRatio.heroCompact : HaloAspectRatio.hero + if isPortrait(placement, compact: compact) { return FlaroAspectRatio.poster } + return compact ? FlaroAspectRatio.heroCompact : FlaroAspectRatio.hero } static func backdropURL( diff --git a/Halo/Session/HomeFeed.swift b/Flaro/Session/HomeFeed.swift similarity index 100% rename from Halo/Session/HomeFeed.swift rename to Flaro/Session/HomeFeed.swift diff --git a/Halo/Session/HomeLayoutSettings.swift b/Flaro/Session/HomeLayoutSettings.swift similarity index 92% rename from Halo/Session/HomeLayoutSettings.swift rename to Flaro/Session/HomeLayoutSettings.swift index 7c878b0..505195b 100644 --- a/Halo/Session/HomeLayoutSettings.swift +++ b/Flaro/Session/HomeLayoutSettings.swift @@ -12,15 +12,15 @@ import MediaKit @Observable final class HomeLayoutSettings { private enum Key { - static let hidden = "halo.home.hiddenHubs" - static let order = "halo.home.hubOrder" - static let heroCount = "halo.home.heroCount" - static let heroSource = "halo.home.heroSource" - static let heroLibraries = "halo.home.heroLibraries" - static let combinesContinueRows = "halo.home.combinesContinueRows" - static let hidesWatchedInLatest = "halo.home.hidesWatchedInLatest" - static let cardScale = "halo.home.cardScale" - static let heroHeight = "halo.home.heroHeight" + static let hidden = "flaro.home.hiddenHubs" + static let order = "flaro.home.hubOrder" + static let heroCount = "flaro.home.heroCount" + static let heroSource = "flaro.home.heroSource" + static let heroLibraries = "flaro.home.heroLibraries" + static let combinesContinueRows = "flaro.home.combinesContinueRows" + static let hidesWatchedInLatest = "flaro.home.hidesWatchedInLatest" + static let cardScale = "flaro.home.cardScale" + static let heroHeight = "flaro.home.heroHeight" } /// Woraus sich der Aufmacher speist. diff --git a/Halo/Session/JellyfinSignInModel.swift b/Flaro/Session/JellyfinSignInModel.swift similarity index 98% rename from Halo/Session/JellyfinSignInModel.swift rename to Flaro/Session/JellyfinSignInModel.swift index 0d55b33..caf582b 100644 --- a/Halo/Session/JellyfinSignInModel.swift +++ b/Flaro/Session/JellyfinSignInModel.swift @@ -5,14 +5,14 @@ import Observation /// Führt durch die Anmeldung an einem Jellyfin-Server. /// -/// Eigenes Modell statt eines weiteren Falls in `HaloSession.Phase`: die +/// Eigenes Modell statt eines weiteren Falls in `FlaroSession.Phase`: die /// Anmeldung besteht aus mehreren Schritten mit eigener Bedienung — Adresse /// eintippen, Code bestätigen lassen, notfalls doch Benutzername und Passwort /// —, und keiner davon interessiert den Rest der App. Was hier ankommt, ist am /// Ende ein einziges Ergebnis: ein Konto und ein Weg dorthin. /// /// **Quick Connect zuerst.** Der sechsstellige Code wird in einem bereits -/// angemeldeten Client bestätigt; das Passwort erreicht Halo dabei nie — +/// angemeldeten Client bestätigt; das Passwort erreicht Flaro dabei nie — /// dieselbe Zusage wie bei Plex, wo die Anmeldemaske von Plex selbst kommt. /// Benutzername und Passwort sind der Rückfall für Server, auf denen Quick /// Connect abgeschaltet ist. Dann hält die App das Passwort für die Dauer diff --git a/Halo/Session/LanguagePreferences.swift b/Flaro/Session/LanguagePreferences.swift similarity index 93% rename from Halo/Session/LanguagePreferences.swift rename to Flaro/Session/LanguagePreferences.swift index 821dafb..40ec1d4 100644 --- a/Halo/Session/LanguagePreferences.swift +++ b/Flaro/Session/LanguagePreferences.swift @@ -1,7 +1,7 @@ import Foundation import Observation import MediaKit -import HaloPlayback +import FlaroPlayback /// Welche Tonspur gewählt werden soll — und auf welcher Ebene das jemand /// gesagt hat. @@ -28,13 +28,13 @@ final class LanguagePreferences { private enum Key { /// Absichtlich die Schlüssel aus `PlaybackSettings`: hier ist nur der /// Besitzer gewechselt, nicht der Wert. - static let audio = "halo.playback.audioLanguage" - static let subtitles = "halo.playback.subtitleLanguage" - static let fallbacks = "halo.languages.fallbacks" - static let avoidsAudioDescription = "halo.languages.avoidsAudioDescription" - static let perLibrary = "halo.languages.library" - static let perSeries = "halo.languages.series" - static let rememberedTracks = "halo.languages.seriesTrack" + static let audio = "flaro.playback.audioLanguage" + static let subtitles = "flaro.playback.subtitleLanguage" + static let fallbacks = "flaro.languages.fallbacks" + static let avoidsAudioDescription = "flaro.languages.avoidsAudioDescription" + static let perLibrary = "flaro.languages.library" + static let perSeries = "flaro.languages.series" + static let rememberedTracks = "flaro.languages.seriesTrack" } /// Die Sprachen, die zur Auswahl stehen. diff --git a/Halo/Session/LibraryFeed.swift b/Flaro/Session/LibraryFeed.swift similarity index 100% rename from Halo/Session/LibraryFeed.swift rename to Flaro/Session/LibraryFeed.swift diff --git a/Halo/Session/MediaItem+Presentation.swift b/Flaro/Session/MediaItem+Presentation.swift similarity index 98% rename from Halo/Session/MediaItem+Presentation.swift rename to Flaro/Session/MediaItem+Presentation.swift index 8a44b0b..fa9b7de 100644 --- a/Halo/Session/MediaItem+Presentation.swift +++ b/Flaro/Session/MediaItem+Presentation.swift @@ -10,7 +10,7 @@ import MediaKit /// sieht nicht nach einem Fehler aus, sondern nach einem Server ohne Bild. /// /// Reine Ableitungen aus `MediaItem`, keine Ansicht — deshalb neben den -/// übrigen Modellen und nicht in HaloDesign, das `MediaItem` gar nicht kennt. +/// übrigen Modellen und nicht in FlaroDesign, das `MediaItem` gar nicht kennt. extension MediaItem { /// Der Titel auf einer Kachel in einer Reihe. /// diff --git a/Halo/Session/NavigationLayoutSettings.swift b/Flaro/Session/NavigationLayoutSettings.swift similarity index 95% rename from Halo/Session/NavigationLayoutSettings.swift rename to Flaro/Session/NavigationLayoutSettings.swift index 83480d8..43ff95a 100644 --- a/Halo/Session/NavigationLayoutSettings.swift +++ b/Flaro/Session/NavigationLayoutSettings.swift @@ -13,8 +13,8 @@ import MediaKit @Observable final class NavigationLayoutSettings { private enum Key { - static let hidden = "halo.navigation.hidden" - static let order = "halo.navigation.order" + static let hidden = "flaro.navigation.hidden" + static let order = "flaro.navigation.order" } private let defaults: UserDefaults diff --git a/Halo/Session/NowPlaying.swift b/Flaro/Session/NowPlaying.swift similarity index 100% rename from Halo/Session/NowPlaying.swift rename to Flaro/Session/NowPlaying.swift diff --git a/Halo/Session/OrientationLock.swift b/Flaro/Session/OrientationLock.swift similarity index 100% rename from Halo/Session/OrientationLock.swift rename to Flaro/Session/OrientationLock.swift diff --git a/Halo/Session/PlaybackCoordinator.swift b/Flaro/Session/PlaybackCoordinator.swift similarity index 100% rename from Halo/Session/PlaybackCoordinator.swift rename to Flaro/Session/PlaybackCoordinator.swift diff --git a/Halo/Session/PlaybackDiagnostics.swift b/Flaro/Session/PlaybackDiagnostics.swift similarity index 99% rename from Halo/Session/PlaybackDiagnostics.swift rename to Flaro/Session/PlaybackDiagnostics.swift index 015dfe5..abc8afb 100644 --- a/Halo/Session/PlaybackDiagnostics.swift +++ b/Flaro/Session/PlaybackDiagnostics.swift @@ -1,6 +1,6 @@ import Foundation import Observation -import HaloPlayback +import FlaroPlayback /// Hält fest, was beim letzten Abspielversuch passiert ist. /// diff --git a/Halo/Session/PlaybackSettings.swift b/Flaro/Session/PlaybackSettings.swift similarity index 87% rename from Halo/Session/PlaybackSettings.swift rename to Flaro/Session/PlaybackSettings.swift index b5310a3..af860bb 100644 --- a/Halo/Session/PlaybackSettings.swift +++ b/Flaro/Session/PlaybackSettings.swift @@ -1,6 +1,6 @@ import Foundation import Observation -import HaloPlayback +import FlaroPlayback /// Was der Nutzer über die Wiedergabe eingestellt hat. /// @@ -28,19 +28,19 @@ final class PlaybackSettings { } private enum Key { - static let wifiQuality = "halo.playback.quality.wifi" - static let cellularQuality = "halo.playback.quality.cellular" - static let autoSkipIntro = "halo.playback.autoSkipIntro" - static let autoSkipCredits = "halo.playback.autoSkipCredits" - static let autoPlayNext = "halo.playback.autoPlayNext" - static let locksLandscape = "halo.playback.locksLandscape" - static let rewindSeconds = "halo.playback.rewindSeconds" - static let forwardSeconds = "halo.playback.forwardSeconds" - static let defaultRate = "halo.playback.defaultRate" - static let remembersQuality = "halo.playback.remembersQuality" - static let rememberedQuality = "halo.playback.rememberedQuality" - static let showsBitrate = "halo.playback.showsBitrate" - static let lastPlaybackStruggled = "halo.playback.struggled" + static let wifiQuality = "flaro.playback.quality.wifi" + static let cellularQuality = "flaro.playback.quality.cellular" + static let autoSkipIntro = "flaro.playback.autoSkipIntro" + static let autoSkipCredits = "flaro.playback.autoSkipCredits" + static let autoPlayNext = "flaro.playback.autoPlayNext" + static let locksLandscape = "flaro.playback.locksLandscape" + static let rewindSeconds = "flaro.playback.rewindSeconds" + static let forwardSeconds = "flaro.playback.forwardSeconds" + static let defaultRate = "flaro.playback.defaultRate" + static let remembersQuality = "flaro.playback.remembersQuality" + static let rememberedQuality = "flaro.playback.rememberedQuality" + static let showsBitrate = "flaro.playback.showsBitrate" + static let lastPlaybackStruggled = "flaro.playback.struggled" } /// Die Sprünge, die es zur Auswahl gibt. diff --git a/Halo/Session/QuickPlay.swift b/Flaro/Session/QuickPlay.swift similarity index 100% rename from Halo/Session/QuickPlay.swift rename to Flaro/Session/QuickPlay.swift diff --git a/Halo/Session/ScreenSafeArea.swift b/Flaro/Session/ScreenSafeArea.swift similarity index 100% rename from Halo/Session/ScreenSafeArea.swift rename to Flaro/Session/ScreenSafeArea.swift diff --git a/Halo/Session/SearchFeed.swift b/Flaro/Session/SearchFeed.swift similarity index 98% rename from Halo/Session/SearchFeed.swift rename to Flaro/Session/SearchFeed.swift index 768987e..4155e13 100644 --- a/Halo/Session/SearchFeed.swift +++ b/Flaro/Session/SearchFeed.swift @@ -15,7 +15,7 @@ import Observation final class SearchFeed { /// Wie viele zuletzt gesuchte Begriffe gemerkt werden. private static let recentLimit = 8 - private static let recentKey = "halo.search.recent" + private static let recentKey = "flaro.search.recent" private(set) var hubs: [MediaRowGroup] = [] private(set) var isSearching = false diff --git a/Halo/Session/SubtitleAppearance.swift b/Flaro/Session/SubtitleAppearance.swift similarity index 91% rename from Halo/Session/SubtitleAppearance.swift rename to Flaro/Session/SubtitleAppearance.swift index 3c28e1f..8eddd93 100644 --- a/Halo/Session/SubtitleAppearance.swift +++ b/Flaro/Session/SubtitleAppearance.swift @@ -1,6 +1,6 @@ import Foundation import Observation -import HaloPlayback +import FlaroPlayback /// Wie Untertitel aussehen sollen — gemerkt über Neustarts hinweg. /// @@ -14,8 +14,8 @@ import HaloPlayback @Observable final class SubtitleAppearance { private enum Key { - static let size = "halo.subtitles.size" - static let palette = "halo.subtitles.palette" + static let size = "flaro.subtitles.size" + static let palette = "flaro.subtitles.palette" } private let defaults: UserDefaults diff --git a/Halo/Session/SubtitleStyle+Labels.swift b/Flaro/Session/SubtitleStyle+Labels.swift similarity index 98% rename from Halo/Session/SubtitleStyle+Labels.swift rename to Flaro/Session/SubtitleStyle+Labels.swift index 753918a..df6644c 100644 --- a/Halo/Session/SubtitleStyle+Labels.swift +++ b/Flaro/Session/SubtitleStyle+Labels.swift @@ -1,4 +1,4 @@ -import HaloPlayback +import FlaroPlayback import Foundation /// Die Namen der Stufen stehen neben dem Modell, nicht darin — dasselbe diff --git a/Halo/Session/TopShelfWriter.swift b/Flaro/Session/TopShelfWriter.swift similarity index 97% rename from Halo/Session/TopShelfWriter.swift rename to Flaro/Session/TopShelfWriter.swift index 976399c..78d158a 100644 --- a/Halo/Session/TopShelfWriter.swift +++ b/Flaro/Session/TopShelfWriter.swift @@ -1,6 +1,6 @@ // **Die ganze Datei nur für den Fernseher.** // -// Sie liegt im geteilten Ordner, weil `Halo/Session/` von beiden Zielen +// Sie liegt im geteilten Ordner, weil `Flaro/Session/` von beiden Zielen // übersetzt wird — und `TVServices` gibt es auf iOS nicht. Ein `#if` um den // einen Import genügte dabei nicht: der Abhängigkeits-Sucher von Clang prüft // ihn trotzdem und bricht mit „Unable to resolve module dependency: @@ -21,7 +21,7 @@ import TVServices @MainActor enum TopShelfWriter { /// Die App-Gruppe, die App und Erweiterung teilen. - static let group = "group.com.scarriffleservices.halo" + static let group = "group.com.scarriffleservices.flaro" /// Die Kante, in der das Regal Plakate zeigt. /// diff --git a/Halo/Views/Components/ItemActionEntries.swift b/Flaro/Views/Components/ItemActionEntries.swift similarity index 98% rename from Halo/Views/Components/ItemActionEntries.swift rename to Flaro/Views/Components/ItemActionEntries.swift index 2609a0a..973851c 100644 --- a/Halo/Views/Components/ItemActionEntries.swift +++ b/Flaro/Views/Components/ItemActionEntries.swift @@ -99,5 +99,5 @@ extension EnvironmentValues { /// /// Die Voreinstellung tut nichts: wo niemand etwas hinterlegt, gibt es /// auch nichts neu zu laden. - @Entry var haloItemChanged: () async -> Void = {} + @Entry var flaroItemChanged: () async -> Void = {} } diff --git a/Halo/Views/Components/QuickPlayTile.swift b/Flaro/Views/Components/QuickPlayTile.swift similarity index 96% rename from Halo/Views/Components/QuickPlayTile.swift rename to Flaro/Views/Components/QuickPlayTile.swift index a6c04ed..3f6e6c1 100644 --- a/Halo/Views/Components/QuickPlayTile.swift +++ b/Flaro/Views/Components/QuickPlayTile.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloDownloads +import FlaroDesign +import FlaroDownloads import MediaKit /// Eine Kachel, die zur Detailseite führt — mit einem Knopf, der sofort @@ -11,7 +11,7 @@ import MediaKit /// Zwei Schritte für eine Absicht. /// /// **Warum eine Hülle und keine Änderung an der Karte.** `PosterCard` liegt -/// in `HaloDesign`, und das Paket ist ohne Abhängigkeiten deklariert — es +/// in `FlaroDesign`, und das Paket ist ohne Abhängigkeiten deklariert — es /// kennt weder `MediaItem` noch `PlaybackCoordinator`. Ein Überzug **in** der /// Karte fiele ausserdem unter ihr `.accessibilityElement(children: .combine)` /// und wäre für VoiceOver gar nicht mehr da. @@ -27,12 +27,12 @@ struct QuickPlayTile: View { var opensDetails: Bool = false @ViewBuilder let card: () -> Card - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @Environment(DownloadCoordinator.self) private var downloads @Environment(LanguagePreferences.self) private var languages - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var isPointerInside = false @State private var errorMessage: String? @@ -48,11 +48,11 @@ struct QuickPlayTile: View { /// Was die umgebende Seite tun soll, wenn sich am Server etwas geändert /// hat. Siehe `ItemActionEntries`. - @Environment(\.haloItemChanged) private var itemChanged + @Environment(\.flaroItemChanged) private var itemChanged /// Der Weg auf die Detailseite — siehe `MediaStack`. - @Environment(\.haloPush) private var push + @Environment(\.flaroPush) private var push - private var isMac: Bool { HaloPlatform.isMac } + private var isMac: Bool { FlaroPlatform.isMac } /// Ob es überhaupt etwas zu starten gibt. /// @@ -236,7 +236,7 @@ struct QuickPlayTile: View { .shadow(color: .black.opacity(0.35), radius: 3, y: 1) } .buttonStyle(.plain) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) .opacity(isPointerInside ? 1 : 0) // Er ist eine Zeigergeste; für VoiceOver gibt es die benannte Aktion // oben. Beides anzubieten hiesse, jede Kachel zu verdoppeln. diff --git a/Halo/Views/Detail/ItemActionsMenu.swift b/Flaro/Views/Detail/ItemActionsMenu.swift similarity index 97% rename from Halo/Views/Detail/ItemActionsMenu.swift rename to Flaro/Views/Detail/ItemActionsMenu.swift index 3eec8ef..546fadf 100644 --- a/Halo/Views/Detail/ItemActionsMenu.swift +++ b/Flaro/Views/Detail/ItemActionsMenu.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Das Aktionsmenü auf einer Detailseite. @@ -17,7 +17,7 @@ struct ItemActionsMenu: View { /// Detailseite neu geladen werden sollte. let onChange: () async -> Void - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var isWorking = false @State private var errorMessage: String? diff --git a/Halo/Views/Detail/MediaDetailView.swift b/Flaro/Views/Detail/MediaDetailView.swift similarity index 89% rename from Halo/Views/Detail/MediaDetailView.swift rename to Flaro/Views/Detail/MediaDetailView.swift index c4c527d..72af4d5 100644 --- a/Halo/Views/Detail/MediaDetailView.swift +++ b/Flaro/Views/Detail/MediaDetailView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Detailseite eines Titels. @@ -10,7 +10,7 @@ import MediaKit struct MediaDetailView: View { let route: MediaRoute - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var state: LoadState = .loading var body: some View { @@ -37,12 +37,12 @@ struct MediaDetailView: View { } } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(state.value?.title ?? route.title) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { if let item = state.value { - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { ItemActionsMenu(item: item) { await load() } } } diff --git a/Halo/Views/Detail/ParentLinks.swift b/Flaro/Views/Detail/ParentLinks.swift similarity index 85% rename from Halo/Views/Detail/ParentLinks.swift rename to Flaro/Views/Detail/ParentLinks.swift index abf61d4..930b691 100644 --- a/Halo/Views/Detail/ParentLinks.swift +++ b/Flaro/Views/Detail/ParentLinks.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Der Weg nach oben: von der Folge zur Staffel, von der Staffel zur Serie. @@ -15,7 +15,7 @@ struct ParentLinks: View { var body: some View { if !destinations.isEmpty { ScrollView(.horizontal) { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { ForEach(destinations, id: \.route.ratingKey) { destination in NavigationLink(value: destination.route) { Label { @@ -24,16 +24,16 @@ struct ParentLinks: View { } icon: { Image(systemName: destination.symbol) } - .font(.haloCaption) - .foregroundStyle(Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.snug) - .background(Color.haloElevated, in: .capsule) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.snug) + .background(Color.flaroElevated, in: .capsule) } .buttonStyle(.plain) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } .scrollIndicators(.never) } diff --git a/Halo/Views/Detail/PlayableDetailView.swift b/Flaro/Views/Detail/PlayableDetailView.swift similarity index 80% rename from Halo/Views/Detail/PlayableDetailView.swift rename to Flaro/Views/Detail/PlayableDetailView.swift index 061ee82..539c35a 100644 --- a/Halo/Views/Detail/PlayableDetailView.swift +++ b/Flaro/Views/Detail/PlayableDetailView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Detailseite von etwas, das sich direkt abspielen lässt — Film oder @@ -7,9 +7,9 @@ import MediaKit struct PlayableDetailView: View { let item: MediaItem - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @Environment(\.horizontalSizeClass) private var sizeClass @State private var selectedVersion = 0 @@ -17,7 +17,7 @@ struct PlayableDetailView: View { var body: some View { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.section) { + VStack(alignment: .leading, spacing: FlaroSpacing.section) { header // Bei einer Folge nicht mehr: dorthin führt jetzt die Zeile @@ -29,14 +29,14 @@ struct PlayableDetailView: View { if item.hasMultipleVersions { VersionPicker(media: item.versions, selection: $selectedVersion) - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } if let summary = item.summary, !summary.isEmpty { Text(summary) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) + .padding(.horizontal, FlaroSpacing.screenMargin) } if !item.genres.isEmpty { @@ -53,13 +53,13 @@ struct PlayableDetailView: View { part: part, isExpanded: $showsTechnicalDetails ) - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } } - .padding(.bottom, HaloSpacing.section) + .padding(.bottom, FlaroSpacing.section) } .scrollIndicators(.never) - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } private var currentMedia: MediaVersion? { @@ -104,9 +104,9 @@ struct PlayableDetailView: View { item.watchProgress == nil ? "hero.play" : "hero.resume", systemImage: "play.fill" ) - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.borderedProminent) .tint(accent) @@ -118,11 +118,11 @@ struct PlayableDetailView: View { } label: { Label("detail.playFromStart", systemImage: "gobackward") .labelStyle(.iconOnly) - .font(.haloCardTitle) - .padding(HaloSpacing.snug) + .font(.flaroCardTitle) + .padding(FlaroSpacing.snug) } .buttonStyle(.bordered) - .tint(Color.haloTextSecondary) + .tint(Color.flaroTextSecondary) } DownloadButton(item: item, mediaIndex: selectedVersion) @@ -210,10 +210,10 @@ struct VersionPicker: View { @Binding var selection: Int var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { Text("detail.versions") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) Picker("detail.versions", selection: $selection) { ForEach(Array(media.enumerated()), id: \.offset) { index, version in @@ -232,17 +232,17 @@ struct TagStrip: View { var body: some View { ScrollView(.horizontal) { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { ForEach(tags, id: \.self) { tag in Text(tag) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.tight) - .background(Color.haloElevated, in: .capsule) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.tight) + .background(Color.flaroElevated, in: .capsule) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } .scrollIndicators(.never) } @@ -251,20 +251,20 @@ struct TagStrip: View { /// Die Besetzung mit Porträts. struct CastRow: View { let cast: [MediaPerson] - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text("detail.cast") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.screenMargin) ScrollView(.horizontal) { - HStack(alignment: .top, spacing: HaloSpacing.regular) { + HStack(alignment: .top, spacing: FlaroSpacing.regular) { ForEach(cast.prefix(20)) { role in - VStack(spacing: HaloSpacing.snug) { - HaloArtwork( + VStack(spacing: FlaroSpacing.snug) { + FlaroArtwork( url: session.backend?.artworkURL(path: role.thumb, width: 160, height: 160), aspectRatio: 1, fallbackSymbol: "person" @@ -273,15 +273,15 @@ struct CastRow: View { .clipShape(.circle) Text(role.name) - .font(.haloCaption) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(2) .multilineTextAlignment(.center) if let character = role.role { Text(character) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } @@ -289,7 +289,7 @@ struct CastRow: View { .accessibilityElement(children: .combine) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } .scrollIndicators(.never) } diff --git a/Halo/Views/Detail/SeriesDetailView.swift b/Flaro/Views/Detail/SeriesDetailView.swift similarity index 88% rename from Halo/Views/Detail/SeriesDetailView.swift rename to Flaro/Views/Detail/SeriesDetailView.swift index dc93fb7..e3a777c 100644 --- a/Halo/Views/Detail/SeriesDetailView.swift +++ b/Flaro/Views/Detail/SeriesDetailView.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloDownloads +import FlaroDesign +import FlaroDownloads import MediaKit /// Die Detailseite einer Serie oder Staffel. @@ -10,10 +10,10 @@ import MediaKit struct SeriesDetailView: View { let item: MediaItem - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(DownloadCoordinator.self) private var downloads @Environment(LanguagePreferences.self) private var languages - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @Environment(\.horizontalSizeClass) private var sizeClass @State private var children: LoadState<[MediaItem]> = .loading @@ -35,16 +35,16 @@ struct SeriesDetailView: View { var body: some View { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.section) { + VStack(alignment: .leading, spacing: FlaroSpacing.section) { header ParentLinks(item: item) if let summary = item.summary, !summary.isEmpty { Text(summary) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) + .padding(.horizontal, FlaroSpacing.screenMargin) } if !item.genres.isEmpty { TagStrip(tags: item.genres) } @@ -58,17 +58,17 @@ struct SeriesDetailView: View { scope: .series(item.id), title: "languages.forThisSeries" ) - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } childList if !item.cast.isEmpty { CastRow(cast: item.cast) } } - .padding(.bottom, HaloSpacing.section) + .padding(.bottom, FlaroSpacing.section) } .scrollIndicators(.never) - .haloRevealsTopChrome() + .flaroRevealsTopChrome() .safeAreaInset(edge: .bottom, spacing: 0) { selectionBar } .confirmationDialog( Text("downloads.quality.title"), @@ -163,34 +163,34 @@ struct SeriesDetailView: View { private var childList: some View { switch children { case .loading: - ProgressView().frame(maxWidth: .infinity).padding(.vertical, HaloSpacing.section) + ProgressView().frame(maxWidth: .infinity).padding(.vertical, FlaroSpacing.section) case .failed(let message): Text(message) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) + .padding(.horizontal, FlaroSpacing.screenMargin) case .loaded(let entries): - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { // **Der Auswählen-Knopf steht bei dem, was er auswählt.** // Oben in der Werkzeugleiste wäre er weiter weg von der Liste // als von allem anderen auf dieser Seite. HStack { Text(item.kind == .show ? "detail.seasons" : "detail.episodes") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) Spacer() if entries.contains(where: { $0.kind == .episode }) { selectionControls(for: entries) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) LazyVStack(spacing: 0) { ForEach(entries) { entry in row(for: entry) - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) } } } @@ -207,16 +207,16 @@ struct SeriesDetailView: View { Button { if gewaehlt { selection.remove(entry.id) } else { selection.insert(entry.id) } } label: { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { ChildRow(item: entry) Image(systemName: gewaehlt ? "checkmark.circle.fill" : "circle") .font(.title3) - .foregroundStyle(gewaehlt ? accent : Color.haloTextMuted) + .foregroundStyle(gewaehlt ? accent : Color.flaroTextMuted) } .contentShape(.rect) } .buttonStyle(.plain) - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) .accessibilityAddTraits(gewaehlt ? .isSelected : []) } else { // **Der Knopf steht neben dem Link, nicht darin.** Ein `Button` im @@ -228,7 +228,7 @@ struct SeriesDetailView: View { // Als Geschwister in einer Reihe statt als Überzug: dann teilt das // Layout den Platz auf, und der Titel kann gar nicht erst unter // dem Knopf verschwinden. - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { NavigationLink(value: MediaRoute(ratingKey: entry.id, title: entry.title)) { ChildRow(item: entry) } @@ -241,7 +241,7 @@ struct SeriesDetailView: View { DownloadButton(item: entry, loadsDetails: true) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } } @@ -249,7 +249,7 @@ struct SeriesDetailView: View { private func selectionControls(for entries: [MediaItem]) -> some View { let folgen = entries.filter { $0.kind == .episode }.map(\.id) - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { if isSelecting { Button(selection.count == folgen.count ? "detail.selectNone" : "detail.selectAll") { selection = selection.count == folgen.count ? [] : Set(folgen) @@ -258,12 +258,12 @@ struct SeriesDetailView: View { isSelecting = false selection = [] } - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } else { Button("detail.select") { isSelecting = true } } } - .font(.haloCaption) + .font(.flaroCaption) .buttonStyle(.plain) .foregroundStyle(accent) } @@ -288,14 +288,14 @@ struct SeriesDetailView: View { ) } } - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.borderedProminent) .tint(accent) .disabled(isWorking) - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) .frame(maxWidth: .infinity) .background(.bar) } @@ -387,11 +387,11 @@ struct SeriesDetailView: View { /// Eine Staffel- oder Folgenzeile. private struct ChildRow: View { let item: MediaItem - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session var body: some View { - HStack(spacing: HaloSpacing.regular) { - HaloArtwork( + HStack(spacing: FlaroSpacing.regular) { + FlaroArtwork( url: session.backend?.artworkURL( path: item.thumb, width: item.kind == .season ? 120 : 240, @@ -411,14 +411,14 @@ private struct ChildRow: View { VStack(alignment: .leading, spacing: 2) { Text(rowTitle) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(2) if let subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } @@ -427,14 +427,14 @@ private struct ChildRow: View { if item.isWatched { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .accessibilityLabel(Text("item.watched")) } } // Das waagerechte Polster liegt jetzt eine Ebene höher, bei der // Reihe aus Zeile und Download-Knopf — sonst klebte der Knopf am // Bildschirmrand. - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) .contentShape(.rect) .accessibilityElement(children: .combine) } diff --git a/Halo/Views/Detail/TechnicalDetails.swift b/Flaro/Views/Detail/TechnicalDetails.swift similarity index 84% rename from Halo/Views/Detail/TechnicalDetails.swift rename to Flaro/Views/Detail/TechnicalDetails.swift index 6b037f5..f0cd6c0 100644 --- a/Halo/Views/Detail/TechnicalDetails.swift +++ b/Flaro/Views/Detail/TechnicalDetails.swift @@ -1,11 +1,11 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback import MediaKit /// Die aufklappbaren technischen Angaben zu einer Datei. /// -/// Zeigt nicht nur, was in der Datei steckt, sondern auch, wie Halo sie +/// Zeigt nicht nur, was in der Datei steckt, sondern auch, wie Flaro sie /// abspielen wird und warum. Bei der Frage „warum ruckelt das" ist das die /// erste Stelle zum Nachsehen — und in Phase 1, wo es noch kein Transcoding /// gibt, erklärt es, warum bei manchen Dateien kein Bild-in-Bild angeboten wird. @@ -34,13 +34,13 @@ struct TechnicalDetails: View { var body: some View { DisclosureGroup(isExpanded: $isExpanded) { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { row("technical.playbackMethod", playbackMethodLabel) if let reason = rationaleLabel { row("technical.reason", reason) } - Divider().overlay(Color.haloSeparator).padding(.vertical, HaloSpacing.tight) + Divider().overlay(Color.flaroSeparator).padding(.vertical, FlaroSpacing.tight) if let container = part.container { row("technical.container", container.uppercased()) } if let codec = media.videoCodec { row("technical.videoCodec", codec.uppercased()) } @@ -55,7 +55,7 @@ struct TechnicalDetails: View { } if !part.audioStreams.isEmpty { - Divider().overlay(Color.haloSeparator).padding(.vertical, HaloSpacing.tight) + Divider().overlay(Color.flaroSeparator).padding(.vertical, FlaroSpacing.tight) ForEach(part.audioStreams) { stream in row("technical.audio", trackLabel(stream)) } @@ -68,36 +68,36 @@ struct TechnicalDetails: View { } if let file = part.file { - Divider().overlay(Color.haloSeparator).padding(.vertical, HaloSpacing.tight) + Divider().overlay(Color.flaroSeparator).padding(.vertical, FlaroSpacing.tight) VStack(alignment: .leading, spacing: 2) { Text("technical.file") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Text(file) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .textSelection(.enabled) } } } - .padding(.top, HaloSpacing.snug) + .padding(.top, FlaroSpacing.snug) } label: { Text("technical.title") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) } - .tint(Color.haloTextSecondary) + .tint(Color.flaroTextSecondary) } private func row(_ label: LocalizedStringKey, _ value: String) -> some View { HStack(alignment: .top) { Text(label) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Spacer() Text(value) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) .multilineTextAlignment(.trailing) } } diff --git a/Halo/Views/Downloads/DownloadButton.swift b/Flaro/Views/Downloads/DownloadButton.swift similarity index 96% rename from Halo/Views/Downloads/DownloadButton.swift rename to Flaro/Views/Downloads/DownloadButton.swift index 6fa8e2c..c359416 100644 --- a/Halo/Views/Downloads/DownloadButton.swift +++ b/Flaro/Views/Downloads/DownloadButton.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloDownloads +import FlaroDesign +import FlaroDownloads import MediaKit /// Der Download-Knopf auf einer Detailseite. @@ -25,10 +25,10 @@ struct DownloadButton: View { /// nicht drückt. var loadsDetails: Bool = false - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(DownloadCoordinator.self) private var downloads @Environment(LanguagePreferences.self) private var languages - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var refusal: DownloadRefusal? @State private var showsQualityChoice = false @@ -64,18 +64,18 @@ struct DownloadButton: View { } else { Label(label, systemImage: symbol) .labelStyle(.iconOnly) - .font(.haloCardTitle) + .font(.flaroCardTitle) } } // Feste Größe, damit der Knopf beim Nachladen nicht springt: der // Kreisel ist schmaler als das Symbol, und eine Zeile, die während // des Wartens die Breite wechselt, sieht aus wie ein Fehler. .frame(width: 22, height: 22) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .disabled(isLoadingDetails) .buttonStyle(.bordered) - .tint(existing?.state == .completed ? accent : Color.haloTextSecondary) + .tint(existing?.state == .completed ? accent : Color.flaroTextSecondary) .accessibilityLabel(Text(label)) .confirmationDialog( Text("downloads.quality.title"), @@ -247,10 +247,10 @@ extension DownloadQuality { struct DownloadAllButton: View { let container: MediaItem - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(DownloadCoordinator.self) private var downloads @Environment(LanguagePreferences.self) private var languages - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var isWorking = false @State private var summary: String? @@ -269,7 +269,7 @@ struct DownloadAllButton: View { } } .buttonStyle(.bordered) - .tint(Color.haloTextSecondary) + .tint(Color.flaroTextSecondary) .disabled(isWorking) .alert( Text("downloads.batch.title"), diff --git a/Halo/Views/Downloads/DownloadQualityChoice.swift b/Flaro/Views/Downloads/DownloadQualityChoice.swift similarity index 99% rename from Halo/Views/Downloads/DownloadQualityChoice.swift rename to Flaro/Views/Downloads/DownloadQualityChoice.swift index 5c81f10..ec993f4 100644 --- a/Halo/Views/Downloads/DownloadQualityChoice.swift +++ b/Flaro/Views/Downloads/DownloadQualityChoice.swift @@ -1,5 +1,5 @@ import Foundation -import HaloDownloads +import FlaroDownloads import MediaKit /// Welche Qualitätsstufen für einen Titel zur Wahl stehen — und wie sie heißen. diff --git a/Halo/Views/Downloads/DownloadsView.swift b/Flaro/Views/Downloads/DownloadsView.swift similarity index 87% rename from Halo/Views/Downloads/DownloadsView.swift rename to Flaro/Views/Downloads/DownloadsView.swift index ec71a40..4a16cfd 100644 --- a/Halo/Views/Downloads/DownloadsView.swift +++ b/Flaro/Views/Downloads/DownloadsView.swift @@ -1,14 +1,14 @@ import SwiftUI -import HaloDesign -import HaloDownloads -import HaloPlayback +import FlaroDesign +import FlaroDownloads +import FlaroPlayback /// Was offline ist und was gerade geladen wird. struct DownloadsView: View { @Environment(DownloadCoordinator.self) private var downloads @Environment(PlaybackCoordinator.self) private var playback @Environment(NetworkConditions.self) private var network - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @Environment(\.horizontalSizeClass) private var sizeClass @@ -24,16 +24,16 @@ struct DownloadsView: View { list } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("sidebar.downloads")) .toolbar { // Nur auf dem iPhone: dort gibt es den Einstellungen-Tab nicht // mehr, und dies ist der Weg dorthin. if sizeClass == .compact { - ToolbarItem(placement: .haloTrailing) { AccountToolbarButton() } + ToolbarItem(placement: .flaroTrailing) { AccountToolbarButton() } } } - .haloInlineTitle() + .flaroInlineTitle() // Keine Rückfrage beim Entfernen. // // Sie stand hier, weil „löschen" nach etwas Endgültigem klingt. Es ist @@ -88,8 +88,8 @@ struct DownloadsView: View { } } .scrollContentBackground(.hidden) - .listRowBackground(Color.haloSurface) - .haloRevealsTopChrome() + .listRowBackground(Color.flaroSurface) + .flaroRevealsTopChrome() } private var active: [DownloadItem] { @@ -125,7 +125,7 @@ struct DownloadsView: View { /// Die Kopfzeile mit Speicherbelegung und Schaltern. private struct StorageSection: View { @Environment(DownloadCoordinator.self) private var downloads - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { @Bindable var downloads = downloads @@ -133,10 +133,10 @@ private struct StorageSection: View { Section { HStack { Text("downloads.used") - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) Spacer() Text(downloads.usedBytes.formatted(.byteCount(style: .file))) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) } if let limit = downloads.storageLimitBytes, limit > 0 { @@ -157,20 +157,20 @@ private struct DownloadRow: View { let onRemove: () -> Void @Environment(DownloadCoordinator.self) private var downloads - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { - HStack(spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { + HStack(spacing: FlaroSpacing.regular) { VStack(alignment: .leading, spacing: 2) { Text(item.title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle = item.subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } @@ -190,19 +190,19 @@ private struct DownloadRow: View { hold == .offline ? "downloads.hold.offline" : "downloads.hold.wifi", systemImage: hold == .offline ? "wifi.slash" : "wifi.exclamationmark" ) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } if let message = item.failureMessage { Text(message) - .font(.haloCaption) - .foregroundStyle(Color.haloDestructive) + .font(.flaroCaption) + .foregroundStyle(Color.flaroDestructive) .lineLimit(2) } } - .padding(.vertical, HaloSpacing.tight) - .listRowBackground(Color.haloSurface) + .padding(.vertical, FlaroSpacing.tight) + .listRowBackground(Color.flaroSurface) .swipeActions(edge: .trailing) { Button("action.delete", role: .destructive, action: onRemove) } @@ -240,18 +240,18 @@ private struct DownloadRow: View { case .completed: Image(systemName: "checkmark.circle.fill") - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .accessibilityLabel(Text("downloads.available")) case .queued, .waiting: Image(systemName: "clock") - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .accessibilityLabel(Text("downloads.queued")) } } private var progress: some View { - VStack(alignment: .leading, spacing: HaloSpacing.tight) { + VStack(alignment: .leading, spacing: FlaroSpacing.tight) { // Ohne bekannte Gesamtgröße ein unbestimmter Balken: ein Balken bei // null Prozent, der sich nie bewegt, sieht aus wie ein hängender // Download. @@ -273,17 +273,17 @@ private struct DownloadRow: View { // gemeinsames Mass für beide Zahlen (siehe `unit`), und die // Prozente am rechten Rand — die wachsen nach innen und schieben // nichts. - HStack(alignment: .firstTextBaseline, spacing: HaloSpacing.snug) { + HStack(alignment: .firstTextBaseline, spacing: FlaroSpacing.snug) { Text(statusLine) .lineLimit(1) - Spacer(minLength: HaloSpacing.snug) + Spacer(minLength: FlaroSpacing.snug) if let percentage { Text(percentage) } } - .font(.haloCaption) + .font(.flaroCaption) .monospacedDigit() - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } } @@ -387,28 +387,28 @@ private struct DownloadRow: View { private struct HoldNotice: View { let hold: TransferHold - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { - HStack(alignment: .top, spacing: HaloSpacing.regular) { + HStack(alignment: .top, spacing: FlaroSpacing.regular) { Image(systemName: hold == .offline ? "wifi.slash" : "wifi.exclamationmark") .font(.title3) .foregroundStyle(accent) VStack(alignment: .leading, spacing: 2) { Text(hold == .offline ? "downloads.hold.offline" : "downloads.hold.wifi") - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) Text(hold == .offline ? "downloads.hold.offline.description" : "downloads.hold.wifi.description") - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } } - .padding(.vertical, HaloSpacing.tight) - .listRowBackground(Color.haloSurface) + .padding(.vertical, FlaroSpacing.tight) + .listRowBackground(Color.flaroSurface) .accessibilityElement(children: .combine) } } diff --git a/Halo/Views/Home/HeroCarousel.swift b/Flaro/Views/Home/HeroCarousel.swift similarity index 97% rename from Halo/Views/Home/HeroCarousel.swift rename to Flaro/Views/Home/HeroCarousel.swift index d414e37..8b5050c 100644 --- a/Halo/Views/Home/HeroCarousel.swift +++ b/Flaro/Views/Home/HeroCarousel.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Der Aufmacher oben auf der Startseite — mehrere Titel, die sich abwechseln. @@ -65,8 +65,8 @@ struct HeroCarousel: View { @State private var isHoveringDots = false @Environment(\.accessibilityReduceMotion) private var reduceMotion - @Environment(\.haloAccent) private var accent - @Environment(HaloSession.self) private var session + @Environment(\.flaroAccent) private var accent + @Environment(FlaroSession.self) private var session @Environment(\.horizontalSizeClass) private var sizeClass @Environment(TopChrome.self) private var chrome @Environment(HomeLayoutSettings.self) private var layout @@ -118,7 +118,7 @@ struct HeroCarousel: View { } var body: some View { - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { heroSection // Die Höhe: fast das ganze Fenster. // @@ -189,7 +189,7 @@ struct HeroCarousel: View { } } - private var isMac: Bool { HaloPlatform.isMac } + private var isMac: Bool { FlaroPlatform.isMac } /// Die beiden Pfeile links und rechts. /// @@ -203,7 +203,7 @@ struct HeroCarousel: View { Spacer() arrow(symbol: "chevron.right", direction: 1) } - .padding(.horizontal, HaloSpacing.regular) + .padding(.horizontal, FlaroSpacing.regular) .opacity(isPointerInside ? 1 : 0) .animation(.easeOut(duration: 0.2), value: isPointerInside) .onContinuousHover { phase in @@ -219,7 +219,7 @@ struct HeroCarousel: View { Image(systemName: symbol) .font(.system(size: 18, weight: .semibold)) .foregroundStyle(.white) - .padding(HaloSpacing.regular) + .padding(FlaroSpacing.regular) .background(.black.opacity(0.45), in: .circle) } .buttonStyle(.plain) @@ -237,10 +237,10 @@ struct HeroCarousel: View { /// Auf dem iPhone und dem iPad bleibt es eine Anzeige. Dort gibt es /// keinen Zeiger, der sich nähern könnte, und getippt wird gewischt. private var indicator: some View { - HStack(spacing: isEnlarged ? HaloSpacing.regular : HaloSpacing.snug) { + HStack(spacing: isEnlarged ? FlaroSpacing.regular : FlaroSpacing.snug) { ForEach(items.indices, id: \.self) { position in Capsule() - .fill(position == currentIndex ? accent : Color.haloTextMuted.opacity(0.4)) + .fill(position == currentIndex ? accent : Color.flaroTextMuted.opacity(0.4)) .frame( width: position == currentIndex ? (isEnlarged ? 28 : 18) : (isEnlarged ? 12 : 6), height: isEnlarged ? 12 : 6 @@ -255,7 +255,7 @@ struct HeroCarousel: View { } // Eine grössere Fläche als die Punkte selbst: der Zeiger soll schon // beim Annähern etwas bewirken, nicht erst beim Treffen. - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) .contentShape(.rect) .onContinuousHover { phase in guard isMac else { return } diff --git a/Halo/Views/Home/HeroSection.swift b/Flaro/Views/Home/HeroSection.swift similarity index 89% rename from Halo/Views/Home/HeroSection.swift rename to Flaro/Views/Home/HeroSection.swift index 4e68112..f6c7732 100644 --- a/Halo/Views/Home/HeroSection.swift +++ b/Flaro/Views/Home/HeroSection.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Der Aufmacher oben auf der Startseite. @@ -12,10 +12,10 @@ struct HeroSection: View { /// Wie weit es dabei hereinrutscht. var incomingOffset: CGFloat = 0 - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @Environment(\.horizontalSizeClass) private var sizeClass var body: some View { @@ -56,21 +56,21 @@ struct HeroSection: View { quickPlay.start(item, backend: session.backend, coordinator: playback) } label: { Label(playLabel, systemImage: "play.fill") - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.borderedProminent) .tint(accent) NavigationLink(value: MediaRoute(ratingKey: item.id, title: item.title)) { Label("hero.details", systemImage: "info.circle") - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.bordered) - .tint(Color.haloTextSecondary) + .tint(Color.flaroTextSecondary) } } diff --git a/Halo/Views/Home/HomeView.swift b/Flaro/Views/Home/HomeView.swift similarity index 87% rename from Halo/Views/Home/HomeView.swift rename to Flaro/Views/Home/HomeView.swift index 4767a90..cfe0ac9 100644 --- a/Halo/Views/Home/HomeView.swift +++ b/Flaro/Views/Home/HomeView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Startseite: Aufmacher oben, darunter die Reihen vom Server. @@ -9,7 +9,7 @@ import MediaKit /// Reihen zeigt und die Logik sonst zweimal dastünde. Kopiert wäre dabei /// ausgerechnet der Teil gewesen, an dem schon mehrere Fehler steckten. struct HomeView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(HomeLayoutSettings.self) private var layout @State private var feed = HomeFeed() @@ -41,17 +41,17 @@ struct HomeView: View { } } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) // Kein Titel: der Name der App gehört auf den Homescreen, nicht // über jeden Bildschirm. Darunter liegt der Aufmacher, und der sagt // ohnehin mehr. .navigationTitle("") - .haloInlineTitle() + .flaroInlineTitle() // Gar kein Hintergrund statt eines weichgezeichneten: die Knöpfe // bringen ihre eigene Glasfläche mit und bleiben auch über dem Bild // lesbar. `.ultraThinMaterial` verschleierte dagegen den halben // Aufmacher. - .haloClearToolbarBackground() + .flaroClearToolbarBackground() // Gar keine Navigationsleiste auf der Startseite. // // Sie war leer — kein Titel, keine Knöpfe — und belegte trotzdem ihre @@ -63,12 +63,12 @@ struct HomeView: View { .refreshable { await feed.load(from: session.backend) } // Was eine Kachel am Server ändert, ändert auch, was hier steht: // eine abgehakte Folge gehört nicht mehr in „Weiterschauen". - .environment(\.haloItemChanged, { await feed.load(from: session.backend) }) + .environment(\.flaroItemChanged, { await feed.load(from: session.backend) }) } private func content(hubs: [MediaRowGroup]) -> some View { ScrollView { - LazyVStack(alignment: .leading, spacing: HaloSpacing.section) { + LazyVStack(alignment: .leading, spacing: FlaroSpacing.section) { let featured = feed.heroItems(for: layout) if !featured.isEmpty { HeroCarousel(items: featured) @@ -78,9 +78,9 @@ struct HomeView: View { MediaRow(row: hub) } } - .padding(.bottom, HaloSpacing.section) + .padding(.bottom, FlaroSpacing.section) } .scrollIndicators(.never) - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } } diff --git a/Halo/Views/Home/MediaRow.swift b/Flaro/Views/Home/MediaRow.swift similarity index 85% rename from Halo/Views/Home/MediaRow.swift rename to Flaro/Views/Home/MediaRow.swift index 4f2868f..e9cdffc 100644 --- a/Halo/Views/Home/MediaRow.swift +++ b/Flaro/Views/Home/MediaRow.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Eine waagerechte Reihe von Titeln. @@ -8,7 +8,7 @@ struct MediaRow: View { /// Wird durchgereicht — siehe `QuickPlayTile`. var opensDetails: Bool = false - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(HomeLayoutSettings.self) private var layout @@ -23,19 +23,19 @@ struct MediaRow: View { private var posterWidth: CGFloat { 116 * layout.cardScale } var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text(row.title) - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.screenMargin) ScrollView(.horizontal) { - LazyHStack(spacing: HaloSpacing.regular) { + LazyHStack(spacing: FlaroSpacing.regular) { ForEach(row.items) { item in QuickPlayTile(item: item, opensDetails: opensDetails) { card(for: item) } } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) // Ohne dieses Ziel rasten die Kacheln nicht ein und die Reihe // bleibt an beliebiger Stelle stehen. .scrollTargetLayout() diff --git a/Halo/Views/Library/LibraryBrowser.swift b/Flaro/Views/Library/LibraryBrowser.swift similarity index 89% rename from Halo/Views/Library/LibraryBrowser.swift rename to Flaro/Views/Library/LibraryBrowser.swift index 9637093..143e5cf 100644 --- a/Halo/Views/Library/LibraryBrowser.swift +++ b/Flaro/Views/Library/LibraryBrowser.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Mediatheken auf dem iPhone — mit einer Leiste zum Wechseln. @@ -20,7 +20,7 @@ import MediaKit /// dessen Einträge zwei Navigationsleisten übereinander tragen. Eine selbst /// gezeichnete Leiste kennt diese Grenze nicht. struct LibraryBrowser: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session /// Was gerade gezeigt wird. /// @@ -36,7 +36,7 @@ struct LibraryBrowser: View { var body: some View { inhalt - .background(Color.haloCanvas) + .background(Color.flaroCanvas) // **Als Sicherheitsrand, nicht als Überzug.** So endet die Liste // über der Leiste, statt dahinter zu verschwinden — und das gilt // auch für die Ladeanzeige und den Fuss „alles geladen". @@ -86,11 +86,11 @@ struct LibraryBrowser: View { } // Dieselbe Fläche wie die Insel auf dem iPad — ein `static`-Bauteil, // kein nachgebautes Aussehen. - .background { HaloSidebar.surface(in: .capsule) } + .background { FlaroSidebar.surface(in: .capsule) } .clipShape(.capsule) .shadow(color: .black.opacity(0.45), radius: 18, y: 6) - .padding(.horizontal, HaloSpacing.regular) - .padding(.bottom, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.bottom, FlaroSpacing.snug) } private var reihe: some View { @@ -99,8 +99,8 @@ struct LibraryBrowser: View { pille(eintrag) } } - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } private func pille(_ eintrag: Eintrag) -> some View { @@ -116,12 +116,12 @@ struct LibraryBrowser: View { } // Kleiner als in der Leiste auf dem iPad: dort stehen drei // Einträge, hier bis zu fünf — und auf einem Telefon. - .font(.haloCaption) - .foregroundStyle(gewaehlt ? Color.haloTextPrimary : Color.haloTextSecondary) - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroCaption) + .foregroundStyle(gewaehlt ? Color.flaroTextPrimary : Color.flaroTextSecondary) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) .background { - if gewaehlt { Capsule().fill(Color.haloElevated) } + if gewaehlt { Capsule().fill(Color.flaroElevated) } } .contentShape(Capsule()) } diff --git a/Halo/Views/Library/LibraryFilterSheet.swift b/Flaro/Views/Library/LibraryFilterSheet.swift similarity index 95% rename from Halo/Views/Library/LibraryFilterSheet.swift rename to Flaro/Views/Library/LibraryFilterSheet.swift index 48ee206..62ce1bf 100644 --- a/Halo/Views/Library/LibraryFilterSheet.swift +++ b/Flaro/Views/Library/LibraryFilterSheet.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Filter und Sortierung einer Mediathek. @@ -14,8 +14,8 @@ struct LibraryFilterSheet: View { let library: MediaLibrary @Environment(\.dismiss) private var dismiss - @Environment(\.haloAccent) private var accent - @Environment(HaloSession.self) private var session + @Environment(\.flaroAccent) private var accent + @Environment(FlaroSession.self) private var session @State private var genres: [MediaFilterOption] = [] /// Ein Arbeitsstand, der erst beim Bestätigen übernommen wird. @@ -91,11 +91,11 @@ struct LibraryFilterSheet: View { // gehen können — sichtbar an Picker-Werten, die in der Standardfarbe // leuchteten, während die Schalter daneben schon die gewählte trugen. .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("filter.title")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { ToolbarItem(placement: .cancellationAction) { Button("action.cancel") { dismiss() } @@ -109,7 +109,7 @@ struct LibraryFilterSheet: View { } .task { await loadGenres() } } - .haloResizableSheet() + .flaroResizableSheet() } private func loadGenres() async { diff --git a/Halo/Views/Library/LibraryView.swift b/Flaro/Views/Library/LibraryView.swift similarity index 88% rename from Halo/Views/Library/LibraryView.swift rename to Flaro/Views/Library/LibraryView.swift index ab11e08..64f629f 100644 --- a/Halo/Views/Library/LibraryView.swift +++ b/Flaro/Views/Library/LibraryView.swift @@ -1,16 +1,16 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Eine Mediathek zum Durchblättern. struct LibraryView: View { let library: MediaLibrary - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session /// Nur für die Kachelgröße. „layout“ heißt hier schon die Wahl /// zwischen Raster und Liste. @Environment(HomeLayoutSettings.self) private var sizing - @AppStorage("halo.library.layout") private var layout: LibraryLayout = .grid + @AppStorage("flaro.library.layout") private var layout: LibraryLayout = .grid @Environment(\.horizontalSizeClass) private var sizeClass @State private var items: [MediaItem] = [] @@ -59,9 +59,9 @@ struct LibraryView: View { } } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(library.title) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { toolbar } .sheet(isPresented: $showsFilters) { LibraryFilterSheet(query: $query, library: library) @@ -80,7 +80,7 @@ struct LibraryView: View { .onChange(of: query) { _, _ in Task { await reload() } } .refreshable { await reload() } // Siehe `HomeView`: was eine Kachel ändert, muss im Raster ankommen. - .environment(\.haloItemChanged, { await reload() }) + .environment(\.flaroItemChanged, { await reload() }) } @ViewBuilder @@ -97,8 +97,8 @@ struct LibraryView: View { // Mindestbreite statt fester Spaltenzahl: dieselbe Ansicht // ergibt auf dem iPhone drei und auf dem iPad sechs Spalten, // ohne dass die Größenklasse abgefragt werden muss. - columns: [GridItem(.adaptive(minimum: 110 * sizing.cardScale), spacing: HaloSpacing.regular)], - spacing: HaloSpacing.loose + columns: [GridItem(.adaptive(minimum: 110 * sizing.cardScale), spacing: FlaroSpacing.regular)], + spacing: FlaroSpacing.loose ) { ForEach(items) { item in QuickPlayTile(item: item) { @@ -119,12 +119,12 @@ struct LibraryView: View { PlaceholderCard(index: position) } } - .padding(HaloSpacing.screenMargin) + .padding(FlaroSpacing.screenMargin) - moreButton.padding(.bottom, HaloSpacing.section) - footerCount.padding(.bottom, HaloSpacing.section) + moreButton.padding(.bottom, FlaroSpacing.section) + footerCount.padding(.bottom, FlaroSpacing.section) } - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } private var list: some View { @@ -137,19 +137,19 @@ struct LibraryView: View { .buttonStyle(.plain) .task { await loadMoreIfNeeded(reaching: item) } - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) } } if pendingCount > 0 { ProgressView() - .padding(.vertical, HaloSpacing.section) + .padding(.vertical, FlaroSpacing.section) .frame(maxWidth: .infinity) } - moreButton.padding(.vertical, HaloSpacing.section) - footerCount.padding(.bottom, HaloSpacing.section) + moreButton.padding(.vertical, FlaroSpacing.section) + footerCount.padding(.bottom, FlaroSpacing.section) } - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } /// Wie viele Kacheln noch kommen — und zwar **gemeldet**, nicht geraten. @@ -173,8 +173,8 @@ struct LibraryView: View { private var footerCount: some View { if let page, !page.hasMore, !items.isEmpty { Text("library.allLoaded \(items.count)") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .frame(maxWidth: .infinity) } } @@ -183,10 +183,10 @@ struct LibraryView: View { @ViewBuilder private var moreButton: some View { if loadMoreFailed, let page, page.hasMore { - VStack(spacing: HaloSpacing.snug) { + VStack(spacing: FlaroSpacing.snug) { Text("library.loadMoreFailed") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Button("library.loadMore") { Task { await loadMore() } } @@ -206,7 +206,7 @@ struct LibraryView: View { // steht der Knopf. Auf dem iPad steht die Suche in der Seitenleiste, // und ein zweiter Weg dorthin wäre nur Verdopplung. if sizeClass == .compact { - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { NavigationLink(value: SearchRoute()) { Image(systemName: "magnifyingglass") } @@ -218,7 +218,7 @@ struct LibraryView: View { // oben, und selbst dort wo es das gibt, ist es in einem Raster kaum // zu treffen. Wer den Verdacht hat, dass etwas fehlt, braucht einen // Knopf und keine Geste. - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Button { Task { await reload() } } label: { @@ -228,7 +228,7 @@ struct LibraryView: View { .disabled(isLoadingMore) } - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Button { showsFilters = true } label: { @@ -239,7 +239,7 @@ struct LibraryView: View { .accessibilityLabel(Text("library.filters")) } - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Button { layout = layout == .grid ? .list : .grid } label: { @@ -255,7 +255,7 @@ struct LibraryView: View { // Reihe etwas steht, kann nur der entscheiden, der die Reihe // schreibt. if sizeClass == .compact { - ToolbarItem(placement: .haloTrailing) { AccountToolbarButton() } + ToolbarItem(placement: .flaroTrailing) { AccountToolbarButton() } } } @@ -380,11 +380,11 @@ enum LibraryLayout: String, CaseIterable { /// Eine Zeile in der Listenansicht. private struct LibraryListRow: View { let item: MediaItem - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session var body: some View { - HStack(spacing: HaloSpacing.regular) { - HaloArtwork( + HStack(spacing: FlaroSpacing.regular) { + FlaroArtwork( url: session.backend?.artworkURL(path: item.thumb, width: 120, height: 180), aspectRatio: PosterCard.aspectRatio ) @@ -393,13 +393,13 @@ private struct LibraryListRow: View { VStack(alignment: .leading, spacing: 2) { Text(item.title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle = item.cardSubtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } } @@ -407,12 +407,12 @@ private struct LibraryListRow: View { if item.isWatched { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .accessibilityLabel(Text("item.watched")) } } - .padding(.horizontal, HaloSpacing.screenMargin) - .padding(.vertical, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.screenMargin) + .padding(.vertical, FlaroSpacing.snug) .contentShape(.rect) .accessibilityElement(children: .combine) } diff --git a/Halo/Views/LiveTV/ChannelSelectionSheet.swift b/Flaro/Views/LiveTV/ChannelSelectionSheet.swift similarity index 88% rename from Halo/Views/LiveTV/ChannelSelectionSheet.swift rename to Flaro/Views/LiveTV/ChannelSelectionSheet.swift index 9aac8a9..757d58e 100644 --- a/Halo/Views/LiveTV/ChannelSelectionSheet.swift +++ b/Flaro/Views/LiveTV/ChannelSelectionSheet.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Welche Sender überhaupt angezeigt werden. @@ -12,8 +12,8 @@ struct ChannelSelectionSheet: View { let channels: [LiveChannel] @Binding var selection: Set - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent @Environment(\.dismiss) private var dismiss var body: some View { @@ -28,9 +28,9 @@ struct ChannelSelectionSheet: View { } } .navigationTitle(Text("liveTV.selection")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { - ToolbarItem(placement: .haloLeading) { + ToolbarItem(placement: .flaroLeading) { // „Alle" statt „keine": eine leere Auswahl bedeutet // ohnehin alle, und ein Knopf, der scheinbar nichts tut, // wäre verwirrend. @@ -57,10 +57,10 @@ struct ChannelSelectionSheet: View { selection.insert(channel.id) } } label: { - HStack(spacing: HaloSpacing.regular) { - HaloArtwork( + HStack(spacing: FlaroSpacing.regular) { + FlaroArtwork( url: session.backend?.artworkURL(path: channel.artworkPath, width: 200, height: 120), - aspectRatio: HaloAspectRatio.channelLogo, + aspectRatio: FlaroAspectRatio.channelLogo, fallbackSymbol: "tv", contentMode: .fit ) @@ -69,17 +69,17 @@ struct ChannelSelectionSheet: View { if let number = channel.number { Text(number) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } Text(channel.title) - .font(.haloBody) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextPrimary) Spacer() Image(systemName: isShown(channel) ? "checkmark.circle.fill" : "circle") - .foregroundStyle(isShown(channel) ? accent : Color.haloTextMuted) + .foregroundStyle(isShown(channel) ? accent : Color.flaroTextMuted) } .contentShape(.rect) } diff --git a/Halo/Views/LiveTV/LiveTVView.swift b/Flaro/Views/LiveTV/LiveTVView.swift similarity index 87% rename from Halo/Views/LiveTV/LiveTVView.swift rename to Flaro/Views/LiveTV/LiveTVView.swift index b76554c..60e82b5 100644 --- a/Halo/Views/LiveTV/LiveTVView.swift +++ b/Flaro/Views/LiveTV/LiveTVView.swift @@ -1,11 +1,11 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback import MediaKit /// Live-TV mit Programmführer. struct LiveTVView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(PlaybackSettings.self) private var settings @Environment(NetworkConditions.self) private var network @@ -74,7 +74,7 @@ struct LiveTVView: View { } description: { Text("liveTV.noChannels.description") } actions: { - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { Button("action.retry") { Task { await load() } } searchTrace } @@ -107,12 +107,12 @@ struct LiveTVView: View { } } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("sidebar.liveTV")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { if case .available = availability, !channels.isEmpty { - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Button { showsSelection = true } label: { @@ -120,7 +120,7 @@ struct LiveTVView: View { } .accessibilityLabel(Text("liveTV.selection")) } - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Picker("liveTV.mode", selection: $mode) { Image(systemName: "calendar.day.timeline.left").tag(LiveTVMode.guide) Image(systemName: "list.bullet").tag(LiveTVMode.channels) @@ -169,11 +169,11 @@ struct LiveTVView: View { @ViewBuilder private var searchTrace: some View { if !trace.isEmpty { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { if let channelSource { Text(channelSource) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } ScrollView { @@ -181,7 +181,7 @@ struct LiveTVView: View { ForEach(trace) { probe in Text("\(probe.path) → \(probe.result)") .font(.system(size: 10, design: .monospaced)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(2) .textSelection(.enabled) } @@ -191,18 +191,18 @@ struct LiveTVView: View { .frame(maxHeight: 200) Button { - HaloClipboard.copy( + FlaroClipboard.copy( trace .map { "\($0.path) → \($0.result)" } .joined(separator: "\n") ) } label: { Label("diagnostics.copy", systemImage: "doc.on.doc") - .font(.haloCaption) + .font(.flaroCaption) } .buttonStyle(.bordered) } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } } @@ -216,11 +216,11 @@ struct LiveTVView: View { isTuning: isTuning == channel.id, onTune: { Task { await tune(channel) } } ) - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) } } } - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } private func load() async { @@ -329,43 +329,43 @@ private struct ChannelRow: View { let isTuning: Bool let onTune: () -> Void - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent private var current: LiveProgram? { programs.first { $0.isRunning() } } private var next: LiveProgram? { programs.first { $0.startsAt > Date() } } var body: some View { Button(action: onTune) { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { // Breiter als hoch und ohne Beschneiden: Sendersymbole sind // fast nie quadratisch, und formatfüllend fehlten links und // rechts genau die Teile, an denen man den Sender erkennt. - HaloArtwork( + FlaroArtwork( url: session.backend?.artworkURL(path: channel.artworkPath, width: 200, height: 120), - aspectRatio: HaloAspectRatio.channelLogo, + aspectRatio: FlaroAspectRatio.channelLogo, fallbackSymbol: "tv", contentMode: .fit ) .frame(width: 56, height: 34) VStack(alignment: .leading, spacing: 2) { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { if let number = channel.number { Text(number) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } Text(channel.title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) } if let current { Text(current.title) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) .lineLimit(1) // Der Balken zeigt, wie weit die Sendung ist — beim // Umschalten die nützlichste Angabe überhaupt. @@ -373,14 +373,14 @@ private struct ChannelRow: View { .padding(.top, 2) } else { Text("liveTV.noProgram") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } if let next { Text("liveTV.next \(next.startsAt.formatted(date: .omitted, time: .shortened)) \(next.title)") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } @@ -395,8 +395,8 @@ private struct ChannelRow: View { .foregroundStyle(accent) } } - .padding(.horizontal, HaloSpacing.screenMargin) - .padding(.vertical, HaloSpacing.regular) + .padding(.horizontal, FlaroSpacing.screenMargin) + .padding(.vertical, FlaroSpacing.regular) .contentShape(.rect) } .buttonStyle(.plain) @@ -427,43 +427,43 @@ private struct TuneFailureSheet: View { var body: some View { NavigationStack { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text(failure.channel) - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) Text(failure.reason) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) ForEach(Array(failure.trace.enumerated()), id: \.offset) { _, line in Text(line) .font(.system(size: 11, design: .monospaced)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .textSelection(.enabled) .frame(maxWidth: .infinity, alignment: .leading) } Button { - HaloClipboard.copy(failure.trace.joined(separator: "\n")) + FlaroClipboard.copy(failure.trace.joined(separator: "\n")) } label: { Label("diagnostics.copy", systemImage: "doc.on.doc") - .font(.haloCaption) + .font(.flaroCaption) } .buttonStyle(.bordered) } - .padding(HaloSpacing.screenMargin) + .padding(FlaroSpacing.screenMargin) .frame(maxWidth: .infinity, alignment: .leading) } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("liveTV.tuneFailed")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { ToolbarItem(placement: .confirmationAction) { Button("action.close") { dismiss() } } } } - .haloResizableSheet() + .flaroResizableSheet() } } diff --git a/Halo/Views/LiveTV/ProgramGuide.swift b/Flaro/Views/LiveTV/ProgramGuide.swift similarity index 82% rename from Halo/Views/LiveTV/ProgramGuide.swift rename to Flaro/Views/LiveTV/ProgramGuide.swift index 8a9436f..8bce61e 100644 --- a/Halo/Views/LiveTV/ProgramGuide.swift +++ b/Flaro/Views/LiveTV/ProgramGuide.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Das Programmraster: Sender als Zeilen, Zeit als Achse. @@ -34,7 +34,7 @@ struct ProgramGuide: View { var onRecord: ((LiveProgram) -> Void)? var onRecordSeries: ((LiveProgram) -> Void)? - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var scrollX: CGFloat = 0 @State private var selection: GuideSelection? @@ -50,7 +50,7 @@ struct ProgramGuide: View { var body: some View { VStack(spacing: 0) { axis - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) ScrollView(.vertical) { HStack(alignment: .top, spacing: 0) { @@ -65,7 +65,7 @@ struct ProgramGuide: View { } .scrollIndicators(.never) } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .sheet(item: $selection) { selection in ProgramDetailSheet( channel: selection.channel, @@ -104,12 +104,12 @@ struct ProgramGuide: View { private var axis: some View { HStack(spacing: 0) { Text("liveTV.channelCount \(channels.count)") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) // Der Abstand gehört in die Spaltenbreite hinein, nicht // dazu: außen addiert er sich, und die ganze Zeitachse steht // acht Punkte neben dem Programm darunter. - .padding(.leading, HaloSpacing.snug) + .padding(.leading, FlaroSpacing.snug) .frame(width: channelWidth, alignment: .leading) .layoutPriority(1) @@ -125,7 +125,7 @@ struct ProgramGuide: View { ForEach(layout.ticks, id: \.self) { tick in Text(tick.formatted(date: .omitted, time: .shortened)) .font(.system(.caption2, design: .rounded)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) .frame(width: layout.tickWidth, alignment: .leading) } } @@ -135,7 +135,7 @@ struct ProgramGuide: View { .clipped() } .frame(height: axisHeight) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) } /// Was auf diesem Sender gerade läuft. @@ -147,20 +147,20 @@ struct ProgramGuide: View { VStack(spacing: 0) { ForEach(channels) { channel in Button { onTune(channel, running(on: channel)) } label: { - HStack(spacing: HaloSpacing.snug) { - HaloArtwork(url: artwork(channel), aspectRatio: 1, fallbackSymbol: "tv") + HStack(spacing: FlaroSpacing.snug) { + FlaroArtwork(url: artwork(channel), aspectRatio: 1, fallbackSymbol: "tv") .frame(width: 30, height: 30) .clipShape(.rect(cornerRadius: 4)) VStack(alignment: .leading, spacing: 1) { Text(channel.title) - .font(.haloCaption) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(2) if let number = channel.number { Text(number) .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } } @@ -170,7 +170,7 @@ struct ProgramGuide: View { ProgressView().controlSize(.mini) } } - .padding(.horizontal, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.snug) .frame(width: channelWidth, height: rowHeight, alignment: .leading) .overlay(alignment: .bottom) { rowSeparator } .contentShape(.rect) @@ -179,7 +179,7 @@ struct ProgramGuide: View { } } .frame(width: channelWidth) - .background(Color.haloElevated.opacity(0.4)) + .background(Color.flaroElevated.opacity(0.4)) } private var lanes: some View { @@ -217,9 +217,9 @@ struct ProgramGuide: View { return ZStack(alignment: .topLeading) { if entries.isEmpty { Text("liveTV.noProgram") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) - .padding(.horizontal, HaloSpacing.regular) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) + .padding(.horizontal, FlaroSpacing.regular) .frame(height: rowHeight, alignment: .leading) } @@ -240,7 +240,7 @@ struct ProgramGuide: View { /// Zeile, die man antippt, gehört dann zum Nachbarsender. private var rowSeparator: some View { Rectangle() - .fill(Color.haloSeparator) + .fill(Color.flaroSeparator) .frame(height: 1) } @@ -250,21 +250,21 @@ struct ProgramGuide: View { return Button { selection = GuideSelection(channel: channel, program: program) } label: { VStack(alignment: .leading, spacing: 2) { Text(program.title) - .font(.haloCaption) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(2) .multilineTextAlignment(.leading) Text(program.startsAt.formatted(date: .omitted, time: .shortened)) .font(.system(.caption2, design: .rounded)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } - .padding(.horizontal, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.snug) .padding(.vertical, 6) .frame(width: layout.width(from: program.startsAt, to: program.endsAt) - 2, height: rowHeight - 2, alignment: .topLeading) .background( - running ? accent.opacity(0.28) : Color.haloElevated, - in: .rect(cornerRadius: HaloRadius.control) + running ? accent.opacity(0.28) : Color.flaroElevated, + in: .rect(cornerRadius: FlaroRadius.control) ) .overlay(alignment: .bottomLeading) { // Der Balken zeigt, wie weit die laufende Sendung ist — beim @@ -299,45 +299,45 @@ private struct ProgramDetailSheet: View { let onRecord: (() -> Void)? let onRecordSeries: (() -> Void)? - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @Environment(\.dismiss) private var dismiss var body: some View { NavigationStack { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { - HStack(spacing: HaloSpacing.regular) { - HaloArtwork(url: artworkURL, aspectRatio: 1, fallbackSymbol: "tv") + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { + FlaroArtwork(url: artworkURL, aspectRatio: 1, fallbackSymbol: "tv") .frame(width: 44, height: 44) .clipShape(.rect(cornerRadius: 6)) VStack(alignment: .leading, spacing: 2) { Text(channel.title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) Text("liveTV.timeRange \(program.startsAt.formatted(date: .omitted, time: .shortened)) \(program.endsAt.formatted(date: .omitted, time: .shortened))") - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } Spacer() } Text(program.title) - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) if let subtitle = program.subtitle { Text(subtitle) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) } if isRunning { Button { onTune(); dismiss() } label: { Label("liveTV.tune", systemImage: "play.fill") .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.borderedProminent) .tint(accent) @@ -348,15 +348,15 @@ private struct ProgramDetailSheet: View { // ohne Aufnahmen fehlt hier schlicht alles. if program.seriesTimerID != nil { Label("liveTV.alreadyScheduled", systemImage: "checkmark.circle") - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } else if onRecord != nil || onRecordSeries != nil { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { if let onRecord { Button { onRecord(); dismiss() } label: { Label("liveTV.record", systemImage: "record.circle") .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.bordered) } @@ -364,7 +364,7 @@ private struct ProgramDetailSheet: View { Button { onRecordSeries(); dismiss() } label: { Label("liveTV.recordSeries", systemImage: "rectangle.stack.badge.record") .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } .buttonStyle(.bordered) } @@ -373,20 +373,20 @@ private struct ProgramDetailSheet: View { if let summary = program.summary, !summary.isEmpty { Text(summary) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) } } - .padding(HaloSpacing.screenMargin) + .padding(FlaroSpacing.screenMargin) .frame(maxWidth: .infinity, alignment: .leading) } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .toolbar { ToolbarItem(placement: .confirmationAction) { Button("action.close") { dismiss() } } } } - .haloResizableSheet() + .flaroResizableSheet() } } diff --git a/Halo/Views/LiveTV/RecordingsView.swift b/Flaro/Views/LiveTV/RecordingsView.swift similarity index 87% rename from Halo/Views/LiveTV/RecordingsView.swift rename to Flaro/Views/LiveTV/RecordingsView.swift index 6cacb46..1f1feb5 100644 --- a/Halo/Views/LiveTV/RecordingsView.swift +++ b/Flaro/Views/LiveTV/RecordingsView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Aufnahmen und was noch vorgemerkt ist. @@ -12,7 +12,7 @@ import MediaKit /// kommt noch" sind dieselbe Frage in zwei Zeitformen, und wer die eine /// stellt, stellt meist gleich die andere mit. struct RecordingsView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var recordings: [LiveRecording] = [] @State private var timers: [LiveTimer] = [] @@ -47,7 +47,7 @@ struct RecordingsView: View { list } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .task { await load() } .refreshable { await load() } .confirmationDialog( @@ -90,7 +90,7 @@ struct RecordingsView: View { } } } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } header: { Text("liveTV.recordings") } @@ -118,23 +118,23 @@ struct RecordingsView: View { } } } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } header: { Text("liveTV.scheduled") } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } } .listStyle(.insetGrouped) .scrollContentBackground(.hidden) - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } // MARK: - Zeilen private func row(_ recording: LiveRecording) -> some View { - HStack(spacing: HaloSpacing.regular) { - HaloArtwork( + HStack(spacing: FlaroSpacing.regular) { + FlaroArtwork( url: session.backend?.artworkURL(path: recording.artworkPath, width: 200, height: 120), aspectRatio: 16 / 9, fallbackSymbol: "recordingtape", @@ -145,23 +145,23 @@ struct RecordingsView: View { VStack(alignment: .leading, spacing: 2) { Text(recording.title) - .font(.haloBody) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) - HStack(spacing: HaloSpacing.tight) { + HStack(spacing: FlaroSpacing.tight) { if recording.isInProgress { // Eine laufende Aufnahme ist keine fertige: sie wächst // noch, und wer sie jetzt öffnet, sieht ein Ende, das // nicht das Ende ist. Label("liveTV.recording.inProgress", systemImage: "record.circle") - .font(.haloCaption) - .foregroundStyle(Color.haloDestructive) + .font(.flaroCaption) + .foregroundStyle(Color.flaroDestructive) } if let subtitle = subtitle(for: recording) { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) .lineLimit(1) } } @@ -175,29 +175,29 @@ struct RecordingsView: View { private func timerRow(_ timer: LiveTimer) -> some View { VStack(alignment: .leading, spacing: 2) { Text(timer.title) - .font(.haloBody) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle = subtitle(channel: timer.channelName, start: timer.startsAt) { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } } } private func seriesRow(_ timer: LiveSeriesTimer) -> some View { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { Image(systemName: "rectangle.stack.badge.record") - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) VStack(alignment: .leading, spacing: 2) { Text(timer.name) - .font(.haloBody) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) Text(timer.recordsAnyChannel ? "liveTV.anyChannel" : "liveTV.series") - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } } } diff --git a/Halo/Views/LoadState.swift b/Flaro/Views/LoadState.swift similarity index 98% rename from Halo/Views/LoadState.swift rename to Flaro/Views/LoadState.swift index e222621..d68af47 100644 --- a/Halo/Views/LoadState.swift +++ b/Flaro/Views/LoadState.swift @@ -52,7 +52,7 @@ extension LoadState { /// Übersetzt Fehler in Sätze, die man einem Menschen zeigen kann. /// -/// **Eine** Stelle. Es gab davon zwei — hier und in `HaloSession` —, und sie +/// **Eine** Stelle. Es gab davon zwei — hier und in `FlaroSession` —, und sie /// waren bereits auseinandergelaufen: derselbe Fehler bekam je nach /// Bildschirm eine andere Erklärung. enum ErrorText { diff --git a/Halo/Views/Navigation/AccountAvatar.swift b/Flaro/Views/Navigation/AccountAvatar.swift similarity index 88% rename from Halo/Views/Navigation/AccountAvatar.swift rename to Flaro/Views/Navigation/AccountAvatar.swift index fd943d4..bab908d 100644 --- a/Halo/Views/Navigation/AccountAvatar.swift +++ b/Flaro/Views/Navigation/AccountAvatar.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Der erste Buchstabe auf einer Scheibe. /// @@ -16,7 +16,7 @@ struct AccountAvatar: View { let name: String var diameter: CGFloat = 26 - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { Text(name.prefix(1).uppercased()) @@ -25,7 +25,7 @@ struct AccountAvatar: View { // Kleinschrift auf einer grossen Scheibe sieht aus wie ein // Fehler. .font(.system(size: diameter * 0.46, weight: .semibold)) - .foregroundStyle(Color.haloCanvas) + .foregroundStyle(Color.flaroCanvas) .frame(width: diameter, height: diameter) .background(accent, in: .circle) } @@ -38,7 +38,7 @@ extension EnvironmentValues { /// Blatt gehört der Wurzel — nur sie überlebt den Wechsel des Bereichs. /// Jeder Bildschirm, der den Knopf zeigt, müsste den Weg dorthin sonst /// als Argument durchgereicht bekommen. - @Entry var haloOpenSettings: () -> Void = {} + @Entry var flaroOpenSettings: () -> Void = {} } /// Das Monogramm als Knopf für eine Werkzeugleiste. @@ -48,8 +48,8 @@ extension EnvironmentValues { /// damit **vor** Suche, Filter und Ansicht statt dahinter. Wo in der Reihe es /// steht, kann nur der entscheiden, der die Reihe schreibt. struct AccountToolbarButton: View { - @Environment(HaloSession.self) private var session - @Environment(\.haloOpenSettings) private var openSettings + @Environment(FlaroSession.self) private var session + @Environment(\.flaroOpenSettings) private var openSettings var body: some View { Button(action: openSettings) { diff --git a/Halo/Views/Navigation/AccountBar.swift b/Flaro/Views/Navigation/AccountBar.swift similarity index 83% rename from Halo/Views/Navigation/AccountBar.swift rename to Flaro/Views/Navigation/AccountBar.swift index 2cd1c8a..45f7a74 100644 --- a/Halo/Views/Navigation/AccountBar.swift +++ b/Flaro/Views/Navigation/AccountBar.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Wer angemeldet ist — unten in der Seitenleiste. /// @@ -22,7 +22,7 @@ import HaloDesign struct AccountBar: View { @Binding var selection: SidebarDestination - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var isExpanded = false @State private var showsSignOutConfirmation = false @@ -55,7 +55,7 @@ struct AccountBar: View { } } .frame(minWidth: 240) - .background(Color.haloSurface) + .background(Color.flaroSurface) // Auf dem iPhone gäbe es diese Leiste gar nicht; die Angabe // steht trotzdem da, damit aus dem Popover dort kein // bildschirmfüllendes Sheet wird. @@ -77,19 +77,19 @@ struct AccountBar: View { Button { isExpanded.toggle() } label: { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { AccountAvatar(name: title) VStack(alignment: .leading, spacing: 0) { Text(title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } @@ -102,10 +102,10 @@ struct AccountBar: View { // Zeigt nach oben: dorthin geht die Karte auf. Image(systemName: "chevron.up") .font(.caption2.weight(.semibold)) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.snug) .contentShape(.rect) } .buttonStyle(.plain) @@ -121,18 +121,18 @@ struct AccountBar: View { ) -> some View { Button(action: perform) { Label(title, systemImage: symbol) - .font(.haloBody) - .foregroundStyle(isDestructive ? Color.haloDestructive : Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(isDestructive ? Color.flaroDestructive : Color.flaroTextPrimary) .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.regular) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) .contentShape(.rect) } .buttonStyle(.plain) } private var separator: some View { - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) } } diff --git a/Halo/Views/Navigation/HaloNavigationBar.swift b/Flaro/Views/Navigation/FlaroNavigationBar.swift similarity index 79% rename from Halo/Views/Navigation/HaloNavigationBar.swift rename to Flaro/Views/Navigation/FlaroNavigationBar.swift index ae32527..ebb0850 100644 --- a/Halo/Views/Navigation/HaloNavigationBar.swift +++ b/Flaro/Views/Navigation/FlaroNavigationBar.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Wo die Navigation sitzt. @@ -49,7 +49,7 @@ struct NavigationGroup: Identifiable { /// mit Kontur; Glas bringt seine Kante selbst mit — sie hellt auf, wo Licht /// fällt, und verschwindet, wo keins ist. Das ist der Unterschied zwischen /// „umrandet" und „aus Glas". -struct HaloSidebar: View { +struct FlaroSidebar: View { /// Alle Einträge, **ungefiltert**. Was davon zu sehen ist, entscheidet /// `layout` — beim Bearbeiten müssen auch die ausgeblendeten dastehen. let groups: [NavigationGroup] @@ -57,7 +57,7 @@ struct HaloSidebar: View { @Binding var placement: NavigationPlacement let layout: NavigationLayoutSettings - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent /// Welche Gruppen eingeklappt sind. Nach Kennung, nicht nach Stellung: /// kommt eine Mediathek dazu, soll nicht plötzlich die falsche zuklappen. @State private var collapsed: Set = [] @@ -80,7 +80,7 @@ struct HaloSidebar: View { static func surface(in shape: some InsettableShape) -> some View { shape .fill(.ultraThinMaterial) - .overlay { shape.fill(Color.haloCanvas.opacity(0.55)) } + .overlay { shape.fill(Color.flaroCanvas.opacity(0.55)) } .overlay { shape.strokeBorder( LinearGradient( @@ -134,10 +134,10 @@ struct HaloSidebar: View { // Ziehen zum Umsortieren mit, und die scrollt von sich aus. if isEditing { Text("sidebar.editHint") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) - .padding(.horizontal, HaloSpacing.regular) - .padding(.bottom, HaloSpacing.snug) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.bottom, FlaroSpacing.snug) List { ForEach(groups) { group in @@ -149,8 +149,8 @@ struct HaloSidebar: View { } header: { if let title = group.title { Text(title) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } } .listRowBackground(Color.clear) @@ -162,13 +162,13 @@ struct HaloSidebar: View { .environment(\.editMode, .constant(.active)) } else { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { ForEach(groups) { group in section(visible(group)) } } - .padding(.horizontal, HaloSpacing.snug) - .padding(.bottom, HaloSpacing.regular) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.bottom, FlaroSpacing.regular) } .scrollBounceBehavior(.basedOnSize) .scrollIndicators(.never) @@ -176,14 +176,14 @@ struct HaloSidebar: View { Spacer(minLength: 0) - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) AccountBar(selection: $selection) - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } .frame(width: Self.width) - .background { Self.surface(in: .rect(cornerRadius: HaloRadius.panel, style: .continuous)) } - .clipShape(.rect(cornerRadius: HaloRadius.panel, style: .continuous)) + .background { Self.surface(in: .rect(cornerRadius: FlaroRadius.panel, style: .continuous)) } + .clipShape(.rect(cornerRadius: FlaroRadius.panel, style: .continuous)) .shadow(color: .black.opacity(0.45), radius: 22, y: 8) // **Kein** Abstand nach rechts: dort schließt der Inhalt unmittelbar // an. Ein Abstand dorthin war der schwarze Spalt zwischen Aufmacher @@ -194,18 +194,18 @@ struct HaloSidebar: View { } private var header: some View { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { Button { withAnimation(.snappy(duration: 0.25)) { isEditing.toggle() } } label: { Text(isEditing ? "action.done" : "sidebar.edit") - .font(.haloCaption) - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.tight) + .font(.flaroCaption) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.tight) .contentShape(Capsule()) } .buttonStyle(.plain) - .foregroundStyle(isEditing ? accent : Color.haloTextSecondary) + .foregroundStyle(isEditing ? accent : Color.flaroTextSecondary) Spacer(minLength: 0) @@ -214,21 +214,21 @@ struct HaloSidebar: View { placement = .top } label: { Image(systemName: "rectangle.topthird.inset.filled") - .font(.haloRowHeader) - .padding(HaloSpacing.tight) + .font(.flaroRowHeader) + .padding(FlaroSpacing.tight) // Ohne eigene Klickfläche reicht das Ziel nur so weit // wie das Zeichen selbst — auf dem Mac trifft man das // mit dem Zeiger kaum. .contentShape(Rectangle()) } .buttonStyle(.plain) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .accessibilityLabel(Text("sidebar.moveToTop")) } } - .padding(.horizontal, HaloSpacing.regular) - .padding(.top, HaloSpacing.regular) - .padding(.bottom, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.top, FlaroSpacing.regular) + .padding(.bottom, FlaroSpacing.snug) } /// Die Einträge einer Gruppe beim Bearbeiten: alle, in der gespeicherten @@ -271,16 +271,16 @@ struct HaloSidebar: View { } label: { HStack { Text(title) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Spacer() Image(systemName: "chevron.down") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .rotationEffect(.degrees(collapsed.contains(group.id) ? -90 : 0)) } - .padding(.horizontal, HaloSpacing.regular) - .padding(.top, HaloSpacing.snug) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.top, FlaroSpacing.snug) } .buttonStyle(.plain) } @@ -301,12 +301,12 @@ struct HaloSidebar: View { private func editRow(_ entry: NavigationEntry) -> some View { let isHidden = layout.isHidden(entry.arrangementID) - return HStack(spacing: HaloSpacing.snug) { + return HStack(spacing: FlaroSpacing.snug) { Button { layout.setHidden(!isHidden, for: entry.arrangementID) } label: { Image(systemName: isHidden ? "circle" : "checkmark.circle.fill") - .foregroundStyle(isHidden ? Color.haloTextMuted : accent) + .foregroundStyle(isHidden ? Color.flaroTextMuted : accent) .contentShape(Rectangle()) } .buttonStyle(.plain) @@ -318,8 +318,8 @@ struct HaloSidebar: View { .lineLimit(1) Spacer(minLength: 0) } - .font(.haloBody) - .foregroundStyle(isHidden ? Color.haloTextMuted : Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(isHidden ? Color.flaroTextMuted : Color.flaroTextPrimary) } private func row(_ entry: NavigationEntry) -> some View { @@ -328,23 +328,23 @@ struct HaloSidebar: View { return Button { selection = entry.destination } label: { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { Image(systemName: entry.symbol) - .font(.haloBody) + .font(.flaroBody) .frame(width: 22, alignment: .center) entry.title - .font(.haloBody) + .font(.flaroBody) // Ein langer Mediathekenname darf die Leiste nicht // breiter machen; sie hat eine feste Breite. .lineLimit(1) Spacer(minLength: 0) } - .foregroundStyle(isSelected ? accent : Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.snug) + .foregroundStyle(isSelected ? accent : Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.snug) .background { if isSelected { - Capsule().fill(Color.haloElevated) + Capsule().fill(Color.flaroElevated) } } .contentShape(Capsule()) @@ -360,29 +360,29 @@ struct HaloSidebar: View { /// Mediatheken stehen hier in einem Menü statt einzeln: oben ist der Platz /// waagerecht begrenzt, und eine Reihe, die bei acht Mediatheken über den Rand /// hinauswächst, wäre schlechter als ein Menü. -struct HaloTopBar: View { +struct FlaroTopBar: View { let groups: [NavigationGroup] @Binding var selection: SidebarDestination @Binding var placement: NavigationPlacement - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { - HStack(spacing: HaloSpacing.tight) { + HStack(spacing: FlaroSpacing.tight) { Button { placement = .sidebar } label: { Image(systemName: "sidebar.leading") - .font(.haloBody) - .padding(.horizontal, HaloSpacing.regular) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroBody) + .padding(.horizontal, FlaroSpacing.regular) + .padding(.vertical, FlaroSpacing.snug) // Ohne eigene Klickfläche reicht das Ziel nur so weit wie // das Zeichen; auf dem Mac war der Umschalter damit // praktisch nicht zu treffen. .contentShape(Capsule()) } .buttonStyle(.plain) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .accessibilityLabel(Text("sidebar.moveToSide")) ForEach(groups) { group in @@ -395,12 +395,12 @@ struct HaloTopBar: View { } } } - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.snug) - .background { HaloSidebar.surface(in: .capsule) } + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) + .background { FlaroSidebar.surface(in: .capsule) } .clipShape(.capsule) .shadow(color: .black.opacity(0.45), radius: 18, y: 6) - .padding(.top, HaloSpacing.snug) + .padding(.top, FlaroSpacing.snug) } private func pill(_ entry: NavigationEntry) -> some View { @@ -410,13 +410,13 @@ struct HaloTopBar: View { selection = entry.destination } label: { entry.title - .font(.haloBody) + .font(.flaroBody) .lineLimit(1) - .foregroundStyle(isSelected ? accent : Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .foregroundStyle(isSelected ? accent : Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) .background { - if isSelected { Capsule().fill(Color.haloElevated) } + if isSelected { Capsule().fill(Color.flaroElevated) } } .contentShape(Capsule()) } @@ -433,21 +433,21 @@ struct HaloTopBar: View { } } } label: { - HStack(spacing: HaloSpacing.tight) { + HStack(spacing: FlaroSpacing.tight) { if let title = group.title { // Ohne das wurde aus „Mediatheken" ein „Mediathe…". // Die Leiste ist so breit wie ihr Inhalt; sie hat keinen // Grund, an einem Wort zu sparen. Text(title).fixedSize() } - Image(systemName: "chevron.down").font(.haloCaption) + Image(systemName: "chevron.down").font(.flaroCaption) } - .font(.haloBody) - .foregroundStyle(containsSelection ? accent : Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) + .font(.flaroBody) + .foregroundStyle(containsSelection ? accent : Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) .background { - if containsSelection { Capsule().fill(Color.haloElevated) } + if containsSelection { Capsule().fill(Color.flaroElevated) } } .contentShape(Capsule()) } diff --git a/Halo/Views/Navigation/MediaStack.swift b/Flaro/Views/Navigation/MediaStack.swift similarity index 91% rename from Halo/Views/Navigation/MediaStack.swift rename to Flaro/Views/Navigation/MediaStack.swift index 6c55008..f30e16d 100644 --- a/Halo/Views/Navigation/MediaStack.swift +++ b/Flaro/Views/Navigation/MediaStack.swift @@ -16,7 +16,7 @@ import MediaKit struct MediaStack: View { @ViewBuilder let content: () -> Content - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var path = NavigationPath() var body: some View { @@ -31,7 +31,7 @@ struct MediaStack: View { .navigationDestination(for: LiveTVRoute.self) { _ in LiveTVView() } .navigationDestination(for: SearchRoute.self) { _ in SearchView() } } - .environment(\.haloPush) { ziel in path.append(ziel) } + .environment(\.flaroPush) { ziel in path.append(ziel) } } } @@ -45,5 +45,5 @@ extension EnvironmentValues { /// /// Die Voreinstellung tut nichts: ausserhalb eines Stapels gibt es nichts /// zu schieben, und ein Absturz wäre die schlechtere Antwort. - @Entry var haloPush: (any Hashable) -> Void = { _ in } + @Entry var flaroPush: (any Hashable) -> Void = { _ in } } diff --git a/Halo/Views/Navigation/RootView.swift b/Flaro/Views/Navigation/RootView.swift similarity index 95% rename from Halo/Views/Navigation/RootView.swift rename to Flaro/Views/Navigation/RootView.swift index 0b63c7e..a30f8fe 100644 --- a/Halo/Views/Navigation/RootView.swift +++ b/Flaro/Views/Navigation/RootView.swift @@ -1,8 +1,8 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit -import HaloDownloads -import HaloPlayback +import FlaroDownloads +import FlaroPlayback /// Die Wurzelansicht. /// @@ -10,8 +10,8 @@ import HaloPlayback /// Welche Form die Navigation danach annimmt — Seitenleiste, Leiste oben, /// Leiste unten —, entscheidet das System und der Nutzer, nicht diese Datei. struct RootView: View { - @Environment(HaloSession.self) private var session - @Environment(HaloTheme.self) private var theme + @Environment(FlaroSession.self) private var session + @Environment(FlaroTheme.self) private var theme @Environment(PlaybackCoordinator.self) private var playback @Environment(PlaybackSettings.self) private var settings @Environment(LanguagePreferences.self) private var languages @@ -35,13 +35,13 @@ struct RootView: View { /// Reihenfolge, Ein- und Ausblenden, und ob ein Eintrag in der Leiste oben /// liegt: das System schreibt es hier hinein, wir geben ihm nur einen Ort /// zum Aufbewahren. - @AppStorage("halo.navigation.customization") + @AppStorage("flaro.navigation.customization") private var customization = TabViewCustomization() /// Ob die Navigation an der Seite oder oben sitzt — gespeichert. /// /// Die Wahl gehört dem Nutzer und muss den Neustart überleben. - @AppStorage("halo.navigation.placement") + @AppStorage("flaro.navigation.placement") private var storedPlacement: NavigationPlacement = .sidebar /// Dieselbe Wahl, aber als Zustand dieser Ansicht. @@ -85,7 +85,7 @@ struct RootView: View { case .launching: ProgressView() .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) case .signedOut: SignInView() @@ -104,7 +104,7 @@ struct RootView: View { library } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .environment(topChrome) // **Aus dem Fenster, nicht aus dem Layout.** // @@ -121,7 +121,7 @@ struct RootView: View { // Durchgang. // // **Nicht** die Ursache des Players, der oben und unten hinausging: - // das war die Seitenleiste selbst, siehe `HaloSidebar`. Diese Zeile + // das war die Seitenleiste selbst, siehe `FlaroSidebar`. Diese Zeile // spart eine Layoutrunde, mehr nicht. .onGeometryChange(for: CGSize.self) { proxy in proxy.size @@ -129,7 +129,7 @@ struct RootView: View { .onAppear { statusBarHeight = ScreenSafeArea.current.top } // Hier statt in der Scene: eine View wertet Beobachtungsänderungen // zuverlässig neu aus, ein Scene-Body nicht. - .haloAccent(theme) + .flaroAccent(theme) .tint(theme.accentColor) // **Einer für alle Kacheln.** Einer je Kachel wäre bei einem // Serverausfall ein Stapel aus zwanzig gleichen Meldungen. @@ -174,7 +174,7 @@ struct RootView: View { // ihn jemand von Hand antippte — auch dann, wenn das WLAN seit einer // Stunde wieder stand. // - // Hier und nicht im Koordinator: `HaloDownloads` hängt bewusst nicht + // Hier und nicht im Koordinator: `FlaroDownloads` hängt bewusst nicht // an `NetworkConditions`, es bekommt den Netzzustand als Wert gereicht // (siehe `TransferHold`). Diese Ansicht kennt beide. .onChange(of: network.isReachable) { _, erreichbar in @@ -289,7 +289,7 @@ struct RootView: View { .overlay(alignment: .topTrailing) { profileButton } } } - .customizationID("halo.tab.home") + .customizationID("flaro.tab.home") // Auf dem iPad jede Mediathek einzeln, auf dem iPhone alle // zusammen hinter einem Eintrag. Der Grund ist nicht Geschmack: @@ -307,12 +307,12 @@ struct RootView: View { } label: { sidebarLabel(Text(library.title), symbol: library.kind.symbol) } - .customizationID("halo.tab.library.\(library.id)") + .customizationID("flaro.tab.library.\(library.id)") } } header: { Text("sidebar.libraries") } - .customizationID("halo.tab.libraries") + .customizationID("flaro.tab.libraries") if session.supportsLiveTV { Tab(value: SidebarDestination.liveTV) { @@ -320,7 +320,7 @@ struct RootView: View { } label: { tabLabel("sidebar.liveTV", symbol: "dot.radiowaves.left.and.right") } - .customizationID("halo.tab.liveTV") + .customizationID("flaro.tab.liveTV") } } else { Tab(value: SidebarDestination.libraryHub) { @@ -328,7 +328,7 @@ struct RootView: View { } label: { tabLabel("sidebar.library", symbol: "books.vertical") } - .customizationID("halo.tab.libraryHub") + .customizationID("flaro.tab.libraryHub") } Tab(value: SidebarDestination.downloads) { @@ -336,7 +336,7 @@ struct RootView: View { } label: { tabLabel("sidebar.downloads", symbol: "arrow.down.circle") } - .customizationID("halo.tab.downloads") + .customizationID("flaro.tab.downloads") // **Nicht auf dem iPhone.** Dort führt der Weg über das // Monogramm oben rechts — siehe `profileButton`. Einstellungen @@ -349,7 +349,7 @@ struct RootView: View { } label: { tabLabel("sidebar.settings", symbol: "gearshape") } - .customizationID("halo.tab.settings") + .customizationID("flaro.tab.settings") } // Die Suche nur dort, wo Platz für sie ist. @@ -362,7 +362,7 @@ struct RootView: View { Tab(value: SidebarDestination.search, role: .search) { mediaStack { SearchView() } } - .customizationID("halo.tab.search") + .customizationID("flaro.tab.search") } } .tabViewStyle(.sidebarAdaptable) @@ -389,7 +389,7 @@ struct RootView: View { if sizeClass == .regular { HStack(spacing: 0) { if placement == .sidebar { - HaloSidebar( + FlaroSidebar( groups: navigationGroups, selection: $selection, placement: $placement.animation(Self.placementAnimation), @@ -433,7 +433,7 @@ struct RootView: View { .background { TabChromeSuppressor().frame(width: 0, height: 0) } .overlay(alignment: .top) { if placement == .top { - HaloTopBar( + FlaroTopBar( groups: navigationGroups.map(visible), selection: $selection, placement: $placement.animation(Self.placementAnimation) @@ -473,7 +473,7 @@ struct RootView: View { // Navigationsstapel um sich — die Medienziele aus `mediaStack` ruft // es nie auf. Und beim Schliessen steht man wieder dort, wo man war, // statt in einem Bereich, den man erst wieder verlassen muss. - .environment(\.haloOpenSettings, { showsSettings = true }) + .environment(\.flaroOpenSettings, { showsSettings = true }) .sheet(isPresented: $showsSettings) { NavigationStack { SettingsView() @@ -481,7 +481,7 @@ struct RootView: View { // jeder weiss das, und wer in einem Unterbildschirm steht, // zieht damit gleich alles weg. Ein Knopf sagt es. .toolbar { - ToolbarItem(placement: .haloTrailing) { + ToolbarItem(placement: .flaroTrailing) { Button("action.close") { showsSettings = false } } } @@ -515,16 +515,16 @@ struct RootView: View { showsSettings = true } label: { AccountAvatar(name: session.accountName ?? session.serverName, diameter: 34) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) // Damit er auch über einem hellen Aufmacher steht. .background(.black.opacity(0.28), in: .circle) } .buttonStyle(.plain) - .padding(.trailing, HaloSpacing.regular) + .padding(.trailing, FlaroSpacing.regular) // **Ohne die Statusleiste.** Auf dem iPhone sitzt der Baum innerhalb // des Sicherheitsrands; sie noch einmal dazuzurechnen schöbe den // Knopf um ihre Höhe nach unten, mitten ins Bild. - .padding(.top, HaloSpacing.snug) + .padding(.top, FlaroSpacing.snug) .opacity(topChrome.isRevealed ? 1 : 0) .allowsHitTesting(topChrome.isRevealed) .animation(.snappy(duration: 0.28), value: topChrome.isRevealed) @@ -543,7 +543,7 @@ struct RootView: View { /// schwebt nichts, worunter man greifen müsste. private var topInset: CGFloat { guard sizeClass == .regular else { return 0 } - return statusBarHeight + (placement == .sidebar ? HaloSidebar.margin : Self.topBarHeight) + return statusBarHeight + (placement == .sidebar ? FlaroSidebar.margin : Self.topBarHeight) } private var navigationGroups: [NavigationGroup] { diff --git a/Halo/Views/Navigation/SidebarDestination.swift b/Flaro/Views/Navigation/SidebarDestination.swift similarity index 100% rename from Halo/Views/Navigation/SidebarDestination.swift rename to Flaro/Views/Navigation/SidebarDestination.swift diff --git a/Halo/Views/Navigation/TabChrome.swift b/Flaro/Views/Navigation/TabChrome.swift similarity index 100% rename from Halo/Views/Navigation/TabChrome.swift rename to Flaro/Views/Navigation/TabChrome.swift diff --git a/Halo/Views/Navigation/TopChrome.swift b/Flaro/Views/Navigation/TopChrome.swift similarity index 98% rename from Halo/Views/Navigation/TopChrome.swift rename to Flaro/Views/Navigation/TopChrome.swift index e8c4348..2f4f70d 100644 --- a/Halo/Views/Navigation/TopChrome.swift +++ b/Flaro/Views/Navigation/TopChrome.swift @@ -40,7 +40,7 @@ extension View { /// die nächstgelegene, und eine Reihe, die man seitwärts schiebt, steht /// immer bei null — die Insel käme beim Blättern zurück, obwohl die Seite /// weit unten steht. - func haloRevealsTopChrome() -> some View { + func flaroRevealsTopChrome() -> some View { modifier(RevealsTopChrome()) } } diff --git a/Halo/Views/Placeholder/PlaceholderScreen.swift b/Flaro/Views/Placeholder/PlaceholderScreen.swift similarity index 86% rename from Halo/Views/Placeholder/PlaceholderScreen.swift rename to Flaro/Views/Placeholder/PlaceholderScreen.swift index 3351b9b..562a9e3 100644 --- a/Halo/Views/Placeholder/PlaceholderScreen.swift +++ b/Flaro/Views/Placeholder/PlaceholderScreen.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Ein noch nicht gebauter Bereich. /// @@ -15,9 +15,9 @@ struct PlaceholderScreen: View { } description: { Text("placeholder.description") } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(destination.title) - .haloInlineTitle() + .flaroInlineTitle() } } diff --git a/Halo/Views/Player/AirPlayButton.swift b/Flaro/Views/Player/AirPlayButton.swift similarity index 100% rename from Halo/Views/Player/AirPlayButton.swift rename to Flaro/Views/Player/AirPlayButton.swift diff --git a/Halo/Views/Player/MacFullScreen.swift b/Flaro/Views/Player/MacFullScreen.swift similarity index 100% rename from Halo/Views/Player/MacFullScreen.swift rename to Flaro/Views/Player/MacFullScreen.swift diff --git a/Halo/Views/Player/MiniPlayer.swift b/Flaro/Views/Player/MiniPlayer.swift similarity index 96% rename from Halo/Views/Player/MiniPlayer.swift rename to Flaro/Views/Player/MiniPlayer.swift index a23166e..219ed7d 100644 --- a/Halo/Views/Player/MiniPlayer.swift +++ b/Flaro/Views/Player/MiniPlayer.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Die schwebende Videokachel. /// @@ -51,7 +51,7 @@ struct MiniPlayer: View { // ist als das Fenster, ließe sich nicht mehr wegziehen. let upper = max(160, min(bounds.width * 0.75, 560)) let width = min(max(chosen * zoom, 140), upper) - return CGSize(width: width, height: (width / HaloAspectRatio.landscape).rounded()) + return CGSize(width: width, height: (width / FlaroAspectRatio.landscape).rounded()) } var body: some View { @@ -105,10 +105,10 @@ struct MiniPlayer: View { } } } - .clipShape(.rect(cornerRadius: HaloRadius.card)) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) .overlay { - RoundedRectangle(cornerRadius: HaloRadius.card) - .strokeBorder(Color.haloSeparator, lineWidth: 0.5) + RoundedRectangle(cornerRadius: FlaroRadius.card) + .strokeBorder(Color.flaroSeparator, lineWidth: 0.5) } .shadow(color: .black.opacity(0.5), radius: 12, y: 4) .contentShape(.rect) @@ -158,9 +158,9 @@ struct MiniPlayer: View { } } } - .padding(HaloSpacing.tight) + .padding(FlaroSpacing.tight) // Über dem Fortschrittsbalken, nicht darauf. - .padding(.bottom, HaloSpacing.tight) + .padding(.bottom, FlaroSpacing.tight) } /// Ein kleiner Knopf, der auf jedem Bild lesbar bleibt. diff --git a/Halo/Views/Player/PlaybackScrubber.swift b/Flaro/Views/Player/PlaybackScrubber.swift similarity index 98% rename from Halo/Views/Player/PlaybackScrubber.swift rename to Flaro/Views/Player/PlaybackScrubber.swift index 0dbadee..7ac17e1 100644 --- a/Halo/Views/Player/PlaybackScrubber.swift +++ b/Flaro/Views/Player/PlaybackScrubber.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Der Regler unter dem Film — mit dem, was schon geladen ist. /// @@ -34,7 +34,7 @@ struct PlaybackScrubber: View { /// Bei jeder Berührung — damit die Bedienung stehen bleibt. let onActivity: () -> Void - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent private static let trackHeight: CGFloat = 5 private static let thumbSize: CGFloat = 13 diff --git a/Halo/Views/Player/PlayerModel.swift b/Flaro/Views/Player/PlayerModel.swift similarity index 99% rename from Halo/Views/Player/PlayerModel.swift rename to Flaro/Views/Player/PlayerModel.swift index ca25264..69f29f9 100644 --- a/Halo/Views/Player/PlayerModel.swift +++ b/Flaro/Views/Player/PlayerModel.swift @@ -2,8 +2,8 @@ import AVKit import Foundation import MediaKit import Observation -import HaloDownloads -import HaloPlayback +import FlaroDownloads +import FlaroPlayback /// Führt einen Abspielwunsch aus. /// @@ -169,10 +169,10 @@ final class PlayerModel { /// Wo das Bild aufhört. /// - /// Liegt bei `HaloPlayback`, weil beide Engines es brauchen. Der Name + /// Liegt bei `FlaroPlayback`, weil beide Engines es brauchen. Der Name /// hier bleibt, damit die Ansicht weiter `PlayerModel.VideoFit` schreiben /// kann. - typealias VideoFit = HaloPlayback.VideoFit + typealias VideoFit = FlaroPlayback.VideoFit /// Bild-in-Bild hängt an genau dieser Ebene und wandert deshalb mit. /// diff --git a/Halo/Views/Player/PlayerView.swift b/Flaro/Views/Player/PlayerView.swift similarity index 95% rename from Halo/Views/Player/PlayerView.swift rename to Flaro/Views/Player/PlayerView.swift index 6d3321d..0d68fc6 100644 --- a/Halo/Views/Player/PlayerView.swift +++ b/Flaro/Views/Player/PlayerView.swift @@ -1,7 +1,7 @@ import SwiftUI -import HaloDesign -import HaloDownloads -import HaloPlayback +import FlaroDesign +import FlaroDownloads +import FlaroPlayback /// Der Vollbildplayer. struct PlayerView: View { @@ -13,14 +13,14 @@ struct PlayerView: View { /// Nutzers statt eine Nebenwirkung der Navigation. let model: PlayerModel - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(PlaybackSettings.self) private var settings @Environment(SubtitleAppearance.self) private var subtitleAppearance @Environment(NetworkConditions.self) private var network @Environment(DownloadCoordinator.self) private var downloads @Environment(PlaybackDiagnostics.self) private var diagnostics - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent /// Woran sich Quer- und Hochformat unterscheiden lassen. /// /// Auf jedem iPhone ist das Querformat senkrecht kompakt und das @@ -72,7 +72,7 @@ struct PlayerView: View { /// Knopf. @State private var isFullScreen = false /// Ob Zeiger und Tastatur gelten. Einmal beim Start ermittelt. - private var isMac: Bool { HaloPlatform.isMac } + private var isMac: Bool { FlaroPlatform.isMac } /// Ob gedreht und mit zwei Fingern gezoomt werden kann. /// /// Ein iPad dreht sich von selbst und braucht keinen Knopf dafür; der @@ -200,7 +200,7 @@ struct PlayerView: View { } .opacity(zeigt ? 1 : 0) .allowsHitTesting(zeigt) - .padding(HaloSpacing.loose) + .padding(FlaroSpacing.loose) // Siehe `controls(for:)`: die Kopfzeile lag sonst hinter // der Dynamic Island, und der Zurück-Pfeil war tot. .padding(.top, safeArea.top) @@ -217,7 +217,7 @@ struct PlayerView: View { // ein zweiter Klick auf dieselbe Stelle legte das Fenster // ins Dock, weil das der Doppelklick auf eine // Fensterleiste tut. - .padding(.top, HaloPlatform.isMac ? Self.titleBarHeight : 0) + .padding(.top, FlaroPlatform.isMac ? Self.titleBarHeight : 0) } } } @@ -334,12 +334,12 @@ struct PlayerView: View { // kein Absturz, aber die falsche Farbe. .sheet(item: $trackPicker) { kind in TrackPickerSheet(model: model, kind: kind) - .environment(\.haloAccent, accent) + .environment(\.flaroAccent, accent) } .sheet(isPresented: $showsQualityChoice) { QualitySheet(model: model) .environment(settings) - .environment(\.haloAccent, accent) + .environment(\.flaroAccent, accent) } .onChange(of: showsQualityChoice) { _, isShown in // Nach dem Schließen die Bedienelemente stehen lassen und die @@ -371,7 +371,7 @@ struct PlayerView: View { Spacer() scrubber(for: model) } - .padding(HaloSpacing.loose) + .padding(FlaroSpacing.loose) // **Zusätzlich zum Polster, nicht statt.** Der Sicherheitsrand sagt, // wo der Bildschirm aufhört; das Polster sagt, wie luftig es aussehen // soll. Ersetzte eines das andere, klebte der Schieberegler im @@ -395,7 +395,7 @@ struct PlayerView: View { } private func topBar(for model: PlayerModel) -> some View { - HStack(alignment: .top, spacing: HaloSpacing.regular) { + HStack(alignment: .top, spacing: FlaroSpacing.regular) { // Der Pfeil verkleinert, er beendet nicht. Das ist der ganze // Zweck der Übung: weiterschauen, während man stöbert. Button { @@ -403,7 +403,7 @@ struct PlayerView: View { } label: { Image(systemName: "chevron.down") .font(.title3) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text("player.minimize")) @@ -414,17 +414,17 @@ struct PlayerView: View { } label: { Image(systemName: "xmark") .font(.callout.weight(.semibold)) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text("player.stop")) VStack(alignment: .leading, spacing: 2) { Text(model.title) - .font(.haloCardTitle) + .font(.flaroCardTitle) .lineLimit(1) if let subtitle = model.subtitle { Text(subtitle) - .font(.haloCaption) + .font(.flaroCaption) .foregroundStyle(.white.opacity(0.7)) .lineLimit(1) } @@ -447,7 +447,7 @@ struct PlayerView: View { ? "arrow.down.right.and.arrow.up.left" : "arrow.up.left.and.arrow.down.right") .font(.callout.weight(.semibold)) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text(isFullScreen ? "player.exitFullScreen" : "player.fullScreen")) .keyboardShortcut("f", modifiers: [.command, .control]) @@ -465,7 +465,7 @@ struct PlayerView: View { ? "arrow.down.right.and.arrow.up.left" : "arrow.up.left.and.arrow.down.right") .font(.title3) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text(model.videoFit == .fill ? "player.fitVideo" : "player.fillVideo")) } @@ -488,7 +488,7 @@ struct PlayerView: View { // Zwei Pfeile im Kreis sagen, was passiert: es dreht. Image(systemName: "arrow.triangle.2.circlepath") .font(.title3) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text("player.rotate")) } @@ -524,7 +524,7 @@ struct PlayerView: View { } label: { Image(systemName: model.isPictureInPictureActive ? "pip.exit" : "pip.enter") .font(.title3) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text("player.pictureInPicture")) } @@ -532,7 +532,7 @@ struct PlayerView: View { if model.engine?.supportsAirPlay == true { AirPlayButton(tint: accent) .frame(width: 28, height: 28) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) .accessibilityLabel(Text("player.airPlay")) } @@ -554,7 +554,7 @@ struct PlayerView: View { } label: { Image(systemName: symbol) .font(.title3) - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } .accessibilityLabel(Text(label)) } @@ -578,15 +578,15 @@ struct PlayerView: View { // sagen nicht, was eingestellt ist; „1080p" schon — und genau // das ist die Frage, wegen der man hier hintippt. Text(model.qualityLabel) - .font(.haloCaption.weight(.semibold)) + .font(.flaroCaption.weight(.semibold)) .monospacedDigit() - .padding(.horizontal, HaloSpacing.snug) - .padding(.vertical, HaloSpacing.tight) + .padding(.horizontal, FlaroSpacing.snug) + .padding(.vertical, FlaroSpacing.tight) .overlay { - RoundedRectangle(cornerRadius: HaloRadius.card) + RoundedRectangle(cornerRadius: FlaroRadius.card) .strokeBorder(.white.opacity(0.5), lineWidth: 1) } - .padding(HaloSpacing.snug) + .padding(FlaroSpacing.snug) } } .disabled(model.isChangingQuality) @@ -594,7 +594,7 @@ struct PlayerView: View { } private func transport(for model: PlayerModel) -> some View { - HStack(spacing: HaloSpacing.section * 2) { + HStack(spacing: FlaroSpacing.section * 2) { Button { model.skip(by: .seconds(-settings.rewindSeconds)) scheduleControlsHide() @@ -652,7 +652,7 @@ struct PlayerView: View { } private func scrubber(for model: PlayerModel) -> some View { - VStack(spacing: HaloSpacing.tight) { + VStack(spacing: FlaroSpacing.tight) { PlaybackScrubber( position: model.currentTime, duration: model.duration ?? .seconds(1), @@ -695,9 +695,9 @@ struct PlayerView: View { } } label: { Label("player.nextEpisode", systemImage: "forward.end.fill") - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.regular) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.regular) } .buttonStyle(.borderedProminent) .tint(accent) @@ -706,21 +706,21 @@ struct PlayerView: View { model.skipActiveMarker() } label: { Text(marker.isIntro ? "player.skipIntro" : "player.skipCredits") - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.regular) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.regular) } .buttonStyle(.bordered) .tint(.white) .transition(.move(edge: .trailing).combined(with: .opacity)) } } - .padding(.horizontal, HaloSpacing.section) + .padding(.horizontal, FlaroSpacing.section) .padding(.leading, safeArea.leading) .padding(.trailing, safeArea.trailing) // Über dem Schieberegler, damit sich beides nicht überdeckt, wenn // die Bedienelemente eingeblendet sind. - .padding(.bottom, (showsControls ? 110 : HaloSpacing.section) + safeArea.bottom) + .padding(.bottom, (showsControls ? 110 : FlaroSpacing.section) + safeArea.bottom) } .animation(.snappy, value: model.activeMarker?.id) .animation(.snappy, value: model.hasReachedEnd) @@ -731,10 +731,10 @@ struct PlayerView: View { ZStack { Color.black.ignoresSafeArea() - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { ProgressView().tint(.white) Text("player.restoring") - .font(.haloCardTitle) + .font(.flaroCardTitle) .foregroundStyle(.white) // Die Stelle ausdrücklich nennen: der ganze Zweck ist, dass // niemand denkt, er müsse sie wiederfinden. @@ -747,33 +747,33 @@ struct PlayerView: View { } private func preparing(for model: PlayerModel) -> some View { - VStack(spacing: HaloSpacing.loose) { + VStack(spacing: FlaroSpacing.loose) { ProgressView().tint(.white) Text(model.title) - .font(.haloCardTitle) + .font(.flaroCardTitle) .foregroundStyle(.white) .multilineTextAlignment(.center) Text("player.preparing") - .font(.haloCaption) + .font(.flaroCaption) .foregroundStyle(.white.opacity(0.6)) } - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) } private func failure(message: String) -> some View { - VStack(spacing: HaloSpacing.loose) { + VStack(spacing: FlaroSpacing.loose) { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) .foregroundStyle(.white.opacity(0.8)) Text(message) - .font(.haloBody) + .font(.flaroBody) .foregroundStyle(.white) .multilineTextAlignment(.center) Button("action.close") { playback.close() } .buttonStyle(.borderedProminent) .tint(accent) } - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) } /// Die beiden Flächen zum Springen. @@ -821,7 +821,7 @@ struct PlayerView: View { .labelStyle(.iconOnly) .font(.system(size: 44)) .foregroundStyle(.white) - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) .background(.black.opacity(0.35), in: .circle) .transition(.opacity.combined(with: .scale)) } diff --git a/Halo/Views/Player/QualitySheet.swift b/Flaro/Views/Player/QualitySheet.swift similarity index 96% rename from Halo/Views/Player/QualitySheet.swift rename to Flaro/Views/Player/QualitySheet.swift index 9305a8c..59b1721 100644 --- a/Halo/Views/Player/QualitySheet.swift +++ b/Flaro/Views/Player/QualitySheet.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback /// Die Qualitätswahl im Player, samt dem, was gerade tatsächlich läuft. /// @@ -11,7 +11,7 @@ import HaloPlayback struct QualitySheet: View { let model: PlayerModel - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent /// **Als Fragezeichen**, nicht als Behauptung. /// /// `@Environment(PlaybackSettings.self)` ohne Optional hält die Anwendung @@ -62,14 +62,14 @@ struct QualitySheet: View { } } .navigationTitle(Text("player.quality")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { ToolbarItem(placement: .confirmationAction) { Button("action.done") { dismiss() } } } } - .haloResizableSheet() + .flaroResizableSheet() } /// Was gerade läuft — gemessen, nicht gewünscht. @@ -158,11 +158,11 @@ struct QualitySheet: View { } label: { HStack { Text(label(for: option)) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) if let detail { Text(detail) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } Spacer() if option == model.quality { diff --git a/Halo/Views/Player/TrackPickerSheet.swift b/Flaro/Views/Player/TrackPickerSheet.swift similarity index 94% rename from Halo/Views/Player/TrackPickerSheet.swift rename to Flaro/Views/Player/TrackPickerSheet.swift index b91477e..b1ea80d 100644 --- a/Halo/Views/Player/TrackPickerSheet.swift +++ b/Flaro/Views/Player/TrackPickerSheet.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback import MediaKit /// Auswahl einer Spur — Ton **oder** Untertitel, nicht beides. @@ -27,7 +27,7 @@ struct TrackPickerSheet: View { let kind: Kind @Environment(\.dismiss) private var dismiss - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { NavigationStack { @@ -68,7 +68,7 @@ struct TrackPickerSheet: View { } label: { Label("player.forgetRememberedTrack", systemImage: "arrow.uturn.backward") } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } header: { Text("player.rememberedTrack") } footer: { @@ -121,23 +121,23 @@ struct TrackPickerSheet: View { } } .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text(kind.title)) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { ToolbarItem(placement: .confirmationAction) { Button("action.close") { dismiss() } } } } - .haloMediumSheet() + .flaroMediumSheet() } private func row(title: String, isSelected: Bool, action: @escaping () -> Void) -> some View { Button(action: action) { HStack { Text(title) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) Spacer() if isSelected { Image(systemName: "checkmark").foregroundStyle(accent) @@ -146,7 +146,7 @@ struct TrackPickerSheet: View { .contentShape(.rect) } .buttonStyle(.plain) - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) .accessibilityAddTraits(isSelected ? [.isButton, .isSelected] : .isButton) } } diff --git a/Halo/Views/Player/VideoSurface.swift b/Flaro/Views/Player/VideoSurface.swift similarity index 99% rename from Halo/Views/Player/VideoSurface.swift rename to Flaro/Views/Player/VideoSurface.swift index 60b2386..4812605 100644 --- a/Halo/Views/Player/VideoSurface.swift +++ b/Flaro/Views/Player/VideoSurface.swift @@ -1,6 +1,6 @@ import AVKit import SwiftUI -import HaloPlayback +import FlaroPlayback #if canImport(UIKit) import UIKit #else diff --git a/Halo/Views/Search/SearchView.swift b/Flaro/Views/Search/SearchView.swift similarity index 82% rename from Halo/Views/Search/SearchView.swift rename to Flaro/Views/Search/SearchView.swift index 4b48fac..87882b1 100644 --- a/Halo/Views/Search/SearchView.swift +++ b/Flaro/Views/Search/SearchView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Sucht über alle Mediatheken des Servers. @@ -8,7 +8,7 @@ import MediaKit /// `SearchFeed`, weil der Fernseher dieselben Fragen an denselben Server /// stellt. Siehe dort, warum das kein Beiwerk ist. struct SearchView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var text = "" @State private var feed = SearchFeed() @@ -23,9 +23,9 @@ struct SearchView: View { results } } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("search.title")) - .haloInlineTitle() + .flaroInlineTitle() .searchable(text: $text) // Jeder Tastendruck löst eine Anfrage aus — aber erst nach einer // kurzen Pause. Ohne sie schickt „Blade Runner" zwölf Anfragen los, @@ -35,12 +35,12 @@ struct SearchView: View { // in der Suche nicht, und deshalb wirkte „als gesehen markieren" aus // dem Kontextmenü zwar am Server, aber die Kachel lud nie neu — es sah // aus, als wäre nichts passiert. - .environment(\.haloItemChanged, { await feed.search(text, from: session.backend) }) + .environment(\.flaroItemChanged, { await feed.search(text, from: session.backend) }) } private var results: some View { ScrollView { - LazyVStack(alignment: .leading, spacing: HaloSpacing.section) { + LazyVStack(alignment: .leading, spacing: FlaroSpacing.section) { ForEach(feed.hubs) { hub in // **Antippen führt auf die Seite, nicht in den Film.** Die // Suche benutzt dieselbe Reihe wie die Startseite — und @@ -51,9 +51,9 @@ struct SearchView: View { MediaRow(row: hub, opensDetails: true) } } - .padding(.vertical, HaloSpacing.loose) + .padding(.vertical, FlaroSpacing.loose) } - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } @ViewBuilder @@ -72,7 +72,7 @@ struct SearchView: View { text = entry } label: { Label(entry, systemImage: "clock.arrow.circlepath") - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } } } header: { @@ -81,12 +81,12 @@ struct SearchView: View { Button("search.forgetRecent", role: .destructive) { feed.forgetRecent() } - .font(.haloCaption) + .font(.flaroCaption) } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } .scrollContentBackground(.hidden) - .haloRevealsTopChrome() + .flaroRevealsTopChrome() } } } diff --git a/Halo/Views/Settings/AppearanceView.swift b/Flaro/Views/Settings/AppearanceView.swift similarity index 78% rename from Halo/Views/Settings/AppearanceView.swift rename to Flaro/Views/Settings/AppearanceView.swift index d797e36..466bd12 100644 --- a/Halo/Views/Settings/AppearanceView.swift +++ b/Flaro/Views/Settings/AppearanceView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Die Farbeinstellungen. /// @@ -8,22 +8,22 @@ import HaloDesign /// Farbe greift die Kontrastkorrektur — und sagt es auch, statt die Farbe /// stillschweigend zu verändern. struct AppearanceView: View { - @Environment(HaloTheme.self) private var theme + @Environment(FlaroTheme.self) private var theme @Environment(HomeLayoutSettings.self) private var layout @State private var customHex: String = "" @State private var showsCustomField = false - private let columns = [GridItem(.adaptive(minimum: 64), spacing: HaloSpacing.regular)] + private let columns = [GridItem(.adaptive(minimum: 64), spacing: FlaroSpacing.regular)] var body: some View { Form { Section { - LazyVGrid(columns: columns, spacing: HaloSpacing.regular) { + LazyVGrid(columns: columns, spacing: FlaroSpacing.regular) { ForEach(AccentOption.palette) { option in swatch(option) } } - .padding(.vertical, HaloSpacing.snug) + .padding(.vertical, FlaroSpacing.snug) } header: { Text("appearance.accent") } footer: { @@ -38,7 +38,7 @@ struct AppearanceView: View { LabeledContent { Text(layout.cardScale.formatted(.percent.precision(.fractionLength(0)))) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .monospacedDigit() } label: { Text("appearance.cardScale") @@ -52,7 +52,7 @@ struct AppearanceView: View { } footer: { Text("appearance.cardScaleExplanation") } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) Section { Toggle("appearance.customColor", isOn: $showsCustomField.animation(.snappy)) @@ -60,7 +60,7 @@ struct AppearanceView: View { if showsCustomField { HStack { TextField("appearance.hexPlaceholder", text: $customHex) - .haloPlainEntry() + .flaroPlainEntry() .onSubmit(applyCustomColor) Button("action.apply", action: applyCustomColor) @@ -69,8 +69,8 @@ struct AppearanceView: View { if !customHex.isEmpty, RGB(string: customHex) == nil { Text("appearance.invalidHex") - .font(.haloCaption) - .foregroundStyle(Color.haloDestructive) + .font(.flaroCaption) + .foregroundStyle(Color.flaroDestructive) } } @@ -80,11 +80,11 @@ struct AppearanceView: View { // als im Farbwähler, und niemand wüsste warum. Label { Text("appearance.adjusted \(theme.accent.hexString)") - .font(.haloCaption) + .font(.flaroCaption) } icon: { Image(systemName: "info.circle") } - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } } @@ -92,11 +92,11 @@ struct AppearanceView: View { PreviewRow() } } - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("appearance.title")) - .haloInlineTitle() + .flaroInlineTitle() .onAppear { if theme.selection.id == AccentOption.customIdentifier { customHex = theme.selection.color.hexString @@ -119,13 +119,13 @@ struct AppearanceView: View { // Dunkel auf der Farbe, nicht weiß: die Palette // enthält helle Töne wie Amber, auf denen Weiß // verschwinden würde. - .foregroundStyle(Color.haloCanvas) + .foregroundStyle(Color.flaroCanvas) } } .overlay { Circle() .strokeBorder( - theme.selection == option ? Color.haloTextPrimary : .clear, + theme.selection == option ? Color.flaroTextPrimary : .clear, lineWidth: 2 ) .padding(-4) @@ -144,27 +144,27 @@ struct AppearanceView: View { /// Zeigt die gewählte Farbe an den Stellen, an denen sie später auftaucht. private struct PreviewRow: View { - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { - HStack(spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { Label("hero.play", systemImage: "play.fill") - .font(.haloCardTitle) - .padding(.horizontal, HaloSpacing.loose) - .padding(.vertical, HaloSpacing.snug) - .background(accent, in: .rect(cornerRadius: HaloRadius.control)) - .foregroundStyle(Color.haloCanvas) + .font(.flaroCardTitle) + .padding(.horizontal, FlaroSpacing.loose) + .padding(.vertical, FlaroSpacing.snug) + .background(accent, in: .rect(cornerRadius: FlaroRadius.control)) + .foregroundStyle(Color.flaroCanvas) Label("sidebar.downloads", systemImage: "arrow.down.circle") - .font(.haloCardTitle) + .font(.flaroCardTitle) .foregroundStyle(accent) } WatchProgressBar(fraction: 0.45) } - .padding(.vertical, HaloSpacing.snug) - .listRowBackground(Color.haloSurface) + .padding(.vertical, FlaroSpacing.snug) + .listRowBackground(Color.flaroSurface) } } diff --git a/Halo/Views/Settings/AudioLanguagePicker.swift b/Flaro/Views/Settings/AudioLanguagePicker.swift similarity index 86% rename from Halo/Views/Settings/AudioLanguagePicker.swift rename to Flaro/Views/Settings/AudioLanguagePicker.swift index 8785ab4..c89b244 100644 --- a/Halo/Views/Settings/AudioLanguagePicker.swift +++ b/Flaro/Views/Settings/AudioLanguagePicker.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// „Welche Tonsprache gilt hier?" — an genau zwei Orten dieselbe Frage. /// @@ -23,15 +23,15 @@ struct AudioLanguagePicker: View { let title: LocalizedStringKey @Environment(LanguagePreferences.self) private var languages - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { Text(title) - .font(.haloBody) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextPrimary) - Spacer(minLength: HaloSpacing.regular) + Spacer(minLength: FlaroSpacing.regular) Menu { // „Wie allgemein" zuerst und als Vorgabe: die meisten Ebenen @@ -53,11 +53,11 @@ struct AudioLanguagePicker: View { } } } label: { - HStack(spacing: HaloSpacing.tight) { + HStack(spacing: FlaroSpacing.tight) { Text(currentLabel) - .font(.haloBody) + .font(.flaroBody) Image(systemName: "chevron.up.chevron.down") - .font(.haloCaption) + .font(.flaroCaption) } .foregroundStyle(accent) } diff --git a/Halo/Views/Settings/DiagnosticsView.swift b/Flaro/Views/Settings/DiagnosticsView.swift similarity index 83% rename from Halo/Views/Settings/DiagnosticsView.swift rename to Flaro/Views/Settings/DiagnosticsView.swift index cc2e7d6..214df5d 100644 --- a/Halo/Views/Settings/DiagnosticsView.swift +++ b/Flaro/Views/Settings/DiagnosticsView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Was beim letzten Abspielversuch passiert ist. /// @@ -7,8 +7,8 @@ import HaloDesign /// wurde, über welche Verbindung, und wie lange jeder Schritt gedauert hat. /// Ein Screenshot davon sagt mehr als jede Beschreibung. struct DiagnosticsView: View { - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent @Environment(PlaybackDiagnostics.self) private var diagnostics var body: some View { Form { @@ -17,14 +17,14 @@ struct DiagnosticsView: View { LabeledContent("settings.serverName", value: session.serverName) if let address = session.serverAddress { LabeledContent("settings.address", value: address) - .font(.haloCaption) + .font(.flaroCaption) } if let beschreibung = session.connectionDescription { LabeledContent("settings.connection", value: beschreibung) - .font(.haloCaption) + .font(.flaroCaption) } } else { - Text("diagnostics.noServer").foregroundStyle(Color.haloTextMuted) + Text("diagnostics.noServer").foregroundStyle(Color.flaroTextMuted) } } @@ -36,11 +36,11 @@ struct DiagnosticsView: View { VStack(alignment: .leading, spacing: 2) { Text("diagnostics.source") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Text(attempt.sourceDescription) .font(.system(.caption2, design: .monospaced)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } } @@ -48,13 +48,13 @@ struct DiagnosticsView: View { ForEach(Array(attempt.steps.enumerated()), id: \.offset) { _, step in Text(step) .font(.system(.caption, design: .monospaced)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } } Section { Button { - HaloClipboard.copy(report(for: attempt)) + FlaroClipboard.copy(report(for: attempt)) } label: { Label("diagnostics.copy", systemImage: "doc.on.doc") } @@ -63,7 +63,7 @@ struct DiagnosticsView: View { } } else { Section("diagnostics.lastAttempt") { - Text("diagnostics.nothingYet").foregroundStyle(Color.haloTextMuted) + Text("diagnostics.nothingYet").foregroundStyle(Color.flaroTextMuted) } } @@ -79,11 +79,11 @@ struct DiagnosticsView: View { // gehen können — sichtbar an Picker-Werten, die in der Standardfarbe // leuchteten, während die Schalter daneben schon die gewählte trugen. .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("diagnostics.title")) - .haloInlineTitle() + .flaroInlineTitle() } /// Der Bericht als Text zum Weitergeben. @@ -92,7 +92,7 @@ struct DiagnosticsView: View { /// Abfrageteil abgelegt, und der Servername verrät nichts Vertrauliches. private func report(for attempt: PlaybackDiagnostics.Attempt) -> String { """ - Halo \(Branding.version) · Build \(Branding.buildStamp) + Flaro \(Branding.version) · Build \(Branding.buildStamp) Verbindung: \(attempt.connection) Titel: \(attempt.title) Weg: \(attempt.method) diff --git a/Halo/Views/Settings/DownloadSettingsView.swift b/Flaro/Views/Settings/DownloadSettingsView.swift similarity index 95% rename from Halo/Views/Settings/DownloadSettingsView.swift rename to Flaro/Views/Settings/DownloadSettingsView.swift index 5ecffb9..2b77ae4 100644 --- a/Halo/Views/Settings/DownloadSettingsView.swift +++ b/Flaro/Views/Settings/DownloadSettingsView.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloDownloads +import FlaroDesign +import FlaroDownloads import MediaKit /// Was beim Herunterladen gilt. @@ -11,7 +11,7 @@ import MediaKit /// rutschen. struct DownloadSettingsView: View { @Environment(DownloadCoordinator.self) private var downloads - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { @Bindable var downloads = downloads @@ -73,11 +73,11 @@ struct DownloadSettingsView: View { } } .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("sidebar.downloads")) - .haloInlineTitle() + .flaroInlineTitle() } private var storageLimitBinding: Binding { diff --git a/Halo/Views/Settings/HomeLayoutView.swift b/Flaro/Views/Settings/HomeLayoutView.swift similarity index 90% rename from Halo/Views/Settings/HomeLayoutView.swift rename to Flaro/Views/Settings/HomeLayoutView.swift index 018c986..2b465fb 100644 --- a/Halo/Views/Settings/HomeLayoutView.swift +++ b/Flaro/Views/Settings/HomeLayoutView.swift @@ -1,11 +1,11 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Reihen der Startseite ein- und ausblenden und umsortieren. struct HomeLayoutView: View { - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent @Environment(HomeLayoutSettings.self) private var layout @State private var rows: [HomeRow] = [] @@ -21,9 +21,9 @@ struct HomeLayoutView: View { var body: some View { list - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("homeLayout.title")) - .haloInlineTitle() + .flaroInlineTitle() .task { await load() } } @@ -48,7 +48,7 @@ struct HomeLayoutView: View { // gerade so weit hereinlugt, wie man es mag. LabeledContent { Text(layout.heroHeight.formatted(.percent.precision(.fractionLength(0)))) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .monospacedDigit() } label: { Text("home.heroHeight") @@ -64,7 +64,7 @@ struct HomeLayoutView: View { } footer: { Text("home.heroExplanation") } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) // **Mehrfachauswahl, und leer heisst alle.** Ein `Picker` könnte // nur eine Mediathek, und „keine" wäre dort ein eigener Eintrag, @@ -84,7 +84,7 @@ struct HomeLayoutView: View { } } label: { Text(library.title) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) } // Damit der ganze Streifen trifft und nicht nur // die Schrift — eine Zeile, bei der nur ein Wort @@ -99,7 +99,7 @@ struct HomeLayoutView: View { } header: { Text("home.heroLibraries") } footer: { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { Text("home.heroLibrariesExplanation") // Lieber eine Erklärung als ein Aufmacher, der // heimlich etwas anderes zeigt: trifft die Auswahl @@ -107,18 +107,18 @@ struct HomeLayoutView: View { // dann muss dastehen, warum. if greiftInsLeere { Text("home.heroLibrariesNoMatch") - .foregroundStyle(Color.haloDestructive) + .foregroundStyle(Color.flaroDestructive) } } } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } Section { Toggle("home.combineContinue", isOn: $layout.combinesContinueRows) Toggle("home.hideWatched", isOn: $layout.hidesWatchedInLatest) } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) if isLoading { ProgressView() @@ -126,8 +126,8 @@ struct HomeLayoutView: View { .listRowBackground(Color.clear) } else if rows.isEmpty { Text("homeLayout.empty") - .foregroundStyle(Color.haloTextSecondary) - .listRowBackground(Color.haloSurface) + .foregroundStyle(Color.flaroTextSecondary) + .listRowBackground(Color.flaroSurface) } Section { @@ -140,9 +140,9 @@ struct HomeLayoutView: View { } )) { Text(row.title) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) } - .listRowBackground(Color.haloSurface) + .listRowBackground(Color.flaroSurface) } .onMove { indices, destination in rows.move(fromOffsets: indices, toOffset: destination) @@ -162,7 +162,7 @@ struct HomeLayoutView: View { if !rows.isEmpty { Spacer() EditButton() - .font(.haloCaption) + .font(.flaroCaption) .textCase(nil) } #endif diff --git a/Halo/Views/Settings/LanguagesView.swift b/Flaro/Views/Settings/LanguagesView.swift similarity index 90% rename from Halo/Views/Settings/LanguagesView.swift rename to Flaro/Views/Settings/LanguagesView.swift index 6777fd3..3764784 100644 --- a/Halo/Views/Settings/LanguagesView.swift +++ b/Flaro/Views/Settings/LanguagesView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Welche Sprachen der Player von sich aus wählen soll. /// @@ -9,7 +9,7 @@ import HaloDesign /// Vorliebe, die niemand äußern kann, ist keine. struct LanguagesView: View { @Environment(LanguagePreferences.self) private var settings - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var isAdding = false @@ -61,11 +61,11 @@ struct LanguagesView: View { } } .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("languages.title")) - .haloInlineTitle() + .flaroInlineTitle() // Eine Rangliste ohne Umsortieren wäre keine. Auf dem iPhone braucht // das Ziehen den Bearbeitungsmodus nicht — `onMove` in einer `Form` // genügt, sobald die Liste bearbeitbar ist. @@ -86,19 +86,19 @@ struct LanguagesView: View { Section { if settings.fallbacks.isEmpty { Text("languages.fallbackEmpty") - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } else { ForEach(Array(settings.fallbacks.enumerated()), id: \.element) { position, code in - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { // Die Nummer, weil die Reihenfolge der ganze Zweck // ist — eine Liste ohne sichtbaren Rang liest sich // wie eine Menge. Text("\(position + 1)") - .font(.haloCaption.monospacedDigit()) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption.monospacedDigit()) + .foregroundStyle(Color.flaroTextSecondary) .frame(minWidth: 18, alignment: .trailing) Text(name(of: code)) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) } } .onMove { von, nach in diff --git a/Halo/Views/Settings/PlaybackSettingsView.swift b/Flaro/Views/Settings/PlaybackSettingsView.swift similarity index 93% rename from Halo/Views/Settings/PlaybackSettingsView.swift rename to Flaro/Views/Settings/PlaybackSettingsView.swift index cbee23b..dfb4b47 100644 --- a/Halo/Views/Settings/PlaybackSettingsView.swift +++ b/Flaro/Views/Settings/PlaybackSettingsView.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback /// Alles, was die Wiedergabe betrifft. /// @@ -9,7 +9,7 @@ import HaloPlayback /// einmal durchgeht und dann in Ruhe lässt. struct PlaybackSettingsView: View { @Environment(PlaybackSettings.self) private var settings - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { @Bindable var settings = settings @@ -36,7 +36,7 @@ struct PlaybackSettingsView: View { } header: { Text("settings.quality") } footer: { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { Text("settings.qualityExplanation") // **Nur mit Beleg.** Eine Warnung, die immer dasteht, @@ -55,8 +55,8 @@ struct PlaybackSettingsView: View { } icon: { Image(systemName: "exclamationmark.triangle") } - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } } } @@ -108,11 +108,11 @@ struct PlaybackSettingsView: View { } } .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("settings.playback")) - .haloInlineTitle() + .flaroInlineTitle() } private func rateLabel(_ rate: Double) -> String { diff --git a/Halo/Views/Settings/ServerSettingsView.swift b/Flaro/Views/Settings/ServerSettingsView.swift similarity index 90% rename from Halo/Views/Settings/ServerSettingsView.swift rename to Flaro/Views/Settings/ServerSettingsView.swift index 12e279f..615b711 100644 --- a/Halo/Views/Settings/ServerSettingsView.swift +++ b/Flaro/Views/Settings/ServerSettingsView.swift @@ -1,11 +1,11 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Mit welchem Server gesprochen wird — und was er gerade anbietet. struct ServerSettingsView: View { - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent var body: some View { Form { @@ -19,7 +19,7 @@ struct ServerSettingsView: View { } if let address = session.serverAddress { LabeledContent("settings.address", value: address) - .font(.haloCaption) + .font(.flaroCaption) .textSelection(.enabled) } if let accountName = session.accountName { @@ -44,11 +44,11 @@ struct ServerSettingsView: View { } } .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("settings.server")) - .haloInlineTitle() + .flaroInlineTitle() } /// Zeigt, ob der Server im selben Netz steht — beim Suchen nach Ruckeln @@ -64,20 +64,20 @@ struct ServerSettingsView: View { struct LibraryRow: View { let library: MediaLibrary - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent @State private var phase: Phase = .idle private enum Phase { case idle, running, done } var body: some View { LabeledContent { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { // Nur, wenn es nicht dasselbe Wort ist: eine Mediathek namens // „Filme" stand sonst als „Filme — Filme" da. if library.title.localizedCaseInsensitiveCompare(library.kind.label) != .orderedSame { Text(library.kind.label) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } if session.backend?.capabilities.contains(.libraryScan) == true { diff --git a/Halo/Views/Settings/SettingsChrome.swift b/Flaro/Views/Settings/SettingsChrome.swift similarity index 84% rename from Halo/Views/Settings/SettingsChrome.swift rename to Flaro/Views/Settings/SettingsChrome.swift index c78a569..5096991 100644 --- a/Halo/Views/Settings/SettingsChrome.swift +++ b/Flaro/Views/Settings/SettingsChrome.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Das farbige Symbol vor einer Einstellungszeile. /// @@ -35,7 +35,7 @@ struct SettingsHeader: View { let explanation: LocalizedStringKey var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Image(systemName: symbol) .font(.title2.weight(.semibold)) .foregroundStyle(.white) @@ -43,16 +43,16 @@ struct SettingsHeader: View { .background(colour.gradient, in: .rect(cornerRadius: 13)) Text(title) - .font(.haloTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroTitle) + .foregroundStyle(Color.flaroTextPrimary) Text(explanation) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(.vertical, HaloSpacing.snug) - .listRowBackground(Color.haloSurface) + .padding(.vertical, FlaroSpacing.snug) + .listRowBackground(Color.flaroSurface) } } diff --git a/Halo/Views/Settings/SettingsView.swift b/Flaro/Views/Settings/SettingsView.swift similarity index 92% rename from Halo/Views/Settings/SettingsView.swift rename to Flaro/Views/Settings/SettingsView.swift index 49a0299..96a7ad2 100644 --- a/Halo/Views/Settings/SettingsView.swift +++ b/Flaro/Views/Settings/SettingsView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Die Einstellungen — als Übersicht, nicht als eine lange Liste. /// @@ -10,8 +10,8 @@ import HaloDesign /// /// Jetzt führt jede Zeile in einen Bildschirm, der zu **einer** Frage gehört. struct SettingsView: View { - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent @State private var showsSignOutConfirmation = false var body: some View { @@ -23,7 +23,7 @@ struct SettingsView: View { } label: { LabeledContent { Text(session.serverName) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } label: { row( "settings.server", @@ -83,12 +83,12 @@ struct SettingsView: View { // gehen können — sichtbar an Picker-Werten, die in der Standardfarbe // leuchteten, während die Schalter daneben schon die gewählte trugen. .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .haloRevealsTopChrome() - .background(Color.haloCanvas) + .flaroRevealsTopChrome() + .background(Color.flaroCanvas) .navigationTitle(Text("sidebar.settings")) - .haloInlineTitle() + .flaroInlineTitle() .confirmationDialog( Text("settings.signOutConfirm"), isPresented: $showsSignOutConfirmation, @@ -115,7 +115,7 @@ struct SettingsView: View { } private func row(_ title: LocalizedStringKey, symbol: String, colour: Color) -> some View { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { SettingsIcon(symbol: symbol, colour: colour) Text(title) } diff --git a/Halo/Views/Settings/SubtitleAppearanceView.swift b/Flaro/Views/Settings/SubtitleAppearanceView.swift similarity index 92% rename from Halo/Views/Settings/SubtitleAppearanceView.swift rename to Flaro/Views/Settings/SubtitleAppearanceView.swift index ed2d35d..744bf9b 100644 --- a/Halo/Views/Settings/SubtitleAppearanceView.swift +++ b/Flaro/Views/Settings/SubtitleAppearanceView.swift @@ -1,11 +1,11 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback /// Wie Untertitel aussehen sollen. struct SubtitleAppearanceView: View { @Environment(SubtitleAppearance.self) private var appearance - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent var body: some View { @Bindable var appearance = appearance @@ -51,11 +51,11 @@ struct SubtitleAppearanceView: View { } } .tint(accent) - .haloForm() + .flaroForm() .scrollContentBackground(.hidden) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("subtitleStyle.title")) - .haloInlineTitle() + .flaroInlineTitle() } /// Ein Beispielsatz über grauer Fläche. @@ -84,10 +84,10 @@ struct SubtitleAppearanceView: View { radius: 2, y: 1 ) - .padding(HaloSpacing.regular) + .padding(FlaroSpacing.regular) } .frame(height: 96) - .clipShape(.rect(cornerRadius: HaloRadius.card)) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) .listRowInsets(EdgeInsets()) .listRowBackground(Color.clear) } diff --git a/Halo/Views/SignIn/ConnectionStateViews.swift b/Flaro/Views/SignIn/ConnectionStateViews.swift similarity index 75% rename from Halo/Views/SignIn/ConnectionStateViews.swift rename to Flaro/Views/SignIn/ConnectionStateViews.swift index ae803c4..7f5ab71 100644 --- a/Halo/Views/SignIn/ConnectionStateViews.swift +++ b/Flaro/Views/SignIn/ConnectionStateViews.swift @@ -1,21 +1,21 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Während ein Weg zum Server gesucht wird. struct ConnectingView: View { let serverName: String var body: some View { - VStack(spacing: HaloSpacing.loose) { + VStack(spacing: FlaroSpacing.loose) { ProgressView() Text(serverName.isEmpty ? String(localized: "connecting.searching") : String(localized: "connecting.to \(serverName)")) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) } } @@ -33,8 +33,8 @@ struct ConnectionFailureView: View { } actions: { Button("action.retry", action: onRetry).buttonStyle(.borderedProminent) Button("action.signOut", action: onSignOut) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } - .background(Color.haloCanvas) + .background(Color.flaroCanvas) } } diff --git a/Halo/Views/SignIn/JellyfinSignInView.swift b/Flaro/Views/SignIn/JellyfinSignInView.swift similarity index 71% rename from Halo/Views/SignIn/JellyfinSignInView.swift rename to Flaro/Views/SignIn/JellyfinSignInView.swift index 6272732..9a898fc 100644 --- a/Halo/Views/SignIn/JellyfinSignInView.swift +++ b/Flaro/Views/SignIn/JellyfinSignInView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import JellyfinKit /// Die Anmeldung an einem Jellyfin-Server. @@ -9,9 +9,9 @@ import JellyfinKit /// diesen Code. Der Weg über den Code steht vorn, weil dabei kein Passwort /// durch diese App läuft. struct JellyfinSignInView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(\.dismiss) private var dismiss - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var model: JellyfinSignInModel? @FocusState private var focusedField: Field? @@ -23,16 +23,16 @@ struct JellyfinSignInView: View { ScrollView { if let model { content(for: model) - .padding(.horizontal, HaloSpacing.section) - .padding(.vertical, HaloSpacing.section) + .padding(.horizontal, FlaroSpacing.section) + .padding(.vertical, FlaroSpacing.section) .frame(maxWidth: 420) .frame(maxWidth: .infinity) } } .scrollDismissesKeyboard(.interactively) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .navigationTitle(Text("signIn.jellyfin.title")) - .haloInlineTitle() + .flaroInlineTitle() .toolbar { ToolbarItem(placement: .cancellationAction) { Button("action.cancel") { @@ -66,7 +66,7 @@ struct JellyfinSignInView: View { private func content(for model: JellyfinSignInModel) -> some View { @Bindable var model = model - VStack(alignment: .leading, spacing: HaloSpacing.section) { + VStack(alignment: .leading, spacing: FlaroSpacing.section) { switch model.step { case .address: addressStep(model: model) @@ -82,8 +82,8 @@ struct JellyfinSignInView: View { if let message = model.errorMessage { Label(message, systemImage: "exclamationmark.triangle") - .font(.haloCaption) - .foregroundStyle(Color.haloDestructive) + .font(.flaroCaption) + .foregroundStyle(Color.flaroDestructive) } } } @@ -94,39 +94,39 @@ struct JellyfinSignInView: View { private func addressStep(model: JellyfinSignInModel) -> some View { @Bindable var model = model - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text("signIn.jellyfin.addressPrompt") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) TextField("signIn.jellyfin.addressPlaceholder", text: $model.address) .textFieldStyle(.plain) .textContentType(.URL) - .haloPlainEntry(isAddress: true) + .flaroPlainEntry(isAddress: true) .submitLabel(.go) .focused($focusedField, equals: .address) .onSubmit { Task { await model.findServer() } } // Hier statt beim Öffnen des Sheets: so bekommt das Feld den // Fokus auch dann, wenn man nach einem Vertipper zurückkommt. .task { focusedField = .address } - .padding(HaloSpacing.regular) - .background(Color.haloElevated, in: .rect(cornerRadius: HaloRadius.control)) - .foregroundStyle(Color.haloTextPrimary) + .padding(FlaroSpacing.regular) + .background(Color.flaroElevated, in: .rect(cornerRadius: FlaroRadius.control)) + .foregroundStyle(Color.flaroTextPrimary) // Ein Beispiel statt einer Erklärung: „Schema, Host, Port" sagt // niemandem etwas, „192.168.1.50" jedem. Text("signIn.jellyfin.addressHint") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Button { focusedField = nil Task { await model.findServer() } } label: { Text("signIn.jellyfin.connect") - .font(.haloRowHeader) + .font(.flaroRowHeader) .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) } .buttonStyle(.borderedProminent) .tint(accent) @@ -138,40 +138,40 @@ struct JellyfinSignInView: View { @ViewBuilder private func quickConnectStep(model: JellyfinSignInModel, code: String) -> some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { foundServerHeader(model: model) Text("signIn.jellyfin.quickConnectHint") - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) Text(code) .font(.system(.largeTitle, design: .monospaced, weight: .semibold)) .tracking(8) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) .textSelection(.enabled) - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) .frame(maxWidth: .infinity) - .background(Color.haloElevated, in: .rect(cornerRadius: HaloRadius.control)) + .background(Color.flaroElevated, in: .rect(cornerRadius: FlaroRadius.control)) - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { ProgressView().controlSize(.small) Text("signIn.jellyfin.waitingForApproval") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) } Text("signIn.jellyfin.privacy") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) Button("signIn.jellyfin.usePassword") { model.useCredentials() } - .font(.haloCaption) + .font(.flaroCaption) Button("signIn.jellyfin.otherAddress") { model.startOver() } - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } } @@ -181,12 +181,12 @@ struct JellyfinSignInView: View { private func credentialsStep(model: JellyfinSignInModel) -> some View { @Bindable var model = model - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { foundServerHeader(model: model) TextField("signIn.jellyfin.userName", text: $model.userName) .textContentType(.username) - .haloPlainEntry() + .flaroPlainEntry() .submitLabel(.next) .focused($focusedField, equals: .userName) .onSubmit { focusedField = .password } @@ -200,8 +200,8 @@ struct JellyfinSignInView: View { focusedField = .password return .handled } - .padding(HaloSpacing.regular) - .background(Color.haloElevated, in: .rect(cornerRadius: HaloRadius.control)) + .padding(FlaroSpacing.regular) + .background(Color.flaroElevated, in: .rect(cornerRadius: FlaroRadius.control)) SecureField("signIn.jellyfin.password", text: $model.password) .textContentType(.password) @@ -214,17 +214,17 @@ struct JellyfinSignInView: View { focusedField = .userName return .handled } - .padding(HaloSpacing.regular) - .background(Color.haloElevated, in: .rect(cornerRadius: HaloRadius.control)) + .padding(FlaroSpacing.regular) + .background(Color.flaroElevated, in: .rect(cornerRadius: FlaroRadius.control)) Button { focusedField = nil Task { await model.signInWithPassword() } } label: { Text("signIn.jellyfin.submit") - .font(.haloRowHeader) + .font(.flaroRowHeader) .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) } .buttonStyle(.borderedProminent) .tint(accent) @@ -234,46 +234,46 @@ struct JellyfinSignInView: View { // verworfen. Gespeichert wird nur der Token, und der im // Schlüsselbund. Text("signIn.jellyfin.passwordNote") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) - Divider().overlay(Color.haloSeparator) + Divider().overlay(Color.flaroSeparator) if model.offersQuickConnect { Button("signIn.jellyfin.useCode") { Task { await model.beginQuickConnect() } } - .font(.haloCaption) + .font(.flaroCaption) } Button("signIn.jellyfin.otherAddress") { model.startOver() } - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) } // MARK: - Bausteine @ViewBuilder private func foundServerHeader(model: JellyfinSignInModel) -> some View { - VStack(alignment: .leading, spacing: HaloSpacing.tight) { + VStack(alignment: .leading, spacing: FlaroSpacing.tight) { Text("signIn.jellyfin.foundServer") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .textCase(.uppercase) Text(model.serverName ?? model.address) - .font(.haloTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroTitle) + .foregroundStyle(Color.flaroTextPrimary) } } @ViewBuilder private func waiting(text: LocalizedStringKey) -> some View { - HStack(spacing: HaloSpacing.snug) { + HStack(spacing: FlaroSpacing.snug) { ProgressView().controlSize(.small) Text(text) - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) } .frame(maxWidth: .infinity, alignment: .center) - .padding(.vertical, HaloSpacing.section) + .padding(.vertical, FlaroSpacing.section) } } diff --git a/Halo/Views/SignIn/SignInView.swift b/Flaro/Views/SignIn/SignInView.swift similarity index 67% rename from Halo/Views/SignIn/SignInView.swift rename to Flaro/Views/SignIn/SignInView.swift index de818f1..1b04848 100644 --- a/Halo/Views/SignIn/SignInView.swift +++ b/Flaro/Views/SignIn/SignInView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Der Anmeldebildschirm. /// @@ -12,54 +12,54 @@ import HaloDesign /// wird die Adresse eingetippt und, wann immer der Server es anbietet, ein /// Code bestätigt statt eines Passworts — das führt `JellyfinSignInView`. struct SignInView: View { - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent @State private var showsJellyfinSignIn = false var body: some View { - VStack(spacing: HaloSpacing.section) { + VStack(spacing: FlaroSpacing.section) { Spacer() - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { Image(systemName: "play.rectangle.on.rectangle") .font(.system(size: 52)) .foregroundStyle(accent) Text(Branding.appName) - .font(.haloHero) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroHero) + .foregroundStyle(Color.flaroTextPrimary) Text("signIn.tagline") - .font(.haloBody) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextSecondary) .multilineTextAlignment(.center) } Spacer() - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { Button { showsJellyfinSignIn = true } label: { Text("signIn.withJellyfin") - .font(.haloRowHeader) + .font(.flaroRowHeader) .frame(maxWidth: .infinity) - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) } .buttonStyle(.borderedProminent) .tint(accent) Text("signIn.privacy") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .multilineTextAlignment(.center) } - Spacer().frame(height: HaloSpacing.section) + Spacer().frame(height: FlaroSpacing.section) } - .padding(.horizontal, HaloSpacing.section) + .padding(.horizontal, FlaroSpacing.section) .frame(maxWidth: 420) .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.haloCanvas) + .background(Color.flaroCanvas) .sheet(isPresented: $showsJellyfinSignIn) { JellyfinSignInView() } diff --git a/HaloTV/Halo.entitlements b/FlaroTV/Flaro.entitlements similarity index 82% rename from HaloTV/Halo.entitlements rename to FlaroTV/Flaro.entitlements index 4140325..b9a5ad2 100644 --- a/HaloTV/Halo.entitlements +++ b/FlaroTV/Flaro.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.scarriffleservices.halo + group.com.scarriffleservices.flaro diff --git a/HaloTV/HaloTVApp.swift b/FlaroTV/FlaroTVApp.swift similarity index 87% rename from HaloTV/HaloTVApp.swift rename to FlaroTV/FlaroTVApp.swift index 7674a8e..1a3e5fb 100644 --- a/HaloTV/HaloTVApp.swift +++ b/FlaroTV/FlaroTVApp.swift @@ -1,12 +1,12 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback import MediaKit import JellyfinKit /// Die App auf dem Fernseher. /// -/// **Eine eigene Wurzel statt eines Zweigs in `HaloApp`.** Der Unterschied +/// **Eine eigene Wurzel statt eines Zweigs in `FlaroApp`.** Der Unterschied /// zwischen Telefon und Fernseher ist keine Feinheit der Darstellung, sondern /// die Bedienart: kein Finger, keine Grössenklasse, keine Werkzeugleiste, kein /// Ziehen zum Aktualisieren, keine halbhohen Sheets — und dafür ein Fokus, den @@ -14,7 +14,7 @@ import JellyfinKit /// wäre kein Zweig, sondern ein zweiter Rumpf in derselben Datei, und der /// nächste, der die iOS-Seite ändert, bräche die andere, ohne es zu merken. /// -/// **Die Modelle dagegen werden geteilt**, nicht kopiert: `Halo/Session/` +/// **Die Modelle dagegen werden geteilt**, nicht kopiert: `Flaro/Session/` /// kommt über einen Pfadeintrag in `project.yml` mit. Die Naht verläuft /// zwischen Modell und Ansicht, nicht zwischen den Plattformen. /// @@ -22,9 +22,9 @@ import JellyfinKit /// iOS-App trägt ausschliesslich Downloads, und die gibt es hier nicht — damit /// fällt beides weg. @main -struct HaloTVApp: App { - @State private var session: HaloSession - @State private var theme = HaloTheme() +struct FlaroTVApp: App { + @State private var session: FlaroSession + @State private var theme = FlaroTheme() @State private var playback = PlaybackCoordinator() @State private var playbackSettings = PlaybackSettings() @State private var languages = LanguagePreferences() @@ -40,7 +40,7 @@ struct HaloTVApp: App { @State private var network = NetworkConditions() init() { - _session = State(initialValue: HaloSession( + _session = State(initialValue: FlaroSession( jellyfinIdentity: JellyfinIdentity.current( client: Branding.appName, version: Branding.version @@ -62,7 +62,7 @@ struct HaloTVApp: App { .environment(homeLayout) .environment(diagnostics) .environment(network) - .haloAccent(theme) + .flaroAccent(theme) .tint(theme.accentColor) // Ein Fernseher ist immer dunkel. Der Schalter in der // Info.plist gilt fürs System, diese Zeile für die Vorschauen. diff --git a/HaloTV/Info.plist b/FlaroTV/Info.plist similarity index 84% rename from HaloTV/Info.plist rename to FlaroTV/Info.plist index 63f8a60..d8cab78 100644 --- a/HaloTV/Info.plist +++ b/FlaroTV/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Halo + Flaro CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -22,10 +22,10 @@ CFBundleURLName - com.scarriffleservices.halo.topshelf + com.scarriffleservices.flaro.topshelf CFBundleURLSchemes - halo + flaro @@ -39,7 +39,7 @@ NSLocalNetworkUsageDescription - Halo benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen. + Flaro benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen. UIUserInterfaceStyle Dark diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/back.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/back.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/back.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/back.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/front.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/front.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/front.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/front.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back@2x.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back@2x.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back@2x.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/back@2x.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front@2x.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front@2x.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front@2x.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/front@2x.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide@2x.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide@2x.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide@2x.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/topshelfwide@2x.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf.png diff --git a/HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf@2x.png b/FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf@2x.png similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf@2x.png rename to FlaroTV/Resources/TVAssets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/topshelf@2x.png diff --git a/HaloTV/Resources/TVAssets.xcassets/Contents.json b/FlaroTV/Resources/TVAssets.xcassets/Contents.json similarity index 100% rename from HaloTV/Resources/TVAssets.xcassets/Contents.json rename to FlaroTV/Resources/TVAssets.xcassets/Contents.json diff --git a/HaloTV/Views/Detail/TVDetailView.swift b/FlaroTV/Views/Detail/TVDetailView.swift similarity index 87% rename from HaloTV/Views/Detail/TVDetailView.swift rename to FlaroTV/Views/Detail/TVDetailView.swift index 4516d8a..a1e2437 100644 --- a/HaloTV/Views/Detail/TVDetailView.swift +++ b/FlaroTV/Views/Detail/TVDetailView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Detailseite auf dem Fernseher. @@ -10,7 +10,7 @@ import MediaKit struct TVDetailView: View { let route: MediaRoute - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @@ -22,8 +22,8 @@ struct TVDetailView: View { case .loading: ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity) case .failed(let meldung): - VStack(spacing: HaloSpacing.regular) { - Text(meldung).font(.haloBody).foregroundStyle(.white) + VStack(spacing: FlaroSpacing.regular) { + Text(meldung).font(.flaroBody).foregroundStyle(.white) Button("action.retry") { Task { await lade() } } } case .loaded(let item): diff --git a/HaloTV/Views/Home/TVHeroSection.swift b/FlaroTV/Views/Home/TVHeroSection.swift similarity index 92% rename from HaloTV/Views/Home/TVHeroSection.swift rename to FlaroTV/Views/Home/TVHeroSection.swift index a9ef245..95e58cc 100644 --- a/HaloTV/Views/Home/TVHeroSection.swift +++ b/FlaroTV/Views/Home/TVHeroSection.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Der Aufmacher oben auf der Startseite des Fernsehers. @@ -13,8 +13,8 @@ struct TVHeroSection: View { let onPlay: () -> Void let onDetails: () -> Void - @Environment(HaloSession.self) private var session - @Environment(\.haloAccent) private var accent + @Environment(FlaroSession.self) private var session + @Environment(\.flaroAccent) private var accent var body: some View { HeroBanner( @@ -25,7 +25,7 @@ struct TVHeroSection: View { HeroArtwork.backdropURLs(for: item, backend: $0, compact: false) } ?? [], logoURL: session.backend.flatMap { HeroArtwork.logoURL(for: item, backend: $0) }, - aspectRatio: HaloAspectRatio.hero, + aspectRatio: FlaroAspectRatio.hero, // Der Aufmacher nimmt die Höhe, die das Karussell ihm gibt. fillsAvailableSpace: true ) { diff --git a/HaloTV/Views/Home/TVHomeView.swift b/FlaroTV/Views/Home/TVHomeView.swift similarity index 87% rename from HaloTV/Views/Home/TVHomeView.swift rename to FlaroTV/Views/Home/TVHomeView.swift index bfb78aa..ff5bd19 100644 --- a/HaloTV/Views/Home/TVHomeView.swift +++ b/FlaroTV/Views/Home/TVHomeView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Startseite auf dem Fernseher. @@ -16,7 +16,7 @@ struct TVHomeView: View { /// hiesse zwei Stapel gleich zu halten. @Binding var deepLinkTarget: MediaRoute? - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(HomeLayoutSettings.self) private var layout @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @@ -54,8 +54,8 @@ struct TVHomeView: View { ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity) case .failed(let meldung): - VStack(spacing: HaloSpacing.regular) { - Text(meldung).font(.haloBody).foregroundStyle(.white) + VStack(spacing: FlaroSpacing.regular) { + Text(meldung).font(.flaroBody).foregroundStyle(.white) Button("action.retry") { Task { await feed.load(from: session.backend) } } @@ -64,7 +64,7 @@ struct TVHomeView: View { case .loaded: let reihen = feed.rows(for: layout) if reihen.isEmpty && session.libraries.isEmpty { - Text("home.empty.title").font(.haloRowHeader).foregroundStyle(.white) + Text("home.empty.title").font(.flaroRowHeader).foregroundStyle(.white) } else { seite(reihen: reihen) } @@ -73,7 +73,7 @@ struct TVHomeView: View { private func seite(reihen: [MediaRowGroup]) -> some View { ScrollView { - LazyVStack(alignment: .leading, spacing: HaloSpacing.section) { + LazyVStack(alignment: .leading, spacing: FlaroSpacing.section) { if let gezeigt = aufmacher ?? feed.heroItems(for: layout).first { TVHeroSection( item: gezeigt, @@ -94,7 +94,7 @@ struct TVHomeView: View { ) } } - .padding(.bottom, HaloSpacing.screenMargin) + .padding(.bottom, FlaroSpacing.screenMargin) } .scrollIndicators(.never) // **Bildfüllend, und der Rand steckt in `screenMargin`.** Der @@ -116,16 +116,16 @@ struct TVHomeView: View { @ViewBuilder private var regalStatus: some View { #if DEBUG - VStack(alignment: .leading, spacing: HaloSpacing.tight) { + VStack(alignment: .leading, spacing: FlaroSpacing.tight) { Text("topShelf.title") - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) Text(TopShelfWriter.status) - .font(.haloCaption) - .foregroundStyle(Color.haloTextSecondary) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextSecondary) } - .padding(.horizontal, HaloSpacing.screenMargin) - .padding(.bottom, HaloSpacing.regular) + .padding(.horizontal, FlaroSpacing.screenMargin) + .padding(.bottom, FlaroSpacing.regular) .frame(maxWidth: .infinity, alignment: .leading) .background(Color.black.opacity(0.6)) #endif diff --git a/HaloTV/Views/Home/TVMediaRow.swift b/FlaroTV/Views/Home/TVMediaRow.swift similarity index 83% rename from HaloTV/Views/Home/TVMediaRow.swift rename to FlaroTV/Views/Home/TVMediaRow.swift index 43d615c..a17c993 100644 --- a/HaloTV/Views/Home/TVMediaRow.swift +++ b/FlaroTV/Views/Home/TVMediaRow.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Eine waagerechte Reihe von Titeln auf dem Fernseher. @@ -13,32 +13,32 @@ struct TVMediaRow: View { /// Wer gerade dran ist — damit der Aufmacher oben mitwandert. var onFocus: (MediaItem) -> Void = { _ in } - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session /// Breit genug, dass die übernächste Karte angeschnitten ist — das zeigt, /// dass es weitergeht, ohne einen Pfeil dafür zu brauchen. private var breite: CGFloat { row.isContinueWatching ? 500 : 280 } var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text(row.title) - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) - .padding(.horizontal, HaloSpacing.screenMargin) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) + .padding(.horizontal, FlaroSpacing.screenMargin) ScrollView(.horizontal) { - LazyHStack(spacing: HaloSpacing.regular) { + LazyHStack(spacing: FlaroSpacing.regular) { ForEach(row.items) { item in karte(for: item).frame(width: breite) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) // **Platz zum Anheben.** Eine fokussierte Karte wächst über // ihren Rahmen hinaus, und eine ScrollView beschneidet am // Rand. Ohne Polster und ohne das abgeschaltete Beschneiden // fehlt der obersten Reihe die Anhebung — und zwar nur ihr, // was wie ein Zufall aussieht statt wie ein Fehler. - .padding(.vertical, HaloSpacing.regular) + .padding(.vertical, FlaroSpacing.regular) } .scrollClipDisabled() .scrollIndicators(.never) diff --git a/HaloTV/Views/Library/TVLibraryView.swift b/FlaroTV/Views/Library/TVLibraryView.swift similarity index 85% rename from HaloTV/Views/Library/TVLibraryView.swift rename to FlaroTV/Views/Library/TVLibraryView.swift index 9bcaf3b..a4c4d07 100644 --- a/HaloTV/Views/Library/TVLibraryView.swift +++ b/FlaroTV/Views/Library/TVLibraryView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Eine Mediathek als Raster auf dem Fernseher. @@ -12,7 +12,7 @@ import MediaKit struct TVLibraryView: View { let libraryID: String - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @@ -24,7 +24,7 @@ struct TVLibraryView: View { private static let spalten = 6 private var raster: [GridItem] { - Array(repeating: GridItem(.flexible(), spacing: HaloSpacing.section), count: Self.spalten) + Array(repeating: GridItem(.flexible(), spacing: FlaroSpacing.section), count: Self.spalten) } var body: some View { @@ -52,8 +52,8 @@ struct TVLibraryView: View { ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity) case .failed(let meldung) where feed.items.isEmpty: - VStack(spacing: HaloSpacing.regular) { - Text(meldung).font(.haloBody).foregroundStyle(.white) + VStack(spacing: FlaroSpacing.regular) { + Text(meldung).font(.flaroBody).foregroundStyle(.white) Button("action.retry") { Task { await feed.reload(from: session.backend) } } } @@ -64,13 +64,13 @@ struct TVLibraryView: View { private func raster(feed: LibraryFeed) -> some View { ScrollView { - VStack(alignment: .leading, spacing: HaloSpacing.section) { + VStack(alignment: .leading, spacing: FlaroSpacing.section) { Text(feed.library.title) - .font(.haloHero) + .font(.flaroHero) .foregroundStyle(.white) - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) - LazyVGrid(columns: raster, spacing: HaloSpacing.section) { + LazyVGrid(columns: raster, spacing: FlaroSpacing.section) { ForEach(Array(feed.items.enumerated()), id: \.element.id) { stelle, item in TVPosterCard( title: item.rowTitle, @@ -111,20 +111,20 @@ struct TVLibraryView: View { .focusable(false) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) if feed.loadMoreFailed { Button("library.loadMore") { Task { await feed.loadMore(from: session.backend) } } - .padding(.horizontal, HaloSpacing.screenMargin) + .padding(.horizontal, FlaroSpacing.screenMargin) } } - .padding(.vertical, HaloSpacing.screenMargin) + .padding(.vertical, FlaroSpacing.screenMargin) } .scrollClipDisabled() .scrollIndicators(.never) - // Siehe `HaloSpacing.screenMargin`: bildfüllend, Rand im Wert. + // Siehe `FlaroSpacing.screenMargin`: bildfüllend, Rand im Wert. .ignoresSafeArea() } } diff --git a/HaloTV/Views/Player/TVPlayerCoordinator.swift b/FlaroTV/Views/Player/TVPlayerCoordinator.swift similarity index 99% rename from HaloTV/Views/Player/TVPlayerCoordinator.swift rename to FlaroTV/Views/Player/TVPlayerCoordinator.swift index f17afd3..282a3cb 100644 --- a/HaloTV/Views/Player/TVPlayerCoordinator.swift +++ b/FlaroTV/Views/Player/TVPlayerCoordinator.swift @@ -1,6 +1,6 @@ import AVKit import UIKit -import HaloPlayback +import FlaroPlayback import MediaKit /// Führt Apples Player — und hält ihn über einen Neuaufbau hinweg am Leben. diff --git a/HaloTV/Views/Player/TVPlayerHost.swift b/FlaroTV/Views/Player/TVPlayerHost.swift similarity index 99% rename from HaloTV/Views/Player/TVPlayerHost.swift rename to FlaroTV/Views/Player/TVPlayerHost.swift index d0cc304..f44eb14 100644 --- a/HaloTV/Views/Player/TVPlayerHost.swift +++ b/FlaroTV/Views/Player/TVPlayerHost.swift @@ -1,6 +1,6 @@ import AVKit import SwiftUI -import HaloPlayback +import FlaroPlayback import MediaKit /// Hängt Apples Player in den SwiftUI-Baum. diff --git a/HaloTV/Views/Player/TVPlayerScreen.swift b/FlaroTV/Views/Player/TVPlayerScreen.swift similarity index 97% rename from HaloTV/Views/Player/TVPlayerScreen.swift rename to FlaroTV/Views/Player/TVPlayerScreen.swift index 0cc54ec..cc9c153 100644 --- a/HaloTV/Views/Player/TVPlayerScreen.swift +++ b/FlaroTV/Views/Player/TVPlayerScreen.swift @@ -1,6 +1,6 @@ import SwiftUI -import HaloDesign -import HaloPlayback +import FlaroDesign +import FlaroPlayback import MediaKit /// Der Player auf dem Fernseher. @@ -14,7 +14,7 @@ import MediaKit struct TVPlayerScreen: View { let request: PlaybackRequest - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(PlaybackSettings.self) private var settings @Environment(LanguagePreferences.self) private var languages diff --git a/HaloTV/Views/Player/TVTrackMenuBuilder.swift b/FlaroTV/Views/Player/TVTrackMenuBuilder.swift similarity index 99% rename from HaloTV/Views/Player/TVTrackMenuBuilder.swift rename to FlaroTV/Views/Player/TVTrackMenuBuilder.swift index 08de4a6..c0615b4 100644 --- a/HaloTV/Views/Player/TVTrackMenuBuilder.swift +++ b/FlaroTV/Views/Player/TVTrackMenuBuilder.swift @@ -1,6 +1,6 @@ import AVKit import UIKit -import HaloPlayback +import FlaroPlayback /// Baut aus unseren Spurlisten die Menüs für Apples Transportleiste. /// diff --git a/HaloTV/Views/Search/TVSearchView.swift b/FlaroTV/Views/Search/TVSearchView.swift similarity index 83% rename from HaloTV/Views/Search/TVSearchView.swift rename to FlaroTV/Views/Search/TVSearchView.swift index d9e7388..e8b290f 100644 --- a/HaloTV/Views/Search/TVSearchView.swift +++ b/FlaroTV/Views/Search/TVSearchView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Die Suche am Fernseher. @@ -12,7 +12,7 @@ import MediaKit /// Das Modell dahinter ist dasselbe wie am Telefon: `SearchFeed` entprellt, /// fragt und merkt sich die letzten Begriffe. struct TVSearchView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @@ -49,7 +49,7 @@ struct TVSearchView: View { private var ergebnisse: some View { ScrollView { - LazyVStack(alignment: .leading, spacing: HaloSpacing.section) { + LazyVStack(alignment: .leading, spacing: FlaroSpacing.section) { ForEach(feed.hubs) { reihe in TVMediaRow( row: reihe, @@ -57,7 +57,7 @@ struct TVSearchView: View { ) } } - .padding(.vertical, HaloSpacing.section) + .padding(.vertical, FlaroSpacing.section) } .scrollClipDisabled() .scrollIndicators(.never) @@ -73,20 +73,20 @@ struct TVSearchView: View { if feed.recent.isEmpty { hinweis("search.hint") } else { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { Text("search.recent") - .font(.haloRowHeader) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroRowHeader) + .foregroundStyle(Color.flaroTextPrimary) ForEach(feed.recent, id: \.self) { eintrag in Button(eintrag) { text = eintrag } } Button("search.forgetRecent", role: .destructive) { feed.forgetRecent() } - .padding(.top, HaloSpacing.regular) + .padding(.top, FlaroSpacing.regular) } .frame(maxWidth: 700, alignment: .leading) - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) } } @@ -96,9 +96,9 @@ struct TVSearchView: View { private func hinweis(_ text: LocalizedStringKey) -> some View { Text(text) - .font(.haloBody) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroBody) + .foregroundStyle(Color.flaroTextMuted) .frame(maxWidth: 700) - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) } } diff --git a/HaloTV/Views/SignIn/TVSignInView.swift b/FlaroTV/Views/SignIn/TVSignInView.swift similarity index 77% rename from HaloTV/Views/SignIn/TVSignInView.swift rename to FlaroTV/Views/SignIn/TVSignInView.swift index 31ddccc..2220de4 100644 --- a/HaloTV/Views/SignIn/TVSignInView.swift +++ b/FlaroTV/Views/SignIn/TVSignInView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import JellyfinKit /// Die Anmeldung am Fernseher. @@ -13,13 +13,13 @@ import JellyfinKit /// Der Weg über Benutzername und Passwort bleibt erreichbar — für Server, auf /// denen Quick Connect abgeschaltet ist. struct TVSignInView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @State private var model: JellyfinSignInModel? var body: some View { ZStack { - Color.haloCanvas.ignoresSafeArea() + Color.flaroCanvas.ignoresSafeArea() if let model { inhalt(for: model) @@ -55,9 +55,9 @@ struct TVSignInView: View { private func inhalt(for model: JellyfinSignInModel) -> some View { @Bindable var model = model - VStack(alignment: .leading, spacing: HaloSpacing.section) { - Text(Branding.appName).font(.haloHero) - Text("signIn.tagline").font(.haloBody).foregroundStyle(Color.haloTextMuted) + VStack(alignment: .leading, spacing: FlaroSpacing.section) { + Text(Branding.appName).font(.flaroHero) + Text("signIn.tagline").font(.flaroBody).foregroundStyle(Color.flaroTextMuted) switch model.step { case .address: @@ -73,10 +73,10 @@ struct TVSignInView: View { } if let fehler = model.errorMessage { - Text(fehler).font(.haloBody).foregroundStyle(Color.haloDestructive) + Text(fehler).font(.flaroBody).foregroundStyle(Color.flaroDestructive) } } - .padding(HaloSpacing.section) + .padding(FlaroSpacing.section) } // MARK: - Die vier Schritte @@ -84,15 +84,15 @@ struct TVSignInView: View { private func adresse(model: JellyfinSignInModel) -> some View { @Bindable var model = model - return VStack(alignment: .leading, spacing: HaloSpacing.regular) { - Text("signIn.jellyfin.addressPrompt").font(.haloRowHeader) + return VStack(alignment: .leading, spacing: FlaroSpacing.regular) { + Text("signIn.jellyfin.addressPrompt").font(.flaroRowHeader) // Auf tvOS öffnet ein Textfeld beim Fokussieren die // Bildschirmtastatur ganzseitig — deshalb steht hier nur das Feld // und keine Maske drumherum. TextField("signIn.jellyfin.addressPlaceholder", text: $model.address) - .haloPlainEntry(isAddress: true) + .flaroPlainEntry(isAddress: true) Text("signIn.jellyfin.addressHint") - .font(.haloCaption).foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption).foregroundStyle(Color.flaroTextMuted) Button("signIn.jellyfin.connect") { Task { await model.findServer() } } @@ -100,9 +100,9 @@ struct TVSignInView: View { } private func codeAnzeigen(model: JellyfinSignInModel, code: String) -> some View { - VStack(alignment: .leading, spacing: HaloSpacing.regular) { + VStack(alignment: .leading, spacing: FlaroSpacing.regular) { if let name = model.serverName { - Text(name).font(.haloRowHeader) + Text(name).font(.flaroRowHeader) } // **Der Code ist hier der Inhalt, nicht eine Beschriftung.** // Er wird aus drei Metern abgelesen und in ein Telefon getippt; @@ -111,11 +111,11 @@ struct TVSignInView: View { .font(.system(size: 96, weight: .bold, design: .monospaced)) .kerning(12) .foregroundStyle(.white) - Text("signIn.jellyfin.quickConnectHint").font(.haloBody) + Text("signIn.jellyfin.quickConnectHint").font(.flaroBody) Label("signIn.jellyfin.waitingForApproval", systemImage: "hourglass") - .font(.haloCaption).foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption).foregroundStyle(Color.flaroTextMuted) - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { Button("signIn.jellyfin.usePassword") { model.useCredentials() } Button("signIn.jellyfin.otherAddress") { model.startOver() } } @@ -125,17 +125,17 @@ struct TVSignInView: View { private func zugangsdaten(model: JellyfinSignInModel) -> some View { @Bindable var model = model - return VStack(alignment: .leading, spacing: HaloSpacing.regular) { + return VStack(alignment: .leading, spacing: FlaroSpacing.regular) { if let name = model.serverName { - Text(name).font(.haloRowHeader) + Text(name).font(.flaroRowHeader) } TextField("signIn.jellyfin.userName", text: $model.userName) - .haloPlainEntry() + .flaroPlainEntry() SecureField("signIn.jellyfin.password", text: $model.password) Text("signIn.jellyfin.passwordNote") - .font(.haloCaption).foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption).foregroundStyle(Color.flaroTextMuted) - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { Button("signIn.jellyfin.submit") { Task { await model.signInWithPassword() } } @@ -150,9 +150,9 @@ struct TVSignInView: View { } private func warten(_ text: LocalizedStringKey) -> some View { - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { ProgressView() - Text(text).font(.haloBody) + Text(text).font(.flaroBody) } } } diff --git a/HaloTV/Views/TVDeepLink.swift b/FlaroTV/Views/TVDeepLink.swift similarity index 88% rename from HaloTV/Views/TVDeepLink.swift rename to FlaroTV/Views/TVDeepLink.swift index efddeaf..23e04da 100644 --- a/HaloTV/Views/TVDeepLink.swift +++ b/FlaroTV/Views/TVDeepLink.swift @@ -9,7 +9,7 @@ import Foundation /// Geprüft ist er trotzdem nicht — das Fernseh-Ziel hat kein Testziel, und /// ihn dafür nach MediaKit zu schieben hiesse, ein Schema dieser App in ein /// Paket zu legen, das keine App kennt. Wer ihn anfasst, prüft ihn am Gerät: -/// `xcrun simctl openurl … halo://play/`. +/// `xcrun simctl openurl … flaro://play/`. enum TVDeepLink: Equatable { /// Abspielen, was dahintersteckt. case play(id: String) @@ -17,12 +17,12 @@ enum TVDeepLink: Equatable { case detail(id: String) /// Muss mit `CFBundleURLSchemes` in der Info.plist übereinstimmen. - static let scheme = "halo" + static let scheme = "flaro" init?(_ url: URL) { guard url.scheme == Self.scheme else { return nil } - // **`host` und nicht `pathComponents[0]`.** Bei `halo://play/42` + // **`host` und nicht `pathComponents[0]`.** Bei `flaro://play/42` // steht „play" im Rechnernamen, nicht im Pfad — der lautet schlicht // „/42". Wer hier den Pfad zerlegt, bekommt die Kennung als Aktion // und wundert sich. diff --git a/HaloTV/Views/TVRootView.swift b/FlaroTV/Views/TVRootView.swift similarity index 88% rename from HaloTV/Views/TVRootView.swift rename to FlaroTV/Views/TVRootView.swift index 501b082..76397e5 100644 --- a/HaloTV/Views/TVRootView.swift +++ b/FlaroTV/Views/TVRootView.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign /// Was auf dem Fernseher zu sehen ist. /// @@ -10,7 +10,7 @@ import HaloDesign /// bestehenden — ein `#if` wäre kein Zweig gewesen, sondern ein zweiter Rumpf /// in derselben Datei. struct TVRootView: View { - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session @Environment(PlaybackCoordinator.self) private var playback @Environment(QuickPlay.self) private var quickPlay @@ -19,7 +19,7 @@ struct TVRootView: View { var body: some View { ZStack { - Color.haloCanvas.ignoresSafeArea() + Color.flaroCanvas.ignoresSafeArea() switch session.phase { case .launching: @@ -82,16 +82,16 @@ struct TVRootView: View { } private func verbinden(mit name: String) -> some View { - VStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { ProgressView() - Text(name).font(.haloRowHeader).foregroundStyle(.white) + Text(name).font(.flaroRowHeader).foregroundStyle(.white) } } private func fehlgeschlagen(_ meldung: String) -> some View { - VStack(spacing: HaloSpacing.regular) { - Text(meldung).font(.haloBody).foregroundStyle(.white) - HStack(spacing: HaloSpacing.regular) { + VStack(spacing: FlaroSpacing.regular) { + Text(meldung).font(.flaroBody).foregroundStyle(.white) + HStack(spacing: FlaroSpacing.regular) { Button("action.retry") { Task { await session.retry() } } Button("action.signOut") { session.signOut() } } diff --git a/HaloTV/Views/TVShell.swift b/FlaroTV/Views/TVShell.swift similarity index 96% rename from HaloTV/Views/TVShell.swift rename to FlaroTV/Views/TVShell.swift index 21574a0..7df07fb 100644 --- a/HaloTV/Views/TVShell.swift +++ b/FlaroTV/Views/TVShell.swift @@ -1,5 +1,5 @@ import SwiftUI -import HaloDesign +import FlaroDesign import MediaKit /// Start, die Mediatheken und die Suche nebeneinander. @@ -14,7 +14,7 @@ import MediaKit struct TVShell: View { @Binding var deepLinkTarget: MediaRoute? - @Environment(HaloSession.self) private var session + @Environment(FlaroSession.self) private var session /// Welcher Reiter offen ist. /// diff --git a/HaloTopShelf/HaloTopShelf.entitlements b/FlaroTopShelf/FlaroTopShelf.entitlements similarity index 82% rename from HaloTopShelf/HaloTopShelf.entitlements rename to FlaroTopShelf/FlaroTopShelf.entitlements index 4140325..b9a5ad2 100644 --- a/HaloTopShelf/HaloTopShelf.entitlements +++ b/FlaroTopShelf/FlaroTopShelf.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.scarriffleservices.halo + group.com.scarriffleservices.flaro diff --git a/HaloTopShelf/Info.plist b/FlaroTopShelf/Info.plist similarity index 97% rename from HaloTopShelf/Info.plist rename to FlaroTopShelf/Info.plist index 5057614..8716d45 100644 --- a/HaloTopShelf/Info.plist +++ b/FlaroTopShelf/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Halo + Flaro CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/HaloTopShelf/TopShelfIdentity.swift b/FlaroTopShelf/TopShelfIdentity.swift similarity index 89% rename from HaloTopShelf/TopShelfIdentity.swift rename to FlaroTopShelf/TopShelfIdentity.swift index 22c42d2..507c202 100644 --- a/HaloTopShelf/TopShelfIdentity.swift +++ b/FlaroTopShelf/TopShelfIdentity.swift @@ -8,11 +8,11 @@ import Foundation enum TopShelfIdentity { /// Muss mit `TopShelfWriter.group` und beiden Entitlement-Dateien /// übereinstimmen. - static let group = "group.com.scarriffleservices.halo" + static let group = "group.com.scarriffleservices.flaro" /// Muss mit `CFBundleURLSchemes` in der Info.plist des Fernseh-Ziels /// übereinstimmen. - static let scheme = "halo" + static let scheme = "flaro" static func playURL(for id: String) -> URL { URL(string: "\(scheme)://play/\(id)") ?? URL(string: "\(scheme)://")! diff --git a/HaloTopShelf/TopShelfProvider.swift b/FlaroTopShelf/TopShelfProvider.swift similarity index 100% rename from HaloTopShelf/TopShelfProvider.swift rename to FlaroTopShelf/TopShelfProvider.swift diff --git a/HaloUITests/LayoutSnapshots.swift b/FlaroUITests/LayoutSnapshots.swift similarity index 94% rename from HaloUITests/LayoutSnapshots.swift rename to FlaroUITests/LayoutSnapshots.swift index 982cdd8..c9cf0f1 100644 --- a/HaloUITests/LayoutSnapshots.swift +++ b/FlaroUITests/LayoutSnapshots.swift @@ -42,8 +42,8 @@ final class LayoutSnapshots: XCTestCase { func testPushedSettingsScreen() throws { let app = XCUIApplication() app.launchArguments = [ - "-HaloStubServer", "http://127.0.0.1:8096", - "-halo.navigation.placement", "sidebar", + "-FlaroStubServer", "http://127.0.0.1:8096", + "-flaro.navigation.placement", "sidebar", ] app.launch() @@ -68,8 +68,8 @@ final class LayoutSnapshots: XCTestCase { // sie womöglich umgestellt. Ein Startargument überschreibt sie, damit // jeder Lauf beim selben Bild anfängt. app.launchArguments = [ - "-HaloStubServer", "http://127.0.0.1:8096", - "-halo.navigation.placement", "sidebar", + "-FlaroStubServer", "http://127.0.0.1:8096", + "-flaro.navigation.placement", "sidebar", ] app.launch() @@ -99,7 +99,7 @@ final class LayoutSnapshots: XCTestCase { /// Leiste unten, und der Aufmacher hochkant statt 16:9. func testPhoneHome() throws { let app = XCUIApplication() - app.launchArguments = ["-HaloStubServer", "http://127.0.0.1:8096"] + app.launchArguments = ["-FlaroStubServer", "http://127.0.0.1:8096"] app.launch() XCTAssertTrue(app.staticTexts["Weiterschauen"].waitForExistence(timeout: 20)) @@ -113,8 +113,8 @@ final class LayoutSnapshots: XCTestCase { // sie womöglich umgestellt. Ein Startargument überschreibt sie, damit // jeder Lauf beim selben Bild anfängt. app.launchArguments = [ - "-HaloStubServer", "http://127.0.0.1:8096", - "-halo.navigation.placement", "sidebar", + "-FlaroStubServer", "http://127.0.0.1:8096", + "-flaro.navigation.placement", "sidebar", ] app.launch() diff --git a/HaloUITests/PlayerSheets.swift b/FlaroUITests/PlayerSheets.swift similarity index 95% rename from HaloUITests/PlayerSheets.swift rename to FlaroUITests/PlayerSheets.swift index 94bfff5..fd8ff6d 100644 --- a/HaloUITests/PlayerSheets.swift +++ b/FlaroUITests/PlayerSheets.swift @@ -10,7 +10,7 @@ import XCTest final class PlayerSheets: XCTestCase { func testQualitySheetOpens() { let app = XCUIApplication() - app.launchArguments = ["-HaloStubServer", "http://127.0.0.1:8096"] + app.launchArguments = ["-FlaroStubServer", "http://127.0.0.1:8096"] app.launch() XCTAssertTrue(app.staticTexts["Weiterschauen"].waitForExistence(timeout: 30)) diff --git a/LICENSES.md b/LICENSES.md index 025b544..ed10845 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -5,7 +5,7 @@ Dieses Repo trägt **zwei** Lizenzen. Welche gilt, hängt vom Verzeichnis ab. | Was | Lizenz | Datei | |---|---|---| | `server/`, `web/`, `Tools/`, `docs/` | **GPL-2.0-only** | [`LICENSE`](LICENSE) | -| `Halo/`, `HaloTV/`, `HaloTopShelf/`, `HaloUITests/`, `DownloadWidget/`, `Packages/HaloKit/` | **MPL-2.0** | [`LICENSE-MPL-2.0`](LICENSE-MPL-2.0) | +| `Flaro/`, `FlaroTV/`, `FlaroTopShelf/`, `FlaroUITests/`, `DownloadWidget/`, `Packages/FlaroKit/` | **MPL-2.0** | [`LICENSE-MPL-2.0`](LICENSE-MPL-2.0) | ## Warum zweierlei diff --git a/Packages/HaloKit/Package.swift b/Packages/FlaroKit/Package.swift similarity index 73% rename from Packages/HaloKit/Package.swift rename to Packages/FlaroKit/Package.swift index 15d432d..1b43314 100644 --- a/Packages/HaloKit/Package.swift +++ b/Packages/FlaroKit/Package.swift @@ -2,21 +2,21 @@ import PackageDescription let package = Package( - name: "HaloKit", + name: "FlaroKit", // Quellsprache ist Englisch, damit die Schlüssel im Code lesbar bleiben. // Deutsch kommt als Übersetzung dazu; die Systemsprache entscheidet. defaultLocalization: "en", // macOS steht hier nur, damit `swift test` die Logik-Tests ohne // Simulator laufen lassen kann — das spart bei jedem Durchlauf Minuten. // Welche Plattformen die Apps bedienen, legt project.yml fest: iOS für - // Halo, tvOS für HaloTV. + // Flaro, tvOS für FlaroTV. platforms: [.iOS(.v26), .macOS(.v26), .tvOS(.v26)], products: [ .library(name: "MediaKit", targets: ["MediaKit"]), .library(name: "JellyfinKit", targets: ["JellyfinKit"]), - .library(name: "HaloDesign", targets: ["HaloDesign"]), - .library(name: "HaloPlayback", targets: ["HaloPlayback"]), - .library(name: "HaloDownloads", targets: ["HaloDownloads"]), + .library(name: "FlaroDesign", targets: ["FlaroDesign"]), + .library(name: "FlaroPlayback", targets: ["FlaroPlayback"]), + .library(name: "FlaroDownloads", targets: ["FlaroDownloads"]), ], targets: [ // Sprechen mit dem Server: Anmeldung, Endpunkte, Modelle. Kein SwiftUI, @@ -42,21 +42,21 @@ let package = Package( // Ein Werkzeug für die Fragen, die reines HTTP sind — ohne Oberfläche, // ohne Gerät, ohne Upload. Eine Runde dauert damit Sekunden statt // eines halben Tages, und das Upload-Kontingent bleibt für Builds. - .executableTarget(name: "HaloProbe", dependencies: ["MediaKit", "JellyfinKit"]), + .executableTarget(name: "FlaroProbe", dependencies: ["MediaKit", "JellyfinKit"]), // Farben, Typografie, wiederkehrende Bausteine. Hängt bewusst von // nichts ab: dadurch bleibt das Umstellen der Akzentfarbe ein // Token-Wechsel und keine Suche quer durch die App. - .target(name: "HaloDesign", resources: [.process("Localizable.xcstrings")]), - .testTarget(name: "HaloDesignTests", dependencies: ["HaloDesign"]), + .target(name: "FlaroDesign", resources: [.process("Localizable.xcstrings")]), + .testTarget(name: "FlaroDesignTests", dependencies: ["FlaroDesign"]), // Die Wiedergabe-Abstraktion samt AVPlayer-Engine und der Entscheidung // direkt / umpacken / umrechnen. - .target(name: "HaloPlayback", dependencies: ["MediaKit"]), - .testTarget(name: "HaloPlaybackTests", dependencies: ["HaloPlayback"]), + .target(name: "FlaroPlayback", dependencies: ["MediaKit"]), + .testTarget(name: "FlaroPlaybackTests", dependencies: ["FlaroPlayback"]), // Warteschlange, Hintergrundübertragung, Offline-Bestand. - .target(name: "HaloDownloads", dependencies: ["MediaKit"], resources: [.process("Localizable.xcstrings")]), - .testTarget(name: "HaloDownloadsTests", dependencies: ["HaloDownloads"]), + .target(name: "FlaroDownloads", dependencies: ["MediaKit"], resources: [.process("Localizable.xcstrings")]), + .testTarget(name: "FlaroDownloadsTests", dependencies: ["FlaroDownloads"]), ] ) diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/AccentEnvironment.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/AccentEnvironment.swift similarity index 65% rename from Packages/HaloKit/Sources/HaloDesign/Color/AccentEnvironment.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/AccentEnvironment.swift index 7ddf118..d8fefa1 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Color/AccentEnvironment.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Color/AccentEnvironment.swift @@ -4,7 +4,7 @@ extension EnvironmentValues { /// Die aktuelle Akzentfarbe, bereits kontrastkorrigiert. /// /// Bewusst eine schlichte `Color` in der Umgebung statt einer Abhängigkeit - /// auf `HaloTheme`: Bausteine wie `WatchProgressBar` sollen sich in einer + /// auf `FlaroTheme`: Bausteine wie `WatchProgressBar` sollen sich in einer /// Vorschau mit einer beliebigen Farbe zeigen lassen, ohne dass dafür ein /// ganzes Theme-Objekt gebaut werden muss. /// Der Vorgabewert liest die gespeicherte Auswahl, statt blind die @@ -14,19 +14,19 @@ extension EnvironmentValues { /// Formulare zeichnen Zeilen teilweise in einem eigenen Kontext. Vorher /// leuchtete dort die Standardfarbe, während daneben schon die gewählte /// stand; jetzt stimmen beide. - @Entry public var haloAccent: Color = AccentOption.storedAccentColor() + @Entry public var flaroAccent: Color = AccentOption.storedAccentColor() } extension View { /// Setzt die Akzentfarbe für diesen Teilbaum. /// - /// Wird einmal an der Wurzel aus `HaloTheme` gesetzt; darunter greift + /// Wird einmal an der Wurzel aus `FlaroTheme` gesetzt; darunter greift /// alles automatisch darauf zu. - public func haloAccent(_ color: Color) -> some View { - environment(\.haloAccent, color) + public func flaroAccent(_ color: Color) -> some View { + environment(\.flaroAccent, color) } - public func haloAccent(_ theme: HaloTheme) -> some View { - environment(\.haloAccent, theme.accentColor) + public func flaroAccent(_ theme: FlaroTheme) -> some View { + environment(\.flaroAccent, theme.accentColor) } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/AccentOption.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/AccentOption.swift similarity index 92% rename from Packages/HaloKit/Sources/HaloDesign/Color/AccentOption.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/AccentOption.swift index e44567a..a07e139 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Color/AccentOption.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Color/AccentOption.swift @@ -3,7 +3,7 @@ import SwiftUI /// Eine wählbare Akzentfarbe. /// -/// Die kuratierten Werte sind so gewählt, dass sie auf `HaloColor.canvas` +/// Die kuratierten Werte sind so gewählt, dass sie auf `FlaroColor.canvas` /// mindestens 4,5:1 erreichen — geprüft von `AccentPaletteTests`, damit beim /// Nachlegen einer Farbe nicht versehentlich etwas Unlesbares hineinrutscht. public struct AccentOption: Sendable, Identifiable { @@ -66,13 +66,13 @@ extension AccentOption { extension AccentOption { /// Wo die Auswahl liegt. /// - /// Hier statt in `HaloTheme`, weil zwei Stellen sie brauchen: das Theme + /// Hier statt in `FlaroTheme`, weil zwei Stellen sie brauchen: das Theme /// und der Vorgabewert der Umgebung. Zwei Kopien derselben Schlüssel /// driften auseinander, und zwar unbemerkt — es fällt erst auf, wenn eine /// Farbe irgendwo nicht mitkommt. enum StorageKey { - static let identifier = "halo.accent.identifier" - static let customHex = "halo.accent.customHex" + static let identifier = "flaro.accent.identifier" + static let customHex = "flaro.accent.customHex" } /// Die gespeicherte Auswahl, oder die Voreinstellung. @@ -102,6 +102,6 @@ extension AccentOption { /// Die gespeicherte Farbe, bereits kontrastkorrigiert. public static func storedAccentColor(in defaults: UserDefaults = .standard) -> Color { - Contrast.legible(stored(in: defaults).color, on: HaloColor.canvas).swiftUIColor + Contrast.legible(stored(in: defaults).color, on: FlaroColor.canvas).swiftUIColor } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/Contrast.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/Contrast.swift similarity index 97% rename from Packages/HaloKit/Sources/HaloDesign/Color/Contrast.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/Contrast.swift index 7a0609f..49f1035 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Color/Contrast.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Color/Contrast.swift @@ -2,7 +2,7 @@ import Foundation /// Kontrastrechnung nach WCAG 2.1. /// -/// Gebraucht wird sie für die eigene Akzentfarbe: Halo lässt jede Farbe zu, +/// Gebraucht wird sie für die eigene Akzentfarbe: Flaro lässt jede Farbe zu, /// aber ein dunkles Marineblau auf fast schwarzem Grund ist als Schrift oder /// Symbol unlesbar. `legible(_:on:)` hebt solche Farben so weit an, bis sie /// den geforderten Kontrast erreichen — und lässt alles in Ruhe, was ihn diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/HaloColor.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/FlaroColor.swift similarity index 59% rename from Packages/HaloKit/Sources/HaloDesign/Color/HaloColor.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/FlaroColor.swift index 1b2f169..5cdd564 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Color/HaloColor.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Color/FlaroColor.swift @@ -2,10 +2,10 @@ import SwiftUI /// Die Flächen- und Schriftfarben der App. /// -/// Halo kennt nur den dunklen Modus — es gibt deshalb bewusst keine +/// Flaro kennt nur den dunklen Modus — es gibt deshalb bewusst keine /// Hell-/Dunkel-Paare, sondern je einen festen Wert. Die einzige Farbe, die -/// sich zur Laufzeit ändert, ist der Akzent; er kommt aus `HaloTheme`. -public enum HaloColor { +/// sich zur Laufzeit ändert, ist der Akzent; er kommt aus `FlaroTheme`. +public enum FlaroColor { // Flächen, von hinten nach vorn gestaffelt. /// Der Seitenhintergrund. public static let canvas = RGB(hex: 0x0B0B0D) @@ -28,18 +28,18 @@ public enum HaloColor { } extension Color { - public static let haloCanvas = HaloColor.canvas.swiftUIColor - public static let haloSurface = HaloColor.surface.swiftUIColor - public static let haloElevated = HaloColor.elevated.swiftUIColor - public static let haloPlaceholder = HaloColor.placeholder.swiftUIColor - public static let haloTextPrimary = HaloColor.textPrimary.swiftUIColor - public static let haloTextSecondary = HaloColor.textSecondary.swiftUIColor - public static let haloTextMuted = HaloColor.textMuted.swiftUIColor - public static let haloDestructive = HaloColor.destructive.swiftUIColor + public static let flaroCanvas = FlaroColor.canvas.swiftUIColor + public static let flaroSurface = FlaroColor.surface.swiftUIColor + public static let flaroElevated = FlaroColor.elevated.swiftUIColor + public static let flaroPlaceholder = FlaroColor.placeholder.swiftUIColor + public static let flaroTextPrimary = FlaroColor.textPrimary.swiftUIColor + public static let flaroTextSecondary = FlaroColor.textSecondary.swiftUIColor + public static let flaroTextMuted = FlaroColor.textMuted.swiftUIColor + public static let flaroDestructive = FlaroColor.destructive.swiftUIColor /// Trennlinien und Ränder. Als Weiß mit Deckkraft statt als fester Wert, /// damit sie auf jeder Flächenstufe gleich zurückhaltend wirken. - public static let haloSeparator = Color.white.opacity(0.10) + public static let flaroSeparator = Color.white.opacity(0.10) /// Die Abdunklung hinter der ausgefahrenen Seitenleiste. - public static let haloScrim = Color.black.opacity(0.55) + public static let flaroScrim = Color.black.opacity(0.55) } diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/HSB.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/HSB.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDesign/Color/HSB.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/HSB.swift diff --git a/Packages/HaloKit/Sources/HaloDesign/Color/RGB.swift b/Packages/FlaroKit/Sources/FlaroDesign/Color/RGB.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDesign/Color/RGB.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Color/RGB.swift diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/ArtworkLoader.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/ArtworkLoader.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDesign/Components/ArtworkLoader.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/ArtworkLoader.swift diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/HaloArtwork.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/FlaroArtwork.swift similarity index 96% rename from Packages/HaloKit/Sources/HaloDesign/Components/HaloArtwork.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/FlaroArtwork.swift index bf6e424..d4e0e7d 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/HaloArtwork.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/FlaroArtwork.swift @@ -7,7 +7,7 @@ import SwiftUI /// Startseite zeigt dutzende Bilder gleichzeitig, und `AsyncImage` lädt sie bei /// jedem Erscheinen neu. Auf dem Mac wurde daraus ein sichtbarer Fehler: der /// Aufmacher und die Hälfte der Plakate blieben grau. -public struct HaloArtwork: View { +public struct FlaroArtwork: View { /// Die Adressen, der Reihe nach probiert. /// /// **Warum mehrere.** Eine Folge hat selten ein eigenes Querformatbild, @@ -90,9 +90,9 @@ public struct HaloArtwork: View { @ViewBuilder private var placeholder: some View { if fillsFrame { - Rectangle().fill(Color.haloPlaceholder) + Rectangle().fill(Color.flaroPlaceholder) } else { - Rectangle().fill(Color.haloPlaceholder).aspectRatio(aspectRatio, contentMode: .fit) + Rectangle().fill(Color.flaroPlaceholder).aspectRatio(aspectRatio, contentMode: .fit) } } @@ -124,7 +124,7 @@ public struct HaloArtwork: View { // mehr Lärm als Hilfe. Das Symbol sagt genug. Image(systemName: fallbackSymbol) .font(.title2) - .foregroundStyle(Color.haloTextMuted) + .foregroundStyle(Color.flaroTextMuted) } else { ShimmerPlaceholder() } diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/HeroBanner.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/HeroBanner.swift similarity index 91% rename from Packages/HaloKit/Sources/HaloDesign/Components/HeroBanner.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/HeroBanner.swift index 9a11321..c863d7c 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/HeroBanner.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/HeroBanner.swift @@ -81,7 +81,7 @@ public struct HeroBanner: View { // 3:4 — mit `scaledToFill` wurde das Bild dadurch links und rechts // kräftig beschnitten, und vom Aufmacher war nur ein Ausschnitt zu // sehen. - aspectRatio: CGFloat = HaloAspectRatio.hero, + aspectRatio: CGFloat = FlaroAspectRatio.hero, fillsAvailableSpace: Bool = false, @ViewBuilder actions: () -> Actions ) { @@ -114,16 +114,16 @@ public struct HeroBanner: View { /// Das Bild — und, solange gewechselt wird, das vorige darunter. /// - /// Ohne Wechsel ist es genau ein `HaloArtwork` wie zuvor: die zweite + /// Ohne Wechsel ist es genau ein `FlaroArtwork` wie zuvor: die zweite /// Ebene entsteht nur, wenn jemand eine vorige Adresse mitgibt. @ViewBuilder private var bild: some View { if outgoingArtworkURLs.isEmpty { - HaloArtwork(urls: artworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) + FlaroArtwork(urls: artworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) } else { ZStack { - HaloArtwork(urls: outgoingArtworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) - HaloArtwork(urls: artworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) + FlaroArtwork(urls: outgoingArtworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) + FlaroArtwork(urls: artworkURLs, aspectRatio: aspectRatio, fillsFrame: fillsAvailableSpace) .opacity(crossfade) .offset(x: incomingOffset) } @@ -166,8 +166,8 @@ public struct HeroBanner: View { LinearGradient( stops: [ .init(color: .clear, location: 0), - .init(color: Color.haloCanvas.opacity(0.75), location: 0.45), - .init(color: Color.haloCanvas, location: 1), + .init(color: Color.flaroCanvas.opacity(0.75), location: 0.45), + .init(color: Color.flaroCanvas, location: 1), ], startPoint: .top, endPoint: .bottom @@ -250,7 +250,7 @@ public struct HeroBanner: View { logo: PlatformImage?, scale: CGFloat ) -> some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug * scale) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug * scale) { if let logo { Image(platform: logo) .resizable() @@ -266,24 +266,24 @@ public struct HeroBanner: View { // eine Systemschrift. Text(title) .font(.system(size: 34 * scale, weight: .bold)) - .foregroundStyle(Color.haloTextPrimary) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(2) } if let metadataContent { metadataContent .font(.system(size: 12 * scale)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } else if let metadata { Text(metadata) .font(.system(size: 12 * scale)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) } if let summary { Text(summary) .font(.system(size: 12 * scale)) - .foregroundStyle(Color.haloTextSecondary) + .foregroundStyle(Color.flaroTextSecondary) .lineLimit(2) // Ein Satz über die ganze Breite eines großen Fensters ist // unlesbar; das Auge verliert die Zeile. Achtzig Zeichen @@ -291,12 +291,12 @@ public struct HeroBanner: View { .frame(maxWidth: 640 * scale, alignment: .leading) } - HStack(spacing: HaloSpacing.regular) { + HStack(spacing: FlaroSpacing.regular) { actions } - .padding(.top, HaloSpacing.tight) + .padding(.top, FlaroSpacing.tight) } - .padding(.horizontal, HaloSpacing.screenMargin * scale) - .padding(.bottom, HaloSpacing.loose * scale) + .padding(.horizontal, FlaroSpacing.screenMargin * scale) + .padding(.bottom, FlaroSpacing.loose * scale) } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/LandscapeCard.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/LandscapeCard.swift similarity index 73% rename from Packages/HaloKit/Sources/HaloDesign/Components/LandscapeCard.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/LandscapeCard.swift index e257a87..5034819 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/LandscapeCard.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/LandscapeCard.swift @@ -20,24 +20,24 @@ public struct LandscapeCard: View { } public var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { - HaloArtwork(url: artworkURL, aspectRatio: Self.aspectRatio, fallbackSymbol: "play.rectangle") + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { + FlaroArtwork(url: artworkURL, aspectRatio: Self.aspectRatio, fallbackSymbol: "play.rectangle") .overlay(alignment: .bottom) { if let progress { WatchProgressBar(fraction: progress) } } - .clipShape(.rect(cornerRadius: HaloRadius.card)) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) VStack(alignment: .leading, spacing: 2) { Text(title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/PlaceholderCard.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/PlaceholderCard.swift similarity index 89% rename from Packages/HaloKit/Sources/HaloDesign/Components/PlaceholderCard.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/PlaceholderCard.swift index b706b87..8364e57 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/PlaceholderCard.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/PlaceholderCard.swift @@ -30,18 +30,18 @@ public struct PlaceholderCard: View { } public var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { - RoundedRectangle(cornerRadius: HaloRadius.card) - .fill(Color.haloElevated) + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { + RoundedRectangle(cornerRadius: FlaroRadius.card) + .fill(Color.flaroElevated) .aspectRatio(PosterCard.aspectRatio, contentMode: .fit) // Zwei Balken an der Stelle von Titel und Jahr. Ohne sie springt // die Zeile, sobald der echte Titel kommt. RoundedRectangle(cornerRadius: 3) - .fill(Color.haloElevated) + .fill(Color.flaroElevated) .frame(height: 10) RoundedRectangle(cornerRadius: 3) - .fill(Color.haloElevated) + .fill(Color.flaroElevated) .frame(width: 32, height: 8) } .opacity(isBright ? 0.85 : 0.45) diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/PosterCard.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/PosterCard.swift similarity index 74% rename from Packages/HaloKit/Sources/HaloDesign/Components/PosterCard.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/PosterCard.swift index c643501..6c7da20 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/PosterCard.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/PosterCard.swift @@ -19,25 +19,25 @@ public struct PosterCard: View { } public var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { - HaloArtwork(url: artworkURL, aspectRatio: Self.aspectRatio) - .clipShape(.rect(cornerRadius: HaloRadius.card)) + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { + FlaroArtwork(url: artworkURL, aspectRatio: Self.aspectRatio) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) .overlay(alignment: .bottom) { if let progress { WatchProgressBar(fraction: progress) } } - .clipShape(.rect(cornerRadius: HaloRadius.card)) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) VStack(alignment: .leading, spacing: 2) { Text(title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/ShimmerPlaceholder.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/ShimmerPlaceholder.swift similarity index 97% rename from Packages/HaloKit/Sources/HaloDesign/Components/ShimmerPlaceholder.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/ShimmerPlaceholder.swift index f9cbc3c..dc5a943 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/ShimmerPlaceholder.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/ShimmerPlaceholder.swift @@ -15,7 +15,7 @@ public struct ShimmerPlaceholder: View { public var body: some View { Rectangle() - .fill(Color.haloPlaceholder) + .fill(Color.flaroPlaceholder) .overlay { if !reduceMotion { GeometryReader { proxy in diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/TVCards.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/TVCards.swift similarity index 90% rename from Packages/HaloKit/Sources/HaloDesign/Components/TVCards.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/TVCards.swift index a1c2710..9126456 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/TVCards.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/TVCards.swift @@ -10,7 +10,7 @@ import SwiftUI /// `scaleEffect` und ein Schatten — sieht auf den ersten Blick gleich aus und /// auf den zweiten falsch: die Parallaxe ist der Teil, den man wiedererkennt. /// -/// Liegt in HaloDesign und nicht im Fernseh-Ziel, aus demselben Grund wie +/// Liegt in FlaroDesign und nicht im Fernseh-Ziel, aus demselben Grund wie /// `PosterCard`: das Paket kennt weder `MediaItem` noch die Wiedergabe, und /// das soll so bleiben. Herein kommen Zeichenketten und Adressen. public struct TVPosterCard: View { @@ -100,7 +100,7 @@ public struct TVLandscapeCard: View { public var body: some View { TVCard( - aspectRatio: HaloAspectRatio.landscape, + aspectRatio: FlaroAspectRatio.landscape, title: title, subtitle: subtitle, artworkURL: artworkURL, @@ -124,13 +124,13 @@ private struct TVCard: View { let action: () -> Void var body: some View { - VStack(alignment: .leading, spacing: HaloSpacing.snug) { + VStack(alignment: .leading, spacing: FlaroSpacing.snug) { Button(action: action) { - HaloArtwork(url: artworkURL, aspectRatio: aspectRatio) + FlaroArtwork(url: artworkURL, aspectRatio: aspectRatio) .overlay(alignment: .bottom) { if let progress { WatchProgressBar(fraction: progress) } } - .clipShape(.rect(cornerRadius: HaloRadius.card)) + .clipShape(.rect(cornerRadius: FlaroRadius.card)) } .buttonStyle(.card) .focused($isFocused) @@ -143,13 +143,13 @@ private struct TVCard: View { // Metern eine Textwand. VStack(alignment: .leading, spacing: 2) { Text(title) - .font(.haloCardTitle) - .foregroundStyle(Color.haloTextPrimary) + .font(.flaroCardTitle) + .foregroundStyle(Color.flaroTextPrimary) .lineLimit(1) if let subtitle { Text(subtitle) - .font(.haloCaption) - .foregroundStyle(Color.haloTextMuted) + .font(.flaroCaption) + .foregroundStyle(Color.flaroTextMuted) .lineLimit(1) } } diff --git a/Packages/HaloKit/Sources/HaloDesign/Components/WatchProgressBar.swift b/Packages/FlaroKit/Sources/FlaroDesign/Components/WatchProgressBar.swift similarity index 94% rename from Packages/HaloKit/Sources/HaloDesign/Components/WatchProgressBar.swift rename to Packages/FlaroKit/Sources/FlaroDesign/Components/WatchProgressBar.swift index 31b026c..709f3f3 100644 --- a/Packages/HaloKit/Sources/HaloDesign/Components/WatchProgressBar.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/Components/WatchProgressBar.swift @@ -5,7 +5,7 @@ public struct WatchProgressBar: View { /// 0…1. Werte außerhalb werden abgeschnitten, damit ein fehlerhafter /// Serverwert den Balken nicht über die Kachel hinausschieben kann. private let fraction: Double - @Environment(\.haloAccent) private var accent + @Environment(\.flaroAccent) private var accent public init(fraction: Double) { self.fraction = fraction.clamped() diff --git a/Packages/HaloKit/Sources/HaloDesign/HaloFont.swift b/Packages/FlaroKit/Sources/FlaroDesign/FlaroFont.swift similarity index 82% rename from Packages/HaloKit/Sources/HaloDesign/HaloFont.swift rename to Packages/FlaroKit/Sources/FlaroDesign/FlaroFont.swift index 964cb77..9abe561 100644 --- a/Packages/HaloKit/Sources/HaloDesign/HaloFont.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/FlaroFont.swift @@ -24,17 +24,17 @@ import SwiftUI extension Font { #if os(macOS) /// Der Titel im Aufmacher-Bild. - public static let haloHero = Font.system(size: 34, weight: .bold) + public static let flaroHero = Font.system(size: 34, weight: .bold) /// Titel auf Detailseiten. - public static let haloTitle = Font.system(size: 22, weight: .semibold) + public static let flaroTitle = Font.system(size: 22, weight: .semibold) /// Überschrift einer Reihe auf der Startseite. - public static let haloRowHeader = Font.system(size: 17, weight: .semibold) + public static let flaroRowHeader = Font.system(size: 17, weight: .semibold) /// Titel unter einem Poster. - public static let haloCardTitle = Font.system(size: 15, weight: .medium) + public static let flaroCardTitle = Font.system(size: 15, weight: .medium) /// Fließtext, Beschreibungen. - public static let haloBody = Font.system(size: 17) + public static let flaroBody = Font.system(size: 17) /// Jahreszahl, Laufzeit, Auflösung — alles, was neben dem Titel steht. - public static let haloCaption = Font.system(size: 12) + public static let flaroCaption = Font.system(size: 12) #elseif os(tvOS) /// Der Titel im Aufmacher-Bild. /// @@ -43,34 +43,34 @@ extension Font { /// verfügbar markiert. Was SwiftUI daraus macht, steht nirgends, und eine /// Schrift, deren Grösse niemand nennen kann, gehört nicht in die grösste /// Zeile der App. - public static let haloHero = Font.system(.title, design: .default, weight: .bold) + public static let flaroHero = Font.system(.title, design: .default, weight: .bold) /// Titel auf Detailseiten. - public static let haloTitle = Font.system(.title2, design: .default, weight: .semibold) + public static let flaroTitle = Font.system(.title2, design: .default, weight: .semibold) /// Überschrift einer Reihe auf der Startseite. - public static let haloRowHeader = Font.system(.headline, design: .default, weight: .semibold) + public static let flaroRowHeader = Font.system(.headline, design: .default, weight: .semibold) /// Titel unter einem Poster — auf tvOS nur bei Fokus sichtbar. /// /// Eine Stufe tiefer als auf iOS, und das ist gewollt: auf tvOS ist /// `caption` schon gross, und ein Raster aus sechs Spalten mit /// `subheadline`-Titeln hätte mehr Schrift als Bild. - public static let haloCardTitle = Font.system(.caption, design: .default, weight: .medium) + public static let flaroCardTitle = Font.system(.caption, design: .default, weight: .medium) /// Fließtext, Beschreibungen. - public static let haloBody = Font.system(.body) + public static let flaroBody = Font.system(.body) /// Jahreszahl, Laufzeit, Auflösung — alles, was neben dem Titel steht. - public static let haloCaption = Font.system(.caption2) + public static let flaroCaption = Font.system(.caption2) #else /// Der Titel im Aufmacher-Bild. - public static let haloHero = Font.system(.largeTitle, design: .default, weight: .bold) + public static let flaroHero = Font.system(.largeTitle, design: .default, weight: .bold) /// Titel auf Detailseiten. - public static let haloTitle = Font.system(.title2, design: .default, weight: .semibold) + public static let flaroTitle = Font.system(.title2, design: .default, weight: .semibold) /// Überschrift einer Reihe auf der Startseite. - public static let haloRowHeader = Font.system(.headline, design: .default, weight: .semibold) + public static let flaroRowHeader = Font.system(.headline, design: .default, weight: .semibold) /// Titel unter einem Poster. - public static let haloCardTitle = Font.system(.subheadline, design: .default, weight: .medium) + public static let flaroCardTitle = Font.system(.subheadline, design: .default, weight: .medium) /// Fließtext, Beschreibungen. - public static let haloBody = Font.system(.body) + public static let flaroBody = Font.system(.body) /// Jahreszahl, Laufzeit, Auflösung — alles, was neben dem Titel steht. - public static let haloCaption = Font.system(.caption) + public static let flaroCaption = Font.system(.caption) #endif } @@ -78,7 +78,7 @@ extension Font { /// /// Eine Skala statt frei gewählter Zahlen: dadurch stimmen die Ränder zwischen /// Bildschirmen überein, ohne dass man sie einzeln nachmisst. -public enum HaloSpacing { +public enum FlaroSpacing { #if os(tvOS) // Durchweg doppelt: ein Fernseher ist 1920 Punkte breit und wird aus drei // Metern gelesen. Acht Punkte Abstand sind dort keine Trennung mehr. @@ -124,15 +124,15 @@ public enum HaloSpacing { /// „neunzig" stimmt nicht; eine zweite Quelle nannte achtzig und hatte recht. /// /// **Und man bekommt sie geschenkt.** SwiftUI legt sie von selbst als -/// Sicherheitsrand an — deshalb steht in `HaloSpacing.screenMargin` nur die +/// Sicherheitsrand an — deshalb steht in `FlaroSpacing.screenMargin` nur die /// Rinne innerhalb davon. Die Zahl hier ist für die Gegenrichtung: ein /// bildfüllender Aufmacher, der den Sicherheitsrand absichtlich übergeht und /// dessen Schrift trotzdem drinbleiben muss. -public enum HaloSafeArea { +public enum FlaroSafeArea { public static let tv = EdgeInsets(top: 60, leading: 80, bottom: 60, trailing: 80) } -public enum HaloRadius { +public enum FlaroRadius { /// Die Ecke einer Kachel. /// /// Auf dem Fernseher grösser: eine Kachel, die sich beim Fokus anhebt, @@ -155,7 +155,7 @@ public enum HaloRadius { /// beschneidet: ein 16:9-Hintergrundbild in einem 3:4-Rahmen verliert links /// und rechts je ein Viertel, und man sieht dem Ergebnis nicht an, dass etwas /// fehlt. -public enum HaloAspectRatio { +public enum FlaroAspectRatio { /// Kinoplakate im Hochformat. public static let poster: CGFloat = 2.0 / 3.0 /// Szenenbilder und Hintergrundbilder. diff --git a/Packages/HaloKit/Sources/HaloDesign/HaloTheme.swift b/Packages/FlaroKit/Sources/FlaroDesign/FlaroTheme.swift similarity index 93% rename from Packages/HaloKit/Sources/HaloDesign/HaloTheme.swift rename to Packages/FlaroKit/Sources/FlaroDesign/FlaroTheme.swift index 7af89e8..7b55353 100644 --- a/Packages/HaloKit/Sources/HaloDesign/HaloTheme.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/FlaroTheme.swift @@ -7,7 +7,7 @@ import SwiftUI /// darauf zu achten. @MainActor @Observable -public final class HaloTheme { +public final class FlaroTheme { private let defaults: UserDefaults /// Was in den Einstellungen ausgewählt ist — unkorrigiert, damit die @@ -24,7 +24,7 @@ public final class HaloTheme { /// Bei den kuratierten Werten ändert die Korrektur nichts — sie erfüllen /// den Grenzwert bereits. Sie greift nur bei eigenen Farben. public var accent: RGB { - Contrast.legible(selection.color, on: HaloColor.canvas) + Contrast.legible(selection.color, on: FlaroColor.canvas) } public var accentColor: Color { accent.swiftUIColor } diff --git a/Packages/HaloKit/Sources/HaloDesign/GuideLayout.swift b/Packages/FlaroKit/Sources/FlaroDesign/GuideLayout.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDesign/GuideLayout.swift rename to Packages/FlaroKit/Sources/FlaroDesign/GuideLayout.swift diff --git a/Packages/HaloKit/Sources/HaloDesign/Localizable.xcstrings b/Packages/FlaroKit/Sources/FlaroDesign/Localizable.xcstrings similarity index 100% rename from Packages/HaloKit/Sources/HaloDesign/Localizable.xcstrings rename to Packages/FlaroKit/Sources/FlaroDesign/Localizable.xcstrings diff --git a/Packages/HaloKit/Sources/HaloDesign/MiniPlayerPlacement.swift b/Packages/FlaroKit/Sources/FlaroDesign/MiniPlayerPlacement.swift similarity index 97% rename from Packages/HaloKit/Sources/HaloDesign/MiniPlayerPlacement.swift rename to Packages/FlaroKit/Sources/FlaroDesign/MiniPlayerPlacement.swift index adce732..534a246 100644 --- a/Packages/HaloKit/Sources/HaloDesign/MiniPlayerPlacement.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/MiniPlayerPlacement.swift @@ -24,7 +24,7 @@ public struct MiniPlayerPlacement: Sendable, Equatable { /// Fehler statt wie eine Entscheidung. public let inset: CGFloat - public init(bounds: CGSize, tile: CGSize, inset: CGFloat = HaloSpacing.regular) { + public init(bounds: CGSize, tile: CGSize, inset: CGFloat = FlaroSpacing.regular) { self.bounds = bounds self.tile = tile self.inset = inset diff --git a/Packages/HaloKit/Sources/HaloDesign/PlatformShims.swift b/Packages/FlaroKit/Sources/FlaroDesign/PlatformShims.swift similarity index 89% rename from Packages/HaloKit/Sources/HaloDesign/PlatformShims.swift rename to Packages/FlaroKit/Sources/FlaroDesign/PlatformShims.swift index 2bf99ef..6acb42e 100644 --- a/Packages/HaloKit/Sources/HaloDesign/PlatformShims.swift +++ b/Packages/FlaroKit/Sources/FlaroDesign/PlatformShims.swift @@ -13,7 +13,7 @@ import AppKit /// nirgends sonst. Sonst wandert er in zwanzig Dateien und wird beim /// nächsten Bildschirm vergessen. /// Auf was für einem Gerät läuft das hier gerade? -public enum HaloPlatform { +public enum FlaroPlatform { /// Läuft die App auf einem Mac? /// /// **Zwei Wege führen dorthin, und für den Übersetzer sehen sie @@ -43,7 +43,7 @@ public enum HaloPlatform { #elseif os(macOS) true #else - #error("HaloPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.") + #error("FlaroPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.") #endif }() @@ -60,7 +60,7 @@ public enum HaloPlatform { #elseif os(iOS) || os(macOS) false #else - #error("HaloPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.") + #error("FlaroPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.") #endif }() } @@ -68,7 +68,7 @@ public enum HaloPlatform { extension View { /// Eine schmale Titelzeile — auf dem Mac und auf dem Fernseher gibt es /// sie nicht. Beide fallen in denselben `#else`-Zweig, und beide zu Recht. - public func haloInlineTitle() -> some View { + public func flaroInlineTitle() -> some View { #if os(iOS) navigationBarTitleDisplayMode(.inline) #else @@ -80,7 +80,7 @@ extension View { /// /// Auf dem Mac ist ein Sheet ein Fenster: dort zählt keine Rastung, /// sondern eine vernünftige Mindestgröße. - public func haloMediumSheet() -> some View { + public func flaroMediumSheet() -> some View { #if os(tvOS) // Auf dem Fernseher ist ein Sheet immer bildfüllend. `presentationDetents` // übersetzt dort zwar — es gibt sie seit tvOS 16 —, bewirkt aber @@ -92,18 +92,18 @@ extension View { // macOS griff nie: auf dem Mac läuft der iOS-Bau, und damit bekam ein // grosses Fenster eine Sheet-Kante in iPhone-Höhe — ein schmaler // Streifen, der unten im Nichts klebt. - presentationDetents(HaloPlatform.isMac ? [.large] : [.medium]) + presentationDetents(FlaroPlatform.isMac ? [.large] : [.medium]) #endif } /// Dasselbe, aber auf Wunsch bis zur vollen Höhe aufziehbar. - public func haloResizableSheet() -> some View { + public func flaroResizableSheet() -> some View { #if os(tvOS) self #else // Siehe oben. Auf dem Mac gibt es kein „halb aufgezogen": ein Fenster // hat die Höhe, die es hat, und eine Liste soll sie nutzen. - presentationDetents(HaloPlatform.isMac ? [.large] : [.medium, .large]) + presentationDetents(FlaroPlatform.isMac ? [.large] : [.medium, .large]) #endif } @@ -117,7 +117,7 @@ extension View { /// Stil ist derselbe, den iOS ohnehin nimmt — und der Grund, warum die /// Einstellungen auf dem Mac wie ein Systemdialog aus einem anderen /// Jahrzehnt aussahen. - public func haloForm() -> some View { + public func flaroForm() -> some View { #if os(macOS) formStyle(.grouped) #else @@ -130,7 +130,7 @@ extension View { /// /// Auf tvOS ebenfalls der `#else`-Zweig: die Bildschirmtastatur dort /// kennt weder `keyboardType` noch Autokorrektur in dieser Form. - public func haloPlainEntry(isAddress: Bool = false) -> some View { + public func flaroPlainEntry(isAddress: Bool = false) -> some View { #if os(iOS) self .keyboardType(isAddress ? .URL : .default) @@ -149,7 +149,7 @@ extension View { /// Leiste verschleierte die Hälfte davon. Auf dem Mac und auf dem /// Fernseher gibt es die Leiste in dieser Form nicht — beide fallen in /// den `#else`-Zweig. - public func haloClearToolbarBackground() -> some View { + public func flaroClearToolbarBackground() -> some View { #if os(iOS) self .toolbarBackground(.hidden, for: .navigationBar) @@ -164,7 +164,7 @@ extension View { /// /// UIKit und AppKit heißen verschieden und meinen dasselbe; die Diagnose soll /// deswegen nicht mit `#if` beginnen. -public enum HaloClipboard { +public enum FlaroClipboard { public static func copy(_ text: String) { #if os(tvOS) // Es gibt keine. `UIPasteboard` ist auf tvOS als nicht verfügbar @@ -186,7 +186,7 @@ public enum HaloClipboard { extension ToolbarItemPlacement { /// Rechts in der Titelzeile. - public static var haloTrailing: ToolbarItemPlacement { + public static var flaroTrailing: ToolbarItemPlacement { #if os(iOS) || os(tvOS) .topBarTrailing #else @@ -195,7 +195,7 @@ extension ToolbarItemPlacement { } /// Links in der Titelzeile. - public static var haloLeading: ToolbarItemPlacement { + public static var flaroLeading: ToolbarItemPlacement { #if os(iOS) || os(tvOS) .topBarLeading #else diff --git a/Packages/HaloKit/Sources/HaloDownloads/DownloadRate.swift b/Packages/FlaroKit/Sources/FlaroDownloads/DownloadRate.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/DownloadRate.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/DownloadRate.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/DownloadState.swift b/Packages/FlaroKit/Sources/FlaroDownloads/DownloadState.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/DownloadState.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/DownloadState.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadActivityPresenter.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadActivityPresenter.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadActivityPresenter.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadActivityPresenter.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadCoordinator.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadCoordinator.swift similarity index 98% rename from Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadCoordinator.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadCoordinator.swift index 3cf9ab6..f0f3a96 100644 --- a/Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadCoordinator.swift +++ b/Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadCoordinator.swift @@ -78,7 +78,7 @@ public enum DownloadRefusal: Error, Equatable, Sendable { public final class DownloadCoordinator { /// Die Kennung der Hintergrundsitzung. Muss über App-Starts hinweg gleich /// bleiben, sonst findet iOS die laufenden Übertragungen nicht wieder. - public static let sessionIdentifier = "com.scarriffleservices.halo.downloads" + public static let sessionIdentifier = "com.scarriffleservices.flaro.downloads" public private(set) var items: [DownloadItem] = [] /// Wie viel die Downloads insgesamt belegen. @@ -134,11 +134,11 @@ public final class DownloadCoordinator { } private enum Key { - static let wifiOnly = "halo.downloads.wifiOnly" - static let storageLimit = "halo.downloads.storageLimit" - static let deleteAfter = "halo.downloads.deleteWatchedAfter" - static let keepUnwatched = "halo.downloads.keepUnwatched" - static let maximumConcurrent = "halo.downloads.maximumConcurrent" + static let wifiOnly = "flaro.downloads.wifiOnly" + static let storageLimit = "flaro.downloads.storageLimit" + static let deleteAfter = "flaro.downloads.deleteWatchedAfter" + static let keepUnwatched = "flaro.downloads.keepUnwatched" + static let maximumConcurrent = "flaro.downloads.maximumConcurrent" } /// Der Container, nicht nur der Kontext. diff --git a/Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadFileStore.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadFileStore.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadFileStore.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadFileStore.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadRules.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadRules.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/Engine/DownloadRules.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Engine/DownloadRules.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/Localizable.xcstrings b/Packages/FlaroKit/Sources/FlaroDownloads/Localizable.xcstrings similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/Localizable.xcstrings rename to Packages/FlaroKit/Sources/FlaroDownloads/Localizable.xcstrings diff --git a/Packages/HaloKit/Sources/HaloDownloads/Model/DownloadActivityAttributes.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Model/DownloadActivityAttributes.swift similarity index 96% rename from Packages/HaloKit/Sources/HaloDownloads/Model/DownloadActivityAttributes.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Model/DownloadActivityAttributes.swift index 27db132..f9b081a 100644 --- a/Packages/HaloKit/Sources/HaloDownloads/Model/DownloadActivityAttributes.swift +++ b/Packages/FlaroKit/Sources/FlaroDownloads/Model/DownloadActivityAttributes.swift @@ -11,7 +11,7 @@ import ActivityKit /// Was im Sperrbildschirm und in der Dynamic Island steht. /// -/// Liegt in HaloDownloads, weil App und Widget-Erweiterung denselben Typ +/// Liegt in FlaroDownloads, weil App und Widget-Erweiterung denselben Typ /// brauchen — zwei getrennte Kopien wären die klassische Quelle für eine Live /// Activity, die sich nicht mehr aktualisieren lässt: ActivityKit gleicht die /// Typen über ihren vollen Namen ab. diff --git a/Packages/HaloKit/Sources/HaloDownloads/Model/DownloadItem.swift b/Packages/FlaroKit/Sources/FlaroDownloads/Model/DownloadItem.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/Model/DownloadItem.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/Model/DownloadItem.swift diff --git a/Packages/HaloKit/Sources/HaloDownloads/TransferHold.swift b/Packages/FlaroKit/Sources/FlaroDownloads/TransferHold.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloDownloads/TransferHold.swift rename to Packages/FlaroKit/Sources/FlaroDownloads/TransferHold.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/AudioTrackChooser.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/AudioTrackChooser.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/AudioTrackChooser.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/AudioTrackChooser.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/DeviceCapabilities.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/DeviceCapabilities.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/DeviceCapabilities.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/DeviceCapabilities.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/DowngradeRule.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/DowngradeRule.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/DowngradeRule.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/DowngradeRule.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/LanguageCode.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/LanguageCode.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/LanguageCode.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/LanguageCode.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/NetworkConditions.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/NetworkConditions.swift similarity index 94% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/NetworkConditions.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/NetworkConditions.swift index cd41c88..0bb38b7 100644 --- a/Packages/HaloKit/Sources/HaloPlayback/Decision/NetworkConditions.swift +++ b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/NetworkConditions.swift @@ -29,7 +29,7 @@ public final class NetworkConditions { isConstrained = path.isConstrained } } - monitor.start(queue: DispatchQueue(label: "halo.network")) + monitor.start(queue: DispatchQueue(label: "flaro.network")) } deinit { diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackDecider.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackDecider.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackDecider.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackDecider.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackDecision.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackDecision.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackDecision.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackDecision.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackPolicy.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackPolicy.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/PlaybackPolicy.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlaybackPolicy.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/PlayerQuality.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlayerQuality.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/PlayerQuality.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/PlayerQuality.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/StallLedger.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/StallLedger.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/StallLedger.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/StallLedger.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/StreamHealth.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/StreamHealth.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/StreamHealth.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/StreamHealth.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/TrackMenu.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/TrackMenu.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/TrackMenu.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/TrackMenu.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Decision/UpgradeRule.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Decision/UpgradeRule.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Decision/UpgradeRule.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Decision/UpgradeRule.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/AVPlayerEngine.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/AVPlayerEngine.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/AVPlayerEngine.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/AVPlayerEngine.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/MediaTrack.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/MediaTrack.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/MediaTrack.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/MediaTrack.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/PlaybackState.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/PlaybackState.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/PlaybackState.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/PlaybackState.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/SubtitleStyle+AVFoundation.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/SubtitleStyle+AVFoundation.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/SubtitleStyle+AVFoundation.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/SubtitleStyle+AVFoundation.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/SubtitleStyle.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/SubtitleStyle.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/SubtitleStyle.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/SubtitleStyle.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Engine/VideoPlayerEngine.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Engine/VideoPlayerEngine.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Engine/VideoPlayerEngine.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Engine/VideoPlayerEngine.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Prefetch/LocalStreamServer.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/LocalStreamServer.swift similarity index 99% rename from Packages/HaloKit/Sources/HaloPlayback/Prefetch/LocalStreamServer.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/LocalStreamServer.swift index 01867f7..264c3fd 100644 --- a/Packages/HaloKit/Sources/HaloPlayback/Prefetch/LocalStreamServer.swift +++ b/Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/LocalStreamServer.swift @@ -70,7 +70,7 @@ public actor LocalStreamServer { } private let cache: StreamCache - private let logger = Logger(subsystem: "app.halo", category: "LocalStreamServer") + private let logger = Logger(subsystem: "app.flaro", category: "LocalStreamServer") private var listener: NWListener? private var port: UInt16? private var playlist: String? diff --git a/Packages/HaloKit/Sources/HaloPlayback/Prefetch/MediaPlaylist.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/MediaPlaylist.swift similarity index 100% rename from Packages/HaloKit/Sources/HaloPlayback/Prefetch/MediaPlaylist.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/MediaPlaylist.swift diff --git a/Packages/HaloKit/Sources/HaloPlayback/Prefetch/StreamCache.swift b/Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/StreamCache.swift similarity index 98% rename from Packages/HaloKit/Sources/HaloPlayback/Prefetch/StreamCache.swift rename to Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/StreamCache.swift index ba5eee5..c3cab84 100644 --- a/Packages/HaloKit/Sources/HaloPlayback/Prefetch/StreamCache.swift +++ b/Packages/FlaroKit/Sources/FlaroPlayback/Prefetch/StreamCache.swift @@ -44,7 +44,7 @@ public actor StreamCache { private let policy: Policy private let session: URLSession private let directory: URL - private let logger = Logger(subsystem: "app.halo", category: "StreamCache") + private let logger = Logger(subsystem: "app.flaro", category: "StreamCache") /// Woher die Schnipsel wirklich kommen. private var upstream: URL? @@ -83,7 +83,7 @@ public actor StreamCache { self.session = URLSession(configuration: configuration) self.directory = FileManager.default.temporaryDirectory - .appending(path: "halo-stream-\(UUID().uuidString)", directoryHint: .isDirectory) + .appending(path: "flaro-stream-\(UUID().uuidString)", directoryHint: .isDirectory) try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) } diff --git a/Packages/HaloKit/Sources/HaloProbe/Leiter.swift b/Packages/FlaroKit/Sources/FlaroProbe/Leiter.swift similarity index 99% rename from Packages/HaloKit/Sources/HaloProbe/Leiter.swift rename to Packages/FlaroKit/Sources/FlaroProbe/Leiter.swift index 0c75312..93b4ca7 100644 --- a/Packages/HaloKit/Sources/HaloProbe/Leiter.swift +++ b/Packages/FlaroKit/Sources/FlaroProbe/Leiter.swift @@ -172,9 +172,9 @@ func jellyfinLeiter(host: String) async { } let identity = JellyfinIdentity( - client: "Halo-Probe", + client: "Flaro-Probe", device: "Mac", - deviceID: "halo-probe-" + UUID().uuidString, + deviceID: "flaro-probe-" + UUID().uuidString, version: "1.0" ) let client = JellyfinClient(baseURL: basis, identity: identity) @@ -471,9 +471,9 @@ func zeigeSpuren(host: String, suche: String) async { guard let passwort = String(validatingCString: getpass("Passwort: ")), !passwort.isEmpty else { return } let identity = JellyfinIdentity( - client: "Halo-Probe", + client: "Flaro-Probe", device: "Mac", - deviceID: "halo-probe-" + UUID().uuidString, + deviceID: "flaro-probe-" + UUID().uuidString, version: "1.0" ) let client = JellyfinClient(baseURL: basis, identity: identity) diff --git a/Packages/HaloKit/Sources/HaloProbe/main.swift b/Packages/FlaroKit/Sources/FlaroProbe/main.swift similarity index 96% rename from Packages/HaloKit/Sources/HaloProbe/main.swift rename to Packages/FlaroKit/Sources/FlaroProbe/main.swift index 773f2c5..8b49dbf 100644 --- a/Packages/HaloKit/Sources/HaloProbe/main.swift +++ b/Packages/FlaroKit/Sources/FlaroProbe/main.swift @@ -46,7 +46,7 @@ if let index = CommandLine.arguments.firstIndex(of: "--jellyfin"), // Kein Modus erkannt. Ohne diesen Hinweis endete das Werkzeug wortlos — und // ein Tippfehler im Argument sähe aus wie ein Server, der nichts liefert. print(""" - HaloProbe — fragt Server über reines HTTP, ohne Oberfläche und ohne Gerät. + FlaroProbe — fragt Server über reines HTTP, ohne Oberfläche und ohne Gerät. --playlist Eine HLS-Leiter prüfen: Varianten, Tonspuren, erster Schnipsel diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinAddress.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinAddress.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinAddress.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinAddress.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinAuthenticator.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinAuthenticator.swift similarity index 98% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinAuthenticator.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinAuthenticator.swift index 18bd93e..f76f1f6 100644 --- a/Packages/HaloKit/Sources/JellyfinKit/JellyfinAuthenticator.swift +++ b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinAuthenticator.swift @@ -54,7 +54,7 @@ public struct QuickConnectRequest: Sendable, Equatable, Decodable { /// /// **Quick Connect** ist der erste: die App zeigt einen sechsstelligen Code, /// den man in einem bereits angemeldeten Client bestätigt. Das Passwort -/// erreicht Halo dabei nie — dieselbe Zusage, die bei Plex gilt, wo die +/// erreicht Flaro dabei nie — dieselbe Zusage, die bei Plex gilt, wo die /// Anmeldemaske von Plex selbst kommt. /// /// **Benutzername und Passwort** sind der Rückfall, falls der Server Quick diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+LiveTV.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+LiveTV.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+LiveTV.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+LiveTV.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Playback.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Playback.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Playback.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Playback.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Recordings.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Recordings.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Recordings.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Recordings.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Segments.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Segments.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend+Segments.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend+Segments.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift similarity index 99% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift index 17a510f..1b2b99a 100644 --- a/Packages/HaloKit/Sources/JellyfinKit/JellyfinBackend.swift +++ b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinBackend.swift @@ -453,7 +453,7 @@ public func isInLocalNetwork() async -> Bool { private static func transcodeSession(itemID: String, deviceID: String) -> [URLQueryItem] { [ URLQueryItem(name: "deviceId", value: deviceID), - URLQueryItem(name: "playSessionId", value: "halo-\(UUID().uuidString)"), + URLQueryItem(name: "playSessionId", value: "flaro-\(UUID().uuidString)"), ] } diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinClient.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinClient.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinClient.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinClient.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift similarity index 95% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift index 480b233..a7637fc 100644 --- a/Packages/HaloKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift +++ b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinIdentity+Current.swift @@ -4,7 +4,7 @@ import UIKit #endif extension JellyfinIdentity { - private static let storageKey = "halo.jellyfin.deviceID" + private static let storageKey = "flaro.jellyfin.deviceID" /// Baut die Kennung für das laufende Gerät und merkt sie sich. /// diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinItem+Media.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinItem+Media.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinItem+Media.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinItem+Media.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinItem.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinItem.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinItem.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinItem.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/JellyfinLibraryMemo.swift b/Packages/FlaroKit/Sources/JellyfinKit/JellyfinLibraryMemo.swift similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/JellyfinLibraryMemo.swift rename to Packages/FlaroKit/Sources/JellyfinKit/JellyfinLibraryMemo.swift diff --git a/Packages/HaloKit/Sources/JellyfinKit/Localizable.xcstrings b/Packages/FlaroKit/Sources/JellyfinKit/Localizable.xcstrings similarity index 100% rename from Packages/HaloKit/Sources/JellyfinKit/Localizable.xcstrings rename to Packages/FlaroKit/Sources/JellyfinKit/Localizable.xcstrings diff --git a/Packages/HaloKit/Sources/MediaKit/Arrangement.swift b/Packages/FlaroKit/Sources/MediaKit/Arrangement.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/Arrangement.swift rename to Packages/FlaroKit/Sources/MediaKit/Arrangement.swift diff --git a/Packages/HaloKit/Sources/MediaKit/Duration+Units.swift b/Packages/FlaroKit/Sources/MediaKit/Duration+Units.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/Duration+Units.swift rename to Packages/FlaroKit/Sources/MediaKit/Duration+Units.swift diff --git a/Packages/HaloKit/Sources/MediaKit/EpisodeSelection.swift b/Packages/FlaroKit/Sources/MediaKit/EpisodeSelection.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/EpisodeSelection.swift rename to Packages/FlaroKit/Sources/MediaKit/EpisodeSelection.swift diff --git a/Packages/HaloKit/Sources/MediaKit/HeroSelection.swift b/Packages/FlaroKit/Sources/MediaKit/HeroSelection.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/HeroSelection.swift rename to Packages/FlaroKit/Sources/MediaKit/HeroSelection.swift diff --git a/Packages/HaloKit/Sources/MediaKit/LibraryCache.swift b/Packages/FlaroKit/Sources/MediaKit/LibraryCache.swift similarity index 98% rename from Packages/HaloKit/Sources/MediaKit/LibraryCache.swift rename to Packages/FlaroKit/Sources/MediaKit/LibraryCache.swift index 4995a51..228a200 100644 --- a/Packages/HaloKit/Sources/MediaKit/LibraryCache.swift +++ b/Packages/FlaroKit/Sources/MediaKit/LibraryCache.swift @@ -47,7 +47,7 @@ public actor LibraryCache { } private let root: URL - private let logger = Logger(subsystem: "app.halo", category: "LibraryCache") + private let logger = Logger(subsystem: "app.flaro", category: "LibraryCache") /// Wie viele Titel höchstens gespeichert werden. /// diff --git a/Packages/HaloKit/Sources/MediaKit/LiveTV.swift b/Packages/FlaroKit/Sources/MediaKit/LiveTV.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/LiveTV.swift rename to Packages/FlaroKit/Sources/MediaKit/LiveTV.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaBackend.swift b/Packages/FlaroKit/Sources/MediaKit/MediaBackend.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaBackend.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaBackend.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaBackendError.swift b/Packages/FlaroKit/Sources/MediaKit/MediaBackendError.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaBackendError.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaBackendError.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaItem.swift b/Packages/FlaroKit/Sources/MediaKit/MediaItem.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaItem.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaItem.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaLibrary.swift b/Packages/FlaroKit/Sources/MediaKit/MediaLibrary.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaLibrary.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaLibrary.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaPlayback.swift b/Packages/FlaroKit/Sources/MediaKit/MediaPlayback.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaPlayback.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaPlayback.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaProfile.swift b/Packages/FlaroKit/Sources/MediaKit/MediaProfile.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaProfile.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaProfile.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaQuery.swift b/Packages/FlaroKit/Sources/MediaKit/MediaQuery.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaQuery.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaQuery.swift diff --git a/Packages/HaloKit/Sources/MediaKit/MediaVersion.swift b/Packages/FlaroKit/Sources/MediaKit/MediaVersion.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/MediaVersion.swift rename to Packages/FlaroKit/Sources/MediaKit/MediaVersion.swift diff --git a/Packages/HaloKit/Sources/MediaKit/PagingWindow.swift b/Packages/FlaroKit/Sources/MediaKit/PagingWindow.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/PagingWindow.swift rename to Packages/FlaroKit/Sources/MediaKit/PagingWindow.swift diff --git a/Packages/HaloKit/Sources/MediaKit/TokenStore.swift b/Packages/FlaroKit/Sources/MediaKit/TokenStore.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/TokenStore.swift rename to Packages/FlaroKit/Sources/MediaKit/TokenStore.swift diff --git a/Packages/HaloKit/Sources/MediaKit/TopShelfSnapshot.swift b/Packages/FlaroKit/Sources/MediaKit/TopShelfSnapshot.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/TopShelfSnapshot.swift rename to Packages/FlaroKit/Sources/MediaKit/TopShelfSnapshot.swift diff --git a/Packages/HaloKit/Sources/MediaKit/VideoResolution.swift b/Packages/FlaroKit/Sources/MediaKit/VideoResolution.swift similarity index 100% rename from Packages/HaloKit/Sources/MediaKit/VideoResolution.swift rename to Packages/FlaroKit/Sources/MediaKit/VideoResolution.swift diff --git a/Packages/HaloKit/Tests/HaloDesignTests/ContrastTests.swift b/Packages/FlaroKit/Tests/FlaroDesignTests/ContrastTests.swift similarity index 96% rename from Packages/HaloKit/Tests/HaloDesignTests/ContrastTests.swift rename to Packages/FlaroKit/Tests/FlaroDesignTests/ContrastTests.swift index eb20689..10f880c 100644 --- a/Packages/HaloKit/Tests/HaloDesignTests/ContrastTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDesignTests/ContrastTests.swift @@ -1,10 +1,10 @@ import Foundation import Testing -@testable import HaloDesign +@testable import FlaroDesign @Suite("Kontrastkorrektur") struct ContrastTests { - private let canvas = HaloColor.canvas + private let canvas = FlaroColor.canvas @Test("Schwarz zu Weiß ergibt das Maximum von 21:1") func extremesProduceMaximumRatio() { @@ -73,7 +73,7 @@ struct AccentPaletteTests { // hier durch, gehört sie nicht in die Palette — nicht in die // Korrektur. for option in AccentOption.palette { - let ratio = Contrast.ratio(option.color, HaloColor.canvas) + let ratio = Contrast.ratio(option.color, FlaroColor.canvas) #expect( ratio >= Contrast.minimumForText, "\(option.id) erreicht nur \(String(format: "%.2f", ratio)):1" @@ -146,7 +146,7 @@ struct DefaultAccentTests { @Test("Die Voreinstellung braucht keine Kontrastkorrektur") func defaultNeedsNoCorrection() { - let corrected = Contrast.legible(AccentOption.default.color, on: HaloColor.canvas) + let corrected = Contrast.legible(AccentOption.default.color, on: FlaroColor.canvas) #expect(corrected == AccentOption.default.color) } } @@ -154,7 +154,7 @@ struct DefaultAccentTests { @Suite("Gespeicherte Akzentfarbe") struct AccentStorageTests { private func freshDefaults() -> UserDefaults { - UserDefaults(suiteName: "halo.accent.tests.\(UUID().uuidString)")! + UserDefaults(suiteName: "flaro.accent.tests.\(UUID().uuidString)")! } @Test("Ohne gespeicherte Auswahl gilt die Voreinstellung") @@ -200,7 +200,7 @@ struct AccentStorageTests { let defaults = freshDefaults() AccentOption.custom(RGB(hex: 0x8B5CF6)).store(in: defaults) - let theme = HaloTheme(defaults: defaults) + let theme = FlaroTheme(defaults: defaults) #expect(theme.accentColor == AccentOption.storedAccentColor(in: defaults)) } } diff --git a/Packages/HaloKit/Tests/HaloDesignTests/HaloPlatformTests.swift b/Packages/FlaroKit/Tests/FlaroDesignTests/FlaroPlatformTests.swift similarity index 87% rename from Packages/HaloKit/Tests/HaloDesignTests/HaloPlatformTests.swift rename to Packages/FlaroKit/Tests/FlaroDesignTests/FlaroPlatformTests.swift index 8009be8..118760c 100644 --- a/Packages/HaloKit/Tests/HaloDesignTests/HaloPlatformTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDesignTests/FlaroPlatformTests.swift @@ -1,8 +1,8 @@ import Testing -@testable import HaloDesign +@testable import FlaroDesign @Suite("Auf welchem Gerät wir laufen") -struct HaloPlatformTests { +struct FlaroPlatformTests { /// **Der Test, den es vorher nicht gab — und der gefehlt hat.** /// @@ -17,6 +17,6 @@ struct HaloPlatformTests { /// hält er wenigstens fest, dass die Frage gestellt gehört. @Test("Ein Gerät ist nicht zugleich Mac und Fernseher") func neverBoth() { - #expect(!(HaloPlatform.isMac && HaloPlatform.isTV)) + #expect(!(FlaroPlatform.isMac && FlaroPlatform.isTV)) } } diff --git a/Packages/HaloKit/Tests/HaloDesignTests/GuideLayoutTests.swift b/Packages/FlaroKit/Tests/FlaroDesignTests/GuideLayoutTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloDesignTests/GuideLayoutTests.swift rename to Packages/FlaroKit/Tests/FlaroDesignTests/GuideLayoutTests.swift index b1e63e4..b101c78 100644 --- a/Packages/HaloKit/Tests/HaloDesignTests/GuideLayoutTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDesignTests/GuideLayoutTests.swift @@ -1,6 +1,6 @@ import Foundation import Testing -@testable import HaloDesign +@testable import FlaroDesign /// Die Geometrie des Programmrasters. @Suite("Raster-Geometrie") diff --git a/Packages/HaloKit/Tests/HaloDesignTests/MiniPlayerPlacementTests.swift b/Packages/FlaroKit/Tests/FlaroDesignTests/MiniPlayerPlacementTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloDesignTests/MiniPlayerPlacementTests.swift rename to Packages/FlaroKit/Tests/FlaroDesignTests/MiniPlayerPlacementTests.swift index 8d882c2..551e08c 100644 --- a/Packages/HaloKit/Tests/HaloDesignTests/MiniPlayerPlacementTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDesignTests/MiniPlayerPlacementTests.swift @@ -1,6 +1,6 @@ import Foundation import Testing -@testable import HaloDesign +@testable import FlaroDesign /// Wo die schwebende Videokachel landet. @Suite("Kachel-Platzierung") diff --git a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadCoordinatorTests.swift b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadCoordinatorTests.swift similarity index 98% rename from Packages/HaloKit/Tests/HaloDownloadsTests/DownloadCoordinatorTests.swift rename to Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadCoordinatorTests.swift index 4aa502e..3059851 100644 --- a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadCoordinatorTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadCoordinatorTests.swift @@ -1,7 +1,7 @@ import Foundation import SwiftData import Testing -@testable import HaloDownloads +@testable import FlaroDownloads /// Baut einen Koordinator, der nichts überträgt. /// @@ -16,7 +16,7 @@ private func makeCoordinator( for: DownloadItem.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true) ) - let defaults = UserDefaults(suiteName: "halo.tests.\(UUID().uuidString)")! + let defaults = UserDefaults(suiteName: "flaro.tests.\(UUID().uuidString)")! let coordinator = DownloadCoordinator( container: container, @@ -48,7 +48,7 @@ private func plan( } private func temporaryRoot() -> URL { - FileManager.default.temporaryDirectory.appending(path: "halo-tests-\(UUID().uuidString)") + FileManager.default.temporaryDirectory.appending(path: "flaro-tests-\(UUID().uuidString)") } @MainActor diff --git a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRateTests.swift b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRateTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRateTests.swift rename to Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRateTests.swift index a62586b..cbb16ac 100644 --- a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRateTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRateTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import HaloDownloads +@testable import FlaroDownloads @Suite("Downloadrate und Restzeit") struct DownloadRateTests { diff --git a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRulesTests.swift b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRulesTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRulesTests.swift rename to Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRulesTests.swift index 0af6a94..b112db0 100644 --- a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadRulesTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadRulesTests.swift @@ -1,7 +1,7 @@ import Foundation import MediaKit import Testing -@testable import HaloDownloads +@testable import FlaroDownloads /// Baut einen Eintrag im gewünschten Zustand, ohne Datenbank. @MainActor diff --git a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadStateTests.swift b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadStateTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloDownloadsTests/DownloadStateTests.swift rename to Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadStateTests.swift index b1b1d14..527ec21 100644 --- a/Packages/HaloKit/Tests/HaloDownloadsTests/DownloadStateTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDownloadsTests/DownloadStateTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import HaloDownloads +@testable import FlaroDownloads @Suite("Download-Zustandsautomat") struct DownloadStateTests { diff --git a/Packages/HaloKit/Tests/HaloDownloadsTests/TransferHoldTests.swift b/Packages/FlaroKit/Tests/FlaroDownloadsTests/TransferHoldTests.swift similarity index 97% rename from Packages/HaloKit/Tests/HaloDownloadsTests/TransferHoldTests.swift rename to Packages/FlaroKit/Tests/FlaroDownloadsTests/TransferHoldTests.swift index 66648de..65ca2d1 100644 --- a/Packages/HaloKit/Tests/HaloDownloadsTests/TransferHoldTests.swift +++ b/Packages/FlaroKit/Tests/FlaroDownloadsTests/TransferHoldTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import HaloDownloads +@testable import FlaroDownloads /// Warum gerade nichts übertragen wird. @Suite("Übertragungssperre") diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/AudioTrackChooserTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/AudioTrackChooserTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/AudioTrackChooserTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/AudioTrackChooserTests.swift index 3668dfb..bd7370a 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/AudioTrackChooserTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/AudioTrackChooserTests.swift @@ -1,7 +1,7 @@ import Testing import Foundation import MediaKit -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Sprachkürzel") struct LanguageCodeTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/DowngradeRuleTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/DowngradeRuleTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/DowngradeRuleTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/DowngradeRuleTests.swift index 8afbeef..5045c9e 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/DowngradeRuleTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/DowngradeRuleTests.swift @@ -1,6 +1,6 @@ import Testing import Foundation -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Wann der teure Weg erlaubt ist") struct DowngradeRuleTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/LocalStreamServerTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/LocalStreamServerTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/LocalStreamServerTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/LocalStreamServerTests.swift index 24142b1..ec6432d 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/LocalStreamServerTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/LocalStreamServerTests.swift @@ -1,7 +1,7 @@ import Foundation import Network import Testing -@testable import HaloPlayback +@testable import FlaroPlayback /// Was der lokale Server dem Player wirklich schickt. /// diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/MediaPlaylistTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/MediaPlaylistTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/MediaPlaylistTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/MediaPlaylistTests.swift index bc963f2..76240e3 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/MediaPlaylistTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/MediaPlaylistTests.swift @@ -1,6 +1,6 @@ import Testing import Foundation -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Abspielliste lesen und umschreiben") struct MediaPlaylistTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/PlaybackDeciderTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/PlaybackDeciderTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/PlaybackDeciderTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/PlaybackDeciderTests.swift index 7f8cd1f..5cd5aa6 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/PlaybackDeciderTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/PlaybackDeciderTests.swift @@ -1,6 +1,6 @@ import Testing import MediaKit -@testable import HaloPlayback +@testable import FlaroPlayback /// Die Tabelle, die festhält, welche Datei auf welchem Weg landet. /// diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/QualityMemoryTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/QualityMemoryTests.swift similarity index 98% rename from Packages/HaloKit/Tests/HaloPlaybackTests/QualityMemoryTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/QualityMemoryTests.swift index e05cf0f..2a19262 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/QualityMemoryTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/QualityMemoryTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import HaloPlayback +@testable import FlaroPlayback /// Die gemerkte Qualitätsstufe über mehrere Filme hinweg. /// diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/StallLedgerTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/StallLedgerTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/StallLedgerTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/StallLedgerTests.swift index 7e3cf1e..117491e 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/StallLedgerTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/StallLedgerTests.swift @@ -1,6 +1,6 @@ import Testing import Foundation -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Rückler-Buchführung") struct StallLedgerTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/StreamHealthTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/StreamHealthTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/StreamHealthTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/StreamHealthTests.swift index 028f45e..58fd7c6 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/StreamHealthTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/StreamHealthTests.swift @@ -1,6 +1,6 @@ import Testing import Foundation -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Zustand des Streams") struct StreamHealthTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/SubtitleStyleTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/SubtitleStyleTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/SubtitleStyleTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/SubtitleStyleTests.swift index fcdfe34..44bf998 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/SubtitleStyleTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/SubtitleStyleTests.swift @@ -2,7 +2,7 @@ import AVFoundation import CoreMedia import Foundation import Testing -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Aussehen der Untertitel") struct SubtitleStyleTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/TrackMenuTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/TrackMenuTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/TrackMenuTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/TrackMenuTests.swift index 722df59..b793551 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/TrackMenuTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/TrackMenuTests.swift @@ -1,6 +1,6 @@ import Testing import MediaKit -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Was im Spurenmenü steht") struct TrackMenuTests { diff --git a/Packages/HaloKit/Tests/HaloPlaybackTests/UpgradeRuleTests.swift b/Packages/FlaroKit/Tests/FlaroPlaybackTests/UpgradeRuleTests.swift similarity index 99% rename from Packages/HaloKit/Tests/HaloPlaybackTests/UpgradeRuleTests.swift rename to Packages/FlaroKit/Tests/FlaroPlaybackTests/UpgradeRuleTests.swift index c979ba4..ce5b43e 100644 --- a/Packages/HaloKit/Tests/HaloPlaybackTests/UpgradeRuleTests.swift +++ b/Packages/FlaroKit/Tests/FlaroPlaybackTests/UpgradeRuleTests.swift @@ -1,6 +1,6 @@ import Foundation import Testing -@testable import HaloPlayback +@testable import FlaroPlayback @Suite("Wann hinaufgeschaltet wird") struct UpgradeRuleTests { diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/AdaptiveURLTests.swift b/Packages/FlaroKit/Tests/JellyfinKitTests/AdaptiveURLTests.swift similarity index 100% rename from Packages/HaloKit/Tests/JellyfinKitTests/AdaptiveURLTests.swift rename to Packages/FlaroKit/Tests/JellyfinKitTests/AdaptiveURLTests.swift diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/AddressTests.swift b/Packages/FlaroKit/Tests/JellyfinKitTests/AddressTests.swift similarity index 100% rename from Packages/HaloKit/Tests/JellyfinKitTests/AddressTests.swift rename to Packages/FlaroKit/Tests/JellyfinKitTests/AddressTests.swift diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/Fixtures/episode.json b/Packages/FlaroKit/Tests/JellyfinKitTests/Fixtures/episode.json similarity index 100% rename from Packages/HaloKit/Tests/JellyfinKitTests/Fixtures/episode.json rename to Packages/FlaroKit/Tests/JellyfinKitTests/Fixtures/episode.json diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/Fixtures/series.json b/Packages/FlaroKit/Tests/JellyfinKitTests/Fixtures/series.json similarity index 100% rename from Packages/HaloKit/Tests/JellyfinKitTests/Fixtures/series.json rename to Packages/FlaroKit/Tests/JellyfinKitTests/Fixtures/series.json diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/PlaybackTests.swift b/Packages/FlaroKit/Tests/JellyfinKitTests/PlaybackTests.swift similarity index 98% rename from Packages/HaloKit/Tests/JellyfinKitTests/PlaybackTests.swift rename to Packages/FlaroKit/Tests/JellyfinKitTests/PlaybackTests.swift index 9ec5cfd..765474e 100644 --- a/Packages/HaloKit/Tests/JellyfinKitTests/PlaybackTests.swift +++ b/Packages/FlaroKit/Tests/JellyfinKitTests/PlaybackTests.swift @@ -15,7 +15,7 @@ struct PlaybackTests { let client = JellyfinClient( baseURL: URL(string: "http://server:8096")!, identity: JellyfinIdentity( - client: "Halo", device: "Testgerät", deviceID: "gerät-1", version: "1" + client: "Flaro", device: "Testgerät", deviceID: "gerät-1", version: "1" ), token: "tok", transport: transport @@ -240,7 +240,7 @@ struct AudioStreamIndexTests { let client = JellyfinClient( baseURL: URL(string: "http://server:8096")!, identity: JellyfinIdentity( - client: "Halo", device: "Testgerät", deviceID: "gerät-1", version: "1" + client: "Flaro", device: "Testgerät", deviceID: "gerät-1", version: "1" ), token: "tok", transport: transport @@ -299,7 +299,7 @@ struct SubtitleStreamIndexTests { client: JellyfinClient( baseURL: URL(string: "http://server:8096")!, identity: JellyfinIdentity( - client: "Halo", device: "Testgerät", deviceID: "gerät-1", version: "1" + client: "Flaro", device: "Testgerät", deviceID: "gerät-1", version: "1" ), token: "tok", transport: transport @@ -358,7 +358,7 @@ struct MasterPlaylistTests { client: JellyfinClient( baseURL: URL(string: "http://server:8096")!, identity: JellyfinIdentity( - client: "Halo", device: "Testgerät", deviceID: "gerät-1", version: "1" + client: "Flaro", device: "Testgerät", deviceID: "gerät-1", version: "1" ), token: "tok", transport: transport diff --git a/Packages/HaloKit/Tests/JellyfinKitTests/TranslationTests.swift b/Packages/FlaroKit/Tests/JellyfinKitTests/TranslationTests.swift similarity index 100% rename from Packages/HaloKit/Tests/JellyfinKitTests/TranslationTests.swift rename to Packages/FlaroKit/Tests/JellyfinKitTests/TranslationTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/.keep b/Packages/FlaroKit/Tests/MediaKitTests/.keep similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/.keep rename to Packages/FlaroKit/Tests/MediaKitTests/.keep diff --git a/Packages/HaloKit/Tests/MediaKitTests/ArrangementTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/ArrangementTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/ArrangementTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/ArrangementTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/DurationUnitsTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/DurationUnitsTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/DurationUnitsTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/DurationUnitsTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/EpisodeSelectionTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/EpisodeSelectionTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/EpisodeSelectionTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/EpisodeSelectionTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/HeroSelectionTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/HeroSelectionTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/HeroSelectionTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/HeroSelectionTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/LibraryCacheTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/LibraryCacheTests.swift similarity index 99% rename from Packages/HaloKit/Tests/MediaKitTests/LibraryCacheTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/LibraryCacheTests.swift index 726f351..7403e88 100644 --- a/Packages/HaloKit/Tests/MediaKitTests/LibraryCacheTests.swift +++ b/Packages/FlaroKit/Tests/MediaKitTests/LibraryCacheTests.swift @@ -8,7 +8,7 @@ struct LibraryCacheTests { /// Jeder Test bekommt ein eigenes Verzeichnis. Ein gemeinsames hiesse, /// dass die Reihenfolge der Tests über ihr Ergebnis mitentscheidet. private func frischerSpeicher() -> LibraryCache { - let ort = URL.temporaryDirectory.appending(path: "halo-cache-\(UUID().uuidString)") + let ort = URL.temporaryDirectory.appending(path: "flaro-cache-\(UUID().uuidString)") return LibraryCache(root: ort) } diff --git a/Packages/HaloKit/Tests/MediaKitTests/PagingWindowTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/PagingWindowTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/PagingWindowTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/PagingWindowTests.swift diff --git a/Packages/HaloKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift similarity index 97% rename from Packages/HaloKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift index 4f0a496..45507bd 100644 --- a/Packages/HaloKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift +++ b/Packages/FlaroKit/Tests/MediaKitTests/TopShelfSnapshotTests.swift @@ -34,7 +34,7 @@ struct TopShelfSnapshotTests { /// geschrieben wurde. @Test("Eine unbekannte App-Gruppe bringt nichts zum Absturz") func unknownGroupIsHarmless() { - let unbekannt = "group.halo.test.\(UUID().uuidString)" + let unbekannt = "group.flaro.test.\(UUID().uuidString)" TopShelfStore.write(TopShelfSnapshot(sections: []), group: unbekannt) let gelesen = TopShelfStore.read(group: unbekannt) #expect(gelesen?.sections.isEmpty ?? true) diff --git a/Packages/HaloKit/Tests/MediaKitTests/VideoResolutionTests.swift b/Packages/FlaroKit/Tests/MediaKitTests/VideoResolutionTests.swift similarity index 100% rename from Packages/HaloKit/Tests/MediaKitTests/VideoResolutionTests.swift rename to Packages/FlaroKit/Tests/MediaKitTests/VideoResolutionTests.swift diff --git a/README.md b/README.md index 66d98e9..be33e70 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# Halo +# Flaro > Ein nativer Jellyfin-Client für iPhone, iPad, Mac und Apple TV — dunkel, > mit ausfahrbarer Seitenleiste und Offline-Downloads. -Der Name ist ein Platzhalter. Siehe [`Halo/Branding.swift`](Halo/Branding.swift). +Der Name ist ein Platzhalter. Siehe [`Flaro/Branding.swift`](Flaro/Branding.swift). ## Stand @@ -11,7 +11,7 @@ Alle sechs Phasen sind gebaut. Anmeldung, Blättern und Wiedergabe laufen am echten Server; der Rest ist unterschiedlich weit geprüft. Der Fahrplan steht in -[`docs/superpowers/specs/2026-09-11-halo-design.md`](docs/superpowers/specs/2026-09-11-halo-design.md). +[`docs/superpowers/specs/2026-09-11-flaro-design.md`](docs/superpowers/specs/2026-09-11-flaro-design.md). | Phase | Inhalt | Stand | |---|---|---| @@ -46,7 +46,7 @@ eingecheckt — das erspart Konflikte in der `pbxproj`. ```bash xcodegen generate -open Halo.xcodeproj +open Flaro.xcodeproj ``` > **Wichtig: Xcode vorher beenden.** @@ -63,16 +63,16 @@ open Halo.xcodeproj > Ob der Build aktuell ist, steht in der App unter **Einstellungen → > Diagnose** als Build-Datum. -Der Code liegt zum größten Teil in lokalen Packages unter `Packages/HaloKit`. +Der Code liegt zum größten Teil in lokalen Packages unter `Packages/FlaroKit`. Das App-Target enthält nur Oberfläche und Navigation. | Modul | Aufgabe | Hängt ab von | |---|---|---| | `MediaKit` | Die serverneutrale Grundschicht: Typen und Rechnungen, die für jeden Server gelten. | — | | `JellyfinKit` | Anmeldung, Endpunkte, Modelle. Kein SwiftUI. | `MediaKit` | -| `HaloDesign` | Farben, Typografie, wiederkehrende Bausteine. | — | -| `HaloPlayback` | Wiedergabe-Abstraktion und die Entscheidung DirectPlay/Umpacken/Transcode. | `MediaKit` | -| `HaloDownloads` | Warteschlange, Hintergrundübertragung, Offline-Bestand. | `MediaKit` | +| `FlaroDesign` | Farben, Typografie, wiederkehrende Bausteine. | — | +| `FlaroPlayback` | Wiedergabe-Abstraktion und die Entscheidung DirectPlay/Umpacken/Transcode. | `MediaKit` | +| `FlaroDownloads` | Warteschlange, Hintergrundübertragung, Offline-Bestand. | `MediaKit` | ### Warum umgepackt wird @@ -95,7 +95,7 @@ Oberfläche bleibt unberührt. Die Logik-Tests laufen ohne Simulator, deshalb in Sekunden: ```bash -cd Packages/HaloKit && swift test +cd Packages/FlaroKit && swift test ``` Die wichtigste Testdatei ist `PlaybackDeciderTests` — sie hält fest, welche @@ -136,10 +136,10 @@ Irgendwann war das Upload-Kontingent des Tages aufgebraucht — für eine Frage, die keine Oberfläche braucht. ```bash -cd Packages/HaloKit -swift run HaloProbe --jellyfin # Welche Leiter baut der Server? -swift run HaloProbe --spuren # Welche Tonspuren hat ein Titel? -swift run HaloProbe --playlist # Was steht in dieser HLS-Leiter? +cd Packages/FlaroKit +swift run FlaroProbe --jellyfin # Welche Leiter baut der Server? +swift run FlaroProbe --spuren # Welche Tonspuren hat ein Titel? +swift run FlaroProbe --playlist # Was steht in dieser HLS-Leiter? ``` `--jellyfin` und `--spuren` fragen nach Benutzername und Passwort und legen den diff --git a/Tools/JellyfinStub/server.py b/Tools/JellyfinStub/server.py index a957ca3..6e6a3c2 100644 --- a/Tools/JellyfinStub/server.py +++ b/Tools/JellyfinStub/server.py @@ -139,7 +139,7 @@ def item(key, name, kind, overview, *, resume=None, series=None): # # Zum Prüfen des Nachladens: mit einer Handvoll Titeln passt alles auf eine # Seite, und ob überhaupt eine zweite angefordert wird, sieht man nie. -FUELLUNG = int(os.environ.get("HALO_STUB_LIBRARY", "0")) +FUELLUNG = int(os.environ.get("FLARO_STUB_LIBRARY", "0")) def fuellmaterial(kind): return [ diff --git a/Tools/TVBrandAssets.py b/Tools/TVBrandAssets.py index a2b5e2b..c77448a 100644 --- a/Tools/TVBrandAssets.py +++ b/Tools/TVBrandAssets.py @@ -16,7 +16,7 @@ import math from pathlib import Path from PIL import Image, ImageDraw, ImageFilter -WURZEL = Path("HaloTV/Resources/TVAssets.xcassets") +WURZEL = Path("FlaroTV/Resources/TVAssets.xcassets") MARKE = WURZEL / "App Icon & Top Shelf Image.brandassets" # Gemessen am gerenderten iOS-Icon, Spalte neben dem Dreieck bzw. durch es. diff --git a/Tools/WindowShot/main.swift b/Tools/WindowShot/main.swift index 913ab65..86136b0 100644 --- a/Tools/WindowShot/main.swift +++ b/Tools/WindowShot/main.swift @@ -8,7 +8,7 @@ import Foundation // Gebaut, weil es sonst keinen Weg dorthin gibt: das System-Python bringt // PyObjC nicht mehr mit, und `screencapture` kennt nur Kennungen, keine Namen. -let name = CommandLine.arguments.dropFirst().first ?? "Halo" +let name = CommandLine.arguments.dropFirst().first ?? "Flaro" /// Zweites Argument: die Prozesskennung. Nötig, sobald zwei Bauarten /// desselben Programms gleichzeitig laufen — sonst trifft es die falsche. let wantedPID = CommandLine.arguments.dropFirst(2).first.flatMap(Int.init) diff --git a/docs/superpowers/specs/2026-10-03-halo-server-design.md b/docs/superpowers/specs/2026-10-03-halo-server-design.md index c94d1ad..f11146e 100644 --- a/docs/superpowers/specs/2026-10-03-halo-server-design.md +++ b/docs/superpowers/specs/2026-10-03-halo-server-design.md @@ -1,3 +1,12 @@ +> **Nachtrag vom selben Tag: das Projekt heisst inzwischen Flaro.** +> +> „Halo" liess sich in App Store Connect nicht eintragen — der Name ist von +> KaVo-ähnlichen Fällen abgesehen vor allem eine Marke von Microsoft, und der +> Upload scheiterte daran. Dieses Dokument bleibt im Wortlaut stehen: es ist +> ein Protokoll der Entscheidungen, nicht ihre Fortschreibung. Überall, wo +> hier „Halo" steht, heisst es heute „Flaro"; die Begründungen gelten +> unverändert. + # Halo-Server — Entwurf *3. Oktober 2026* diff --git a/project.yml b/project.yml index 8f39928..9cd12b4 100644 --- a/project.yml +++ b/project.yml @@ -1,4 +1,4 @@ -name: Halo +name: Flaro options: bundleIdPrefix: com.scarriffleservices @@ -19,11 +19,11 @@ settings: SWIFT_STRICT_CONCURRENCY: complete packages: - HaloKit: - path: Packages/HaloKit + FlaroKit: + path: Packages/FlaroKit targets: - Halo: + Flaro: type: application platform: iOS # Ausdrücklich aufgeschrieben, statt Xcode raten zu lassen. @@ -34,39 +34,39 @@ targets: # auflösen. Hier steht es im Projekt und überlebt jedes Neuerzeugen. scheme: testTargets: - - HaloUITests + - FlaroUITests sources: # Das Icon ist ein Bündel, also technisch ein Ordner. Ohne diesen # Ausschluss läuft XcodeGen hinein und trägt icon.json und play.svg # einzeln ein — dann fehlt das Icon, ohne dass irgendwo ein Fehler # auftaucht. # - - path: Halo + - path: Flaro excludes: - "AppIcon.icon" - - path: Halo/AppIcon.icon + - path: Flaro/AppIcon.icon type: file dependencies: - target: DownloadWidget # Die neutrale Grundschicht. Ausdrücklich genannt, obwohl sie über # JellyfinKit ohnehin im Baum hängt: die App soll sie direkt benutzen und # nicht davon abhängen, dass ein bestimmter Server sie mitschleppt. - - package: HaloKit + - package: FlaroKit product: MediaKit # Der Server. Steht auf der neutralen Schicht, nicht darin — kommt ein # zweiter dazu, steht er daneben. - - package: HaloKit + - package: FlaroKit product: JellyfinKit - - package: HaloKit - product: HaloDesign - - package: HaloKit - product: HaloPlayback - - package: HaloKit - product: HaloDownloads + - package: FlaroKit + product: FlaroDesign + - package: FlaroKit + product: FlaroPlayback + - package: FlaroKit + product: FlaroDownloads settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.halo - PRODUCT_NAME: Halo + PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flaro + PRODUCT_NAME: Flaro # Verweist auf AppIcon.icon. Der frühere leere AppIcon.appiconset im # Asset-Katalog ist entfernt — zwei Dinge namens AppIcon, und Xcode # nimmt das falsche. @@ -75,9 +75,9 @@ targets: TARGETED_DEVICE_FAMILY: "1,2" CODE_SIGN_STYLE: Automatic info: - path: Halo/Info.plist + path: Flaro/Info.plist properties: - CFBundleDisplayName: Halo + CFBundleDisplayName: Flaro UILaunchScreen: UIColorName: LaunchBackground @@ -136,25 +136,25 @@ targets: # Freigabe. Fehlt der Text, verweigert das System sie kommentarlos und # die Serversuche findet dauerhaft nichts. NSLocalNetworkUsageDescription: >- - Halo benötigt Zugriff auf das lokale Netzwerk, um deinen + Flaro benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen und Medien direkt von ihm zu laden. # Die Fassung für den Fernseher. # # **Eigene Ansichten, geteilte Modelle.** Die Quellenpfade sind die ganze - # Aussage dieses Blocks: `HaloTV/` gehört nur hierher, `Halo/Session/` + # Aussage dieses Blocks: `FlaroTV/` gehört nur hierher, `Flaro/Session/` # gehört beiden. Keine Datei muss dafür umziehen — XcodeGen nimmt Pfade je # Ziel, und die iOS-Fassung sieht von der ganzen Sache nichts. - HaloTV: + FlaroTV: type: application platform: tvOS - # Aus demselben Grund ausgeschrieben wie bei Halo — siehe dort. + # Aus demselben Grund ausgeschrieben wie bei Flaro — siehe dort. scheme: {} sources: # Die eigenen Ansichten. - - path: HaloTV + - path: FlaroTV # Die Modelle. - - path: Halo/Session + - path: Flaro/Session excludes: # Ein Fernseher dreht sich nicht. - "OrientationLock.swift" @@ -169,60 +169,60 @@ targets: # aufruft. Beide sind reine Übersetzer ohne UIKit. # Reines Modell trotz des Ordnernamens: LoadState, ErrorText und # MediaRoute liegen zusammen in einer Datei. - - path: Halo/Views/LoadState.swift + - path: Flaro/Views/LoadState.swift type: file # Der Player. **Ohne PlayerView** — die zeichnet auf iOS das Bild und # alle Bedienelemente selbst; auf dem Fernseher macht das Apples # AVPlayerViewController. Das Modell darunter ist dasselbe. - - path: Halo/Views/Player/PlayerModel.swift + - path: Flaro/Views/Player/PlayerModel.swift type: file - - path: Halo/Views/Player/VideoSurface.swift + - path: Flaro/Views/Player/VideoSurface.swift type: file - - path: Halo/Branding.swift + - path: Flaro/Branding.swift type: file # Die Texte. Dieselben Schlüssel, dieselben Übersetzungen. - - path: Halo/Resources/Localizable.xcstrings + - path: Flaro/Resources/Localizable.xcstrings type: file dependencies: - - package: HaloKit + - package: FlaroKit product: MediaKit - - package: HaloKit + - package: FlaroKit product: JellyfinKit - - package: HaloKit - product: HaloDesign - - package: HaloKit - product: HaloPlayback + - package: FlaroKit + product: FlaroDesign + - package: FlaroKit + product: FlaroPlayback # Nur damit `PlayerModel` den Typ nennen darf. Angelegt wird nie einer: - # HaloTVApp reicht `nil` durch, und damit gibt es auf tvOS auch keinen + # FlaroTVApp reicht `nil` durch, und damit gibt es auf tvOS auch keinen # ModelContainer und kein SwiftData. - - package: HaloKit - product: HaloDownloads - - target: HaloTopShelf + - package: FlaroKit + product: FlaroDownloads + - target: FlaroTopShelf settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.halo.tv - PRODUCT_NAME: Halo + PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flaro.tv + PRODUCT_NAME: Flaro # Nur Apple TV. TARGETED_DEVICE_FAMILY: "3" # Kein AppIcon.icon: Icon Composer deckt iOS, macOS und watchOS ab, # tvOS nicht. Der Name zeigt auf die Brand-Assets-Gruppe im Katalog, - # und der Katalog liegt unter HaloTV/Resources. + # und der Katalog liegt unter FlaroTV/Resources. ASSETCATALOG_COMPILER_APPICON_NAME: "App Icon & Top Shelf Image" CODE_SIGN_STYLE: Automatic - CODE_SIGN_ENTITLEMENTS: HaloTV/Halo.entitlements + CODE_SIGN_ENTITLEMENTS: FlaroTV/Flaro.entitlements info: - path: HaloTV/Info.plist + path: FlaroTV/Info.plist properties: - CFBundleDisplayName: Halo + CFBundleDisplayName: Flaro UIUserInterfaceStyle: Dark ITSAppUsesNonExemptEncryption: false # Über dieses Schema schickt das Regal zurück in die App. Ohne den # Eintrag kennt tvOS es nicht, und ein Druck auf ein Plakat tut # nichts — ohne Fehler, ohne Hinweis. CFBundleURLTypes: - - CFBundleURLName: com.scarriffleservices.halo.topshelf + - CFBundleURLName: com.scarriffleservices.flaro.topshelf CFBundleURLSchemes: - - halo + - flaro # Was hier **nicht** steht, und warum: # # UILaunchScreen — tvOS hat kein Startbild dieser Art. @@ -234,7 +234,7 @@ targets: NSAppTransportSecurity: NSAllowsLocalNetworking: true NSLocalNetworkUsageDescription: >- - Halo benötigt Zugriff auf das lokale Netzwerk, um deinen + Flaro benötigt Zugriff auf das lokale Netzwerk, um deinen Jellyfin-Server zu erreichen. # Das Regal über der App im tvOS-Menü. @@ -243,25 +243,25 @@ targets: # läuft — und mit weniger als einer Sekunde Zeit. Sie liest deshalb nur eine # Datei aus der App-Gruppe, die die App beim Laden der Startseite ablegt: # kein Netz, kein Schlüsselbund, kein Blättern. - HaloTopShelf: + FlaroTopShelf: type: tv-app-extension platform: tvOS sources: - - path: HaloTopShelf + - path: FlaroTopShelf dependencies: - - package: HaloKit + - package: FlaroKit product: MediaKit settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.halo.tv.topshelf - PRODUCT_NAME: HaloTopShelf + PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flaro.tv.topshelf + PRODUCT_NAME: FlaroTopShelf CODE_SIGN_STYLE: Automatic - CODE_SIGN_ENTITLEMENTS: HaloTopShelf/HaloTopShelf.entitlements + CODE_SIGN_ENTITLEMENTS: FlaroTopShelf/FlaroTopShelf.entitlements SKIP_INSTALL: YES info: - path: HaloTopShelf/Info.plist + path: FlaroTopShelf/Info.plist properties: - CFBundleDisplayName: Halo + CFBundleDisplayName: Flaro # **Die Erweiterung hat ihre eigene Info.plist.** # # Und damit ihre eigene Transportsicherheit: die Ausnahme in der @@ -278,17 +278,17 @@ targets: # Nur zum Hinsehen: fährt die App mit der Attrappe in einen bestimmten # Zustand und hält sie dort an, damit von außen fotografiert werden kann. # Kein Teil der App, wird nie mitgeliefert. - HaloUITests: + FlaroUITests: type: bundle.ui-testing platform: iOS sources: - - HaloUITests + - FlaroUITests dependencies: - - target: Halo + - target: Flaro settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.halo.uitests - TEST_TARGET_NAME: Halo + PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flaro.uitests + TEST_TARGET_NAME: Flaro TARGETED_DEVICE_FAMILY: "1,2" CODE_SIGN_STYLE: Automatic GENERATE_INFOPLIST_FILE: YES @@ -303,13 +303,13 @@ targets: sources: - path: DownloadWidget dependencies: - - package: HaloKit - product: HaloDownloads - - package: HaloKit - product: HaloDesign + - package: FlaroKit + product: FlaroDownloads + - package: FlaroKit + product: FlaroDesign settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.halo.downloadwidget + PRODUCT_BUNDLE_IDENTIFIER: com.scarriffleservices.flaro.downloadwidget PRODUCT_NAME: DownloadWidget CODE_SIGN_STYLE: Automatic SKIP_INSTALL: YES @@ -320,6 +320,6 @@ targets: info: path: DownloadWidget/Info.plist properties: - CFBundleDisplayName: Halo Downloads + CFBundleDisplayName: Flaro Downloads NSExtension: NSExtensionPointIdentifier: com.apple.widgetkit-extension diff --git a/server/Dockerfile b/server/Dockerfile index a600957..14d8cdf 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -14,17 +14,17 @@ WORKDIR /src # weil eine Liste bei jedem neuen Projekt vergessen wird — und dann scheitert # erst der Bau im Container, nicht der auf dem Arbeitsrechner. COPY --parents src/*/*.csproj ./ -RUN dotnet restore src/Halo.Api/Halo.Api.csproj -a $TARGETARCH +RUN dotnet restore src/Flaro.Api/Flaro.Api.csproj -a $TARGETARCH COPY . . -RUN dotnet publish src/Halo.Api/Halo.Api.csproj -a $TARGETARCH -c Release -o /app --no-restore +RUN dotnet publish src/Flaro.Api/Flaro.Api.csproj -a $TARGETARCH -c Release -o /app --no-restore FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime # ffmpeg und die Treiber für Hardware-Beschleunigung. # -# **Als eigenes Programm, nicht als Bibliothek.** Halo ruft es als Prozess +# **Als eigenes Programm, nicht als Bibliothek.** Flaro ruft es als Prozess # auf; damit bleiben die Lizenzen getrennt (ffmpeg steht unter GPL) und ein # Absturz dort nimmt den Server nicht mit. # @@ -57,9 +57,9 @@ RUN mkdir -p /config /cache /transcode && chown -R 1000:1000 /config /cache /tra # braucht weder ein Startskript noch root im Container. USER 1000:1000 -ENV HALO_DATA=/config \ - HALO_CACHE=/cache \ - HALO_TRANSCODE=/transcode \ +ENV FLARO_DATA=/config \ + FLARO_CACHE=/cache \ + FLARO_TRANSCODE=/transcode \ ASPNETCORE_URLS=http://+:8080 \ DOTNET_gcServer=0 @@ -69,4 +69,4 @@ COPY --from=build /app . EXPOSE 8080 VOLUME ["/config", "/cache", "/transcode"] -ENTRYPOINT ["dotnet", "Halo.Api.dll"] +ENTRYPOINT ["dotnet", "Flaro.Api.dll"] diff --git a/server/Flaro.slnx b/server/Flaro.slnx new file mode 100644 index 0000000..d866a72 --- /dev/null +++ b/server/Flaro.slnx @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/server/Halo.slnx b/server/Halo.slnx deleted file mode 100644 index 887226e..0000000 --- a/server/Halo.slnx +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/server/README.md b/server/README.md index 8a042bb..70e89f3 100644 --- a/server/README.md +++ b/server/README.md @@ -1,4 +1,4 @@ -# Halo-Server +# Flaro-Server Ein Medienserver, der dieselben Dinge kann wie Jellyfin — und zwei, die dort nicht gehen. @@ -7,7 +7,7 @@ nicht gehen. Metadaten — und **Download mit allen Ton- und Textspuren**, der Grund für dieses Projekt. Was noch fehlt: Streamen mit Umrechnen (HLS), Fortschritt und Gesehen-Status, Live TV. Der Entwurf samt Begründungen steht -in [`docs/superpowers/specs/2026-10-03-halo-server-design.md`](../docs/superpowers/specs/2026-10-03-halo-server-design.md). +in [`docs/superpowers/specs/2026-10-03-flaro-server-design.md`](../docs/superpowers/specs/2026-10-03-flaro-server-design.md). ## Warum @@ -35,13 +35,13 @@ Administratorkonto an. Danach verschliesst sich dieser Weg von selbst. | Variable | Bedeutung | Voreinstellung im Image | |---|---|---| -| `HALO_DATA` | Datenbank und Einstellungen. **Das Einzige, was gesichert werden muss.** | `/config` | -| `HALO_CACHE` | Bilder und Zwischenstände. Darf verlorengehen. | `/cache` | -| `HALO_TRANSCODE` | Kratzplatz fürs Umrechnen. Am besten auf eine SSD. | `/transcode` | +| `FLARO_DATA` | Datenbank und Einstellungen. **Das Einzige, was gesichert werden muss.** | `/config` | +| `FLARO_CACHE` | Bilder und Zwischenstände. Darf verlorengehen. | `/cache` | +| `FLARO_TRANSCODE` | Kratzplatz fürs Umrechnen. Am besten auf eine SSD. | `/transcode` | | `ASPNETCORE_URLS` | Worauf gelauscht wird | `http://+:8080` | | `TZ` | Zeitzone | `UTC` | -| `HALO_RATELIMIT_SIGNIN` | Anmeldeversuche je Minute und Adresse | `10` | -| `HALO_RATELIMIT_POLLING` | Nachfragen je Minute und Adresse | `120` | +| `FLARO_RATELIMIT_SIGNIN` | Anmeldeversuche je Minute und Adresse | `10` | +| `FLARO_RATELIMIT_POLLING` | Nachfragen je Minute und Adresse | `120` | **Medienpfade stehen hier nicht.** Sie werden in der Oberfläche als Bibliotheken angelegt und liegen in der Datenbank — sonst müsste der Container @@ -60,7 +60,7 @@ diese Meldung stünde dort nur „unable to open database file". ### Hardware fürs Umrechnen -`/dev/dri` durchreichen — VAAPI deckt Intel wie AMD ab. Fehlt es, rechnet Halo +`/dev/dri` durchreichen — VAAPI deckt Intel wie AMD ab. Fehlt es, rechnet Flaro in Software; das ist langsamer, aber es läuft. **Zum blossen Umpacken wird gar keine Hardware gebraucht**, und das ist der Weg, der den meisten Beständen hilft. @@ -69,7 +69,7 @@ hilft. ```bash dotnet test # 293 Tests -dotnet run --project src/Halo.Api # auf http://localhost:5000 +dotnet run --project src/Flaro.Api # auf http://localhost:5000 ``` Ohne gesetzte Variablen legt der Server `data/`, `cache/` und `transcode/` @@ -77,10 +77,10 @@ neben dem Programm an. ```bash # Image bauen, beide Architekturen -docker buildx build --platform linux/amd64,linux/arm64 -t halo:dev . +docker buildx build --platform linux/amd64,linux/arm64 -t flaro:dev . # Eine Migration nach einer Modelländerung -dotnet ef migrations add --project src/Halo.Data --startup-project src/Halo.Api --output-dir Migrations +dotnet ef migrations add --project src/Flaro.Data --startup-project src/Flaro.Api --output-dir Migrations ``` Migrationen wandern **beim Start** des Servers, nicht über ein eigenes @@ -90,13 +90,13 @@ Kommando. | Projekt | Aufgabe | |---|---| -| `Halo.Core` | Domäne: Benutzer, Anmeldung, Bibliotheken. Kennt kein HTTP und keine Datenbank | -| `Halo.Data` | EF Core, SQLite, Migrationen | -| `Halo.Media` | ffprobe und ffmpeg. Als eigene Prozesse, nicht eingebunden | -| `Halo.Metadata` | TMDB und die Bildablage. Hinter einer Schnittstelle, damit alles andere ohne Netz prüfbar bleibt | -| `Halo.Api` | Endpunkte, Authentifizierung, Auslieferung der Oberfläche | +| `Flaro.Core` | Domäne: Benutzer, Anmeldung, Bibliotheken. Kennt kein HTTP und keine Datenbank | +| `Flaro.Data` | EF Core, SQLite, Migrationen | +| `Flaro.Media` | ffprobe und ffmpeg. Als eigene Prozesse, nicht eingebunden | +| `Flaro.Metadata` | TMDB und die Bildablage. Hinter einer Schnittstelle, damit alles andere ohne Netz prüfbar bleibt | +| `Flaro.Api` | Endpunkte, Authentifizierung, Auslieferung der Oberfläche | -Was sicherheitsrelevant ist, liegt in `Halo.Core` — dort ist es ohne Server +Was sicherheitsrelevant ist, liegt in `Flaro.Core` — dort ist es ohne Server prüfbar, und die Tests dazu sind die, die am meisten wert sind. ### Zwei Entscheidungen, die beim Lesen Fragen aufwerfen @@ -133,7 +133,7 @@ und ändert sich dabei. ### Metadaten -**Halo bringt einen TMDB-Zugang mit** — es läuft ohne Einrichtung. Wer lieber +**Flaro bringt einen TMDB-Zugang mit** — es läuft ohne Einrichtung. Wer lieber seinen eigenen benutzt, hinterlegt ihn unter `PUT /api/v1/settings/metadata`; der sticht den mitgelieferten. Abgefragt werden kann nur, *ob* einer da ist und ob es der mitgelieferte ist — der Zugang selbst kommt nie zurück. @@ -144,8 +144,8 @@ in der Adresse, und kann nur lesen) oder der ältere **API Key**. Die Sprache steht auf `en-US` und wird in denselben Einstellungen umgestellt. -Bilder werden einmal heruntergeladen und liegen unter `HALO_CACHE`. Die App -holt sie bei Halo, nicht bei TMDB: sonst bräuchte sie dafür Internet, und TMDB +Bilder werden einmal heruntergeladen und liegen unter `FLARO_CACHE`. Die App +holt sie bei Flaro, nicht bei TMDB: sonst bräuchte sie dafür Internet, und TMDB erführe, wer wann was ansieht. **Der Hinweis auf TMDB ist Pflicht, keine Höflichkeit.** Deren Bedingungen @@ -182,12 +182,12 @@ geschehen. **Umgepackt wird auf die Platte, nicht in die Antwort.** Der bequeme Weg wäre, ffmpeg unmittelbar durchzureichen — dann kennt aber niemand die Länge im Voraus: kein Fortschrittsbalken, kein Fortsetzen, kein Springen. Dafür gibt es -`HALO_TRANSCODE`. +`FLARO_TRANSCODE`. | Variable | Bedeutung | Voreinstellung | |---|---|---| -| `HALO_FFMPEG` | Pfad zu ffmpeg | `ffmpeg` | -| `HALO_FFPROBE` | Pfad zu ffprobe | `ffprobe` | +| `FLARO_FFMPEG` | Pfad zu ffmpeg | `ffmpeg` | +| `FLARO_FFPROBE` | Pfad zu ffprobe | `ffprobe` | ## Zuordnung korrigieren @@ -213,7 +213,7 @@ Nur Administratoren, denn es ändert die Mediathek für alle. ## Beiwerk im Medienordner -Mit `HALO_WRITE_SIDECARS=true` legt Halo Plakate, Hintergründe und +Mit `FLARO_WRITE_SIDECARS=true` legt Flaro Plakate, Hintergründe und NFO-Dateien neben den Film: ``` @@ -230,20 +230,20 @@ Serien bekommen `tvshow.nfo` eine Ebene über dem Staffelordner, Folgen je eine NFO und ein Standbild neben der Datei. **Standardmässig aus**, und die Medieneinhängung muss dafür ohne `:ro` -auskommen. Was Halo schreiben darf, steht als Erlaubnisliste im Code +auskommen. Was Flaro schreiben darf, steht als Erlaubnisliste im Code (`SidecarNames`): alles andere wird abgelehnt, und Mediendateien werden nie geändert, umbenannt oder gelöscht. Transcode-Zwischenstände kommen nie in -einen Medienordner — die liegen unter `HALO_TRANSCODE`. +einen Medienordner — die liegen unter `FLARO_TRANSCODE`. **Gelesen wird auch ohne Schreiberlaubnis.** Eine vorhandene NFO ist eine -Zuordnung, die jemand getroffen hat; Halo nimmt sie und sucht gar nicht erst. +Zuordnung, die jemand getroffen hat; Flaro nimmt sie und sucht gar nicht erst. Dasselbe gilt für Ordnernamen der Form `Der Untergang (2004) [tmdbid-613]` — -Halo liest sie, benennt aber **nichts** um. Umbenennen ist der einzige +Flaro liest sie, benennt aber **nichts** um. Umbenennen ist der einzige Eingriff, der eine Bibliothek wirklich zerlegen kann. | Variable | Bedeutung | Voreinstellung | |---|---|---| -| `HALO_WRITE_SIDECARS` | Beiwerk in den Medienordner schreiben | `false` | +| `FLARO_WRITE_SIDECARS` | Beiwerk in den Medienordner schreiben | `false` | ## Anmeldung am Fernseher diff --git a/server/docker-compose.example.yml b/server/docker-compose.example.yml index 4f690b2..24c0d36 100644 --- a/server/docker-compose.example.yml +++ b/server/docker-compose.example.yml @@ -4,9 +4,9 @@ # Form ist bewährt, die Werte sind es nur dort. services: - halo: - image: ghcr.io/scarriffle/halo:latest - container_name: halo + flaro: + image: ghcr.io/scarriffle/flaro:latest + container_name: flaro restart: unless-stopped ports: @@ -27,7 +27,7 @@ services: # # Der Gewinn: dein Bestand wird tragbar. Ein Umzug, ein Neuaufbau der # Datenbank oder ein zweiter Server finden Zuordnungen und Bilder wieder. - - HALO_WRITE_SIDECARS=true + - FLARO_WRITE_SIDECARS=true # Wem die Dateien auf dem Wirt gehören. 1000:1000 ist die Voreinstellung # im Image; wer andere Kennungen hat, trägt sie hier ein — `id -u` und @@ -36,10 +36,10 @@ services: volumes: # Datenbank und Einstellungen. **Das Einzige, was gesichert werden muss.** - - /volume4/CRITICAL/Docker/Halo/config:/config + - /volume4/CRITICAL/Docker/Flaro/config:/config # Bilder und Zwischenstände. Darf verlorengehen, baut sich neu auf. - - /volume4/CRITICAL/Docker/Halo/cache:/cache + - /volume4/CRITICAL/Docker/Flaro/cache:/cache # Kratzplatz fürs Umrechnen. Am besten auf eine SSD und nicht dorthin, # wo die Medien liegen — hier wird viel und kurzlebig geschrieben. @@ -47,9 +47,9 @@ services: # Die Medien. # - # **Ohne `:ro`, weil HALO_WRITE_SIDECARS oben an ist.** Wer das nicht + # **Ohne `:ro`, weil FLARO_WRITE_SIDECARS oben an ist.** Wer das nicht # will, setzt die Variable auf `false` und hängt wieder mit `:ro` ein — - # dann bleiben Bilder und Metadaten nur in Halos Cache, und der Bestand + # dann bleiben Bilder und Metadaten nur in Flaros Cache, und der Bestand # ist gegen jeden Zugriff des Servers geschützt. # # Transcode-Zwischenstände kommen hier **nie** her; die liegen auf dem @@ -60,7 +60,7 @@ services: - /volume4/MYFLIX:/media # Hardware fürs Umrechnen. Intel wie AMD laufen über VAAPI an derselben - # Stelle. Fehlt die Zeile, rechnet Halo in Software — langsamer, aber es + # Stelle. Fehlt die Zeile, rechnet Flaro in Software — langsamer, aber es # läuft. Zum blossen Umpacken wird sie ohnehin nicht gebraucht. devices: - /dev/dri:/dev/dri diff --git a/server/src/Halo.Api/Auth/TokenAuthentication.cs b/server/src/Flaro.Api/Auth/TokenAuthentication.cs similarity index 91% rename from server/src/Halo.Api/Auth/TokenAuthentication.cs rename to server/src/Flaro.Api/Auth/TokenAuthentication.cs index 8df59d7..6104973 100644 --- a/server/src/Halo.Api/Auth/TokenAuthentication.cs +++ b/server/src/Flaro.Api/Auth/TokenAuthentication.cs @@ -1,12 +1,12 @@ using System.Security.Claims; using System.Text.Encodings.Web; -using Halo.Core.Auth; -using Halo.Data; +using Flaro.Core.Auth; +using Flaro.Data; using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -namespace Halo.Api.Auth; +namespace Flaro.Api.Auth; /// Weist Anfragen über ein Geräte-Token aus. /// @@ -26,11 +26,11 @@ public sealed class TokenAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, - HaloContext db, + FlaroContext db, TimeProvider clock) : AuthenticationHandler(options, logger, encoder) { - public const string SchemeName = "HaloToken"; - public const string DeviceTokenIdClaim = "halo:device"; + public const string SchemeName = "FlaroToken"; + public const string DeviceTokenIdClaim = "flaro:device"; protected override async Task HandleAuthenticateAsync() { @@ -66,14 +66,14 @@ public sealed class TokenAuthenticationHandler( new Claim(ClaimTypes.NameIdentifier, benutzer.Id.ToString()), new Claim(ClaimTypes.Name, benutzer.UserName), new Claim(DeviceTokenIdClaim, eintrag.Id.ToString()), - .. benutzer.IsAdministrator ? new[] { new Claim(ClaimTypes.Role, HaloRoles.Administrator) } : [], + .. benutzer.IsAdministrator ? new[] { new Claim(ClaimTypes.Role, FlaroRoles.Administrator) } : [], ], SchemeName); return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(ausweis), SchemeName)); } } -public static class HaloRoles +public static class FlaroRoles { public const string Administrator = "administrator"; } diff --git a/server/src/Halo.Api/Endpoints/AuthEndpoints.cs b/server/src/Flaro.Api/Endpoints/AuthEndpoints.cs similarity index 89% rename from server/src/Halo.Api/Endpoints/AuthEndpoints.cs rename to server/src/Flaro.Api/Endpoints/AuthEndpoints.cs index 8c79be6..4ee009f 100644 --- a/server/src/Halo.Api/Endpoints/AuthEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/AuthEndpoints.cs @@ -1,11 +1,11 @@ using System.Security.Claims; -using Halo.Api.Auth; -using Halo.Core.Auth; -using Halo.Core.Users; -using Halo.Data; +using Flaro.Api.Auth; +using Flaro.Core.Auth; +using Flaro.Core.Users; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class AuthEndpoints { @@ -23,7 +23,7 @@ public static class AuthEndpoints public static void MapAuth(this IEndpointRouteBuilder app) { app.MapPost("/api/v1/auth/login", async ( - LoginRequest anfrage, HaloContext db, TimeProvider clock) => + LoginRequest anfrage, FlaroContext db, TimeProvider clock) => { var name = anfrage.UserName.Trim(); var benutzer = await db.Users.FirstOrDefaultAsync(u => u.UserName == name); @@ -46,9 +46,9 @@ public static class AuthEndpoints var antwort = await TokenAusstellen(db, clock, benutzer, anfrage.DeviceName, anfrage.DeviceId); await db.SaveChangesAsync(); return Results.Ok(antwort); - }).AllowAnonymous().RequireRateLimiting(HaloRateLimits.SignIn); + }).AllowAnonymous().RequireRateLimiting(FlaroRateLimits.SignIn); - app.MapPost("/api/v1/auth/logout", async (ClaimsPrincipal wer, HaloContext db, TimeProvider clock) => + app.MapPost("/api/v1/auth/logout", async (ClaimsPrincipal wer, FlaroContext db, TimeProvider clock) => { if (!Guid.TryParse(wer.FindFirstValue(TokenAuthenticationHandler.DeviceTokenIdClaim), out var id)) return Results.Unauthorized(); @@ -67,7 +67,7 @@ public static class AuthEndpoints ? Results.Ok(new AuthenticatedUser( id, wer.FindFirstValue(ClaimTypes.Name) ?? "", - wer.IsInRole(HaloRoles.Administrator))) + wer.IsInRole(FlaroRoles.Administrator))) : Results.Unauthorized()) .RequireAuthorization(); @@ -77,7 +77,7 @@ public static class AuthEndpoints /// Anmelden mit einem Code auf dem Bildschirm. private static void MapCodeFlow(IEndpointRouteBuilder app) { - app.MapPost("/api/v1/auth/code", async (CodeRequest anfrage, HaloContext db, TimeProvider clock) => + app.MapPost("/api/v1/auth/code", async (CodeRequest anfrage, FlaroContext db, TimeProvider clock) => { var jetzt = clock.GetUtcNow(); @@ -99,10 +99,10 @@ public static class AuthEndpoints await db.SaveChangesAsync(); return Results.Ok(new CodeResponse(code.Code, abholschein, code.ExpiresAt)); - }).AllowAnonymous().RequireRateLimiting(HaloRateLimits.SignIn); + }).AllowAnonymous().RequireRateLimiting(FlaroRateLimits.SignIn); app.MapGet("/api/v1/auth/code/{handle}", async ( - string handle, HaloContext db, TimeProvider clock) => + string handle, FlaroContext db, TimeProvider clock) => { var jetzt = clock.GetUtcNow(); var fingerabdruck = Secrets.Fingerprint(handle); @@ -124,10 +124,10 @@ public static class AuthEndpoints await db.SaveChangesAsync(); return Results.Ok(new CodeState("approved", antwort)); - }).AllowAnonymous().RequireRateLimiting(HaloRateLimits.Polling); + }).AllowAnonymous().RequireRateLimiting(FlaroRateLimits.Polling); app.MapPost("/api/v1/auth/code/{code}", async ( - string code, ClaimsPrincipal wer, HaloContext db, TimeProvider clock) => + string code, ClaimsPrincipal wer, FlaroContext db, TimeProvider clock) => { if (!Guid.TryParse(wer.FindFirstValue(ClaimTypes.NameIdentifier), out var benutzerId)) return Results.Unauthorized(); @@ -144,7 +144,7 @@ public static class AuthEndpoints // Damit am Telefon steht, was man da gerade hereinlässt. return Results.Ok(new { device = offen.DeviceName }); - }).RequireAuthorization().RequireRateLimiting(HaloRateLimits.SignIn); + }).RequireAuthorization().RequireRateLimiting(FlaroRateLimits.SignIn); } /// Stellt ein Geräte-Token aus und löst ein älteres desselben Geräts ab. @@ -155,7 +155,7 @@ public static class AuthEndpoints /// und nach einem Jahr weiss niemand mehr, welches Telefon welches ist. /// private static async Task TokenAusstellen( - HaloContext db, TimeProvider clock, User benutzer, string geräteName, string geräteKennung) + FlaroContext db, TimeProvider clock, User benutzer, string geräteName, string geräteKennung) { var jetzt = clock.GetUtcNow(); var kennung = Kürzen(geräteKennung, 128); diff --git a/server/src/Halo.Api/Endpoints/Contracts.cs b/server/src/Flaro.Api/Endpoints/Contracts.cs similarity index 99% rename from server/src/Halo.Api/Endpoints/Contracts.cs rename to server/src/Flaro.Api/Endpoints/Contracts.cs index 2066aa7..0575885 100644 --- a/server/src/Halo.Api/Endpoints/Contracts.cs +++ b/server/src/Flaro.Api/Endpoints/Contracts.cs @@ -1,4 +1,4 @@ -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; // Was über die Leitung geht. Bewusst eigene Typen und nicht die der Domäne: // sonst wandert jede Umbenennung im Datenmodell ungefragt in die Schnittstelle diff --git a/server/src/Halo.Api/Endpoints/DeliveryEndpoints.cs b/server/src/Flaro.Api/Endpoints/DeliveryEndpoints.cs similarity index 93% rename from server/src/Halo.Api/Endpoints/DeliveryEndpoints.cs rename to server/src/Flaro.Api/Endpoints/DeliveryEndpoints.cs index aa90ad2..892146d 100644 --- a/server/src/Halo.Api/Endpoints/DeliveryEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/DeliveryEndpoints.cs @@ -1,16 +1,16 @@ -using Halo.Core.Delivery; -using Halo.Core.Library; -using Halo.Data; +using Flaro.Core.Delivery; +using Flaro.Core.Library; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class DeliveryEndpoints { public static void MapDelivery(this IEndpointRouteBuilder app) { app.MapGet("/api/v1/files/{id:guid}/delivery", async ( - Guid id, HaloContext db, PackageCoordinator packer) => + Guid id, FlaroContext db, PackageCoordinator packer) => { var gefunden = await Laden(db, id); if (gefunden is not var (datei, plan)) return Results.NotFound(); @@ -19,7 +19,7 @@ public static class DeliveryEndpoints }).RequireAuthorization(); app.MapGet("/api/v1/files/{id:guid}/download", async ( - Guid id, HaloContext db, PackageCoordinator packer) => + Guid id, FlaroContext db, PackageCoordinator packer) => { var gefunden = await Laden(db, id); if (gefunden is not var (datei, plan)) return Results.NotFound(); @@ -64,7 +64,7 @@ public static class DeliveryEndpoints }).RequireAuthorization(); } - private static async Task<(MediaFile Datei, DeliveryPlan Plan)?> Laden(HaloContext db, Guid id) + private static async Task<(MediaFile Datei, DeliveryPlan Plan)?> Laden(FlaroContext db, Guid id) { var datei = await db.MediaFiles.AsNoTracking().FirstOrDefaultAsync(f => f.Id == id); if (datei is null) return null; diff --git a/server/src/Halo.Api/Endpoints/ImageEndpoints.cs b/server/src/Flaro.Api/Endpoints/ImageEndpoints.cs similarity index 95% rename from server/src/Halo.Api/Endpoints/ImageEndpoints.cs rename to server/src/Flaro.Api/Endpoints/ImageEndpoints.cs index 1d8b566..e6e05e2 100644 --- a/server/src/Halo.Api/Endpoints/ImageEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/ImageEndpoints.cs @@ -1,7 +1,7 @@ -using Halo.Metadata; +using Flaro.Metadata; using Microsoft.AspNetCore.Mvc; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class ImageEndpoints { diff --git a/server/src/Halo.Api/Endpoints/LibraryContentEndpoints.cs b/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs similarity index 94% rename from server/src/Halo.Api/Endpoints/LibraryContentEndpoints.cs rename to server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs index 5a6ce4d..11a1472 100644 --- a/server/src/Halo.Api/Endpoints/LibraryContentEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs @@ -1,7 +1,7 @@ -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class LibraryContentEndpoints { @@ -26,7 +26,7 @@ public static class LibraryContentEndpoints /// Je Datei einzeln zu fragen hiesse: fünfzig Titel, fünfzig Abfragen — /// und SQLite führt die nacheinander aus. /// - private static async Task> SpurenZu(HaloContext db, List dateien) + private static async Task> SpurenZu(FlaroContext db, List dateien) { if (dateien.Count == 0) return Array.Empty<(Guid, MediaStreamResponse)>().ToLookup(x => x.Item1, x => x.Item2); @@ -48,7 +48,7 @@ public static class LibraryContentEndpoints private static void MapScan(IEndpointRouteBuilder app) { app.MapPost("/api/v1/libraries/{id:guid}/scan", async ( - Guid id, HaloContext db, ScanCoordinator koordinator) => + Guid id, FlaroContext db, ScanCoordinator koordinator) => { if (!await db.Libraries.AnyAsync(l => l.Id == id)) return Results.NotFound(); @@ -57,7 +57,7 @@ public static class LibraryContentEndpoints : Results.Problem( "Für diese Bibliothek läuft bereits ein Scan.", statusCode: StatusCodes.Status409Conflict); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); app.MapGet("/api/v1/libraries/{id:guid}/scan", (Guid id, ScanCoordinator koordinator) => { @@ -82,7 +82,7 @@ public static class LibraryContentEndpoints private static void MapBrowse(IEndpointRouteBuilder app) { app.MapGet("/api/v1/movies", async ( - HaloContext db, Guid? library, string? search, int skip = 0, int take = 50) => + FlaroContext db, Guid? library, string? search, int skip = 0, int take = 50) => { var abfrage = db.Movies.AsNoTracking(); if (library is { } kennung) abfrage = abfrage.Where(m => m.LibraryId == kennung); @@ -115,7 +115,7 @@ public static class LibraryContentEndpoints }).RequireAuthorization(); app.MapGet("/api/v1/series", async ( - HaloContext db, Guid? library, string? search, int skip = 0, int take = 50) => + FlaroContext db, Guid? library, string? search, int skip = 0, int take = 50) => { var abfrage = db.Series.AsNoTracking(); if (library is { } kennung) abfrage = abfrage.Where(s => s.LibraryId == kennung); @@ -146,7 +146,7 @@ public static class LibraryContentEndpoints return Results.Ok(new Page(items, gesamt)); }).RequireAuthorization(); - app.MapGet("/api/v1/series/{id:guid}/episodes", async (Guid id, HaloContext db, int? season) => + app.MapGet("/api/v1/series/{id:guid}/episodes", async (Guid id, FlaroContext db, int? season) => { if (!await db.Series.AnyAsync(s => s.Id == id)) return Results.NotFound(); diff --git a/server/src/Halo.Api/Endpoints/ManagementEndpoints.cs b/server/src/Flaro.Api/Endpoints/ManagementEndpoints.cs similarity index 88% rename from server/src/Halo.Api/Endpoints/ManagementEndpoints.cs rename to server/src/Flaro.Api/Endpoints/ManagementEndpoints.cs index e7db424..eba52d5 100644 --- a/server/src/Halo.Api/Endpoints/ManagementEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/ManagementEndpoints.cs @@ -1,24 +1,24 @@ -using Halo.Api.Auth; -using Halo.Core.Libraries; -using Halo.Core.Users; -using Halo.Data; +using Flaro.Api.Auth; +using Flaro.Core.Libraries; +using Flaro.Core.Users; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class ManagementEndpoints { public static void MapManagement(this IEndpointRouteBuilder app) { - var benutzer = app.MapGroup("/api/v1/users").RequireAuthorization(HaloPolicies.Administrator); + var benutzer = app.MapGroup("/api/v1/users").RequireAuthorization(FlaroPolicies.Administrator); - benutzer.MapGet("/", async (HaloContext db) => + benutzer.MapGet("/", async (FlaroContext db) => Results.Ok(await db.Users .OrderBy(u => u.UserName) .Select(u => new AuthenticatedUser(u.Id, u.UserName, u.IsAdministrator)) .ToListAsync())); - benutzer.MapPost("/", async (CreateUserRequest anfrage, HaloContext db) => + benutzer.MapPost("/", async (CreateUserRequest anfrage, FlaroContext db) => { var name = anfrage.UserName.Trim(); if (name.Length == 0 || anfrage.Password.Length < 8) @@ -45,14 +45,14 @@ public static class ManagementEndpoints var bibliotheken = app.MapGroup("/api/v1/libraries"); // Lesen darf jeder Angemeldete — ohne die Liste zeigt die App nichts an. - bibliotheken.MapGet("/", async (HaloContext db) => + bibliotheken.MapGet("/", async (FlaroContext db) => Results.Ok(await db.Libraries .OrderBy(l => l.Name) .Select(l => new LibraryResponse(l.Id, l.Name, l.Kind.ToString(), l.Path)) .ToListAsync())) .RequireAuthorization(); - bibliotheken.MapPost("/", async (CreateLibraryRequest anfrage, HaloContext db) => + bibliotheken.MapPost("/", async (CreateLibraryRequest anfrage, FlaroContext db) => { if (!Enum.TryParse(anfrage.Kind, ignoreCase: true, out var art)) return Results.ValidationProblem(new Dictionary @@ -75,16 +75,16 @@ public static class ManagementEndpoints await db.SaveChangesAsync(); return Results.Created($"/api/v1/libraries/{neu.Id}", new LibraryResponse(neu.Id, neu.Name, neu.Kind.ToString(), neu.Path)); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); } } -public static class HaloPolicies +public static class FlaroPolicies { public const string Administrator = "administrator-only"; } -public static class HaloRateLimits +public static class FlaroRateLimits { /// Für alles, was ein Geheimnis prüft. public const string SignIn = "sign-in"; diff --git a/server/src/Halo.Api/Endpoints/MatchEndpoints.cs b/server/src/Flaro.Api/Endpoints/MatchEndpoints.cs similarity index 84% rename from server/src/Halo.Api/Endpoints/MatchEndpoints.cs rename to server/src/Flaro.Api/Endpoints/MatchEndpoints.cs index a6024e0..ac5de8b 100644 --- a/server/src/Halo.Api/Endpoints/MatchEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/MatchEndpoints.cs @@ -1,9 +1,9 @@ -using Halo.Core.Library; -using Halo.Core.Metadata; -using Halo.Data; +using Flaro.Core.Library; +using Flaro.Core.Metadata; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; /// Eine falsche Zuordnung von Hand richtigstellen. /// @@ -26,7 +26,7 @@ public static class MatchEndpoints public static void MapMatching(this IEndpointRouteBuilder app) { app.MapGet("/api/v1/movies/{id:guid}/matches", async ( - Guid id, HaloContext db, IMetadataProvider anbieter, string? query, int? year) => + Guid id, FlaroContext db, IMetadataProvider anbieter, string? query, int? year) => { var film = await db.Movies.AsNoTracking().FirstOrDefaultAsync(m => m.Id == id); if (film is null) return Results.NotFound(); @@ -38,10 +38,10 @@ public static class MatchEndpoints return Results.Ok(vorschläge.Select(v => new MatchCandidate(v.ProviderId, v.Title, v.Year, v.Overview, v.Poster?.Url)).ToList()); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); app.MapPut("/api/v1/movies/{id:guid}/match", async ( - Guid id, ApplyMatchRequest anfrage, HaloContext db, + Guid id, ApplyMatchRequest anfrage, FlaroContext db, IMetadataProvider anbieter, MatchApplier anwender) => { var film = await db.Movies.FirstOrDefaultAsync(m => m.Id == id); @@ -54,10 +54,10 @@ public static class MatchEndpoints await anwender.ApplyAsync(film, gefunden); return Results.Ok(new MatchState(film.TmdbId, film.ImdbId, film.MatchLocked, film.Title, film.Year)); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); app.MapGet("/api/v1/series/{id:guid}/matches", async ( - Guid id, HaloContext db, IMetadataProvider anbieter, string? query, int? year) => + Guid id, FlaroContext db, IMetadataProvider anbieter, string? query, int? year) => { var serie = await db.Series.AsNoTracking().FirstOrDefaultAsync(s => s.Id == id); if (serie is null) return Results.NotFound(); @@ -67,10 +67,10 @@ public static class MatchEndpoints return Results.Ok(vorschläge.Select(v => new MatchCandidate(v.ProviderId, v.Title, v.Year, v.Overview, v.Poster?.Url)).ToList()); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); app.MapPut("/api/v1/series/{id:guid}/match", async ( - Guid id, ApplyMatchRequest anfrage, HaloContext db, + Guid id, ApplyMatchRequest anfrage, FlaroContext db, IMetadataProvider anbieter, MatchApplier anwender) => { var serie = await db.Series.FirstOrDefaultAsync(s => s.Id == id); @@ -83,6 +83,6 @@ public static class MatchEndpoints await anwender.ApplyAsync(serie, gefunden); return Results.Ok(new MatchState(serie.TmdbId, serie.ImdbId, serie.MatchLocked, serie.Title, serie.Year)); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); } } diff --git a/server/src/Halo.Api/Endpoints/SettingsEndpoints.cs b/server/src/Flaro.Api/Endpoints/SettingsEndpoints.cs similarity index 84% rename from server/src/Halo.Api/Endpoints/SettingsEndpoints.cs rename to server/src/Flaro.Api/Endpoints/SettingsEndpoints.cs index f9ec169..7607242 100644 --- a/server/src/Halo.Api/Endpoints/SettingsEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/SettingsEndpoints.cs @@ -1,15 +1,15 @@ -using Halo.Core; -using Halo.Data; -using Halo.Metadata; +using Flaro.Core; +using Flaro.Data; +using Flaro.Metadata; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class SettingsEndpoints { public static void MapSettings(this IEndpointRouteBuilder app) { - app.MapGet("/api/v1/settings/metadata", async (HaloContext db) => + app.MapGet("/api/v1/settings/metadata", async (FlaroContext db) => { var zeilen = await db.Settings.AsNoTracking() .Where(s => s.Key == DatabaseTmdbSettings.ApiKeyName || s.Key == DatabaseTmdbSettings.LanguageName) @@ -28,10 +28,10 @@ public static class SettingsEndpoints // Damit in der Oberfläche steht, ob der mitgelieferte Schlüssel // benutzt wird — und dass ein eigener ihn ablösen würde. UsingProjectKey: aufgelöst.UsingProjectKey)); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); app.MapPut("/api/v1/settings/metadata", async ( - MetadataSettingsRequest anfrage, HaloContext db) => + MetadataSettingsRequest anfrage, FlaroContext db) => { if (anfrage.ApiKey is not null) await Setzen(db, DatabaseTmdbSettings.ApiKeyName, anfrage.ApiKey.Trim()); if (anfrage.Language is not null) @@ -49,10 +49,10 @@ public static class SettingsEndpoints await db.SaveChangesAsync(); return Results.NoContent(); - }).RequireAuthorization(HaloPolicies.Administrator); + }).RequireAuthorization(FlaroPolicies.Administrator); } - private static async Task Setzen(HaloContext db, string schlüssel, string wert) + private static async Task Setzen(FlaroContext db, string schlüssel, string wert) { var vorhanden = await db.Settings.FindAsync(schlüssel); if (vorhanden is null) db.Settings.Add(new ServerSetting { Key = schlüssel, Value = wert }); diff --git a/server/src/Halo.Api/Endpoints/SystemEndpoints.cs b/server/src/Flaro.Api/Endpoints/SystemEndpoints.cs similarity index 93% rename from server/src/Halo.Api/Endpoints/SystemEndpoints.cs rename to server/src/Flaro.Api/Endpoints/SystemEndpoints.cs index 5ef5811..9344a42 100644 --- a/server/src/Halo.Api/Endpoints/SystemEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/SystemEndpoints.cs @@ -1,9 +1,9 @@ -using Halo.Core.Users; -using Halo.Metadata; -using Halo.Data; +using Flaro.Core.Users; +using Flaro.Metadata; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api.Endpoints; +namespace Flaro.Api.Endpoints; public static class SystemEndpoints { @@ -18,7 +18,7 @@ public static class SystemEndpoints public static void MapSystem(this IEndpointRouteBuilder app) { - app.MapGet("/api/v1/system/info", async (HaloContext db) => + app.MapGet("/api/v1/system/info", async (FlaroContext db) => Results.Ok(new SystemInfo( Version: typeof(SystemEndpoints).Assembly.GetName().Version?.ToString(3) ?? "0.0.0", NeedsSetup: !await db.Users.AnyAsync(), @@ -30,7 +30,7 @@ public static class SystemEndpoints ]))) .AllowAnonymous(); - app.MapPost("/api/v1/system/setup", async (SetupRequest anfrage, HaloContext db) => + app.MapPost("/api/v1/system/setup", async (SetupRequest anfrage, FlaroContext db) => { if (string.IsNullOrWhiteSpace(anfrage.UserName) || anfrage.Password.Length < 8) return Results.ValidationProblem(new Dictionary diff --git a/server/src/Halo.Api/Halo.Api.csproj b/server/src/Flaro.Api/Flaro.Api.csproj similarity index 65% rename from server/src/Halo.Api/Halo.Api.csproj rename to server/src/Flaro.Api/Flaro.Api.csproj index ccea93a..ccf464e 100644 --- a/server/src/Halo.Api/Halo.Api.csproj +++ b/server/src/Flaro.Api/Flaro.Api.csproj @@ -1,10 +1,10 @@ - - - - + + + + diff --git a/server/src/Halo.Api/HaloPaths.cs b/server/src/Flaro.Api/FlaroPaths.cs similarity index 84% rename from server/src/Halo.Api/HaloPaths.cs rename to server/src/Flaro.Api/FlaroPaths.cs index 33810a2..93b2d1c 100644 --- a/server/src/Halo.Api/HaloPaths.cs +++ b/server/src/Flaro.Api/FlaroPaths.cs @@ -1,4 +1,4 @@ -namespace Halo.Api; +namespace Flaro.Api; /// Wo der Server seine Sachen ablegt. /// @@ -21,26 +21,26 @@ namespace Halo.Api; /// Container neu starten, um eine Bibliothek hinzuzufügen. /// /// -public sealed class HaloPaths +public sealed class FlaroPaths { public required string Data { get; init; } public required string Cache { get; init; } public required string Transcode { get; init; } - public string DatabaseFile => Path.Combine(Data, "halo.db"); + public string DatabaseFile => Path.Combine(Data, "flaro.db"); - public static HaloPaths FromEnvironment(string contentRoot) + public static FlaroPaths FromEnvironment(string contentRoot) { string lies(string name, string voreinstellung) => Environment.GetEnvironmentVariable(name) is { Length: > 0 } wert ? wert : Path.Combine(contentRoot, voreinstellung); - return new HaloPaths + return new FlaroPaths { - Data = lies("HALO_DATA", "data"), - Cache = lies("HALO_CACHE", "cache"), - Transcode = lies("HALO_TRANSCODE", "transcode"), + Data = lies("FLARO_DATA", "data"), + Cache = lies("FLARO_CACHE", "cache"), + Transcode = lies("FLARO_TRANSCODE", "transcode"), }; } @@ -62,13 +62,13 @@ public sealed class HaloPaths /// public void EnsureExists() { - foreach (var (name, pfad) in new[] { ("HALO_DATA", Data), ("HALO_CACHE", Cache), ("HALO_TRANSCODE", Transcode) }) + foreach (var (name, pfad) in new[] { ("FLARO_DATA", Data), ("FLARO_CACHE", Cache), ("FLARO_TRANSCODE", Transcode) }) { try { Directory.CreateDirectory(pfad); - var probe = Path.Combine(pfad, $".halo-schreibprobe-{Environment.ProcessId}"); + var probe = Path.Combine(pfad, $".flaro-schreibprobe-{Environment.ProcessId}"); File.WriteAllBytes(probe, []); File.Delete(probe); } @@ -78,7 +78,7 @@ public sealed class HaloPaths $""" {name} zeigt auf „{pfad}“, und dort lässt sich nicht schreiben. - Halo läuft als Benutzer {Kennung()}. Dieses Verzeichnis muss + Flaro läuft als Benutzer {Kennung()}. Dieses Verzeichnis muss ihm gehören oder für ihn beschreibbar sein. Im Container ist das fast immer die Einhängung: entweder gehört diff --git a/server/src/Halo.Api/MatchApplier.cs b/server/src/Flaro.Api/MatchApplier.cs similarity index 98% rename from server/src/Halo.Api/MatchApplier.cs rename to server/src/Flaro.Api/MatchApplier.cs index 3b96660..77bd3e9 100644 --- a/server/src/Halo.Api/MatchApplier.cs +++ b/server/src/Flaro.Api/MatchApplier.cs @@ -1,9 +1,9 @@ -using Halo.Core.Library; -using Halo.Core.Metadata; -using Halo.Data; +using Flaro.Core.Library; +using Flaro.Core.Metadata; +using Flaro.Data; using Microsoft.EntityFrameworkCore; -namespace Halo.Api; +namespace Flaro.Api; /// Schreibt eine korrigierte Zuordnung überallhin, wo sie hingehört. /// @@ -14,7 +14,7 @@ namespace Halo.Api; /// Entscheidung wieder überstimmt. /// public sealed class MatchApplier( - HaloContext db, + FlaroContext db, IMetadataProvider anbieter, IImageStore bilder, IImageReader bildLeser, diff --git a/server/src/Halo.Api/MetadataSettings.cs b/server/src/Flaro.Api/MetadataSettings.cs similarity index 82% rename from server/src/Halo.Api/MetadataSettings.cs rename to server/src/Flaro.Api/MetadataSettings.cs index a13edd9..6b1a615 100644 --- a/server/src/Halo.Api/MetadataSettings.cs +++ b/server/src/Flaro.Api/MetadataSettings.cs @@ -1,20 +1,20 @@ -using Halo.Data; -using Halo.Metadata; +using Flaro.Data; +using Flaro.Metadata; using Microsoft.EntityFrameworkCore; -namespace Halo.Api; +namespace Flaro.Api; /// Holt die Metadaten-Einstellungen aus der Datenbank. /// /// -/// Hier und nicht in Halo.Data: sonst müsste die Datenschicht +/// Hier und nicht in Flaro.Data: sonst müsste die Datenschicht /// den Metadaten-Anbieter kennen. So zeigen beide Richtungen auf den Kern und /// keine aufeinander. /// /// Je Anfrage einmal gelesen und dann gemerkt. Ohne das fragte ein Scan über /// tausend Titel tausendmal dieselbe Zeile ab. /// -public sealed class DatabaseTmdbSettings(HaloContext db) : ITmdbSettingsSource +public sealed class DatabaseTmdbSettings(FlaroContext db) : ITmdbSettingsSource { public const string ApiKeyName = "metadata.tmdb.apiKey"; public const string LanguageName = "metadata.language"; diff --git a/server/src/Halo.Api/PackageCoordinator.cs b/server/src/Flaro.Api/PackageCoordinator.cs similarity index 96% rename from server/src/Halo.Api/PackageCoordinator.cs rename to server/src/Flaro.Api/PackageCoordinator.cs index a05e56c..7de433d 100644 --- a/server/src/Halo.Api/PackageCoordinator.cs +++ b/server/src/Flaro.Api/PackageCoordinator.cs @@ -1,11 +1,11 @@ using System.Collections.Concurrent; -using Halo.Core.Delivery; -using Halo.Core.Library; -using Halo.Data; -using Halo.Media; +using Flaro.Core.Delivery; +using Flaro.Core.Library; +using Flaro.Data; +using Flaro.Media; using Microsoft.EntityFrameworkCore; -namespace Halo.Api; +namespace Flaro.Api; public sealed record PackageState( string Status, @@ -27,12 +27,12 @@ public sealed record PackageState( /// /// /// Der Preis ist Plattenplatz und Wartezeit vor dem ersten Byte. Dafür gibt -/// es HALO_TRANSCODE, und dafür meldet dieser Weg einen Fortschritt. +/// es FLARO_TRANSCODE, und dafür meldet dieser Weg einen Fortschritt. /// /// public sealed class PackageCoordinator( IServiceScopeFactory scopes, - HaloPaths pfade, + FlaroPaths pfade, TimeProvider clock, ILogger log) { @@ -77,7 +77,7 @@ public sealed class PackageCoordinator( try { using var bereich = scopes.CreateScope(); - var db = bereich.ServiceProvider.GetRequiredService(); + var db = bereich.ServiceProvider.GetRequiredService(); var datei = await db.MediaFiles.AsNoTracking().FirstOrDefaultAsync(f => f.Id == fileId) ?? throw new InvalidOperationException("Diese Datei steht nicht mehr in der Mediathek."); diff --git a/server/src/Halo.Api/Program.cs b/server/src/Flaro.Api/Program.cs similarity index 84% rename from server/src/Halo.Api/Program.cs rename to server/src/Flaro.Api/Program.cs index 776d9bf..a7dc27d 100644 --- a/server/src/Halo.Api/Program.cs +++ b/server/src/Flaro.Api/Program.cs @@ -1,24 +1,24 @@ using System.Threading.RateLimiting; -using Halo.Api; -using Halo.Api.Auth; -using Halo.Api.Endpoints; -using Halo.Core.Metadata; -using Halo.Data; -using Halo.Core.Library; -using Halo.Media; -using Halo.Metadata; +using Flaro.Api; +using Flaro.Api.Auth; +using Flaro.Api.Endpoints; +using Flaro.Core.Metadata; +using Flaro.Data; +using Flaro.Core.Library; +using Flaro.Media; +using Flaro.Metadata; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); -var pfade = HaloPaths.FromEnvironment(builder.Environment.ContentRootPath); +var pfade = FlaroPaths.FromEnvironment(builder.Environment.ContentRootPath); pfade.EnsureExists(); builder.Services.AddSingleton(pfade); builder.Services.AddSingleton(TimeProvider.System); -builder.Services.AddDbContext(o => o.UseSqlite($"Data Source={pfade.DatabaseFile}")); +builder.Services.AddDbContext(o => o.UseSqlite($"Data Source={pfade.DatabaseFile}")); builder.Services .AddAuthentication(TokenAuthenticationHandler.SchemeName) @@ -31,9 +31,9 @@ builder.Services.AddAuthorizationBuilder() // dem irgendwann einer vergessen wird. .SetFallbackPolicy(new Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder() .RequireAuthenticatedUser().Build()) - .AddPolicy(HaloPolicies.Administrator, p => p.RequireRole(HaloRoles.Administrator)); + .AddPolicy(FlaroPolicies.Administrator, p => p.RequireRole(FlaroRoles.Administrator)); -// Einstellbar, weil die richtige Zahl vom Aufbau abhängt: steht Halo hinter +// Einstellbar, weil die richtige Zahl vom Aufbau abhängt: steht Flaro hinter // einem Reverse Proxy, erscheinen **alle** Benutzer unter derselben Adresse // und teilen sich einen Eimer. Wer das nicht weiss, hält die eigene App für // kaputt, während in Wahrheit der Nachbar im selben Haus gerade angemeldet hat. @@ -47,16 +47,16 @@ int grenze(string name, int voreinstellung) => builder.Services.AddRateLimiter(o => { o.RejectionStatusCode = StatusCodes.Status429TooManyRequests; - var beimAnmelden = grenze("HALO_RATELIMIT_SIGNIN", 10); - var beimNachfragen = grenze("HALO_RATELIMIT_POLLING", 120); + var beimAnmelden = grenze("FLARO_RATELIMIT_SIGNIN", 10); + var beimNachfragen = grenze("FLARO_RATELIMIT_POLLING", 120); // Je Absender, nicht global: sonst sperrt ein einzelner Angreifer den // ganzen Haushalt aus, und die Abwehr wäre selbst der Ausfall. - o.AddPolicy(HaloRateLimits.SignIn, anfrage => RateLimitPartition.GetFixedWindowLimiter( + o.AddPolicy(FlaroRateLimits.SignIn, anfrage => RateLimitPartition.GetFixedWindowLimiter( anfrage.Connection.RemoteIpAddress?.ToString() ?? "unbekannt", _ => new FixedWindowRateLimiterOptions { PermitLimit = beimAnmelden, Window = TimeSpan.FromMinutes(1) })); - o.AddPolicy(HaloRateLimits.Polling, anfrage => RateLimitPartition.GetFixedWindowLimiter( + o.AddPolicy(FlaroRateLimits.Polling, anfrage => RateLimitPartition.GetFixedWindowLimiter( anfrage.Connection.RemoteIpAddress?.ToString() ?? "unbekannt", _ => new FixedWindowRateLimiterOptions { PermitLimit = beimNachfragen, Window = TimeSpan.FromMinutes(1) })); }); @@ -100,7 +100,7 @@ var app = builder.Build(); // hochfährt, soll nicht erst eine Migration von Hand anstossen müssen. using (var bereich = app.Services.CreateScope()) { - await bereich.ServiceProvider.GetRequiredService().Database.MigrateAsync(); + await bereich.ServiceProvider.GetRequiredService().Database.MigrateAsync(); } app.UseExceptionHandler(); diff --git a/server/src/Halo.Api/Properties/launchSettings.json b/server/src/Flaro.Api/Properties/launchSettings.json similarity index 100% rename from server/src/Halo.Api/Properties/launchSettings.json rename to server/src/Flaro.Api/Properties/launchSettings.json diff --git a/server/src/Halo.Api/ScanCoordinator.cs b/server/src/Flaro.Api/ScanCoordinator.cs similarity index 98% rename from server/src/Halo.Api/ScanCoordinator.cs rename to server/src/Flaro.Api/ScanCoordinator.cs index a03bef7..84158e5 100644 --- a/server/src/Halo.Api/ScanCoordinator.cs +++ b/server/src/Flaro.Api/ScanCoordinator.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; -using Halo.Data; +using Flaro.Data; -namespace Halo.Api; +namespace Flaro.Api; public sealed record ScanState( string Status, diff --git a/server/src/Halo.Api/appsettings.Development.json b/server/src/Flaro.Api/appsettings.Development.json similarity index 100% rename from server/src/Halo.Api/appsettings.Development.json rename to server/src/Flaro.Api/appsettings.Development.json diff --git a/server/src/Halo.Api/appsettings.json b/server/src/Flaro.Api/appsettings.json similarity index 100% rename from server/src/Halo.Api/appsettings.json rename to server/src/Flaro.Api/appsettings.json diff --git a/server/src/Halo.Core/Auth/AuthCode.cs b/server/src/Flaro.Core/Auth/AuthCode.cs similarity index 98% rename from server/src/Halo.Core/Auth/AuthCode.cs rename to server/src/Flaro.Core/Auth/AuthCode.cs index 7ca70ed..d2744dd 100644 --- a/server/src/Halo.Core/Auth/AuthCode.cs +++ b/server/src/Flaro.Core/Auth/AuthCode.cs @@ -1,6 +1,6 @@ using System.Security.Cryptography; -namespace Halo.Core.Auth; +namespace Flaro.Core.Auth; /// Eine laufende Anmeldung per Code — für den Fernseher. /// diff --git a/server/src/Halo.Core/Auth/DeviceToken.cs b/server/src/Flaro.Core/Auth/DeviceToken.cs similarity index 97% rename from server/src/Halo.Core/Auth/DeviceToken.cs rename to server/src/Flaro.Core/Auth/DeviceToken.cs index 7a079c4..becd684 100644 --- a/server/src/Halo.Core/Auth/DeviceToken.cs +++ b/server/src/Flaro.Core/Auth/DeviceToken.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Auth; +namespace Flaro.Core.Auth; /// Ein angemeldetes Gerät. /// diff --git a/server/src/Halo.Core/Auth/Secrets.cs b/server/src/Flaro.Core/Auth/Secrets.cs similarity index 97% rename from server/src/Halo.Core/Auth/Secrets.cs rename to server/src/Flaro.Core/Auth/Secrets.cs index 2142cfb..ddecaf3 100644 --- a/server/src/Halo.Core/Auth/Secrets.cs +++ b/server/src/Flaro.Core/Auth/Secrets.cs @@ -1,7 +1,7 @@ using System.Security.Cryptography; using System.Text; -namespace Halo.Core.Auth; +namespace Flaro.Core.Auth; /// Erzeugt und verfingert Geheimnisse, die Clients vorzeigen. /// diff --git a/server/src/Halo.Core/Delivery/DeliveryDecider.cs b/server/src/Flaro.Core/Delivery/DeliveryDecider.cs similarity index 98% rename from server/src/Halo.Core/Delivery/DeliveryDecider.cs rename to server/src/Flaro.Core/Delivery/DeliveryDecider.cs index e5ce7a4..1be610a 100644 --- a/server/src/Halo.Core/Delivery/DeliveryDecider.cs +++ b/server/src/Flaro.Core/Delivery/DeliveryDecider.cs @@ -1,6 +1,6 @@ -using Halo.Core.Library; +using Flaro.Core.Library; -namespace Halo.Core.Delivery; +namespace Flaro.Core.Delivery; public enum DeliveryMethod { diff --git a/server/src/Halo.Core/Delivery/DeviceProfile.cs b/server/src/Flaro.Core/Delivery/DeviceProfile.cs similarity index 98% rename from server/src/Halo.Core/Delivery/DeviceProfile.cs rename to server/src/Flaro.Core/Delivery/DeviceProfile.cs index f30067f..7ad7b98 100644 --- a/server/src/Halo.Core/Delivery/DeviceProfile.cs +++ b/server/src/Flaro.Core/Delivery/DeviceProfile.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Delivery; +namespace Flaro.Core.Delivery; /// Was ein Gerät von sich aus öffnen kann. /// diff --git a/server/src/Halo.Core/Halo.Core.csproj b/server/src/Flaro.Core/Flaro.Core.csproj similarity index 100% rename from server/src/Halo.Core/Halo.Core.csproj rename to server/src/Flaro.Core/Flaro.Core.csproj diff --git a/server/src/Halo.Core/Libraries/Library.cs b/server/src/Flaro.Core/Libraries/Library.cs similarity index 85% rename from server/src/Halo.Core/Libraries/Library.cs rename to server/src/Flaro.Core/Libraries/Library.cs index 5e26baf..fc249e0 100644 --- a/server/src/Halo.Core/Libraries/Library.cs +++ b/server/src/Flaro.Core/Libraries/Library.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Libraries; +namespace Flaro.Core.Libraries; public enum LibraryKind { Movies, Shows } @@ -7,7 +7,7 @@ public enum LibraryKind { Movies, Shows } /// /// In der Datenbank und nicht in der Umgebung. Stünden Medienpfade in /// der Compose-Datei, müsste der Container neu starten, um eine Bibliothek -/// hinzuzufügen — und ein Fremder, der Halo zum ersten Mal startet, hätte +/// hinzuzufügen — und ein Fremder, der Flaro zum ersten Mal startet, hätte /// keine Datei, in der schon das Richtige steht. /// public sealed class Library diff --git a/server/src/Halo.Core/Library/FileNames.cs b/server/src/Flaro.Core/Library/FileNames.cs similarity index 99% rename from server/src/Halo.Core/Library/FileNames.cs rename to server/src/Flaro.Core/Library/FileNames.cs index 376d5dd..fb2f696 100644 --- a/server/src/Halo.Core/Library/FileNames.cs +++ b/server/src/Flaro.Core/Library/FileNames.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Text.RegularExpressions; -namespace Halo.Core.Library; +namespace Flaro.Core.Library; public sealed record MovieName(string Title, int? Year); diff --git a/server/src/Halo.Core/Library/IMediaProbe.cs b/server/src/Flaro.Core/Library/IMediaProbe.cs similarity index 96% rename from server/src/Halo.Core/Library/IMediaProbe.cs rename to server/src/Flaro.Core/Library/IMediaProbe.cs index da44104..004ca67 100644 --- a/server/src/Halo.Core/Library/IMediaProbe.cs +++ b/server/src/Flaro.Core/Library/IMediaProbe.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Was in einer Datei steckt. public sealed record ProbeResult( diff --git a/server/src/Halo.Core/Library/ISidecarStore.cs b/server/src/Flaro.Core/Library/ISidecarStore.cs similarity index 97% rename from server/src/Halo.Core/Library/ISidecarStore.cs rename to server/src/Flaro.Core/Library/ISidecarStore.cs index 6262b5f..905ed55 100644 --- a/server/src/Halo.Core/Library/ISidecarStore.cs +++ b/server/src/Flaro.Core/Library/ISidecarStore.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Legt Beiwerk neben eine Mediendatei. /// diff --git a/server/src/Halo.Core/Library/LibraryWalker.cs b/server/src/Flaro.Core/Library/LibraryWalker.cs similarity index 99% rename from server/src/Halo.Core/Library/LibraryWalker.cs rename to server/src/Flaro.Core/Library/LibraryWalker.cs index 109aa4e..22a7369 100644 --- a/server/src/Halo.Core/Library/LibraryWalker.cs +++ b/server/src/Flaro.Core/Library/LibraryWalker.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Eine gefundene Datei, so wie das Dateisystem sie beschreibt. public sealed record ScannedFile(string Path, long SizeBytes, DateTimeOffset ModifiedAt); diff --git a/server/src/Halo.Core/Library/MediaEntities.cs b/server/src/Flaro.Core/Library/MediaEntities.cs similarity index 99% rename from server/src/Halo.Core/Library/MediaEntities.cs rename to server/src/Flaro.Core/Library/MediaEntities.cs index b334524..8fc59e3 100644 --- a/server/src/Halo.Core/Library/MediaEntities.cs +++ b/server/src/Flaro.Core/Library/MediaEntities.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Eine Datei auf der Platte, so wie der Scan sie vorgefunden hat. /// diff --git a/server/src/Halo.Core/Library/MediaExtensions.cs b/server/src/Flaro.Core/Library/MediaExtensions.cs similarity index 98% rename from server/src/Halo.Core/Library/MediaExtensions.cs rename to server/src/Flaro.Core/Library/MediaExtensions.cs index a0d7afa..a885fa9 100644 --- a/server/src/Halo.Core/Library/MediaExtensions.cs +++ b/server/src/Flaro.Core/Library/MediaExtensions.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Welche Dateien überhaupt als Medien gelten. /// diff --git a/server/src/Halo.Core/Library/MediaStreams.cs b/server/src/Flaro.Core/Library/MediaStreams.cs similarity index 98% rename from server/src/Halo.Core/Library/MediaStreams.cs rename to server/src/Flaro.Core/Library/MediaStreams.cs index a9da49d..cd97281 100644 --- a/server/src/Halo.Core/Library/MediaStreams.cs +++ b/server/src/Flaro.Core/Library/MediaStreams.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; public enum MediaStreamKind { Video, Audio, Subtitle } diff --git a/server/src/Halo.Core/Library/Nfo.cs b/server/src/Flaro.Core/Library/Nfo.cs similarity index 99% rename from server/src/Halo.Core/Library/Nfo.cs rename to server/src/Flaro.Core/Library/Nfo.cs index 605268f..4c6c19c 100644 --- a/server/src/Halo.Core/Library/Nfo.cs +++ b/server/src/Flaro.Core/Library/Nfo.cs @@ -1,7 +1,7 @@ using System.Xml; using System.Xml.Linq; -namespace Halo.Core.Library; +namespace Flaro.Core.Library; public sealed record MovieNfo( string Title, int? Year, string? Plot, ProviderIds Ids, bool Locked); @@ -132,7 +132,7 @@ public static class Nfo /// /// /// DTDs sind abgeschaltet und es wird nichts nachgeladen. Diese - /// Dateien liegen im Medienordner, und was dort liegt, hat Halo nicht + /// Dateien liegen im Medienordner, und was dort liegt, hat Flaro nicht /// geschrieben — eine NFO mit einer externen Entität könnte sonst den /// Server dazu bringen, fremde Dateien zu lesen und auszuliefern. /// diff --git a/server/src/Halo.Core/Library/ProviderIds.cs b/server/src/Flaro.Core/Library/ProviderIds.cs similarity index 91% rename from server/src/Halo.Core/Library/ProviderIds.cs rename to server/src/Flaro.Core/Library/ProviderIds.cs index 13c8324..c594b93 100644 --- a/server/src/Halo.Core/Library/ProviderIds.cs +++ b/server/src/Flaro.Core/Library/ProviderIds.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Kennungen bei Metadatenquellen, wie sie in Namen stehen können. public sealed record ProviderIds(int? Tmdb, string? Imdb) @@ -16,10 +16,10 @@ public sealed record ProviderIds(int? Tmdb, string? Imdb) /// Die Form stammt von Kodi und wird von Jellyfin gelesen: /// Der Untergang (2004) [tmdbid-613]. Wer seine Ordner so benennt — /// von Hand oder über Radarr —, hat seine Zuordnung damit festgeschrieben, -/// und Halo soll sie nicht erneut erraten. +/// und Flaro soll sie nicht erneut erraten. /// /// -/// Halo benennt nichts um. Es liest nur. Festgeschrieben wird eine +/// Flaro benennt nichts um. Es liest nur. Festgeschrieben wird eine /// Korrektur in der NFO-Datei — dieselbe Wirkung, ohne den einen Eingriff, /// der eine Bibliothek wirklich zerlegen kann. /// diff --git a/server/src/Halo.Core/Library/SidecarNames.cs b/server/src/Flaro.Core/Library/SidecarNames.cs similarity index 94% rename from server/src/Halo.Core/Library/SidecarNames.cs rename to server/src/Flaro.Core/Library/SidecarNames.cs index 7ccc783..54fdf35 100644 --- a/server/src/Halo.Core/Library/SidecarNames.cs +++ b/server/src/Flaro.Core/Library/SidecarNames.cs @@ -1,6 +1,6 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; -/// Welche Dateien Halo neben eine Mediendatei legen darf — und sonst keine. +/// Welche Dateien Flaro neben eine Mediendatei legen darf — und sonst keine. /// /// /// @@ -17,7 +17,7 @@ namespace Halo.Core.Library; /// /// /// Transcode-Zwischenstände gehören ausdrücklich nicht hierher. Die -/// liegen unter HALO_TRANSCODE, auf einem eigenen Volume. Ein +/// liegen unter FLARO_TRANSCODE, auf einem eigenen Volume. Ein /// Medienordner ist kein Kratzplatz. /// /// diff --git a/server/src/Halo.Core/Library/SortTitle.cs b/server/src/Flaro.Core/Library/SortTitle.cs similarity index 97% rename from server/src/Halo.Core/Library/SortTitle.cs rename to server/src/Flaro.Core/Library/SortTitle.cs index e13758f..9c77cbc 100644 --- a/server/src/Halo.Core/Library/SortTitle.cs +++ b/server/src/Flaro.Core/Library/SortTitle.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Library; +namespace Flaro.Core.Library; /// Der Titel, nach dem sortiert wird — ohne den Artikel davor. /// diff --git a/server/src/Halo.Core/Metadata/IMetadataProvider.cs b/server/src/Flaro.Core/Metadata/IMetadataProvider.cs similarity index 99% rename from server/src/Halo.Core/Metadata/IMetadataProvider.cs rename to server/src/Flaro.Core/Metadata/IMetadataProvider.cs index 995f22e..4610d26 100644 --- a/server/src/Halo.Core/Metadata/IMetadataProvider.cs +++ b/server/src/Flaro.Core/Metadata/IMetadataProvider.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Metadata; +namespace Flaro.Core.Metadata; /// Ein Bild bei der Quelle — noch nicht heruntergeladen. public sealed record RemoteImage(string Url); diff --git a/server/src/Halo.Core/Settings.cs b/server/src/Flaro.Core/Settings.cs similarity index 95% rename from server/src/Halo.Core/Settings.cs rename to server/src/Flaro.Core/Settings.cs index 8468190..be6a0ae 100644 --- a/server/src/Halo.Core/Settings.cs +++ b/server/src/Flaro.Core/Settings.cs @@ -1,4 +1,4 @@ -namespace Halo.Core; +namespace Flaro.Core; /// Eine Einstellung des Servers, als Zeile in der Datenbank. /// diff --git a/server/src/Halo.Core/Users/PasswordHasher.cs b/server/src/Flaro.Core/Users/PasswordHasher.cs similarity index 99% rename from server/src/Halo.Core/Users/PasswordHasher.cs rename to server/src/Flaro.Core/Users/PasswordHasher.cs index c98eac8..5570dc2 100644 --- a/server/src/Halo.Core/Users/PasswordHasher.cs +++ b/server/src/Flaro.Core/Users/PasswordHasher.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using System.Text; using Konscious.Security.Cryptography; -namespace Halo.Core.Users; +namespace Flaro.Core.Users; /// Passwörter als Argon2id, im PHC-Format abgelegt. /// diff --git a/server/src/Halo.Core/Users/User.cs b/server/src/Flaro.Core/Users/User.cs similarity index 96% rename from server/src/Halo.Core/Users/User.cs rename to server/src/Flaro.Core/Users/User.cs index e99a897..a2ade8c 100644 --- a/server/src/Halo.Core/Users/User.cs +++ b/server/src/Flaro.Core/Users/User.cs @@ -1,4 +1,4 @@ -namespace Halo.Core.Users; +namespace Flaro.Core.Users; /// Ein Benutzerkonto. /// diff --git a/server/src/Halo.Data/Converters.cs b/server/src/Flaro.Data/Converters.cs similarity index 99% rename from server/src/Halo.Data/Converters.cs rename to server/src/Flaro.Data/Converters.cs index feab212..83d5f67 100644 --- a/server/src/Halo.Data/Converters.cs +++ b/server/src/Flaro.Data/Converters.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -namespace Halo.Data; +namespace Flaro.Data; /// Zeitpunkte als Unix-Millisekunden statt als Text. /// diff --git a/server/src/Halo.Data/Halo.Data.csproj b/server/src/Flaro.Data/Flaro.Data.csproj similarity index 90% rename from server/src/Halo.Data/Halo.Data.csproj rename to server/src/Flaro.Data/Flaro.Data.csproj index e8abc84..5da3a9e 100644 --- a/server/src/Halo.Data/Halo.Data.csproj +++ b/server/src/Flaro.Data/Flaro.Data.csproj @@ -1,7 +1,7 @@  - + diff --git a/server/src/Halo.Data/HaloContext.cs b/server/src/Flaro.Data/FlaroContext.cs similarity index 96% rename from server/src/Halo.Data/HaloContext.cs rename to server/src/Flaro.Data/FlaroContext.cs index 3691051..40248a3 100644 --- a/server/src/Halo.Data/HaloContext.cs +++ b/server/src/Flaro.Data/FlaroContext.cs @@ -1,11 +1,11 @@ -using Halo.Core; -using Halo.Core.Auth; -using Halo.Core.Libraries; -using Halo.Core.Library; -using Halo.Core.Users; +using Flaro.Core; +using Flaro.Core.Auth; +using Flaro.Core.Libraries; +using Flaro.Core.Library; +using Flaro.Core.Users; using Microsoft.EntityFrameworkCore; -namespace Halo.Data; +namespace Flaro.Data; /// Die Datenbank des Servers. /// @@ -19,7 +19,7 @@ namespace Halo.Data; /// nicht — dann ist der Umstieg auf PostgreSQL fällig, und deshalb steht hier /// nirgends SQLite-eigenes SQL. /// -public sealed class HaloContext(DbContextOptions options) : DbContext(options) +public sealed class FlaroContext(DbContextOptions options) : DbContext(options) { public DbSet Users => Set(); public DbSet DeviceTokens => Set(); diff --git a/server/src/Halo.Data/LibraryScanner.cs b/server/src/Flaro.Data/LibraryScanner.cs similarity index 98% rename from server/src/Halo.Data/LibraryScanner.cs rename to server/src/Flaro.Data/LibraryScanner.cs index 927405a..142eb14 100644 --- a/server/src/Halo.Data/LibraryScanner.cs +++ b/server/src/Flaro.Data/LibraryScanner.cs @@ -1,9 +1,9 @@ -using Halo.Core.Libraries; -using Halo.Core.Library; +using Flaro.Core.Libraries; +using Flaro.Core.Library; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -namespace Halo.Data; +namespace Flaro.Data; public sealed record ScanReport( int FilesFound, @@ -31,7 +31,7 @@ public sealed record ScanReport( /// /// public sealed class LibraryScanner( - HaloContext db, IMediaProbe sonde, TimeProvider clock, ILogger log) + FlaroContext db, IMediaProbe sonde, TimeProvider clock, ILogger log) { public async Task ScanAsync(Guid libraryId, CancellationToken ct = default) { diff --git a/server/src/Halo.Data/MetadataEnricher.cs b/server/src/Flaro.Data/MetadataEnricher.cs similarity index 99% rename from server/src/Halo.Data/MetadataEnricher.cs rename to server/src/Flaro.Data/MetadataEnricher.cs index c77b6b9..9907549 100644 --- a/server/src/Halo.Data/MetadataEnricher.cs +++ b/server/src/Flaro.Data/MetadataEnricher.cs @@ -1,9 +1,9 @@ -using Halo.Core.Library; -using Halo.Core.Metadata; +using Flaro.Core.Library; +using Flaro.Core.Metadata; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -namespace Halo.Data; +namespace Flaro.Data; public sealed record EnrichReport( int MoviesMatched, int SeriesMatched, int EpisodesMatched, bool Skipped, string? Error); @@ -25,7 +25,7 @@ public sealed record EnrichReport( /// /// public sealed class MetadataEnricher( - HaloContext db, + FlaroContext db, IMetadataProvider anbieter, IImageStore bilder, IImageReader bildLeser, diff --git a/server/src/Halo.Data/Migrations/20261003095500_Initial.Designer.cs b/server/src/Flaro.Data/Migrations/20261003095500_Initial.Designer.cs similarity index 91% rename from server/src/Halo.Data/Migrations/20261003095500_Initial.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003095500_Initial.Designer.cs index c5e3f98..f99ebd3 100644 --- a/server/src/Halo.Data/Migrations/20261003095500_Initial.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003095500_Initial.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003095500_Initial")] partial class Initial { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -156,7 +156,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -187,9 +187,9 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003095500_Initial.cs b/server/src/Flaro.Data/Migrations/20261003095500_Initial.cs similarity index 99% rename from server/src/Halo.Data/Migrations/20261003095500_Initial.cs rename to server/src/Flaro.Data/Migrations/20261003095500_Initial.cs index ec712be..87dac3c 100644 --- a/server/src/Halo.Data/Migrations/20261003095500_Initial.cs +++ b/server/src/Flaro.Data/Migrations/20261003095500_Initial.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class Initial : Migration diff --git a/server/src/Halo.Data/Migrations/20261003115256_Mediathek.Designer.cs b/server/src/Flaro.Data/Migrations/20261003115256_Mediathek.Designer.cs similarity index 89% rename from server/src/Halo.Data/Migrations/20261003115256_Mediathek.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003115256_Mediathek.Designer.cs index 6f8a33b..0666583 100644 --- a/server/src/Halo.Data/Migrations/20261003115256_Mediathek.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003115256_Mediathek.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003115256_Mediathek")] partial class Mediathek { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -173,7 +173,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -214,7 +214,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -252,7 +252,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -290,7 +290,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -306,7 +306,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -337,45 +337,45 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003115256_Mediathek.cs b/server/src/Flaro.Data/Migrations/20261003115256_Mediathek.cs similarity index 99% rename from server/src/Halo.Data/Migrations/20261003115256_Mediathek.cs rename to server/src/Flaro.Data/Migrations/20261003115256_Mediathek.cs index 39470c2..5c2b802 100644 --- a/server/src/Halo.Data/Migrations/20261003115256_Mediathek.cs +++ b/server/src/Flaro.Data/Migrations/20261003115256_Mediathek.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class Mediathek : Migration diff --git a/server/src/Halo.Data/Migrations/20261003120100_Metadaten.Designer.cs b/server/src/Flaro.Data/Migrations/20261003120100_Metadaten.Designer.cs similarity index 90% rename from server/src/Halo.Data/Migrations/20261003120100_Metadaten.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003120100_Metadaten.Designer.cs index f64f72f..cec8cda 100644 --- a/server/src/Halo.Data/Migrations/20261003120100_Metadaten.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003120100_Metadaten.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003120100_Metadaten")] partial class Metadaten { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -176,7 +176,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -217,7 +217,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -261,7 +261,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -305,7 +305,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -321,7 +321,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -352,45 +352,45 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003120100_Metadaten.cs b/server/src/Flaro.Data/Migrations/20261003120100_Metadaten.cs similarity index 98% rename from server/src/Halo.Data/Migrations/20261003120100_Metadaten.cs rename to server/src/Flaro.Data/Migrations/20261003120100_Metadaten.cs index 8bb9234..b84ed49 100644 --- a/server/src/Halo.Data/Migrations/20261003120100_Metadaten.cs +++ b/server/src/Flaro.Data/Migrations/20261003120100_Metadaten.cs @@ -2,7 +2,7 @@ #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class Metadaten : Migration diff --git a/server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs b/server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs similarity index 90% rename from server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs index 88d9f04..8c1d7b0 100644 --- a/server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003123234_GeparsterTitel")] partial class GeparsterTitel { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -176,7 +176,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -217,7 +217,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -265,7 +265,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -313,7 +313,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -329,7 +329,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -360,45 +360,45 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.cs b/server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.cs similarity index 97% rename from server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.cs rename to server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.cs index f1753fd..9445ecf 100644 --- a/server/src/Halo.Data/Migrations/20261003123234_GeparsterTitel.cs +++ b/server/src/Flaro.Data/Migrations/20261003123234_GeparsterTitel.cs @@ -2,7 +2,7 @@ #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class GeparsterTitel : Migration diff --git a/server/src/Halo.Data/Migrations/20261003150124_Spuren.Designer.cs b/server/src/Flaro.Data/Migrations/20261003150124_Spuren.Designer.cs similarity index 90% rename from server/src/Halo.Data/Migrations/20261003150124_Spuren.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003150124_Spuren.Designer.cs index b1339e8..465c2b6 100644 --- a/server/src/Halo.Data/Migrations/20261003150124_Spuren.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003150124_Spuren.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003150124_Spuren")] partial class Spuren { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -176,7 +176,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -230,7 +230,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -282,7 +282,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaStreams"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -330,7 +330,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -378,7 +378,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -394,7 +394,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -425,54 +425,54 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { - b.HasOne("Halo.Core.Library.MediaFile", null) + b.HasOne("Flaro.Core.Library.MediaFile", null) .WithMany() .HasForeignKey("MediaFileId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003150124_Spuren.cs b/server/src/Flaro.Data/Migrations/20261003150124_Spuren.cs similarity index 99% rename from server/src/Halo.Data/Migrations/20261003150124_Spuren.cs rename to server/src/Flaro.Data/Migrations/20261003150124_Spuren.cs index 1bd01e5..75027d8 100644 --- a/server/src/Halo.Data/Migrations/20261003150124_Spuren.cs +++ b/server/src/Flaro.Data/Migrations/20261003150124_Spuren.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class Spuren : Migration diff --git a/server/src/Halo.Data/Migrations/20261003165210_Zuordnung.Designer.cs b/server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.Designer.cs similarity index 90% rename from server/src/Halo.Data/Migrations/20261003165210_Zuordnung.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.Designer.cs index ef14929..1ab9c1c 100644 --- a/server/src/Halo.Data/Migrations/20261003165210_Zuordnung.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003165210_Zuordnung")] partial class Zuordnung { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -176,7 +176,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -230,7 +230,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -282,7 +282,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaStreams"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -337,7 +337,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -392,7 +392,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -408,7 +408,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -439,54 +439,54 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { - b.HasOne("Halo.Core.Library.MediaFile", null) + b.HasOne("Flaro.Core.Library.MediaFile", null) .WithMany() .HasForeignKey("MediaFileId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003165210_Zuordnung.cs b/server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.cs similarity index 98% rename from server/src/Halo.Data/Migrations/20261003165210_Zuordnung.cs rename to server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.cs index 2bb8f9c..6b1e7e8 100644 --- a/server/src/Halo.Data/Migrations/20261003165210_Zuordnung.cs +++ b/server/src/Flaro.Data/Migrations/20261003165210_Zuordnung.cs @@ -2,7 +2,7 @@ #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class Zuordnung : Migration diff --git a/server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs b/server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs similarity index 90% rename from server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs rename to server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs index b757341..033ab00 100644 --- a/server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs +++ b/server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.Designer.cs @@ -1,6 +1,6 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -8,9 +8,9 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] + [DbContext(typeof(FlaroContext))] [Migration("20261003165918_GeparstesJahr")] partial class GeparstesJahr { @@ -20,7 +20,7 @@ namespace Halo.Data.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -68,7 +68,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -111,7 +111,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -140,7 +140,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -176,7 +176,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -230,7 +230,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -282,7 +282,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaStreams"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -340,7 +340,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -398,7 +398,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -414,7 +414,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -445,54 +445,54 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { - b.HasOne("Halo.Core.Library.MediaFile", null) + b.HasOne("Flaro.Core.Library.MediaFile", null) .WithMany() .HasForeignKey("MediaFileId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.cs b/server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.cs similarity index 96% rename from server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.cs rename to server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.cs index 315584e..f0b6d2b 100644 --- a/server/src/Halo.Data/Migrations/20261003165918_GeparstesJahr.cs +++ b/server/src/Flaro.Data/Migrations/20261003165918_GeparstesJahr.cs @@ -2,7 +2,7 @@ #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { /// public partial class GeparstesJahr : Migration diff --git a/server/src/Halo.Data/Migrations/HaloContextModelSnapshot.cs b/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs similarity index 90% rename from server/src/Halo.Data/Migrations/HaloContextModelSnapshot.cs rename to server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs index 898d171..2959b7d 100644 --- a/server/src/Halo.Data/Migrations/HaloContextModelSnapshot.cs +++ b/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs @@ -1,23 +1,23 @@ // using System; -using Halo.Data; +using Flaro.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable -namespace Halo.Data.Migrations +namespace Flaro.Data.Migrations { - [DbContext(typeof(HaloContext))] - partial class HaloContextModelSnapshot : ModelSnapshot + [DbContext(typeof(FlaroContext))] + partial class FlaroContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); - modelBuilder.Entity("Halo.Core.Auth.AuthCode", b => + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -65,7 +65,7 @@ namespace Halo.Data.Migrations b.ToTable("AuthCodes"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -108,7 +108,7 @@ namespace Halo.Data.Migrations b.ToTable("DeviceTokens"); }); - modelBuilder.Entity("Halo.Core.Libraries.Library", b => + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -137,7 +137,7 @@ namespace Halo.Data.Migrations b.ToTable("Libraries"); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -173,7 +173,7 @@ namespace Halo.Data.Migrations b.ToTable("Episodes"); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -227,7 +227,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaFiles"); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -279,7 +279,7 @@ namespace Halo.Data.Migrations b.ToTable("MediaStreams"); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -337,7 +337,7 @@ namespace Halo.Data.Migrations b.ToTable("Movies"); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -395,7 +395,7 @@ namespace Halo.Data.Migrations b.ToTable("Series"); }); - modelBuilder.Entity("Halo.Core.ServerSetting", b => + modelBuilder.Entity("Flaro.Core.ServerSetting", b => { b.Property("Key") .HasMaxLength(128) @@ -411,7 +411,7 @@ namespace Halo.Data.Migrations b.ToTable("Settings"); }); - modelBuilder.Entity("Halo.Core.Users.User", b => + modelBuilder.Entity("Flaro.Core.Users.User", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -442,54 +442,54 @@ namespace Halo.Data.Migrations b.ToTable("Users"); }); - modelBuilder.Entity("Halo.Core.Auth.DeviceToken", b => + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => { - b.HasOne("Halo.Core.Users.User", null) + b.HasOne("Flaro.Core.Users.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Episode", b => + modelBuilder.Entity("Flaro.Core.Library.Episode", b => { - b.HasOne("Halo.Core.Library.Series", null) + b.HasOne("Flaro.Core.Library.Series", null) .WithMany() .HasForeignKey("SeriesId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaFile", b => + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.MediaStream", b => + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => { - b.HasOne("Halo.Core.Library.MediaFile", null) + b.HasOne("Flaro.Core.Library.MediaFile", null) .WithMany() .HasForeignKey("MediaFileId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Movie", b => + modelBuilder.Entity("Flaro.Core.Library.Movie", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("Halo.Core.Library.Series", b => + modelBuilder.Entity("Flaro.Core.Library.Series", b => { - b.HasOne("Halo.Core.Libraries.Library", null) + b.HasOne("Flaro.Core.Libraries.Library", null) .WithMany() .HasForeignKey("LibraryId") .OnDelete(DeleteBehavior.Cascade) diff --git a/server/src/Halo.Media/FfmpegOptions.cs b/server/src/Flaro.Media/FfmpegOptions.cs similarity index 81% rename from server/src/Halo.Media/FfmpegOptions.cs rename to server/src/Flaro.Media/FfmpegOptions.cs index 8021278..65f019a 100644 --- a/server/src/Halo.Media/FfmpegOptions.cs +++ b/server/src/Flaro.Media/FfmpegOptions.cs @@ -1,4 +1,4 @@ -namespace Halo.Media; +namespace Flaro.Media; /// Wo ffmpeg liegt und wie lange es brauchen darf. /// @@ -20,6 +20,6 @@ public sealed record FfmpegOptions(string Ffmpeg = "ffmpeg", string Ffprobe = "f public TimeSpan ProbeTimeout { get; init; } = TimeSpan.FromSeconds(30); public static FfmpegOptions FromEnvironment() => new( - Environment.GetEnvironmentVariable("HALO_FFMPEG") is { Length: > 0 } f ? f : "ffmpeg", - Environment.GetEnvironmentVariable("HALO_FFPROBE") is { Length: > 0 } p ? p : "ffprobe"); + Environment.GetEnvironmentVariable("FLARO_FFMPEG") is { Length: > 0 } f ? f : "ffmpeg", + Environment.GetEnvironmentVariable("FLARO_FFPROBE") is { Length: > 0 } p ? p : "ffprobe"); } diff --git a/server/src/Halo.Media/FfmpegRemuxer.cs b/server/src/Flaro.Media/FfmpegRemuxer.cs similarity index 99% rename from server/src/Halo.Media/FfmpegRemuxer.cs rename to server/src/Flaro.Media/FfmpegRemuxer.cs index 6bd5cb1..0ec0623 100644 --- a/server/src/Halo.Media/FfmpegRemuxer.cs +++ b/server/src/Flaro.Media/FfmpegRemuxer.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Globalization; using Microsoft.Extensions.Logging; -namespace Halo.Media; +namespace Flaro.Media; public sealed record RemuxOutcome(bool Success, string? Error); diff --git a/server/src/Halo.Media/FfprobeRunner.cs b/server/src/Flaro.Media/FfprobeRunner.cs similarity index 99% rename from server/src/Halo.Media/FfprobeRunner.cs rename to server/src/Flaro.Media/FfprobeRunner.cs index 1be0039..2a6346f 100644 --- a/server/src/Halo.Media/FfprobeRunner.cs +++ b/server/src/Flaro.Media/FfprobeRunner.cs @@ -1,10 +1,10 @@ using System.Diagnostics; using System.Globalization; using System.Text.Json; -using Halo.Core.Library; +using Flaro.Core.Library; using Microsoft.Extensions.Logging; -namespace Halo.Media; +namespace Flaro.Media; /// Liest die technischen Angaben einer Datei mit ffprobe. /// diff --git a/server/src/Halo.Media/Halo.Media.csproj b/server/src/Flaro.Media/Flaro.Media.csproj similarity index 84% rename from server/src/Halo.Media/Halo.Media.csproj rename to server/src/Flaro.Media/Flaro.Media.csproj index 14ec534..56d8b23 100644 --- a/server/src/Halo.Media/Halo.Media.csproj +++ b/server/src/Flaro.Media/Flaro.Media.csproj @@ -1,7 +1,7 @@  - + diff --git a/server/src/Halo.Media/RemuxArguments.cs b/server/src/Flaro.Media/RemuxArguments.cs similarity index 98% rename from server/src/Halo.Media/RemuxArguments.cs rename to server/src/Flaro.Media/RemuxArguments.cs index 15800ac..f1e1a7c 100644 --- a/server/src/Halo.Media/RemuxArguments.cs +++ b/server/src/Flaro.Media/RemuxArguments.cs @@ -1,7 +1,7 @@ -using Halo.Core.Delivery; -using Halo.Core.Library; +using Flaro.Core.Delivery; +using Flaro.Core.Library; -namespace Halo.Media; +namespace Flaro.Media; public sealed record RemuxRequest( string SourcePath, diff --git a/server/src/Halo.Media/SidecarWriter.cs b/server/src/Flaro.Media/SidecarWriter.cs similarity index 91% rename from server/src/Halo.Media/SidecarWriter.cs rename to server/src/Flaro.Media/SidecarWriter.cs index cd76576..af5f1c4 100644 --- a/server/src/Halo.Media/SidecarWriter.cs +++ b/server/src/Flaro.Media/SidecarWriter.cs @@ -1,20 +1,20 @@ -using Halo.Core.Library; +using Flaro.Core.Library; using Microsoft.Extensions.Logging; -namespace Halo.Media; +namespace Flaro.Media; -/// Ob Halo überhaupt in Medienordner schreiben darf. +/// Ob Flaro überhaupt in Medienordner schreiben darf. /// /// /// Standardmässig aus. In fremde Medienordner zu schreiben ist nichts, /// was ein Server ungefragt tun sollte — und für viele Aufbauten ist die /// Einhängung ohnehin schreibgeschützt. Wer es will, setzt -/// HALO_WRITE_SIDECARS=true und hängt die Medien ohne :ro ein. +/// FLARO_WRITE_SIDECARS=true und hängt die Medien ohne :ro ein. /// public sealed record SidecarOptions(bool Enabled) { public static SidecarOptions FromEnvironment() => new( - Environment.GetEnvironmentVariable("HALO_WRITE_SIDECARS")?.Trim() + Environment.GetEnvironmentVariable("FLARO_WRITE_SIDECARS")?.Trim() .Equals("true", StringComparison.OrdinalIgnoreCase) == true); } @@ -52,7 +52,7 @@ public sealed class SidecarWriter(SidecarOptions optionen, ILoggerWas TMDB verlangt, wenn man ihre Daten benutzt. /// diff --git a/server/src/Halo.Metadata/Halo.Metadata.csproj b/server/src/Flaro.Metadata/Flaro.Metadata.csproj similarity index 83% rename from server/src/Halo.Metadata/Halo.Metadata.csproj rename to server/src/Flaro.Metadata/Flaro.Metadata.csproj index 7b42664..d307572 100644 --- a/server/src/Halo.Metadata/Halo.Metadata.csproj +++ b/server/src/Flaro.Metadata/Flaro.Metadata.csproj @@ -1,7 +1,7 @@  - + diff --git a/server/src/Halo.Metadata/ImageCache.cs b/server/src/Flaro.Metadata/ImageCache.cs similarity index 98% rename from server/src/Halo.Metadata/ImageCache.cs rename to server/src/Flaro.Metadata/ImageCache.cs index 8c8a32f..2777f1e 100644 --- a/server/src/Halo.Metadata/ImageCache.cs +++ b/server/src/Flaro.Metadata/ImageCache.cs @@ -1,9 +1,9 @@ using System.Security.Cryptography; using System.Text; -using Halo.Core.Metadata; +using Flaro.Core.Metadata; using Microsoft.Extensions.Logging; -namespace Halo.Metadata; +namespace Flaro.Metadata; public sealed record ImageCacheOptions(string Root); @@ -80,7 +80,7 @@ public sealed class ImageCache( /// /// /// Die Prüfung ist kein Schmuck. Der Schlüssel kommt aus einer - /// Adresszeile. Ohne sie wäre /api/v1/images/..%2f..%2fconfig%2fhalo.db + /// Adresszeile. Ohne sie wäre /api/v1/images/..%2f..%2fconfig%2fflaro.db /// ein Weg, die Datenbank herunterzuladen — samt aller Token-Fingerabdrücke. /// public string? PfadFür(string schlüssel) diff --git a/server/src/Halo.Metadata/TmdbClient.cs b/server/src/Flaro.Metadata/TmdbClient.cs similarity index 99% rename from server/src/Halo.Metadata/TmdbClient.cs rename to server/src/Flaro.Metadata/TmdbClient.cs index 2fe937d..04041a3 100644 --- a/server/src/Halo.Metadata/TmdbClient.cs +++ b/server/src/Flaro.Metadata/TmdbClient.cs @@ -1,10 +1,10 @@ using System.Net; using System.Text.Json; using System.Text.Json.Serialization; -using Halo.Core.Metadata; +using Flaro.Core.Metadata; using Microsoft.Extensions.Logging; -namespace Halo.Metadata; +namespace Flaro.Metadata; /// Fragt TMDB nach Titeln, Beschreibungen und Bildern. /// diff --git a/server/src/Halo.Metadata/TmdbSettings.cs b/server/src/Flaro.Metadata/TmdbSettings.cs similarity index 90% rename from server/src/Halo.Metadata/TmdbSettings.cs rename to server/src/Flaro.Metadata/TmdbSettings.cs index 2292a56..228ae85 100644 --- a/server/src/Halo.Metadata/TmdbSettings.cs +++ b/server/src/Flaro.Metadata/TmdbSettings.cs @@ -1,9 +1,9 @@ -namespace Halo.Metadata; +namespace Flaro.Metadata; /// Was TMDB braucht, um zu antworten. public sealed record TmdbSettings(string? ApiKey, string Language, bool UsingProjectKey = false) { - /// Englisch als Voreinstellung, weil Halo nicht nur hier läuft. + /// Englisch als Voreinstellung, weil Flaro nicht nur hier läuft. /// /// /// Der Betreiber stellt in der Oberfläche um. Eine deutsche Voreinstellung @@ -16,8 +16,8 @@ public sealed record TmdbSettings(string? ApiKey, string Language, bool UsingPro /// /// /// - /// Hier gehört der Schlüssel hinein, den Halo ausliefert. Ist er - /// leer, verlangt Halo von jedem Betreiber einen eigenen — die Mediathek + /// Hier gehört der Schlüssel hinein, den Flaro ausliefert. Ist er + /// leer, verlangt Flaro von jedem Betreiber einen eigenen — die Mediathek /// läuft dann trotzdem, nur mit Dateinamen statt Titeln. /// /// diff --git a/server/tests/Halo.Api.Tests/AbgeschalteterAnbieter.cs b/server/tests/Flaro.Api.Tests/AbgeschalteterAnbieter.cs similarity index 97% rename from server/tests/Halo.Api.Tests/AbgeschalteterAnbieter.cs rename to server/tests/Flaro.Api.Tests/AbgeschalteterAnbieter.cs index 696fe5a..0915460 100644 --- a/server/tests/Halo.Api.Tests/AbgeschalteterAnbieter.cs +++ b/server/tests/Flaro.Api.Tests/AbgeschalteterAnbieter.cs @@ -1,6 +1,6 @@ -using Halo.Core.Metadata; +using Flaro.Core.Metadata; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Ein Anbieter, der nichts weiss und nichts fragt. /// diff --git a/server/tests/Halo.Api.Tests/CodeSignInTests.cs b/server/tests/Flaro.Api.Tests/CodeSignInTests.cs similarity index 92% rename from server/tests/Halo.Api.Tests/CodeSignInTests.cs rename to server/tests/Flaro.Api.Tests/CodeSignInTests.cs index 97fb1b4..07372f8 100644 --- a/server/tests/Halo.Api.Tests/CodeSignInTests.cs +++ b/server/tests/Flaro.Api.Tests/CodeSignInTests.cs @@ -1,19 +1,19 @@ using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Anmelden am Fernseher, ohne ein Passwort zu tippen. public class CodeSignInTests { - private static Task CodeAnfordern(HaloServer server) => + private static Task CodeAnfordern(FlaroServer server) => server.Client.PostAsJsonAsync("/api/v1/auth/code", new CodeRequest("Wohnzimmer", "tv-1")); [Test] public async Task Der_ganze_Weg_vom_Bildschirm_zum_Token() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); // 1. Der Fernseher fordert an und zeigt den Code. @@ -52,7 +52,7 @@ public class CodeSignInTests [Test] public async Task Ein_fremder_Abholschein_bekommt_nichts() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync(); @@ -69,7 +69,7 @@ public class CodeSignInTests [Test] public async Task Der_Code_ist_kein_Abholschein() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var echt = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync(); @@ -82,7 +82,7 @@ public class CodeSignInTests [Test] public async Task Ein_abgeholtes_Token_gibt_es_kein_zweites_Mal() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync(); @@ -100,7 +100,7 @@ public class CodeSignInTests [Test] public async Task Ein_Unangemeldeter_kann_nichts_bestaetigen() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync(); @@ -114,7 +114,7 @@ public class CodeSignInTests [Test] public async Task Ein_erfundener_Code_laesst_sich_nicht_bestaetigen() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var versuch = await server.AlsAngemeldeter(telefon.Token).PostAsync("/api/v1/auth/code/000000", null); @@ -125,7 +125,7 @@ public class CodeSignInTests [Test] public async Task Die_Bestaetigung_nennt_das_Geraet() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var angefordert = await (await CodeAnfordern(server)).Content.ReadFromJsonAsync(); diff --git a/server/tests/Halo.Api.Tests/DeliveryTests.cs b/server/tests/Flaro.Api.Tests/DeliveryTests.cs similarity index 96% rename from server/tests/Halo.Api.Tests/DeliveryTests.cs rename to server/tests/Flaro.Api.Tests/DeliveryTests.cs index 4bcfd5a..548f45b 100644 --- a/server/tests/Halo.Api.Tests/DeliveryTests.cs +++ b/server/tests/Flaro.Api.Tests/DeliveryTests.cs @@ -2,9 +2,9 @@ using System.Diagnostics; using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Der Weg vom Scan bis zur heruntergeladenen Datei. public class DeliveryTests @@ -21,7 +21,7 @@ public class DeliveryTests { if (Werkzeug("ffmpeg") is null) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert."); _bestand = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-liefern-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-liefern-" + Guid.CreateVersion7().ToString("N"))).FullName; } [TearDown] @@ -56,7 +56,7 @@ public class DeliveryTests private async Task<(HttpClient Client, MediaFileResponse Datei)> Eingelesen() { - var server = _server = new HaloServer(); + var server = _server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -78,7 +78,7 @@ public class DeliveryTests return (client, filme.Items[0].Files.Single()); } - private HaloServer? _server; + private FlaroServer? _server; [TearDown] public void ServerSchliessen() { _server?.Dispose(); _server = null; } diff --git a/server/tests/Halo.Api.Tests/Halo.Api.Tests.csproj b/server/tests/Flaro.Api.Tests/Flaro.Api.Tests.csproj similarity index 79% rename from server/tests/Halo.Api.Tests/Halo.Api.Tests.csproj rename to server/tests/Flaro.Api.Tests/Flaro.Api.Tests.csproj index 691dc86..5841313 100644 --- a/server/tests/Halo.Api.Tests/Halo.Api.Tests.csproj +++ b/server/tests/Flaro.Api.Tests/Flaro.Api.Tests.csproj @@ -22,9 +22,9 @@ - - - + + + diff --git a/server/tests/Halo.Api.Tests/HaloPathsTests.cs b/server/tests/Flaro.Api.Tests/FlaroPathsTests.cs similarity index 83% rename from server/tests/Halo.Api.Tests/HaloPathsTests.cs rename to server/tests/Flaro.Api.Tests/FlaroPathsTests.cs index dc145b2..d0d820d 100644 --- a/server/tests/Halo.Api.Tests/HaloPathsTests.cs +++ b/server/tests/Flaro.Api.Tests/FlaroPathsTests.cs @@ -1,9 +1,9 @@ -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Was beim allerersten Start passiert, bevor irgendetwas anderes greift. -public class HaloPathsTests +public class FlaroPathsTests { - private static readonly string[] Variablen = ["HALO_DATA", "HALO_CACHE", "HALO_TRANSCODE"]; + private static readonly string[] Variablen = ["FLARO_DATA", "FLARO_CACHE", "FLARO_TRANSCODE"]; private string _wurzel = ""; private Dictionary _vorher = []; @@ -26,7 +26,7 @@ public class HaloPathsTests { _vorher = Variablen.ToDictionary(v => v, Environment.GetEnvironmentVariable); _wurzel = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-pfade-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-pfade-" + Guid.CreateVersion7().ToString("N"))).FullName; } [TearDown] @@ -43,7 +43,7 @@ public class HaloPathsTests foreach (var (name, wert) in _vorher) Environment.SetEnvironmentVariable(name, wert); } - private HaloPaths Pfade(string? daten = null) => new() + private FlaroPaths Pfade(string? daten = null) => new() { Data = daten ?? Path.Combine(_wurzel, "config"), Cache = Path.Combine(_wurzel, "cache"), @@ -90,7 +90,7 @@ public class HaloPathsTests var fehler = Assert.Throws(() => Pfade(daten: gesperrt).EnsureExists()); - Assert.That(fehler!.Message, Does.Contain("HALO_DATA"), "Welche Einstellung gemeint ist"); + Assert.That(fehler!.Message, Does.Contain("FLARO_DATA"), "Welche Einstellung gemeint ist"); Assert.That(fehler.Message, Does.Contain(gesperrt), "Welches Verzeichnis"); Assert.That(fehler.Message, Does.Contain("uid="), "Als wer der Server läuft"); Assert.That(fehler.Message, Does.Contain("chown"), "Was zu tun ist"); @@ -99,20 +99,20 @@ public class HaloPathsTests [Test] public void Ohne_Umgebungsvariablen_liegt_alles_neben_dem_Programm() { - foreach (var name in new[] { "HALO_DATA", "HALO_CACHE", "HALO_TRANSCODE" }) + foreach (var name in new[] { "FLARO_DATA", "FLARO_CACHE", "FLARO_TRANSCODE" }) Environment.SetEnvironmentVariable(name, null); - var pfade = HaloPaths.FromEnvironment(_wurzel); + var pfade = FlaroPaths.FromEnvironment(_wurzel); Assert.That(pfade.Data, Is.EqualTo(Path.Combine(_wurzel, "data"))); - Assert.That(pfade.DatabaseFile, Does.EndWith("halo.db")); + Assert.That(pfade.DatabaseFile, Does.EndWith("flaro.db")); } [Test] public void Die_Umgebung_sticht_die_Voreinstellung() { - Environment.SetEnvironmentVariable("HALO_DATA", "/woanders"); - Assert.That(HaloPaths.FromEnvironment(_wurzel).Data, Is.EqualTo("/woanders")); + Environment.SetEnvironmentVariable("FLARO_DATA", "/woanders"); + Assert.That(FlaroPaths.FromEnvironment(_wurzel).Data, Is.EqualTo("/woanders")); // Zurückgesetzt wird im TearDown, für alle drei Variablen gemeinsam. } } diff --git a/server/tests/Halo.Api.Tests/HaloServer.cs b/server/tests/Flaro.Api.Tests/FlaroServer.cs similarity index 85% rename from server/tests/Halo.Api.Tests/HaloServer.cs rename to server/tests/Flaro.Api.Tests/FlaroServer.cs index 3eab9b8..e0b99cc 100644 --- a/server/tests/Halo.Api.Tests/HaloServer.cs +++ b/server/tests/Flaro.Api.Tests/FlaroServer.cs @@ -1,12 +1,12 @@ using System.Net.Http.Headers; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Halo.Core.Metadata; +using Flaro.Core.Metadata; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Ein frisch gestarteter Server mit leerer Datenbank. /// @@ -16,7 +16,7 @@ namespace Halo.Api.Tests; /// Migration wandern lassen, Einrichtung anbieten. Genau diese Kette sieht ein /// Fremder als Erstes — und genau sie würde ein Ersatzspeicher überspringen. /// -public sealed class HaloServer : IDisposable +public sealed class FlaroServer : IDisposable { private readonly WebApplicationFactory _grundlage; private readonly WebApplicationFactory _fabrik; @@ -24,16 +24,16 @@ public sealed class HaloServer : IDisposable public HttpClient Client { get; } - public HaloServer(Action? dienste = null) + public FlaroServer(Action? dienste = null) { - _verzeichnis = Path.Combine(Path.GetTempPath(), "halo-test-" + Guid.CreateVersion7().ToString("N")); - Environment.SetEnvironmentVariable("HALO_DATA", Path.Combine(_verzeichnis, "config")); - Environment.SetEnvironmentVariable("HALO_CACHE", Path.Combine(_verzeichnis, "cache")); - Environment.SetEnvironmentVariable("HALO_TRANSCODE", Path.Combine(_verzeichnis, "transcode")); + _verzeichnis = Path.Combine(Path.GetTempPath(), "flaro-test-" + Guid.CreateVersion7().ToString("N")); + Environment.SetEnvironmentVariable("FLARO_DATA", Path.Combine(_verzeichnis, "config")); + Environment.SetEnvironmentVariable("FLARO_CACHE", Path.Combine(_verzeichnis, "cache")); + Environment.SetEnvironmentVariable("FLARO_TRANSCODE", Path.Combine(_verzeichnis, "transcode")); // Die Zugriffsgrenze zählt je Adresse, und im Test kommt alles von // derselben. Ohne das Hochsetzen prüfte man hier die Grenze statt der // Anmeldung. - Environment.SetEnvironmentVariable("HALO_RATELIMIT_SIGNIN", "10000"); + Environment.SetEnvironmentVariable("FLARO_RATELIMIT_SIGNIN", "10000"); _grundlage = new WebApplicationFactory(); _fabrik = _grundlage.WithWebHostBuilder(b => b.ConfigureServices(sammlung => diff --git a/server/tests/Halo.Api.Tests/ManagementTests.cs b/server/tests/Flaro.Api.Tests/ManagementTests.cs similarity index 92% rename from server/tests/Halo.Api.Tests/ManagementTests.cs rename to server/tests/Flaro.Api.Tests/ManagementTests.cs index c1bc600..15e07a3 100644 --- a/server/tests/Halo.Api.Tests/ManagementTests.cs +++ b/server/tests/Flaro.Api.Tests/ManagementTests.cs @@ -1,15 +1,15 @@ using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; public class ManagementTests { [Test] public async Task Ein_Administrator_legt_Benutzer_an() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -25,7 +25,7 @@ public class ManagementTests [Test] public async Task Ein_gewoehnlicher_Benutzer_darf_keine_Benutzer_anlegen() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/users", new CreateUserRequest("mitbewohner", "auch-ein-langes-passwort", false)); @@ -40,7 +40,7 @@ public class ManagementTests [Test] public async Task Derselbe_Benutzername_zweimal_wird_abgelehnt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -60,7 +60,7 @@ public class ManagementTests [Test] public async Task Eine_Bibliothek_auf_einem_Pfad_den_es_nicht_gibt_wird_abgelehnt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var versuch = await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/libraries", @@ -73,7 +73,7 @@ public class ManagementTests [Test] public async Task Eine_Bibliothek_auf_einem_vorhandenen_Pfad_wird_angelegt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); var pfad = Path.GetTempPath(); @@ -89,7 +89,7 @@ public class ManagementTests [Test] public async Task Eine_unbekannte_Art_wird_abgelehnt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var versuch = await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/libraries", diff --git a/server/tests/Halo.Api.Tests/MatchCorrectionTests.cs b/server/tests/Flaro.Api.Tests/MatchCorrectionTests.cs similarity index 94% rename from server/tests/Halo.Api.Tests/MatchCorrectionTests.cs rename to server/tests/Flaro.Api.Tests/MatchCorrectionTests.cs index 2692054..c1a4d9d 100644 --- a/server/tests/Halo.Api.Tests/MatchCorrectionTests.cs +++ b/server/tests/Flaro.Api.Tests/MatchCorrectionTests.cs @@ -1,20 +1,20 @@ using System.Diagnostics; using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; -using Halo.Core.Library; -using Halo.Core.Metadata; -using Halo.Media; +using Flaro.Api.Endpoints; +using Flaro.Core.Library; +using Flaro.Core.Metadata; +using Flaro.Media; using Microsoft.Extensions.DependencyInjection; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Eine falsche Zuordnung richtigstellen — und sie festschreiben. public class MatchCorrectionTests { private string _bestand = ""; private string _filmOrdner = ""; - private HaloServer? _server; + private FlaroServer? _server; private static string? Werkzeug(string name) => (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator) @@ -26,7 +26,7 @@ public class MatchCorrectionTests { if (Werkzeug("ffmpeg") is null) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert."); _bestand = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-korrektur-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-korrektur-" + Guid.CreateVersion7().ToString("N"))).FullName; _filmOrdner = Directory.CreateDirectory(Path.Combine(_bestand, "Dark (2017)")).FullName; var start = new ProcessStartInfo(Werkzeug("ffmpeg")!) { RedirectStandardError = true, UseShellExecute = false }; @@ -87,7 +87,7 @@ public class MatchCorrectionTests private async Task<(HttpClient Client, MovieResponse Film)> Eingelesen(bool beiwerkSchreiben) { - _server = new HaloServer(dienste => + _server = new FlaroServer(dienste => { dienste.AddScoped(); dienste.AddSingleton( @@ -213,13 +213,13 @@ public class MatchCorrectionTests (await client.PutAsJsonAsync($"/api/v1/movies/{film.Id}/match", new ApplyMatchRequest(613))) .EnsureSuccessStatusCode(); - var bibliotheken = await client.GetFromJsonAsync>("/api/v1/libraries"); - (await client.PostAsync($"/api/v1/libraries/{bibliotheken!.Single().Id}/scan", null)) + var bibliothek = (await client.GetFromJsonAsync>("/api/v1/libraries"))!.Single(); + (await client.PostAsync($"/api/v1/libraries/{bibliothek.Id}/scan", null)) .EnsureSuccessStatusCode(); for (var i = 0; i < 150; i++) { var stand = await client.GetFromJsonAsync( - $"/api/v1/libraries/{bibliotheken.Single().Id}/scan"); + $"/api/v1/libraries/{bibliothek.Id}/scan"); if (stand!.State == "finished") break; await Task.Delay(50); } diff --git a/server/tests/Halo.Api.Tests/MetadataTests.cs b/server/tests/Flaro.Api.Tests/MetadataTests.cs similarity index 94% rename from server/tests/Halo.Api.Tests/MetadataTests.cs rename to server/tests/Flaro.Api.Tests/MetadataTests.cs index 9fb8531..a4c8b48 100644 --- a/server/tests/Halo.Api.Tests/MetadataTests.cs +++ b/server/tests/Flaro.Api.Tests/MetadataTests.cs @@ -1,10 +1,10 @@ using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; -using Halo.Core.Metadata; +using Flaro.Api.Endpoints; +using Flaro.Core.Metadata; using Microsoft.Extensions.DependencyInjection; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Ein Anbieter, der antwortet, was der Test vorgibt. public sealed class VorgegebenerAnbieter : IMetadataProvider @@ -84,7 +84,7 @@ public class MetadataTests [SetUp] public void Anlegen() => _bestand = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-meta-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-meta-" + Guid.CreateVersion7().ToString("N"))).FullName; [TearDown] public void Aufraeumen() @@ -99,14 +99,14 @@ public class MetadataTests File.WriteAllBytes(voll, new byte[1024]); } - private HaloServer MitAnbieter(VorgegebenerAnbieter anbieter, VorgegebeneBilder? bilder = null) => + private FlaroServer MitAnbieter(VorgegebenerAnbieter anbieter, VorgegebeneBilder? bilder = null) => new(dienste => { dienste.AddScoped(_ => anbieter); dienste.AddScoped(_ => bilder ?? new VorgegebeneBilder()); }); - private async Task<(HttpClient Admin, Guid Library)> Einrichten(HaloServer server, string art) + private async Task<(HttpClient Admin, Guid Library)> Einrichten(FlaroServer server, string art) { var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -282,7 +282,7 @@ public class MetadataSettingsTests [Test] public async Task Der_Schluessel_kommt_nicht_zurueck() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -308,12 +308,12 @@ public class MetadataSettingsTests [Test] public async Task Ohne_eigenen_Schluessel_greift_der_mitgelieferte() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var stand = await server.AlsAngemeldeter(admin.Token) .GetFromJsonAsync("/api/v1/settings/metadata"); - var mitgeliefert = Halo.Metadata.TmdbSettings.ProjectApiKey.Length > 0; + var mitgeliefert = Flaro.Metadata.TmdbSettings.ProjectApiKey.Length > 0; Assert.That(stand!.HasApiKey, Is.EqualTo(mitgeliefert)); Assert.That(stand.UsingProjectKey, Is.EqualTo(mitgeliefert)); Assert.That(stand.Language, Is.EqualTo("en-US")); @@ -322,7 +322,7 @@ public class MetadataSettingsTests [Test] public async Task Ein_eigener_Schluessel_loest_den_mitgelieferten_ab() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -338,7 +338,7 @@ public class MetadataSettingsTests [Test] public async Task Der_Pflichthinweis_zu_TMDB_steht_in_der_Systemauskunft() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var info = await server.Client.GetFromJsonAsync("/api/v1/system/info"); var tmdb = info!.Attributions.Single(); @@ -352,7 +352,7 @@ public class MetadataSettingsTests [TestCase("de-de")] public async Task Eine_unsinnige_Sprachkennung_wird_abgelehnt(string sprache) { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var antwort = await server.AlsAngemeldeter(admin.Token) @@ -364,7 +364,7 @@ public class MetadataSettingsTests [Test] public async Task Ein_gewoehnlicher_Benutzer_sieht_die_Einstellungen_nicht() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); await server.AlsAngemeldeter(admin.Token).PostAsJsonAsync("/api/v1/users", new CreateUserRequest("gast", "auch-ein-langes-passwort", false)); @@ -380,7 +380,7 @@ public class ImageEndpointTests [Test] public async Task Bilder_gibt_es_nur_fuer_Angemeldete() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.Client.GetAsync($"/api/v1/images/{new string('a', 64)}"); @@ -388,12 +388,12 @@ public class ImageEndpointTests } /// Der Weg, der sonst die Datenbank herausgäbe. - [TestCase("..%2F..%2Fconfig%2Fhalo.db")] + [TestCase("..%2F..%2Fconfig%2Fflaro.db")] [TestCase("nichtsechzigvierzeichen")] [TestCase("ZZZZ567890123456789012345678901234567890123456789012345678901234")] public async Task Ein_Schluessel_der_keiner_ist_fuehrt_ins_Leere(string versuch) { - using var server = new HaloServer(); + using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var antwort = await server.AlsAngemeldeter(admin.Token).GetAsync($"/api/v1/images/{versuch}"); diff --git a/server/tests/Halo.Api.Tests/ScanTests.cs b/server/tests/Flaro.Api.Tests/ScanTests.cs similarity index 93% rename from server/tests/Halo.Api.Tests/ScanTests.cs rename to server/tests/Flaro.Api.Tests/ScanTests.cs index 5f8cb7d..4e0ba08 100644 --- a/server/tests/Halo.Api.Tests/ScanTests.cs +++ b/server/tests/Flaro.Api.Tests/ScanTests.cs @@ -1,8 +1,8 @@ using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; /// Scan gegen einen echten Ordner mit echten Dateien. public class ScanTests @@ -11,7 +11,7 @@ public class ScanTests [SetUp] public void Anlegen() => _bestand = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-medien-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-medien-" + Guid.CreateVersion7().ToString("N"))).FullName; [TearDown] public void Aufraeumen() @@ -26,7 +26,7 @@ public class ScanTests File.WriteAllBytes(voll, new byte[bytes]); } - private async Task<(HttpClient Admin, Guid LibraryId)> MitBibliothek(HaloServer server, string art) + private async Task<(HttpClient Admin, Guid LibraryId)> MitBibliothek(FlaroServer server, string art) { var admin = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(admin.Token); @@ -62,7 +62,7 @@ public class ScanTests Datei("The Matrix (1999).srt"); // zählt nicht Datei("@eaDir/The.Matrix.1999.mkv"); // zählt auch nicht - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); var stand = await Scannen(client, bibliothek); @@ -89,7 +89,7 @@ public class ScanTests Datei("The Matrix (1999).mkv"); Datei("Inception (2010).mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); @@ -106,7 +106,7 @@ public class ScanTests Datei("The Matrix (1999).mkv"); Datei("Inception (2010).mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); @@ -125,7 +125,7 @@ public class ScanTests Datei("The Matrix (1999) - 1080p.mkv"); Datei("The Matrix (1999) - 2160p.mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); @@ -142,7 +142,7 @@ public class ScanTests Datei("Dark/Staffel 2/Dark - S02E01 - Anfänge.mkv"); Datei("Notruf.Hafenkante.S01E01.German.1080p.mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Shows"); await Scannen(client, bibliothek); @@ -169,7 +169,7 @@ public class ScanTests Datei("Dark/Dark - S01E01.mkv"); Datei("Dark/Irgendein Mitschnitt.mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Shows"); var stand = await Scannen(client, bibliothek); @@ -185,7 +185,7 @@ public class ScanTests Datei("The Matrix Reloaded (2003).mkv"); Datei("Inception (2010).mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); @@ -199,7 +199,7 @@ public class ScanTests { for (var i = 1; i <= 5; i++) Datei($"Film {i} (200{i}).mkv"); - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); await Scannen(client, bibliothek); @@ -215,7 +215,7 @@ public class ScanTests [Test] public async Task Vor_dem_ersten_Lauf_steht_der_Scan_auf_untaetig() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, bibliothek) = await MitBibliothek(server, "Movies"); var stand = await client.GetFromJsonAsync($"/api/v1/libraries/{bibliothek}/scan"); @@ -225,7 +225,7 @@ public class ScanTests [Test] public async Task Ein_gewoehnlicher_Benutzer_stoesst_keinen_Scan_an() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var (admin, bibliothek) = await MitBibliothek(server, "Movies"); await admin.PostAsJsonAsync("/api/v1/users", new CreateUserRequest("gast", "auch-ein-langes-passwort", false)); @@ -240,7 +240,7 @@ public class ScanTests [Test] public async Task Eine_Bibliothek_die_es_nicht_gibt_laesst_sich_nicht_scannen() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var (client, _) = await MitBibliothek(server, "Movies"); var versuch = await client.PostAsync($"/api/v1/libraries/{Guid.CreateVersion7()}/scan", null); diff --git a/server/tests/Halo.Api.Tests/SetupTests.cs b/server/tests/Flaro.Api.Tests/SetupTests.cs similarity index 89% rename from server/tests/Halo.Api.Tests/SetupTests.cs rename to server/tests/Flaro.Api.Tests/SetupTests.cs index ba8b961..e24a4d9 100644 --- a/server/tests/Halo.Api.Tests/SetupTests.cs +++ b/server/tests/Flaro.Api.Tests/SetupTests.cs @@ -1,15 +1,15 @@ using System.Net; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; public class SetupTests { [Test] public async Task Ein_frischer_Server_verlangt_Einrichtung() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var info = await server.Client.GetFromJsonAsync("/api/v1/system/info"); Assert.That(info!.NeedsSetup, Is.True); } @@ -17,7 +17,7 @@ public class SetupTests [Test] public async Task Die_Einrichtung_legt_einen_Administrator_an() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var antwort = await server.Client.PostAsJsonAsync("/api/v1/system/setup", new SetupRequest("guido", "ein-langes-passwort")); @@ -40,7 +40,7 @@ public class SetupTests [Test] public async Task Ein_zweites_Mal_Einrichten_wird_abgelehnt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var zweiterVersuch = await server.Client.PostAsJsonAsync("/api/v1/system/setup", @@ -52,7 +52,7 @@ public class SetupTests [Test] public async Task Ein_zu_kurzes_Passwort_wird_abgelehnt() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var antwort = await server.Client.PostAsJsonAsync("/api/v1/system/setup", new SetupRequest("guido", "kurz")); @@ -64,7 +64,7 @@ public class SetupTests [Test] public async Task Die_Gesundheitsfrage_braucht_keine_Anmeldung() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var antwort = await server.Client.GetAsync("/health"); Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.OK)); } diff --git a/server/tests/Halo.Api.Tests/SignInTests.cs b/server/tests/Flaro.Api.Tests/SignInTests.cs similarity index 91% rename from server/tests/Halo.Api.Tests/SignInTests.cs rename to server/tests/Flaro.Api.Tests/SignInTests.cs index dc681ea..480219b 100644 --- a/server/tests/Halo.Api.Tests/SignInTests.cs +++ b/server/tests/Flaro.Api.Tests/SignInTests.cs @@ -1,16 +1,16 @@ using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; -using Halo.Api.Endpoints; +using Flaro.Api.Endpoints; -namespace Halo.Api.Tests; +namespace Flaro.Api.Tests; public class SignInTests { [Test] public async Task Mit_richtigem_Passwort_gibt_es_ein_Token() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); Assert.That(anmeldung.Token, Is.Not.Empty); @@ -20,7 +20,7 @@ public class SignInTests [Test] public async Task Mit_falschem_Passwort_gibt_es_keines() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.Client.PostAsJsonAsync("/api/v1/auth/login", @@ -33,7 +33,7 @@ public class SignInTests [Test] public async Task Ein_unbekannter_Name_antwortet_wie_ein_falsches_Passwort() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var unbekannt = await server.Client.PostAsJsonAsync("/api/v1/auth/login", @@ -55,7 +55,7 @@ public class SignInTests [Test] public async Task Mit_Token_beantwortet_der_Server_wer_man_ist() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); var ich = await server.AlsAngemeldeter(anmeldung.Token) @@ -68,7 +68,7 @@ public class SignInTests [Test] public async Task Ohne_Token_antwortet_er_nicht() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.Client.GetAsync("/api/v1/auth/me"); @@ -78,7 +78,7 @@ public class SignInTests [Test] public async Task Ein_erfundenes_Token_gilt_nicht() { - using var server = new HaloServer(); + using var server = new FlaroServer(); await server.SetUpAndSignIn(); var antwort = await server.AlsAngemeldeter("ausgedacht-aber-lang-genug").GetAsync("/api/v1/auth/me"); @@ -95,7 +95,7 @@ public class SignInTests [Test] public async Task Abmelden_entwertet_das_Token_augenblicklich() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var anmeldung = await server.SetUpAndSignIn(); var client = server.AlsAngemeldeter(anmeldung.Token); @@ -109,7 +109,7 @@ public class SignInTests [Test] public async Task Eine_erneute_Anmeldung_desselben_Geraets_entwertet_das_alte_Token() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var erste = await server.SetUpAndSignIn(); var zweite = await server.SignIn("guido", "ein-langes-passwort", "iPhone", "geraet-1"); @@ -124,7 +124,7 @@ public class SignInTests [Test] public async Task Ein_anderes_Geraet_laesst_das_erste_in_Ruhe() { - using var server = new HaloServer(); + using var server = new FlaroServer(); var telefon = await server.SetUpAndSignIn(); var fernseher = await server.SignIn("guido", "ein-langes-passwort", "Apple TV", "geraet-2"); diff --git a/server/tests/Halo.Core.Tests/AuthCodeTests.cs b/server/tests/Flaro.Core.Tests/AuthCodeTests.cs similarity index 98% rename from server/tests/Halo.Core.Tests/AuthCodeTests.cs rename to server/tests/Flaro.Core.Tests/AuthCodeTests.cs index 1d81805..bde65c1 100644 --- a/server/tests/Halo.Core.Tests/AuthCodeTests.cs +++ b/server/tests/Flaro.Core.Tests/AuthCodeTests.cs @@ -1,6 +1,6 @@ -using Halo.Core.Auth; +using Flaro.Core.Auth; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; public class SecretsTests { diff --git a/server/tests/Halo.Core.Tests/DeliveryDeciderTests.cs b/server/tests/Flaro.Core.Tests/DeliveryDeciderTests.cs similarity index 98% rename from server/tests/Halo.Core.Tests/DeliveryDeciderTests.cs rename to server/tests/Flaro.Core.Tests/DeliveryDeciderTests.cs index 7e60c20..0c61a5a 100644 --- a/server/tests/Halo.Core.Tests/DeliveryDeciderTests.cs +++ b/server/tests/Flaro.Core.Tests/DeliveryDeciderTests.cs @@ -1,7 +1,7 @@ -using Halo.Core.Delivery; -using Halo.Core.Library; +using Flaro.Core.Delivery; +using Flaro.Core.Library; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; public class DeliveryDeciderTests { diff --git a/server/tests/Halo.Core.Tests/FileNameTests.cs b/server/tests/Flaro.Core.Tests/FileNameTests.cs similarity index 99% rename from server/tests/Halo.Core.Tests/FileNameTests.cs rename to server/tests/Flaro.Core.Tests/FileNameTests.cs index af6a470..2c1ea0c 100644 --- a/server/tests/Halo.Core.Tests/FileNameTests.cs +++ b/server/tests/Flaro.Core.Tests/FileNameTests.cs @@ -1,6 +1,6 @@ -using Halo.Core.Library; +using Flaro.Core.Library; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; public class FilmNamenTests { diff --git a/server/tests/Halo.Core.Tests/Halo.Core.Tests.csproj b/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj similarity index 91% rename from server/tests/Halo.Core.Tests/Halo.Core.Tests.csproj rename to server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj index 7aa314b..2dda915 100644 --- a/server/tests/Halo.Core.Tests/Halo.Core.Tests.csproj +++ b/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj @@ -21,7 +21,7 @@ - + diff --git a/server/tests/Halo.Core.Tests/LibraryWalkerTests.cs b/server/tests/Flaro.Core.Tests/LibraryWalkerTests.cs similarity index 97% rename from server/tests/Halo.Core.Tests/LibraryWalkerTests.cs rename to server/tests/Flaro.Core.Tests/LibraryWalkerTests.cs index e30f5ea..5f64097 100644 --- a/server/tests/Halo.Core.Tests/LibraryWalkerTests.cs +++ b/server/tests/Flaro.Core.Tests/LibraryWalkerTests.cs @@ -1,6 +1,6 @@ -using Halo.Core.Library; +using Flaro.Core.Library; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; public class LibraryWalkerTests { @@ -8,7 +8,7 @@ public class LibraryWalkerTests [SetUp] public void Anlegen() => _wurzel = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-bestand-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-bestand-" + Guid.CreateVersion7().ToString("N"))).FullName; [TearDown] public void Aufraeumen() diff --git a/server/tests/Halo.Core.Tests/PasswordHasherTests.cs b/server/tests/Flaro.Core.Tests/PasswordHasherTests.cs similarity index 98% rename from server/tests/Halo.Core.Tests/PasswordHasherTests.cs rename to server/tests/Flaro.Core.Tests/PasswordHasherTests.cs index 16a6fbe..d6ce53c 100644 --- a/server/tests/Halo.Core.Tests/PasswordHasherTests.cs +++ b/server/tests/Flaro.Core.Tests/PasswordHasherTests.cs @@ -1,6 +1,6 @@ -using Halo.Core.Users; +using Flaro.Core.Users; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; public class PasswordHasherTests { diff --git a/server/tests/Halo.Core.Tests/SidecarTests.cs b/server/tests/Flaro.Core.Tests/SidecarTests.cs similarity index 95% rename from server/tests/Halo.Core.Tests/SidecarTests.cs rename to server/tests/Flaro.Core.Tests/SidecarTests.cs index dbec577..5576aec 100644 --- a/server/tests/Halo.Core.Tests/SidecarTests.cs +++ b/server/tests/Flaro.Core.Tests/SidecarTests.cs @@ -1,8 +1,8 @@ -using Halo.Core.Library; +using Flaro.Core.Library; -namespace Halo.Core.Tests; +namespace Flaro.Core.Tests; -/// Was Halo in einen Medienordner schreiben darf. +/// Was Flaro in einen Medienordner schreiben darf. /// /// /// Die wichtigste Prüfdatei des Projekts. Damit Plakate neben dem Film @@ -86,7 +86,7 @@ public class SidecarNamesTests /// /// /// Die beiden Hälften könnten auseinanderlaufen — jemand ändert das Muster - /// für Folgen und vergisst die Liste. Dann schriebe Halo nichts mehr und + /// für Folgen und vergisst die Liste. Dann schriebe Flaro nichts mehr und /// niemand wüsste, warum. /// [TestCase("Dark - S01E01.mkv")] @@ -190,12 +190,12 @@ public class NfoTests Assert.That(Nfo.ReadLocked(kaputt), Is.False); } - /// Eine NFO aus dem Medienordner hat Halo nicht geschrieben. + /// Eine NFO aus dem Medienordner hat Flaro nicht geschrieben. /// /// /// Ohne abgeschaltete DTDs wäre das ein Weg, fremde Dateien zu lesen. /// Eine untergeschobene NFO mit einer externen Entität könnte den Server - /// dazu bringen, /config/halo.db einzulesen und den Inhalt als + /// dazu bringen, /config/flaro.db einzulesen und den Inhalt als /// Titel auszuliefern. /// [Test] diff --git a/server/tests/Halo.Media.Tests/FfprobeRunnerTests.cs b/server/tests/Flaro.Media.Tests/FfprobeRunnerTests.cs similarity index 97% rename from server/tests/Halo.Media.Tests/FfprobeRunnerTests.cs rename to server/tests/Flaro.Media.Tests/FfprobeRunnerTests.cs index f1f9218..3012226 100644 --- a/server/tests/Halo.Media.Tests/FfprobeRunnerTests.cs +++ b/server/tests/Flaro.Media.Tests/FfprobeRunnerTests.cs @@ -1,7 +1,7 @@ -using Halo.Core.Library; +using Flaro.Core.Library; using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Media.Tests; +namespace Flaro.Media.Tests; public class FfprobeRunnerTests { @@ -13,7 +13,7 @@ public class FfprobeRunnerTests { if (!Testdateien.FfmpegVorhanden) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert."); _ordner = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-probe-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-probe-" + Guid.CreateVersion7().ToString("N"))).FullName; _ffprobe = new FfprobeRunner( new FfmpegOptions(Testdateien.Suchen("ffmpeg")!, Testdateien.Suchen("ffprobe")!), NullLogger.Instance); diff --git a/server/tests/Halo.Media.Tests/Halo.Media.Tests.csproj b/server/tests/Flaro.Media.Tests/Flaro.Media.Tests.csproj similarity index 84% rename from server/tests/Halo.Media.Tests/Halo.Media.Tests.csproj rename to server/tests/Flaro.Media.Tests/Flaro.Media.Tests.csproj index 82330b5..231695f 100644 --- a/server/tests/Halo.Media.Tests/Halo.Media.Tests.csproj +++ b/server/tests/Flaro.Media.Tests/Flaro.Media.Tests.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/server/tests/Halo.Media.Tests/RemuxArgumentsTests.cs b/server/tests/Flaro.Media.Tests/RemuxArgumentsTests.cs similarity index 98% rename from server/tests/Halo.Media.Tests/RemuxArgumentsTests.cs rename to server/tests/Flaro.Media.Tests/RemuxArgumentsTests.cs index bdcebba..430fd69 100644 --- a/server/tests/Halo.Media.Tests/RemuxArgumentsTests.cs +++ b/server/tests/Flaro.Media.Tests/RemuxArgumentsTests.cs @@ -1,7 +1,7 @@ -using Halo.Core.Delivery; -using Halo.Core.Library; +using Flaro.Core.Delivery; +using Flaro.Core.Library; -namespace Halo.Media.Tests; +namespace Flaro.Media.Tests; /// Die ffmpeg-Zeile, betrachtet ohne sie auszuführen. public class RemuxArgumentsTests diff --git a/server/tests/Halo.Media.Tests/RemuxTests.cs b/server/tests/Flaro.Media.Tests/RemuxTests.cs similarity index 97% rename from server/tests/Halo.Media.Tests/RemuxTests.cs rename to server/tests/Flaro.Media.Tests/RemuxTests.cs index 4b8d740..3a51241 100644 --- a/server/tests/Halo.Media.Tests/RemuxTests.cs +++ b/server/tests/Flaro.Media.Tests/RemuxTests.cs @@ -1,8 +1,8 @@ -using Halo.Core.Delivery; -using Halo.Core.Library; +using Flaro.Core.Delivery; +using Flaro.Core.Library; using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Media.Tests; +namespace Flaro.Media.Tests; /// Umpacken, von der echten Datei bis zum Ergebnis. public class RemuxTests @@ -16,7 +16,7 @@ public class RemuxTests { if (!Testdateien.FfmpegVorhanden) Assert.Ignore("ffmpeg ist auf diesem Rechner nicht installiert."); _ordner = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-umpacken-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-umpacken-" + Guid.CreateVersion7().ToString("N"))).FullName; var optionen = new FfmpegOptions(Testdateien.Suchen("ffmpeg")!, Testdateien.Suchen("ffprobe")!); _ffprobe = new FfprobeRunner(optionen, NullLogger.Instance); _umpacker = new FfmpegRemuxer(optionen, NullLogger.Instance); diff --git a/server/tests/Halo.Media.Tests/SidecarWriterTests.cs b/server/tests/Flaro.Media.Tests/SidecarWriterTests.cs similarity index 94% rename from server/tests/Halo.Media.Tests/SidecarWriterTests.cs rename to server/tests/Flaro.Media.Tests/SidecarWriterTests.cs index 79fce01..9b09541 100644 --- a/server/tests/Halo.Media.Tests/SidecarWriterTests.cs +++ b/server/tests/Flaro.Media.Tests/SidecarWriterTests.cs @@ -1,8 +1,8 @@ using System.Text; -using Halo.Core.Library; +using Flaro.Core.Library; using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Media.Tests; +namespace Flaro.Media.Tests; /// Was wirklich auf der Platte landet. public class SidecarWriterTests @@ -14,7 +14,7 @@ public class SidecarWriterTests public void Anlegen() { _ordner = Directory.CreateDirectory(Path.Combine( - Path.GetTempPath(), "halo-beiwerk-" + Guid.CreateVersion7().ToString("N"), + Path.GetTempPath(), "flaro-beiwerk-" + Guid.CreateVersion7().ToString("N"), "Der Untergang (2004)")).FullName; _film = Path.Combine(_ordner, "Der Untergang (2004).mkv"); File.WriteAllBytes(_film, new byte[2048]); @@ -96,7 +96,7 @@ public class SidecarWriterTests /// /// /// Eine vorhandene NFO ist eine Zuordnung, die jemand getroffen hat. Sie - /// zu übergehen, bloss weil Halo selbst nicht schreiben darf, wäre + /// zu übergehen, bloss weil Flaro selbst nicht schreiben darf, wäre /// verkehrt herum. /// [Test] @@ -121,7 +121,7 @@ public class SidecarWriterTests await Schreiber().WriteTextAsync(_film, SidecarNames.MovieNfo, "neu"); Assert.That(await File.ReadAllTextAsync(Path.Combine(_ordner, "movie.nfo")), Is.EqualTo("neu")); - Assert.That(Directory.GetFiles(_ordner, "*.halo-tmp"), Is.Empty, "Keine Reste vom Umbenennen"); + Assert.That(Directory.GetFiles(_ordner, "*.flaro-tmp"), Is.Empty, "Keine Reste vom Umbenennen"); } [Test] diff --git a/server/tests/Halo.Media.Tests/Testdateien.cs b/server/tests/Flaro.Media.Tests/Testdateien.cs similarity index 96% rename from server/tests/Halo.Media.Tests/Testdateien.cs rename to server/tests/Flaro.Media.Tests/Testdateien.cs index d0c1d3f..430c792 100644 --- a/server/tests/Halo.Media.Tests/Testdateien.cs +++ b/server/tests/Flaro.Media.Tests/Testdateien.cs @@ -1,13 +1,13 @@ using System.Diagnostics; -namespace Halo.Media.Tests; +namespace Flaro.Media.Tests; /// Baut echte Mediendateien mit ffmpeg. /// /// /// Echte Dateien und keine aufgezeichneten Antworten. Bei TMDB ist das /// umgekehrt richtig — dort steht ein fremder Dienst dahinter. Hier geht es -/// um das Werkzeug, das Halo im Container ohnehin mitbringt, und die Frage +/// um das Werkzeug, das Flaro im Container ohnehin mitbringt, und die Frage /// „liest er die zweite Tonspur mitsamt Sprache?" lässt sich nur an einer /// Datei beantworten, die wirklich zwei hat. /// @@ -67,7 +67,7 @@ public static class Testdateien /// daran entscheidet sich, ob eine sonst tadellose Datei stumm bliebe. /// Der Encoder gilt bei ffmpeg als erprobungsweise und braucht deshalb /// -strict -2 — das betrifft nur das Erzeugen dieser Testvorlage, - /// nicht Halo. + /// nicht Flaro. /// public static void MitUntauglichemTon(string ziel) => Ausführen("ffmpeg", [ diff --git a/server/tests/Halo.Metadata.Tests/Attrappe.cs b/server/tests/Flaro.Metadata.Tests/Attrappe.cs similarity index 98% rename from server/tests/Halo.Metadata.Tests/Attrappe.cs rename to server/tests/Flaro.Metadata.Tests/Attrappe.cs index 89decde..25489c0 100644 --- a/server/tests/Halo.Metadata.Tests/Attrappe.cs +++ b/server/tests/Flaro.Metadata.Tests/Attrappe.cs @@ -1,7 +1,7 @@ using System.Net; using System.Text; -namespace Halo.Metadata.Tests; +namespace Flaro.Metadata.Tests; /// Ein HTTP-Gegenüber, das antwortet, was der Test vorgibt. /// diff --git a/server/tests/Halo.Metadata.Tests/Halo.Metadata.Tests.csproj b/server/tests/Flaro.Metadata.Tests/Flaro.Metadata.Tests.csproj similarity index 82% rename from server/tests/Halo.Metadata.Tests/Halo.Metadata.Tests.csproj rename to server/tests/Flaro.Metadata.Tests/Flaro.Metadata.Tests.csproj index a9ddc52..d3add5c 100644 --- a/server/tests/Halo.Metadata.Tests/Halo.Metadata.Tests.csproj +++ b/server/tests/Flaro.Metadata.Tests/Flaro.Metadata.Tests.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/server/tests/Halo.Metadata.Tests/ImageCacheTests.cs b/server/tests/Flaro.Metadata.Tests/ImageCacheTests.cs similarity index 95% rename from server/tests/Halo.Metadata.Tests/ImageCacheTests.cs rename to server/tests/Flaro.Metadata.Tests/ImageCacheTests.cs index 262f2fa..ae5b954 100644 --- a/server/tests/Halo.Metadata.Tests/ImageCacheTests.cs +++ b/server/tests/Flaro.Metadata.Tests/ImageCacheTests.cs @@ -1,9 +1,9 @@ using System.Net; using System.Text; -using Halo.Core.Metadata; +using Flaro.Core.Metadata; using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Metadata.Tests; +namespace Flaro.Metadata.Tests; public class ImageCacheTests { @@ -11,7 +11,7 @@ public class ImageCacheTests [SetUp] public void Anlegen() => _wurzel = Directory.CreateDirectory( - Path.Combine(Path.GetTempPath(), "halo-bilder-" + Guid.CreateVersion7().ToString("N"))).FullName; + Path.Combine(Path.GetTempPath(), "flaro-bilder-" + Guid.CreateVersion7().ToString("N"))).FullName; [TearDown] public void Aufraeumen() @@ -71,10 +71,10 @@ public class ImageCacheTests /// /// Die wichtigste Prüfung dieser Datei. Der Schlüssel kommt aus /// einer URL. Liesse er Pfadangaben durch, wäre - /// /api/v1/images/../../config/halo.db ein Weg, die Datenbank + /// /api/v1/images/../../config/flaro.db ein Weg, die Datenbank /// herunterzuladen — samt aller Token-Fingerabdrücke. /// - [TestCase("../../config/halo.db")] + [TestCase("../../config/flaro.db")] [TestCase("..%2f..%2fconfig")] [TestCase("/etc/passwd")] [TestCase("abc")] diff --git a/server/tests/Halo.Metadata.Tests/TmdbClientTests.cs b/server/tests/Flaro.Metadata.Tests/TmdbClientTests.cs similarity index 99% rename from server/tests/Halo.Metadata.Tests/TmdbClientTests.cs rename to server/tests/Flaro.Metadata.Tests/TmdbClientTests.cs index f7bf623..253d082 100644 --- a/server/tests/Halo.Metadata.Tests/TmdbClientTests.cs +++ b/server/tests/Flaro.Metadata.Tests/TmdbClientTests.cs @@ -1,8 +1,8 @@ using System.Net; -using Halo.Core.Metadata; +using Flaro.Core.Metadata; using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Metadata.Tests; +namespace Flaro.Metadata.Tests; public class TmdbClientTests { diff --git a/server/tests/Halo.Metadata.Tests/ZugangTests.cs b/server/tests/Flaro.Metadata.Tests/ZugangTests.cs similarity index 98% rename from server/tests/Halo.Metadata.Tests/ZugangTests.cs rename to server/tests/Flaro.Metadata.Tests/ZugangTests.cs index 2815f7c..aabd353 100644 --- a/server/tests/Halo.Metadata.Tests/ZugangTests.cs +++ b/server/tests/Flaro.Metadata.Tests/ZugangTests.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging.Abstractions; -namespace Halo.Metadata.Tests; +namespace Flaro.Metadata.Tests; -/// Womit sich Halo bei TMDB ausweist. +/// Womit sich Flaro bei TMDB ausweist. public class ZugangTests { private const string LeseToken = "eyJhbGciOiJIUzI1NiJ9.beispiel.unterschrift"; diff --git a/web/README.md b/web/README.md index 57846e4..32efd93 100644 --- a/web/README.md +++ b/web/README.md @@ -1,4 +1,4 @@ -# Halo Web +# Flaro Web Die Weboberfläche für Rechner, auf denen es keine App gibt — Windows vor allem. @@ -28,7 +28,7 @@ Nebenbei bleibt der Zugangsschlüssel beim Server, statt im Browser zu liegen. ```bash cd app && npm install && npm run build -cd ../server && HALO_SERVER=http://jellyfin:8096 node index.js +cd ../server && FLARO_SERVER=http://jellyfin:8096 node index.js ``` Dann auf `http://localhost:8080`. Der Port lässt sich über `PORT` ändern. @@ -36,7 +36,7 @@ Dann auf `http://localhost:8080`. Der Port lässt sich über `PORT` ändern. ## Entwickeln ```bash -cd app && HALO_SERVER=http://jellyfin:8096 npm run dev +cd app && FLARO_SERVER=http://jellyfin:8096 npm run dev ``` Vite übernimmt dabei das Weiterreichen, der eigene Server wird nicht gebraucht. diff --git a/web/app/index.html b/web/app/index.html index 616e5f8..6d2119e 100644 --- a/web/app/index.html +++ b/web/app/index.html @@ -3,7 +3,7 @@ -Halo +Flaro
diff --git a/web/app/package-lock.json b/web/app/package-lock.json index ba62eef..a0fe749 100644 --- a/web/app/package-lock.json +++ b/web/app/package-lock.json @@ -1,11 +1,11 @@ { - "name": "halo-web", + "name": "flaro-web", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "halo-web", + "name": "flaro-web", "version": "0.1.0", "dependencies": { "hls.js": "^1.5.18" diff --git a/web/app/package.json b/web/app/package.json index d46ec0c..e77adda 100644 --- a/web/app/package.json +++ b/web/app/package.json @@ -1,5 +1,5 @@ { - "name": "halo-web", + "name": "flaro-web", "private": true, "version": "0.1.0", "type": "module", diff --git a/web/app/src/api.ts b/web/app/src/api.ts index a47fd49..e15c18e 100644 --- a/web/app/src/api.ts +++ b/web/app/src/api.ts @@ -14,13 +14,13 @@ import type { Wiedergabeauskunft } from './types'; const BASIS = '/api'; export const sitzung = { - token: localStorage.getItem('halo.token') ?? '', - userId: localStorage.getItem('halo.user') ?? '', + token: localStorage.getItem('flaro.token') ?? '', + userId: localStorage.getItem('flaro.user') ?? '', geraet: - localStorage.getItem('halo.device') ?? + localStorage.getItem('flaro.device') ?? (() => { - const id = 'halo-web-' + Math.random().toString(36).slice(2, 12); - localStorage.setItem('halo.device', id); + const id = 'flaro-web-' + Math.random().toString(36).slice(2, 12); + localStorage.setItem('flaro.device', id); return id; })(), }; @@ -32,7 +32,7 @@ export const sitzung = { * angemeldet, obwohl der Schlüssel stimmt. */ function kopfzeile(): string { const teile = [ - 'Client="Halo Web"', + 'Client="Flaro Web"', 'Device="Browser"', `DeviceId="${sitzung.geraet}"`, 'Version="0.1"', @@ -90,13 +90,13 @@ export async function anmelden(benutzer: string, passwort: string): Promise(schluessel: K): Einstellungen[K] { - const roh = localStorage.getItem('halo.' + schluessel); + const roh = localStorage.getItem('flaro.' + schluessel); if (roh == null) return vorgabe[schluessel]; const art = typeof vorgabe[schluessel]; if (Array.isArray(vorgabe[schluessel])) { @@ -92,7 +92,7 @@ export function setzen(schluessel: K, wert: Einst // Listen müssen als JSON abgelegt werden — ein Array landete sonst als // "a,b,c" im Speicher und käme als Zeichenkette zurück. localStorage.setItem( - 'halo.' + schluessel, + 'flaro.' + schluessel, Array.isArray(wert) ? JSON.stringify(wert) : String(wert), ); anwenden(); diff --git a/web/app/src/style.css b/web/app/src/style.css index d46531c..313c629 100644 --- a/web/app/src/style.css +++ b/web/app/src/style.css @@ -1,7 +1,7 @@ /* ============================================================================ - Halo Web — ein Frontend für Jellyfin + Flaro Web — ein Frontend für Jellyfin ---------------------------------------------------------------------------- - Die Werte stammen aus HaloDesign, nicht aus dem Augenmaß: dieselben + Die Werte stammen aus FlaroDesign, nicht aus dem Augenmaß: dieselben Flächen, dieselben Radien, dieselben Abstände wie in der App. ========================================================================= */ :root { diff --git a/web/app/src/views/login.ts b/web/app/src/views/login.ts index f41efea..b428e98 100644 --- a/web/app/src/views/login.ts +++ b/web/app/src/views/login.ts @@ -4,7 +4,7 @@ import { esc } from '../util'; export function anmeldung(wurzel: HTMLElement, fertig: () => void): void { wurzel.innerHTML = `