Skip to content

Commit c8608c7

Browse files
committed
[*] refactor: video editor
1 parent 718547e commit c8608c7

File tree

5 files changed

+139
-184
lines changed

5 files changed

+139
-184
lines changed
Lines changed: 46 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use super::command::with_history_manager;
2-
use crate::{global_store, logic_cb, logic::toast, slint_generatedAppWindow::AppWindow};
2+
use crate::{
3+
global_store, logic::toast, logic_cb,
4+
slint_generatedAppWindow::AppWindow,
5+
};
36
use slint::ComponentHandle;
47
use std::{sync::Arc, time::Duration};
58
use video_editor::{
6-
preview::playback::{PlaybackController, PlaybackState},
79
preview::config::PreviewConfig,
10+
preview::playback::{PlaybackController, PlaybackState},
811
tracks::unified_mixer::UnifiedMixerConfig,
912
};
1013

@@ -18,98 +21,74 @@ pub fn init(ui: &AppWindow) {
1821
logic_cb!(video_editor_calc_timeline_offset, ui, index, total_index);
1922
}
2023

21-
// Get or create the playback controller
22-
fn get_playback_controller(ui: &AppWindow) -> Arc<std::sync::Mutex<PlaybackController>> {
23-
// Get preview config from store
24-
let new_project_config = global_store!(ui).get_video_editor_new_project_config();
25-
let ui_preview_config = new_project_config.preview_config;
26-
27-
// Convert UI preview config to UnifiedMixerConfig, then to PreviewConfig
28-
let mixer_config: UnifiedMixerConfig = ui_preview_config.into();
29-
let preview_config: PreviewConfig = mixer_config.into();
30-
31-
let manager = with_history_manager(|state| Arc::new(state.tracks_manager.clone()));
32-
let fps = preview_config.frame_rate();
33-
34-
Arc::new(std::sync::Mutex::new(PlaybackController::new(manager, fps)))
35-
}
36-
3724
fn video_editor_preview_toggle(ui: &AppWindow) {
3825
let controller = get_playback_controller(ui);
39-
let mut controller = controller.lock().unwrap();
26+
let mut ctrl = controller.lock().unwrap();
4027

41-
match controller.state() {
28+
match ctrl.state() {
4229
PlaybackState::Playing => {
43-
// Pause
44-
controller.pause();
30+
ctrl.pause();
4531
global_store!(ui).set_video_editor_is_previewing(false);
4632
toast::async_toast_info(ui.as_weak(), "Paused".to_string());
4733
}
4834
PlaybackState::Stopped | PlaybackState::Paused => {
49-
// Start/resume playing
50-
controller.play();
35+
ctrl.play();
5136
global_store!(ui).set_video_editor_is_previewing(true);
5237
toast::async_toast_info(ui.as_weak(), "Playing".to_string());
5338

54-
// Start background thread to update UI during playback
55-
start_playback_update_thread(ui.as_weak(), get_playback_controller(ui));
39+
start_playback_update_thread(ui.as_weak(), controller.clone());
5640
}
5741
}
5842
}
5943

6044
fn video_editor_preview_rewind(ui: &AppWindow) {
6145
let controller = get_playback_controller(ui);
62-
let mut controller = controller.lock().unwrap();
46+
let mut ctrl = controller.lock().unwrap();
6347

64-
// Rewind by 5 seconds
65-
let current_position = controller.position();
48+
let current_position = ctrl.position();
6649
let rewind_amount = Duration::from_secs(5);
6750

6851
if current_position >= rewind_amount {
6952
let new_position = current_position - rewind_amount;
70-
controller.set_position(new_position);
53+
ctrl.set_position(new_position);
7154
global_store!(ui).set_video_editor_timeline_offset(new_position.as_millis() as i32);
7255
toast::async_toast_info(ui.as_weak(), "Rewind".to_string());
7356
} else {
74-
controller.set_position(Duration::ZERO);
57+
ctrl.set_position(Duration::ZERO);
7558
global_store!(ui).set_video_editor_timeline_offset(0);
7659
toast::async_toast_info(ui.as_weak(), "Already at start".to_string());
7760
}
7861
}
7962

8063
fn video_editor_preview_fast_forward(ui: &AppWindow) {
8164
let controller = get_playback_controller(ui);
82-
let mut controller = controller.lock().unwrap();
65+
let mut ctrl = controller.lock().unwrap();
8366

84-
// Fast forward by 5 seconds
85-
let current_position = controller.position();
67+
let current_position = ctrl.position();
8668
let total_duration = with_history_manager(|state| state.tracks_manager.duration);
8769
let forward_amount = Duration::from_secs(5);
8870

8971
let new_position = current_position.saturating_add(forward_amount);
9072
if new_position < total_duration {
91-
controller.set_position(new_position);
73+
ctrl.set_position(new_position);
9274
global_store!(ui).set_video_editor_timeline_offset(new_position.as_millis() as i32);
9375
toast::async_toast_info(ui.as_weak(), "Fast forward".to_string());
9476
} else {
95-
controller.set_position(total_duration);
77+
ctrl.set_position(total_duration);
9678
global_store!(ui).set_video_editor_timeline_offset(total_duration.as_millis() as i32);
9779
toast::async_toast_info(ui.as_weak(), "Already at end".to_string());
9880
}
9981
}
10082

10183
fn video_editor_preview_jump_previous_segment(ui: &AppWindow) {
102-
// Find previous segment end position
10384
let current_offset = global_store!(ui).get_video_editor_timeline_offset();
10485
let current_position = Duration::from_millis(current_offset as u64);
10586

10687
let prev_segment_end = with_history_manager(|state| {
10788
let mut prev_end = Duration::ZERO;
10889

109-
// Search all tracks for the segment that ends before current position
11090
for track in state.tracks_manager.iter() {
111-
let segments = track.segments();
112-
for segment in segments {
91+
for segment in track.segments() {
11392
let segment_end = segment.timeline_offset + segment.duration;
11493
if segment_end < current_position && segment_end > prev_end {
11594
prev_end = segment_end;
@@ -129,17 +108,14 @@ fn video_editor_preview_jump_previous_segment(ui: &AppWindow) {
129108
}
130109

131110
fn video_editor_preview_jump_next_segment(ui: &AppWindow) {
132-
// Find next segment start position
133111
let current_offset = global_store!(ui).get_video_editor_timeline_offset();
134112
let current_position = Duration::from_millis(current_offset as u64);
135113

136114
let next_segment_start = with_history_manager(|state| {
137115
let mut next_start = None;
138116

139-
// Search all tracks for the segment that starts after current position
140117
for track in state.tracks_manager.iter() {
141-
let segments = track.segments();
142-
for segment in segments {
118+
for segment in track.segments() {
143119
if segment.timeline_offset > current_position {
144120
if next_start.is_none() || segment.timeline_offset < next_start.unwrap() {
145121
next_start = Some(segment.timeline_offset);
@@ -162,24 +138,18 @@ fn video_editor_preview_jump_next_segment(ui: &AppWindow) {
162138
fn video_editor_preview_change_volume(ui: &AppWindow, value: f32) {
163139
let value = value.clamp(0.0, 100.0);
164140

165-
// Update UI state for persistence
166-
// Note: volume is stored in UI state as preview_volumn (note the spelling)
167141
let ui_state = global_store!(ui).get_video_editor_ui_state();
168142
let mut state: super::common_type::VideoEditorUIState = ui_state.into();
169143
state.preview_volumn = value;
170144
let ui_state_new: crate::slint_generatedAppWindow::VideoEditorUIState = state.into();
171145
global_store!(ui).set_video_editor_ui_state(ui_state_new);
172-
173-
// Volume control is now persisted in UI state
174-
// Actual audio volume control would require:
175-
// 1. Adding a GainFilter to audio segments during playback
176-
// 2. Or implementing volume control in the UnifiedMixer
177-
// 3. Passing the gain value to the audio output pipeline
178-
// For now, the volume setting is saved and can be used during export
179146
}
180147

181-
fn video_editor_calc_timeline_offset(_ui: &AppWindow, index: i32, _total_index: i32) -> slint::SharedString {
182-
// Calculate timeline offset in SRT timestamp format: "HH:MM:SS,mmm"
148+
fn video_editor_calc_timeline_offset(
149+
_ui: &AppWindow,
150+
index: i32,
151+
_total_index: i32,
152+
) -> slint::SharedString {
183153
let total_ms = index;
184154

185155
let hours = total_ms / 3600000;
@@ -194,36 +164,39 @@ fn video_editor_calc_timeline_offset(_ui: &AppWindow, index: i32, _total_index:
194164
.into()
195165
}
196166

197-
// Background thread to update UI during playback
167+
// Helper functions
168+
169+
fn get_playback_controller(ui: &AppWindow) -> Arc<std::sync::Mutex<PlaybackController>> {
170+
let new_project_config = global_store!(ui).get_video_editor_new_project_config();
171+
let ui_preview_config = new_project_config.preview_config;
172+
173+
let mixer_config: UnifiedMixerConfig = ui_preview_config.into();
174+
let preview_config: PreviewConfig = mixer_config.into();
175+
176+
let manager = with_history_manager(|state| Arc::new(state.tracks_manager.clone()));
177+
let fps = preview_config.frame_rate();
178+
179+
Arc::new(std::sync::Mutex::new(PlaybackController::new(manager, fps)))
180+
}
181+
198182
fn start_playback_update_thread(
199183
ui_weak: slint::Weak<AppWindow>,
200184
controller: Arc<std::sync::Mutex<PlaybackController>>,
201185
) {
202186
std::thread::spawn(move || {
203-
// Frame rendering requires implementing:
204-
// 1. UnifiedVideoTracksCompositorIterator for video rendering
205-
// 2. UnifiedAudioTracksMixerIterator for audio playback
206-
// 3. Converting rendered frames to Slint images
207-
// 4. Audio output integration
208-
//
209-
// For now, we update timeline position during playback
210-
// Full preview implementation would need a dedicated render pipeline
211-
212187
let frame_rate = controller.lock().unwrap().frame_rate() as f32;
213188
let frame_duration = Duration::from_secs_f64(1.0 / frame_rate as f64);
214189

215190
loop {
216-
// Check if still playing
217191
let (is_playing, percentage, position) = {
218192
let ctrl = controller.lock().unwrap();
219-
let state = ctrl.state();
220-
let percentage = ctrl.percentage();
221-
let position = ctrl.position();
222-
223-
(state == PlaybackState::Playing, percentage, position)
193+
(
194+
ctrl.state() == PlaybackState::Playing,
195+
ctrl.percentage(),
196+
ctrl.position(),
197+
)
224198
};
225199

226-
// Check if playback is finished (reached 100%)
227200
if is_playing && percentage >= 1.0 {
228201
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
229202
global_store!(ui).set_video_editor_is_previewing(false);
@@ -236,15 +209,13 @@ fn start_playback_update_thread(
236209
break;
237210
}
238211

239-
// Update timeline position
240212
let position_ms = position.as_millis() as i32;
241213
let ui_weak_clone = ui_weak.clone();
242214
let _ = ui_weak_clone.upgrade_in_event_loop(move |ui| {
243215
global_store!(ui).set_video_editor_timeline_offset(position_ms);
244216
});
245217

246-
// Sleep for frame duration
247218
std::thread::sleep(frame_duration);
248219
}
249220
});
250-
}
221+
}

0 commit comments

Comments
 (0)