mastodon-ios/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel+Load.swift

149 lines
5.3 KiB
Swift

//
// AttachmentViewModel+Load.swift
//
//
// Created by MainasuK on 2022/11/8.
//
import os.log
import UIKit
import AVKit
import UniformTypeIdentifiers
extension AttachmentViewModel {
@MainActor
func load(input: Input) async throws -> Output {
switch input {
case .image(let image):
guard let data = image.pngData() else {
throw AttachmentError.invalidAttachmentType
}
return .image(data, imageKind: .png)
case .url(let url):
do {
let output = try await AttachmentViewModel.load(url: url)
return output
} catch {
throw error
}
case .pickerResult(let pickerResult):
do {
let output = try await AttachmentViewModel.load(itemProvider: pickerResult.itemProvider)
return output
} catch {
throw error
}
case .itemProvider(let itemProvider):
do {
let output = try await AttachmentViewModel.load(itemProvider: itemProvider)
return output
} catch {
throw error
}
}
}
private static func load(url: URL) async throws -> Output {
guard let uti = UTType(filenameExtension: url.pathExtension) else {
throw AttachmentError.invalidAttachmentType
}
if uti.conforms(to: .image) {
guard url.startAccessingSecurityScopedResource() else {
throw AttachmentError.invalidAttachmentType
}
defer { url.stopAccessingSecurityScopedResource() }
let imageData = try Data(contentsOf: url)
return .image(imageData, imageKind: imageData.kf.imageFormat == .PNG ? .png : .jpg)
} else if uti.conforms(to: .movie) {
guard url.startAccessingSecurityScopedResource() else {
throw AttachmentError.invalidAttachmentType
}
defer { url.stopAccessingSecurityScopedResource() }
let fileName = UUID().uuidString
let tempDirectoryURL = FileManager.default.temporaryDirectory
let fileURL = tempDirectoryURL.appendingPathComponent(fileName).appendingPathExtension(url.pathExtension)
try FileManager.default.createDirectory(at: tempDirectoryURL, withIntermediateDirectories: true, attributes: nil)
try FileManager.default.copyItem(at: url, to: fileURL)
return .video(fileURL, mimeType: UTType.movie.preferredMIMEType ?? "video/mp4")
} else {
throw AttachmentError.invalidAttachmentType
}
}
private static func load(itemProvider: NSItemProvider) async throws -> Output {
if itemProvider.isImage() {
guard let result = try await itemProvider.loadImageData() else {
throw AttachmentError.invalidAttachmentType
}
let imageKind: Output.ImageKind = {
if let type = result.type {
if type == UTType.png {
return .png
}
if type == UTType.jpeg {
return .jpg
}
}
let imageData = result.data
if imageData.kf.imageFormat == .PNG {
return .png
}
if imageData.kf.imageFormat == .JPEG {
return .jpg
}
assertionFailure("unknown image kind")
return .jpg
}()
return .image(result.data, imageKind: imageKind)
} else if itemProvider.isMovie() {
guard let result = try await itemProvider.loadVideoData() else {
throw AttachmentError.invalidAttachmentType
}
return .video(result.url, mimeType: "video/mp4")
} else {
assertionFailure()
throw AttachmentError.invalidAttachmentType
}
}
}
extension AttachmentViewModel {
static func createThumbnailForVideo(url: URL) -> UIImage? {
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
let asset = AVURLAsset(url: url)
let assetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImageGenerator.appliesPreferredTrackTransform = true // fix orientation
do {
let cgImage = try assetImageGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
let image = UIImage(cgImage: cgImage)
return image
} catch {
AttachmentViewModel.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): thumbnail generate fail: \(error.localizedDescription)")
return nil
}
}
}
extension NSItemProvider {
func isImage() -> Bool {
return hasRepresentationConforming(
toTypeIdentifier: UTType.image.identifier,
fileOptions: []
)
}
func isMovie() -> Bool {
return hasRepresentationConforming(
toTypeIdentifier: UTType.movie.identifier,
fileOptions: []
)
}
}