react-native-compressor 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +30 -7
  2. package/android/build.gradle +8 -0
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +527 -0
  4. package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +31 -0
  5. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +157 -0
  6. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.kt +34 -0
  7. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.kt +199 -0
  8. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressorOptions.kt +57 -0
  9. package/android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt +31 -0
  10. package/android/src/main/java/com/reactnativecompressor/Image/ImageSize.kt +3 -0
  11. package/android/src/main/java/com/reactnativecompressor/Utils/Downloader.kt +119 -0
  12. package/android/src/main/java/com/reactnativecompressor/Utils/EventEmitterHandler.kt +66 -0
  13. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.kt +61 -0
  14. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.kt +177 -0
  15. package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +117 -0
  16. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +162 -0
  17. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +63 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +125 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +86 -0
  20. package/android/src/newarch/CompressorSpec.kt +5 -0
  21. package/android/src/oldarch/CompressorSpec.kt +32 -0
  22. package/ios/Audio/AudioMain.swift +115 -0
  23. package/ios/Compressor-Bridging-Header.h +1 -2
  24. package/ios/Compressor.h +0 -8
  25. package/ios/Compressor.mm +28 -389
  26. package/ios/CompressorManager.swift +96 -0
  27. package/ios/Image/ImageCompressor.swift +354 -0
  28. package/ios/Image/ImageCompressorOptions.swift +93 -0
  29. package/ios/Image/ImageMain.swift +27 -0
  30. package/ios/Utils/Downloader.swift +157 -0
  31. package/ios/Utils/EventEmitterHandler.swift +54 -0
  32. package/ios/Utils/MediaCache.swift +48 -0
  33. package/ios/Utils/Uploader.swift +99 -0
  34. package/ios/Utils/Utils.swift +110 -0
  35. package/ios/Video/{VideoCompressor.swift → VideoMain.swift} +167 -146
  36. package/lib/commonjs/Image/index.js +1 -1
  37. package/lib/commonjs/Image/index.js.map +1 -1
  38. package/lib/commonjs/Main.js +1 -8
  39. package/lib/commonjs/Main.js.map +1 -1
  40. package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
  41. package/lib/commonjs/Video/index.js +4 -33
  42. package/lib/commonjs/Video/index.js.map +1 -1
  43. package/lib/commonjs/expo-plugin/compressor.js.map +1 -1
  44. package/lib/commonjs/index.js +11 -6
  45. package/lib/commonjs/index.js.map +1 -1
  46. package/lib/commonjs/utils/Downloader.js +38 -0
  47. package/lib/commonjs/utils/Downloader.js.map +1 -0
  48. package/lib/commonjs/utils/Uploader.js +40 -0
  49. package/lib/commonjs/utils/Uploader.js.map +1 -0
  50. package/lib/commonjs/utils/index.js +35 -0
  51. package/lib/commonjs/utils/index.js.map +1 -1
  52. package/lib/module/Image/index.js +1 -1
  53. package/lib/module/Image/index.js.map +1 -1
  54. package/lib/module/Main.js +1 -7
  55. package/lib/module/Main.js.map +1 -1
  56. package/lib/module/Spec/NativeCompressor.js.map +1 -1
  57. package/lib/module/Video/index.js +5 -33
  58. package/lib/module/Video/index.js.map +1 -1
  59. package/lib/module/expo-plugin/compressor.js.map +1 -1
  60. package/lib/module/index.js +6 -6
  61. package/lib/module/index.js.map +1 -1
  62. package/lib/module/utils/Downloader.js +31 -0
  63. package/lib/module/utils/Downloader.js.map +1 -0
  64. package/lib/module/utils/Uploader.js +33 -0
  65. package/lib/module/utils/Uploader.js.map +1 -0
  66. package/lib/module/utils/index.js +2 -0
  67. package/lib/module/utils/index.js.map +1 -1
  68. package/lib/typescript/Image/index.d.ts +4 -0
  69. package/lib/typescript/Image/index.d.ts.map +1 -1
  70. package/lib/typescript/Main.d.ts +1 -2
  71. package/lib/typescript/Main.d.ts.map +1 -1
  72. package/lib/typescript/Spec/NativeCompressor.d.ts +8 -2
  73. package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
  74. package/lib/typescript/Video/index.d.ts +4 -28
  75. package/lib/typescript/Video/index.d.ts.map +1 -1
  76. package/lib/typescript/index.d.ts +5 -4
  77. package/lib/typescript/index.d.ts.map +1 -1
  78. package/lib/typescript/utils/Downloader.d.ts +2 -0
  79. package/lib/typescript/utils/Downloader.d.ts.map +1 -0
  80. package/lib/typescript/utils/Uploader.d.ts +28 -0
  81. package/lib/typescript/utils/Uploader.d.ts.map +1 -0
  82. package/lib/typescript/utils/index.d.ts +2 -1
  83. package/lib/typescript/utils/index.d.ts.map +1 -1
  84. package/package.json +6 -4
  85. package/src/Image/index.tsx +5 -1
  86. package/src/Main.tsx +1 -15
  87. package/src/Spec/NativeCompressor.ts +14 -2
  88. package/src/Video/index.tsx +11 -84
  89. package/src/expo-plugin/compressor.ts +1 -1
  90. package/src/index.tsx +6 -3
  91. package/src/utils/Downloader.tsx +38 -0
  92. package/src/utils/Uploader.tsx +78 -0
  93. package/src/utils/index.tsx +2 -0
  94. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.java +0 -605
  95. package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +0 -220
  96. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.java +0 -58
  97. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +0 -257
  98. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +0 -69
  99. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageSize.java +0 -13
  100. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploadHelper.java +0 -39
  101. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploader.java +0 -129
  102. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +0 -65
  103. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.java +0 -210
  104. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +0 -272
  105. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression/AutoVideoCompression.java +0 -87
  106. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.java +0 -182
  107. package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +0 -112
  108. package/android/src/newarch/CompressorSpec.java +0 -9
  109. package/android/src/newarch/VideoCompressorSpec.java +0 -9
  110. package/android/src/oldarch/CompressorSpec.java +0 -34
  111. package/android/src/oldarch/VideoCompressorSpec.java +0 -26
  112. package/ios/Image/ImageCompressor.h +0 -22
  113. package/ios/Image/ImageCompressor.m +0 -540
  114. package/ios/Image/ImageCompressorOptions.h +0 -28
  115. package/ios/Image/ImageCompressorOptions.m +0 -97
  116. package/ios/Utils/Downloader.h +0 -16
  117. package/ios/Utils/Downloader.m +0 -150
  118. package/ios/Utils/MediaCache.h +0 -11
  119. package/ios/Utils/MediaCache.m +0 -51
  120. package/ios/Video/VideoCompressor.h +0 -5
  121. package/ios/Video/VideoCompressor.m +0 -1
  122. package/lib/commonjs/Spec/NativeVideoCompressor.js +0 -10
  123. package/lib/commonjs/Spec/NativeVideoCompressor.js.map +0 -1
  124. package/lib/module/Spec/NativeVideoCompressor.js +0 -3
  125. package/lib/module/Spec/NativeVideoCompressor.js.map +0 -1
  126. package/lib/typescript/Spec/NativeVideoCompressor.d.ts +0 -13
  127. package/lib/typescript/Spec/NativeVideoCompressor.d.ts.map +0 -1
  128. package/src/Spec/NativeVideoCompressor.ts +0 -14
