Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
client.rs 9.48 KiB
use futures_signals::signal::Mutable;
use std::net::TcpStream;
use tungstenite::{Message, WebSocket};
use uuid::Uuid;
use crate::song::ApiSong;
use crate::song::ApiSongState;
use crate::song::Song;
use crate::websockets_command::Command;
pub fn handle_client(websocket: &mut WebSocket<TcpStream>, playlist: &Mutable<Vec<Song>>) -> () {
println!("New client connected!");
loop {
if !websocket.can_write() {
println!("Client connection lost!");
break;
}
handle_client_command(websocket, playlist);
}
}
fn handle_client_command(
websocket: &mut WebSocket<TcpStream>,
playlist: &Mutable<Vec<Song>>,
) -> () {
let msg = websocket.read_message();
if let Err(_) = msg {
eprintln!("Error receiving websocket message!");
return;
}
let msg = msg.unwrap();
match msg {
Message::Text(json) => {
let cmd = serde_json::from_str(&json);
if let Err(_) = cmd {
eprintln!("Invalid command json!");
return;
}
let cmd = cmd.unwrap();
match cmd {
Command::Add { url } => {
if let Some(host) = url.host_str() {
if host != "youtube.com"
&& host != "www.youtube.com"
&& host != "youtu.be"
&& host != "www.youtu.be"
{
eprintln!("Not a youtube url!");
return;
}
} else {
eprintln!("Not a youtube url!");
return;
}
if let Some(path_segments) = url.path_segments().map(|c| c.collect::<Vec<_>>())
{
if path_segments[0] != "watch" && path_segments[0] != "v" {
eprintln!("Not a youtube video url!");
return;
}
} else {
eprintln!("Not a youtube video url!");
return;
}
let mut playlist = playlist.lock_mut();
(*playlist).push(Song::Queued {
uuid: Uuid::new_v4(),
url: url.clone(),
});
drop(playlist);
println!("Added song with url {}!", url);
}
Command::Remove { uuid } => {
let uuid = Uuid::parse_str(&uuid);
if let Err(_) = uuid {
eprintln!("Invalid uuid in command 'Remove'!");
return;
}
let uuid = uuid.unwrap();
let mut playlist = playlist.lock_mut();
if let Some(index) = (*playlist).iter().position(|song| song.equals_uuid(&uuid))
{
playlist.remove(index);
println!("Skipped song with uuid {}!", uuid);
} else {
eprintln!("No song with uuid {} to skip!", uuid);
}
drop(playlist);
}
Command::Playlist => {
let playlist = playlist.lock_ref();
let playlist_api = generate_playlist_api(&playlist);
drop(playlist);
send_playlist_api(websocket, &playlist_api);
}
}
}
Message::Close(_) => {
println!("WebSocket connection closed by peer!");
}
_ => {}
}
}
fn generate_playlist_api(playlist: &Vec<Song>) -> Vec<ApiSong> {
playlist
.iter()
.map(|song| match song {
Song::Queued { uuid, url, .. } => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: None,
artist: None,
artist_url: None,
length: None,
thumbnail_url: None,
state: ApiSongState::Queued,
},
Song::InfoFetching { uuid, url, .. } => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: None,
artist: None,
artist_url: None,
length: None,
thumbnail_url: None,
state: ApiSongState::InfoFetching,
},
Song::InfoFetched {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::InfoFetched,
},
Song::Downloading {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::Downloading,
},
Song::Downloaded {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::Downloaded,
},
Song::Converting {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::Converting,
},
Song::Ready {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::Ready,
},
Song::Playing {
uuid,
url,
title,
artist,
artist_url,
length,
thumbnail_file,
..
} => ApiSong {
uuid: uuid.to_string(),
url: url.to_string(),
title: title.to_owned(),
artist: artist.to_owned(),
artist_url: if let Some(artist_url) = artist_url {
Some(artist_url.to_string())
} else {
None
},
length: length.to_owned(),
thumbnail_url: thumbnail_file.to_owned(),
state: ApiSongState::Playing,
},
})
.collect()
}
fn send_playlist_api(websocket: &mut WebSocket<TcpStream>, playlist_api: &Vec<ApiSong>) -> () {
let playlist_json = serde_json::to_string(&playlist_api).unwrap();
let msg = Message::text(playlist_json);
if let Err(_) = websocket.write_message(msg) {
eprintln!("Error sending websocket message!");
return;
}
/* println!("Returned playlist contents!");
for song in playlist_api {
println!("{:?}", song);
} */
}