added path searching and standard message types
This commit is contained in:
parent
7257db9c9a
commit
36530b8b22
5 changed files with 182 additions and 107 deletions
|
@ -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,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
36
src/main.rs
36
src/main.rs
|
@ -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
95
src/message_types.rs
Normal 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,
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue