From 3d5af68335d84e750ef1d8f426e0c445de231f0c Mon Sep 17 00:00:00 2001 From: Marcin Kulik Date: Mon, 22 Jan 2024 14:37:14 +0100 Subject: [PATCH] Add support for playback from URL Also, this fixes #387. --- Cargo.lock | 43 +++++++++++++++++++++++++++++++++++------- Cargo.toml | 1 + src/cmd/play.rs | 50 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c45665b..fd1b208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "serde", "serde_json", "signal-hook", + "tempfile", "termion", "uuid", "which", @@ -334,6 +335,12 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fd-lock" version = "4.0.1" @@ -611,9 +618,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" @@ -807,13 +814,22 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_termios" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ - "redox_syscall", + "redox_syscall 0.2.16", ] [[package]] @@ -891,15 +907,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1102,6 +1118,19 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "termion" version = "2.0.1" @@ -1110,7 +1139,7 @@ checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90" dependencies = [ "libc", "numtoa", - "redox_syscall", + "redox_syscall 0.2.16", "redox_termios", ] diff --git a/Cargo.toml b/Cargo.toml index fa37acb..ddd655e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ reqwest = { version = "0.11.23", default-features = false, features = ["blocking rustyline = "13.0.0" config = { version = "0.13.4", default-features = false, features = ["toml", "ini"] } which = "5.0.0" +tempfile = "3.9.0" diff --git a/src/cmd/play.rs b/src/cmd/play.rs index 0954a21..5fb17e0 100644 --- a/src/cmd/play.rs +++ b/src/cmd/play.rs @@ -1,15 +1,18 @@ use crate::config::Config; use crate::format::asciicast; use crate::logger; -use crate::{ - player::{self, KeyBindings}, - tty, -}; -use anyhow::Result; +use crate::player::{self, KeyBindings}; +use crate::tty; +use anyhow::{anyhow, Result}; use clap::Args; +use reqwest::Url; +use std::io; +use std::path::{Path, PathBuf}; +use tempfile::NamedTempFile; #[derive(Debug, Args)] pub struct Cli { + #[arg(value_name = "FILENAME_OR_URL")] filename: String, /// Limit idle time to a given number of seconds @@ -36,8 +39,10 @@ impl Cli { logger::info!("Replaying session from {}", self.filename); + let path = get_path(&self.filename)?; + let ended = loop { - let recording = asciicast::open_from_path(&self.filename)?; + let recording = asciicast::open_from_path(&path)?; let tty = tty::DevTty::open()?; let keys = get_key_bindings(config)?; @@ -65,6 +70,39 @@ impl Cli { } } +enum LocalPath { + Normal(PathBuf), + Temporary(NamedTempFile), +} + +impl AsRef for LocalPath { + fn as_ref(&self) -> &Path { + match self { + LocalPath::Normal(p) => p, + LocalPath::Temporary(f) => f.path(), + } + } +} + +fn get_path(filename: &str) -> Result { + if filename.starts_with("https://") || filename.starts_with("http://") { + download_asciicast(filename) + .map(LocalPath::Temporary) + .map_err(|e| anyhow!("download failed: {e}")) + } else { + Ok(LocalPath::Normal(PathBuf::from(filename))) + } +} + +fn download_asciicast(url: &str) -> Result { + let mut response = reqwest::blocking::get(Url::parse(url)?)?; + response.error_for_status_ref()?; + let mut file = NamedTempFile::new()?; + io::copy(&mut response, &mut file)?; + + Ok(file) +} + fn get_key_bindings(config: &Config) -> Result { let mut keys = KeyBindings::default();