Unable remove metadata from video getting Can not save Error Domain=AVFoundationErrorDomain Code=-11823

41 views Asked by At

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("")
        }
    }
}
1

There are 1 answers

0
Anis Mansuri On

in removeMetadataAndGPSInfo(from videoURL: URL, handler: StringHandler?) function, after saving video to document directory we have to check if file exist or not.

let outputURL = saveVideoToDocumentDirectory(videoURL: videoURL)!
if FileManager.default.fileExists(atPath: outputURL.path) {
    try? FileManager.default.removeItem(at: outputURL)
}
exportSession?.outputURL = outputURL

With above line change I was able to remove meta data and store in gallary.