Skip to content

Commit 35329fe

Browse files
committed
[*] refactor: video-editor
1 parent 876a998 commit 35329fe

File tree

2 files changed

+71
-144
lines changed

2 files changed

+71
-144
lines changed

wayshot/src/logic/video_editor/export.rs

Lines changed: 51 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::project::PROJECT_STATE;
22
use crate::{
3-
global_logic, global_store,
3+
global_logic,
44
logic::{toast, tr::tr},
55
logic_cb,
66
slint_generatedAppWindow::{
@@ -131,15 +131,11 @@ fn video_editor_export_video(ui: &AppWindow, config: UIVideoEditorExportVideoCon
131131
toast::async_toast_success(ui_weak, "Video export completed".to_string());
132132
}
133133
Ok(Err(e)) => toast::async_toast_warn(ui_weak, format!("Video export failed: {}", e)),
134-
Err(_) => {
135-
toast::async_toast_warn(ui_weak, "Video export was cancelled".to_string());
136-
}
134+
Err(_) => toast::async_toast_warn(ui_weak, "Video export was cancelled".to_string()),
137135
}
138136
});
139137
}
140138

141-
// todo
142-
143139
fn video_editor_export_audio(ui: &AppWindow, config: UIVideoEditorExportAudioConfig) {
144140
let ui_weak = ui.as_weak();
145141

@@ -155,49 +151,36 @@ fn video_editor_export_audio(ui: &AppWindow, config: UIVideoEditorExportAudioCon
155151
.to_string()
156152
};
157153

158-
let ext = match config.format {
159-
crate::slint_generatedAppWindow::AudioFormat::Aac => "aac",
160-
crate::slint_generatedAppWindow::AudioFormat::Mp3 => "mp3",
161-
crate::slint_generatedAppWindow::AudioFormat::Ogg => "ogg",
162-
crate::slint_generatedAppWindow::AudioFormat::Wav => "wav",
163-
crate::slint_generatedAppWindow::AudioFormat::Flac => "flac",
164-
};
154+
let ext = global_logic!(ui)
155+
.invoke_audio_format_to_str(config.format)
156+
.to_ascii_lowercase();
157+
let format = config.format.into();
158+
let channels = global_logic!(ui).invoke_audio_channels_to_int(config.channels) as u16;
159+
let sample_rate = global_logic!(ui).invoke_audio_sample_rate_to_int(config.sample_rate) as u32;
165160

166161
let Some(output_path) = picker_save_file(
167162
ui_weak.clone(),
168163
&tr("Export Audio"),
169164
&tr("Audio File"),
170-
&[ext],
165+
&[&ext],
171166
&format!("{}.{}", default_name, ext),
172167
) else {
173168
return;
174169
};
175170

171+
let file_name = output_path
172+
.file_name()
173+
.and_then(|s| s.to_str())
174+
.unwrap_or("video.mp4")
175+
.to_string();
176+
176177
tokio::spawn(async move {
177178
let manager = {
178179
let state_guard = PROJECT_STATE.lock().unwrap();
179180
let state = state_guard.as_ref().unwrap();
180181
Arc::new(state.tracks_manager.clone())
181182
};
182183

183-
let format = config.format.into();
184-
185-
let channels = match config.channels {
186-
crate::slint_generatedAppWindow::AudioChannels::Mono => 1,
187-
crate::slint_generatedAppWindow::AudioChannels::Stereo => 2,
188-
};
189-
190-
let sample_rate = match config.sample_rate {
191-
crate::slint_generatedAppWindow::AudioSampleRate::Hz8000 => 8000,
192-
crate::slint_generatedAppWindow::AudioSampleRate::Hz16000 => 16000,
193-
crate::slint_generatedAppWindow::AudioSampleRate::Hz24000 => 24000,
194-
crate::slint_generatedAppWindow::AudioSampleRate::Hz32000 => 32000,
195-
crate::slint_generatedAppWindow::AudioSampleRate::Hz44100 => 44100,
196-
crate::slint_generatedAppWindow::AudioSampleRate::Hz48000 => 48000,
197-
crate::slint_generatedAppWindow::AudioSampleRate::Hz96000 => 96000,
198-
crate::slint_generatedAppWindow::AudioSampleRate::Hz192000 => 192000,
199-
};
200-
201184
let export_config = AudioExportConfig::default()
202185
.with_output_path(output_path.clone())
203186
.with_format(format)
@@ -206,18 +189,19 @@ fn video_editor_export_audio(ui: &AppWindow, config: UIVideoEditorExportAudioCon
206189

207190
let task_id = NEXT_TASK_ID.fetch_add(1, Ordering::SeqCst);
208191

209-
let mut task = UIVideoEditorExportQueueItem::default();
210-
task.name = format!("Audio [{}]", task_id).into();
211-
task.media_type = UIMediaType::Audio;
212-
task.progress = 0.0;
213-
task.is_cancelled = false;
192+
let task = UIVideoEditorExportQueueItem {
193+
id: format!("Audio [{}]", task_id).into(),
194+
name: file_name.into(),
195+
media_type: UIMediaType::Audio,
196+
progress: 0.0,
197+
is_cancelled: false,
198+
};
214199

215200
_ = ui_weak.upgrade_in_event_loop(move |ui| {
216201
store_video_editor_export_queue!(ui).push(task);
217202
});
218203

219204
let ui_weak_for_progress = ui_weak.clone();
220-
221205
let result = tokio::task::spawn_blocking(move || {
222206
let exporter = AudioExporter::new(manager, export_config);
223207
exporter.export_with_progress(move |progress| {
@@ -233,30 +217,10 @@ fn video_editor_export_audio(ui: &AppWindow, config: UIVideoEditorExportAudioCon
233217
match result {
234218
Ok(Ok(_)) => {
235219
update_export_task_progress(&ui_weak, task_id, 100.0);
236-
237-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
238-
crate::logic::toast::async_toast_success(
239-
ui.as_weak(),
240-
"Audio export completed".to_string(),
241-
);
242-
});
243-
}
244-
Ok(Err(e)) => {
245-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
246-
crate::logic::toast::async_toast_warn(
247-
ui.as_weak(),
248-
format!("Audio export failed: {}", e),
249-
);
250-
});
251-
}
252-
Err(_) => {
253-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
254-
crate::logic::toast::async_toast_warn(
255-
ui.as_weak(),
256-
"Audio export was cancelled".to_string(),
257-
);
258-
});
220+
toast::async_toast_success(ui_weak, "Audio export completed".to_string());
259221
}
222+
Ok(Err(e)) => toast::async_toast_warn(ui_weak, format!("Audio export failed: {}", e)),
223+
Err(_) => toast::async_toast_warn(ui_weak, "Audio export was cancelled".to_string()),
260224
}
261225
});
262226
}
@@ -276,142 +240,85 @@ fn video_editor_export_subtitle(ui: &AppWindow, ty: UISubtitleType) {
276240
.to_string()
277241
};
278242

