mek sock to http server and add playlsit funktion

This commit is contained in:
2026-06-23 17:42:43 +02:00
parent 3784b1d31d
commit 792d41517e
14 changed files with 1600 additions and 230 deletions
+77 -14
View File
@@ -1,36 +1,99 @@
use std::sync::{Arc, Mutex};
use std::thread;
use log::{debug, error, info, warn};
use crate::{
state::DaemonState,
socket::start_socket,
db::init_db,
audio::AudioEngine,
config::Config,
db::open_db,
server::start_server,
state::DaemonState,
};
pub fn run() {
let state = Arc::new(Mutex::new(DaemonState::new()));
pub fn run(cfg: Config) {
info!("Starting sjnmusicd");
init_db();
let conn = open_db();
let state = Arc::new(Mutex::new(DaemonState::new(conn)));
let socket_state = state.clone();
thread::spawn(move || {
start_socket(socket_state);
});
// HTTP server thread.
let server_state = state.clone();
let host = cfg.server.host.clone();
let port = cfg.server.port;
let server_handle = thread::Builder::new()
.name("sjnmusic-http".into())
.spawn(move || {
start_server(server_state, &host, port);
});
if let Err(e) = server_handle {
error!("Failed to spawn HTTP server thread: {e}");
}
// Audio playback loop (runs on the main thread).
let mut audio = AudioEngine::new();
info!("Playback loop ready");
loop {
{
let mut state = state.lock().unwrap();
// Clear "now playing" once the audio engine reports the song has
// ended so /now-playing and /queue reflect reality while the
// next iteration decides what's up next.
if !audio.is_playing() {
if let Some(prev) = state.current.take() {
debug!("Playback finished for {}", prev.name);
}
}
if !state.queue.is_empty() && !audio.is_playing() {
let song = state.queue.remove(0);
audio.play(&song.path);
state.current = Some(song);
// Snapshot the next song first, attempt audio.play, and only
// remove from the queue (memory + DB) when playback actually
// starts so decode/init failures don't silently lose it.
let next = state.queue[0].clone();
debug!("Starting playback: {} ({})", next.name, next.path);
match audio.play(&next.path) {
Ok(()) => match state.pop_played() {
Ok(Some(popped)) => {
let name = popped.name.clone();
state.current = Some(popped);
debug!("DB queue row removed for {name}");
info!("Now playing: {name}");
}
Ok(None) => {
// Should not happen — we peeked a non-empty queue.
state.current = Some(next.clone());
warn!(
"pop_played returned None after successful audio.play for {}",
next.name
);
}
Err(e) => {
// pop_played is DB-first; on Err the in-memory
// queue has NOT been mutated, so the song is
// already at index 0 — do not re-insert or we
// would duplicate it.
error!(
"Played {} but failed to update DB queue ({e}); \
leaving in-memory queue untouched",
next.name
);
state.current = Some(next);
}
},
Err(e) => {
// The previous "now playing" is no longer accurate.
state.current = None;
error!(
"Failed to play {} ({e}); leaving in queue",
next.path
);
}
}
}
}
std::thread::sleep(std::time::Duration::from_millis(100));
thread::sleep(std::time::Duration::from_millis(200));
}
}