react-native-video-trim 4.0.0 → 5.0.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 (95) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +124 -83
  3. package/VideoTrim.podspec +4 -4
  4. package/android/build.gradle +15 -54
  5. package/android/gradle.properties +2 -0
  6. package/android/src/main/AndroidManifest.xml +1 -1
  7. package/android/src/main/java/com/videotrim/VideoTrimModule.kt +660 -0
  8. package/android/src/main/java/com/videotrim/VideoTrimPackage.kt +33 -0
  9. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/enums/ErrorCode.java +1 -1
  10. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/IVideoTrimmerView.java +1 -1
  11. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/VideoTrimListener.java +5 -4
  12. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/MediaMetadataUtil.java +1 -1
  13. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/StorageUtil.java +1 -1
  14. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/VideoTrimmerUtil.java +49 -39
  15. package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/widgets/VideoTrimmerView.java +49 -46
  16. package/ios/AssetLoader.h +19 -0
  17. package/ios/AssetLoader.mm +87 -0
  18. package/ios/ErrorCode.h +9 -0
  19. package/ios/ProgressAlertController.h +15 -0
  20. package/ios/ProgressAlertController.mm +78 -0
  21. package/ios/VideoTrim.h +31 -0
  22. package/ios/VideoTrim.mm +663 -0
  23. package/ios/VideoTrimmer.h +67 -0
  24. package/ios/VideoTrimmer.mm +863 -0
  25. package/ios/VideoTrimmerThumb.h +23 -0
  26. package/ios/VideoTrimmerThumb.mm +175 -0
  27. package/ios/VideoTrimmerViewController.h +52 -0
  28. package/ios/VideoTrimmerViewController.mm +533 -0
  29. package/lib/module/NativeVideoTrim.js +5 -0
  30. package/lib/module/NativeVideoTrim.js.map +1 -0
  31. package/lib/module/index.js +59 -34
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/typescript/src/NativeVideoTrim.d.ts +107 -0
  34. package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -0
  35. package/lib/typescript/src/index.d.ts +21 -10
  36. package/lib/typescript/src/index.d.ts.map +1 -1
  37. package/package.json +15 -18
  38. package/src/NativeVideoTrim.ts +113 -0
  39. package/src/index.tsx +68 -39
  40. package/android/CMakeLists.txt +0 -24
  41. package/android/src/main/cpp/cpp-adapter.cpp +0 -6
  42. package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrim.kt +0 -629
  43. package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrimPackage.kt +0 -22
  44. package/ios/AssetLoader.swift +0 -99
  45. package/ios/ErrorCode.swift +0 -17
  46. package/ios/ProgressAlertController.swift +0 -100
  47. package/ios/VideoTrim.swift +0 -60
  48. package/ios/VideoTrimImpl.swift +0 -860
  49. package/ios/VideoTrimmer.swift +0 -872
  50. package/ios/VideoTrimmerThumb.swift +0 -175
  51. package/ios/VideoTrimmerViewController.swift +0 -557
  52. package/lib/module/VideoTrim.nitro.js +0 -4
  53. package/lib/module/VideoTrim.nitro.js.map +0 -1
  54. package/lib/typescript/src/VideoTrim.nitro.d.ts +0 -240
  55. package/lib/typescript/src/VideoTrim.nitro.d.ts.map +0 -1
  56. package/nitrogen/generated/android/c++/JEditorConfig.hpp +0 -229
  57. package/nitrogen/generated/android/c++/JFileValidationResult.hpp +0 -61
  58. package/nitrogen/generated/android/c++/JFunc_void.hpp +0 -74
  59. package/nitrogen/generated/android/c++/JFunc_void_std__string_std__unordered_map_std__string__std__string_.hpp +0 -89
  60. package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +0 -131
  61. package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +0 -67
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +0 -70
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/FileValidationResult.kt +0 -28
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void.kt +0 -80
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void_std__string_std__unordered_map_std__string__std__string_.kt +0 -80
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +0 -82
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/videotrimOnLoad.kt +0 -35
  68. package/nitrogen/generated/android/videotrim+autolinking.cmake +0 -78
  69. package/nitrogen/generated/android/videotrim+autolinking.gradle +0 -27
  70. package/nitrogen/generated/android/videotrimOnLoad.cpp +0 -50
  71. package/nitrogen/generated/android/videotrimOnLoad.hpp +0 -25
  72. package/nitrogen/generated/ios/VideoTrim+autolinking.rb +0 -60
  73. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +0 -88
  74. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +0 -331
  75. package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +0 -53
  76. package/nitrogen/generated/ios/VideoTrimAutolinking.mm +0 -33
  77. package/nitrogen/generated/ios/VideoTrimAutolinking.swift +0 -25
  78. package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.cpp +0 -11
  79. package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +0 -116
  80. package/nitrogen/generated/ios/swift/EditorConfig.swift +0 -519
  81. package/nitrogen/generated/ios/swift/FileValidationResult.swift +0 -57
  82. package/nitrogen/generated/ios/swift/Func_void.swift +0 -46
  83. package/nitrogen/generated/ios/swift/Func_void_FileValidationResult.swift +0 -46
  84. package/nitrogen/generated/ios/swift/Func_void_bool.swift +0 -46
  85. package/nitrogen/generated/ios/swift/Func_void_double.swift +0 -46
  86. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +0 -46
  87. package/nitrogen/generated/ios/swift/Func_void_std__string_std__unordered_map_std__string__std__string_.swift +0 -54
  88. package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +0 -46
  89. package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +0 -53
  90. package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +0 -222
  91. package/nitrogen/generated/shared/c++/EditorConfig.hpp +0 -245
  92. package/nitrogen/generated/shared/c++/FileValidationResult.hpp +0 -77
  93. package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +0 -26
  94. package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +0 -76
  95. package/src/VideoTrim.nitro.ts +0 -244
