Skip to content

Commit 303fe7f

Browse files
committed
[*] refactor: video-editor
1 parent 3856283 commit 303fe7f

File tree

2 files changed

+349
-2
lines changed

2 files changed

+349
-2
lines changed

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

Lines changed: 265 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use super::command::Command;
2-
use crate::{Error, Result, tracks::manager::Manager, tracks::track::Track};
3-
use std::sync::Arc;
2+
use crate::{
3+
Error, Result,
4+
tracks::{manager::Manager, track::Track},
5+
};
6+
use std::{sync::Arc, time::Duration};
47

58
pub struct AddTrackCommand {
69
track: Track,
@@ -257,3 +260,263 @@ impl Command for SetTrackVisibilityCommand {
257260
format!("{} track {}", action, self.track_index)
258261
}
259262
}
263+
264+
pub struct TrimGapCommand {
265+
track_index: usize,
266+
segment_index: usize,
267+
start_gap: Option<Duration>,
268+
end_gap: Option<Duration>,
269+
}
270+
271+
impl TrimGapCommand {
272+
pub fn new(track_index: usize, segment_index: usize) -> Self {
273+
Self {
274+
track_index,
275+
segment_index,
276+
start_gap: None,
277+
end_gap: None,
278+
}
279+
}
280+
}
281+
282+
impl Command for TrimGapCommand {
283+
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
284+
let manager_len = manager.len();
285+
let track = manager
286+
.get(self.track_index)
287+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
288+
289+
let segments = track.segments();
290+
291+
if self.segment_index == 0 {
292+
self.start_gap = segments.first().map(|s| s.timeline_offset);
293+
} else {
294+
let prev_segment = &segments[self.segment_index - 1];
295+
let target_segment = &segments[self.segment_index];
296+
let prev_end = prev_segment.timeline_offset + prev_segment.duration;
297+
self.start_gap = Some(target_segment.timeline_offset.saturating_sub(prev_end));
298+
}
299+
300+
if self.segment_index == segments.len() - 1 {
301+
let target_segment = &segments[self.segment_index];
302+
let segment_end = target_segment.timeline_offset + target_segment.duration;
303+
self.end_gap = Some(track.duration().saturating_sub(segment_end));
304+
} else {
305+
let target_segment = &segments[self.segment_index];
306+
let next_segment = &segments[self.segment_index + 1];
307+
let segment_end = target_segment.timeline_offset + target_segment.duration;
308+
self.end_gap = Some(next_segment.timeline_offset.saturating_sub(segment_end));
309+
}
310+
311+
let track = manager
312+
.get_mut(self.track_index)
313+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
314+
315+
track.trim_gap(self.segment_index)
316+
}
317+
318+
fn undo(&mut self, manager: &mut Manager) -> Result<()> {
319+
let start_gap = self.start_gap.unwrap_or(Duration::ZERO);
320+
let end_gap = self.end_gap.unwrap_or(Duration::ZERO);
321+
322+
if start_gap.is_zero() && end_gap.is_zero() {
323+
return Ok(());
324+
}
325+
326+
let manager_len = manager.len();
327+
let track = manager
328+
.get_mut(self.track_index)
329+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
330+
331+
let segments_count = track.segments_count();
332+
333+
if start_gap > Duration::ZERO {
334+
let start_index = if self.segment_index == 0 {
335+
0
336+
} else {
337+
self.segment_index
338+
};
339+
340+
for i in start_index..segments_count {
341+
track.shift_segment_timeline(i, start_gap)?;
342+
}
343+
}
344+
345+
if end_gap > Duration::ZERO {
346+
let end_index = if self.segment_index == segments_count - 1 {
347+
self.segment_index + 1
348+
} else {
349+
self.segment_index + 2
350+
};
351+
352+
if end_index <= segments_count {
353+
for i in end_index..segments_count {
354+
track.shift_segment_timeline(i, end_gap)?;
355+
}
356+
}
357+
}
358+
359+
Ok(())
360+
}
361+
362+
fn describe(&self) -> String {
363+
format!(
364+
"Trim gap from segment {} in track {}",
365+
self.segment_index, self.track_index
366+
)
367+
}
368+
}
369+
370+
pub struct TrimStartGapCommand {
371+
track_index: usize,
372+
segment_index: usize,
373+
removed_gap: Option<Duration>,
374+
}
375+
376+
impl TrimStartGapCommand {
377+
pub fn new(track_index: usize, segment_index: usize) -> Self {
378+
Self {
379+
track_index,
380+
segment_index,
381+
removed_gap: None,
382+
}
383+
}
384+
}
385+
386+
impl Command for TrimStartGapCommand {
387+
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
388+
let manager_len = manager.len();
389+
let track = manager
390+
.get(self.track_index)
391+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
392+
393+
let segments = track.segments();
394+
if self.segment_index == 0 {
395+
self.removed_gap = segments.first().map(|s| s.timeline_offset);
396+
} else {
397+
let prev_segment = &segments[self.segment_index - 1];
398+
let target_segment = &segments[self.segment_index];
399+
let prev_end = prev_segment.timeline_offset + prev_segment.duration;
400+
self.removed_gap = Some(target_segment.timeline_offset.saturating_sub(prev_end));
401+
}
402+
403+
let track = manager
404+
.get_mut(self.track_index)
405+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
406+
track.trim_start_gap(self.segment_index)
407+
}
408+
409+
fn undo(&mut self, manager: &mut Manager) -> Result<()> {
410+
let removed_gap = self
411+
.removed_gap
412+
.ok_or_else(|| Error::InvalidConfig("Command not yet executed".into()))?;
413+
414+
if removed_gap.is_zero() {
415+
return Ok(());
416+
}
417+
418+
let manager_len = manager.len();
419+
let track = manager
420+
.get_mut(self.track_index)
421+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
422+
423+
let start_index = if self.segment_index == 0 {
424+
0
425+
} else {
426+
self.segment_index
427+
};
428+
for i in start_index..track.segments_count() {
429+
track.shift_segment_timeline(i, removed_gap)?;
430+
}
431+
432+
Ok(())
433+
}
434+
435+
fn describe(&self) -> String {
436+
format!(
437+
"Trim start gap from segment {} in track {}",
438+
self.segment_index, self.track_index
439+
)
440+
}
441+
}
442+
443+
pub struct TrimEndGapCommand {
444+
track_index: usize,
445+
segment_index: usize,
446+
removed_gap: Option<Duration>,
447+
}
448+
449+
impl TrimEndGapCommand {
450+
pub fn new(track_index: usize, segment_index: usize) -> Self {
451+
Self {
452+
track_index,
453+
segment_index,
454+
removed_gap: None,
455+
}
456+
}
457+
}
458+
459+
impl Command for TrimEndGapCommand {
460+
fn execute(&mut self, manager: &mut Manager) -> Result<()> {
461+
let manager_len = manager.len();
462+
let track = manager
463+
.get(self.track_index)
464+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
465+
466+
let segments = track.segments();
467+
if self.segment_index == segments.len() - 1 {
468+
let target_segment = &segments[self.segment_index];
469+
let segment_end = target_segment.timeline_offset + target_segment.duration;
470+
self.removed_gap = Some(track.duration().saturating_sub(segment_end));
471+
} else {
472+
let target_segment = &segments[self.segment_index];
473+
let next_segment = &segments[self.segment_index + 1];
474+
let segment_end = target_segment.timeline_offset + target_segment.duration;
475+
self.removed_gap = Some(next_segment.timeline_offset.saturating_sub(segment_end));
476+
}
477+
478+
let track = manager
479+
.get_mut(self.track_index)
480+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
481+
482+
track.trim_end_gap(self.segment_index)
483+
}
484+
485+
fn undo(&mut self, manager: &mut Manager) -> Result<()> {
486+
let removed_gap = self
487+
.removed_gap
488+
.ok_or_else(|| Error::InvalidConfig("Command not yet executed".into()))?;
489+
490+
if removed_gap.is_zero() {
491+
return Ok(());
492+
}
493+
494+
let manager_len = manager.len();
495+
let track = manager
496+
.get_mut(self.track_index)
497+
.ok_or_else(|| Error::IndexOutOfBounds(self.track_index, manager_len))?;
498+
499+
let segments = track.segments();
500+
501+
let end_index = if self.segment_index == segments.len() - 1 {
502+
self.segment_index + 1
503+
} else {
504+
self.segment_index + 2
505+
};
506+
507+
if end_index <= track.segments_count() {
508+
for i in end_index..track.segments_count() {
509+
track.shift_segment_timeline(i, removed_gap)?;
510+
}
511+
}
512+
513+
Ok(())
514+
}
515+
516+
fn describe(&self) -> String {
517+
format!(
518+
"Trim end gap from segment {} in track {}",
519+
self.segment_index, self.track_index
520+
)
521+
}
522+
}

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,90 @@ impl Track {
479479
}
480480
}
481481

