mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 14:13:53 +01:00
update dependencies #1
This commit is contained in:
parent
81ec0d6668
commit
a316f6282e
@ -6,9 +6,9 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
musicdb-lib = { path = "../musicdb-lib" }
|
||||||
clap = { version = "4.4.6", features = ["derive"] }
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
musicdb-lib = { version = "0.1.0", path = "../musicdb-lib" }
|
|
||||||
regex = "1.9.3"
|
regex = "1.9.3"
|
||||||
speedy2d = { version = "1.12.0", optional = true }
|
speedy2d = { version = "1.12.0", optional = true }
|
||||||
toml = "0.7.6"
|
toml = "0.7.6"
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
io::BufRead,
|
io::{BufRead, BufReader, Read, Write},
|
||||||
io::{BufReader, Read, Write},
|
path::{Path, PathBuf},
|
||||||
path::Path,
|
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -12,6 +11,7 @@ pub struct Client<T: Write + Read>(BufReader<T>);
|
|||||||
impl<T: Write + Read> Client<T> {
|
impl<T: Write + Read> Client<T> {
|
||||||
pub fn new(mut con: BufReader<T>) -> std::io::Result<Self> {
|
pub fn new(mut con: BufReader<T>) -> std::io::Result<Self> {
|
||||||
writeln!(con.get_mut(), "get")?;
|
writeln!(con.get_mut(), "get")?;
|
||||||
|
con.get_mut().flush()?;
|
||||||
Ok(Self(con))
|
Ok(Self(con))
|
||||||
}
|
}
|
||||||
pub fn cover_bytes(&mut self, id: CoverId) -> Result<Result<Vec<u8>, String>, std::io::Error> {
|
pub fn cover_bytes(&mut self, id: CoverId) -> Result<Result<Vec<u8>, String>, std::io::Error> {
|
||||||
@ -20,9 +20,9 @@ impl<T: Write + Read> Client<T> {
|
|||||||
"{}",
|
"{}",
|
||||||
con_get_encode_string(&format!("cover-bytes\n{id}"))
|
con_get_encode_string(&format!("cover-bytes\n{id}"))
|
||||||
)?;
|
)?;
|
||||||
|
self.0.get_mut().flush()?;
|
||||||
let mut response = String::new();
|
let mut response = String::new();
|
||||||
self.0.read_line(&mut response)?;
|
self.0.read_line(&mut response)?;
|
||||||
let response = con_get_decode_line(&response);
|
|
||||||
if response.starts_with("len: ") {
|
if response.starts_with("len: ") {
|
||||||
if let Ok(len) = response[4..].trim().parse() {
|
if let Ok(len) = response[4..].trim().parse() {
|
||||||
let mut bytes = vec![0; len];
|
let mut bytes = vec![0; len];
|
||||||
@ -41,9 +41,9 @@ impl<T: Write + Read> Client<T> {
|
|||||||
"{}",
|
"{}",
|
||||||
con_get_encode_string(&format!("song-file\n{id}",))
|
con_get_encode_string(&format!("song-file\n{id}",))
|
||||||
)?;
|
)?;
|
||||||
|
self.0.get_mut().flush()?;
|
||||||
let mut response = String::new();
|
let mut response = String::new();
|
||||||
self.0.read_line(&mut response)?;
|
self.0.read_line(&mut response)?;
|
||||||
let response = con_get_decode_line(&response);
|
|
||||||
if response.starts_with("len: ") {
|
if response.starts_with("len: ") {
|
||||||
if let Ok(len) = response[4..].trim().parse() {
|
if let Ok(len) = response[4..].trim().parse() {
|
||||||
let mut bytes = vec![0; len];
|
let mut bytes = vec![0; len];
|
||||||
@ -62,9 +62,9 @@ impl<T: Write + Read> Client<T> {
|
|||||||
"{}",
|
"{}",
|
||||||
con_get_encode_string(&format!("custom-file\n{path}",))
|
con_get_encode_string(&format!("custom-file\n{path}",))
|
||||||
)?;
|
)?;
|
||||||
|
self.0.get_mut().flush()?;
|
||||||
let mut response = String::new();
|
let mut response = String::new();
|
||||||
self.0.read_line(&mut response)?;
|
self.0.read_line(&mut response)?;
|
||||||
let response = con_get_decode_line(&response);
|
|
||||||
if response.starts_with("len: ") {
|
if response.starts_with("len: ") {
|
||||||
if let Ok(len) = response[4..].trim().parse() {
|
if let Ok(len) = response[4..].trim().parse() {
|
||||||
let mut bytes = vec![0; len];
|
let mut bytes = vec![0; len];
|
||||||
@ -77,15 +77,91 @@ impl<T: Write + Read> Client<T> {
|
|||||||
Ok(Err(response))
|
Ok(Err(response))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn song_file_by_path(
|
||||||
|
&mut self,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Result<Vec<u8>, String>, std::io::Error> {
|
||||||
|
writeln!(
|
||||||
|
self.0.get_mut(),
|
||||||
|
"{}",
|
||||||
|
con_get_encode_string(&format!("song-file-by-path\n{path}",))
|
||||||
|
)?;
|
||||||
|
self.0.get_mut().flush()?;
|
||||||
|
let mut response = String::new();
|
||||||
|
self.0.read_line(&mut response)?;
|
||||||
|
if response.starts_with("len: ") {
|
||||||
|
if let Ok(len) = response[4..].trim().parse() {
|
||||||
|
let mut bytes = vec![0; len];
|
||||||
|
self.0.read_exact(&mut bytes)?;
|
||||||
|
Ok(Ok(bytes))
|
||||||
|
} else {
|
||||||
|
Ok(Err(response))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Err(response))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// tell the server to search for files that are not in its song database.
|
||||||
|
///
|
||||||
|
/// ## `extensions`:
|
||||||
|
/// If `None`, the server uses a default set of music-related extensions (`[".mp3", ...]`).
|
||||||
|
/// If `Some([])`, allow all extensions, even ones like `.jpg` and files without extensions.
|
||||||
|
/// If `Some(...)`, only allow the specified extensions. Note: These are actually suffixes, for example `mp3` would allow a file named `test_mp3`, while `.mp3` would only allow `test.mp3`.
|
||||||
|
/// Because of this, you usually want to include the `.` before the extension, and double extensions like `.tar.gz` are also supported.
|
||||||
|
pub fn find_unused_song_files(
|
||||||
|
&mut self,
|
||||||
|
extensions: Option<&[&str]>,
|
||||||
|
) -> Result<Result<Vec<(String, bool)>, String>, std::io::Error> {
|
||||||
|
let mut str = "find-unused-song-files".to_owned();
|
||||||
|
if let Some(extensions) = extensions {
|
||||||
|
if extensions.is_empty() {
|
||||||
|
str.push_str("\nextensions");
|
||||||
|
} else {
|
||||||
|
str.push_str("\nextensions=");
|
||||||
|
for (i, ext) in extensions.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
str.push(':');
|
||||||
|
}
|
||||||
|
str.push_str(ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(self.0.get_mut(), "{}", con_get_encode_string(&str))?;
|
||||||
|
self.0.get_mut().flush()?;
|
||||||
|
let mut response = String::new();
|
||||||
|
self.0.read_line(&mut response)?;
|
||||||
|
let len_line = response.trim();
|
||||||
|
if len_line.starts_with("len: ") {
|
||||||
|
if let Ok(len) = len_line[4..].trim().parse() {
|
||||||
|
let mut out = Vec::with_capacity(len);
|
||||||
|
for _ in 0..len {
|
||||||
|
let mut line = String::new();
|
||||||
|
self.0.read_line(&mut line)?;
|
||||||
|
let line = line.trim_end_matches(['\n', '\r']);
|
||||||
|
if line.starts_with('#') {
|
||||||
|
out.push((line[1..].to_owned(), false))
|
||||||
|
} else if line.starts_with('!') {
|
||||||
|
out.push((line[1..].to_owned(), true))
|
||||||
|
} else {
|
||||||
|
return Ok(Err(format!("bad line-format: {line}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Ok(out))
|
||||||
|
} else {
|
||||||
|
Ok(Err(format!("bad len in len-line: {len_line}")))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(Err(format!("bad len-line: {len_line}")))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_one_connection_as_get(
|
pub fn handle_one_connection_as_get(
|
||||||
db: Arc<Mutex<Database>>,
|
db: Arc<Mutex<Database>>,
|
||||||
connection: &mut BufReader<impl Read + Write>,
|
connection: &mut BufReader<impl Read + Write>,
|
||||||
) -> Result<(), std::io::Error> {
|
) -> Result<(), std::io::Error> {
|
||||||
let mut line = String::new();
|
|
||||||
loop {
|
loop {
|
||||||
line.clear();
|
let mut line = String::new();
|
||||||
if connection.read_line(&mut line).is_ok() {
|
if connection.read_line(&mut line).is_ok() {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -145,7 +221,12 @@ pub fn handle_one_connection_as_get(
|
|||||||
parent = None;
|
parent = None;
|
||||||
}
|
}
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
fs::read(parent.join(path)).ok()
|
let path = parent.join(path);
|
||||||
|
if path.starts_with(parent) {
|
||||||
|
fs::read(path).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -156,6 +237,78 @@ pub fn handle_one_connection_as_get(
|
|||||||
writeln!(connection.get_mut(), "no data")?;
|
writeln!(connection.get_mut(), "no data")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"song-file-by-path" => {
|
||||||
|
if let Some(bytes) = request.next().and_then(|path| {
|
||||||
|
let db = db.lock().unwrap();
|
||||||
|
let mut parent = Some(db.lib_directory.clone());
|
||||||
|
// check for malicious paths [TODO: Improve]
|
||||||
|
if Path::new(path).is_absolute() {
|
||||||
|
parent = None;
|
||||||
|
}
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
let path = parent.join(path);
|
||||||
|
if path.starts_with(parent) {
|
||||||
|
fs::read(path).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
writeln!(connection.get_mut(), "len: {}", bytes.len())?;
|
||||||
|
connection.get_mut().write_all(&bytes)?;
|
||||||
|
} else {
|
||||||
|
writeln!(connection.get_mut(), "no data")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"find-unused-song-files" => {
|
||||||
|
// configure search
|
||||||
|
let mut extensions = None;
|
||||||
|
loop {
|
||||||
|
if let Some(line) = request.next() {
|
||||||
|
if let Some((key, value)) = line.split_once("=") {
|
||||||
|
match key.trim() {
|
||||||
|
"extensions" => {
|
||||||
|
extensions = Some(Some(
|
||||||
|
value
|
||||||
|
.split(':')
|
||||||
|
.map(|v| v.trim().to_owned())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match line.trim() {
|
||||||
|
"extensions" => extensions = Some(None),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// search
|
||||||
|
let lib_dir = db.lock().unwrap().lib_directory.clone();
|
||||||
|
let unused = find_unused_song_files(
|
||||||
|
&db,
|
||||||
|
&lib_dir,
|
||||||
|
&FindUnusedSongFilesConfig {
|
||||||
|
extensions: extensions
|
||||||
|
.unwrap_or_else(|| Some(vec![".mp3".to_owned()])),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
writeln!(connection.get_mut(), "len: {}", unused.len())?;
|
||||||
|
for path in unused {
|
||||||
|
if let Some(path) = path.to_str().filter(|v| !v.contains('\n')) {
|
||||||
|
writeln!(connection.get_mut(), "#{path}")?;
|
||||||
|
} else {
|
||||||
|
let path = path.to_string_lossy().replace('\n', "");
|
||||||
|
writeln!(connection.get_mut(), "!{path}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,3 +348,74 @@ pub fn con_get_encode_string(line: &str) -> String {
|
|||||||
}
|
}
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_unused_song_files(
|
||||||
|
db: &Arc<Mutex<Database>>,
|
||||||
|
path: &impl AsRef<Path>,
|
||||||
|
cfg: &FindUnusedSongFilesConfig,
|
||||||
|
) -> Vec<PathBuf> {
|
||||||
|
let mut files = vec![];
|
||||||
|
find_unused_song_files_internal(db, path, &"", cfg, &mut files, &mut vec![], true);
|
||||||
|
files
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FindUnusedSongFilesConfig {
|
||||||
|
extensions: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_unused_song_files_internal(
|
||||||
|
db: &Arc<Mutex<Database>>,
|
||||||
|
path: &impl AsRef<Path>,
|
||||||
|
rel_path: &impl AsRef<Path>,
|
||||||
|
cfg: &FindUnusedSongFilesConfig,
|
||||||
|
unused_files: &mut Vec<PathBuf>,
|
||||||
|
files_buf: &mut Vec<PathBuf>,
|
||||||
|
is_final: bool,
|
||||||
|
) {
|
||||||
|
if let Ok(rd) = std::fs::read_dir(path.as_ref()) {
|
||||||
|
for entry in rd {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
if let Ok(file_type) = entry.file_type() {
|
||||||
|
let path = entry.path();
|
||||||
|
let rel_path = rel_path.as_ref().join(entry.file_name());
|
||||||
|
if file_type.is_dir() {
|
||||||
|
find_unused_song_files_internal(
|
||||||
|
db,
|
||||||
|
&path,
|
||||||
|
&rel_path,
|
||||||
|
cfg,
|
||||||
|
unused_files,
|
||||||
|
files_buf,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else if file_type.is_file() {
|
||||||
|
if match &cfg.extensions {
|
||||||
|
None => true,
|
||||||
|
Some(exts) => {
|
||||||
|
if let Some(name) = path.file_name().and_then(|v| v.to_str()) {
|
||||||
|
exts.iter().any(|ext| name.ends_with(ext))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
files_buf.push(rel_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_final && files_buf.len() > 0) || files_buf.len() > 50 {
|
||||||
|
let db = db.lock().unwrap();
|
||||||
|
for song in db.songs().values() {
|
||||||
|
if let Some(i) = files_buf
|
||||||
|
.iter()
|
||||||
|
.position(|path| path == &song.location.rel_path)
|
||||||
|
{
|
||||||
|
files_buf.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unused_files.extend(std::mem::replace(files_buf, vec![]).into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,11 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
musicdb-lib = { path = "../musicdb-lib", features = ["playback"] }
|
||||||
axum = { version = "0.6.19", features = ["headers"] }
|
axum = { version = "0.6.19", features = ["headers"] }
|
||||||
clap = { version = "4.4.6", features = ["derive"] }
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
headers = "0.3.8"
|
headers = "0.3.8"
|
||||||
musicdb-lib = { version = "0.1.0", path = "../musicdb-lib", features = ["playback"] }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
Loading…
Reference in New Issue
Block a user