Skip to content

Commit 96f435d

Browse files
committed
[*] refactor
1 parent 99c28f9 commit 96f435d

File tree

6 files changed

+91
-46
lines changed

6 files changed

+91
-46
lines changed

lib/video-editor/src/commands/segment.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ impl AddSegmentCommand {
2727

2828
impl Command for AddSegmentCommand {
2929
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
30+
if self.segment.duration.is_zero() {
31+
return Err(Error::InvalidConfig(
32+
"Cannot add segment with zero duration".into(),
33+
));
34+
}
3035
let track = get_track_from_manager(manager, self.track_index)?;
3136
track.add_segment(self.segment.clone());
3237
self.segment_index = Some(track.segments_count() - 1);
@@ -74,6 +79,11 @@ impl InsertSegmentCommand {
7479

7580
impl Command for InsertSegmentCommand {
7681
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
82+
if self.segment.duration.is_zero() {
83+
return Err(Error::InvalidConfig(
84+
"Cannot insert segment with zero duration".into(),
85+
));
86+
}
7787
let track = get_track_from_manager(manager, self.track_index)?;
7888
track.insert_segment_shift(self.segment_index, self.segment.clone())?;
7989
manager.update_duration();
@@ -128,6 +138,12 @@ impl InsertSegmentAtTimeCommand {
128138

129139
impl Command for InsertSegmentAtTimeCommand {
130140
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
141+
if self.segment.duration.is_zero() {
142+
return Err(Error::InvalidConfig(
143+
"Cannot insert segment with zero duration".into(),
144+
));
145+
}
146+
131147
let track = get_track_from_manager(manager, self.track_index)?;
132148

133149
// Insert segment preserving its timeline_offset

lib/video-editor/src/metadata.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,16 @@ impl Metadata {
104104
}
105105

106106
pub fn new_subtitle() -> Self {
107+
// This value (~24.8 days) is effectively unlimited for video editing purposes.
108+
const SAFE_MAX_DURATION: Duration = Duration::from_secs(2_147_483);
109+
107110
Self {
111+
duration: SAFE_MAX_DURATION,
108112
subtitles: vec![SubtitleMetadata {
109113
index: 0,
110114
codec_id: ffmpeg::codec::Id::None,
111115
language: None,
112-
duration: Duration::MAX,
116+
duration: SAFE_MAX_DURATION,
113117
}],
114118
..Default::default()
115119
}

lib/video-editor/src/tracks/track.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
2-
audio_track::AudioTrack, image_track::ImageTrack, segment::Segment, subtitle_track::SubtitleTrack,
3-
video_track::VideoTrack,
2+
audio_track::AudioTrack, image_track::ImageTrack, segment::Segment,
3+
subtitle_track::SubtitleTrack, video_track::VideoTrack,
44
};
55
use crate::{
66
Error, Result, ensure_file_exists,
@@ -543,9 +543,21 @@ impl Track {
543543
)));
544544
}
545545

546+
if split_offset.is_zero() {
547+
return Err(Error::InvalidConfig(
548+
"Split offset cannot be zero, would create zero-duration segment".into(),
549+
));
550+
}
551+
546552
let left_duration = split_offset;
547553
let right_duration = segment.duration - split_offset;
548554

555+
if right_duration.is_zero() {
556+
return Err(Error::InvalidConfig(
557+
"Split would create zero-duration right segment".into(),
558+
));
559+
}
560+
549561
let left_seg = Self::create_split_segment(
550562
segment,
551563
segment.timeline_offset,
@@ -730,6 +742,13 @@ impl Track {
730742
duration: Duration,
731743
shift_timeline: bool,
732744
) -> Result<()> {
745+
let segment = self.get_segment(segment_index)?;
746+
if segment.duration <= duration {
747+
return Err(Error::InvalidConfig(
748+
"Cannot shrink segment to zero or negative duration".into(),
749+
));
750+
}
751+
733752
self.modify_segment(segment_index, |seg| {
734753
if !shift_timeline {
735754
seg.timeline_offset += duration;
@@ -738,7 +757,7 @@ impl Track {
738757
if !seg.metadata.is_time_independent() {
739758
seg.source_offset += duration;
740759
}
741-
seg.duration = seg.duration.saturating_sub(duration);
760+
seg.duration = seg.duration - duration;
742761
})?;
743762

744763
if shift_timeline {
@@ -758,8 +777,15 @@ impl Track {
758777
duration: Duration,
759778
shift_timeline: bool,
760779
) -> Result<()> {
780+
let segment = self.get_segment(segment_index)?;
781+
if segment.duration <= duration {
782+
return Err(Error::InvalidConfig(
783+
"Cannot shrink segment to zero or negative duration".into(),
784+
));
785+
}
786+
761787
self.modify_segment(segment_index, |seg| {
762-
seg.duration = seg.duration.saturating_sub(duration);
788+
seg.duration = seg.duration - duration;
763789
})?;
764790

765791
if shift_timeline {
@@ -991,8 +1017,8 @@ impl Track {
9911017
let segment_duration = entry.end.saturating_sub(entry.start);
9921018
Arc::new(
9931019
Segment::new_with_source_offset(
994-
entry.start, // timeline_offset(初始化时使用源时间)
995-
Duration::ZERO, // source_offset = 0(不受源时间限制)
1020+
entry.start, // timeline_offset(初始化时使用源时间)
1021+
Duration::ZERO, // source_offset = 0(不受源时间限制)
9961022
segment_duration,
9971023
metadata.clone(),
9981024
)

lib/video-editor/tests/track_operations_test.rs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,7 @@ fn create_empty_track() -> Track {
6363
hiding: false,
6464
muted: false,
6565
locked: false,
66-
track: InnerTrack::new(
67-
create_test_metadata(),
68-
Duration::ZERO,
69-
vec![],
70-
),
66+
track: InnerTrack::new(create_test_metadata(), Duration::ZERO, vec![]),
7167
}))
7268
}
7369

@@ -696,19 +692,6 @@ fn test_empty_track_operations() {
696692
assert!(track.split_segment(0, Duration::from_secs(1)).is_err());
697693
}
698694

699-
#[test]
700-
fn test_saturating_operations() {
701-
let mut track = create_test_track();
702-
703-
// Shrink by more than duration should saturate at ZERO
704-
track
705-
.shrink_segment_right(0, Duration::from_secs(20), false)
706-
.unwrap();
707-
708-
// Duration should be ZERO, not negative
709-
assert_eq!(track.segments()[0].duration, Duration::ZERO);
710-
}
711-
712695
#[test]
713696
fn test_large_duration_values() {
714697
let mut track = create_empty_track();
@@ -822,9 +805,11 @@ fn test_stretch_segment_left_basic() {
822805
);
823806
track.add_segment(Arc::new(segment));
824807
// Set timeline_offset to match source_offset for testing
825-
track.modify_segment(0, |seg| {
826-
seg.timeline_offset = Duration::from_secs(3);
827-
}).unwrap();
808+
track
809+
.modify_segment(0, |seg| {
810+
seg.timeline_offset = Duration::from_secs(3);
811+
})
812+
.unwrap();
828813

829814
// Stretch left by 2 seconds
830815
track
@@ -849,17 +834,21 @@ fn test_stretch_segment_left_with_shift() {
849834
);
850835
track.add_segment(Arc::new(segment1));
851836
// Set timeline_offset to 3
852-
track.modify_segment(0, |seg| {
853-
seg.timeline_offset = Duration::from_secs(3);
854-
}).unwrap();
837+
track
838+
.modify_segment(0, |seg| {
839+
seg.timeline_offset = Duration::from_secs(3);
840+
})
841+
.unwrap();
855842
track.add_segment(create_test_segment(
856843
Duration::from_secs(8),
857844
Duration::from_secs(3),
858845
));
859846
// Set second segment's timeline_offset to 8
860-
track.modify_segment(1, |seg| {
861-
seg.timeline_offset = Duration::from_secs(8);
862-
}).unwrap();
847+
track
848+
.modify_segment(1, |seg| {
849+
seg.timeline_offset = Duration::from_secs(8);
850+
})
851+
.unwrap();
863852

864853
// Stretch left with timeline shift
865854
track
@@ -887,9 +876,11 @@ fn test_stretch_segment_left_clamped_to_source_start() {
887876
);
888877
track.add_segment(Arc::new(segment));
889878
// Set timeline_offset to 2
890-
track.modify_segment(0, |seg| {
891-
seg.timeline_offset = Duration::from_secs(2);
892-
}).unwrap();
879+
track
880+
.modify_segment(0, |seg| {
881+
seg.timeline_offset = Duration::from_secs(2);
882+
})
883+
.unwrap();
893884

894885
// Try to stretch left by 5 seconds (more than source_offset)
895886
track
@@ -930,9 +921,11 @@ fn test_is_segment_overlap_overlapping() {
930921
Duration::from_secs(5),
931922
));
932923
// Manually set the second segment's timeline_offset to 3 (overlapping with first)
933-
track.modify_segment(1, |seg| {
934-
seg.timeline_offset = Duration::from_secs(3);
935-
}).unwrap();
924+
track
925+
.modify_segment(1, |seg| {
926+
seg.timeline_offset = Duration::from_secs(3);
927+
})
928+
.unwrap();
936929

937930
// Segments overlap (0-5 and 3-8)
938931
assert!(track.is_segment_overlap(0, 1));
@@ -976,9 +969,11 @@ fn test_is_segment_overlap_contained() {
976969
Duration::from_secs(4),
977970
));
978971
// Manually set the second segment's timeline_offset to 3 (contained in first)
979-
track.modify_segment(1, |seg| {
980-
seg.timeline_offset = Duration::from_secs(3);
981-
}).unwrap();
972+
track
973+
.modify_segment(1, |seg| {
974+
seg.timeline_offset = Duration::from_secs(3);
975+
})
976+
.unwrap();
982977

983978
// Second segment is contained in first (0-10 contains 3-7)
984979
assert!(track.is_segment_overlap(0, 1));
@@ -1002,4 +997,3 @@ fn test_is_segment_overlap_same_segment() {
1002997
// A segment overlaps with itself
1003998
assert!(track.is_segment_overlap(0, 0));
1004999
}
1005-

wayshot/src/logic/video_editor/segment.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ fn video_editor_commit_segment_resize(
802802
new_duration_ms: i32,
803803
new_offset_ms: i32,
804804
) {
805+
if new_duration_ms <= 0 {
806+
crate::toast_warn!(ui, "Cannot resize segment to zero duration");
807+
return;
808+
}
809+
805810
let shift_timeline = global_store!(ui)
806811
.get_video_editor_ui_state()
807812
.enabled_link_track;

wayshot/ui/panel/desktop/video-editor/tracks/segment.slint

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,8 @@ component Segment inherits Rectangle {
428428
let raw-duration = root.drag-start-duration-ms + round(diff-ms);
429429

430430
// 计算基于源文件的最大 duration(源文件总时长 - 源文件起始偏移)
431-
// image 类型不受源文件时长限制(图片没有固定时长)
432-
let max-source-duration = root.track-type == VideoEditorTrackType.Image ? 100000000 : (root.segment.source-duration - root.segment.source-offset);
431+
// image 和 subtitle 类型不受源文件时长限制(图片没有固定时长,字幕是纯文本
432+
let max-source-duration = (root.track-type == VideoEditorTrackType.Image || root.track-type == VideoEditorTrackType.Subtitle) ? 100000000 : (root.segment.source-duration - root.segment.source-offset);
433433

434434
// 非联动模式下,受下一个 segment 位置限制
435435
let max-next-segment-duration = root.max-end-ms - root.drag-start-offset-ms;

0 commit comments

Comments
 (0)