229 lines
11 KiB
Swift
229 lines
11 KiB
Swift
/* JellyfinPlayer/Swiftfin is subject to the terms of the Mozilla Public
|
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
|
*/
|
|
|
|
import SwiftUI
|
|
|
|
import KeychainSwift
|
|
import Nuke
|
|
import Combine
|
|
import JellyfinAPI
|
|
|
|
struct ContentView: View {
|
|
@Environment(\.managedObjectContext) private var viewContext
|
|
@EnvironmentObject var orientationInfo: OrientationInfo
|
|
@EnvironmentObject var jsi: justSignedIn
|
|
|
|
@StateObject private var globalData = GlobalData()
|
|
|
|
@FetchRequest(entity: Server.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Server.name, ascending: true)])
|
|
private var servers: FetchedResults<Server>
|
|
|
|
@FetchRequest(entity: SignedInUser.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \SignedInUser.username, ascending: true)])
|
|
private var savedUsers: FetchedResults<SignedInUser>
|
|
|
|
@State private var needsToSelectServer = false
|
|
@State private var isLoading = false
|
|
@State private var tabSelection: String = "Home"
|
|
@State private var libraries: [String] = []
|
|
@State private var library_names: [String: String] = [:]
|
|
@State private var librariesShowRecentlyAdded: [String] = []
|
|
@State private var libraryPrefillID: String = ""
|
|
@State private var showSettingsPopover: Bool = false
|
|
@State private var viewDidLoad: Bool = false
|
|
@State private var loadState: Int = 2
|
|
|
|
private var recentFilterSet: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.descending], sortBy: ["DateCreated"])
|
|
|
|
func startup() {
|
|
if(viewDidLoad == true) {
|
|
return
|
|
}
|
|
|
|
viewDidLoad = true
|
|
|
|
let size = UIScreen.main.bounds.size
|
|
if size.width < size.height {
|
|
orientationInfo.orientation = .portrait
|
|
} else {
|
|
orientationInfo.orientation = .landscape
|
|
}
|
|
|
|
ImageCache.shared.costLimit = 125 * 1024 * 1024 // 125MB memory
|
|
DataLoader.sharedUrlCache.diskCapacity = 1000 * 1024 * 1024 // 1000MB disk
|
|
|
|
if servers.isEmpty {
|
|
isLoading = false
|
|
needsToSelectServer = true
|
|
} else {
|
|
isLoading = true
|
|
let savedUser = savedUsers[0]
|
|
|
|
let keychain = KeychainSwift()
|
|
if keychain.get("AccessToken_\(savedUser.user_id ?? "")") != nil {
|
|
globalData.authToken = keychain.get("AccessToken_\(savedUser.user_id ?? "")") ?? ""
|
|
globalData.server = servers[0]
|
|
globalData.user = savedUser
|
|
}
|
|
|
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
|
var deviceName = UIDevice.current.name;
|
|
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
|
|
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]");
|
|
|
|
var header = "MediaBrowser "
|
|
header.append("Client=\"SwiftFin\", ")
|
|
header.append("Device=\"\(deviceName)\", ")
|
|
header.append("DeviceId=\"\(globalData.user.device_uuid ?? "")\", ")
|
|
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
|
|
header.append("Token=\"\(globalData.authToken)\"")
|
|
|
|
globalData.authHeader = header
|
|
JellyfinAPI.basePath = globalData.server.baseURI ?? ""
|
|
JellyfinAPI.customHeaders = ["X-Emby-Authorization": globalData.authHeader]
|
|
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
UserAPI.getCurrentUser()
|
|
.sink(receiveCompletion: { completion in
|
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
|
loadState = loadState - 1
|
|
}, receiveValue: { response in
|
|
libraries = response.configuration?.orderedViews ?? []
|
|
librariesShowRecentlyAdded = libraries.filter { element in
|
|
return !(response.configuration?.latestItemsExcludes?.contains(element))!
|
|
}
|
|
|
|
if(loadState == 1) {
|
|
isLoading = false
|
|
}
|
|
})
|
|
.store(in: &globalData.pendingAPIRequests)
|
|
|
|
UserViewsAPI.getUserViews(userId: globalData.user.user_id ?? "")
|
|
.sink(receiveCompletion: { completion in
|
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
|
loadState = loadState - 1
|
|
}, receiveValue: { response in
|
|
response.items?.forEach({ item in
|
|
library_names[item.id ?? ""] = item.name
|
|
})
|
|
|
|
if(loadState == 1) {
|
|
isLoading = false
|
|
}
|
|
})
|
|
.store(in: &globalData.pendingAPIRequests)
|
|
}
|
|
|
|
let defaults = UserDefaults.standard
|
|
if defaults.integer(forKey: "InNetworkBandwidth") == 0 {
|
|
defaults.setValue(40_000_000, forKey: "InNetworkBandwidth")
|
|
}
|
|
if defaults.integer(forKey: "OutOfNetworkBandwidth") == 0 {
|
|
defaults.setValue(40_000_000, forKey: "OutOfNetworkBandwidth")
|
|
}
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
if (needsToSelectServer == true) {
|
|
NavigationView {
|
|
ConnectToServerView(isActive: $needsToSelectServer)
|
|
}
|
|
.navigationViewStyle(StackNavigationViewStyle())
|
|
.environmentObject(globalData)
|
|
} else if (globalData.expiredCredentials == true) {
|
|
NavigationView {
|
|
ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server,
|
|
reauth_deviceId: globalData.user.device_uuid ?? "", isActive: $globalData.expiredCredentials)
|
|
}
|
|
.navigationViewStyle(StackNavigationViewStyle())
|
|
.environmentObject(globalData)
|
|
} else {
|
|
if !jsi.did {
|
|
LoadingView(isShowing: $isLoading) {
|
|
VStack() {
|
|
if(loadState == 0) {
|
|
TabView(selection: $tabSelection) {
|
|
NavigationView {
|
|
VStack(alignment: .leading) {
|
|
ScrollView {
|
|
Spacer().frame(height: orientationInfo.orientation == .portrait ? 0 : 16)
|
|
ContinueWatchingView()
|
|
NextUpView()
|
|
ForEach(librariesShowRecentlyAdded, id: \.self) { library_id in
|
|
VStack(alignment: .leading) {
|
|
HStack {
|
|
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold)
|
|
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
|
|
Spacer()
|
|
NavigationLink(destination: LazyView {
|
|
LibraryView(usingParentID: library_id,
|
|
title: library_names[library_id] ?? "", usingFilters: recentFilterSet)
|
|
}) {
|
|
Text("See All").font(.subheadline).fontWeight(.bold)
|
|
}
|
|
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
|
LatestMediaView(usingParentID: library_id)
|
|
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
|
|
}
|
|
Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 20 : 30)
|
|
}
|
|
.navigationTitle("Home")
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
|
Button {
|
|
showSettingsPopover = true
|
|
} label: {
|
|
Image(systemName: "gear")
|
|
}
|
|
}
|
|
}
|
|
.fullScreenCover(isPresented: $showSettingsPopover) {
|
|
SettingsView(viewModel: SettingsViewModel(), close: $showSettingsPopover)
|
|
}
|
|
}
|
|
}
|
|
.navigationViewStyle(StackNavigationViewStyle())
|
|
.tabItem {
|
|
Text("Home")
|
|
Image(systemName: "house")
|
|
}
|
|
.tag("Home")
|
|
NavigationView {
|
|
LibraryListView(libraries: library_names)
|
|
}
|
|
.navigationViewStyle(StackNavigationViewStyle())
|
|
.tabItem {
|
|
Text("All Media")
|
|
Image(systemName: "folder")
|
|
}
|
|
.tag("All Media")
|
|
}
|
|
} else {
|
|
Text("Loading...")
|
|
}
|
|
}
|
|
}
|
|
.environmentObject(globalData)
|
|
.onAppear(perform: startup)
|
|
.alert(isPresented: $globalData.networkError) {
|
|
Alert(title: Text("Network Error"), message: Text("An error occured while performing a network request"), dismissButton: .default(Text("Ok")))
|
|
}
|
|
} else {
|
|
Text("Please wait...")
|
|
.onAppear(perform: {
|
|
DispatchQueue.main.async { [self] in
|
|
_viewDidLoad.wrappedValue = false
|
|
usleep(500_000)
|
|
self.jsi.did = false
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|