mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
added out_queue
function, rudamentary listenbrainz scrobbling and other small changes
This commit is contained in:
parent
a75081d4fc
commit
ab529f4c9b
6 changed files with 244 additions and 54 deletions
|
@ -37,3 +37,4 @@ serde_json = "1.0.111"
|
||||||
deunicode = "1.4.2"
|
deunicode = "1.4.2"
|
||||||
opener = { version = "0.7.0", features = ["reveal"] }
|
opener = { version = "0.7.0", features = ["reveal"] }
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
|
listenbrainz = "0.7.0"
|
||||||
|
|
|
@ -90,11 +90,18 @@ impl ConfigLibraries {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ConfigConnections {
|
||||||
|
pub listenbrainz_token: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub backup_folder: Option<PathBuf>,
|
pub backup_folder: Option<PathBuf>,
|
||||||
pub libraries: ConfigLibraries,
|
pub libraries: ConfigLibraries,
|
||||||
pub volume: f32,
|
pub volume: f32,
|
||||||
|
pub connections: ConfigConnections,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -212,10 +219,10 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test3() {
|
fn test3() {
|
||||||
let config = Config::read_file(PathBuf::from("test-config/config_test.json")).unwrap();
|
let (config, lib) = read_config_lib();
|
||||||
let uuid = config.libraries.get_default().unwrap().uuid;
|
|
||||||
let lib = MusicLibrary::init(Arc::new(RwLock::from(config.clone())), uuid).unwrap();
|
|
||||||
|
|
||||||
dbg!(lib);
|
_ = config.write_file();
|
||||||
|
|
||||||
|
dbg!(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,95 @@
|
||||||
use super::controller::Controller;
|
use std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
error::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
use listenbrainz::ListenBrainz;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::config::Config, music_controller::controller::{Controller, QueueCmd, QueueResponse}, music_storage::library::{MusicLibrary, Song, Tag}
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::controller::DatabaseResponse;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Controller {
|
impl Controller {
|
||||||
//more stuff goes here
|
pub fn listenbrainz_authenticate(&mut self) -> Result<ListenBrainz, Box<dyn Error>> {
|
||||||
|
let config = &self.config.read().unwrap();
|
||||||
|
let mut client = ListenBrainz::new();
|
||||||
|
|
||||||
|
let lbz_token = match &config.connections.listenbrainz_token {
|
||||||
|
Some(token) => token,
|
||||||
|
None => todo!("No ListenBrainz token in config")
|
||||||
|
};
|
||||||
|
|
||||||
|
if !client.is_authenticated() {
|
||||||
|
client.authenticate(lbz_token)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
pub fn lbz_scrobble(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box<dyn Error>> {
|
||||||
|
let config = &self.config.read().unwrap();
|
||||||
|
|
||||||
|
&self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid));
|
||||||
|
let res = &self.db_mail.recv()?;
|
||||||
|
let song = match res {
|
||||||
|
DatabaseResponse::Song(song) => song,
|
||||||
|
_ => todo!()
|
||||||
|
};
|
||||||
|
let unknown = &"unknown".to_string();
|
||||||
|
let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown);
|
||||||
|
let track = song.get_tag(&Tag::Title).unwrap_or(unknown);
|
||||||
|
let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str());
|
||||||
|
|
||||||
|
client.listen(artist, track, release)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lbz_now_playing(&self, client: ListenBrainz, uuid: Uuid) -> Result<(), Box<dyn Error>> {
|
||||||
|
let config = &self.config.read().unwrap();
|
||||||
|
|
||||||
|
&self.db_mail.send(super::controller::DatabaseCmd::QueryUuid(uuid));
|
||||||
|
let res = &self.db_mail.recv()?;
|
||||||
|
let song = match res {
|
||||||
|
DatabaseResponse::Song(song) => song,
|
||||||
|
_ => todo!()
|
||||||
|
};
|
||||||
|
let unknown = &"unknown".to_string();
|
||||||
|
let artist = song.get_tag(&Tag::Artist).unwrap_or(unknown);
|
||||||
|
let track = song.get_tag(&Tag::Title).unwrap_or(unknown);
|
||||||
|
let release = song.get_tag(&Tag::Album).map(|rel| rel.as_str());
|
||||||
|
|
||||||
|
client.listen(artist, track, release)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_super {
|
||||||
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::config::config::tests::read_config_lib;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn listenbrainz() {
|
||||||
|
let mut c = Controller::start(".\\test-config\\config_test.json").unwrap();
|
||||||
|
|
||||||
|
let client = c.listenbrainz_authenticate().unwrap();
|
||||||
|
|
||||||
|
c.q_new().unwrap();
|
||||||
|
c.queue_mail[0].send(QueueCmd::SetVolume(0.04)).unwrap();
|
||||||
|
|
||||||
|
let songs = c.lib_get_songs();
|
||||||
|
|
||||||
|
c.q_enqueue(0, songs[1].location.to_owned()).unwrap();
|
||||||
|
c.q_play(0).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
sleep(Duration::from_secs(100));
|
||||||
|
c.lbz_scrobble(client, songs[1].uuid).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ use std::path::PathBuf;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use crossbeam_channel::{Sender, Receiver};
|
use crossbeam_channel::{Sender, Receiver};
|
||||||
use crossbeam_channel;
|
use crossbeam_channel;
|
||||||
|
use listenbrainz::ListenBrainz;
|
||||||
use std::thread::spawn;
|
use std::thread::spawn;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::music_controller::queue::{QueueItem, QueueItemType};
|
||||||
use crate::music_storage::library::{Tag, URI};
|
use crate::music_storage::library::{Tag, URI};
|
||||||
use crate::{
|
use crate::{
|
||||||
music_storage::library::{MusicLibrary, Song},
|
music_storage::library::{MusicLibrary, Song},
|
||||||
|
@ -21,27 +23,27 @@ use crate::{
|
||||||
|
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
// queues: Vec<Queue>,
|
// queues: Vec<Queue>,
|
||||||
config: Arc<RwLock<Config>>,
|
pub config: Arc<RwLock<Config>>,
|
||||||
// library: MusicLibrary,
|
// library: MusicLibrary,
|
||||||
controller_mail: MailMan<ControllerCmd, ControllerResponse>,
|
pub(super) controller_mail: MailMan<ControllerCmd, ControllerResponse>,
|
||||||
db_mail: MailMan<DatabaseCmd, DatabaseResponse>,
|
pub(super) db_mail: MailMan<DatabaseCmd, DatabaseResponse>,
|
||||||
queue_mail: Vec<MailMan<QueueCmd, QueueResponse>>,
|
pub(super) queue_mail: Vec<MailMan<QueueCmd, QueueResponse>>,
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ControllerCmd {
|
pub(super) enum ControllerCmd {
|
||||||
Default,
|
Default,
|
||||||
Test
|
Test
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ControllerResponse {
|
pub(super) enum ControllerResponse {
|
||||||
Empty,
|
Empty,
|
||||||
QueueMailMan(MailMan<QueueCmd, QueueResponse>),
|
QueueMailMan(MailMan<QueueCmd, QueueResponse>),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DatabaseCmd {
|
pub(super) enum DatabaseCmd {
|
||||||
Default,
|
Default,
|
||||||
Test,
|
Test,
|
||||||
SaveLibrary,
|
SaveLibrary,
|
||||||
|
@ -49,11 +51,10 @@ pub enum DatabaseCmd {
|
||||||
QueryUuid(Uuid),
|
QueryUuid(Uuid),
|
||||||
QueryUuids(Vec<Uuid>),
|
QueryUuids(Vec<Uuid>),
|
||||||
ReadFolder(String),
|
ReadFolder(String),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum DatabaseResponse {
|
pub(super) enum DatabaseResponse {
|
||||||
Empty,
|
Empty,
|
||||||
Song(Song),
|
Song(Song),
|
||||||
Songs(Vec<Song>),
|
Songs(Vec<Song>),
|
||||||
|
@ -61,7 +62,7 @@ enum DatabaseResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum QueueCmd {
|
pub(super) enum QueueCmd {
|
||||||
Default,
|
Default,
|
||||||
Test,
|
Test,
|
||||||
Play,
|
Play,
|
||||||
|
@ -73,25 +74,26 @@ enum QueueCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum QueueResponse {
|
pub(super) enum QueueResponse {
|
||||||
Default,
|
Default,
|
||||||
Test,
|
Test,
|
||||||
Index(i32),
|
Index(i32),
|
||||||
|
Uuid(Uuid),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MailMan<T, U> {
|
pub(super) struct MailMan<T: Send, U: Send> {
|
||||||
pub tx: Sender<T>,
|
pub tx: Sender<T>,
|
||||||
rx: Receiver<U>
|
rx: Receiver<U>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MailMan<T, T> {
|
impl<T: Send> MailMan<T, T> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let (tx, rx) = unbounded::<T>();
|
let (tx, rx) = unbounded::<T>();
|
||||||
MailMan { tx, rx }
|
MailMan { tx, rx }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, U> MailMan<T, U> {
|
impl<T: Send, U: Send> MailMan<T, U> {
|
||||||
pub fn double() -> (MailMan<T, U>, MailMan<U, T>) {
|
pub fn double() -> (MailMan<T, U>, MailMan<U, T>) {
|
||||||
let (tx, rx) = unbounded::<T>();
|
let (tx, rx) = unbounded::<T>();
|
||||||
let (tx1, rx1) = unbounded::<U>();
|
let (tx1, rx1) = unbounded::<U>();
|
||||||
|
@ -108,14 +110,16 @@ impl<T, U> MailMan<T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv(&self) -> Result<U, Box<dyn Error>> {
|
pub fn recv(&self) -> Result<U, Box<dyn Error>> {
|
||||||
let u = self.rx.recv().unwrap();
|
let u = self.rx.recv()?;
|
||||||
Ok(u)
|
Ok(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
impl Controller {
|
impl Controller {
|
||||||
pub fn start(config_path: String) -> Result<Self, Box<dyn Error>> {
|
pub fn start<P>(config_path: P) -> Result<Self, Box<dyn Error>>
|
||||||
|
where std::path::PathBuf: std::convert::From<P>
|
||||||
|
{
|
||||||
let config_path = PathBuf::from(config_path);
|
let config_path = PathBuf::from(config_path);
|
||||||
let config = Config::read_file(config_path)?;
|
let config = Config::read_file(config_path)?;
|
||||||
let uuid = config.libraries.get_default()?.uuid;
|
let uuid = config.libraries.get_default()?.uuid;
|
||||||
|
@ -185,7 +189,6 @@ impl Controller {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
Controller {
|
Controller {
|
||||||
// queues: Vec::new(),
|
// queues: Vec::new(),
|
||||||
|
@ -197,7 +200,7 @@ impl Controller {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lib_get_songs(&self) -> Vec<Song> {
|
pub fn lib_get_songs(&self) -> Vec<Song> {
|
||||||
self.db_mail.send(DatabaseCmd::GetSongs);
|
self.db_mail.send(DatabaseCmd::GetSongs);
|
||||||
match self.db_mail.recv().unwrap() {
|
match self.db_mail.recv().unwrap() {
|
||||||
DatabaseResponse::Songs(songs) => songs,
|
DatabaseResponse::Songs(songs) => songs,
|
||||||
|
@ -205,7 +208,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lib_scan_folder(&self, folder: String) -> Result<(), Box<dyn Error>> {
|
pub fn lib_scan_folder(&self, folder: String) -> Result<(), Box<dyn Error>> {
|
||||||
let mail = &self.db_mail;
|
let mail = &self.db_mail;
|
||||||
mail.send(DatabaseCmd::ReadFolder(folder))?;
|
mail.send(DatabaseCmd::ReadFolder(folder))?;
|
||||||
dbg!(mail.recv()?);
|
dbg!(mail.recv()?);
|
||||||
|
@ -259,14 +262,14 @@ impl Controller {
|
||||||
Ok(self.queue_mail.len() - 1)
|
Ok(self.queue_mail.len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn q_play(&self, index: usize) -> Result<(), Box<dyn Error>> {
|
pub fn q_play(&self, index: usize) -> Result<(), Box<dyn Error>> {
|
||||||
let mail = &self.queue_mail[index];
|
let mail = &self.queue_mail[index];
|
||||||
mail.send(QueueCmd::Play)?;
|
mail.send(QueueCmd::Play)?;
|
||||||
dbg!(mail.recv()?);
|
dbg!(mail.recv()?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn q_pause(&self, index: usize) -> Result<(), Box<dyn Error>> {
|
pub fn q_pause(&self, index: usize) -> Result<(), Box<dyn Error>> {
|
||||||
let mail = &self.queue_mail[index];
|
let mail = &self.queue_mail[index];
|
||||||
mail.send(QueueCmd::Pause)?;
|
mail.send(QueueCmd::Pause)?;
|
||||||
dbg!(mail.recv()?);
|
dbg!(mail.recv()?);
|
||||||
|
@ -286,7 +289,7 @@ impl Controller {
|
||||||
// Ok(())
|
// Ok(())
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn q_enqueue(&self, index: usize, uri: URI) -> Result<(), Box<dyn Error>> {
|
pub fn q_enqueue(&self, index: usize, uri: URI) -> Result<(), Box<dyn Error>> {
|
||||||
let mail = &self.queue_mail[index];
|
let mail = &self.queue_mail[index];
|
||||||
mail.send(QueueCmd::Enqueue(uri))?;
|
mail.send(QueueCmd::Enqueue(uri))?;
|
||||||
// dbg!(mail.recv()?);
|
// dbg!(mail.recv()?);
|
||||||
|
|
|
@ -89,10 +89,10 @@ pub enum PlayerLocation {
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct QueueItem<'a> {
|
pub struct QueueItem<'a> {
|
||||||
item: QueueItemType<'a>,
|
pub(super) item: QueueItemType<'a>,
|
||||||
state: QueueState,
|
pub(super) state: QueueState,
|
||||||
source: PlayerLocation,
|
pub(super) source: PlayerLocation,
|
||||||
by_human: bool
|
pub(super) by_human: bool
|
||||||
}
|
}
|
||||||
impl QueueItem<'_> {
|
impl QueueItem<'_> {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
use std::{fs::File, io::Read, path:: PathBuf, sync::{Arc, RwLock}};
|
use std::{fs::File, io::Read, path:: PathBuf, sync::{Arc, RwLock}};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use chrono::Duration;
|
use std::time::Duration;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use super::library::{AlbumArt, MusicLibrary, Song, Tag, URI};
|
use super::library::{AlbumArt, MusicLibrary, Song, Tag, URI};
|
||||||
|
|
||||||
use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment, Playlist as List2};
|
use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment, Playlist as List2};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum SortOrder {
|
pub enum SortOrder {
|
||||||
Manual,
|
Manual,
|
||||||
Tag(Tag)
|
Tag(Vec<Tag>)
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Playlist {
|
pub struct Playlist {
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
title: String,
|
title: String,
|
||||||
|
@ -29,9 +32,24 @@ impl Playlist {
|
||||||
pub fn play_count(&self) -> i32 {
|
pub fn play_count(&self) -> i32 {
|
||||||
self.play_count
|
self.play_count
|
||||||
}
|
}
|
||||||
pub fn play_time(&self) -> chrono::Duration {
|
pub fn play_time(&self) -> Duration {
|
||||||
self.play_time
|
self.play_time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn title(&self) -> &String {
|
||||||
|
&self.title
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cover(&self) -> Option<&AlbumArt> {
|
||||||
|
match &self.cover {
|
||||||
|
Some(e) => Some(e),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tracks(&self) -> Vec<Uuid> {
|
||||||
|
self.tracks.to_owned()
|
||||||
|
}
|
||||||
pub fn set_tracks(&mut self, tracks: Vec<Uuid>) {
|
pub fn set_tracks(&mut self, tracks: Vec<Uuid>) {
|
||||||
self.tracks = tracks;
|
self.tracks = tracks;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +93,15 @@ impl Playlist {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_file(&self, path: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
super::utils::write_file(self, PathBuf::from(path))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(path: &str) -> Result<Playlist, Box<dyn Error>> {
|
||||||
|
super::utils::read_file(PathBuf::from(path))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_m3u8(&mut self, lib: Arc<RwLock<MusicLibrary>>, location: &str) -> Result<(), Box<dyn Error>> {
|
pub fn to_m3u8(&mut self, lib: Arc<RwLock<MusicLibrary>>, location: &str) -> Result<(), Box<dyn Error>> {
|
||||||
let lib = lib.read().unwrap();
|
let lib = lib.read().unwrap();
|
||||||
let seg = self.tracks
|
let seg = self.tracks
|
||||||
|
@ -174,20 +201,70 @@ impl Playlist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn title(&self) -> &String {
|
|
||||||
&self.title
|
|
||||||
}
|
pub fn out_tracks(&self, lib: Arc<RwLock<MusicLibrary>>) -> (Vec<Song>, Vec<&Uuid>) {
|
||||||
fn cover(&self) -> Option<&AlbumArt> {
|
let lib = lib.read().unwrap();
|
||||||
match &self.cover {
|
let mut songs = vec![];
|
||||||
Some(e) => Some(e),
|
let mut invalid_uuids = vec![];
|
||||||
None => None,
|
|
||||||
}
|
for uuid in &self.tracks {
|
||||||
}
|
if let Some((track, _)) = lib.query_uuid(uuid) {
|
||||||
fn tracks(&self) -> Vec<Uuid> {
|
songs.push(track.to_owned());
|
||||||
self.tracks.to_owned()
|
}else {
|
||||||
|
invalid_uuids.push(uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let SortOrder::Tag(sort_by) = &self.sort_order {
|
||||||
|
println!("sorting by: {:?}", sort_by);
|
||||||
|
|
||||||
|
songs.par_sort_by(|a, b| {
|
||||||
|
for (i, sort_option) in sort_by.iter().enumerate() {
|
||||||
|
dbg!(&i);
|
||||||
|
let tag_a = match sort_option {
|
||||||
|
Tag::Field(field_selection) => match a.get_field(field_selection.as_str()) {
|
||||||
|
Some(field_value) => field_value.to_string(),
|
||||||
|
None => continue,
|
||||||
|
},
|
||||||
|
_ => match a.get_tag(sort_option) {
|
||||||
|
Some(tag_value) => tag_value.to_owned(),
|
||||||
|
None => continue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let tag_b = match sort_option {
|
||||||
|
Tag::Field(field_selection) => match b.get_field(field_selection) {
|
||||||
|
Some(field_value) => field_value.to_string(),
|
||||||
|
None => continue,
|
||||||
|
},
|
||||||
|
_ => match b.get_tag(sort_option) {
|
||||||
|
Some(tag_value) => tag_value.to_owned(),
|
||||||
|
None => continue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
dbg!(&i);
|
||||||
|
|
||||||
|
if let (Ok(num_a), Ok(num_b)) = (tag_a.parse::<i32>(), tag_b.parse::<i32>()) {
|
||||||
|
// If parsing succeeds, compare as numbers
|
||||||
|
return dbg!(num_a.cmp(&num_b));
|
||||||
|
} else {
|
||||||
|
// If parsing fails, compare as strings
|
||||||
|
return dbg!(tag_a.cmp(&tag_b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all tags are equal, sort by Track number
|
||||||
|
let path_a = PathBuf::from(a.get_field("location").unwrap().to_string());
|
||||||
|
let path_b = PathBuf::from(b.get_field("location").unwrap().to_string());
|
||||||
|
|
||||||
|
path_a.file_name().cmp(&path_b.file_name())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
(songs, invalid_uuids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Default for Playlist {
|
impl Default for Playlist {
|
||||||
|
@ -199,7 +276,7 @@ impl Default for Playlist {
|
||||||
tracks: Vec::default(),
|
tracks: Vec::default(),
|
||||||
sort_order: SortOrder::Manual,
|
sort_order: SortOrder::Manual,
|
||||||
play_count: 0,
|
play_count: 0,
|
||||||
play_time: Duration::zero(),
|
play_time: Duration::from_millis(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +284,7 @@ impl Default for Playlist {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_super {
|
mod test_super {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::config::config::tests::read_config_lib;
|
use crate::{config::config::tests::read_config_lib, music_storage::playlist};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_to_m3u8() {
|
fn list_to_m3u8() {
|
||||||
|
@ -219,11 +296,24 @@ mod test_super {
|
||||||
_ = playlist.to_m3u8(Arc::new(RwLock::from(lib)), ".\\test-config\\playlists\\playlist.m3u8");
|
_ = playlist.to_m3u8(Arc::new(RwLock::from(lib)), ".\\test-config\\playlists\\playlist.m3u8");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn m3u8_to_list() {
|
fn m3u8_to_list() -> Playlist {
|
||||||
let (_, lib) = read_config_lib();
|
let (_, lib) = read_config_lib();
|
||||||
let arc = Arc::new(RwLock::from(lib));
|
let arc = Arc::new(RwLock::from(lib));
|
||||||
let playlist = Playlist::from_m3u8(".\\test-config\\playlists\\playlist.m3u8", arc).unwrap();
|
let playlist = Playlist::from_m3u8(".\\test-config\\playlists\\playlist.m3u8", arc).unwrap();
|
||||||
dbg!(playlist);
|
|
||||||
|
playlist.to_file(".\\test-config\\playlists\\playlist");
|
||||||
|
dbg!(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn out_queue_sort() {
|
||||||
|
let (_, lib) = read_config_lib();
|
||||||
|
let mut list = m3u8_to_list();
|
||||||
|
list.sort_order = SortOrder::Tag(vec![Tag::Album]);
|
||||||
|
|
||||||
|
let songs = &list.out_tracks(Arc::new(RwLock::from(lib)));
|
||||||
|
|
||||||
|
dbg!(songs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue