From 8f4a948ee762f90604023efd6cff10a580e045e7 Mon Sep 17 00:00:00 2001 From: vitrinekast Date: Thu, 7 Dec 2023 10:46:30 +0100 Subject: [PATCH] duo interface --- webinterface/index.php | 91 +++++++++++--------- webinterface/script.js | 184 +++++++++++++++++++++++++---------------- webinterface/style.css | 123 ++++++++++++++++++++++----- 3 files changed, 265 insertions(+), 133 deletions(-) diff --git a/webinterface/index.php b/webinterface/index.php index a4ce345..58fd746 100644 --- a/webinterface/index.php +++ b/webinterface/index.php @@ -3,49 +3,56 @@ - + Upload to the archive -
- - - -
-

Signal lost archive unzipped

-

some info on what this even is

- -
- - - - Upload a file instead - -
- - -
- -
- -

Info

-

Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla nobis eligendi earum assumenda iste ratione, blanditiis cupiditate natus non labore voluptatibus nostrum animi voluptatum ut quibusdam excepturi sit cumque molestiae?

- - -
- -
-
- + +
+
+ +
+

Great Success!

+

Your broadcast is now part of the community archive, and will be broadcasted asap.

+

Stay strong, survivor!

+
+ +
+

Radio Studio V0.1

+

Start your community stream, together with another survivor

+ Upload a file instead +
+ +
+

Making radio unzipped

+

waiting for your friend

+
+ +
+ +
+ +
+

Great Success!

+

Your broadcast is now part of the community archive, and will be broadcasted asap.

+

Stay strong, survivor!

+
+ +
+

Radio Studio V0.1

+

Start your community stream, together with another survivor

+ Upload a file instead +
+ +
+

Radio Studio V0.1

+

waiting for your friend

