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)
----