Browse Source

ogg + visualizer (da detto)

boyska 4 years ago
parent
commit
8638bee434
5 changed files with 363 additions and 47 deletions
  1. 4 6
      app.py
  2. 139 0
      static/js/detto.js
  3. 29 0
      static/js/upload.js
  4. 148 0
      static/zerbino.css
  5. 43 41
      templates/index.htm

+ 4 - 6
app.py

@@ -27,7 +27,6 @@ def sendmail(sender, to, subject, body):
     #msg["Subject"] = subject
     #p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
     args = ["/usr/bin/mail", "-s", subject, '--'] + to
-    app.logger.info('asd: %s', ' '.join(args))
     p = Popen(args, stdin=PIPE)
     # p.communicate(msg.as_bytes())
     p.communicate(body.encode('utf8'))
@@ -58,21 +57,20 @@ def site(site):
 
 @app.route("/upload/<site>", methods=["POST"])
 def upload(site):
-    stream = request.files["audio_data"].stream
-    temp_fname = '_%s.wav' % uuid4().hex
+    temp_fname = '_%s.ogg' % uuid4().hex
     temp_fpath = os.path.join(UPLOAD_DIR, temp_fname)
     # prima scrivi su un file temporaneo, poi fai rename
     h = hashlib.new('sha1')
     with open(temp_fpath, "wb") as buf:
         while True:
-            some_data = stream.read(1024)
+            some_data = request.stream.read(1024)
             if not some_data:
                 break
             buf.write(some_data)
             h.update(some_data)
     # rinomina con l'hash
     app.logger.info('hash = %s', h.hexdigest())
-    fname = '%s.wav' % h.hexdigest()
+    fname = '%s.ogg' % h.hexdigest()
     os.rename(temp_fpath, os.path.join(UPLOAD_DIR, fname))
     if site in read_config()["sites"]:
         to = read_config()["sites"][site].get("email", [])
@@ -101,5 +99,5 @@ def dl(fname):
     with open(fpath, "rb") as buf:
         content = buf.read()
     r = make_response(content)
-    r.headers['Content-Type'] = 'audio/wav'  # TODO: better detect
+    r.headers['Content-Type'] = 'audio/ogg'  # TODO: better detect
     return r

+ 139 - 0
static/js/detto.js

@@ -0,0 +1,139 @@
+// detto - cesco@ventuordici.org - 2020
+
+/* THE CHINOTTO-WARE LICENSE" (Revision 42):
+ * cesco@ventuordici.org wrote this code.
+ * As long as you retain this notice you can do whatever you want with this stuff.
+ * I reserve the right to do the absolute minimum provided by law, up to and including absolutely nothing.
+ * If we meet some day, and you think this stuff is worth it, you can buy me a chinotto in return.
+ */
+
+var audioCtx;
+const canvas = document.querySelector('.visualizer');
+canvasCtx = canvas.getContext("2d");
+
+document.addEventListener('DOMContentLoaded', function() {
+	if(window.location.hash) {
+		document.getElementById('visualizer').style.display = 'none';
+		document.getElementById('record_audio').style.display = 'none';
+		document.getElementById('url').style.display = 'none';
+		document.getElementById('stop_record').style.display = 'none';
+
+	        var downloadbutton = document.getElementById('download');
+	        if (downloadbutton) { downloadbutton.addEventListener('click', content_download, false); }
+	} else {
+		document.getElementById('player').style.display = 'none';
+		document.getElementById('download').style.display = 'none';
+	        var recordbutton = document.getElementById('record_audio');
+	        if (recordbutton) {
+			recordbutton.addEventListener('click', processa_audio, false); 
+		}
+
+	        var stopbutton = document.getElementById('stop_record');
+	        if (stopbutton) { stopbutton.addEventListener('click', stop_recorder, false); }
+	}
+}, false);
+
+function processa_audio(){
+        filename = "";
+	fileext = ".webm";
+	var constraints = { audio: true };
+	var options = {
+		audioBitsPerSecond : 64000,
+		mimeType : 'audio/webm;codecs=opus'
+	}
+	audioCtx = new AudioContext();
+
+	navigator.mediaDevices.getUserMedia(constraints).then(stream => {
+		document.getElementById("record_audio").disabled = true;
+		window.localStream = stream;
+		const recorder = new MediaRecorder(stream, options);
+		window.localRecorder = recorder;
+		console.log("creato recorder " + recorder );
+		visualize(stream);
+		const chunks = [];
+
+		recorder.ondataavailable = function(e) {
+			// console.log("aggiungo chunk");
+			chunks.push(e.data);
+		}
+
+		recorder.onstop = function(e) {
+				console.log("stoppato recorder");
+				const blob = new Blob(chunks, { type: 'audio/webm' });
+				var reader = new FileReader();
+				reader.onload = function(event) {
+					console.log("reader loaded");
+					var content = event.target.result;
+					critta(content);
+					document.getElementById("record_audio").disabled = false;
+				}
+				reader.readAsArrayBuffer(blob);
+		}
+	recorder.start(1000);
+
+	});
+}
+
+function stop_recorder(stream){
+	console.log("stopping");
+	localRecorder.stop();
+	localStream.getAudioTracks()[0].stop();
+	localStream.getTracks().forEach(track => track.stop());
+}
+
+function visualize(stream) {
+  console.log("start visualize");
+
+  if(!audioCtx) {
+    audioCtx = new AudioContext();
+	console.log("creating audioctx" + audioCtx);
+  }
+
+  const source = audioCtx.createMediaStreamSource(stream);
+
+  const analyser = audioCtx.createAnalyser();
+  console.log("created analyser" + analyser);
+
+  analyser.fftSize = 2048;
+  const bufferLength = analyser.frequencyBinCount;
+  const dataArray = new Uint8Array(bufferLength);
+
+  source.connect(analyser);
+  draw()
+
+  function draw() {
+    // console.log("start draw");
+    WIDTH = canvas.width
+    HEIGHT = canvas.height;
+
+    requestAnimationFrame(draw);
+    analyser.getByteTimeDomainData(dataArray);
+
+    canvasCtx.fillStyle = 'rgb(255, 255, 255)';
+    canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
+    canvasCtx.lineWidth = 2;
+    canvasCtx.strokeStyle = 'rgb(150, 0, 0)';
+
+    canvasCtx.beginPath();
+
+    let sliceWidth = WIDTH * 1.0 / bufferLength;
+    let x = 0;
+
+
+    for(let i = 0; i < bufferLength; i++) {
+
+      let v = dataArray[i] / 128.0;
+      let y = v * HEIGHT/2;
+
+	if(i === 0) {
+		canvasCtx.moveTo(x, y);
+	} else {
+		canvasCtx.lineTo(x, y);
+	}
+	x += sliceWidth;
+    }
+
+    canvasCtx.lineTo(canvas.width, canvas.height/2);
+    canvasCtx.stroke();
+  }
+}

