react-native-compressor 1.7.1 → 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 +40 -15
  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} +168 -147
  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,354 @@
1
+ import Accelerate
2
+ import CoreGraphics
3
+ import Photos
4
+ import React
5
+ import Foundation
6
+ import MobileCoreServices
7
+
8
+
9
+ class ImageCompressor {
10
+ static func findTargetSize(_ image: UIImage, maxWidth: Int, maxHeight: Int) -> CGSize {
11
+ let width = image.size.width
12
+ let height = image.size.height
13
+
14
+ if width > height {
15
+ let newHeight = height / (width / CGFloat(maxWidth))
16
+ return CGSize(width: CGFloat(maxWidth), height: newHeight)
17
+ }
18
+
19
+ let newWidth = width / (height / CGFloat(maxHeight))
20
+ return CGSize(width: newWidth, height: CGFloat(maxHeight))
21
+ }
22
+
23
+
24
+ static func decodeImage(_ value: String) -> UIImage? {
25
+ if let data = Data(base64Encoded: value, options: .ignoreUnknownCharacters) {
26
+ return UIImage(data: data)
27
+ }
28
+ return nil
29
+ }
30
+
31
+
32
+ static func loadImage(_ path: String) -> UIImage? {
33
+ var image: UIImage?
34
+
35
+ if path.hasPrefix("data:") || path.hasPrefix("file:") {
36
+ if let imageUrl = URL(string: path), let imageData = try? Data(contentsOf: imageUrl) {
37
+ image = UIImage(data: imageData)
38
+ }
39
+ } else {
40
+ image = UIImage(contentsOfFile: path)
41
+ }
42
+
43
+ return image
44
+ }
45
+
46
+
47
+ static func manualResize(_ image: UIImage, maxWidth: Int, maxHeight: Int) -> UIImage {
48
+ let targetSize = findTargetSize(image, maxWidth: maxWidth, maxHeight: maxHeight)
49
+
50
+ if let cgImage = image.cgImage {
51
+ let sourceWidth = Int(image.size.width)
52
+ let sourceHeight = Int(image.size.height)
53
+ let targetWidth = Int(targetSize.width)
54
+ let targetHeight = Int(targetSize.height)
55
+ let bytesPerPixel = 4
56
+ let sourceBytesPerRow = sourceWidth * bytesPerPixel
57
+ let targetBytesPerRow = targetWidth * bytesPerPixel
58
+ let bitsPerComponent = 8
59
+
60
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
61
+
62
+ var sourceData = [UInt8](repeating: 0, count: sourceHeight * sourceBytesPerRow)
63
+ let context = CGContext(data: &sourceData,
64
+ width: Int(sourceWidth),
65
+ height: Int(sourceHeight),
66
+ bitsPerComponent: bitsPerComponent,
67
+ bytesPerRow: sourceBytesPerRow,
68
+ space: colorSpace,
69
+ bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)
70
+
71
+ context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: sourceWidth, height: sourceHeight))
72
+
73
+
74
+ var targetData = [UInt8](repeating: 0, count: targetWidth * targetHeight * bytesPerPixel)
75
+
76
+ var srcBuffer = vImage_Buffer(data: &sourceData, height: vImagePixelCount(sourceHeight), width: vImagePixelCount(sourceWidth), rowBytes: sourceBytesPerRow)
77
+ var targetBuffer = vImage_Buffer(data: &targetData, height: vImagePixelCount(targetHeight), width: vImagePixelCount(targetWidth), rowBytes: targetBytesPerRow)
78
+
79
+ let error = vImageScale_ARGB8888(&srcBuffer, &targetBuffer, nil, vImage_Flags(kvImageHighQualityResampling))
80
+
81
+ if error != kvImageNoError {
82
+ free(&targetData)
83
+ let exception = NSException(name: NSExceptionName(rawValue: "drawing_error"), reason: "Problem while rendering your image", userInfo: nil)
84
+ exception.raise()
85
+ }
86
+
87
+ let targetContext = CGContext(data: &targetData,
88
+ width: targetWidth,
89
+ height: targetHeight,
90
+ bitsPerComponent: bitsPerComponent,
91
+ bytesPerRow: targetBytesPerRow,
92
+ space: colorSpace,
93
+ bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)
94
+
95
+ let targetRef = targetContext?.makeImage()
96
+ let resizedImage = UIImage(cgImage: targetRef!)
97
+
98
+
99
+ return resizedImage
100
+ }
101
+ return UIImage()
102
+ }
103
+
104
+
105
+ static func manualCompress(_ image: UIImage, output: Int, quality: Float, outputExtension: String, isBase64: Bool) -> String {
106
+ var data: Data
107
+ var exception: NSException?
108
+
109
+ switch OutputType(rawValue: output)! {
110
+ case .jpg:
111
+ data = image.jpegData(compressionQuality: CGFloat(quality))!
112
+ case .png:
113
+ data = image.pngData()!
114
+ }
115
+
116
+ if isBase64 {
117
+ return data.base64EncodedString(options: [])
118
+ } else {
119
+ let filePath = Utils.generateCacheFilePath(outputExtension)
120
+ do {
121
+ try data.write(to: URL(fileURLWithPath: filePath), options: .atomic)
122
+ let returnablePath = makeValidUri(filePath)
123
+ return returnablePath
124
+ } catch {
125
+ exception = NSException(name: NSExceptionName(rawValue: "file_error"), reason: "Error writing file", userInfo: nil)
126
+ exception?.raise()
127
+ }
128
+ }
129
+
130
+ return ""
131
+ }
132
+
133
+
134
+ static func scaleAndRotateImage(_ image: UIImage) -> UIImage {
135
+ if image.imageOrientation == .up {
136
+ return image
137
+ }
138
+
139
+ var transform = CGAffineTransform.identity
140
+
141
+ switch image.imageOrientation {
142
+ case .down, .downMirrored:
143
+ transform = transform.translatedBy(x: image.size.width, y: image.size.height)
144
+ transform = transform.rotated(by: CGFloat.pi)
145
+ case .left, .leftMirrored:
146
+ transform = transform.translatedBy(x: image.size.width, y: 0)
147
+ transform = transform.rotated(by: CGFloat.pi / 2)
148
+ case .right, .rightMirrored:
149
+ transform = transform.translatedBy(x: 0, y: image.size.height)
150
+ transform = transform.rotated(by: -CGFloat.pi / 2)
151
+ default:
152
+ break
153
+ }
154
+
155
+ switch image.imageOrientation {
156
+ case .upMirrored, .downMirrored:
157
+ transform = transform.translatedBy(x: image.size.width, y: 0)
158
+ transform = transform.scaledBy(x: -1, y: 1)
159
+ case .leftMirrored, .rightMirrored:
160
+ transform = transform.translatedBy(x: image.size.height, y: 0)
161
+ transform = transform.scaledBy(x: -1, y: 1)
162
+ default:
163
+ break
164
+ }
165
+
166
+ if let cgImage = image.cgImage, let colorSpace = cgImage.colorSpace {
167
+ guard let context = CGContext(data: nil, width: Int(image.size.width), height: Int(image.size.height),
168
+ bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0,
169
+ space: colorSpace, bitmapInfo: cgImage.bitmapInfo.rawValue) else {
170
+ return image
171
+ }
172
+
173
+ context.concatenate(transform)
174
+
175
+ switch image.imageOrientation {
176
+ case .left, .leftMirrored, .right, .rightMirrored:
177
+ context.draw(cgImage, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
178
+ default:
179
+ context.draw(cgImage, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
180
+ }
181
+
182
+ if let rotatedCGImage = context.makeImage() {
183
+ return UIImage(cgImage: rotatedCGImage)
184
+ }
185
+ }
186
+
187
+ return image
188
+ }
189
+
190
+
191
+ static func makeValidUri(_ filePath: String) -> String {
192
+ let fileWithUrl = URL(fileURLWithPath: filePath)
193
+ let absoluteUrl = fileWithUrl.deletingLastPathComponent()
194
+ let fileUrl = "file://\(absoluteUrl.path)/\(fileWithUrl.lastPathComponent)"
195
+ return fileUrl
196
+ }
197
+
198
+
199
+ static func manualCompressHandler(_ imagePath: String, options: ImageCompressorOptions) -> String {
200
+ var exception: NSException?
201
+ var image: UIImage?
202
+
203
+ switch options.input {
204
+ case .base64:
205
+ image = ImageCompressor.decodeImage(imagePath)
206
+ case .uri:
207
+ image = ImageCompressor.loadImage(imagePath)
208
+ }
209
+
210
+ if let _image = image {
211
+ image = ImageCompressor.scaleAndRotateImage(_image)
212
+ let outputExtension = ImageCompressorOptions.getOutputInString(options.output)
213
+ let resizedImage = ImageCompressor.manualResize(_image, maxWidth: options.maxWidth, maxHeight: options.maxHeight)
214
+ let isBase64 = options.returnableOutputType == .rbase64
215
+ return ImageCompressor.manualCompress(resizedImage, output: options.output.rawValue, quality: options.quality, outputExtension: outputExtension, isBase64: isBase64)
216
+ } else {
217
+ exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
218
+ exception?.raise()
219
+ }
220
+
221
+ return ""
222
+ }
223
+
224
+
225
+ static func autoCompressHandler(_ imagePath: String, options: ImageCompressorOptions) -> String {
226
+ var exception: NSException?
227
+ var image = ImageCompressor.loadImage(imagePath)
228
+
229
+ if var image = image {
230
+ image = ImageCompressor.scaleAndRotateImage(image)
231
+ let outputExtension = ImageCompressorOptions.getOutputInString(options.output)
232
+
233
+ var actualHeight = image.size.height
234
+ var actualWidth = image.size.width
235
+ let maxHeight: CGFloat = 1280.0
236
+ let maxWidth: CGFloat = 1280.0
237
+ var imgRatio = actualWidth / actualHeight
238
+ let maxRatio = maxWidth / maxHeight
239
+ let compressionQuality: CGFloat = 0.8
240
+
241
+ if actualHeight > maxHeight || actualWidth > maxWidth {
242
+ if imgRatio < maxRatio {
243
+ imgRatio = maxHeight / actualHeight
244
+ actualWidth = imgRatio * actualWidth
245
+ actualHeight = maxHeight
246
+ } else if imgRatio > maxRatio {
247
+ imgRatio = maxWidth / actualWidth
248
+ actualHeight = imgRatio * actualHeight
249
+ actualWidth = maxWidth
250
+ } else {
251
+ actualHeight = maxHeight
252
+ actualWidth = maxWidth
253
+ }
254
+ }
255
+
256
+ let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
257
+ UIGraphicsBeginImageContext(rect.size)
258
+ image.draw(in: rect)
259
+ if let img = UIGraphicsGetImageFromCurrentImageContext() {
260
+ let imageData = img.jpegData(compressionQuality: compressionQuality)
261
+ UIGraphicsEndImageContext()
262
+ let filePath = Utils.generateCacheFilePath(outputExtension)
263
+ do {
264
+ try imageData?.write(to: URL(fileURLWithPath: filePath), options: .atomic)
265
+ let returnablePath = ImageCompressor.makeValidUri(filePath)
266
+ return returnablePath
267
+ } catch {
268
+ exception = NSException(name: NSExceptionName(rawValue: "file_error"), reason: "Error writing file", userInfo: nil)
269
+ exception?.raise()
270
+ }
271
+ }
272
+ } else {
273
+ exception = NSException(name: NSExceptionName(rawValue: "unsupported_value"), reason: "Unsupported value type.", userInfo: nil)
274
+ exception?.raise()
275
+ }
276
+
277
+ return ""
278
+ }
279
+
280
+ static func getAbsoluteImagePath(_ imagePath: String, options: ImageCompressorOptions, completionHandler: @escaping (String) -> Void) {
281
+ if imagePath.hasPrefix("http://") || imagePath.hasPrefix("https://") {
282
+ let uuid=options.uuid ?? ""
283
+ let progressDivider=options.progressDivider
284
+ Downloader.downloadFileAndSaveToCache(imagePath, uuid: uuid,progressDivider: progressDivider) { downloadedPath in
285
+ completionHandler(downloadedPath)
286
+ }
287
+ return
288
+ } else if !imagePath.contains("ph://") {
289
+ completionHandler(imagePath)
290
+ return
291
+ }
292
+
293
+ let imageURL = URL(string: imagePath.replacingOccurrences(of: " ", with: "%20"))
294
+ var size = CGSize.zero
295
+ var scale: CGFloat = 1
296
+ var resizeMode = RCTResizeMode.contain
297
+ var assetID = ""
298
+ var results: PHFetchResult<PHAsset>?
299
+
300
+ if imageURL?.scheme?.caseInsensitiveCompare("assets-library") == .orderedSame {
301
+ assetID = imageURL?.absoluteString ?? ""
302
+ results = PHAsset.fetchAssets(withALAssetURLs: [imageURL!], options: nil)
303
+ } else {
304
+ assetID = imagePath.replacingOccurrences(of: "ph://", with: "")
305
+ results = PHAsset.fetchAssets(withLocalIdentifiers: [assetID], options: nil)
306
+ }
307
+
308
+ guard let asset = results?.firstObject else {
309
+ return
310
+ }
311
+
312
+ let imageOptions = PHImageRequestOptions()
313
+ imageOptions.isNetworkAccessAllowed = true
314
+ imageOptions.deliveryMode = .highQualityFormat
315
+
316
+ var useMaximumSize = size.equalTo(CGSize.zero)
317
+ var targetSize: CGSize
318
+
319
+ if useMaximumSize {
320
+ targetSize = PHImageManagerMaximumSize
321
+ imageOptions.resizeMode = .none
322
+ } else {
323
+ targetSize = CGSize(width: size.width * scale, height: size.height * scale)
324
+ imageOptions.resizeMode = .fast
325
+ }
326
+
327
+ var contentMode = PHImageContentMode.aspectFill
328
+ if resizeMode == .contain {
329
+ contentMode = .aspectFit
330
+ }
331
+
332
+ PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: imageOptions) { result, info in
333
+ if let result = result {
334
+ let imageName = assetID.replacingOccurrences(of: "/", with: "_")
335
+ if let imagePath = saveImageIntoCache(result, withName: imageName) {
336
+ completionHandler(imagePath)
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ static func saveImageIntoCache(_ image: UIImage, withName name: String) -> String? {
343
+ if let imageData = image.jpegData(compressionQuality: 1),
344
+ let filePath: String? = Utils.generateCacheFilePath("jpg") {
345
+ do {
346
+ try imageData.write(to: URL(fileURLWithPath: filePath!), options: .atomic)
347
+ return filePath
348
+ } catch {
349
+ return nil
350
+ }
351
+ }
352
+ return nil
353
+ }
354
+ }
@@ -0,0 +1,93 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ enum OutputType: Int {
5
+ case jpg
6
+ case png
7
+ }
8
+
9
+ enum InputType: Int {
10
+ case base64
11
+ case uri
12
+ }
13
+
14
+ enum ReturnableOutputType: Int {
15
+ case rbase64
16
+ case ruri
17
+ }
18
+
19
+ class ImageCompressorOptions: NSObject {
20
+ static func fromDictionary(_ dictionary: [String: Any]?) -> ImageCompressorOptions {
21
+ let options = ImageCompressorOptions()
22
+
23
+ guard let dictionary = dictionary else {
24
+ return options
25
+ }
26
+
27
+ for (key, value) in dictionary {
28
+ switch key {
29
+ case "autoCompress":
30
+ options.autoCompress = (value as? Bool) ?? false
31
+ case "maxWidth":
32
+ options.maxWidth = (value as? Int) ?? 1280
33
+ case "maxHeight":
34
+ options.maxHeight = (value as? Int) ?? 1280
35
+ case "progressDivider":
36
+ options.progressDivider = (value as? Int) ?? 0
37
+ case "quality":
38
+ options.quality = (value as? Float) ?? 0.8
39
+ case "input":
40
+ options.parseInput(value as? String)
41
+ case "output":
42
+ options.parseOutput(value as? String)
43
+ case "returnableOutputType":
44
+ options.parseReturnableOutput(value as? String)
45
+ case "uuid":
46
+ options.uuid = value as? String
47
+ default:
48
+ break
49
+ }
50
+ }
51
+
52
+ return options
53
+ }
54
+
55
+ static func getOutputInString(_ output: OutputType) -> String {
56
+ return (output == .jpg) ? "jpg" : "png"
57
+ }
58
+
59
+ var autoCompress: Bool = true
60
+ var maxWidth: Int = 1280
61
+ var maxHeight: Int = 1280
62
+ var progressDivider: Int = 0
63
+ var quality: Float = 0.8
64
+ var input: InputType = .uri
65
+ var output: OutputType = .jpg
66
+ var returnableOutputType: ReturnableOutputType = .ruri
67
+ var uuid: String?
68
+
69
+ override init() {
70
+ super.init()
71
+ }
72
+
73
+ private func parseInput(_ input: String?) {
74
+ guard let input = input else { return }
75
+
76
+ let inputTranslations: [String: InputType] = ["base64": .base64, "uri": .uri]
77
+ self.input = inputTranslations[input] ?? .uri
78
+ }
79
+
80
+ private func parseReturnableOutput(_ input: String?) {
81
+ guard let input = input else { return }
82
+
83
+ let outputTranslations: [String: ReturnableOutputType] = ["base64": .rbase64, "uri": .ruri]
84
+ self.returnableOutputType = outputTranslations[input] ?? .ruri
85
+ }
86
+
87
+ private func parseOutput(_ output: String?) {
88
+ guard let output = output else { return }
89
+
90
+ let outputTranslations: [String: OutputType] = ["jpg": .jpg, "png": .png]
91
+ self.output = outputTranslations[output] ?? .jpg
92
+ }
93
+ }
@@ -0,0 +1,27 @@
1
+ //
2
+ // ImageMain.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 10/09/2023.
6
+ //
7
+
8
+ import Foundation
9
+ class ImageMain {
10
+ static func image_compress(_ imagePath: String, optionMap: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
11
+ do {
12
+ let options = ImageCompressorOptions.fromDictionary(optionMap as! [String : Any])
13
+ ImageCompressor.getAbsoluteImagePath(imagePath, options: options) { absoluteImagePath in
14
+ if options.autoCompress {
15
+ let result = ImageCompressor.autoCompressHandler(absoluteImagePath, options: options)
16
+ resolve(result)
17
+ } else {
18
+ let result = ImageCompressor.manualCompressHandler(absoluteImagePath, options: options)
19
+ resolve(result)
20
+ }
21
+ MediaCache.removeCompletedImagePath(absoluteImagePath)
22
+ }
23
+ } catch {
24
+ reject(error.localizedDescription, error.localizedDescription, nil)
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,157 @@
1
+ import Foundation
2
+
3
+ class Downloader: NSObject, URLSessionDownloadDelegate {
4
+ private var session: URLSession?
5
+ private var task: URLSessionDownloadTask?
6
+ private var statusCode: NSNumber?
7
+ private var lastProgressEmitTimestamp: TimeInterval = 0
8
+ private var lastProgressValue: NSNumber?
9
+ private var contentLength: NSNumber?
10
+ private var bytesWritten: NSNumber?
11
+ private var resumeData: Data?
12
+ private var toFile: String?
13
+ private var progressDivider: Int=0
14
+
15
+ typealias ProgressCallback = (NSNumber) -> Void
16
+ typealias DownloadCompleteCallback = (String) -> Void
17
+ typealias ErrorCallback = (Error) -> Void
18
+
19
+ var globalProgressCallback: ProgressCallback?
20
+ var globalDownloadCompleteCallback: DownloadCompleteCallback?
21
+ var globalErrorCallback: ErrorCallback?
22
+
23
+ private var fileHandle: FileHandle?
24
+
25
+ func generateCacheFilePath(extension ext: String) -> String {
26
+ let uuid = UUID()
27
+ let imageNameWithoutExtension = uuid.uuidString
28
+ let imageName = imageNameWithoutExtension + "." + ext
29
+ let filePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(imageName).path
30
+ return filePath
31
+ }
32
+
33
+ static func downloadFileAndSaveToCache(_ fileUrl: String, uuid: String,progressDivider: Int, completion: @escaping (String) -> Void) {
34
+ let downloader = Downloader()
35
+
36
+ downloader.downloadFile(fromUrl: fileUrl,progressDivider: progressDivider) { filePath in
37
+ MediaCache.addCompletedImagePath(filePath)
38
+ print("download completed file path: \(filePath)")
39
+ completion(filePath)
40
+ } progressCallback: { progress in
41
+ EventEmitterHandler.emitDownloadProgress(progress, uuid: uuid)
42
+ } errorCallback: { error in
43
+ print("error downloadFile", error)
44
+ }
45
+ }
46
+
47
+ func downloadFile(fromUrl: String, progressDivider: Int, downloadCompleteCallback: @escaping DownloadCompleteCallback, progressCallback: @escaping ProgressCallback, errorCallback: @escaping ErrorCallback) -> String? {
48
+ globalProgressCallback = progressCallback
49
+ globalDownloadCompleteCallback = downloadCompleteCallback
50
+ globalErrorCallback = errorCallback
51
+ self.progressDivider=progressDivider
52
+
53
+ lastProgressEmitTimestamp = 0
54
+ bytesWritten = 0
55
+
56
+ guard let url = URL(string: fromUrl) else {
57
+ let error = NSError(domain: "Downloader", code: NSURLErrorBadURL, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
58
+ globalErrorCallback?(error)
59
+ return nil
60
+ }
61
+
62
+ let fileExtension = url.pathExtension
63
+ toFile = generateCacheFilePath(extension: fileExtension)
64
+
65
+ if FileManager.default.fileExists(atPath: toFile!) {
66
+ fileHandle = FileHandle(forWritingAtPath: toFile!)
67
+
68
+ if fileHandle == nil {
69
+ let error = NSError(domain: "Downloader", code: NSURLErrorFileDoesNotExist, userInfo: [NSLocalizedDescriptionKey: "Failed to write target file at path: \(toFile!)"])
70
+ globalErrorCallback?(error)
71
+ return nil
72
+ } else {
73
+ fileHandle?.closeFile()
74
+ }
75
+ }
76
+
77
+ let config = URLSessionConfiguration.default
78
+ session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
79
+ task = session?.downloadTask(with: url)
80
+ task?.resume()
81
+
82
+ return nil
83
+ }
84
+
85
+ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
86
+ guard let httpResponse = downloadTask.response as? HTTPURLResponse else { return }
87
+
88
+ statusCode = NSNumber(value: httpResponse.statusCode)
89
+ contentLength = NSNumber(value: httpResponse.expectedContentLength)
90
+
91
+ if statusCode?.intValue == 200 {
92
+ self.bytesWritten = NSNumber(value: totalBytesWritten)
93
+
94
+ let doubleBytesWritten = Double(truncating: self.bytesWritten!)
95
+ let doubleContentLength = Double(truncating: contentLength!)
96
+ let doublePercents = doubleBytesWritten / doubleContentLength * 100
97
+ let progress = NSNumber(value: floor(doublePercents))
98
+
99
+ if self.progressDivider==0||progress.intValue % self.progressDivider == 0 {
100
+ if (progress != lastProgressValue) || (bytesWritten == contentLength as! Int64) {
101
+ lastProgressValue = progress
102
+ let progressPercentage = NSNumber(value: Double(truncating: self.bytesWritten!) / Double(truncating: contentLength!))
103
+ globalProgressCallback?(progressPercentage)
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
110
+ guard let httpResponse = downloadTask.response as? HTTPURLResponse else { return }
111
+
112
+ if statusCode == nil {
113
+ statusCode = NSNumber(value: httpResponse.statusCode)
114
+ }
115
+
116
+ let destURL = URL(fileURLWithPath: toFile!)
117
+ let fm = FileManager.default
118
+
119
+ do {
120
+ if statusCode!.intValue >= 200 && statusCode!.intValue < 300 {
121
+ do {
122
+ try fm.removeItem(at: destURL) // Remove file at destination path, if it exists
123
+ }
124
+ catch {
125
+ print("unable to remove item \(error)")
126
+ }
127
+ try fm.moveItem(at: location, to: destURL)
128
+ bytesWritten = NSNumber(value: try fm.attributesOfItem(atPath: toFile!)[.size] as! Int64)
129
+ }
130
+ } catch {
131
+ print("Downloader: Unable to move tempfile to destination. \(error)")
132
+ }
133
+
134
+ // Manually flush and invalidate the session to free up space
135
+ if session != nil {
136
+ session.flush(completionHandler: {
137
+ session.finishTasksAndInvalidate()
138
+ })
139
+ }
140
+
141
+ let fileURL = URL(fileURLWithPath: toFile!)
142
+ globalDownloadCompleteCallback?(fileURL.absoluteString)
143
+ }
144
+
145
+ func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
146
+ if let error = error {
147
+ print("Downloader: didCompleteWithError \(error), \(error.localizedDescription)")
148
+ if error._code != NSURLErrorCancelled {
149
+ resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
150
+
151
+ if resumeData == nil {
152
+ globalErrorCallback?(error)
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,54 @@
1
+ //
2
+ // EventEmitterHandler.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 09/09/2023.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ class EventEmitterHandler {
11
+ static var sharedCompressorObject: Any!
12
+ static var hasListener: Bool=false
13
+
14
+ static func initCompressorInstance(_ object: Any) {
15
+ sharedCompressorObject = object
16
+ }
17
+
18
+
19
+ static func stopObserving() -> Void {
20
+ hasListener = false
21
+ }
22
+
23
+ static func startObserving() -> Void {
24
+ hasListener = true
25
+ }
26
+
27
+ static func emitDownloadProgress(_ progress: NSNumber, uuid: String) {
28
+ var params = [String: Any]()
29
+ var data = [String: Any]()
30
+ params["uuid"] = uuid
31
+ data["progress"] = progress
32
+ params["data"] = data
33
+
34
+ (sharedCompressorObject as AnyObject).sendEvent(withName: "downloadProgress", body: params)
35
+ }
36
+
37
+ static func emitVideoCompressProgress(_ progress: Float, uuid: String) {
38
+ if(self.hasListener){
39
+ (sharedCompressorObject as AnyObject).sendEvent(withName: "videoCompressProgress", body: ["uuid": uuid, "data": ["progress": progress]])
40
+ }
41
+ }
42
+
43
+ static func emituploadProgress(_ uuid: String, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
44
+ if(self.hasListener){
45
+ (sharedCompressorObject as AnyObject).sendEvent(withName: "uploadProgress", body: ["uuid": uuid, "data": ["written": totalBytesSent, "total": totalBytesExpectedToSend]])
46
+ }
47
+ }
48
+
49
+ static func emitBackgroundTaskExpired(_ backgroundTaskId:UIBackgroundTaskIdentifier) {
50
+ if(self.hasListener){
51
+ (sharedCompressorObject as AnyObject).sendEvent(withName: "backgroundTaskExpired", body: ["backgroundTaskId": backgroundTaskId])
52
+ }
53
+ }
54
+ }