Last active
August 6, 2021 18:02
-
-
Save mohno007/49611fbf8f4675562e53cda2783cb0fa to your computer and use it in GitHub Desktop.
Add volume and mute controls for Google Meet
This file contains hidden or 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 characters
// ==UserScript== | |
// @name Google Meet Audio Control | |
// @namespace http://tampermonkey.net/ | |
// @version 0.2.0 | |
// @description Add volume and mute controls for Google Meet | |
// @author mohno007 | |
// @license CC0-1.0 | |
// @downloadURL https://gist.github.com/mohno007/49611fbf8f4675562e53cda2783cb0fa/raw/google_meet_audio_control.user.js | |
// @updateURL https://gist.github.com/mohno007/49611fbf8f4675562e53cda2783cb0fa/raw/google_meet_audio_control.user.js | |
// @match https://meet.google.com/* | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const mount = ({ root, view, reducers, initialState }) => { | |
const reduce = (state) => { | |
const mountedReducers = reducers(state); | |
const actions = new Proxy({}, { | |
get: (target, prop, receiver) => (...args) => { | |
const newState = mountedReducers[prop](...args); | |
reduce(newState); | |
}, | |
}) | |
const newView = view({ state, actions }); | |
[...root.childNodes].forEach((node) => node.remove()); | |
root.appendChild(newView); | |
}; | |
reduce(initialState); | |
}; | |
const addChild = (parent, child) => { | |
if (child == null) {} | |
else if (child instanceof Array) { child.forEach((c) => addChild(parent, c)); } | |
else if (child instanceof HTMLElement || child instanceof DocumentFragment) { parent.appendChild(child); } | |
else { parent.appendChild(document.createTextNode(child)); } | |
}; | |
const h = (name, props = {}, children = []) => { | |
const elem = name && name.length > 0 ? document.createElement(name) : document.createDocumentFragment(); | |
for (const [prop, value] of Object.entries(props)) { | |
if (prop === 'class') { | |
elem.className = value; | |
} else if (prop.startsWith('on') && typeof value === 'function') { | |
const eventName = prop.slice(2).toLowerCase(); | |
elem.addEventListener(eventName, value); | |
} else if (prop === 'style' && typeof value === 'object') { | |
Object.assign(elem.style, value); | |
} else { | |
elem[prop] = value; | |
} | |
} | |
for (const child of children) { | |
addChild(elem, child); | |
} | |
return elem; | |
}; | |
const audioView = ({ audio, onChange }) => { | |
const handleVolumeChange = (ev) => { | |
const value = ev.target.value; | |
onChange({ type: 'volume', value }); | |
}; | |
const handleToggleMute = (ev) => { | |
ev.preventDefault(); | |
onChange({ type: 'mute', value: !audio.muted }); | |
}; | |
return h('div', {}, [ | |
h('input', { type: 'range', min: 0, max: 100, style: { width: '180px' }, onChange: handleVolumeChange, value: audio.volume }), | |
h('button', { type: 'button', style: { width: '40px', background: audio.muted ? '#F99' : '#eee' }, onClick: handleToggleMute }, [audio.muted ? '๐' : '๐']), | |
]); | |
}; | |
const view = ({ state, actions }) => { | |
const handleAudioChange = (audio) => (ev) => { | |
if (ev.type === 'volume') { | |
actions.updateVolume({ audio, value: ev.value }); | |
} else if (ev.type === 'mute') { | |
actions.updateMuted({ audio, value: ev.value }); | |
} | |
}; | |
return h('div', { | |
style: { | |
zIndex: 2**31-1, | |
position: 'fixed', | |
top: 0, | |
left: 'calc(50vw - 120px)', | |
width: '240px', | |
margin: 0, | |
padding: 0, | |
opacity: 0.2, | |
transition: 'opacity .1s ease-in-out', | |
}, | |
onMouseEnter: (ev) => { ev.target.style.opacity = 1.0; }, | |
onMouseLeave: (ev) => { ev.target.style.opacity = 0.2; }, | |
}, [ | |
audioView({ audio: state, onChange: handleAudioChange(state) }), | |
]); | |
}; | |
const initialState = { volume: 100.0, muted: false }; | |
const reducers = (state) => ({ | |
updateVolume({ value }) { | |
[...document.querySelectorAll('audio')].forEach(e => { e.volume = value / 100.0; }); | |
console.debug(`UPDATED VOLUME ${value}`); | |
return { ...state, volume: value }; | |
}, | |
updateMuted({ value }) { | |
[...document.querySelectorAll('audio')].forEach(e => { e.muted = value; }); | |
console.debug(`UPDATED MUTED: ${value}`); | |
return { ...state, muted: value }; | |
}, | |
}); | |
const root = document.createElement('div'); | |
document.body.appendChild(root); | |
mount({ root, view, reducers, initialState }); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great shit bro