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.
Files changed (31) hide show
  1. package/README.md +168 -33
  2. package/android/src/main/AndroidManifest.xml +13 -0
  3. package/android/src/main/java/com/videotrim/VideoTrimModule.java +282 -75
  4. package/android/src/main/java/com/videotrim/enums/ErrorCode.java +10 -0
  5. package/android/src/main/java/com/videotrim/interfaces/VideoTrimListener.java +4 -1
  6. package/android/src/main/java/com/videotrim/utils/MediaMetadataUtil.java +75 -0
  7. package/android/src/main/java/com/videotrim/utils/StorageUtil.java +2 -2
  8. package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.java +26 -16
  9. package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +310 -81
  10. package/android/src/main/res/drawable/airpodsmax.xml +19 -0
  11. package/android/src/main/res/drawable/exclamationmark_triangle_fill.xml +15 -0
  12. package/android/src/main/res/drawable/thumb_container_bg.xml +8 -0
  13. package/android/src/main/res/layout/video_trimmer_view.xml +71 -4
  14. package/android/src/main/res/xml/file_paths.xml +5 -0
  15. package/ios/AssetLoader.swift +99 -0
  16. package/ios/ErrorCode.swift +16 -0
  17. package/ios/ProgressAlertController.swift +100 -0
  18. package/ios/VideoTrim.mm +4 -2
  19. package/ios/VideoTrim.swift +472 -177
  20. package/ios/VideoTrimmer.swift +16 -10
  21. package/ios/VideoTrimmerViewController.swift +191 -22
  22. package/lib/commonjs/index.js +25 -55
  23. package/lib/commonjs/index.js.map +1 -1
  24. package/lib/module/index.js +24 -55
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/typescript/index.d.ts +215 -9
  27. package/lib/typescript/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/index.tsx +229 -66
  30. package/android/src/main/java/iknow/android/utils/BuildConfig.java +0 -18
  31. 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
- **Note that for both Android and iOS you have to try on real device**
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 { isValidVideo, showEditor } from 'react-native-video-trim';
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
- isValidVideo(result.assets![0]?.uri || '').then((res) =>
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
- isValidVideo('invalid file path').then((res) => console.log(res));
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
- - `saveToPhoto` (optional, `default = true`): whether to save video to photo/gallery after editing
157
- - `removeAfterSavedToPhoto` (optional, `default = false`): whether to remove output file from storage after saved to Photo
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` (optional): minimum duration for the trimmed video
160
- - `cancelButtonText` (optional): text of left button in Editor dialog
161
- - `saveButtonText` (optional): text of right button in Editor dialog
162
- - `enableCancelDialog` (optional, `default = true`): whether to show alert dialog on press Cancel
163
- - `cancelDialogTitle` (optional, `default = "Warning!"`)
164
- - `cancelDialogMessage` (optional, `default = "Are you sure want to cancel?"`)
165
- - `cancelDialogCancelText` (optional, `default = "Close"`)
166
- - `cancelDialogConfirmText` (optional, `default = "Proceed"`)
167
- - `enableSaveDialog` (optional, `default = true`): whether to show alert dialog on press Save
168
- - `saveDialogTitle` (optional, `default = "Confirmation!"`)
169
- - `saveDialogMessage` (optional, `default = "Are you sure want to save?"`)
170
- - `saveDialogCancelText` (optional, `default = "Close"`)
171
- - `saveDialogConfirmText` (optional, `default = "Proceed"`)
172
- - `fullScreenModalIOS` (optional, `default = false`): whether to open editor in fullscreen modal
173
- - `trimmingText` (optional, `default = "Trimming video..."`): trimming text on the progress dialog
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
- ## isValidVideo(videoPath: string)
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
- This method is to check if a path is a actual video and editable. It returns `Promise<boolean>`
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>