123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- //
- // NCCameraRoll.swift
- // Nextcloud
- //
- // Created by Marino Faggiana on 21/12/22.
- // Copyright © 2022 Marino Faggiana. All rights reserved.
- //
- // Author Marino Faggiana <marino.faggiana@nextcloud.com>
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- //
- import Foundation
- import NextcloudKit
- import JGProgressHUD
- class NCCameraRoll: NSObject {
- func extractCameraRoll(from metadata: tableMetadata, viewController: UIViewController?, hud: JGProgressHUD, completition: @escaping (_ metadatas: [tableMetadata]) -> Void) {
- var chunkSize = NCGlobal.shared.chunkSizeMBCellular
- if NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi {
- chunkSize = NCGlobal.shared.chunkSizeMBEthernetOrWiFi
- }
- var metadatas: [tableMetadata] = []
- let metadataSource = tableMetadata.init(value: metadata)
- guard !metadata.isExtractFile else { return completition([metadataSource]) }
- guard !metadataSource.assetLocalIdentifier.isEmpty else {
- let filePath = CCUtility.getDirectoryProviderStorageOcId(metadataSource.ocId, fileNameView: metadataSource.fileName)!
- metadataSource.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath)
- let results = NextcloudKit.shared.nkCommonInstance.getInternalType(fileName: metadataSource.fileNameView, mimeType: metadataSource.contentType, directory: false)
- metadataSource.contentType = results.mimeType
- metadataSource.iconName = results.iconName
- metadataSource.classFile = results.classFile
- if let date = NCUtilityFileSystem.shared.getFileCreationDate(filePath: filePath) {
- metadataSource.creationDate = date
- }
- if let date = NCUtilityFileSystem.shared.getFileModificationDate(filePath: filePath) {
- metadataSource.date = date
- }
- if metadataSource.size > chunkSize {
- metadataSource.chunk = chunkSize
- } else {
- metadataSource.chunk = 0
- }
- metadataSource.e2eEncrypted = metadata.isDirectoryE2EE
- if metadataSource.chunk > 0 || metadataSource.e2eEncrypted {
- metadataSource.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
- }
- metadataSource.isExtractFile = true
- if let metadata = NCManageDatabase.shared.addMetadata(metadataSource) {
- metadatas.append(metadata)
- }
- return completition(metadatas)
- }
- extractImageVideoFromAssetLocalIdentifier(metadata: metadataSource, modifyMetadataForUpload: true, viewController: viewController, hud: hud) { metadata, fileNamePath, error in
- if let metadata = metadata, let fileNamePath = fileNamePath, !error {
- metadatas.append(metadata)
- let toPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
- NCUtilityFileSystem.shared.moveFile(atPath: fileNamePath, toPath: toPath)
- let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadataSource.assetLocalIdentifier], options: nil)
- if metadata.livePhoto, fetchAssets.count > 0 {
- self.createMetadataLivePhoto(metadata: metadata, asset: fetchAssets.firstObject) { metadata in
- if let metadata = metadata, let metadata = NCManageDatabase.shared.addMetadata(metadata) {
- metadatas.append(metadata)
- }
- completition(metadatas)
- }
- } else {
- completition(metadatas)
- }
- } else {
- completition(metadatas)
- }
- }
- }
- func extractImageVideoFromAssetLocalIdentifier(metadata: tableMetadata,
- modifyMetadataForUpload: Bool,
- viewController: UIViewController?,
- hud: JGProgressHUD,
- completion: @escaping (_ metadata: tableMetadata?, _ fileNamePath: String?, _ error: Bool) -> Void) {
- var fileNamePath: String?
- let metadata = tableMetadata.init(value: metadata)
- var compatibilityFormat: Bool = false
- var chunkSize = NCGlobal.shared.chunkSizeMBCellular
- if NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi {
- chunkSize = NCGlobal.shared.chunkSizeMBEthernetOrWiFi
- }
- func callCompletionWithError(_ error: Bool = true) {
- if error {
- completion(nil, nil, true)
- } else {
- var metadataReturn = metadata
- if modifyMetadataForUpload {
- if metadata.size > chunkSize {
- metadata.chunk = chunkSize
- } else {
- metadata.chunk = 0
- }
- metadata.e2eEncrypted = metadata.isDirectoryE2EE
- if metadata.chunk > 0 || metadata.e2eEncrypted {
- metadata.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
- }
- metadata.isExtractFile = true
- if let metadata = NCManageDatabase.shared.addMetadata(metadata) {
- metadataReturn = metadata
- }
- }
- completion(metadataReturn, fileNamePath, error)
- }
- }
- let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadata.assetLocalIdentifier], options: nil)
- guard fetchAssets.count > 0, let asset = fetchAssets.firstObject else {
- return callCompletionWithError()
- }
- let extensionAsset = asset.originalFilename.pathExtension.uppercased()
- let creationDate = asset.creationDate ?? Date()
- let modificationDate = asset.modificationDate ?? Date()
- if asset.mediaType == PHAssetMediaType.image && (extensionAsset == "HEIC" || extensionAsset == "DNG") && CCUtility.getFormatCompatibility() {
- let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".jpg"
- metadata.contentType = "image/jpeg"
- fileNamePath = NSTemporaryDirectory() + fileName
- metadata.fileNameView = fileName
- if !metadata.isDirectoryE2EE {
- metadata.fileName = fileName
- }
- compatibilityFormat = true
- } else {
- fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
- }
- guard let fileNamePath = fileNamePath else { return callCompletionWithError() }
- if asset.mediaType == PHAssetMediaType.image {
- let options = PHImageRequestOptions()
- options.isNetworkAccessAllowed = true
- if compatibilityFormat {
- options.deliveryMode = .opportunistic
- } else {
- options.deliveryMode = .highQualityFormat
- }
- options.isSynchronous = true
- if extensionAsset == "DNG" {
- options.version = PHImageRequestOptionsVersion.original
- }
- options.progressHandler = { progress, error, _, _ in
- print(progress)
- if error != nil { return callCompletionWithError() }
- }
- PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
- guard var data = data else { return callCompletionWithError() }
- if compatibilityFormat {
- guard let ciImage = CIImage(data: data), let colorSpace = ciImage.colorSpace, let dataJPEG = CIContext().jpegRepresentation(of: ciImage, colorSpace: colorSpace) else { return callCompletionWithError() }
- data = dataJPEG
- }
- NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
- do {
- try data.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic)
- } catch { return callCompletionWithError() }
- metadata.creationDate = creationDate as NSDate
- metadata.date = modificationDate as NSDate
- metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
- return callCompletionWithError(false)
- }
- } else if asset.mediaType == PHAssetMediaType.video {
- let options = PHVideoRequestOptions()
- options.isNetworkAccessAllowed = true
- options.version = PHVideoRequestOptionsVersion.current
- options.progressHandler = { progress, error, _, _ in
- print(progress)
- if error != nil { return callCompletionWithError() }
- }
- PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { asset, _, _ in
- if let asset = asset as? AVURLAsset {
- NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
- do {
- try FileManager.default.copyItem(at: asset.url, to: URL(fileURLWithPath: fileNamePath))
- metadata.creationDate = creationDate as NSDate
- metadata.date = modificationDate as NSDate
- metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
- return callCompletionWithError(false)
- } catch { return callCompletionWithError() }
- } else if let asset = asset as? AVComposition, asset.tracks.count > 1, let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality), let viewController = viewController {
- DispatchQueue.main.async {
- hud.indicatorView = JGProgressHUDRingIndicatorView()
- if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
- indicatorView.ringWidth = 1.5
- }
- hud.textLabel.text = NSLocalizedString("_exporting_video_", comment: "")
- hud.show(in: viewController.view)
- hud.tapOnHUDViewBlock = { _ in
- exporter.cancelExport()
- }
- }
- exporter.outputURL = URL(fileURLWithPath: fileNamePath)
- exporter.outputFileType = AVFileType.mp4
- exporter.shouldOptimizeForNetworkUse = true
- exporter.exportAsynchronously {
- DispatchQueue.main.async { hud.dismiss() }
- if exporter.status == .completed {
- metadata.creationDate = creationDate as NSDate
- metadata.date = modificationDate as NSDate
- metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
- return callCompletionWithError(false)
- } else { return callCompletionWithError() }
- }
- while exporter.status == AVAssetExportSession.Status.exporting || exporter.status == AVAssetExportSession.Status.waiting {
- hud.progress = exporter.progress
- }
- } else {
- return callCompletionWithError()
- }
- }
- } else {
- return callCompletionWithError()
- }
- }
- private func createMetadataLivePhoto(metadata: tableMetadata,
- asset: PHAsset?,
- completion: @escaping (_ metadata: tableMetadata?) -> Void) {
- guard let asset = asset else { return completion(nil) }
- let options = PHLivePhotoRequestOptions()
- options.deliveryMode = PHImageRequestOptionsDeliveryMode.fastFormat
- options.isNetworkAccessAllowed = true
- let ocId = NSUUID().uuidString
- let fileName = (metadata.fileName as NSString).deletingPathExtension + ".mov"
- let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
- var chunkSize = NCGlobal.shared.chunkSizeMBCellular
- if NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi {
- chunkSize = NCGlobal.shared.chunkSizeMBEthernetOrWiFi
- }
- PHImageManager.default().requestLivePhoto(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.default, options: options) { livePhoto, _ in
- guard let livePhoto = livePhoto else { return completion(nil) }
- var videoResource: PHAssetResource?
- for resource in PHAssetResource.assetResources(for: livePhoto) where resource.type == PHAssetResourceType.pairedVideo {
- videoResource = resource
- break
- }
- guard let videoResource = videoResource else { return completion(nil) }
- NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath)
- PHAssetResourceManager.default().writeData(for: videoResource, toFile: URL(fileURLWithPath: fileNamePath), options: nil) { error in
- if error != nil { return completion(nil) }
- let metadataLivePhoto = NCManageDatabase.shared.createMetadata(account: metadata.account,
- user: metadata.user,
- userId: metadata.userId,
- fileName: fileName,
- fileNameView: fileName,
- ocId: ocId,
- serverUrl: metadata.serverUrl,
- urlBase: metadata.urlBase,
- url: "",
- contentType: "",
- isLivePhoto: true)
- metadataLivePhoto.classFile = NKCommon.TypeClassFile.video.rawValue
- metadataLivePhoto.isExtractFile = true
- metadataLivePhoto.session = metadata.session
- metadataLivePhoto.sessionSelector = metadata.sessionSelector
- metadataLivePhoto.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
- metadataLivePhoto.status = metadata.status
- if metadataLivePhoto.size > chunkSize {
- metadataLivePhoto.chunk = chunkSize
- } else {
- metadataLivePhoto.chunk = 0
- }
- metadataLivePhoto.e2eEncrypted = metadata.isDirectoryE2EE
- if metadataLivePhoto.chunk > 0 || metadataLivePhoto.e2eEncrypted {
- metadataLivePhoto.session = NextcloudKit.shared.nkCommonInstance.sessionIdentifierUpload
- }
- metadataLivePhoto.creationDate = metadata.creationDate
- metadataLivePhoto.date = metadata.date
- metadataLivePhoto.uploadDate = metadata.uploadDate
- return completion(NCManageDatabase.shared.addMetadata(metadataLivePhoto))
- }
- }
- }
- }
|