download playlist once at startup and keep it in memory
This commit is contained in:
@@ -143,7 +143,8 @@ fn main() {
|
|||||||
|
|
||||||
let search = ui::search::SearchView::new(spotify.clone(), queue.clone());
|
let search = ui::search::SearchView::new(spotify.clone(), queue.clone());
|
||||||
|
|
||||||
let mut playlists = ui::playlist::PlaylistView::new(queue.clone(), spotify.clone());
|
let mut playlists =
|
||||||
|
ui::playlist::PlaylistView::new(event_manager.clone(), queue.clone(), spotify.clone());
|
||||||
|
|
||||||
let mut queueview = ui::queue::QueueView::new(queue.clone());
|
let mut queueview = ui::queue::QueueView::new(queue.clone());
|
||||||
|
|
||||||
@@ -187,7 +188,7 @@ fn main() {
|
|||||||
s.call_on_id("main", |v: &mut ui::layout::Layout| {
|
s.call_on_id("main", |v: &mut ui::layout::Layout| {
|
||||||
v.set_view("playlists");
|
v.set_view("playlists");
|
||||||
});
|
});
|
||||||
ev.send(Event::Playlist(PlaylistEvent::Refresh));
|
ev.send(Event::Playlist(PlaylistEvent::Show));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,9 +262,14 @@ impl Spotify {
|
|||||||
self.api.current_user_playlists(limit, offset)
|
self.api.current_user_playlists(limit, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_playlist_tracks(&self, playlist_id: &str) -> Result<Page<PlaylistTrack>, Error> {
|
pub fn user_playlist_tracks(
|
||||||
|
&self,
|
||||||
|
playlist_id: &str,
|
||||||
|
limit: u32,
|
||||||
|
offset: u32,
|
||||||
|
) -> Result<Page<PlaylistTrack>, Error> {
|
||||||
self.api
|
self.api
|
||||||
.user_playlist_tracks(&self.user, playlist_id, None, 50, 0, None)
|
.user_playlist_tracks(&self.user, playlist_id, None, limit, offset, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&self, track: &Track) {
|
pub fn load(&self, track: &Track) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
use cursive::direction::Orientation;
|
use cursive::direction::Orientation;
|
||||||
use cursive::event::Key;
|
use cursive::event::Key;
|
||||||
@@ -8,58 +9,61 @@ use cursive::views::*;
|
|||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
use rspotify::spotify::model::playlist::SimplifiedPlaylist;
|
use rspotify::spotify::model::playlist::SimplifiedPlaylist;
|
||||||
|
|
||||||
|
use events::{Event, EventManager};
|
||||||
use queue::Queue;
|
use queue::Queue;
|
||||||
use spotify::Spotify;
|
use spotify::Spotify;
|
||||||
use track::Track;
|
use track::Track;
|
||||||
use ui::splitbutton::SplitButton;
|
use ui::splitbutton::SplitButton;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Playlist {
|
||||||
|
meta: SimplifiedPlaylist,
|
||||||
|
tracks: Vec<Track>,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum PlaylistEvent {
|
pub enum PlaylistEvent {
|
||||||
Refresh,
|
Show,
|
||||||
|
NewList(Playlist),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlaylistView {
|
pub struct PlaylistView {
|
||||||
pub view: Option<BoxView<ScrollView<IdView<LinearLayout>>>>,
|
pub view: Option<BoxView<ScrollView<IdView<LinearLayout>>>>,
|
||||||
queue: Arc<Mutex<Queue>>,
|
queue: Arc<Mutex<Queue>>,
|
||||||
spotify: Arc<Spotify>,
|
playlists: Arc<RwLock<Vec<Playlist>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaylistView {
|
impl PlaylistView {
|
||||||
pub fn new(queue: Arc<Mutex<Queue>>, spotify: Arc<Spotify>) -> PlaylistView {
|
pub fn new(ev: EventManager, queue: Arc<Mutex<Queue>>, spotify: Arc<Spotify>) -> PlaylistView {
|
||||||
let playlists = LinearLayout::new(Orientation::Vertical).with_id("playlists");
|
let playlists_view = LinearLayout::new(Orientation::Vertical).with_id("playlists");
|
||||||
let scrollable = ScrollView::new(playlists).full_screen();
|
let scrollable = ScrollView::new(playlists_view).full_screen();
|
||||||
|
let playlists = Arc::new(RwLock::new(Vec::new()));
|
||||||
|
|
||||||
|
{
|
||||||
|
let spotify = spotify.clone();
|
||||||
|
let playlists = playlists.clone();
|
||||||
|
Self::load_playlists(ev, spotify, playlists);
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistView {
|
PlaylistView {
|
||||||
view: Some(scrollable),
|
view: Some(scrollable),
|
||||||
queue: queue,
|
queue: queue,
|
||||||
spotify: spotify,
|
playlists: playlists,
|
||||||
}
|
|
||||||
}
|
|
||||||
fn clear_playlists(&self, playlists: &mut ViewRef<LinearLayout>) {
|
|
||||||
while playlists.len() > 0 {
|
|
||||||
playlists.remove_child(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_button(&self, playlist: &SimplifiedPlaylist) -> SplitButton {
|
fn create_button(&self, playlist: &Playlist) -> SplitButton {
|
||||||
let collab = match playlist.collaborative {
|
let trackcount = format!("{} tracks", playlist.tracks.len());
|
||||||
true => "collaborative",
|
let mut button = SplitButton::new(&playlist.meta.name, &trackcount);
|
||||||
false => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut button = SplitButton::new(&playlist.name, collab);
|
|
||||||
|
|
||||||
// <enter> plays the selected playlist
|
// <enter> plays the selected playlist
|
||||||
{
|
{
|
||||||
let id = playlist.id.clone();
|
|
||||||
let spotify_ref = self.spotify.clone();
|
|
||||||
let queue_ref = self.queue.clone();
|
let queue_ref = self.queue.clone();
|
||||||
|
let playlist = playlist.clone();
|
||||||
button.add_callback(Key::Enter, move |_s| {
|
button.add_callback(Key::Enter, move |_s| {
|
||||||
let tracks = spotify_ref.user_playlist_tracks(&id).unwrap().items;
|
let mut locked_queue = queue_ref.lock().expect("could not acquire lock");
|
||||||
let mut locked_queue = queue_ref.lock().expect("Could not aquire lock");
|
|
||||||
|
|
||||||
let mut first_played = false;
|
let mut first_played = false;
|
||||||
for playlist_track in tracks {
|
for track in playlist.tracks.iter() {
|
||||||
let index = locked_queue.append_next(&Track::new(&playlist_track.track));
|
let index = locked_queue.append_next(track);
|
||||||
if !first_played {
|
if !first_played {
|
||||||
locked_queue.play(index);
|
locked_queue.play(index);
|
||||||
first_played = true;
|
first_played = true;
|
||||||
@@ -70,14 +74,12 @@ impl PlaylistView {
|
|||||||
|
|
||||||
// <space> queues the selected playlist
|
// <space> queues the selected playlist
|
||||||
{
|
{
|
||||||
let id = playlist.id.clone();
|
|
||||||
let spotify_ref = self.spotify.clone();
|
|
||||||
let queue_ref = self.queue.clone();
|
let queue_ref = self.queue.clone();
|
||||||
|
let playlist = playlist.clone();
|
||||||
button.add_callback(' ', move |_s| {
|
button.add_callback(' ', move |_s| {
|
||||||
let tracks = spotify_ref.user_playlist_tracks(&id).unwrap().items;
|
let mut locked_queue = queue_ref.lock().expect("could not acquire lock");
|
||||||
let mut locked_queue = queue_ref.lock().expect("Could not aquire lock");
|
for track in playlist.tracks.iter() {
|
||||||
for playlist_track in tracks {
|
locked_queue.append(track);
|
||||||
locked_queue.append(&Track::new(&playlist_track.track));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -85,10 +87,82 @@ impl PlaylistView {
|
|||||||
button
|
button
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_playlists(&self, playlists: &mut ViewRef<LinearLayout>) {
|
fn load_playlist(list: &SimplifiedPlaylist, spotify: Arc<Spotify>) -> Playlist {
|
||||||
let playlists_response = self.spotify.current_user_playlist(50, 0).unwrap().items;
|
debug!("got list: {}", list.name);
|
||||||
for playlist in &playlists_response {
|
let id = list.id.clone();
|
||||||
let button = self.create_button(playlist);
|
|
||||||
|
let mut collected_tracks = Vec::new();
|
||||||
|
|
||||||
|
let mut tracks_result = spotify.user_playlist_tracks(&id, 50, 0).ok();
|
||||||
|
while let Some(ref tracks) = tracks_result.clone() {
|
||||||
|
for listtrack in &tracks.items {
|
||||||
|
collected_tracks.push(Track::new(&listtrack.track));
|
||||||
|
}
|
||||||
|
debug!("got {} tracks", tracks.items.len());
|
||||||
|
|
||||||
|
// load next batch if necessary
|
||||||
|
tracks_result = match tracks.next {
|
||||||
|
Some(_) => {
|
||||||
|
debug!("requesting tracks again..");
|
||||||
|
spotify
|
||||||
|
.user_playlist_tracks(&id, 50, tracks.offset + tracks.items.len() as u32)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Playlist {
|
||||||
|
meta: list.clone(),
|
||||||
|
tracks: collected_tracks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_playlists(
|
||||||
|
ev: EventManager,
|
||||||
|
spotify: Arc<Spotify>,
|
||||||
|
playlists: Arc<RwLock<Vec<Playlist>>>,
|
||||||
|
) {
|
||||||
|
thread::spawn(move || {
|
||||||
|
debug!("loading playlists");
|
||||||
|
let mut lists_result = spotify.current_user_playlist(50, 0).ok();
|
||||||
|
while let Some(ref lists) = lists_result.clone() {
|
||||||
|
for list in &lists.items {
|
||||||
|
let playlist = Self::load_playlist(&list, spotify.clone());
|
||||||
|
ev.send(Event::Playlist(PlaylistEvent::NewList(playlist.clone())));
|
||||||
|
playlists
|
||||||
|
.write()
|
||||||
|
.expect("could not acquire write lock on playlists")
|
||||||
|
.push(playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load next batch if necessary
|
||||||
|
lists_result = match lists.next {
|
||||||
|
Some(_) => {
|
||||||
|
debug!("requesting playlists again..");
|
||||||
|
spotify
|
||||||
|
.current_user_playlist(50, lists.offset + lists.items.len() as u32)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_playlists(&self, playlists: &mut ViewRef<LinearLayout>) {
|
||||||
|
while playlists.len() > 0 {
|
||||||
|
playlists.remove_child(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate(&self, playlists: &mut ViewRef<LinearLayout>) {
|
||||||
|
for list in self
|
||||||
|
.playlists
|
||||||
|
.read()
|
||||||
|
.expect("could not acquire read lock on playlists")
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
let button = self.create_button(&list);
|
||||||
playlists.add_child(button);
|
playlists.add_child(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,11 +172,13 @@ impl PlaylistView {
|
|||||||
|
|
||||||
if let Some(mut playlists) = view_ref {
|
if let Some(mut playlists) = view_ref {
|
||||||
match event {
|
match event {
|
||||||
PlaylistEvent::Refresh => {
|
PlaylistEvent::Show => {
|
||||||
// FIXME: do this only once at startup or when requested by
|
|
||||||
// the user
|
|
||||||
self.clear_playlists(&mut playlists);
|
self.clear_playlists(&mut playlists);
|
||||||
self.show_playlists(&mut playlists);
|
self.populate(&mut playlists);
|
||||||
|
}
|
||||||
|
PlaylistEvent::NewList(list) => {
|
||||||
|
let button = self.create_button(&list);
|
||||||
|
playlists.add_child(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user