Skip to content

Instantly share code, notes, and snippets.

@marek-saji
Last active February 1, 2024 08:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marek-saji/184024f096be7b9ddf54272d6d225af8 to your computer and use it in GitHub Desktop.
Save marek-saji/184024f096be7b9ddf54272d6d225af8 to your computer and use it in GitHub Desktop.
userscript: Zoom Web Client with less clicking
// ==UserScript==
// @name Zoom Web Client with less clicking
// @description Make using Web Client in Zoom a default and require less clicking.
// @author Marek ‘saji’ Augustynowicz <marek.aug@gmail.com>
// @copyright ISC
// @version 1.0.3
// @icon https://st1.zoom.us/zoom.ico
// @supportURL https://gist.github.com/marek-saji/184024f096be7b9ddf54272d6d225af8
//
// @updateURL https://gist.githubusercontent.com/marek-saji/184024f096be7b9ddf54272d6d225af8/raw
// @downloadURL https://gist.githubusercontent.com/marek-saji/184024f096be7b9ddf54272d6d225af8/raw
// @grant none
// @match https://*.zoom.us/j/*
// @match https://*.zoom.us/wc/*/join?*
// @match https://*.zoom.us/wc/join/*
// ==/UserScript==
const JOB_DEBOUNCE_MS = 100;
const JOB_MAX_TRIES = 10;
function log (...args)
{
console.debug('zoom.userscript', ...args);
}
function debounce (callback)
{
let timeoutId;
return () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(
() => { callback(); },
JOB_DEBOUNCE_MS
);
}
}
/**
* Things we want to do rely on DOM elements be present. Re–try wevery time DOM is mutated.
* Will stop trying when {@param job} returns true or after trying {@see JOB_MAX_TRIES} times.
*/
function tryUntilItWorks (jobs)
{
const jobsName = jobs.map(job => job.name).join('+');
log(jobsName, 'queued');
const runJobs = () => {
for (const job of jobs)
{
log(job.name, 'runs');
if (job()) {
log(job.name, 'succeeded');
return true;
}
}
};
if (!runJobs())
{
let tries = 0;
const observer = new MutationObserver(debounce(() => {
tries += 1;
if (tries > JOB_MAX_TRIES)
{
log(jobsName, 'tried to run', JOB_MAX_TRIES, 'times without a success. Giving up.');
observer.disconnect();
}
else if (runJobs())
{
observer.disconnect();
}
}));
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
}
}
/**
* Click “Continue in Browser”
*/
function preJoin ()
{
const joinFromBrowser = document.querySelector('[web_client]');
if (joinFromBrowser)
{
joinFromBrowser.click();
return true;
}
}
/**
* Escape a full–page iframe 🙄
*/
function escapeIFrame ()
{
const iframe = document.querySelector('iframe#webclient');
if (iframe?.src)
{
window.location.href = iframe.src;
return true;
}
}
/**
* Make sure audio aud video are enabled and join
*/
function join ()
{
const audioButton = document.getElementById('preview-audio-control-button');
const videoButton = document.getElementById('preview-video-control-button');
const joinButton = document.querySelector('.preview-join-button');
if (audioButton && videoButton && joinButton)
{
if (audioButton.getAttribute('aria-label') === 'Unmute')
{
audioButton.click();
}
if (videoButton.getAttribute('aria-label') === 'Start Video')
{
videoButton.click();
}
joinButton.click();
tryUntilItWorks([setup]);
return true;
}
}
/**
* Post–join setup for things Zoom WebClient can’t remember
* - Mirror video
* - Grid view
*/
function setup ()
{
// video mirror and grid view options might be rendered only after we open menus they are in for the first time
// click menu buttons twice to open and close them, which will trigger the render
const videoMenuButton = document.querySelector('[aria-label="More video controls"]');
const viewMenuButton = document.querySelector('[aria-label="View"]');
videoMenuButton?.click(); videoMenuButton?.click();
viewMenuButton?.click(); viewMenuButton?.click();
let videoMirrorButton = document.querySelector('[aria-label="Mirror My Video"]');
let galleryViewButton = document.querySelector('[aria-label="Gallery View"]');
if (videoMirrorButton && galleryViewButton)
{
if (!videoMirrorButton.classList.contains('video-option-menu__pop-menu--checked'))
{
videoMirrorButton.click();
}
if (!galleryViewButton.classList.contains('full-screen-widget__pop-menu--checked'))
{
galleryViewButton.click();
}
return true;
}
}
tryUntilItWorks([preJoin, escapeIFrame, join, setup]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment