mirror of
https://github.com/socatlolmeow/drift.git
synced 2026-11-03 19:27:19 +00:00
(fix): queue bugs
This commit is contained in:
parent
4431840c03
commit
e2dfb42373
2 changed files with 93 additions and 49 deletions
140
src/app.rs
140
src/app.rs
|
|
@ -100,7 +100,9 @@ pub struct App {
|
||||||
pub repeat: RepeatMode,
|
pub repeat: RepeatMode,
|
||||||
played: HashSet<usize>,
|
played: HashSet<usize>,
|
||||||
fail_streak: u8,
|
fail_streak: u8,
|
||||||
autoplay: bool,
|
result_cursor: Option<usize>,
|
||||||
|
radio: Vec<Track>,
|
||||||
|
on_radio: bool,
|
||||||
seek_step: f64,
|
seek_step: f64,
|
||||||
volume_step: f64,
|
volume_step: f64,
|
||||||
resume: Option<(String, f64)>,
|
resume: Option<(String, f64)>,
|
||||||
|
|
@ -124,7 +126,6 @@ impl App {
|
||||||
config::migrate_legacy();
|
config::migrate_legacy();
|
||||||
let (cfg, cfg_warning) = Config::load();
|
let (cfg, cfg_warning) = Config::load();
|
||||||
let p = cfg.playback;
|
let p = cfg.playback;
|
||||||
let session = if p.restore_session { Session::load() } else { Session::default() };
|
|
||||||
let warning = cfg_warning.or(store_warning);
|
let warning = cfg_warning.or(store_warning);
|
||||||
let seed = SystemTime::now()
|
let seed = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
|
|
@ -136,8 +137,8 @@ impl App {
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
results: Vec::new(),
|
results: Vec::new(),
|
||||||
results_title: "Results".into(),
|
results_title: "Results".into(),
|
||||||
queue: session.queue,
|
queue: Vec::new(),
|
||||||
current: session.current,
|
current: None,
|
||||||
store,
|
store,
|
||||||
states: Default::default(),
|
states: Default::default(),
|
||||||
open: None,
|
open: None,
|
||||||
|
|
@ -158,7 +159,9 @@ impl App {
|
||||||
repeat: p.repeat,
|
repeat: p.repeat,
|
||||||
played: HashSet::new(),
|
played: HashSet::new(),
|
||||||
fail_streak: 0,
|
fail_streak: 0,
|
||||||
autoplay: p.autoplay,
|
result_cursor: None,
|
||||||
|
radio: Vec::new(),
|
||||||
|
on_radio: false,
|
||||||
seek_step: p.seek_step,
|
seek_step: p.seek_step,
|
||||||
volume_step: p.volume_step,
|
volume_step: p.volume_step,
|
||||||
resume: None,
|
resume: None,
|
||||||
|
|
@ -173,19 +176,22 @@ impl App {
|
||||||
tx,
|
tx,
|
||||||
rng: seed,
|
rng: seed,
|
||||||
};
|
};
|
||||||
|
if p.restore_session {
|
||||||
|
let s = Session::load();
|
||||||
|
app.resume = s
|
||||||
|
.current
|
||||||
|
.and_then(|c| s.queue.get(c))
|
||||||
|
.map(|t| (t.id.clone(), s.position));
|
||||||
|
app.current = s.current;
|
||||||
|
app.queue = s.queue;
|
||||||
|
if !app.queue.is_empty() {
|
||||||
|
app.states[View::Queue.idx()].select(Some(app.current.unwrap_or(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
match warning {
|
match warning {
|
||||||
Some(w) => app.error(w),
|
Some(w) => app.error(w),
|
||||||
None => app.info("Welcome! Press / to search, ? for help."),
|
None => app.info("Welcome! Press / to search, ? for help."),
|
||||||
}
|
}
|
||||||
if let Some(t) = app.now_track().cloned() {
|
|
||||||
app.position = session.position;
|
|
||||||
app.duration = t.duration.map_or(0.0, f64::from);
|
|
||||||
app.resume = Some((t.id, session.position));
|
|
||||||
app.states[View::Queue.idx()].select(app.current);
|
|
||||||
if app.autoplay {
|
|
||||||
app.play_default();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +206,7 @@ impl App {
|
||||||
let session = Session {
|
let session = Session {
|
||||||
queue: self.queue.clone(),
|
queue: self.queue.clone(),
|
||||||
current: self.current,
|
current: self.current,
|
||||||
position: self.position,
|
position: if self.on_radio { 0.0 } else { self.position },
|
||||||
};
|
};
|
||||||
let text = session.render()?;
|
let text = session.render()?;
|
||||||
if !force && text == self.saved {
|
if !force && text == self.saved {
|
||||||
|
|
@ -309,6 +315,9 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn now_track(&self) -> Option<&Track> {
|
pub fn now_track(&self) -> Option<&Track> {
|
||||||
|
if self.on_radio {
|
||||||
|
return self.result_cursor.and_then(|i| self.radio.get(i));
|
||||||
|
}
|
||||||
self.current.and_then(|i| self.queue.get(i))
|
self.current.and_then(|i| self.queue.get(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -833,6 +842,7 @@ impl App {
|
||||||
if !keep_resume {
|
if !keep_resume {
|
||||||
self.resume = None;
|
self.resume = None;
|
||||||
}
|
}
|
||||||
|
self.on_radio = false;
|
||||||
self.current = Some(i);
|
self.current = Some(i);
|
||||||
self.played.insert(i);
|
self.played.insert(i);
|
||||||
self.play_state = PlayState::Loading;
|
self.play_state = PlayState::Loading;
|
||||||
|
|
@ -862,8 +872,8 @@ impl App {
|
||||||
if self.queue.is_empty() {
|
if self.queue.is_empty() {
|
||||||
match self.view {
|
match self.view {
|
||||||
View::Results if !self.results.is_empty() => {
|
View::Results if !self.results.is_empty() => {
|
||||||
let t = self.results[self.selected().min(self.results.len() - 1)].clone();
|
let i = self.selected().min(self.results.len() - 1);
|
||||||
self.play_now(t);
|
self.play_result(i);
|
||||||
}
|
}
|
||||||
View::Playlists if !self.store.playlists.is_empty() => {
|
View::Playlists if !self.store.playlists.is_empty() => {
|
||||||
let i = self.selected();
|
let i = self.selected();
|
||||||
|
|
@ -888,10 +898,11 @@ impl App {
|
||||||
|
|
||||||
fn activate_at(&mut self, view: View, i: usize) {
|
fn activate_at(&mut self, view: View, i: usize) {
|
||||||
match view {
|
match view {
|
||||||
View::Results => match self.results.get(i).cloned() {
|
View::Results => {
|
||||||
Some(t) => self.play_now(t),
|
if i < self.results.len() {
|
||||||
None => {}
|
self.play_result(i);
|
||||||
},
|
}
|
||||||
|
}
|
||||||
View::Queue => {
|
View::Queue => {
|
||||||
if i < self.queue.len() {
|
if i < self.queue.len() {
|
||||||
self.play_index(i);
|
self.play_index(i);
|
||||||
|
|
@ -909,14 +920,30 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_now(&mut self, t: Track) {
|
fn play_result(&mut self, i: usize) {
|
||||||
if let Some(k) = self.queue.iter().position(|q| q.id == t.id) {
|
if i >= self.results.len() {
|
||||||
return self.play_index(k);
|
return;
|
||||||
|
}
|
||||||
|
self.radio = self.results.clone();
|
||||||
|
self.play_radio(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn play_radio(&mut self, i: usize) {
|
||||||
|
let Some(track) = self.radio.get(i).cloned() else { return };
|
||||||
|
self.on_radio = true;
|
||||||
|
self.result_cursor = Some(i);
|
||||||
|
self.resume = None;
|
||||||
|
self.play_state = PlayState::Loading;
|
||||||
|
self.position = 0.0;
|
||||||
|
self.duration = track.duration.map_or(0.0, f64::from);
|
||||||
|
let url = track.url();
|
||||||
|
match self.with_player(|p| p.load(&url)) {
|
||||||
|
Ok(()) => self.info(format!("♪ {}", track.label())),
|
||||||
|
Err(e) => {
|
||||||
|
self.play_state = PlayState::Stopped;
|
||||||
|
self.error(format!("Playback failed: {e:#}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let at = self.current.map_or(self.queue.len(), |c| (c + 1).min(self.queue.len()));
|
|
||||||
self.queue.insert(at, t);
|
|
||||||
self.played.clear();
|
|
||||||
self.play_index(at);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pause(&mut self, pause: bool) {
|
fn set_pause(&mut self, pause: bool) {
|
||||||
|
|
@ -951,13 +978,24 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) {
|
fn next(&mut self) {
|
||||||
if self.queue.is_empty() {
|
if self.queue.is_empty() && self.result_cursor.is_none() {
|
||||||
return self.error("Queue is empty");
|
return self.error("Queue is empty");
|
||||||
}
|
}
|
||||||
self.advance(false);
|
self.advance(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) {
|
fn prev(&mut self) {
|
||||||
|
if let Some(cursor) = self.result_cursor.filter(|_| self.on_radio) {
|
||||||
|
if self.position > 3.0 {
|
||||||
|
return self.seek(Amount::Abs(0.0));
|
||||||
|
}
|
||||||
|
if cursor > 0 {
|
||||||
|
self.play_radio(cursor - 1);
|
||||||
|
} else {
|
||||||
|
self.seek(Amount::Abs(0.0));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
let Some(c) = self.current else {
|
let Some(c) = self.current else {
|
||||||
return self.error("Nothing is playing");
|
return self.error("Nothing is playing");
|
||||||
};
|
};
|
||||||
|
|
@ -981,16 +1019,20 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(&mut self, auto: bool) {
|
fn advance(&mut self, auto: bool) {
|
||||||
let len = self.queue.len();
|
|
||||||
if len == 0 {
|
|
||||||
return self.stop_playback();
|
|
||||||
}
|
|
||||||
if auto && self.repeat == RepeatMode::One {
|
if auto && self.repeat == RepeatMode::One {
|
||||||
if let Some(c) = self.current {
|
if self.on_radio {
|
||||||
|
if let Some(c) = self.result_cursor {
|
||||||
|
return self.play_radio(c);
|
||||||
|
}
|
||||||
|
} else if let Some(c) = self.current {
|
||||||
return self.play_index(c);
|
return self.play_index(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let next = if self.shuffle {
|
|
||||||
|
let len = self.queue.len();
|
||||||
|
let next = if len == 0 {
|
||||||
|
None
|
||||||
|
} else if self.shuffle {
|
||||||
let mut pool: Vec<usize> = (0..len).filter(|i| !self.played.contains(i)).collect();
|
let mut pool: Vec<usize> = (0..len).filter(|i| !self.played.contains(i)).collect();
|
||||||
if pool.is_empty() && self.repeat == RepeatMode::All {
|
if pool.is_empty() && self.repeat == RepeatMode::All {
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
|
|
@ -1013,9 +1055,16 @@ impl App {
|
||||||
None => Some(0),
|
None => Some(0),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let next = next.or_else(|| self.next_from_results());
|
|
||||||
match next {
|
match next {
|
||||||
Some(i) => self.play_index(i),
|
Some(i) => self.play_index(i),
|
||||||
|
None => self.advance_radio(auto),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance_radio(&mut self, auto: bool) {
|
||||||
|
let next = self.result_cursor.map(|c| c + 1).filter(|&i| i < self.radio.len());
|
||||||
|
match next {
|
||||||
|
Some(i) => self.play_radio(i),
|
||||||
None if auto => {
|
None if auto => {
|
||||||
self.stop_playback();
|
self.stop_playback();
|
||||||
self.info("Queue finished");
|
self.info("Queue finished");
|
||||||
|
|
@ -1024,17 +1073,6 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_from_results(&mut self) -> Option<usize> {
|
|
||||||
let cur_id = self.now_track()?.id.clone();
|
|
||||||
let r = self.results.iter().position(|t| t.id == cur_id)?;
|
|
||||||
let next = self.results[r + 1..]
|
|
||||||
.iter()
|
|
||||||
.find(|t| !self.queue.iter().any(|q| q.id == t.id))?
|
|
||||||
.clone();
|
|
||||||
self.queue.push(next);
|
|
||||||
Some(self.queue.len() - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn seek(&mut self, amount: Amount) {
|
fn seek(&mut self, amount: Amount) {
|
||||||
if !matches!(self.play_state, PlayState::Playing | PlayState::Paused) {
|
if !matches!(self.play_state, PlayState::Playing | PlayState::Paused) {
|
||||||
return self.error("Nothing is playing");
|
return self.error("Nothing is playing");
|
||||||
|
|
@ -1134,6 +1172,7 @@ impl App {
|
||||||
self.queue = self.results.clone();
|
self.queue = self.results.clone();
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
self.current = None;
|
self.current = None;
|
||||||
|
self.result_cursor = None;
|
||||||
self.play_index(start.min(self.queue.len() - 1));
|
self.play_index(start.min(self.queue.len() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1141,7 +1180,9 @@ impl App {
|
||||||
self.queue.clear();
|
self.queue.clear();
|
||||||
self.current = None;
|
self.current = None;
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
self.stop_playback();
|
if !self.on_radio {
|
||||||
|
self.stop_playback();
|
||||||
|
}
|
||||||
self.states[View::Queue.idx()].select(None);
|
self.states[View::Queue.idx()].select(None);
|
||||||
self.info("Queue cleared");
|
self.info("Queue cleared");
|
||||||
}
|
}
|
||||||
|
|
@ -1154,6 +1195,7 @@ impl App {
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
let active = self.play_state != PlayState::Stopped;
|
let active = self.play_state != PlayState::Stopped;
|
||||||
match self.current {
|
match self.current {
|
||||||
|
Some(c) if c == i && self.on_radio => self.current = i.checked_sub(1),
|
||||||
Some(c) if c == i => {
|
Some(c) if c == i => {
|
||||||
if self.queue.is_empty() {
|
if self.queue.is_empty() {
|
||||||
self.current = None;
|
self.current = None;
|
||||||
|
|
@ -1266,6 +1308,7 @@ impl App {
|
||||||
self.queue = tracks;
|
self.queue = tracks;
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
self.current = None;
|
self.current = None;
|
||||||
|
self.result_cursor = None;
|
||||||
self.states[View::Queue.idx()].select(Some(0));
|
self.states[View::Queue.idx()].select(Some(0));
|
||||||
self.play_index(0);
|
self.play_index(0);
|
||||||
self.info(format!("Playing playlist \u{201c}{name}\u{201d} ({n} tracks)"));
|
self.info(format!("Playing playlist \u{201c}{name}\u{201d} ({n} tracks)"));
|
||||||
|
|
@ -1299,6 +1342,7 @@ impl App {
|
||||||
self.queue = tracks;
|
self.queue = tracks;
|
||||||
self.played.clear();
|
self.played.clear();
|
||||||
self.current = None;
|
self.current = None;
|
||||||
|
self.result_cursor = None;
|
||||||
self.states[View::Queue.idx()].select(Some(i));
|
self.states[View::Queue.idx()].select(Some(i));
|
||||||
self.play_index(i);
|
self.play_index(i);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ fn draw_tracks(f: &mut Frame, app: &mut App, area: Rect, view: View) {
|
||||||
|
|
||||||
if tracks.is_empty() {
|
if tracks.is_empty() {
|
||||||
let hint: &[&str] = if view == View::Queue {
|
let hint: &[&str] = if view == View::Queue {
|
||||||
&["", "The queue is empty.", "Press Enter on a search result, or `a` to add it."]
|
&["", "The queue is empty.", "Press `a` on a search result to queue it."]
|
||||||
} else {
|
} else {
|
||||||
&["", "No results yet.", "Press / and type a query, or use :search \"daft punk\""]
|
&["", "No results yet.", "Press / and type a query, or use :search \"daft punk\""]
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue