fix
This commit is contained in:
parent
a16a41acbf
commit
988377e0ea
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>36</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchScreen</key>
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = VideoPlayer.storyboard; sourceTree = "<group>"; };
|
||||
5338F74D263B61370014BF09 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>";
|
||||
};
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -27,7 +27,7 @@ network.</string>
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>33</string>
|
||||
<string>36</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.coremedia.hls.low-latency</key>
|
||||
<true/>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.coremedia.hls.low-latency</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<NSFetchRequestResult> = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<AnyCancellable>();
|
||||
var pendingAPIRequests = Set<AnyCancellable>();
|
||||
}
|
||||
|
||||
extension GlobalData: Equatable {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
----
|
||||
|
||||
|
|
Loading…
Reference in New Issue