react-native-compressor 1.7.2 → 1.8.1

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 (130) hide show
  1. package/README.md +51 -8
  2. package/android/build.gradle +9 -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 +169 -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/Utils/createVideoThumbnail.kt +158 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +63 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +125 -0
  20. package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +86 -0
  21. package/android/src/newarch/CompressorSpec.kt +5 -0
  22. package/android/src/oldarch/CompressorSpec.kt +34 -0
  23. package/ios/Audio/AudioMain.swift +115 -0
  24. package/ios/Compressor-Bridging-Header.h +1 -2
  25. package/ios/Compressor.h +0 -8
  26. package/ios/Compressor.mm +37 -389
  27. package/ios/CompressorManager.swift +107 -0
  28. package/ios/Image/ImageCompressor.swift +354 -0
  29. package/ios/Image/ImageCompressorOptions.swift +93 -0
  30. package/ios/Image/ImageMain.swift +27 -0
  31. package/ios/Utils/CreateVideoThumbnail.swift +111 -0
  32. package/ios/Utils/Downloader.swift +157 -0
  33. package/ios/Utils/EventEmitterHandler.swift +54 -0
  34. package/ios/Utils/MediaCache.swift +48 -0
  35. package/ios/Utils/Uploader.swift +99 -0
  36. package/ios/Utils/Utils.swift +110 -0
  37. package/ios/Video/{VideoCompressor.swift → VideoMain.swift} +167 -146
  38. package/lib/commonjs/Image/index.js +1 -1
  39. package/lib/commonjs/Image/index.js.map +1 -1
  40. package/lib/commonjs/Main.js +1 -8
  41. package/lib/commonjs/Main.js.map +1 -1
  42. package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
  43. package/lib/commonjs/Video/index.js +4 -33
  44. package/lib/commonjs/Video/index.js.map +1 -1
  45. package/lib/commonjs/expo-plugin/compressor.js.map +1 -1
  46. package/lib/commonjs/index.js +25 -6
  47. package/lib/commonjs/index.js.map +1 -1
  48. package/lib/commonjs/utils/Downloader.js +38 -0
  49. package/lib/commonjs/utils/Downloader.js.map +1 -0
  50. package/lib/commonjs/utils/Uploader.js +40 -0
  51. package/lib/commonjs/utils/Uploader.js.map +1 -0
  52. package/lib/commonjs/utils/index.js +47 -1
  53. package/lib/commonjs/utils/index.js.map +1 -1
  54. package/lib/module/Image/index.js +1 -1
  55. package/lib/module/Image/index.js.map +1 -1
  56. package/lib/module/Main.js +1 -7
  57. package/lib/module/Main.js.map +1 -1
  58. package/lib/module/Spec/NativeCompressor.js.map +1 -1
  59. package/lib/module/Video/index.js +5 -33
  60. package/lib/module/Video/index.js.map +1 -1
  61. package/lib/module/expo-plugin/compressor.js.map +1 -1
  62. package/lib/module/index.js +9 -7
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/utils/Downloader.js +31 -0
  65. package/lib/module/utils/Downloader.js.map +1 -0
  66. package/lib/module/utils/Uploader.js +33 -0
  67. package/lib/module/utils/Uploader.js.map +1 -0
  68. package/lib/module/utils/index.js +9 -0
  69. package/lib/module/utils/index.js.map +1 -1
  70. package/lib/typescript/Image/index.d.ts +4 -0
  71. package/lib/typescript/Image/index.d.ts.map +1 -1
  72. package/lib/typescript/Main.d.ts +1 -2
  73. package/lib/typescript/Main.d.ts.map +1 -1
  74. package/lib/typescript/Spec/NativeCompressor.d.ts +16 -2
  75. package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
  76. package/lib/typescript/Video/index.d.ts +4 -28
  77. package/lib/typescript/Video/index.d.ts.map +1 -1
  78. package/lib/typescript/index.d.ts +7 -4
  79. package/lib/typescript/index.d.ts.map +1 -1
  80. package/lib/typescript/utils/Downloader.d.ts +2 -0
  81. package/lib/typescript/utils/Downloader.d.ts.map +1 -0
  82. package/lib/typescript/utils/Uploader.d.ts +28 -0
  83. package/lib/typescript/utils/Uploader.d.ts.map +1 -0
  84. package/lib/typescript/utils/index.d.ts +4 -1
  85. package/lib/typescript/utils/index.d.ts.map +1 -1
  86. package/package.json +6 -4
  87. package/src/Image/index.tsx +5 -1
  88. package/src/Main.tsx +1 -15
  89. package/src/Spec/NativeCompressor.ts +25 -2
  90. package/src/Video/index.tsx +11 -84
  91. package/src/expo-plugin/compressor.ts +1 -1
  92. package/src/index.tsx +12 -3
  93. package/src/utils/Downloader.tsx +38 -0
  94. package/src/utils/Uploader.tsx +78 -0
  95. package/src/utils/index.tsx +15 -0
  96. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.java +0 -605
  97. package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +0 -220
  98. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.java +0 -58
  99. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +0 -257
  100. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +0 -69
  101. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageSize.java +0 -13
  102. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploadHelper.java +0 -39
  103. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploader.java +0 -129
  104. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +0 -65
  105. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.java +0 -210
  106. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +0 -272
  107. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression/AutoVideoCompression.java +0 -87
  108. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.java +0 -182
  109. package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +0 -112
  110. package/android/src/newarch/CompressorSpec.java +0 -9
  111. package/android/src/newarch/VideoCompressorSpec.java +0 -9
  112. package/android/src/oldarch/CompressorSpec.java +0 -34
  113. package/android/src/oldarch/VideoCompressorSpec.java +0 -26
  114. package/ios/Image/ImageCompressor.h +0 -22
  115. package/ios/Image/ImageCompressor.m +0 -540
  116. package/ios/Image/ImageCompressorOptions.h +0 -28
  117. package/ios/Image/ImageCompressorOptions.m +0 -97
  118. package/ios/Utils/Downloader.h +0 -16
  119. package/ios/Utils/Downloader.m +0 -150
  120. package/ios/Utils/MediaCache.h +0 -11
  121. package/ios/Utils/MediaCache.m +0 -51
  122. package/ios/Video/VideoCompressor.h +0 -5
  123. package/ios/Video/VideoCompressor.m +0 -1
  124. package/lib/commonjs/Spec/NativeVideoCompressor.js +0 -10
  125. package/lib/commonjs/Spec/NativeVideoCompressor.js.map +0 -1
  126. package/lib/module/Spec/NativeVideoCompressor.js +0 -3
  127. package/lib/module/Spec/NativeVideoCompressor.js.map +0 -1
  128. package/lib/typescript/Spec/NativeVideoCompressor.d.ts +0 -13
  129. package/lib/typescript/Spec/NativeVideoCompressor.d.ts.map +0 -1
  130. 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,111 @@
