您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Pick a preferred audio output device for HTML5 audio and video elements.
// ==UserScript== // @name Audio Output Picker // @namespace https://greasyfork.org/en/users/670188-hacker09?sort=daily_installs // @version 2 // @description Pick a preferred audio output device for HTML5 audio and video elements. // @author hacker09 // @include * // @icon https://i.imgur.com/RHFAjq3.png // @grant GM_registerMenuCommand // @grant GM_deleteValue // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (async()=>{ 'use strict'; const findMediaElement = async ()=>{ //Finds the active media element, whether it's a <video> or an <audio> tag. while (true) { let mediaElement = document.querySelector('video, audio, .video-stream'); //Look for the standard video player OR any audio element first. if (mediaElement) {return mediaElement;} await new Promise(resolve => setTimeout(resolve, 150)); //If nothing is found, wait and retry. } }; const getDevicesWithPermission = async () => { //Get permissions and devices. await navigator.mediaDevices.getUserMedia({ audio: true }); return (await navigator.mediaDevices.enumerateDevices()).filter(d => d.kind === "audiooutput"); }; const mediaElement = await findMediaElement(); //Find the active <video> or <audio> element. const setDevice = (id) => mediaElement.setSinkId(id); const applySavedDevice = async () => { const id = GM_getValue(location.href) || GM_getValue(location.hostname); if (id) { //Only act if a saved setting exists. const devices = await getDevicesWithPermission(); if(devices && devices.some(d => d.deviceId === id)){ setDevice(id); } } }; const selectAudioDevice = async (scope) => { const devices = await getDevicesWithPermission(); //Ask for mic permission. if (!devices || devices.length === 0) return; //Exit if mic permission denied or no devices. const choice = parseInt(prompt(devices.map((d,i) => `${i+1}: ${d.label||`Device ${i+1}`}`).join('\n')), 10)-1; if(devices[choice]) { const key = scope === 'URL' ? location.href : location.hostname; GM_setValue(key, devices[choice].deviceId); setDevice(devices[choice].deviceId); location.reload(); //Reload to apply the setting and update the menu text from "Save" to "DELETE". } }; ['URL', 'DOMAIN'].forEach(scope => { const key = scope === 'URL' ? location.href : location.hostname; if (GM_getValue(key)) { GM_registerMenuCommand(`DELETE ${scope}`, () => { GM_deleteValue(key); location.reload(); }); } else { GM_registerMenuCommand(`Save ${scope}`, () => selectAudioDevice(scope)); } }); mediaElement.addEventListener('loadedmetadata', applySavedDevice); })();