I've created a class to select a video from gallery, file manager, and camera. I want to remove the GPS location info of the video and save the new video to gallery. Currently, I'm facing below error.
Cannot Save Error Domain=AVFoundationErrorDomain Code=-11823 "Cannot Save" UserInfo={NSLocalizedRecoverySuggestion=The requested file name is already in use. Try a different file name or location., NSLocalizedDescription=Cannot Save, NSUnderlyingError=0x600000d29860 {Error Domain=NSOSStatusErrorDomain Code=-12101 "(null)"}}
First I'm trying to remove metadata and then saving the modified video to gallery. Below are the plist keys:
<key>NSPhotoLibraryUsageDescription</key>
<key>NSPhotoLibraryAddUsageDescription</key>
To test function, just call.
VideoHandler1.shared.removeMetaData(filePath: videoPath)
Class:
public class VideoHandler1: NSObject {
public static let shared = VideoHandler1()
var inputPath = ""
var outputPath = ""
public func removeMetaData(filePath: String) {
inputPath = filePath
guard let newUrl = URL(string: filePath) else { return }
// Remove metadata and GPS info
removeMetadataAndGPSInfo(from: newUrl) { [weak self] modifiedVideo in
if let modifiedVideoURL = URL(string: modifiedVideo) {
// Save the modified video to the photo library
self?.saveVideoToPhotoLibrary(videoURL: modifiedVideoURL)
}
}
}
}
extension VideoHandler1 {
func removeMetadataAndGPSInfo(from videoURL: URL, handler: StringHandler?) {
let asset = AVAsset(url: videoURL)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)
let outputURL = saveVideoToDocumentDirectory(videoURL: videoURL)
exportSession?.outputURL = saveVideoToDocumentDirectory(videoURL: videoURL)
// exportSession?.outputFileType = AVFileType(rawValue: videoURL.pathExtension)
exportSession?.outputFileType = .mp4
// Remove metadata tracks that contain GPS information
exportSession?.metadata = asset.metadata.filter { $0.key as! AVMetadataKey != AVMetadataKey.commonKeyLocation }
exportSession?.exportAsynchronously(completionHandler: {
if exportSession?.status == .completed {
print("Metadata and GPS info removed successfully.")
handler?(outputURL?.absoluteString ?? "")
} else if let error = exportSession?.error {
print(error)
handler?("")
}
})
}
func saveVideoToPhotoLibrary(videoURL: URL) {
if #available(iOS 14, *) {
PHPhotoLibrary.requestAuthorization(for: PHAccessLevel.readWrite) { _ in
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
}, completionHandler: { success, error in
if success {
self.outputPath = videoURL.absoluteString
} else if let error = error {
print("Failed to save video to photo library: \(error.localizedDescription)")
}
})
}
} else {
// Fallback on earlier versions
}
}
func saveVideoToDocumentDirectory(videoURL: URL) -> URL? {
emptyDocumentDirectory()
let fileManager = FileManager.default
// Get the document directory URL
guard let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
print("Document directory not found")
return nil
}
// Set the destination URL
let destinationURL = documentDirectory.appendingPathComponent("\(generateRandomString()).\(videoURL.pathExtension)")
do {
// If the destination file already exists, remove it
if fileManager.fileExists(atPath: destinationURL.path) {
try fileManager.removeItem(at: destinationURL)
}
// Copy the video file to the document directory
try fileManager.copyItem(at: videoURL, to: destinationURL)
print("Video saved to document directory:", destinationURL)
return destinationURL
} catch {
print("Error saving video:", error.localizedDescription)
return nil
}
}
func emptyDocumentDirectory() {
let fileManager = FileManager.default
let documentsUrl = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
do {
let contents = try fileManager.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
for fileUrl in contents {
try fileManager.removeItem(at: fileUrl)
}
print("Document directory emptied.")
} catch {
print("Error emptying document directory: \(error)")
}
}
func generateRandomString() -> String {
let characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var randomString = ""
// Get the current timestamp
let timestamp = Date().timeIntervalSince1970
let timestampInt = Int(timestamp)
// Use the timestamp to seed the random number generator
srand48(timestampInt)
for _ in 0 ..< 10 {
let randomIndex = Int(drand48() * Double(characters.count))
let character = characters[characters.index(characters.startIndex, offsetBy: randomIndex)]
randomString.append(character)
}
return randomString
}
func deleteFileFromDocumentDirectory(videoURL: URL) {
// Get the document directory URL
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
do {
// If the destination file already exists, remove it
if FileManager.default.fileExists(atPath: videoURL.path) {
try FileManager.default.removeItem(at: videoURL)
}
} catch {
print("")
}
}
}
in
removeMetadataAndGPSInfo(from videoURL: URL, handler: StringHandler?)function, after saving video to document directory we have to check if file exist or not.With above line change I was able to remove meta data and store in gallary.