react-native-video-trim 2.0.0 → 2.1.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 +33 -9
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/java/com/videotrim/VideoTrimModule.java +185 -64
- package/android/src/main/java/com/videotrim/enums/ErrorCode.java +11 -0
- package/android/src/main/java/com/videotrim/interfaces/VideoTrimListener.java +2 -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 +15 -8
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +239 -70
- 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 +51 -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/VideoTrim.mm +4 -2
- package/ios/VideoTrim.swift +380 -167
- package/ios/VideoTrimmer.swift +16 -10
- package/ios/VideoTrimmerViewController.swift +78 -12
- package/lib/commonjs/index.js +20 -57
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +19 -57
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +47 -9
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +56 -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
|
@@ -14,7 +14,28 @@
|
|
|
14
14
|
<VideoView
|
|
15
15
|
android:id="@+id/video_loader"
|
|
16
16
|
android:layout_width="match_parent"
|
|
17
|
-
android:layout_height="match_parent"
|
|
17
|
+
android:layout_height="match_parent"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<FrameLayout
|
|
21
|
+
android:id="@+id/audioBannerView"
|
|
22
|
+
android:layout_width="match_parent"
|
|
23
|
+
android:layout_height="match_parent"
|
|
24
|
+
android:layout_marginStart="50dp"
|
|
25
|
+
android:layout_marginTop="50dp"
|
|
26
|
+
android:layout_marginEnd="50dp"
|
|
27
|
+
android:layout_marginBottom="50dp"
|
|
28
|
+
android:padding="50dp"
|
|
29
|
+
android:visibility="gone">
|
|
30
|
+
|
|
31
|
+
<ImageView
|
|
32
|
+
android:layout_width="match_parent"
|
|
33
|
+
android:layout_height="match_parent"
|
|
34
|
+
android:layout_gravity="center"
|
|
35
|
+
android:contentDescription="Airpods Max"
|
|
36
|
+
android:src="@drawable/airpodsmax"
|
|
37
|
+
/>
|
|
38
|
+
</FrameLayout>
|
|
18
39
|
|
|
19
40
|
</RelativeLayout>
|
|
20
41
|
|
|
@@ -38,7 +59,8 @@
|
|
|
38
59
|
android:layout_height="match_parent"
|
|
39
60
|
android:layout_marginHorizontal="20dp"
|
|
40
61
|
android:orientation="horizontal"
|
|
41
|
-
android:padding="4dp"
|
|
62
|
+
android:padding="4dp"
|
|
63
|
+
/>
|
|
42
64
|
|
|
43
65
|
<RelativeLayout
|
|
44
66
|
android:id="@+id/trimmerContainerWrapper"
|
|
@@ -201,7 +223,30 @@
|
|
|
201
223
|
android:layout_gravity="center"
|
|
202
224
|
android:padding="12dp"
|
|
203
225
|
android:src="@drawable/play_fill"
|
|
204
|
-
android:tint="@color/white"
|
|
226
|
+
android:tint="@color/white"
|
|
227
|
+
android:visibility="gone"
|
|
228
|
+
/>
|
|
229
|
+
|
|
230
|
+
<ImageView
|
|
231
|
+
android:id="@+id/failToLoadBtn"
|
|
232
|
+
android:layout_width="wrap_content"
|
|
233
|
+
android:layout_height="match_parent"
|
|
234
|
+
android:layout_gravity="center"
|
|
235
|
+
android:padding="12dp"
|
|
236
|
+
android:src="@drawable/exclamationmark_triangle_fill"
|
|
237
|
+
android:tint="@color/trim_color"
|
|
238
|
+
android:visibility="gone"
|
|
239
|
+
/>
|
|
240
|
+
|
|
241
|
+
<ProgressBar
|
|
242
|
+
android:id="@+id/loadingIndicator"
|
|
243
|
+
style="?android:attr/progressBarStyle"
|
|
244
|
+
android:layout_width="wrap_content"
|
|
245
|
+
android:layout_height="wrap_content"
|
|
246
|
+
android:layout_gravity="center"
|
|
247
|
+
android:padding="10dp"
|
|
248
|
+
android:indeterminateTint="@color/white"
|
|
249
|
+
/>
|
|
205
250
|
|
|
206
251
|
<TextView
|
|
207
252
|
android:id="@+id/saveBtn"
|
|
@@ -212,7 +257,9 @@
|
|
|
212
257
|
android:padding="10dp"
|
|
213
258
|
android:text="@string/save"
|
|
214
259
|
android:textColor="#2196F3"
|
|
215
|
-
android:textSize="16dp"
|
|
260
|
+
android:textSize="16dp"
|
|
261
|
+
android:visibility="gone"
|
|
262
|
+
/>
|
|
216
263
|
|
|
217
264
|
</FrameLayout>
|
|
218
265
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AssetLoader.swift
|
|
3
|
+
// react-native-video-trim
|
|
4
|
+
//
|
|
5
|
+
// Created by ByteDance on 7/27/24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import AVFoundation
|
|
9
|
+
|
|
10
|
+
protocol AssetLoaderDelegate: AnyObject {
|
|
11
|
+
func assetLoader(_ loader: AssetLoader, didFailWithError error: Error, forKey key: String)
|
|
12
|
+
func assetLoaderDidSucceed(_ loader: AssetLoader)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class AssetLoader: NSObject {
|
|
16
|
+
var asset: AVURLAsset?
|
|
17
|
+
weak var delegate: AssetLoaderDelegate?
|
|
18
|
+
|
|
19
|
+
func loadAsset(url: URL, isVideoType: Bool) {
|
|
20
|
+
// Creating AVURLAsset (not blocking the main thread)
|
|
21
|
+
asset = AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
|
|
22
|
+
let keys = ["duration", "tracks"]
|
|
23
|
+
|
|
24
|
+
// Asynchronous property loading
|
|
25
|
+
asset?.loadValuesAsynchronously(forKeys: keys) {
|
|
26
|
+
DispatchQueue.main.async {
|
|
27
|
+
self.assetLoaded(isVideoType: isVideoType)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private func assetLoaded(isVideoType: Bool) {
|
|
33
|
+
guard let asset = asset else { return }
|
|
34
|
+
|
|
35
|
+
let keys = ["duration", "tracks"]
|
|
36
|
+
for key in keys {
|
|
37
|
+
var error: NSError?
|
|
38
|
+
let status = asset.statusOfValue(forKey: key, error: &error)
|
|
39
|
+
|
|
40
|
+
if status == .failed {
|
|
41
|
+
if let error = error {
|
|
42
|
+
delegate?.assetLoader(self, didFailWithError: error, forKey: key)
|
|
43
|
+
}
|
|
44
|
+
return
|
|
45
|
+
} else if status == .cancelled {
|
|
46
|
+
delegate?.assetLoader(self, didFailWithError: NSError(domain: "AssetLoader", code: -1, userInfo: [NSLocalizedDescriptionKey: "\(key) loading was cancelled"]), forKey: key)
|
|
47
|
+
return
|
|
48
|
+
} else if status != .loaded {
|
|
49
|
+
delegate?.assetLoader(self, didFailWithError: NSError(domain: "AssetLoader", code: -1, userInfo: [NSLocalizedDescriptionKey: "\(key) is in an unknown state"]), forKey: key)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if isVideoType {
|
|
55
|
+
// Process the tracks to load the remaining properties
|
|
56
|
+
self.processAssetTracks()
|
|
57
|
+
} else {
|
|
58
|
+
delegate?.assetLoaderDidSucceed(self)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private func processAssetTracks() {
|
|
63
|
+
guard let asset = asset else { return }
|
|
64
|
+
|
|
65
|
+
let videoTracks = asset.tracks(withMediaType: .video)
|
|
66
|
+
guard let videoTrack = videoTracks.first else {
|
|
67
|
+
delegate?.assetLoader(self, didFailWithError: NSError(domain: "AssetLoader", code: -1, userInfo: [NSLocalizedDescriptionKey: "No video tracks found"]), forKey: "tracks")
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let trackKeys = ["naturalSize", "preferredTransform"]
|
|
72
|
+
videoTrack.loadValuesAsynchronously(forKeys: trackKeys) {
|
|
73
|
+
DispatchQueue.main.async {
|
|
74
|
+
self.trackPropertiesLoaded(track: videoTrack)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private func trackPropertiesLoaded(track: AVAssetTrack) {
|
|
80
|
+
var error: NSError?
|
|
81
|
+
|
|
82
|
+
let naturalSizeStatus = track.statusOfValue(forKey: "naturalSize", error: &error)
|
|
83
|
+
let preferredTransformStatus = track.statusOfValue(forKey: "preferredTransform", error: &error)
|
|
84
|
+
|
|
85
|
+
if naturalSizeStatus == .loaded, preferredTransformStatus == .loaded {
|
|
86
|
+
let naturalSize = track.naturalSize
|
|
87
|
+
let preferredTransform = track.preferredTransform
|
|
88
|
+
|
|
89
|
+
print("Natural size: \(naturalSize)")
|
|
90
|
+
print("Preferred transform: \(preferredTransform)")
|
|
91
|
+
delegate?.assetLoaderDidSucceed(self)
|
|
92
|
+
} else {
|
|
93
|
+
if let error = error {
|
|
94
|
+
let failedKey = naturalSizeStatus != .loaded ? "naturalSize" : "preferredTransform"
|
|
95
|
+
delegate?.assetLoader(self, didFailWithError: error, forKey: failedKey)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ErrorCode.swift
|
|
3
|
+
// react-native-video-trim
|
|
4
|
+
//
|
|
5
|
+
// Created by ByteDance on 7/27/24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
enum ErrorCode: String {
|
|
11
|
+
case trimmingFailed = "TRIMMING_FAILED"
|
|
12
|
+
case failToLoadVideo = "FAIL_TO_LOAD_VIDEO"
|
|
13
|
+
case failToSaveToPhoto = "FAIL_TO_SAVE_TO_PHOTO"
|
|
14
|
+
case failToShare = "FAIL_TO_SHARE"
|
|
15
|
+
case noPhotoPermission = "NO_PHOTO_PERMISSION"
|
|
16
|
+
}
|
package/ios/VideoTrim.mm
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
@interface RCT_EXTERN_MODULE(VideoTrim, RCTEventEmitter)
|
|
5
5
|
|
|
6
6
|
RCT_EXTERN_METHOD(showEditor:(NSString*)uri withConfig:(NSDictionary *)config)
|
|
7
|
-
RCT_EXTERN_METHOD(isValidVideo:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
|
|
8
|
-
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
9
7
|
RCT_EXTERN_METHOD(listFiles:(RCTPromiseResolveBlock)resolve
|
|
10
8
|
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
11
9
|
RCT_EXTERN_METHOD(cleanFiles:(RCTPromiseResolveBlock)resolve
|
|
12
10
|
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
13
11
|
RCT_EXTERN_METHOD(deleteFile:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
|
|
14
12
|
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
13
|
+
RCT_EXTERN_METHOD(closeEditor:(RCTPromiseResolveBlock)resolve
|
|
14
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
15
|
+
RCT_EXTERN_METHOD(isValidFile:(NSString*)uri withResolver:(RCTPromiseResolveBlock)resolve
|
|
16
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
15
17
|
@end
|