mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-12-14 11:56:16 +01:00
lib_dir is no longer saved in dbfile
This commit is contained in:
@@ -6,6 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
directories = "5.0.1"
|
||||
musicdb-lib = { version = "0.1.0", path = "../musicdb-lib" }
|
||||
regex = "1.9.3"
|
||||
|
||||
@@ -198,7 +198,7 @@ impl Gui {
|
||||
| Command::Pause
|
||||
| Command::Stop
|
||||
| Command::Save
|
||||
| Command::SetLibraryDirectory(..) => {}
|
||||
| Command::InitComplete => {}
|
||||
Command::NextSong
|
||||
| Command::QueueUpdate(..)
|
||||
| Command::QueueAdd(..)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
eprintln, fs,
|
||||
io::{BufReader, Write},
|
||||
net::{SocketAddr, TcpStream},
|
||||
path::PathBuf,
|
||||
@@ -8,15 +7,12 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use gui::GuiEvent;
|
||||
use musicdb_lib::{
|
||||
data::{
|
||||
album::Album,
|
||||
artist::Artist,
|
||||
database::{ClientIo, Cover, Database},
|
||||
queue::QueueContent,
|
||||
song::Song,
|
||||
CoverId, DatabaseLocation, GeneralData, SongId,
|
||||
database::{ClientIo, Database},
|
||||
CoverId, SongId,
|
||||
},
|
||||
load::ToFromBytes,
|
||||
player::Player,
|
||||
@@ -44,12 +40,24 @@ mod gui_text;
|
||||
mod gui_wrappers;
|
||||
mod textcfg;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
/// the address to be used for the tcp connection to the server
|
||||
addr: SocketAddr,
|
||||
/// what to do
|
||||
#[command(subcommand)]
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
enum Mode {
|
||||
Cli,
|
||||
#[cfg(feature = "speedy2d")]
|
||||
/// graphical user interface
|
||||
Gui,
|
||||
SyncPlayer,
|
||||
SyncPlayerWithoutData,
|
||||
/// play in sync with the server, but load the songs from a local copy of the lib-dir
|
||||
SyncplayerLocal { lib_dir: PathBuf },
|
||||
/// play in sync with the server, and fetch the songs from it too. slower than the local variant for obvious reasons
|
||||
SyncplayerNetwork,
|
||||
}
|
||||
|
||||
fn get_config_file_path() -> PathBuf {
|
||||
@@ -57,36 +65,15 @@ fn get_config_file_path() -> PathBuf {
|
||||
.unwrap()
|
||||
.config_dir()
|
||||
.to_path_buf()
|
||||
// if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") {
|
||||
// let mut config_home: PathBuf = config_home.into();
|
||||
// config_home.push("musicdb-client");
|
||||
// config_home
|
||||
// } else if let Ok(home) = std::env::var("HOME") {
|
||||
// let mut config_home: PathBuf = home.into();
|
||||
// config_home.push(".config");
|
||||
// config_home.push("musicdb-client");
|
||||
// config_home
|
||||
// } else {
|
||||
// eprintln!("No config directory!");
|
||||
// std::process::exit(24);
|
||||
// }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = std::env::args().skip(1);
|
||||
let mode = match args.next().as_ref().map(|v| v.trim()) {
|
||||
Some("cli") => Mode::Cli,
|
||||
Some("gui") => Mode::Gui,
|
||||
Some("syncplayer") => Mode::SyncPlayer,
|
||||
Some("syncplayernd") => Mode::SyncPlayerWithoutData,
|
||||
_ => {
|
||||
println!("Run with argument <cli/gui/syncplayer/syncplayernd>!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let addr = args.next().unwrap_or("127.0.0.1:26314".to_string());
|
||||
let addr = addr.parse::<SocketAddr>().unwrap();
|
||||
// parse args
|
||||
let args = Args::parse();
|
||||
// start
|
||||
let addr = args.addr;
|
||||
let mut con = TcpStream::connect(addr).unwrap();
|
||||
let mode = args.mode;
|
||||
writeln!(con, "main").unwrap();
|
||||
let database = Arc::new(Mutex::new(Database::new_clientside()));
|
||||
#[cfg(feature = "speedy2d")]
|
||||
@@ -95,16 +82,21 @@ fn main() {
|
||||
#[cfg(feature = "speedy2d")]
|
||||
let sender = Arc::clone(&update_gui_sender);
|
||||
let con_thread = {
|
||||
let mode = mode.clone();
|
||||
let database = Arc::clone(&database);
|
||||
let mut con = con.try_clone().unwrap();
|
||||
// this is all you need to keep the db in sync
|
||||
thread::spawn(move || {
|
||||
let mut player = if matches!(mode, Mode::SyncPlayer | Mode::SyncPlayerWithoutData) {
|
||||
Some(Player::new().unwrap())
|
||||
let mut player =
|
||||
if matches!(mode, Mode::SyncplayerLocal { .. } | Mode::SyncplayerNetwork) {
|
||||
Some(Player::new().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Mode::SyncplayerLocal { lib_dir } = mode {
|
||||
let mut db = database.lock().unwrap();
|
||||
db.lib_directory = lib_dir;
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if matches!(mode, Mode::SyncPlayerWithoutData) {
|
||||
let mut db = database.lock().unwrap();
|
||||
let client_con: Box<dyn ClientIo> = Box::new(TcpStream::connect(addr).unwrap());
|
||||
db.remote_server_as_song_file_source = Some(Arc::new(Mutex::new(
|
||||
@@ -114,7 +106,7 @@ fn main() {
|
||||
loop {
|
||||
if let Some(player) = &mut player {
|
||||
let mut db = database.lock().unwrap();
|
||||
if !db.lib_directory.as_os_str().is_empty() {
|
||||
if db.is_client_init() {
|
||||
player.update(&mut db);
|
||||
}
|
||||
}
|
||||
@@ -131,15 +123,8 @@ fn main() {
|
||||
})
|
||||
};
|
||||
match mode {
|
||||
Mode::Cli => {
|
||||
Looper {
|
||||
con: &mut con,
|
||||
database: &database,
|
||||
}
|
||||
.cmd_loop();
|
||||
}
|
||||
#[cfg(feature = "speedy2d")]
|
||||
Mode::Gui => {
|
||||
#[cfg(feature = "speedy2d")]
|
||||
{
|
||||
let occasional_refresh_sender = Arc::clone(&sender);
|
||||
thread::spawn(move || loop {
|
||||
@@ -159,162 +144,12 @@ fn main() {
|
||||
)
|
||||
};
|
||||
}
|
||||
Mode::SyncPlayer | Mode::SyncPlayerWithoutData => {
|
||||
Mode::SyncplayerLocal { .. } | Mode::SyncplayerNetwork => {
|
||||
con_thread.join().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
struct Looper<'a> {
|
||||
pub con: &'a mut TcpStream,
|
||||
pub database: &'a Arc<Mutex<Database>>,
|
||||
}
|
||||
impl<'a> Looper<'a> {
|
||||
pub fn cmd_loop(&mut self) {
|
||||
loop {
|
||||
println!();
|
||||
let line = self.read_line(" > enter a command (help for help)");
|
||||
let line = line.trim();
|
||||
match line {
|
||||
"resume" => Command::Resume,
|
||||
"pause" => Command::Pause,
|
||||
"stop" => Command::Stop,
|
||||
"next" => Command::NextSong,
|
||||
"set-lib-dir" => {
|
||||
let line = self.read_line("Enter the new (absolute) library directory, or leave empty to abort");
|
||||
if !line.is_empty() {
|
||||
Command::SetLibraryDirectory(line.into())
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
"add-song" => {
|
||||
let song = Song {
|
||||
id: 0,
|
||||
location: self.read_line("The songs file is located, relative to the library root, at...").into(),
|
||||
title: self.read_line("The songs title is..."),
|
||||
album: self.read_line_ido("The song is part of the album with the id... (empty for None)"),
|
||||
artist: self.read_line_id("The song is made by the artist with the id..."),
|
||||
more_artists: accumulate(|| self.read_line_ido("The song is made with support by other artist, one of which has the id... (will ask repeatedly; leave empty once done)")),
|
||||
cover: self.read_line_ido("The song should use the cover with the id... (empty for None - will default to album or artist cover, if available)"),
|
||||
general: GeneralData::default(),
|
||||
cached_data: Arc::new(Mutex::new(None)),
|
||||
};
|
||||
println!("You are about to add the following song to the database:");
|
||||
println!(" + {song}");
|
||||
if self.read_line("Are you sure? (type 'yes' to continue)").to_lowercase().trim() == "yes" {
|
||||
Command::AddSong(song)
|
||||
} else {
|
||||
println!("[-] Aborted - no event will be sent to the database.");
|
||||
continue;
|
||||
}
|
||||
},
|
||||
"update-song" => {
|
||||
let song_id = self.read_line_id("The ID of the song is...");
|
||||
if let Some(mut song) = self.database.lock().unwrap().get_song(&song_id).cloned() {
|
||||
println!("You are now editing the song {song}.");
|
||||
loop {
|
||||
match self.read_line("What do you want to edit? (title/album/artist/location or done)").to_lowercase().trim() {
|
||||
"done" => break,
|
||||
"title" => {
|
||||
println!("prev: '{}'", song.title);
|
||||
song.title = self.read_line("");
|
||||
}
|
||||
"album" => {
|
||||
println!("prev: '{}'", song.album.map_or(String::new(), |v| v.to_string()));
|
||||
song.album = self.read_line_ido("");
|
||||
}
|
||||
"artist" => {
|
||||
println!("prev: '{}'", song.artist);
|
||||
song.artist = self.read_line_id("");
|
||||
}
|
||||
"location" => {
|
||||
println!("prev: '{:?}'", song.location);
|
||||
song.location = self.read_line("").into();
|
||||
}
|
||||
_ => println!("[-] must be title/album/artist/location or done"),
|
||||
}
|
||||
}
|
||||
println!("You are about to update the song:");
|
||||
println!(" + {song}");
|
||||
if self.read_line("Are you sure? (type 'yes' to continue)").to_lowercase().trim() == "yes" {
|
||||
Command::ModifySong(song)
|
||||
} else {
|
||||
println!("[-] Aborted - no event will be sent to the database.");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
println!("[-] No song with that ID found, aborting.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
"queue-clear" => Command::QueueUpdate(vec![], QueueContent::Folder(0, vec![], String::new()).into()),
|
||||
"queue-add-to-end" => Command::QueueAdd(vec![], QueueContent::Song(self.read_line_id("The ID of the song that should be added to the end of the queue is...")).into()),
|
||||
"save" => Command::Save,
|
||||
"status" => {
|
||||
let db = self.database.lock().unwrap();
|
||||
println!("DB contains {} songs:", db.songs().len());
|
||||
for song in db.songs().values() {
|
||||
println!("> [{}]: {}", song.id, song);
|
||||
}
|
||||
println!("Queue: {:?}, then {:?}", db.queue.get_current(), db.queue.get_next());
|
||||
continue;
|
||||
}
|
||||
"exit" => {
|
||||
println!("<< goodbye");
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
println!("Type 'exit' to exit, 'status' to see the db, 'resume', 'pause', 'stop', 'next', 'queue-clear', 'queue-add-to-end', 'add-song', 'add-album', 'add-artist', 'update-song', 'update-album', 'update-artist', 'set-lib-dir', or 'save' to control playback or update the db.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
.to_bytes(self.con)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_line(&mut self, q: &str) -> String {
|
||||
loop {
|
||||
if !q.is_empty() {
|
||||
println!("{q}");
|
||||
}
|
||||
let mut line = String::new();
|
||||
std::io::stdin().read_line(&mut line).unwrap();
|
||||
while line.ends_with('\n') || line.ends_with('\r') {
|
||||
line.pop();
|
||||
}
|
||||
if line.trim() == "#" {
|
||||
self.cmd_loop();
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_line_id(&mut self, q: &str) -> u64 {
|
||||
loop {
|
||||
if let Ok(v) = self.read_line(q).trim().parse() {
|
||||
return v;
|
||||
} else {
|
||||
println!("[-] Must be a positive integer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn read_line_ido(&mut self, q: &str) -> Option<u64> {
|
||||
loop {
|
||||
let line = self.read_line(q);
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(v) = line.parse() {
|
||||
return Some(v);
|
||||
} else {
|
||||
println!("[-] Must be a positive integer or nothing for None.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn accumulate<F: FnMut() -> Option<T>, T>(mut f: F) -> Vec<T> {
|
||||
let mut o = vec![];
|
||||
loop {
|
||||
|
||||
Reference in New Issue
Block a user