Commit 63c05ef8 authored by shenyong's avatar shenyong

首页数据构造修改

parent a7703dda
...@@ -45,6 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -45,6 +45,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
AdvManager.shared.initAdertisementSDK() AdvManager.shared.initAdertisementSDK()
PMEmailManager.shareManager.restore() PMEmailManager.shareManager.restore()
// 相册基本资源加载
PhotoManager.shared.config() PhotoManager.shared.config()
return true return true
......
...@@ -260,3 +260,14 @@ class TrashDatabase { ...@@ -260,3 +260,14 @@ class TrashDatabase {
sqlite3_close(db) sqlite3_close(db)
} }
} }
extension TrashDatabase{
func dealHomeData(type:Int){
}
}
import Foundation
import Photos
actor AssetSizeCache {
static let shared = AssetSizeCache()
private let cache: NSCache<NSString, NSNumber> = NSCache<NSString, NSNumber>()
private let fileQueue = DispatchQueue(label: "com.assetsize.fileoperations", qos: .utility)
// 临时存储,用于跟踪修改
private var tempStorage: [String: Int64] = [:]
private var hasChanges: Bool = false
private let cachePath: String = {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0].appendingPathComponent("AssetSizeCache.plist").path
}()
private init() {
Task {
await loadCache()
}
}
private func loadCache() {
fileQueue.sync {
if let dict = NSDictionary(contentsOfFile: cachePath) as? [String: Int64] {
for (key, value) in dict {
cache.setObject(NSNumber(value: value), forKey: key as NSString)
tempStorage[key] = value
}
}
}
}
func getSize(for asset: PHAsset) -> Int64? {
// 优先从内存缓存中读取
if let size = tempStorage[asset.localIdentifier] {
return size
}
return cache.object(forKey: asset.localIdentifier as NSString)?.int64Value
}
func setSize(_ size: Int64, for asset: PHAsset) {
cache.setObject(NSNumber(value: size), forKey: asset.localIdentifier as NSString)
tempStorage[asset.localIdentifier] = size
hasChanges = true
}
// 手动触发保存到文件
func saveToFile() {
guard hasChanges else { return }
let path = self.cachePath
let storageToSave = self.tempStorage
fileQueue.async {
(storageToSave as NSDictionary).write(toFile: path, atomically: true)
}
hasChanges = false
}
func clearCache() {
cache.removeAllObjects()
tempStorage.removeAll()
hasChanges = true
let path = self.cachePath
fileQueue.async {
try? FileManager.default.removeItem(atPath: path)
}
}
}
//
// PhotoCleanTool.swift
// PhoneManager
//
// Created by edy on 2025/5/15.
//
import Foundation
struct PhotoCleanTool{
// app内手动删除资源
func deleteLocalResouces(for ids:[String]){
let other = PhotoManager.shared.otherModels.filter{ids.contains($0.localIdentifier)}
let screen = PhotoManager.shared.screenShotModels.filter{ids.contains($0.localIdentifier)}
let video = PhotoManager.shared.videoModels.filter{ids.contains($0.localIdentifier)}
PhotoManager.shared.otherModels = other
PhotoManager.shared.screenShotModels = screen
PhotoManager.shared.videoModels = video
}
// 删除无效资源
func cleanIneffectiveResources(){
}
}
...@@ -9,9 +9,9 @@ import Foundation ...@@ -9,9 +9,9 @@ import Foundation
import Photos import Photos
import UIKit import UIKit
@MainActor
// 移除顶层的 @MainActor
class PhotoDuplicateManager: @unchecked Sendable { class PhotoDuplicateManager: @unchecked Sendable {
static let shared = PhotoDuplicateManager() static let shared = PhotoDuplicateManager()
private let stateManager = PhotoDuplicateStateManager() // 使用新的状态管理器 private let stateManager = PhotoDuplicateStateManager() // 使用新的状态管理器
private init() {} private init() {}
...@@ -39,9 +39,11 @@ class PhotoDuplicateManager: @unchecked Sendable { ...@@ -39,9 +39,11 @@ class PhotoDuplicateManager: @unchecked Sendable {
mediaType: MediaType = .photo, mediaType: MediaType = .photo,
loacalHandler: (([[AssetModel]]) -> Void)?, loacalHandler: (([[AssetModel]]) -> Void)?,
progressHandler: (([AssetModel]) -> Void)?, progressHandler: (([AssetModel]) -> Void)?,
completionHandler: (([[AssetModel]]) -> Void)?) { completionHandler: (([[AssetModel]]) -> Void)?) {
Task {
Task.detached(priority: .background) { [weak self] in
guard let self = self else { return }
// 1. 加载本地数据 // 1. 加载本地数据
await loadStoredData() await loadStoredData()
print("本地数据加载完成") print("本地数据加载完成")
...@@ -50,9 +52,6 @@ class PhotoDuplicateManager: @unchecked Sendable { ...@@ -50,9 +52,6 @@ class PhotoDuplicateManager: @unchecked Sendable {
let cachedGroups = await stateManager.getAllDuplicateGroups() let cachedGroups = await stateManager.getAllDuplicateGroups()
print("通知已缓存的结果", cachedGroups.count) print("通知已缓存的结果", cachedGroups.count)
await MainActor.run { await MainActor.run {
// for group in cachedGroups {
// progressHandler?(group.assets)
// }
let groups = cachedGroups.map{$0.assets} let groups = cachedGroups.map{$0.assets}
loacalHandler?(groups) loacalHandler?(groups)
} }
...@@ -65,8 +64,8 @@ class PhotoDuplicateManager: @unchecked Sendable { ...@@ -65,8 +64,8 @@ class PhotoDuplicateManager: @unchecked Sendable {
for asset in assets { for asset in assets {
// 创建特征键(分辨率+创建时间+文件大小) // 创建特征键(分辨率+创建时间+文件大小)
let resolution = "\(asset.pixelWidth)x\(asset.pixelHeight)" let resolution = "\(asset.pixelWidth)x\(asset.pixelHeight)"
// let createTime = asset.creationDate?.timeIntervalSince1970 ?? 0 // let createTime = asset.creationDate?.timeIntervalSince1970 ?? 0
let fileSize = getAssetSize(asset) let fileSize = await getAssetSize(asset: asset)
let featureKey = "\(resolution)_\(fileSize)" let featureKey = "\(resolution)_\(fileSize)"
if tempGroups[featureKey] == nil { if tempGroups[featureKey] == nil {
...@@ -149,45 +148,212 @@ class PhotoDuplicateManager: @unchecked Sendable { ...@@ -149,45 +148,212 @@ class PhotoDuplicateManager: @unchecked Sendable {
completionHandler?(allGroups.map { $0.assets }) completionHandler?(allGroups.map { $0.assets })
} }
} }
}
// MARK: - 辅助方法 }
nonisolated private func areAssetsExactlyDuplicate(_ assets: [PHAsset]) -> Bool { // func findDuplicateAssets(
guard let firstAsset = assets.first else { return false } // in assets: [PHAsset],
// mediaType: MediaType = .photo,
return assets.allSatisfy { asset in // loacalHandler: @Sendable @escaping ([[AssetModel]]) -> Void,
// 检查分辨率完全相同 // progressHandler: @Sendable @escaping ([AssetModel]) -> Void,
if asset.pixelWidth != firstAsset.pixelWidth || // completionHandler: @Sendable @escaping ([[AssetModel]]) -> Void
asset.pixelHeight != firstAsset.pixelHeight { // ) {
return false // // 使用后台优先级
} // Task.detached(priority: .background) { [weak self] in
// guard let self = self else { return }
// 检查文件大小完全相同 //
let firstSize = getAssetSize(firstAsset) // do {
let currentSize = getAssetSize(asset) // // 批量处理,减少主线程切换
if firstSize != currentSize { // let batchSize = 100
return false // var accumulatedProgress: [AssetModel] = []
} //
// for i in stride(from: 0, to: assets.count, by: batchSize) {
// 检查创建时间是否接近(避免连拍照片) // let end = min(i + batchSize, assets.count)
// if let time1 = asset.creationDate, // let batchAssets = Array(assets[i..<end])
// let time2 = firstAsset.creationDate { //
// let timeDiff = abs(time1.timeIntervalSince(time2)) // // 处理一批资源
// if timeDiff < 1.0 { // 1秒内的连拍照片不算重复 // let results = try await self.processBatch(batchAssets)
// return false // accumulatedProgress.append(contentsOf: results)
//
// // 每处理一定数量后才更新UI
// if accumulatedProgress.count >= 50 {
// await MainActor.run {
// progressHandler(accumulatedProgress)
// }
// accumulatedProgress.removeAll()
// }
//
// // 给主线程喘息的机会
// try await Task.sleep(nanoseconds: 1_000_000) // 1ms
// }
//
// // 1. 加载本地数据
// await self.loadStoredData()
// print("本地数据加载完成")
//
// // 2. 通知已缓存的结果
// let cachedGroups = await self.stateManager.getAllDuplicateGroups()
// print("通知已缓存的结果", cachedGroups.count)
//
// await MainActor.run {
// let groups = cachedGroups.map { $0.assets }
// loacalHandler(groups)
// }
//
// // 3. 按特征预分组(分辨率、时间、文件大小)
// let featureGroups = try await self.groupAssetsByFeatures(assets)
//
// // 如果没有需要处理的组,直接返回缓存结果
// if featureGroups.isEmpty {
// let total = cachedGroups.map { $0.assets }
// await MainActor.run {
// completionHandler(total)
// }
// return
// }
//
// // 4. 并发处理每组资源
// try await self.processFeatureGroups(featureGroups, progressHandler: progressHandler)
//
// // 5. 完成处理
// if await !self.stateManager.getPendingDuplicateGroups().isEmpty {
// await self.savePendingDuplicateGroups()
// }
//
// let allGroups = await self.stateManager.getAllDuplicateGroups()
// await MainActor.run {
// print("执行完毕")
// completionHandler(allGroups.map { $0.assets })
// }
// } catch {
// print("查找重复资源失败: \(error)")
// await MainActor.run {
// completionHandler([]) // 返回空数组或错误处理
// } // }
// } // }
// }
return true // }
}
} // 按特征分组(分辨率、文件大小等)
// private func groupAssetsByFeatures(_ assets: [PHAsset]) async throws -> [[PHAsset]] {
// var tempGroups: [String: [PHAsset]] = [:]
//
// // 使用串行队列确保线程安全
// let queue = DispatchQueue(label: "com.example.assetGrouping")
//
// // 并发处理所有资源
// await withThrowingTaskGroup(of: Void.self) { group in
// for asset in assets {
// group.addTask {
// // 创建特征键(分辨率+文件大小)
// let resolution = "\(asset.pixelWidth)x\(asset.pixelHeight)"
// let fileSize = await self.getAssetSize(asset: asset)
// let featureKey = "\(resolution)_\(fileSize)"
//
// // 使用同步队列更新共享字典
// queue.sync {
// if tempGroups[featureKey] == nil {
// tempGroups[featureKey] = []
// }
// tempGroups[featureKey]?.append(asset)
// }
// }
// }
// }
//
// // 只保留有多个资源的组
// return tempGroups.values.filter { $0.count > 1 }
// }
// 处理特征分组并查找重复项
// private func processFeatureGroups(
// _ featureGroups: [[PHAsset]],
// progressHandler: (([AssetModel]) -> Void)?
// ) async throws {
// let maxConcurrency = 2 // 最大并发数
// let batchSize = max(1, featureGroups.count / maxConcurrency)
//
// for batchIndex in stride(from: 0, to: featureGroups.count, by: batchSize) {
// let endIndex = min(batchIndex + batchSize, featureGroups.count)
// let batch = Array(featureGroups[batchIndex..<endIndex])
//
// await withThrowingTaskGroup(of: Void.self) { group in
// for assets in batch {
// group.addTask { [weak self] in
// guard let self = self else { return }
//
// // 只需要计算phash值并比较
// var hashGroups: [String: [PHAsset]] = [:]
//
// // 处理单个组内的资源,控制并发
// await withThrowingTaskGroup(of: Void.self) { innerGroup in
// for asset in assets {
// innerGroup.addTask {
// if let hash = await self.getOrCalculateHash(for: asset) {
// // 使用同步队列更新共享字典
// DispatchQueue.global().sync {
// if hashGroups[hash] == nil {
// hashGroups[hash] = []
// }
// hashGroups[hash]?.append(asset)
// }
// }
// }
// }
// }
//
// // 找出phash值相同的组
// let duplicateGroups = hashGroups.values.filter { $0.count > 1 }
//
// // 处理找到的重复组
// for duplicateGroup in duplicateGroups {
// let groupId = UUID().uuidString
// let assetModels = await self.createAssetModels(from: duplicateGroup)
//
// // 通知进度
// await MainActor.run {
// progressHandler?(assetModels)
// }
//
// // 保存重复组
// await self.stateManager.appendDuplicateGroup(
// DuplicateGroupModel(groupId: groupId, assets: assetModels)
// )
//
// if await self.stateManager.shouldSavePendingGroups() {
// await self.savePendingDuplicateGroups()
// }
// }
// }
// }
// }
// }
// }
// MARK: - 辅助方法
nonisolated private func getAssetSize(_ asset: PHAsset) -> Int64 { // nonisolated private func getAssetSize(_ asset: PHAsset) -> Int64 {
// if let resource = PHAssetResource.assetResources(for: asset).first,
// let size = resource.value(forKey: "fileSize") as? Int64 {
// return size
// }
// return 0
// }
func getAssetSize(asset: PHAsset) async -> Int64 {
// 先尝试从缓存获取
if let cachedSize = await AssetSizeCache.shared.getSize(for: asset) {
return cachedSize
}
// 如果缓存中没有,则计算大小
if let resource = PHAssetResource.assetResources(for: asset).first, if let resource = PHAssetResource.assetResources(for: asset).first,
let size = resource.value(forKey: "fileSize") as? Int64 { let size = resource.value(forKey: "fileSize") as? Int64 {
// 将计算结果存入缓存
await AssetSizeCache.shared.setSize(size, for: asset)
return size return size
} }
return 0 return 0
} }
...@@ -197,7 +363,7 @@ class PhotoDuplicateManager: @unchecked Sendable { ...@@ -197,7 +363,7 @@ class PhotoDuplicateManager: @unchecked Sendable {
for asset in assets { for asset in assets {
modelGroup.addTask { modelGroup.addTask {
let size = self.getAssetSize(asset) let size = await self.getAssetSize(asset: asset)
return AssetModel( return AssetModel(
localIdentifier: asset.localIdentifier, localIdentifier: asset.localIdentifier,
assetSize: Double(size), assetSize: Double(size),
...@@ -304,4 +470,47 @@ extension PhotoDuplicateManager { ...@@ -304,4 +470,47 @@ extension PhotoDuplicateManager {
let average = UInt8(pixels.reduce(0, { UInt32($0) + UInt32($1) }) / UInt32(pixels.count)) let average = UInt8(pixels.reduce(0, { UInt32($0) + UInt32($1) }) / UInt32(pixels.count))
return pixels.map { $0 > average ? "1" : "0" }.joined() return pixels.map { $0 > average ? "1" : "0" }.joined()
} }
// private func processBatch(_ assets: [PHAsset]) async throws -> [AssetModel] {
// var results: [AssetModel] = []
//
// // 按特征预分组
// let featureGroups = try await self.groupAssetsByFeatures(assets)
//
// // 处理每个特征组
// for assets in featureGroups {
// // 计算hash并分组
// var hashGroups: [String: [PHAsset]] = [:]
//
// for asset in assets {
// if let hash = await self.getOrCalculateHash(for: asset) {
// if hashGroups[hash] == nil {
// hashGroups[hash] = []
// }
// hashGroups[hash]?.append(asset)
// }
// }
//
// // 找出hash值相同的组
// let duplicateGroups = hashGroups.values.filter { $0.count > 1 }
//
// // 处理找到的重复组
// for duplicateGroup in duplicateGroups {
// let assetModels = await self.createAssetModels(from: duplicateGroup)
// results.append(contentsOf: assetModels)
//
// // 保存到状态管理器
// let groupId = UUID().uuidString
// await self.stateManager.appendDuplicateGroup(
// DuplicateGroupModel(groupId: groupId, assets: assetModels)
// )
// }
// }
//
// return results
//
// }
} }
...@@ -88,6 +88,15 @@ class PhotoManager{ ...@@ -88,6 +88,15 @@ class PhotoManager{
// 重复图片分组 // 重复图片分组
var duplicateModels:[[AssetModel]] = [] var duplicateModels:[[AssetModel]] = []
// 过滤垃圾桶/保留相似图片分组
var filterSimilarModels:[[AssetModel]] = []
// 过滤垃圾桶/保留相似截图分组
var filterSimilarScreenShotModels:[[AssetModel]] = []
// 过滤垃圾桶/保留相似视频分组
var filterSimilarVideoModels:[[AssetModel]] = []
// // 过滤垃圾桶/保留重复图片分组
// var filterDuplicateModels:[[AssetModel]] = []
// 截图 // 截图
var screenShotModels:[AssetModel] = [] var screenShotModels:[AssetModel] = []
// 视频 // 视频
...@@ -95,6 +104,14 @@ class PhotoManager{ ...@@ -95,6 +104,14 @@ class PhotoManager{
// 其他 // 其他
var otherModels:[AssetModel] = [] var otherModels:[AssetModel] = []
// 过滤垃圾桶/保留截图
var filterScreenShotModels:[AssetModel] = []
// 过滤垃圾桶/保留视频
var filterVideoModels:[AssetModel] = []
// 过滤垃圾桶/保留其他
var filterOtherModels:[AssetModel] = []
private(set) var screenShotTotalSize:Int64 = 0 private(set) var screenShotTotalSize:Int64 = 0
private(set) var videoTotalSize:Int64 = 0 private(set) var videoTotalSize:Int64 = 0
private(set) var otherTotalSize:Int64 = 0 private(set) var otherTotalSize:Int64 = 0
...@@ -331,13 +348,30 @@ extension PhotoManager{ ...@@ -331,13 +348,30 @@ extension PhotoManager{
// 获取文件大小 // 获取文件大小
func getAssetSize(for asset:PHAsset) ->Int64{ // func getAssetSize(for asset:PHAsset) ->Int64{
// if let resource = PHAssetResource.assetResources(for: asset).first,
// let size = resource.value(forKey: "fileSize") as? Int64 {
// return size
// } else {
// return 0
// }
// }
func getAssetSize(for asset: PHAsset) async -> Int64 {
// 先尝试从缓存获取
if let cachedSize = await AssetSizeCache.shared.getSize(for: asset) {
return cachedSize
}
// 如果缓存中没有,则计算大小
if let resource = PHAssetResource.assetResources(for: asset).first, if let resource = PHAssetResource.assetResources(for: asset).first,
let size = resource.value(forKey: "fileSize") as? Int64 { let size = resource.value(forKey: "fileSize") as? Int64 {
// 将计算结果存入缓存
await AssetSizeCache.shared.setSize(size, for: asset)
return size return size
} else {
return 0
} }
return 0
} }
...@@ -585,6 +619,10 @@ extension PhotoManager{ ...@@ -585,6 +619,10 @@ extension PhotoManager{
} }
} }
func getTotalSize(source:[[AssetModel]]) ->Double{
return source.flatMap{$0}.reduce(0){$0+$1.assetSize}
}
// 获取所有资产的总数 // 获取所有资产的总数
// func fetchTotalAssets(completion: @escaping ([PHAsset]) -> Void) { // func fetchTotalAssets(completion: @escaping ([PHAsset]) -> Void) {
// DispatchQueue.global(qos: .background).async { // DispatchQueue.global(qos: .background).async {
...@@ -606,10 +644,9 @@ extension PhotoManager{ ...@@ -606,10 +644,9 @@ extension PhotoManager{
// } // }
} }
extension PhotoManager{ extension PhotoManager{
// 批量转模型
func convertAssetsToModel(for assets: [PHAsset], mediaType: Int) async -> [AssetModel] { func convertAssetsToModel(for assets: [PHAsset], mediaType: Int) async -> [AssetModel] {
let batchSize = 4 // 控制并发数量 let batchSize = 4 // 控制并发数量
var results: [AssetModel] = [] var results: [AssetModel] = []
...@@ -624,7 +661,7 @@ extension PhotoManager{ ...@@ -624,7 +661,7 @@ extension PhotoManager{
group.addTask { group.addTask {
return AssetModel( return AssetModel(
localIdentifier: asset.localIdentifier, localIdentifier: asset.localIdentifier,
assetSize: Double(self.getAssetSize(for: asset)), assetSize: Double(await self.getAssetSize(for: asset)),
createDate: asset.creationDate ?? Date(), createDate: asset.creationDate ?? Date(),
mediaType: mediaType mediaType: mediaType
) )
...@@ -640,6 +677,7 @@ extension PhotoManager{ ...@@ -640,6 +677,7 @@ extension PhotoManager{
return results return results
} }
// 获取总数据
func getModelsData(){ func getModelsData(){
Task{ Task{
let start = CFAbsoluteTimeGetCurrent() let start = CFAbsoluteTimeGetCurrent()
...@@ -660,3 +698,5 @@ extension PhotoManager{ ...@@ -660,3 +698,5 @@ extension PhotoManager{
} }
} }
} }
...@@ -10,7 +10,7 @@ import Photos ...@@ -10,7 +10,7 @@ import Photos
import UIKit import UIKit
@MainActor
class PhotoSimilarManager: @unchecked Sendable { class PhotoSimilarManager: @unchecked Sendable {
static let shared = PhotoSimilarManager() static let shared = PhotoSimilarManager()
...@@ -198,7 +198,6 @@ class PhotoSimilarManager: @unchecked Sendable { ...@@ -198,7 +198,6 @@ class PhotoSimilarManager: @unchecked Sendable {
} else { } else {
assetSize = 0 assetSize = 0
} }
let model = AssetModel( let model = AssetModel(
localIdentifier: asset.localIdentifier, localIdentifier: asset.localIdentifier,
assetSize: assetSize, assetSize: assetSize,
......
...@@ -9,7 +9,7 @@ import Foundation ...@@ -9,7 +9,7 @@ import Foundation
import Photos import Photos
import UIKit import UIKit
@MainActor
class ScreenshotSimilarJSONManager: @unchecked Sendable { class ScreenshotSimilarJSONManager: @unchecked Sendable {
static let shared = ScreenshotSimilarJSONManager() static let shared = ScreenshotSimilarJSONManager()
......
...@@ -44,7 +44,7 @@ private actor VideoAssetCacheManager { ...@@ -44,7 +44,7 @@ private actor VideoAssetCacheManager {
} }
@MainActor
class VideoSimilarJSONManager: @unchecked Sendable { class VideoSimilarJSONManager: @unchecked Sendable {
static let shared = VideoSimilarJSONManager() static let shared = VideoSimilarJSONManager()
private let stateManager = VideoSimilarStateManager() private let stateManager = VideoSimilarStateManager()
......
...@@ -162,6 +162,9 @@ class CustomProgressBar: UIView { ...@@ -162,6 +162,9 @@ class CustomProgressBar: UIView {
var chaoticProgress: CGFloat = 0 { var chaoticProgress: CGFloat = 0 {
didSet { didSet {
guard chaoticProgress != oldValue else{
return
}
scheduleProgressUpdate() scheduleProgressUpdate()
} }
} }
...@@ -240,7 +243,7 @@ class CustomProgressBar: UIView { ...@@ -240,7 +243,7 @@ class CustomProgressBar: UIView {
guard let self = self else { return } guard let self = self else { return }
// 计算总容量和各部分比例 // 计算总容量和各部分比例
let total = max(self.totalProgress, 0.001) // 避免除以0 let total = max(self.totalProgress + self.chaoticProgress, 0.001) // 避免除以0
let usedRatio = min(max(self.usedProgress / total, 0), 1) let usedRatio = min(max(self.usedProgress / total, 0), 1)
let chaoticRatio = min(max(self.chaoticProgress / total, 0), 1) let chaoticRatio = min(max(self.chaoticProgress / total, 0), 1)
......
...@@ -31,8 +31,9 @@ class HomeView:UIView { ...@@ -31,8 +31,9 @@ class HomeView:UIView {
// var model:PhotosManagerModel? // var model:PhotosManagerModel?
var viewModel:HomeViewModel? // var viewModel:HomeviewModel
private var viewModel = HomeViewModel.shared
var attribet:NSAttributedString? var attribet:NSAttributedString?
...@@ -73,41 +74,51 @@ class HomeView:UIView { ...@@ -73,41 +74,51 @@ class HomeView:UIView {
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
viewModel = HomeViewModel() // viewModel = HomeViewModel()
setupUI() setupUI()
viewModel?.setupBindings() viewModel.setupBindings()
viewModel?.homeDataChanged = {[weak self] section,row in viewModel.homeDataChanged = {[weak self] section,row in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
DispatchQueue.main.async {
if let cell = weakSelf.collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? HomeTitleCollectionCell { if let cell = weakSelf.collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? HomeTitleCollectionCell {
// 只更新需要改变的内容 // 只更新需要改变的内容
let model = weakSelf.viewModel?.headerGroup[row] let model = weakSelf.viewModel.headerGroup[row]
cell.reloadUIWithModel(model: model) cell.reloadUIWithModel(model: model)
} }
if let cell = weakSelf.collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? HomeOtherCollectionCell { if let cell = weakSelf.collectionView.cellForItem(at: IndexPath(row: row, section: section)) as? HomeOtherCollectionCell {
// 只更新需要改变的内容 // 只更新需要改变的内容
let model = weakSelf.viewModel?.cardGroup[row] let model = weakSelf.viewModel.cardGroup[row]
cell.reloadUIWithModel(model: model) cell.reloadUIWithModel(model: model)
}
weakSelf.homeHeader?.progressBar.chaoticProgress = CGFloat(weakSelf.viewModel.totalSize)
weakSelf.reloadHeadSize()
} }
weakSelf.homeHeader?.progressBar.chaoticProgress = CGFloat(weakSelf.viewModel?.totalSize ?? 0)
weakSelf.reloadHeadSize()
} }
viewModel?.coverHadChange = { [weak self] in viewModel.reloadCellHeight = {[weak self] in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
print("刷新一次封面") DispatchQueue.main.async {
weakSelf.collectionView.reloadData() weakSelf.collectionView.reloadData()
}
} }
// viewModel.coverHadChange = { [weak self] in
// guard let weakSelf = self else { return }
// print("刷新一次封面")
// weakSelf.collectionView.reloadData()
// }
collectionView.reloadData() collectionView.reloadData()
} }
func reloadHeadSize(){ func reloadHeadSize(){
self.attribet = setFileAndCount(count: viewModel?.totalFilesCount ?? 0, fileSize: Double(viewModel?.totalSize ?? 0)) self.attribet = setFileAndCount(count: viewModel.totalFilesCount, fileSize: Double(viewModel.totalSize))
tipLabel.attributedText = attribet tipLabel.attributedText = attribet
} }
...@@ -230,10 +241,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -230,10 +241,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
switch section { switch section {
case 0: case 0:
return viewModel?.headerGroup.count ?? 0 // model?.titleModelArray.count ?? 0 return viewModel.headerGroup.count// model?.titleModelArray.count ?? 0
case 1: case 1:
return viewModel?.cardGroup.count ?? 0 //model?.otherModelArray.count ?? 0 return viewModel.cardGroup.count//model?.otherModelArray.count ?? 0
default: default:
return 0 return 0
...@@ -248,7 +259,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -248,7 +259,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
switch section { switch section {
case 0: case 0:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeTitleCollectionCell.identifiers, for: indexPath) as! HomeTitleCollectionCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeTitleCollectionCell.identifiers, for: indexPath) as! HomeTitleCollectionCell
let model = viewModel?.headerGroup[indexPath.row] let model = viewModel.headerGroup[indexPath.row]
cell.reloadUIWithModel(model: model) cell.reloadUIWithModel(model: model)
// cell.reloadCoverData() // cell.reloadCoverData()
cell.homeTititlAction = {[weak self] idx in cell.homeTititlAction = {[weak self] idx in
...@@ -259,14 +270,14 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -259,14 +270,14 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
self.titleCallBack(model,.similar) self.titleCallBack(model,.similar)
} }
} }
cell.reloadCoverData(viewModel?.headCoverImages[indexPath.row] ?? []) // cell.reloadCoverData(viewModel.headCoverImages[indexPath.row] ?? [])
if indexPath.row == 0 { // if indexPath.row == 0 {
self.dupHeadCell = cell // self.dupHeadCell = cell
// cell.reloadCoverData(viewModel?.dupCoverImage ?? []) // // cell.reloadCoverData(viewModel.dupCoverImage ?? [])
}else{ // }else{
// cell.reloadCoverData(viewModel?.similarCoverImage ?? []) // // cell.reloadCoverData(viewModel.similarCoverImage ?? [])
self.similarHeadCell = cell // self.similarHeadCell = cell
} // }
if cell.model?.assets.count ?? 0 > 0 { if cell.model?.assets.count ?? 0 > 0 {
cell.fileLabel?.isHidden = false cell.fileLabel?.isHidden = false
...@@ -278,15 +289,15 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -278,15 +289,15 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
case 1: case 1:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeOtherCollectionCell.identifier, for: indexPath) as! HomeOtherCollectionCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeOtherCollectionCell.identifier, for: indexPath) as! HomeOtherCollectionCell
cell.dealMediaType(indexPath.row) cell.dealMediaType(indexPath.row)
let model = viewModel?.cardGroup[indexPath.row] let model = viewModel.cardGroup[indexPath.row]
cell.reloadUIWithModel(model: model) cell.reloadUIWithModel(model: model)
Task { // Task {
if let image = await viewModel?.coverCache.getImage(index: indexPath.row) { // if let image = await viewModel.coverCache.getImage(index: indexPath.row) {
await MainActor.run { // await MainActor.run {
cell.setCoverImageOrVideo(image: image) // cell.setCoverImageOrVideo(image: image)
} // }
} // }
} // }
return cell return cell
default: default:
return UICollectionViewCell() return UICollectionViewCell()
...@@ -298,10 +309,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -298,10 +309,10 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
if indexPath.section == 0 { if indexPath.section == 0 {
let model = viewModel?.headerGroup[indexPath.row] //model?.titleModelArray[indexPath.row] let model = viewModel.headerGroup[indexPath.row] //model?.titleModelArray[indexPath.row]
if model?.assets.count ?? 0 > 0 { if model.assets.count > 0 {
return (model?.assets.first?.count ?? 0) > 2 ? ((collection.width - marginLR - 20) / 2.5) + 64 : ((collection.width - 2 * marginLR - 10) / 2) + 64 return (model.assets.first?.count ?? 0) > 2 ? ((collection.width - marginLR - 20) / 2.5) + 64 : ((collection.width - 2 * marginLR - 10) / 2) + 64
}else{ }else{
return 52 return 52
...@@ -310,9 +321,9 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -310,9 +321,9 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
}else { }else {
let model = viewModel?.cardGroup[indexPath.row] //model?.otherModelArray[indexPath.row] let model = viewModel.cardGroup[indexPath.row] //model?.otherModelArray[indexPath.row]
return itemWidth + 12 + UILabel.getSizeWith(font: UIFont.systemFont(ofSize: 16, weight: .bold),lineSpacing: 5, width: itemWidth - 32, numberOfLines: 0, content: model?.folderName ?? "").height return itemWidth + 12 + UILabel.getSizeWith(font: UIFont.systemFont(ofSize: 16, weight: .bold),lineSpacing: 5, width: itemWidth - 32, numberOfLines: 0, content: model.folderName).height
} }
} }
...@@ -360,7 +371,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -360,7 +371,7 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (indexPath.section == 0) { if (indexPath.section == 0) {
let smodel = viewModel?.headerGroup[indexPath.row] //model?.titleModelArray[indexPath.row] let smodel = viewModel.headerGroup[indexPath.row] //model?.titleModelArray[indexPath.row]
if indexPath.row == 0 { if indexPath.row == 0 {
titleCallBack(smodel,.duplicates) titleCallBack(smodel,.duplicates)
}else{ }else{
...@@ -368,8 +379,8 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -368,8 +379,8 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
} }
}else{ }else{
guard let smodel = viewModel?.cardGroup[indexPath.row] else { return } //model?.otherModelArray[indexPath.row] //guard let smodel = viewModel.cardGroup[indexPath.row] else { return } //model?.otherModelArray[indexPath.row]
let smodel = viewModel.cardGroup[indexPath.row]
otherItemCallBack(smodel,indexPath.row) otherItemCallBack(smodel,indexPath.row)
} }
} }
...@@ -385,8 +396,8 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol ...@@ -385,8 +396,8 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
} }
header.cleanNowButtonCallback = {[weak self] in header.cleanNowButtonCallback = {[weak self] in
guard let self = self else {return} guard let self = self else {return}
let smodel = self.viewModel?.headerGroup[0] //self.model?.titleModelArray[0] let smodel = self.viewModel.headerGroup[0]
let vc = HomeInfoViewController(ids: smodel!.assets , type: .duplicates,titleText: smodel!.folderName) let vc = HomeInfoViewController(ids: smodel.assets , type: .duplicates,titleText: smodel.folderName)
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true) self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
} }
if indexPath.section != 0 { if indexPath.section != 0 {
......
...@@ -281,6 +281,14 @@ class HomeOtherCollectionCell: UICollectionViewCell { ...@@ -281,6 +281,14 @@ class HomeOtherCollectionCell: UICollectionViewCell {
}else{ }else{
self.sizeLabel.text = String(format: "(%.2lf) GB" ,sizeKB/(1000 * 1000)) self.sizeLabel.text = String(format: "(%.2lf) GB" ,sizeKB/(1000 * 1000))
} }
let placeImage = self.mediaType == 0 ? "othermoren" : self.mediaType == 2 ? "photosmoren" : "videosmoren"
if let id = model.assets.first?.first?.localIdentifier{
imageView.asset.load(withLocalIdentifier:id,placeholder: UIImage(named: placeImage))
}else{
self.imageView.image = UIImage.init(named: placeImage)
}
} }
......
...@@ -131,18 +131,26 @@ class HomeTitleCollectionCell:UICollectionViewCell { ...@@ -131,18 +131,26 @@ class HomeTitleCollectionCell:UICollectionViewCell {
fileLabel?.text = "\(count)" + " Photos " + (model.allFileSize > 0 ? "(\(formatFileSize(model.allFileSize)))" : "(Calculating...)") fileLabel?.text = "\(count)" + " Photos " + (model.allFileSize > 0 ? "(\(formatFileSize(model.allFileSize)))" : "(Calculating...)")
fileLabel?.sizeToFit() fileLabel?.sizeToFit()
collectionView?.reloadData()
}
func reloadCoverData(_ assets:[AssetModel]){
assetsModels.removeAll() assetsModels.removeAll()
assetsModels = assets.compactMap({ model in
return ImageCollectionModel.init(asset: model) if let assets = model.assets.first{
}) assetsModels = assets.compactMap({ model in
Print("刷新头部封面\(self)",assets.count) return ImageCollectionModel.init(asset: model)
collectionView?.reloadData() })
collectionView?.reloadData()
}
} }
// func reloadCoverData(_ assets:[AssetModel]){
// assetsModels.removeAll()
// assetsModels = assets.compactMap({ model in
// return ImageCollectionModel.init(asset: model)
// })
// Print("刷新头部封面\(self)",assets.count)
// collectionView?.reloadData()
// }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
......
...@@ -47,8 +47,6 @@ class ImageCollectionCell:UICollectionViewCell { ...@@ -47,8 +47,6 @@ class ImageCollectionCell:UICollectionViewCell {
backImageView?.asset.load(withLocalIdentifier: model.asset.localIdentifier,placeholder: UIImage.init(named: "othermoren")) backImageView?.asset.load(withLocalIdentifier: model.asset.localIdentifier,placeholder: UIImage.init(named: "othermoren"))
} }
} }
......
...@@ -4,82 +4,119 @@ import Combine ...@@ -4,82 +4,119 @@ import Combine
class HomeViewModel { class HomeViewModel {
// 封面图片资源缓存 static let shared = HomeViewModel()
actor CoverCacheActor {
private init(){}
private var cardCoverImage:[UIImage?] = [
UIImage.init(named: "videosmoren"), func config(){}
UIImage.init(named: "photosmoren"),
UIImage.init(named: "photosmoren"),
UIImage.init(named: "videosmoren"), let trash = TrashDatabase.shared.queryAll().compactMap{$0.localIdentifier}
UIImage.init(named: "othermoren")
] let keep = GroupDatabase.shared.queryAll().compactMap{$0.localIdentifier}
func getImage(index:Int) ->UIImage?{ // 封面图片资源缓存
return cardCoverImage.safeGet(index: index) ?? nil // actor CoverCacheActor {
} //
// private var cardCoverImage:[UIImage?] = [
// UIImage.init(named: "videosmoren"),
func setImage(index:Int,image:UIImage?) { // UIImage.init(named: "photosmoren"),
guard let image = image else{ // UIImage.init(named: "photosmoren"),
return // UIImage.init(named: "videosmoren"),
} // UIImage.init(named: "othermoren")
if cardCoverImage.count > index{ // ]
cardCoverImage[index] = image //
} // func getImage(index:Int) ->UIImage?{
} // return cardCoverImage.safeGet(index: index) ?? nil
} // }
//
//
// func setImage(index:Int,image:UIImage?) {
// guard let image = image else{
// return
// }
// if cardCoverImage.count > index{
// cardCoverImage[index] = image
// }
// }
// }
private var photoManager = PhotoManager.shared private var photoManager = PhotoManager.shared
let coverCache = CoverCacheActor() // let coverCache = CoverCacheActor()
// 相册资源总大小 // 相册资源总大小
var totalSize:Int64{ var totalSize:Int64{
return videoSize + otherSize + screentSize return photoManager.videoTotalSize + photoManager.otherTotalSize + photoManager.screenShotTotalSize //videoSize + otherSize + screentSize
} }
var videoSize:Int64 = 0 // var videoSize:Int64 = 0
var otherSize:Int64 = 0 // var otherSize:Int64 = 0
var screentSize:Int64 = 0 // var screentSize:Int64 = 0
var totalFilesCount:Int = 0 var totalFilesCount:Int = 0
// 首页UI数据结构 // 首页UI数据结构
var headerGroup:[HomePhotosModel] = [ var headerGroup:[HomePhotosModel]{
HomePhotosModel.init(folderName: HomeUIEnum.Dublicates.title, allFileSize: 0, assets: []), return [
HomePhotosModel.init(folderName: HomeUIEnum.Similar.title, allFileSize: 0, assets: []) HomePhotosModel.init(
] folderName: HomeUIEnum.Dublicates.title,
allFileSize:getTotalSize(source: photoManager.duplicateModels),
var cardGroup:[HomePhotosModel] = [ assets: photoManager.duplicateModels),
HomePhotosModel.init(folderName: HomeUIEnum.Videos.title, allFileSize: 0, assets: []),
HomePhotosModel.init(folderName: HomeUIEnum.SimilarScreenshots.title, allFileSize: 0, assets: []), HomePhotosModel.init(
HomePhotosModel.init(folderName: HomeUIEnum.Screensshots.title, allFileSize: 0, assets: []), folderName: HomeUIEnum.Similar.title,
HomePhotosModel.init(folderName: HomeUIEnum.SimilarVideos.title, allFileSize: 0, assets: []), allFileSize:getTotalSize(source: photoManager.filterSimilarModels),
HomePhotosModel.init(folderName: HomeUIEnum.Other.title, allFileSize: 0, assets: []), assets:photoManager.filterSimilarModels)
] ]
}
// 首页UI数据结构
var cardGroup:[HomePhotosModel]{
return [
HomePhotosModel.init(
folderName: HomeUIEnum.Videos.title,
allFileSize:getTotalSize(source: [photoManager.filterVideoModels]),
assets: [photoManager.filterVideoModels]),
HomePhotosModel.init(
folderName: HomeUIEnum.SimilarScreenshots.title,
allFileSize: getTotalSize(source: photoManager.filterSimilarScreenShotModels),
assets: photoManager.filterSimilarScreenShotModels),
HomePhotosModel.init(
folderName: HomeUIEnum.Screensshots.title,
allFileSize:getTotalSize(source: [photoManager.filterScreenShotModels]),
assets:[photoManager.filterScreenShotModels]),
HomePhotosModel.init(
folderName: HomeUIEnum.SimilarVideos.title,
allFileSize:getTotalSize(source: photoManager.filterSimilarVideoModels),
assets: photoManager.filterSimilarVideoModels),
HomePhotosModel.init(
folderName: HomeUIEnum.Other.title,
allFileSize:getTotalSize(source: [photoManager.filterOtherModels]),
assets: [photoManager.filterOtherModels]),
]
}
func getTotalSize(source:[[AssetModel]]) ->Double{
return source.flatMap{$0}.reduce(0){$0+$1.assetSize}
}
var hadLoad = false var hadLoad = false
// 数据获取回调 // 数据获取回调
var homeDataChanged:((_ section:Int,_ row:Int) ->Void)? var homeDataChanged:((_ section:Int,_ row:Int) ->Void)?
// 封面获取回调 var reloadCellHeight:(() ->Void)?
var coverHadChange:(() ->Void)?
// 添加状态变化的回调闭包 // var dupCoverImage:[AssetModel] = []
var onLoadingStateChanged: ((BaseDataLoadingState) -> Void)? //
// var similarCoverImage:[AssetModel] = []
var dupCoverImage:[AssetModel] = [] //
// var headCoverImages:[[AssetModel]]{
var similarCoverImage:[AssetModel] = [] // return [
// dupCoverImage,
var headCoverImages:[[AssetModel]]{ // similarCoverImage
return [ // ]
dupCoverImage, // }
similarCoverImage
]
}
func setupBindings() { func setupBindings() {
...@@ -106,9 +143,11 @@ class HomeViewModel { ...@@ -106,9 +143,11 @@ class HomeViewModel {
photoManager.convertScreenShotModels {[weak self] screens, size in photoManager.convertScreenShotModels {[weak self] screens, size in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
let type = HomeUIEnum.Screensshots let type = HomeUIEnum.Screensshots
weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [screens]) // weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [screens])
weakSelf.getCoverImage(type: type, identifier: screens.first?.localIdentifier)
weakSelf.screentSize = size // weakSelf.getCoverImage(type: type, identifier: screens.first?.localIdentifier)
//weakSelf.screentSize = size
weakSelf.filterResource()
weakSelf.homeDataChanged?(1,type.index) weakSelf.homeDataChanged?(1,type.index)
} }
...@@ -116,9 +155,10 @@ class HomeViewModel { ...@@ -116,9 +155,10 @@ class HomeViewModel {
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
let type = HomeUIEnum.Other let type = HomeUIEnum.Other
weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [others]) // weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [others])
weakSelf.getCoverImage(type: type, identifier: others.first?.localIdentifier) // weakSelf.getCoverImage(type: type, identifier: others.first?.localIdentifier)
weakSelf.otherSize = size // weakSelf.otherSize = size
weakSelf.filterResource()
weakSelf.homeDataChanged?(1,type.index) weakSelf.homeDataChanged?(1,type.index)
} }
...@@ -126,9 +166,10 @@ class HomeViewModel { ...@@ -126,9 +166,10 @@ class HomeViewModel {
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
let type = HomeUIEnum.Videos let type = HomeUIEnum.Videos
weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [videos]) // weakSelf.cardGroup[type.index] = HomePhotosModel.init(folderName: type.title, allFileSize: Double(size), assets: [videos])
weakSelf.getCoverImage(type: type, identifier: videos.first?.localIdentifier) // weakSelf.getCoverImage(type: type, identifier: videos.first?.localIdentifier)
weakSelf.videoSize = size // weakSelf.videoSize = size
weakSelf.filterResource()
weakSelf.homeDataChanged?(1,type.index) weakSelf.homeDataChanged?(1,type.index)
} }
startMainTask() startMainTask()
...@@ -136,185 +177,270 @@ class HomeViewModel { ...@@ -136,185 +177,270 @@ class HomeViewModel {
func startMainTask(){ func startMainTask(){
DispatchQueue.main.async {[weak self] in getSimilarOptimizer()
guard let weakSelf = self else { return } getSimilarScreenOptimizer()
weakSelf.getSimilarOptimizer() getSimilarVideoOptimizer()
weakSelf.getSimilarScreenOptimizer() getGroupDuplicateImages()
weakSelf.getSimilarVideoOptimizer()
weakSelf.getGroupDuplicateImages() // DispatchQueue.main.async {[weak self] in
} // guard let weakSelf = self else { return }
// weakSelf.getSimilarOptimizer()
// weakSelf.getSimilarScreenOptimizer()
// weakSelf.getSimilarVideoOptimizer()
// //weakSelf.getGroupDuplicateImages()
// }
} }
// 获取相似图片 // 获取相似图片
@MainActor func getSimilarOptimizer(){ func getSimilarOptimizer(){
let type = HomeUIEnum.Similar let type = HomeUIEnum.Similar
var currentGorup:[[AssetModel]] = [] // var currentGorup:[[AssetModel]] = []
var currentSize:Double = 0 // var currentSize:Double = 0
var firstId:String? // var firstId:String?
var hadblock = false
PhotoSimilarManager.shared.findSimilarAssets(in: photoManager.otherAssets) {[weak self] group in PhotoSimilarManager.shared.findSimilarAssets(in: photoManager.otherAssets) {[weak self] group in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
currentGorup.append(group) // currentGorup.append(group)
currentSize += group.reduce(0){$0+$1.assetSize} // currentSize += group.reduce(0){$0+$1.assetSize}
// weakSelf.totalSize += Int64(currentSize) // weakSelf.totalSize += Int64(currentSize)
weakSelf.headerGroup[type.index].assets = currentGorup // weakSelf.headerGroup[type.index].assets = currentGorup
weakSelf.headerGroup[type.index].allFileSize = currentSize // weakSelf.headerGroup[type.index].allFileSize = currentSize
weakSelf.photoManager.similarModels.append(group)
if let id = currentGorup.first?.first?.localIdentifier{ // if let id = weakSelf.photoManager.similarModels.first?.first?.localIdentifier{
if firstId != id{ // if firstId != id{
firstId = id // firstId = id
weakSelf.similarCoverImage = group // weakSelf.similarCoverImage = group
weakSelf.coverHadChange?() // weakSelf.coverHadChange?()
} // }
// }
if !hadblock{
weakSelf.reloadCellHeight?()
hadblock = true
} }
weakSelf.homeDataChanged?(0,type.index) weakSelf.homeDataChanged?(0,type.index)
} completionHandler: {[weak self] totalGroup in } completionHandler: {[weak self] totalGroup in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
print("获取相似图片完成",totalGroup.count) print("获取相似图片完成",totalGroup.count)
weakSelf.photoManager.similarModels = totalGroup weakSelf.photoManager.similarModels = totalGroup
weakSelf.filterResource()
weakSelf.homeDataChanged?(0,type.index)
} }
} }
// 获取相似截图 // 获取相似截图
@MainActor func getSimilarScreenOptimizer(){ func getSimilarScreenOptimizer(){
let type = HomeUIEnum.SimilarScreenshots let type = HomeUIEnum.SimilarScreenshots
var currentGorup:[[AssetModel]] = [] // var currentGorup:[[AssetModel]] = []
var currentSize:Double = 0 // var currentSize:Double = 0
var firstId:String? // var firstId:String?
ScreenshotSimilarJSONManager.shared.findSimilarAssets(in: photoManager.screenShotAssets) {[weak self] group in ScreenshotSimilarJSONManager.shared.findSimilarAssets(in: photoManager.screenShotAssets) {[weak self] group in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
currentGorup.append(group) // currentGorup.append(group)
currentSize += group.reduce(0){$0+$1.assetSize} // currentSize += group.reduce(0){$0+$1.assetSize}
// weakSelf.totalSize += Int64(currentSize) // weakSelf.totalSize += Int64(currentSize)
weakSelf.cardGroup[type.index].assets = currentGorup // weakSelf.cardGroup[type.index].assets = currentGorup
weakSelf.cardGroup[type.index].allFileSize = currentSize // weakSelf.cardGroup[type.index].allFileSize = currentSize
//
if let id = currentGorup.first?.first?.localIdentifier{ // if let id = currentGorup.first?.first?.localIdentifier{
if firstId != id{ // if firstId != id{
firstId = id // firstId = id
weakSelf.getCoverImage(type: type, identifier: firstId) // weakSelf.getCoverImage(type: type, identifier: firstId)
} // }
} // }
weakSelf.photoManager.similarScreenShotModels.append(group)
weakSelf.homeDataChanged?(1,type.index) weakSelf.homeDataChanged?(1,type.index)
} completionHandler: {[weak self] totalGroup in } completionHandler: {[weak self] totalGroup in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
print("获取相似截图完成",totalGroup.count) print("获取相似截图完成",totalGroup.count)
weakSelf.filterResource()
weakSelf.photoManager.similarScreenShotModels = totalGroup weakSelf.photoManager.similarScreenShotModels = totalGroup
} }
} }
@MainActor func getSimilarVideoOptimizer(){ func getSimilarVideoOptimizer(){
let type = HomeUIEnum.SimilarVideos let type = HomeUIEnum.SimilarVideos
var currentGorup:[[AssetModel]] = [] // var currentGorup:[[AssetModel]] = []
var currentSize:Double = 0 // var currentSize:Double = 0
var firstId:String? // var firstId:String?
VideoSimilarJSONManager.shared.findSimilarVideos(in: photoManager.videoAssets) {[weak self] group in VideoSimilarJSONManager.shared.findSimilarVideos(in: photoManager.videoAssets) {[weak self] group in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
currentGorup.append(group) // currentGorup.append(group)
currentSize += group.reduce(0){$0+$1.assetSize} // currentSize += group.reduce(0){$0+$1.assetSize}
//
weakSelf.cardGroup[type.index].assets = currentGorup // weakSelf.cardGroup[type.index].assets = currentGorup
weakSelf.cardGroup[type.index].allFileSize = currentSize // weakSelf.cardGroup[type.index].allFileSize = currentSize
//
if let id = currentGorup.first?.first?.localIdentifier{ // if let id = currentGorup.first?.first?.localIdentifier{
if firstId != id{ // if firstId != id{
firstId = id // firstId = id
weakSelf.getCoverImage(type: type, identifier: firstId) // weakSelf.getCoverImage(type: type, identifier: firstId)
} // }
} // }
weakSelf.photoManager.similarVideoModels.append(group)
weakSelf.homeDataChanged?(1,type.index) weakSelf.homeDataChanged?(1,type.index)
} completionHandler: {[weak self] totalGroup in } completionHandler: {[weak self] totalGroup in
print("获取相似视频完成",totalGroup.count) print("获取相似视频完成",totalGroup.count)
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
weakSelf.filterResource()
weakSelf.photoManager.similarVideoModels = totalGroup weakSelf.photoManager.similarVideoModels = totalGroup
} }
} }
// 获取重复图片 // 获取重复图片
@MainActor func getGroupDuplicateImages(){ func getGroupDuplicateImages(){
let type = HomeUIEnum.Dublicates let type = HomeUIEnum.Dublicates
var currentGorup:[[AssetModel]] = [] // var currentGorup:[[AssetModel]] = []
var currentSize:Double = 0 // var currentSize:Double = 0
var firstId:String? // var firstId:String?
PhotoDuplicateManager.shared.findDuplicateAssets(in: photoManager.otherAssets, mediaType: .photo) {[weak self] groups in PhotoDuplicateManager.shared.findDuplicateAssets(in: photoManager.otherAssets, mediaType: .photo) {[weak self] groups in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
// let size = groups.map{$0}.reduce(into: 0){$0+$1.assetSize} // let size = groups.map{$0}.reduce(into: 0){$0+$1.assetSize}
weakSelf.headerGroup[type.index].assets = groups // weakSelf.headerGroup[type.index].assets = groups
weakSelf.headerGroup[type.index].allFileSize = currentSize // weakSelf.headerGroup[type.index].allFileSize = currentSize
if let id = groups.first?.first?.localIdentifier{ weakSelf.photoManager.duplicateModels = groups
if firstId != id{
firstId = id
weakSelf.dupCoverImage = groups.first ?? []
weakSelf.coverHadChange?()
}
}
// if let id = groups.first?.first?.localIdentifier{
// if firstId != id{
// firstId = id
// weakSelf.dupCoverImage = groups.first ?? []
// weakSelf.coverHadChange?()
// }
// }
let currentThread = Thread.current
if currentThread.isMainThread {
print("在主线程执行")
} else {
print("在后台线程执行")
}
weakSelf.reloadCellHeight?()
weakSelf.homeDataChanged?(0,type.index) weakSelf.homeDataChanged?(0,type.index)
} progressHandler: {[weak self] group in } progressHandler: {group in
guard let weakSelf = self else { return } // guard let weakSelf = self else { return }
// currentGorup.append(group)
currentGorup.append(group) // currentSize += group.reduce(0){$0+$1.assetSize}
currentSize += group.reduce(0){$0+$1.assetSize} //
// weakSelf.headerGroup[type.index].assets = currentGorup
// weakSelf.headerGroup[type.index].allFileSize = currentSize
weakSelf.headerGroup[type.index].assets = currentGorup // weakSelf.photoManager.duplicateModels = groups
weakSelf.headerGroup[type.index].allFileSize = currentSize
if let id = currentGorup.first?.first?.localIdentifier{ // 从 group 中过滤掉存在于 duplicateModels 中的元素
if firstId != id{
firstId = id
weakSelf.dupCoverImage = group
weakSelf.coverHadChange?()
}
}
weakSelf.homeDataChanged?(0,type.index) // let filteredGroup = group.filter { item in
// !weakSelf.photoManager.duplicateModels.flatMap { $0 }.contains(item)
// }
//
//
// if let id = currentGorup.first?.first?.localIdentifier{
// if firstId != id{
// firstId = id
// weakSelf.dupCoverImage = group
// weakSelf.coverHadChange?()
// }
// }
//
// weakSelf.homeDataChanged?(0,type.index)
} completionHandler: {[weak self] totalGroup in } completionHandler: {[weak self] totalGroup in
guard let weakSelf = self else { return } guard let weakSelf = self else { return }
print("获得重复图片完成",totalGroup.count)
let currentThread = Thread.current
if currentThread.isMainThread {
print("在主线程执行")
} else {
print("在后台线程执行")
}
weakSelf.photoManager.duplicateModels = totalGroup weakSelf.photoManager.duplicateModels = totalGroup
weakSelf.reloadCellHeight?()
weakSelf.homeDataChanged?(0,type.index)
} }
} }
func getCoverImage(type:HomeUIEnum,identifier:String?){ // func getCoverImage(type:HomeUIEnum,identifier:String?){
guard let identifier = identifier else{ // guard let identifier = identifier else{
return // return
} // }
print("执行一次\(type.title)获取封面,id=\(identifier)") // print("执行一次\(type.title)获取封面,id=\(identifier)")
Task { // Task {
PhotoManager.shared.getImage(localIdentifier:identifier,completion: { [weak self] image in // PhotoManager.shared.getImage(localIdentifier:identifier,completion: { [weak self] image in
guard let self = self else { return } // guard let self = self else { return }
Task { // Task {
switch type { // switch type {
case .Dublicates: // case .Dublicates:
break // break
case .Similar: // case .Similar:
break // break
default: // default:
await self.coverCache.setImage(index: type.index, image: image) // await self.coverCache.setImage(index: type.index, image: image)
await MainActor.run { // await MainActor.run {
self.coverHadChange?() // self.coverHadChange?()
} // }
} // }
} // }
}) // })
// }
// }
}
extension HomeViewModel{
// 基本资源过滤保留和垃圾桶资源
func filterResource(){
let filterArray = trash + keep
let others = removeAssets(withIdentifiers: filterArray, from: photoManager.otherModels)
let videos = removeAssets(withIdentifiers: filterArray, from: photoManager.videoModels)
let screens = removeAssets(withIdentifiers: filterArray, from: photoManager.screenShotModels)
photoManager.filterOtherModels = others
photoManager.filterVideoModels = videos
photoManager.filterScreenShotModels = screens
let similarPhotos = filterGroups(photoManager.similarModels, byExcludingIDs: filterArray)
let similarVideos = filterGroups(photoManager.similarVideoModels, byExcludingIDs: filterArray)
let similarShots = filterGroups(photoManager.similarScreenShotModels, byExcludingIDs: filterArray)
photoManager.filterSimilarModels = similarPhotos
photoManager.filterSimilarVideoModels = similarVideos
photoManager.filterSimilarScreenShotModels = similarShots
reloadCellHeight?()
}
func removeAssets(withIdentifiers identifiers: [String], from assets: [AssetModel]) -> [AssetModel] {
let identifierSet = Set(identifiers)
return assets.filter { !identifierSet.contains($0.localIdentifier) }
}
func filterGroups(_ groups: [[AssetModel]], byExcludingIDs ids: [String]) -> [[AssetModel]] {
let excludeSet = Set(ids)
return groups.filter { group in
// 检查子数组中是否所有元素的ID都不在排除列表中
group.allSatisfy { !excludeSet.contains($0.localIdentifier) }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment