Commit 4a1b6582 authored by CZ1004's avatar CZ1004

【新增】视频压缩基础代码

parent 210ea389
......@@ -8,28 +8,52 @@
import Foundation
import Photos
typealias CompressSelectCellCallback = (ResourceModel,Bool)->Void
typealias CompressSelectCellCallback = (AssetModel,Bool)->Void
class CompressSelectCell : UICollectionViewCell {
var callBack : CompressSelectCellCallback = {model,choose in}
var currentMediaType : Int = 0
var currentMediaType : CompressType = .compressPhoto {
didSet{
DispatchQueue.main.async {
if self.currentMediaType == .compressPhoto{
self.playImageView.isHidden = true
}else{
self.playImageView.isHidden = false
}
}
}
}
var model : ResourceModel? {
var model : AssetModel? {
didSet{
guard let model = self.model else {return}
self.backImageView.image = UIImage()
let viewModel = CompressViewModel()
viewModel.getImageFromAssetIdentifier(identifier: model.ident) {[weak self] image in
guard let self else { return}
DispatchQueue.main.async {
self.backImageView.image = image
if self.currentMediaType == .compressPhoto {
let viewModel = CompressViewModel()
viewModel.getImageFromAssetIdentifier(identifier: model.localIdentifier) {[weak self] image in
guard let self else { return}
DispatchQueue.main.async {
self.backImageView.image = image
}
}
}else {
PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: model) { image in
DispatchQueue.main.async {
self.backImageView.image = image
}
} errorHandler: {
print("获取视频图片资源失败")
}
}
// 这里不再进行计算了 直接按照50%的去计算
let saveSize = model.orgSize/2
let saveSize = model.assetSize/2
let sizeKB : Double = saveSize/1000
DispatchQueue.main.async {
if sizeKB < 1000{
......@@ -65,6 +89,15 @@ class CompressSelectCell : UICollectionViewCell {
return view
}()
lazy var playImageView: UIImageView = {
let view = UIImageView()
view.backgroundColor = .clear
view.image = UIImage(named: "btn_play_home")
view.isUserInteractionEnabled = true
view.isHidden = true
return view
}()
lazy var saveSizeView: UIView = {
let view = UIView()
......@@ -127,6 +160,7 @@ class CompressSelectCell : UICollectionViewCell {
self.addGestureRecognizer(tap)
self.addSubview(self.backImageView)
self.backImageView.addSubview(self.playImageView)
self.addSubview(self.saveSizeView)
self.saveSizeView.addSubview(self.saveSizeLabel)
self.saveSizeView.addSubview(self.moreImageView)
......@@ -137,6 +171,10 @@ class CompressSelectCell : UICollectionViewCell {
self.backImageView.snp.makeConstraints { make in
make.left.right.height.width.equalToSuperview()
}
self.playImageView.snp.makeConstraints { make in
make.center.equalToSuperview()
make.width.height.equalTo(43)
}
self.saveSizeView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.bottom.equalToSuperview().offset(-12)
......@@ -176,16 +214,79 @@ class CompressSelectCell : UICollectionViewCell {
@objc func imageClick(){
if self.currentMediaType == 0 {
// 如果是图片
let vc : PreViewController = PreViewController()
vc.imageIdent = self.model!.ident
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
if self.currentMediaType == .compressPhoto {
// // 如果是图片
// let vc : PreViewController = PreViewController()
// vc.imageIdent = self.model!.localIdentifier
// self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
// 点击之后跳转详情页面
if let tempModel = self.model {
let vc = PMShowImgVideoController()
vc.state = .similarPhotos
vc.currentIdx = 0
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = true
dataSource.id = tempModel
// 获取image
dataSource.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: tempModel.localIdentifier)
vc.homeDataSource = [dataSource]
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
}
}else{
// 如果是视频
let vc : PreVideoController = PreVideoController(localIdentifier: self.model!.ident)
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
}
// let vc : PreVideoController = PreVideoController(localIdentifier: self.model!.localIdentifier)
// self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
if let tempModel = self.model {
// 获取视频的图片
PhotoAndVideoMananger.mananger.getVideoImageByIdent(ident: tempModel) { image in
// 点击之后跳转详情页面
let vc = PMShowImgVideoController()
vc.state = .similarVideos
vc.currentIdx = 0
let dataSource = ImageSeletedCollectionItem()
dataSource.isSeleted = true
dataSource.id = tempModel
dataSource.image = image
// 表示这个是视频
vc.homeDataSource = [dataSource]
vc.getVideoURLFromLocalIdentifier(localIdentifier: tempModel.localIdentifier) { url, error in
if url != nil{
DispatchQueue.main.async {
vc.url = url
self.responderViewController()?.navigationController?.pushViewController(vc, animated: true)
}
}else{
let alert = UIAlertController(title: nil, message: "ICloud video cannot be viewed", preferredStyle: .alert)
self.responderViewController()?.present(alert, animated: true, completion: nil)
// 2 秒后关闭弹窗
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true, completion: nil)
}
}
}
} errorHandler: {
DispatchQueue.main.async {
let alert = UIAlertController(title: nil, message: "Get Video image failure", preferredStyle: .alert)
self.responderViewController()?.navigationController?.present(alert, animated: true)
// 1秒之后消失
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert.dismiss(animated: true, completion: nil)
self.responderViewController()?.navigationController?.popViewController(animated: true)
}
}
}
}
}
}
}
......@@ -11,13 +11,13 @@ import Lottie
class CompressCompletedViewController : BaseViewController{
var model : [ResourceModel]?
var model : [AssetModel]?
var comDataSource : [Data] = []
var comVideoDataSource : [URL?] = []
var currentMediaType : Int = 0
var currentMediaType : CompressType = .compressPhoto
lazy var animationView : LottieAnimationView = {
let animationView = LottieAnimationView(name: "CompressCompletedLight")
......@@ -256,7 +256,7 @@ class CompressCompletedViewController : BaseViewController{
}
@objc func completedAction(){
if currentMediaType == 0 {
if currentMediaType == .compressPhoto {
// 将压缩后的照片存到相册
for imageData in self.comDataSource {
PHPhotoLibrary.shared().performChanges({
......@@ -298,7 +298,7 @@ class CompressCompletedViewController : BaseViewController{
// 删除文件逻辑【系统自动提示是否删除】
var idents :[String] = []
for ele in self.model! {
idents.append(ele.ident)
idents.append(ele.localIdentifier)
}
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: idents, options: nil)
let fileSize = FileTool().calculateTotalAssetSize(fetchResult: fetchResult)
......@@ -310,8 +310,8 @@ class CompressCompletedViewController : BaseViewController{
var count = 0
for ele in self.model! {
count = count + 1
self.updateCompressData(flag: ele.ident)
let deleteModel = AssetModel(localIdentifier: ele.ident, assetSize: ele.orgSize, createDate: ele.createDate)
self.updateCompressData(flag: ele.localIdentifier)
let deleteModel = AssetModel(localIdentifier: ele.localIdentifier, assetSize: ele.assetSize, createDate: ele.createDate)
models.append(deleteModel)
}
if(success){
......@@ -355,9 +355,9 @@ class CompressCompletedViewController : BaseViewController{
DispatchQueue.main.async {
// 移除VC中的数据
let compressVC = self.navigationController?.viewControllers.first(where: { $0 is CompressController }) as! CompressController as CompressController
compressVC.resourceData.removeAll { $0.ident == flag }
compressVC.resourceData.removeAll { $0.localIdentifier == flag }
// 移除单利中的数据
Singleton.shared.resourceModel.removeAll{ $0.ident == flag }
Singleton.shared.resourceModel.removeAll{ $0.localIdentifier == flag }
}
}
......
......@@ -10,6 +10,12 @@ import SnapKit
import Photos
import SVProgressHUD
enum CompressType {
case compressVideo
case compressPhoto
}
class CompressController : BaseViewController {
private var compressNav:CompressNavView?
......@@ -24,10 +30,10 @@ class CompressController : BaseViewController {
// 资源类型 - 相册或者视频
var currentResourceType : Int = 0
var currentResourceType : CompressType = .compressPhoto
// 所有的数据
var resourceData = [ResourceModel](){
var resourceData = [AssetModel](){
didSet{
DispatchQueue.main.async {
self.collectionView.reloadData()
......@@ -36,7 +42,7 @@ class CompressController : BaseViewController {
}
// 当前页面选中的
var selectedModel : [ResourceModel] = []
var selectedModel : [AssetModel] = []
lazy var collectionView:UICollectionView = {
......@@ -117,9 +123,9 @@ class CompressController : BaseViewController {
if datas.count > 0 {
// 这里需要重新排序下
if self.currentSort == 0 {
self.resourceData = datas.sorted {$0.orgSize > $1.orgSize }
self.resourceData = datas.sorted {$0.assetSize > $1.assetSize }
}else if self.currentSort == 1 {
self.resourceData = datas.sorted {$0.orgSize < $1.orgSize }
self.resourceData = datas.sorted {$0.assetSize < $1.assetSize }
}else if self.currentSort == 2 {
self.resourceData = datas.sorted {$0.createDate > $1.createDate }
}else{
......@@ -166,13 +172,12 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CompressSelectCell", for: indexPath) as! CompressSelectCell
cell.model = self.resourceData[indexPath.row]
cell.currentMediaType = self.currentResourceType
cell.model = self.resourceData[indexPath.row]
if self.selectedModel.count == 0 {
cell.choose = false
}
cell.callBack = {[weak self] (model:ResourceModel,choose:Bool) ->() in
cell.callBack = {[weak self] (model:AssetModel,choose:Bool) ->() in
guard let self else{ return}
// 这里临时管理一个当前选择的资源
......@@ -247,15 +252,15 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
}
header.changeView.callBack = {[weak self] flag in
guard let self else {return}
if self.currentResourceType != flag as! Int {
self.currentResourceType = flag as! Int
if self.currentResourceType != flag {
self.currentResourceType = flag
// 先移除下,防止点到
self.resourceData.removeAll()
// 如果是图片,直接从缓存中加载
if self.currentResourceType == 0 {
if self.currentResourceType == .compressPhoto {
self.getViewData()
}else{
CompressViewModel().getAllPhotosToAssets(sortType: self.currentSort, assetType: flag as! Int) { [weak self] models in
CompressViewModel().getAllPhotosToAssets(sortType: self.currentSort, assetType: flag) { [weak self] models in
guard let self else {return}
self.resourceData = models
}
......@@ -308,7 +313,7 @@ extension CompressController:WaterfallMutiSectionDelegate,UICollectionViewDataSo
func updateSubmitButton(){
if self.selectedModel.count > 0 {
self.submitButton.backgroundColor = UIColor(red: 0, green: 0.51, blue: 1, alpha: 1)
if self.currentResourceType == 0 {
if self.currentResourceType == .compressPhoto {
self.submitButton.setTitle("Compress \(self.selectedModel.count) photos", for: UIControl.State.normal)
}else{
self.submitButton.setTitle("Compress \(self.selectedModel.count) videos", for: UIControl.State.normal)
......
......@@ -9,9 +9,9 @@ import Foundation
class CompressQualityController : BaseViewController{
var model : [ResourceModel]? {
var model : [AssetModel]? {
didSet{
let ident = model!.first!.ident
let ident = model!.first!.localIdentifier
let image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: ident)
self.imageView.image = image
}
......@@ -20,7 +20,7 @@ class CompressQualityController : BaseViewController{
var currentQulityType : Int = 0
var currentMediaType : Int = 0
var currentMediaType : CompressType = .compressPhoto
private var compressNav:CompressNavView?
......@@ -196,7 +196,7 @@ class CompressQualityController : BaseViewController{
var sum = 0.0
var orgAllSize = 0.0
for modelData in self.model! {
orgAllSize = orgAllSize + modelData.orgSize
orgAllSize = orgAllSize + modelData.assetSize
}
sum = orgAllSize - compressAllSize
sum = sum / 1000
......@@ -236,7 +236,7 @@ class CompressQualityController : BaseViewController{
}
var comDataSource : [Data] = []
if self.currentMediaType == 0 {
if self.currentMediaType == .compressPhoto {
// 表示压缩图片
manager.compress(assets: self.model!, compressionQuality: currentQulity) {progress in
compressingView.animationView.setProgress(CGFloat(progress), animated: false, duration: 0.1)
......
//
// ResourceModel.swift
// PhoneManager
//
// Created by edy on 2025/4/2.
//
import Foundation
import Photos
struct ResourceModel : Equatable{
var ident : String
var orgSize: Double
var createDate : Date
init(ident: String, orgSize: Double, createDate: Date) {
self.ident = ident
self.orgSize = orgSize
self.createDate = createDate
}
}
......@@ -11,15 +11,15 @@ class CompressCustomHeaderView: UICollectionReusableView{
var callBack: callBack<Any> = {text in }
var modeData = [ResourceModel]() {
var modeData = [AssetModel]() {
didSet{
// 计算图片总大小
var sum = 0.0
// 计算可节省大小
var saveSum = 0.0
for model in self.modeData{
sum = sum + model.orgSize
saveSum = saveSum + model.orgSize * 0.8
sum = sum + model.assetSize
saveSum = saveSum + model.assetSize * 0.8
}
sum = sum / 1000
saveSum = saveSum / 1000
......
......@@ -9,7 +9,7 @@ import Foundation
class CompressSwitchView : UIView {
var callBack: callBack<Any> = {flag in }
var callBack: callBack<CompressType> = {flag in }
var currentButton : UIButton?
......@@ -40,32 +40,23 @@ class CompressSwitchView : UIView {
self.backgroundColor = .clear
self.addSubview(self.leftButton)
// // 暂时屏蔽
// self.addSubview(self.rightButton)
// self.leftButton.snp.makeConstraints { make in
// make.left.equalToSuperview().offset(4)
// make.top.equalToSuperview().offset(4)
// make.bottom.equalToSuperview().offset(-4)
// make.width.equalTo(self.snp.width).multipliedBy(0.5).offset(-6)
// }
//
// self.rightButton.snp.makeConstraints { make in
// make.right.equalToSuperview().offset(-4)
// make.top.equalToSuperview().offset(4)
// make.bottom.equalToSuperview().offset(-4)
// make.left.equalTo(self.leftButton.snp.right).offset(4)
// }
self.addSubview(self.rightButton)
self.leftButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(4)
make.top.equalToSuperview().offset(4)
make.bottom.equalToSuperview().offset(-4)
make.width.equalTo(self.snp.width).multipliedBy(0.5).offset(-6)
}
self.rightButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-4)
make.top.equalToSuperview().offset(4)
make.bottom.equalToSuperview().offset(-4)
make.left.equalTo(self.leftButton.snp.right).offset(4)
}
// 初始化的时候设置默认值
self.currentButton = self.leftButton
self.currentButton?.setTitleColor(UIColor(red: 0, green: 0.51, blue: 1, alpha: 1), for: .normal)
......@@ -88,8 +79,12 @@ class CompressSwitchView : UIView {
sender.setTitleColor(UIColor(red: 0, green: 0.51, blue: 1, alpha: 1), for: .normal)
sender.backgroundColor = .white
self.currentButton = sender
if (sender.tag - 1000) == 0 {
self.callBack(.compressPhoto)
}else{
self.callBack(.compressVideo)
}
self.callBack(sender.tag - 1000)
}
}
......@@ -10,7 +10,7 @@ import Foundation
class CompressingView: UIView{
var data: [ResourceModel]?
var data: [AssetModel]?
override init(frame: CGRect) {
super.init(frame: frame)
......
......@@ -9,7 +9,7 @@ import Foundation
import Photos
typealias Finished = ([ResourceModel])->Void
typealias Finished = ([AssetModel])->Void
struct PhotoInfo {
var localIdentifier : String
......@@ -67,15 +67,15 @@ class CompressViewModel{
/// 获取相册所有图片
/// - Returns: 图片信息
func getAllPhotosToAssets(sortType: Int, assetType : Int,_ finished: @escaping Finished){
func getAllPhotosToAssets(sortType: Int, assetType : CompressType,_ finished: @escaping Finished){
var models : [ResourceModel] = []
var models : [AssetModel] = []
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
var photosAssets : PHFetchResult<PHAsset>
if assetType == 0 {
if assetType == .compressPhoto {
photosAssets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
}else{
photosAssets = PHAsset.fetchAssets(with: .video, options: fetchOptions)
......@@ -102,16 +102,16 @@ class CompressViewModel{
// 获取图片的localIdentifier
let localIdentifier = asset.localIdentifier
let model = ResourceModel.init(ident: localIdentifier, orgSize: Double(sizeInMB), createDate: creationDate)
let model = AssetModel.init(localIdentifier: localIdentifier, assetSize: Double(sizeInMB), createDate: creationDate)
models.append(model)
count = count + 1
group.leave()
if count == assetsArray.count {
// 默认按照文件大小排序
if sortType == 0 {
finished(models.sorted { $0.orgSize > $1.orgSize })
finished(models.sorted { $0.assetSize > $1.assetSize })
}else if sortType == 1 {
finished(models.sorted { $0.orgSize < $1.orgSize })
finished(models.sorted { $0.assetSize < $1.assetSize })
}else if sortType == 2 {
finished(models.sorted { $0.createDate > $1.createDate })
}else{
......@@ -129,12 +129,12 @@ class CompressViewModel{
/// - sortType: 排序的类型,升序或者降序。0-升序,1-降序
/// - sortKind: 排序的种类 大小或者时间 0-大小,1-时间
/// - Returns: 返回排序结果
func sortRsource(resource:[ResourceModel],sortType:Int,sortKind:Int)->[ResourceModel]{
func sortRsource(resource:[AssetModel],sortType:Int,sortKind:Int)->[AssetModel]{
if sortKind == 0 {
if sortType == 0 {
return resource.sorted { $0.orgSize < $1.orgSize }
return resource.sorted { $0.assetSize < $1.assetSize }
}else{
return resource.sorted { $0.orgSize > $1.orgSize }
return resource.sorted { $0.assetSize > $1.assetSize }
}
}else{
if sortType == 0 {
......@@ -153,7 +153,7 @@ class CompressViewModel{
/// - quality: 要锁质量
/// - progress: 进度回调
/// - completion: 完成回调
func compressVideos(models: [ResourceModel], quality: Float,
func compressVideos(models: [AssetModel], quality: Float,
progress: @escaping (String, Float) -> Void,
completion: @escaping ([URL?], [Error?]) -> Void) {
var outputURLs: [URL?] = Array(repeating: nil, count: models.count)
......@@ -168,7 +168,7 @@ class CompressViewModel{
let model = models[index]
let fetchOptions = PHFetchOptions()
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [model.ident], options: fetchOptions)
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [model.localIdentifier], options: fetchOptions)
guard let asset = assets.firstObject else {
errors[index] = NSError(domain: "VideoCompressor", code: 1, userInfo: [NSLocalizedDescriptionKey: "Asset not found"])
completedCount += 1
......@@ -275,7 +275,7 @@ class CompressViewModel{
exportSession.videoComposition = videoComposition
let progressTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
progress(model.ident, exportSession.progress)
progress(model.localIdentifier, exportSession.progress)
}
exportSession.exportAsynchronously {
......@@ -312,7 +312,7 @@ class CompressViewModel{
/// - compressionQuality: 图片质量
/// - progressHandler: 进度
/// - completion: 完成
func compress(assets: [ResourceModel], compressionQuality : CGFloat, progressHandler: @escaping (Float) -> Void, completion: @escaping ([Data?], [Error?]) -> Void) {
func compress(assets: [AssetModel], compressionQuality : CGFloat, progressHandler: @escaping (Float) -> Void, completion: @escaping ([Data?], [Error?]) -> Void) {
var compressedDataArray: [Data?] = Array(repeating: nil, count: assets.count)
var errorArray: [Error?] = Array(repeating: nil, count: assets.count)
......@@ -320,7 +320,7 @@ class CompressViewModel{
let totalCount = assets.count
for (index, model) in assets.enumerated() {
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [model.ident], options: nil)
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [model.localIdentifier], options: nil)
compressSingleAsset(fetchResult.firstObject!,compressionQuality) { (progress) in
let singleProgress = Float(completedCount) / Float(totalCount) + progress / Float(totalCount)
progressHandler(singleProgress)
......
......@@ -45,7 +45,7 @@ class HomePhotosDetailViewController : BaseViewController {
var model : HomePhotosModel
var datas : [ResourceModel] = []
var datas : [AssetModel] = []
// 排序
var currentSort : Int = 0 {
......
......@@ -113,7 +113,7 @@ class PhotoDataManager {
})
// 将单利中的数据也删除
for item in data {
Singleton.shared.resourceModel.removeAll{ $0.ident == item.localIdentifier }
Singleton.shared.resourceModel.removeAll{ $0.localIdentifier == item.localIdentifier }
}
}
......
......@@ -899,7 +899,7 @@ class PhotoAndVideoMananger {
// 获取当前版本的照片或视频
options.version = .current
// 尽可能快地提供结果
options.deliveryMode = .fastFormat
options.deliveryMode = .highQualityFormat
// 允许从iCloud请求
options.isNetworkAccessAllowed = true
// 异步请求
......
......@@ -14,7 +14,7 @@ class Singleton {
// 私有化初始化方法,防止外部创建新实例
private init() {}
var resourceModel : [ResourceModel] = []
var resourceModel : [AssetModel] = []
var maxDeleteCount : Int = 25
......
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