feat: add initial ffmpeg implementation
This commit is contained in:
parent
1d93482209
commit
8f3c7e4052
6 changed files with 237 additions and 32 deletions
129
src/main.rs
129
src/main.rs
|
@ -1,3 +1,4 @@
|
|||
mod ffmpeg;
|
||||
mod gobject;
|
||||
mod relm;
|
||||
mod zipline;
|
||||
|
@ -42,6 +43,8 @@ impl Config {
|
|||
#[derive(Debug)]
|
||||
enum Message {
|
||||
OpenFilePicker,
|
||||
SetOutFilename(String),
|
||||
SetMergeTracks(bool),
|
||||
SetFolder(ZiplineFolder),
|
||||
StartTheProcess,
|
||||
Nothing,
|
||||
|
@ -49,24 +52,49 @@ enum Message {
|
|||
|
||||
#[derive(Debug)]
|
||||
enum ProgressMessage {
|
||||
SetStep(Step),
|
||||
SetTotal(usize),
|
||||
Progress(usize),
|
||||
AbsProgress(usize),
|
||||
IncProgress(usize),
|
||||
Finish(String),
|
||||
Error(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Step {
|
||||
Waiting,
|
||||
Converting,
|
||||
Uploading,
|
||||
Thumbnail,
|
||||
}
|
||||
|
||||
impl Step {
|
||||
fn button_text(&self) -> &'static str {
|
||||
match self {
|
||||
Step::Waiting => "Send",
|
||||
Step::Converting => "Converting...",
|
||||
Step::Uploading => "Uploading...",
|
||||
Step::Thumbnail => "Generating thumbnail...",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UploadInfo {
|
||||
config: Config,
|
||||
video_path: Option<PathBuf>,
|
||||
out_filename: Option<String>,
|
||||
merge_tracks: bool,
|
||||
folder: Option<ZiplineFolder>,
|
||||
}
|
||||
|
||||
struct Tyrolienne {
|
||||
config: Config,
|
||||
locked: bool,
|
||||
step: Step,
|
||||
progress: usize,
|
||||
total: usize,
|
||||
video_path: Option<PathBuf>,
|
||||
out_filename: Option<String>,
|
||||
merge_tracks: bool,
|
||||
folder: Option<ZiplineFolder>,
|
||||
dialog: Controller<Dialog>,
|
||||
toast: Controller<Toast>,
|
||||
|
@ -83,6 +111,8 @@ impl Tyrolienne {
|
|||
fn clone_as_info(&self) -> UploadInfo {
|
||||
UploadInfo {
|
||||
config: self.config.clone(),
|
||||
out_filename: self.out_filename.clone(),
|
||||
merge_tracks: self.merge_tracks,
|
||||
video_path: self.video_path.clone(),
|
||||
folder: self.folder.clone(),
|
||||
}
|
||||
|
@ -128,11 +158,26 @@ impl AsyncComponent for Tyrolienne {
|
|||
set_title: "Video file",
|
||||
#[watch]
|
||||
set_subtitle: &model.display_video_path(),
|
||||
add_suffix = >k::Image {
|
||||
set_margin_end: 8,
|
||||
set_icon_name: Some("document-open-symbolic"),
|
||||
},
|
||||
connect_activated => Message::OpenFilePicker,
|
||||
},
|
||||
|
||||
adw::EntryRow {
|
||||
set_title: "Output file name",
|
||||
connect_changed[sender] => move |e| sender.input(Message::SetOutFilename(e.text().into())),
|
||||
},
|
||||
|
||||
adw::SwitchRow {
|
||||
set_title: "Merge audio tracks",
|
||||
set_active: true,
|
||||
connect_active_notify[sender] => move |s| sender.input(Message::SetMergeTracks(s.is_active())),
|
||||
},
|
||||
|
||||
adw::ComboRow {
|
||||
set_title: "Folder",
|
||||
set_title: "Zipline folder",
|
||||
set_model: Some(&folder_store),
|
||||
set_expression: Some(&folder_expression),
|
||||
connect_activated[sender] => move |r| {
|
||||
|
@ -158,9 +203,10 @@ impl AsyncComponent for Tyrolienne {
|
|||
|
||||
gtk::Button {
|
||||
#[watch]
|
||||
set_label: if model.locked { "Uploading..." } else { "Send" },
|
||||
set_label: model.step.button_text(),
|
||||
#[watch]
|
||||
set_sensitive: !model.locked && model.video_path.is_some(),
|
||||
set_sensitive: matches!(model.step, Step::Waiting) && model.video_path.is_some(),
|
||||
set_css_classes: &["suggested-action"],
|
||||
connect_clicked => Message::StartTheProcess,
|
||||
}
|
||||
}
|
||||
|
@ -179,10 +225,12 @@ impl AsyncComponent for Tyrolienne {
|
|||
|
||||
let model = Tyrolienne {
|
||||
config,
|
||||
locked: false,
|
||||
step: Step::Waiting,
|
||||
progress: 0,
|
||||
total: 1,
|
||||
video_path: None,
|
||||
out_filename: None,
|
||||
merge_tracks: true,
|
||||
folder: None,
|
||||
dialog: Dialog::builder()
|
||||
.launch(toast_overlay.clone())
|
||||
|
@ -232,9 +280,6 @@ impl AsyncComponent for Tyrolienne {
|
|||
) {
|
||||
match message {
|
||||
Message::Nothing => {}
|
||||
Message::SetFolder(folder) => {
|
||||
self.folder = (folder.id != GtkZiplineFolder::NONE_ID).then_some(folder)
|
||||
}
|
||||
Message::OpenFilePicker => {
|
||||
let file = rfd::AsyncFileDialog::new()
|
||||
.add_filter("Video file", &["mp4", "mkv", "webm"])
|
||||
|
@ -245,8 +290,14 @@ impl AsyncComponent for Tyrolienne {
|
|||
self.video_path = Some(file.path().to_owned());
|
||||
}
|
||||
}
|
||||
Message::SetOutFilename(name) => {
|
||||
self.out_filename = if name.is_empty() { None } else { Some(name) }
|
||||
}
|
||||
Message::SetMergeTracks(m) => self.merge_tracks = m,
|
||||
Message::SetFolder(folder) => {
|
||||
self.folder = (folder.id != GtkZiplineFolder::NONE_ID).then_some(folder)
|
||||
}
|
||||
Message::StartTheProcess => {
|
||||
self.locked = true;
|
||||
let info = self.clone_as_info();
|
||||
sender.command(|out, shutdown| {
|
||||
Box::pin(
|
||||
|
@ -271,21 +322,30 @@ impl AsyncComponent for Tyrolienne {
|
|||
_root: &Self::Root,
|
||||
) {
|
||||
match message {
|
||||
ProgressMessage::SetTotal(total) => self.total = total,
|
||||
ProgressMessage::Progress(prog) => self.progress += prog,
|
||||
ProgressMessage::Finish(_) | ProgressMessage::Error(_) => {
|
||||
self.locked = false;
|
||||
ProgressMessage::SetStep(step) => self.step = step,
|
||||
ProgressMessage::SetTotal(total) => {
|
||||
self.progress = 0;
|
||||
self.total = total;
|
||||
}
|
||||
ProgressMessage::AbsProgress(prog) => self.progress = prog,
|
||||
ProgressMessage::IncProgress(prog) => self.progress += prog,
|
||||
ProgressMessage::Finish(url) => {
|
||||
self.step = Step::Waiting;
|
||||
self.progress = 0;
|
||||
self.total = 1;
|
||||
|
||||
if let ProgressMessage::Finish(url) = message {
|
||||
self.toast.emit(ToastInput::Show(url));
|
||||
} else if let ProgressMessage::Error(e) = message {
|
||||
self.dialog.emit(DialogInput::Show {
|
||||
heading: "An error occurred".into(),
|
||||
body: e.to_string(),
|
||||
});
|
||||
}
|
||||
// TODO copy to clipboard here instead, the toast disappears after a short while
|
||||
self.toast.emit(ToastInput::Show(url));
|
||||
}
|
||||
ProgressMessage::Error(e) => {
|
||||
self.step = Step::Waiting;
|
||||
self.progress = 0;
|
||||
self.total = 1;
|
||||
|
||||
self.dialog.emit(DialogInput::Show {
|
||||
heading: "An error occurred".into(),
|
||||
body: e.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,19 +397,28 @@ fn get_config() -> Result<Config> {
|
|||
}
|
||||
|
||||
async fn the_process(app: UploadInfo, sender: &Sender<ProgressMessage>) -> Result<String> {
|
||||
let Some(ref video_path) = app.video_path else {
|
||||
bail!("No video given!");
|
||||
};
|
||||
|
||||
sender.emit(ProgressMessage::SetStep(Step::Converting));
|
||||
|
||||
let video_meta = ffmpeg::get_video_meta(video_path).await?;
|
||||
sender.emit(ProgressMessage::SetTotal(video_meta.duration_us));
|
||||
|
||||
let out_path = ffmpeg::convert_video(video_path, sender.clone()).await?;
|
||||
|
||||
sender.emit(ProgressMessage::SetStep(Step::Uploading));
|
||||
if let Some(folder) = app.folder.as_ref() {
|
||||
tracing::info!("uploading to folder '{}'...", folder.name);
|
||||
} else {
|
||||
tracing::info!("uploading video...");
|
||||
}
|
||||
|
||||
let Some(ref video_path) = app.video_path else {
|
||||
bail!("No video given!");
|
||||
};
|
||||
|
||||
let res = zipline::upload_file(&app.config, sender, app.folder.as_ref(), video_path).await?;
|
||||
let res = zipline::upload_file(&app.config, sender, app.folder.as_ref(), &out_path).await?;
|
||||
let zp_file = &res.files[0];
|
||||
|
||||
sender.emit(ProgressMessage::SetStep(Step::Thumbnail));
|
||||
tracing::info!("recalculating thumbnails...");
|
||||
|
||||
zipline::recalc_thumbnails(&app.config).await?;
|
||||
|
@ -365,8 +434,10 @@ async fn the_process(app: UploadInfo, sender: &Sender<ProgressMessage>) -> Resul
|
|||
|
||||
// TODO get w&h from video
|
||||
Ok(format!(
|
||||
"https://autocompressor.net/av1?v={}&i={}&w=1920&h=1080",
|
||||
"https://autocompressor.net/av1?v={}&i={}&w={}&h={}",
|
||||
Encoded(&zp_file.url),
|
||||
Encoded(&thumbnail_url)
|
||||
Encoded(&thumbnail_url),
|
||||
video_meta.width,
|
||||
video_meta.height,
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue