introduce track data class

This commit is contained in:
Henrik Friedrichsen
2019-03-06 00:15:28 +01:00
parent 0f3cc41115
commit 91e89c2970
10 changed files with 98 additions and 75 deletions

View File

@@ -1,16 +1,15 @@
use crossbeam_channel::{unbounded, Receiver, Sender, TryIter};
use cursive::{CbFunc, Cursive};
use rspotify::spotify::model::track::FullTrack;
use spotify::PlayerStatus;
use queue::QueueChange;
use spotify::PlayerStatus;
use track::Track;
use ui::playlist::PlaylistEvent;
pub enum Event {
Queue(QueueChange),
PlayerStatus(PlayerStatus),
PlayerTrack(Option<FullTrack>),
PlayerTrack(Option<Track>),
Playlist(PlaylistEvent),
}

View File

@@ -35,6 +35,7 @@ mod events;
mod queue;
mod spotify;
mod theme;
mod track;
mod ui;
use events::{Event, EventManager};

View File

@@ -1,12 +1,12 @@
use std::collections::vec_deque::Iter;
use std::collections::VecDeque;
use rspotify::spotify::model::track::FullTrack;
use track::Track;
use events::{Event, EventManager};
pub struct Queue {
queue: VecDeque<FullTrack>,
queue: VecDeque<Track>,
ev: EventManager,
}
@@ -24,35 +24,35 @@ impl Queue {
ev: ev,
}
}
pub fn remove(&mut self, index: usize) -> Option<FullTrack> {
pub fn remove(&mut self, index: usize) -> Option<Track> {
match self.queue.remove(index) {
Some(track) => {
debug!("Removed from queue: {}", &track.name);
debug!("Removed from queue: {}", &track);
self.ev.send(Event::Queue(QueueChange::Remove(index)));
Some(track)
}
None => None,
}
}
pub fn enqueue(&mut self, track: FullTrack) {
debug!("Queued: {}", &track.name);
pub fn enqueue(&mut self, track: Track) {
debug!("Queued: {}", &track);
self.queue.push_back(track);
self.ev.send(Event::Queue(QueueChange::Enqueue));
}
pub fn dequeue(&mut self) -> Option<FullTrack> {
pub fn dequeue(&mut self) -> Option<Track> {
match self.queue.pop_front() {
Some(track) => {
debug!("Dequeued : {}", track.name);
debug!("Dequeued : {}", track);
self.ev.send(Event::Queue(QueueChange::Dequeue));
Some(track)
}
None => None,
}
}
pub fn peek(&self) -> Option<&FullTrack> {
pub fn peek(&self) -> Option<&Track> {
self.queue.get(0)
}
pub fn iter(&self) -> Iter<FullTrack> {
pub fn iter(&self) -> Iter<Track> {
self.queue.iter()
}
}

View File

@@ -14,7 +14,6 @@ use rspotify::spotify::client::Spotify as SpotifyAPI;
use rspotify::spotify::model::page::Page;
use rspotify::spotify::model::playlist::{PlaylistTrack, SimplifiedPlaylist};
use rspotify::spotify::model::search::SearchTracks;
use rspotify::spotify::model::track::FullTrack;
use failure::Error;
@@ -34,9 +33,10 @@ use std::time::{Duration, SystemTime};
use events::{Event, EventManager};
use queue::Queue;
use track::Track;
enum WorkerCommand {
Load(FullTrack),
Load(Track),
Play,
Pause,
Stop,
@@ -51,7 +51,7 @@ pub enum PlayerStatus {
pub struct Spotify {
status: RwLock<PlayerStatus>,
track: RwLock<Option<FullTrack>>,
track: RwLock<Option<Track>>,
pub api: SpotifyAPI,
elapsed: RwLock<Option<Duration>>,
since: RwLock<Option<SystemTime>>,
@@ -99,9 +99,7 @@ impl futures::Future for Worker {
debug!("message received!");
match cmd {
WorkerCommand::Load(track) => {
let trackid =
SpotifyId::from_base62(&track.id).expect("could not load track");
self.play_task = Box::new(self.player.load(trackid, false, 0));
self.play_task = Box::new(self.player.load(track.id, false, 0));
info!("player loading track..");
self.events.send(Event::PlayerTrack(Some(track)));
}
@@ -127,10 +125,8 @@ impl futures::Future for Worker {
let mut queue = self.queue.lock().unwrap();
if let Some(track) = queue.dequeue() {
debug!("next track in queue: {}", track.name);
let trackid =
SpotifyId::from_base62(&track.id).expect("could not load track");
self.play_task = Box::new(self.player.load(trackid, false, 0));
debug!("next track in queue: {}", track);
self.play_task = Box::new(self.player.load(track.id, false, 0));
self.player.play();
self.events.send(Event::PlayerTrack(Some(track)));
@@ -246,7 +242,7 @@ impl Spotify {
(*status).clone()
}
pub fn get_current_track(&self) -> Option<FullTrack> {
pub fn get_current_track(&self) -> Option<Track> {
let track = self
.track
.read()
@@ -311,7 +307,7 @@ impl Spotify {
.user_playlist_tracks(&self.user, playlist_id, None, 50, 0, None)
}
pub fn load(&self, track: FullTrack) {
pub fn load(&self, track: Track) {
info!("loading track: {:?}", track);
self.channel
.unbounded_send(WorkerCommand::Load(track))
@@ -340,7 +336,7 @@ impl Spotify {
*status = new_status;
}
pub fn update_track(&self, new_track: Option<FullTrack>) {
pub fn update_track(&self, new_track: Option<Track>) {
self.set_elapsed(None);
self.set_since(None);

54
src/track.rs Normal file
View File

@@ -0,0 +1,54 @@
use std::fmt;
use librespot::core::spotify_id::SpotifyId;
use rspotify::spotify::model::track::FullTrack;
#[derive(Clone)]
pub struct Track {
pub id: SpotifyId,
pub duration: u32,
pub artists: String,
pub title: String,
}
impl Track {
pub fn new(track: &FullTrack) -> Track {
let artists_joined = track
.artists
.iter()
.map(|ref artist| artist.name.clone())
.collect::<Vec<String>>()
.join(", ");
Track {
id: SpotifyId::from_base62(&track.id).expect("could not load track"),
duration: track.duration_ms / 1000,
artists: artists_joined,
title: track.name.clone(),
}
}
pub fn duration_str(&self) -> String {
let minutes = self.duration / 60;
let seconds = self.duration % 60;
format!("{:02}:{:02}", minutes, seconds)
}
}
impl fmt::Display for Track {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} - {}", self.artists, self.title)
}
}
impl fmt::Debug for Track {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"({} - {} ({})",
self.artists,
self.title,
self.id.to_base62()
)
}
}

View File

@@ -9,6 +9,7 @@ use rspotify::spotify::model::playlist::SimplifiedPlaylist;
use queue::Queue;
use spotify::Spotify;
use track::Track;
pub enum PlaylistEvent {
Refresh,
@@ -49,7 +50,7 @@ impl PlaylistView {
let tracks = spotify_ref.user_playlist_tracks(&id).unwrap().items;
let mut locked_queue = queue_ref.lock().expect("Could not aquire lock");
for playlist_track in tracks {
locked_queue.enqueue(playlist_track.track.clone());
locked_queue.enqueue(Track::new(&playlist_track.track));
}
});

View File

@@ -8,10 +8,9 @@ use cursive::Cursive;
use std::sync::Arc;
use std::sync::Mutex;
use rspotify::spotify::model::track::FullTrack;
use queue::{Queue, QueueChange};
use spotify::Spotify;
use track::Track;
use ui::trackbutton::TrackButton;
pub struct QueueView {
@@ -72,7 +71,7 @@ impl QueueView {
}
}
fn create_button(&self, track: &FullTrack) -> TrackButton {
fn create_button(&self, track: &Track) -> TrackButton {
let mut button = TrackButton::new(&track);
// 'd' deletes the selected track
{

View File

@@ -9,6 +9,7 @@ use std::sync::Mutex;
use queue::Queue;
use spotify::Spotify;
use track::Track;
use ui::trackbutton::TrackButton;
pub struct SearchView {
@@ -29,7 +30,9 @@ impl SearchView {
results.clear();
if let Ok(tracks) = tracks {
for track in tracks.tracks.items {
for search_track in tracks.tracks.items {
let track = Track::new(&search_track);
let s = spotify.clone();
let mut button = TrackButton::new(&track);

View File

@@ -51,21 +51,6 @@ impl View for StatusBar {
});
if let Some(ref t) = self.spotify.get_current_track() {
let name = format!(
"{} - {}",
t.artists
.iter()
.map(|ref artist| artist.name.clone())
.collect::<Vec<String>>()
.join(", "),
t.name
)
.to_string();
let minutes = t.duration_ms / 60000;
let seconds = (t.duration_ms % 60000) / 1000;
let formatted_duration = format!("{:02}:{:02}", minutes, seconds);
let elapsed = self.spotify.get_current_progress();
let formatted_elapsed = format!(
"{:02}:{:02}",
@@ -73,18 +58,18 @@ impl View for StatusBar {
elapsed.as_secs() % 60
);
let duration = format!("{} / {} ", formatted_elapsed, formatted_duration);
let duration = format!("{} / {} ", formatted_elapsed, t.duration_str());
let offset = HAlign::Right.get_offset(duration.width(), printer.size.x);
printer.with_color(style, |printer| {
printer.print((4, 1), &name);
printer.print((4, 1), &t.to_string());
printer.print((offset, 1), &duration);
});
printer.with_color(style_bar, |printer| {
printer.print((0, 0), &"".repeat(printer.size.x));
let duration_width = (((printer.size.x as u32) * (elapsed.as_millis() as u32))
/ t.duration_ms) as usize;
let duration_width =
(((printer.size.x as u32) * (elapsed.as_secs() as u32)) / t.duration) as usize;
printer.print((0, 0), &format!("{}{}", "=".repeat(duration_width), ">"));
});
} else {

View File

@@ -6,15 +6,14 @@ use cursive::traits::View;
use cursive::vec::Vec2;
use cursive::Cursive;
use cursive::Printer;
use rspotify::spotify::model::track::FullTrack;
use unicode_width::UnicodeWidthStr;
use track::Track;
pub struct TrackButton {
callbacks: Vec<(EventTrigger, Callback)>,
track: FullTrack,
title: String,
duration: String,
track: Track,
enabled: bool,
last_size: Vec2,
@@ -22,24 +21,10 @@ pub struct TrackButton {
}
impl TrackButton {
pub fn new(track: &FullTrack) -> TrackButton {
let artists = track
.artists
.iter()
.map(|ref artist| artist.name.clone())
.collect::<Vec<String>>()
.join(", ");
let formatted_title = format!("{} - {}", artists, track.name);
let minutes = track.duration_ms / 60000;
let seconds = (track.duration_ms % 60000) / 1000;
let formatted_duration = format!("{:02}:{:02}", minutes, seconds);
pub fn new(track: &Track) -> TrackButton {
TrackButton {
callbacks: Vec::new(),
track: track.clone(),
title: formatted_title,
duration: formatted_duration,
enabled: true,
last_size: Vec2::zero(),
invalidated: true,
@@ -72,9 +57,9 @@ impl View for TrackButton {
};
// shorten titles that are too long and append ".." to indicate this
let mut title_shortened = self.title.clone();
title_shortened.truncate(printer.size.x - self.duration.width() - 1);
if title_shortened.width() < self.title.width() {
let mut title_shortened = self.track.to_string();
title_shortened.truncate(printer.size.x - self.track.duration_str().width() - 1);
if title_shortened.width() < self.track.to_string().width() {
let offset = title_shortened.width() - 2;
title_shortened.replace_range(offset.., "..");
}
@@ -84,10 +69,10 @@ impl View for TrackButton {
});
// track duration goes to the end of the line
let offset = HAlign::Right.get_offset(self.duration.width(), printer.size.x);
let offset = HAlign::Right.get_offset(self.track.duration_str().width(), printer.size.x);
printer.with_color(style, |printer| {
printer.print((offset, 0), &self.duration);
printer.print((offset, 0), &self.track.duration_str());
});
}