absolutely destroy this beautiful static html file by converting it to a slightly more dynamic flask app + docker container
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
95037c0f82
commit
dfca30bb66
@ -1,5 +1,21 @@
|
|||||||
pipeline:
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: plugins/docker
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
settings:
|
||||||
|
registry: gitea.malloc.hackerbots.net
|
||||||
|
repo: gitea.malloc.hackerbots.net/tdfischer/hackerbots-live
|
||||||
|
daemon_off: true
|
||||||
|
auto_tag: true
|
||||||
|
tags: latest
|
||||||
|
username:
|
||||||
|
from_secret: GITEA_USERNAME
|
||||||
|
password:
|
||||||
|
from_secret: GITEA_PASSWORD
|
||||||
deploy:
|
deploy:
|
||||||
|
when:
|
||||||
|
branch: master
|
||||||
image: alpine:3.13
|
image: alpine:3.13
|
||||||
volumes:
|
volumes:
|
||||||
- /var/web/live/:/deploy
|
- /var/web/live/:/deploy
|
||||||
|
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.9-alpine
|
||||||
|
|
||||||
|
RUN pip install pipenv && pip install gunicorn==20.1.0 eventlet==0.30.2
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
RUN pipenv install --system
|
||||||
|
|
||||||
|
CMD ["gunicorn", "-k", "eventlet", "-w", "1", "-b", "0.0.0.0:5000", "app:app"]
|
15
Pipfile
Normal file
15
Pipfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[[source]]
|
||||||
|
name = "pypi"
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
flask = "*"
|
||||||
|
flask-json = "*"
|
||||||
|
requests = "*"
|
||||||
|
flask-socketio = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.9"
|
51
app.py
Normal file
51
app.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from flask import Flask, render_template
|
||||||
|
from flask_json import FlaskJSON, as_json
|
||||||
|
import requests
|
||||||
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = 'secret'
|
||||||
|
json = FlaskJSON(app)
|
||||||
|
socketio = SocketIO(app, logger=True, log_output=True)
|
||||||
|
socketio.init_app(app, cors_allowed_origins="*")
|
||||||
|
|
||||||
|
CURRENT_LISTENERS = 0
|
||||||
|
|
||||||
|
def heartbeat():
|
||||||
|
global CURRENT_LISTENERS
|
||||||
|
backendFetch = requests.get('http://hackerbots.net:8000/status-json.xsl')
|
||||||
|
stats = backendFetch.json()['icestats']['source']
|
||||||
|
print(stats)
|
||||||
|
return {
|
||||||
|
'title': stats.get('title', 'Unknown Track'),
|
||||||
|
'listeners': CURRENT_LISTENERS,
|
||||||
|
'show_title': 'Flask Test Show'
|
||||||
|
}
|
||||||
|
|
||||||
|
@socketio.on('live.hello')
|
||||||
|
def on_hello(*args, **kwargs):
|
||||||
|
print("Hello!!!")
|
||||||
|
|
||||||
|
@socketio.on('connect')
|
||||||
|
def on_new_listener():
|
||||||
|
global CURRENT_LISTENERS
|
||||||
|
CURRENT_LISTENERS += 1
|
||||||
|
socketio.emit('live.heartbeat', heartbeat())
|
||||||
|
|
||||||
|
@socketio.on('disconnect')
|
||||||
|
def on_new_listener():
|
||||||
|
global CURRENT_LISTENERS
|
||||||
|
CURRENT_LISTENERS -= 1
|
||||||
|
socketio.emit('live.heartbeat', heartbeat())
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
@app.route("/status.json")
|
||||||
|
@as_json
|
||||||
|
def status():
|
||||||
|
return heartbeat()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
socketio.run(app, port=5000, host='0.0.0.0')
|
208
index.html
208
index.html
@ -1,208 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<title>live.hackerbots.net</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<!-- <script type="text/javascript" src="../dist/butterchurn.js"></script> -->
|
|
||||||
<script type="text/javascript" src="https://unpkg.com/lodash"></script>
|
|
||||||
<script type="text/javascript" src="https://unpkg.com/butterchurn"></script>
|
|
||||||
<script type="text/javascript" src="https://unpkg.com/butterchurn-presets"></script>
|
|
||||||
<script type="text/javascript" src="https://unpkg.com/butterchurn-presets/lib/butterchurnPresetsExtra.min.js"></script>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Bungee+Spice&display=swap" rel="stylesheet">
|
|
||||||
<script
|
|
||||||
src="https://code.jquery.com/jquery-3.1.1.min.js"
|
|
||||||
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/normalize.css/normalize.css" />
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(function() {
|
|
||||||
var visualizer = null;
|
|
||||||
var rendering = false;
|
|
||||||
var sourceNode = null;
|
|
||||||
var delayedAudible = null;
|
|
||||||
var cycleInterval = null;
|
|
||||||
var presets = {};
|
|
||||||
var presetKeys = [];
|
|
||||||
var presetIndexHist = [];
|
|
||||||
var presetIndex = 0;
|
|
||||||
var presetCycle = true;
|
|
||||||
var presetCycleLength = 15000;
|
|
||||||
var presetRandom = true;
|
|
||||||
|
|
||||||
function startRenderer() {
|
|
||||||
if (!document.hidden && visualizer) {
|
|
||||||
var fps = 60;
|
|
||||||
setTimeout(() => {
|
|
||||||
requestAnimationFrame(startRenderer);
|
|
||||||
}, 1000 / fps);
|
|
||||||
visualizer.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function visToggle() {
|
|
||||||
if (!document.hidden) {
|
|
||||||
startRenderer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener('visibilitychange', visToggle);
|
|
||||||
|
|
||||||
function initPlayer() {
|
|
||||||
var audioContext = new AudioContext();
|
|
||||||
var mediaElement = document.getElementById('audio-player');
|
|
||||||
var mediaSrc = audioContext.createMediaElementSource(mediaElement);
|
|
||||||
|
|
||||||
//var canvas = document.getElementById('canvas');
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
visualizer = butterchurn.default.createVisualizer(audioContext, canvas , {
|
|
||||||
width: canvas.width,
|
|
||||||
height: canvas.height,
|
|
||||||
pixelRatio: window.devicePixelRatio || 1,
|
|
||||||
textureRatio: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
mediaSrc.connect(audioContext.destination);
|
|
||||||
visualizer.connectAudio(mediaSrc);
|
|
||||||
|
|
||||||
var presets = {};
|
|
||||||
if (window.butterchurnPresets) {
|
|
||||||
Object.assign(presets, butterchurnPresets.getPresets());
|
|
||||||
}
|
|
||||||
if (window.butterchurnPresetsExtra) {
|
|
||||||
Object.assign(presets, butterchurnPresetsExtra.getPresets());
|
|
||||||
}
|
|
||||||
|
|
||||||
visualizer.loadPreset(_.sample(presets));
|
|
||||||
|
|
||||||
cycleInterval = setInterval(() => {
|
|
||||||
visualizer.loadPreset(_.sample(presets), 5.7);
|
|
||||||
}, presetCycleLength);
|
|
||||||
|
|
||||||
$(canvas).click(() => {
|
|
||||||
visualizer.loadPreset(_.sample(presets), 1.0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateMeta() {
|
|
||||||
$.getJSON('/status.json', (data) => {
|
|
||||||
var meta = data.icestats.source;
|
|
||||||
var title = meta.title;
|
|
||||||
var listeners = meta.listeners;
|
|
||||||
console.log(data.icestats.source);
|
|
||||||
$('#title').text(title);
|
|
||||||
$('#listeners').text(listeners + " listeners");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMeta();
|
|
||||||
setInterval(updateMeta, 3000);
|
|
||||||
|
|
||||||
var isSetup = false;
|
|
||||||
|
|
||||||
$('#the-button').click(() => {
|
|
||||||
if (!isSetup) {
|
|
||||||
initPlayer();
|
|
||||||
isSetup = true;
|
|
||||||
$('#the-button').remove();
|
|
||||||
startRenderer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style type="text/css">
|
|
||||||
html, body, #canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
animation: rainbow-bg 10s linear;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
#the-button {
|
|
||||||
animation: throb 2.5s ease-in-out infinite alternate;
|
|
||||||
font-family: 'Bungee Spice', cursive;
|
|
||||||
font-size: 6em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#metadata {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
z-index: 1000;
|
|
||||||
color: #fff;
|
|
||||||
font-family: 'Bungee Spice', sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
padding: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rainbow-bg{
|
|
||||||
100%,0%{
|
|
||||||
background-color: rgb(255,0,0);
|
|
||||||
}
|
|
||||||
8%{
|
|
||||||
background-color: rgb(255,127,0);
|
|
||||||
}
|
|
||||||
16%{
|
|
||||||
background-color: rgb(255,255,0);
|
|
||||||
}
|
|
||||||
25%{
|
|
||||||
background-color: rgb(127,255,0);
|
|
||||||
}
|
|
||||||
33%{
|
|
||||||
background-color: rgb(0,255,0);
|
|
||||||
}
|
|
||||||
41%{
|
|
||||||
background-color: rgb(0,255,127);
|
|
||||||
}
|
|
||||||
50%{
|
|
||||||
background-color: rgb(0,255,255);
|
|
||||||
}
|
|
||||||
58%{
|
|
||||||
background-color: rgb(0,127,255);
|
|
||||||
}
|
|
||||||
66%{
|
|
||||||
background-color: rgb(0,0,255);
|
|
||||||
}
|
|
||||||
75%{
|
|
||||||
background-color: rgb(127,0,255);
|
|
||||||
}
|
|
||||||
83%{
|
|
||||||
background-color: rgb(255,0,255);
|
|
||||||
}
|
|
||||||
91%{
|
|
||||||
background-color: rgb(255,0,127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes throb{
|
|
||||||
from{
|
|
||||||
transform: scale(1.0);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: scale(0.75);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<audio id='audio-player' autoplay src="https://live.hackerbots.net/listen.mp3">No audio</audio>
|
|
||||||
<div id="the-button">HIT IT, JACK ▶</div>
|
|
||||||
<div id="metadata"><span id="title"></span><p><span id="listeners"></span></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
116
static/main.css
Normal file
116
static/main.css
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
html, body, #canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butterviz {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
animation: rainbow-bg 10s linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader {
|
||||||
|
font-size: xx-large;
|
||||||
|
font-family: 'Bungee Spice';
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
transition: all .75s ease;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader p {
|
||||||
|
padding: 10rem 5rem 1rem 5rem;
|
||||||
|
background: url(/static/raccoon.gif);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: top center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader.show {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#the-button {
|
||||||
|
animation: throb 2.5s ease-in-out infinite alternate;
|
||||||
|
font-family: 'Bungee Spice', cursive;
|
||||||
|
font-size: 6em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#metadata {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 1000;
|
||||||
|
color: #fff;
|
||||||
|
font-family: 'Bungee Spice', sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 3rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: center;
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rainbow-bg{
|
||||||
|
100%,0%{
|
||||||
|
background-color: rgb(255,0,0);
|
||||||
|
}
|
||||||
|
8%{
|
||||||
|
background-color: rgb(255,127,0);
|
||||||
|
}
|
||||||
|
16%{
|
||||||
|
background-color: rgb(255,255,0);
|
||||||
|
}
|
||||||
|
25%{
|
||||||
|
background-color: rgb(127,255,0);
|
||||||
|
}
|
||||||
|
33%{
|
||||||
|
background-color: rgb(0,255,0);
|
||||||
|
}
|
||||||
|
41%{
|
||||||
|
background-color: rgb(0,255,127);
|
||||||
|
}
|
||||||
|
50%{
|
||||||
|
background-color: rgb(0,255,255);
|
||||||
|
}
|
||||||
|
58%{
|
||||||
|
background-color: rgb(0,127,255);
|
||||||
|
}
|
||||||
|
66%{
|
||||||
|
background-color: rgb(0,0,255);
|
||||||
|
}
|
||||||
|
75%{
|
||||||
|
background-color: rgb(127,0,255);
|
||||||
|
}
|
||||||
|
83%{
|
||||||
|
background-color: rgb(255,0,255);
|
||||||
|
}
|
||||||
|
91%{
|
||||||
|
background-color: rgb(255,0,127);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes throb{
|
||||||
|
from{
|
||||||
|
transform: scale(1.0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(0.75);
|
||||||
|
}
|
||||||
|
}
|
60
static/main.js
Normal file
60
static/main.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { io } from "https://cdn.socket.io/4.4.1/socket.io.esm.min.js"
|
||||||
|
import Visualizer from "./viz.js"
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
function updateMeta(data) {
|
||||||
|
var title = data.title;
|
||||||
|
var listeners = data.listeners;
|
||||||
|
$('#title').text(title);
|
||||||
|
$('#listeners').text(listeners + " listeners");
|
||||||
|
}
|
||||||
|
|
||||||
|
function visToggle() {
|
||||||
|
if (!document.hidden) {
|
||||||
|
visualizer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('visibilitychange', visToggle);
|
||||||
|
|
||||||
|
var visualizer = null;
|
||||||
|
var audioPlayer = document.getElementById("audio-player");
|
||||||
|
|
||||||
|
$('#the-button').click(() => {
|
||||||
|
if (visualizer == null) {
|
||||||
|
var loaderWidget = document.getElementById("loader");
|
||||||
|
|
||||||
|
audioPlayer.play();
|
||||||
|
|
||||||
|
$(loaderWidget).addClass("show");
|
||||||
|
$('#the-button').remove();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
visualizer = new Visualizer(audioPlayer);
|
||||||
|
setTimeout(() => {
|
||||||
|
visualizer.start();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var socket = io();
|
||||||
|
socket.on('connect', () => {
|
||||||
|
socket.emit('live.hello', {});
|
||||||
|
});
|
||||||
|
socket.on('live.heartbeat', (heartbeat) => {
|
||||||
|
console.log('heartbeat')
|
||||||
|
console.log(heartbeat)
|
||||||
|
updateMeta(heartbeat)
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("resize", _.debounce(() => {
|
||||||
|
visualizer.resize();
|
||||||
|
}, 100));
|
||||||
|
|
||||||
|
audioPlayer.addEventListener("waiting", (evt) => {
|
||||||
|
$(loaderWidget).addClass("show");
|
||||||
|
});
|
||||||
|
audioPlayer.addEventListener("playing", (evt) => {
|
||||||
|
$(loaderWidget).removeClass("show");
|
||||||
|
});
|
||||||
|
}
|
BIN
static/raccoon.gif
Normal file
BIN
static/raccoon.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
67
static/viz.js
Normal file
67
static/viz.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import "https://unpkg.com/butterchurn@2.6.7"
|
||||||
|
import "https://unpkg.com/butterchurn-presets@2.4.7"
|
||||||
|
import "https://unpkg.com/butterchurn-presets/lib/butterchurnPresetsExtra.min.js"
|
||||||
|
|
||||||
|
export default class Visualizer {
|
||||||
|
butterViz = null;
|
||||||
|
canvas = null;
|
||||||
|
cycleInterval = null;
|
||||||
|
presetCycleLength = 15000;
|
||||||
|
|
||||||
|
constructor(mediaElement) {
|
||||||
|
var audioContext = new AudioContext();
|
||||||
|
var mediaSrc = audioContext.createMediaElementSource(mediaElement);
|
||||||
|
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.canvas.className = "butterviz";
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
this.canvas.width = this.canvas.offsetWidth;
|
||||||
|
this.canvas.height = this.canvas.offsetHeight;
|
||||||
|
this.butterViz = butterchurn.default.createVisualizer(audioContext, this.canvas , {
|
||||||
|
width: this.canvas.width,
|
||||||
|
height: this.canvas.height,
|
||||||
|
pixelRatio: window.devicePixelRatio || 1,
|
||||||
|
textureRatio: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
mediaSrc.connect(audioContext.destination);
|
||||||
|
this.butterViz.connectAudio(mediaSrc);
|
||||||
|
|
||||||
|
var presets = {};
|
||||||
|
if (window.butterchurnPresets) {
|
||||||
|
Object.assign(presets, butterchurnPresets.getPresets());
|
||||||
|
}
|
||||||
|
if (window.butterchurnPresetsExtra) {
|
||||||
|
Object.assign(presets, butterchurnPresetsExtra.getPresets());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cycleInterval = setInterval(() => {
|
||||||
|
this.butterViz.loadPreset(_.sample(presets), 5.7);
|
||||||
|
}, this.presetCycleLength);
|
||||||
|
|
||||||
|
$(this.canvas).click(() => {
|
||||||
|
this.butterViz.loadPreset(_.sample(presets), 1.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.butterViz.loadPreset(_.sample(presets));
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.canvas.width = window.innerWidth;
|
||||||
|
this.canvas.height = window.innerHeight;
|
||||||
|
this.butterViz.setRendererSize(this.canvas.offsetWidth, this.canvas.offsetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (!document.hidden && this.butterViz) {
|
||||||
|
var fps = 60;
|
||||||
|
setTimeout(() => {
|
||||||
|
requestAnimationFrame(() => {this.start()});
|
||||||
|
}, 1000 / fps);
|
||||||
|
this.butterViz.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
templates/index.html
Normal file
31
templates/index.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>live.hackerbots.net</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<script type="text/javascript" src="https://unpkg.com/lodash"></script>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Bungee+Spice&display=swap" rel="stylesheet">
|
||||||
|
<script
|
||||||
|
src="https://code.jquery.com/jquery-3.1.1.min.js"
|
||||||
|
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/normalize.css/normalize.css" />
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { init } from "/static/main.js"
|
||||||
|
$(init)
|
||||||
|
</script>
|
||||||
|
<link rel="stylesheet" href="/static/main.css" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<audio crossorigin="anonymous" id='audio-player' src="https://live.hackerbots.net/listen.mp3">No audio</audio>
|
||||||
|
<div id="loader"><p>Buffering...</p></div>
|
||||||
|
<div id="the-button">HIT IT, JACK ▶</div>
|
||||||
|
<div id="metadata"><div id="title">Loading...</div><div id="listeners"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user