您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Neura V3 - Enhance YouTube video quality controls. Join the community at https://discord.gg/aeXXdqEKxw
当前为
// ==UserScript== // @name Neura V3 // @name:zh-TW Neura V3 // @name:zh-CN Neura V3 // @name:ja Neura V3 // @author Kan // @version 3.1 // @description Neura V3 - Enhance YouTube video quality controls. Join the community at https://discord.gg/aeXXdqEKxw // @description:zh-tw Neura V3 - 增強YouTube影片質量控制。加入社群 https://discord.gg/aeXXdqEKxw // @description:zh-cn Neura V3 - 增强YouTube视频质量控制。加入社区 https://discord.gg/aeXXdqEKxw // @description:ja Neura V3 - YouTube動画品質制御の強化。コミュニティに参加 https://discord.gg/aeXXdqEKxw // @match *://www.youtube.com/* // @match *://m.youtube.com/* // @match *://www.youtube-nocookie.com/* // @exclude *://www.youtube.com/live_chat* // @grant GM.getValue // @grant GM.setValue // @grant GM.deleteValue // @grant GM.listValues // @grant GM.registerMenuCommand // @grant GM.unregisterMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @license MIT // @namespace .gg/Neura // ==/UserScript== /*jshint esversion: 11 */ (function() { "use strict"; const DEFAULT_SETTINGS = { targetResolution: "hd2160", expandMenu: false, debug: false }; const BROWSER_LANGUAGE = navigator.language || navigator.userLanguage; const GET_PREFERRED_LANGUAGE = () => { if (BROWSER_LANGUAGE.startsWith('zh') && BROWSER_LANGUAGE !== 'zh-TW'){ return 'zh-CN'; } else { return BROWSER_LANGUAGE; } }; const TRANSLATIONS = { 'en-US': { qualityMenu: 'Quality Menu', debug: 'DEBUG' }, 'zh-TW': { qualityMenu: '畫質選單', debug: '偵錯' }, 'zh-CN': { qualityMenu: '画质菜单', debug: '排错' }, 'ja': { qualityMenu: '画質メニュー', debug: 'デバッグ' } }; const GET_LOCALIZED_TEXT = () => { const language = GET_PREFERRED_LANGUAGE(); return TRANSLATIONS[language] || TRANSLATIONS['en-US']; }; const QUALITIES = { highres: 4320, hd2880: 2880, hd2160: 2160, hd1440: 1440, hd1080: 1080, hd720: 720, large: 480, medium: 360, small: 240, tiny: 144, }; const PREMIUM_INDICATOR_LABEL = "Premium"; let userSettings = { ...DEFAULT_SETTINGS }; let useCompatilibtyMode = false; let isBrokenOrMissingGMAPI = false; let menuItems = []; let videoId = ''; let resolvedTarget = ''; let targetLabel = ''; let ytPlayer = document.getElementById("movie_player") || document.getElementsByClassName("html5-video-player")[0]; let video = document.querySelector('video'); // --- CLASS DEFINITIONS ----------- class AllowedExceptionError extends Error { constructor(message) { super(message); this.name = "Allowed Exception"; } } // --- GM FUNCTION OVERRIDES ------ const GMCustomRegisterMenuCommand = useCompatilibtyMode ? GM_registerMenuCommand : GM.registerMenuCommand; const GMCustomUnregisterMenuCommand = useCompatilibtyMode ? GM_unregisterMenuCommand : GM.unregisterMenuCommand; const GMCustomGetValue = useCompatilibtyMode ? GM_getValue : GM.getValue; const GMCustomSetValue = useCompatilibtyMode ? GM_setValue : GM.setValue; const GMCustomListValues = useCompatilibtyMode ? GM_listValues : GM.listValues; const GMCustomDeleteValue = useCompatilibtyMode ? GM_deleteValue : GM.deleteValue; // --- FUNCTIONS ------ function debugLog(message) { if (!userSettings.debug) return; const stack = new Error().stack; const stackLines = stack.split("\n"); const callerLine = stackLines[2] ? stackLines[2].trim() : "Line not found"; message += ""; if (!message.endsWith(".")) { message += "."; } console.log(`[Neura DEBUG] ${message} Function called ${callerLine}`); } // Attempt to set the video resolution to target quality or the next best quality function setResolution(force = false) { try { checkVideoValid(force); resolvedTarget = findNextAvailableQuality(userSettings.targetResolution, ytPlayer.getAvailableQualityLevels()); const premiumData = ytPlayer.getAvailableQualityData().find(q => q.quality === resolvedTarget && q.qualityLabel.trim().endsWith(PREMIUM_INDICATOR_LABEL) && q.isPlayable); ytPlayer.setPlaybackQualityRange(resolvedTarget, resolvedTarget, premiumData?.formatId); targetLabel = QUALITIES[resolvedTarget] + "p" + (premiumData ? " " + PREMIUM_INDICATOR_LABEL : ""); videoId = ytPlayer.getVideoData().video_id; debugLog(`Setting quality to: ${targetLabel}`); } catch (error) { debugLog("Did not set resolution. " + error); } } function checkVideoValid(force) { try { if (/^https?:\/\/(www\.)?youtube\.com\/shorts\//.test(window.location.href)) { throw new AllowedExceptionError("Skipping Youtube Shorts."); } if (!ytPlayer?.getAvailableQualityLabels().length) { throw "Video data missing."; } if (videoId == ytPlayer.getVideoData().video_id && !force) { throw new AllowedExceptionError("Duplicate Load. Skipping."); } } catch (error) { throw error; } } function findNextAvailableQuality(target, availableQualities) { const targetValue = QUALITIES[target]; return availableQualities .map(q => ({ quality: q, value: QUALITIES[q] })) .find(q => q.value <= targetValue)?.quality; } function compareQualityLabelButIgnoreFrameRate(label1, label2) { const normalize = label => label.replace(new RegExp(`(\d+p)\d*( ${PREMIUM_INDICATOR_LABEL})?$`), '$1').trim(); return normalize(label1) === normalize(label2); } // Tries to validate setting changes. If this fails to detect a successful update it will call setResolution to attempt to force the playback quality to update again. function verifyChange(retries = 10) { const retryDelay = 500; try { checkVideoValid(true); let success = ytPlayer.getPreferredQuality() !== "auto"; let currentQualityLabel = ytPlayer.getPlaybackQualityLabel(); let isManualChange = !compareQualityLabelButIgnoreFrameRate(currentQualityLabel, targetLabel); if (success) { debugLog(`Quality is ${currentQualityLabel}. ${isManualChange ? "Manual change detected!" : "Change verified!"}`); } else { throw "Unable to verify change"; } } catch (error) { if (error instanceof AllowedExceptionError) { debugLog(error); } else if (retries) { debugLog(`Error when verifying quality change. [${error}] Attempting quality change and reverifying after ${retryDelay}ms...`); setResolution(true); setTimeout(() => verifyChange(retries - 1), retryDelay); } else { debugLog(`Failed to verify change after multiple attempts: ${error}`); } } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址