Checkpoint this work with some basic websockets and prettier layout
This commit is contained in:
parent
1428cc3289
commit
24b9c14f92
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
BIN
sounds/boo.wav
BIN
sounds/boo.wav
Binary file not shown.
59
src/main.rs
59
src/main.rs
|
@ -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?)
|
||||
}
|
||||
|
|
138
views/index.hbs
138
views/index.hbs
|
@ -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:
|
||||
<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>
|
||||
|
||||
|
|
Loading…
Reference in New Issue