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)
})();