123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- <section class="new-clip">
- <style>
- #record-voice + label + div, #upload-file + label + div {
- margin-left: 1em;
- padding: .5em;
- }
- #script-select {
- max-width: calc(100% - 6ch);
- }
- #title-input {
- max-width: calc(100% - 16ch);
- }
- section.new-clip .new-clip {
- margin-bottom: 1em;
- }
- .new-clip .hbox {
- display: flex;
- align-items: center;
- }
- .new-clip .hbox select, .hbox input[type=text] {
- width: 100%;
- flex-shrink: 1;
- }
- .new-clip .hbox label {
- margin-left: 1em;
- margin-right: .5em;
- }
- .new-clip .hbox label:first-child {
- margin-left: 0px;
- }
- .new-clip h2 {
- margin-top: 0px;
- }
- .new-clip #submit {
- float: right;
- }
- #upload-file + label + div, #record-voice + label + div {
- }
-
- #record-voice + label + div { display: none; }
- #record-voice:checked + label + div { display: block; }
- #upload-file + label + div { display: none;}
- #upload-file:checked + label + div { display: block;}
- </style>
- <h2>Add a Clip</h2>
- <div class="hbox">
- <label for="title-input">Title</label>
- <input type="text" id="title-input" value="Untitled"/>
- <label for="color-select">Color</label>
- <input type="color" id="color-select" value="#000000"/>
- </div>
- <br>
-
- <div class="hbox">
- <label for="script-select">Script </label>
- <select class="script" id="script-select">
- {% include 'speech-scripts.html' %}
- </select>
- </div>
- <textarea rows="6" id="transcript"></textarea>
- <script>
- globalState.render(['transcript'], current => {
- $('#transcript').value = current.transcript;
- });
- function formatTranscript(value) {
- return value.replace(/\s\s+/g, '\n').trim()
- }
- globalState.set('transcript', formatTranscript($('#script-select').value));
- $('#script-select').addEventListener('change', evt => {
- let value = $('#script-select').value;
- globalState.set('transcript', value == 'custom' ? '' : formatTranscript(value));
- });
- $('#transcript').addEventListener('change', evt => {
- if (globalState.get('transcript') != $('#transcript').value) {
- globalState.set('transcript', $('#transcript').value);
- $('#script-select').value = 'custom';
- }
- });
- </script>
-
- <input type="radio" name="add-recording" id="upload-file" checked>
- <label for="upload-file">Upload a file</label>
- <div>
- <input type='file' id='audio-file-input' name='recording'
- capture="user" accept="audio/*,video/*,audio/mpeg,audio/mp4,audio/x-aiff,audio/ogg,audio/vorbis,audio/opus,audio/vnd.wav,.wav,.mp3,.ogg,.opus,.m4a,.webm"/>
- </div>
- <br>
- <input type="radio" name="add-recording" id="record-voice">
- <label for="record-voice">Record your voice</label>
- <div>
- <button id="start-recording">⏺︎ Start</button>
- <button id="stop-recording">⏹︎ Stop</button>
- Recorded <span id="recording-count">0</span> seconds
- <br>
- <br>
- <audio controls id="recording-preview"></audio>
- </div>
-
- <button id="submit" class='attention' onclick="submitAudio()">Add Clip</button>
- <script>
- try {
-
- let mediaRecorder = null;
- let audioChunks = [];
- let recordedAudio = null;
- function recordAudio() {
- let stopButton = document.querySelector("#stop-recording");
- let startButton = document.querySelector("#start-recording");
- const startRecording = () => {
- $('#recording-count').innerHTML = '0';
- let recordingInterval = setInterval(() => {
- $('#recording-count').innerHTML = Number($('#recording-count').innerHTML) + 1;
- }, 1000);
- mediaRecorder.addEventListener("stop", () => {
- clearInterval(recordingInterval);
- });
- stopButton.disabled = false;
- startButton.disabled = true;
- audioChunks = [];
- mediaRecorder.start();
- mediaRecorder.addEventListener("dataavailable", event => {
- audioChunks.push(event.data);
- });
- }
- function alertRecordingFailure(exception) {
- alert(
- String(window.location).slice(0, 5) == 'https'
- ?
- "Sorry, something went wrong. "
- + "Please ensure that your microphone is connected "
- + "and that your browser allows us to record from it. "
- + "Alternately, you can try uploading a file recorded from a native app instead."
- :
- "Recording in-browser is only available "
- + "when accessing the site over a secure connection. "
- + "To record, reload the page ensuring "
- + "that the URL starts with 'https', "
- + "including the 's' at the end."
- );
- console.error(exception);
- }
- if (mediaRecorder) { startRecording(); } else {
- try {
- navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
-
- mediaRecorder = new MediaRecorder(stream);
-
- mediaRecorder.addEventListener("stop", () => {
- stopButton.disabled = true;
- startButton.disabled = false;
- const audioBlob = new Blob(audioChunks);
- recordedAudio = audioBlob;
- let reader = new FileReader(recordedAudio);
- reader.readAsDataURL(recordedAudio);
- reader.addEventListener("load", () => {
- $('#recording-preview').src = reader.result.replace(
- /data:.*;base64/, "data:audio/wav;base64"
- );
- $('#recording-preview').load();
- });
- });
- stopButton.addEventListener("click", () => {mediaRecorder.stop();}, false);
- startRecording();
- }).catch(exception => alertRecordingFailure(exception));
- } catch(exception) { alertRecordingFailure(exception); }
- }
- }
- $('#start-recording').addEventListener('click', evt => {
- recordAudio();
- });
- function submitAudio() {
- let clip = new Clip();
- let formData = new FormData();
- let transcriptText = $('#transcript').value;
- formData.append('transcript', transcriptText);
- formData.append('referrer', document.referrer);
- formData.append('screen-width', window.screen.width);
- formData.append('screen-height', window.screen.height);
- formData.append('lang', navigator.language || navigator.userLanguage);
- clip.transcript = transcriptText;
- clip.title = $('#title-input').value;
- clip.color = $('#color-select').value;
- if ($('#upload-file').checked) {
- fileInput = $('#audio-file-input')
- if (fileInput.files.length < 1) return
- let recording = fileInput.files[0];
- formData.append('recording', recording);
- clip.loadAudioFile(recording);
- } else if ($('#record-voice').checked) {
- formData.append('recording', recordedAudio);
- clip.loadAudioFile(recordedAudio);
- } else {
- alert('Please upload a file or make a recording.');
- return;
- }
- $('button.new-clip').classList.add('hidden');
- $('button.new-clip + .spinner').classList.remove('hidden');
-
- hideModal();
-
- fetch('./backend.cgi', { method: 'POST', body: formData})
- .then(response => {
- if (!response.ok) throw new Error(response.status);
- if (response.headers.get('Content-Length') < 2) return {};
- return response.json();
- }).then(data => {
- $('button.new-clip').classList.remove('hidden');
- $('button.new-clip + .spinner').classList.add('hidden');
- console.assert(data.hasOwnProperty('phones') && data.phones.length < 1);
-
- clip.loadResponse(data);
- globalState.mutate('clips', clips => clips.push(clip));
- globalState.set('playing', false);
- globalState.set('playingClip', clip);
- globalState.set('previewClip', clip);
- }).catch(() => {
- alert(
- "Sorry, we weren't able to process your recording. " +
- "Make sure to speak clearly and ensure " +
- "that your transcript matches the audio. " +
- "For increased chance of success," +
- "try using short clips with a 1-second period of silence."
- );
- $('button.new-clip').classList.remove('hidden');
- $('button.new-clip + .spinner').classList.add('hidden');
- });
- }
- } catch (exception) {
- alert("An unknown error occured.");
- console.error(exception);
- }
- </script>
- </section>
|