add ServerEnvironment.setUp, reset func

add SessionManager.updateHeader, login, logout
This commit is contained in:
PangMo5 2021-06-14 02:40:19 +09:00
parent bc03de481d
commit 40127b0710
12 changed files with 159 additions and 57 deletions

View File

@ -97,6 +97,8 @@
62EC3530267666A5000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
62EC353126766848000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
62EC353526766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
/* End PBXBuildFile section */
@ -219,6 +221,7 @@
628B953B2670D1FC0091AF3B /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.swift; sourceTree = "<group>"; };
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -402,6 +405,7 @@
621338922660107500A81A2A /* StringExtensions.swift */,
6267B3D526710B8900A7371D /* CollectionExtensions.swift */,
6267B3D92671138200A7371D /* ImageExtensions.swift */,
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -609,6 +613,7 @@
5321753F2671DEA6005491E6 /* SettingsViewModel.swift in Sources */,
535870AA2669D8AE00D05A09 /* BlurHashDecode.swift in Sources */,
535870652669D21600D05A09 /* ContentView.swift in Sources */,
62EC353526766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
535870A62669D8AE00D05A09 /* LazyView.swift in Sources */,
53C4404F266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */,
5358706F2669D21700D05A09 /* JellyfinPlayer_tvOS.xcdatamodeld in Sources */,
@ -653,6 +658,7 @@
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */,
5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */,
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,

View File

@ -12,7 +12,6 @@ import JellyfinAPI
struct ConnectToServerView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var globalData: GlobalData
@EnvironmentObject var jsi: justSignedIn
@State private var uri = ""

View File

