Browse Source

Merge pull request #2640 from nextcloud/cache

Cache media image
Marino Faggiana 1 year ago
parent
commit
a0f20cd88f

+ 21 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -384,6 +384,8 @@
 		F769CA1A2966EA3C00039397 /* ComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769CA182966EA3C00039397 /* ComponentView.swift */; };
 		F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
 		F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
+		F76B649C2ADFFAED00014640 /* NCMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B649B2ADFFAED00014640 /* NCMediaManager.swift */; };
+		F76B649E2ADFFDEC00014640 /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = F76B649D2ADFFDEC00014640 /* LRUCache */; };
 		F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
 		F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
 		F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
@@ -1068,6 +1070,7 @@
 		F769CA162965AB7C00039397 /* NCUploadAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCUploadAssets.swift; sourceTree = "<group>"; };
 		F769CA182966EA3C00039397 /* ComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComponentView.swift; sourceTree = "<group>"; };
 		F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCBrand.swift; sourceTree = "<group>"; };
+		F76B649B2ADFFAED00014640 /* NCMediaManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCMediaManager.swift; sourceTree = "<group>"; };
 		F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityIndicator.swift; sourceTree = "<group>"; };
 		F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerPDFSearch.swift; sourceTree = "<group>"; };
 		F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerPDFSearchCell.xib; sourceTree = "<group>"; };
@@ -1504,6 +1507,7 @@
 				F74E7720277A2EF40013B958 /* XLForm in Frameworks */,
 				F73ADD1C265546890069EA0D /* SwiftEntryKit in Frameworks */,
 				F31F69642A2F929600162F76 /* PreviewSnapshots in Frameworks */,
+				F76B649E2ADFFDEC00014640 /* LRUCache in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2376,6 +2380,7 @@
 				F720B5B72507B9A5008C94E5 /* Cell */,
 				F7501C302212E57400FB1415 /* NCMedia.storyboard */,
 				F7501C312212E57400FB1415 /* NCMedia.swift */,
+				F76B649B2ADFFAED00014640 /* NCMediaManager.swift */,
 				F7F1E54B2492369A00E42386 /* NCMediaCommandView.xib */,
 			);
 			path = Media;
@@ -2889,6 +2894,7 @@
 				F7A1050D29E587AF00FFD92B /* TagListView */,
 				F31F69632A2F929600162F76 /* PreviewSnapshots */,
 				F7F623B42A5EF4D30022D3D4 /* Gzip */,
+				F76B649D2ADFFDEC00014640 /* LRUCache */,
 			);
 			productName = "Crypto Cloud";
 			productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */;
@@ -3064,6 +3070,7 @@
 				F31F69622A2F929600162F76 /* XCRemoteSwiftPackageReference "swiftui-preview-snapshots" */,
 				F31F69672A2F92F000162F76 /* XCRemoteSwiftPackageReference "SnapshotTestingHEIC" */,
 				F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */,
+				F76B649A2ADFFAD200014640 /* XCRemoteSwiftPackageReference "LRUCache" */,
 			);
 			productRefGroup = F7F67B9F1A24D27800EE80DA;
 			projectDirPath = "";
@@ -3657,6 +3664,7 @@
 				F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */,
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */,
+				F76B649C2ADFFAED00014640 /* NCMediaManager.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
 				F7A60F86292D215000FCE1F2 /* NCShareAccounts.swift in Sources */,
 				F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */,
@@ -4989,6 +4997,14 @@
 				minimumVersion = 4.3.0;
 			};
 		};
+		F76B649A2ADFFAD200014640 /* XCRemoteSwiftPackageReference "LRUCache" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/nicklockwood/LRUCache";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 1.0.4;
+			};
+		};
 		F76DA961277B760E0082465B /* XCRemoteSwiftPackageReference "Queuer" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/FabrizioBrancati/Queuer";
@@ -5341,6 +5357,11 @@
 			package = F75EAED626D2552E00F4320E /* XCRemoteSwiftPackageReference "MarqueeLabel" */;
 			productName = MarqueeLabel;
 		};
+		F76B649D2ADFFDEC00014640 /* LRUCache */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F76B649A2ADFFAD200014640 /* XCRemoteSwiftPackageReference "LRUCache" */;
+			productName = LRUCache;
+		};
 		F76DA962277B760E0082465B /* Queuer */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F76DA961277B760E0082465B /* XCRemoteSwiftPackageReference "Queuer" */;

+ 4 - 0
iOSClient/AppDelegate.swift

@@ -126,6 +126,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
             NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
 
+            DispatchQueue.global().async { NCMediaManager.shared.createCache(account: self.account) }
+
         } else {
 
             CCUtility.deleteAllChainStore()
@@ -594,6 +596,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Initialize Auto upload with \(items) uploads")
         }
 
+        DispatchQueue.global().async { NCMediaManager.shared.createCache(account: account) }
+        
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeUser)
     }
 

+ 17 - 0
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -1250,4 +1250,21 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not access database: \(error)")
         }
     }
