IceCubesApp/Packages/StatusKit/Sources/StatusKit/Editor/Components/Compressor.swift
Thomas Ricouard 1f858414d8 format .
2024-02-14 12:48:14 +01:00

110 lines
3.6 KiB
Swift

import AVFoundation
import Foundation
import UIKit
public extension StatusEditor {
actor Compressor {
public init() {}
enum CompressorError: Error {
case noData
}
public func compressImageFrom(url: URL) async -> Data? {
await withCheckedContinuation { continuation in
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {
continuation.resume(returning: nil)
return
}
let maxPixelSize: Int = if Bundle.main.bundlePath.hasSuffix(".appex") {
1536
} else {
4096
}
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
] as [CFString: Any] as CFDictionary
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else {
continuation.resume(returning: nil)
return
}
let data = NSMutableData()
guard let imageDestination = CGImageDestinationCreateWithData(data, UTType.jpeg.identifier as CFString, 1, nil) else {
continuation.resume(returning: nil)
return
}
let isPNG: Bool = {
guard let utType = cgImage.utType else { return false }
return (utType as String) == UTType.png.identifier
}()
let destinationProperties = [
kCGImageDestinationLossyCompressionQuality: isPNG ? 1.0 : 0.75,
] as CFDictionary
CGImageDestinationAddImage(imageDestination, cgImage, destinationProperties)
CGImageDestinationFinalize(imageDestination)
continuation.resume(returning: data as Data)
}
}
public func compressImageForUpload(_ image: UIImage) async throws -> Data {
var image = image
if image.size.height > 5000 || image.size.width > 5000 {
image = image.resized(to: .init(width: image.size.width / 4,
height: image.size.height / 4))
}
guard var imageData = image.jpegData(compressionQuality: 0.8) else {
throw CompressorError.noData
}
let maxSize = 10 * 1024 * 1024
if imageData.count > maxSize {
while imageData.count > maxSize {
guard let compressedImage = UIImage(data: imageData),
let compressedData = compressedImage.jpegData(compressionQuality: 0.8)
else {
throw CompressorError.noData
}
imageData = compressedData
}
}
return imageData
}
func compressVideo(_ url: URL) async -> URL? {
await withCheckedContinuation { continuation in
let urlAsset = AVURLAsset(url: url, options: nil)
let presetName: String = if Bundle.main.bundlePath.hasSuffix(".appex") {
AVAssetExportPreset1280x720
} else {
AVAssetExportPreset1920x1080
}
guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: presetName) else {
continuation.resume(returning: nil)
return
}
let outputURL = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(url.pathExtension)")
exportSession.outputURL = outputURL
exportSession.outputFileType = .mp4
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously { () in
continuation.resume(returning: outputURL)
}
}
}
}
}