oshit is taht
parent
3f7004cc06
commit
53714ead7c
|
|
@ -9,6 +9,6 @@ edition = "2021"
|
||||||
dioxus = "0.4.3"
|
dioxus = "0.4.3"
|
||||||
lib = { path = "../src-lib" }
|
lib = { path = "../src-lib" }
|
||||||
base64 = "0.21.5"
|
base64 = "0.21.5"
|
||||||
#dioxus-web = "0.4.3"
|
|
||||||
opener = "0.6.1"
|
opener = "0.6.1"
|
||||||
|
#dioxus-web = "0.4.3"
|
||||||
dioxus-desktop = "0.4.3"
|
dioxus-desktop = "0.4.3"
|
||||||
|
|
|
||||||
146
src/src/main.rs
146
src/src/main.rs
|
|
@ -3,6 +3,8 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use lib;
|
use lib;
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// launch the dioxus app in a webview
|
// launch the dioxus app in a webview
|
||||||
|
|
@ -17,20 +19,30 @@ fn App(cx: Scope) -> Element {
|
||||||
let status = use_state(cx, String::new);
|
let status = use_state(cx, String::new);
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
header {
|
header {
|
||||||
h1 { "home-video-blue-extractinator" }
|
style { "
|
||||||
|
body {{
|
||||||
|
max-width: 600pt;
|
||||||
|
margin: auto;
|
||||||
|
}}
|
||||||
|
label {{
|
||||||
|
display: block;
|
||||||
|
}}
|
||||||
|
label:has(input[type=checkbox]:checked) {{
|
||||||
|
background-color: lightgreen;
|
||||||
|
}}
|
||||||
|
" }
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
rsx! {
|
rsx! {
|
||||||
|
h1 { "home-video-blue-extractinator" }
|
||||||
div {
|
div {
|
||||||
input {
|
input {
|
||||||
r#type: "file",
|
r#type: "file",
|
||||||
onchange: |evt| {
|
onchange: |evt| {
|
||||||
to_owned![file];
|
to_owned![file];
|
||||||
async move {
|
if let Some(file_engine) = &evt.files {
|
||||||
if let Some(file_engine) = &evt.files {
|
for f in &file_engine.files() {
|
||||||
for f in &file_engine.files() {
|
file.set(f.clone());
|
||||||
file.set(f.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -38,7 +50,7 @@ fn App(cx: Scope) -> Element {
|
||||||
p { file.get().clone() }
|
p { file.get().clone() }
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
input { r#type: "button", value: "analyze", disabled: file.get().len() == 0, onclick: move |evt| {
|
input { r#type: "button", value: "analyze", disabled: file.get().len() == 0, onclick: |evt| {
|
||||||
to_owned![file];
|
to_owned![file];
|
||||||
to_owned![analysis];
|
to_owned![analysis];
|
||||||
to_owned![status];
|
to_owned![status];
|
||||||
|
|
@ -49,20 +61,13 @@ fn App(cx: Scope) -> Element {
|
||||||
} else {
|
} else {
|
||||||
status.set(format!(
|
status.set(format!(
|
||||||
"found {} clips to keep and {} clips to drop",
|
"found {} clips to keep and {} clips to drop",
|
||||||
analyzed.with_content.len(),
|
analyzed.result.iter().filter(|x| x.has_content).count(),
|
||||||
analyzed.without_content.len(),
|
analyzed.result.iter().filter(|x| !x.has_content).count(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
analysis.set(analyzed);
|
analysis.set(analyzed);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
input { r#type: "button", value: "clipify", disabled: file.get().len() == 0, onclick: move |evt| {
|
|
||||||
to_owned![file];
|
|
||||||
to_owned![status];
|
|
||||||
async move {
|
|
||||||
status.set(clipify(file.get().clone()));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
br {}
|
br {}
|
||||||
|
|
@ -70,28 +75,43 @@ fn App(cx: Scope) -> Element {
|
||||||
br {}
|
br {}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
h3 { "Clips to Keep" }
|
h3 { "Content Spans" }
|
||||||
div {
|
form {
|
||||||
analysis.get().with_content.iter().map(|a| {
|
onsubmit: |evt| {
|
||||||
rsx! {
|
let content_spans: Vec<_> = evt.values.iter()
|
||||||
p {
|
.filter(|kv| kv.1.len() > 0)
|
||||||
"{a.start}..{a.stop}: "
|
.map(|kv| {
|
||||||
br {}
|
let span_str = kv.0;
|
||||||
img { src: "data:image/png;base64, {a.screenshot}" }
|
let idx = span_str.find("..").unwrap();
|
||||||
}
|
lib::video::ContentSpan{
|
||||||
|
start: f32::from_str(&span_str[0..idx]).unwrap(),
|
||||||
|
stop: f32::from_str(&span_str[idx+2..span_str.len()]).unwrap(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
status.set(format!("clipifying {:?}", content_spans));
|
||||||
|
let file = file.get().clone();
|
||||||
|
to_owned![status];
|
||||||
|
async move {
|
||||||
|
let f = clipify(file, content_spans).await;
|
||||||
|
status.set(f);
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
}
|
input { r#type: "submit", value: "clipify selected spans", disabled: file.get().len() == 0 }
|
||||||
}
|
hr {}
|
||||||
div {
|
analysis.get().result.iter().map(|a| {
|
||||||
h3 { "Clips to Remove" }
|
|
||||||
div {
|
|
||||||
analysis.get().without_content.iter().map(|a| {
|
|
||||||
rsx! {
|
rsx! {
|
||||||
p {
|
div {
|
||||||
"{a.start}..{a.stop}: "
|
label {
|
||||||
br {}
|
input {
|
||||||
img { src: "data:image/png;base64, {a.screenshot}" }
|
r#type: "checkbox",
|
||||||
|
checked: a.has_content,
|
||||||
|
name: "{a.start}..{a.stop}",
|
||||||
|
}
|
||||||
|
"{a.start}..{a.stop}: "
|
||||||
|
br {}
|
||||||
|
img { src: "data:image/png;base64, {a.screenshot}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -103,25 +123,25 @@ fn App(cx: Scope) -> Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Analysis {
|
struct Analysis {
|
||||||
with_content: Vec<Analyzed>,
|
result: Vec<Analyzed>,
|
||||||
without_content: Vec<Analyzed>,
|
|
||||||
err: String,
|
err: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analysis {
|
impl Analysis {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self{
|
Self{
|
||||||
with_content: vec![],
|
result: vec![],
|
||||||
without_content: vec![],
|
|
||||||
err: "".to_string(),
|
err: "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Analyzed {
|
struct Analyzed {
|
||||||
start: String,
|
start: String,
|
||||||
stop: String,
|
stop: String,
|
||||||
screenshot: String,
|
screenshot: String,
|
||||||
|
has_content: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze(file: String) -> Analysis {
|
fn analyze(file: String) -> Analysis {
|
||||||
|
|
@ -129,8 +149,7 @@ fn analyze(file: String) -> Analysis {
|
||||||
//let content_spans: Result<Vec<lib::video::ContentSpan>, String> = Ok(vec![lib::video::ContentSpan{start: 0.5, stop: 20.2}]);
|
//let content_spans: Result<Vec<lib::video::ContentSpan>, String> = Ok(vec![lib::video::ContentSpan{start: 0.5, stop: 20.2}]);
|
||||||
if content_spans.is_err() {
|
if content_spans.is_err() {
|
||||||
return Analysis{
|
return Analysis{
|
||||||
with_content: vec![],
|
result: vec![],
|
||||||
without_content: vec![],
|
|
||||||
err: format!("failed to analyze {}: {}", file, content_spans.err().unwrap()),
|
err: format!("failed to analyze {}: {}", file, content_spans.err().unwrap()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -155,26 +174,51 @@ fn analyze(file: String) -> Analysis {
|
||||||
screenshot: match lib::video::screenshot_png(&file, ts) {
|
screenshot: match lib::video::screenshot_png(&file, ts) {
|
||||||
Ok(png) => general_purpose::STANDARD.encode(&png),
|
Ok(png) => general_purpose::STANDARD.encode(&png),
|
||||||
Err(_) => a_png.to_string(),
|
Err(_) => a_png.to_string(),
|
||||||
}
|
},
|
||||||
|
has_content: false,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut result = vec![];
|
||||||
|
content_spans.iter()
|
||||||
|
.map(screenshot)
|
||||||
|
.map(|x| {
|
||||||
|
let mut x = x.clone();
|
||||||
|
x.has_content = true;
|
||||||
|
x
|
||||||
|
})
|
||||||
|
.for_each(|x| result.push(x));
|
||||||
|
non_content_spans.iter()
|
||||||
|
.map(screenshot)
|
||||||
|
.for_each(|x| result.push(x));
|
||||||
|
result.sort_by(|x, y| x.start.partial_cmp(&y.start).unwrap());
|
||||||
|
|
||||||
Analysis{
|
Analysis{
|
||||||
with_content: content_spans.iter().map(screenshot).collect(),
|
result: result,
|
||||||
without_content: non_content_spans.iter().map(screenshot).collect(),
|
|
||||||
err: "".to_string(),
|
err: "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clipify(file: String) -> String {
|
async fn clipify(file: String, content_spans: Vec<lib::video::ContentSpan>) -> String {
|
||||||
match lib::clipify(&file.to_string()) {
|
let d = format!("{}.d", &file);
|
||||||
Ok(path) => {
|
match content_spans.iter()
|
||||||
match opener::open(path.clone()) {
|
.map(|content_span| {
|
||||||
Ok(_) => path,
|
lib::video::clip(
|
||||||
|
&format!("{}/{}.mp4", &d, content_span.start),
|
||||||
|
&file,
|
||||||
|
*content_span,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|x| x.is_err())
|
||||||
|
.map(|x| x.err().unwrap())
|
||||||
|
.nth(0) {
|
||||||
|
Some(err_msg) => err_msg,
|
||||||
|
None => {
|
||||||
|
match opener::open(d.clone()) {
|
||||||
|
Ok(_) => d,
|
||||||
Err(msg) => format!("failed to open clipify result: {}", msg),
|
Err(msg) => format!("failed to open clipify result: {}", msg),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(msg) => msg,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue