wake-ios/wake/View/Components/Upload/VideoPicker.swift
2025-08-21 19:39:30 +08:00

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?()
}
}
}
}
}
}