Add seeking
This commit is contained in:
@@ -297,6 +297,28 @@ impl CommandManager {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let spotify = spotify.clone();
|
||||
self.register(
|
||||
"seek",
|
||||
Vec::new(),
|
||||
Box::new(move |_s, args| {
|
||||
if let Some(arg) = args.get(0) {
|
||||
match arg.chars().next().unwrap() {
|
||||
'+' | '-' => {
|
||||
spotify.seek_relative(arg.parse::<i32>().unwrap_or(0));
|
||||
},
|
||||
_ => {
|
||||
spotify.seek(arg.parse::<u32>().unwrap_or(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_aliases(&self, name: &String) -> String {
|
||||
@@ -308,7 +330,7 @@ impl CommandManager {
|
||||
}
|
||||
|
||||
pub fn handle(&self, s: &mut Cursive, cmd: String) {
|
||||
let components: Vec<String> = cmd.split(' ').map(|s| s.to_string()).collect();
|
||||
let components: Vec<String> = cmd.trim().split(' ').map(|s| s.to_string()).collect();
|
||||
|
||||
let result = if let Some(cb) = self.commands.get(&self.handle_aliases(&components[0])) {
|
||||
cb(s, components[1..].to_vec())
|
||||
@@ -363,6 +385,8 @@ impl CommandManager {
|
||||
kb.insert("Enter".into(), "play selected".into());
|
||||
kb.insert("d".into(), "delete selected".into());
|
||||
kb.insert("/".into(), "search".into());
|
||||
kb.insert(".".into(), "seek +500".into());
|
||||
kb.insert(",".into(), "seek -500".into());
|
||||
|
||||
kb.insert("F1".into(), "queue".into());
|
||||
kb.insert("F2".into(), "search".into());
|
||||
|
||||
@@ -35,7 +35,7 @@ fn get_metadata(queue: Arc<Queue>) -> HashMap<String, Variant<Box<RefArg>>> {
|
||||
);
|
||||
hm.insert(
|
||||
"mpris:length".to_string(),
|
||||
Variant(Box::new(track.map(|t| t.duration * 1_000_000).unwrap_or(0))),
|
||||
Variant(Box::new(track.map(|t| t.duration * 1_000).unwrap_or(0))),
|
||||
);
|
||||
hm.insert(
|
||||
"mpris:artUrl".to_string(),
|
||||
|
||||
@@ -40,6 +40,7 @@ enum WorkerCommand {
|
||||
Play,
|
||||
Pause,
|
||||
Stop,
|
||||
Seek(u32),
|
||||
RequestToken(oneshot::Sender<Token>),
|
||||
}
|
||||
|
||||
@@ -135,6 +136,9 @@ impl futures::Future for Worker {
|
||||
self.events.send(Event::Player(PlayerEvent::Stopped));
|
||||
self.active = false;
|
||||
}
|
||||
WorkerCommand::Seek(pos) => {
|
||||
self.player.seek(pos);
|
||||
}
|
||||
WorkerCommand::RequestToken(sender) => {
|
||||
self.token_task = Spotify::get_token(&self.session, sender);
|
||||
progress = true;
|
||||
@@ -499,4 +503,21 @@ impl Spotify {
|
||||
info!("stop()");
|
||||
self.channel.unbounded_send(WorkerCommand::Stop).unwrap();
|
||||
}
|
||||
|
||||
pub fn seek(&self, position_ms: u32) {
|
||||
self.set_elapsed(Some(Duration::from_millis(position_ms.into())));
|
||||
self.set_since(if self.get_current_status() == PlayerEvent::Playing {
|
||||
Some(SystemTime::now())
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
self.channel.unbounded_send(WorkerCommand::Seek(position_ms)).unwrap();
|
||||
}
|
||||
|
||||
pub fn seek_relative(&self, delta: i32) {
|
||||
let progress = self.get_current_progress();
|
||||
let new = (progress.as_secs() * 1000) as i32 + progress.subsec_millis() as i32 + delta;
|
||||
self.seek(std::cmp::max(0, new) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Track {
|
||||
title: track.name.clone(),
|
||||
track_number: track.track_number,
|
||||
disc_number: track.disc_number,
|
||||
duration: track.duration_ms / 1000,
|
||||
duration: track.duration_ms,
|
||||
artists: artists,
|
||||
album: track.album.name.clone(),
|
||||
album_artists: album_artists,
|
||||
@@ -54,8 +54,8 @@ impl Track {
|
||||
}
|
||||
|
||||
pub fn duration_str(&self) -> String {
|
||||
let minutes = self.duration / 60;
|
||||
let seconds = self.duration % 60;
|
||||
let minutes = self.duration / 60_000;
|
||||
let seconds = (self.duration / 1000) % 60;
|
||||
format!("{:02}:{:02}", minutes, seconds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ pub struct Layout {
|
||||
error: Option<String>,
|
||||
error_time: Option<SystemTime>,
|
||||
screenchange: bool,
|
||||
last_size: Vec2,
|
||||
ev: events::EventManager,
|
||||
theme: Theme,
|
||||
}
|
||||
@@ -49,8 +50,9 @@ impl Layout {
|
||||
cmdline_focus: false,
|
||||
error: None,
|
||||
error_time: None,
|
||||
ev: ev.clone(),
|
||||
screenchange: true,
|
||||
last_size: Vec2::new(0, 0),
|
||||
ev: ev.clone(),
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
@@ -166,6 +168,30 @@ impl View for Layout {
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if let Event::Mouse {
|
||||
position,
|
||||
..
|
||||
} = event {
|
||||
let error = self.get_error();
|
||||
|
||||
let cmdline_visible = self.cmdline.get_content().len() > 0;
|
||||
let mut cmdline_height = if cmdline_visible { 1 } else { 0 };
|
||||
if error.is_some() {
|
||||
cmdline_height += 1;
|
||||
}
|
||||
|
||||
if position.y < self.last_size.y - 2 - cmdline_height {
|
||||
if let Some(ref id) = self.focus {
|
||||
let screen = self.views.get_mut(id).unwrap();
|
||||
screen.view.on_event(event.clone());
|
||||
}
|
||||
} else if position.y < self.last_size.y - cmdline_height {
|
||||
self.statusbar.on_event(event.relativized(Vec2::new(0, self.last_size.y - 2 - cmdline_height)));
|
||||
}
|
||||
|
||||
return EventResult::Consumed(None);
|
||||
}
|
||||
|
||||
if self.cmdline_focus {
|
||||
return self.cmdline.on_event(event);
|
||||
}
|
||||
@@ -179,6 +205,10 @@ impl View for Layout {
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.last_size = size;
|
||||
|
||||
self.statusbar.layout(Vec2::new(size.x, 2));
|
||||
|
||||
self.cmdline.layout(Vec2::new(size.x, 1));
|
||||
|
||||
if let Some(ref id) = self.focus {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use cursive::align::HAlign;
|
||||
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
|
||||
use cursive::theme::{ColorStyle, ColorType, PaletteColor};
|
||||
use cursive::traits::View;
|
||||
use cursive::vec::Vec2;
|
||||
@@ -13,6 +14,7 @@ use spotify::{PlayerEvent, Spotify};
|
||||
pub struct StatusBar {
|
||||
queue: Arc<Queue>,
|
||||
spotify: Arc<Spotify>,
|
||||
last_size: Vec2,
|
||||
}
|
||||
|
||||
impl StatusBar {
|
||||
@@ -20,6 +22,7 @@ impl StatusBar {
|
||||
StatusBar {
|
||||
queue: queue,
|
||||
spotify: spotify,
|
||||
last_size: Vec2::new(0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +66,8 @@ impl View for StatusBar {
|
||||
|
||||
if let Some(ref t) = self.queue.get_current() {
|
||||
let elapsed = self.spotify.get_current_progress();
|
||||
let elapsed_ms = elapsed.as_secs() as u32 * 1000 + elapsed.subsec_millis();
|
||||
|
||||
let formatted_elapsed = format!(
|
||||
"{:02}:{:02}",
|
||||
elapsed.as_secs() / 60,
|
||||
@@ -80,7 +85,7 @@ impl View for StatusBar {
|
||||
printer.with_color(style_bar, |printer| {
|
||||
printer.print((0, 0), &"—".repeat(printer.size.x));
|
||||
let duration_width =
|
||||
(((printer.size.x as u32) * (elapsed.as_secs() as u32)) / t.duration) as usize;
|
||||
(((printer.size.x as u32) * elapsed_ms) / t.duration) as usize;
|
||||
printer.print((0, 0), &format!("{}{}", "=".repeat(duration_width), ">"));
|
||||
});
|
||||
} else {
|
||||
@@ -90,7 +95,50 @@ impl View for StatusBar {
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.last_size = size;
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
Vec2::new(constraint.x, 2)
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
if let Event::Mouse {
|
||||
offset,
|
||||
position,
|
||||
event
|
||||
} = event {
|
||||
let position = position - offset;
|
||||
|
||||
if position.y == 0 {
|
||||
if event == MouseEvent::WheelUp {
|
||||
self.spotify.seek_relative(-500);
|
||||
}
|
||||
|
||||
if event == MouseEvent::WheelDown {
|
||||
self.spotify.seek_relative(500);
|
||||
}
|
||||
|
||||
if event == MouseEvent::Press(MouseButton::Left) ||
|
||||
event == MouseEvent::Hold(MouseButton::Left)
|
||||
{
|
||||
if let Some(ref t) = self.queue.get_current() {
|
||||
let f: f32 = position.x as f32 / self.last_size.x as f32;
|
||||
let new = t.duration as f32 * f;
|
||||
self.spotify.seek(new as u32);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if event == MouseEvent::Press(MouseButton::Left) {
|
||||
self.queue.toggleplayback();
|
||||
}
|
||||
}
|
||||
|
||||
EventResult::Consumed(None)
|
||||
} else {
|
||||
EventResult::Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user