react-native-compressor 1.8.16 → 1.8.18
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 +103 -10
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +58 -27
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioHelper.kt +49 -0
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +2 -6
- package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +5 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/HttpCallManager.kt +41 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +70 -33
- package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +0 -2
- package/android/src/oldarch/CompressorSpec.kt +1 -0
- package/ios/Audio/AudioHelper.swift +49 -0
- package/ios/Audio/AudioMain.swift +46 -82
- package/ios/Audio/AudioOptions.swift +37 -0
- package/ios/Audio/FormatConverter/FormatConverter+Compressed.swift +326 -0
- package/ios/Audio/FormatConverter/FormatConverter+PCM.swift +239 -0
- package/ios/Audio/FormatConverter/FormatConverter+Utilities.swift +103 -0
- package/ios/Audio/FormatConverter/FormatConverter.swift +264 -0
- package/ios/Compressor.mm +4 -1
- package/ios/Compressor.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/ios/CompressorManager.swift +5 -0
- package/ios/Utils/Uploader.swift +18 -1
- package/ios/Utils/UrlTaskManager.swift +58 -0
- package/ios/Video/NextLevelSessionExporter.swift +694 -0
- package/ios/Video/VideoMain.swift +1 -2
- package/lib/commonjs/Audio/index.js +1 -3
- package/lib/commonjs/Audio/index.js.map +1 -1
- package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/Uploader.js +13 -2
- package/lib/commonjs/utils/Uploader.js.map +1 -1
- package/lib/commonjs/utils/index.js +0 -2
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/Audio/index.js +1 -3
- package/lib/module/Audio/index.js.map +1 -1
- package/lib/module/Spec/NativeCompressor.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/Uploader.js +11 -1
- package/lib/module/utils/Uploader.js.map +1 -1
- package/lib/module/utils/index.js +0 -3
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/Audio/index.d.ts.map +1 -1
- package/lib/typescript/Spec/NativeCompressor.d.ts +1 -0
- package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +3 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/Uploader.d.ts +3 -1
- package/lib/typescript/utils/Uploader.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +4 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native-compressor.podspec +0 -1
- package/src/Audio/index.tsx +1 -3
- package/src/Spec/NativeCompressor.ts +2 -0
- package/src/index.tsx +2 -0
- package/src/utils/Uploader.tsx +18 -1
- package/src/utils/index.tsx +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img height="150" src="/media/logo.png"
|
|
2
|
+
<img height="150" src="/media/logo.png" />
|
|
3
3
|
</div>
|
|
4
|
+
|
|
4
5
|
<br/>
|
|
5
6
|
|
|
6
7
|
<div align="center">
|
|
@@ -15,6 +16,11 @@
|
|
|
15
16
|
|
|
16
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
|
|
|
19
|
+
<div align="center">
|
|
20
|
+
<b>Also supports Auto/Manual Compression | Background Upload | Download File | Create Video Thumbnail</b>
|
|
21
|
+
</div>
|
|
22
|
+
<br/>
|
|
23
|
+
|
|
18
24
|
<div align="center">
|
|
19
25
|
<pre>
|
|
20
26
|
<img height="90" src="/media/whatsapp_logo.png"/> <img height="90" src="/media/compress_media.png"/>
|
|
@@ -129,9 +135,9 @@ react-native link react-native-compressor
|
|
|
129
135
|
|
|
130
136
|
#### iOS
|
|
131
137
|
|
|
132
|
-
1. In XCode,
|
|
133
|
-
2.
|
|
134
|
-
3.
|
|
138
|
+
1. In XCode, open Podfile
|
|
139
|
+
2. paste this line `pod 'react-native-compressor', :path => '../node_modules/react-native-compressor'` into `Podfile`
|
|
140
|
+
3. run this command inside ios folder `pod install`
|
|
135
141
|
4. Run your project (`Cmd+R`)<
|
|
136
142
|
|
|
137
143
|
#### Android
|
|
@@ -270,6 +276,17 @@ const result = await Audio.compress(
|
|
|
270
276
|
'file://path_of_file/file_example_MP3_2MG.wav', // recommended wav file but can be use mp3 file
|
|
271
277
|
{ quality: 'medium' }
|
|
272
278
|
);
|
|
279
|
+
|
|
280
|
+
// OR
|
|
281
|
+
|
|
282
|
+
const result = await Audio.compress(
|
|
283
|
+
'file://path_of_file/file_example_MP3_2MG.wav', // recommended wav file but can be use mp3 file
|
|
284
|
+
{
|
|
285
|
+
bitrate: 64000,
|
|
286
|
+
samplerate: 44100,
|
|
287
|
+
channels: 1,
|
|
288
|
+
}
|
|
289
|
+
);
|
|
273
290
|
```
|
|
274
291
|
|
|
275
292
|
### Background Upload
|
|
@@ -300,6 +317,53 @@ const uploadResult = await backgroundUpload(
|
|
|
300
317
|
);
|
|
301
318
|
```
|
|
302
319
|
|
|
320
|
+
### Cancel Background Upload
|
|
321
|
+
for cancellation Upload, there is two ways
|
|
322
|
+
1. by calling, cancelUpload function
|
|
323
|
+
2. by calling abort function
|
|
324
|
+
|
|
325
|
+
##### cancelUpload (support single and all)
|
|
326
|
+
```js
|
|
327
|
+
import { cancelUpload, backgroundUpload } from 'react-native-compressor';
|
|
328
|
+
|
|
329
|
+
// if we will call without passing any param then it will remove last pushed uploading
|
|
330
|
+
cancelUpload()
|
|
331
|
+
|
|
332
|
+
// if you pass true as second param then it will cancel all the uploading
|
|
333
|
+
cancelUpload("",true)
|
|
334
|
+
|
|
335
|
+
// if there is multiple files are uploading, and you wanna cancel specific uploading then you pass specific video id like this
|
|
336
|
+
let videoId=''
|
|
337
|
+
const uploadResult = await backgroundUpload(
|
|
338
|
+
url,
|
|
339
|
+
fileUrl,
|
|
340
|
+
{ httpMethod: 'PUT', getCancellationId: (cancellationId) =>(videoId = cancellationId), },
|
|
341
|
+
(written, total) => {
|
|
342
|
+
console.log(written, total);
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
cancelUpload(videoId)
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
##### cancel by calling abort
|
|
349
|
+
```js
|
|
350
|
+
import { backgroundUpload } from 'react-native-compressor';
|
|
351
|
+
|
|
352
|
+
const abortSignalRef = useRef(new AbortController());
|
|
353
|
+
|
|
354
|
+
const uploadResult = await backgroundUpload(
|
|
355
|
+
url,
|
|
356
|
+
fileUrl,
|
|
357
|
+
{ httpMethod: 'PUT' },
|
|
358
|
+
(written, total) => {
|
|
359
|
+
console.log(written, total);
|
|
360
|
+
},
|
|
361
|
+
abortSignalRef.current.signal
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
abortSignalRef.current?.abort(); // this will cancel uploading
|
|
365
|
+
```
|
|
366
|
+
|
|
303
367
|
### Download File
|
|
304
368
|
|
|
305
369
|
```js
|
|
@@ -377,8 +441,14 @@ await clearCache(); // this will clear cache of thumbnails cache directory
|
|
|
377
441
|
- ###### `compress(url: string, options?: videoCompresssionType , onProgress?: (progress: number)): Promise<string>`
|
|
378
442
|
|
|
379
443
|
- ###### `cancelCompression(cancellationId: string): void`
|
|
444
|
+
|
|
380
445
|
we can get cancellationId from `getCancellationId` which is the callback method of compress method options
|
|
381
446
|
|
|
447
|
+
- ###### `activateBackgroundTask(onExpired?: (data: any) => void): Promise<any>`
|
|
448
|
+
if you wanna compress video while app is in backgroup then you should call this method before compression
|
|
449
|
+
- ###### `deactivateBackgroundTask(): Promise<any>`
|
|
450
|
+
if you call `activateBackgroundTask` method, then after video compression, you should call `deactivateBackgroundTask` for disable background task mode.
|
|
451
|
+
|
|
382
452
|
### videoCompresssionType
|
|
383
453
|
|
|
384
454
|
- ###### `compressionMethod: compressionMethod` (default: "manual")
|
|
@@ -415,10 +485,20 @@ await clearCache(); // this will clear cache of thumbnails cache directory
|
|
|
415
485
|
|
|
416
486
|
### audioCompresssionType
|
|
417
487
|
|
|
418
|
-
- ###### `quality
|
|
488
|
+
- ###### `quality?: qualityType` (default: medium)
|
|
489
|
+
|
|
419
490
|
we can also control bitrate through quality. qualityType can be `low` | `medium` | `high`
|
|
420
491
|
|
|
421
|
-
|
|
492
|
+
- ###### `bitrate?: number` Range [64000-320000]
|
|
493
|
+
|
|
494
|
+
we can control bitrate of audio through bitrate, it should be in the range of `64000-320000`
|
|
495
|
+
|
|
496
|
+
- ###### `samplerate?: number` Range [44100 - 192000]
|
|
497
|
+
|
|
498
|
+
we can control samplerate of audio through samplerate, it should be in the range of `44100 - 192000`
|
|
499
|
+
|
|
500
|
+
- ###### `channels?: number` Typically 1 or 2
|
|
501
|
+
we can control channels of audio through channels, Typically 1 or 2
|
|
422
502
|
|
|
423
503
|
## Background Upload
|
|
424
504
|
|
|
@@ -460,12 +540,25 @@ export declare type UploaderOptions = (
|
|
|
460
540
|
) & {
|
|
461
541
|
headers?: Record<string, string>;
|
|
462
542
|
httpMethod?: UploaderHttpMethod;
|
|
543
|
+
getCancellationId?: (cancellationId: string) => void;
|
|
463
544
|
};
|
|
464
545
|
```
|
|
465
546
|
|
|
466
547
|
**Note:** some of the uploader code is borrowed from [Expo](https://github.com/expo/expo)
|
|
467
548
|
I tested file uploader on this backend [Nodejs-File-Uploader](https://github.com/numandev1/nodejs-file-uploader)
|
|
468
549
|
|
|
550
|
+
### Cancel Background Upload
|
|
551
|
+
for cancellation Upload, there is two ways, you can use one of it
|
|
552
|
+
- ##### cancelUpload: ( uuid?: string, shouldCancelAll?: boolean) => void
|
|
553
|
+
1. If we call without passing any param then it will remove the last pushed uploading
|
|
554
|
+
2. If you pass true as the second param then it will cancel all the uploading
|
|
555
|
+
3. if there is multiple files are uploading, and you wanna cancel specific uploading then you pass a specific video ID like this
|
|
556
|
+
|
|
557
|
+
- ##### we can use [AbortController](https://github.com/facebook/react-native/blob/255fef5263afdf9933ba2f8a3dbcbca39ea9928a/packages/react-native/types/modules/globals.d.ts#L531) in backgroundUpload [Usage](#cancel-background-upload)
|
|
558
|
+
`const abortSignalRef = useRef(new AbortController());`
|
|
559
|
+
|
|
560
|
+
`abortSignalRef.current?.abort();`
|
|
561
|
+
|
|
469
562
|
### Download
|
|
470
563
|
|
|
471
564
|
- ##### download: ( fileUrl: string, downloadProgress?: (progress: number) => void, progressDivider?: number ) => Promise< string >
|
|
@@ -492,11 +585,11 @@ const metaData = await getVideoMetaData(filePath);
|
|
|
492
585
|
|
|
493
586
|
```
|
|
494
587
|
{
|
|
495
|
-
"duration":
|
|
588
|
+
"duration": 20.11,
|
|
496
589
|
"extension": "mp4",
|
|
497
|
-
"height":
|
|
498
|
-
"size":
|
|
499
|
-
"width":
|
|
590
|
+
"height": 1080,
|
|
591
|
+
"size": 16940.0,
|
|
592
|
+
"width": 1920
|
|
500
593
|
}
|
|
501
594
|
```
|
|
502
595
|
|
package/android/build.gradle
CHANGED
|
@@ -4,6 +4,7 @@ package com.reactnativecompressor.Audio
|
|
|
4
4
|
import android.annotation.SuppressLint
|
|
5
5
|
import com.facebook.react.bridge.Promise
|
|
6
6
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap
|
|
7
8
|
import com.naman14.androidlame.LameBuilder
|
|
8
9
|
import com.naman14.androidlame.WaveReader
|
|
9
10
|
import com.reactnativecompressor.Utils.MediaCache
|
|
@@ -27,21 +28,22 @@ class AudioCompressor {
|
|
|
27
28
|
@JvmStatic
|
|
28
29
|
fun CompressAudio(
|
|
29
30
|
fileUrl: String,
|
|
30
|
-
|
|
31
|
+
optionMap: ReadableMap,
|
|
31
32
|
context: ReactApplicationContext,
|
|
32
33
|
promise: Promise,
|
|
33
34
|
) {
|
|
34
|
-
|
|
35
|
+
val realPath = Utils.getRealPath(fileUrl, context)
|
|
36
|
+
var _fileUrl=realPath
|
|
37
|
+
val filePathWithoutFileUri = realPath!!.replace("file://", "")
|
|
35
38
|
try {
|
|
36
|
-
|
|
37
|
-
var wavPath=filePath;
|
|
39
|
+
var wavPath=filePathWithoutFileUri;
|
|
38
40
|
var isNonWav:Boolean=false
|
|
39
41
|
if (fileUrl.endsWith(".mp4", ignoreCase = true))
|
|
40
42
|
{
|
|
41
43
|
addLog("mp4 file found")
|
|
42
44
|
val mp3Path= Utils.generateCacheFilePath("mp3", context)
|
|
43
45
|
AudioExtractor().genVideoUsingMuxer(fileUrl, mp3Path, -1, -1, true, false)
|
|
44
|
-
_fileUrl=
|
|
46
|
+
_fileUrl=Utils.slashifyFilePath(mp3Path)
|
|
45
47
|
wavPath= Utils.generateCacheFilePath("wav", context)
|
|
46
48
|
try {
|
|
47
49
|
val converter = Converter()
|
|
@@ -58,7 +60,7 @@ class AudioCompressor {
|
|
|
58
60
|
wavPath= Utils.generateCacheFilePath("wav", context)
|
|
59
61
|
try {
|
|
60
62
|
val converter = Converter()
|
|
61
|
-
converter.convert(
|
|
63
|
+
converter.convert(filePathWithoutFileUri, wavPath)
|
|
62
64
|
} catch (e: JavaLayerException) {
|
|
63
65
|
addLog("JavaLayerException error"+e.localizedMessage)
|
|
64
66
|
e.printStackTrace();
|
|
@@ -67,7 +69,7 @@ class AudioCompressor {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
|
|
70
|
-
autoCompressHelper(wavPath,
|
|
72
|
+
autoCompressHelper(wavPath,filePathWithoutFileUri, optionMap,context) { mp3Path, finished ->
|
|
71
73
|
if (finished) {
|
|
72
74
|
val returnableFilePath:String="file://$mp3Path"
|
|
73
75
|
addLog("finished: " + returnableFilePath)
|
|
@@ -87,28 +89,23 @@ class AudioCompressor {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
private fun getAudioBitrateByQuality(quality: String): Int {
|
|
91
|
-
return when (quality) {
|
|
92
|
-
"low" -> 64 // Set your low bitrate value here
|
|
93
|
-
"medium" -> 128 // Set your medium bitrate value here
|
|
94
|
-
"high" -> 256 // Set your high bitrate value here
|
|
95
|
-
else -> 128 // Default to medium bitrate if quality is not recognized
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
92
|
@SuppressLint("WrongConstant")
|
|
100
93
|
private fun autoCompressHelper(
|
|
101
94
|
fileUrl: String,
|
|
102
|
-
|
|
95
|
+
actualFileUrl: String,
|
|
96
|
+
optionMap: ReadableMap,
|
|
103
97
|
context: ReactApplicationContext,
|
|
104
98
|
completeCallback: (String, Boolean) -> Unit
|
|
105
99
|
) {
|
|
100
|
+
|
|
101
|
+
val options = AudioHelper.fromMap(optionMap)
|
|
102
|
+
val quality = options.quality
|
|
103
|
+
|
|
106
104
|
var isCompletedCallbackTriggered:Boolean=false
|
|
107
105
|
try {
|
|
108
106
|
var mp3Path = Utils.generateCacheFilePath("mp3", context)
|
|
109
107
|
val input = File(fileUrl)
|
|
110
108
|
val output = File(mp3Path)
|
|
111
|
-
val audioBitrate= getAudioBitrateByQuality(quality)
|
|
112
109
|
|
|
113
110
|
val CHUNK_SIZE = 8192
|
|
114
111
|
addLog("Initialising wav reader")
|
|
@@ -122,12 +119,46 @@ class AudioCompressor {
|
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
addLog("Intitialising encoder")
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// for bitrate
|
|
125
|
+
var audioBitrate:Int
|
|
126
|
+
if(options.bitrate != -1)
|
|
127
|
+
{
|
|
128
|
+
audioBitrate= options.bitrate/1000
|
|
129
|
+
}
|
|
130
|
+
else
|
|
131
|
+
{
|
|
132
|
+
audioBitrate=AudioHelper.getDestinationBitrateByQuality(actualFileUrl, quality!!)
|
|
133
|
+
Utils.addLog("dest bitrate: $audioBitrate")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var androidLame = LameBuilder();
|
|
137
|
+
androidLame.setOutBitrate(audioBitrate)
|
|
138
|
+
|
|
139
|
+
// for channels
|
|
140
|
+
var audioChannels:Int
|
|
141
|
+
if(options.channels != -1){
|
|
142
|
+
audioChannels= options.channels!!
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
{
|
|
146
|
+
audioChannels=waveReader!!.channels
|
|
147
|
+
}
|
|
148
|
+
androidLame.setOutChannels(audioChannels)
|
|
149
|
+
|
|
150
|
+
// for sample rate
|
|
151
|
+
androidLame.setInSampleRate(waveReader!!.sampleRate)
|
|
152
|
+
var audioSampleRate:Int
|
|
153
|
+
if(options.samplerate != -1){
|
|
154
|
+
audioSampleRate= options.samplerate!!
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
{
|
|
158
|
+
audioSampleRate=waveReader!!.sampleRate
|
|
159
|
+
}
|
|
160
|
+
androidLame.setOutSampleRate(audioSampleRate)
|
|
161
|
+
val androidLameBuild=androidLame.build()
|
|
131
162
|
|
|
132
163
|
try {
|
|
133
164
|
outputStream = BufferedOutputStream(FileOutputStream(output), OUTPUT_STREAM_BUFFER)
|
|
@@ -154,7 +185,7 @@ class AudioCompressor {
|
|
|
154
185
|
if (bytesRead > 0) {
|
|
155
186
|
|
|
156
187
|
var bytesEncoded = 0
|
|
157
|
-
bytesEncoded =
|
|
188
|
+
bytesEncoded = androidLameBuild.encode(buffer_l, buffer_r, bytesRead, mp3Buf)
|
|
158
189
|
addLog("bytes encoded=$bytesEncoded")
|
|
159
190
|
|
|
160
191
|
if (bytesEncoded > 0) {
|
|
@@ -177,7 +208,7 @@ class AudioCompressor {
|
|
|
177
208
|
if (bytesRead > 0) {
|
|
178
209
|
var bytesEncoded = 0
|
|
179
210
|
|
|
180
|
-
bytesEncoded =
|
|
211
|
+
bytesEncoded = androidLameBuild.encode(buffer_l, buffer_l, bytesRead, mp3Buf)
|
|
181
212
|
addLog("bytes encoded=$bytesEncoded")
|
|
182
213
|
|
|
183
214
|
if (bytesEncoded > 0) {
|
|
@@ -202,7 +233,7 @@ class AudioCompressor {
|
|
|
202
233
|
}
|
|
203
234
|
|
|
204
235
|
addLog("flushing final mp3buffer")
|
|
205
|
-
val outputMp3buf =
|
|
236
|
+
val outputMp3buf = androidLameBuild.flush(mp3Buf)
|
|
206
237
|
addLog("flushed $outputMp3buf bytes")
|
|
207
238
|
if (outputMp3buf > 0) {
|
|
208
239
|
try {
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
package com.reactnativecompressor.Audio
|
|
2
2
|
|
|
3
|
+
import android.media.MediaExtractor
|
|
4
|
+
import android.media.MediaFormat
|
|
3
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import com.reactnativecompressor.Utils.Utils
|
|
7
|
+
import java.io.File
|
|
8
|
+
import java.io.IOException
|
|
9
|
+
|
|
4
10
|
|
|
5
11
|
class AudioHelper {
|
|
6
12
|
|
|
7
13
|
var quality: String? = "medium"
|
|
14
|
+
var bitrate: Int = -1
|
|
15
|
+
var samplerate: Int = -1
|
|
16
|
+
var channels: Int = -1
|
|
8
17
|
var progressDivider: Int? = 0
|
|
9
18
|
|
|
10
19
|
companion object {
|
|
@@ -15,9 +24,49 @@ class AudioHelper {
|
|
|
15
24
|
val key = iterator.nextKey()
|
|
16
25
|
when (key) {
|
|
17
26
|
"quality" -> options.quality = map.getString(key)
|
|
27
|
+
"bitrate" -> {
|
|
28
|
+
val bitrate = map.getInt(key)
|
|
29
|
+
options.bitrate = if (bitrate > 320000 || bitrate < 64000) 64000 else bitrate
|
|
30
|
+
}
|
|
31
|
+
"samplerate" -> options.samplerate = map.getInt(key)
|
|
32
|
+
"channels" -> options.channels = map.getInt(key)
|
|
18
33
|
}
|
|
19
34
|
}
|
|
20
35
|
return options
|
|
21
36
|
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
fun getAudioBitrate(path: String): Int {
|
|
40
|
+
val file = File(path)
|
|
41
|
+
val fileSize = file.length() * 8 // size in bits
|
|
42
|
+
|
|
43
|
+
val mex = MediaExtractor()
|
|
44
|
+
try {
|
|
45
|
+
mex.setDataSource(path)
|
|
46
|
+
} catch (e: IOException) {
|
|
47
|
+
e.printStackTrace()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
val mf = mex.getTrackFormat(0)
|
|
51
|
+
val durationUs = mf.getLong(MediaFormat.KEY_DURATION)
|
|
52
|
+
val durationSec = durationUs / 1_000_000.0 // convert duration to seconds
|
|
53
|
+
|
|
54
|
+
return (fileSize / durationSec).toInt()/1000 // bitrate in bits per second
|
|
55
|
+
}
|
|
56
|
+
fun getDestinationBitrateByQuality(path: String, quality: String): Int {
|
|
57
|
+
val originalBitrate = getAudioBitrate(path)
|
|
58
|
+
var destinationBitrate = originalBitrate
|
|
59
|
+
Utils.addLog("source bitrate: $originalBitrate")
|
|
60
|
+
|
|
61
|
+
when (quality.toLowerCase()) {
|
|
62
|
+
"low" -> destinationBitrate = maxOf(64, (originalBitrate * 0.3).toInt())
|
|
63
|
+
"medium" -> destinationBitrate = (originalBitrate * 0.5).toInt()
|
|
64
|
+
"high" -> destinationBitrate = minOf(320, (originalBitrate * 0.7).toInt())
|
|
65
|
+
else -> Utils.addLog("Invalid quality level. Please enter 'low', 'medium', or 'high'.")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return destinationBitrate
|
|
69
|
+
}
|
|
70
|
+
|
|
22
71
|
}
|
|
23
72
|
}
|
|
@@ -3,7 +3,6 @@ package com.reactnativecompressor.Audio
|
|
|
3
3
|
import com.facebook.react.bridge.Promise
|
|
4
4
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
|
-
import com.reactnativecompressor.Utils.Utils
|
|
7
6
|
|
|
8
7
|
class AudioMain(private val reactContext: ReactApplicationContext) {
|
|
9
8
|
fun compress_audio(
|
|
@@ -11,11 +10,8 @@ class AudioMain(private val reactContext: ReactApplicationContext) {
|
|
|
11
10
|
optionMap: ReadableMap,
|
|
12
11
|
promise: Promise) {
|
|
13
12
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
val realPath = Utils.getRealPath(fileUrl, reactContext)
|
|
17
|
-
Utils.addLog(fileUrl + "\n realPath= " + realPath)
|
|
18
|
-
AudioCompressor.CompressAudio(realPath!!, quality!!,reactContext,promise)
|
|
13
|
+
|
|
14
|
+
AudioCompressor.CompressAudio(fileUrl,optionMap,reactContext,promise)
|
|
19
15
|
} catch (ex: Exception) {
|
|
20
16
|
promise.reject(ex)
|
|
21
17
|
}
|
|
@@ -125,6 +125,11 @@ class CompressorModule(private val reactContext: ReactApplicationContext) : Comp
|
|
|
125
125
|
uploader.upload(fileUrl, options, reactContext, promise)
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
@ReactMethod
|
|
129
|
+
override fun cancelUpload(uuid: String,shouldCancelAll:Boolean) {
|
|
130
|
+
uploader.cancelUpload(uuid,shouldCancelAll)
|
|
131
|
+
}
|
|
132
|
+
|
|
128
133
|
@ReactMethod
|
|
129
134
|
override fun download(
|
|
130
135
|
fileUrl: String,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package com.reactnativecompressor.Utils
|
|
2
|
+
|
|
3
|
+
import okhttp3.Call
|
|
4
|
+
|
|
5
|
+
class HttpCallManager {
|
|
6
|
+
private var resumableCalls: MutableMap<String, Call?> = HashMap()
|
|
7
|
+
|
|
8
|
+
fun registerTask(call: Call, uuid: String) {
|
|
9
|
+
resumableCalls[uuid] = call
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
fun taskForId(uuid: String): Call? {
|
|
13
|
+
return resumableCalls[uuid]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// will use in future
|
|
17
|
+
fun downloadTaskForId(uuid: String): Call? {
|
|
18
|
+
return taskForId(uuid)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fun uploadTaskForId(uuid: String): Call? {
|
|
22
|
+
return taskForId(uuid)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fun taskPop(): Call? {
|
|
26
|
+
val lastUuid = resumableCalls.keys.lastOrNull()
|
|
27
|
+
val lastCall = resumableCalls.remove(lastUuid)
|
|
28
|
+
return lastCall
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun unregisterTask(uuid: String) {
|
|
32
|
+
resumableCalls.remove(uuid)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fun cancelAllTasks() {
|
|
36
|
+
for ((_, call) in resumableCalls) {
|
|
37
|
+
call?.cancel()
|
|
38
|
+
}
|
|
39
|
+
resumableCalls.clear()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -26,36 +26,45 @@ import java.net.URLConnection
|
|
|
26
26
|
import java.util.concurrent.TimeUnit
|
|
27
27
|
|
|
28
28
|
class Uploader(private val reactContext: ReactApplicationContext) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
val TAG = "asyncTaskUploader"
|
|
30
|
+
var client: OkHttpClient? = null
|
|
31
|
+
val MIN_EVENT_DT_MS: Long = 100
|
|
32
|
+
val httpCallManager = HttpCallManager()
|
|
33
|
+
|
|
34
|
+
fun upload(
|
|
35
|
+
fileUriString: String,
|
|
36
|
+
_options: ReadableMap,
|
|
37
|
+
reactContext: ReactApplicationContext,
|
|
38
|
+
promise: Promise
|
|
39
|
+
) {
|
|
40
|
+
val options: UploaderOptions = convertReadableMapToUploaderOptions(_options)
|
|
41
|
+
val url = options.url
|
|
42
|
+
val uuid = options.uuid
|
|
43
|
+
val progressListener: CountingRequestListener = object : CountingRequestListener {
|
|
44
|
+
private var mLastUpdate: Long = -1
|
|
45
|
+
override fun onProgress(bytesWritten: Long, contentLength: Long) {
|
|
46
|
+
val currentTime = System.currentTimeMillis()
|
|
47
|
+
|
|
48
|
+
// Throttle events. Sending too many events will block the JS event loop.
|
|
49
|
+
// Make sure to send the last event when we're at 100%.
|
|
50
|
+
if (currentTime > mLastUpdate + MIN_EVENT_DT_MS || bytesWritten == contentLength) {
|
|
51
|
+
mLastUpdate = currentTime
|
|
52
|
+
EventEmitterHandler.sendUploadProgressEvent(bytesWritten, contentLength, uuid)
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
}
|
|
56
|
+
val request = createUploadRequest(
|
|
57
|
+
url, fileUriString, options
|
|
58
|
+
) { requestBody -> CountingRequestBody(requestBody, progressListener) }
|
|
59
|
+
|
|
60
|
+
okHttpClient?.let {
|
|
61
|
+
val call = it.newCall(request)
|
|
62
|
+
httpCallManager.registerTask(call,uuid)
|
|
63
|
+
call.enqueue(
|
|
64
|
+
object : Callback {
|
|
56
65
|
override fun onFailure(call: Call, e: IOException) {
|
|
57
66
|
Log.e(TAG, e.message.toString())
|
|
58
|
-
promise.reject(TAG, e.message
|
|
67
|
+
promise.reject(TAG, e.message)
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
override fun onResponse(call: Call, response: Response) {
|
|
@@ -67,12 +76,12 @@ class Uploader(private val reactContext: ReactApplicationContext) {
|
|
|
67
76
|
promise.resolve(param)
|
|
68
77
|
}
|
|
69
78
|
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
79
|
+
} ?: run {
|
|
80
|
+
promise.reject(UploaderOkHttpNullException())
|
|
74
81
|
}
|
|
75
82
|
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
@get:Synchronized
|
|
77
86
|
private val okHttpClient: OkHttpClient?
|
|
78
87
|
get() {
|
|
@@ -87,7 +96,12 @@ class Uploader(private val reactContext: ReactApplicationContext) {
|
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
@Throws(IOException::class)
|
|
90
|
-
private fun createUploadRequest(
|
|
99
|
+
private fun createUploadRequest(
|
|
100
|
+
url: String,
|
|
101
|
+
fileUriString: String,
|
|
102
|
+
options: UploaderOptions,
|
|
103
|
+
decorator: RequestBodyDecorator
|
|
104
|
+
): Request {
|
|
91
105
|
val fileUri = Uri.parse(slashifyFilePath(fileUriString))
|
|
92
106
|
fileUri.checkIfFileExists()
|
|
93
107
|
|
|
@@ -101,7 +115,11 @@ class Uploader(private val reactContext: ReactApplicationContext) {
|
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
@SuppressLint("NewApi")
|
|
104
|
-
private fun createRequestBody(
|
|
118
|
+
private fun createRequestBody(
|
|
119
|
+
options: UploaderOptions,
|
|
120
|
+
decorator: RequestBodyDecorator,
|
|
121
|
+
file: File
|
|
122
|
+
): RequestBody {
|
|
105
123
|
return when (options.uploadType) {
|
|
106
124
|
UploadType.BINARY_CONTENT -> {
|
|
107
125
|
val mimeType: String? = if (options.mimeType?.isNotEmpty() == true) {
|
|
@@ -122,7 +140,11 @@ class Uploader(private val reactContext: ReactApplicationContext) {
|
|
|
122
140
|
val mimeType: String = options.mimeType ?: URLConnection.guessContentTypeFromName(file.name)
|
|
123
141
|
|
|
124
142
|
val fieldName = options.fieldName ?: file.name
|
|
125
|
-
bodyBuilder.addFormDataPart(
|
|
143
|
+
bodyBuilder.addFormDataPart(
|
|
144
|
+
fieldName,
|
|
145
|
+
file.name,
|
|
146
|
+
decorator.decorate(file.asRequestBody(mimeType.toMediaTypeOrNull()))
|
|
147
|
+
)
|
|
126
148
|
bodyBuilder.build()
|
|
127
149
|
}
|
|
128
150
|
}
|
|
@@ -175,4 +197,19 @@ class Uploader(private val reactContext: ReactApplicationContext) {
|
|
|
175
197
|
}
|
|
176
198
|
return responseHeaders
|
|
177
199
|
}
|
|
200
|
+
|
|
201
|
+
fun cancelUpload(uuid:String,shouldCancelAll:Boolean) {
|
|
202
|
+
if(shouldCancelAll)
|
|
203
|
+
{
|
|
204
|
+
httpCallManager.cancelAllTasks()
|
|
205
|
+
}
|
|
206
|
+
else if(uuid=="")
|
|
207
|
+
{
|
|
208
|
+
httpCallManager.taskPop()?.cancel()
|
|
209
|
+
}
|
|
210
|
+
else
|
|
211
|
+
{
|
|
212
|
+
httpCallManager.uploadTaskForId(uuid)?.cancel()
|
|
213
|
+
}
|
|
214
|
+
}
|
|
178
215
|
}
|
|
@@ -8,7 +8,6 @@ import android.net.Uri
|
|
|
8
8
|
import android.os.Build
|
|
9
9
|
import android.text.TextUtils
|
|
10
10
|
import android.webkit.URLUtil
|
|
11
|
-
import androidx.annotation.RequiresApi
|
|
12
11
|
import com.facebook.react.bridge.Arguments
|
|
13
12
|
import com.facebook.react.bridge.GuardedResultAsyncTask
|
|
14
13
|
import com.facebook.react.bridge.Promise
|
|
@@ -39,7 +38,6 @@ class CreateVideoThumbnailClass(private val reactContext: ReactApplicationContex
|
|
|
39
38
|
weakContext = WeakReference(reactContext.applicationContext)
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
@RequiresApi(Build.VERSION_CODES.N)
|
|
43
41
|
override fun doInBackgroundGuarded(): ReadableMap? {
|
|
44
42
|
val format = "jpeg"
|
|
45
43
|
val cacheName = if (options.hasKey("cacheName")) options.getString("cacheName") else ""
|
|
@@ -23,6 +23,7 @@ abstract class CompressorSpec(context: ReactApplicationContext?) : ReactContextB
|
|
|
23
23
|
abstract fun compress(fileUrl: String, optionMap: ReadableMap, promise: Promise)
|
|
24
24
|
abstract fun cancelCompression(uuid: String)
|
|
25
25
|
abstract fun upload(fileUrl: String, options: ReadableMap, promise: Promise)
|
|
26
|
+
abstract fun cancelUpload(uuid: String, shouldCancelAll:Boolean)
|
|
26
27
|
|
|
27
28
|
abstract fun download(fileUrl: String, options: ReadableMap, promise: Promise)
|
|
28
29
|
abstract fun activateBackgroundTask(options: ReadableMap, promise: Promise)
|