This commit is contained in:
bart
2026-11-04 19:41:25 +01:00
parent 99cbc00357
commit d77cdfedab
12 changed files with 2181 additions and 605 deletions
+86 -13
View File
@@ -54,6 +54,7 @@ pub struct PlayerController {
status_tx: tokio::sync::watch::Sender<PlayerStatus>,
status_rx: tokio::sync::watch::Receiver<PlayerStatus>,
pub window_id: Mutex<Option<u64>>,
pub ipc_tx: Mutex<Option<tokio::sync::mpsc::UnboundedSender<serde_json::Value>>>,
}
impl PlayerController {
@@ -67,6 +68,7 @@ impl PlayerController {
status_tx,
status_rx,
window_id: Mutex::new(None),
ipc_tx: Mutex::new(None),
})
}
@@ -139,6 +141,13 @@ impl PlayerController {
cmd.arg(format!("--input-ipc-server={}", self.config.ipc_socket));
cmd.arg(format!("--hwdec={}", self.config.hwdec));
cmd.arg(format!("--vo={}", self.config.vo));
cmd.arg("--osd-bar=yes");
cmd.arg("--osd-level=1");
cmd.arg("--osd-font-size=28");
cmd.arg("--osd-on-seek=msg-bar");
cmd.arg("--input-vo-keyboard=no");
cmd.arg("--input-cursor=no");
cmd.arg("--input-cursor-passthrough=yes");
// Configure yt-dlp for YouTube streams
let ytdlp_path = if std::path::Path::new("/home/bart/.local/bin/yt-dlp").exists() {
@@ -189,12 +198,19 @@ impl PlayerController {
}
}
// Create persistent IPC command channel
let (ipc_tx, ipc_rx) = tokio::sync::mpsc::unbounded_channel::<serde_json::Value>();
{
let mut guard = self.ipc_tx.lock().await;
*guard = Some(ipc_tx);
}
// Start background IPC monitoring task
let sock_file = self.config.ipc_socket.clone();
let status_arc = self.status.clone();
let status_tx = self.status_tx.clone();
tokio::spawn(async move {
let _ = monitor_mpv_ipc(sock_file, status_arc, status_tx).await;
let _ = monitor_mpv_ipc(sock_file, status_arc, status_tx, ipc_rx).await;
});
Ok(())
@@ -212,7 +228,7 @@ impl PlayerController {
PlaybackState::Stopped => PlaybackState::Stopped,
};
let _ = self.send_ipc_cmd(&["cycle", "pause"]).await;
let _ = self.send_ipc_cmd(&["osd-msg", "cycle", "pause"]).await;
{
let mut st = self.status.lock().await;
st.state = new_state;
@@ -222,6 +238,10 @@ impl PlayerController {
pub async fn stop(&self) {
let _ = self.send_ipc_cmd(&["stop"]).await;
{
let mut guard = self.ipc_tx.lock().await;
*guard = None;
}
let mut proc_guard = self.child_process.lock().await;
if let Some(mut child) = proc_guard.take() {
let _ = child.kill();
@@ -241,7 +261,17 @@ impl PlayerController {
}
pub async fn seek(&self, seconds: f64) {
let _ = self.send_ipc_cmd(&["seek", &seconds.to_string(), "relative"]).await;
let _ = self.send_ipc_cmd(&["osd-msg-bar", "seek", &seconds.to_string(), "relative"]).await;
}
pub async fn seek_percent(&self, percent: f64) {
let p = percent.clamp(0.0, 100.0);
let _ = self.send_ipc_cmd(&["osd-msg-bar", "seek", &p.to_string(), "absolute-percent"]).await;
}
pub async fn seek_time(&self, seconds: f64) {
let s = seconds.max(0.0);
let _ = self.send_ipc_cmd(&["osd-msg-bar", "seek", &s.to_string(), "absolute"]).await;
}
pub async fn toggle_mute(&self) {
@@ -272,7 +302,7 @@ impl PlayerController {
let mut st = self.status.lock().await;
st.volume = vol;
}
let _ = self.send_ipc_cmd(&["set", "volume", &vol.to_string()]).await;
let _ = self.send_ipc_cmd(&["osd-msg-bar", "set", "volume", &vol.to_string()]).await;
self.notify_status().await;
tracing::info!("Player volume set to {}", vol);
}
@@ -283,7 +313,7 @@ impl PlayerController {
st.volume = (st.volume + 5.0).min(100.0);
st.volume
};
let _ = self.send_ipc_cmd(&["add", "volume", "5"]).await;
let _ = self.send_ipc_cmd(&["osd-msg-bar", "add", "volume", "5"]).await;
self.notify_status().await;
tracing::info!("Player volume increased to {}", new_vol);
}
@@ -294,28 +324,49 @@ impl PlayerController {
st.volume = (st.volume - 5.0).max(0.0);
st.volume
};
let _ = self.send_ipc_cmd(&["add", "volume", "-5"]).await;
let _ = self.send_ipc_cmd(&["osd-msg-bar", "add", "volume", "-5"]).await;
self.notify_status().await;
tracing::info!("Player volume decreased to {}", new_vol);
}
pub async fn cycle_subtitle(&self) {
let _ = self.send_ipc_cmd(&["cycle", "sub"]).await;
let _ = self.send_ipc_cmd(&[
"expand-properties",
"show-text",
"💬 Ondertitels: ${current-tracks/sub/title:${current-tracks/sub/lang:${sub:Geen beschikbaar}}}",
"2500",
]).await;
tracing::info!("Cycled subtitle track");
}
pub async fn cycle_audio(&self) {
let _ = self.send_ipc_cmd(&["cycle", "audio"]).await;
let _ = self.send_ipc_cmd(&[
"expand-properties",
"show-text",
"🎬 Audio: ${current-tracks/audio/title:${current-tracks/audio/lang:${audio:Standaard}}}",
"2500",
]).await;
tracing::info!("Cycled audio track");
}
pub async fn set_video_visible(&self, visible: bool) {
let arg = if visible { "auto" } else { "no" };
let _ = self.send_ipc_cmd(&["set", "vid", arg]).await;
tracing::info!("Set MPV video visibility to {}", visible);
}
async fn send_ipc_cmd(&self, args: &[&str]) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut stream = UnixStream::connect(&self.config.ipc_socket).await?;
let cmd_json = serde_json::json!({
"command": args
});
stream.write_all(format!("{}\n", cmd_json).as_bytes()).await?;
Ok(())
let guard = self.ipc_tx.lock().await;
if let Some(ref tx) = *guard {
let cmd_json = serde_json::json!({
"command": args
});
let _ = tx.send(cmd_json);
return Ok(());
}
Err("MPV IPC not connected".into())
}
}
@@ -323,6 +374,7 @@ async fn monitor_mpv_ipc(
sock_path: String,
status: Arc<Mutex<PlayerStatus>>,
status_tx: tokio::sync::watch::Sender<PlayerStatus>,
mut ipc_rx: tokio::sync::mpsc::UnboundedReceiver<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Wait for socket to become available
for _ in 0..30 {
@@ -356,7 +408,23 @@ async fn monitor_mpv_ipc(
let _ = writer.write_all(format!("{}\n", c).as_bytes()).await;
}
while let Ok(Some(line)) = lines.next_line().await {
loop {
tokio::select! {
cmd_opt = ipc_rx.recv() => {
match cmd_opt {
Some(cmd_val) => {
let payload = format!("{}\n", cmd_val);
if let Err(e) = writer.write_all(payload.as_bytes()).await {
tracing::warn!("MPV IPC write error: {}", e);
break;
}
}
None => break,
}
}
line_res = lines.next_line() => {
match line_res {
Ok(Some(line)) => {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&line) {
if let Some(event) = v.get("event").and_then(|e| e.as_str()) {
if event == "property-change" {
@@ -487,6 +555,11 @@ async fn monitor_mpv_ipc(
break;
}
}
}
}
_ => break,
}
}
}
}