From 278bb7844fd58f27693df316d17ab8dbf72024ec Mon Sep 17 00:00:00 2001 From: Henrik Friedrichsen Date: Sun, 23 Dec 2018 12:44:40 +0100 Subject: [PATCH] implement track search and playback --- Cargo.toml | 2 +- src/main.rs | 74 +++++++++++++++++++++++------------------------- src/spotify.rs | 10 ++++--- src/ui/mod.rs | 1 + src/ui/search.rs | 55 +++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 src/ui/mod.rs create mode 100644 src/ui/search.rs diff --git a/Cargo.toml b/Cargo.toml index a76ef52..136e807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Henrik Friedrichsen "] [dependencies] -cursive = "0.9" +cursive = "0.10" crossbeam-channel = "0.2" env_logger = "0.5.13" failure = "0.1.3" diff --git a/src/main.rs b/src/main.rs index 1acc555..62bf601 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,14 +16,13 @@ extern crate log; extern crate env_logger; use std::env; -use std::io; +use std::fs::OpenOptions; use std::io::prelude::*; use std::path::PathBuf; use std::process; use std::sync::{Arc, Mutex}; use cursive::views::*; -use cursive::CbFunc; use cursive::Cursive; use librespot::core::spotify_id::SpotifyId; @@ -31,33 +30,38 @@ use librespot::core::spotify_id::SpotifyId; mod config; mod spotify; mod theme; - -pub trait CbSpotify: Send { - fn call_box(self: Box, &mut spotify::Spotify); -} - -impl () + Send> CbSpotify for F { - fn call_box(self: Box, s: &mut spotify::Spotify) { - (*self)(s) - } -} +mod ui; fn main() { - let loglines = Arc::new(Mutex::new(Vec::new())); + let logbuf = TextContent::new("Welcome to ncspot\n"); + let logview = TextView::new_with_content(logbuf.clone()); std::env::set_var("RUST_LOG", "ncspot=trace"); + std::env::set_var("RUST_BACKTRACE", "full"); + let mut builder = env_logger::Builder::from_default_env(); { - let mut loglines = loglines.clone(); - builder.format(move |buf, record| { - let mut lines = loglines.lock().unwrap(); - lines.push(format!("[{}] {}", record.level(), record.args())); - Ok(()) - }).init(); + builder + .format(move |_, record| { + let line = format!("[{}] {}\n", record.level(), record.args()); + logbuf.clone().append(line.clone()); + + let mut file = OpenOptions::new() + .create(true) + .write(true) + .append(true) + .open("ncspot.log") + .unwrap(); + if let Err(e) = writeln!(file, "{}", line) { + eprintln!("Couldn't write to file: {}", e); + } + Ok(()) + }) + .init(); } - // let mut cursive = Cursive::default(); - // cursive.add_global_callback('q', |s| s.quit()); - // cursive.set_theme(theme::default()); + let mut cursive = Cursive::default(); + cursive.add_global_callback('q', |s| s.quit()); + cursive.set_theme(theme::default()); let path = match env::var_os("HOME") { None => { @@ -69,22 +73,16 @@ fn main() { let cfg = config::load(path.to_str().unwrap()).expect("could not load configuration file"); - let spotify = spotify::Spotify::new(cfg.username, cfg.password, cfg.client_id); + let spotify = Arc::new(spotify::Spotify::new( + cfg.username, + cfg.password, + cfg.client_id, + )); - // let track = SpotifyId::from_base62("24zYR2ozYbnhhwulk2NLD4").expect("could not load track"); + let logpanel = Panel::new(logview).title("Log"); + //cursive.add_fullscreen_layer(logpanel); + let search = ui::search::SearchView::new(spotify.clone()); + cursive.add_fullscreen_layer(search.view); - // spotify.load(track); - // thread::sleep(time::Duration::new(3, 0)); - // spotify.play(); - // thread::sleep(time::Duration::new(3, 0)); - // spotify.pause(); - // thread::sleep(time::Duration::new(3, 0)); - // spotify.play(); - - // thread::sleep(time::Duration::new(8, 0)); - // spotify.load(track); - // spotify.play(); - - let _ = io::stdin().read(&mut [0u8]).unwrap(); - // cursive.run(); + cursive.run(); } diff --git a/src/spotify.rs b/src/spotify.rs index 8db63c6..b77e5b6 100644 --- a/src/spotify.rs +++ b/src/spotify.rs @@ -156,16 +156,18 @@ impl Spotify { println!("Spotify::run() finished"); } - pub fn search(&mut self, query: &str, limit: u32, offset: u32) -> Result { + pub fn search(&self, query: &str, limit: u32, offset: u32) -> Result { self.api.search_track(query, limit, offset, None) } - pub fn load(&mut self, track: SpotifyId) { + pub fn load(&self, track: SpotifyId) { info!("loading track: {:?}", track); - self.channel.unbounded_send(WorkerCommand::Load(track)).unwrap(); + self.channel + .unbounded_send(WorkerCommand::Load(track)) + .unwrap(); } - pub fn play(&mut self) { + pub fn play(&self) { info!("play()"); self.channel.unbounded_send(WorkerCommand::Play).unwrap(); } diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..a557bff --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1 @@ +pub mod search; diff --git a/src/ui/search.rs b/src/ui/search.rs new file mode 100644 index 0000000..ec5ec43 --- /dev/null +++ b/src/ui/search.rs @@ -0,0 +1,55 @@ +use cursive::direction::Orientation; +use cursive::traits::Boxable; +use cursive::traits::Identifiable; +use cursive::views::*; +use cursive::Cursive; +use std::sync::Arc; + +use librespot::core::spotify_id::SpotifyId; + +use spotify::Spotify; + +pub struct SearchView { + pub view: Panel, +} + +impl SearchView { + pub fn search_handler(s: &mut Cursive, input: &str, spotify: Arc) { + let mut results: ViewRef = s.find_id("search_results").unwrap(); + let tracks = spotify.search(input, 10, 0); + + results.clear(); + + if let Ok(tracks) = tracks { + for track in tracks.tracks.items { + let s = spotify.clone(); + let trackid = SpotifyId::from_base62(&track.id).expect("could not load track"); + let button = Button::new(track.name, move |_cursive| { + s.load(trackid); + s.play(); + }); + results.add_child(&track.id, button); + } + } + } + + pub fn new(spotify: Arc) -> SearchView { + let spotify_ref = spotify.clone(); + let searchfield = EditView::new() + .on_submit(move |s, input| { + SearchView::search_handler(s, input, spotify_ref.clone()); + }) + .with_id("search_edit") + .full_width() + .fixed_height(1); + let results = ListView::new() + .with_id("search_results") + .full_width() + .full_height(); + let layout = LinearLayout::new(Orientation::Vertical) + .child(searchfield) + .child(results); + let rootpanel = Panel::new(layout).title("Search"); + return SearchView { view: rootpanel }; + } +}