+
+ +
+
@@ -56,7 +63,7 @@
-
+ @@ -77,9 +84,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $uploaddir = getcwd() . '/uploads/'; $uploadfile = $uploaddir . basename($_FILES['userfile']['name']); - // TODO check file type - if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) { + $base = pathinfo($uploadfile)['filename']; + + echo shell_exec("/usr/bin/ffmpeg -i " . $uploadfile . " /var/www/html/radio/random-radio-radio/webinterface/uploads/" . $base . ".wav 2>&1"); + echo json_encode("File is valid, and was successfully uploaded.\n"); } else { echo json_encode("Possible file upload attack!\n"); diff --git a/webinterface/script.js b/webinterface/script.js index 9e139c6..46f3924 100644 --- a/webinterface/script.js +++ b/webinterface/script.js @@ -4,6 +4,8 @@ let chunks = []; var cContext; var xie = 1; var mediaRecorder = false; +var recordEls; +var state = "idle"; var sendFile = function (blob) { const formData = new FormData(); @@ -16,10 +18,13 @@ var sendFile = function (blob) { }); } +var analyser, dataArray, barW, waves; + var initVisual = function (stream) { + waves = []; var audioContext = new AudioContext(); const ms = audioContext.createMediaStreamSource(stream); - var analyser = audioContext.createAnalyser(); + analyser = audioContext.createAnalyser(); ms.connect(analyser); analyser.connect(audioContext.destination); @@ -27,64 +32,48 @@ var initVisual = function (stream) { analyser.fftSize = 128; var bufferLength = analyser.frequencyBinCount; - var dataArray = new Uint8Array(bufferLength); - var barW = canvasEl.width / bufferLength; - - - var waves = []; - - - var animateVisual = function () { - - var cHeight = canvasEl.height; - cContext.clearRect(0, 0, canvasEl.width, canvasEl.height); - - // scale from 0 to 255 - analyser.getByteFrequencyData(dataArray); - - // if height is 510 then maxscale/2; - // height is (canvasEl.height/2)/255 - var hRatio = (canvasEl.height) / 255; - - // analyser.getByteTimeDomainData(dataArray); + dataArray = new Uint8Array(bufferLength); + barW = 29; +} - // clear the previous shape - // cContext.fillStyle = "rgba(0, 0, 0, 1)"; - cContext.beginPath(); - // cContext.rect(0, 0, canvasEl.width, canvasEl.height); - cContext.fill(); - - - cContext.fill(); - waves.push(dataArray[0]); - var max = 144; - if (waves.length > (max + 1)) { - waves.shift(); - } +var animateVisual = function () { - cContext.fillStyle = "#231F20"; + var cHeight = canvasEl.height; + cContext.clearRect(0, 0, canvasEl.width, canvasEl.height); - var barWidth = Math.ceil(canvasEl.width / max); + // scale from 0 to 255 + analyser.getByteFrequencyData(dataArray); + // analyser.getByteTimeDomainData(dataArray); + var hRatio = (canvasEl.height) / 255; + cContext.beginPath(); + cContext.fill(); + cContext.fillStyle = "#231F20"; + waves.push(dataArray[0]); - // first value is oldest value - for (var i = waves.length; i > 0; i--) { - var x = canvasEl.width / 2 - ((waves.length - i) * barWidth); - var y = canvasEl.height / 2; - var bHeight = waves[i] * hRatio; - console.log(waves[i]); - - cContext.fillRect(x, y - (bHeight/2), barWidth, bHeight) - } + var max = 144; + if (waves.length > (max + 1)) { + waves.shift(); + } + var barWidth = Math.ceil(canvasEl.width / max); + // first value is oldest value + for (var i = waves.length; i > 0; i--) { + var x = canvasEl.width - ((waves.length - i) * barWidth); + var y = canvasEl.height / 2; + var bHeight = waves[i] * hRatio; + cContext.fillRect(x, y - (bHeight / 2), barWidth, bHeight) + } + if (state === "recording") { requestAnimationFrame(animateVisual); - } + } else { + cContext.clearRect(0, 0, canvasEl.width, canvasEl.height); - animateVisual(); + } } @@ -98,7 +87,6 @@ var initRecording = function (autostart) { initVisual(stream); mediaRecorder = new MediaRecorder(stream); - mediaRecorder.ondataavailable = (e) => { chunks.push(e.data); }; @@ -108,13 +96,30 @@ var initRecording = function (autostart) { type: mediaRecorder.mimeType }); chunks = []; - sendFile(blob).then((data) => { - micEl.setAttribute("state", "finished"); + + sendFile(blob).then((response) => { + if (response.status == 200) { + console.log("all went well"); + } else { + alert("Ooops (" + response.status + ").... something went wrong. we are saving the file on your device instead"); + var a = document.createElement("a"); + document.body.appendChild(a); + a.style = "display: none"; + var url = window.URL.createObjectURL(blob); + a.href = url; + a.download = new Date() + ".webm"; + a.click(); + window.URL.revokeObjectURL(url); + } + state = "finished"; + updateAllStates(state); }); } if (autostart) { mediaRecorder.start(); + animateVisual(); + } }) } else { @@ -122,51 +127,88 @@ var initRecording = function (autostart) { } } -var startRecording = function (e) { + + +var startRecording = function () { if (!mediaRecorder) { console.log("did not initalise"); - initRecording(true); - e.currentTarget.setAttribute("state", "recording"); + initRecording(false); + } else { if (mediaRecorder.state === "recording") { console.log("recording was already happening") mediaRecorder.stop(); - e.currentTarget.setAttribute("state", "uploading"); + state = "uploading"; + updateAllStates(state); } else { console.log("not yet recording, about tos tart"); mediaRecorder.start(); - e.currentTarget.setAttribute("active", "recording"); + animateVisual(); } } +} + +var updateAllStates = function (newState) { + document.body.setAttribute("state", newState); + recordEls.forEach((el) => { + el.setAttribute("state", newState) + }) +} + +var onRecordElsClick = function (e) { + console.log(e.currentTarget) + var target = e.currentTarget.getAttribute("target"); + + + if (state === "idle") { + state = "waiting"; + initRecording(false); + e.currentTarget.setAttribute("state", state); + document.body.setAttribute("state", state + "--" + e.currentTarget.getAttribute("target")); + + } else if (state === "waiting") { + if (e.currentTarget.getAttribute("state") === "waiting") { + state = "idle"; + e.currentTarget.setAttribute("state", state); + document.body.setAttribute("state", state); + } else { + state = "recording"; + startRecording(); + updateAllStates(state); + } + } else if (state === "recording") { + state === "uploading"; + updateAllStates(state); + mediaRecorder.stop(); + } } + + window.onload = function () { console.log("load app"); - micEl = document.querySelector('.fn-start-recording'); + recordEls = document.querySelectorAll(".fn-touch-record"); + document.body.setAttribute("state", state); + + recordEls.forEach((el) => { + el.addEventListener("click", onRecordElsClick) + }) + canvasEl = document.querySelector('canvas'); canvasEl.width = window.innerWidth; - canvasEl.height = window.innerHeight; cContext = canvasEl.getContext("2d"); - infoButEl = document.querySelector('.fn-open-dialog-info'); - formButEl = document.querySelector('.fn-open-dialog-form'); formDialogEl = document.querySelector('.fn-dialog-form'); - infoDialogEl = document.querySelector(".fn-dialog-info"); - - micEl.addEventListener('click', startRecording); - infoButEl.addEventListener("click", function (e) { - e.preventDefault(); - infoDialogEl.showModal(); + document.querySelectorAll('.fn-open-dialog-form').forEach((button) => { + button.addEventListener("click", function (e) { + e.preventDefault(); + formDialogEl.showModal(); + }) }) - formButEl.addEventListener('click', function (e) { - e.preventDefault(); - formDialogEl.showModal(); - }) - -} \ No newline at end of file +} diff --git a/webinterface/style.css b/webinterface/style.css index 93664b5..cd84097 100644 --- a/webinterface/style.css +++ b/webinterface/style.css @@ -5,7 +5,6 @@ --gutter-sm: .5rem; --gutter-md: 1rem; --gutter-lg: 3rem; - --font-size-base: 1rem; } @@ -23,7 +22,35 @@ body { height: 100%; margin: 0 0; padding: 0 0; + overflow: hidden; +} + +.state { + display: none; +} + +body[state="idle"] .state--idle { + display: block; +} +body[state="waiting--bottom"] .multi__bottom .state--waiting, +body[state="waiting--bottom"] .multi__top .state--idle { + display: block; +} + +body[state="waiting--top"] .multi__top .state--waiting, +body[state="waiting--top"] .multi__bottom .state--idle { + display: block; +} + +body[state="recording"] .state--recording { + display: block; + color: white; +} + + +body[state="finished"] .state--finished { + display: block; } @@ -31,7 +58,12 @@ canvas { position: fixed; width: 100%; height: 100%; + max-height: 120px; pointer-events: none; + z-index: -1; + top: 50%; + left: 0; + transform: translate3d(0, -50%, 0); } * { @@ -93,12 +125,15 @@ input[type="submit"] { .button--record { border-radius: 100%; - max-width: 500px; - max-height: 500px; + border: none; + max-width: 150px; + max-height: 150px; aspect-ratio: 1 / 1; - width: calc(100% - var(--gutter-lg) - var(--gutter-lg)); position: relative; background-color: transparent; + font-family: sans-serif; + overflow: hidden; + font-weight: 400; } .button--record:after { @@ -123,32 +158,32 @@ input[type="submit"] { top: 0; left: 0; background-color: var(--black); - border-radius: 100%; z-index: -1; - transform: scale(1); + transform: translate3d(0, 0%, 0); transition: transform .1s linear; } .button--record[state="recording"]:before, .button--record[state="uploading"]:before { - transform: scale(0); + transform: translate3d(0, 100%, 0); } .button--record[state="recording"], .button--record[state="uploading"] { color: var(--black); animation-delay: 1s; - border: none; - border: 1rem dotted var(--black); background-color: transparent; - border-right-color: transparent; - border-left-color: transparent; animation: 8s linear recording infinite; + border: .3rem dashed var(--black); +} + +.multi__bottom .button--record { + animation-delay: 1s; } .button--record[state="recording"] { - border-style: dashed; - animation-duration: 2s; + /* border-style: dashed; */ + animation-duration: 4s; } .button--record[state="recording"]:after, @@ -164,10 +199,15 @@ input[type="submit"] { content: "...uploading.."; } +.button--record[state="waiting"]:after { + animation-duration: 8s; + content: "...waiting.."; +} + .button--record[state="finished"]:after { animation-duration: 8s; - content: "File is uploaded"; + content: "Upload complete"; } @@ -179,17 +219,13 @@ input[type="submit"] { dialog { width: auto; - max-width: 400px; + max-width: min(90vw, 400px); flex-direction: column; } dialog::backdrop { - background-image: linear-gradient(45deg, - magenta, - rebeccapurple, - dodgerblue, - green); - opacity: 0.75; + background: black; + opacity: 0.5; } .dialog__close { @@ -218,4 +254,49 @@ label { dialog[open] { display: flex; +} + + +.multi { + display: flex; + width: 100%; + height: 100%; + padding: var(--gutter-md); + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.multi__top, +.multi__bottom { + height: 100%; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; +} + +.multi h1 { + margin-top: -.5rem; + +} + +.multi button { + margin-top: auto; +} + +.multi__top { + transform: rotate(180deg); +} + +.button--multi { + aspect-ratio: 1/1; + border-radius: 100%; + max-width: 230px; + max-height: 230px; + transition: none; +} + +.button--multi:first-of-type { + transform: rotate(180deg); } \ No newline at end of file