89 lines
3.7 KiB
Swift
89 lines
3.7 KiB
Swift
import SwiftUI
|
|
import PhotosUI
|
|
import AVFoundation
|
|
import os.log
|
|
|
|
struct VideoPicker: UIViewControllerRepresentable {
|
|
@Binding var selectedVideoURL: URL?
|
|
@Binding var thumbnailImage: UIImage?
|
|
var onDismiss: (() -> Void)?
|
|
|
|
func makeUIViewController(context: Context) -> PHPickerViewController {
|
|
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
|
|
configuration.filter = .videos
|
|
configuration.selectionLimit = 1
|
|
configuration.preferredAssetRepresentationMode = .current
|
|
|
|
let picker = PHPickerViewController(configuration: configuration)
|
|
picker.delegate = context.coordinator
|
|
return picker
|
|
}
|
|
|
|
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {}
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator(self)
|
|
}
|
|
|
|
class Coordinator: NSObject, PHPickerViewControllerDelegate {
|
|
let parent: VideoPicker
|
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.example.app", category: "VideoPicker")
|
|
|
|
init(_ parent: VideoPicker) {
|
|
self.parent = parent
|
|
}
|
|
|
|
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
|
guard let result = results.first else {
|
|
parent.onDismiss?()
|
|
return
|
|
}
|
|
|
|
// 获取视频URL
|
|
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] url, error in
|
|
guard let self = self, let videoURL = url, error == nil else {
|
|
self?.logger.error("Failed to load video: \(error?.localizedDescription ?? "Unknown error")")
|
|
DispatchQueue.main.async {
|
|
self?.parent.onDismiss?()
|
|
}
|
|
return
|
|
}
|
|
|
|
// 创建临时文件URL
|
|
let tempDirectory = FileManager.default.temporaryDirectory
|
|
let targetURL = tempDirectory.appendingPathComponent("\(UUID().uuidString).\(videoURL.pathExtension)")
|
|
|
|
do {
|
|
// 将视频复制到临时目录
|
|
if FileManager.default.fileExists(atPath: targetURL.path) {
|
|
try FileManager.default.removeItem(at: targetURL)
|
|
}
|
|
try FileManager.default.copyItem(at: videoURL, to: targetURL)
|
|
|
|
// 提取视频首帧
|
|
MediaUtils.extractFirstFrame(from: targetURL) { [weak self] result in
|
|
switch result {
|
|
case .success(let image):
|
|
DispatchQueue.main.async {
|
|
self?.parent.thumbnailImage = image
|
|
self?.parent.selectedVideoURL = targetURL
|
|
self?.parent.onDismiss?()
|
|
}
|
|
case .failure(let error):
|
|
self?.logger.error("Failed to extract video thumbnail: \(error.localizedDescription)")
|
|
DispatchQueue.main.async {
|
|
self?.parent.onDismiss?()
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
self.logger.error("Failed to copy video file: \(error.localizedDescription)")
|
|
DispatchQueue.main.async {
|
|
self.parent.onDismiss?()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|