added path searching and standard message types

This commit is contained in:
Nickiel12 2022-12-21 19:09:59 -08:00
parent 7257db9c9a
commit 36530b8b22
5 changed files with 182 additions and 107 deletions

View file

@ -1,8 +1,7 @@
use derive_more::From; use derive_more::From;
use rusqlite::{params, Connection, Result}; use rusqlite::{params, Connection, Result};
use crate::file_operations::ItemTag; use crate::message_types::{ItemTag, PartialTag};
use crate::server_handling::PartialTag;
/// Catch all Error for database creation errors /// Catch all Error for database creation errors
#[derive(From, Debug)] #[derive(From, Debug)]
@ -16,6 +15,22 @@ pub struct DatabaseRequest {
pub search_tag: PartialTag, pub search_tag: PartialTag,
} }
impl SearchType {
pub fn is_where(self: &Self) -> bool {
match self {
SearchType::Where => true,
SearchType::Like => false,
}
}
pub fn is_like(self: &Self) -> bool {
match self {
SearchType::Where => false,
SearchType::Like => true,
}
}
}
pub enum SearchType { pub enum SearchType {
Where, Where,
Like, Like,
@ -114,38 +129,54 @@ impl DBObject {
let mut conditions = Vec::<String>::new(); let mut conditions = Vec::<String>::new();
let does_have_wild = if request.search_type.is_like() {
("'%", "%'")
} else {
("'", "'")
};
if request.search_tag.has_path() { if request.search_tag.has_path() {
conditions.push(format!( conditions.push(format!(
"path = '{}'", "path = {}{}{}",
request.search_tag.path.clone().unwrap() does_have_wild.0,
request.search_tag.path.clone().unwrap(),
does_have_wild.1,
)); ));
} }
if request.search_tag.has_title() { if request.search_tag.has_title() {
conditions.push(format!( conditions.push(format!(
"title = '{}'", "title = {}{}{}",
request.search_tag.title.clone().unwrap() does_have_wild.0,
request.search_tag.title.clone().unwrap(),
does_have_wild.1,
)); ));
} }
if request.search_tag.has_artist() { if request.search_tag.has_artist() {
conditions.push(format!( conditions.push(format!(
"artist = '{}'", "artist = {}{}{}",
request.search_tag.artist.clone().unwrap() does_have_wild.0,
request.search_tag.artist.clone().unwrap(),
does_have_wild.1,
)); ));
} }
if request.search_tag.has_album() { if request.search_tag.has_album() {
conditions.push(format!( conditions.push(format!(
"album = '{}'", "album = {}{}{}",
request.search_tag.album.clone().unwrap() does_have_wild.0,
request.search_tag.album.clone().unwrap(),
does_have_wild.1,
)); ));
} }
if request.search_tag.has_album_artist() { if request.search_tag.has_album_artist() {
conditions.push(format!( conditions.push(format!(
"album_artist = '{}'", "album_artist = {}{}{}",
request.search_tag.album_artist.clone().unwrap() does_have_wild.0,
request.search_tag.album_artist.clone().unwrap(),
does_have_wild.1,
)); ));
} }

View file

@ -1,8 +1,9 @@
use id3::{Tag, TagLike}; use id3::{Tag, TagLike};
use std::path::PathBuf; use std::path::PathBuf;
use scan_dir::ScanDir; use scan_dir::ScanDir;
use crate::message_types::{ItemTag};
const SUPPORTED_FILETYPES: [&str; 1] = ["mp3"]; const SUPPORTED_FILETYPES: [&str; 1] = ["mp3"];
/// The object that iteratively and recursively scans the directories /// The object that iteratively and recursively scans the directories
@ -77,27 +78,6 @@ impl Iterator for MusicScanner {
} }
} }
/// A struct that defines all the music tags supported by Sousa
#[derive(Debug, Clone)]
pub struct ItemTag {
pub path: String,
pub title: String,
pub artist: String,
pub album: String,
pub album_artist: String,
}
impl ItemTag {
pub fn new() -> Self {
ItemTag {
path: String::new(),
title: String::new(),
artist: String::new(),
album: String::new(),
album_artist: String::new(),
}
}
}
/// Returns the music information from a filepath /// Returns the music information from a filepath
pub fn get_tag(filepath: &PathBuf) -> Result<ItemTag, id3::Error> { pub fn get_tag(filepath: &PathBuf) -> Result<ItemTag, id3::Error> {

View file

@ -1,5 +1,4 @@
use rodio::{Decoder, OutputStream, Sink}; use rodio::{Decoder, OutputStream, Sink};
use server_handling::UIRequest;
use std::io::BufReader; use std::io::BufReader;
use std::net::TcpStream; use std::net::TcpStream;
use std::path::PathBuf; use std::path::PathBuf;
@ -11,11 +10,13 @@ use clap::Parser;
use dirs_next; use dirs_next;
use crate::db_operations::DatabaseRequest; use crate::db_operations::DatabaseRequest;
use crate::server_handling::PartialTag;
pub mod db_operations; pub mod db_operations;
pub mod file_operations; pub mod file_operations;
pub mod server_handling; pub mod server_handling;
pub mod message_types;
use crate::message_types::{PartialTag, ServerResponse, UIRequest};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about=None)] #[command(author, version, about, long_about=None)]
@ -74,7 +75,7 @@ fn main() {
} }
let test_tag = PartialTag { let test_tag = PartialTag {
title: Some("%bees%".to_string()), title: Some("bees".to_string()),
..PartialTag::default() ..PartialTag::default()
}; };
@ -124,7 +125,32 @@ fn main() {
UIRequest::Play => sink.play(), UIRequest::Play => sink.play(),
UIRequest::Pause => sink.pause(), UIRequest::Pause => sink.pause(),
UIRequest::Skip(skip_direction) => todo!(), UIRequest::Skip(skip_direction) => todo!(),
UIRequest::GetList(request) => todo!(), UIRequest::Search(request) => {
println!("got a: {:?}", request);
let items = dbo
.get(&DatabaseRequest {
search_type: db_operations::SearchType::Like,
search_tag: request,
})
.unwrap();
match items {
None => sockets[i].write_message("None".into()).unwrap(),
Some(items) => {
sockets[i]
.write_message(
serde_json::to_string(&ServerResponse {
search_results: items,
})
.unwrap()
.into(),
)
.unwrap();
}
}
//println!("got from db: {:?}", items);
}
UIRequest::SwitchTo(partial_tag) => todo!(), UIRequest::SwitchTo(partial_tag) => todo!(),
UIRequest::GetStatus => todo!(), UIRequest::GetStatus => todo!(),
}, },

