From 79093eca1e1eff6145cbccc5f0dbd2dea0f04439 Mon Sep 17 00:00:00 2001 From: Bolli Date: Fri, 9 Oct 2020 19:26:52 +0200 Subject: [PATCH] feat: add insert command to paste spotify links (#277) * feat: add insert command to paste spotify links - use Ctrl-v to paste from clipboard or use :insert without an argument * fix: handle paste on disabled clipboard feature * fix: handle wrong URIs Co-authored-by: Henrik Friedrichsen --- Cargo.lock | 1 + Cargo.toml | 1 + src/command.rs | 10 +++++++ src/commands.rs | 2 ++ src/main.rs | 2 ++ src/ui/listview.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index e24f376..a3a37c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1728,6 +1728,7 @@ dependencies = [ "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "notify-rust 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "rspotify 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.115 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index c2534e1..251347d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ url = "1.7" strum = "0.17.1" strum_macros = "0.17.1" libc = "0.2" +regex = "1" [dependencies.cursive] version = "0.15" diff --git a/src/command.rs b/src/command.rs index 301be7f..2940c33 100644 --- a/src/command.rs +++ b/src/command.rs @@ -114,6 +114,7 @@ pub enum Command { Help, ReloadConfig, Noop, + Insert(Option), } impl fmt::Display for Command { @@ -169,6 +170,7 @@ impl fmt::Display for Command { Command::Jump(term) => format!("jump {}", term), Command::Help => "help".to_string(), Command::ReloadConfig => "reload".to_string(), + Command::Insert(_) => "insert".to_string(), }; write!(f, "{}", repr) } @@ -346,6 +348,14 @@ pub fn parse(input: &str) -> Option { "voldown" => Some(Command::VolumeDown), "help" => Some(Command::Help), "reload" => Some(Command::ReloadConfig), + "insert" => { + if args.is_empty() { + Some(Command::Insert(None)) + } else { + args.get(0) + .map(|url| Command::Insert(Some((*url).to_string()))) + } + } "noop" => Some(Command::Noop), _ => None, } diff --git a/src/commands.rs b/src/commands.rs index 30bbbd7..ce64da9 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -184,6 +184,7 @@ impl CommandManager { | Command::Delete | Command::Back | Command::Open(_) + | Command::Insert(_) | Command::Goto(_) => Ok(None), _ => Err("Unknown Command".into()), } @@ -372,6 +373,7 @@ impl CommandManager { kb.insert("Shift+Up".into(), Command::Shift(ShiftMode::Up, None)); kb.insert("Shift+Down".into(), Command::Shift(ShiftMode::Down, None)); + kb.insert("Ctrl+v".into(), Command::Insert(None)); kb } diff --git a/src/main.rs b/src/main.rs index 2eb0cfd..ad31e43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,8 @@ extern crate url; extern crate strum; extern crate strum_macros; +extern crate regex; + use std::fs; use std::path::PathBuf; use std::process; diff --git a/src/ui/listview.rs b/src/ui/listview.rs index 8a0e4a2..26c4d48 100644 --- a/src/ui/listview.rs +++ b/src/ui/listview.rs @@ -9,11 +9,16 @@ use cursive::view::ScrollBase; use cursive::{Cursive, Printer, Rect, Vec2}; use unicode_width::UnicodeWidthStr; +use crate::album::Album; +use crate::artist::Artist; use crate::command::{Command, GotoMode, JumpMode, MoveAmount, MoveMode, TargetMode}; use crate::commands::CommandResult; +use crate::episode::Episode; use crate::library::Library; use crate::playable::Playable; +use crate::playlist::Playlist; use crate::queue::Queue; +use crate::show::Show; use crate::track::Track; use crate::traits::{IntoBoxedViewExt, ListItem, ViewExt}; use crate::ui::album::AlbumView; @@ -21,6 +26,7 @@ use crate::ui::artist::ArtistView; use crate::ui::contextmenu::ContextMenu; #[cfg(feature = "share_clipboard")] use clipboard::{ClipboardContext, ClipboardProvider}; +use regex::Regex; pub type Paginator = Box>>) + Send + Sync>; pub struct Pagination { @@ -525,6 +531,65 @@ impl ViewExt for ListView { } } } + Command::Insert(url) => { + let url = match url.as_ref().map(String::as_str) { + #[cfg(feature = "share_clipboard")] + Some("") | None => ClipboardProvider::new() + .and_then(|mut ctx: ClipboardContext| ctx.get_contents()) + .ok() + .unwrap(), + Some(url) => url.to_owned(), + // do nothing if clipboard feature is disabled and there is no url provided + #[allow(unreachable_patterns)] + _ => return Ok(CommandResult::Consumed(None)), + }; + + let spotify = self.queue.get_spotify(); + + let re = + Regex::new("https://open\\.spotify\\.com/(user/[^/]+/)?([a-z]+)/.+").unwrap(); + let captures = re.captures(&url); + + if let Some(captures) = captures { + let target: Option> = match &captures[2] { + "track" => spotify + .track(&url) + .and_then(|track| Some(Track::from(&track).as_listitem())), + "album" => spotify + .album(&url) + .and_then(|album| Some(Album::from(&album).as_listitem())), + "playlist" => spotify + .playlist(&url) + .and_then(|playlist| Some(Playlist::from(&playlist).as_listitem())), + "artist" => spotify + .artist(&url) + .and_then(|artist| Some(Artist::from(&artist).as_listitem())), + "episode" => spotify + .episode(&url) + .and_then(|episode| Some(Episode::from(&episode).as_listitem())), + "show" => spotify + .get_show(&url) + .and_then(|show| Some(Show::from(&show).as_listitem())), + _ => None, + }; + + let queue = self.queue.clone(); + let library = self.library.clone(); + // if item has a dedicated view, show it; otherwise open the context menu + if let Some(target) = target { + let view = target.open(queue.clone(), library.clone()); + return match view { + Some(view) => Ok(CommandResult::View(view)), + None => { + let contextmenu = ContextMenu::new(target.as_ref(), queue, library); + Ok(CommandResult::Modal(Box::new(contextmenu))) + } + }; + } + } + + return Ok(CommandResult::Consumed(None)); + } _ => {} };