react-native-video-trim 2.0.0 → 2.2.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.
- package/README.md +168 -33
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/java/com/videotrim/VideoTrimModule.java +282 -75
- package/android/src/main/java/com/videotrim/enums/ErrorCode.java +10 -0
- package/android/src/main/java/com/videotrim/interfaces/VideoTrimListener.java +4 -1
- package/android/src/main/java/com/videotrim/utils/MediaMetadataUtil.java +75 -0
- package/android/src/main/java/com/videotrim/utils/StorageUtil.java +2 -2
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.java +26 -16
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +310 -81
- package/android/src/main/res/drawable/airpodsmax.xml +19 -0
- package/android/src/main/res/drawable/exclamationmark_triangle_fill.xml +15 -0
- package/android/src/main/res/drawable/thumb_container_bg.xml +8 -0
- package/android/src/main/res/layout/video_trimmer_view.xml +71 -4
- package/android/src/main/res/xml/file_paths.xml +5 -0
- package/ios/AssetLoader.swift +99 -0
- package/ios/ErrorCode.swift +16 -0
- package/ios/ProgressAlertController.swift +100 -0
- package/ios/VideoTrim.mm +4 -2
- package/ios/VideoTrim.swift +472 -177
- package/ios/VideoTrimmer.swift +16 -10
- package/ios/VideoTrimmerViewController.swift +191 -22
- package/lib/commonjs/index.js +25 -55
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +24 -55
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +215 -9
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +229 -66
- package/android/src/main/java/iknow/android/utils/BuildConfig.java +0 -18
- package/android/src/main/java/iknow/android/utils/DateUtil.java +0 -64
package/README.md
CHANGED
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
<img src="images/ios.gif" width="300" />
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
|
+
## Features
|
|
10
|
+
- ✅ Support video and audio
|
|
11
|
+
- ✅ Support local and remote files
|
|
12
|
+
- ✅ Save to Photos, Documents and Share to other apps
|
|
13
|
+
- ✅ Check if file is valid video/audio
|
|
14
|
+
- ✅ File operations: list, clean up, delete specific file
|
|
15
|
+
|
|
16
|
+
<img src="images/document_picker.png" width="300" />
|
|
17
|
+
<img src="images/share_sheet.png" width="300" />
|
|
18
|
+
|
|
9
19
|
## Installation
|
|
10
20
|
|
|
11
21
|
```sh
|
|
@@ -29,7 +39,13 @@ npx pod-install ios
|
|
|
29
39
|
```
|
|
30
40
|
|
|
31
41
|
## Usage
|
|
32
|
-
|
|
42
|
+
|
|
43
|
+
> [!IMPORTANT]
|
|
44
|
+
> Note that for both Android and iOS you have to try on real device
|
|
45
|
+
|
|
46
|
+
> [!IMPORTANT]
|
|
47
|
+
> If you plan to trim remote file, you must install FFMPEG version from "https" onwards, "min" version won't work. See bottom to know how to install specific FFMPEG version
|
|
48
|
+
|
|
33
49
|
|
|
34
50
|
```js
|
|
35
51
|
import { showEditor } from 'react-native-video-trim';
|
|
@@ -57,7 +73,7 @@ import {
|
|
|
57
73
|
NativeEventEmitter,
|
|
58
74
|
NativeModules,
|
|
59
75
|
} from 'react-native';
|
|
60
|
-
import {
|
|
76
|
+
import { isValidFile, showEditor } from 'react-native-video-trim';
|
|
61
77
|
import { launchImageLibrary } from 'react-native-image-picker';
|
|
62
78
|
import { useEffect } from 'react';
|
|
63
79
|
|
|
@@ -66,6 +82,11 @@ export default function App() {
|
|
|
66
82
|
const eventEmitter = new NativeEventEmitter(NativeModules.VideoTrim);
|
|
67
83
|
const subscription = eventEmitter.addListener('VideoTrim', (event) => {
|
|
68
84
|
switch (event.name) {
|
|
85
|
+
case 'onLoad': {
|
|
86
|
+
// on media loaded successfully
|
|
87
|
+
console.log('onLoadListener', event);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
69
90
|
case 'onShow': {
|
|
70
91
|
console.log('onShowListener', event);
|
|
71
92
|
break;
|
|
@@ -86,10 +107,22 @@ export default function App() {
|
|
|
86
107
|
console.log('onCancelTrimming', event);
|
|
87
108
|
break;
|
|
88
109
|
}
|
|
110
|
+
case 'onCancel': {
|
|
111
|
+
console.log('onCancel', event);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
89
114
|
case 'onError': {
|
|
90
115
|
console.log('onError', event);
|
|
91
116
|
break;
|
|
92
117
|
}
|
|
118
|
+
case 'onLog': {
|
|
119
|
+
console.log('onLog', event);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'onStatistics': {
|
|
123
|
+
console.log('onStatistics', event);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
93
126
|
}
|
|
94
127
|
});
|
|
95
128
|
|
|
@@ -107,7 +140,7 @@ export default function App() {
|
|
|
107
140
|
assetRepresentationMode: 'current',
|
|
108
141
|
});
|
|
109
142
|
|
|
110
|
-
|
|
143
|
+
isValidFile(result.assets![0]?.uri || '').then((res) =>
|
|
111
144
|
console.log(res)
|
|
112
145
|
);
|
|
113
146
|
|
|
@@ -121,7 +154,7 @@ export default function App() {
|
|
|
121
154
|
</TouchableOpacity>
|
|
122
155
|
<TouchableOpacity
|
|
123
156
|
onPress={() => {
|
|
124
|
-
|
|
157
|
+
isValidFile('invalid file path').then((res) => console.log(res));
|
|
125
158
|
}}
|
|
126
159
|
style={{
|
|
127
160
|
padding: 10,
|
|
@@ -151,34 +184,90 @@ Main method to show Video Editor UI.
|
|
|
151
184
|
|
|
152
185
|
*Params*:
|
|
153
186
|
- `videoPath`: Path to video file, if this is an invalid path, `onError` event will be fired
|
|
154
|
-
- `config` (optional):
|
|
155
|
-
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
187
|
+
- `config` (optional, every sub props of `config` is optional):
|
|
188
|
+
|
|
189
|
+
- `type` (`default = video`): which player to use, `video` or `audio`
|
|
190
|
+
- `outputExt` (`default = mp4`): output file extension
|
|
191
|
+
- `enableHapticFeedback` (`default = true`): whether to enable haptic feedback
|
|
192
|
+
- `saveToPhoto` (Video-only, `default = false`): whether to save video to photo/gallery after editing
|
|
193
|
+
- `openDocumentsOnFinish` (`default = false`): open Document Picker on done trimming
|
|
194
|
+
- `openShareSheetOnFinish` (`default = false`): open Share Sheet on done trimming
|
|
195
|
+
- `removeAfterSavedToPhoto` (`default = false`): whether to remove output file from storage after saved to Photo successfully
|
|
196
|
+
- `removeAfterFailedToSavePhoto` (`default = false`): whether to remove output file if fail to save to Photo
|
|
197
|
+
- `removeAfterSavedToDocuments` (`default = false`): whether to remove output file from storage after saved Documents successfully
|
|
198
|
+
- `removeAfterFailedToSaveDocuments` (`default = false`): whether to remove output file from storage after fail to save to Documents
|
|
199
|
+
- `removeAfterShared` (`default = false`): whether to remove output file from storage after saved Share successfully. iOS only, on Android you'll have to manually remove the file (this is because on Android there's no way to detect when sharing is successful)
|
|
200
|
+
- `removeAfterFailedToShare` (`default = false`): whether to remove output file from storage after fail to Share. iOS only, on Android you'll have to manually remove the file
|
|
158
201
|
- `maxDuration` (optional): maximum duration for the trimmed video
|
|
159
|
-
- `minDuration` (
|
|
160
|
-
- `cancelButtonText` (
|
|
161
|
-
- `saveButtonText` (
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
202
|
+
- `minDuration` (`default = 1000`): minimum duration for the trimmed video
|
|
203
|
+
- `cancelButtonText` (`default= "Cancel"`): text of left button in Editor dialog
|
|
204
|
+
- `saveButtonText` (`default= "Save"`): text of right button in Editor dialog
|
|
205
|
+
- `enableCancelDialog` (`default = true`): whether to show alert dialog on press Cancel
|
|
206
|
+
- `cancelDialogTitle` (`default = "Warning!"`)
|
|
207
|
+
- `cancelDialogMessage` (`default = "Are you sure want to cancel?"`)
|
|
208
|
+
- `cancelDialogCancelText` (`default = "Close"`)
|
|
209
|
+
- `cancelDialogConfirmText` (`default = "Proceed"`)
|
|
210
|
+
- `enableSaveDialog` (`default = true`): whether to show alert dialog on press Save
|
|
211
|
+
- `saveDialogTitle` (`default = "Confirmation!"`)
|
|
212
|
+
- `saveDialogMessage` (`default = "Are you sure want to save?"`)
|
|
213
|
+
- `saveDialogCancelText` (`default = "Close"`)
|
|
214
|
+
- `saveDialogConfirmText` (`default = "Proceed"`)
|
|
215
|
+
- `fullScreenModalIOS` (`default = false`): whether to open editor in fullscreen modal
|
|
216
|
+
- `trimmingText` (`default = "Trimming video..."`): trimming text on the progress dialog
|
|
217
|
+
- `autoplay` (`default = false`): whether to autoplay media on load
|
|
218
|
+
- `jumpToPositionOnLoad` (optional): which time position should jump on media loaded (millisecond)
|
|
219
|
+
- `closeWhenFinish` (`default = true`): should editor close on finish trimming
|
|
220
|
+
- `enableCancelTrimming` (`default = true`): enable cancel trimming
|
|
221
|
+
- `cancelTrimmingButtonText` (`default = "Cancel"`)
|
|
222
|
+
- `enableCancelTrimmingDialog` (`default = true`)
|
|
223
|
+
- `cancelTrimmingDialogTitle` (`default = "Warning!"`)
|
|
224
|
+
- `cancelTrimmingDialogMessage` (`default = "Are you sure want to cancel trimming?"`)
|
|
225
|
+
- `cancelTrimmingDialogCancelText` (`default = "Close"`)
|
|
226
|
+
- `cancelTrimmingDialogConfirmText` (`default = "Proceed"`)
|
|
227
|
+
- `headerText` (optional)
|
|
228
|
+
- `headerTextSize` (`default = 16`)
|
|
229
|
+
- `headerTextColor` (`default = white`)
|
|
230
|
+
- `alertOnFailToLoad` (`default = true`)
|
|
231
|
+
- `alertOnFailTitle` (`default = "Error"`)
|
|
232
|
+
- `alertOnFailMessage` (`default = "Fail to load media. Possibly invalid file or no network connection"`)
|
|
233
|
+
- `alertOnFailCloseText` (`default = "Close"`)
|
|
174
234
|
|
|
175
235
|
If `saveToPhoto = true`, you must ensure that you have request permission to write to photo/gallery
|
|
176
236
|
- For Android: you need to have `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />` in AndroidManifest.xml
|
|
177
237
|
- For iOS: you need `NSPhotoLibraryUsageDescription` in Info.plist
|
|
178
238
|
|
|
179
|
-
|
|
239
|
+
If `openShareSheetOnFinish=true`, on Android you'll need to update `AndroidManifest.xml` like below:
|
|
240
|
+
```xml
|
|
241
|
+
</application>
|
|
242
|
+
...
|
|
243
|
+
<provider
|
|
244
|
+
android:name="androidx.core.content.FileProvider"
|
|
245
|
+
android:authorities="${applicationId}.provider"
|
|
246
|
+
android:exported="false"
|
|
247
|
+
android:grantUriPermissions="true">
|
|
248
|
+
<meta-data
|
|
249
|
+
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
250
|
+
android:resource="@xml/file_paths" />
|
|
251
|
+
</provider>
|
|
252
|
+
</application>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
If you face issue when building Android app related to `file_paths`, then you may need to create `res/xml/file_paths.xml`: with the following content:
|
|
256
|
+
```xml
|
|
257
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
258
|
+
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
|
259
|
+
<files-path name="internal_files" path="." />
|
|
260
|
+
<external-path name="external_files" path="." />
|
|
261
|
+
</paths>
|
|
262
|
+
```
|
|
180
263
|
|
|
181
|
-
|
|
264
|
+
## isValidFile(videoPath: string)
|
|
265
|
+
|
|
266
|
+
This method is to check if a path is a valid video/audio
|
|
267
|
+
|
|
268
|
+
## closeEditor()
|
|
269
|
+
|
|
270
|
+
Close Editor
|
|
182
271
|
|
|
183
272
|
## listFiles()
|
|
184
273
|
Return array of generated output files in app storage. (`Promise<string[]>`)
|
|
@@ -196,43 +285,43 @@ useEffect(() => {
|
|
|
196
285
|
const eventEmitter = new NativeEventEmitter(NativeModules.VideoTrim);
|
|
197
286
|
const subscription = eventEmitter.addListener('VideoTrim', (event) => {
|
|
198
287
|
switch (event.name) {
|
|
288
|
+
case 'onLoad': {
|
|
289
|
+
console.log('onLoadListener', event);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
199
292
|
case 'onShow': {
|
|
200
|
-
// on Dialog show
|
|
201
293
|
console.log('onShowListener', event);
|
|
202
294
|
break;
|
|
203
295
|
}
|
|
204
296
|
case 'onHide': {
|
|
205
|
-
// on Dialog hide
|
|
206
297
|
console.log('onHide', event);
|
|
207
298
|
break;
|
|
208
299
|
}
|
|
209
300
|
case 'onStartTrimming': {
|
|
210
|
-
// on start trimming
|
|
211
301
|
console.log('onStartTrimming', event);
|
|
212
302
|
break;
|
|
213
303
|
}
|
|
214
304
|
case 'onFinishTrimming': {
|
|
215
|
-
// on trimming is done
|
|
216
305
|
console.log('onFinishTrimming', event);
|
|
217
306
|
break;
|
|
218
307
|
}
|
|
219
308
|
case 'onCancelTrimming': {
|
|
220
|
-
// when user clicks Cancel button
|
|
221
309
|
console.log('onCancelTrimming', event);
|
|
222
310
|
break;
|
|
223
311
|
}
|
|
312
|
+
case 'onCancel': {
|
|
313
|
+
console.log('onCancel', event);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
224
316
|
case 'onError': {
|
|
225
|
-
// any error occured: invalid file, lack of permissions to write to photo/gallery, unexpected error...
|
|
226
317
|
console.log('onError', event);
|
|
227
318
|
break;
|
|
228
319
|
}
|
|
229
320
|
case 'onLog': {
|
|
230
|
-
// FFMPEG logs (while trimming)
|
|
231
321
|
console.log('onLog', event);
|
|
232
322
|
break;
|
|
233
323
|
}
|
|
234
324
|
case 'onStatistics': {
|
|
235
|
-
// FFMPEG stats (while trimming)
|
|
236
325
|
console.log('onStatistics', event);
|
|
237
326
|
break;
|
|
238
327
|
}
|
|
@@ -244,6 +333,52 @@ useEffect(() => {
|
|
|
244
333
|
};
|
|
245
334
|
}, []);
|
|
246
335
|
```
|
|
336
|
+
# Audio support
|
|
337
|
+
<img src="images/audio_android.jpg" width="200" />
|
|
338
|
+
<img src="images/audio_ios.jpg" width="200" />
|
|
339
|
+
|
|
340
|
+
For audio only you have to pass `type=audio` and `outputExt`:
|
|
341
|
+
```ts
|
|
342
|
+
showEditor(url, {
|
|
343
|
+
type: 'audio', // important
|
|
344
|
+
outputExt: 'wav', // important: any audio type for output file extension
|
|
345
|
+
})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
You must install FFMPEG version from "https" onwards, "min" version won't work. Eg.
|
|
349
|
+
```gradle
|
|
350
|
+
// Android: android/build.gradle > buildscript > ext
|
|
351
|
+
|
|
352
|
+
buildscript {
|
|
353
|
+
ext {
|
|
354
|
+
ffmpegKitPackage = "full"
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
// iOS:
|
|
359
|
+
|
|
360
|
+
FFMPEGKIT_PACKAGE=https npx pod-install ios
|
|
361
|
+
|
|
362
|
+
// or
|
|
363
|
+
|
|
364
|
+
FFMPEGKIT_PACKAGE=https pod install
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
# Cancel trimming
|
|
368
|
+
<img src="images/progress.jpg" width="200" />
|
|
369
|
+
<img src="images/cancel_confirm.jpg" width="200" />
|
|
370
|
+
|
|
371
|
+
While trimming, you can press Cancel to terminate the process.
|
|
372
|
+
|
|
373
|
+
Related props: `enableCancelTrimming, cancelTrimmingButtonText, enableCancelTrimmingDialog, cancelTrimmingDialogTitle, cancelTrimmingDialogMessage, cancelTrimmingDialogCancelText, cancelTrimmingDialogConfirmText`
|
|
374
|
+
|
|
375
|
+
# Fail to load media
|
|
376
|
+
<img src="images/fail_to_load_media.jpg" width="200" />
|
|
377
|
+
|
|
378
|
+
If there's error while loading media, there'll be a prompt
|
|
379
|
+
|
|
380
|
+
Related props: `alertOnFailToLoad, alertOnFailTitle, alertOnFailMessage, alertOnFailCloseText`
|
|
381
|
+
|
|
247
382
|
# FFMPEG Version
|
|
248
383
|
This library uses FFMPEG-Kit Android under the hood, by default FFMPEG-min is used, which gives smallest bundle size: https://github.com/arthenica/ffmpeg-kit#9-packages
|
|
249
384
|
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.videotrim">
|
|
2
2
|
|
|
3
3
|
<uses-permission android:name="android.permission.VIBRATE" />
|
|
4
|
+
|
|
5
|
+
<application>
|
|
6
|
+
<!-- FileProvider setup -->
|
|
7
|
+
<provider
|
|
8
|
+
android:name="androidx.core.content.FileProvider"
|
|
9
|
+
android:authorities="${applicationId}.provider"
|
|
10
|
+
android:exported="false"
|
|
11
|
+
android:grantUriPermissions="true">
|
|
12
|
+
<meta-data
|
|
13
|
+
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
14
|
+
android:resource="@xml/file_paths" />
|
|
15
|
+
</provider>
|
|
16
|
+
</application>
|
|
4
17
|
</manifest>
|