bugs bugs bugs *squash*
This commit is contained in:
parent
6cf0bc1f11
commit
18cc3ac15d
|
@ -677,7 +677,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
@ -706,7 +706,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
@ -857,7 +857,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
@ -889,7 +889,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
|
@ -920,7 +920,7 @@
|
||||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
@ -945,7 +945,7 @@
|
||||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WidgetExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 36;
|
CURRENT_PROJECT_VERSION = 41;
|
||||||
DEVELOPMENT_TEAM = 9R8RREG67J;
|
DEVELOPMENT_TEAM = 9R8RREG67J;
|
||||||
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
INFOPLIST_FILE = WidgetExtension/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
|
||||||
|
|
|
@ -274,23 +274,23 @@ struct ConnectToServerView: View {
|
||||||
ForEach(publicUsers, id: \.id) { publicUser in
|
ForEach(publicUsers, id: \.id) { publicUser in
|
||||||
HStack {
|
HStack {
|
||||||
Button() {
|
Button() {
|
||||||
if publicUser.hasPassword! {
|
if (publicUser.hasPassword ?? true) {
|
||||||
lastPublicUsers = publicUsers
|
lastPublicUsers = publicUsers
|
||||||
username = publicUser.name!
|
username = publicUser.name ?? ""
|
||||||
usernameDisabled = true
|
usernameDisabled = true
|
||||||
publicUsers = []
|
publicUsers = []
|
||||||
} else {
|
} else {
|
||||||
publicUsers = []
|
publicUsers = []
|
||||||
password = ""
|
password = ""
|
||||||
username = publicUser.name!
|
username = publicUser.name ?? ""
|
||||||
doLogin()
|
doLogin()
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text(publicUser.name!).font(.subheadline).fontWeight(.semibold)
|
Text(publicUser.name ?? "").font(.subheadline).fontWeight(.semibold)
|
||||||
Spacer()
|
Spacer()
|
||||||
if publicUser.primaryImageTag != "" {
|
if publicUser.primaryImageTag != nil {
|
||||||
LazyImage(source: URL(string: "\(uri)/Users/\(publicUser.id!)/Images/Primary?width=200&quality=80&tag=\(publicUser.primaryImageTag!)"))
|
LazyImage(source: URL(string: "\(uri)/Users/\(publicUser.id ?? "")/Images/Primary?width=200&quality=80&tag=\(publicUser.primaryImageTag!)"))
|
||||||
.contentMode(.aspectFill)
|
.contentMode(.aspectFill)
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
.cornerRadius(30.0)
|
.cornerRadius(30.0)
|
||||||
|
|
|
@ -147,74 +147,77 @@ struct ContentView: View {
|
||||||
.environmentObject(globalData)
|
.environmentObject(globalData)
|
||||||
} else {
|
} else {
|
||||||
if !jsi.did {
|
if !jsi.did {
|
||||||
LoadingView(isShowing: $isLoading) {
|
if(isLoading) {
|
||||||
|
ProgressView()
|
||||||
|
} else {
|
||||||
VStack {
|
VStack {
|
||||||
if loadState == 0 {
|
TabView(selection: $tabSelection) {
|
||||||
TabView(selection: $tabSelection) {
|
NavigationView {
|
||||||
NavigationView {
|
VStack(alignment: .leading) {
|
||||||
VStack(alignment: .leading) {
|
ScrollView {
|
||||||
ScrollView {
|
Spacer().frame(height: orientationInfo.orientation == .portrait ? 0 : 16)
|
||||||
Spacer().frame(height: orientationInfo.orientation == .portrait ? 0 : 16)
|
ContinueWatchingView()
|
||||||
ContinueWatchingView()
|
NextUpView()
|
||||||
NextUpView()
|
|
||||||
ForEach(librariesShowRecentlyAdded, id: \.self) { library_id in
|
ForEach(librariesShowRecentlyAdded, id: \.self) { library_id in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold)
|
Text("Latest \(library_names[library_id] ?? "")").font(.title2).fontWeight(.bold)
|
||||||
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
|
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 16))
|
||||||
Spacer()
|
Spacer()
|
||||||
NavigationLink(destination: LazyView {
|
NavigationLink(destination: LazyView {
|
||||||
LibraryView(usingParentID: library_id,
|
LibraryView(usingParentID: library_id,
|
||||||
title: library_names[library_id] ?? "", usingFilters: recentFilterSet)
|
title: library_names[library_id] ?? "", usingFilters: recentFilterSet)
|
||||||
}) {
|
}) {
|
||||||
|
HStack() {
|
||||||
Text("See All").font(.subheadline).fontWeight(.bold)
|
Text("See All").font(.subheadline).fontWeight(.bold)
|
||||||
|
Image(systemName: "chevron.right").font(Font.subheadline.bold())
|
||||||
}
|
}
|
||||||
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
}
|
||||||
LatestMediaView(usingParentID: library_id)
|
}.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||||
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
|
LatestMediaView(usingParentID: library_id)
|
||||||
}
|
}.padding(EdgeInsets(top: 4, leading: 0, bottom: 0, trailing: 0))
|
||||||
Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 20 : 30)
|
|
||||||
}
|
}
|
||||||
.navigationTitle("Home")
|
|
||||||
.toolbar {
|
Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 20 : 30)
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
}
|
||||||
Button {
|
.navigationTitle("Home")
|
||||||
showSettingsPopover = true
|
.toolbar {
|
||||||
} label: {
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
Image(systemName: "gear")
|
Button {
|
||||||
}
|
showSettingsPopover = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "gear")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $showSettingsPopover) {
|
}
|
||||||
SettingsView(viewModel: SettingsViewModel(), close: $showSettingsPopover)
|
.fullScreenCover(isPresented: $showSettingsPopover) {
|
||||||
}
|
SettingsView(viewModel: SettingsViewModel(), close: $showSettingsPopover)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
.tabItem {
|
|
||||||
Text("Home")
|
|
||||||
Image(systemName: "house")
|
|
||||||
}
|
|
||||||
.tag("Home")
|
|
||||||
NavigationView {
|
|
||||||
LibraryListView(libraries: library_names)
|
|
||||||
}
|
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
.tabItem {
|
|
||||||
Text("All Media")
|
|
||||||
Image(systemName: "folder")
|
|
||||||
}
|
|
||||||
.tag("All Media")
|
|
||||||
}
|
}
|
||||||
} else {
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
Text("Loading...")
|
.tabItem {
|
||||||
|
Text("Home")
|
||||||
|
Image(systemName: "house")
|
||||||
|
}
|
||||||
|
.tag("Home")
|
||||||
|
NavigationView {
|
||||||
|
LibraryListView(libraries: library_names)
|
||||||
|
}
|
||||||
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
.tabItem {
|
||||||
|
Text("All Media")
|
||||||
|
Image(systemName: "folder")
|
||||||
|
}
|
||||||
|
.tag("All Media")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.environmentObject(globalData)
|
||||||
.environmentObject(globalData)
|
.onAppear(perform: startup)
|
||||||
.onAppear(perform: startup)
|
.alert(isPresented: $globalData.networkError) {
|
||||||
.alert(isPresented: $globalData.networkError) {
|
Alert(title: Text("Network Error"), message: Text("An error occured while performing a network request"), dismissButton: .default(Text("Ok")))
|
||||||
Alert(title: Text("Network Error"), message: Text("An error occured while performing a network request"), dismissButton: .default(Text("Ok")))
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("Please wait...")
|
Text("Please wait...")
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct ContinueWatchingView: View {
|
||||||
|
|
||||||
func onAppear() {
|
func onAppear() {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
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])
|
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
|
.sink(receiveCompletion: { completion in
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
}, receiveValue: { response in
|
}, receiveValue: { response in
|
||||||
|
@ -56,7 +56,7 @@ struct ContinueWatchingView: View {
|
||||||
NavigationLink(destination: ItemView(item: item)) {
|
NavigationLink(destination: ItemView(item: item)) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Spacer().frame(height: 10)
|
Spacer().frame(height: 10)
|
||||||
LazyImage(source: item.getBackdropImage(baseURL: globalData.server.baseURI ?? "", maxWidth: 320))
|
LazyImage(source: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: 320))
|
||||||
.placeholderAndFailure {
|
.placeholderAndFailure {
|
||||||
Image(uiImage: UIImage(blurHash: item.getBackdropImageBlurHash(), size: CGSize(width: 48, height: 32))!)
|
Image(uiImage: UIImage(blurHash: item.getBackdropImageBlurHash(), size: CGSize(width: 48, height: 32))!)
|
||||||
.resizable()
|
.resizable()
|
||||||
|
@ -70,7 +70,7 @@ struct ContinueWatchingView: View {
|
||||||
.overlay(
|
.overlay(
|
||||||
Group {
|
Group {
|
||||||
if item.type == "Episode" {
|
if item.type == "Episode" {
|
||||||
Text("\(item.name!)")
|
Text("\(item.name ?? "")")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.padding(6)
|
.padding(6)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
@ -84,7 +84,7 @@ struct ContinueWatchingView: View {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
|
.fill(Color(red: 172/255, green: 92/255, blue: 195/255))
|
||||||
.mask(ProgressBar())
|
.mask(ProgressBar())
|
||||||
.frame(width: CGFloat(item.userData!.playedPercentage!*3.2), height: 7)
|
.frame(width: CGFloat(item.userData?.playedPercentage ?? 0 * 3.2), height: 7)
|
||||||
.padding(0), alignment: .bottomLeading
|
.padding(0), alignment: .bottomLeading
|
||||||
)
|
)
|
||||||
Text(item.seriesName ?? item.name ?? "")
|
Text(item.seriesName ?? item.name ?? "")
|
||||||
|
|
|
@ -46,6 +46,8 @@ network.</string>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchScreen</key>
|
<key>UILaunchScreen</key>
|
||||||
<dict/>
|
<dict/>
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>VideoPlayer</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
|
|
|
@ -46,9 +46,9 @@ struct LatestMediaView: View {
|
||||||
NavigationLink(destination: ItemView(item: item)) {
|
NavigationLink(destination: ItemView(item: item)) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Spacer().frame(height: 10)
|
Spacer().frame(height: 10)
|
||||||
LazyImage(source: item.getSeriesPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100))
|
LazyImage(source: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100))
|
||||||
.placeholderAndFailure {
|
.placeholderAndFailure {
|
||||||
Image(uiImage: UIImage(blurHash: item.getSeriesPrimaryImageBlurHash(), size: CGSize(width: 16, height: 20))!)
|
Image(uiImage: UIImage(blurHash: item.getPrimaryImageBlurHash(), size: CGSize(width: 16, height: 20))!)
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 100, height: 150)
|
.frame(width: 100, height: 150)
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
|
@ -61,11 +61,14 @@ struct LatestMediaView: View {
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
Text(String(item.productionYear ?? 0))
|
if(item.productionYear != nil) {
|
||||||
.font(.caption)
|
Text(String(item.productionYear ?? 0))
|
||||||
.fontWeight(.semibold)
|
.foregroundColor(.secondary)
|
||||||
.foregroundColor(.secondary)
|
.font(.caption)
|
||||||
.lineLimit(1)
|
.fontWeight(.medium)
|
||||||
|
} else {
|
||||||
|
Text(item.type!)
|
||||||
|
}
|
||||||
}.frame(width: 100)
|
}.frame(width: 100)
|
||||||
Spacer().frame(width: 15)
|
Spacer().frame(width: 15)
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,10 +84,14 @@ struct LibrarySearchView: View {
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
Text(String(item.productionYear ?? 0))
|
if(item.productionYear != nil) {
|
||||||
.foregroundColor(.secondary)
|
Text(String(item.productionYear ?? 0))
|
||||||
.font(.caption)
|
.foregroundColor(.secondary)
|
||||||
.fontWeight(.medium)
|
.font(.caption)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
} else {
|
||||||
|
Text(item.type!)
|
||||||
|
}
|
||||||
}.frame(width: 100)
|
}.frame(width: 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,10 +122,14 @@ struct LibraryView: View {
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
Text(String(item.productionYear ?? 0))
|
if(item.productionYear != nil) {
|
||||||
.foregroundColor(.secondary)
|
Text(String(item.productionYear ?? 0))
|
||||||
.font(.caption)
|
.foregroundColor(.secondary)
|
||||||
.fontWeight(.medium)
|
.font(.caption)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
} else {
|
||||||
|
Text(item.type!)
|
||||||
|
}
|
||||||
}.frame(width: 100)
|
}.frame(width: 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,15 +96,17 @@ struct MovieItemView: View {
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.offset(y: -4)
|
.offset(y: -4)
|
||||||
HStack {
|
HStack {
|
||||||
Text(String(item.productionYear ?? 0)).font(.subheadline)
|
if(item.productionYear != nil) {
|
||||||
.fontWeight(.medium)
|
Text(String(item.productionYear ?? 0)).font(.subheadline)
|
||||||
.foregroundColor(.secondary)
|
.fontWeight(.medium)
|
||||||
.lineLimit(1)
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
Text(item.getItemRuntime()).font(.subheadline)
|
Text(item.getItemRuntime()).font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
if item.officialRating != "" {
|
if item.officialRating != nil {
|
||||||
Text(item.officialRating!).font(.subheadline)
|
Text(item.officialRating!).font(.subheadline)
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
@ -311,10 +313,12 @@ struct MovieItemView: View {
|
||||||
.offset(x: 14, y: 0)
|
.offset(x: 14, y: 0)
|
||||||
Spacer().frame(height: 1)
|
Spacer().frame(height: 1)
|
||||||
HStack {
|
HStack {
|
||||||
Text(String(item.productionYear ?? 0)).font(.subheadline)
|
if(item.productionYear != nil) {
|
||||||
.fontWeight(.medium)
|
Text(String(item.productionYear ?? 0)).font(.subheadline)
|
||||||
.foregroundColor(.secondary)
|
.fontWeight(.medium)
|
||||||
.lineLimit(1)
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
Text(item.getItemRuntime()).font(.subheadline)
|
Text(item.getItemRuntime()).font(.subheadline)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|
|
@ -296,11 +296,13 @@ struct SeasonItemView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
LoadingView(isShowing: $isLoading) {
|
if(isLoading) {
|
||||||
|
ProgressView()
|
||||||
|
.onAppear(perform: onAppear)
|
||||||
|
} else {
|
||||||
innerBody
|
innerBody
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.navigationTitle("\(item.name ?? "") - \(item.seriesName ?? "")")
|
||||||
}
|
}
|
||||||
.onAppear(perform: onAppear)
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.navigationTitle("\(item.name ?? "") - \(item.seriesName ?? "")")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct SeriesItemView: View {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], isMissing: false)
|
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
|
||||||
.sink(receiveCompletion: { completion in
|
.sink(receiveCompletion: { completion in
|
||||||
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
|
||||||
}, receiveValue: { response in
|
}, receiveValue: { response in
|
||||||
|
@ -51,7 +51,10 @@ struct SeriesItemView: View {
|
||||||
@State private var tracks: [GridItem] = []
|
@State private var tracks: [GridItem] = []
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
LoadingView(isShowing: $isLoading) {
|
if(isLoading) {
|
||||||
|
ProgressView()
|
||||||
|
.onAppear(perform: onAppear)
|
||||||
|
} else {
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
Spacer().frame(height: 16)
|
Spacer().frame(height: 16)
|
||||||
LazyVGrid(columns: tracks) {
|
LazyVGrid(columns: tracks) {
|
||||||
|
@ -87,9 +90,8 @@ struct SeriesItemView: View {
|
||||||
recalcTracks()
|
recalcTracks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.overrideViewPreference(.unspecified)
|
||||||
|
.navigationTitle(item.name ?? "")
|
||||||
}
|
}
|
||||||
.overrideViewPreference(.unspecified)
|
|
||||||
.onAppear(perform: onAppear)
|
|
||||||
.navigationTitle(item.name ?? "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,11 @@ struct SettingsView: View {
|
||||||
@State private var outOfNetworkStreamBitrate: Int = 40_000_000
|
@State private var outOfNetworkStreamBitrate: Int = 40_000_000
|
||||||
@State private var autoSelectSubtitles: Bool = false
|
@State private var autoSelectSubtitles: Bool = false
|
||||||
@State private var autoSelectSubtitlesLangcode: String = "none"
|
@State private var autoSelectSubtitlesLangcode: String = "none"
|
||||||
|
@State private var username: String = ""
|
||||||
|
|
||||||
func onAppear() {
|
func onAppear() {
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
|
_username.wrappedValue = globalData.user.username!
|
||||||
_inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth")
|
_inNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "InNetworkBandwidth")
|
||||||
_outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth")
|
_outOfNetworkStreamBitrate.wrappedValue = defaults.integer(forKey: "OutOfNetworkBandwidth")
|
||||||
_autoSelectSubtitles.wrappedValue = defaults.bool(forKey: "AutoSelectSubtitles")
|
_autoSelectSubtitles.wrappedValue = defaults.bool(forKey: "AutoSelectSubtitles")
|
||||||
|
@ -63,7 +65,7 @@ struct SettingsView: View {
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Signed in as \(globalData.user.username!)").foregroundColor(.primary)
|
Text("Signed in as \(username)").foregroundColor(.primary)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Server")
|
||||||
|
@ -97,7 +99,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(.top, 12)
|
||||||
.navigationBarTitle("Settings", displayMode: .inline)
|
.navigationBarTitle("Settings", displayMode: .inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItemGroup(placement: .navigationBarLeading) {
|
ToolbarItemGroup(placement: .navigationBarLeading) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import MediaPlayer
|
||||||
struct Subtitle {
|
struct Subtitle {
|
||||||
var name: String
|
var name: String
|
||||||
var id: Int32
|
var id: Int32
|
||||||
var url: URL
|
var url: URL?
|
||||||
var delivery: SubtitleDeliveryMethod
|
var delivery: SubtitleDeliveryMethod
|
||||||
var codec: String
|
var codec: String
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
commandCenter.pauseCommand.addTarget { _ in
|
commandCenter.pauseCommand.addTarget { _ in
|
||||||
self.mediaPlayer.pause()
|
self.mediaPlayer.pause()
|
||||||
self.sendProgressReport(eventName: "pause")
|
self.sendProgressReport(eventName: "pause")
|
||||||
|
self.mainActionButton.setImage(UIImage(systemName: "play"), for: .normal)
|
||||||
return .success
|
return .success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +205,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
commandCenter.playCommand.addTarget { _ in
|
commandCenter.playCommand.addTarget { _ in
|
||||||
self.mediaPlayer.play()
|
self.mediaPlayer.play()
|
||||||
self.sendProgressReport(eventName: "unpause")
|
self.sendProgressReport(eventName: "unpause")
|
||||||
|
self.mainActionButton.setImage(UIImage(systemName: "pause"), for: .normal)
|
||||||
return .success
|
return .success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +262,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
// View has loaded.
|
// View has loaded.
|
||||||
|
|
||||||
// Rotate to landscape only if necessary
|
// Rotate to landscape only if necessary
|
||||||
|
|
||||||
UIViewController.attemptRotationToDeviceOrientation()
|
UIViewController.attemptRotationToDeviceOrientation()
|
||||||
|
|
||||||
mediaPlayer.perform(Selector(("setTextRendererFontSize:")), with: 14)
|
mediaPlayer.perform(Selector(("setTextRendererFontSize:")), with: 14)
|
||||||
|
@ -269,9 +272,9 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
mediaPlayer.drawable = videoContentView
|
mediaPlayer.drawable = videoContentView
|
||||||
|
|
||||||
if manifest.type == "Movie" {
|
if manifest.type == "Movie" {
|
||||||
titleLabel.text = manifest.name
|
titleLabel.text = manifest.name ?? ""
|
||||||
} else {
|
} else {
|
||||||
titleLabel.text = "S\(String(manifest.parentIndexNumber!)):E\(String(manifest.indexNumber!)) “\(manifest.name!)”"
|
titleLabel.text = "S\(String(manifest.parentIndexNumber ?? 0)):E\(String(manifest.indexNumber ?? 0)) “\(manifest.name ?? "")”"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch max bitrate from UserDefaults depending on current connection mode
|
// Fetch max bitrate from UserDefaults depending on current connection mode
|
||||||
|
@ -283,14 +286,15 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
builder.setMaxBitrate(bitrate: maxBitrate)
|
builder.setMaxBitrate(bitrate: maxBitrate)
|
||||||
let profile = builder.buildProfile()
|
let profile = builder.buildProfile()
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
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)
|
delegate?.showLoadingView(self)
|
||||||
|
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
|
.sink(receiveCompletion: { completion in
|
||||||
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
|
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
|
||||||
}, receiveValue: { [self] response in
|
}, receiveValue: { [self] response in
|
||||||
playSessionId = response.playSessionId!
|
playSessionId = response.playSessionId ?? ""
|
||||||
let mediaSource = response.mediaSources!.first.self!
|
let mediaSource = response.mediaSources!.first.self!
|
||||||
if mediaSource.transcodingUrl != nil {
|
if mediaSource.transcodingUrl != nil {
|
||||||
// Item is being transcoded by request of server
|
// Item is being transcoded by request of server
|
||||||
|
@ -299,14 +303,19 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
item.videoType = .transcode
|
item.videoType = .transcode
|
||||||
item.videoUrl = streamURL!
|
item.videoUrl = streamURL!
|
||||||
|
|
||||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "")
|
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: nil, delivery: .embed, codec: "")
|
||||||
subtitleTrackArray.append(disableSubtitleTrack)
|
subtitleTrackArray.append(disableSubtitleTrack)
|
||||||
|
|
||||||
// Loop through media streams and add to array
|
// Loop through media streams and add to array
|
||||||
for stream in mediaSource.mediaStreams! {
|
for stream in mediaSource.mediaStreams! {
|
||||||
if stream.type == .subtitle {
|
if stream.type == .subtitle {
|
||||||
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
var deliveryUrl: URL?
|
||||||
let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!)
|
if(stream.deliveryMethod == .external) {
|
||||||
|
deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
||||||
|
} else {
|
||||||
|
deliveryUrl = nil
|
||||||
|
}
|
||||||
|
let subtitle = Subtitle(name: stream.displayTitle ?? "Unknown", id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec ?? "webvtt")
|
||||||
subtitleTrackArray.append(subtitle)
|
subtitleTrackArray.append(subtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,14 +344,19 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
item.videoUrl = streamURL
|
item.videoUrl = streamURL
|
||||||
item.videoType = .directPlay
|
item.videoType = .directPlay
|
||||||
|
|
||||||
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "")
|
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: nil, delivery: .embed, codec: "")
|
||||||
subtitleTrackArray.append(disableSubtitleTrack)
|
subtitleTrackArray.append(disableSubtitleTrack)
|
||||||
|
|
||||||
// Loop through media streams and add to array
|
// Loop through media streams and add to array
|
||||||
for stream in mediaSource.mediaStreams! {
|
for stream in mediaSource.mediaStreams! {
|
||||||
if stream.type == .subtitle {
|
if stream.type == .subtitle {
|
||||||
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
var deliveryUrl: URL?
|
||||||
let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!)
|
if(stream.deliveryMethod == .external) {
|
||||||
|
deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
|
||||||
|
} else {
|
||||||
|
deliveryUrl = nil
|
||||||
|
}
|
||||||
|
let subtitle = Subtitle(name: stream.displayTitle ?? "Unknown", id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec ?? "webvtt")
|
||||||
subtitleTrackArray.append(subtitle)
|
subtitleTrackArray.append(subtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,21 +379,28 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
playbackItem = item
|
playbackItem = item
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setupNowPlayingCC()
|
|
||||||
|
|
||||||
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
|
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
print(manifest.userData?.playbackPositionTicks ?? 0)
|
|
||||||
mediaPlayer.jumpForward(Int32(manifest.userData?.playbackPositionTicks ?? 0/10000000))
|
//1 second = 10,000,000 ticks
|
||||||
|
|
||||||
|
let startTicks = round(Double(manifest.userData?.playbackPositionTicks ?? 0), toNearest: 10_000_000)
|
||||||
|
let startSeconds = Int32(startTicks) / 10_000_000
|
||||||
|
mediaPlayer.jumpForward(startSeconds)
|
||||||
|
|
||||||
|
//Pause and load captions into memory.
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
subtitleTrackArray.forEach { sub in
|
subtitleTrackArray.forEach { sub in
|
||||||
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
|
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
|
||||||
print("adding subs for id: \(sub.id) w/ url: \(sub.url)")
|
mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false)
|
||||||
mediaPlayer.addPlaybackSlave(sub.url, type: .subtitle, enforce: false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Wait for captions to load
|
||||||
delegate?.showLoadingView(self)
|
delegate?.showLoadingView(self)
|
||||||
while mediaPlayer.numberOfSubtitlesTracks != subtitleTrackArray.count - 1 {}
|
while mediaPlayer.numberOfSubtitlesTracks != subtitleTrackArray.count - 1 {}
|
||||||
|
|
||||||
|
//Select default track & resume playback
|
||||||
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
mediaPlayer.play()
|
mediaPlayer.play()
|
||||||
|
@ -414,6 +435,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
|
||||||
break
|
break
|
||||||
case .playing :
|
case .playing :
|
||||||
print("Video is playing")
|
print("Video is playing")
|
||||||
|
self.setupNowPlayingCC()
|
||||||
sendProgressReport(eventName: "unpause")
|
sendProgressReport(eventName: "unpause")
|
||||||
delegate?.hideLoadingView(self)
|
delegate?.hideLoadingView(self)
|
||||||
paused = false
|
paused = false
|
||||||
|
|
|
@ -52,7 +52,9 @@ extension BaseItemDto {
|
||||||
|
|
||||||
if self.primaryImageAspectRatio ?? 0.0 < 1.0 {
|
if self.primaryImageAspectRatio ?? 0.0 < 1.0 {
|
||||||
imageType = "Backdrop"
|
imageType = "Backdrop"
|
||||||
imageTag = (self.backdropImageTags ?? [""])[0]
|
if(!(self.backdropImageTags?.isEmpty ?? true)) {
|
||||||
|
imageTag = (self.backdropImageTags ?? [""])[0]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
imageType = "Primary"
|
imageType = "Primary"
|
||||||
imageTag = self.imageTags?["Primary"] ?? ""
|
imageTag = self.imageTags?["Primary"] ?? ""
|
||||||
|
@ -60,10 +62,13 @@ extension BaseItemDto {
|
||||||
|
|
||||||
if imageTag == "" {
|
if imageTag == "" {
|
||||||
imageType = "Backdrop"
|
imageType = "Backdrop"
|
||||||
imageTag = self.parentBackdropImageTags?[0] ?? ""
|
if(!(self.parentBackdropImageTags?.isEmpty ?? true)) {
|
||||||
|
imageTag = (self.parentBackdropImageTags ?? [""])[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +80,7 @@ extension BaseItemDto {
|
||||||
print(imageTag)
|
print(imageTag)
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(baseURL)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
let urlString = "\(baseURL)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +88,7 @@ extension BaseItemDto {
|
||||||
let imageType = "Primary"
|
let imageType = "Primary"
|
||||||
let imageTag = self.seriesPrimaryImageTag ?? ""
|
let imageTag = self.seriesPrimaryImageTag ?? ""
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(baseURL)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
let urlString = "\(baseURL)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +101,7 @@ extension BaseItemDto {
|
||||||
}
|
}
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
|
|
||||||
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +133,10 @@ extension BaseItemDto {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func round(_ value: Double, toNearest: Double) -> Double {
|
||||||
|
return round(value / toNearest) * toNearest
|
||||||
|
}
|
||||||
|
|
||||||
extension BaseItemPerson {
|
extension BaseItemPerson {
|
||||||
func getImage(baseURL: String, maxWidth: Int) -> URL {
|
func getImage(baseURL: String, maxWidth: Int) -> URL {
|
||||||
let imageType = "Primary"
|
let imageType = "Primary"
|
||||||
|
@ -135,7 +144,7 @@ extension BaseItemPerson {
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
|
|
||||||
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue