1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
/.idea/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
|
||||
@@ -33,6 +33,7 @@ unicode-width = "0.1.5"
|
||||
dbus = { version = "0.6.4", optional = true }
|
||||
rand = "0.6.5"
|
||||
webbrowser = "0.5"
|
||||
clipboard = "0.5.0"
|
||||
|
||||
[dependencies.rspotify]
|
||||
git = "https://github.com/samrayleung/rspotify"
|
||||
|
||||
@@ -56,6 +56,8 @@ have them configurable.
|
||||
* `r` to toggle repeat mode
|
||||
* `z` to toggle shuffle playback
|
||||
* `q` quits ncspot
|
||||
* `x` copies a sharable URL to the song to the system clipboard
|
||||
* `Shift-x` copies a sharable URL to the currently selected item to the system clipboard
|
||||
|
||||
You can also open a Vim style commandprompt using `:`, the following commands
|
||||
are supported:
|
||||
@@ -65,6 +67,7 @@ are supported:
|
||||
* `stop`: Stop playback
|
||||
* `previous`/`next`: Play previous/next track
|
||||
* `clear`: Clear playlist
|
||||
* `share [current | selected]`: Copies a sharable URL of either the selected item or the currernt song to the system clipboard
|
||||
|
||||
The screens can be opened with `queue`, `search`, `playlists` and `log`, whereas
|
||||
`search` can be supplied with a search term that will be entered after opening
|
||||
|
||||
@@ -199,6 +199,12 @@ impl ListItem for Album {
|
||||
Some(AlbumView::new(queue, library, self).as_boxed_view_ext())
|
||||
}
|
||||
|
||||
fn share_url(&self) -> Option<String> {
|
||||
self.id
|
||||
.clone()
|
||||
.map(|id| format!("https://open.spotify.com/album/{}", id))
|
||||
}
|
||||
|
||||
fn artist(&self) -> Option<Artist> {
|
||||
Some(Artist::new(
|
||||
self.artist_ids[0].clone(),
|
||||
|
||||
@@ -193,4 +193,10 @@ impl ListItem for Artist {
|
||||
fn open(&self, queue: Arc<Queue>, library: Arc<Library>) -> Option<Box<dyn ViewExt>> {
|
||||
Some(ArtistView::new(queue, library, self).as_boxed_view_ext())
|
||||
}
|
||||
|
||||
fn share_url(&self) -> Option<String> {
|
||||
self.id
|
||||
.clone()
|
||||
.map(|id| format!("https://open.spotify.com/artist/{}", id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,9 +284,9 @@ impl CommandManager {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_keybindings<'a>(
|
||||
pub fn register_keybindings(
|
||||
this: Arc<Self>,
|
||||
cursive: &'a mut Cursive,
|
||||
cursive: &mut Cursive,
|
||||
keybindings: Option<HashMap<String, String>>,
|
||||
) {
|
||||
let mut kb = Self::default_keybindings();
|
||||
@@ -321,6 +321,8 @@ impl CommandManager {
|
||||
kb.insert(",".into(), "seek -500".into());
|
||||
kb.insert("r".into(), "repeat".into());
|
||||
kb.insert("z".into(), "shuffle".into());
|
||||
kb.insert("x".into(), "share current".into());
|
||||
kb.insert("Shift+x".into(), "share selected".into());
|
||||
|
||||
kb.insert("F1".into(), "focus queue".into());
|
||||
kb.insert("F2".into(), "focus search".into());
|
||||
|
||||
@@ -2,6 +2,7 @@ extern crate clap;
|
||||
extern crate crossbeam_channel;
|
||||
#[macro_use]
|
||||
extern crate cursive;
|
||||
extern crate clipboard;
|
||||
extern crate directories;
|
||||
extern crate failure;
|
||||
extern crate futures;
|
||||
|
||||
@@ -166,4 +166,11 @@ impl ListItem for Playlist {
|
||||
fn open(&self, queue: Arc<Queue>, library: Arc<Library>) -> Option<Box<dyn ViewExt>> {
|
||||
Some(PlaylistView::new(queue, library, self).as_boxed_view_ext())
|
||||
}
|
||||
|
||||
fn share_url(&self) -> Option<String> {
|
||||
Some(format!(
|
||||
"https://open.spotify.com/user/{}/playlist/{}",
|
||||
self.owner_id, self.id
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,12 @@ impl ListItem for Track {
|
||||
None
|
||||
}
|
||||
|
||||
fn share_url(&self) -> Option<String> {
|
||||
self.id
|
||||
.clone()
|
||||
.map(|id| format!("https://open.spotify.com/track/{}", id))
|
||||
}
|
||||
|
||||
fn album(&self, queue: Arc<Queue>) -> Option<Album> {
|
||||
let spotify = queue.get_spotify();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ pub trait ListItem: Sync + Send + 'static {
|
||||
fn queue(&mut self, queue: Arc<Queue>);
|
||||
fn toggle_saved(&mut self, library: Arc<Library>);
|
||||
fn open(&self, queue: Arc<Queue>, library: Arc<Library>) -> Option<Box<dyn ViewExt>>;
|
||||
fn share_url(&self) -> Option<String>;
|
||||
|
||||
fn album(&self, _queue: Arc<Queue>) -> Option<Album> {
|
||||
None
|
||||
|
||||
@@ -9,6 +9,7 @@ use cursive::view::ScrollBase;
|
||||
use cursive::{Cursive, Printer, Rect, Vec2};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||
use commands::CommandResult;
|
||||
use library::Library;
|
||||
use queue::Queue;
|
||||
@@ -215,6 +216,10 @@ impl<I: ListItem> View for ListView<I> {
|
||||
self.content.read().unwrap().len() != self.last_content_len
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
Vec2::new(constraint.x, self.content.read().unwrap().len())
|
||||
}
|
||||
|
||||
fn on_event(&mut self, e: Event) -> EventResult {
|
||||
match e {
|
||||
Event::Mouse {
|
||||
@@ -262,10 +267,6 @@ impl<I: ListItem> View for ListView<I> {
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
Vec2::new(constraint.x, self.content.read().unwrap().len())
|
||||
}
|
||||
|
||||
fn important_area(&self, view_size: Vec2) -> Rect {
|
||||
if self.content.read().unwrap().len() > 0 {
|
||||
Rect::from((view_size.x, self.selected))
|
||||
@@ -314,6 +315,26 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
|
||||
}
|
||||
}
|
||||
|
||||
if cmd == "share" {
|
||||
let source = args.get(0);
|
||||
let url =
|
||||
source.and_then(|source| match source.as_str() {
|
||||
"selected" => self.content.read().ok().and_then(|content| {
|
||||
content.get(self.selected).and_then(ListItem::share_url)
|
||||
}),
|
||||
"current" => self.queue.get_current().and_then(|t| t.share_url()),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some(url) = url {
|
||||
ClipboardProvider::new()
|
||||
.and_then(|mut ctx: ClipboardContext| ctx.set_contents(url))
|
||||
.ok();
|
||||
};
|
||||
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
|
||||
if cmd == "move" {
|
||||
if let Some(dir) = args.get(0) {
|
||||
let amount: usize = args
|
||||
|
||||
Reference in New Issue
Block a user