From 736d401cf6a2b1f4d7f4261dad7c3b379f725df6 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 4 Nov 2019 10:48:25 -0500 Subject: allow adjusting the playback speed of ttyrecs --- src/cmd/play.rs | 24 +++++++++++++++++++----- src/config.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 6 ++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/cmd/play.rs b/src/cmd/play.rs index 6efcc74..8925701 100644 --- a/src/cmd/play.rs +++ b/src/cmd/play.rs @@ -5,6 +5,9 @@ use std::io::Write as _; pub struct Config { #[serde(default)] ttyrec: crate::config::Ttyrec, + + #[serde(default)] + play: crate::config::Play, } impl crate::config::Config for Config { @@ -12,19 +15,26 @@ impl crate::config::Config for Config { &mut self, matches: &clap::ArgMatches<'a>, ) -> Result<()> { - self.ttyrec.merge_args(matches) + self.ttyrec.merge_args(matches)?; + self.play.merge_args(matches)?; + Ok(()) } fn run( &self, ) -> Box + Send> { - Box::new(PlaySession::new(&self.ttyrec.filename)) + Box::new(PlaySession::new( + &self.ttyrec.filename, + self.play.playback_ratio, + )) } } pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { - crate::config::Ttyrec::cmd(app.about("Play recorded terminal sessions")) + crate::config::Ttyrec::cmd(crate::config::Play::cmd( + app.about("Play recorded terminal sessions"), + )) } pub fn config( @@ -60,10 +70,11 @@ struct PlaySession { to_write: DumbDelayQueue>, // to_write: tokio::timer::delay_queue::DelayQueue>, base_time: std::time::Instant, + playback_ratio: f32, } impl PlaySession { - fn new(filename: &str) -> Self { + fn new(filename: &str, playback_ratio: f32) -> Self { Self { file: FileState::Closed { filename: filename.to_string(), @@ -71,6 +82,7 @@ impl PlaySession { to_write: DumbDelayQueue::new(), // to_write: tokio::timer::delay_queue::DelayQueue::new(), base_time: std::time::Instant::now(), + playback_ratio, } } } @@ -122,7 +134,9 @@ impl PlaySession { { self.to_write.insert_at( frame.data, - self.base_time + frame.time - reader.offset().unwrap(), + self.base_time + + (frame.time - reader.offset().unwrap()) + .div_f32(self.playback_ratio), ); } else { self.file = FileState::Eof; diff --git a/src/config.rs b/src/config.rs index 6a9c2c0..85daf27 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ const FILENAME_OPTION: &str = "filename"; const LISTEN_ADDRESS_OPTION: &str = "listen-address"; const LOGIN_PLAIN_OPTION: &str = "login-plain"; const LOGIN_RECURSE_CENTER_OPTION: &str = "login-recurse-center"; +const PLAYBACK_RATIO_OPTION: &str = "playback-ratio"; const READ_TIMEOUT_OPTION: &str = "read-timeout-secs"; const TLS_IDENTITY_FILE_OPTION: &str = "tls-identity-file"; const TLS_OPTION: &str = "tls"; @@ -695,6 +696,55 @@ fn default_ttyrec_filename() -> String { DEFAULT_TTYREC_FILENAME.to_string() } +#[derive(serde::Deserialize, Debug)] +pub struct Play { + #[serde(default = "default_playback_ratio")] + pub playback_ratio: f32, +} + +impl Play { + pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { + let playback_ratio_help = + "Speed to play back the ttyrec at (defaults to 1.0)"; + app.arg( + clap::Arg::with_name(PLAYBACK_RATIO_OPTION) + .long(PLAYBACK_RATIO_OPTION) + .takes_value(true) + .value_name("RATIO") + .help(playback_ratio_help), + ) + } + + pub fn merge_args<'a>( + &mut self, + matches: &clap::ArgMatches<'a>, + ) -> Result<()> { + if matches.is_present(PLAYBACK_RATIO_OPTION) { + self.playback_ratio = matches + .value_of(PLAYBACK_RATIO_OPTION) + .unwrap() + .to_string() + .parse() + .context(crate::error::ParseFloat { + name: PLAYBACK_RATIO_OPTION, + })?; + } + Ok(()) + } +} + +impl Default for Play { + fn default() -> Self { + Self { + playback_ratio: default_playback_ratio(), + } + } +} + +fn default_playback_ratio() -> f32 { + 1.0 +} + pub fn oauth_configs<'a, D>( deserializer: D, ) -> std::result::Result< diff --git a/src/error.rs b/src/error.rs index e6e4530..3c3d663 100644 --- a/src/error.rs +++ b/src/error.rs @@ -217,6 +217,12 @@ pub enum Error { source: std::array::TryFromSliceError, }, + #[snafu(display("failed to parse float option {}: {}", name, source))] + ParseFloat { + name: String, + source: std::num::ParseFloatError, + }, + #[snafu(display("failed to parse response json: {}", source))] ParseJson { source: reqwest::Error }, -- cgit v1.2.3-54-g00ecf