Keep playing random tracks after the playlist ends

This commit is contained in:
Elias Projahn 2022-02-08 17:44:21 +01:00
parent 42d1d047e3
commit 487fd0a048
3 changed files with 35 additions and 7 deletions

View file

@ -1,5 +1,6 @@
use crate::{Backend, BackendState, Player, Result}; use crate::{Backend, BackendState, Player, Result};
use gio::prelude::*; use gio::prelude::*;
use glib::clone;
use log::warn; use log::warn;
use musicus_database::Database; use musicus_database::Database;
use std::path::PathBuf; use std::path::PathBuf;
@ -41,10 +42,16 @@ impl Backend {
let mut db_path = path.clone(); let mut db_path = path.clone();
db_path.push("musicus.db"); db_path.push("musicus.db");
let database = Database::new(db_path.to_str().unwrap())?; let database = Rc::new(Database::new(db_path.to_str().unwrap())?);
self.database.replace(Some(Rc::new(database))); self.database.replace(Some(Rc::clone(&database)));
let player = Player::new(path); let player = Player::new(path);
// Keep adding random tracks in case the playlist ends.
player.set_generate_next_track_cb(clone!(@weak database => @default-panic, move || {
database.random_track().unwrap()
}));
self.player.replace(Some(player)); self.player.replace(Some(player));
self.set_state(BackendState::Ready); self.set_state(BackendState::Ready);

View file

@ -17,6 +17,7 @@ pub struct Player {
current_track: Cell<Option<usize>>, current_track: Cell<Option<usize>>,
playing: Cell<bool>, playing: Cell<bool>,
duration: Cell<u64>, duration: Cell<u64>,
generate_next_track_cb: RefCell<Option<Box<dyn Fn() -> Track>>>,
playlist_cbs: RefCell<Vec<Box<dyn Fn(Vec<Track>)>>>, playlist_cbs: RefCell<Vec<Box<dyn Fn(Vec<Track>)>>>,
track_cbs: RefCell<Vec<Box<dyn Fn(usize)>>>, track_cbs: RefCell<Vec<Box<dyn Fn(usize)>>>,
duration_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>, duration_cbs: RefCell<Vec<Box<dyn Fn(u64)>>>,
@ -44,6 +45,7 @@ impl Player {
current_track: Cell::new(None), current_track: Cell::new(None),
playing: Cell::new(false), playing: Cell::new(false),
duration: Cell::new(0), duration: Cell::new(0),
generate_next_track_cb: RefCell::new(None),
playlist_cbs: RefCell::new(Vec::new()), playlist_cbs: RefCell::new(Vec::new()),
track_cbs: RefCell::new(Vec::new()), track_cbs: RefCell::new(Vec::new()),
duration_cbs: RefCell::new(Vec::new()), duration_cbs: RefCell::new(Vec::new()),
@ -144,6 +146,10 @@ impl Player {
result result
} }
pub fn set_generate_next_track_cb<F: Fn() -> Track + 'static>(&self, cb: F) {
self.generate_next_track_cb.replace(Some(Box::new(cb)));
}
pub fn add_playlist_cb<F: Fn(Vec<Track>) + 'static>(&self, cb: F) { pub fn add_playlist_cb<F: Fn(Vec<Track>) + 'static>(&self, cb: F) {
self.playlist_cbs.borrow_mut().push(Box::new(cb)); self.playlist_cbs.borrow_mut().push(Box::new(cb));
} }
@ -270,7 +276,9 @@ impl Player {
} }
pub fn has_next(&self) -> bool { pub fn has_next(&self) -> bool {
if let Some(current_track) = self.current_track.get() { if self.generate_next_track_cb.borrow().is_some() {
true
} else if let Some(current_track) = self.current_track.get() {
let playlist = self.playlist.borrow(); let playlist = self.playlist.borrow();
current_track + 1 < playlist.len() current_track + 1 < playlist.len()
} else { } else {
@ -285,9 +293,11 @@ impl Player {
)) ))
})?; })?;
let playlist = self.playlist.borrow(); if current_track + 1 < self.playlist.borrow().len() {
current_track += 1;
if current_track + 1 < playlist.len() { } else if let Some(cb) = &*self.generate_next_track_cb.borrow() {
let new_track = cb();
self.add_item(new_track)?;
current_track += 1; current_track += 1;
} else { } else {
return Err(Error::Other(String::from("No existing next track."))); return Err(Error::Other(String::from("No existing next track.")));

View file

@ -49,7 +49,7 @@ struct MediumRow {
} }
/// Table data for a [`Track`]. /// Table data for a [`Track`].
#[derive(Insertable, Queryable, Debug, Clone)] #[derive(Insertable, Queryable, QueryableByName, Debug, Clone)]
#[table_name = "tracks"] #[table_name = "tracks"]
struct TrackRow { struct TrackRow {
pub id: String, pub id: String,
@ -224,6 +224,17 @@ impl Database {
Ok(tracks) Ok(tracks)
} }
/// Get a random track from the database.
pub fn random_track(&self) -> Result<Track> {
let row = diesel::sql_query("SELECT * FROM tracks ORDER BY RANDOM() LIMIT 1")
.load::<TrackRow>(&self.connection)?
.into_iter()
.next()
.ok_or(Error::Other("Failed to generate random track"))?;
self.get_track_from_row(row)
}
/// Retrieve all available information on a medium from related tables. /// Retrieve all available information on a medium from related tables.
fn get_medium_data(&self, row: MediumRow) -> Result<Medium> { fn get_medium_data(&self, row: MediumRow) -> Result<Medium> {
let track_rows = tracks::table let track_rows = tracks::table