@@ -1,6 +1,7 @@
1
- package com.margelo.nitro.videotrim.interfaces;
1
+ package com.videotrim.interfaces;
2
2
 
3
- import com.margelo.nitro.videotrim.enums.ErrorCode;
3
+ import com.facebook.react.bridge.ReadableMap;
4
+ import com.videotrim.enums.ErrorCode;
4
5
 
5
6
  import java.util.Map;
6
7
 
@@ -12,6 +13,6 @@ public interface VideoTrimListener {
12
13
  void onError(String errorMessage, ErrorCode errorCode);
13
14
  void onCancel();
14
15
  void onSave();
15
- void onLog(Map<String, String> log);
16
- void onStatistics(Map<String, String> statistics);
16
+ void onLog(ReadableMap log);
17
+ void onStatistics(ReadableMap statistics);
17
18
  }
@@ -1,4 +1,4 @@
1
- package com.margelo.nitro.videotrim.utils;
1
+ package com.videotrim.utils;
2
2
 
3
3
  import android.media.MediaMetadataRetriever;
4
4
  import android.util.Log;
@@ -1,4 +1,4 @@
1
- package com.margelo.nitro.videotrim.utils;
1
+ package com.videotrim.utils;
2
2
 
3
3
  import android.content.ContentValues;
4
4
  import android.content.Context;
@@ -1,4 +1,4 @@
1
- package com.margelo.nitro.videotrim.utils;
1
+ package com.videotrim.utils;
2
2
 
3
3
  import android.annotation.SuppressLint;
4
4
  import android.graphics.Bitmap;
@@ -9,12 +9,16 @@ import com.arthenica.ffmpegkit.FFmpegKit;
9
9
  import com.arthenica.ffmpegkit.FFmpegSession;
10
10
  import com.arthenica.ffmpegkit.ReturnCode;
11
11
  import com.arthenica.ffmpegkit.SessionState;
12
- import com.margelo.nitro.videotrim.enums.ErrorCode;
13
- import com.margelo.nitro.videotrim.interfaces.VideoTrimListener;
12
+ import com.facebook.react.bridge.Arguments;
13
+ import com.facebook.react.bridge.WritableMap;
14
+ import com.videotrim.enums.ErrorCode;
15
+ import com.videotrim.interfaces.VideoTrimListener;
14
16
 
15
17
  import java.text.SimpleDateFormat;
18
+ import java.util.ArrayList;
16
19
  import java.util.Date;
17
20
  import java.util.HashMap;
21
+ import java.util.List;
18
22
  import java.util.Map;
19
23
  import java.util.TimeZone;
20
24
 
