From 00d8c0ed458cb4689e8690cb2e0e8a74c87f708b Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Thu, 20 Jun 2024 12:37:26 +0200 Subject: [PATCH] Send the position of each channel, they are now sorted when rendered in HTML. --- Cargo.toml | 2 +- ice/ice.py | 33 +++++++++++++-------- src/ice.rs | 71 ++++++++++++++++++++++++++++----------------- src/main.rs | 52 +++++++++++++++++++++++++++++---- templates/main.html | 2 +- 5 files changed, 115 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd438a9..6616b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ askama_axum = "0.4" shared_child = "1" rsevents = "0.3" - [profile.release] codegen-units = 1 lto = true +strip = true diff --git a/ice/ice.py b/ice/ice.py index 34cba11..faa309c 100644 --- a/ice/ice.py +++ b/ice/ice.py @@ -4,30 +4,36 @@ initdata = Ice.InitializationData() initdata.properties = Ice.createProperties(None, initdata.properties) initdata.properties.setProperty('Ice.ImplicitContext', 'Shared') +# Return a tuple (path: string, positions: int list) +# where 'positions' is the position of each path level. def getChannelPath(channels, channelId, first=True): if channelId == 0: - return "/" + return ("/", [0]) else: channel = channels[channelId] - return getChannelPath(channels, channel.parent, False) + channel.name + \ - ("" if first else "/") + (path, positions) = getChannelPath(channels, channel.parent, False) + return (path + channel.name + ("" if first else "/"), positions + [channel.position]) def getUsers(server): return map(lambda u: (u.name, u.channel, u.selfMute, u.selfDeaf), server.getUsers().values()) +def userToJson(channels, user): + (username, channelId, selfMute, selfDeaf) = user + (channelPath, positions) = getChannelPath(channels, channelId) + return { + 'username': username, + 'channelPath': channelPath, + 'channelPathPositions': positions, + 'selfMute': selfMute, + 'selfDeaf': selfDeaf + } + def getUsersAsJson(server): channels = server.getChannels() users = getUsers(server) + return json.dumps( - [ - { - 'username': username, - 'channel': getChannelPath(channels, channel), - 'selfMute': selfMute, - 'selfDeaf': selfDeaf - } - for (username, channel, selfMute, selfDeaf) in users - ] + list(map(lambda user: userToJson(channels, user), users)) ) with Ice.initialize(sys.argv, initdata) as commmunicator: @@ -36,7 +42,10 @@ with Ice.initialize(sys.argv, initdata) as commmunicator: meta = MumbleServer.MetaPrx.uncheckedCast(proxy) server = meta.getServer(1) + # print(getUsersAsJson(server)) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("127.0.0.1", 4978)) s.listen() while True: diff --git a/src/ice.rs b/src/ice.rs index c772ddc..2a791d2 100644 --- a/src/ice.rs +++ b/src/ice.rs @@ -11,7 +11,8 @@ use std::{ time::{self, Duration}, }; -use json; +use itertools::Itertools; +use json::{self, JsonValue}; use rsevents::{AutoResetEvent, Awaitable, EventState}; use shared_child::SharedChild; @@ -21,9 +22,10 @@ const CACHED_VALUES_DURATION: Duration = Duration::from_secs(5); #[derive(Debug, Clone)] pub struct User { pub name: String, - pub channel: String, + pub channel_path: String, + pub channel_path_positions: Vec, pub self_mute: bool, - pub self_deaf: bool, // 🔇 + pub self_deaf: bool, } pub struct Ice { @@ -63,30 +65,36 @@ impl Ice { let mut command = Command::new(PYTHON_PATH); command.arg("./ice/ice.py"); - let child = SharedChild::spawn(&mut command).unwrap(); - let child_arc = Arc::new(child); - let child_arc_cloned = child_arc.clone(); - - thread::spawn(move || { - kill_event_arc_clone1.wait(); - child_arc_cloned.kill() - }); - - println!("ice.py launched!"); - - child_arc.wait().unwrap(); - - // if kill_event is set then quit loop. - if dropped_arc_clone.load(Ordering::Relaxed) { - println!("Stopping main python loop..."); - kill_event_arc_clone2.set(); - return; - } else { - kill_event_arc_clone2.set(); + match SharedChild::spawn(&mut command) { + Ok(child) => { + let child_arc = Arc::new(child); + let child_arc_cloned = child_arc.clone(); + + thread::spawn(move || { + kill_event_arc_clone1.wait(); + child_arc_cloned.kill() + }); + + println!("ice.py launched!"); + + if let Err(err) = child_arc.wait() { + println!("Error when waiting python process: {}", err); + } + + // if kill_event is set then quit loop. + if dropped_arc_clone.load(Ordering::Relaxed) { + println!("Stopping main python loop..."); + kill_event_arc_clone2.set(); + return; + } else { + kill_event_arc_clone2.set(); + } + + print!("Python stopped!"); + } + Err(err) => print!("Can't lauch python: {}, retrying in a while...", err), } - print!("Python stopped!"); - let elapsed = time::Instant::now() - time_beginning_loop; if elapsed < RELAUNCH_PERIOD { let to_wait = RELAUNCH_PERIOD - elapsed; @@ -136,13 +144,24 @@ impl Ice { let mut users: Vec = vec![]; for i in 0..json.len() { let user_json = &json[i]; + users.push(User { name: user_json["username"].as_str().unwrap().to_string(), - channel: user_json["channel"].as_str().unwrap().to_string(), + channel_path: user_json["channelPath"].as_str().unwrap().to_string(), + // TODO: a bit ugly. + channel_path_positions: match &user_json["channelPathPositions"] { + JsonValue::Array(array) => { + array.iter().map(|v| v.as_i32().unwrap()).collect_vec() + } + _ => vec![], + }, self_mute: user_json["selfMute"].as_bool().unwrap(), self_deaf: user_json["selfDeaf"].as_bool().unwrap(), }); } + + // dbg!(&users); + self.cached_users = users.clone(); self.cached_users_timestamp = time::Instant::now(); users diff --git a/src/main.rs b/src/main.rs index 2055ed5..075f57f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::{ - cmp::Ordering, + cmp::{min, Ordering}, net::SocketAddr, sync::{Arc, Mutex}, }; @@ -7,12 +7,13 @@ use std::{ use askama::Template; use askama_axum::IntoResponse; use axum::{extract::State, routing::get, Router}; +use itertools::Itertools; use tokio::signal; use tower_http::services::ServeDir; mod ice; -#[derive(Eq)] +#[derive(Eq, Debug)] struct User { name: String, self_mute: bool, @@ -47,11 +48,49 @@ impl Ord for User { } } +#[derive(Eq, Debug)] struct Channel { - name: String, + path: String, + positions: Vec, users: Vec, } +impl PartialEq for Channel { + fn eq(&self, other: &Self) -> bool { + self.path.eq(&other.path) + } +} + +impl PartialOrd for Channel { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for Channel { + fn cmp(&self, other: &Self) -> Ordering { + let self_path = self.path.split('/').collect_vec(); + let other_path = other.path.split('/').collect_vec(); + let l = min(self.positions.len(), other.positions.len()); + for i in 0..l { + if let order @ (Ordering::Greater | Ordering::Less) = + self.positions[i].cmp(&other.positions[i]) + { + return order; + } + + if let order @ (Ordering::Greater | Ordering::Less) = self_path[i].cmp(other_path[i]) { + return order; + } + + if i == l - 1 { + return self.positions.len().cmp(&other.positions.len()); + } + } + Ordering::Equal + } +} + #[derive(Template)] #[template(path = "main.html")] struct MainTemplate { @@ -86,13 +125,14 @@ async fn root(State(ice): State>>) -> impl IntoResponse { let mut channels = Vec::::new(); 'next_user: for u in &users { for c in &mut channels { - if c.name == u.channel { + if c.path == u.channel_path { c.users.push(User::from_ice_user(u)); continue 'next_user; } } let channel = Channel { - name: u.channel.clone(), + path: u.channel_path.clone(), + positions: u.channel_path_positions.clone(), users: vec![User::from_ice_user(u)], }; channels.push(channel); @@ -102,6 +142,8 @@ async fn root(State(ice): State>>) -> impl IntoResponse { c.users.sort(); } + channels.sort(); + MainTemplate { channels } } diff --git a/templates/main.html b/templates/main.html index 5bc6fba..16a3c99 100644 --- a/templates/main.html +++ b/templates/main.html @@ -15,7 +15,7 @@

No user connected

{% else %} {% for channel in channels %} -

{{ channel.name }}

+

{{ channel.path }}

    {% for user in channel.users %}
  • {{ user.name }} -- 2.45.2