// // VideoHandler.swift // Pocket Poster // // Created by lemin on 6/8/25. // import Foundation import AVFoundation import UIKit extension CGImage { var png: Data? { guard let mutableData = CFDataCreateMutable(nil, 0), let destination = CGImageDestinationCreateWithData(mutableData, "public.png" as CFString, 1, nil) else { return nil } CGImageDestinationAddImage(destination, self, nil) guard CGImageDestinationFinalize(destination) else { return nil } return mutableData as Data } var jpg: Data? { let uiimg = UIImage(cgImage: self) return uiimg.jpegData(compressionQuality: 0.7) } } class VideoHandler { static func createCaml(from url: URL, autoReverses: Bool) throws -> URL { // copy the resources let descrURL = SymHandler.getDocumentsDirectory().appendingPathComponent(UUID().uuidString, conformingTo: .directory) try FileManager.default.createDirectory(at: descrURL, withIntermediateDirectories: false) print("pre resource") guard let rc = Bundle.main.url(forResource: "VideoCAML", withExtension: nil) else { throw URLError(.fileDoesNotExist) } print("post resource") try FileManager.default.copyItem(at: rc, to: descrURL.appendingPathComponent("videoCAML")) print("post copy") let coreAnimDir = descrURL.appending(path: "videoCAML/versions/1/contents/9183.Custom-810w-1080h@2x~ipad.wallpaper/9183.Custom_Background-810w-1080h@2x~ipad.ca") let assetsFolder = coreAnimDir.appendingPathComponent("assets", conformingTo: .directory) if !FileManager.default.fileExists(atPath: assetsFolder.path()) { try FileManager.default.createDirectory(at: assetsFolder, withIntermediateDirectories: true) } // load the video let asset = AVURLAsset(url: url) let assetImageGenerator = AVAssetImageGenerator(asset: asset) assetImageGenerator.appliesPreferredTrackTransform = true assetImageGenerator.apertureMode = AVAssetImageGenerator.ApertureMode.encodedPixels // create the caml guard let track = asset.tracks(withMediaType: .video).first else { print("Failed to get video track") throw AVError(.decodeFailed) } let size = track.naturalSize.applying(track.preferredTransform) let width = Int(size.width) let height = Int(size.height) let fps = track.nominalFrameRate let duration = CMTimeGetSeconds(asset.duration) let totalFrames = Int(fps * Float(duration)) let animDur = Double(totalFrames) / Double(fps) var camlData = """ """ // write the caml guard let caData = camlData.data(using: .utf8) else { throw URLError(.unknown) } try caData.write(to: coreAnimDir.appendingPathComponent("main.caml")) // write the other info xml camlData = """ assetManifest assetManifest.caml documentHeight \(height) documentResizesToView documentWidth \(width) dynamicGuidesEnabled geometryFlipped guidesEnabled interactiveMouseEventsEnabled interactiveShowsCursor interactiveTouchEventsEnabled loopEnd 0.0 loopStart 0.0 loopingEnabled multitouchDisablesMouse multitouchEnabled presentationMouseEventsEnabled presentationShowsCursor presentationTouchEventsEnabled rootDocument main.caml savesWindowFrame scalesToFitInPlayer showsTouches snappingEnabled timelineMarkers [(null)] touchesColor 1 1 0 0.8 unitsInPixelsInPlayer """ guard let xmlData = camlData.data(using: .utf8) else { throw URLError(.unknown) } try xmlData.write(to: coreAnimDir.appendingPathComponent("index.xml", conformingTo: .xml)) return descrURL } }