1
+ //
2
+ // CreateVideoThumbnail.swift
3
+ // react-native-compressor
4
+ //
5
+ // Created by Numan on 13/09/2023.
6
+ //
7
+
8
+ import Foundation
9
+ import AVFoundation
10
+ import UIKit
11
+
12
+ class CreateVideoThumbnail: NSObject {
13
+
14
+ func create(_ fileUrl:String, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
15
+ let headers = options["headers"] as? [String: Any] ?? [:]
16
+ let format = "jpeg"
17
+
18
+ do {
19
+ // Prepare cache folder
20
+ var tempDirectory = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last ?? ""
21
+ tempDirectory = (tempDirectory as NSString).appendingPathComponent("thumbnails/")
22
+ // Create thumbnail directory if not exists
23
+ try FileManager.default.createDirectory(atPath: tempDirectory, withIntermediateDirectories: true, attributes: nil)
24
+ let fileName = "thumb-\(ProcessInfo.processInfo.globallyUniqueString).\(format)"
25
+ let fullPath = (tempDirectory as NSString).appendingPathComponent(fileName)
26
+
27
+ if FileManager.default.fileExists(atPath: fullPath) {
28
+ if let imageData = try? Data(contentsOf: URL(fileURLWithPath: fullPath)) {
29
+ if let thumbnail = UIImage(data: imageData) {
30
+ resolve([
31
+ "path": fullPath,
32
+ "size": Float(imageData.count),
33
+ "mime": "image/\(format)",
34
+ "width": Float(thumbnail.size.width),
35
+ "height": Float(thumbnail.size.height)
36
+ ])
37
+ return
38
+ }
39
+ }
40
+ }
41
+
42
+ var vidURL: URL?
43
+ let fileUrlLowerCase = fileUrl.lowercased()
44
+
45
+ if fileUrlLowerCase.hasPrefix("http://") || fileUrlLowerCase.hasPrefix("https://") || fileUrlLowerCase.hasPrefix("file://") {
46
+ vidURL = URL(string: fileUrl)
47
+ } else {
48
+ // Consider it's file url path
49
+ vidURL = URL(fileURLWithPath: fileUrl)
50
+ }
51
+
52
+ let asset = AVURLAsset(url: vidURL!, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
53
+ generateThumbImage(asset: asset, atTime: 0, completion: { thumbnail in
54
+ // Generate thumbnail
55
+ var data: Data? = thumbnail.jpegData(compressionQuality: 1.0)
56
+
57
+ if let data = data {
58
+ try? data.write(to: URL(fileURLWithPath: fullPath))
59
+ resolve([
60
+ "path": fullPath,
61
+ "size": Float(data.count),
62
+ "mime": "image/\(format)",
63
+ "width": Float(thumbnail.size.width),
64
+ "height": Float(thumbnail.size.height)
65
+ ] as [String : Any])
66
+ }
67
+ }, failure: { error in
68
+ reject(error._domain, error.localizedDescription, nil)
69
+ })
70
+
71
+ } catch {
72
+ reject("Error", error.localizedDescription, nil)
73
+ }
74
+ }
75
+
76
+ static func cleanCacheDir(cacheDir: String, resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
77
+ let cachePathDir: String = cacheDir.isEmpty ? "thumbnails/" : cacheDir
78
+ var cacheDirectoryPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).last ?? ""
79
+ cacheDirectoryPath = (cacheDirectoryPath as NSString).appendingPathComponent(cachePathDir)
80
+
81
+ let fm = FileManager.default
82
+ do {
83
+ let files = try fm.contentsOfDirectory(atPath: cacheDirectoryPath)
84
+ for file in files {
85
+ let filePath = (cacheDirectoryPath as NSString).appendingPathComponent(file)
86
+ try fm.removeItem(atPath: filePath)
87
+ }
88
+ resolve("done")
89
+ } catch {
90
+ // Handle error if necessary
91
+ reject("Error", error.localizedDescription, nil)
92
+ }
93
+ }
94
+
95
+ func generateThumbImage(asset: AVURLAsset, atTime timeStamp: Int, completion: @escaping (UIImage) -> Void, failure: @escaping (Error) -> Void) {
96
+ let generator = AVAssetImageGenerator(asset: asset)
97
+ generator.appliesPreferredTrackTransform = true
98
+ generator.maximumSize = CGSize(width: 512, height: 512)
99
+ generator.requestedTimeToleranceBefore = CMTimeMake(value: 0, timescale: 1000)
100
+ generator.requestedTimeToleranceAfter = CMTimeMake(value: 0, timescale: 1000)
101
+ let time = CMTimeMake(value: Int64(timeStamp), timescale: 1000)
102
+ generator.generateCGImagesAsynchronously(forTimes: [NSValue(time: time)]) { _, image, _, result, error in
103
+ if result == .succeeded, let cgImage = image {
104
+ let thumbnail = UIImage(cgImage: cgImage)
105
+ completion(thumbnail)
106
+ } else if let error = error {
107
+ failure(error)
108
+ }
109
+ }
110
+ }
111
+ }