95
src/message_types.rs Normal file
View file

@ -0,0 +1,95 @@
use serde::{Deserialize, Serialize};
/// A struct that defines all the music tags supported by Sousa
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ItemTag {
pub path: String,
pub title: String,
pub artist: String,
pub album: String,
pub album_artist: String,
}
impl Default for ItemTag {
fn default() -> Self {
ItemTag {
path: String::new(),
title: String::new(),
artist: String::new(),
album: String::new(),
album_artist: String::new(),
}
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct PartialTag {
pub path: Option<String>,
pub title: Option<String>,
pub artist: Option<String>,
pub album: Option<String>,
pub album_artist: Option<String>,
}
impl Default for PartialTag {
fn default() -> Self {
PartialTag {
path: None,
title: None,
artist: None,
album: None,
album_artist: None,
}
}
}
impl PartialTag {
pub fn has_path(self: &Self) -> bool {
self.path.is_some()
}
pub fn has_title(self: &Self) -> bool {
self.title.is_some()
}
pub fn has_artist(self: &Self) -> bool {
self.artist.is_some()
}
pub fn has_album(self: &Self) -> bool {
self.album.is_some()
}
pub fn has_album_artist(self: &Self) -> bool {
self.album_artist.is_some()
}
pub fn is_empty(self: &Self) -> bool {
return self.path.is_none()
&& self.title.is_none()
&& self.artist.is_none()
&& self.album.is_none()
&& self.album_artist.is_none();
}
}
#[derive(Serialize, Deserialize)]
pub struct ServerResponse {
pub search_results: Vec<ItemTag>,
}
#[derive(Serialize, Deserialize)]
pub enum SkipDirection {
Forward,
Backward,
}
#[derive(Serialize, Deserialize)]
pub enum UIRequest {
Play,
Pause,
Skip(SkipDirection),
Search(PartialTag),
SwitchTo(PartialTag),
GetStatus,
}

View file

@ -1,74 +1,17 @@
use serde::{Deserialize, Serialize}; use crate::message_types::{UIRequest};
#[derive(Deserialize, Serialize)]
pub struct PartialTag {
pub path: Option<String>,
pub title: Option<String>,
pub artist: Option<String>,
pub album: Option<String>,
pub album_artist: Option<String>,
}
impl Default for PartialTag {
fn default() -> Self {
PartialTag {
path: None,
title: None,
artist: None,
album: None,
album_artist: None,
}
}
}
impl PartialTag {
pub fn has_path(self: &Self) -> bool {
self.path.is_some()
}
pub fn has_title(self: &Self) -> bool {
self.title.is_some()
}
pub fn has_artist(self: &Self) -> bool {
self.artist.is_some()
}
pub fn has_album(self: &Self) -> bool {
self.album.is_some()
}
pub fn has_album_artist(self: &Self) -> bool {
self.album_artist.is_some()
}
pub fn is_empty(self: &Self) -> bool {
return self.path.is_none()
&& self.title.is_none()
&& self.artist.is_none()
&& self.album.is_none()
&& self.album_artist.is_none();
}
}
#[derive(Serialize, Deserialize)]
pub enum SkipDirection {
Forward,
Backward,
}
#[derive(Serialize, Deserialize)]
pub enum UIRequest {
Play,
Pause,
Skip(SkipDirection),
GetList(String),
SwitchTo(PartialTag),
GetStatus,
}
/// Pass a
pub fn handle_request(socket_message: String) -> Result<UIRequest, serde_json::Error> { pub fn handle_request(socket_message: String) -> Result<UIRequest, serde_json::Error> {
println!("Recieved a socket message: {}", socket_message); println!("Recieved a socket message: {}", socket_message);
let request: UIRequest = serde_json::from_str(&socket_message)?; let request: UIRequest = serde_json::from_str(&socket_message)?;
Ok(request) Ok(request)
} }
fn sanitize_input(input: UIRequest) -> Result<UIRequest, ()> {
// if UIRequest is a search string, make sure it is
// not empty
// has no %, this is a fuzzy search, This program handles that, maybe replace * with %
// has a type of request (e.g. "title search: value")
Ok(UIRequest::Pause)
}