From fdea0d4573a5721a4aae1f3a07c999cd4aa06409 Mon Sep 17 00:00:00 2001 From: Aiden Vigue Date: Sat, 22 May 2021 18:34:58 -0400 Subject: [PATCH] cleanup --- JellyfinPlayer.xcodeproj/project.pbxproj | 4 ++-- JellyfinPlayer/ConnectToServerView.swift | 27 ++++++--------------- JellyfinPlayer/ContentView.swift | 11 ++------- JellyfinPlayer/EpisodeItemView.swift | 30 +++++++++--------------- JellyfinPlayer/Info.plist | 2 +- JellyfinPlayer/JellyfinPlayerApp.swift | 17 ++++---------- JellyfinPlayer/MovieItemView.swift | 25 +++++++++----------- JellyfinPlayer/PlayerDemo.swift | 26 ++++---------------- fastlane/Fastfile | 18 +++++++------- 9 files changed, 52 insertions(+), 108 deletions(-) diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index b9180ac4..40cf4ada 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -470,7 +470,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\""; DEVELOPMENT_TEAM = 9R8RREG67J; ENABLE_BITCODE = NO; @@ -499,7 +499,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer/Preview Content\""; DEVELOPMENT_TEAM = 9R8RREG67J; diff --git a/JellyfinPlayer/ConnectToServerView.swift b/JellyfinPlayer/ConnectToServerView.swift index fc7cb5f7..c797934c 100644 --- a/JellyfinPlayer/ConnectToServerView.swift +++ b/JellyfinPlayer/ConnectToServerView.swift @@ -16,6 +16,7 @@ import Sentry struct ConnectToServerView: View { @Environment(\.managedObjectContext) private var viewContext + @EnvironmentObject var globalData: GlobalData @EnvironmentObject var jsi: justSignedIn @State private var uri = ""; @State private var isWorking = false; @@ -80,12 +81,9 @@ struct ConnectToServerView: View { switch result { case .success(let response): let server = response.body - print("Found server: " + server.ServerName) _serverName.wrappedValue = server.ServerName _server_id.wrappedValue = server.Id - if(!server.StartupWizardCompleted) { - print("Server needs configured") - } else { + if(server.StartupWizardCompleted) { _isConnected.wrappedValue = true; } case .failure(_): @@ -97,7 +95,7 @@ struct ConnectToServerView: View { HStack { Text("Connect") Spacer() - ProgressView().isHidden(!isWorking) + ProgressView().isHidden(!isWorking) } }.disabled(isWorking || uri.isEmpty) }.alert(isPresented: $isErrored) { @@ -108,14 +106,13 @@ struct ConnectToServerView: View { TextField("Username", text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField("Password", text: $password) + SecureField("Password (optional)", text: $password) .disableAutocorrection(true) .autocapitalization(.none) Button { _isWorking.wrappedValue = true let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String; let authHeader = "MediaBrowser Client=\"SwiftFin\", Device=\"\(UIDevice.current.name)\", DeviceId=\"\(serverSkipped ? reauthDeviceID : userUUID.uuidString)\", Version=\"\(appVersion ?? "0.0.1")\""; - print(authHeader) let authJson: [String: Any] = ["Username": _username.wrappedValue, "Pw": _password.wrappedValue] let request = RestRequest(method: .post, url: uri + "/Users/authenticatebyname") request.headerParameters["X-Emby-Authorization"] = authHeader @@ -128,8 +125,6 @@ struct ConnectToServerView: View { case .success(let response): do { let json = try JSON(data: response.body) - dump(json) - let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "Server") let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) @@ -158,21 +153,18 @@ struct ConnectToServerView: View { newUser.username = _username.wrappedValue newUser.user_id = json["User"]["Id"].string ?? "" + globalData.authHeader = authHeader + let keychain = KeychainSwift() keychain.set(json["AccessToken"].string ?? "", forKey: "AccessToken_\(json["User"]["Id"].string ?? "")") do { try viewContext.save() - print("Saved to Core Data Store") - _rootIsActive.wrappedValue = false DispatchQueue.main.async { [self] in jsi.did = true } } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + SentrySDK.capture(error: error) } } catch { @@ -196,14 +188,9 @@ struct ConnectToServerView: View { } } }.navigationTitle("Connect to Server") - .navigationViewStyle(StackNavigationViewStyle()) - .navigationBarBackButtonHidden(true) .alert(isPresented: $serverSkippedAlert) { Alert(title: Text("Error"), message: Text("Credentials have expired"), dismissButton: .default(Text("Sign in again"))) } .onAppear(perform: start) - .introspectTabBarController { (UITabBarController) in - UITabBarController.tabBar.isHidden = true - } } } diff --git a/JellyfinPlayer/ContentView.swift b/JellyfinPlayer/ContentView.swift index fe0893bd..a148c9d0 100644 --- a/JellyfinPlayer/ContentView.swift +++ b/JellyfinPlayer/ContentView.swift @@ -345,12 +345,6 @@ struct ContentView: View { TabView(selection: $tabSelection) { NavigationView() { VStack { - NavigationLink(destination: ConnectToServerView(isActive: $needsToSelectServer), isActive: $needsToSelectServer) { - EmptyView() - }.isDetailLink(false) - NavigationLink(destination: ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server, reauth_deviceId: globalData.user?.device_uuid ?? "", isActive: $isSignInErrored), isActive: $isSignInErrored) { - EmptyView() - }.isDetailLink(false) if(!needsToSelectServer && !isSignInErrored) { VStack(alignment: .leading) { ScrollView() { @@ -362,7 +356,7 @@ struct ContentView: View { HStack() { Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold).padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16)) Spacer() - NavigationLink(destination: LibraryView(prefill: library_id, names: library_names, libraries: libraries, filter: "&SortBy=DateCreated&SortOrder=Descending")) { + NavigationLink(destination: LibraryView(prefill: library_id, names: [library_id: library_names[library_id] ?? ""], libraries: [library_id], filter: "&SortBy=DateCreated&SortOrder=Descending")) { Text("See All").font(.subheadline).fontWeight(.bold) } }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) @@ -400,9 +394,8 @@ struct ContentView: View { }) .tag("All Media") - }.edgesIgnoringSafeArea(isPortrait ? [] : [.leading,.trailing]) + } }.environmentObject(globalData) - .edgesIgnoringSafeArea(isPortrait ? [] : [.leading,.trailing]) .onAppear(perform: startup) .navigationViewStyle(StackNavigationViewStyle()) .alert(isPresented: $isNetworkErrored) { diff --git a/JellyfinPlayer/EpisodeItemView.swift b/JellyfinPlayer/EpisodeItemView.swift index 99a184dc..6f1b3b8d 100644 --- a/JellyfinPlayer/EpisodeItemView.swift +++ b/JellyfinPlayer/EpisodeItemView.swift @@ -21,6 +21,8 @@ struct EpisodeItemView: View { @State private var playing: Bool = false; @State private var vc: PreferenceUIHostingController? = nil; @State private var progressString: String = ""; + @State private var viewDidLoad: Bool = false; + @State private var watched: Bool = false { didSet { if(watched == true) { @@ -83,17 +85,20 @@ struct EpisodeItemView: View { } } - func loadData() { - if(UIDevice.current.orientation.isLandscape) { - orientationInfo.orientation = .landscape; - } else { - orientationInfo.orientation = .portrait; - } + func unlockOrientations() { if(_vc.wrappedValue != nil) { _vc.wrappedValue?._prefersHomeIndicatorAutoHidden = false; _vc.wrappedValue?._orientations = .allButUpsideDown; _vc.wrappedValue?._viewPreference = .unspecified; } + } + + func loadData() { + unlockOrientations(); + if(_viewDidLoad.wrappedValue == true) { + return + } + _viewDidLoad.wrappedValue = true; let url = "/Users/\(globalData.user?.user_id ?? "")/Items/\(item.Id)" let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url) @@ -202,14 +207,6 @@ struct EpisodeItemView: View { } } - @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? - @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? - - var isPortrait: Bool { - let result = verticalSizeClass == .regular && horizontalSizeClass == .compact - return result - } - var body: some View { if(playing) { PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations) @@ -586,16 +583,11 @@ struct EpisodeItemView: View { } .navigationBarTitleDisplayMode(.inline) .navigationTitle("\(fullItem.Name) - S\(String(fullItem.ParentIndexNumber ?? 0)):E\(String(fullItem.IndexNumber ?? 0)) - \(fullItem.SeriesName ?? "")") - .supportedOrientations(.allButUpsideDown) - .prefersHomeIndicatorAutoHidden(false) .withHostingWindow() { window in let rootVC = window?.rootViewController; let UIHostingcontroller: PreferenceUIHostingController = rootVC as! PreferenceUIHostingController; vc = UIHostingcontroller; } - .introspectTabBarController { (UITabBarController) in - UITabBarController.tabBar.isHidden = false - } }.onAppear(perform: loadData) } } diff --git a/JellyfinPlayer/Info.plist b/JellyfinPlayer/Info.plist index 476332f2..a5fb7843 100644 --- a/JellyfinPlayer/Info.plist +++ b/JellyfinPlayer/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 9 + 10 DTXApplicationID 8c1f6941-ec78-480c-b589-b41aca29a52e DTXBeaconURL diff --git a/JellyfinPlayer/JellyfinPlayerApp.swift b/JellyfinPlayer/JellyfinPlayerApp.swift index 8e20f32d..90c2b8e1 100644 --- a/JellyfinPlayer/JellyfinPlayerApp.swift +++ b/JellyfinPlayer/JellyfinPlayerApp.swift @@ -24,29 +24,20 @@ class OrientationInfo: ObservableObject { case landscape } - @Published var orientation: Orientation + @Published var orientation: Orientation = .portrait; private var _observer: NSObjectProtocol? init() { - // fairly arbitrary starting value for 'flat' orientations - if UIDevice.current.orientation.isLandscape { - self.orientation = .landscape - } - else { - self.orientation = .portrait - } - - // unowned self because we unregister before self becomes invalid - _observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [unowned self] note in + _observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: nil) { [weak self] note in guard let device = note.object as? UIDevice else { return } if device.orientation.isPortrait { - self.orientation = .portrait + self?.orientation = .portrait } else if device.orientation.isLandscape { - self.orientation = .landscape + self?.orientation = .landscape } } } diff --git a/JellyfinPlayer/MovieItemView.swift b/JellyfinPlayer/MovieItemView.swift index f7a0fcdd..35c00fc8 100644 --- a/JellyfinPlayer/MovieItemView.swift +++ b/JellyfinPlayer/MovieItemView.swift @@ -69,6 +69,8 @@ struct MovieItemView: View { @State private var playing: Bool = false; @State private var vc: PreferenceUIHostingController? = nil; @State private var progressString: String = ""; + @State private var viewDidLoad: Bool = false; + @State private var watched: Bool = false { didSet { if(watched == true) { @@ -130,12 +132,20 @@ struct MovieItemView: View { } } - func loadData() { + func unlockOrientations() { if(_vc.wrappedValue != nil) { _vc.wrappedValue?._prefersHomeIndicatorAutoHidden = false; _vc.wrappedValue?._orientations = .allButUpsideDown; _vc.wrappedValue?._viewPreference = .unspecified; } + } + + func loadData() { + unlockOrientations() + if(_viewDidLoad.wrappedValue == true) { + return; + } + _viewDidLoad.wrappedValue = true; let url = "/Users/\(globalData.user?.user_id ?? "")/Items/\(item.Id)" let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url) @@ -243,14 +253,6 @@ struct MovieItemView: View { } } - @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? - @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? - - var isPortrait: Bool { - let result = verticalSizeClass == .regular && horizontalSizeClass == .compact - return result - } - var body: some View { if(playing) { PlayerDemo(item: fullItem, playing: $playing).onAppear(perform: lockOrientations) @@ -626,16 +628,11 @@ struct MovieItemView: View { } .navigationBarTitleDisplayMode(.inline) .navigationTitle(fullItem.Name) - .supportedOrientations(.allButUpsideDown) - .prefersHomeIndicatorAutoHidden(false) .withHostingWindow() { window in let rootVC = window?.rootViewController; let UIHostingcontroller: PreferenceUIHostingController = rootVC as! PreferenceUIHostingController; vc = UIHostingcontroller; } - .introspectTabBarController { (UITabBarController) in - UITabBarController.tabBar.isHidden = false - } }.onAppear(perform: loadData) } } diff --git a/JellyfinPlayer/PlayerDemo.swift b/JellyfinPlayer/PlayerDemo.swift index d9bd1aeb..a42f004f 100644 --- a/JellyfinPlayer/PlayerDemo.swift +++ b/JellyfinPlayer/PlayerDemo.swift @@ -38,11 +38,10 @@ extension String { } struct PlayerDemo: View { @EnvironmentObject var globalData: GlobalData - @Environment(\.presentationMode) var presentationMode: Binding var item: DetailItem; @State private var pbitem: PlaybackItem = PlaybackItem(videoType: VideoType.direct, videoUrl: URL(string: "https://example.com")!, subtitles: []); @State private var streamLoading = false; - @State private var vlcplayer: VLCMediaPlayer = VLCMediaPlayer(options: ["-vv", "--sub-margin=-50"]); + @State private var vlcplayer: VLCMediaPlayer = VLCMediaPlayer(options: ["--sub-margin=-50"]); @State private var isPlaying = false; @State private var subtitles: [Subtitle] = []; @State private var inactivity: Bool = true; @@ -206,7 +205,7 @@ struct PlayerDemo: View { func startStream() { _streamLoading.wrappedValue = true; - let request = RestRequest(method: .post, url: (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=0&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=70000000") + let request = RestRequest(method: .post, url: (globalData.server?.baseURI ?? "") + "/Items/\(item.Id)/PlaybackInfo?UserId=\(globalData.user?.user_id ?? "")&StartTimeTicks=\(item.Progress)&IsPlayback=true&AutoOpenLiveStream=true&MaxStreamingBitrate=70000000") request.headerParameters["X-Emby-Authorization"] = globalData.authHeader request.contentType = "application/json" request.acceptType = "application/json" @@ -220,14 +219,7 @@ struct PlayerDemo: View { let json = try JSON(data: body) _playSessionId.wrappedValue = json["PlaySessionId"].string ?? ""; if(json["MediaSources"][0]["TranscodingUrl"].string != nil) { - //Video is transcoded due to TranscodingReason - also may just be remuxed - for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] { - if(stream["Type"].string == "Subtitle") { - print("Found subtitle track: \(stream["DeliveryUrl"].string ?? "")") - } - } let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")\((json["MediaSources"][0]["TranscodingUrl"].string ?? "").replacingOccurrences(of: "master.m3u8", with: "main.m3u8"))")! - print(streamURL); let item = PlaybackItem(videoType: VideoType.hls, videoUrl: streamURL, subtitles: []) let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed") _subtitles.wrappedValue.append(disableSubtitleTrack); @@ -243,14 +235,12 @@ struct PlayerDemo: View { pbitem.subtitles = subtitles; _isPlaying.wrappedValue = true; } else { - print("Direct play of item \(item.Name)") let streamURL: URL = URL(string: "\(globalData.server?.baseURI ?? "")/Videos/\(item.Id)/stream?Static=true&mediaSourceId=\(item.Id)&deviceId=\(globalData.user?.device_uuid ?? "")&api_key=\(globalData.authToken)&Tag=\(json["MediaSources"][0]["ETag"])")!; let item = PlaybackItem(videoType: VideoType.direct, videoUrl: streamURL, subtitles: []) let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: "Embed") _subtitles.wrappedValue.append(disableSubtitleTrack); for (_,stream):(String, JSON) in json["MediaSources"][0]["MediaStreams"] { if(stream["Type"].string == "Subtitle") { - print("Found subtitle track with title \(stream["DisplayTitle"].string ?? "") Delivery method: \(stream["DeliveryMethod"].string ?? "")") let deliveryUrl = URL(string: "\(globalData.server?.baseURI ?? "")\(stream["DeliveryUrl"].string ?? "")")! let subtitle = Subtitle(name: stream["DisplayTitle"].string ?? "", id: Int32(stream["Index"].int ?? 0), url: deliveryUrl, delivery: stream["DeliveryMethod"].string ?? "") _subtitles.wrappedValue.append(subtitle); @@ -260,7 +250,8 @@ struct PlayerDemo: View { pbitem.subtitles = subtitles; _isPlaying.wrappedValue = true; } - DispatchQueue.global(qos: .userInitiated).async { [self] in + + DispatchQueue.global(qos: .userInteractive).async { [self] in self.keepUpWithPlayerState() } } catch { @@ -280,8 +271,7 @@ struct PlayerDemo: View { while(vlcplayer.state == VLCMediaPlayerState.paused) { let secondsScrubbedTo = round(_scrub.wrappedValue * videoDuration); let scrubRemaining = videoDuration - secondsScrubbedTo; - usleep(10000) - + usleep(50000) let remainingTime = scrubRemaining; let hours = floor(remainingTime / 3600); let minutes = (remainingTime.truncatingRemainder(dividingBy: 3600)) / 60; @@ -401,14 +391,8 @@ struct PlayerDemo: View { .navigationBarHidden(true) .navigationBarBackButtonHidden(true) .statusBar(hidden: true) - .introspectTabBarController { (UITabBarController) in - UITabBarController.tabBar.isHidden = true - } - .prefersHomeIndicatorAutoHidden(true) - .supportedOrientations(.landscapeRight) .edgesIgnoringSafeArea(.all) .onTapGesture(perform: resetTimer) - .overrideViewPreference(.dark) .fullScreenCover(isPresented: self.$captionConfiguration) { NavigationView() { VStack() { diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c78488a6..0e3d2a87 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -22,15 +22,6 @@ platform :ios do gym(output_name: "JellyfinPlayer.ipa") identifier_v = get_version_number(xcodeproj: "JellyfinPlayer.xcodeproj") identifier_s = get_info_plist_value(path: "JellyfinPlayer/Info.plist", key: "CFBundleVersion") - dynatrace_process_symbols( - appId: "8c1f6941-ec78-480c-b589-b41aca29a52e", - os: "ios", - bundleId: "me.vigue.jellyfin", - versionStr: identifier_v, - version: identifier_s, - server: "https://ofa89490.live.dynatrace.com", - symbolsfile: "JellyfinPlayer.app.dSYM.zip" - ) upload_symbols_to_sentry( org_slug: 'aidenvigue', project_slug: 'jellyfin-swift-ios', @@ -45,5 +36,14 @@ platform :ios do upload_assets: ["JellyfinPlayer.ipa"] ) upload_to_testflight + dynatrace_process_symbols( + appId: "8c1f6941-ec78-480c-b589-b41aca29a52e", + os: "ios", + bundleId: "me.vigue.jellyfin", + versionStr: identifier_v, + version: identifier_s, + server: "https://ofa89490.live.dynatrace.com", + symbolsfile: "JellyfinPlayer.app.dSYM.zip" + ) end end