Generate video thumbnails
This commit is contained in:
@ -2,6 +2,7 @@ import Foundation
|
||||
import AppKit
|
||||
import SDWebImageAVIFCoder
|
||||
import SDWebImageWebPCoder
|
||||
import AVFoundation
|
||||
|
||||
final class ImageGenerator {
|
||||
|
||||
@ -162,10 +163,9 @@ final class ImageGenerator {
|
||||
}
|
||||
|
||||
private func createAvif(image: NSBitmapImageRep, quality: CGFloat) -> Data? {
|
||||
return Data()
|
||||
// let newImage = NSImage(size: image.size)
|
||||
// newImage.addRepresentation(image)
|
||||
// return SDImageAVIFCoder.shared.encodedData(with: newImage, format: .AVIF, options: [.encodeCompressionQuality: quality])
|
||||
let newImage = NSImage(size: image.size)
|
||||
newImage.addRepresentation(image)
|
||||
return SDImageAVIFCoder.shared.encodedData(with: newImage, format: .AVIF, options: [.encodeCompressionQuality: quality])
|
||||
}
|
||||
|
||||
private func createWebp(image: NSBitmapImageRep, quality: CGFloat) -> Data? {
|
||||
@ -173,4 +173,63 @@ final class ImageGenerator {
|
||||
newImage.addRepresentation(image)
|
||||
return SDImageWebPCoder.shared.encodedData(with: newImage, format: .webP, options: [.encodeCompressionQuality: quality])
|
||||
}
|
||||
|
||||
// MARK: Video thumbnails
|
||||
|
||||
@discardableResult
|
||||
func createVideoThumbnail(for videoId: String) async -> Bool {
|
||||
guard let image = await storage.with(file: videoId, perform: generateThumbnail) else {
|
||||
print("Failed to generate thumbnail image for video \(videoId)")
|
||||
return false
|
||||
}
|
||||
let scaled = create(image: image, width: image.size.width, height: image.size.height)
|
||||
guard let data = scaled.representation(using: .jpeg, properties: [.compressionFactor: NSNumber(value: 0.6)]) else {
|
||||
print("Failed to get thumbnail jpg data of video \(videoId)")
|
||||
return false
|
||||
}
|
||||
if !storage.save(thumbnail: data, for: videoId) {
|
||||
print("Failed to save thumbnail of video \(videoId)")
|
||||
}
|
||||
print("Generated video thumbnail for \(videoId)")
|
||||
return true
|
||||
}
|
||||
|
||||
private func generateThumbnail(for url: URL) async -> NSImage? {
|
||||
let time = CMTime(seconds: 1, preferredTimescale: 600)
|
||||
let asset = AVURLAsset(url: url)
|
||||
let imageGenerator = AVAssetImageGenerator(asset: asset)
|
||||
imageGenerator.appliesPreferredTrackTransform = true // Correct for orientation
|
||||
return await withCheckedContinuation { continuation in
|
||||
imageGenerator.generateCGImageAsynchronously(for: time) { cgImage, _, error in
|
||||
if let error {
|
||||
print("Error generating thumbnail for \(url.path()): \(error.localizedDescription)")
|
||||
}
|
||||
if let cgImage {
|
||||
let image = NSImage(cgImage: cgImage, size: NSSize(width: cgImage.width, height: cgImage.height))
|
||||
continuation.resume(returning: image)
|
||||
} else {
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getVideoDuration(for videoId: String) async -> TimeInterval? {
|
||||
guard let duration = await storage.with(file: videoId, perform: getVideoDuration) else {
|
||||
print("Failed to determine duration for video \(videoId)")
|
||||
return nil
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
private func getVideoDuration(url: URL) async -> TimeInterval? {
|
||||
let asset = AVURLAsset(url: url)
|
||||
do {
|
||||
let duration = try await asset.load(.duration)
|
||||
return CMTimeGetSeconds(duration)
|
||||
} catch {
|
||||
print("ImageGenerator: Failed to determine video duration: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user