react-native-compressor 1.7.0 → 1.7.2
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.
- package/README.md +42 -20
- package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +31 -22
- package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +28 -16
- package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +4 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +65 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +161 -2
- package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +3 -16
- package/android/src/oldarch/CompressorSpec.java +13 -7
- package/ios/Compressor-Bridging-Header.h +1 -0
- package/ios/Compressor.h +3 -2
- package/ios/Compressor.mm +56 -24
- package/ios/Image/ImageCompressor.h +12 -7
- package/ios/Image/ImageCompressor.m +164 -67
- package/ios/Image/ImageCompressorOptions.h +1 -0
- package/ios/Image/ImageCompressorOptions.m +5 -1
- package/ios/Utils/Downloader.h +16 -0
- package/ios/Utils/Downloader.m +150 -0
- package/ios/Utils/MediaCache.h +11 -0
- package/ios/Utils/MediaCache.m +51 -0
- package/ios/Video/VideoCompressor.swift +13 -4
- package/lib/commonjs/Image/index.js +25 -3
- package/lib/commonjs/Image/index.js.map +1 -1
- package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
- package/lib/commonjs/Video/index.js +14 -1
- package/lib/commonjs/Video/index.js.map +1 -1
- package/lib/module/Image/index.js +25 -3
- package/lib/module/Image/index.js.map +1 -1
- package/lib/module/Spec/NativeCompressor.js.map +1 -1
- package/lib/module/Video/index.js +14 -1
- package/lib/module/Video/index.js.map +1 -1
- package/lib/typescript/Image/index.d.ts +4 -0
- package/lib/typescript/Image/index.d.ts.map +1 -1
- package/lib/typescript/Spec/NativeCompressor.d.ts +2 -0
- package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
- package/lib/typescript/Video/index.d.ts +1 -0
- package/lib/typescript/Video/index.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/Image/index.tsx +36 -3
- package/src/Spec/NativeCompressor.ts +2 -0
- package/src/Video/index.tsx +23 -1
package/README.md
CHANGED
|
@@ -5,18 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
<div align="center">
|
|
7
7
|
|
|
8
|
-
[](https://discord.gg/
|
|
8
|
+
[](https://discord.gg/6Wx8Em8KAN)
|
|
9
9
|
[](https://github.com/numandev1/react-native-compressor/stargazers)
|
|
10
10
|
[](https://www.npmjs.com/package/react-native-compressor)
|
|
11
11
|

|
|
12
12
|

|
|
13
|
+
[](http://makeapullrequest.com)
|
|
13
14
|
|
|
14
15
|
</div>
|
|
15
16
|
|
|
16
|
-
**REACT-NATIVE-COMPRESSOR** is a react-native package, which
|
|
17
|
+
**REACT-NATIVE-COMPRESSOR** is a react-native package, which helps us to Compress `Image`, `Video`, and `Audio` before uploading, same like **Whatsapp** without knowing the compression `algorithm`
|
|
17
18
|
|
|
18
19
|
<div align="center">
|
|
19
|
-
<img height="
|
|
20
|
+
<img height="90" src="/media/whatsapp_logo.png"/>
|
|
21
|
+
|
|
22
|
+
<img height="90" src="/media/compress_media.png"/>
|
|
20
23
|
<h2 align="center">🗜️Compress Image, Video, and Audio same like Whatsapp</h2>
|
|
21
24
|
</div>
|
|
22
25
|
|
|
@@ -81,8 +84,8 @@ yarn add react-native-compressor
|
|
|
81
84
|
```
|
|
82
85
|
|
|
83
86
|
### [New Architecture (Turbo Module)](https://reactnative.dev/docs/new-architecture-intro) Supported
|
|
84
|
-
you can give feedback on [Discord channel](https://discord.gg/dtYzk8sp)
|
|
85
87
|
|
|
88
|
+
you can give feedback on [Discord channel](https://discord.gg/dtYzk8sp)
|
|
86
89
|
|
|
87
90
|
### Managed Expo
|
|
88
91
|
|
|
@@ -158,8 +161,12 @@ react-native link react-native-compressor
|
|
|
158
161
|
```js
|
|
159
162
|
import { Image } from 'react-native-compressor';
|
|
160
163
|
|
|
161
|
-
const result = await Image.compress('file://path_of_file/image.jpg'
|
|
162
|
-
|
|
164
|
+
const result = await Image.compress('file://path_of_file/image.jpg');
|
|
165
|
+
// OR
|
|
166
|
+
const result = await Image.compress('https://path_of_file/image.jpg', {
|
|
167
|
+
downloadProgress: (progress) => {
|
|
168
|
+
console.log('downloadProgress: ', progress);
|
|
169
|
+
},
|
|
163
170
|
});
|
|
164
171
|
```
|
|
165
172
|
|
|
@@ -171,6 +178,7 @@ const result = await Image.compress('file://path_of_file/image.jpg', {
|
|
|
171
178
|
import { Image } from 'react-native-compressor';
|
|
172
179
|
|
|
173
180
|
const result = await Image.compress('file://path_of_file/image.jpg', {
|
|
181
|
+
compressionMethod: 'manual',
|
|
174
182
|
maxWidth: 1000,
|
|
175
183
|
quality: 0.8,
|
|
176
184
|
});
|
|
@@ -185,15 +193,23 @@ import { Video } from 'react-native-compressor';
|
|
|
185
193
|
|
|
186
194
|
const result = await Video.compress(
|
|
187
195
|
'file://path_of_file/BigBuckBunny.mp4',
|
|
196
|
+
{},
|
|
197
|
+
(progress) => {
|
|
198
|
+
console.log('Compression Progress: ', progress);
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
//OR
|
|
203
|
+
|
|
204
|
+
const result = await Video.compress(
|
|
205
|
+
'https://example.com/video.mp4',
|
|
188
206
|
{
|
|
189
|
-
|
|
207
|
+
downloadProgress: (progress) => {
|
|
208
|
+
console.log('downloadProgress: ', progress);
|
|
209
|
+
},
|
|
190
210
|
},
|
|
191
211
|
(progress) => {
|
|
192
|
-
|
|
193
|
-
console.log('Compression Progress: ', progress);
|
|
194
|
-
} else {
|
|
195
|
-
setCompressingProgress(progress);
|
|
196
|
-
}
|
|
212
|
+
console.log('Compression Progress: ', progress);
|
|
197
213
|
}
|
|
198
214
|
);
|
|
199
215
|
```
|
|
@@ -207,13 +223,11 @@ import { Video } from 'react-native-compressor';
|
|
|
207
223
|
|
|
208
224
|
const result = await Video.compress(
|
|
209
225
|
'file://path_of_file/BigBuckBunny.mp4',
|
|
210
|
-
{
|
|
226
|
+
{
|
|
227
|
+
compressionMethod: 'manual',
|
|
228
|
+
},
|
|
211
229
|
(progress) => {
|
|
212
|
-
|
|
213
|
-
console.log('Compression Progress: ', progress);
|
|
214
|
-
} else {
|
|
215
|
-
setCompressingProgress(progress);
|
|
216
|
-
}
|
|
230
|
+
console.log('Compression Progress: ', progress);
|
|
217
231
|
}
|
|
218
232
|
);
|
|
219
233
|
```
|
|
@@ -286,10 +300,14 @@ const uploadResult = await backgroundUpload(
|
|
|
286
300
|
|
|
287
301
|
### CompressorOptions
|
|
288
302
|
|
|
289
|
-
- ###### `compressionMethod: compressionMethod` (default: "
|
|
303
|
+
- ###### `compressionMethod: compressionMethod` (default: "auto")
|
|
290
304
|
|
|
291
305
|
if you want to compress images like **whatsapp** then make this prop `auto`. Can be either `manual` or `auto`, defines the Compression Method.
|
|
292
306
|
|
|
307
|
+
- ##### `downloadProgress?: (progress: number) => void;`
|
|
308
|
+
|
|
309
|
+
it is callback, only trigger when we pass image url from server
|
|
310
|
+
|
|
293
311
|
- ###### `maxWidth: number` (default: 1280)
|
|
294
312
|
|
|
295
313
|
The maximum width boundary used as the main boundary in resizing a landscape image.
|
|
@@ -326,11 +344,15 @@ const uploadResult = await backgroundUpload(
|
|
|
326
344
|
|
|
327
345
|
if you want to compress videos like **whatsapp** then make this prop `auto`. Can be either `manual` or `auto`, defines the Compression Method.
|
|
328
346
|
|
|
347
|
+
- ##### `downloadProgress?: (progress: number) => void;`
|
|
348
|
+
|
|
349
|
+
it is callback, only trigger when we pass image url from server
|
|
350
|
+
|
|
329
351
|
- ###### `maxSize: number` (default: 640)
|
|
330
352
|
|
|
331
353
|
The maximum size can be height in case of portrait video or can be width in case of landscape video.
|
|
332
354
|
|
|
333
|
-
- ###### `bitrate:
|
|
355
|
+
- ###### `bitrate: number`
|
|
334
356
|
|
|
335
357
|
bitrate of video which reduce or increase video size. if compressionMethod will auto then this prop will not work
|
|
336
358
|
|
|
@@ -6,17 +6,15 @@ import com.facebook.react.bridge.Promise;
|
|
|
6
6
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
7
7
|
import com.facebook.react.bridge.ReactMethod;
|
|
8
8
|
|
|
9
|
-
import androidx.annotation.NonNull;
|
|
10
|
-
import androidx.annotation.Nullable;
|
|
11
9
|
import androidx.annotation.RequiresApi;
|
|
12
10
|
|
|
13
11
|
import com.facebook.react.bridge.Arguments;
|
|
14
12
|
import com.facebook.react.bridge.ReactContext;
|
|
15
13
|
import com.facebook.react.bridge.ReadableMap;
|
|
16
14
|
import com.facebook.react.bridge.WritableMap;
|
|
17
|
-
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
18
15
|
import com.reactnativecompressor.Image.ImageCompressor;
|
|
19
16
|
import com.reactnativecompressor.Image.utils.ImageCompressorOptions;
|
|
17
|
+
import com.reactnativecompressor.Utils.MediaCache;
|
|
20
18
|
import com.reactnativecompressor.Utils.Utils;
|
|
21
19
|
import com.reactnativecompressor.Video.VideoCompressorHelper;
|
|
22
20
|
import com.reactnativecompressor.CompressorSpec;
|
|
@@ -40,7 +38,7 @@ public class CompressorModule extends CompressorSpec {
|
|
|
40
38
|
public static final String NAME = "Compressor";
|
|
41
39
|
private final ReactApplicationContext reactContext;
|
|
42
40
|
|
|
43
|
-
CompressorModule(ReactApplicationContext context) {
|
|
41
|
+
public CompressorModule(ReactApplicationContext context) {
|
|
44
42
|
super(context);
|
|
45
43
|
this.reactContext = context;
|
|
46
44
|
}
|
|
@@ -51,15 +49,6 @@ public class CompressorModule extends CompressorSpec {
|
|
|
51
49
|
return NAME;
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
private void sendEvent(ReactContext reactContext,
|
|
56
|
-
String eventName,
|
|
57
|
-
@Nullable WritableMap params) {
|
|
58
|
-
reactContext
|
|
59
|
-
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
60
|
-
.emit(eventName, params);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
52
|
//Image
|
|
64
53
|
@ReactMethod
|
|
65
54
|
public void image_compress(
|
|
@@ -67,8 +56,9 @@ public class CompressorModule extends CompressorSpec {
|
|
|
67
56
|
ReadableMap optionMap,
|
|
68
57
|
Promise promise) {
|
|
69
58
|
try {
|
|
70
|
-
imagePath=Utils.getRealPath(imagePath,reactContext);
|
|
71
59
|
final ImageCompressorOptions options = ImageCompressorOptions.fromMap(optionMap);
|
|
60
|
+
imagePath=Utils.getRealPath(imagePath,reactContext,options.uuid);
|
|
61
|
+
|
|
72
62
|
|
|
73
63
|
if(options.compressionMethod==ImageCompressorOptions.CompressionMethod.auto)
|
|
74
64
|
{
|
|
@@ -80,6 +70,7 @@ public class CompressorModule extends CompressorSpec {
|
|
|
80
70
|
String returnableResult=ImageCompressor.manualCompressImage(imagePath,options,reactContext);
|
|
81
71
|
promise.resolve(returnableResult);
|
|
82
72
|
}
|
|
73
|
+
MediaCache.removeCompletedImagePath(imagePath);
|
|
83
74
|
} catch (Exception ex) {
|
|
84
75
|
promise.reject(ex);
|
|
85
76
|
}
|
|
@@ -163,17 +154,34 @@ public class CompressorModule extends CompressorSpec {
|
|
|
163
154
|
@ReactMethod
|
|
164
155
|
public void getFileSize(String filePath, Promise promise)
|
|
165
156
|
{
|
|
166
|
-
filePath
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
157
|
+
if(filePath.startsWith("http://")||filePath.startsWith("https://"))
|
|
158
|
+
{
|
|
159
|
+
promise.resolve(Utils.getFileSizeFromURL(filePath));
|
|
160
|
+
}
|
|
161
|
+
else
|
|
162
|
+
{
|
|
163
|
+
filePath=Utils.getRealPath(filePath,reactContext);
|
|
164
|
+
Uri uri= Uri.parse(filePath);
|
|
165
|
+
ContentResolver contentResolver = reactContext.getContentResolver();
|
|
166
|
+
long fileSize = getLength(uri, contentResolver);
|
|
167
|
+
if (fileSize >= 0) {
|
|
168
|
+
promise.resolve(String.valueOf(fileSize));
|
|
169
|
+
} else {
|
|
170
|
+
promise.resolve("");
|
|
171
|
+
}
|
|
174
172
|
}
|
|
175
173
|
}
|
|
176
174
|
|
|
175
|
+
@ReactMethod
|
|
176
|
+
public void addListener(String eventName) {
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@ReactMethod
|
|
181
|
+
public void removeListeners(double count) {
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
177
185
|
public static long getLength(Uri uri, ContentResolver contentResolver) {
|
|
178
186
|
AssetFileDescriptor assetFileDescriptor = null;
|
|
179
187
|
try {
|
|
@@ -208,4 +216,5 @@ public class CompressorModule extends CompressorSpec {
|
|
|
208
216
|
return -1L;
|
|
209
217
|
}
|
|
210
218
|
}
|
|
219
|
+
|
|
211
220
|
}
|
|
@@ -201,22 +201,7 @@ public class ImageCompressor {
|
|
|
201
201
|
bmp.recycle();
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
exif = new ExifInterface(imagePath);
|
|
207
|
-
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
|
|
208
|
-
Matrix matrix = new Matrix();
|
|
209
|
-
if (orientation == 6) {
|
|
210
|
-
matrix.postRotate(90);
|
|
211
|
-
} else if (orientation == 3) {
|
|
212
|
-
matrix.postRotate(180);
|
|
213
|
-
} else if (orientation == 8) {
|
|
214
|
-
matrix.postRotate(270);
|
|
215
|
-
}
|
|
216
|
-
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
|
|
217
|
-
} catch (IOException e) {
|
|
218
|
-
e.printStackTrace();
|
|
219
|
-
}
|
|
204
|
+
scaledBitmap = correctImageOrientation(scaledBitmap, imagePath);
|
|
220
205
|
|
|
221
206
|
final ByteArrayOutputStream imageDataByteArrayOutputStream = ImageCompressor.compress(scaledBitmap, compressorOptions.output, compressorOptions.quality);
|
|
222
207
|
String returnableResult = ImageCompressor.encodeImage(imageDataByteArrayOutputStream,isBase64,compressorOptions.output.toString(),reactContext);
|
|
@@ -242,4 +227,31 @@ public class ImageCompressor {
|
|
|
242
227
|
|
|
243
228
|
return inSampleSize;
|
|
244
229
|
}
|
|
230
|
+
|
|
231
|
+
public static Bitmap correctImageOrientation(Bitmap bitmap, String imagePath) {
|
|
232
|
+
try {
|
|
233
|
+
ExifInterface exif = new ExifInterface(imagePath);
|
|
234
|
+
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
|
235
|
+
Matrix matrix = new Matrix();
|
|
236
|
+
|
|
237
|
+
switch (orientation) {
|
|
238
|
+
case ExifInterface.ORIENTATION_ROTATE_90:
|
|
239
|
+
matrix.postRotate(90);
|
|
240
|
+
break;
|
|
241
|
+
case ExifInterface.ORIENTATION_ROTATE_180:
|
|
242
|
+
matrix.postRotate(180);
|
|
243
|
+
break;
|
|
244
|
+
case ExifInterface.ORIENTATION_ROTATE_270:
|
|
245
|
+
matrix.postRotate(270);
|
|
246
|
+
break;
|
|
247
|
+
default:
|
|
248
|
+
return bitmap; // No rotation needed
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
|
252
|
+
} catch (IOException e) {
|
|
253
|
+
e.printStackTrace();
|
|
254
|
+
return bitmap; // Return original bitmap if an error occurs
|
|
255
|
+
}
|
|
256
|
+
}
|
|
245
257
|
}
|
package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java
CHANGED
|
@@ -33,6 +33,9 @@ public class ImageCompressorOptions {
|
|
|
33
33
|
case "returnableOutputType":
|
|
34
34
|
options.returnableOutputType = ReturnableOutputType.valueOf(map.getString(key));
|
|
35
35
|
break;
|
|
36
|
+
case "uuid":
|
|
37
|
+
options.uuid = map.getString(key);
|
|
38
|
+
break;
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -61,5 +64,6 @@ public class ImageCompressorOptions {
|
|
|
61
64
|
public float quality = 0.8f;
|
|
62
65
|
public InputType input = InputType.uri;
|
|
63
66
|
public OutputType output = OutputType.jpg;
|
|
67
|
+
public String uuid = "";
|
|
64
68
|
public ReturnableOutputType returnableOutputType = ReturnableOutputType.uri;
|
|
65
69
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package com.reactnativecompressor.Utils;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
|
|
5
|
+
import java.io.File;
|
|
6
|
+
import java.util.ArrayList;
|
|
7
|
+
import java.util.List;
|
|
8
|
+
|
|
9
|
+
public class MediaCache {
|
|
10
|
+
|
|
11
|
+
private static final String TAG = "MediaCache";
|
|
12
|
+
private static List<String> completedImagePaths = new ArrayList<>();
|
|
13
|
+
|
|
14
|
+
public static void addCompletedImagePath(String imagePath) {
|
|
15
|
+
if (imagePath != null) {
|
|
16
|
+
// Your code to add the imagePath to a list or perform other actions if needed.
|
|
17
|
+
completedImagePaths.add(imagePath);
|
|
18
|
+
Log.d(TAG, "Successfully added image path: " + imagePath);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static void removeCompletedImagePath(String imagePath) {
|
|
23
|
+
if (imagePath != null) {
|
|
24
|
+
if (completedImagePaths.contains(imagePath)) {
|
|
25
|
+
// Image path exists in the list, so remove it
|
|
26
|
+
completedImagePaths.remove(imagePath);
|
|
27
|
+
|
|
28
|
+
if (imagePath.startsWith("file://")) {
|
|
29
|
+
imagePath = imagePath.substring(7); // Remove "file://"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Remove the image file
|
|
33
|
+
File file = new File(imagePath);
|
|
34
|
+
if (file.exists()) {
|
|
35
|
+
if (file.delete()) {
|
|
36
|
+
Log.d(TAG, "Successfully deleted image file: " + imagePath);
|
|
37
|
+
} else {
|
|
38
|
+
Log.d(TAG, "Failed to delete image file: " + imagePath);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
Log.d(TAG, "Image file not found: " + imagePath);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
Log.d(TAG, "Image path not found in the completedImagePaths list: " + imagePath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static void cleanupCache() {
|
|
50
|
+
// Iterate through the list of completed image paths and delete the corresponding files
|
|
51
|
+
for (String imagePath : completedImagePaths) {
|
|
52
|
+
File file = new File(imagePath);
|
|
53
|
+
if (file.exists()) {
|
|
54
|
+
if (file.delete()) {
|
|
55
|
+
Log.d(TAG, "Successfully deleted image file during cache cleanup: " + imagePath);
|
|
56
|
+
} else {
|
|
57
|
+
Log.d(TAG, "Failed to delete image file during cache cleanup: " + imagePath);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
Log.d(TAG, "Image file not found during cache cleanup: " + imagePath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
completedImagePaths.clear();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -13,10 +13,26 @@ import com.facebook.react.bridge.WritableMap;
|
|
|
13
13
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
14
14
|
import numan.dev.videocompressor.VideoCompressTask;
|
|
15
15
|
import numan.dev.videocompressor.VideoCompressor;
|
|
16
|
+
|
|
17
|
+
import okhttp3.Call;
|
|
18
|
+
import okhttp3.Callback;
|
|
19
|
+
import okhttp3.OkHttpClient;
|
|
20
|
+
import okhttp3.Request;
|
|
21
|
+
import okhttp3.Response;
|
|
22
|
+
import okhttp3.ResponseBody;
|
|
23
|
+
|
|
24
|
+
import java.io.BufferedInputStream;
|
|
16
25
|
import java.io.File;
|
|
26
|
+
import java.io.FileOutputStream;
|
|
27
|
+
import java.io.IOException;
|
|
28
|
+
import java.net.HttpURLConnection;
|
|
29
|
+
import java.net.URL;
|
|
30
|
+
import java.net.URLConnection;
|
|
17
31
|
import java.util.HashMap;
|
|
18
32
|
import java.util.Map;
|
|
19
33
|
import java.util.UUID;
|
|
34
|
+
import java.util.concurrent.Semaphore;
|
|
35
|
+
import java.util.concurrent.atomic.AtomicReference;
|
|
20
36
|
|
|
21
37
|
public class Utils {
|
|
22
38
|
static int videoCompressionThreshold=10;
|
|
@@ -41,8 +57,10 @@ public class Utils {
|
|
|
41
57
|
}
|
|
42
58
|
@Override
|
|
43
59
|
public void onFinish(boolean result) {
|
|
60
|
+
String fileUrl="file://"+destinationPath;
|
|
44
61
|
//convert finish,result(true is success,false is fail)
|
|
45
|
-
promise.resolve(
|
|
62
|
+
promise.resolve(fileUrl);
|
|
63
|
+
MediaCache.removeCompletedImagePath(fileUrl);
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
@Override
|
|
@@ -97,7 +115,25 @@ public class Utils {
|
|
|
97
115
|
.emit(eventName, params);
|
|
98
116
|
}
|
|
99
117
|
|
|
100
|
-
public static
|
|
118
|
+
public static int getFileSizeFromURL(String urlString) {
|
|
119
|
+
URL url;
|
|
120
|
+
HttpURLConnection conn = null;
|
|
121
|
+
try {
|
|
122
|
+
url = new URL(urlString);
|
|
123
|
+
conn = (HttpURLConnection) url.openConnection();
|
|
124
|
+
conn.setRequestMethod("HEAD");
|
|
125
|
+
conn.getInputStream();
|
|
126
|
+
return conn.getContentLength();
|
|
127
|
+
} catch (IOException e) {
|
|
128
|
+
throw new RuntimeException(e);
|
|
129
|
+
} finally {
|
|
130
|
+
if (conn != null) {
|
|
131
|
+
conn.disconnect();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public static String getRealPath(String fileUrl,ReactApplicationContext reactContext, String... args){
|
|
101
137
|
if(fileUrl.startsWith("content://"))
|
|
102
138
|
{
|
|
103
139
|
try {
|
|
@@ -108,6 +144,129 @@ public class Utils {
|
|
|
108
144
|
Log.d(TAG, " Please see this issue: https://github.com/numandev1/react-native-compressor/issues/25");
|
|
109
145
|
}
|
|
110
146
|
}
|
|
147
|
+
else if(fileUrl.startsWith("http://")||fileUrl.startsWith("https://"))
|
|
148
|
+
{
|
|
149
|
+
String uuid = (args.length > 0) ? args[0] : "";
|
|
150
|
+
fileUrl=downloadMediaWithProgress(fileUrl,reactContext, uuid);
|
|
151
|
+
Log.d(TAG, "getRealPath: "+fileUrl);
|
|
152
|
+
}
|
|
153
|
+
|
|
111
154
|
return fileUrl;
|
|
112
155
|
}
|
|
156
|
+
|
|
157
|
+
public static String downloadMediaWithProgress(String mediaUrl, ReactApplicationContext reactContext, String uuid) {
|
|
158
|
+
downloadCompression[0]=0;
|
|
159
|
+
OkHttpClient client = new OkHttpClient();
|
|
160
|
+
|
|
161
|
+
Request request = new Request.Builder()
|
|
162
|
+
.url(mediaUrl)
|
|
163
|
+
.build();
|
|
164
|
+
|
|
165
|
+
final Semaphore semaphore = new Semaphore(0); // Semaphore to wait for the download to complete
|
|
166
|
+
final AtomicReference<String> filePathRef = new AtomicReference<>(null); // To store the file path
|
|
167
|
+
|
|
168
|
+
// Perform the request asynchronously
|
|
169
|
+
client.newCall(request).enqueue(new Callback() {
|
|
170
|
+
@Override
|
|
171
|
+
public void onResponse(Call call, Response response) throws IOException {
|
|
172
|
+
if (response.isSuccessful()) {
|
|
173
|
+
ResponseBody responseBody = response.body();
|
|
174
|
+
if (responseBody != null) {
|
|
175
|
+
String fileExtension = "unknown"; // Default extension
|
|
176
|
+
|
|
177
|
+
// Detect the file extension based on the Content-Type header
|
|
178
|
+
String contentType = response.header("Content-Type");
|
|
179
|
+
if (contentType != null) {
|
|
180
|
+
if (contentType.equals("image/jpeg")) {
|
|
181
|
+
fileExtension = "jpg";
|
|
182
|
+
} else if (contentType.equals("image/png")) {
|
|
183
|
+
fileExtension = "png";
|
|
184
|
+
} else if (contentType.equals("video/mp4")) {
|
|
185
|
+
fileExtension = "mp4";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
File cacheDir = reactContext.getCacheDir();
|
|
190
|
+
String randomFileName = UUID.randomUUID().toString() + "." + fileExtension;
|
|
191
|
+
File mediaFile = new File(cacheDir, randomFileName);
|
|
192
|
+
|
|
193
|
+
try (FileOutputStream fos = new FileOutputStream(mediaFile)) {
|
|
194
|
+
BufferedInputStream inputStream = new BufferedInputStream(responseBody.byteStream());
|
|
195
|
+
byte[] buffer = new byte[4096];
|
|
196
|
+
int bytesRead;
|
|
197
|
+
long totalBytesRead = 0;
|
|
198
|
+
long totalBytes = responseBody.contentLength();
|
|
199
|
+
|
|
200
|
+
if (totalBytes <= 0) {
|
|
201
|
+
totalBytes = 31457280;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
Log.d(TAG, totalBytesRead+" totalBytesRead "+totalBytes);
|
|
205
|
+
Log.d(TAG, response.toString()+" responseBody "+responseBody.toString());
|
|
206
|
+
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
207
|
+
fos.write(buffer, 0, bytesRead);
|
|
208
|
+
totalBytesRead += bytesRead;
|
|
209
|
+
double progressRatio = (double) totalBytesRead / totalBytes;
|
|
210
|
+
int progress = (int) (progressRatio * 100);
|
|
211
|
+
sendProgressUpdate(progress, uuid, reactContext);
|
|
212
|
+
}
|
|
213
|
+
fos.flush();
|
|
214
|
+
String filePath="file://" + mediaFile.getAbsolutePath();
|
|
215
|
+
MediaCache.addCompletedImagePath(filePath);
|
|
216
|
+
filePathRef.set(filePath); // Set the file path
|
|
217
|
+
} catch (IOException e) {
|
|
218
|
+
e.printStackTrace();
|
|
219
|
+
sendErrorResult(e.getMessage(), uuid, reactContext);
|
|
220
|
+
} finally {
|
|
221
|
+
semaphore.release(); // Release the semaphore when download is complete
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
sendErrorResult("Failed to download media: " + response.message(), uuid, reactContext);
|
|
226
|
+
semaphore.release(); // Release the semaphore in case of error
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Override
|
|
231
|
+
public void onFailure(Call call, IOException e) {
|
|
232
|
+
e.printStackTrace();
|
|
233
|
+
sendErrorResult(e.getMessage(), uuid, reactContext);
|
|
234
|
+
semaphore.release(); // Release the semaphore in case of failure
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
semaphore.acquire(); // Wait for the download to complete
|
|
240
|
+
} catch (InterruptedException e) {
|
|
241
|
+
e.printStackTrace();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return filePathRef.get(); // Return the file path
|
|
245
|
+
}
|
|
246
|
+
static final int[] downloadCompression = {0};
|
|
247
|
+
private static void sendProgressUpdate(int progress, String uuid, ReactApplicationContext reactContext) {
|
|
248
|
+
int roundProgress=Math.round(progress);
|
|
249
|
+
if(roundProgress%videoCompressionThreshold==0&&roundProgress> downloadCompression[0]) {
|
|
250
|
+
WritableMap params = Arguments.createMap();
|
|
251
|
+
WritableMap data = Arguments.createMap();
|
|
252
|
+
params.putString("uuid", uuid);
|
|
253
|
+
data.putDouble("progress", progress / 100.0);
|
|
254
|
+
params.putMap("data", data);
|
|
255
|
+
sendEvent(reactContext, "downloadProgress", params);
|
|
256
|
+
Log.d(TAG, "downloadProgress: " + (progress / 100.0));
|
|
257
|
+
downloadCompression[0] =roundProgress;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private static void sendErrorResult(String error, String uuid, ReactApplicationContext reactContext) {
|
|
262
|
+
WritableMap params = Arguments.createMap();
|
|
263
|
+
WritableMap data = Arguments.createMap();
|
|
264
|
+
params.putString("uuid", uuid);
|
|
265
|
+
params.putString("error", error);
|
|
266
|
+
params.putMap("data", data);
|
|
267
|
+
Log.d(TAG, "videoDownloadError: "+error);
|
|
268
|
+
sendEvent(reactContext, "videoDownloadError", params);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
|
|
113
272
|
}
|
|
@@ -8,13 +8,9 @@ import androidx.annotation.Nullable;
|
|
|
8
8
|
|
|
9
9
|
import com.facebook.react.bridge.Promise;
|
|
10
10
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
11
|
-
import com.facebook.react.bridge.ReactContext;
|
|
12
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
13
11
|
import com.facebook.react.bridge.ReactMethod;
|
|
14
12
|
import com.facebook.react.bridge.ReadableMap;
|
|
15
|
-
import com.
|
|
16
|
-
import com.facebook.react.module.annotations.ReactModule;
|
|
17
|
-
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
13
|
+
import com.reactnativecompressor.Utils.MediaCache;
|
|
18
14
|
import com.reactnativecompressor.VideoCompressorSpec;
|
|
19
15
|
import com.reactnativecompressor.Utils.RealPathUtil;
|
|
20
16
|
|
|
@@ -41,14 +37,6 @@ public class VideoModule extends VideoCompressorSpec {
|
|
|
41
37
|
return NAME;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
private void sendEvent(ReactContext reactContext,
|
|
45
|
-
String eventName,
|
|
46
|
-
@Nullable WritableMap params) {
|
|
47
|
-
reactContext
|
|
48
|
-
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
49
|
-
.emit(eventName, params);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
40
|
//Video
|
|
53
41
|
@ReactMethod
|
|
54
42
|
public void compress(
|
|
@@ -56,7 +44,8 @@ public class VideoModule extends VideoCompressorSpec {
|
|
|
56
44
|
ReadableMap optionMap,
|
|
57
45
|
Promise promise) {
|
|
58
46
|
final VideoCompressorHelper options = VideoCompressorHelper.fromMap(optionMap);
|
|
59
|
-
|
|
47
|
+
|
|
48
|
+
fileUrl=getRealPath(fileUrl,reactContext,options.uuid);
|
|
60
49
|
|
|
61
50
|
if(options.compressionMethod==VideoCompressorHelper.CompressionMethod.auto)
|
|
62
51
|
{
|
|
@@ -66,8 +55,6 @@ public class VideoModule extends VideoCompressorSpec {
|
|
|
66
55
|
{
|
|
67
56
|
VideoCompressorHelper.VideoCompressManual(fileUrl,options,promise,reactContext);
|
|
68
57
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
58
|
}
|
|
72
59
|
|
|
73
60
|
@ReactMethod
|
|
@@ -11,18 +11,24 @@ abstract class CompressorSpec extends ReactContextBaseJavaModule {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
public abstract void image_compress(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
String imagePath,
|
|
15
|
+
ReadableMap optionMap,
|
|
16
|
+
Promise promise);
|
|
17
17
|
|
|
18
18
|
public abstract void compress_audio(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
String fileUrl,
|
|
20
|
+
ReadableMap optionMap,
|
|
21
|
+
Promise promise);
|
|
22
22
|
|
|
23
23
|
public abstract void generateFilePath(String extension, Promise promise);
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
public abstract void getRealPath(String path, String type, Promise promise);
|
|
26
|
+
|
|
25
27
|
public abstract void getVideoMetaData(String filePath, Promise promise);
|
|
26
28
|
|
|
27
29
|
public abstract void getFileSize(String filePath, Promise promise);
|
|
30
|
+
|
|
31
|
+
public abstract void addListener(String eventName);
|
|
32
|
+
|
|
33
|
+
public abstract void removeListeners(double count);
|
|
28
34
|
}
|
package/ios/Compressor.h
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
#import <React/RCTEventEmitter.h>
|
|
1
2
|
|
|
2
3
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
4
|
#import "RNCompressorSpec.h"
|
|
4
5
|
|
|
5
|
-
@interface Compressor :
|
|
6
|
+
@interface Compressor : RCTEventEmitter <NativeCompressorSpec>
|
|
6
7
|
#else
|
|
7
8
|
#import <React/RCTBridgeModule.h>
|
|
8
9
|
|
|
9
|
-
@interface Compressor :
|
|
10
|
+
@interface Compressor : RCTEventEmitter <RCTBridgeModule>
|
|
10
11
|
#endif
|
|
11
12
|
|
|
12
13
|
@end
|