482+
pub fn trim_gap(&mut self, segment_index: usize) -> Result<()> {
483+
self.trim_start_gap(segment_index)?;
484+
self.trim_end_gap(segment_index)
485+
}
486+
487+
pub fn trim_start_gap(&mut self, segment_index: usize) -> Result<()> {
488+
if segment_index >= self.segments_count() {
489+
return Err(Error::IndexOutOfBounds(
490+
segment_index,
491+
self.segments_count(),
492+
));
493+
}
494+
495+
if segment_index == 0 {
496+
let segments = self.segments();
497+
let first_segment = &segments[0];
498+
let gap_to_remove = first_segment.timeline_offset;
499+
500+
if gap_to_remove.is_zero() {
501+
return Ok(());
502+
}
503+
504+
for i in 0..self.segments_count() {
505+
self.shift_segment_timeline_backward(i, gap_to_remove)?;
506+
}
507+
} else {
508+
// Remove gap between segment_index-1 and segment_index
509+
let segments = self.segments();
510+
let prev_segment = &segments[segment_index - 1];
511+
let target_segment = &segments[segment_index];
512+
513+
let prev_end = prev_segment.timeline_offset + prev_segment.duration;
514+
let gap = target_segment.timeline_offset.saturating_sub(prev_end);
515+
516+
if gap.is_zero() {
517+
return Ok(());
518+
}
519+
520+
for i in segment_index..self.segments_count() {
521+
self.shift_segment_timeline_backward(i, gap)?;
522+
}
523+
}
524+
525+
Self::update_track_duration(self);
526+
Ok(())
527+
}
528+
529+
pub fn trim_end_gap(&mut self, segment_index: usize) -> Result<()> {
530+
if segment_index >= self.segments_count() {
531+
return Err(Error::IndexOutOfBounds(
532+
segment_index,
533+
self.segments_count(),
534+
));
535+
}
536+
537+
let segments = self.segments();
538+
let target_segment = &segments[segment_index];
539+
540+
if segment_index == self.segments_count() - 1 {
541+
let segment_end = target_segment.timeline_offset + target_segment.duration;
542+
let current_duration = self.duration();
543+
544+
if segment_end >= current_duration {
545+
return Ok(());
546+
}
547+
} else {
548+
// Remove gap between segment_index and segment_index+1
549+
let next_segment = &segments[segment_index + 1];
550+
let segment_end = target_segment.timeline_offset + target_segment.duration;
551+
let gap = next_segment.timeline_offset.saturating_sub(segment_end);
552+
553+
if gap.is_zero() {
554+
return Ok(());
555+
}
556+
557+
for i in (segment_index + 1)..self.segments_count() {
558+
self.shift_segment_timeline_backward(i, gap)?;
559+
}
560+
}
561+
562+
Self::update_track_duration(self);
563+
Ok(())
564+
}
565+
482566
pub fn remove_all_gaps(&mut self) -> Result<()> {
483567
let segments = self.segments().to_vec();
484568

0 commit comments

Comments
 (0)