SongMeta implemented
This commit is contained in:
0
src/audioplayer.rs
Normal file
0
src/audioplayer.rs
Normal file
51
src/main.rs
51
src/main.rs
@ -1,45 +1,10 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use rodio::{Decoder, OutputStream, Sink, Source};
|
||||
pub mod songmeta;
|
||||
use crate::songmeta::songmeta::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SongMeta {
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
duration: Duration,
|
||||
cover_path: Path,
|
||||
path: Path,
|
||||
}
|
||||
|
||||
impl SongMeta {
|
||||
fn new(filepath: Path) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct AudioPlayer {
|
||||
sink: Sink,
|
||||
playlist: Vec<SongMeta>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum PlayerError {
|
||||
IndexNotFound,
|
||||
SomethingElse,
|
||||
}
|
||||
|
||||
|
||||
impl AudioPlayer {
|
||||
fn new() -> Self;
|
||||
fn from_playlist(playlist: Vec<SongMeta>) -> Self;
|
||||
fn add_playlist(&mut self, playlist: Vec<SongMeta>);
|
||||
fn skip_forward(&mut self);
|
||||
fn skip_backwards(&mut self);
|
||||
fn pause(&mut self);
|
||||
fn play(&mut self);
|
||||
fn set_vol(&mut self, vol: f32);
|
||||
fn goto(&mut self; index: usize) -> Result<(), PlayerError>;
|
||||
}
|
||||
fn main() {
|
||||
let mysong = SongMeta::frompath(&String::from("/home/justine/NAS/Musique/A_classer/Bleach/12 Big Cheese.mp3"));
|
||||
dbg!(mysong);
|
||||
|
||||
let mysong2 = SongMeta::frompath(&String::from("/home/justine/NAS/Musique/Folk/Galaverna - Dodsdans/Galaverna - Dodsdans - 07 Smell of ember.flac"));
|
||||
dbg!(mysong2);
|
||||
}
|
||||
31
src/playlist.rs
Normal file
31
src/playlist.rs
Normal file
@ -0,0 +1,31 @@
|
||||
pub mod songmeta;
|
||||
use crate::songmeta::songmeta::*;
|
||||
|
||||
///Stores a list of SongMetas
|
||||
#[derive(Debug, Clone)]
|
||||
struct Playlist {
|
||||
songs: Vec<SongMeta>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum PlaylistError {
|
||||
IndexNotFound,
|
||||
SomethingElse,
|
||||
}
|
||||
|
||||
impl Playlist {
|
||||
///Returns a new empty playlist.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
songs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
///Returns a playlist from a file containing a list of audiofile paths.
|
||||
fn from_file(path: &Path) -> Self;
|
||||
|
||||
///Removes a song at index (if possible)
|
||||
fn pop() -> Result<(), PlayListError>;
|
||||
|
||||
|
||||
}
|
||||
97
src/proto.rs
Normal file
97
src/proto.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use rodio::{Decoder, OutputStream, Sink, Source};
|
||||
|
||||
|
||||
//-----------------------------------------Structs
|
||||
|
||||
|
||||
///Stores a list of SongMetas
|
||||
#[derive(Debug, Clone)]
|
||||
struct Playlist {
|
||||
songs: Vec<SongMeta>,
|
||||
}
|
||||
|
||||
struct AudioPlayer {
|
||||
sink: Sink,
|
||||
playlist: Vec<SongMeta>,
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------Errors
|
||||
#[derive(Debug, Clone)]
|
||||
enum PlayerError {
|
||||
IndexNotFound,
|
||||
SomethingElse,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum PlaylistError {
|
||||
IndexNotFound,
|
||||
SomethingElse,
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------Impls
|
||||
|
||||
|
||||
impl Playlist {
|
||||
///Returns a new empty playlist.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
songs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
///Returns a playlist from a file containing a list of audiofile paths.
|
||||
fn from_file(path: &Path) -> Self;
|
||||
|
||||
///Removes a song at index (if possible)
|
||||
fn pop() -> Result<(), PlayListError>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl AudioPlayer {
|
||||
|
||||
///Returns a new empty player.
|
||||
fn new() -> Self {
|
||||
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
|
||||
let sink = Sink::try_new(&stream_handle).unwrap();
|
||||
let playlist = vec![];
|
||||
|
||||
Self {
|
||||
sink: sink,
|
||||
playlist: playlist,
|
||||
}
|
||||
}
|
||||
|
||||
///Creates a new player from an existing playlist.
|
||||
///Does not consume the playlist.
|
||||
fn from_playlist(playlist: &Playlist) -> Self {
|
||||
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
|
||||
let sink = Sink::try_new(&stream_handle).unwrap();
|
||||
|
||||
Self {
|
||||
sink: sink,
|
||||
playlist: playlist.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///Appends a playlist to the current one, consuming it.
|
||||
fn add_playlist(&mut self, playlist: Vec<SongMeta>) {
|
||||
self.playlist.append(&mut playlist);
|
||||
}
|
||||
|
||||
fn skip_forward(&mut self);
|
||||
fn skip_backwards(&mut self);
|
||||
fn pause(&mut self);
|
||||
fn play(&mut self);
|
||||
fn set_vol(&mut self, vol: f32);
|
||||
fn goto(&mut self, index: usize) -> Result<(), PlayerError>;
|
||||
}
|
||||
|
||||
146
src/songmeta.rs
Normal file
146
src/songmeta.rs
Normal file
@ -0,0 +1,146 @@
|
||||
pub mod songmeta {
|
||||
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use regex::Regex;
|
||||
use std::ffi::OsStr;
|
||||
use metadata::media_file::MediaFileMetadata;
|
||||
use std::time::Duration;
|
||||
|
||||
///Stores metadata about a song.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SongMeta {
|
||||
pub title: String,
|
||||
pub artist: String,
|
||||
pub album: String,
|
||||
pub track: usize,
|
||||
pub duration: Duration,
|
||||
pub cover_path: Option<String>,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SongError {
|
||||
///Given path is not existing or is not a media file.
|
||||
InvalidPath,
|
||||
///Given path is not an audio file
|
||||
NotAudio,
|
||||
}
|
||||
|
||||
impl fmt::Display for SongError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SongError::InvalidPath => write!(f, "Path does not exist or is not a media"),
|
||||
SongError::NotAudio => write!(f, "Path exists but is not an audio file"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SongError {}
|
||||
|
||||
|
||||
impl SongMeta {
|
||||
|
||||
///Returns a SongMeta from the path of an audio file.
|
||||
///Returns an error if need be.
|
||||
///Also looks for the path of the cover, which may be None.
|
||||
pub fn frompath(path: &String) -> Result<Self, Box<dyn Error>> {
|
||||
let fpath = Path::new(path);
|
||||
let md = match MediaFileMetadata::new(&fpath) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return Err(Box::new(SongError::InvalidPath));
|
||||
},
|
||||
};
|
||||
|
||||
//cover
|
||||
let cover_path = match Self::find_cover_path(path.clone()) {
|
||||
Some(v) => Some(v),
|
||||
None => None,
|
||||
};
|
||||
|
||||
//duration
|
||||
let length = md._duration.clone().unwrap_or(0.0) as u64;
|
||||
let duration = Duration::from_secs(length);
|
||||
|
||||
//title
|
||||
let filename = fpath.file_name().unwrap_or(&OsStr::new("Unkown title"));
|
||||
let filename = String::from(filename.to_str().unwrap_or("Unkown"));
|
||||
let title = md.title.clone().unwrap_or(filename.clone());
|
||||
|
||||
//other
|
||||
let mut artist = String::from("Artist unkown");
|
||||
let mut album = String::from("Album unkown");
|
||||
let mut trackorder: usize = 0;
|
||||
let mut track_in_name: usize = 0;
|
||||
|
||||
//Find track order in filename first
|
||||
if let Some(nbr) = Self::find_nbr_in_filename(&filename) {
|
||||
track_in_name = nbr;
|
||||
}
|
||||
|
||||
for i in md.filtered_tags {
|
||||
if i.0.to_lowercase() == "artist" {
|
||||
artist = i.1.clone();
|
||||
}
|
||||
if i.0.to_lowercase() == "album" {
|
||||
album = i.1.clone();
|
||||
}
|
||||
if i.0.to_lowercase() == "track" {
|
||||
trackorder = i.1.clone().parse().unwrap_or(track_in_name);
|
||||
}
|
||||
}
|
||||
|
||||
let val = SongMeta {
|
||||
title: title,
|
||||
artist: artist,
|
||||
album: album,
|
||||
track: trackorder,
|
||||
duration: duration,
|
||||
path: path.clone(),
|
||||
cover_path: cover_path,
|
||||
};
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
|
||||
///Looks for a cover path (cover.jpg or cover.png)
|
||||
fn find_cover_path(path: String) -> Option<String> {
|
||||
let possible_paths = vec!["cover.jpg", "cover.png"];
|
||||
let mut foundpath = String::new();
|
||||
let mut found = false;
|
||||
let mut p = PathBuf::from(&path);
|
||||
if !p.pop() {
|
||||
return None;
|
||||
}
|
||||
|
||||
//Search...
|
||||
for poss in possible_paths {
|
||||
let mut test = p.clone();
|
||||
test.push(Path::new(poss));
|
||||
if test.exists() {
|
||||
foundpath = test.to_string_lossy().into_owned().clone();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
return Some(foundpath);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
///Tries to find a logical track number in the filename,
|
||||
///at the beginning of it.
|
||||
fn find_nbr_in_filename(filename: &String) -> Option<usize> {
|
||||
//"^[0-9]+"
|
||||
let regex = Regex::new(r"(?<tracknbr>^[0-9]+)").unwrap();
|
||||
let cap = regex.captures(filename.as_str());
|
||||
if let Some(found) = cap {
|
||||
return Some(found["tracknbr"].to_string().parse().unwrap_or(0) as usize);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user