Commit 656d7fa9 authored by shenyong's avatar shenyong

app内资源删除

parent d9f0c377
......@@ -69,137 +69,16 @@ actor PhotoSimilarStateManager {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
}
}
// 截图状态管理 actor
actor ScreenshotSimilarStateManager {
private var timeGroups: [TimeGroupModel] = []
private var similarGroups: [SimilarGroupModel] = []
private var pendingSimilarGroups: [SimilarGroupModel] = []
private var processedGroupCount: Int = 0
private var assetsImageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
func appendTimeGroup(_ group: TimeGroupModel) {
timeGroups.append(group)
}
func appendSimilarGroup(_ group: SimilarGroupModel) {
pendingSimilarGroups.append(group)
processedGroupCount += 1
}
func getAllTimeGroups() -> [TimeGroupModel] {
return timeGroups
}
func getpendingSimilarGroups() -> [SimilarGroupModel] {
return pendingSimilarGroups
}
func getAllSimilarGroups() -> [SimilarGroupModel] {
return similarGroups
}
func getCachedImage(for identifier: String) -> UIImage? {
return assetsImageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
assetsImageCache[identifier] = image
}
func shouldSavePendingGroups() -> Bool {
return processedGroupCount >= 10
}
func getCachedHash(for identifier: String) async -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) async {
hashCache[identifier] = hash
}
func savePendingGroups() {
similarGroups.append(contentsOf: pendingSimilarGroups)
pendingSimilarGroups.removeAll()
processedGroupCount = 0
}
func loadStoredData(timeGroups: [TimeGroupModel], similarGroups: [SimilarGroupModel]) {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
}
}
// 视频状态管理 actor
actor VideoSimilarStateManager {
private var timeGroups: [TimeGroupModel] = []
private var similarGroups: [SimilarGroupModel] = []
private var pendingSimilarGroups: [SimilarGroupModel] = []
private var processedGroupCount: Int = 0
private var assetsImageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
func appendTimeGroup(_ group: TimeGroupModel) {
timeGroups.append(group)
}
func appendSimilarGroup(_ group: SimilarGroupModel) {
pendingSimilarGroups.append(group)
processedGroupCount += 1
}
func getAllTimeGroups() -> [TimeGroupModel] {
return timeGroups
}
func getpendingSimilarGroups() -> [SimilarGroupModel] {
return pendingSimilarGroups
}
func getAllSimilarGroups() -> [SimilarGroupModel] {
return similarGroups
}
func getCachedImage(for identifier: String) -> UIImage? {
return assetsImageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
assetsImageCache[identifier] = image
}
func shouldSavePendingGroups() -> Bool {
return processedGroupCount >= 10
}
func getCachedHash(for identifier: String) async -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) async {
hashCache[identifier] = hash
}
func savePendingGroups() {
similarGroups.append(contentsOf: pendingSimilarGroups)
pendingSimilarGroups.removeAll()
processedGroupCount = 0
}
func loadStoredData(timeGroups: [TimeGroupModel], similarGroups: [SimilarGroupModel]) {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
func deleteData(for idsToRemove:[String]){
let updateGroups = self.similarGroups.removingGroupsWithEmptyAssetsWithDetails(afterRemovingIDs: idsToRemove)
self.similarGroups = updateGroups.updatedGroups
}
}
actor PhotoDuplicateStateManager {
private var duplicateGroups: [DuplicateGroupModel] = []
private var pendingDuplicateGroups: [DuplicateGroupModel] = []
private var duplicateGroups: [SimilarGroupModel] = []
private var pendingDuplicateGroups: [SimilarGroupModel] = []
// 缓存
private var imageCache: [String: UIImage] = [:]
......@@ -207,15 +86,15 @@ actor PhotoDuplicateStateManager {
// MARK: - 公共方法
func loadStoredData(duplicateGroups: [DuplicateGroupModel]) {
func loadStoredData(duplicateGroups: [SimilarGroupModel]) {
self.duplicateGroups = duplicateGroups
}
func getAllDuplicateGroups() -> [DuplicateGroupModel] {
func getAllDuplicateGroups() -> [SimilarGroupModel] {
return duplicateGroups
}
func appendDuplicateGroup(_ group: DuplicateGroupModel) {
func appendDuplicateGroup(_ group: SimilarGroupModel) {
pendingDuplicateGroups.append(group)
}
......@@ -228,7 +107,7 @@ actor PhotoDuplicateStateManager {
pendingDuplicateGroups.removeAll()
}
func getPendingDuplicateGroups() -> [DuplicateGroupModel] {
func getPendingDuplicateGroups() -> [SimilarGroupModel] {
return pendingDuplicateGroups
}
......@@ -253,5 +132,10 @@ actor PhotoDuplicateStateManager {
func setCachedHash(_ hash: String, for identifier: String) {
hashCache[identifier] = hash
}
func deleteData(for idsToRemove:[String]){
let updateGroups = self.duplicateGroups.removingGroupsWithEmptyAssetsWithDetails(afterRemovingIDs: idsToRemove)
self.duplicateGroups = updateGroups.updatedGroups
}
}
......@@ -702,10 +702,14 @@ extension PhotoManager{
extension PhotoManager{
// 处理app图片删除
func removeDataWhenDeleteInPage(data:[AssetModel]){
func removeDataWhenDeleteInPage(data:[AssetModel],completionHandler: (() -> Void)? = nil){
Task.detached(priority: .background) { [weak self] in
guard let self = self else { return }
let deletes = data.compactMap{$0.localIdentifier}
// 临时数据更新
let others = removeAssets(withIdentifiers: deletes, from: otherModels)
let videos = removeAssets(withIdentifiers: deletes, from: videoModels)
let screens = removeAssets(withIdentifiers: deletes, from: screenShotModels)
......@@ -714,12 +718,28 @@ extension PhotoManager{
videoModels = videos
screenShotModels = screens
// 临时数据更新
let similarPhotos = filterGroups(similarModels, byExcludingIDs: deletes)
let similarVideos = filterGroups(similarVideoModels, byExcludingIDs: deletes)
let similarShots = filterGroups(similarScreenShotModels, byExcludingIDs: deletes)
similarModels = similarPhotos
similarVideoModels = similarVideos
similarScreenShotModels = similarShots
// 保存到本地
saveToLocal(type: "video", models: self.videoModels)
saveToLocal(type: "other", models: self.otherModels)
saveToLocal(type: "screenshot", models: self.screenShotModels)
await PhotoSimilarManager.shared.removeLocalFileWithIds(for: deletes)
await ScreenshotSimilarJSONManager.shared.removeLocalFileWithIds(for: deletes)
await VideoSimilarJSONManager.shared.removeLocalFileWithIds(for: deletes)
await PhotoDuplicateManager.shared.removeLocalFileWithIds(for: deletes)
completionHandler?()
}
}
......
......@@ -86,17 +86,18 @@ struct SimilarGroupModel: Codable {
var assets: [AssetModel]
}
// 重复图片组模型
struct DuplicateGroupModel: Codable {
let groupId: String
let assets: [AssetModel]
}
struct Match {
let groupIndex: Int
let matchedElements: [AssetModel]
}
struct RemovalResult {
let updatedGroups: [SimilarGroupModel]
let removedAssets: [AssetModel]
let removedGroups: [SimilarGroupModel]
}
extension Sequence where Element == [AssetModel] {
/// 判断二维数组中是否包含某个子数组,其元素的localIdentifier集合与给定数组完全相同(忽略顺序)
......@@ -124,3 +125,69 @@ extension Sequence where Element == [AssetModel] {
return matches
}
}
extension SimilarGroupModel {
/// 过滤掉assets中包含指定ID的元素
func removingAssets(withIDs ids: Set<String>) -> SimilarGroupModel {
let filteredAssets = self.assets.filter { !ids.contains($0.localIdentifier) }
return SimilarGroupModel(groupId: self.groupId, assets: filteredAssets)
}
}
extension Array where Element == SimilarGroupModel {
/// 批量过滤所有组中的assets,移除包含指定ID的元素,并移除assets为空的组
func removingGroupsWithEmptyAssets(afterRemovingIDs ids: [String]) -> [SimilarGroupModel] {
let idSet = Set(ids)
return self.compactMap { group in
let filteredGroup = group.removingAssets(withIDs: idSet)
return filteredGroup.assets.isEmpty ? nil : filteredGroup
}
}
/// 原地修改:直接在原数组上移除包含指定ID的元素,并移除assets为空的组
mutating func removeGroupsWithEmptyAssetsInPlace(afterRemovingIDs ids: [String]) {
self = self.removingGroupsWithEmptyAssets(afterRemovingIDs: ids)
}
/// 删除日志,拿到执行删除后的更新数据,删除数据,移除的组
func removingGroupsWithEmptyAssetsWithDetails(afterRemovingIDs ids: [String]) -> RemovalResult {
let idSet = Set(ids)
var removedAssets = [AssetModel]()
var removedGroups = [SimilarGroupModel]()
let updated = self.compactMap { (group: SimilarGroupModel) -> SimilarGroupModel? in
let filteredAssets = group.assets.filter { !idSet.contains($0.localIdentifier) }
let removedInGroup = group.assets.filter { idSet.contains($0.localIdentifier) }
removedAssets.append(contentsOf: removedInGroup)
// 修改判断条件:当assets数量小于2时移除整个组
if filteredAssets.count < 2 {
removedGroups.append(group)
return nil
} else {
return SimilarGroupModel(groupId: group.groupId, assets: filteredAssets)
}
}
return RemovalResult(
updatedGroups: updated,
removedAssets: removedAssets,
removedGroups: removedGroups
)
}
}
extension Sequence where Element == [AssetModel] {
/// 删除二维数组中包含指定ID的元素,并移除所有元素数量少于2的子数组
func removingElementsAndSmallGroups(ids: [String]) -> [[AssetModel]] {
let idSet = Set(ids)
return self.compactMap { group in
let filtered = group.filter { !idSet.contains($0.localIdentifier) }
return filtered.count < 2 ? nil : filtered // 修改判断条件
}
}
}
......@@ -349,6 +349,16 @@ extension PhotoSimilarManager{
return SimilarGroupModel(groupId: group.groupId, assets: validAssets)
}.filter { !$0.assets.isEmpty }
}
// 移除本地文件资源
func removeLocalFileWithIds(for ids:[String]) async{
await stateManager.deleteData(for: ids)
// 保存到文件
if let data = try? JSONEncoder().encode(await stateManager.getAllSimilarGroups()) {
try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
}
}
}
// MARK: - pHash获取
......
......@@ -382,6 +382,17 @@ extension ScreenshotSimilarJSONManager{
return SimilarGroupModel(groupId: group.groupId, assets: validAssets)
}.filter { !$0.assets.isEmpty }
}
// 移除本地文件资源
func removeLocalFileWithIds(for ids:[String]) async{
await stateManager.deleteData(for: ids)
// 保存到文件
if let data = try? JSONEncoder().encode(await stateManager.getAllSimilarGroups()) {
try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
}
}
}
// MARK: - pHash获取
......
......@@ -47,7 +47,7 @@ private actor VideoAssetCacheManager {
class VideoSimilarJSONManager: @unchecked Sendable {
static let shared = VideoSimilarJSONManager()
private let stateManager = VideoSimilarStateManager()
private let stateManager = PhotoSimilarStateManager()
private init() {}
// 类中添加缓存管理器实例
......@@ -633,6 +633,16 @@ extension VideoSimilarJSONManager {
try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
}
}
// 移除本地文件资源
func removeLocalFileWithIds(for ids:[String]) async{
await stateManager.deleteData(for: ids)
// 保存到文件
if let data = try? JSONEncoder().encode(await stateManager.getAllSimilarGroups()) {
try? data.write(to: URL(fileURLWithPath: similarGroupsPath))
}
}
}
......
......@@ -328,7 +328,8 @@ class CompressCompletedViewController : BaseViewController{
models.append(deleteModel)
}
if(success){
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: models)
// PhotoDataManager.manager.removeDataWhenDeleteInPage(data: models)
PhotoManager.shared.removeDataWhenDeleteInPage(data: models)
print("删除文件成功")
self.showDeleteSuccess(fileCount:count, fileSize: fileSize)
}else {
......
......@@ -95,28 +95,16 @@ class HomeInfoViewController:BaseViewController {
self.showDeleteSuccess(fileCount: tempStringArray.count, fileSize: fileSize)
// 删除缓存数据
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: imgs)
// 更新下ids
PhotoDataManager.manager.loadFromFileSystem(resultModel: {[weak self] model in
DispatchQueue.main.async {
if self?.type == .duplicates {
self?.ids = model.titleModelArray[0].assets
}
if self?.type == .similar {
self?.ids = model.titleModelArray[1].assets
}
if self?.type == .SimilarVideos {
self?.ids = model.otherModelArray[3].assets
}
if self?.type == .similarScreenshots {
self?.ids = model.otherModelArray[1].assets
}
self?.tablewView.ids = self?.ids
self?.tablewView.deleteModel(array: imgs)
}
})
PhotoManager.shared.removeDataWhenDeleteInPage(data: imgs)
let new = self.ids?.removingElementsAndSmallGroups(ids: imgs.compactMap{$0.localIdentifier})
self.ids = new
self.tablewView.ids = self.ids
self.tablewView.deleteModel(array: imgs)
self.setDefaultPage()
}
......
......@@ -36,7 +36,7 @@ class HomePhotosDetailViewController : BaseViewController {
func dealData(){
var dataArray : [AssetModel] = []
for item in self.model.assets {
for item in self.model.originalAssets {
dataArray = dataArray + item
}
self.resourceData = self.filterTrashData(array: dataArray)
......@@ -245,6 +245,8 @@ class HomePhotosDetailViewController : BaseViewController {
showTipsVC()
}
func showTipsVC(){
guard let mediaType = mediaType else{
......@@ -271,7 +273,7 @@ class HomePhotosDetailViewController : BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
dealData()
// 目的是为了消除cell 的选择按钮状态
if self.selectedModel.count == 0 {
self.collectionView.reloadData()
......@@ -692,8 +694,10 @@ extension HomePhotosDetailViewController:WaterfallMutiSectionDelegate,UICollecti
}
// 清理下缓存数据
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: self.selectedModel)
// PhotoDataManager.manager.removeDataWhenDeleteInPage(data: self.selectedModel)
PhotoManager.shared.removeDataWhenDeleteInPage(data: self.selectedModel) {
//删除完成刷新数据
}
// 更新页面
DispatchQueue.main.async {
// 删除完成之后,移除下当前选择的数据
......
......@@ -29,7 +29,7 @@ class HomeVideoDetailController :BaseViewController {
func dealData(){
var dataArray : [AssetModel] = []
for item in self.model.assets {
for item in self.model.originalAssets {
dataArray = dataArray + item
}
self.resourceData = self.filterTrashData(array: dataArray)
......@@ -209,7 +209,7 @@ class HomeVideoDetailController :BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
dealData()
// 目的是为了消除cell 的选择按钮状态
if self.selectedModel.count == 0 {
self.collectionView.reloadData()
......@@ -345,6 +345,7 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HomeVideoDetailCustomHeaderView", for: indexPath) as! HomeVideoDetailCustomHeaderView
// 记录当前的 headerView
currentHeaderView = header
header.compressionTipView.reloadData()
header.sizeLabel.text = "\(self.resourceData.count) Videos"
header.sortCallback = {[weak self] in
guard let self else {return}
......@@ -582,9 +583,11 @@ extension HomeVideoDetailController:WaterfallMutiSectionDelegate,UICollectionVie
// 清理下缓存数据
PhotoDataManager.manager.removeDataWhenDeleteInPage(data: self.selectedModel)
//PhotoDataManager.manager.removeDataWhenDeleteInPage(data: self.selectedModel)
PhotoManager.shared.removeDataWhenDeleteInPage(data: self.selectedModel) {
// 删除完成刷新数据
}
// 更新页面
......
......@@ -131,7 +131,6 @@ class HomeViewController:BaseViewController {
if otherItemRow == 0 {
DispatchQueue.main.async {
let vc:HomeVideoDetailController = HomeVideoDetailController(model: model)
vc.dealData()
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -158,7 +157,6 @@ class HomeViewController:BaseViewController {
}else{
vc.mediaType = .Other
}
vc.dealData()
self.navigationController?.pushViewController(vc, animated: true)
}
}
......
......@@ -36,10 +36,13 @@ class HomePhotosModel:Codable {
var allFileSize:Double
var assets:[[AssetModel]]
init(folderName: String, allFileSize: Double, assets: [[AssetModel]]) {
var originalAssets:[[AssetModel]]
init(folderName: String, allFileSize: Double, assets: [[AssetModel]],originalAssets:[[AssetModel]] = []) {
self.folderName = folderName
self.allFileSize = allFileSize
self.assets = assets
self.originalAssets = originalAssets
}
}
......
......@@ -30,6 +30,7 @@ class HomeCollectionViewHeader : UICollectionReusableView {
lazy var permissionView : PMPermissionView = {
let view = Bundle.main.loadNibNamed("PMPermissionView", owner: nil, options: nil)?.last as! PMPermissionView
view.isHidden = true
return view
}()
......
......@@ -261,7 +261,6 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeTitleCollectionCell.identifiers, for: indexPath) as! HomeTitleCollectionCell
let model = viewModel.headerGroup[indexPath.row]
cell.reloadUIWithModel(model: model)
// cell.reloadCoverData()
cell.homeTititlAction = {[weak self] idx in
guard let self = self else { return }
if indexPath.row == 0 {
......@@ -270,14 +269,6 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
self.titleCallBack(model,.similar)
}
}
// cell.reloadCoverData(viewModel.headCoverImages[indexPath.row] ?? [])
// if indexPath.row == 0 {
// self.dupHeadCell = cell
// // cell.reloadCoverData(viewModel.dupCoverImage ?? [])
// }else{
// // cell.reloadCoverData(viewModel.similarCoverImage ?? [])
// self.similarHeadCell = cell
// }
if cell.model?.assets.count ?? 0 > 0 {
cell.fileLabel?.isHidden = false
......@@ -291,13 +282,6 @@ extension HomeView:WaterfallMutiSectionDelegate,UICollectionViewDataSource,UICol
cell.dealMediaType(indexPath.row)
let model = viewModel.cardGroup[indexPath.row]
cell.reloadUIWithModel(model: model)
// Task {
// if let image = await viewModel.coverCache.getImage(index: indexPath.row) {
// await MainActor.run {
// cell.setCoverImageOrVideo(image: image)
// }
// }
// }
return cell
default:
return UICollectionViewCell()
......
......@@ -9,10 +9,28 @@ import UIKit
class VideocompressionHeadView: UIView {
@IBOutlet weak var sizeL: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
layer.cornerRadius = 8
layer.masksToBounds = true
}
func reloadData(){
let totall = PhotoManager.shared.getTotalSize(source: [PhotoManager.shared.videoModels])
let sizeKB : Double = totall/2
if sizeKB < 1000{
self.sizeL.text = String(format: "(%.2lf) KB" ,sizeKB)
}else if sizeKB < (1000 * 1000) && sizeKB > 1000{
self.sizeL.text = String(format: "(%.2lf) MB" ,sizeKB/1000)
}else{
self.sizeL.text = String(format: "(%.2lf) GB" ,sizeKB/(1000 * 1000))
}
}
}
......@@ -58,6 +58,9 @@
<constraint firstItem="vvL-sB-aRX" firstAttribute="centerX" secondItem="Qxg-Hm-QKr" secondAttribute="centerX" id="uEJ-q5-lB3"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="sizeL" destination="rDm-i5-MN1" id="AYR-5C-uKV"/>
</connections>
<point key="canvasLocation" x="187.78625954198472" y="250"/>
</view>
</objects>
......
......@@ -104,6 +104,18 @@ class MaintainViewListController: BaseViewController {
return maintaiEmptyView
}()
func dealBottomView(){
if selectAsset.count > 0{
let count = selectAsset.flatMap{$0}.count
maintaiBottomView.numberL.text = "\(count)"
view.addSubview(maintaiBottomView)
maintaiBottomView.show()
}else{
maintaiBottomView.disMiss()
}
}
}
extension MaintainViewListController:UICollectionViewDataSource,UICollectionViewDelegate{
......@@ -133,6 +145,7 @@ extension MaintainViewListController:UICollectionViewDataSource,UICollectionView
guard let weakSelf = self else { return }
weakSelf.selectAsset = selects
weakSelf.getData()
weakSelf.dealBottomView()
}
navigationController?.pushViewController(vc, animated: true)
}
......@@ -153,14 +166,7 @@ extension MaintainViewListController:UICollectionViewDataSource,UICollectionView
}
if selectAsset.count > 0{
let count = selectAsset.flatMap{$0}.count
maintaiBottomView.numberL.text = "\(count)"
view.addSubview(maintaiBottomView)
maintaiBottomView.show()
}else{
maintaiBottomView.disMiss()
}
dealBottomView()
}
}
......@@ -659,10 +659,10 @@ class PhotoAndVideoMananger {
static func deleteAssets(localIdentifiers: [String],suc:@escaping () -> ()) {
// 获取要删除的 PHAsset
PMLoadingHUD.share.show()
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: localIdentifiers, options: nil)
// 开始删除操作
PMLoadingHUD.share.show()
PHPhotoLibrary.shared().performChanges({
// 创建删除请求
PHAssetChangeRequest.deleteAssets(fetchResult)
......
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