introduce track data class
This commit is contained in:
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ mod events;
|
||||
mod queue;
|
||||
mod spotify;
|
||||
mod theme;
|
||||
mod track;
|
||||
mod ui;
|
||||
|
||||
use events::{Event, EventManager};
|
||||
|
||||
20
src/queue.rs
20
src/queue.rs
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
54
src/track.rs
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user