Working base via tcp (play-pause, prev, next, currently playing)

This commit is contained in:
vandechat96
2023-08-03 23:35:49 +02:00
parent e6f53e0d83
commit 55ee8095ab
9 changed files with 261 additions and 130 deletions

4
Cargo.lock generated
View File

@@ -894,6 +894,10 @@ dependencies = [
name = "deckui"
version = "0.1.0"
dependencies = [
"anyhow",
"env_logger",
"lib",
"log",
"slint",
"slint-build",
]

View File

@@ -1,4 +1,6 @@
use lib::{start_client, start_server, ServerMessage, ClientMessage};
use std::{process::Command, string::FromUtf8Error, thread, time::Duration};
use lib::{tcp::start_server, ClientMessage, ServerMessage};
use log::info;
fn main() {
@@ -7,11 +9,53 @@ fn main() {
);
let (sender_s, receiver_c) = start_server().unwrap();
let (sender_c, receiver_s) = start_client().unwrap();
sender_s.send(ServerMessage::NowPlaying("Hello".to_string())).unwrap();
sender_c.send(ClientMessage::PlayPause).unwrap();
loop{
let mut i = 0;
loop {
i += 1;
if i == 50 {
i = 0;
sender_s
.send(ServerMessage::NowPlaying(get_np().unwrap()))
.unwrap();
}
if receiver_c.is_empty() {
thread::sleep(Duration::from_millis(200));
continue;
}
let msg = receiver_c.recv().unwrap();
info!("[Client] : {msg:?}");
match msg {
ClientMessage::Next => {
Command::new("playerctl").arg("next").spawn().unwrap();
thread::sleep(Duration::from_millis(200));
sender_s
.send(ServerMessage::NowPlaying(get_np().unwrap()))
.unwrap();
}
ClientMessage::PlayPause => {
Command::new("playerctl").arg("play-pause").spawn().unwrap();
}
ClientMessage::Previous => {
Command::new("playerctl").arg("previous").spawn().unwrap();
thread::sleep(Duration::from_millis(200));
sender_s
.send(ServerMessage::NowPlaying(get_np().unwrap()))
.unwrap();
}
};
}
}
fn get_np() -> Result<String, FromUtf8Error> {
let output = Command::new("playerctl")
.arg("metadata")
.arg("-f")
.arg("{{title}} - {{artist}}")
.output()
.unwrap()
.stdout;
let title = String::from_utf8(output)?;
Ok(title.trim().to_string())
}

View File

@@ -14,6 +14,12 @@ path = "src/main.rs"
name = "deckui"
[dependencies]
lib = {path = "../lib"}
anyhow = "1.0"
log = "0.4"
env_logger = "0.10"
slint = "1.0"
[build-dependencies]

View File

@@ -1,19 +1,68 @@
use std::{net::IpAddr, str::FromStr, thread, time::Duration, process::exit};
use lib::{tcp::start_client, ServerMessage};
use log::info;
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
env_logger::init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);
let mut retries = 1;
let (sender_co, receiver_s) = loop{
let res = start_client(IpAddr::from_str("192.168.0.101").unwrap());
if let Ok((s,r)) = res{
break (s,r)
}
println!("Failde to connect retrying in {} ms", 200*retries);
thread::sleep(Duration::from_millis(200*retries));
retries*=5;
if retries >= 500 {
println!("Could not connect to server exiting...");
exit(1);
}
};
info!("Connected to Server");
let ui = AppWindow::new()?;
let ui_handle = ui.as_weak();
let sender_c = sender_co.clone();
ui.global::<MediaLogic>().on_play_pause(move || {
ui_handle.unwrap().set_now_playing("a".into());
sender_c.send(lib::ClientMessage::PlayPause).unwrap();
});
let sender_c = sender_co.clone();
ui.global::<MediaLogic>().on_next(move || {
sender_c.send(lib::ClientMessage::Next).unwrap();
});
let sender_c = sender_co.clone();
ui.global::<MediaLogic>().on_previous(move || {
sender_c.send(lib::ClientMessage::Previous).unwrap();
});
let ui_handle = ui.as_weak();
thread::spawn(move || {
loop {
if receiver_s.is_empty(){
thread::sleep(Duration::from_millis(200));
continue;
}
let msg = receiver_s.recv().unwrap();
info!("Received : {msg:?}");
match msg {
ServerMessage::NowPlaying(title) => ui_handle.upgrade_in_event_loop(move |handle| handle.set_now_playing(title.into())).unwrap(),
};
}
});
info!("Launching UI");
ui.run()
}

