add video thumbnailing via ffmpeg
parent
aa67b93e4e
commit
3186b9ce6e
|
@ -883,12 +883,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
|
@ -1689,19 +1683,6 @@ dependencies = [
|
|||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
@ -1710,9 +1691,21 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
|||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_hc 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.3",
|
||||
"rand_hc 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1726,20 +1719,15 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
|
@ -1749,6 +1737,15 @@ dependencies = [
|
|||
"getrandom 0.1.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
|
@ -1758,6 +1755,15 @@ dependencies = [
|
|||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.1"
|
||||
|
@ -1783,15 +1789,6 @@ dependencies = [
|
|||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
|
@ -2092,13 +2089,17 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
version = "0.3.7"
|
||||
name = "tempfile"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||
dependencies = [
|
||||
"rand 0.4.6",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"rand 0.8.4",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2441,7 +2442,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempdir",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"tree_magic_mini",
|
||||
|
|
|
@ -48,6 +48,7 @@ filebuffer = "0.4.0"
|
|||
tiny-keccak = { version = "2.0", features = ["k12"] }
|
||||
unsigned-varint = { version = "^0", features = ["std"] }
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
tempfile = "^3.2.0"
|
||||
walkdir = "2"
|
||||
|
||||
mime = "^0.3.16"
|
||||
|
@ -62,9 +63,6 @@ webbrowser = { version = "^0.5.5", optional = true }
|
|||
|
||||
nonempty = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.7"
|
||||
|
||||
[features]
|
||||
default = ["desktop", "thumbnails"]
|
||||
desktop = ["webbrowser", "opener", "is_executable"]
|
||||
|
|
|
@ -10,8 +10,11 @@ use std::{
|
|||
};
|
||||
|
||||
use self::text::TextPath;
|
||||
use self::video::VideoPath;
|
||||
|
||||
|
||||
pub mod text;
|
||||
pub mod video;
|
||||
|
||||
pub trait Thumbnailable {
|
||||
fn get_thumbnail(&self) -> Result<Vec<u8>>;
|
||||
|
@ -56,6 +59,7 @@ impl ThumbnailStore {
|
|||
|
||||
let data = match tree_magic_mini::from_filepath(&file.path) {
|
||||
Some(tm) if tm.starts_with("text") => Ok(TextPath(&file.path).get_thumbnail()?),
|
||||
Some(tm) if tm.starts_with("video") => Ok(VideoPath(&file.path).get_thumbnail()?),
|
||||
Some(unknown) => Err(anyhow!("No capability for {:?} thumbnails.", unknown)),
|
||||
_ => Err(anyhow!("Unknown file type, or file doesn't exist."))
|
||||
}?;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
use anyhow::anyhow;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use super::Thumbnailable;
|
||||
|
||||
pub struct VideoPath<'a>(pub &'a Path);
|
||||
|
||||
impl<'a> Thumbnailable for VideoPath<'a> {
|
||||
fn get_thumbnail(&self) -> Result<Vec<u8>> {
|
||||
let duration_cmd = Command::new("ffprobe")
|
||||
.args(["-v", "error"])
|
||||
.args(["-show_entries", "format=duration"])
|
||||
.args(["-of", "default=noprint_wrappers=1:nokey=1"])
|
||||
.arg(self.0)
|
||||
.output()?;
|
||||
if !duration_cmd.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Failed to retrieve file duration: {:?}",
|
||||
String::from_utf8_lossy(&duration_cmd.stderr)
|
||||
));
|
||||
}
|
||||
let duration = String::from_utf8_lossy(&duration_cmd.stdout).trim().parse::<f64>()?;
|
||||
let outfile = tempfile::Builder::new().suffix(".png").tempfile()?;
|
||||
let thumbnail_cmd = Command::new("ffmpeg")
|
||||
.args(["-i", &self.0.to_string_lossy()])
|
||||
.args(["-vframes", "1"])
|
||||
.args(["-ss", &(duration / 2.0).to_string()])
|
||||
.arg(&*outfile.path().to_string_lossy())
|
||||
.arg("-y")
|
||||
.output()?;
|
||||
|
||||
if !thumbnail_cmd.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Failed to render thumbnail: {:?}",
|
||||
String::from_utf8_lossy(&thumbnail_cmd.stderr)
|
||||
));
|
||||
}
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
outfile.as_file().read_to_end(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue