NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Youtube Audio Device Selector // @namespace https://github.com/DoKM/ // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @homepageURL https://github.com/DoKM/youtube-volume-selector // @grant none // @version 1.0 // @author DoKM // @license MIT // @description Audio Device Selector for youtube // ==/UserScript== (async function () { async function init() { addGlobalStyle(`/* Dropdown Button */ .dropbtn { background-color: #ffffff00; color: white; padding: 10px 16px; font-size: 16px; border: none; cursor: pointer; } /* Dropdown button on hover & focus */ .dropbtn:hover, .dropbtn:focus { background-color: #ffffff13; } /* The container <div> - needed to position the dropdown content */ .dropdown { position: relative; display: inline-block; } /* Dropdown Content (Hidden by Default) */ .dropdown-content { display: none; position: absolute; background-color: #f1f1f1; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 301; } /* Links inside the dropdown */ .dropdown-content div { color: black; padding: 12px 16px; text-decoration: none; display: block; z-index: 301; } /* Change color of dropdown links on hover */ .dropdown-content div:hover {background-color: #ddd} /* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */ .show {display:block;}`); const constrains = { audio: true, video: true } navigator.mediaDevices.getUserMedia(constrains) let gotDevices = function (deviceInfos) { let useableDevices = [] for (var i = 0; i !== deviceInfos.length; ++i) { var deviceInfo = deviceInfos[i]; if (deviceInfo.kind === 'audiooutput' && (deviceInfo.deviceId != "default" && deviceInfo.deviceId != "communications")) { useableDevices.push(deviceInfo) } } return useableDevices; } let tempDevices = await navigator.mediaDevices.enumerateDevices() let outputDevices = await gotDevices(tempDevices) //now callback will show device names and deviceId. const menu = createMenu() const v = document.getElementsByTagName("video")[0] createMenuOptions(menu, outputDevices, v) } function createMenuOptions(menu, outputDevices, video) { outputDevices.forEach(device => { let box = document.createElement("div") box.innerText = device.label box.addEventListener("click", () => { video.setSinkId(device.deviceId) }) menu.appendChild(box) }) } function createMenu() { const infoBox = getInfoBox() const dropDownBox = document.createElement("div") dropDownBox.classList.add("dropdown") const button = document.createElement("button") button.classList.add("dropbtn") button.innerText = "Audio Devices" const dropDown = document.createElement("div") dropDown.classList.add("dropdown-content") button.addEventListener("click", () => { dropDown.classList.toggle("show"); }) window.onclick = function (event) { if (!event.target.matches('.dropbtn')) { var dropdowns = document.getElementsByClassName("dropdown-content"); var i; for (i = 0; i < dropdowns.length; i++) { var openDropdown = dropdowns[i]; if (openDropdown.classList.contains('show')) { openDropdown.classList.remove('show'); } } } } dropDownBox.appendChild(button) dropDownBox.appendChild(dropDown) infoBox.appendChild(dropDownBox) return dropDown; } function getInfoBox() { const infoElements = document.querySelectorAll("#info"); let validInfoBox infoElements.forEach(element => { if (element.tagName == "DIV" && element.className == 'style-scope ytd-watch-flexy') { validInfoBox = element } }) return validInfoBox } function addGlobalStyle(css) { var head, style; head = document.getElementsByTagName('head')[0]; if (!head) { return; } style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); } let interval = setInterval(() => { if (getInfoBox != undefined) { clearInterval(interval) setTimeout(init(), 500) } }, 500) })();