View File

@@ -5,7 +5,7 @@ export { MediaLogic }
export component AppWindow inherits Window {
background: white;
in-out property <string> now_playing: "test";
in-out property <string> now_playing: "Loading...";
VerticalLayout {
alignment: center;
@@ -15,8 +15,9 @@ export component AppWindow inherits Window {
horizontal-alignment: center;
color: black;
text: root.now_playing;
font-size:50px;
font-size:35px;
font-weight: 600;
wrap: word-wrap;
}
}

View File

@@ -14,6 +14,9 @@ export component MediaControls inherits Clickable {
width: 450px;
Clickable {
icon: @image-url("../../icons/back.png");
action => {
MediaLogic.previous();
}
}
Clickable {
@@ -25,6 +28,9 @@ export component MediaControls inherits Clickable {
}
Clickable {
icon: @image-url("../../icons/skip.png");
action => {
MediaLogic.next();
}
}

View File

@@ -1,30 +1,8 @@
use std::{
io::{self, Read, Write},
net::{TcpListener, TcpStream},
result::Result::Ok,
thread,
time::Duration,
};
pub mod tcp;
use std::fmt::{Debug, Display};
use anyhow::Result;
use crossbeam::channel::{self, Receiver, Sender};
use log::{error, info};
use rmp_serde::{Deserializer, Serializer};
use serde::{Deserialize, Serialize};
pub fn create_listener(address: &str, port: u16) -> TcpListener {
TcpListener::bind((address, port)).unwrap_or_else(|e| {
if e.kind() == io::ErrorKind::AddrInUse {
info!("Port {} is already being used by another program", port);
std::process::exit(1);
} else if e.kind() == io::ErrorKind::AddrNotAvailable {
info!("Address {} not available", address);
std::process::exit(1);
} else {
panic!("{:?}", e);
}
})
}
#[derive(Serialize, Deserialize, Debug)]
pub enum ClientMessage {
@@ -38,103 +16,16 @@ pub enum ServerMessage {
NowPlaying(String),
}
pub fn start_server() -> Result<(Sender<ServerMessage>, Receiver<ClientMessage>)> {
let (sender_c, reciever_c) = channel::bounded(4);
let (sender_s, reciever_s) = channel::bounded(4);
let server = create_listener("0.0.0.0", 6487);
info!("Listening on port {}", 6487);
server.set_nonblocking(true)?;
thread::spawn(move || {
for stream_result in server.incoming() {
match stream_result {
Ok(stream) => {
let reciever_s = reciever_s.clone();
let sender_c = sender_c.clone();
thread::spawn(move || {
if let Err(error) = handle_connection(stream, reciever_s, sender_c,"Client") {
error!("error while handling stream: {}", error);
}
})
} // Spawn a new thread, ignore the return value because we don't need to join threads
_ => continue,
};
}
});
Ok((sender_s, reciever_c))
pub enum Origin {
Server,
Client,
}
pub fn start_client() -> Result<(Sender<ClientMessage>, Receiver<ServerMessage>)> {
let (sender_c, reciever_c) = channel::bounded(4);
let (sender_s, reciever_s) = channel::bounded(4);
info!("Connecting");
let server = TcpStream::connect("127.0.0.1:6487")?;
info!("Connected successfully to {}", "127.0.0.1:6487");
server.set_nonblocking(true)?;
thread::spawn(move || {
handle_connection(server, reciever_c, sender_s, "Server") });
Ok((sender_c, reciever_s))
}
fn handle_connection<R,S>(
mut stream: TcpStream,
reciever: Receiver<R>,
sender: Sender<S>,
origin: &str,
) -> Result<()>
where R : Serialize
{
loop {
thread::sleep(Duration::from_millis(200));
if !reciever.is_empty() {
let msg: R = reciever.recv()?;
let mut msg_buf = Vec::new();
msg.serialize(&mut Serializer::new(&mut msg_buf))?;
stream.write(&msg_buf)?;
impl Display for Origin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Origin::Client => write!(f, "Client"),
Origin::Server => write!(f, "Server"),
}
handle_read(&mut stream, origin)?;
}
}
fn handle_read(stream: &mut TcpStream, origin: &str) -> Result<()> {
let mut byte_read = 0;
let mut read_buff = [0u8; 4096];
if byte_read == 0 {
byte_read = match stream.read(&mut read_buff) {
Ok(0) => return Ok(()), // Socket closed
Ok(n) => n,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => 0, // If there is no data, return 0 bytes written
Err(_) => return Ok(()),
};
}
// Write data from tunnel to stream
if byte_read > 0 {
info!("[{origin}] {:?}", &read_buff[0..byte_read]);
if origin == "Server" {
let a: ServerMessage = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap();
info!("{:?}", a);
} else {
let a: ClientMessage = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap();
info!("{:?}", a);
}
//sender_c.send(msg);
}
Ok(())
}

130
lib/src/tcp/mod.rs Normal file
View File

@@ -0,0 +1,130 @@
use crate::*;
use std::{
fmt::Debug,
io::{self, Read, Write},
net::{IpAddr, SocketAddr, TcpListener, TcpStream},
result::Result::Ok,
thread,
time::Duration,
};
use anyhow::Result;
use crossbeam::channel::{self, Receiver, Sender};
use log::{error, info};
use rmp_serde::Serializer;
use serde::{Deserialize, Serialize};
const PORT: u16 = 6478;
pub fn create_listener(address: &str, port: u16) -> TcpListener {
TcpListener::bind((address, port)).unwrap_or_else(|e| {
if e.kind() == io::ErrorKind::AddrInUse {
info!("Port {} is already being used by another program", port);
std::process::exit(1);
} else if e.kind() == io::ErrorKind::AddrNotAvailable {
info!("Address {} not available", address);
std::process::exit(1);
} else {
panic!("{:?}", e);
}
})
}
fn handle_connection<R, S>(
mut stream: TcpStream,
reciever: Receiver<R>,
sender: Sender<S>,
origin: Origin,
) -> Result<()>
where
R: Serialize + Debug,
S: for<'a> Deserialize<'a> + Debug,
{
stream.set_nonblocking(true)?;
loop {
thread::sleep(Duration::from_millis(200));
while !reciever.is_empty() {
let msg: R = reciever.recv()?;
let mut msg_buf = Vec::new();
msg.serialize(&mut Serializer::new(&mut msg_buf))?;
info!("Sent to {origin} : {msg:?}");
stream.write(&msg_buf)?;
}
handle_read(&mut stream, &origin, &sender)?;
}
}
fn handle_read<S>(stream: &mut TcpStream, origin: &Origin, sender: &Sender<S>) -> Result<()>
where
S: for<'a> Deserialize<'a> + Debug,
{
let mut byte_read = 0;
let mut read_buff = [0u8; 4096];
if byte_read == 0 {
byte_read = match stream.read(&mut read_buff) {
Ok(0) => return Ok(()), // Socket closed
Ok(n) => n,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => 0, // If there is no data, return 0 bytes written
Err(_) => return Ok(()),
};
}
if byte_read > 0 {
let msg: S = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap();
info!("[{origin}] {:?}", msg);
sender.send(msg).expect("Could not send");
}
Ok(())
}
pub fn start_server() -> Result<(Sender<ServerMessage>, Receiver<ClientMessage>)> {
let (sender_c, reciever_c) = channel::bounded(4);
let (sender_s, reciever_s) = channel::bounded(4);
let server = create_listener("0.0.0.0", PORT);
info!("Listening on port {}", PORT);
server.set_nonblocking(true)?;
thread::spawn(move || {
for stream_result in server.incoming() {
match stream_result {
Ok(stream) => {
let reciever_s = reciever_s.clone();
let sender_c = sender_c.clone();
thread::spawn(move || {
if let Err(error) =
handle_connection(stream, reciever_s, sender_c, Origin::Client)
{
error!("error while handling stream: {}", error);
return;
}
})
} // Spawn a new thread, ignore the return value because we don't need to join threads
_ => continue,
};
}
});
Ok((sender_s, reciever_c))
}
pub fn start_client(ip: IpAddr) -> Result<(Sender<ClientMessage>, Receiver<ServerMessage>)> {
let (sender_c, reciever_c) = channel::bounded(4);
let (sender_s, reciever_s) = channel::bounded(4);
let serv_addr = SocketAddr::new(ip, PORT);
let server = TcpStream::connect(serv_addr)?;
info!("Connected successfully to {}", serv_addr);
thread::spawn(move || handle_connection(server, reciever_c, sender_s, Origin::Server));
Ok((sender_c, reciever_s))
}

View File

@@ -4,5 +4,5 @@ commands:
deckui: |
cargo watch -d 2 -w deckui -- sh ./buildui.sh
deckapp: |
cargo watch -d 2 -w deckapp -w lib -x "run -p deckapp"
cargo watch -d 2 -w deckapp -w lib -x "run --release -p deckapp"