279-
let ext = match ty {
280-
UISubtitleType::Srt => "srt",
281-
UISubtitleType::Vtt => "vtt",
282-
UISubtitleType::Ass => "ass",
283-
};
243+
let format = ty.into();
244+
let ext = global_logic!(ui).invoke_subtitle_to_str(ty);
284245

285246
let Some(output_path) = picker_save_file(
286247
ui_weak.clone(),
287248
&tr("Export Subtitle"),
288249
&tr("Subtitle File"),
289-
&[ext],
250+
&[&ext],
290251
&format!("{}.{}", default_name, ext),
291252
) else {
292253
return;
293254
};
294255

256+
let file_name = output_path
257+
.file_name()
258+
.and_then(|s| s.to_str())
259+
.unwrap_or("video.mp4")
260+
.to_string();
261+
295262
tokio::spawn(async move {
296263
let manager = {
297264
let state_guard = PROJECT_STATE.lock().unwrap();
298265
let state = state_guard.as_ref().unwrap();
299266
Arc::new(state.tracks_manager.clone())
300267
};
301268

302-
let format = ty.into();
303-
304269
let export_config = SubtitleExportConfig::default()
305270
.with_output_base_path(output_path.clone())
306271
.with_format(format);
307272

308273
let task_id = NEXT_TASK_ID.fetch_add(1, Ordering::SeqCst);
309274

310-
let mut task = UIVideoEditorExportQueueItem::default();
311-
task.name = format!("Subtitle [{}]", task_id).into();
312-
task.media_type = UIMediaType::Subtitle;
313-
task.progress = 0.0;
314-
task.is_cancelled = false;
275+
let task = UIVideoEditorExportQueueItem {
276+
name: file_name.into(),
277+
id: format!("Subtitle [{}]", task_id).into(),
278+
media_type: UIMediaType::Subtitle,
279+
progress: 0.0,
280+
is_cancelled: false,
281+
};
315282

316283
_ = ui_weak.upgrade_in_event_loop(move |ui| {
317284
store_video_editor_export_queue!(ui).push(task);
318285
});
319286

320287
let ui_weak_for_progress = ui_weak.clone();
321-
322288
let result = tokio::task::spawn_blocking(move || {
323289
let exporter = SubtitleExporter::new(manager, export_config);
324-
325290
update_export_task_progress(&ui_weak_for_progress, task_id, 50.0);
326-
327291
let result = exporter.export_all_tracks();
328-
329292
update_export_task_progress(&ui_weak_for_progress, task_id, 100.0);
330-
331293
result
332294
})
333295
.await;
334296

335297
match result {
336298
Ok(Ok(_)) => {
337-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
338-
crate::logic::toast::async_toast_success(
339-
ui.as_weak(),
340-
"Subtitle export completed".to_string(),
341-
);
342-
});
299+
toast::async_toast_success(ui_weak, "Subtitle export completed".to_string())
343300
}
344301
Ok(Err(e)) => {
345-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
346-
crate::logic::toast::async_toast_warn(
347-
ui.as_weak(),
348-
format!("Subtitle export failed: {}", e),
349-
);
350-
});
351-
}
352-
Err(_) => {
353-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
354-
crate::logic::toast::async_toast_warn(
355-
ui.as_weak(),
356-
"Subtitle export was cancelled".to_string(),
357-
);
358-
});
302+
toast::async_toast_warn(ui_weak, format!("Subtitle export failed: {}", e))
359303
}
304+
Err(_) => toast::async_toast_warn(ui_weak, "Subtitle export was cancelled".to_string()),
360305
}
361306
});
362307
}
363308

364309
fn video_editor_export_queue_cancel(ui: &AppWindow, index: i32) {
365-
if index < 0 {
366-
crate::logic::toast::async_toast_warn(ui.as_weak(), "Invalid export index".to_string());
367-
return;
310+
if let Some(mut task) = store_video_editor_export_queue!(ui).row_data(index as usize) {
311+
task.is_cancelled = true;
312+
store_video_editor_export_queue!(ui).set_row_data(index as usize, task);
313+
crate::toast_info!(ui, "Export marked for cancellation".to_string());
368314
}
369-
370-
let ui_weak = ui.as_weak();
371-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
372-
let queue = global_store!(ui).get_video_editor_export_queue();
373-
let model = queue
374-
.as_any()
375-
.downcast_ref::<VecModel<UIVideoEditorExportQueueItem>>()
376-
.expect("We know we set a VecModel<UIVideoEditorExportQueueItem> earlier");
377-
378-
let index = index as usize;
379-
if index < model.row_count() {
380-
if let Some(task) = model.row_data(index) {
381-
let mut updated = task.clone();
382-
updated.is_cancelled = true;
383-
model.set_row_data(index, updated);
384-
}
385-
}
386-
});
387-
388-
crate::logic::toast::async_toast_info(
389-
ui.as_weak(),
390-
"Export marked for cancellation".to_string(),
391-
);
392315
}
393316

394317
fn video_editor_export_queue_remove(ui: &AppWindow, index: i32) {
395-
if index < 0 {
396-
crate::logic::toast::async_toast_warn(ui.as_weak(), "Invalid export index".to_string());
397-
return;
318+
if index > 0 && (index as usize) < store_video_editor_export_queue!(ui).row_count() {
319+
store_video_editor_export_queue!(ui).remove(index as usize);
320+
crate::toast_success!(ui, "Export removed from queue");
398321
}
399-
400-
let ui_weak = ui.as_weak();
401-
_ = ui_weak.upgrade_in_event_loop(move |ui| {
402-
let queue = global_store!(ui).get_video_editor_export_queue();
403-
let model = queue
404-
.as_any()
405-
.downcast_ref::<VecModel<UIVideoEditorExportQueueItem>>()
406-
.expect("We know we set a VecModel<UIVideoEditorExportQueueItem> earlier");
407-
408-
let index = index as usize;
409-
if index < model.row_count() {
410-
model.remove(index);
411-
}
412-
});
413-
414-
crate::logic::toast::async_toast_success(ui.as_weak(), "Export removed from queue".to_string());
415322
}
416323

417324
pub fn picker_save_file(

wayshot/ui/logic.slint

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,26 @@ export global Logic {
416416
}
417417
}
418418

419+
pure public function subtitle-to-str(subtitle: SubtitleType) -> string {
420+
if (subtitle == SubtitleType.Srt) {
421+
return "srt";
422+
} else if (subtitle == SubtitleType.Vtt) {
423+
return "vtt";
424+
} else {
425+
return "ass";
426+
}
427+
}
428+
429+
pure public function subtitle-from-str(subtitle: string) -> SubtitleType {
430+
if (subtitle == "srt") {
431+
return SubtitleType.Srt;
432+
} else if (subtitle == "vtt") {
433+
return SubtitleType.Vtt;
434+
} else {
435+
return SubtitleType.Ass;
436+
}
437+
}
438+
419439
pure public function audio-channels-to-int(channels: AudioChannels) -> int {
420440
if (channels == AudioChannels.Mono) {
421441
return 1;

0 commit comments

Comments
 (0)