react-native-video-trim 6.2.3 → 7.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.
@@ -77,6 +77,29 @@ class VideoTrimmerViewController: UIViewController {
77
77
  private var headerTextColor: Double?
78
78
  private var headerView: UIView?
79
79
 
80
+ private(set) var rotationCount = 0
81
+ private(set) var isFlipped = false
82
+ private var isVideoType = true
83
+ private var playerContainerView: UIView!
84
+ private var transformStackView: UIStackView?
85
+
86
+ private var cropBtn: UIButton?
87
+ private var cropOverlayView: CropOverlayView?
88
+ private(set) var isCropActive = false
89
+
90
+ private struct TransformSnapshot: Equatable {
91
+ let rotationCount: Int
92
+ let isFlipped: Bool
93
+ let isCropActive: Bool
94
+ let cropNormalized: CGRect?
95
+ }
96
+ private var undoStack: [TransformSnapshot] = []
97
+ private var redoStack: [TransformSnapshot] = []
98
+ private var undoBtn: UIButton?
99
+ private var redoBtn: UIButton?
100
+ private var preCropSnapshot: TransformSnapshot?
101
+
102
+
80
103
  var isSeekInProgress: Bool = false // Marker
81
104
  private var chaseTime = CMTime.zero
82
105
  private var preferredFrameRate: Float = 23.98
@@ -260,31 +283,44 @@ class VideoTrimmerViewController: UIViewController {
260
283
  headerView = UIView()
261
284
  headerView!.translatesAutoresizingMaskIntoConstraints = false
262
285
  view.addSubview(headerView!)
263
- let headerTextView = UITextView()
264
- headerTextView.text = headerText
265
- headerTextView.textAlignment = .center
266
-
267
- headerTextView.textColor = RCTConvert.uiColor(headerTextColor)
268
- // UIColor.color(fromHexNumber: headerTextColor as NSNumber?, defaultColor: .white)
269
-
270
- headerTextView.font = UIFont.systemFont(ofSize: CGFloat(headerTextSize)) // Set font size here
271
- headerTextView.translatesAutoresizingMaskIntoConstraints = false
272
- headerView!.addSubview(headerTextView)
286
+ let scrollView = UIScrollView()
287
+ scrollView.showsHorizontalScrollIndicator = false
288
+ scrollView.showsVerticalScrollIndicator = false
289
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
290
+ headerView!.addSubview(scrollView)
291
+
292
+ let headerLabel = UILabel()
293
+ headerLabel.text = headerText
294
+ headerLabel.textAlignment = .center
295
+ headerLabel.textColor = RCTConvert.uiColor(headerTextColor)
296
+ headerLabel.font = UIFont.systemFont(ofSize: CGFloat(headerTextSize))
297
+ headerLabel.numberOfLines = 1
298
+ headerLabel.translatesAutoresizingMaskIntoConstraints = false
299
+ scrollView.addSubview(headerLabel)
273
300
 
274
301
  NSLayoutConstraint.activate([
275
- // HeaderView constraints
276
302
  headerView!.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
277
303
  headerView!.leadingAnchor.constraint(equalTo: view.leadingAnchor),
278
304
  headerView!.trailingAnchor.constraint(equalTo: view.trailingAnchor),
279
- headerView!.heightAnchor.constraint(greaterThanOrEqualToConstant: 50),
280
305
 
281
- // HeaderText constraints
282
- headerTextView.topAnchor.constraint(equalTo: headerView!.topAnchor),
283
- headerTextView.bottomAnchor.constraint(equalTo: headerView!.bottomAnchor),
284
- headerTextView.leadingAnchor.constraint(equalTo: headerView!.leadingAnchor),
285
- headerTextView.trailingAnchor.constraint(equalTo: headerView!.trailingAnchor),
306
+ scrollView.topAnchor.constraint(equalTo: headerView!.topAnchor, constant: 6),
307
+ scrollView.bottomAnchor.constraint(equalTo: headerView!.bottomAnchor, constant: -2),
308
+ scrollView.leadingAnchor.constraint(equalTo: headerView!.leadingAnchor),
309
+ scrollView.trailingAnchor.constraint(equalTo: headerView!.trailingAnchor),
310
+ scrollView.heightAnchor.constraint(equalTo: headerLabel.heightAnchor),
311
+
312
+ headerLabel.topAnchor.constraint(equalTo: scrollView.topAnchor),
313
+ headerLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
314
+ headerLabel.leadingAnchor.constraint(greaterThanOrEqualTo: scrollView.leadingAnchor, constant: 16),
315
+ headerLabel.trailingAnchor.constraint(lessThanOrEqualTo: scrollView.trailingAnchor, constant: -16),
286
316
  ])
287
317
 
318
+ let centerX = headerLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor)
319
+ centerX.priority = .defaultHigh
320
+ let minWidth = headerLabel.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor, constant: -32)
321
+ minWidth.priority = .defaultLow
322
+ NSLayoutConstraint.activate([centerX, minWidth])
323
+
288
324
  view.layoutIfNeeded() // layout after activate constraints, otherwise headerView height = screen height, which leads to playerViewController is missing at runtime
