2024-07-13 01:29:46 +00:00
|
|
|
import Foundation
|
2024-07-14 13:42:51 +00:00
|
|
|
import Photos
|
2024-07-13 01:29:46 +00:00
|
|
|
import UIKit
|
|
|
|
|
|
|
|
final class FileProcessing {
|
|
|
|
static let shared = FileProcessing()
|
|
|
|
|
|
|
|
static var fileFolder: URL {
|
|
|
|
// swiftlint:disable:next force_unwrapping
|
|
|
|
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
2024-07-14 08:52:15 +00:00
|
|
|
let subdirectoryURL = documentsURL.appendingPathComponent(Const.fileFolder)
|
|
|
|
if !FileManager.default.fileExists(atPath: subdirectoryURL.path) {
|
|
|
|
try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
|
|
}
|
|
|
|
return subdirectoryURL
|
2024-07-13 01:29:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-14 10:08:51 +00:00
|
|
|
func createThumbnail(localName: String) -> String? {
|
|
|
|
let thumbnailFileName = "thumb_\(localName)"
|
2024-07-14 08:52:15 +00:00
|
|
|
let thumbnailUrl = FileProcessing.fileFolder.appendingPathComponent(thumbnailFileName)
|
2024-07-14 10:08:51 +00:00
|
|
|
let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
2024-07-13 01:29:46 +00:00
|
|
|
|
|
|
|
// check if thumbnail already exists
|
|
|
|
if FileManager.default.fileExists(atPath: thumbnailUrl.path) {
|
2024-07-14 10:08:51 +00:00
|
|
|
return thumbnailFileName
|
2024-07-13 01:29:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// create thumbnail if not exists
|
2024-07-14 10:08:51 +00:00
|
|
|
switch localName.attachmentType {
|
2024-07-13 01:29:46 +00:00
|
|
|
case .image:
|
|
|
|
guard let image = UIImage(contentsOfFile: localUrl.path) else { return nil }
|
|
|
|
let targetSize = CGSize(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
|
|
|
guard let thumbnail = scaleAndCropImage(image, targetSize) else { return nil }
|
|
|
|
guard let data = thumbnail.pngData() else { return nil }
|
|
|
|
do {
|
|
|
|
try data.write(to: thumbnailUrl)
|
2024-07-14 10:08:51 +00:00
|
|
|
return thumbnailFileName
|
2024-07-13 01:29:46 +00:00
|
|
|
} catch {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2024-07-14 13:42:51 +00:00
|
|
|
|
|
|
|
func fetchGallery() -> [SharingGalleryItem] {
|
|
|
|
let fetchOptions = PHFetchOptions()
|
|
|
|
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
|
|
|
let assets = PHAsset.fetchAssets(with: fetchOptions)
|
|
|
|
var items: [SharingGalleryItem] = []
|
|
|
|
assets.enumerateObjects { asset, _, _ in
|
|
|
|
if asset.mediaType == .image {
|
|
|
|
items.append(.init(id: asset.localIdentifier, type: .photo))
|
|
|
|
} else if asset.mediaType == .video {
|
|
|
|
items.append(.init(id: asset.localIdentifier, type: .video))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return items
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillGalleryItemsThumbnails(items: [SharingGalleryItem]) -> [SharingGalleryItem] {
|
|
|
|
var result: [SharingGalleryItem] = []
|
|
|
|
let ids = items
|
|
|
|
.filter { $0.thumbnail == nil }
|
|
|
|
.map { $0.id }
|
|
|
|
|
|
|
|
let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
|
|
|
|
assets.enumerateObjects { asset, _, _ in
|
|
|
|
if asset.mediaType == .image {
|
|
|
|
PHImageManager.default().requestImage(
|
|
|
|
for: asset,
|
|
|
|
targetSize: PHImageManagerMaximumSize,
|
|
|
|
contentMode: .aspectFill,
|
|
|
|
options: nil
|
|
|
|
) { image, _ in
|
|
|
|
image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
|
|
|
if let image {
|
|
|
|
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
|
|
|
result.append(.init(id: asset.localIdentifier, type: .photo, thumbnail: data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if asset.mediaType == .video {
|
|
|
|
PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
|
|
|
|
if let avAsset {
|
|
|
|
let imageGenerator = AVAssetImageGenerator(asset: avAsset)
|
|
|
|
imageGenerator.appliesPreferredTrackTransform = true
|
|
|
|
let time = CMTimeMake(value: 1, timescale: 2)
|
|
|
|
do {
|
|
|
|
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
|
|
|
|
let thumbnail = UIImage(cgImage: imageRef)
|
|
|
|
thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
|
|
|
if let image {
|
|
|
|
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
|
|
|
result.append(.init(id: asset.localIdentifier, type: .video, thumbnail: data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
print("Failed to create thumbnail image")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
2024-07-13 01:29:46 +00:00
|
|
|
}
|
|
|
|
|
2024-07-14 13:42:51 +00:00
|
|
|
private extension FileProcessing {
|
|
|
|
func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? {
|
|
|
|
let aspect = img.size.width / img.size.height
|
|
|
|
let targetAspect = size.width / size.height
|
|
|
|
var newWidth: CGFloat
|
|
|
|
var newHeight: CGFloat
|
|
|
|
if aspect < targetAspect {
|
|
|
|
newWidth = size.width
|
|
|
|
newHeight = size.width / aspect
|
|
|
|
} else {
|
|
|
|
newHeight = size.height
|
|
|
|
newWidth = size.height * aspect
|
|
|
|
}
|
|
|
|
|
|
|
|
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
|
|
|
img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight))
|
|
|
|
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
|
|
|
UIGraphicsEndImageContext()
|
|
|
|
|
|
|
|
return newImage
|
2024-07-13 16:37:26 +00:00
|
|
|
}
|
2024-07-13 01:29:46 +00:00
|
|
|
|
2024-07-14 13:42:51 +00:00
|
|
|
func syncEnumrate(_ ids: [String]? = nil) -> [PHAsset] {
|
|
|
|
var result: [PHAsset] = []
|
2024-07-13 01:29:46 +00:00
|
|
|
|
2024-07-14 13:42:51 +00:00
|
|
|
let fetchOptions = PHFetchOptions()
|
|
|
|
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
|
|
|
if let ids {
|
|
|
|
fetchOptions.predicate = NSPredicate(format: "localIdentifier IN %@", ids)
|
|
|
|
}
|
|
|
|
let assets = PHAsset.fetchAssets(with: fetchOptions)
|
|
|
|
assets.enumerateObjects { asset, _, _ in
|
|
|
|
result.append(asset)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
2024-07-13 01:29:46 +00:00
|
|
|
}
|