From fae8706f7ab7c1141c7ae4737b12891cb23213fd Mon Sep 17 00:00:00 2001 From: rabite Date: Tue, 2 Jul 2019 00:52:49 +0200 Subject: got binary building to work properly --- Cargo.lock | 54 ++--- Cargo.toml | 21 +- hunter-media/.gitignore | 1 - hunter-media/Cargo.lock | 6 - hunter-media/Cargo.toml | 25 -- hunter-media/src/main.rs | 587 ---------------------------------------------- src/hunter-media.rs | 592 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 630 insertions(+), 656 deletions(-) delete mode 100644 hunter-media/.gitignore delete mode 100644 hunter-media/Cargo.lock delete mode 100644 hunter-media/Cargo.toml delete mode 100644 hunter-media/src/main.rs create mode 100644 src/hunter-media.rs diff --git a/Cargo.lock b/Cargo.lock index 41e1918..b7e04c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,11 +71,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -83,7 +82,7 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -126,9 +125,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -207,7 +207,7 @@ dependencies = [ [[package]] name = "deflate" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -244,7 +244,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -255,7 +255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -444,15 +444,18 @@ dependencies = [ [[package]] name = "hunter" -version = "1.3.0" +version = "1.3.4" dependencies = [ "alphanumeric-sort 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "async_value 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs-2 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)", + "gstreamer-app 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "lscolors 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -472,17 +475,6 @@ dependencies = [ "users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hunter-media" -version = "0.1.0" -dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer 0.11.6 (registry+https://github.com/rust-lang/crates.io-index)", - "gstreamer-app 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "image" version = "0.21.2" @@ -728,7 +720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -900,7 +892,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)", + "deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1194,7 +1186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.36" +version = "0.15.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1209,7 +1201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1219,7 +1211,7 @@ version = "0.1.4" source = "git+https://github.com/myfreeweb/systemstat?tag=v0.1.4#05d844e805b0ae85c859fddb5d6b1f506e5e114c" dependencies = [ "bytesize 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1407,15 +1399,15 @@ dependencies = [ "checksum async_value 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e59ea0ca05a6f84b5ce4689a5b9610cccf587190688daaaea1835b08b7c7ccc0" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" -"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "18b50f5258d1a9ad8396d2d345827875de4261b158124d4c819d9b351454fae5" +"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytesize 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "16d794c5fe594cfa8fbe8ae274de4048176c69f2d9ac5e637166e73b71d460b8" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" @@ -1424,7 +1416,7 @@ dependencies = [ "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" -"checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" +"checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" "checksum dirs-2 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50b7e2b65c73137ec48935d50a5ae89b03150df566b7e14a1371df044e76765c" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" @@ -1533,7 +1525,7 @@ dependencies = [ "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)" = "8b4f551a91e2e3848aeef8751d0d4eec9489b6474c720fd4c55958d8d31a430c" +"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum systemstat 0.1.4 (git+https://github.com/myfreeweb/systemstat?tag=v0.1.4)" = "" "checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" diff --git a/Cargo.toml b/Cargo.toml index ee25b80..05d3740 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["default-run"] [package] name = "hunter" -version = "1.3.0" +version = "1.3.4" authors = ["rabite0"] edition = "2018" description = "Fast, lag-free terminal file browser" @@ -40,20 +40,29 @@ pathbuftools = "0.1" clap = "2.33" mime = "0.3.13" +image = { version = "0.21.1", optional = true } +gstreamer = { version = "0.11.2", optional = true } +gstreamer-app = { version = "0.11.2", optional = true } + [build-dependencies] termion = "1.5.1" rustc_version = "0.2.3" [features] -default = ["video"] -video = [] +default = ["img", "video"] +video = ["gstreamer", "gstreamer-app"] +img = ["image"] +[[bin]] +name = "hunter" +path = "src/main.rs" +[[bin]] +name = "hunter-media" +path = "src/hunter-media.rs" +required-features = ["img"] -[workspace] -members = [".", "hunter-media"] -default-members = [".", "hunter-media"] [patch.crates-io] diff --git a/hunter-media/.gitignore b/hunter-media/.gitignore deleted file mode 100644 index 2f7896d..0000000 --- a/hunter-media/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target/ diff --git a/hunter-media/Cargo.lock b/hunter-media/Cargo.lock deleted file mode 100644 index c2c6cce..0000000 --- a/hunter-media/Cargo.lock +++ /dev/null @@ -1,6 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "hunter-preview" -version = "0.1.0" - diff --git a/hunter-media/Cargo.toml b/hunter-media/Cargo.toml deleted file mode 100644 index eb0df6a..0000000 --- a/hunter-media/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "hunter-media" -version = "0.1.0" -authors = ["rabite0"] -edition = "2018" -description = "hunter's preview generator for image/video/audio files" -homepage = "https://github.com/rabite0/hunter" -repository = "https://github.com/rabite0/hunter" -readme = "../README.md" -license = "WTFPL" -keywords = ["cli", "terminal", "file"] -categories = ["command-line-utilities"] - - -[dependencies] -termion = "1.5.1" -failure = "0.1.5" -image = "0.21.1" -gstreamer = { version = "0.11.2", optional = true } -gstreamer-app = { version = "0.11.2", optional = true } - - -[features] -default = ["video"] -video = ["gstreamer", "gstreamer-app"] diff --git a/hunter-media/src/main.rs b/hunter-media/src/main.rs deleted file mode 100644 index 41f48a1..0000000 --- a/hunter-media/src/main.rs +++ /dev/null @@ -1,587 +0,0 @@ -// Based on https://github.com/jD91mZM2/termplay -// MIT License - -use image::{Pixel, FilterType, DynamicImage, GenericImageView}; - -use termion::color::{Bg, Fg, Rgb}; -#[cfg(feature = "video")] -use termion::input::TermRead; - - -#[cfg(feature = "video")] -use gstreamer::{self, prelude::*}; -#[cfg(feature = "video")] -use gstreamer_app; - -use failure::Error; -#[cfg(feature = "video")] -use failure::format_err; - - -use std::io::Write; -#[cfg(feature = "video")] -use std::sync::{Arc, RwLock}; - -pub type MResult = Result; - -fn main() -> MResult<()> { - let args = std::env::args().collect::>(); - let xsize: usize = args.get(1) - .expect("Provide xsize") - .parse::() - .unwrap(); - let ysize = args.get(2) - .expect("provide ysize") - .parse() - .unwrap(); - let xpos = args.get(3) - .expect("provide xpos") - .parse() - .unwrap(); - let ypos = args.get(4) - .expect("provide ypos") - .parse() - .unwrap(); - let preview_type = args.get(5) - .expect("Provide preview type") - .parse::() - .unwrap(); - let autoplay = args.get(6) - .expect("Autoplay?") - .parse::() - .unwrap(); - let mute = args.get(7) - .expect("Muted?") - .parse::() - .unwrap(); - let path = args.get(8).expect("Provide path"); - - - let result = - match preview_type.as_ref() { - #[cfg(feature = "video")] - "video" => video_preview(path, - xsize, - ysize, - 0, - 0, - autoplay, - mute, - false), - - "image" => image_preview(path, - xsize, - ysize), - - #[cfg(feature = "video")] - "audio" => audio_preview(path, - autoplay, - mute), - - #[cfg(feature = "video")] - "video-raw" => video_preview(path, - xsize, - ysize, - xpos, - ypos, - autoplay, - mute, - true), - #[cfg(feature = "video")] - _ => { panic!("Available types: video(-raw)/image/audio") } - - #[cfg(not(feature = "video"))] - _ => { panic!("Available type: image") } - }; - - if result.is_err() { - println!("{:?}", &result); - result - } else { - Ok(()) - } -} - -fn image_preview(path: &str, - xsize: usize, - ysize: usize) -> MResult<()> { - let img = image::open(&path)?; - - let renderer = Renderer::new(xsize, - ysize, - 0, - 0); - - renderer.send_image(&img)?; - - Ok(()) -} - -// #[cfg(feature = "video")] -fn video_preview(path: &String, - xsize: usize, - ysize: usize, - xpos: usize, - ypos: usize, - autoplay: bool, - mute: bool, - raw: bool) - -> MResult<()> { - - let (player, appsink) = make_gstreamer()?; - - let uri = format!("file://{}", &path); - - player.set_property("uri", &uri)?; - - - let renderer = Renderer::new(xsize, ysize, xpos, ypos); - let renderer = Arc::new(RwLock::new(renderer)); - let crenderer = renderer.clone(); - - - - - let p = player.clone(); - - appsink.set_callbacks( - gstreamer_app::AppSinkCallbacks::new() - .new_sample({ - move |sink| { - let sample = match sink.pull_sample() { - Some(sample) => sample, - None => return gstreamer::FlowReturn::Eos, - }; - - let position = p.query_position::() - .map(|p| p.seconds().unwrap_or(0)) - .unwrap_or(0); - - let duration = p.query_duration::() - .map(|d| d.seconds().unwrap_or(0)) - .unwrap_or(0); - - if let Ok(mut renderer) = crenderer.write() { - match renderer.send_frame(&*sample, - position, - duration, - raw) { - Ok(()) => { - if autoplay == false { - // Just render first frame to get a static image - match p.set_state(gstreamer::State::Paused) - .into_result() { - Ok(_) => gstreamer::FlowReturn::Eos, - Err(_) => gstreamer::FlowReturn::Error - } - } else { - gstreamer::FlowReturn::Ok - } - } - Err(err) => { - println!("{:?}", err); - gstreamer::FlowReturn::Error - } - } - } else { gstreamer::FlowReturn::Error } - - } - }) - .eos({ - move |_| { - std::process::exit(0); - } - }) - .build() - ); - - if mute == true || autoplay == false { - player.set_property("volume", &0.0)?; - } - player.set_state(gstreamer::State::Playing).into_result()?; - - - - - - read_keys(player, Some(renderer))?; - - Ok(()) -} - -// #[cfg(feature = "video")] -fn read_keys(player: gstreamer::Element, - renderer: Option>>) -> MResult<()> { - let seek_time = gstreamer::ClockTime::from_seconds(5); - - let stdin = std::io::stdin(); - let mut stdin = stdin.lock(); - - loop { - let input = stdin - .read_line()? - .unwrap_or_else(|| String::from("q")); - - - match input.as_str() { - "q" => std::process::exit(0), - ">" => { - if let Some(mut time) = player - .query_position::() { - time += seek_time; - - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } - }, - "<" => { - if let Some(mut time) = player - .query_position::() { - if time >= seek_time { - time -= seek_time; - } else { - time = gstreamer::ClockTime(Some(0)); - } - - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } - } - "p" => { - player.set_state(gstreamer::State::Playing).into_result()?; - - // To actually start playing again - if let Some(time) = player - .query_position::() { - player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::from_time(time) - )?; - } - } - "a" => { - player.set_state(gstreamer::State::Paused).into_result()?; - } - "m" => { - player.set_property("volume", &0.0)?; - } - "u" => { - player.set_property("volume", &1.0)?; - } - "xy" => { - if let Some(ref renderer) = renderer { - let xsize = stdin.read_line()?; - let ysize = stdin.read_line()?; - - let xsize = xsize.unwrap_or(String::from("0")).parse::()?; - let ysize = ysize.unwrap_or(String::from("0")).parse::()?; - - let mut renderer = renderer - .write() - .map_err(|_| format_err!("Renderer RwLock failed!"))?; - - renderer.set_size(xsize, ysize)?; - } - } - _ => {} - } - } -} - -// #[cfg(feature = "video")] -pub fn audio_preview(path: &String, - autoplay: bool, - mute: bool) - -> MResult<()> { - let (player, _) = make_gstreamer()?; - - let uri = format!("file://{}", &path); - - player.set_property("uri", &uri)?; - let p = player.clone(); - - // Since events don't work with audio files... - std::thread::spawn(move || -> MResult<()> { - let mut last_pos = None; - let sleep_duration = std::time::Duration::from_millis(50); - let mut stdout = std::io::stdout(); - loop { - std::thread::sleep(sleep_duration); - - let position = p.query_position::() - .map(|p| p.seconds().unwrap_or(0)) - .unwrap_or(0); - - let duration = p.query_duration::() - .map(|d| d.seconds().unwrap_or(0)) - .unwrap_or(0); - - // Just redo loop until position changes - if last_pos == Some(position) { - continue - } - - last_pos = Some(position); - - // MediaView needs empty line as separator - writeln!(stdout, "")?; - // Send position and duration - writeln!(stdout, "{}", position)?; - writeln!(stdout, "{}", duration)?; - stdout.flush()?; - } - - }); - - if mute == true || autoplay == false{ - player.set_property("volume", &0.0)?; - } else { - player.set_state(gstreamer::State::Playing).into_result()?; - } - - read_keys(player, None)?; - - Ok(()) -} - -// #[cfg(feature = "video")] -pub fn make_gstreamer() -> MResult<(gstreamer::Element, - gstreamer_app::AppSink)> { - gstreamer::init()?; - - let player = gstreamer::ElementFactory::make("playbin", None) - .ok_or(format_err!("Can't create playbin"))?; - - let videorate = gstreamer::ElementFactory::make("videorate", None) - .ok_or(format_err!("Can't create videorate element"))?; - - let pnmenc = gstreamer::ElementFactory::make("pnmenc", None) - .ok_or(format_err!("Can't create PNM-encoder"))?; - - let sink = gstreamer::ElementFactory::make("appsink", None) - .ok_or(format_err!("Can't create appsink"))?; - - let appsink = sink.clone() - .downcast::() - .unwrap(); - - - videorate.set_property("max-rate", &30)?; - - let elems = &[&videorate, &pnmenc, &sink]; - - let bin = gstreamer::Bin::new(None); - bin.add_many(elems)?; - gstreamer::Element::link_many(elems)?; - - // make input for bin point to first element - let sink = elems[0].get_static_pad("sink").unwrap(); - let ghost = gstreamer::GhostPad::new("sink", &sink) - .ok_or(format_err!("Can't create GhostPad"))?; - - ghost.set_active(true)?; - bin.add_pad(&ghost)?; - - player.set_property("video-sink", &bin.upcast::())?; - - Ok((player, appsink)) -} - - -struct Renderer { - xsize: usize, - ysize: usize, - #[cfg(feature = "video")] - xpos: usize, - #[cfg(feature = "video")] - ypos: usize, - last_frame: Option, - #[cfg(feature = "video")] - position: Option, - #[cfg(feature = "video")] - duration: Option -} - -impl Renderer { - fn new(xsize: usize, - ysize: usize, - xpos: usize, - ypos: usize) -> Renderer { - Renderer { - xsize, - ysize, - #[cfg(feature = "video")] - xpos, - #[cfg(feature = "video")] - ypos, - #[cfg(feature = "video")] - last_frame: None, - #[cfg(feature = "video")] - position: None, - #[cfg(feature = "video")] - duration: None - } - } - - - // #[cfg(feature = "video")] - fn set_size(&mut self, xsize: usize, ysize: usize) -> MResult<()> { - self.xsize = xsize; - self.ysize = ysize; - - if let Some(ref frame) = self.last_frame { - let pos = self.position.unwrap_or(0); - let dur = self.duration.unwrap_or(0); - - // Use send_image, because send_frame takes SampleRef - self.send_image(frame)?; - - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - - writeln!(stdout, "")?; - writeln!(stdout, "{}", pos)?; - writeln!(stdout, "{}", dur)?; - } - Ok(()) - } - - fn send_image(&self, image: &DynamicImage) -> MResult<()> { - let rendered_img = self.render_image(image); - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - - for line in rendered_img { - writeln!(stdout, "{}", line)?; - } - - Ok(()) - } - - // #[cfg(feature = "video")] - fn send_frame(&mut self, - frame: &gstreamer::sample::SampleRef, - position: u64, - duration: u64, - raw: bool) - -> MResult<()> { - let buffer = frame.get_buffer() - .ok_or(format_err!("Couldn't get buffer from frame!"))?; - let map = buffer.map_readable() - .ok_or(format_err!("Couldn't get buffer from frame!"))?; - - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - - - if !raw { - let img = image::load_from_memory_with_format(&map, - image::ImageFormat::PNM)?; - let rendered_img = self.render_image(&img); - - self.last_frame = Some(img); - self.position = Some(position as usize); - self.duration = Some(duration as usize); - - for line in rendered_img { - writeln!(stdout, "{}", line)?; - } - } else { - stdout.write_all(map.as_slice())?; - - // Add newline after frame data - write!(stdout, "\n")?; - } - - // Empty line means end of frame - writeln!(stdout, "")?; - - // Send position and duration - writeln!(stdout, "{}", position)?; - writeln!(stdout, "{}", duration)?; - - #[cfg(feature = "video")] - match raw { - true => { - writeln!(stdout, "{}", self.xpos)?; - writeln!(stdout, "{}", self.ypos)?; - } - _ => {} - } - - Ok(()) - } - - pub fn render_image(&self, image: &DynamicImage) -> Vec { - let (xsize, ysize) = self.max_size(&image); - - let img = image.resize_exact(xsize as u32, - ysize as u32, - FilterType::Nearest).to_rgba(); - - - let rows = img.pixels() - .collect::>() - .chunks(xsize as usize) - .map(|line| line.to_vec()) - .collect::>>(); - - rows.chunks(2) - .map(|rows| { - rows[0] - .iter() - .zip(rows[1].iter()) - .map(|(upper, lower)| { - let upper_color = upper.to_rgb(); - let lower_color = lower.to_rgb(); - - format!("{}{}▀{}", - Fg(Rgb(upper_color[0], upper_color[1], upper_color[2])), - Bg(Rgb(lower_color[0], lower_color[1], lower_color[2])), - termion::style::Reset - ) - }).collect() - }).collect() - } - - pub fn max_size(&self, image: &DynamicImage) -> (usize, usize) - { - let xsize = self.xsize; - let ysize = self.ysize; - let img_xsize = image.width(); - let img_ysize = image.height(); - let img_ratio = img_xsize as f32 / img_ysize as f32; - - let mut new_x = xsize; - let mut new_y; - - new_y = if img_ratio < 1 as f32 { - (xsize as f32 * img_ratio) as usize - } else { - (xsize as f32 / img_ratio) as usize - }; - - // Multiply by two because of half-block - if new_y > ysize*2 { - new_y = self.ysize * 2; - - new_x = if img_ratio < 1 as f32 { - (ysize as f32 / img_ratio) as usize * 2 - } else { - (ysize as f32 * img_ratio) as usize * 2 - }; - } - - // To make half-block encoding easier, y should be divisible by 2 - if new_y as u32 % 2 == 1 { - new_y += 1; - } - - - (new_x as usize, new_y as usize) - } -} diff --git a/src/hunter-media.rs b/src/hunter-media.rs new file mode 100644 index 0000000..193dc06 --- /dev/null +++ b/src/hunter-media.rs @@ -0,0 +1,592 @@ +// Based on https://github.com/jD91mZM2/termplay +// MIT License + +use image::{Pixel, FilterType, DynamicImage, GenericImageView}; + +use termion::color::{Bg, Fg, Rgb}; +#[cfg(feature = "video")] +use termion::input::TermRead; + + +#[cfg(feature = "video")] +use gstreamer::{self, prelude::*}; +#[cfg(feature = "video")] +use gstreamer_app; + +use failure::Error; +#[cfg(feature = "video")] +use failure::format_err; + + +use std::io::Write; +#[cfg(feature = "video")] +use std::sync::{Arc, RwLock}; + +pub type MResult = Result; + +fn main() -> MResult<()> { + let args = std::env::args().collect::>(); + let xsize: usize = args.get(1) + .expect("Provide xsize") + .parse::() + .unwrap(); + let ysize = args.get(2) + .expect("provide ysize") + .parse() + .unwrap(); + #[cfg(feature = "video")] + let xpos = args.get(3) + .expect("provide xpos") + .parse::() + .unwrap(); + #[cfg(feature = "video")] + let ypos = args.get(4) + .expect("provide ypos") + .parse::() + .unwrap(); + let preview_type = args.get(5) + .expect("Provide preview type") + .parse::() + .unwrap(); + #[cfg(feature = "video")] + let autoplay = args.get(6) + .expect("Autoplay?") + .parse::() + .unwrap(); + #[cfg(feature = "video")] + let mute = args.get(7) + .expect("Muted?") + .parse::() + .unwrap(); + let path = args.get(8).expect("Provide path"); + + + let result = + match preview_type.as_ref() { + #[cfg(feature = "video")] + "video" => video_preview(path, + xsize, + ysize, + 0, + 0, + autoplay, + mute, + false), + + "image" => image_preview(path, + xsize, + ysize), + + #[cfg(feature = "video")] + "audio" => audio_preview(path, + autoplay, + mute), + + #[cfg(feature = "video")] + "video-raw" => video_preview(path, + xsize, + ysize, + xpos, + ypos, + autoplay, + mute, + true), + #[cfg(feature = "video")] + _ => { panic!("Available types: video(-raw)/image/audio") } + + #[cfg(not(feature = "video"))] + _ => { panic!("Available type: image") } + }; + + if result.is_err() { + println!("{:?}", &result); + result + } else { + Ok(()) + } +} + +fn image_preview(path: &str, + xsize: usize, + ysize: usize) -> MResult<()> { + let img = image::open(&path)?; + + let renderer = Renderer::new(xsize, + ysize, + 0, + 0); + + renderer.send_image(&img)?; + + Ok(()) +} + +#[cfg(feature = "video")] +fn video_preview(path: &String, + xsize: usize, + ysize: usize, + xpos: usize, + ypos: usize, + autoplay: bool, + mute: bool, + raw: bool) + -> MResult<()> { + + let (player, appsink) = make_gstreamer()?; + + let uri = format!("file://{}", &path); + + player.set_property("uri", &uri)?; + + + let renderer = Renderer::new(xsize, ysize, xpos, ypos); + let renderer = Arc::new(RwLock::new(renderer)); + let crenderer = renderer.clone(); + + + + + let p = player.clone(); + + appsink.set_callbacks( + gstreamer_app::AppSinkCallbacks::new() + .new_sample({ + move |sink| { + let sample = match sink.pull_sample() { + Some(sample) => sample, + None => return gstreamer::FlowReturn::Eos, + }; + + let position = p.query_position::() + .map(|p| p.seconds().unwrap_or(0)) + .unwrap_or(0); + + let duration = p.query_duration::() + .map(|d| d.seconds().unwrap_or(0)) + .unwrap_or(0); + + if let Ok(mut renderer) = crenderer.write() { + match renderer.send_frame(&*sample, + position, + duration, + raw) { + Ok(()) => { + if autoplay == false { + // Just render first frame to get a static image + match p.set_state(gstreamer::State::Paused) + .into_result() { + Ok(_) => gstreamer::FlowReturn::Eos, + Err(_) => gstreamer::FlowReturn::Error + } + } else { + gstreamer::FlowReturn::Ok + } + } + Err(err) => { + println!("{:?}", err); + gstreamer::FlowReturn::Error + } + } + } else { gstreamer::FlowReturn::Error } + + } + }) + .eos({ + move |_| { + std::process::exit(0); + } + }) + .build() + ); + + if mute == true || autoplay == false { + player.set_property("volume", &0.0)?; + } + player.set_state(gstreamer::State::Playing).into_result()?; + + + + + + read_keys(player, Some(renderer))?; + + Ok(()) +} + +#[cfg(feature = "video")] +fn read_keys(player: gstreamer::Element, + renderer: Option>>) -> MResult<()> { + let seek_time = gstreamer::ClockTime::from_seconds(5); + + let stdin = std::io::stdin(); + let mut stdin = stdin.lock(); + + loop { + let input = stdin + .read_line()? + .unwrap_or_else(|| String::from("q")); + + + match input.as_str() { + "q" => std::process::exit(0), + ">" => { + if let Some(mut time) = player + .query_position::() { + time += seek_time; + + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; + } + }, + "<" => { + if let Some(mut time) = player + .query_position::() { + if time >= seek_time { + time -= seek_time; + } else { + time = gstreamer::ClockTime(Some(0)); + } + + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; + } + } + "p" => { + player.set_state(gstreamer::State::Playing).into_result()?; + + // To actually start playing again + if let Some(time) = player + .query_position::() { + player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::from_time(time) + )?; + } + } + "a" => { + player.set_state(gstreamer::State::Paused).into_result()?; + } + "m" => { + player.set_property("volume", &0.0)?; + } + "u" => { + player.set_property("volume", &1.0)?; + } + "xy" => { + if let Some(ref renderer) = renderer { + let xsize = stdin.read_line()?; + let ysize = stdin.read_line()?; + + let xsize = xsize.unwrap_or(String::from("0")).parse::()?; + let ysize = ysize.unwrap_or(String::from("0")).parse::()?; + + let mut renderer = renderer + .write() + .map_err(|_| format_err!("Renderer RwLock failed!"))?; + + renderer.set_size(xsize, ysize)?; + } + } + _ => {} + } + } +} + +#[cfg(feature = "video")] +pub fn audio_preview(path: &String, + autoplay: bool, + mute: bool) + -> MResult<()> { + let (player, _) = make_gstreamer()?; + + let uri = format!("file://{}", &path); + + player.set_property("uri", &uri)?; + let p = player.clone(); + + // Since events don't work with audio files... + std::thread::spawn(move || -> MResult<()> { + let mut last_pos = None; + let sleep_duration = std::time::Duration::from_millis(50); + let mut stdout = std::io::stdout(); + loop { + std::thread::sleep(sleep_duration); + + let position = p.query_position::() + .map(|p| p.seconds().unwrap_or(0)) + .unwrap_or(0); + + let duration = p.query_duration::() + .map(|d| d.seconds().unwrap_or(0)) + .unwrap_or(0); + + // Just redo loop until position changes + if last_pos == Some(position) { + continue + } + + last_pos = Some(position); + + // MediaView needs empty line as separator + writeln!(stdout, "")?; + // Send position and duration + writeln!(stdout, "{}", position)?; + writeln!(stdout, "{}", duration)?; + stdout.flush()?; + } + + }); + + if mute == true || autoplay == false{ + player.set_property("volume", &0.0)?; + } else { + player.set_state(gstreamer::State::Playing).into_result()?; + } + + read_keys(player, None)?; + + Ok(()) +} + +#[cfg(feature = "video")] +pub fn make_gstreamer() -> MResult<(gstreamer::Element, + gstreamer_app::AppSink)> { + gstreamer::init()?; + + let player = gstreamer::ElementFactory::make("playbin", None) + .ok_or(format_err!("Can't create playbin"))?; + + let videorate = gstreamer::ElementFactory::make("videorate", None) + .ok_or(format_err!("Can't create videorate element"))?; + + let pnmenc = gstreamer::ElementFactory::make("pnmenc", None) + .ok_or(format_err!("Can't create PNM-encoder"))?; + + let sink = gstreamer::ElementFactory::make("appsink", None) + .ok_or(format_err!("Can't create appsink"))?; + + let appsink = sink.clone() + .downcast::() + .unwrap(); + + + videorate.set_property("max-rate", &30)?; + + let elems = &[&videorate, &pnmenc, &sink]; + + let bin = gstreamer::Bin::new(None); + bin.add_many(elems)?; + gstreamer::Element::link_many(elems)?; + + // make input for bin point to first element + let sink = elems[0].get_static_pad("sink").unwrap(); + let ghost = gstreamer::GhostPad::new("sink", &sink) + .ok_or(format_err!("Can't create GhostPad"))?; + + ghost.set_active(true)?; + bin.add_pad(&ghost)?; + + player.set_property("video-sink", &bin.upcast::())?; + + Ok((player, appsink)) +} + + +struct Renderer { + xsize: usize, + ysize: usize, + #[cfg(feature = "video")] + xpos: usize, + #[cfg(feature = "video")] + ypos: usize, + #[cfg(feature = "video")] + last_frame: Option, + #[cfg(feature = "video")] + position: Option, + #[cfg(feature = "video")] + duration: Option +} + +impl Renderer { + fn new(xsize: usize, + ysize: usize, + xpos: usize, + ypos: usize) -> Renderer { + Renderer { + xsize, + ysize, + #[cfg(feature = "video")] + xpos, + #[cfg(feature = "video")] + ypos, + #[cfg(feature = "video")] + last_frame: None, + #[cfg(feature = "video")] + position: None, + #[cfg(feature = "video")] + duration: None + } + } + + + #[cfg(feature = "video")] + fn set_size(&mut self, xsize: usize, ysize: usize) -> MResult<()> { + self.xsize = xsize; + self.ysize = ysize; + + if let Some(ref frame) = self.last_frame { + let pos = self.position.unwrap_or(0); + let dur = self.duration.unwrap_or(0); + + // Use send_image, because send_frame takes SampleRef + self.send_image(frame)?; + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + + writeln!(stdout, "")?; + writeln!(stdout, "{}", pos)?; + writeln!(stdout, "{}", dur)?; + } + Ok(()) + } + + fn send_image(&self, image: &DynamicImage) -> MResult<()> { + let rendered_img = self.render_image(image); + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + + for line in rendered_img { + writeln!(stdout, "{}", line)?; + } + + Ok(()) + } + + #[cfg(feature = "video")] + fn send_frame(&mut self, + frame: &gstreamer::sample::SampleRef, + position: u64, + duration: u64, + raw: bool) + -> MResult<()> { + let buffer = frame.get_buffer() + .ok_or(format_err!("Couldn't get buffer from frame!"))?; + let map = buffer.map_readable() + .ok_or(format_err!("Couldn't get buffer from frame!"))?; + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + + + if !raw { + let img = image::load_from_memory_with_format(&map, + image::ImageFormat::PNM)?; + let rendered_img = self.render_image(&img); + + self.last_frame = Some(img); + self.position = Some(position as usize); + self.duration = Some(duration as usize); + + for line in rendered_img { + writeln!(stdout, "{}", line)?; + } + } else { + stdout.write_all(map.as_slice())?; + + // Add newline after frame data + write!(stdout, "\n")?; + } + + // Empty line means end of frame + writeln!(stdout, "")?; + + // Send position and duration + writeln!(stdout, "{}", position)?; + writeln!(stdout, "{}", duration)?; + + #[cfg(feature = "video")] + match raw { + true => { + writeln!(stdout, "{}", self.xpos)?; + writeln!(stdout, "{}", self.ypos)?; + } + _ => {} + } + + Ok(()) + } + + pub fn render_image(&self, image: &DynamicImage) -> Vec { + let (xsize, ysize) = self.max_size(&image); + + let img = image.resize_exact(xsize as u32, + ysize as u32, + FilterType::Nearest).to_rgba(); + + + let rows = img.pixels() + .collect::>() + .chunks(xsize as usize) + .map(|line| line.to_vec()) + .collect::>>(); + + rows.chunks(2) + .map(|rows| { + rows[0] + .iter() + .zip(rows[1].iter()) + .map(|(upper, lower)| { + let upper_color = upper.to_rgb(); + let lower_color = lower.to_rgb(); + + format!("{}{}▀{}", + Fg(Rgb(upper_color[0], upper_color[1], upper_color[2])), + Bg(Rgb(lower_color[0], lower_color[1], lower_color[2])), + termion::style::Reset + ) + }).collect() + }).collect() + } + + pub fn max_size(&self, image: &DynamicImage) -> (usize, usize) + { + let xsize = self.xsize; + let ysize = self.ysize; + let img_xsize = image.width(); + let img_ysize = image.height(); + let img_ratio = img_xsize as f32 / img_ysize as f32; + + let mut new_x = xsize; + let mut new_y; + + new_y = if img_ratio < 1 as f32 { + (xsize as f32 * img_ratio) as usize + } else { + (xsize as f32 / img_ratio) as usize + }; + + // Multiply by two because of half-block + if new_y > ysize*2 { + new_y = self.ysize * 2; + + new_x = if img_ratio < 1 as f32 { + (ysize as f32 / img_ratio) as usize * 2 + } else { + (ysize as f32 * img_ratio) as usize * 2 + }; + } + + // To make half-block encoding easier, y should be divisible by 2 + if new_y as u32 % 2 == 1 { + new_y += 1; + } + + + (new_x as usize, new_y as usize) + } +} -- cgit v1.2.3