289
325
  }
290
326
  }
@@ -407,14 +443,37 @@ class VideoTrimmerViewController: UIViewController {
407
443
  }
408
444
 
409
445
  try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
446
+
447
+ setupTransformButtons()
448
+
449
+ let topAnchor: NSLayoutYAxisAnchor
450
+ if let transformStack = transformStackView {
451
+ topAnchor = transformStack.bottomAnchor
452
+ } else if let headerView = headerView {
453
+ topAnchor = headerView.bottomAnchor
454
+ } else {
455
+ topAnchor = view.safeAreaLayoutGuide.topAnchor
456
+ }
457
+
458
+ playerContainerView = UIView()
459
+ playerContainerView.clipsToBounds = true
460
+ view.addSubview(playerContainerView)
461
+ playerContainerView.translatesAutoresizingMaskIntoConstraints = false
462
+ NSLayoutConstraint.activate([
463
+ playerContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
464
+ playerContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
465
+ playerContainerView.topAnchor.constraint(equalTo: topAnchor, constant: 4),
466
+ playerContainerView.bottomAnchor.constraint(equalTo: trimmer.topAnchor, constant: -16)
467
+ ])
468
+
410
469
  addChild(playerController)
411
- view.addSubview(playerController.view)
470
+ playerContainerView.addSubview(playerController.view)
412
471
  playerController.view.translatesAutoresizingMaskIntoConstraints = false
413
472
  NSLayoutConstraint.activate([
414
- playerController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
415
- playerController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
416
- playerController.view.topAnchor.constraint(equalTo: headerView != nil ? headerView!.bottomAnchor : view.safeAreaLayoutGuide.topAnchor),
417
- playerController.view.bottomAnchor.constraint(equalTo: trimmer.topAnchor, constant: -16)
473
+ playerController.view.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor),
474
+ playerController.view.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor),
475
+ playerController.view.topAnchor.constraint(equalTo: playerContainerView.topAnchor),
476
+ playerController.view.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor)
418
477
  ])
419
478
 
420
479
  // Add observer for the end of playback
@@ -427,6 +486,364 @@ class VideoTrimmerViewController: UIViewController {
427
486
  playBtn.setImage(self.playIcon, for: .normal)
428
487
  }
429
488
 
489
+ // MARK: - Transform (Rotation/Flip/Crop) + Undo/Redo
490
+ private func setupTransformButtons() {
491
+ guard isVideoType else { return }
492
+
493
+ let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium)
494
+ let dimmed = UIColor.white.withAlphaComponent(0.5)
495
+
496
+ let flipBtn = UIButton(type: .system)
497
+ flipBtn.setImage(UIImage(systemName: "arrow.trianglehead.left.and.right.righttriangle.left.righttriangle.right", withConfiguration: symbolConfig), for: .normal)
498
+ flipBtn.tintColor = .white
499
+ flipBtn.addTarget(self, action: #selector(onFlipTapped), for: .touchUpInside)
500
+
501
+ let rotateBtn = UIButton(type: .system)
502
+ rotateBtn.setImage(UIImage(systemName: "rotate.left", withConfiguration: symbolConfig), for: .normal)
503
+ rotateBtn.tintColor = .white
504
+ rotateBtn.addTarget(self, action: #selector(onRotateTapped), for: .touchUpInside)
505
+
506
+ let cropButton = UIButton(type: .system)
507
+ cropButton.setImage(UIImage(systemName: "crop", withConfiguration: symbolConfig), for: .normal)
508
+ cropButton.tintColor = UIColor.white.withAlphaComponent(0.5)
509
+ cropButton.addTarget(self, action: #selector(onCropTapped), for: .touchUpInside)
510
+ self.cropBtn = cropButton
511
+
512
+ let undoButton = UIButton(type: .system)
513
+ undoButton.setImage(UIImage(systemName: "arrow.uturn.backward", withConfiguration: symbolConfig), for: .normal)
514
+ undoButton.tintColor = dimmed
515
+ undoButton.isEnabled = false
516
+ undoButton.addTarget(self, action: #selector(onUndoTapped), for: .touchUpInside)
517
+ self.undoBtn = undoButton
518
+
519
+ let redoButton = UIButton(type: .system)
520
+ redoButton.setImage(UIImage(systemName: "arrow.uturn.forward", withConfiguration: symbolConfig), for: .normal)
521
+ redoButton.tintColor = dimmed
522
+ redoButton.isEnabled = false
523
+ redoButton.addTarget(self, action: #selector(onRedoTapped), for: .touchUpInside)
524
+ self.redoBtn = redoButton
525
+
526
+ let leftStack = UIStackView(arrangedSubviews: [flipBtn, rotateBtn, cropButton])
527
+ leftStack.axis = .horizontal
528
+ leftStack.spacing = 12
529
+
530
+ let rightStack = UIStackView(arrangedSubviews: [undoButton, redoButton])
531
+ rightStack.axis = .horizontal
532
+ rightStack.spacing = 12
533
+
534
+ let spacer = UIView()
535
+ spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
536
+
537
+ let fullRow = UIStackView(arrangedSubviews: [leftStack, spacer, rightStack])
538
+ fullRow.axis = .horizontal
539
+ fullRow.translatesAutoresizingMaskIntoConstraints = false
540
+ fullRow.alpha = 0
541
+
542
+ view.addSubview(fullRow)
543
+ let btnSize: CGFloat = 28
544
+ NSLayoutConstraint.activate([
545
+ flipBtn.widthAnchor.constraint(equalToConstant: btnSize),
546
+ flipBtn.heightAnchor.constraint(equalToConstant: btnSize),
547
+ rotateBtn.widthAnchor.constraint(equalToConstant: btnSize),
548
+ rotateBtn.heightAnchor.constraint(equalToConstant: btnSize),
549
+ cropButton.widthAnchor.constraint(equalToConstant: btnSize),
550
+ cropButton.heightAnchor.constraint(equalToConstant: btnSize),
551
+ undoButton.widthAnchor.constraint(equalToConstant: btnSize),
552
+ undoButton.heightAnchor.constraint(equalToConstant: btnSize),
553
+ redoButton.widthAnchor.constraint(equalToConstant: btnSize),
554
+ redoButton.heightAnchor.constraint(equalToConstant: btnSize),
555
+ fullRow.topAnchor.constraint(equalTo: headerView != nil ? headerView!.bottomAnchor : view.safeAreaLayoutGuide.topAnchor, constant: 4),
556
+ fullRow.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
557
+ fullRow.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
558
+ ])
559
+
560
+ self.transformStackView = fullRow
561
+ }
562
+
563
+ @objc private func onFlipTapped() {
564
+ pushUndo()
565
+ isFlipped.toggle()
566
+ if enableHapticFeedback {
567
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
568
+ }
569
+ updateVideoTransform(resetCrop: true)
570
+ }
571
+
572
+ @objc private func onRotateTapped() {
573
+ pushUndo()
574
+ if isFlipped {
575
+ rotationCount = (rotationCount - 1 + 4) % 4
576
+ } else {
577
+ rotationCount = (rotationCount + 1) % 4
578
+ }
579
+ if enableHapticFeedback {
580
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
581
+ }
582
+ updateVideoTransform(resetCrop: true)
583
+ }
584
+
585
+ private func updateVideoTransform(resetCrop: Bool = false) {
586
+ let angle = -CGFloat(rotationCount) * (.pi / 2)
587
+ var transform = CGAffineTransform.identity
588
+
589
+ if isFlipped {
590
+ transform = transform.scaledBy(x: -1, y: 1)
591
+ }
592
+ transform = transform.rotated(by: angle)
593
+
594
+ if rotationCount % 2 != 0 {
595
+ let bounds = playerContainerView.bounds
596
+ if bounds.width > 0 && bounds.height > 0 {
597
+ let fitScale = min(bounds.width / bounds.height, bounds.height / bounds.width)
598
+ transform = transform.scaledBy(x: fitScale, y: fitScale)
599
+ }
600
+ }
601
+
602
+ UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
603
+ self.playerController.view.transform = transform
604
+ } completion: { _ in
605
+ if resetCrop && self.isCropActive {
606
+ self.updateCropAllowedRect()
607
+ self.cropOverlayView?.resetCrop()
608
+ }
609
+ }
610
+ }
611
+
612
+ // MARK: - Undo / Redo
613
+
614
+ private func currentSnapshot() -> TransformSnapshot {
615
+ TransformSnapshot(
616
+ rotationCount: rotationCount,
617
+ isFlipped: isFlipped,
618
+ isCropActive: isCropActive,
619
+ cropNormalized: cropNormalizedRect
620
+ )
621
+ }
622
+
623
+ private func pushUndo() {
624
+ undoStack.append(currentSnapshot())
625
+ redoStack.removeAll()
626
+ updateUndoRedoButtons()
627
+ }
628
+
629
+ @objc private func onUndoTapped() {
630
+ guard let prev = undoStack.popLast() else { return }
631
+ redoStack.append(currentSnapshot())
632
+ applySnapshot(prev)
633
+ updateUndoRedoButtons()
634
+ }
635
+
636
+ @objc private func onRedoTapped() {
637
+ guard let next = redoStack.popLast() else { return }
638
+ undoStack.append(currentSnapshot())
639
+ applySnapshot(next)
640
+ updateUndoRedoButtons()
641
+ }
642
+
643
+ private func applySnapshot(_ snap: TransformSnapshot) {
644
+ rotationCount = snap.rotationCount
645
+ isFlipped = snap.isFlipped
646
+
647
+ let angle = -CGFloat(rotationCount) * (.pi / 2)
648
+ var transform = CGAffineTransform.identity
649
+ if isFlipped { transform = transform.scaledBy(x: -1, y: 1) }
650
+ transform = transform.rotated(by: angle)
651
+ if rotationCount % 2 != 0 {
652
+ let bounds = playerContainerView.bounds
653
+ if bounds.width > 0 && bounds.height > 0 {
654
+ let fitScale = min(bounds.width / bounds.height, bounds.height / bounds.width)
655
+ transform = transform.scaledBy(x: fitScale, y: fitScale)
656
+ }
657
+ }
658
+
659
+ let wasActive = isCropActive
660
+ isCropActive = snap.isCropActive
661
+ cropBtn?.tintColor = isCropActive ? .white : UIColor.white.withAlphaComponent(0.5)
662
+
663
+ UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
664
+ self.playerController.view.transform = transform
665
+ } completion: { _ in
666
+ if self.isCropActive {
667
+ self.showCropOverlayImmediate()
668
+ self.updateCropAllowedRect()
669
+ if let norm = snap.cropNormalized {
670
+ self.setCropFromNormalized(norm)
671
+ } else {
672
+ self.cropOverlayView?.resetCrop()
673
+ }
674
+ } else if wasActive {
675
+ self.cropOverlayView?.isHidden = true
676
+ self.cropOverlayView?.alpha = 0
677
+ }
678
+ }
679
+ }
680
+
681
+ private func setCropFromNormalized(_ norm: CGRect) {
682
+ let vr = getVideoDisplayRectInContainer()
683
+ guard vr.width > 1, vr.height > 1 else { return }
684
+ cropOverlayView?.cropRect = CGRect(
685
+ x: vr.minX + norm.origin.x * vr.width,
686
+ y: vr.minY + norm.origin.y * vr.height,
687
+ width: norm.size.width * vr.width,
688
+ height: norm.size.height * vr.height
689
+ )
690
+ }
691
+
692
+ private func updateUndoRedoButtons() {
693
+ let dimmed = UIColor.white.withAlphaComponent(0.5)
694
+ undoBtn?.tintColor = undoStack.isEmpty ? dimmed : .white
695
+ undoBtn?.isEnabled = !undoStack.isEmpty
696
+ redoBtn?.tintColor = redoStack.isEmpty ? dimmed : .white
697
+ redoBtn?.isEnabled = !redoStack.isEmpty
698
+ }
699
+
700
+ // MARK: - Crop
701
+
702
+ @objc private func onCropTapped() {
703
+ isCropActive.toggle()
704
+ cropBtn?.tintColor = isCropActive ? .white : UIColor.white.withAlphaComponent(0.5)
705
+
706
+ if enableHapticFeedback {
707
+ UIImpactFeedbackGenerator(style: .light).impactOccurred()
708
+ }
709
+
710
+ if isCropActive {
711
+ showCropOverlay()
712
+ } else {
713
+ hideCropOverlay()
714
+ }
715
+ }
716
+
717
+ private func showCropOverlay() {
718
+ if let existing = cropOverlayView {
719
+ existing.isHidden = false
720
+ existing.alpha = 0
721
+ playerContainerView.layoutIfNeeded()
722
+ updateCropAllowedRect()
723
+ UIView.animate(withDuration: 0.2) { existing.alpha = 1 }
724
+ return
725
+ }
726
+
727
+ createCropOverlay()
728
+ playerContainerView.layoutIfNeeded()
729
+ updateCropAllowedRect()
730
+ UIView.animate(withDuration: 0.2) { self.cropOverlayView?.alpha = 1 }
731
+ }
732
+
733
+ private func showCropOverlayImmediate() {
734
+ if let existing = cropOverlayView {
735
+ existing.isHidden = false
736
+ existing.alpha = 1
737
+ return
738
+ }
739
+ createCropOverlay()
740
+ cropOverlayView?.alpha = 1
741
+ }
742
+
743
+ private func createCropOverlay() {
744
+ let overlay = CropOverlayView()
745
+ overlay.translatesAutoresizingMaskIntoConstraints = false
746
+ overlay.alpha = 0
747
+ playerContainerView.addSubview(overlay)
748
+ NSLayoutConstraint.activate([
749
+ overlay.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor),
750
+ overlay.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor),
751
+ overlay.topAnchor.constraint(equalTo: playerContainerView.topAnchor),
752
+ overlay.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor),
753
+ ])
754
+ cropOverlayView = overlay
755
+
756
+ overlay.onCropBegan = { [weak self] in
757
+ self?.preCropSnapshot = self?.currentSnapshot()
758
+ }
759
+ overlay.onCropEnded = { [weak self] in
760
+ guard let self = self, let snap = self.preCropSnapshot else { return }
761
+ if self.currentSnapshot() != snap {
762
+ self.undoStack.append(snap)
763
+ self.redoStack.removeAll()
764
+ self.updateUndoRedoButtons()
765
+ }
766
+ self.preCropSnapshot = nil
767
+ }
768
+ }
769
+
770
+ private func hideCropOverlay() {
771
+ guard let overlay = cropOverlayView else { return }
772
+ UIView.animate(withDuration: 0.2, animations: {
773
+ overlay.alpha = 0
774
+ }) { _ in
775
+ overlay.isHidden = true
776
+ }
777
+ }
778
+
779
+ private func updateCropAllowedRect() {
780
+ guard let overlay = cropOverlayView else { return }
781
+ overlay.allowedRect = getVideoDisplayRectInContainer()
782
+ }
783
+
784
+ func getVideoDisplayRectInContainer() -> CGRect {
785
+ guard let containerView = playerContainerView,
786
+ let asset = asset,
787
+ let track = asset.tracks(withMediaType: .video).first else {
788
+ return playerContainerView?.bounds ?? .zero
789
+ }
790
+
791
+ let raw = track.naturalSize
792
+ let pt = track.preferredTransform
793
+ let angle = atan2(pt.b, pt.a)
794
+ let isSourceRotated = abs(angle - .pi / 2) < 0.1 || abs(angle + .pi / 2) < 0.1
795
+ let displayedSize = isSourceRotated
796
+ ? CGSize(width: raw.height, height: raw.width)
797
+ : raw
798
+
799
+ let pvBounds = playerController.view.bounds
800
+ guard pvBounds.width > 0, pvBounds.height > 0,
801
+ displayedSize.width > 0, displayedSize.height > 0 else {
802
+ return containerView.bounds
803
+ }
804
+
805
+ let videoAR = displayedSize.width / displayedSize.height
806
+ let viewAR = pvBounds.width / pvBounds.height
807
+
808
+ var videoRect: CGRect
809
+ if videoAR > viewAR {
810
+ let h = pvBounds.width / videoAR
811
+ videoRect = CGRect(x: 0, y: (pvBounds.height - h) / 2,
812
+ width: pvBounds.width, height: h)
813
+ } else {
814
+ let w = pvBounds.height * videoAR
815
+ videoRect = CGRect(x: (pvBounds.width - w) / 2, y: 0,
816
+ width: w, height: pvBounds.height)
817
+ }
818
+
819
+ return playerController.view.convert(videoRect, to: containerView)
820
+ }
821
+
822
+ var cropNormalizedRect: CGRect? {
823
+ guard isCropActive,
824
+ let overlay = cropOverlayView, !overlay.isHidden else { return nil }
825
+
826
+ let videoRect = getVideoDisplayRectInContainer()
827
+ guard videoRect.width > 1, videoRect.height > 1 else { return nil }
828
+
829
+ let cr = overlay.cropRect
830
+ let nx = (cr.minX - videoRect.minX) / videoRect.width
831
+ let ny = (cr.minY - videoRect.minY) / videoRect.height
832
+ let nw = cr.width / videoRect.width
833
+ let nh = cr.height / videoRect.height
834
+
835
+ if nx < 0.01 && ny < 0.01 && nw > 0.99 && nh > 0.99 {
836
+ return nil
837
+ }
838
+
839
+ return CGRect(
840
+ x: max(0, min(1, nx)),
841
+ y: max(0, min(1, ny)),
842
+ width: max(0, min(1, nw)),
843
+ height: max(0, min(1, nh))
844
+ )
845
+ }
846
+
430
847
  private func setupTimeObserver() {
431
848
  timeObserverToken = player.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 30), queue: .main) { [weak self] time in
432
849
  guard let self = self else { return }
@@ -504,6 +921,7 @@ class VideoTrimmerViewController: UIViewController {
504
921
  enableHapticFeedback = config["enableHapticFeedback"] as? Bool ?? true
505
922
  zoomOnWaitingDuration = (config["zoomOnWaitingDuration"] as? Double ?? 5.0) / 1000.0 // convert ms to s
506
923
  autoplay = config["autoplay"] as? Bool ?? false
924
+ isVideoType = (config["type"] as? String ?? "video") == "video"
507
925
  headerText = config["headerText"] as? String
508
926
  headerTextSize = config["headerTextSize"] as? Int ?? 16
509
927
  headerTextColor = config["headerTextColor"] as? Double
@@ -530,6 +948,7 @@ class VideoTrimmerViewController: UIViewController {
530
948
  self.playBtn.isEnabled = true
531
949
  self.saveBtn.alpha = 1
532
950
  self.saveBtn.isEnabled = true
951
+ self.transformStackView?.alpha = 1
533
952
  })
534
953
 
535
954
  if jumpToPositionOnLoad > 0 {
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAkBA;AACA;AACA;;AAgGA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;AAgGA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
@@ -14,8 +14,7 @@ function createBaseOptions(overrides = {}) {
14
14
  outputExt: 'mp4',
15
15
  removeAfterSavedToPhoto: false,
16
16
  removeAfterFailedToSavePhoto: false,
17
- enableRotation: false,
18
- rotationAngle: 0,
17
+ enablePreciseTrimming: false,
19
18
  ...overrides
20
19
  };
21
20
  }
@@ -1 +1 @@
1
- {"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enableRotation","rotationAngle","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","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","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","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","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,gBAAgB,MAAM,sBAAmB;AAChD,OAAOC,gBAAgB,MAAM,cAAW;AAQxC,SAASC,YAAY,QAAQ,cAAc;;AAE3C;AACA,MAAMC,QAAQ,GAAG,CAAC,CAAEC,MAAM,CAASC,qBAAqB;AACxD,MAAMC,SAAS,GAAGH,QAAQ,GAAGH,gBAAgB,GAAGC,gBAAgB;AAEhE,SAASM,iBAAiBA,CAACC,SAA+B,GAAG,CAAC,CAAC,EAAe;EAC5E,OAAO;IACLC,WAAW,EAAE,KAAK;IAClBC,IAAI,EAAE,OAAO;IACbC,SAAS,EAAE,KAAK;IAChBC,uBAAuB,EAAE,KAAK;IAC9BC,4BAA4B,EAAE,KAAK;IACnCC,qBAAqB,EAAE,KAAK;IAC5B,GAAGN;EACL,CAAC;AACH;AAEA,SAASO,kBAAkBA,CACzBP,SAAgC,GAAG,CAAC,CAAC,EACvB;EACd,OAAO;IACLQ,oBAAoB,EAAE,IAAI;IAC1BC,WAAW,EAAE,CAAC,CAAC;IACfC,WAAW,EAAE,CAAC,CAAC;IACfC,qBAAqB,EAAE,KAAK;IAC5BC,sBAAsB,EAAE,KAAK;IAC7BC,2BAA2B,EAAE,KAAK;IAClCC,gCAAgC,EAAE,KAAK;IACvCC,iBAAiB,EAAE,KAAK;IACxBC,wBAAwB,EAAE,KAAK;IAC/BC,gBAAgB,EAAE,QAAQ;IAC1BC,cAAc,EAAE,MAAM;IACtBC,kBAAkB,EAAE,IAAI;IACxBC,iBAAiB,EAAE,UAAU;IAC7BC,mBAAmB,EAAE,8BAA8B;IACnDC,sBAAsB,EAAE,OAAO;IAC/BC,uBAAuB,EAAE,SAAS;IAClCC,gBAAgB,EAAE,IAAI;IACtBC,eAAe,EAAE,eAAe;IAChCC,iBAAiB,EAAE,4BAA4B;IAC/CC,oBAAoB,EAAE,OAAO;IAC7BC,qBAAqB,EAAE,SAAS;IAChCC,YAAY,EAAE,mBAAmB;IACjCC,kBAAkB,EAAE,KAAK;IACzBC,QAAQ,EAAE,KAAK;IACfC,oBAAoB,EAAE,CAAC,CAAC;IACxBC,eAAe,EAAE,IAAI;IACrBC,oBAAoB,EAAE,IAAI;IAC1BC,wBAAwB,EAAE,QAAQ;IAClCC,0BAA0B,EAAE,IAAI;IAChCC,yBAAyB,EAAE,UAAU;IACrCC,2BAA2B,EAAE,uCAAuC;IACpEC,8BAA8B,EAAE,OAAO;IACvCC,+BAA+B,EAAE,SAAS;IAC1CC,UAAU,EAAE,EAAE;IACdC,cAAc,EAAE,EAAE;IAClBC,eAAe,EAAEjD,YAAY,CAAC,OAAO,CAAW;IAChDkD,YAAY,EAAElD,YAAY,CAAC,SAAS,CAAW;IAC/CmD,eAAe,EAAEnD,YAAY,CAAC,OAAO,CAAW;IAChDoD,qBAAqB,EAAE,IAAI;IAC3BC,iBAAiB,EAAE,IAAI;IACvBC,gBAAgB,EAAE,OAAO;IACzBC,kBAAkB,EAChB,oEAAoE;IACtEC,oBAAoB,EAAE,OAAO;IAC7B,GAAGnD,iBAAiB,CAACC,SAAS,CAAC;IAC/B,GAAGA;EACL,CAAC;AACH;AAEA,SAASmD,iBAAiBA,CAACnD,SAA+B,GAAG,CAAC,CAAC,EAAe;EAC5E,OAAO;IACLoD,SAAS,EAAE,CAAC;IACZC,OAAO,EAAE,IAAI;IACb,GAAGtD,iBAAiB,CAACC,SAAS,CAAC;IAC/B,GAAGA;EACL,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASsD,UAAUA,CACxBC,QAAgB,EAChBC,MAMC,EACK;EACN,MAAM;IAAEb,eAAe;IAAEC,YAAY;IAAEC;EAAgB,CAAC,GAAGW,MAAM;EACjE,MAAMC,gBAAgB,GAAG/D,YAAY,CAACiD,eAAe,IAAI,OAAO,CAAC;EACjE,MAAMe,aAAa,GAAGhE,YAAY,CAACkD,YAAY,IAAI,SAAS,CAAC;EAC7D,MAAMe,gBAAgB,GAAGjE,YAAY,CAACmD,eAAe,IAAI,OAAO,CAAC;EAEjE/C,SAAS,CAACwD,UAAU,CAClBC,QAAQ,EACRhD,kBAAkB,CAAC;IACjB,GAAGiD,MAAM;IACTb,eAAe,EAAEc,gBAAuB;IACxCb,YAAY,EAAEc,aAAoB;IAClCb,eAAe,EAAEc;EACnB,CAAC,CACH,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAAA,EAAsB;EAC7C,OAAO9D,SAAS,CAAC8D,SAAS,CAAC,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAAA,EAAoB;EAC5C,OAAO/D,SAAS,CAAC+D,UAAU,CAAC,CAAC;AAC/B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAACP,QAAgB,EAAoB;EAC7D,IAAI,CAACA,QAAQ,EAAEQ,IAAI,CAAC,CAAC,CAACC,MAAM,EAAE;IAC5B,MAAM,IAAIC,KAAK,CAAC,4BAA4B,CAAC;EAC/C;EACA,OAAOnE,SAAS,CAACgE,UAAU,CAACP,QAAQ,CAAC;AACvC;;AAEA;AACA;AACA;AACA,OAAO,SAASW,WAAWA,CAAA,EAAS;EAClC,OAAOpE,SAAS,CAACoE,WAAW,CAAC,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,WAAWA,CAACC,GAAW,EAAiC;EACtE,OAAOtE,SAAS,CAACqE,WAAW,CAACC,GAAG,CAAC;AACnC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASL,IAAIA,CAClBK,GAAW,EACXC,OAA6B,EACR;EACrB,OAAOvE,SAAS,CAACiE,IAAI,CAACK,GAAG,EAAEjB,iBAAiB,CAACkB,OAAO,CAAC,CAAC;AACxD;AAEA,cAAc,sBAAmB;AACjC,eAAevE,SAAS","ignoreList":[]}
@@ -14,10 +14,16 @@ export interface BaseOptions {
14
14
  removeAfterSavedToPhoto: boolean;
15
15
  /** Whether to remove the output file if saving to the photo library fails. */
16
16
  removeAfterFailedToSavePhoto: boolean;
17
- /** Whether to enable video rotation during trimming. */
18
- enableRotation: boolean;
19
- /** Rotation angle in degrees (e.g. `90`, `180`, `270`). Only used when `enableRotation` is `true`. */
20
- rotationAngle: number;
17
+ /**
18
+ * When `true`, FFmpeg re-encodes the video using the platform's hardware encoder
19
+ * (h264_videotoolbox on iOS, h264_mediacodec on Android) for frame-accurate trimming.
20
+ * When `false` (default), uses stream copy (`-c copy`) which is much faster but can
21
+ * only cut at keyframes — the actual start/end may drift by several seconds.
22
+ *
23
+ * Note: if the user applies any transform (flip/rotate/crop), re-encoding already
24
+ * happens regardless of this flag, so precise trimming comes for free in that case.
25
+ */
26
+ enablePreciseTrimming: boolean;
21
27
  }
22
28
  /**
23
29
  * Configuration for the video trimmer editor UI.
@@ -1 +1 @@
1
- {"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,uBAAuB,EAAE,OAAO,CAAC;IACjC,8EAA8E;IAC9E,4BAA4B,EAAE,OAAO,CAAC;IACtC;;;;;;;;OAQG;IACH,qBAAqB,EAAE,OAAO,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,2EAA2E;IAC3E,oBAAoB,EAAE,OAAO,CAAC;IAC9B,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,qBAAqB,EAAE,OAAO,CAAC;IAC/B,+DAA+D;IAC/D,sBAAsB,EAAE,OAAO,CAAC;IAChC,8EAA8E;IAC9E,2BAA2B,EAAE,OAAO,CAAC;IACrC,sEAAsE;IACtE,gCAAgC,EAAE,OAAO,CAAC;IAC1C,kEAAkE;IAClE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,wBAAwB,EAAE,OAAO,CAAC;IAClC,kCAAkC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,kBAAkB,EAAE,OAAO,CAAC;IAC5B,+CAA+C;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,uBAAuB,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oFAAoF;IACpF,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,eAAe,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,oBAAoB,EAAE,OAAO,CAAC;IAC9B,2CAA2C;IAC3C,wBAAwB,EAAE,MAAM,CAAC;IACjC,iFAAiF;IACjF,0BAA0B,EAAE,OAAO,CAAC;IACpC,wDAAwD;IACxD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0DAA0D;IAC1D,2BAA2B,EAAE,MAAM,CAAC;IACpC,iEAAiE;IACjE,8BAA8B,EAAE,MAAM,CAAC;IACvC,iEAAiE;IACjE,+BAA+B,EAAE,MAAM,CAAC;IACxC,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kGAAkG;IAClG,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,wDAAwD;IACxD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IACzD,+DAA+D;IAC/D,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,sGAAsG;IACtG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,0EAA0E;IAC1E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,iEAAiE;IACjE,WAAW,IAAI,IAAI,CAAC;IACpB,yEAAyE;IACzE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACxD,mFAAmF;IACnF,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE7D,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7C,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9C,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,yFAAyF;IACzF,QAAQ,CAAC,gBAAgB,EAAE,YAAY,CAAC;QACtC,gDAAgD;QAChD,UAAU,EAAE,MAAM,CAAC;QACnB,uDAAuD;QACvD,SAAS,EAAE,MAAM,CAAC;QAClB,qDAAqD;QACrD,OAAO,EAAE,MAAM,CAAC;QAChB,oDAAoD;QACpD,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;QAC3B,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;QAC5B,oDAAoD;QACpD,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;;AAED,wBAAmE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA6EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,CAeN;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,cAAc,mBAAmB,CAAC;AAClC,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA4EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,CAeN;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,cAAc,mBAAmB,CAAC;AAClC,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-video-trim",
3
- "version": "6.2.3",
3
+ "version": "7.0.0",
4
4
  "description": "Video trimmer for your React Native app",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -16,10 +16,16 @@ export interface BaseOptions {
16
16
  removeAfterSavedToPhoto: boolean;
17
17
  /** Whether to remove the output file if saving to the photo library fails. */
18
18
  removeAfterFailedToSavePhoto: boolean;
19
- /** Whether to enable video rotation during trimming. */
20
- enableRotation: boolean;
21
- /** Rotation angle in degrees (e.g. `90`, `180`, `270`). Only used when `enableRotation` is `true`. */
22
- rotationAngle: number;
19
+ /**
20
+ * When `true`, FFmpeg re-encodes the video using the platform's hardware encoder
21
+ * (h264_videotoolbox on iOS, h264_mediacodec on Android) for frame-accurate trimming.
22
+ * When `false` (default), uses stream copy (`-c copy`) which is much faster but can
23
+ * only cut at keyframes — the actual start/end may drift by several seconds.
24
+ *
25
+ * Note: if the user applies any transform (flip/rotate/crop), re-encoding already
26
+ * happens regardless of this flag, so precise trimming comes for free in that case.
27
+ */
28
+ enablePreciseTrimming: boolean;
23
29
  }
24
30
 
25
31
  /**
package/src/index.tsx CHANGED
@@ -20,8 +20,7 @@ function createBaseOptions(overrides: Partial<BaseOptions> = {}): BaseOptions {
20
20
  outputExt: 'mp4',
21
21
  removeAfterSavedToPhoto: false,
22
22
  removeAfterFailedToSavePhoto: false,
23
- enableRotation: false,
24
- rotationAngle: 0,
23
+ enablePreciseTrimming: false,
25
24
  ...overrides,
26
25
  };
27
26
  }