react-native-video-trim 4.0.0 → 4.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 +36 -8
- package/VideoTrim.podspec +1 -1
- package/android/build.gradle +9 -1
- package/android/gradle.properties +2 -0
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrim.kt +138 -121
- package/android/src/main/java/com/margelo/nitro/videotrim/utils/VideoTrimmerUtil.java +29 -21
- package/android/src/main/java/com/margelo/nitro/videotrim/widgets/VideoTrimmerView.java +10 -6
- package/ios/VideoTrim.swift +7 -0
- package/ios/VideoTrimImpl.swift +221 -124
- package/lib/module/index.js +38 -11
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/VideoTrim.nitro.d.ts +74 -57
- package/lib/typescript/src/VideoTrim.nitro.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JEditorConfig.hpp +54 -46
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +20 -0
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JTrimOptions.hpp +109 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +14 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/TrimOptions.kt +40 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +43 -0
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +11 -0
- package/nitrogen/generated/ios/swift/EditorConfig.swift +116 -94
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +19 -0
- package/nitrogen/generated/ios/swift/TrimOptions.swift +189 -0
- package/nitrogen/generated/shared/c++/EditorConfig.hpp +54 -46
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/TrimOptions.hpp +125 -0
- package/package.json +1 -1
- package/src/VideoTrim.nitro.ts +76 -57
- package/src/index.tsx +45 -11
package/ios/VideoTrimImpl.swift
CHANGED
|
@@ -11,53 +11,12 @@ import ffmpegkit
|
|
|
11
11
|
class VideoTrimImpl: NSObject
|
|
12
12
|
{
|
|
13
13
|
private let FILE_PREFIX = "trimmedVideo"
|
|
14
|
+
private let BEFORE_TRIM_PREFIX = "beforeTrim"
|
|
14
15
|
private var isShowing = false
|
|
15
16
|
private var vc: VideoTrimmerViewController?
|
|
16
17
|
private var outputFile: URL?
|
|
17
|
-
|
|
18
|
-
// private var saveToPhoto = false
|
|
19
|
-
// private var removeAfterSavedToPhoto = false
|
|
20
|
-
// private var removeAfterFailedToSavePhoto = false
|
|
21
|
-
// private var removeAfterSavedToDocuments = false
|
|
22
|
-
// private var removeAfterFailedToSaveDocuments = false
|
|
23
|
-
// private var removeAfterShared = false
|
|
24
|
-
// private var removeAfterFailedToShare = false
|
|
25
|
-
//
|
|
26
|
-
// private var trimmingText = "Trimming video..."
|
|
27
|
-
// private var enableCancelDialog = true
|
|
28
|
-
// private var cancelDialogTitle = "Warning!"
|
|
29
|
-
// private var cancelDialogMessage = "Are you sure want to cancel?"
|
|
30
|
-
// private var cancelDialogCancelText = "Close"
|
|
31
|
-
// private var cancelDialogConfirmText = "Proceed"
|
|
32
|
-
// private var enableSaveDialog = true
|
|
33
|
-
// private var saveDialogTitle = "Confirmation!"
|
|
34
|
-
// private var saveDialogMessage = "Are you sure want to save?"
|
|
35
|
-
// private var saveDialogCancelText = "Close"
|
|
36
|
-
// private var saveDialogConfirmText = "Proceed"
|
|
37
|
-
// private var fullScreenModalIOS = false
|
|
38
|
-
// private var cancelButtonText = "Cancel"
|
|
39
|
-
// private var saveButtonText = "Save"
|
|
40
|
-
|
|
41
18
|
private var isVideoType = true
|
|
42
|
-
// private var outputExt = "mp4"
|
|
43
|
-
// private var openDocumentsOnFinish = false
|
|
44
|
-
// private var openShareSheetOnFinish = false
|
|
45
|
-
|
|
46
|
-
// private var closeWhenFinish = true
|
|
47
|
-
// private var enableCancelTrimming = true
|
|
48
|
-
// private var cancelTrimmingButtonText = "Cancel"
|
|
49
|
-
// private var enableCancelTrimmingDialog = true
|
|
50
|
-
// private var cancelTrimmingDialogTitle = "Warning!"
|
|
51
|
-
// private var cancelTrimmingDialogMessage = "Are you sure want to trimming?"
|
|
52
|
-
// private var cancelTrimmingDialogCancelText = "Close"
|
|
53
|
-
// private var cancelTrimmingDialogConfirmText = "Proceed"
|
|
54
|
-
// private var alertOnFailToLoad = true
|
|
55
|
-
// private var alertOnFailTitle = "Error"
|
|
56
|
-
// private var alertOnFailMessage =
|
|
57
|
-
// "Fail to load media. Possibly invalid file or no network connection"
|
|
58
|
-
// private var alertOnFailCloseText = "Close"
|
|
59
19
|
private var editorConfig: EditorConfig!
|
|
60
|
-
|
|
61
20
|
private var onEvent: ((_ eventName: String, _ payload: Dictionary<String, String>) -> Void)?
|
|
62
21
|
|
|
63
22
|
func showEditor(
|
|
@@ -71,81 +30,16 @@ class VideoTrimImpl: NSObject
|
|
|
71
30
|
|
|
72
31
|
self.editorConfig = editorConfig
|
|
73
32
|
self.onEvent = onEvent
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// removeAfterShared = config["removeAfterShared"] as? Bool ?? false
|
|
85
|
-
// removeAfterFailedToShare =
|
|
86
|
-
// config["removeAfterFailedToShare"] as? Bool ?? false
|
|
87
|
-
//
|
|
88
|
-
// enableCancelDialog = config["enableCancelDialog"] as? Bool ?? true
|
|
89
|
-
// cancelDialogTitle = config["cancelDialogTitle"] as? String ?? "Warning!"
|
|
90
|
-
// cancelDialogMessage =
|
|
91
|
-
// config["cancelDialogMessage"] as? String ?? "Are you sure want to cancel?"
|
|
92
|
-
// cancelDialogCancelText =
|
|
93
|
-
// config["cancelDialogCancelText"] as? String ?? "Close"
|
|
94
|
-
// cancelDialogConfirmText =
|
|
95
|
-
// config["cancelDialogConfirmText"] as? String ?? "Proceed"
|
|
96
|
-
//
|
|
97
|
-
// enableSaveDialog = config["enableSaveDialog"] as? Bool ?? true
|
|
98
|
-
// saveDialogTitle = config["saveDialogTitle"] as? String ?? "Confirmation!"
|
|
99
|
-
// saveDialogMessage =
|
|
100
|
-
// config["saveDialogMessage"] as? String ?? "Are you sure want to save?"
|
|
101
|
-
// saveDialogCancelText = config["saveDialogCancelText"] as? String ?? "Close"
|
|
102
|
-
// saveDialogConfirmText =
|
|
103
|
-
// config["saveDialogConfirmText"] as? String ?? "Proceed"
|
|
104
|
-
// trimmingText = config["trimmingText"] as? String ?? "Trimming video..."
|
|
105
|
-
// fullScreenModalIOS = config["fullScreenModalIOS"] as? Bool ?? false
|
|
106
|
-
isVideoType = editorConfig.type == "video"
|
|
107
|
-
// outputExt = config["outputExt"] as? String ?? "mp4"
|
|
108
|
-
// openDocumentsOnFinish = config["openDocumentsOnFinish"] as? Bool ?? false
|
|
109
|
-
// openShareSheetOnFinish = config["openShareSheetOnFinish"] as? Bool ?? false
|
|
110
|
-
//
|
|
111
|
-
// closeWhenFinish = config["closeWhenFinish"] as? Bool ?? true
|
|
112
|
-
// enableCancelTrimming = config["enableCancelTrimming"] as? Bool ?? true
|
|
113
|
-
// cancelTrimmingButtonText =
|
|
114
|
-
// config["cancelTrimmingButtonText"] as? String ?? "Cancel"
|
|
115
|
-
// enableCancelTrimmingDialog =
|
|
116
|
-
// config["enableCancelTrimmingDialog"] as? Bool ?? true
|
|
117
|
-
// cancelTrimmingDialogTitle =
|
|
118
|
-
// config["cancelTrimmingDialogTitle"] as? String ?? "Warning!"
|
|
119
|
-
// cancelTrimmingDialogMessage =
|
|
120
|
-
// config["cancelTrimmingDialogMessage"] as? String
|
|
121
|
-
// ?? "Are you sure want to cancel trimming?"
|
|
122
|
-
// cancelTrimmingDialogCancelText =
|
|
123
|
-
// config["cancelTrimmingDialogCancelText"] as? String ?? "Close"
|
|
124
|
-
// cancelTrimmingDialogConfirmText =
|
|
125
|
-
// config["cancelTrimmingDialogConfirmText"] as? String ?? "Proceed"
|
|
126
|
-
// alertOnFailToLoad = config["alertOnFailToLoad"] as? Bool ?? true
|
|
127
|
-
// alertOnFailTitle = config["alertOnFailTitle"] as? String ?? "Error"
|
|
128
|
-
// alertOnFailMessage =
|
|
129
|
-
// config["alertOnFailMessage"] as? String
|
|
130
|
-
// ?? "Fail to load media. Possibly invalid file or no network connection"
|
|
131
|
-
// alertOnFailCloseText = config["alertOnFailCloseText"] as? String ?? "Close"
|
|
132
|
-
//
|
|
133
|
-
// if let cancelBtnText = config["cancelButtonText"] as? String,
|
|
134
|
-
// !cancelBtnText.isEmpty
|
|
135
|
-
// {
|
|
136
|
-
// self.cancelButtonText = cancelBtnText
|
|
137
|
-
// }
|
|
138
|
-
//
|
|
139
|
-
// if let saveButtonText = config["saveButtonText"] as? String,
|
|
140
|
-
// !saveButtonText.isEmpty
|
|
141
|
-
// {
|
|
142
|
-
// self.saveButtonText = saveButtonText
|
|
143
|
-
// }
|
|
144
|
-
//
|
|
145
|
-
let destPath = URL(string: uri)
|
|
146
|
-
let newPath = renameFile(at: destPath!, newName: "beforeTrim")
|
|
147
|
-
|
|
148
|
-
guard let destPath = newPath else {
|
|
33
|
+
self.isVideoType = editorConfig.type == "video"
|
|
34
|
+
|
|
35
|
+
let destPath: URL?
|
|
36
|
+
if uri.starts(with: "http://") || uri.starts(with: "https://") {
|
|
37
|
+
destPath = URL(string: uri)
|
|
38
|
+
} else {
|
|
39
|
+
destPath = renameFile(at: URL(string: uri)!, newName: BEFORE_TRIM_PREFIX)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
guard let destPath = destPath else {
|
|
149
43
|
onError(message: "Fail to rename file", code: .invalidFilePath)
|
|
150
44
|
self.isShowing = false
|
|
151
45
|
return
|
|
@@ -319,7 +213,8 @@ class VideoTrimImpl: NSObject
|
|
|
319
213
|
at: documentsDirectory, includingPropertiesForKeys: nil)
|
|
320
214
|
|
|
321
215
|
for fileURL in directoryContents {
|
|
322
|
-
|
|
216
|
+
let last = fileURL.lastPathComponent
|
|
217
|
+
if last.starts(with: FILE_PREFIX) || last.starts(with: BEFORE_TRIM_PREFIX) {
|
|
323
218
|
files.append(fileURL)
|
|
324
219
|
}
|
|
325
220
|
}
|
|
@@ -427,13 +322,21 @@ class VideoTrimImpl: NSObject
|
|
|
427
322
|
root.present(progressAlert, animated: true, completion: nil)
|
|
428
323
|
}
|
|
429
324
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
let cmds = [
|
|
325
|
+
var cmds = [
|
|
433
326
|
"-ss",
|
|
434
327
|
"\(startTime * 1000)ms",
|
|
435
328
|
"-to",
|
|
436
329
|
"\(endTime * 1000)ms",
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
if self.editorConfig.enableRotation {
|
|
333
|
+
cmds = cmds + [
|
|
334
|
+
"-display_rotation",
|
|
335
|
+
"\(self.editorConfig.rotationAngle)",
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
cmds = cmds + [
|
|
437
340
|
"-i",
|
|
438
341
|
"\(inputFile)",
|
|
439
342
|
"-c",
|
|
@@ -602,7 +505,7 @@ class VideoTrimImpl: NSObject
|
|
|
602
505
|
self.onError(message: message, code: .failToShare)
|
|
603
506
|
|
|
604
507
|
if self.editorConfig.removeAfterFailedToShare {
|
|
605
|
-
let _ = self.deleteFile(url:
|
|
508
|
+
let _ = self.deleteFile(url: fileURL)
|
|
606
509
|
}
|
|
607
510
|
return
|
|
608
511
|
}
|
|
@@ -610,12 +513,12 @@ class VideoTrimImpl: NSObject
|
|
|
610
513
|
if completed {
|
|
611
514
|
print("User completed the sharing activity")
|
|
612
515
|
if self.editorConfig.removeAfterShared {
|
|
613
|
-
let _ = self.deleteFile(url:
|
|
516
|
+
let _ = self.deleteFile(url: fileURL)
|
|
614
517
|
}
|
|
615
518
|
} else {
|
|
616
519
|
print("User cancelled or failed to complete the sharing activity")
|
|
617
520
|
if self.editorConfig.removeAfterFailedToShare {
|
|
618
|
-
let _ = self.deleteFile(url:
|
|
521
|
+
let _ = self.deleteFile(url: fileURL)
|
|
619
522
|
}
|
|
620
523
|
}
|
|
621
524
|
|
|
@@ -632,6 +535,45 @@ class VideoTrimImpl: NSObject
|
|
|
632
535
|
|
|
633
536
|
}
|
|
634
537
|
|
|
538
|
+
private func shareFile(fileURL: URL, options: TrimOptions) {
|
|
539
|
+
DispatchQueue.main.async {
|
|
540
|
+
// Create an instance of UIActivityViewController
|
|
541
|
+
let activityViewController = UIActivityViewController(
|
|
542
|
+
activityItems: [fileURL], applicationActivities: nil)
|
|
543
|
+
|
|
544
|
+
activityViewController.completionWithItemsHandler = {
|
|
545
|
+
activityType, completed, returnedItems, error in
|
|
546
|
+
|
|
547
|
+
if let error = error {
|
|
548
|
+
let message = "Sharing error: \(error.localizedDescription)"
|
|
549
|
+
print(message)
|
|
550
|
+
|
|
551
|
+
if options.removeAfterFailedToShare {
|
|
552
|
+
let _ = self.deleteFile(url: fileURL)
|
|
553
|
+
}
|
|
554
|
+
return
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if completed {
|
|
558
|
+
print("User completed the sharing activity")
|
|
559
|
+
if options.removeAfterShared {
|
|
560
|
+
let _ = self.deleteFile(url: fileURL)
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
print("User cancelled or failed to complete the sharing activity")
|
|
564
|
+
if options.removeAfterFailedToShare {
|
|
565
|
+
let _ = self.deleteFile(url: fileURL)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Present the share sheet
|
|
571
|
+
if let root = RCTPresentedViewController() {
|
|
572
|
+
root.present(activityViewController, animated: true, completion: nil)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
635
577
|
func closeEditor(_ onComplete: (() -> Void)? = nil) {
|
|
636
578
|
guard let vc = vc else { return }
|
|
637
579
|
// some how in case we trim a very short video the view controller is still visible after first .dismiss call
|
|
@@ -661,6 +603,161 @@ class VideoTrimImpl: NSObject
|
|
|
661
603
|
return FileValidationResult(isValid: result.isValid, fileType: result.fileType, duration: result.duration)
|
|
662
604
|
}
|
|
663
605
|
|
|
606
|
+
func trim(url: String, options: TrimOptions) async throws -> String {
|
|
607
|
+
let timestamp = Int(Date().timeIntervalSince1970)
|
|
608
|
+
let outputName = "\(FILE_PREFIX)_\(timestamp).\(options.outputExt)"
|
|
609
|
+
let documentsDirectory = FileManager.default.urls(
|
|
610
|
+
for: .documentDirectory, in: .userDomainMask
|
|
611
|
+
).first!
|
|
612
|
+
let outputPath = documentsDirectory.appendingPathComponent(outputName)
|
|
613
|
+
|
|
614
|
+
let formatter = DateFormatter()
|
|
615
|
+
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"
|
|
616
|
+
formatter.timeZone = TimeZone(identifier: "UTC")
|
|
617
|
+
let dateTime = formatter.string(from: Date())
|
|
618
|
+
|
|
619
|
+
return try await withCheckedThrowingContinuation { continuation in
|
|
620
|
+
var destPath = url
|
|
621
|
+
if !(url.starts(with: "http://") || url.starts(with: "https://")) {
|
|
622
|
+
let renamed = renameFile(at: URL(string: url)!, newName: "\(BEFORE_TRIM_PREFIX)_\(timestamp)")
|
|
623
|
+
|
|
624
|
+
guard let r = renamed else {
|
|
625
|
+
continuation.resume(
|
|
626
|
+
throwing: NSError(
|
|
627
|
+
domain: "VideoTrim",
|
|
628
|
+
code: -996,
|
|
629
|
+
userInfo: [
|
|
630
|
+
NSLocalizedDescriptionKey: "Fail to rename file"
|
|
631
|
+
]
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
return
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
destPath = r.absoluteString
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
var cmds = [
|
|
641
|
+
"-ss",
|
|
642
|
+
"\(options.startTime)ms",
|
|
643
|
+
"-to",
|
|
644
|
+
"\(options.endTime)ms",
|
|
645
|
+
]
|
|
646
|
+
|
|
647
|
+
if options.enableRotation {
|
|
648
|
+
cmds = cmds + [
|
|
649
|
+
"-display_rotation",
|
|
650
|
+
"\(options.rotationAngle)",
|
|
651
|
+
]
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
cmds = cmds + [
|
|
655
|
+
"-i",
|
|
656
|
+
"\(destPath)",
|
|
657
|
+
"-c",
|
|
658
|
+
"copy",
|
|
659
|
+
"-metadata",
|
|
660
|
+
"creation_time=\(dateTime)",
|
|
661
|
+
outputPath.absoluteString,
|
|
662
|
+
]
|
|
663
|
+
|
|
664
|
+
print("Command: ", cmds.joined(separator: " "))
|
|
665
|
+
|
|
666
|
+
FFmpegKit.execute(
|
|
667
|
+
withArgumentsAsync: cmds,
|
|
668
|
+
withCompleteCallback: { session in
|
|
669
|
+
let state = session?.getState()
|
|
670
|
+
let returnCode = session?.getReturnCode()
|
|
671
|
+
if ReturnCode.isSuccess(returnCode) {
|
|
672
|
+
if options.saveToPhoto && (options.type == "video") {
|
|
673
|
+
PHPhotoLibrary.requestAuthorization { status in
|
|
674
|
+
guard status == .authorized else {
|
|
675
|
+
continuation.resume(
|
|
676
|
+
throwing: NSError(
|
|
677
|
+
domain: "VideoTrim",
|
|
678
|
+
code: -998,
|
|
679
|
+
userInfo: [
|
|
680
|
+
NSLocalizedDescriptionKey: "Permission to access Photo Library is not granted"
|
|
681
|
+
]
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
return
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
PHPhotoLibrary.shared().performChanges({
|
|
688
|
+
let request =
|
|
689
|
+
PHAssetChangeRequest.creationRequestForAssetFromVideo(
|
|
690
|
+
atFileURL: outputPath)
|
|
691
|
+
request?.creationDate = Date()
|
|
692
|
+
}) { success, error in
|
|
693
|
+
if success {
|
|
694
|
+
print("Edited video saved to Photo Library successfully.")
|
|
695
|
+
|
|
696
|
+
if options.removeAfterSavedToPhoto {
|
|
697
|
+
let _ = self.deleteFile(url: outputPath)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
continuation.resume(returning: outputPath.absoluteString)
|
|
701
|
+
} else {
|
|
702
|
+
if options.removeAfterFailedToSavePhoto {
|
|
703
|
+
let _ = self.deleteFile(url: outputPath)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
continuation.resume(
|
|
707
|
+
throwing: NSError(
|
|
708
|
+
domain: "VideoTrim",
|
|
709
|
+
code: -997,
|
|
710
|
+
userInfo: [
|
|
711
|
+
NSLocalizedDescriptionKey: "Failed to save edited video to Photo Library: \(error?.localizedDescription ?? "Unknown error")"
|
|
712
|
+
]
|
|
713
|
+
)
|
|
714
|
+
)
|
|
715
|
+
return
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
} else {
|
|
722
|
+
if options.openDocumentsOnFinish {
|
|
723
|
+
self.saveFileToFilesApp(fileURL: outputPath)
|
|
724
|
+
} else if options.openShareSheetOnFinish {
|
|
725
|
+
self.shareFile(fileURL: outputPath)
|
|
726
|
+
}
|
|
727
|
+
continuation.resume(returning: outputPath.absoluteString)
|
|
728
|
+
}
|
|
729
|
+
} else if ReturnCode.isCancel(returnCode) {
|
|
730
|
+
// CANCEL
|
|
731
|
+
continuation.resume(
|
|
732
|
+
throwing: NSError(
|
|
733
|
+
domain: "VideoTrim",
|
|
734
|
+
code: -999,
|
|
735
|
+
userInfo: [
|
|
736
|
+
NSLocalizedDescriptionKey: "Trimming cancelled"
|
|
737
|
+
]
|
|
738
|
+
)
|
|
739
|
+
)
|
|
740
|
+
} else {
|
|
741
|
+
// FAILURE
|
|
742
|
+
continuation.resume(
|
|
743
|
+
throwing: NSError(
|
|
744
|
+
domain: "VideoTrim",
|
|
745
|
+
code: -1,
|
|
746
|
+
userInfo: [
|
|
747
|
+
NSLocalizedDescriptionKey: "Command failed with state \(String(describing: FFmpegKitConfig.sessionState(toString: state ?? .failed))) and rc \(String(describing: returnCode)).\(String(describing: session?.getFailStackTrace()))"
|
|
748
|
+
]
|
|
749
|
+
)
|
|
750
|
+
)
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
withLogCallback: { log in
|
|
754
|
+
print("FFmpeg process started with log " + (log!.getMessage()))
|
|
755
|
+
},
|
|
756
|
+
withStatisticsCallback: { statistics in
|
|
757
|
+
})
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
664
761
|
private func onError(message: String, code: ErrorCode) {
|
|
665
762
|
let eventPayload: [String: String] = [
|
|
666
763
|
"message": message,
|
package/lib/module/index.js
CHANGED
|
@@ -3,10 +3,27 @@
|
|
|
3
3
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
4
|
import { processColor } from 'react-native';
|
|
5
5
|
const VideoTrimHybridObject = NitroModules.createHybridObject('VideoTrim');
|
|
6
|
+
function createBaseOptions(overrides = {}) {
|
|
7
|
+
return {
|
|
8
|
+
saveToPhoto: false,
|
|
9
|
+
type: 'video',
|
|
10
|
+
outputExt: 'mp4',
|
|
11
|
+
openDocumentsOnFinish: false,
|
|
12
|
+
openShareSheetOnFinish: false,
|
|
13
|
+
removeAfterSavedToPhoto: false,
|
|
14
|
+
removeAfterFailedToSavePhoto: false,
|
|
15
|
+
removeAfterSavedToDocuments: false,
|
|
16
|
+
removeAfterFailedToSaveDocuments: false,
|
|
17
|
+
removeAfterShared: false,
|
|
18
|
+
removeAfterFailedToShare: false,
|
|
19
|
+
enableRotation: false,
|
|
20
|
+
rotationAngle: 0,
|
|
21
|
+
...overrides
|
|
22
|
+
};
|
|
23
|
+
}
|
|
6
24
|
function createEditorConfig(overrides = {}) {
|
|
7
25
|
return {
|
|
8
26
|
enableHapticFeedback: true,
|
|
9
|
-
saveToPhoto: false,
|
|
10
27
|
maxDuration: -1,
|
|
11
28
|
// Adjust default as needed
|
|
12
29
|
minDuration: 1000,
|
|
@@ -24,16 +41,6 @@ function createEditorConfig(overrides = {}) {
|
|
|
24
41
|
saveDialogConfirmText: 'Proceed',
|
|
25
42
|
trimmingText: 'Trimming video...',
|
|
26
43
|
fullScreenModalIOS: false,
|
|
27
|
-
type: 'video',
|
|
28
|
-
outputExt: 'mp4',
|
|
29
|
-
openDocumentsOnFinish: false,
|
|
30
|
-
openShareSheetOnFinish: false,
|
|
31
|
-
removeAfterSavedToPhoto: false,
|
|
32
|
-
removeAfterFailedToSavePhoto: false,
|
|
33
|
-
removeAfterSavedToDocuments: false,
|
|
34
|
-
removeAfterFailedToSaveDocuments: false,
|
|
35
|
-
removeAfterShared: false,
|
|
36
|
-
removeAfterFailedToShare: false,
|
|
37
44
|
autoplay: false,
|
|
38
45
|
// Adjust default as needed
|
|
39
46
|
jumpToPositionOnLoad: -1,
|
|
@@ -53,6 +60,15 @@ function createEditorConfig(overrides = {}) {
|
|
|
53
60
|
alertOnFailTitle: 'Error',
|
|
54
61
|
alertOnFailMessage: 'Fail to load media. Possibly invalid file or no network connection',
|
|
55
62
|
alertOnFailCloseText: 'Close',
|
|
63
|
+
...createBaseOptions(overrides),
|
|
64
|
+
...overrides
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createTrimOptions(overrides = {}) {
|
|
68
|
+
return {
|
|
69
|
+
startTime: 0,
|
|
70
|
+
endTime: 1000,
|
|
71
|
+
...createBaseOptions(overrides),
|
|
56
72
|
...overrides
|
|
57
73
|
};
|
|
58
74
|
}
|
|
@@ -122,4 +138,15 @@ export function closeEditor(onComplete) {
|
|
|
122
138
|
export function isValidFile(url) {
|
|
123
139
|
return VideoTrimHybridObject.isValidFile(url);
|
|
124
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Trim a video file
|
|
144
|
+
*
|
|
145
|
+
* @param {string} url: absolute non-empty file path to edit
|
|
146
|
+
* @param {TrimOptions} options: trim options
|
|
147
|
+
* @returns {Promise<string>} A **Promise** which resolves to the trimmed file path
|
|
148
|
+
*/
|
|
149
|
+
export function trim(url, options) {
|
|
150
|
+
return VideoTrimHybridObject.trim(url, createTrimOptions(options));
|
|
151
|
+
}
|
|
125
152
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NitroModules","processColor","VideoTrimHybridObject","createHybridObject","
|
|
1
|
+
{"version":3,"names":["NitroModules","processColor","VideoTrimHybridObject","createHybridObject","createBaseOptions","overrides","saveToPhoto","type","outputExt","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","enableRotation","rotationAngle","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","noop","showEditor","filePath","config","onEvent","color","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","onComplete","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,15 +1,78 @@
|
|
|
1
1
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
-
export interface
|
|
3
|
-
/**
|
|
4
|
-
* Enable haptic feedback
|
|
5
|
-
* @default true
|
|
6
|
-
*/
|
|
7
|
-
enableHapticFeedback: boolean;
|
|
2
|
+
export interface BaseOptions {
|
|
8
3
|
/**
|
|
9
4
|
* Save the output file to Photos Library. Only video is supported. Note that you have to make sure you have permission to save to Photos Library.
|
|
10
5
|
* @default false
|
|
11
6
|
*/
|
|
12
7
|
saveToPhoto: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Type of the file to edit. If video file, recommend to use `video`. If audio file, recommend to use `audio`.
|
|
10
|
+
* @default "video"
|
|
11
|
+
*/
|
|
12
|
+
type: string;
|
|
13
|
+
/**
|
|
14
|
+
* Output file extension. If video file, recommend to use `mp4` or `mov`. If audio file, recommend to use `wav` or `m4a`.
|
|
15
|
+
* @default "mp4"
|
|
16
|
+
* @example "mp4", "mov", "wav", "m4a", "3gp", "avi", "mkv", "flv", "wmv", "webm"
|
|
17
|
+
*/
|
|
18
|
+
outputExt: string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to open Documents app after finish editing
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
openDocumentsOnFinish: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to open Share Sheet after finish editing
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
openShareSheetOnFinish: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Remove the file after saved to Photos Library
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
removeAfterSavedToPhoto: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Remove the file after failed to save to Photos Library
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
removeAfterFailedToSavePhoto: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Remove the file after saved to Documents app
|
|
41
|
+
* @default false
|
|
42
|
+
*/
|
|
43
|
+
removeAfterSavedToDocuments: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Remove the file after failed to save to Documents app
|
|
46
|
+
* @default false
|
|
47
|
+
*/
|
|
48
|
+
removeAfterFailedToSaveDocuments: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Remove the file after shared to other apps. Currently only support iOS, on Android there's no way to detect if the file is shared or not.
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
removeAfterShared: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Remove the file after failed to share to other apps. Currently only support iOS, on Android there's no way to detect if the file is shared or not.
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
removeAfterFailedToShare: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Enable rotation
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
enableRotation: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Rotation angle in degrees
|
|
66
|
+
* @default 0
|
|
67
|
+
*/
|
|
68
|
+
rotationAngle: number;
|
|
69
|
+
}
|
|
70
|
+
export interface EditorConfig extends BaseOptions {
|
|
71
|
+
/**
|
|
72
|
+
* Enable haptic feedback
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
enableHapticFeedback: boolean;
|
|
13
76
|
maxDuration: number;
|
|
14
77
|
/**
|
|
15
78
|
* Minimum duration for trimmer
|
|
@@ -86,57 +149,6 @@ export interface EditorConfig {
|
|
|
86
149
|
* @default false
|
|
87
150
|
*/
|
|
88
151
|
fullScreenModalIOS: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* Type of the file to edit. If video file, recommend to use `video`. If audio file, recommend to use `audio`.
|
|
91
|
-
* @default "video"
|
|
92
|
-
*/
|
|
93
|
-
type: string;
|
|
94
|
-
/**
|
|
95
|
-
* Output file extension. If video file, recommend to use `mp4` or `mov`. If audio file, recommend to use `wav` or `m4a`.
|
|
96
|
-
* @default "mp4"
|
|
97
|
-
* @example "mp4", "mov", "wav", "m4a", "3gp", "avi", "mkv", "flv", "wmv", "webm"
|
|
98
|
-
*/
|
|
99
|
-
outputExt: string;
|
|
100
|
-
/**
|
|
101
|
-
* Whether to open Documents app after finish editing
|
|
102
|
-
* @default false
|
|
103
|
-
*/
|
|
104
|
-
openDocumentsOnFinish: boolean;
|
|
105
|
-
/**
|
|
106
|
-
* Whether to open Share Sheet after finish editing
|
|
107
|
-
* @default false
|
|
108
|
-
*/
|
|
109
|
-
openShareSheetOnFinish: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* Remove the file after saved to Photos Library
|
|
112
|
-
* @default false
|
|
113
|
-
*/
|
|
114
|
-
removeAfterSavedToPhoto: boolean;
|
|
115
|
-
/**
|
|
116
|
-
* Remove the file after failed to save to Photos Library
|
|
117
|
-
* @default false
|
|
118
|
-
*/
|
|
119
|
-
removeAfterFailedToSavePhoto: boolean;
|
|
120
|
-
/**
|
|
121
|
-
* Remove the file after saved to Documents app
|
|
122
|
-
* @default false
|
|
123
|
-
*/
|
|
124
|
-
removeAfterSavedToDocuments: boolean;
|
|
125
|
-
/**
|
|
126
|
-
* Remove the file after failed to save to Documents app
|
|
127
|
-
* @default false
|
|
128
|
-
*/
|
|
129
|
-
removeAfterFailedToSaveDocuments: boolean;
|
|
130
|
-
/**
|
|
131
|
-
* Remove the file after shared to other apps. Currently only support iOS, on Android there's no way to detect if the file is shared or not.
|
|
132
|
-
* @default false
|
|
133
|
-
*/
|
|
134
|
-
removeAfterShared: boolean;
|
|
135
|
-
/**
|
|
136
|
-
* Remove the file after failed to share to other apps. Currently only support iOS, on Android there's no way to detect if the file is shared or not.
|
|
137
|
-
* @default false
|
|
138
|
-
*/
|
|
139
|
-
removeAfterFailedToShare: boolean;
|
|
140
152
|
/**
|
|
141
153
|
* Whether to enable autoplay on load
|
|
142
154
|
*/
|
|
@@ -221,6 +233,10 @@ export interface EditorConfig {
|
|
|
221
233
|
*/
|
|
222
234
|
alertOnFailCloseText: string;
|
|
223
235
|
}
|
|
236
|
+
export interface TrimOptions extends BaseOptions {
|
|
237
|
+
startTime: number;
|
|
238
|
+
endTime: number;
|
|
239
|
+
}
|
|
224
240
|
export interface FileValidationResult {
|
|
225
241
|
isValid: boolean;
|
|
226
242
|
fileType: string;
|
|
@@ -236,5 +252,6 @@ export interface VideoTrim extends HybridObject<{
|
|
|
236
252
|
deleteFile(filePath: string): Promise<boolean>;
|
|
237
253
|
closeEditor(onComplete: () => void): void;
|
|
238
254
|
isValidFile(url: string): Promise<FileValidationResult>;
|
|
255
|
+
trim(url: string, options: TrimOptions): Promise<string>;
|
|
239
256
|
}
|
|
240
257
|
//# sourceMappingURL=VideoTrim.nitro.d.ts.map
|