@@ -0,0 +1,48 @@
1
+ import Foundation
2
+
3
+ class MediaCache {
4
+ static var completedImagePaths: [String] = []
5
+
6
+ class func addCompletedImagePath(_ imagePath: String) {
7
+ if !imagePath.isEmpty {
8
+ completedImagePaths.append(imagePath)
9
+ }
10
+ }
11
+
12
+ class func removeCompletedImagePath(_ imagePath: String) {
13
+ if !imagePath.isEmpty {
14
+ if let index = completedImagePaths.firstIndex(of: imagePath) {
15
+ completedImagePaths.remove(at: index)
16
+ if let fileURL = URL(string: imagePath) {
17
+ let fileSystemPath = fileURL.path
18
+
19
+ let fileManager = FileManager.default
20
+ do {
21
+ if fileManager.fileExists(atPath: fileSystemPath) {
22
+ try fileManager.removeItem(atPath: fileSystemPath)
23
+ }
24
+ } catch {
25
+ print("Error deleting image at path: \(fileSystemPath)")
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ class func cleanupCache() {
33
+ let fileManager = FileManager.default
34
+ if let cacheDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first {
35
+ for imagePath in completedImagePaths {
36
+ let absoluteImagePath = (cacheDirectory as NSString).appendingPathComponent(imagePath)
37
+ do {
38
+ if fileManager.fileExists(atPath: absoluteImagePath) {
39
+ try fileManager.removeItem(atPath: absoluteImagePath)
40
+ }
41
+ } catch {
42
+ print("Error deleting image at path: \(absoluteImagePath)")
43
+ }
44
+ }
45
+ completedImagePaths.removeAll()
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,99 @@
1
+ //
2
+ // Uploader.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 10/09/2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ struct UploadError: Error {
11
+ private let message: String
12
+
13
+ var localizedDescription: String {
14
+ return message
15
+ }
16
+
17
+ init(message: String) {
18
+ self.message = message
19
+ }
20
+ }
21
+
22
+ class Uploader : NSObject, URLSessionTaskDelegate{
23
+ var uploadResolvers: [String: RCTPromiseResolveBlock] = [:]
24
+ var uploadRejectors: [String: RCTPromiseRejectBlock] = [:]
25
+
26
+ func upload(filePath: String, options: [String: Any], resolve:@escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) -> Void {
27
+ let fileUrl = Utils.makeValidUri(filePath: filePath)
28
+
29
+ guard let uuid = options["uuid"] as? String else {
30
+ let uploadError = UploadError(message: "UUID is missing")
31
+ reject("Upload Failed", "UUID is missing", uploadError)
32
+ return
33
+ }
34
+
35
+ guard let remoteUrl = options["url"] as? String else {
36
+ let uploadError = UploadError(message: "url is missing")
37
+ reject("Upload Failed", "url is missing", uploadError)
38
+ return
39
+ }
40
+
41
+ guard let method = options["method"] as? String else {
42
+ let uploadError = UploadError(message: "method is missing")
43
+ reject("Upload Failed", "method is missing", uploadError)
44
+ return
45
+ }
46
+
47
+ guard let file = URL(string: fileUrl) else{
48
+ let uploadError = UploadError(message: "invalid file url")
49
+ reject("Failed", "Upload Failed", uploadError)
50
+ return
51
+ }
52
+
53
+ let headers = options["headers"] as? [String: String] ?? [:]
54
+
55
+ let url = URL(string: remoteUrl)!
56
+ var request = URLRequest(url: url)
57
+ request.httpMethod=method
58
+ for(header, v) in headers{
59
+ request.setValue(v, forHTTPHeaderField: header)
60
+ }
61
+
62
+ uploadResolvers[uuid] = resolve
63
+ uploadRejectors[uuid] = reject
64
+ // TODO: ADD Headers
65
+ let config = URLSessionConfiguration.background(withIdentifier: uuid)
66
+ let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
67
+ let task = session.uploadTask(with: request, fromFile: file)
68
+ task.resume()
69
+ }
70
+
71
+ func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
72
+ guard let uuid = session.configuration.identifier else {return}
73
+ guard let reject = uploadRejectors[uuid] else{return}
74
+ guard let resolve = uploadResolvers[uuid] else{return}
75
+ guard error == nil else {
76
+ reject("failed", "Upload Failed", error)
77
+ uploadRejectors[uuid] = nil
78
+ return;
79
+ }
80
+
81
+ guard let response = task.response as? HTTPURLResponse else {
82
+ let uploadError = UploadError(message: "Response is not defined")
83
+ reject("failed", "Upload Failed", uploadError)
84
+ uploadRejectors[uuid] = nil
85
+ return;
86
+ }
87
+
88
+ let result: [String : Any] = ["status": response.statusCode, "headers": response.allHeaderFields, "body": ""]
89
+
90
+ resolve(result)
91
+ uploadResolvers[uuid] = nil
92
+ }
93
+
94
+ func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
95
+ {
96
+ guard let uuid = session.configuration.identifier else {return}
97
+ EventEmitterHandler.emituploadProgress(uuid,totalBytesSent: totalBytesSent,totalBytesExpectedToSend: totalBytesExpectedToSend)
98
+ }
99
+ }
@@ -0,0 +1,110 @@
1
+ //
2
+ // Utils.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 10/09/2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ class Utils {
11
+ static func generateCacheFilePath(_ extension: String) -> String {
12
+ let uuid = UUID()
13
+ let imageNameWithoutExtension = uuid.uuidString
14
+ let imageName = imageNameWithoutExtension + "." + `extension`
15
+ let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(imageName)
16
+ return filePath
17
+ }
18
+
19
+ static func makeValidUri(filePath: String) -> String {
20
+ let fileWithUrl = URL(fileURLWithPath: filePath)
21
+ let absoluteUrl = fileWithUrl.deletingLastPathComponent()
22
+ let fileUrl = "file://\(absoluteUrl.path)/\(fileWithUrl.lastPathComponent)"
23
+ return fileUrl;
24
+ }
25
+
26
+ static func getFileSize(from urlString: String, completion: @escaping (NSNumber?, Error?) -> Void) {
27
+ if let url = URL(string: urlString) {
28
+ var request = URLRequest(url: url)
29
+ request.httpMethod = "HEAD"
30
+
31
+ let session = URLSession.shared
32
+ let task = session.dataTask(with: request) { data, response, error in
33
+ if let error = error {
34
+ completion(nil, error)
35
+ } else if let httpResponse = response as? HTTPURLResponse {
36
+ let contentLength = NSNumber(value: httpResponse.expectedContentLength)
37
+ completion(contentLength, nil)
38
+ } else {
39
+ let error = NSError(domain: "FileDownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response."])
40
+ completion(nil, error)
41
+ }
42
+ }
43
+
44
+ task.resume()
45
+ }
46
+ }
47
+
48
+ static func getFileSize(_ filePath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
49
+ do {
50
+ if filePath.hasPrefix("http://") || filePath.hasPrefix("https://") {
51
+ getFileSize(from: filePath) { fileSize, error in
52
+ if let error = error {
53
+ print("Error: \(error.localizedDescription)")
54
+ reject(String(error._code), error.localizedDescription, error)
55
+ } else if let fileSize = fileSize {
56
+ print("File size: \(fileSize)")
57
+ resolve(fileSize.stringValue)
58
+ }
59
+ }
60
+ } else {
61
+ var filePath = filePath
62
+ if filePath.hasPrefix("file://") {
63
+ filePath = filePath.replacingOccurrences(of: "file://", with: "")
64
+ }
65
+ let fileManager = FileManager.default
66
+ var isDir: ObjCBool = false
67
+ if !fileManager.fileExists(atPath: filePath, isDirectory: &isDir) || isDir.boolValue {
68
+ let err = NSError(domain: "file not found", code: -15, userInfo: nil)
69
+ reject(String(err.code), err.localizedDescription, err)
70
+ return
71
+ }
72
+ if let attrs = try? fileManager.attributesOfItem(atPath: filePath),
73
+ let fileSize = attrs[.size] as? UInt64 {
74
+ let fileSizeString = String(fileSize)
75
+ resolve(fileSizeString)
76
+ }
77
+ }
78
+ } catch {
79
+ reject(error.localizedDescription, error.localizedDescription, nil)
80
+ }
81
+ }
82
+
83
+
84
+ static func getRealPath(_ path: String, type: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
85
+ do {
86
+ if type == "video" {
87
+ VideoCompressor.getAbsoluteVideoPath(path, options: [:]) { absoluteImagePath in
88
+ resolve(absoluteImagePath)
89
+ }
90
+ } else {
91
+ let options = ImageCompressorOptions.fromDictionary([:])
92
+ ImageCompressor.getAbsoluteImagePath(path, options: options) { absoluteImagePath in
93
+ resolve(absoluteImagePath)
94
+ }
95
+ }
96
+ } catch {
97
+ reject(error.localizedDescription, error.localizedDescription, nil)
98
+ }
99
+ }
100
+
101
+ static func generateFilePath(_ _extension: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
102
+ do {
103
+ let outputUri = generateCacheFilePath(_extension)
104
+ resolve(outputUri)
105
+ } catch {
106
+ reject(error.localizedDescription, error.localizedDescription, nil)
107
+ }
108
+ }
109
+
110
+ }
@@ -3,6 +3,7 @@ import AssetsLibrary
3
3
  import AVFoundation
4
4
  import NextLevelSessionExporter
5
5
  import Photos
6
+ import MobileCoreServices
6
7
 
7
8
  struct CompressionError: Error {
8
9
  private let message: String
@@ -16,39 +17,28 @@ struct CompressionError: Error {
16
17
  }
17
18
  }
18
19
 
19
- struct UploadError: Error {
20
- private let message: String
21
-
22
- var localizedDescription: String {
23
- return message
24
- }
25
-
26
- init(message: String) {
27
- self.message = message
28
- }
29
- }
30
-
31
- @available(iOS 11.0, *)
32
- @objc(VideoCompressor)
33
- class VideoCompressor: RCTEventEmitter, URLSessionTaskDelegate {
20
+ class VideoCompressor {
34
21
  var backgroundTaskId: UIBackgroundTaskIdentifier = .invalid;
35
- var hasListener: Bool=false
36
- var uploadResolvers: [String: RCTPromiseResolveBlock] = [:]
37
- var uploadRejectors: [String: RCTPromiseRejectBlock] = [:]
22
+
38
23
  var compressorExports: [String: NextLevelSessionExporter] = [:]
39
- let videoCompressionThreshold:Int=7
40
-
41
- override init() {
42
- super.init()
43
- ImageCompressor.initVideoCompressorInstance(self)
44
- }
45
-
46
- override static func requiresMainQueueSetup() -> Bool {
47
- return false
48
- }
49
-
24
+
25
+ let metadatas: [String] = [
26
+ "albumName",
27
+ "artist",
28
+ "comment",
29
+ "copyrights",
30
+ "creationDate",
31
+ "date",
32
+ "encodedby",
33
+ "genre",
34
+ "language",
35
+ "location",
36
+ "lastModifiedDate",
37
+ "performer",
38
+ "publisher",
39
+ "title"
40
+ ]
50
41
 
51
- @objc(activateBackgroundTask:withResolver:withRejecter:)
52
42
  func activateBackgroundTask(options: [String: Any], resolve:@escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) -> Void {
53
43
  guard backgroundTaskId == .invalid else {
54
44
  reject("failed", "There is a background task already", nil)
@@ -57,14 +47,13 @@ class VideoCompressor: RCTEventEmitter, URLSessionTaskDelegate {
57
47
  backgroundTaskId = UIApplication.shared.beginBackgroundTask(
58
48
  withName: "video-upload",
59
49
  expirationHandler: {
60
- self.sendEvent(withName: "backgroundTaskExpired", body: ["backgroundTaskId": self.backgroundTaskId])
50
+ EventEmitterHandler.emitBackgroundTaskExpired(self.backgroundTaskId)
61
51
  UIApplication.shared.endBackgroundTask(self.backgroundTaskId)
62
52
  self.backgroundTaskId = .invalid
63
53
  })
64
54
  resolve(backgroundTaskId)
65
55
  }
66
56
 
67
- @objc(deactivateBackgroundTask:withResolver:withRejecter:)
68
57
  func deactivateBackgroundTask(options: [String: Any], resolve:@escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) -> Void {
69
58
  guard backgroundTaskId != .invalid else {
70
59
  reject("failed", "There is no active background task", nil)
@@ -75,14 +64,11 @@ class VideoCompressor: RCTEventEmitter, URLSessionTaskDelegate {
75
64
  backgroundTaskId = .invalid
76
65
  }
77
66
 
78
- @objc(compress:withOptions:withResolver:withRejecter:)
79
67
  func compress(fileUrl: String, options: [String: Any], resolve:@escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) -> Void {
68
+ let progressDivider=options["progressDivider"] as? Int ?? 0
80
69
  compressVideo(url: URL(string: fileUrl)!, options:options,
81
- onProgress: { progress in
82
- print("Progress", progress)
83
- if(self.hasListener){
84
- self.sendEvent(withName: "videoCompressProgress", body: ["uuid": options["uuid"], "data": ["progress": progress]])
85
- }
70
+ onProgress: { progress in
71
+ EventEmitterHandler.emitVideoCompressProgress(progress, uuid: options["uuid"] as! String)
86
72
  }, onCompletion: { newUrl in
87
73
  resolve("\(newUrl)");
88
74
  }, onFailure: { error in
@@ -90,105 +76,10 @@ class VideoCompressor: RCTEventEmitter, URLSessionTaskDelegate {
90
76
  })
91
77
  }
92
78
 
93
- @objc(cancelCompression:)
94
79
  func cancelCompression(uuid: String) -> Void {
95
80
  compressorExports[uuid]?.cancelExport()
96
81
  }
97
82
 
98
- func makeValidUri(filePath: String) -> String {
99
- let fileWithUrl = URL(fileURLWithPath: filePath)
100
- let absoluteUrl = fileWithUrl.deletingLastPathComponent()
101
- let fileUrl = "file://\(absoluteUrl.path)/\(fileWithUrl.lastPathComponent)"
102
- return fileUrl;
103
- }
104
-
105
- @objc(upload:withOptions:withResolver:withRejecter:)
106
- func upload(filePath: String, options: [String: Any], resolve:@escaping RCTPromiseResolveBlock, reject:@escaping RCTPromiseRejectBlock) -> Void {
107
- let fileUrl = makeValidUri(filePath: filePath)
108
-
109
- guard let uuid = options["uuid"] as? String else {
110
- let uploadError = UploadError(message: "UUID is missing")
111
- reject("Upload Failed", "UUID is missing", uploadError)
112
- return
113
- }
114
-
115
- guard let remoteUrl = options["url"] as? String else {
116
- let uploadError = UploadError(message: "url is missing")
117
- reject("Upload Failed", "url is missing", uploadError)
118
- return
119
- }
120
-
121
- guard let method = options["method"] as? String else {
122
- let uploadError = UploadError(message: "method is missing")
123
- reject("Upload Failed", "method is missing", uploadError)
124
- return
125
- }
126
-
127
- guard let file = URL(string: fileUrl) else{
128
- let uploadError = UploadError(message: "invalid file url")
129
- reject("Failed", "Upload Failed", uploadError)
130
- return
131
- }
132
-
133
- let headers = options["headers"] as? [String: String] ?? [:]
134
-
135
- let url = URL(string: remoteUrl)!
136
- var request = URLRequest(url: url)
137
- request.httpMethod=method
138
- for(header, v) in headers{
139
- request.setValue(v, forHTTPHeaderField: header)
140
- }
141
-
142
- uploadResolvers[uuid] = resolve
143
- uploadRejectors[uuid] = reject
144
- // TODO: ADD Headers
145
- let config = URLSessionConfiguration.background(withIdentifier: uuid)
146
- let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
147
- let task = session.uploadTask(with: request, fromFile: file)
148
- task.resume()
149
- }
150
-
151
- func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
152
- guard let uuid = session.configuration.identifier else {return}
153
- guard let reject = uploadRejectors[uuid] else{return}
154
- guard let resolve = uploadResolvers[uuid] else{return}
155
- guard error == nil else {
156
- reject("failed", "Upload Failed", error)
157
- uploadRejectors[uuid] = nil
158
- return;
159
- }
160
-
161
- guard let response = task.response as? HTTPURLResponse else {
162
- let uploadError = UploadError(message: "Response is not defined")
163
- reject("failed", "Upload Failed", uploadError)
164
- uploadRejectors[uuid] = nil
165
- return;
166
- }
167
-
168
- let result: [String : Any] = ["status": response.statusCode, "headers": response.allHeaderFields, "body": ""]
169
-
170
- resolve(result)
171
- uploadResolvers[uuid] = nil
172
- }
173
-
174
- func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
175
- {
176
- guard let uuid = session.configuration.identifier else {return}
177
- self.sendEvent(withName: "VideoCompressorProgress", body: ["uuid": uuid, "data": ["written": totalBytesSent, "total": totalBytesExpectedToSend]])
178
- }
179
-
180
- override func supportedEvents() -> [String]! {
181
- return ["videoCompressProgress", "VideoCompressorProgress", "backgroundTaskExpired","downloadProgress"]
182
- }
183
-
184
- override func stopObserving() -> Void {
185
- hasListener = false
186
- }
187
-
188
- override func startObserving() -> Void {
189
- hasListener = true
190
- }
191
-
192
83
  func getfileSize(forURL url: Any) -> Double {
193
84
  var fileURL: URL?
194
85
  var fileSize: Double = 0.0
@@ -213,12 +104,12 @@ func makeValidUri(filePath: String) -> String {
213
104
  func compressVideo(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
214
105
  let uuid:String = options["uuid"] as! String
215
106
 
216
- ImageCompressor.getAbsoluteVideoPath(url.absoluteString, uuid: uuid) { absoluteVideoPath in
107
+ VideoCompressor.getAbsoluteVideoPath(url.absoluteString, options: options) { absoluteVideoPath in
217
108
  var minimumFileSizeForCompress:Double=0.0;
218
- let videoURL = URL(string: absoluteVideoPath!)
109
+ let videoURL = URL(string: absoluteVideoPath)
219
110
  let fileSize=self.getfileSize(forURL: videoURL!);
220
111
  if((options["minimumFileSizeForCompress"]) != nil)
221
- {
112
+ {0
222
113
  minimumFileSizeForCompress=options["minimumFileSizeForCompress"] as! Double;
223
114
  }
224
115
  if(fileSize>minimumFileSizeForCompress)
@@ -280,6 +171,7 @@ func makeValidUri(filePath: String) -> String {
280
171
  func autoCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
281
172
  let maxSize:Float = options["maxSize"] as! Float;
282
173
  let uuid:String = options["uuid"] as! String
174
+ let progressDivider=options["progressDivider"] as? Int ?? 0
283
175
 
284
176
  let asset = AVAsset(url: url)
285
177
  guard asset.tracks.count >= 1 else {
@@ -304,7 +196,7 @@ func makeValidUri(filePath: String) -> String {
304
196
  height: Int(resultHeight), width: Int(resultWidth)
305
197
  );
306
198
 
307
- exportVideoHelper(url: url, asset: asset, bitRate: videoBitRate, resultWidth: resultWidth, resultHeight: resultHeight,uuid: uuid) { progress in
199
+ exportVideoHelper(url: url, asset: asset, bitRate: videoBitRate, resultWidth: resultWidth, resultHeight: resultHeight,uuid: uuid,progressDivider: progressDivider) { progress in
308
200
  onProgress(progress)
309
201
  } onCompletion: { outputURL in
310
202
  onCompletion(outputURL)
@@ -316,6 +208,7 @@ func makeValidUri(filePath: String) -> String {
316
208
  func manualCompressionHelper(url: URL, options: [String: Any], onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
317
209
  let uuid:String = options["uuid"] as! String
318
210
  var bitRate=options["bitrate"] as! Float?;
211
+ let progressDivider=options["progressDivider"] as? Int ?? 0
319
212
  let asset = AVAsset(url: url)
320
213
  guard asset.tracks.count >= 1 else {
321
214
  let error = CompressionError(message: "Invalid video URL, no track found")
@@ -343,7 +236,7 @@ func makeValidUri(filePath: String) -> String {
343
236
 
344
237
  let videoBitRate = bitRate ?? height*width*1.5
345
238
 
346
- exportVideoHelper(url: url, asset: asset, bitRate: Int(videoBitRate), resultWidth: width, resultHeight: height,uuid: uuid) { progress in
239
+ exportVideoHelper(url: url, asset: asset, bitRate: Int(videoBitRate), resultWidth: width, resultHeight: height,uuid: uuid,progressDivider: progressDivider) { progress in
347
240
  onProgress(progress)
348
241
  } onCompletion: { outputURL in
349
242
  onCompletion(outputURL)
@@ -352,12 +245,13 @@ func makeValidUri(filePath: String) -> String {
352
245
  }
353
246
  }
354
247
 
355
- func exportVideoHelper(url: URL,asset: AVAsset, bitRate: Int,resultWidth:Float,resultHeight:Float,uuid:String, onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
356
- var videoCompressionCounter:Int=0
248
+ func exportVideoHelper(url: URL,asset: AVAsset, bitRate: Int,resultWidth:Float,resultHeight:Float,uuid:String,progressDivider: Int, onProgress: @escaping (Float) -> Void, onCompletion: @escaping (URL) -> Void, onFailure: @escaping (Error) -> Void){
249
+ var currentVideoCompression:Int=0
250
+
357
251
  var tmpURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
358
252
  .appendingPathComponent(ProcessInfo().globallyUniqueString)
359
253
  .appendingPathExtension("mp4")
360
- tmpURL = URL(string:makeValidUri(filePath: tmpURL.absoluteString))!
254
+ tmpURL = URL(string: Utils.makeValidUri(filePath: tmpURL.absoluteString))!
361
255
 
362
256
  let exporter = NextLevelSessionExporter(withAsset: asset)
363
257
  exporter.outputURL = tmpURL
@@ -384,15 +278,14 @@ func makeValidUri(filePath: String) -> String {
384
278
 
385
279
  compressorExports[uuid] = exporter
386
280
  exporter.export(progressHandler: { (progress) in
387
- let _progress:Float=progress*100;
388
- if(Int(_progress)==videoCompressionCounter)
281
+ let roundProgress:Int=Int((progress*100).rounded());
282
+ if(progressDivider==0||(roundProgress%progressDivider==0&&roundProgress>currentVideoCompression))
389
283
  {
390
- videoCompressionCounter=Int(_progress)+self.videoCompressionThreshold
284
+ currentVideoCompression=roundProgress
391
285
  onProgress(progress)
392
286
  }
393
-
394
287
  }, completionHandler: { result in
395
- videoCompressionCounter=0;
288
+ currentVideoCompression=0;
396
289
  switch exporter.status {
397
290
  case .completed:
398
291
  onCompletion(exporter.outputURL!)
@@ -413,4 +306,132 @@ func makeValidUri(filePath: String) -> String {
413
306
  return tracks[0];
414
307
  }
415
308
 
309
+
310
+ func getVideoMetaData(_ filePath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
311
+ do {
312
+ VideoCompressor.getAbsoluteVideoPath(filePath, options: [:]) { absoluteImagePath in
313
+ if absoluteImagePath.hasPrefix("file://") {
314
+ let absoluteImagePath = absoluteImagePath.replacingOccurrences(of: "file://", with: "")
315
+ let fileManager = FileManager.default
316
+ var isDir: ObjCBool = false
317
+
318
+ if !fileManager.fileExists(atPath: absoluteImagePath, isDirectory: &isDir) || isDir.boolValue {
319
+ let err = NSError(domain: "file not found", code: -15, userInfo: nil)
320
+ reject(String(err.code), err.localizedDescription, err)
321
+ return
322
+ }
323
+
324
+ let attrs = try? fileManager.attributesOfItem(atPath: absoluteImagePath)
325
+ if let fileSize = attrs?[FileAttributeKey.size] as? UInt64 {
326
+ let fileSizeString = String(fileSize)
327
+
328
+ var result: [String: Any] = [:]
329
+ let assetOptions: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
330
+ let asset = AVURLAsset(url: URL(fileURLWithPath: absoluteImagePath), options: assetOptions)
331
+ if let avAsset = asset.tracks(withMediaType: .video).first {
332
+ let size = avAsset.naturalSize
333
+ let _extension = (absoluteImagePath as NSString).pathExtension
334
+ let time = asset.duration
335
+ let seconds = Int(ceil(Double(time.value / Int64(time.timescale))))
336
+
337
+ result["width"] = String(format: "%.2f", size.width)
338
+ result["height"] = String(format: "%.2f", size.height)
339
+ result["extension"] = _extension
340
+ result["size"] = fileSizeString
341
+ result["duration"] = String(seconds)
342
+
343
+ var commonMetadata: [AVMetadataItem] = []
344
+ for key in self.metadatas {
345
+ let items = AVMetadataItem.metadataItems(from: asset.commonMetadata, withKey: key, keySpace: AVMetadataKeySpace.common)
346
+ commonMetadata.append(contentsOf: items)
347
+ }
348
+
349
+ for item in commonMetadata {
350
+ if let value = item.value {
351
+ result[item.commonKey!.rawValue] = value
352
+ }
353
+ }
354
+
355
+ resolve(result)
356
+ }
357
+ }
358
+ }
359
+ }
360
+ } catch {
361
+ reject(error.localizedDescription, error.localizedDescription, nil)
362
+ }
363
+ }
364
+
365
+ static func getAbsoluteVideoPath(_ videoPath: String, options: [String: Any], completionHandler: @escaping (String) -> Void) {
366
+ if videoPath.hasPrefix("http://") || videoPath.hasPrefix("https://") {
367
+ let uuid=options["uuid"] as? String ?? ""
368
+ let progressDivider=options["progressDivider"] as? Int ?? 0
369
+ Downloader.downloadFileAndSaveToCache(videoPath, uuid: uuid,progressDivider:progressDivider) { downloadedPath in
370
+ completionHandler(downloadedPath)
371
+ }
372
+ return
373
+ } else if !videoPath.contains("ph://") {
374
+ completionHandler(videoPath)
375
+ return
376
+ }
377
+ let assetId = videoPath.replacingOccurrences(of: "ph://", with: "")
378
+ let outputFileType = AVFileType.mp4
379
+ let pressetType = AVAssetExportPresetPassthrough
380
+
381
+ if assetId.isEmpty {
382
+ let error = NSError(domain: "RNGalleryManager", code: -91, userInfo: nil)
383
+ let exception = NSException(name: NSExceptionName(rawValue: "RNGalleryManager"), reason: "Empty asset ID.", userInfo: nil)
384
+ exception.raise()
385
+ return
386
+ }
387
+
388
+ let localIds = [assetId]
389
+ guard let videoAsset = PHAsset.fetchAssets(withLocalIdentifiers: localIds, options: nil).firstObject else {
390
+ return
391
+ }
392
+
393
+ let mimeType = UTTypeCopyPreferredTagWithClass(outputFileType as CFString, kUTTagClassMIMEType)?.takeRetainedValue() as String? ?? ""
394
+ let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeRetainedValue() as String? ?? ""
395
+ let extensionValue = UTTypeCopyPreferredTagWithClass(uti as CFString, kUTTagClassFilenameExtension)?.takeRetainedValue() as String? ?? ""
396
+ let path = Utils.generateCacheFilePath(extensionValue)
397
+ let outputUrl = URL(string: "file://\(path)")!
398
+
399
+ let videoRequestOptions = PHVideoRequestOptions()
400
+ videoRequestOptions.isNetworkAccessAllowed = true
401
+ videoRequestOptions.deliveryMode = .highQualityFormat
402
+
403
+ PHImageManager.default().requestExportSession(forVideo: videoAsset, options: videoRequestOptions, exportPreset: pressetType) { exportSession, _ in
404
+ guard let exportSession = exportSession else {
405
+ let error = NSError(domain: "RNGalleryManager", code: -91, userInfo: nil)
406
+ let exception = NSException(name: NSExceptionName(rawValue: "RNGalleryManager"), reason: "Export session is nil.", userInfo: nil)
407
+ exception.raise()
408
+ return
409
+ }
410
+
411
+ exportSession.shouldOptimizeForNetworkUse = true
412
+ exportSession.outputFileType = outputFileType
413
+ exportSession.outputURL = outputUrl
414
+
415
+ exportSession.exportAsynchronously {
416
+ switch exportSession.status {
417
+ case .failed:
418
+ let error = exportSession.error ?? NSError(domain: "RNGalleryManager", code: -91, userInfo: nil)
419
+ let codeWithDomain = "E\(error.localizedDescription)\(error)"
420
+ let exception = NSException(name: NSExceptionName(rawValue: codeWithDomain), reason: "Video export failed.", userInfo: nil)
421
+ exception.raise()
422
+ case .cancelled:
423
+ let error = NSError(domain: "RNGalleryManager", code: -91, userInfo: nil)
424
+ let exception = NSException(name: NSExceptionName(rawValue: "RNGalleryManager"), reason: "Video export cancelled.", userInfo: nil)
425
+ exception.raise()
426
+ case .completed:
427
+ completionHandler(outputUrl.absoluteString)
428
+ default:
429
+ let error = NSError(domain: "RNGalleryManager", code: -91, userInfo: nil)
430
+ let exception = NSException(name: NSExceptionName(rawValue: "RNGalleryManager"), reason: "Unknown status.", userInfo: nil)
431
+ exception.raise()
432
+ }
433
+ }
434
+ }
435
+ }
436
+
416
437
  }