mirror of
https://github.com/Dangoware/dmp-core.git
synced 2025-04-19 13:32:53 -05:00
Database works
This commit is contained in:
parent
7f57367aad
commit
db53bed111
5 changed files with 260 additions and 181 deletions
|
@ -16,7 +16,7 @@ pub struct Config {
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let path = PathBuf::from("./music_database.db3");
|
let path = PathBuf::from("./music_database");
|
||||||
|
|
||||||
return Config {
|
return Config {
|
||||||
db_path: Box::new(path),
|
db_path: Box::new(path),
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{RwLock, Arc};
|
use std::sync::{RwLock, Arc};
|
||||||
|
|
||||||
use rusqlite::Result;
|
|
||||||
|
|
||||||
use crate::music_controller::config::Config;
|
use crate::music_controller::config::Config;
|
||||||
use crate::music_player::music_player::{MusicPlayer, PlayerStatus, DecoderMessage, DSPMessage};
|
use crate::music_player::music_player::{MusicPlayer, PlayerStatus, DecoderMessage, DSPMessage};
|
||||||
use crate::music_storage::music_db::Song;
|
use crate::music_storage::music_db::Song;
|
||||||
|
|
|
@ -232,7 +232,7 @@ impl MusicPlayer {
|
||||||
// Handles message received from MusicPlayer struct
|
// Handles message received from MusicPlayer struct
|
||||||
match message {
|
match message {
|
||||||
Some(DecoderMessage::OpenSong(song)) => {
|
Some(DecoderMessage::OpenSong(song)) => {
|
||||||
let song_uri = song.path.clone();
|
let song_uri = song.location.clone();
|
||||||
match SongHandler::new(&song_uri) {
|
match SongHandler::new(&song_uri) {
|
||||||
Ok(new_handler) => {
|
Ok(new_handler) => {
|
||||||
song_handler = Some(new_handler);
|
song_handler = Some(new_handler);
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
use file_format::{FileFormat, Kind};
|
use file_format::{FileFormat, Kind};
|
||||||
use lofty::{AudioFile, Probe, TaggedFileExt, ItemKey, ItemValue, TagType};
|
use lofty::{AudioFile, ItemKey, ItemValue, Probe, TagType, TaggedFileExt};
|
||||||
use std::{error::Error, io::BufReader};
|
use std::{error::Error, io::BufReader};
|
||||||
|
|
||||||
|
use chrono::{serde::ts_seconds_option, DateTime, Utc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use chrono::{DateTime, Utc, serde::ts_seconds_option};
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use std::io::BufWriter;
|
use bincode::{deserialize_from, serialize_into};
|
||||||
use std::fs;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fs;
|
||||||
|
use std::io::BufWriter;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use bincode::{serialize_into, deserialize_from};
|
|
||||||
|
|
||||||
use crate::music_controller::config::Config;
|
use crate::music_controller::config::Config;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct AlbumArt {
|
pub struct AlbumArt {
|
||||||
pub path: Option<URI>;
|
pub index: u16,
|
||||||
|
pub path: Option<URI>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores information about a single song
|
/// Stores information about a single song
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
pub path: URI,
|
pub location: URI,
|
||||||
pub plays: i32,
|
pub plays: i32,
|
||||||
pub skips: i32,
|
pub skips: i32,
|
||||||
pub favorited: bool,
|
pub favorited: bool,
|
||||||
pub rating: u8,
|
pub rating: Option<u8>,
|
||||||
pub format: Option<FileFormat>,
|
pub format: Option<FileFormat>,
|
||||||
pub duration: Duration,
|
pub duration: Duration,
|
||||||
pub play_time: Duration,
|
pub play_time: Duration,
|
||||||
|
@ -33,25 +35,26 @@ pub struct Song {
|
||||||
pub last_played: Option<DateTime<Utc>>,
|
pub last_played: Option<DateTime<Utc>>,
|
||||||
#[serde(with = "ts_seconds_option")]
|
#[serde(with = "ts_seconds_option")]
|
||||||
pub date_added: Option<DateTime<Utc>>,
|
pub date_added: Option<DateTime<Utc>>,
|
||||||
|
pub album_art: Vec<AlbumArt>,
|
||||||
pub tags: Vec<(String, String)>,
|
pub tags: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
pub fn get_tag(&self, target_key: String) -> Option<String> {
|
pub fn get_tag(&self, target_key: &str) -> Option<&String> {
|
||||||
for tag in self.tags {
|
let index = self.tags.iter().position(|r| r.0 == target_key);
|
||||||
if tag.0 == target_key {
|
|
||||||
return Some(tag.1)
|
match index {
|
||||||
}
|
Some(i) => return Some(&self.tags[i].1),
|
||||||
|
None => None,
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tags(&self, target_keys: Vec<String>) -> Vec<Option<String>> {
|
pub fn get_tags(&self, target_keys: &Vec<String>) -> Vec<Option<String>> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for tag in self.tags {
|
for tag in &self.tags {
|
||||||
for key in target_keys {
|
for key in target_keys {
|
||||||
if tag.0 == key {
|
if &tag.0 == key {
|
||||||
results.push(Some(tag.1))
|
results.push(Some(tag.1.to_owned()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.push(None);
|
results.push(None);
|
||||||
|
@ -60,13 +63,14 @@ impl Song {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub enum URI{
|
pub enum URI {
|
||||||
Local(String),
|
Local(String),
|
||||||
|
//Cue(String, Duration), TODO: Make cue stuff work
|
||||||
Remote(Service, String),
|
Remote(Service, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub enum Service {
|
pub enum Service {
|
||||||
InternetRadio,
|
InternetRadio,
|
||||||
Spotify,
|
Spotify,
|
||||||
|
@ -79,142 +83,6 @@ pub struct Playlist {
|
||||||
cover_art: Box<Path>,
|
cover_art: Box<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the database
|
|
||||||
///
|
|
||||||
/// If the database file already exists, return the database, otherwise create it first
|
|
||||||
/// This needs to be run before anything else to retrieve the library vec
|
|
||||||
pub fn init_db(config: &Config) -> Result<Vec<Song>, Box<dyn Error>> {
|
|
||||||
let mut library: Vec<Song> = Vec::new();
|
|
||||||
|
|
||||||
match config.db_path.try_exists() {
|
|
||||||
Ok(_) => {
|
|
||||||
// The database exists, so get it from the file
|
|
||||||
let database = fs::File::open(config.db_path.into_boxed_path())?;
|
|
||||||
let reader = BufReader::new(database);
|
|
||||||
library = deserialize_from(reader)?;
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
// Create the database if it does not exist
|
|
||||||
let mut writer = BufWriter::new(
|
|
||||||
fs::File::create(config.db_path.into_boxed_path())?
|
|
||||||
);
|
|
||||||
serialize_into(&mut writer, &library)?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(library)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path_in_db(query_path: &Path, library: &Vec<Song>) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_all_music(
|
|
||||||
config: &Config,
|
|
||||||
target_path: &str,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
|
|
||||||
let mut current_dir = PathBuf::new();
|
|
||||||
for entry in WalkDir::new(target_path).follow_links(true).into_iter().filter_map(|e| e.ok()) {
|
|
||||||
let target_file = entry;
|
|
||||||
let is_file = fs::metadata(target_file.path())?.is_file();
|
|
||||||
|
|
||||||
// Ensure the target is a file and not a directory, if it isn't, skip this loop
|
|
||||||
if !is_file {
|
|
||||||
current_dir = target_file.into_path();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let format = FileFormat::from_file(target_file.path())?;
|
|
||||||
let extension = target_file
|
|
||||||
.path()
|
|
||||||
.extension()
|
|
||||||
.expect("Could not find file extension");
|
|
||||||
|
|
||||||
// If it's a normal file, add it to the database
|
|
||||||
// if it's a cuesheet, do a bunch of fancy stuff
|
|
||||||
if format.kind() == Kind::Audio {
|
|
||||||
add_file_to_db(target_file.path())
|
|
||||||
} else if extension.to_ascii_lowercase() == "cue" {
|
|
||||||
// TODO: implement cuesheet support
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_file_to_db(target_file: &Path) {
|
|
||||||
// TODO: Fix error handling here
|
|
||||||
let tagged_file = match lofty::read_from_path(target_file) {
|
|
||||||
Ok(tagged_file) => tagged_file,
|
|
||||||
|
|
||||||
Err(_) => match Probe::open(target_file)
|
|
||||||
.expect("ERROR: Bad path provided!")
|
|
||||||
.read() {
|
|
||||||
Ok(tagged_file) => tagged_file,
|
|
||||||
|
|
||||||
Err(_) => return
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure the tags exist, if not, insert blank data
|
|
||||||
let blank_tag = &lofty::Tag::new(TagType::Id3v2);
|
|
||||||
let tag = match tagged_file.primary_tag() {
|
|
||||||
Some(primary_tag) => primary_tag,
|
|
||||||
|
|
||||||
None => match tagged_file.first_tag() {
|
|
||||||
Some(first_tag) => first_tag,
|
|
||||||
None => blank_tag
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut custom_insert = String::new();
|
|
||||||
let mut loops = 0;
|
|
||||||
for (loops, item) in tag.items().enumerate() {
|
|
||||||
let mut custom_key = String::new();
|
|
||||||
match item.key() {
|
|
||||||
ItemKey::TrackArtist |
|
|
||||||
ItemKey::TrackTitle |
|
|
||||||
ItemKey::AlbumTitle |
|
|
||||||
ItemKey::Genre |
|
|
||||||
ItemKey::TrackNumber |
|
|
||||||
ItemKey::Year |
|
|
||||||
ItemKey::RecordingDate => continue,
|
|
||||||
ItemKey::Unknown(unknown) => custom_key.push_str(&unknown),
|
|
||||||
custom => custom_key.push_str(&format!("{:?}", custom))
|
|
||||||
// TODO: This is kind of cursed, maybe fix?
|
|
||||||
};
|
|
||||||
|
|
||||||
let custom_value = match item.value() {
|
|
||||||
ItemValue::Text(value) => value,
|
|
||||||
ItemValue::Locator(value) => value,
|
|
||||||
ItemValue::Binary(_) => ""
|
|
||||||
};
|
|
||||||
|
|
||||||
if loops > 0 {
|
|
||||||
custom_insert.push_str(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the format as a string
|
|
||||||
let short_format: Option<String> = match FileFormat::from_file(target_file) {
|
|
||||||
Ok(fmt) => Some(fmt.to_string()),
|
|
||||||
Err(_) => None
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{}", short_format.as_ref().unwrap());
|
|
||||||
|
|
||||||
let duration = tagged_file.properties().duration().as_secs().to_string();
|
|
||||||
|
|
||||||
// TODO: Fix error handling
|
|
||||||
let binding = fs::canonicalize(target_file).unwrap();
|
|
||||||
let abs_path = binding.to_str().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_song_to_db(new_song: Song) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MusicObject {
|
pub enum MusicObject {
|
||||||
Song(Song),
|
Song(Song),
|
||||||
|
@ -222,20 +90,231 @@ pub enum MusicObject {
|
||||||
Playlist(Playlist),
|
Playlist(Playlist),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MusicObject {
|
#[derive(Debug)]
|
||||||
pub fn as_song(&self) -> Option<&Song> {
|
pub struct MusicLibrary {
|
||||||
match self {
|
pub library: Vec<Song>,
|
||||||
MusicObject::Song(data) => Some(data),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query the database, returning a list of items
|
impl MusicLibrary {
|
||||||
pub fn query (
|
/// Initialize the database
|
||||||
query_string: &String, // The query itself
|
///
|
||||||
target_tags: &Vec<String>, // The tags to search
|
/// If the database file already exists, return the Library, otherwise create
|
||||||
sort_by: &Vec<String>, // Tags to sort the resulting data by
|
/// the database first. This needs to be run before anything else to retrieve
|
||||||
) -> Option<Vec<MusicObject>> {
|
/// the library vec
|
||||||
unimplemented!()
|
pub fn init(config: &Config) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let mut library: Vec<Song> = Vec::new();
|
||||||
|
let mut backup_path = config.db_path.clone();
|
||||||
|
backup_path.set_extension("bkp");
|
||||||
|
|
||||||
|
match config.db_path.try_exists() {
|
||||||
|
Ok(true) => {
|
||||||
|
// The database exists, so get it from the file
|
||||||
|
let database = fs::File::open(config.db_path.to_path_buf())?;
|
||||||
|
let reader = BufReader::new(database);
|
||||||
|
library = deserialize_from(reader)?;
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
// Create the database if it does not exist
|
||||||
|
// possibly from the backup file
|
||||||
|
if backup_path.try_exists().is_ok_and(|x| x == true) {
|
||||||
|
let database = fs::File::open(config.db_path.to_path_buf())?;
|
||||||
|
let reader = BufReader::new(database);
|
||||||
|
library = deserialize_from(reader)?;
|
||||||
|
} else {
|
||||||
|
let mut writer = BufWriter::new(fs::File::create(config.db_path.to_path_buf())?);
|
||||||
|
serialize_into(&mut writer, &library)?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(error) => return Err(error.into())
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { library })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self, config: &Config) -> Result<(), Box<dyn Error>> {
|
||||||
|
match config.db_path.try_exists() {
|
||||||
|
Ok(true) => {
|
||||||
|
// The database exists, rename it to `.bkp` and
|
||||||
|
// write the new database
|
||||||
|
let mut backup_name = config.db_path.clone();
|
||||||
|
backup_name.set_extension("bkp");
|
||||||
|
fs::rename(config.db_path.as_path(), backup_name.as_path())?;
|
||||||
|
|
||||||
|
// TODO: Make this save properly like in config.rs
|
||||||
|
|
||||||
|
let mut writer = BufWriter::new(fs::File::create(config.db_path.to_path_buf())?);
|
||||||
|
serialize_into(&mut writer, &self.library)?;
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
// Create the database if it does not exist
|
||||||
|
let mut writer = BufWriter::new(fs::File::create(config.db_path.to_path_buf())?);
|
||||||
|
serialize_into(&mut writer, &self.library)?;
|
||||||
|
},
|
||||||
|
Err(error) => return Err(error.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_by_uri(&self, path: &URI) -> Option<Song> {
|
||||||
|
for track in &self.library {
|
||||||
|
if path == &track.location {
|
||||||
|
return Some(track.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_all_music(&mut self, target_path: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut current_dir = PathBuf::new();
|
||||||
|
for entry in WalkDir::new(target_path)
|
||||||
|
.follow_links(true)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
{
|
||||||
|
let target_file = entry;
|
||||||
|
let is_file = fs::metadata(target_file.path())?.is_file();
|
||||||
|
|
||||||
|
// Ensure the target is a file and not a directory, if it isn't, skip this loop
|
||||||
|
if !is_file {
|
||||||
|
current_dir = target_file.into_path();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let format = FileFormat::from_file(target_file.path())?;
|
||||||
|
let extension = target_file
|
||||||
|
.path()
|
||||||
|
.extension()
|
||||||
|
.expect("Could not find file extension");
|
||||||
|
|
||||||
|
// If it's a normal file, add it to the database
|
||||||
|
// if it's a cuesheet, do a bunch of fancy stuff
|
||||||
|
if format.kind() == Kind::Audio {
|
||||||
|
match self.add_file_to_db(target_file.path()) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_error) => () //println!("{}, {:?}: {}", format, target_file.file_name(), error)
|
||||||
|
};
|
||||||
|
} else if extension.to_ascii_lowercase() == "cue" {
|
||||||
|
// TODO: implement cuesheet support
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file_to_db(&mut self, target_file: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// TODO: Fix error handling here
|
||||||
|
let tagged_file = match lofty::read_from_path(target_file) {
|
||||||
|
Ok(tagged_file) => tagged_file,
|
||||||
|
|
||||||
|
Err(_) => match Probe::open(target_file)?.read() {
|
||||||
|
Ok(tagged_file) => tagged_file,
|
||||||
|
|
||||||
|
Err(error) => return Err(error.into()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure the tags exist, if not, insert blank data
|
||||||
|
let blank_tag = &lofty::Tag::new(TagType::Id3v2);
|
||||||
|
let tag = match tagged_file.primary_tag() {
|
||||||
|
Some(primary_tag) => primary_tag,
|
||||||
|
|
||||||
|
None => match tagged_file.first_tag() {
|
||||||
|
Some(first_tag) => first_tag,
|
||||||
|
None => blank_tag,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tags: Vec<(String, String)> = Vec::new();
|
||||||
|
for item in tag.items() {
|
||||||
|
let mut key = String::new();
|
||||||
|
match item.key() {
|
||||||
|
ItemKey::Unknown(unknown) => key.push_str(&unknown),
|
||||||
|
custom => key = format!("{:?}", custom),
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = match item.value() {
|
||||||
|
ItemValue::Text(value) => String::from(value),
|
||||||
|
ItemValue::Locator(value) => String::from(value),
|
||||||
|
ItemValue::Binary(_) => String::from(""),
|
||||||
|
};
|
||||||
|
|
||||||
|
tags.push((key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the album artwork information
|
||||||
|
let mut album_art: Vec<AlbumArt> = Vec::new();
|
||||||
|
for (i, _art) in tag.pictures().iter().enumerate() {
|
||||||
|
let new_art = AlbumArt {
|
||||||
|
index: i as u16,
|
||||||
|
path: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
album_art.push(new_art)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the format as a string
|
||||||
|
let format: Option<FileFormat> = match FileFormat::from_file(target_file) {
|
||||||
|
Ok(fmt) => Some(fmt),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = tagged_file.properties().duration();
|
||||||
|
|
||||||
|
// TODO: Fix error handling
|
||||||
|
let binding = fs::canonicalize(target_file).unwrap();
|
||||||
|
let abs_path = binding.to_str().unwrap();
|
||||||
|
|
||||||
|
let new_song = Song {
|
||||||
|
location: URI::Local(abs_path.to_string()),
|
||||||
|
plays: 0,
|
||||||
|
skips: 0,
|
||||||
|
favorited: false,
|
||||||
|
rating: None,
|
||||||
|
format,
|
||||||
|
duration,
|
||||||
|
play_time: Duration::from_secs(0),
|
||||||
|
last_played: None,
|
||||||
|
date_added: Some(chrono::offset::Utc::now()),
|
||||||
|
tags,
|
||||||
|
album_art,
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.add_song_to_db(new_song) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(error) => ()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_song_to_db(&mut self, new_song: Song) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
match self.find_by_uri(&new_song.location) {
|
||||||
|
Some(_) => return Err(format!("URI already in database: {:?}", new_song.location).into()),
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.library.push(new_song);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_song_tags(&mut self, new_tags: Song) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
match self.find_by_uri(&new_tags.location) {
|
||||||
|
Some(_) => (),
|
||||||
|
None => return Err(format!("URI not in database!").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query the database, returning a list of items
|
||||||
|
pub fn query(
|
||||||
|
&self,
|
||||||
|
query_string: &String, // The query itself
|
||||||
|
target_tags: &Vec<String>, // The tags to search
|
||||||
|
sort_by: &Vec<String>, // Tags to sort the resulting data by
|
||||||
|
) -> Option<Vec<MusicObject>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl MusicTracker for LastFM {
|
||||||
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Your time is off.").as_secs() - 30;
|
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Your time is off.").as_secs() - 30;
|
||||||
let string_timestamp = timestamp.to_string();
|
let string_timestamp = timestamp.to_string();
|
||||||
|
|
||||||
let (artist, track) = match (song.artist, song.title) {
|
let (artist, track) = match (song.get_tag("Artist"), song.get_tag("Title")) {
|
||||||
(Some(artist), Some(track)) => (artist, track),
|
(Some(artist), Some(track)) => (artist, track),
|
||||||
_ => return Err(TrackerError::InvalidSong)
|
_ => return Err(TrackerError::InvalidSong)
|
||||||
};
|
};
|
||||||
|
@ -94,7 +94,7 @@ impl MusicTracker for LastFM {
|
||||||
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
||||||
let mut params: BTreeMap<&str, &str> = BTreeMap::new();
|
let mut params: BTreeMap<&str, &str> = BTreeMap::new();
|
||||||
|
|
||||||
let (artist, track) = match (song.artist, song.title) {
|
let (artist, track) = match (song.get_tag("Artist"), song.get_tag("Title")) {
|
||||||
(Some(artist), Some(track)) => (artist, track),
|
(Some(artist), Some(track)) => (artist, track),
|
||||||
_ => return Err(TrackerError::InvalidSong)
|
_ => return Err(TrackerError::InvalidSong)
|
||||||
};
|
};
|
||||||
|
@ -259,11 +259,13 @@ impl DiscordRPC {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl MusicTracker for DiscordRPC {
|
impl MusicTracker for DiscordRPC {
|
||||||
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
||||||
|
let unknown = String::from("Unknown");
|
||||||
|
|
||||||
// Sets song title
|
// Sets song title
|
||||||
let song_name = if let Some(song_name) = song.title {
|
let song_name = if let Some(song_name) = song.get_tag("Title") {
|
||||||
song_name
|
song_name
|
||||||
} else {
|
} else {
|
||||||
String::from("Unknown")
|
&unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
let _client_thread = self.client.start();
|
let _client_thread = self.client.start();
|
||||||
|
@ -316,7 +318,7 @@ pub struct ListenBrainz {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl MusicTracker for ListenBrainz {
|
impl MusicTracker for ListenBrainz {
|
||||||
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
async fn track_now(&mut self, song: Song) -> Result<(), TrackerError> {
|
||||||
let (artist, track) = match (song.artist, song.title) {
|
let (artist, track) = match (song.get_tag("Artist"), song.get_tag("Title")) {
|
||||||
(Some(artist), Some(track)) => (artist, track),
|
(Some(artist), Some(track)) => (artist, track),
|
||||||
_ => return Err(TrackerError::InvalidSong)
|
_ => return Err(TrackerError::InvalidSong)
|
||||||
};
|
};
|
||||||
|
@ -342,7 +344,7 @@ impl MusicTracker for ListenBrainz {
|
||||||
async fn track_song(&mut self, song: Song) -> Result<(), TrackerError> {
|
async fn track_song(&mut self, song: Song) -> Result<(), TrackerError> {
|
||||||
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Your time is off.").as_secs() - 30;
|
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Your time is off.").as_secs() - 30;
|
||||||
|
|
||||||
let (artist, track) = match (song.artist, song.title) {
|
let (artist, track) = match (song.get_tag("Artist"), song.get_tag("Title")) {
|
||||||
(Some(artist), Some(track)) => (artist, track),
|
(Some(artist), Some(track)) => (artist, track),
|
||||||
_ => return Err(TrackerError::InvalidSong)
|
_ => return Err(TrackerError::InvalidSong)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue