Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
P
PhoneManager
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Yang
PhoneManager
Commits
4b9dcdaf
Commit
4b9dcdaf
authored
May 05, 2025
by
CZ1004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修改方法测试
parent
c6f00afb
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
309 additions
and
18 deletions
+309
-18
HomeViewController.swift
PhoneManager/Class/Session/Home/HomeViewController.swift
+53
-18
PhotoVideoManager.swift
...r/Class/Tool/Class/ResouceManager/PhotoVideoManager.swift
+256
-0
No files found.
PhoneManager/Class/Session/Home/HomeViewController.swift
View file @
4b9dcdaf
...
...
@@ -92,28 +92,63 @@ class HomeViewController:BaseViewController {
}
func
setupData
()
{
let
model1
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
duplicates
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
PhotoDataManager
.
manager
.
loadFromFileSystem
(
resultModel
:
{[
weak
self
]
model
in
self
?
.
homeView
?
.
model
=
model
})
let
model2
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
similar
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
PhotoAndVideoMananger
.
mananger
.
fetchAllFile
{[
weak
self
]
index
,
FileSize
in
let
model3
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
videos
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
guard
let
self
else
{
return
}
let
model4
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
similarScreenshots
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
self
.
homeView
?
.
model
?
.
allFileNumber
=
index
self
.
homeView
?
.
model
?
.
allFileSize
=
FileSize
self
.
homeView
?
.
setTitle
()
}
completion
:
{[
weak
self
]
fileSize
,
index
in
let
model5
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
screenshots
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
guard
let
self
else
{
return
}
let
model6
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
SimilarVideos
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
self
.
homeView
?
.
model
?
.
allFileNumber
=
index
self
.
homeView
?
.
model
?
.
allFileSize
=
fileSize
self
.
homeView
?
.
setTitle
(
)
let
model7
:
HomePhotosModel
=
HomePhotosModel
(
folderName
:
PhotsFileType
.
Other
.
rawValue
,
allFileSize
:
0
,
assets
:
[])
PhotoVideoManager
.
shared
.
findResourceInfo
{
progress
,
model
in
Print
(
"--------
\(
progress
)
"
)
model1
.
assets
=
model
.
dupPhotos
.
map
{
innerArray
in
innerArray
.
map
{
$0
.
localIdentifier
}
}
model2
.
assets
=
model
.
similarPhotos
.
map
{
innerArray
in
innerArray
.
map
{
$0
.
localIdentifier
}
}
model3
.
assets
=
[
model
.
videos
.
map
{
$0
.
localIdentifier
}]
model4
.
assets
=
model
.
similarScreenShots
.
map
{
innerArray
in
innerArray
.
map
{
$0
.
localIdentifier
}
}
model5
.
assets
=
[
model
.
screenShots
.
map
{
$0
.
localIdentifier
}]
model6
.
assets
=
model
.
similarVideos
.
map
{
innerArray
in
innerArray
.
map
{
$0
.
localIdentifier
}
}
model7
.
assets
=
[
model
.
others
.
map
{
$0
.
localIdentifier
}]
let
allModel
:
PhotosManagerModel
=
PhotosManagerModel
(
allFileNumber
:
0
,
allFileSize
:
0
,
titleModelArray
:
[
model1
,
model2
],
otherModelArray
:
[
model3
,
model4
,
model5
,
model6
,
model7
])
self
.
homeView
?
.
model
=
allModel
}
// PhotoDataManager.manager.loadFromFileSystem(resultModel: {[weak self] model in
//
// self?.homeView?.model = model
// })
//
// PhotoAndVideoMananger.mananger.fetchAllFile {[weak self] index, FileSize in
//
// guard let self else {return}
//
// self.homeView?.model?.allFileNumber = index
// self.homeView?.model?.allFileSize = FileSize
// self.homeView?.setTitle()
// } completion: {[weak self] fileSize,index in
//
// guard let self else {return}
//
// self.homeView?.model?.allFileNumber = index
// self.homeView?.model?.allFileSize = fileSize
// self.homeView?.setTitle()
//
// }
}
...
...
PhoneManager/Class/Tool/Class/ResouceManager/PhotoVideoManager.swift
0 → 100644
View file @
4b9dcdaf
import
Foundation
import
Photos
enum
SourceType
{
case
video
case
shotScreen
case
photo
case
other
}
struct
AssetModel
{
var
resourceType
:
SourceType
var
localIdentifier
:
String
var
createTime
:
Date
var
assetSize
:
Double
var
isCloud
:
Bool
var
width
:
Int
var
height
:
Int
}
struct
ResourceAllModel
{
var
dupPhotos
:
[[
AssetModel
]]
var
similarPhotos
:
[[
AssetModel
]]
var
similarScreenShots
:
[[
AssetModel
]]
var
similarVideos
:
[[
AssetModel
]]
var
photos
:
[
AssetModel
]
var
videos
:
[
AssetModel
]
var
screenShots
:
[
AssetModel
]
var
others
:
[
AssetModel
]
}
class
PhotoVideoManager
{
static
let
shared
=
PhotoVideoManager
()
private
let
processingQueue
=
DispatchQueue
(
label
:
"com.photoVideoManager.processing"
,
qos
:
.
userInitiated
)
// MARK: - 核心方法
func
findResourceInfo
(
progress
:
@escaping
(
Double
,
ResourceAllModel
)
->
Void
)
{
processingQueue
.
async
{
let
allAssets
=
self
.
findAllResource
()
var
resourceModel
=
self
.
createEmptyModel
()
for
(
index
,
asset
)
in
allAssets
.
enumerated
()
{
let
model
=
self
.
getPropertyFromAsset
(
asset
:
asset
)
self
.
classifyAsset
(
model
,
into
:
&
resourceModel
)
let
currentProgress
=
Double
(
index
+
1
)
/
Double
(
allAssets
.
count
)
*
0.4
self
.
safeUpdateProgress
(
currentProgress
,
model
:
resourceModel
,
progress
:
progress
)
}
self
.
processAdvancedFeatures
(
model
:
&
resourceModel
,
progress
:
progress
)
}
}
// MARK: - 私有方法
private
func
processAdvancedFeatures
(
model
:
inout
ResourceAllModel
,
progress
:
@escaping
(
Double
,
ResourceAllModel
)
->
Void
)
{
let
totalSteps
=
4.0
var
currentStep
=
0.0
// 阶段1:处理重复照片(同时收集所有重复项的ID)
model
.
dupPhotos
=
self
.
findDuplicates
(
in
:
model
.
photos
)
let
duplicateIDs
=
self
.
getAllDuplicateIdentifiers
(
dupGroups
:
model
.
dupPhotos
)
currentStep
+=
1
self
.
updateStageProgress
(
currentStep
:
currentStep
,
totalSteps
:
totalSteps
,
model
:
model
,
baseProgress
:
0.4
,
progress
:
progress
)
// 阶段2:处理相似照片(排除已标记重复的)
let
filteredPhotos
=
model
.
photos
.
filter
{
!
duplicateIDs
.
contains
(
$0
.
localIdentifier
)
}
model
.
similarPhotos
=
self
.
findSimilar
(
in
:
filteredPhotos
)
currentStep
+=
1
self
.
updateStageProgress
(
currentStep
:
currentStep
,
totalSteps
:
totalSteps
,
model
:
model
,
baseProgress
:
0.4
,
progress
:
progress
)
// 阶段3:处理相似截图(保持原逻辑)
model
.
similarScreenShots
=
self
.
findSimilar
(
in
:
model
.
screenShots
)
currentStep
+=
1
self
.
updateStageProgress
(
currentStep
:
currentStep
,
totalSteps
:
totalSteps
,
model
:
model
,
baseProgress
:
0.4
,
progress
:
progress
)
// 阶段4:处理相似视频(保持原逻辑)
model
.
similarVideos
=
self
.
findSimilar
(
in
:
model
.
videos
)
currentStep
+=
1
self
.
updateStageProgress
(
currentStep
:
currentStep
,
totalSteps
:
totalSteps
,
model
:
model
,
baseProgress
:
0.4
,
progress
:
progress
)
}
// 新增辅助方法:获取所有重复项的标识符
private
func
getAllDuplicateIdentifiers
(
dupGroups
:
[[
AssetModel
]])
->
Set
<
String
>
{
var
identifiers
=
Set
<
String
>
()
for
group
in
dupGroups
{
for
asset
in
group
{
identifiers
.
insert
(
asset
.
localIdentifier
)
}
}
return
identifiers
}
// MARK: - 核心算法
private
func
findDuplicates
(
in
assets
:
[
AssetModel
])
->
[[
AssetModel
]]
{
var
groupingDict
=
[
String
:
[
AssetModel
]]()
for
asset
in
assets
{
// 使用更精确的组合键(时间戳+尺寸+宽高)
let
timeStamp
=
String
(
format
:
"%.0f"
,
asset
.
createTime
.
timeIntervalSince1970
)
let
key
=
"
\(
timeStamp
)
_
\(
asset
.
assetSize
)
_
\(
asset
.
width
)
_
\(
asset
.
height
)
"
groupingDict
[
key
,
default
:
[]]
.
append
(
asset
)
}
return
groupingDict
.
values
.
filter
{
$0
.
count
>
1
}
}
private
func
findSimilar
(
in
assets
:
[
AssetModel
])
->
[[
AssetModel
]]
{
guard
!
assets
.
isEmpty
else
{
return
[]
}
// 根据资源类型设置不同阈值
let
typeThresholds
:
[
SourceType
:
(
time
:
TimeInterval
,
sizeDiff
:
Double
)]
=
[
.
photo
:
(
300
,
0.2
),
// 5分钟,20%大小差异
.
shotScreen
:
(
60
,
0.1
),
// 1分钟,10%差异
.
video
:
(
600
,
0.3
)
// 10分钟,30%差异
]
let
threshold
=
typeThresholds
[
assets
[
0
]
.
resourceType
]
??
(
300
,
0.2
)
let
sortedAssets
=
assets
.
sorted
{
$0
.
createTime
<
$1
.
createTime
}
var
groups
=
[[
AssetModel
]]()
var
currentGroup
:
[
AssetModel
]
=
[]
for
asset
in
sortedAssets
{
if
let
last
=
currentGroup
.
last
{
let
timeDiff
=
asset
.
createTime
.
timeIntervalSince
(
last
.
createTime
)
let
sizeRatio
=
asset
.
assetSize
/
last
.
assetSize
let
sizeValid
=
sizeRatio
>
(
1
-
threshold
.
sizeDiff
)
&&
sizeRatio
<
(
1
+
threshold
.
sizeDiff
)
let
sameAspect
=
asset
.
width
*
last
.
height
==
asset
.
height
*
last
.
width
if
timeDiff
<=
threshold
.
time
&&
sameAspect
&&
sizeValid
{
currentGroup
.
append
(
asset
)
}
else
{
if
currentGroup
.
count
>
1
{
groups
.
append
(
currentGroup
)
}
currentGroup
=
[
asset
]
}
}
else
{
currentGroup
.
append
(
asset
)
}
}
if
currentGroup
.
count
>
1
{
groups
.
append
(
currentGroup
)
}
return
groups
}
// MARK: - 辅助方法
private
func
safeUpdateProgress
(
_
value
:
Double
,
model
:
ResourceAllModel
,
progress
:
@escaping
(
Double
,
ResourceAllModel
)
->
Void
)
{
DispatchQueue
.
main
.
async
{
progress
(
min
(
max
(
value
,
0.0
),
1.0
),
model
)
}
}
private
func
updateStageProgress
(
currentStep
:
Double
,
totalSteps
:
Double
,
model
:
ResourceAllModel
,
baseProgress
:
Double
,
progress
:
@escaping
(
Double
,
ResourceAllModel
)
->
Void
)
{
let
stageProgress
=
baseProgress
+
(
currentStep
/
totalSteps
)
*
(
1.0
-
baseProgress
)
safeUpdateProgress
(
stageProgress
,
model
:
model
,
progress
:
progress
)
}
}
// MARK: - 基础功能扩展
extension
PhotoVideoManager
{
private
func
findAllResource
()
->
[
PHAsset
]
{
let
fetchOptions
=
PHFetchOptions
()
fetchOptions
.
sortDescriptors
=
[
NSSortDescriptor
(
key
:
"creationDate"
,
ascending
:
false
)]
var
results
=
[
PHAsset
]()
let
photoAssets
=
PHAsset
.
fetchAssets
(
with
:
.
image
,
options
:
fetchOptions
)
let
videoAssets
=
PHAsset
.
fetchAssets
(
with
:
.
video
,
options
:
fetchOptions
)
photoAssets
.
enumerateObjects
{
asset
,
_
,
_
in
results
.
append
(
asset
)
}
videoAssets
.
enumerateObjects
{
asset
,
_
,
_
in
results
.
append
(
asset
)
}
return
results
}
private
func
createEmptyModel
()
->
ResourceAllModel
{
ResourceAllModel
(
dupPhotos
:
[],
similarPhotos
:
[],
similarScreenShots
:
[],
similarVideos
:
[],
photos
:
[],
videos
:
[],
screenShots
:
[],
others
:
[]
)
}
private
func
getPropertyFromAsset
(
asset
:
PHAsset
)
->
AssetModel
{
let
isCloud
=
!
(
asset
.
sourceType
==
.
typeUserLibrary
)
return
AssetModel
(
resourceType
:
determineResourceType
(
asset
:
asset
),
localIdentifier
:
asset
.
localIdentifier
,
createTime
:
asset
.
creationDate
??
Date
(),
assetSize
:
getAssetSize
(
asset
:
asset
),
isCloud
:
isCloud
,
width
:
asset
.
pixelWidth
,
// 新增宽度
height
:
asset
.
pixelHeight
// 新增高度
)
}
private
func
determineResourceType
(
asset
:
PHAsset
)
->
SourceType
{
if
asset
.
mediaType
==
.
video
{
return
.
video
}
if
asset
.
mediaSubtypes
.
contains
(
.
photoScreenshot
)
{
return
.
shotScreen
}
return
asset
.
mediaType
==
.
image
?
.
photo
:
.
other
}
private
func
getAssetSize
(
asset
:
PHAsset
)
->
Double
{
return
PHAssetResource
.
assetResources
(
for
:
asset
)
.
first
?
.
value
(
forKey
:
"fileSize"
)
as?
Double
??
0
}
private
func
classifyAsset
(
_
asset
:
AssetModel
,
into
model
:
inout
ResourceAllModel
)
{
switch
asset
.
resourceType
{
case
.
photo
:
model
.
photos
.
append
(
asset
)
case
.
video
:
model
.
videos
.
append
(
asset
)
case
.
shotScreen
:
model
.
screenShots
.
append
(
asset
)
case
.
other
:
model
.
others
.
append
(
asset
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment