142 lines
4.3 KiB
Rust
142 lines
4.3 KiB
Rust
pub mod playlist {
|
|
|
|
use std::path::{Path,PathBuf};
|
|
use std::error::Error;
|
|
use std::fmt;
|
|
use std::fs;
|
|
use serde::{Deserialize, Serialize};
|
|
use crate::songmeta::songmeta::*;
|
|
use std::io::Write;
|
|
use rand::thread_rng;
|
|
use rand::prelude::SliceRandom;
|
|
|
|
|
|
|
|
|
|
//----------------ERROR
|
|
|
|
#[derive(Debug)]
|
|
pub enum PlaylistError {
|
|
IndexNotFound,
|
|
FileNotFound,
|
|
FileCantWrite,
|
|
}
|
|
|
|
impl fmt::Display for PlaylistError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
PlaylistError::IndexNotFound => write!(f, "Playlist does not have that index"),
|
|
PlaylistError::FileNotFound => write!(f, "Given playlist file is not readable or does not exist"),
|
|
PlaylistError::FileCantWrite => write!(f, "Can't write playlist file at the given path. Is it valid ?"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Error for PlaylistError {}
|
|
|
|
|
|
//----------------STRUCT
|
|
|
|
///Stores a list of SongMetas to be used with a player.
|
|
///Index starts at zero !
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Playlist {
|
|
pub songs: Vec<SongMeta>,
|
|
}
|
|
|
|
impl Playlist {
|
|
///Returns a new empty playlist.
|
|
pub fn new() -> Self {
|
|
Self {
|
|
songs: vec![],
|
|
}
|
|
}
|
|
|
|
///Returns a playlist from a file.
|
|
///File must be yaml
|
|
pub fn from_file(path: &String) -> Result<Self, Box<dyn Error>> {
|
|
|
|
let p = Path::new(path);
|
|
|
|
//Check existence
|
|
if !p.exists() {
|
|
return Err(Box::new(PlaylistError::FileNotFound));
|
|
}
|
|
|
|
//Read the yaml
|
|
let content = fs::read_to_string(p)?;
|
|
let res: Playlist = serde_yaml::from_str(&content)?;
|
|
return Ok(res);
|
|
}
|
|
|
|
///Writes the playlist with its index to a file with the path p
|
|
///Path must be fully qualified
|
|
pub fn to_file(&self, path: &String) -> Result<(), Box<dyn Error>>{
|
|
let p = Path::new(&path);
|
|
|
|
//Check parent
|
|
let mut parent = PathBuf::from(path);
|
|
let _ = &parent.pop();
|
|
if !&parent.exists() {
|
|
return Err(Box::new(PlaylistError::FileCantWrite));
|
|
}
|
|
|
|
//Serialize and write
|
|
let serialized = serde_yaml::to_string(&self.clone())?;
|
|
let mut file = fs::OpenOptions::new()
|
|
.write(true)
|
|
.append(false)
|
|
.create(true)
|
|
.open(&p)?;
|
|
file.write_all(format!("---\n{}", serialized).as_bytes())?;
|
|
return Ok(());
|
|
}
|
|
|
|
///shortcut to myplaylist.songs.push(SongMeta::frompath(&String::from("/a/file/path")))
|
|
pub fn add_song_from_path(&mut self, path: &String) -> Result<(), Box<dyn Error>> {
|
|
let song = SongMeta::frompath(path)?;
|
|
self.songs.push(song);
|
|
return Ok(());
|
|
}
|
|
|
|
///Checks if a number is a valid index in this playlist.
|
|
pub fn check_index(&self, index: usize) -> bool {
|
|
if self.songs.len() == 0 {
|
|
return false;
|
|
}
|
|
if index > (self.songs.len() - 1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///Removes a song at index (if possible)
|
|
pub fn remove(&mut self, index: usize) -> Result<(), Box<dyn Error>> {
|
|
if !self.check_index(index.clone()) {
|
|
return Err(Box::new(PlaylistError::IndexNotFound));
|
|
}
|
|
self.songs.remove(index);
|
|
return Ok(());
|
|
}
|
|
|
|
///Shuffles the playlist
|
|
pub fn shuffle(&mut self) {
|
|
//Return if playlist is empty
|
|
if self.songs.len() == 0 {
|
|
return;
|
|
}
|
|
self.songs.shuffle(&mut thread_rng());
|
|
}
|
|
|
|
///Sorts the playlist by artist, album, trackorder, title
|
|
pub fn sort(&mut self) {
|
|
self.songs.sort_by(|a, b| {
|
|
a.artist
|
|
.cmp(&b.artist)
|
|
.then_with(|| a.album.cmp(&b.album))
|
|
.then_with(|| a.track.cmp(&b.track))
|
|
.then_with(|| a.title.cmp(&b.title))
|
|
});
|
|
}
|
|
}
|
|
} |