From 988377e0ea769a700c1173a0e46b4f7ea9e6c479 Mon Sep 17 00:00:00 2001 From: Aiden Vigue Date: Wed, 9 Jun 2021 21:25:50 -0700 Subject: [PATCH] fix --- JellyfinPlayer tvOS/Info.plist | 2 +- JellyfinPlayer.xcodeproj/project.pbxproj | 20 ++- JellyfinPlayer/ConnectToServerView.swift | 2 - JellyfinPlayer/ContentView.swift | 60 +++++---- JellyfinPlayer/ContinueWatchingView.swift | 16 ++- JellyfinPlayer/Info.plist | 2 +- JellyfinPlayer/JellyfinPlayer.entitlements | 4 +- JellyfinPlayer/LatestMediaView.swift | 16 ++- JellyfinPlayer/LibrarySearchView.swift | 20 +-- JellyfinPlayer/LibraryView.swift | 27 ++-- JellyfinPlayer/NextUpView.swift | 16 ++- JellyfinPlayer/SeasonItemView.swift | 20 +-- JellyfinPlayer/SeriesItemView.swift | 21 +-- JellyfinPlayer/SettingsView.swift | 67 ++------- JellyfinPlayer/VideoPlayer.swift | 150 ++++++++++----------- Shared/Typings/Typings.swift | 2 +- Shared/ViewModel/SettingsViewModel.swift | 43 ++++++ fastlane/Fastfile | 16 ++- fastlane/README.md | 14 +- 19 files changed, 276 insertions(+), 242 deletions(-) create mode 100644 Shared/ViewModel/SettingsViewModel.swift diff --git a/JellyfinPlayer tvOS/Info.plist b/JellyfinPlayer tvOS/Info.plist index 4897485f..0c4c61b7 100644 --- a/JellyfinPlayer tvOS/Info.plist +++ b/JellyfinPlayer tvOS/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1 + 36 LSRequiresIPhoneOS UILaunchScreen diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index 65c3d247..acbed074 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; }; + 5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */; }; 53313B90265EEA6D00947AA3 /* VideoPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */; }; 53352571265EA0A0006CCA86 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 53352570265EA0A0006CCA86 /* Introspect */; }; 5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; }; @@ -108,6 +109,7 @@ /* Begin PBXFileReference section */ 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = ""; }; + 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; 53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = VideoPlayer.storyboard; sourceTree = ""; }; 5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = ""; }; 535870602669D21600D05A09 /* JellyfinPlayer tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JellyfinPlayer tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -187,6 +189,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 532175392671BCED005491E6 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 5321753A2671BCFC005491E6 /* SettingsViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 535870612669D21600D05A09 /* JellyfinPlayer tvOS */ = { isa = PBXGroup; children = ( @@ -212,6 +222,7 @@ 535870752669D60C00D05A09 /* Shared */ = { isa = PBXGroup; children = ( + 532175392671BCED005491E6 /* ViewModel */, 621338912660106C00A81A2A /* Extensions */, AE8C3157265D6F5E008AA076 /* Resources */, 535870AB2669D8D300D05A09 /* Typings */, @@ -484,6 +495,7 @@ 62133890265F83A900A81A2A /* LibraryListView.swift in Sources */, 53892770263C25230035E14B /* NextUpView.swift in Sources */, 535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */, + 5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */, 5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */, 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */, 53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */, @@ -687,8 +699,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 33; + CURRENT_PROJECT_VERSION = 36; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9R8RREG67J; ENABLE_BITCODE = NO; @@ -703,6 +716,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; @@ -716,8 +730,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 33; + CURRENT_PROJECT_VERSION = 36; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9R8RREG67J; @@ -733,6 +748,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; diff --git a/JellyfinPlayer/ConnectToServerView.swift b/JellyfinPlayer/ConnectToServerView.swift index b3ce2658..b8f718e2 100644 --- a/JellyfinPlayer/ConnectToServerView.swift +++ b/JellyfinPlayer/ConnectToServerView.swift @@ -5,8 +5,6 @@ * Copyright 2021 Aiden Vigue & Jellyfin Contributors */ -//MARK: refactor this file! it's the first swift file I ever wrote and it clearly shows. - import SwiftUI import CoreData import KeychainSwift diff --git a/JellyfinPlayer/ContentView.swift b/JellyfinPlayer/ContentView.swift index f852dacd..5f6a16a5 100644 --- a/JellyfinPlayer/ContentView.swift +++ b/JellyfinPlayer/ContentView.swift @@ -85,36 +85,38 @@ struct ContentView: View { JellyfinAPI.basePath = globalData.server.baseURI ?? "" JellyfinAPI.customHeaders = ["X-Emby-Authorization": globalData.authHeader] - 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 + 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 + } }) - - if(loadState == 1) { - isLoading = false - } - }) - .store(in: &globalData.pendingAPIRequests) + .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 { diff --git a/JellyfinPlayer/ContinueWatchingView.swift b/JellyfinPlayer/ContinueWatchingView.swift index 8d47086c..e2c91a11 100644 --- a/JellyfinPlayer/ContinueWatchingView.swift +++ b/JellyfinPlayer/ContinueWatchingView.swift @@ -37,13 +37,15 @@ struct ContinueWatchingView: View { @State private var items: [BaseItemDto] = [] func onAppear() { - ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb]) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - }, receiveValue: { response in - items = response.items ?? [] - }) - .store(in: &globalData.pendingAPIRequests) + DispatchQueue.global(qos: .userInitiated).async { + ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb]) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + }, receiveValue: { response in + items = response.items ?? [] + }) + .store(in: &globalData.pendingAPIRequests) + } } var body: some View { diff --git a/JellyfinPlayer/Info.plist b/JellyfinPlayer/Info.plist index 4bfa545a..c14d7bca 100644 --- a/JellyfinPlayer/Info.plist +++ b/JellyfinPlayer/Info.plist @@ -27,7 +27,7 @@ network. CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 33 + 36 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/JellyfinPlayer/JellyfinPlayer.entitlements b/JellyfinPlayer/JellyfinPlayer.entitlements index 6d616546..e7a5f472 100644 --- a/JellyfinPlayer/JellyfinPlayer.entitlements +++ b/JellyfinPlayer/JellyfinPlayer.entitlements @@ -2,11 +2,11 @@ + com.apple.developer.coremedia.hls.low-latency + com.apple.security.app-sandbox com.apple.security.network.client - com.apple.developer.coremedia.hls.low-latency - diff --git a/JellyfinPlayer/LatestMediaView.swift b/JellyfinPlayer/LatestMediaView.swift index 7d8c7af6..26bd2f63 100644 --- a/JellyfinPlayer/LatestMediaView.swift +++ b/JellyfinPlayer/LatestMediaView.swift @@ -26,13 +26,15 @@ struct LatestMediaView: View { } viewDidLoad = true; - UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - }, receiveValue: { response in - items = response - }) - .store(in: &globalData.pendingAPIRequests) + DispatchQueue.global(qos: .userInitiated).async { + UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + }, receiveValue: { response in + items = response + }) + .store(in: &globalData.pendingAPIRequests) + } } var body: some View { diff --git a/JellyfinPlayer/LibrarySearchView.swift b/JellyfinPlayer/LibrarySearchView.swift index 45e1d391..0f7f9404 100644 --- a/JellyfinPlayer/LibrarySearchView.swift +++ b/JellyfinPlayer/LibrarySearchView.swift @@ -30,15 +30,17 @@ struct LibrarySearchView: View { func requestSearch(query: String) { isLoading = true - print(usingParentID) - ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - }, receiveValue: { response in - items = response.items ?? [] - isLoading = false - }) - .store(in: &globalData.pendingAPIRequests) + + DispatchQueue.global(qos: .userInitiated).async { + ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + }, receiveValue: { response in + items = response.items ?? [] + isLoading = false + }) + .store(in: &globalData.pendingAPIRequests) + } } //MARK: tracks for grid diff --git a/JellyfinPlayer/LibraryView.swift b/JellyfinPlayer/LibraryView.swift index 3da7828e..2d00b364 100644 --- a/JellyfinPlayer/LibraryView.swift +++ b/JellyfinPlayer/LibraryView.swift @@ -1,4 +1,5 @@ -/* JellyfinPlayer/Swiftfin is subject to the terms of the Mozilla Public +/* + * 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/. * @@ -61,17 +62,19 @@ struct LibraryView: View { isLoading = true items = [] - ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - isLoading = false - }, receiveValue: { response in - let x = ceil(Double(response.totalRecordCount!) / 100.0) - totalPages = Int(x) - items = response.items ?? [] - isLoading = false - }) - .store(in: &globalData.pendingAPIRequests) + DispatchQueue.global(qos: .userInitiated).async { + ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + isLoading = false + }, receiveValue: { response in + let x = ceil(Double(response.totalRecordCount!) / 100.0) + totalPages = Int(x) + items = response.items ?? [] + isLoading = false + }) + .store(in: &globalData.pendingAPIRequests) + } } //MARK: tracks for grid diff --git a/JellyfinPlayer/NextUpView.swift b/JellyfinPlayer/NextUpView.swift index 0265be14..58477209 100644 --- a/JellyfinPlayer/NextUpView.swift +++ b/JellyfinPlayer/NextUpView.swift @@ -21,13 +21,15 @@ struct NextUpView: View { } viewDidLoad = true; - TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people]) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - }, receiveValue: { response in - items = response.items ?? [] - }) - .store(in: &globalData.pendingAPIRequests) + DispatchQueue.global(qos: .userInitiated).async { + TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people]) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + }, receiveValue: { response in + items = response.items ?? [] + }) + .store(in: &globalData.pendingAPIRequests) + } } var body: some View { diff --git a/JellyfinPlayer/SeasonItemView.swift b/JellyfinPlayer/SeasonItemView.swift index 6b0f8231..8215b536 100644 --- a/JellyfinPlayer/SeasonItemView.swift +++ b/JellyfinPlayer/SeasonItemView.swift @@ -28,15 +28,17 @@ struct SeasonItemView: View { return } - TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id!) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - isLoading = false - }, receiveValue: { response in - viewDidLoad = true - episodes = response.items ?? [] - }) - .store(in: &globalData.pendingAPIRequests) + DispatchQueue.global(qos: .userInitiated).async { + TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id!) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + isLoading = false + }, receiveValue: { response in + viewDidLoad = true + episodes = response.items ?? [] + }) + .store(in: &globalData.pendingAPIRequests) + } } @ViewBuilder diff --git a/JellyfinPlayer/SeriesItemView.swift b/JellyfinPlayer/SeriesItemView.swift index 8f547d8f..55992c05 100644 --- a/JellyfinPlayer/SeriesItemView.swift +++ b/JellyfinPlayer/SeriesItemView.swift @@ -26,15 +26,18 @@ struct SeriesItemView: View { } isLoading = true - TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people]) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) - }, receiveValue: { response in - isLoading = false - viewDidLoad = true - seasons = response.items ?? [] - }) - .store(in: &globalData.pendingAPIRequests) + + DispatchQueue.global(qos: .userInitiated).async { + TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people]) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: globalData, completion: completion) + }, receiveValue: { response in + isLoading = false + viewDidLoad = true + seasons = response.items ?? [] + }) + .store(in: &globalData.pendingAPIRequests) + } } //MARK: Grid tracks diff --git a/JellyfinPlayer/SettingsView.swift b/JellyfinPlayer/SettingsView.swift index d5ae9398..cd922b0c 100644 --- a/JellyfinPlayer/SettingsView.swift +++ b/JellyfinPlayer/SettingsView.swift @@ -9,30 +9,20 @@ import CoreData import SwiftUI struct SettingsView: View { - @ObservedObject - var viewModel: SettingsViewModel + @Environment(\.managedObjectContext) private var viewContext + + @EnvironmentObject var globalData: GlobalData + @EnvironmentObject var jsi: justSignedIn + + @ObservedObject var viewModel: SettingsViewModel - @Binding - var close: Bool - @Environment(\.managedObjectContext) - private var viewContext - @EnvironmentObject - var globalData: GlobalData - @EnvironmentObject - var jsi: justSignedIn - @State - private var username: String = "" - @State - private var inNetworkStreamBitrate: Int = 40_000_000 - @State - private var outOfNetworkStreamBitrate: Int = 40_000_000 - @State - private var autoSelectSubtitles: Bool = false - @State - private var autoSelectSubtitlesLangcode: String = "none" + @Binding var close: Bool + @State private var inNetworkStreamBitrate: Int = 40_000_000 + @State private var outOfNetworkStreamBitrate: Int = 40_000_000 + @State private var autoSelectSubtitles: Bool = false + @State private var autoSelectSubtitlesLangcode: String = "none" func onAppear() { - _username.wrappedValue = globalData.user.username ?? "" let defaults = UserDefaults.standard _inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth") _outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth") @@ -73,7 +63,7 @@ struct SettingsView: View { Section { HStack { - Text("Signed in as \(username)").foregroundColor(.primary) + Text("Signed in as \(globalData.user.username!)").foregroundColor(.primary) Spacer() Button { let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "Server") @@ -121,36 +111,3 @@ struct SettingsView: View { }.onAppear(perform: onAppear) } } - -struct UserSettings: Decodable { - var LocalMaxBitrate: Int; - var RemoteMaxBitrate: Int; - var AutoSelectSubtitles: Bool; - var AutoSelectSubtitlesLangcode: String; - var SubtitlePositionOffset: Int; - var SubtitleFontName: String; -} - -struct Bitrates: Codable, Hashable { - public var name: String - public var value: Int -} - -final class SettingsViewModel: ObservableObject { - var bitrates: [Bitrates] = [] - - init() { - let url = Bundle.main.url(forResource: "bitrates", withExtension: "json")! - - do { - let jsonData = try Data(contentsOf: url, options: .mappedIfSafe) - do { - self.bitrates = try JSONDecoder().decode([Bitrates].self, from: jsonData) - } catch { - print(error) - } - } catch { - print(error) - } - } -} diff --git a/JellyfinPlayer/VideoPlayer.swift b/JellyfinPlayer/VideoPlayer.swift index 22b3e44f..abe365a9 100644 --- a/JellyfinPlayer/VideoPlayer.swift +++ b/JellyfinPlayer/VideoPlayer.swift @@ -287,88 +287,88 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe let playbackInfo = PlaybackInfoDto(userId: globalData.user.user_id, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, deviceProfile: profile, autoOpenLiveStream: true) - MediaInfoAPI.getPostedPlaybackInfo(itemId: manifest.id!, userId: globalData.user.user_id, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, playbackInfoDto: playbackInfo) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: self.globalData, completion: completion) - }, receiveValue: { [self] response in - playSessionId = response.playSessionId! - let mediaSource = response.mediaSources!.first.self! - if(mediaSource.transcodingUrl != nil) { - //Item is being transcoded by request of server - let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)") - let item = PlaybackItem() - item.videoType = .transcode - item.videoUrl = streamURL! - - let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "") - subtitleTrackArray.append(disableSubtitleTrack); - - //Loop through media streams and add to array - for stream in mediaSource.mediaStreams! { - if(stream.type == .subtitle) { - let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! - let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!) - subtitleTrackArray.append(subtitle); + DispatchQueue.global(qos: .userInitiated).async { [self] in + MediaInfoAPI.getPostedPlaybackInfo(itemId: manifest.id!, userId: globalData.user.user_id, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, playbackInfoDto: playbackInfo) + .sink(receiveCompletion: { completion in + HandleAPIRequestCompletion(globalData: self.globalData, completion: completion) + }, receiveValue: { [self] response in + playSessionId = response.playSessionId! + let mediaSource = response.mediaSources!.first.self! + if(mediaSource.transcodingUrl != nil) { + //Item is being transcoded by request of server + let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)") + let item = PlaybackItem() + item.videoType = .transcode + item.videoUrl = streamURL! + + let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "") + subtitleTrackArray.append(disableSubtitleTrack); + + //Loop through media streams and add to array + for stream in mediaSource.mediaStreams! { + if(stream.type == .subtitle) { + let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! + let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!) + subtitleTrackArray.append(subtitle); + } + + if(stream.type == .audio) { + let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!)) + if(stream.isDefault! == true) { + selectedAudioTrack = Int32(stream.index!); + } + audioTrackArray.append(subtitle); + } } - if(stream.type == .audio) { - let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!)) - if(stream.isDefault! == true) { - selectedAudioTrack = Int32(stream.index!); + if(selectedAudioTrack == -1) { + if(audioTrackArray.count > 0) { + selectedAudioTrack = audioTrackArray[0].id; } - audioTrackArray.append(subtitle); - } - } - - if(selectedAudioTrack == -1) { - if(audioTrackArray.count > 0) { - selectedAudioTrack = audioTrackArray[0].id; - } - } - - self.sendPlayReport() - playbackItem = item; - } else { - //Item will be directly played by the client. - let streamURL: URL = URL(string: "\(globalData.server.baseURI!)/Videos/\(manifest.id!)/stream?Static=true&mediaSourceId=\(manifest.id!)&deviceId=\(globalData.user.device_uuid!)&api_key=\(globalData.authToken)&Tag=\(mediaSource.eTag!)")!; - - let item = PlaybackItem() - item.videoUrl = streamURL - item.videoType = .directPlay - - let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "") - subtitleTrackArray.append(disableSubtitleTrack); - - //Loop through media streams and add to array - for stream in mediaSource.mediaStreams! { - if(stream.type == .subtitle) { - let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! - let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!) - subtitleTrackArray.append(subtitle); } - if(stream.type == .audio) { - let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!)) - if(stream.isDefault! == true) { - selectedAudioTrack = Int32(stream.index!); + self.sendPlayReport() + playbackItem = item; + } else { + //Item will be directly played by the client. + let streamURL: URL = URL(string: "\(globalData.server.baseURI!)/Videos/\(manifest.id!)/stream?Static=true&mediaSourceId=\(manifest.id!)&deviceId=\(globalData.user.device_uuid!)&api_key=\(globalData.authToken)&Tag=\(mediaSource.eTag!)")!; + + let item = PlaybackItem() + item.videoUrl = streamURL + item.videoType = .directPlay + + let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "") + subtitleTrackArray.append(disableSubtitleTrack); + + //Loop through media streams and add to array + for stream in mediaSource.mediaStreams! { + if(stream.type == .subtitle) { + let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! + let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!) + subtitleTrackArray.append(subtitle); + } + + if(stream.type == .audio) { + let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!)) + if(stream.isDefault! == true) { + selectedAudioTrack = Int32(stream.index!); + } + audioTrackArray.append(subtitle); } - audioTrackArray.append(subtitle); } + + if(selectedAudioTrack == -1) { + if(audioTrackArray.count > 0) { + selectedAudioTrack = audioTrackArray[0].id; + } + } + + self.sendPlayReport() + playbackItem = item; } - if(selectedAudioTrack == -1) { - if(audioTrackArray.count > 0) { - selectedAudioTrack = audioTrackArray[0].id; - } - } + self.setupNowPlayingCC() - self.sendPlayReport() - playbackItem = item; - } - - self.setupNowPlayingCC() - - DispatchQueue.global(qos: .background).async { mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl) mediaPlayer.play() print(manifest.userData?.playbackPositionTicks ?? 0) @@ -385,9 +385,9 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack; mediaPlayer.pause() mediaPlayer.play() - } - }) - .store(in: &globalData.pendingAPIRequests) + }) + .store(in: &globalData.pendingAPIRequests) + } } override func viewWillAppear(_ animated: Bool) { diff --git a/Shared/Typings/Typings.swift b/Shared/Typings/Typings.swift index 47e939dc..2f201187 100644 --- a/Shared/Typings/Typings.swift +++ b/Shared/Typings/Typings.swift @@ -34,7 +34,7 @@ class GlobalData: ObservableObject { @Published var isInNetwork: Bool = true; @Published var networkError: Bool = false; @Published var expiredCredentials: Bool = false; - @Published var pendingAPIRequests = Set(); + var pendingAPIRequests = Set(); } extension GlobalData: Equatable { diff --git a/Shared/ViewModel/SettingsViewModel.swift b/Shared/ViewModel/SettingsViewModel.swift new file mode 100644 index 00000000..564f69a5 --- /dev/null +++ b/Shared/ViewModel/SettingsViewModel.swift @@ -0,0 +1,43 @@ +// + /* + * 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 + +struct UserSettings: Decodable { + var LocalMaxBitrate: Int; + var RemoteMaxBitrate: Int; + var AutoSelectSubtitles: Bool; + var AutoSelectSubtitlesLangcode: String; + var SubtitlePositionOffset: Int; + var SubtitleFontName: String; +} + +struct Bitrates: Codable, Hashable { + public var name: String + public var value: Int +} + +final class SettingsViewModel: ObservableObject { + var bitrates: [Bitrates] = [] + + init() { + let url = Bundle.main.url(forResource: "bitrates", withExtension: "json")! + + do { + let jsonData = try Data(contentsOf: url, options: .mappedIfSafe) + do { + self.bitrates = try JSONDecoder().decode([Bitrates].self, from: jsonData) + } catch { + print(error) + } + } catch { + print(error) + } + } +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 5af89bdf..55f136f3 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -3,14 +3,16 @@ update_fastlane default_platform(:ios) platform :ios do - desc "Build targets" - lane :build do - gym - end desc "Push a new beta build to TestFlight" - lane :beta do + lane :beta_ios do increment_build_number(xcodeproj: "JellyfinPlayer.xcodeproj") - gym(output_name: "JellyfinPlayer.ipa") + gym(output_name: "JellyfinPlayer.ipa", scheme: "JellyfinPlayer") upload_to_testflight end -end + desc "Push a new beta build to TestFlight (tvOS)" + lane :beta_tvos do + increment_build_number(xcodeproj: "JellyfinPlayer.xcodeproj") + gym(output_name: "JellyfinPlayer.ipa", scheme: "JellyfinPlayer tvOS") + upload_to_testflight + end +end \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md index d3e56654..7c8b3df0 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -16,16 +16,16 @@ or alternatively using `brew install fastlane` # Available Actions ## iOS -### ios build +### ios beta_ios ``` -fastlane ios build -``` -Build targets -### ios beta -``` -fastlane ios beta +fastlane ios beta_ios ``` Push a new beta build to TestFlight +### ios beta_tvos +``` +fastlane ios beta_tvos +``` +Push a new beta build to TestFlight (tvOS) ----