@@ -43,7 +47,7 @@ public class VideoTrimmerUtil {
43
47
  public static final int THUMB_WIDTH = UnitConverter.dpToPx(25); // x2 for better resolution
44
48
  private static final int THUMB_RESOLUTION_RES = 2; // double thumb resolution for better quality
45
49
 
46
- public static FFmpegSession trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, final VideoTrimListener callback) {
50
+ public static FFmpegSession trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, boolean enableRotation, double rotationAngle, final VideoTrimListener callback) {
47
51
  // Get the current date and time
48
52
  Date currentDate = new Date();
49
53
 
@@ -55,26 +59,32 @@ public class VideoTrimmerUtil {
55
59
  // Format the current date and time
56
60
  String formattedDateTime = dateFormat.format(currentDate);
57
61
 
58
- String[] cmds = {
59
- "-ss",
60
- startMs + "ms",
61
- "-to",
62
- endMs + "ms",
63
- "-i",
64
- inputFile,
65
- "-c",
66
- "copy",
67
- "-metadata",
68
- "creation_time=" + formattedDateTime,
69
- outputFile
70
- };
71
- Log.d(TAG,"Command111: " + String.join(",", cmds));
72
-
73
- FFmpegSession s = FFmpegKit.execute("-protocols");
74
- Log.d(TAG, "1111getOutput: " + s.getOutput());
75
- Log.d(TAG, "1111getAllLogs: " + s.getAllLogs());
76
-
77
- return FFmpegKit.executeWithArgumentsAsync(cmds, session -> {
62
+ // create list to store commands
63
+ List<String> cmds = new ArrayList<>();
64
+ cmds.add("-ss");
65
+ cmds.add(startMs + "ms");
66
+ cmds.add("-to");
67
+ cmds.add(endMs + "ms");
68
+
69
+ if (enableRotation) {
70
+ // add "-display_rotation" and rotation angle to the command, contact, not creating new
71
+ cmds.add("-display_rotation");
72
+ cmds.add(String.valueOf(rotationAngle));
73
+ }
74
+
75
+ cmds.add("-i");
76
+ cmds.add(inputFile);
77
+ cmds.add("-c");
78
+ cmds.add("copy");
79
+ cmds.add("-metadata");
80
+ cmds.add("creation_time=" + formattedDateTime);
81
+ cmds.add(outputFile);
82
+
83
+ String[] command = cmds.toArray(new String[0]);
84
+
85
+ Log.d(TAG,"Command: " + String.join(",", command));
86
+
87
+ return FFmpegKit.executeWithArgumentsAsync(command, session -> {
78
88
  SessionState state = session.getState();
79
89
  ReturnCode returnCode = session.getReturnCode();
80
90
  if (ReturnCode.isSuccess(session.getReturnCode())) {
@@ -91,11 +101,11 @@ public class VideoTrimmerUtil {
91
101
  }, log -> {
92
102
  Log.d(TAG, "FFmpeg process started with log " + log.getMessage());
93
103
 
94
- Map<String, String> map = new HashMap<>();
95
- map.put("level", String.valueOf(log.getLevel().getValue()));
96
- map.put("message", log.getMessage());
97
- map.put("sessionId", String.valueOf(log.getSessionId()));
98
- map.put("logStr", log.toString());
104
+ WritableMap map = Arguments.createMap();
105
+ map.putInt("level", log.getLevel().getValue());
106
+ map.putString("message", log.getMessage());
107
+ map.putDouble("sessionId", log.getSessionId());
108
+ map.putString("logStr", log.toString());
99
109
 
100
110
  callback.onLog(map);
101
111
  }, statistics -> {
@@ -106,16 +116,16 @@ public class VideoTrimmerUtil {
106
116
  callback.onTrimmingProgress(Math.min(Math.max(completePercentage, 0), 100));
107
117
  }
108
118
 
109
- Map<String, String> map = new HashMap<>();
110
- map.put("sessionId", String.valueOf(statistics.getSessionId()));
111
- map.put("videoFrameNumber", String.valueOf(statistics.getVideoFrameNumber()));
112
- map.put("videoFps", String.valueOf(statistics.getVideoFps()));
113
- map.put("videoQuality", String.valueOf(statistics.getVideoQuality()));
114
- map.put("size", String.valueOf(statistics.getSize()));
115
- map.put("time", String.valueOf(statistics.getTime()));
116
- map.put("bitrate", String.valueOf(statistics.getBitrate()));
117
- map.put("speed", String.valueOf(statistics.getSpeed()));
118
- map.put("statisticsStr", statistics.toString());
119
+ WritableMap map = Arguments.createMap();
120
+ map.putDouble("sessionId", statistics.getSessionId());
121
+ map.putInt("videoFrameNumber", statistics.getVideoFrameNumber());
122
+ map.putDouble("videoFps", statistics.getVideoFps());
123
+ map.putDouble("videoQuality", statistics.getVideoQuality());
124
+ map.putDouble("size", statistics.getSize());
125
+ map.putDouble("time", statistics.getTime());
126
+ map.putDouble("bitrate", statistics.getBitrate());
127
+ map.putDouble("speed", statistics.getSpeed());
128
+ map.putString("statisticsStr", statistics.toString());
119
129
  callback.onStatistics(map);
120
130
  });
121
131
  }
@@ -1,13 +1,14 @@
1
- package com.margelo.nitro.videotrim.widgets;
1
+ package com.videotrim.widgets;
2
2
 
3
3
  import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
4
- import static com.margelo.nitro.videotrim.utils.VideoTrimmerUtil.RECYCLER_VIEW_PADDING;
5
- import static com.margelo.nitro.videotrim.utils.VideoTrimmerUtil.VIDEO_FRAMES_WIDTH;
4
+ import static com.videotrim.utils.VideoTrimmerUtil.RECYCLER_VIEW_PADDING;
5
+ import static com.videotrim.utils.VideoTrimmerUtil.VIDEO_FRAMES_WIDTH;
6
6
 
7
7
  import android.content.Context;
8
8
  import android.content.pm.ActivityInfo;
9
9
  import android.content.res.Configuration;
10
10
  import android.graphics.Bitmap;
11
+ import android.graphics.Color;
11
12
  import android.graphics.drawable.GradientDrawable;
12
13
  import android.media.MediaMetadataRetriever;
13
14
  import android.media.MediaPlayer;
@@ -34,17 +35,18 @@ import androidx.appcompat.app.AlertDialog;
34
35
 
35
36
  import com.arthenica.ffmpegkit.FFmpegSession;
36
37
  import com.facebook.react.bridge.ReactApplicationContext;
37
- import com.margelo.nitro.videotrim.EditorConfig;
38
- import com.margelo.nitro.videotrim.R;
39
- import com.margelo.nitro.videotrim.enums.ErrorCode;
40
- import com.margelo.nitro.videotrim.interfaces.IVideoTrimmerView;
41
- import com.margelo.nitro.videotrim.interfaces.VideoTrimListener;
42
- import com.margelo.nitro.videotrim.utils.MediaMetadataUtil;
43
- import com.margelo.nitro.videotrim.utils.StorageUtil;
44
- import com.margelo.nitro.videotrim.utils.VideoTrimmerUtil;
38
+ import com.facebook.react.bridge.ReadableMap;
39
+ import com.videotrim.R;
40
+ import com.videotrim.enums.ErrorCode;
41
+ import com.videotrim.interfaces.IVideoTrimmerView;
42
+ import com.videotrim.interfaces.VideoTrimListener;
43
+ import com.videotrim.utils.MediaMetadataUtil;
44
+ import com.videotrim.utils.StorageUtil;
45
+ import com.videotrim.utils.VideoTrimmerUtil;
45
46
 
46
47
  import java.io.IOException;
47
48
  import java.util.Locale;
49
+ import java.util.Objects;
48
50
 
49
51
  import iknow.android.utils.DeviceUtil;
50
52
  import iknow.android.utils.thread.BackgroundExecutor;
@@ -87,6 +89,9 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
87
89
  private RelativeLayout trimmerContainerWrapper;
88
90
 
89
91
  private long startTime = 0, endTime = 0;
92
+ private boolean enableRotation = false;
93
+ private double rotationAngle = 0.0;
94
+
90
95
  private Vibrator vibrator;
91
96
  private boolean didClampWhilePanning = false;
92
97
 
@@ -116,16 +121,16 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
116
121
  private String alertOnFailCloseText = "Close";
117
122
  private View currentSelectedhandle;
118
123
 
119
- public VideoTrimmerView(ReactApplicationContext context, EditorConfig config, AttributeSet attrs) {
124
+ public VideoTrimmerView(ReactApplicationContext context, ReadableMap config, AttributeSet attrs) {
120
125
  this(context, attrs, 0, config);
121
126
  }
122
127
 
123
- public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs, int defStyleAttr, EditorConfig config) {
128
+ public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs, int defStyleAttr, ReadableMap config) {
124
129
  super(context, attrs, defStyleAttr);
125
130
  init(context, config);
126
131
  }
127
132
 
128
- private void init(ReactApplicationContext context, EditorConfig config) {
133
+ private void init(ReactApplicationContext context, ReadableMap config) {
129
134
  this.mContext = context;
130
135
 
131
136
  context.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
@@ -374,6 +379,8 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
374
379
  mDuration,
375
380
  startTime,
376
381
  endTime,
382
+ enableRotation,
383
+ rotationAngle,
377
384
  mOnTrimVideoListener
378
385
  );
379
386
  }
@@ -445,51 +452,47 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
445
452
  return screenWidth;
446
453
  }
447
454
 
448
- private void configure(EditorConfig config) {
449
- if (config.getMaxDuration() > 0) {
450
- mMaxDuration = (long) Math.max(0, config.getMaxDuration() * 1000L);
455
+ private void configure(ReadableMap config) {
456
+ if (config.hasKey("maxDuration") && config.getDouble("maxDuration") > 0) {
457
+ mMaxDuration = (long) Math.max(0, config.getDouble("maxDuration") * 1000L);
451
458
  }
452
459
 
453
- if (config.getMinDuration() > 0) {
454
- mMinDuration = (long) Math.max(1000L, config.getMinDuration() * 1000L);
460
+ if (config.hasKey("minDuration") && config.getDouble("minDuration") > 0) {
461
+ mMinDuration = (long) Math.max(1000L, config.getDouble("minDuration") * 1000L);
455
462
  }
456
463
 
457
- cancelBtn.setText(config.getCancelButtonText());
458
-
459
- saveBtn.setText(config.getSaveButtonText());
460
-
461
- isVideoType = config.getType().equals("video");
464
+ cancelBtn.setText(config.getString("cancelButtonText"));
465
+ saveBtn.setText(config.getString("saveButtonText"));
466
+ isVideoType = config.hasKey("type") && Objects.equals(config.getString("type"), "video");
467
+ System.out.println("1111 isVideoType: " + isVideoType);
462
468
 
463
- mOutputExt = config.getOutputExt();
469
+ mOutputExt = config.hasKey("outputExt") ? config.getString("outputExt") : "mp4";
464
470
  if (!isVideoType) {
465
471
  mOutputExt = "wav";
466
472
  }
473
+ enableHapticFeedback = config.hasKey("enableHapticFeedback") && config.getBoolean("enableHapticFeedback");
474
+ autoplay = config.hasKey("autoplay") && config.getBoolean("autoplay");
467
475
 
468
- enableHapticFeedback = config.getEnableHapticFeedback();
469
- autoplay = config.getAutoplay();
470
-
471
- if (config.getJumpToPositionOnLoad() > 0) {
472
- jumpToPositionOnLoad = (long) config.getJumpToPositionOnLoad();
476
+ if (config.hasKey("jumpToPositionOnLoad") && config.getDouble("jumpToPositionOnLoad") > 0) {
477
+ jumpToPositionOnLoad = (long) Math.max(0, config.getDouble("jumpToPositionOnLoad") * 1000L);
473
478
  }
479
+ headerText.setText(config.hasKey("headerText") ? config.getString("headerText") : "");
474
480
 
475
- headerText.setText(config.getHeaderText());
476
-
477
- int textSize = (int) config.getHeaderTextSize();
478
- if (textSize < 0) {
479
- textSize = 16;
480
- }
481
- headerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
482
- headerText.setTextColor((int) config.getHeaderTextColor());
483
-
484
- headerView.setVisibility(View.VISIBLE);
485
-
486
- alertOnFailToLoad = config.getAlertOnFailToLoad();
487
-
488
- alertOnFailTitle = config.getAlertOnFailTitle();
481
+ int textSize = config.hasKey("headerTextSize") ? config.getInt("headerTextSize") : 16;
482
+ if (textSize < 0) {
483
+ textSize = 16;
484
+ }
489
485
 
490
- alertOnFailMessage = config.getAlertOnFailMessage();
486
+ headerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
487
+ headerText.setTextColor(config.hasKey("headerTextColor") ? config.getInt("headerTextColor") : Color.BLACK);
491
488
 
492
- alertOnFailCloseText = config.getAlertOnFailCloseText();
489
+ headerView.setVisibility(View.VISIBLE);
490
+ alertOnFailToLoad = config.hasKey("alertOnFailToLoad") && config.getBoolean("alertOnFailToLoad");
491
+ alertOnFailTitle = config.hasKey("alertOnFailTitle") ? config.getString("alertOnFailTitle") : "Error";
492
+ alertOnFailMessage = config.hasKey("alertOnFailMessage") ? config.getString("alertOnFailMessage") : "Fail to load media. Possibly invalid file or no network connection";
493
+ alertOnFailCloseText = config.hasKey("alertOnFailCloseText") ? config.getString("alertOnFailCloseText") : "Close";
494
+ enableRotation = config.hasKey("enableRotation") && config.getBoolean("enableRotation");
495
+ rotationAngle = config.hasKey("rotationAngle") ? config.getDouble("rotationAngle") : 0.0;
493
496
  }
494
497
 
495
498
  private void startTimingRunnable() {
@@ -0,0 +1,19 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <AVFoundation/AVFoundation.h>
3
+
4
+ @class AssetLoader;
5
+
6
+ @protocol AssetLoaderDelegate <NSObject>
7
+ @optional
8
+ - (void)assetLoaderDidSucceed:(AssetLoader *)assetLoader;
9
+ - (void)assetLoader:(AssetLoader *)assetLoader didFailWithError:(NSError *)error forKey:(NSString *)key;
10
+ @end
11
+
12
+ @interface AssetLoader : NSObject
13
+
14
+ @property (nonatomic, weak) id<AssetLoaderDelegate> delegate;
15
+ @property (nonatomic, strong, readonly) AVURLAsset *asset;
16
+
17
+ - (void)loadAssetWithURL:(NSURL *)url isVideoType:(BOOL)isVideoType;
18
+
19
+ @end
@@ -0,0 +1,87 @@
1
+ #import "AssetLoader.h"
2
+
3
+ @interface AssetLoader ()
4
+ @property (nonatomic, strong) AVURLAsset *asset;
5
+ @end
6
+
7
+ @implementation AssetLoader
8
+
9
+ - (void)loadAssetWithURL:(NSURL *)url isVideoType:(BOOL)isVideoType {
10
+ NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey: @YES };
11
+ self.asset = [AVURLAsset URLAssetWithURL:url options:options];
12
+ NSArray *keys = @[ @"duration", @"tracks" ];
13
+ [self.asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
14
+ [self assetLoaded:isVideoType];
15
+ }];
16
+ }
17
+
18
+ - (void)assetLoaded:(BOOL)isVideoType {
19
+ NSArray *keys = @[ @"duration", @"tracks" ];
20
+ for (NSString *key in keys) {
21
+ NSError *error = nil;
22
+ AVKeyValueStatus status = [self.asset statusOfValueForKey:key error:&error];
23
+ if (status == AVKeyValueStatusFailed) {
24
+ if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
25
+ [self.delegate assetLoader:self didFailWithError:error forKey:key];
26
+ }
27
+ return;
28
+ } else if (status == AVKeyValueStatusCancelled) {
29
+ NSError *cancelError = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@ loading was cancelled", key] }];
30
+ if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
31
+ [self.delegate assetLoader:self didFailWithError:cancelError forKey:key];
32
+ }
33
+ return;
34
+ } else if (status != AVKeyValueStatusLoaded) {
35
+ NSError *unknownError = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@ is in an unknown state", key] }];
36
+ if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
37
+ [self.delegate assetLoader:self didFailWithError:unknownError forKey:key];
38
+ }
39
+ return;
40
+ }
41
+ }
42
+ if (isVideoType) {
43
+ [self processAssetTracks];
44
+ } else {
45
+ if ([self.delegate respondsToSelector:@selector(assetLoaderDidSucceed:)]) {
46
+ [self.delegate assetLoaderDidSucceed:self];
47
+ }
48
+ }
49
+ }
50
+
51
+ - (void)processAssetTracks {
52
+ NSArray *videoTracks = [self.asset tracksWithMediaType:AVMediaTypeVideo];
53
+ AVAssetTrack *videoTrack = videoTracks.firstObject;
54
+ if (!videoTrack) {
55
+ NSError *error = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"No video tracks found" }];
56
+ if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
57
+ [self.delegate assetLoader:self didFailWithError:error forKey:@"tracks"];
58
+ }
59
+ return;
60
+ }
61
+ NSArray *trackKeys = @[ @"naturalSize", @"preferredTransform" ];
62
+ [videoTrack loadValuesAsynchronouslyForKeys:trackKeys completionHandler:^{
63
+ [self trackPropertiesLoaded:videoTrack];
64
+ }];
65
+ }
66
+
67
+ - (void)trackPropertiesLoaded:(AVAssetTrack *)track {
68
+ NSError *error = nil;
69
+ AVKeyValueStatus naturalSizeStatus = [track statusOfValueForKey:@"naturalSize" error:&error];
70
+ AVKeyValueStatus preferredTransformStatus = [track statusOfValueForKey:@"preferredTransform" error:&error];
71
+ if (naturalSizeStatus == AVKeyValueStatusLoaded && preferredTransformStatus == AVKeyValueStatusLoaded) {
72
+ CGSize naturalSize = track.naturalSize;
73
+ CGAffineTransform preferredTransform = track.preferredTransform;
74
+ NSLog(@"Natural size: %@", NSStringFromCGSize(naturalSize));
75
+ NSLog(@"Preferred transform: %@", NSStringFromCGAffineTransform(preferredTransform));
76
+ if ([self.delegate respondsToSelector:@selector(assetLoaderDidSucceed:)]) {
77
+ [self.delegate assetLoaderDidSucceed:self];
78
+ }
79
+ } else {
80
+ if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
81
+ NSString *failedKey = (naturalSizeStatus != AVKeyValueStatusLoaded) ? @"naturalSize" : @"preferredTransform";
82
+ [self.delegate assetLoader:self didFailWithError:error forKey:failedKey];
83
+ }
84
+ }
85
+ }
86
+
87
+ @end
@@ -0,0 +1,9 @@
1
+ // ErrorCode.h
2
+ typedef NS_ENUM(NSInteger, ErrorCode) {
3
+ ErrorCodeTrimmingFailed,
4
+ ErrorCodeFailToLoadMedia,
5
+ ErrorCodeFailToSaveToPhoto,
6
+ ErrorCodeFailToShare,
7
+ ErrorCodeNoPhotoPermission,
8
+ ErrorCodeInvalidFilePath
9
+ };
@@ -0,0 +1,15 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ @interface ProgressAlertController : UIViewController
4
+
5
+ @property (nonatomic, copy) void (^onDismiss)(void);
6
+ @property (nonatomic, strong) UILabel *titleLabel;
7
+ @property (nonatomic, strong) UIProgressView *progressBar;
8
+ @property (nonatomic, strong) UIButton *actionButton;
9
+
10
+ - (void)setTitle:(NSString *)title;
11
+ - (void)setCancelTitle:(NSString *)title;
12
+ - (void)setProgress:(float)progress;
13
+ - (void)showCancelBtn;
14
+
15
+ @end
@@ -0,0 +1,78 @@
1
+ #import "ProgressAlertController.h"
2
+
3
+ @implementation ProgressAlertController
4
+
5
+ - (void)viewDidLoad {
6
+ [super viewDidLoad];
7
+ [self setupBackground];
8
+ [self setupAlertView];
9
+ }
10
+
11
+ - (void)setupBackground {
12
+ self.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
13
+ }
14
+
15
+ - (void)setupAlertView {
16
+ UIView *alertView = [[UIView alloc] init];
17
+ alertView.backgroundColor = [UIColor colorWithRed:28/255.0 green:28/255.0 blue:30/255.0 alpha:1.0];
18
+ alertView.layer.cornerRadius = 12;
19
+ alertView.translatesAutoresizingMaskIntoConstraints = NO;
20
+ [self.view addSubview:alertView];
21
+ [NSLayoutConstraint activateConstraints:@[
22
+ [alertView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
23
+ [alertView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
24
+ [alertView.widthAnchor constraintEqualToConstant:270]
25
+ ]];
26
+ self.titleLabel = [[UILabel alloc] init];
27
+ self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
28
+ self.titleLabel.textAlignment = NSTextAlignmentCenter;
29
+ self.titleLabel.font = [UIFont systemFontOfSize:18];
30
+ self.titleLabel.numberOfLines = 0;
31
+ self.titleLabel.textColor = [UIColor whiteColor];
32
+ [alertView addSubview:self.titleLabel];
33
+ self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
34
+ self.progressBar.translatesAutoresizingMaskIntoConstraints = NO;
35
+ [alertView addSubview:self.progressBar];
36
+ self.actionButton = [UIButton buttonWithType:UIButtonTypeSystem];
37
+ [self.actionButton setTitle:@"Cancel" forState:UIControlStateNormal];
38
+ [self.actionButton setTitleColor:[UIColor systemPinkColor] forState:UIControlStateNormal];
39
+ self.actionButton.titleLabel.font = [UIFont systemFontOfSize:16];
40
+ [self.actionButton addTarget:self action:@selector(dismissAlert) forControlEvents:UIControlEventTouchUpInside];
41
+ self.actionButton.translatesAutoresizingMaskIntoConstraints = NO;
42
+ self.actionButton.hidden = YES;
43
+ [alertView addSubview:self.actionButton];
44
+ [NSLayoutConstraint activateConstraints:@[
45
+ [self.titleLabel.topAnchor constraintEqualToAnchor:alertView.topAnchor constant:16],
46
+ [self.titleLabel.leadingAnchor constraintEqualToAnchor:alertView.leadingAnchor constant:16],
47
+ [self.titleLabel.trailingAnchor constraintEqualToAnchor:alertView.trailingAnchor constant:-16],
48
+ [self.progressBar.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:16],
49
+ [self.progressBar.leadingAnchor constraintEqualToAnchor:alertView.leadingAnchor constant:16],
50
+ [self.progressBar.trailingAnchor constraintEqualToAnchor:alertView.trailingAnchor constant:-16],
51
+ [self.actionButton.topAnchor constraintEqualToAnchor:self.progressBar.bottomAnchor constant:16],
52
+ [self.actionButton.bottomAnchor constraintEqualToAnchor:alertView.bottomAnchor constant:-16],
53
+ [self.actionButton.centerXAnchor constraintEqualToAnchor:alertView.centerXAnchor]
54
+ ]];
55
+ }
56
+
57
+ - (void)dismissAlert {
58
+ if (self.onDismiss) self.onDismiss();
59
+ }
60
+
61
+ - (void)setTitle:(NSString *)text {
62
+ self.titleLabel.text = text;
63
+ }
64
+
65
+ - (void)setCancelTitle:(NSString *)text {
66
+ [self.actionButton setTitle:text forState:UIControlStateNormal];
67
+ }
68
+
69
+ - (void)setProgress:(float)progress {
70
+ [self.progressBar setProgress:progress animated:YES];
71
+ }
72
+
73
+ - (void)showCancelBtn {
74
+ [self.view layoutIfNeeded];
75
+ self.actionButton.hidden = NO;
76
+ }
77
+
78
+ @end
@@ -0,0 +1,31 @@
1
+ #import <VideoTrimSpec/VideoTrimSpec.h>
2
+ #import <AVFoundation/AVFoundation.h>
3
+ #import <UIKit/UIKit.h>
4
+ #import "AssetLoader.h"
5
+
6
+ @class VideoTrimmerViewController;
7
+ @class ProgressAlertController;
8
+
9
+ @interface VideoTrim : NativeVideoTrimSpecBase <NativeVideoTrimSpec, AssetLoaderDelegate, UIDocumentPickerDelegate>
10
+
11
+ @property (nonatomic, strong) NSString *FILE_PREFIX;
12
+ @property (nonatomic, strong) NSString *BEFORE_TRIM_PREFIX;
13
+ @property (nonatomic, assign) BOOL isShowing;
14
+ @property (nonatomic, strong) VideoTrimmerViewController *vc;
15
+ @property (nonatomic, strong) NSURL *outputFile;
16
+ @property (nonatomic, assign) BOOL isVideoType;
17
+ //@property (nonatomic, assign) JS::NativeVideoTrim::EditorConfig editorConfig;
18
+
19
+ // Helper methods
20
+ - (NSURL *)renameFileAtURL:(NSURL *)url newName:(NSString *)newName;
21
+ - (void)trimWithViewController:(VideoTrimmerViewController *)viewController
22
+ inputFile:(NSURL *)inputFile
23
+ videoDuration:(double)videoDuration
24
+ startTime:(double)startTime
25
+ endTime:(double)endTime;
26
+ - (void)saveFileToFilesApp:(NSURL *)fileURL;
27
+ - (void)shareFile:(NSURL *)fileURL;
28
+ - (int)deleteFileAtURL:(NSURL *)url;
29
+ - (void)onError:(NSString *)message code:(NSString *)code;
30
+
31
+ @end