+ 29 - 0
static/js/upload.js

@@ -0,0 +1,29 @@
+/* jshint browser: true */
+/* global $ */
+
+function critta(content) {
+  // questa funzione non critta, ma si deve comunque chiamare così!
+  console.log(content)
+  var submit = new XMLHttpRequest()
+  submit.open('POST', "upload/" + $('body').data('site'),true)
+  submit.onload = function (evt) {
+    console.log('ok, caricato!')
+    $('#bar').hide()
+    $('#status').text('Hai lasciato un messaggio in segreteria, grazie!')
+  }
+  submit.send(content)
+}
+
+$(function () {
+  $('#stop_record').hide()
+  $('#visualizer').hide()
+  $('#record_audio').on('click', function () {
+    $('#record_audio').hide()
+    $('#stop_record').show()
+    $('#visualizer').show()
+  })
+  $('#stop_record').on('click', function () {
+    $('#stop_record').hide()
+    $('#visualizer').hide()
+  })
+})

+ 148 - 0
static/zerbino.css

@@ -0,0 +1,148 @@
+html,body {
+	margin:0;
+	padding:0;
+	height:100%;
+}
+
+#container {
+	position:relative;
+	margin:0 auto; /* center, not in IE5 */
+	height:auto !important; /* real browsers */
+	height:100%; /* IE6: treaded as min-height*/
+	min-height:100%; /* real browsers */
+}
+
+#content {
+	padding:1em 1em 3em; /* bottom padding for footer */
+}
+
+
+#footer {
+	margin:0.3em;
+	position: absolute;
+	bottom: 0; 
+	left: 0; 
+	display: block;  
+	unicode-bidi: embed; 
+	font-family: monospace;
+}
+
+#image {
+	float:left;
+	margin: 0.3em;
+}
+
+#help {
+	margin: 0.3em;
+	margin-left: 0.5em;
+	float:left;
+}
+
+#bar {
+	margin:0.5em;
+	float: left;
+	clear: left;
+}
+
+#status {
+	margin:0.3em;
+	float: left;
+}
+
+#url {
+	margin:0.5em;
+	clear:both;
+}
+
+#input {
+	margin:0.3em;
+}
+
+
+#urlinput {
+	margin:0.3em;
+	width:55%;
+	padding: 0.5em;
+	border-radius:5px;
+	border:1px solid #7ac9b7;
+}
+
+#formarea {
+	clear: both;
+//	display:inline-block;
+// 	position:relative;
+}
+
+#text {
+	margin:0.3em;
+	width:95%;
+	padding: 0.5em;
+	border-radius:5px;
+	border:1px solid #7ac9b7;
+	white-space: pre;
+  	display:block;
+	float: left;
+}
+
+#save_text, #download, #clear_text {
+	margin:0.2em;
+	padding: 0.2em;
+//	display: block;
+	width: 8em;
+}
+
+.clippy{
+	opacity:.3;
+}
+
+
+ul {
+	list-style-type:none;
+}
+
+ul li {
+//	width:65%;
+}
+
+
+.hide {
+  display: none;
+}
+
+.hide:target {
+  display: block;
+}
+
+.visualizer, #player {
+  display: block;
+  margin: 0.5em;
+  width:41%;
+  height: 8vh;
+}
+
+.visualizer {
+  clear: both;
+}
+
+#player {
+ float: left;
+}
+
+#record_audio, #stop_record {
+  margin: 0.5em;
+  width:20%;
+}
+
+button:disabled {
+    background: #A5A5A5;
+}
+
+#download {
+	margin: 0.5em;
+	height: 8vh;
+	float: left;
+}
+
+#download img {
+	height: 5vh;
+}

