From 5eb57179f300ef02c31223f64093819151a8da3d Mon Sep 17 00:00:00 2001 From: Aiden Vigue Date: Mon, 24 May 2021 14:23:00 -0400 Subject: [PATCH] start using lazy loading --- JellyfinPlayer.xcodeproj/project.pbxproj | 51 ++---- .../xcshareddata/swiftpm/Package.resolved | 9 - JellyfinPlayer/ContinueWatchingView.swift | 146 ++++++++--------- JellyfinPlayer/LatestMediaView.swift | 8 +- JellyfinPlayer/LibraryView.swift | 155 +++++++++--------- JellyfinPlayer/MovieItemView.swift | 6 +- JellyfinPlayer/NextUpView.swift | 2 +- 7 files changed, 173 insertions(+), 204 deletions(-) diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index bfb96394..2335ccb1 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 535BAE9F2649E569005FA86D /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAE9E2649E569005FA86D /* ItemView.swift */; }; 535BAEA5264A151C005FA86D /* VLCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA4264A151C005FA86D /* VLCPlayer.swift */; }; 535BAEA7264A18AA005FA86D /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA6264A18AA005FA86D /* VideoPlayerView.swift */; }; + 53773D88265C2466000809AA /* ExyteGrid in Frameworks */ = {isa = PBXBuildFile; productRef = 53773D87265C2466000809AA /* ExyteGrid */; }; 5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBF4263B596A003A4E83 /* JellyfinPlayerApp.swift */; }; 5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBF6263B596A003A4E83 /* ContentView.swift */; }; 5377CBF9263B596B003A4E83 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5377CBF8263B596B003A4E83 /* Assets.xcassets */; }; @@ -41,8 +42,6 @@ 53D5E3DD264B47EE00BADDC8 /* MobileVLCKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; }; 53D5E3DE264B47EE00BADDC8 /* MobileVLCKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; }; - 53E0D5F8265821F50046A05F /* ExyteGrid in Frameworks */ = {isa = PBXBuildFile; productRef = 53E0D5F7265821F50046A05F /* ExyteGrid */; }; - 53E4E645263F6BC000F67C6B /* PartialSheet in Frameworks */ = {isa = PBXBuildFile; productRef = 53E4E644263F6BC000F67C6B /* PartialSheet */; }; 53E4E647263F6CF100F67C6B /* LibraryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E646263F6CF100F67C6B /* LibraryFilterView.swift */; }; 53E4E649263F725B00F67C6B /* MultiSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E648263F725B00F67C6B /* MultiSelector.swift */; }; 53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; }; @@ -113,8 +112,8 @@ buildActionMask = 2147483647; files = ( 538CD954263E3DC100BB5AF0 /* SDWebImageSwiftUI in Frameworks */, - 53E4E645263F6BC000F67C6B /* PartialSheet in Frameworks */, 5302F82C2658B5FE00647A2E /* Dynatrace.framework in Frameworks */, + 53773D88265C2466000809AA /* ExyteGrid in Frameworks */, 5338F757263B7E2E0014BF09 /* KeychainSwift in Frameworks */, 53D5E3DD264B47EE00BADDC8 /* MobileVLCKit.xcframework in Frameworks */, 5338F754263B65E10014BF09 /* SwiftyRequest in Frameworks */, @@ -123,7 +122,6 @@ 53892782263CC8770035E14B /* URLImage in Frameworks */, 53D2F74A264C69F6005792BB /* Introspect in Frameworks */, 5389277A263CBFE70035E14B /* SwiftyJSON in Frameworks */, - 53E0D5F8265821F50046A05F /* ExyteGrid in Frameworks */, 5338F751263B62E80014BF09 /* HidingViews in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -227,10 +225,9 @@ 53892779263CBFE70035E14B /* SwiftyJSON */, 53892781263CC8770035E14B /* URLImage */, 538CD953263E3DC100BB5AF0 /* SDWebImageSwiftUI */, - 53E4E644263F6BC000F67C6B /* PartialSheet */, 53D2F749264C69F6005792BB /* Introspect */, - 53E0D5F7265821F50046A05F /* ExyteGrid */, 5302F8292658791C00647A2E /* Sentry */, + 53773D87265C2466000809AA /* ExyteGrid */, ); productName = JellyfinPlayer; productReference = 5377CBF1263B596A003A4E83 /* JellyfinPlayer.app */; @@ -269,10 +266,9 @@ 53892778263CBFE70035E14B /* XCRemoteSwiftPackageReference "SwiftyJSON" */, 53892780263CC8770035E14B /* XCRemoteSwiftPackageReference "url-image" */, 538CD952263E3DC100BB5AF0 /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */, - 53E4E643263F6BC000F67C6B /* XCRemoteSwiftPackageReference "PartialSheet" */, 53D2F748264C69F6005792BB /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */, - 53E0D5F6265821F50046A05F /* XCRemoteSwiftPackageReference "Grid" */, 5302F8282658791C00647A2E /* XCRemoteSwiftPackageReference "sentry-cocoa" */, + 53773D86265C2466000809AA /* XCRemoteSwiftPackageReference "Grid" */, ); productRefGroup = 5377CBF2263B596A003A4E83 /* Products */; projectDirPath = ""; @@ -583,6 +579,14 @@ minimumVersion = 19.0.0; }; }; + 53773D86265C2466000809AA /* XCRemoteSwiftPackageReference "Grid" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/exyte/Grid"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.4.0; + }; + }; 53892778263CBFE70035E14B /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON"; @@ -615,22 +619,6 @@ minimumVersion = 0.1.3; }; }; - 53E0D5F6265821F50046A05F /* XCRemoteSwiftPackageReference "Grid" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/exyte/Grid"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.4.0; - }; - }; - 53E4E643263F6BC000F67C6B /* XCRemoteSwiftPackageReference "PartialSheet" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/AndreaMiotto/PartialSheet"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.1.11; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -654,6 +642,11 @@ package = 5338F755263B7E2E0014BF09 /* XCRemoteSwiftPackageReference "keychain-swift" */; productName = KeychainSwift; }; + 53773D87265C2466000809AA /* ExyteGrid */ = { + isa = XCSwiftPackageProductDependency; + package = 53773D86265C2466000809AA /* XCRemoteSwiftPackageReference "Grid" */; + productName = ExyteGrid; + }; 53892779263CBFE70035E14B /* SwiftyJSON */ = { isa = XCSwiftPackageProductDependency; package = 53892778263CBFE70035E14B /* XCRemoteSwiftPackageReference "SwiftyJSON" */; @@ -674,16 +667,6 @@ package = 53D2F748264C69F6005792BB /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; productName = Introspect; }; - 53E0D5F7265821F50046A05F /* ExyteGrid */ = { - isa = XCSwiftPackageProductDependency; - package = 53E0D5F6265821F50046A05F /* XCRemoteSwiftPackageReference "Grid" */; - productName = ExyteGrid; - }; - 53E4E644263F6BC000F67C6B /* PartialSheet */ = { - isa = XCSwiftPackageProductDependency; - package = 53E4E643263F6BC000F67C6B /* XCRemoteSwiftPackageReference "PartialSheet" */; - productName = PartialSheet; - }; /* End XCSwiftPackageProductDependency section */ /* Begin XCVersionGroup section */ diff --git a/JellyfinPlayer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/JellyfinPlayer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ba6bdf5b..648c15da 100644 --- a/JellyfinPlayer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/JellyfinPlayer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -55,15 +55,6 @@ "version": "1.9.200" } }, - { - "package": "PartialSheet", - "repositoryURL": "https://github.com/AndreaMiotto/PartialSheet", - "state": { - "branch": null, - "revision": "936465232b6399e402e79d5b031622af9a5e9960", - "version": "2.1.11" - } - }, { "package": "SDWebImage", "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", diff --git a/JellyfinPlayer/ContinueWatchingView.swift b/JellyfinPlayer/ContinueWatchingView.swift index 0728f566..0eef304d 100644 --- a/JellyfinPlayer/ContinueWatchingView.swift +++ b/JellyfinPlayer/ContinueWatchingView.swift @@ -107,86 +107,82 @@ struct ContinueWatchingView: View { var body: some View { if(resumeItems.count != 0) { - VStack(alignment: .leading) { - ScrollView(.horizontal, showsIndicators: false) { - LazyHStack() { - if(isLoading == false) { - Spacer().frame(width:16) - ForEach(resumeItems, id: \.Id) { item in - NavigationLink(destination: ItemView(item: item)) { - VStack(alignment: .leading) { - Spacer().frame(height: 10) - if(item.Type == "Episode") { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=550&quality=95&tag=\(item.Image)")!) - .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) - .resizable() - .frame(width: 320, height: 180) - .cornerRadius(10) - } - .frame(width: 320, height: 180) - .cornerRadius(10) - .overlay( - ZStack { - Text("S\(String(item.ParentIndexNumber ?? 0)):E\(String(item.IndexNumber ?? 0)) - \(item.Name)") - .font(.caption) - .padding(6) - .foregroundColor(.white) - }.background(Color.black) - .opacity(0.8) - .cornerRadius(10.0) - .padding(6), alignment: .topTrailing - ) - .overlay( - Rectangle() - .fill(Color(red: 172/255, green: 92/255, blue: 195/255)) - .mask(CustomShape(radius: 10)) - .frame(width: CGFloat((item.ItemProgress/100)*320), height: 7) - .padding(0), alignment: .bottomLeading - ) - .shadow(radius: 5) - } else { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=550&quality=85&tag=\(item.Image)")!) - .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size - .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!) - .resizable() - .frame(width: 320, height: 180) - .cornerRadius(10) - } - .frame(width: 320, height: 180) - .cornerRadius(10) - .overlay( - Rectangle() - .fill(Color(red: 172/255, green: 92/255, blue: 195/255)) - .mask(CustomShape(radius: 10)) - .frame(width: CGFloat((item.ItemProgress/100)*320), height: 7) + ScrollView(.horizontal, showsIndicators: false) { + LazyHStack() { + if(isLoading == false) { + Spacer().frame(width:16) + ForEach(resumeItems, id: \.Id) { item in + NavigationLink(destination: ItemView(item: item)) { + VStack(alignment: .leading) { + Spacer().frame(height: 10) + if(item.Type == "Episode") { + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=550&quality=80&tag=\(item.Image)")!) + .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size + .placeholder { + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) + .resizable() + .frame(width: 320, height: 180) + .cornerRadius(10) + } + .frame(width: 320, height: 180) + .cornerRadius(10) + .overlay( + ZStack { + Text("S\(String(item.ParentIndexNumber ?? 0)):E\(String(item.IndexNumber ?? 0)) - \(item.Name)") + .font(.caption) + .padding(6) + .foregroundColor(.white) + }.background(Color.black) + .opacity(0.8) + .cornerRadius(10.0) + .padding(6), alignment: .topTrailing + ) + .overlay( + Rectangle() + .fill(Color(red: 172/255, green: 92/255, blue: 195/255)) + .mask(CustomShape(radius: 10)) + .frame(width: CGFloat((item.ItemProgress/100)*320), height: 7) .padding(0), alignment: .bottomLeading - ) - .shadow(radius: 5) - } - Text("\(item.Type == "Episode" ? item.SeriesName ?? "" : item.Name)") - .font(.callout) - .fontWeight(.semibold) - .foregroundColor(.primary) - .lineLimit(1) - .frame(width: 320, alignment: .leading) - Spacer().frame(height: 5) - }.padding(.trailing, 5) - } + ) + .shadow(radius: 5) + } else { + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=550&quality=80&tag=\(item.Image)")!) + .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size + .placeholder { + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) + .resizable() + .frame(width: 320, height: 180) + .cornerRadius(10) + } + .frame(width: 320, height: 180) + .cornerRadius(10) + .overlay( + Rectangle() + .fill(Color(red: 172/255, green: 92/255, blue: 195/255)) + .mask(CustomShape(radius: 10)) + .frame(width: CGFloat((item.ItemProgress/100)*320), height: 7) + .padding(0), alignment: .bottomLeading + ) + .shadow(radius: 5) + } + Text("\(item.Type == "Episode" ? item.SeriesName ?? "" : item.Name)") + .font(.callout) + .fontWeight(.semibold) + .foregroundColor(.primary) + .lineLimit(1) + .frame(width: 320, alignment: .leading) + Spacer().frame(height: 5) + }.padding(.trailing, 5) } - Spacer().frame(width:14) } - }.frame(height: 215) - } - .frame(height: 215) - .padding(.bottom, 10) + Spacer().frame(width:14) + } + }.frame(height: 215) } + .frame(height: 215) + .padding(.bottom, 10) } else { - VStack() { - EmptyView() - }.onAppear(perform: onAppear) + EmptyView().onAppear(perform: onAppear) } } } diff --git a/JellyfinPlayer/LatestMediaView.swift b/JellyfinPlayer/LatestMediaView.swift index 44f91a87..f077fba6 100644 --- a/JellyfinPlayer/LatestMediaView.swift +++ b/JellyfinPlayer/LatestMediaView.swift @@ -91,10 +91,10 @@ struct LatestMediaView: View { VStack(alignment: .leading) { if(item.Type == "Series") { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=85&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=80&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 16))!) + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) .resizable() .frame(width: 100, height: 150) .cornerRadius(10) @@ -122,10 +122,10 @@ struct LatestMediaView: View { ).shadow(radius: 6) } else { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=250&quality=85&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=80&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 16, height: 16))!) + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) .resizable() .frame(width: 100, height: 150) .cornerRadius(10) diff --git a/JellyfinPlayer/LibraryView.swift b/JellyfinPlayer/LibraryView.swift index 77aae1bf..7ee9a4e4 100644 --- a/JellyfinPlayer/LibraryView.swift +++ b/JellyfinPlayer/LibraryView.swift @@ -10,6 +10,7 @@ import SwiftyRequest import SwiftyJSON import ExyteGrid import SDWebImageSwiftUI +import URLImage struct LibraryView: View { @Environment(\.managedObjectContext) private var viewContext @@ -173,87 +174,85 @@ struct LibraryView: View { var body: some View { if(prefill_id != "") { LoadingView(isShowing: $isLoading) { - GeometryReader { geometry in - ScrollView(.vertical) { - LazyVGrid(columns: tracks) { - ForEach(items, id: \.Id) { item in - NavigationLink(destination: ItemView(item: item )) { - VStack(alignment: .leading) { - if(item.Type == "Movie") { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")) - .resizable() - .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) - .resizable() - .frame(width: 100, height: 150) - .cornerRadius(10) - } - .frame(width:100, height: 150) - .cornerRadius(10) - .shadow(radius: 5) - } else { - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=90&tag=\(item.Image)")) - .resizable() - .placeholder { - Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) - .resizable() - .frame(width: 100, height: 150) - .cornerRadius(10) - } - .frame(width:100, height: 150) - .cornerRadius(10).overlay( - ZStack { - if(item.ItemBadge == 0) { - Image(systemName: "checkmark") - .font(.caption) - .padding(3) - .foregroundColor(.white) - } else { - Text("\(String(item.ItemBadge ?? 0))") - .font(.caption) - .padding(3) - .foregroundColor(.white) - } - }.background(Color.black) - .opacity(0.8) - .cornerRadius(10.0) - .padding(3), alignment: .topTrailing - ) - .shadow(radius: 5) - } - Text(item.Name) - .font(.caption) - .fontWeight(.semibold) - .foregroundColor(.primary) - .lineLimit(1) - Text(String(item.ProductionYear)) - .foregroundColor(.secondary) - .font(.caption) - .fontWeight(.medium) - }.frame(width: 100) - } - } - if(startIndex + endIndex < totalItems) { - HStack() { - Spacer() - Button() { - startIndex += endIndex; - loadItems() - } label: { - HStack() { - Text("Load more").font(.callout) - Image(systemName: "arrow.clockwise") - } + ScrollView(.vertical) { + LazyVGrid(columns: tracks) { + ForEach(items, id: \.Id) { item in + NavigationLink(destination: ItemView(item: item )) { + VStack(alignment: .leading) { + if(item.Type == "Movie") { + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=80&tag=\(item.Image)")) + .resizable() + .placeholder { + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) + .resizable() + .frame(width: 100, height: 150) + .cornerRadius(10) + } + .frame(width:100, height: 150) + .cornerRadius(10) + .shadow(radius: 5) + } else { + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?maxWidth=150&quality=80&tag=\(item.Image)")) + .resizable() + .placeholder { + Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 6, height: 6))!) + .resizable() + .frame(width: 100, height: 150) + .cornerRadius(10) + } + .frame(width:100, height: 150) + .cornerRadius(10).overlay( + ZStack { + if(item.ItemBadge == 0) { + Image(systemName: "checkmark") + .font(.caption) + .padding(3) + .foregroundColor(.white) + } else { + Text("\(String(item.ItemBadge ?? 0))") + .font(.caption) + .padding(3) + .foregroundColor(.white) + } + }.background(Color.black) + .opacity(0.8) + .cornerRadius(10.0) + .padding(3), alignment: .topTrailing + ) + .shadow(radius: 5) } - Spacer() - } + Text(item.Name) + .font(.caption) + .fontWeight(.semibold) + .foregroundColor(.primary) + .lineLimit(1) + Text(String(item.ProductionYear)) + .foregroundColor(.secondary) + .font(.caption) + .fontWeight(.medium) + }.frame(width: 100) } - Spacer().frame(height: 2) } + if(startIndex + endIndex < totalItems) { + HStack() { + Spacer() + Button() { + startIndex += endIndex; + loadItems() + } label: { + HStack() { + Text("Load more").font(.callout) + Image(systemName: "arrow.clockwise") + } + } + Spacer() + } + } + Spacer().frame(height: 2) } - .onChange(of: isPortrait) { _ in - recalcTracks() - } + } + .onChange(of: isPortrait) { _ in + recalcTracks() } } .overrideViewPreference(.unspecified) @@ -292,7 +291,7 @@ struct LibraryView: View { }.onAppear(perform: listOnAppear).overrideViewPreference(.unspecified).navigationTitle("All Media") .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { - NavigationLink(destination: LibrarySearchView(url: "/Users/\(globalData.user?.user_id ?? "")/Items?Limit=60&StartIndex=0&Recursive=true&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo&ImageTypeLimit=1&EnableImageTypes=Primary%2CBackdrop%2CThumb%2CBanner&IncludeItemTypes=Movie,Series\(extraParam)", close: $closeSearch), isActive: $closeSearch) { + NavigationLink(destination: LibrarySearchView(url: "/Users/\(globalData.user?.user_id ?? "")/Items?Limit=30&StartIndex=0&Recursive=true&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo&ImageTypeLimit=1&EnableImageTypes=Primary%2CBackdrop%2CThumb%2CBanner&IncludeItemTypes=Movie,Series\(extraParam)", close: $closeSearch), isActive: $closeSearch) { Image(systemName: "magnifyingglass") } } diff --git a/JellyfinPlayer/MovieItemView.swift b/JellyfinPlayer/MovieItemView.swift index cbb277d7..ae5d948e 100644 --- a/JellyfinPlayer/MovieItemView.swift +++ b/JellyfinPlayer/MovieItemView.swift @@ -447,7 +447,7 @@ struct MovieItemView: View { WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Backdrop?maxWidth=750&quality=90&tag=\(fullItem.Backdrop)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { - Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 32, height: 32))!) + Image(uiImage: UIImage(blurHash: (fullItem.BackdropBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.BackdropBlurHash), size: CGSize(width: 16, height: 16))!) .resizable() .frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom) } @@ -461,7 +461,7 @@ struct MovieItemView: View { WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(fullItem.Id)/Images/Primary?maxWidth=250&quality=90&tag=\(fullItem.Poster)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { - Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 32, height: 32))!) + Image(uiImage: UIImage(blurHash: (fullItem.PosterBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : fullItem.PosterBlurHash), size: CGSize(width: 16, height: 16))!) .resizable() .frame(width: 120, height: 180) } @@ -577,7 +577,7 @@ struct MovieItemView: View { WebImage(url: cast.Image) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { - Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 32, height: 32))!) + Image(uiImage: UIImage(blurHash: (cast.ImageBlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : cast.ImageBlurHash), size: CGSize(width: 8, height: 8))!) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 100, height: 100) diff --git a/JellyfinPlayer/NextUpView.swift b/JellyfinPlayer/NextUpView.swift index 7d85ac43..ad7bfc91 100644 --- a/JellyfinPlayer/NextUpView.swift +++ b/JellyfinPlayer/NextUpView.swift @@ -79,7 +79,7 @@ struct NextUpView: View { NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { Spacer().frame(height:10) - WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.SeriesId ?? "")/Images/\(item.ImageType)?maxWidth=250&quality=85&tag=\(item.Image)")!) + WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.SeriesId ?? "")/Images/\(item.ImageType)?maxWidth=150&quality=80&tag=\(item.Image)")!) .resizable() // Resizable like SwiftUI.Image, you must use this modifier or the view will use the image bitmap size .placeholder { Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 16, height: 16))!)