new
This commit is contained in:
+210
-42
@@ -21,17 +21,51 @@ pub enum RemoteNavAction {
|
||||
Back,
|
||||
ToggleOSD,
|
||||
CycleSubtitle,
|
||||
CycleAudio,
|
||||
ToggleMute,
|
||||
TogglePlaybackView,
|
||||
ActivateSection(usize),
|
||||
VolumeUp,
|
||||
VolumeDown,
|
||||
PlayPause,
|
||||
Stop,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RemoteNavSender {
|
||||
tx: UnboundedSender<RemoteNavAction>,
|
||||
ctx: Arc<std::sync::Mutex<Option<eframe::egui::Context>>>,
|
||||
}
|
||||
|
||||
impl RemoteNavSender {
|
||||
pub fn new(tx: UnboundedSender<RemoteNavAction>, ctx: Arc<std::sync::Mutex<Option<eframe::egui::Context>>>) -> Self {
|
||||
Self { tx, ctx }
|
||||
}
|
||||
|
||||
pub fn send(&self, action: RemoteNavAction) -> Result<(), tokio::sync::mpsc::error::SendError<RemoteNavAction>> {
|
||||
let res = self.tx.send(action);
|
||||
if let Ok(guard) = self.ctx.lock() {
|
||||
if let Some(ref c) = *guard {
|
||||
c.request_repaint();
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnboundedSender<RemoteNavAction>> for RemoteNavSender {
|
||||
fn from(tx: UnboundedSender<RemoteNavAction>) -> Self {
|
||||
Self {
|
||||
tx,
|
||||
ctx: Arc::new(std::sync::Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct YatseServerState {
|
||||
pub player: Arc<PlayerController>,
|
||||
pub nav_tx: UnboundedSender<RemoteNavAction>,
|
||||
pub nav_tx: RemoteNavSender,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -142,17 +176,26 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
}
|
||||
"Application.SetVolume" => {
|
||||
let vol_param = req.params.as_ref()
|
||||
.and_then(|p| p.get("volume").or_else(|| p.as_array().and_then(|a| a.first())))
|
||||
.and_then(|v| v.as_f64());
|
||||
.and_then(|p| p.get("volume").or_else(|| p.as_array().and_then(|a| a.first())));
|
||||
|
||||
let new_vol = match vol_param {
|
||||
Some(v) => {
|
||||
state.player.set_volume(v).await;
|
||||
v.clamp(0.0, 100.0) as i64
|
||||
}
|
||||
None => {
|
||||
let new_vol = if let Some(v_str) = vol_param.and_then(|v| v.as_str()) {
|
||||
if v_str == "increment" {
|
||||
state.player.volume_up().await;
|
||||
let _ = state.nav_tx.send(RemoteNavAction::VolumeUp);
|
||||
state.player.status.lock().await.volume as i64
|
||||
} else if v_str == "decrement" {
|
||||
state.player.volume_down().await;
|
||||
let _ = state.nav_tx.send(RemoteNavAction::VolumeDown);
|
||||
state.player.status.lock().await.volume as i64
|
||||
} else {
|
||||
state.player.status.lock().await.volume as i64
|
||||
}
|
||||
} else if let Some(v) = vol_param.and_then(|v| v.as_f64()) {
|
||||
state.player.set_volume(v).await;
|
||||
let _ = state.nav_tx.send(RemoteNavAction::VolumeUp);
|
||||
v.clamp(0.0, 100.0) as i64
|
||||
} else {
|
||||
state.player.status.lock().await.volume as i64
|
||||
};
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
@@ -163,9 +206,11 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
"Player.GetActivePlayers" => {
|
||||
let st = state.player.status.lock().await;
|
||||
if st.state != PlaybackState::Stopped {
|
||||
let pid = if st.media_type == "audio" { 0 } else { 1 };
|
||||
let ptype = if st.media_type == "audio" { "audio" } else { "video" };
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"result": [{"playerid": 1, "playertype": "internal", "type": "video"}],
|
||||
"result": [{"playerid": pid, "playertype": "internal", "type": ptype}],
|
||||
"id": id
|
||||
})
|
||||
} else {
|
||||
@@ -181,28 +226,55 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
let time = seconds_to_kodi_time(st.time_pos);
|
||||
let totaltime = seconds_to_kodi_time(st.duration);
|
||||
let speed = if st.state == PlaybackState::Playing { 1 } else { 0 };
|
||||
let ptype = if st.media_type == "audio" { "audio" } else { "video" };
|
||||
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"canseek": true,
|
||||
"canchangespeed": true,
|
||||
"canmove": false,
|
||||
"canzoom": false,
|
||||
"canrotate": false,
|
||||
"speed": speed,
|
||||
"time": time,
|
||||
"totaltime": totaltime,
|
||||
"percentage": if st.duration > 0.0 { (st.time_pos / st.duration) * 100.0 } else { 0.0 }
|
||||
"percentage": if st.duration > 0.0 { (st.time_pos / st.duration) * 100.0 } else { 0.0 },
|
||||
"type": ptype,
|
||||
"live": false,
|
||||
"subtitleenabled": true,
|
||||
"subtitles": [
|
||||
{"index": 0, "name": "Ondertitel 1", "language": ""},
|
||||
{"index": 1, "name": "Ondertitel 2", "language": ""}
|
||||
],
|
||||
"currentsubtitle": {"index": 0, "name": "Ondertitel", "language": ""},
|
||||
"audiostreams": [
|
||||
{"index": 0, "name": "Audiostroom 1", "language": ""},
|
||||
{"index": 1, "name": "Audiostroom 2", "language": ""}
|
||||
],
|
||||
"currentaudiostream": {"index": 0, "name": "Audiostroom", "language": ""}
|
||||
},
|
||||
"id": id
|
||||
})
|
||||
}
|
||||
"Player.GetItem" => {
|
||||
let st = state.player.status.lock().await;
|
||||
let title = if !st.stream_title.is_empty() {
|
||||
&st.stream_title
|
||||
} else if !st.title.is_empty() {
|
||||
&st.title
|
||||
} else {
|
||||
"Media"
|
||||
};
|
||||
let itype = if st.media_type == "audio" { "song" } else { "movie" };
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"item": {
|
||||
"label": st.title,
|
||||
"title": st.title,
|
||||
"type": "movie"
|
||||
"label": title,
|
||||
"title": title,
|
||||
"artist": if !st.artist.is_empty() { vec![st.artist.clone()] } else { Vec::new() },
|
||||
"type": itype
|
||||
}
|
||||
},
|
||||
"id": id
|
||||
@@ -220,6 +292,7 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
}
|
||||
"Player.Stop" => {
|
||||
state.player.stop().await;
|
||||
let _ = state.nav_tx.send(RemoteNavAction::Stop);
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"result": "OK",
|
||||
@@ -227,17 +300,87 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
})
|
||||
}
|
||||
"Player.Seek" => {
|
||||
if let Some(params) = req.params {
|
||||
if let Some(sec) = params.get("value").and_then(|v| v.as_f64()) {
|
||||
state.player.seek(sec).await;
|
||||
let mut result_pct = 0.0;
|
||||
if let Some(ref params) = req.params {
|
||||
let val = params.get("value").or_else(|| params.as_array().and_then(|a| a.get(1)));
|
||||
if let Some(v) = val {
|
||||
if let Some(pct) = v.get("percentage").and_then(|p| p.as_f64()) {
|
||||
state.player.seek_percent(pct).await;
|
||||
result_pct = pct;
|
||||
} else if let Some(time_obj) = v.get("time") {
|
||||
let h = time_obj.get("hours").and_then(|x| x.as_f64()).unwrap_or(0.0);
|
||||
let m = time_obj.get("minutes").and_then(|x| x.as_f64()).unwrap_or(0.0);
|
||||
let s = time_obj.get("seconds").and_then(|x| x.as_f64()).unwrap_or(0.0);
|
||||
let total_secs = h * 3600.0 + m * 60.0 + s;
|
||||
state.player.seek_time(total_secs).await;
|
||||
} else if let Some(step) = v.as_str() {
|
||||
match step.to_lowercase().as_str() {
|
||||
"smallforward" => { state.player.seek(10.0).await; }
|
||||
"smallbackward" => { state.player.seek(-10.0).await; }
|
||||
"bigforward" => { state.player.seek(60.0).await; }
|
||||
"bigbackward" => { state.player.seek(-60.0).await; }
|
||||
_ => {}
|
||||
}
|
||||
} else if let Some(n) = v.as_f64() {
|
||||
if n >= 0.0 && n <= 100.0 {
|
||||
state.player.seek_percent(n).await;
|
||||
result_pct = n;
|
||||
} else {
|
||||
state.player.seek(n).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let st = state.player.status.lock().await;
|
||||
let time = seconds_to_kodi_time(st.time_pos);
|
||||
let totaltime = seconds_to_kodi_time(st.duration);
|
||||
let pct = if st.duration > 0.0 { (st.time_pos / st.duration) * 100.0 } else { result_pct };
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"result": {"percentage": 50.0},
|
||||
"result": {
|
||||
"percentage": pct,
|
||||
"time": time,
|
||||
"totaltime": totaltime
|
||||
},
|
||||
"id": id
|
||||
})
|
||||
}
|
||||
"Player.SetSubtitle" => {
|
||||
state.player.cycle_subtitle().await;
|
||||
let _ = state.nav_tx.send(RemoteNavAction::CycleSubtitle);
|
||||
json!({"jsonrpc": "2.0", "result": "OK", "id": id})
|
||||
}
|
||||
"Player.SetAudioStream" => {
|
||||
state.player.cycle_audio().await;
|
||||
json!({"jsonrpc": "2.0", "result": "OK", "id": id})
|
||||
}
|
||||
"Player.SetSpeed" => {
|
||||
if let Some(ref params) = req.params {
|
||||
let speed_val = params.get("speed").or_else(|| params.as_array().and_then(|a| a.get(1)));
|
||||
if let Some(s) = speed_val.and_then(|v| v.as_i64()) {
|
||||
if s == 0 {
|
||||
let st = state.player.status.lock().await;
|
||||
if st.state == PlaybackState::Playing {
|
||||
drop(st);
|
||||
state.player.play_pause().await;
|
||||
}
|
||||
} else if s == 1 {
|
||||
let st = state.player.status.lock().await;
|
||||
if st.state == PlaybackState::Paused {
|
||||
drop(st);
|
||||
state.player.play_pause().await;
|
||||
}
|
||||
}
|
||||
} else if let Some(s_str) = speed_val.and_then(|v| v.as_str()) {
|
||||
if s_str == "increment" || s_str == "decrement" {
|
||||
state.player.play_pause().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
let st = state.player.status.lock().await;
|
||||
let speed = if st.state == PlaybackState::Playing { 1 } else { 0 };
|
||||
json!({"jsonrpc": "2.0", "result": {"speed": speed}, "id": id})
|
||||
}
|
||||
"Player.Open" => {
|
||||
if let Some(params) = req.params {
|
||||
if let Some(item) = params.get("item") {
|
||||
@@ -285,50 +428,75 @@ async fn dispatch_rpc(state: &YatseServerState, req: JsonRpcRequest) -> Value {
|
||||
json!({"jsonrpc": "2.0", "result": "OK", "id": id})
|
||||
}
|
||||
"Input.ExecuteAction" => {
|
||||
let action = req.params.as_ref()
|
||||
let raw_action = req.params.as_ref()
|
||||
.and_then(|p| p.get("action").or_else(|| p.as_array().and_then(|a| a.first())))
|
||||
.and_then(|a| a.as_str())
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
.unwrap_or("");
|
||||
let action = if raw_action.to_lowercase().starts_with("action(") && raw_action.ends_with(')') {
|
||||
raw_action[7..raw_action.len() - 1].trim().to_lowercase()
|
||||
} else {
|
||||
raw_action.to_lowercase()
|
||||
};
|
||||
match action.as_str() {
|
||||
"up" | "cursorup" => { let _ = state.nav_tx.send(RemoteNavAction::Up); }
|
||||
"down" | "cursordown" => { let _ = state.nav_tx.send(RemoteNavAction::Down); }
|
||||
"left" | "cursorleft" => { let _ = state.nav_tx.send(RemoteNavAction::Left); }
|
||||
"right" | "cursorright" => { let _ = state.nav_tx.send(RemoteNavAction::Right); }
|
||||
"select" | "enter" => { let _ = state.nav_tx.send(RemoteNavAction::Select); }
|
||||
"back" | "previousmenu" | "parentdir" | "close" => { let _ = state.nav_tx.send(RemoteNavAction::Back); }
|
||||
"togglefullscreen" => { let _ = state.nav_tx.send(RemoteNavAction::ToggleOSD); }
|
||||
"osd" | "nextsubtitle" => { let _ = state.nav_tx.send(RemoteNavAction::CycleSubtitle); }
|
||||
"title" | "menu" => { let _ = state.nav_tx.send(RemoteNavAction::TogglePlaybackView); }
|
||||
"volumeup" => {
|
||||
"up" | "cursorup" | "3" => { let _ = state.nav_tx.send(RemoteNavAction::Up); }
|
||||
"down" | "cursordown" | "4" => { let _ = state.nav_tx.send(RemoteNavAction::Down); }
|
||||
"left" | "cursorleft" | "1" | "stepback" | "smallstepback" => { let _ = state.nav_tx.send(RemoteNavAction::Left); }
|
||||
"right" | "cursorright" | "2" | "stepforward" | "smallstepforward" => { let _ = state.nav_tx.send(RemoteNavAction::Right); }
|
||||
"bigstepforward" => {
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.seek(60.0).await; });
|
||||
}
|
||||
"bigstepback" => {
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.seek(-60.0).await; });
|
||||
}
|
||||
"select" | "enter" | "7" => { let _ = state.nav_tx.send(RemoteNavAction::Select); }
|
||||
"back" | "previousmenu" | "parentdir" | "close" | "9" | "10" | "escape" => { let _ = state.nav_tx.send(RemoteNavAction::Back); }
|
||||
"togglefullscreen" | "12" => { let _ = state.nav_tx.send(RemoteNavAction::ToggleOSD); }
|
||||
"osd" | "nextsubtitle" | "114" | "122" => { let _ = state.nav_tx.send(RemoteNavAction::CycleSubtitle); }
|
||||
"audionextlanguage" | "nextaudio" => {
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.cycle_audio().await; });
|
||||
}
|
||||
"title" | "menu" | "11" => { let _ = state.nav_tx.send(RemoteNavAction::TogglePlaybackView); }
|
||||
"volumeup" | "88" => {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::VolumeUp);
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.volume_up().await; });
|
||||
}
|
||||
"volumedown" => {
|
||||
"volumedown" | "89" => {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::VolumeDown);
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.volume_down().await; });
|
||||
}
|
||||
"playpause" | "pause" | "play" => { state.player.play_pause().await; }
|
||||
"stop" => {
|
||||
"playpause" | "pause" | "play" | "15" => { state.player.play_pause().await; }
|
||||
"stop" | "14" => {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::Stop);
|
||||
state.player.stop().await;
|
||||
}
|
||||
"mute" | "togglemute" | "13" => {
|
||||
let p = state.player.clone();
|
||||
tokio::spawn(async move { p.toggle_mute().await; });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
json!({"jsonrpc": "2.0", "result": "OK", "id": id})
|
||||
}
|
||||
"GUI.ActivateWindow" => {
|
||||
if let Some(params) = req.params {
|
||||
let window = params.get("window").and_then(|w| w.as_str()).unwrap_or("").to_lowercase();
|
||||
match window.as_str() {
|
||||
"videos" | "video" | "movies" => { let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(1)); }
|
||||
"tvshows" => { let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(2)); }
|
||||
"music" => { let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(3)); }
|
||||
"pictures" | "photos" => { let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(4)); }
|
||||
"home" => { let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(0)); }
|
||||
_ => {}
|
||||
let p_str = params.to_string().to_lowercase();
|
||||
if p_str.contains("tvshow") || p_str.contains("series") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(3));
|
||||
} else if p_str.contains("movietitles") || p_str.contains("movie") || p_str.contains("video") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(2));
|
||||
} else if p_str.contains("music") || p_str.contains("song") || p_str.contains("artist") || p_str.contains("album") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(4));
|
||||
} else if p_str.contains("picture") || p_str.contains("photo") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(5));
|
||||
} else if p_str.contains("livetv") || p_str.contains("channel") || p_str.contains("tv") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(1));
|
||||
} else if p_str.contains("home") {
|
||||
let _ = state.nav_tx.send(RemoteNavAction::ActivateSection(0));
|
||||
}
|
||||
}
|
||||
json!({"jsonrpc": "2.0", "result": "OK", "id": id})
|
||||
|
||||
Reference in New Issue
Block a user