+ 43 - 41
templates/index.htm

@@ -1,45 +1,47 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <meta charset="UTF-8">
-    <title>Manda un messaggino</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
-    <!-- Latest compiled and minified Bootstrap CSS -->
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-    <link rel="stylesheet" type="text/css" href="/static/style.css">
-  </head>
-  <body data-site="{{site}}">
-    <h1>Simple Recorder.js demo</h1>
-    <!--
-    <p><small>Made by the <a href="https://addpipe.com" target="_blank">Pipe Video Recording Platform</a></small></p>
-    <p>This demo uses <a href="https://github.com/mattdiamond/Recorderjs" target="_blank">Recorder.js</a> to record wav/PCM audio directly in the browser. Matt Diamond‘s <a target="_blank" href="https://github.com/mattdiamond/Recorderjs">Recorder.js</a> is a popular JavaScript library for recording audio in the browser as uncompressed pcm audio in .wav containers. Before it, the only way to record audio was to use Flash.</p>
-    <p>Check out the <a href="https://github.com/addpipe/simple-recorderjs-demo" target="_blank">code on GitHub</a> and <a href="https://addpipe.com/blog/using-recorder-js-to-capture-wav-audio-in-your-html5-web-site/" target="_blank">our blog post on using Recorder.js to capture WAV audio</a>.</p>
-    -->
-    <div id="controls">
-  	 <button id="recordButton">Registra</button>
-  	 <button id="pauseButton" disabled>Pausa</button>
-  	 <button id="stopButton" disabled>Stop</button>
-    </div>
-    <div id="formats">Format: start recording to see sample rate</div>
-  	<p><strong>Recordings:</strong></p>
-  	<ol id="recordingsList"></ol>
+<head>
+	<meta name="description" content="ricettacolo omertoso">
+	<meta name="keywords" content="file hosting sharing crypto vault">
+	<meta name="author" content="esiliati.org">
+	<script type="text/javascript" src="js/zerbino-up.js"></script>
+	<script type="text/javascript" src="js/zerbino-down.js"></script>
+	<script type="text/javascript" src="js/qrcode.js"></script>
+	<script type="text/javascript" src="js/clipboard.js" defer></script>
+        <script type="text/javascript" src="js/FileSaver.js"></script>
+        <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
+        <link rel="stylesheet" href="{{url_for('static', filename='zerbino.css')}}">
+	<link rel="stylesheet" href="/zerbino.css">
+	<link rel="shortcut icon" href="/key-128.png">
+    <title>Lascia un messaggio - {{site}}</title>
+</head>
+<body data-site="{{site}}">
+
+<div id="container">
+
+<div id="content">
+<!-- <div id="image"><a href="/"><img alt="logo" src="logo" height="80"/></a></div> -->
+<section id="help"></section>
+
+<button id="record_audio" type="button">Registra<img src="record.png"/></button>
+<button id="stop_record" type="button">Stop<img src="stop.png"/></button>
+
+<canvas id="visualizer" class="visualizer"></canvas>
+<audio  id="player" controls="controls"></audio>
+<button id="download" type="button"><img src="download.png"/></button>
+<br/>
+
+<div id="bar"></div>
+<div id="status"></div>
+
+</div>
+
+<div id="footer"><footer>:: powered by <a href="//www.esiliati.org/">www.esiliati.org</a> - info: <a href="mailto:staff@esiliati.org/">staff@esiliati.org</a> - provided by <a href="http://lepisma.ortiche.net">lepisma.ortiche.net</a>, a leaf of <a href="//uichi.ortiche.net">ortiche.net</a> ::</footer>
+</div>
+
+</div>
     <script src="{{ url_for('static', filename='js/jquery-1.12.4.js') }}"></script>
-    <script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
-    <!-- <script src="/static/js/audiorecord/lame.all.js"></script> -->
-    <!--
-    <script src="/static/js/audiorecord/audioRecord.js"></script>
-    -->
-    <!--
-    <script>
-        var rec = null;
-        window.onload = function init() {
-            audioRecorder.requestDevice(function(recorderObject){
-                rec = recorderObject;
-            }, {recordAsOGG: false});
-        };
-    </script>
-    -->
-  	<script src="/static/app.js"></script>
-  </body>
+    <script type="text/javascript" src="{{url_for('static', filename='js/upload.js')}}"></script>
+    <script type="text/javascript" src="{{url_for('static', filename='js/detto.js')}}"></script>
+</body>
 </html>