+
+    func getMediaOcIdEtag(account: String) -> [String: String] {
+
+        let predicate = NSPredicate(format: "account == %@ AND (classFile == %@ OR classFile == %@)", account, NKCommon.TypeClassFile.image.rawValue, NKCommon.TypeClassFile.video.rawValue)
+        var results: [String: String] = [:]
+
+        do {
+            let realm = try Realm()
+            for result in realm.objects(tableMetadata.self).filter(predicate) {
+                results[result.ocId] = result.etag
+            }
+        } catch let error {
+            NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
+        }
+
+        return results
+    }
 }

+ 4 - 1
iOSClient/Media/NCMedia.swift

@@ -370,7 +370,10 @@ extension NCMedia: UICollectionViewDataSource {
     func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
         guard let cell = (cell as? NCGridMediaCell), indexPath.row < self.metadatas.count else { return }
         let metadata = self.metadatas[indexPath.row]
-        if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
+        if let image = NCMediaManager.shared.getImage(ocId: metadata.ocId) {
+            cell.imageItem.backgroundColor = nil
+            cell.imageItem.image = image
+        } else if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
             cell.imageItem.backgroundColor = nil
             cell.imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
         } else {

+ 89 - 0
iOSClient/Media/NCMediaManager.swift

@@ -0,0 +1,89 @@
+//
+//  NCMediaManager.swift
+//  Nextcloud
+//
+//  Created by Milen on 10.10.23.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+
+import UIKit
+import LRUCache
+
+class NCMediaManager {
+
+    public static let shared: NCMediaManager = {
+        let instance = NCMediaManager()
+        return instance
+    }()
+
+    typealias ThumbnailLRUCache = LRUCache<String, UIImage>
+    let cache: ThumbnailLRUCache = ThumbnailLRUCache(countLimit: 1000)
+
+    func createCache(account: String) {
+
+        let resultsMedia = NCManageDatabase.shared.getMediaOcIdEtag(account: account)
+        guard !resultsMedia.isEmpty,
+              let directory = CCUtility.getDirectoryProviderStorage() else { return }
+
+        let ext = ".preview.ico"
+        let manager = FileManager.default
+        let resourceKeys = Set<URLResourceKey>([.nameKey, .pathKey, .fileSizeKey, .creationDateKey])
+        struct FileInfo {
+            var path: URL
+            var ocId: String
+            var date: Date
+        }
+        var files: [FileInfo] = []
+
+        let startDate = Date()
+        print("--------- start ThumbnailLRUCache image process ---------")
+
+        // Get files only image / video
+        if let enumerator = manager.enumerator(at: URL(fileURLWithPath: directory), includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles]) {
+            for case let fileURL as URL in enumerator where fileURL.lastPathComponent.hasSuffix(ext) {
+                let fileName = fileURL.lastPathComponent
+                let ocId = fileURL.deletingLastPathComponent().lastPathComponent
+                guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
+                        let size = resourceValues.fileSize,
+                        size > 0,
+                        let date = resourceValues.creationDate,
+                        let etag = resultsMedia[ocId],
+                        fileName == etag + ext else { continue }
+                files.append(FileInfo(path: fileURL, ocId: ocId, date: date))
+            }
+        }
+
+        // Sort for most recent
+        files.sort(by: { $0.date > $1.date })
+        if let firstDate = files.first?.date, let lastDate = files.last?.date {
+            print("First date: \(firstDate)")
+            print("Last date: \(lastDate)")
+        }
+
+        // Insert in cache
+        cache.removeAllValues()
+        for file in files {
+            autoreleasepool {
+                if let image = UIImage(contentsOfFile: file.path.path) {
+                    cache.setValue(image, forKey: file.ocId)
+                }
+            }
+        }
+
+        let endDate = Date()
+        let diffDate = endDate.timeIntervalSinceReferenceDate - startDate.timeIntervalSinceReferenceDate
+        print("Counter process: \(cache.count)")
+        print("Time process: \(diffDate)")
+        print("--------- stop ThumbnailLRUCache image process ---------")
+    }
+
+    func getImage(ocId: String) -> UIImage? {
+
+        return cache.value(forKey: ocId)
+    }
+
+    func setImage(ocId: String, image: UIImage) {
+
+        cache.setValue(image, forKey: ocId)
+    }
+}

+ 2 - 1
iOSClient/Networking/NCOperationQueue.swift

@@ -194,7 +194,7 @@ class NCOperationDownloadThumbnail: ConcurrentOperation {
             fileNameIconLocalPath: fileNameIconLocalPath,
             sizeIcon: NCGlobal.shared.sizeIcon,
             etag: etagResource,
-            options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in
+            options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, imagePreview, imageIcon, _, etag, error in
 
             if error == .success, let imageIcon = imageIcon {
                 NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag)
@@ -214,6 +214,7 @@ class NCOperationDownloadThumbnail: ConcurrentOperation {
                     }
                     NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedThumbnail, userInfo: ["ocId": self.metadata.ocId])
                 }
+                NCMediaManager.shared.setImage(ocId: self.metadata.ocId, image: imageIcon)
             }
             self.finish()
         }