@ -17,8 +17,6 @@ struct ContentView: View {
@EnvironmentObject var orientationInfo: OrientationInfo
@EnvironmentObject var jsi: justSignedIn
@StateObject private var globalData = GlobalData()
@State private var needsToSelectServer = false
@State private var isLoading = false
@State private var tabSelection: String = "Home"

View File

@ -9,7 +9,6 @@ import Foundation
import SwiftUI
struct LibraryListView: View {
@EnvironmentObject var globalData: GlobalData
@State var library_ids: [String] = ["favorites", "genres"]
@State var library_names: [String: String] = ["favorites": "Favorites", "genres": "Genres"]

View File

@ -9,7 +9,6 @@ import SwiftUI
import JellyfinAPI
struct NextUpView: View {
@EnvironmentObject var globalData: GlobalData
@State private var items: [BaseItemDto] = []
@State private var viewDidLoad: Bool = false

View File

@ -9,7 +9,6 @@ import SwiftUI
import JellyfinAPI
struct SeriesItemView: View {
@EnvironmentObject private var globalData: GlobalData
@EnvironmentObject private var orientationInfo: OrientationInfo
var item: BaseItemDto

View File

@ -11,7 +11,6 @@ import SwiftUI
struct SettingsView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var globalData: GlobalData
@EnvironmentObject var jsi: justSignedIn
@ObservedObject var viewModel: SettingsViewModel

View File

@ -39,7 +39,6 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
weak var delegate: PlayerViewControllerDelegate?
var mediaPlayer = VLCMediaPlayer()
var globalData = GlobalData()
@IBOutlet weak var timeText: UILabel!
@IBOutlet weak var videoContentView: UIView!

View File

@ -0,0 +1,33 @@
//
/*
* 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
*/
// https://www.hackingwithswift.com/quick-start/swiftui/how-to-detect-device-rotation
import Foundation
import SwiftUI
// Our custom view modifier to track rotation and
// call our action
struct DeviceRotationViewModifier: ViewModifier {
let action: (UIDeviceOrientation) -> Void
func body(content: Content) -> some View {
content
.onAppear()
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
action(UIDevice.current.orientation)
}
}
}
// A View wrapper to make the modifier easier to use
extension View {
func onRotate(perform action: @escaping (UIDeviceOrientation) -> Void) -> some View {
self.modifier(DeviceRotationViewModifier(action: action))
}
}

View File

@ -1,23 +1,59 @@
//
/*
* 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
*/
/*
* 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 Foundation
import Combine
import CoreData
import Foundation
import JellyfinAPI
final class ServerEnvironment {
static let shared = ServerEnvironment()
var server: Server?
fileprivate(set) var server: Server!
init() {
let serverRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Server")
let servers = try? PersistenceController.shared.container.viewContext.fetch(serverRequest) as? [Server]
server = servers?.first
guard let baseURI = server?.baseURI else { return }
JellyfinAPI.basePath = baseURI
}
func setUp(with uri: String) -> AnyPublisher<Server, Error> {
var uri = uri
if !uri.contains("http") {
uri = "https://" + uri
}
if uri.last == "/" {
uri = String(uri.dropLast())
}
JellyfinAPI.basePath = uri
return SystemAPI.getPublicSystemInfo()
.map { response in
let server = Server(context: PersistenceController.shared.container.viewContext)
server.baseURI = uri
server.name = response.serverName
server.server_id = response.id
return server
}
.handleEvents(receiveOutput: { [unowned self] response in
server = response
_ = try? PersistenceController.shared.container.viewContext.save()
}).eraseToAnyPublisher()
}
func reset() throws {
JellyfinAPI.basePath = ""
server = nil
let serverRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: serverRequest)
try PersistenceController.shared.container.viewContext.execute(deleteRequest)
}
}

View File

@ -1,35 +1,47 @@
//
/*
* 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
*/
/*
* 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 Foundation
import Combine
import CoreData
import Foundation
import JellyfinAPI
import KeychainSwift
import UIKit
final class SessionManager {
static let shared = SessionManager()
var user: SignedInUser?
var authHeader: String?
fileprivate(set) var user: SignedInUser!
fileprivate(set) var authHeader: String!
fileprivate(set) var deviceIDString: String
init() {
let savedUserRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "SignedInUser")
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest) as? [SignedInUser]
user = savedUsers?.first
let keychain = KeychainSwift()
// need prefix
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
if let deviceID = keychain.get("DeviceIDString") {
self.deviceIDString = deviceID
} else {
self.deviceIDString = UUID().uuidString
keychain.set(deviceIDString, forKey: "DeviceIDString")
}
guard let authToken = keychain.get("AccessToken_\(user?.user_id ?? "")") else {
return
}
updateHeader(with: authToken)
}
func updateHeader(with authToken: String?) {
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
var deviceName = UIDevice.current.name
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
@ -38,10 +50,54 @@ final class SessionManager {
var header = "MediaBrowser "
header.append("Client=\"SwiftFin\", ")
header.append("Device=\"\(deviceName)\", ")
header.append("DeviceId=\"\(user?.device_uuid ?? "")\", ")
header.append("DeviceId=\"\(deviceIDString)\", ")
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
header.append("Token=\"\(authToken)\"")
if let token = authToken {
header.append("Token=\"\(token)\"")
}
self.authHeader = header
authHeader = header
JellyfinAPI.customHeaders["X-Emby-Authorization"] = authHeader
}
func login(username: String, password: String) -> AnyPublisher<SignedInUser, Error> {
updateHeader(with: nil)
return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password))
.map { [unowned self] response -> (SignedInUser, String?) in
let user = SignedInUser(context: PersistenceController.shared.container.viewContext)
user.device_uuid = deviceIDString
user.username = response.user?.name
user.user_id = response.user?.id
return (user, response.accessToken)
}
.handleEvents(receiveOutput: { [unowned self] response, accessToken in
user = response
_ = try? PersistenceController.shared.container.viewContext.save()
if let userID = user.user_id,
let token = accessToken
{
let keychain = KeychainSwift()
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
keychain.set(token, forKey: "AccessToken_\(userID)")
}
updateHeader(with: accessToken)
})
.map(\.0)
.eraseToAnyPublisher()
}
func logout() throws {
let keychain = KeychainSwift()
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
keychain.delete("AccessToken_\(user.user_id ?? "")")
JellyfinAPI.customHeaders["X-Emby-Authorization"] = nil
user = nil
authHeader = nil
let userRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "SignedInUser")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: userRequest)
try PersistenceController.shared.container.viewContext.execute(deleteRequest)
}
}

View File

@ -26,24 +26,3 @@ public enum SortBy: String, Codable, CaseIterable {
class justSignedIn: ObservableObject {
@Published var did: Bool = false
}
class GlobalData: ObservableObject {
@Published var user: SignedInUser!
@Published var authToken: String = ""
@Published var server: Server!
@Published var authHeader: String = ""
@Published var isInNetwork: Bool = true
@Published var networkError: Bool = false
@Published var expiredCredentials: Bool = false
var pendingAPIRequests = Set<AnyCancellable>()
}
extension GlobalData: Equatable {
static func == (lhs: GlobalData, rhs: GlobalData) -> Bool {
lhs.user == rhs.user
&& lhs.authToken == rhs.authToken
&& lhs.server == rhs.server
&& lhs.authHeader == rhs.authHeader
}
}