SongMeta implemented

This commit is contained in:
Justine
2024-08-11 15:58:46 +02:00
parent 44902c452c
commit 194cfdff0d
7 changed files with 706 additions and 43 deletions

0
src/audioplayer.rs Normal file
View File

View File

@ -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
View 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
View 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
View 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;
}
}
}