Last active
September 21, 2021 17:45
Revisions
-
Victor Toulouse revised this gist
May 14, 2021 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -24,6 +24,7 @@ audio.controls = true; function setupRecorder(composer) { if (!navigator.mediaDevices.getUserMedia) { console.log('getUserMedia not supported'); return; } const button = document.createElement('div'); -
Victor Toulouse created this gist
May 14, 2021 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,122 @@ // ==UserScript== // @name Element Voice Messages // @namespace http://tampermonkey.net/ // @version 0.1 // @description Adds a button to record voice messages. Click the button to start recording, click again to stop. // @author vctls // @match https://app.element.io/ // @grant none // ==/UserScript== /* As of 2021-05-14, Element.io does not have voice messages. But the attachment button contains a hidden file input, which if changed automatically triggers the Upload modal. This script uses the MediaRecorder interface to record audio blobs, which are then put into the hidden input using a DataTransfer object. An <audio> html element is appended to the upload modal to preview the recorded audio. */ // Create an audio element to preview recorded audio. const audio = document.createElement('audio'); audio.controls = true; function setupRecorder(composer) { if (!navigator.mediaDevices.getUserMedia) { console.log('getUserMedia not supported'); } const button = document.createElement('div'); button.id = 'recordButton'; button.className = 'mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_record'; button.role = 'button'; // Add some style. const style = document.createElement('style'); style.appendChild(document.createTextNode( '#recordButton::before{background-color: indianred; border-radius: 20px; border-style: double;}' + '.recording{animation: blinker 1s linear infinite;}' + '.recording:before{background-color:red !important;}' + '@keyframes blinker {50% {opacity: 0;}}' )); document.getElementsByTagName('head')[0].appendChild(style); const constraints = {audio: true}; let chunks = []; let onSuccess = function (stream) { const mediaRecorder = new MediaRecorder(stream); button.onclick = function () { if (mediaRecorder.state === 'recording') { mediaRecorder.stop(); } else { mediaRecorder.start(); // Set two minutes security timeout. setTimeout(function () { mediaRecorder.stop(); }, 120000); } } mediaRecorder.onstart = function (e) { document.getElementById('recordButton').classList.add('recording'); }; mediaRecorder.onstop = function (e) { document.getElementById('recordButton').classList.remove('recording'); const blob = new Blob(chunks, {'type': 'audio/ogg; codecs=opus'}); chunks = []; audio.src = window.URL.createObjectURL(blob); // Put blob in file input. let file = new File([blob], "audio.opus",{type:"audio/ogg", lastModified:new Date().getTime()}); let container = new DataTransfer(); container.items.add(file); let fileInput = document.querySelector('.mx_MessageComposer_upload input[type="file"]'); fileInput.files = container.files; // Trigger input change. let event = document.createEvent("UIEvents"); event.initUIEvent("change", true, true); fileInput.dispatchEvent(event); } mediaRecorder.ondataavailable = function (e) { chunks.push(e.data); } } let onError = function (err) { console.log('The following error occured: ' + err); } navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError); composer.append(button); } function check(changes, observer) { const composer = document.querySelector('.mx_MessageComposer_row'); if (composer !== null && !composer.hasButton) { setupRecorder(composer); composer.hasButton = true; } const modalContent = document.getElementById('mx_Dialog_content'); if ( modalContent !== null && !modalContent.hasAudio && modalContent.textContent.includes('audio.opus') ) { console.log('Appending audio clip'); modalContent.append(audio); modalContent.hasAudio = true; } } (function () { 'use strict'; (new MutationObserver(check)).observe(document, {childList: true, subtree: true}); })();