fix
This commit is contained in:
parent
a16a41acbf
commit
988377e0ea
|
@ -19,7 +19,7 @@
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>36</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchScreen</key>
|
<key>UILaunchScreen</key>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
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 */; };
|
53313B90265EEA6D00947AA3 /* VideoPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */; };
|
||||||
53352571265EA0A0006CCA86 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 53352570265EA0A0006CCA86 /* Introspect */; };
|
53352571265EA0A0006CCA86 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 53352570265EA0A0006CCA86 /* Introspect */; };
|
||||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; };
|
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5338F74D263B61370014BF09 /* ConnectToServerView.swift */; };
|
||||||
|
@ -108,6 +109,7 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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 */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
532175392671BCED005491E6 /* ViewModel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5321753A2671BCFC005491E6 /* SettingsViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
535870612669D21600D05A09 /* JellyfinPlayer tvOS */ = {
|
535870612669D21600D05A09 /* JellyfinPlayer tvOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -212,6 +222,7 @@
|
||||||
535870752669D60C00D05A09 /* Shared */ = {
|
535870752669D60C00D05A09 /* Shared */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
532175392671BCED005491E6 /* ViewModel */,
|
||||||
621338912660106C00A81A2A /* Extensions */,
|
621338912660106C00A81A2A /* Extensions */,
|
||||||
AE8C3157265D6F5E008AA076 /* Resources */,
|
AE8C3157265D6F5E008AA076 /* Resources */,
|
||||||
535870AB2669D8D300D05A09 /* Typings */,
|
535870AB2669D8D300D05A09 /* Typings */,
|
||||||
|
@ -484,6 +495,7 @@
|
||||||
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
62133890265F83A900A81A2A /* LibraryListView.swift in Sources */,
|
||||||
53892770263C25230035E14B /* NextUpView.swift in Sources */,
|
53892770263C25230035E14B /* NextUpView.swift in Sources */,
|
||||||
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
|
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
|
||||||
|
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */,
|
||||||
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
|
||||||
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
|
||||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
|
||||||
|
@ -687,8 +699,9 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 33;
|
CURRENT_PROJECT_VERSION = 36;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
@ -703,6 +716,7 @@
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin;
|
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@ -716,8 +730,9 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 33;
|
CURRENT_PROJECT_VERSION = 36;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
|
@ -733,6 +748,7 @@
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.0.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin;
|
PRODUCT_BUNDLE_IDENTIFIER = me.vigue.jellyfin;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
* 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 SwiftUI
|
||||||
import CoreData
|
import CoreData
|
||||||
import KeychainSwift
|
import KeychainSwift
|
||||||
|
|
|
@ -85,36 +85,38 @@ struct ContentView: View {
|
||||||
JellyfinAPI.basePath = globalData.server.baseURI ?? ""
|
JellyfinAPI.basePath = globalData.server.baseURI ?? ""
|
||||||
JellyfinAPI.customHeaders = ["X-Emby-Authorization": globalData.authHeader]
|
JellyfinAPI.customHeaders = ["X-Emby-Authorization": globalData.authHeader]
|
||||||
|
|
||||||
UserAPI.getCurrentUser()
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
UserAPI.getCurrentUser()
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
loadState = loadState - 1
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
}, receiveValue: { response in
|
loadState = loadState - 1
|
||||||
libraries = response.configuration?.orderedViews ?? []
|
}, receiveValue: { response in
|
||||||
librariesShowRecentlyAdded = libraries.filter { element in
|
libraries = response.configuration?.orderedViews ?? []
|
||||||
return !(response.configuration?.latestItemsExcludes?.contains(element))!
|
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)
|
|
||||||
|
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
if(loadState == 1) {
|
|
||||||
isLoading = false
|
UserViewsAPI.getUserViews(userId: globalData.user.user_id ?? "")
|
||||||
}
|
.sink(receiveCompletion: { completion in
|
||||||
})
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
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
|
let defaults = UserDefaults.standard
|
||||||
if defaults.integer(forKey: "InNetworkBandwidth") == 0 {
|
if defaults.integer(forKey: "InNetworkBandwidth") == 0 {
|
||||||
|
|
|
@ -37,13 +37,15 @@ struct ContinueWatchingView: View {
|
||||||
@State private var items: [BaseItemDto] = []
|
@State private var items: [BaseItemDto] = []
|
||||||
|
|
||||||
func onAppear() {
|
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])
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb])
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
}, receiveValue: { response in
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
items = response.items ?? []
|
}, receiveValue: { response in
|
||||||
})
|
items = response.items ?? []
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
|
@ -27,7 +27,7 @@ network.</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>33</string>
|
<string>36</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<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">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.developer.coremedia.hls.low-latency</key>
|
||||||
|
<true/>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.developer.coremedia.hls.low-latency</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -26,13 +26,15 @@ struct LatestMediaView: View {
|
||||||
}
|
}
|
||||||
viewDidLoad = true;
|
viewDidLoad = true;
|
||||||
|
|
||||||
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12)
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12)
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
}, receiveValue: { response in
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
items = response
|
}, receiveValue: { response in
|
||||||
})
|
items = response
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
|
@ -30,15 +30,17 @@ struct LibrarySearchView: View {
|
||||||
|
|
||||||
func requestSearch(query: String) {
|
func requestSearch(query: String) {
|
||||||
isLoading = true
|
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)
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
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)
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
}, receiveValue: { response in
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
items = response.items ?? []
|
}, receiveValue: { response in
|
||||||
isLoading = false
|
items = response.items ?? []
|
||||||
})
|
isLoading = false
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: tracks for grid
|
//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
|
* 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/.
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*
|
*
|
||||||
|
@ -61,17 +62,19 @@ struct LibraryView: View {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
items = []
|
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)
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
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)
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
isLoading = false
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
}, receiveValue: { response in
|
isLoading = false
|
||||||
let x = ceil(Double(response.totalRecordCount!) / 100.0)
|
}, receiveValue: { response in
|
||||||
totalPages = Int(x)
|
let x = ceil(Double(response.totalRecordCount!) / 100.0)
|
||||||
items = response.items ?? []
|
totalPages = Int(x)
|
||||||
isLoading = false
|
items = response.items ?? []
|
||||||
})
|
isLoading = false
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: tracks for grid
|
//MARK: tracks for grid
|
||||||
|
|
|
@ -21,13 +21,15 @@ struct NextUpView: View {
|
||||||
}
|
}
|
||||||
viewDidLoad = true;
|
viewDidLoad = true;
|
||||||
|
|
||||||
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
}, receiveValue: { response in
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
items = response.items ?? []
|
}, receiveValue: { response in
|
||||||
})
|
items = response.items ?? []
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
|
@ -28,15 +28,17 @@ struct SeasonItemView: View {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id!)
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
.sink(receiveCompletion: { completion in
|
TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id!)
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
isLoading = false
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
}, receiveValue: { response in
|
isLoading = false
|
||||||
viewDidLoad = true
|
}, receiveValue: { response in
|
||||||
episodes = response.items ?? []
|
viewDidLoad = true
|
||||||
})
|
episodes = response.items ?? []
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
|
@ -26,15 +26,18 @@ struct SeriesItemView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
|
|
||||||
.sink(receiveCompletion: { completion in
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
|
||||||
}, receiveValue: { response in
|
.sink(receiveCompletion: { completion in
|
||||||
isLoading = false
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
viewDidLoad = true
|
}, receiveValue: { response in
|
||||||
seasons = response.items ?? []
|
isLoading = false
|
||||||
})
|
viewDidLoad = true
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
seasons = response.items ?? []
|
||||||
|
})
|
||||||
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: Grid tracks
|
//MARK: Grid tracks
|
||||||
|
|
|
@ -9,30 +9,20 @@ import CoreData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@ObservedObject
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
var viewModel: SettingsViewModel
|
|
||||||
|
@EnvironmentObject var globalData: GlobalData
|
||||||
|
@EnvironmentObject var jsi: justSignedIn
|
||||||
|
|
||||||
|
@ObservedObject var viewModel: SettingsViewModel
|
||||||
|
|
||||||
@Binding
|
@Binding var close: Bool
|
||||||
var close: Bool
|
@State private var inNetworkStreamBitrate: Int = 40_000_000
|
||||||
@Environment(\.managedObjectContext)
|
@State private var outOfNetworkStreamBitrate: Int = 40_000_000
|
||||||
private var viewContext
|
@State private var autoSelectSubtitles: Bool = false
|
||||||
@EnvironmentObject
|
@State private var autoSelectSubtitlesLangcode: String = "none"
|
||||||
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"
|
|
||||||
|
|
||||||
func onAppear() {
|
func onAppear() {
|
||||||
_username.wrappedValue = globalData.user.username ?? ""
|
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
_inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth")
|
_inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth")
|
||||||
_outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth")
|
_outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth")
|
||||||
|
@ -73,7 +63,7 @@ struct SettingsView: View {
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Signed in as \(username)").foregroundColor(.primary)
|
Text("Signed in as \(globalData.user.username!)").foregroundColor(.primary)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
||||||
|
@ -121,36 +111,3 @@ struct SettingsView: View {
|
||||||
}.onAppear(perform: onAppear)
|
}.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)
|
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)
|
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||||
.sink(receiveCompletion: { completion in
|
MediaInfoAPI.getPostedPlaybackInfo(itemId: manifest.id!, userId: globalData.user.user_id, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, playbackInfoDto: playbackInfo)
|
||||||
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
|
.sink(receiveCompletion: { completion in
|
||||||
}, receiveValue: { [self] response in
|
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
|
||||||
playSessionId = response.playSessionId!
|
}, receiveValue: { [self] response in
|
||||||
let mediaSource = response.mediaSources!.first.self!
|
playSessionId = response.playSessionId!
|
||||||
if(mediaSource.transcodingUrl != nil) {
|
let mediaSource = response.mediaSources!.first.self!
|
||||||
//Item is being transcoded by request of server
|
if(mediaSource.transcodingUrl != nil) {
|
||||||
let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)")
|
//Item is being transcoded by request of server
|
||||||
let item = PlaybackItem()
|
let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)")
|
||||||
item.videoType = .transcode
|
let item = PlaybackItem()
|
||||||
item.videoUrl = streamURL!
|
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);
|
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! {
|
//Loop through media streams and add to array
|
||||||
if(stream.type == .subtitle) {
|
for stream in mediaSource.mediaStreams! {
|
||||||
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
if(stream.type == .subtitle) {
|
||||||
let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!)
|
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
||||||
subtitleTrackArray.append(subtitle);
|
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) {
|
if(selectedAudioTrack == -1) {
|
||||||
let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!))
|
if(audioTrackArray.count > 0) {
|
||||||
if(stream.isDefault! == true) {
|
selectedAudioTrack = audioTrackArray[0].id;
|
||||||
selectedAudioTrack = Int32(stream.index!);
|
|
||||||
}
|
}
|
||||||
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) {
|
self.sendPlayReport()
|
||||||
let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!))
|
playbackItem = item;
|
||||||
if(stream.isDefault! == true) {
|
} else {
|
||||||
selectedAudioTrack = Int32(stream.index!);
|
//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) {
|
self.setupNowPlayingCC()
|
||||||
if(audioTrackArray.count > 0) {
|
|
||||||
selectedAudioTrack = audioTrackArray[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sendPlayReport()
|
|
||||||
playbackItem = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setupNowPlayingCC()
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .background).async {
|
|
||||||
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
|
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
print(manifest.userData?.playbackPositionTicks ?? 0)
|
print(manifest.userData?.playbackPositionTicks ?? 0)
|
||||||
|
@ -385,9 +385,9 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack;
|
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack;
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
}
|
})
|
||||||
})
|
.store(in: &globalData.pendingAPIRequests)
|
||||||
.store(in: &globalData.pendingAPIRequests)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ class GlobalData: ObservableObject {
|
||||||
@Published var isInNetwork: Bool = true;
|
@Published var isInNetwork: Bool = true;
|
||||||
@Published var networkError: Bool = false;
|
@Published var networkError: Bool = false;
|
||||||
@Published var expiredCredentials: Bool = false;
|
@Published var expiredCredentials: Bool = false;
|
||||||
@Published var pendingAPIRequests = Set<AnyCancellable>();
|
var pendingAPIRequests = Set<AnyCancellable>();
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GlobalData: Equatable {
|
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)
|
default_platform(:ios)
|
||||||
|
|
||||||
platform :ios do
|
platform :ios do
|
||||||
desc "Build targets"
|
|
||||||
lane :build do
|
|
||||||
gym
|
|
||||||
end
|
|
||||||
desc "Push a new beta build to TestFlight"
|
desc "Push a new beta build to TestFlight"
|
||||||
lane :beta do
|
lane :beta_ios do
|
||||||
increment_build_number(xcodeproj: "JellyfinPlayer.xcodeproj")
|
increment_build_number(xcodeproj: "JellyfinPlayer.xcodeproj")
|
||||||
gym(output_name: "JellyfinPlayer.ipa")
|
gym(output_name: "JellyfinPlayer.ipa", scheme: "JellyfinPlayer")
|
||||||
upload_to_testflight
|
upload_to_testflight
|
||||||
end
|
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
|
# Available Actions
|
||||||
## iOS
|
## iOS
|
||||||
### ios build
|
### ios beta_ios
|
||||||
```
|
```
|
||||||
fastlane ios build
|
fastlane ios beta_ios
|
||||||
```
|
|
||||||
Build targets
|
|
||||||
### ios beta
|
|
||||||
```
|
|
||||||
fastlane ios beta
|
|
||||||
```
|
```
|
||||||
Push a new beta build to TestFlight
|
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