Checkpoint this work with some basic websockets and prettier layout

This commit is contained in:
R Tyler Croy 2020-09-17 15:25:26 -07:00
parent 1428cc3289
commit 24b9c14f92
No known key found for this signature in database
GPG Key ID: E5C92681BEF6CEA2
6 changed files with 312 additions and 11 deletions

124
Cargo.lock generated
View File

@ -266,6 +266,19 @@ dependencies = [
"syn",
]
[[package]]
name = "async-tungstenite"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5c45a0dd44b7e6533ac4e7acc38ead1a3b39885f5bbb738140d30ea528abc7c"
dependencies = [
"futures-io",
"futures-util",
"log",
"pin-project",
"tungstenite",
]
[[package]]
name = "atomic-waker"
version = "1.0.0"
@ -446,6 +459,12 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "cache-padded"
version = "1.1.1"
@ -478,6 +497,8 @@ name = "charcuterie"
version = "0.1.0"
dependencies = [
"async-std",
"async-tungstenite",
"futures",
"glob",
"handlebars",
"log",
@ -728,6 +749,27 @@ dependencies = [
"web-sys",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "futures"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.5"
@ -735,6 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -743,6 +786,17 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
[[package]]
name = "futures-executor"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.5"
@ -776,6 +830,12 @@ dependencies = [
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
[[package]]
name = "futures-task"
version = "0.3.5"
@ -801,9 +861,13 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project",
"pin-utils",
"proc-macro-hack",
@ -925,6 +989,17 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]]
name = "http"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-types"
version = "2.4.0"
@ -976,6 +1051,15 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6854dd77ddc4f9ba1a448f487e27843583d407648150426a30c2ea3a2c39490a"
[[package]]
name = "input_buffer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754"
dependencies = [
"bytes",
]
[[package]]
name = "itoa"
version = "0.4.6"
@ -1257,7 +1341,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
"sha-1 0.8.2",
]
[[package]]
@ -1595,6 +1679,19 @@ dependencies = [
"opaque-debug 0.2.3",
]
[[package]]
name = "sha-1"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpuid-bool",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "sha1"
version = "0.6.0"
@ -1850,6 +1947,25 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
[[package]]
name = "tungstenite"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"input_buffer",
"log",
"rand",
"sha-1 0.9.1",
"url",
"utf-8",
]
[[package]]
name = "typenum"
version = "1.12.0"
@ -1908,6 +2024,12 @@ dependencies = [
"serde",
]
[[package]]
name = "utf-8"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
[[package]]
name = "vec-arena"
version = "0.5.2"

View File

@ -8,6 +8,8 @@ edition = "2018"
[dependencies]
async-std = { version = "~1.6.1", features = ["attributes"] }
async-tungstenite = "~0.8.0"
futures = "~0.3.5"
glob = "~0.3.0"
handlebars = { version = "~3.4.0", features = ["dir_source"] }
log = "~0.4.11"

BIN
assets/board.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

View File

@ -2,33 +2,53 @@
extern crate serde_json;
use async_std::task;
use async_std::net::{TcpListener, TcpStream};
use futures::prelude::*;
use glob::glob;
use handlebars::Handlebars;
use log::*;
use rodio::Source;
use serde::Deserialize;
use tide::{Body, Request, StatusCode};
use std::fs::File;
use std::io::BufReader;
#[derive(Debug, Deserialize)]
struct Query {
admin: String,
}
async fn index(req: Request<()>) -> Result<Body, tide::Error> {
let mut hb = Handlebars::new();
hb.register_templates_directory(".hbs", "views")
.expect("Failed to register templates");
if let Err(failure) = hb.register_templates_directory(".hbs", "views") {
error!("Failed to render: {:?}", failure);
return Err(tide::Error::from_str(StatusCode::InternalServerError, "Could not render templates"))
}
let mut admin = false;
if let Ok(query) = req.query::<Query>() {
info!("admin: {:?}", query);
admin = query.admin == "rtyler";
}
let mut sounds = vec![];
for sound in glob("sounds/*.wav").expect("Failed to glob sounds") {
if let Ok(sound) = sound {
if let Some(name) = sound.as_path().file_stem() {
sounds.push(name.to_os_string().into_string().unwrap());
// factory whistle is a special admin only sound ^_^
if name != "factorywhistle" {
sounds.push(name.to_os_string().into_string().unwrap());
}
}
}
}
info!("sounds: {:?}", sounds);
let view = hb.render("index", &json!({"sounds": sounds}))
let view = hb.render("index",
&json!({"sounds": sounds, "admin" : admin}))
.expect("Failed to render");
let mut body = Body::from_string(view);
body.set_mime("text/html");
@ -48,6 +68,24 @@ async fn play(req: Request<()>) -> tide::Result {
}
}
async fn handle_websocket(stream: TcpStream) {
let addr = stream
.peer_addr()
.expect("connected streams should have a peer address");
info!("Peer address: {}", addr);
let ws_stream = async_tungstenite::accept_async(stream)
.await
.expect("Error during the websocket handshake occurred");
info!("New WebSocket connection: {}", addr);
let (write, read) = ws_stream.split();
read.forward(write)
.await
.expect("Failed to forward message")
}
#[async_std::main]
async fn main() -> Result<(), tide::Error> {
pretty_env_logger::init();
@ -55,6 +93,19 @@ async fn main() -> Result<(), tide::Error> {
let mut app = tide::new();
app.at("/play/:sound").post(play);
app.at("/").get(index);
app.at("/assets").serve_dir("assets/");
task::spawn(async move {
let addr = "0.0.0.0:9078";
// Create the event loop and TCP listener we'll accept connections on.
let try_socket = TcpListener::bind(&addr).await;
let listener = try_socket.expect("Failed to bind");
info!("Listening on: {}", addr);
while let Ok((stream, _)) = listener.accept().await {
task::spawn(handle_websocket(stream));
}
});
Ok(app.listen("0.0.0.0:9077").await?)
}

View File

@ -1,16 +1,142 @@
<html>
<head>
<title>Charcuterie</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<script type="text/javascript">
<!--
let username = "Unknown Ape";
function playSound(sound) {
const http = new XMLHttpRequest();
http.open("POST", `${window.location.origin}/play/${sound}`);
http.send();
socket.send(JSON.stringify({
action: `${sound}'ed`,
show: true,
actor: username,
}));
return false;
}
var timeoutHandle;
function countdown(minutes) {
document.getElementById("timer").className = "text-success";
var seconds = 60;
var mins = minutes
function tick() {
var counter = document.getElementById("timer");
var current_minutes = mins-1
if (current_minutes == 0) {
counter.className = "text-danger";
}
seconds--;
counter.innerHTML =
current_minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
if( seconds > 0 ) {
timeoutHandle=setTimeout(tick, 1000);
} else {
if(mins > 1){
// countdown(mins-1); never reach “00″ issue solved:Contributed by Victor Streithorst
setTimeout(function () { countdown(mins - 1); }, 1000);
}
}
}
tick();
}
function gainEntry() {
wsConnect();
username = document.getElementById("username").value;
document.getElementById("entry").style = "display: none;"
document.getElementById("board").style = "display: block;"
}
function wsConnect() {
const socket = new WebSocket(`ws://${window.location.hostname}:9078`);
window.socket = socket;
socket.onopen = (event) => {
console.log('Websocket connected! 😺')
socket.send(JSON.stringify({
action: "connected",
show: true,
actor: username,
}));
}
socket.onerror = (err) => {
console.error('😿 Websocket encountered an error:', err)
socket.close()
wsConnect(socket.app)
}
socket.onclose = (event) => {
console.log('Websocketconnection lost, retrying..')
wsConnect(socket.app)
}
socket.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.show) {
const messages = document.getElementById("messages");
let m = document.createElement('div');
m.innerHTML = `${data.actor} ${data.action}`;
messages.insertBefore(m, messages.firstChild);
}
};
}
-->
</script>
<body>
A sound board for Meet, get it?
<div class="container">
<center>
<img src="/assets/board.png" />
<br/>
A sound board for Meet, get it?
</center>
{{#each sounds}}
<form action="/play/{{this}}" method="POST">
<input type="submit" value="{{this}}"/>
</form>
{{/each}}
<br clear="all"/>
<div id="entry" class="container">
<div class="container">
<form action="/" method="GET" onsubmit="gainEntry(); return false;">
<input class="w-25" id="username" placeholder="Enter your name"/>
<input type="submit" value="Join"/>
</form>
</div>
</div>
<div id="board" style="display: none;" class="container">
<div class="row">
<div class="col-8">
{{#each sounds}}
<input class="w-25 m-2 btn btn-primary" type="button" onclick="return playSound('{{this}}');" value="{{this}}"/>
{{/each}}
</div>
<div class="col-4">
<div class="row">
Time remaining:&nbsp;
<div id="timer" style="font-weight: bold;" class="text-success"></div>
</div>
<div class="row">
<div id="messages"></div>
</div>
</div>
</div>
</div>
{{#if admin}}
<div class="mt-5 container">
<input class="btn btn-danger" type="button" onclick="return playSound('factorywhistle');" value="Time is up"/>
</div>
{{else}}
{{/if}}
</div>
</body>
</html>