yattee/Shared/YatteeApp.swift

281 lines
9.6 KiB
Swift
Raw Normal View History

2021-09-25 08:18:22 +00:00
import Defaults
import MediaPlayer
import PINCache
import SDWebImage
import SDWebImageWebPCoder
import Siesta
2021-06-09 20:51:23 +00:00
import SwiftUI
@main
2021-11-07 13:32:01 +00:00
struct YatteeApp: App {
2022-01-06 15:02:53 +00:00
static var version: String {
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
}
static var build: String {
Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
}
static var isForPreviews: Bool {
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
2021-10-24 09:16:04 +00:00
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
#elseif os(iOS)
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
2021-10-24 09:16:04 +00:00
#endif
@State private var configured = false
@StateObject private var accounts = AccountsModel()
@StateObject private var comments = CommentsModel()
@StateObject private var instances = InstancesModel()
2021-11-08 23:14:28 +00:00
@StateObject private var menu = MenuModel()
@StateObject private var navigation = NavigationModel()
@StateObject private var networkState = NetworkStateModel()
@StateObject private var player = PlayerModel()
2022-02-16 20:23:11 +00:00
@StateObject private var playerControls = PlayerControlsModel()
@StateObject private var playerTime = PlayerTimeModel()
@StateObject private var playlists = PlaylistsModel()
@StateObject private var recents = RecentsModel()
@StateObject private var search = SearchModel()
@StateObject private var subscriptions = SubscriptionsModel()
@StateObject private var thumbnails = ThumbnailsModel()
2021-11-08 23:14:28 +00:00
let persistenceController = PersistenceController.shared
2021-06-09 20:51:23 +00:00
var body: some Scene {
WindowGroup {
ContentView()
.onAppear(perform: configure)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(accounts)
.environmentObject(comments)
.environmentObject(instances)
.environmentObject(navigation)
.environmentObject(networkState)
.environmentObject(player)
2022-02-16 20:23:11 +00:00
.environmentObject(playerControls)
.environmentObject(playerTime)
.environmentObject(playlists)
.environmentObject(recents)
.environmentObject(subscriptions)
.environmentObject(thumbnails)
2021-11-08 23:14:28 +00:00
.environmentObject(menu)
.environmentObject(search)
2022-01-06 15:35:45 +00:00
#if os(macOS)
.background(
HostingWindowFinder { window in
Windows.mainWindow = window
}
)
#else
.onReceive(
NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)
) { _ in
player.handleEnterForeground()
}
2022-06-05 17:12:00 +00:00
.onReceive(
NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)
) { _ in
player.handleEnterBackground()
}
#endif
2021-12-24 19:20:05 +00:00
#if os(iOS)
.handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"]))
#endif
2021-06-09 20:51:23 +00:00
}
2021-12-24 19:20:05 +00:00
#if os(iOS)
.handlesExternalEvents(matching: Set(["*"]))
#endif
2021-07-27 22:40:04 +00:00
#if !os(tvOS)
2021-11-08 16:29:35 +00:00
.commands {
SidebarCommands()
2021-12-07 23:09:49 +00:00
2021-11-08 16:29:35 +00:00
CommandGroup(replacing: .newItem, addition: {})
2021-12-07 23:09:49 +00:00
2021-11-08 23:14:28 +00:00
MenuCommands(model: Binding<MenuModel>(get: { menu }, set: { _ in }))
2021-11-08 16:29:35 +00:00
}
2021-07-27 22:40:04 +00:00
#endif
2021-09-25 08:18:22 +00:00
#if os(macOS)
WindowGroup(player.windowTitle) {
VideoPlayerView()
.onAppear(perform: configure)
2022-01-06 15:35:45 +00:00
.background(
HostingWindowFinder { window in
Windows.playerWindow = window
2022-04-03 12:23:42 +00:00
NotificationCenter.default.addObserver(
forName: NSWindow.willExitFullScreenNotification,
object: window,
queue: OperationQueue.main
) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.player.playingFullScreen = false
2022-04-03 12:23:42 +00:00
}
}
2022-01-06 15:35:45 +00:00
}
)
.onAppear { player.presentingPlayer = true }
.onDisappear { player.presentingPlayer = false }
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environment(\.navigationStyle, .sidebar)
.environmentObject(accounts)
.environmentObject(comments)
.environmentObject(instances)
.environmentObject(navigation)
.environmentObject(networkState)
.environmentObject(player)
2022-02-16 20:23:11 +00:00
.environmentObject(playerControls)
.environmentObject(playerTime)
.environmentObject(playlists)
.environmentObject(recents)
2022-06-24 22:48:57 +00:00
.environmentObject(search)
.environmentObject(subscriptions)
.environmentObject(thumbnails)
2021-12-24 19:20:05 +00:00
.handlesExternalEvents(preferring: Set(["player", "*"]), allowing: Set(["player", "*"]))
}
2021-12-24 19:20:05 +00:00
.handlesExternalEvents(matching: Set(["player", "*"]))
2021-09-25 08:18:22 +00:00
Settings {
SettingsView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
2021-12-24 19:20:05 +00:00
.environmentObject(accounts)
.environmentObject(instances)
.environmentObject(player)
2022-02-16 20:23:11 +00:00
.environmentObject(playerControls)
2021-09-25 08:18:22 +00:00
}
#endif
}
func configure() {
guard !Self.isForPreviews, !configured else {
return
}
configured = true
SiestaLog.Category.enabled = .common
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
#if !os(macOS)
configureNowPlayingInfoCenter()
#endif
#if os(iOS)
if Defaults[.lockPortraitWhenBrowsing] {
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
}
#endif
if let account = accounts.lastUsed ??
instances.lastUsed?.anonymousAccount ??
InstancesModel.all.first?.anonymousAccount
{
accounts.setCurrent(account)
}
if accounts.current.isNil {
navigation.presentingWelcomeScreen = true
}
playlists.accounts = accounts
search.accounts = accounts
subscriptions.accounts = accounts
comments.player = player
menu.accounts = accounts
menu.navigation = navigation
menu.player = player
playerControls.player = player
player.accounts = accounts
player.comments = comments
player.controls = playerControls
player.networkState = networkState
player.playerTime = playerTime
if !accounts.current.isNil {
player.restoreQueue()
}
if !Defaults[.saveRecents] {
recents.clear()
}
var section = Defaults[.visibleSections].min()?.tabSelection
#if os(macOS)
if section == .playlists {
section = .search
}
#endif
navigation.tabSelection = section ?? .search
subscriptions.load()
playlists.load()
#if os(macOS)
Windows.player.open()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
Windows.main.focus()
}
#endif
}
func configureNowPlayingInfoCenter() {
#if !os(macOS)
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
UIApplication.shared.beginReceivingRemoteControlEvents()
#endif
MPRemoteCommandCenter.shared().playCommand.addTarget { _ in
player.play()
return .success
}
MPRemoteCommandCenter.shared().pauseCommand.addTarget { _ in
player.pause()
return .success
}
MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false
MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = false
MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { remoteEvent in
guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent
else {
return .commandFailed
}
player.backend.seek(to: event.positionTime)
return .success
}
let skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand
skipForwardCommand.isEnabled = true
skipForwardCommand.preferredIntervals = [10]
skipForwardCommand.addTarget { _ in
player.backend.seek(relative: .secondsInDefaultTimescale(10))
return .success
}
let skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand
skipBackwardCommand.isEnabled = true
skipBackwardCommand.preferredIntervals = [10]
skipBackwardCommand.addTarget { _ in
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
return .success
}
}
2021-06-09 20:51:23 +00:00
}