2021-09-25 08:18:22 +00:00
|
|
|
import Defaults
|
2022-06-18 12:39:49 +00:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
static var isForPreviews: Bool {
|
|
|
|
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:08:38 +00:00
|
|
|
static var logsDirectory: URL {
|
2024-02-01 22:54:16 +00:00
|
|
|
temporaryDirectory
|
|
|
|
}
|
|
|
|
|
|
|
|
static var settingsExportDirectory: URL {
|
|
|
|
temporaryDirectory
|
|
|
|
}
|
|
|
|
|
|
|
|
private static var temporaryDirectory: URL {
|
2022-07-06 22:08:38 +00:00
|
|
|
URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
|
|
|
}
|
|
|
|
|
2021-10-24 09:16:04 +00:00
|
|
|
#if os(macOS)
|
|
|
|
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
2022-01-02 19:41:04 +00:00
|
|
|
#elseif os(iOS)
|
|
|
|
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
2023-07-24 17:38:32 +00:00
|
|
|
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
2021-10-24 09:16:04 +00:00
|
|
|
#endif
|
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
@State private var configured = false
|
|
|
|
|
2023-04-22 20:13:59 +00:00
|
|
|
@StateObject private var accounts = AccountsModel.shared
|
2022-11-24 20:36:05 +00:00
|
|
|
@StateObject private var comments = CommentsModel.shared
|
|
|
|
@StateObject private var instances = InstancesModel.shared
|
|
|
|
@StateObject private var menu = MenuModel.shared
|
2023-04-22 20:13:59 +00:00
|
|
|
@StateObject private var navigation = NavigationModel.shared
|
2022-11-24 20:36:05 +00:00
|
|
|
@StateObject private var networkState = NetworkStateModel.shared
|
|
|
|
@StateObject private var player = PlayerModel.shared
|
|
|
|
@StateObject private var playlists = PlaylistsModel.shared
|
|
|
|
@StateObject private var recents = RecentsModel.shared
|
|
|
|
@StateObject private var settings = SettingsModel.shared
|
2022-12-11 15:15:42 +00:00
|
|
|
@StateObject private var subscriptions = SubscribedChannelsModel.shared
|
2022-11-24 20:36:05 +00:00
|
|
|
@StateObject private var thumbnails = ThumbnailsModel.shared
|
2021-11-08 23:14:28 +00:00
|
|
|
|
2021-12-26 21:14:46 +00:00
|
|
|
let persistenceController = PersistenceController.shared
|
|
|
|
|
2023-05-25 12:28:29 +00:00
|
|
|
var favorites: FavoritesModel { .shared }
|
2022-09-01 18:01:01 +00:00
|
|
|
var playerControls: PlayerControlsModel { .shared }
|
|
|
|
|
2021-06-09 20:51:23 +00:00
|
|
|
var body: some Scene {
|
|
|
|
WindowGroup {
|
|
|
|
ContentView()
|
2022-06-18 12:39:49 +00:00
|
|
|
.onAppear(perform: configure)
|
2021-12-26 21:14:46 +00:00
|
|
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
2023-07-24 17:38:32 +00:00
|
|
|
.environment(\.navigationStyle, navigationStyle)
|
2022-01-06 15:35:45 +00:00
|
|
|
#if os(macOS)
|
|
|
|
.background(
|
|
|
|
HostingWindowFinder { window in
|
|
|
|
Windows.mainWindow = window
|
|
|
|
}
|
|
|
|
)
|
|
|
|
#else
|
2023-05-07 19:45:18 +00:00
|
|
|
.onReceive(
|
2022-01-06 15:35:45 +00:00
|
|
|
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()
|
|
|
|
}
|
2021-12-19 17:17:04 +00:00
|
|
|
#endif
|
2021-12-24 19:20:05 +00:00
|
|
|
#if os(iOS)
|
|
|
|
.handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"]))
|
2021-12-19 17:17:04 +00:00
|
|
|
#endif
|
2023-07-24 17:38:32 +00:00
|
|
|
#if !os(tvOS)
|
|
|
|
.onOpenURL { url in
|
|
|
|
URLBookmarkModel.shared.saveBookmark(url)
|
|
|
|
OpenURLHandler(navigationStyle: navigationStyle).handle(url)
|
|
|
|
}
|
|
|
|
#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
|
|
|
|
2022-11-24 20:36:05 +00:00
|
|
|
MenuCommands(model: Binding<MenuModel>(get: { MenuModel.shared }, 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)
|
2021-12-19 17:17:04 +00:00
|
|
|
WindowGroup(player.windowTitle) {
|
|
|
|
VideoPlayerView()
|
2022-06-18 12:39:49 +00:00
|
|
|
.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
|
|
|
|
2023-06-17 12:09:51 +00:00
|
|
|
NotificationCenter.default.addObserver( // swiftlint:disable:this discarded_notification_center_observer
|
2022-04-03 12:23:42 +00:00
|
|
|
forName: NSWindow.willExitFullScreenNotification,
|
|
|
|
object: window,
|
|
|
|
queue: OperationQueue.main
|
|
|
|
) { _ in
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
2022-06-18 12:39:49 +00:00
|
|
|
self.player.playingFullScreen = false
|
2022-04-03 12:23:42 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-06 15:35:45 +00:00
|
|
|
}
|
|
|
|
)
|
2021-12-19 17:17:04 +00:00
|
|
|
.onAppear { player.presentingPlayer = true }
|
|
|
|
.onDisappear { player.presentingPlayer = false }
|
2021-12-26 21:14:46 +00:00
|
|
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
2021-12-19 17:17:04 +00:00
|
|
|
.environment(\.navigationStyle, .sidebar)
|
2021-12-24 19:20:05 +00:00
|
|
|
.handlesExternalEvents(preferring: Set(["player", "*"]), allowing: Set(["player", "*"]))
|
2023-07-24 17:38:32 +00:00
|
|
|
.onOpenURL { url in
|
|
|
|
URLBookmarkModel.shared.saveBookmark(url)
|
|
|
|
OpenURLHandler(navigationStyle: navigationStyle).handle(url)
|
|
|
|
}
|
2021-12-19 17:17:04 +00:00
|
|
|
}
|
2021-12-24 19:20:05 +00:00
|
|
|
.handlesExternalEvents(matching: Set(["player", "*"]))
|
2021-12-19 17:17:04 +00:00
|
|
|
|
2021-09-25 08:18:22 +00:00
|
|
|
Settings {
|
|
|
|
SettingsView()
|
2021-12-26 21:14:46 +00:00
|
|
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
2021-09-25 08:18:22 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2022-06-18 12:39:49 +00:00
|
|
|
|
|
|
|
func configure() {
|
|
|
|
guard !Self.isForPreviews, !configured else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
configured = true
|
|
|
|
|
2022-08-08 17:26:04 +00:00
|
|
|
#if DEBUG
|
|
|
|
SiestaLog.Category.enabled = .common
|
|
|
|
#endif
|
2024-05-10 13:38:19 +00:00
|
|
|
SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared)
|
2022-06-18 12:39:49 +00:00
|
|
|
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
|
|
|
|
|
2022-07-04 09:35:27 +00:00
|
|
|
if !Defaults[.lastAccountIsPublic] {
|
2022-11-24 20:36:05 +00:00
|
|
|
AccountsModel.shared.configureAccount()
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
|
|
|
|
2022-10-26 11:11:35 +00:00
|
|
|
if let countryOfPublicInstances = Defaults[.countryOfPublicInstances] {
|
2022-11-24 20:36:05 +00:00
|
|
|
InstancesManifest.shared.setPublicAccount(countryOfPublicInstances, asCurrent: AccountsModel.shared.current.isNil)
|
2022-07-01 21:28:32 +00:00
|
|
|
}
|
|
|
|
|
2022-11-24 20:36:05 +00:00
|
|
|
if !AccountsModel.shared.current.isNil {
|
2022-06-18 12:39:49 +00:00
|
|
|
player.restoreQueue()
|
|
|
|
}
|
|
|
|
|
|
|
|
if !Defaults[.saveRecents] {
|
|
|
|
recents.clear()
|
|
|
|
}
|
|
|
|
|
2023-05-25 15:01:53 +00:00
|
|
|
let startupSection = Defaults[.startupSection]
|
|
|
|
var section: TabSelection? = startupSection.tabSelection
|
2022-06-18 12:39:49 +00:00
|
|
|
|
|
|
|
#if os(macOS)
|
|
|
|
if section == .playlists {
|
|
|
|
section = .search
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-11-24 20:36:05 +00:00
|
|
|
NavigationModel.shared.tabSelection = section ?? .search
|
2022-08-15 12:49:12 +00:00
|
|
|
|
2022-06-18 12:39:49 +00:00
|
|
|
playlists.load()
|
|
|
|
|
2022-08-29 13:31:36 +00:00
|
|
|
#if !os(macOS)
|
2022-07-11 16:10:51 +00:00
|
|
|
player.updateRemoteCommandCenter()
|
2022-06-18 12:39:49 +00:00
|
|
|
#endif
|
2022-08-08 18:02:46 +00:00
|
|
|
|
2022-08-14 16:59:04 +00:00
|
|
|
if player.presentingPlayer {
|
|
|
|
player.presentingPlayer = false
|
|
|
|
}
|
2022-08-21 14:53:46 +00:00
|
|
|
|
|
|
|
#if os(iOS)
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
|
|
if Defaults[.lockPortraitWhenBrowsing] {
|
|
|
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2022-11-14 22:13:29 +00:00
|
|
|
|
|
|
|
URLBookmarkModel.shared.refreshAll()
|
2023-05-25 12:28:29 +00:00
|
|
|
|
|
|
|
migrateHomeHistoryItems()
|
2024-05-01 12:30:19 +00:00
|
|
|
migrateQualityProfiles()
|
2023-05-25 12:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func migrateHomeHistoryItems() {
|
|
|
|
guard Defaults[.homeHistoryItems] > 0 else { return }
|
|
|
|
|
|
|
|
if favorites.addableItems().contains(where: { $0.section == .history }) {
|
|
|
|
let historyItem = FavoriteItem(section: .history)
|
|
|
|
favorites.add(historyItem)
|
|
|
|
favorites.setListingStyle(.list, historyItem)
|
|
|
|
favorites.setLimit(Defaults[.homeHistoryItems], historyItem)
|
|
|
|
|
|
|
|
print("migrated home history items: \(favorites.limit(historyItem))")
|
|
|
|
}
|
|
|
|
|
|
|
|
Defaults[.homeHistoryItems] = -1
|
2022-06-18 12:39:49 +00:00
|
|
|
}
|
2023-07-24 17:38:32 +00:00
|
|
|
|
2024-05-01 12:30:19 +00:00
|
|
|
@Default(.qualityProfiles) private var qualityProfilesData
|
|
|
|
|
|
|
|
func migrateQualityProfiles() {
|
|
|
|
for profile in qualityProfilesData where profile.order.isEmpty {
|
|
|
|
var updatedProfile = profile
|
|
|
|
updatedProfile.order = Array(QualityProfile.Format.allCases.indices)
|
|
|
|
QualityProfilesModel.shared.update(profile, updatedProfile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 17:38:32 +00:00
|
|
|
var navigationStyle: NavigationStyle {
|
|
|
|
#if os(iOS)
|
|
|
|
return horizontalSizeClass == .compact ? .tab : .sidebar
|
|
|
|
#elseif os(tvOS)
|
|
|
|
return .tab
|
|
|
|
#else
|
|
|
|
return .sidebar
|
|
|
|
#endif
|
|
|
|
}
|
2021-06-09 20:51:23 +00:00
|
|
|
}
|