react-native-video-trim 5.0.4 → 5.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 +1 -1
- package/VideoTrim.podspec +1 -1
- package/android/src/main/java/com/videotrim/VideoTrimModule.kt +4 -4
- package/ios/AssetLoader.swift +99 -0
- package/ios/ErrorCode.swift +17 -0
- package/ios/ProgressAlertController.swift +100 -0
- package/ios/VideoTrim-Bridging-Header.h +1 -0
- package/ios/VideoTrim.h +2 -28
- package/ios/VideoTrim.mm +156 -639
- package/ios/VideoTrim.swift +946 -0
- package/ios/VideoTrimProtocol.swift +10 -0
- package/ios/VideoTrimmer.swift +872 -0
- package/ios/VideoTrimmerThumb.swift +175 -0
- package/ios/VideoTrimmerViewController.swift +578 -0
- package/lib/typescript/src/NativeVideoTrim.d.ts +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/NativeVideoTrim.ts +1 -1
- package/ios/AssetLoader.h +0 -19
- package/ios/AssetLoader.mm +0 -87
- package/ios/ErrorCode.h +0 -9
- package/ios/ProgressAlertController.h +0 -12
- package/ios/ProgressAlertController.mm +0 -106
- package/ios/VideoTrimmer.h +0 -67
- package/ios/VideoTrimmer.mm +0 -863
- package/ios/VideoTrimmerThumb.h +0 -23
- package/ios/VideoTrimmerThumb.mm +0 -175
- package/ios/VideoTrimmerViewController.h +0 -52
- package/ios/VideoTrimmerViewController.mm +0 -533
package/README.md
CHANGED
|
@@ -282,7 +282,7 @@ Main method to show Video Editor UI.
|
|
|
282
282
|
- `alertOnFailCloseText` (`default = "Close"`)
|
|
283
283
|
- `enableRotation` (`default = false`)
|
|
284
284
|
- `rotationAngle` (`default = 0`)
|
|
285
|
-
- `
|
|
285
|
+
- `changeStatusBarColorOnOpen` (`default = false`): Update status bar color to black background color when editor is opened (useful in somecases where your theme has titlebar in different color than black)
|
|
286
286
|
|
|
287
287
|
If `saveToPhoto = true`, you must ensure that you have request permission to write to photo/gallery
|
|
288
288
|
- For Android: you need to have `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />` in AndroidManifest.xml
|
package/VideoTrim.podspec
CHANGED
|
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
14
|
s.source = { :git => "https://github.com/maitrungduc1410/react-native-video-trim.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
|
-
s.source_files = "ios/**/*.{h,m,mm,
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
17
|
s.private_header_files = "ios/**/*.h"
|
|
18
18
|
|
|
19
19
|
s.dependency "ffmpeg-mobile-#{ENV['FFMPEGKIT_PACKAGE'] || 'min'}", ENV['FFMPEGKIT_PACKAGE_VERSION'] || '~> 6.0'
|
|
@@ -165,7 +165,7 @@ class VideoTrimModule(reactContext: ReactApplicationContext) :
|
|
|
165
165
|
|
|
166
166
|
alertDialog?.show()
|
|
167
167
|
|
|
168
|
-
if (
|
|
168
|
+
if (shouldChangeStatusBarColorOnOpen) {
|
|
169
169
|
changeStatusBarColor()
|
|
170
170
|
}
|
|
171
171
|
}
|
|
@@ -511,7 +511,7 @@ class VideoTrimModule(reactContext: ReactApplicationContext) :
|
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
-
if (
|
|
514
|
+
if (shouldChangeStatusBarColorOnOpen) {
|
|
515
515
|
restoreStatusBarColor()
|
|
516
516
|
}
|
|
517
517
|
}
|
|
@@ -694,8 +694,8 @@ class VideoTrimModule(reactContext: ReactApplicationContext) :
|
|
|
694
694
|
reactApplicationContext.currentActivity?.startActivity(Intent.createChooser(shareIntent, "Share file"))
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
-
val
|
|
698
|
-
get() = editorConfig?.hasKey("
|
|
697
|
+
val shouldChangeStatusBarColorOnOpen: Boolean
|
|
698
|
+
get() = editorConfig?.hasKey("changeStatusBarColorOnOpen") == true && editorConfig?.getBoolean("changeStatusBarColorOnOpen") == true
|
|
699
699
|
|
|
700
700
|
companion object {
|
|
701
701
|
const val NAME = "VideoTrim"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AssetLoader.swift
|
|
3
|
+
// VideoTrim
|
|
4
|
+
//
|
|
5
|
+
// Created by Duc Trung Mai on 20/5/25.
|
|
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,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ErrorCode.swift
|
|
3
|
+
// VideoTrim
|
|
4
|
+
//
|
|
5
|
+
// Created by Duc Trung Mai on 20/5/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
enum ErrorCode: String {
|
|
11
|
+
case trimmingFailed = "TRIMMING_FAILED"
|
|
12
|
+
case failToLoadMedia = "FAIL_TO_LOAD_MEDIA"
|
|
13
|
+
case failToSaveToPhoto = "FAIL_TO_SAVE_TO_PHOTO"
|
|
14
|
+
case failToShare = "FAIL_TO_SHARE"
|
|
15
|
+
case noPhotoPermission = "NO_PHOTO_PERMISSION"
|
|
16
|
+
case invalidFilePath = "INVALID_FILE_PATH"
|
|
17
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ProgressAlertController.swift
|
|
3
|
+
// VideoTrim
|
|
4
|
+
//
|
|
5
|
+
// Created by Duc Trung Mai on 20/5/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
class ProgressAlertController: UIViewController {
|
|
11
|
+
var onDismiss: (() -> Void)?
|
|
12
|
+
|
|
13
|
+
private let titleLabel = UILabel()
|
|
14
|
+
private let progressBar = UIProgressView(progressViewStyle: .default)
|
|
15
|
+
private let actionButton = UIButton(type: .system)
|
|
16
|
+
|
|
17
|
+
override func viewDidLoad() {
|
|
18
|
+
super.viewDidLoad()
|
|
19
|
+
|
|
20
|
+
setupBackground()
|
|
21
|
+
setupAlertView()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private func setupBackground() {
|
|
25
|
+
view.backgroundColor = UIColor.black.withAlphaComponent(0.4)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private func setupAlertView() {
|
|
29
|
+
let alertView = UIView()
|
|
30
|
+
alertView.backgroundColor = UIColor(red: 28/255, green: 28/255, blue: 30/255, alpha: 1.0)
|
|
31
|
+
alertView.layer.cornerRadius = 12
|
|
32
|
+
alertView.translatesAutoresizingMaskIntoConstraints = false
|
|
33
|
+
view.addSubview(alertView)
|
|
34
|
+
|
|
35
|
+
// AlertView Constraints
|
|
36
|
+
NSLayoutConstraint.activate([
|
|
37
|
+
alertView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
|
38
|
+
alertView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
|
39
|
+
alertView.widthAnchor.constraint(equalToConstant: 270)
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
// Title Label
|
|
43
|
+
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
44
|
+
titleLabel.textAlignment = .center
|
|
45
|
+
titleLabel.font = UIFont.systemFont(ofSize: 18)
|
|
46
|
+
titleLabel.numberOfLines = 0
|
|
47
|
+
titleLabel.textColor = .white
|
|
48
|
+
alertView.addSubview(titleLabel)
|
|
49
|
+
|
|
50
|
+
// Progress Bar
|
|
51
|
+
progressBar.translatesAutoresizingMaskIntoConstraints = false
|
|
52
|
+
alertView.addSubview(progressBar)
|
|
53
|
+
|
|
54
|
+
// Action Button
|
|
55
|
+
actionButton.setTitle("Cancel", for: .normal)
|
|
56
|
+
actionButton.setTitleColor(.systemPink, for: .normal)
|
|
57
|
+
actionButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
|
|
58
|
+
actionButton.addTarget(self, action: #selector(dismissAlert), for: .touchUpInside)
|
|
59
|
+
actionButton.translatesAutoresizingMaskIntoConstraints = false
|
|
60
|
+
actionButton.isHidden = true
|
|
61
|
+
alertView.addSubview(actionButton)
|
|
62
|
+
|
|
63
|
+
// Constraints for titleLabel, progressBar, and actionButton
|
|
64
|
+
NSLayoutConstraint.activate([
|
|
65
|
+
titleLabel.topAnchor.constraint(equalTo: alertView.topAnchor, constant: 16),
|
|
66
|
+
titleLabel.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 16),
|
|
67
|
+
titleLabel.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -16),
|
|
68
|
+
|
|
69
|
+
progressBar.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16),
|
|
70
|
+
progressBar.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 16),
|
|
71
|
+
progressBar.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -16),
|
|
72
|
+
|
|
73
|
+
actionButton.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 16),
|
|
74
|
+
actionButton.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -16),
|
|
75
|
+
actionButton.centerXAnchor.constraint(equalTo: alertView.centerXAnchor)
|
|
76
|
+
])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@objc private func dismissAlert() {
|
|
80
|
+
self.onDismiss?()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func setTitle(_ text: String) {
|
|
84
|
+
titleLabel.text = text
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func setCancelTitle(_ text: String) {
|
|
88
|
+
actionButton.setTitle(text, for: .normal)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func setProgress(_ progress: Float) {
|
|
92
|
+
progressBar.setProgress(progress, animated: true)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func showCancelBtn() {
|
|
96
|
+
// Ensure that the button is properly added to the view hierarchy and that the layout has been updated before hiding the button
|
|
97
|
+
view.layoutIfNeeded()
|
|
98
|
+
actionButton.isHidden = false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/ios/VideoTrim.h
CHANGED
|
@@ -1,31 +1,5 @@
|
|
|
1
1
|
#import <VideoTrimSpec/VideoTrimSpec.h>
|
|
2
|
-
#import <
|
|
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;
|
|
2
|
+
#import <VideoTrim-Swift.h>
|
|
30
3
|
|
|
4
|
+
@interface VideoTrim : NativeVideoTrimSpecBase <NativeVideoTrimSpec, VideoTrimProtocol>
|
|
31
5
|
@end
|