implement track search and playback

This commit is contained in:
Henrik Friedrichsen
2018-12-23 12:44:40 +01:00
parent ae1cb96385
commit 278bb7844f
5 changed files with 99 additions and 43 deletions

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
authors = ["Henrik Friedrichsen <henrik@affekt.org>"]
[dependencies]
cursive = "0.9"
cursive = "0.10"
crossbeam-channel = "0.2"
env_logger = "0.5.13"
failure = "0.1.3"

View File

@@ -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<Self>, &mut spotify::Spotify);
}
impl<F: FnOnce(&mut spotify::Spotify) -> () + Send> CbSpotify for F {
fn call_box(self: Box<Self>, 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();
}

View File

@@ -156,16 +156,18 @@ impl Spotify {
println!("Spotify::run() finished");
}
pub fn search(&mut self, query: &str, limit: u32, offset: u32) -> Result<SearchTracks, Error> {
pub fn search(&self, query: &str, limit: u32, offset: u32) -> Result<SearchTracks, Error> {
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();
}

1
src/ui/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod search;

55
src/ui/search.rs Normal file
View File

@@ -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<LinearLayout>,
}
impl SearchView {
pub fn search_handler(s: &mut Cursive, input: &str, spotify: Arc<Spotify>) {
let mut results: ViewRef<ListView> = 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<Spotify>) -> 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 };
}
}