您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Redirects YouTube videos to an Invidious instance.
当前为
// ==UserScript== // @name Redirect to Invidious // @author André Kugland // @description Redirects YouTube videos to an Invidious instance. // @namespace https://github.com/kugland // @license MIT // @version 0.3.1 // @match *://*.youtube.com/ // @match *://*.youtube.com/* // @run-at document-start // @noframes // @grant GM_xmlhttpRequest // @grant GM.xmlhttpRequest // @homepageURL https://greasyfork.org/scripts/477967-redirect-to-invidious // ==/UserScript== (() => { // select-instance.ts function getStyle() { const style = document.createElement("style"); style.textContent = ` #invidious-instance-container { font-family: mono; font-size: 12px; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(5px); display: grid; place-content: center; z-index: 10000; } #invidious-instance-table { background-color: white; padding: 10px 15px; box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.5); } #invidious-instance-table td { padding-left: 10px; } #invidious-instance-table td:first-child { padding-left: 0; } #invidious-instance-table button { font-family: mono; font-size: 12px; padding: 4px 5px; border: none; background-color: #007bff; color: white; cursor: pointer; } #invidious-instance-table .selected { font-weight: bold; } `; return style; } function addProtocol(uri) { if (uri.startsWith("http")) return uri; else return `https://${uri}`; } async function getInstances() { const instancesLastUpdatedKey = "invidious-redirect-instances-last-updated"; const instancesKey = "invidious-redirect-instances"; const now = Date.now(); const lastUpdated = parseInt(localStorage.getItem(instancesLastUpdatedKey) ?? "0"); if (lastUpdated && now - lastUpdated < 864e5) { return JSON.parse(localStorage.getItem(instancesKey)); } else { const instances = await new Promise((resolve) => { (GM.xmlHttpRequest || GM_xmlhttpRequest)({ method: "GET", headers: { "Content-Type": "application/json" }, url: "https://raw.githubusercontent.com/kugland/invidious-redirect/master/instances.json", onload: function(response) { resolve(JSON.parse(response.responseText)); } }); }); localStorage.setItem(instancesKey, JSON.stringify(instances)); localStorage.setItem(instancesLastUpdatedKey, now.toString()); return instances; } } async function getTable(current) { const instances = await getInstances(); let sorted = Array.from(Object.keys(instances)).sort((a, b) => instances[a] !== instances[b] ? instances[a].localeCompare(instances[b]) : a.localeCompare(b)).map((uri) => [uri, instances[uri]]); return sorted.map(([uri, region]) => [uri, region]).filter(([uri]) => !uri.endsWith(".i2p") && !uri.endsWith(".onion")).map(([uri, region]) => { let url = addProtocol(uri); uri = uri.replace(/^https?:\/\//, ""); return ` <tr data-url="${url}" class=${current == url ? "selected" : ""}> <td><a href="${url}" target="_blank">${uri}</a></td> <td>${region.toLowerCase()}</td> <td><button>select</button></td> </tr> `; }).join(""); } async function showTable(current) { const table = document.createElement("div"); table.id = "invidious-instance-container"; table.innerHTML = `<table id="invidious-instance-table">${await getTable(current)}</table>`; table.appendChild(getStyle()); document.body.appendChild(table); return await new Promise((resolve) => { table.querySelectorAll("button").forEach((button) => { button.addEventListener("click", (e) => { const tr = e.target.closest("tr"); if (tr) { const url = tr.getAttribute("data-url"); if (url) { table.remove(); resolve(url); } } }); }); }); } // invidious-redirect.ts var instanceKey = "invidious-redirect-instance"; var defaultInstance = "https://yewtu.be"; localStorage.getItem(instanceKey) || localStorage.setItem(instanceKey, defaultInstance); function makeUrl(videoId) { const instance = localStorage.getItem(instanceKey); return new URL(`${instance}/watch?v=${videoId}`).href; } function getVideoId(href) { const url = new URL(href, window.location.href); if (url.pathname === "/watch") { return url.searchParams.get("v") ?? ""; } else { const videoId = url.pathname.match(/^\/shorts\/([a-zA-Z0-9_-]+)$/)?.[1]; if (videoId) return videoId; } throw new Error(`Unable to parse URL: ${href}`); } document.addEventListener("click", (event) => { if (event.target instanceof HTMLElement) { try { const href = event.target.closest("a")?.getAttribute("href"); if (href) { event.preventDefault(); event.stopPropagation(); window.location.assign(makeUrl(getVideoId(href))); } } catch (e) { } } }, true); var currentUrl = window.location.href; setInterval(() => { if (window.location.href !== currentUrl) { currentUrl = window.location.href; try { window.location.replace(makeUrl(getVideoId(currentUrl))); } catch (e) { } } }, 150); try { window.location.replace(makeUrl(getVideoId(currentUrl))); } catch (e) { } ((fn) => { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn); })(() => { const css = document.createElement("style"); css.textContent = "#set-invidious-url:hover { opacity: 1 !important; }"; document.head.appendChild(css); const button = document.createElement("img"); button.id = "set-invidious-url"; button.tabIndex = -1; button.src = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAJFBMVEXv8e7 Z4ePn6+kWt/CZzvChpKFrbWrT1dJVV1WJjIm2uLXCxMH33HXYAAAAp0lEQVR4AeXNIQ7CMABG4ceSsXSYIXFVFaCAC5 BwgblNV4HDkMwiIA0YDMnkDMHWoHY5PPwGSfjsE4+fNbZIyXIBOszR1iu+lICWFmiuRGsOaPURbXOyKINb6FDyR/AoZ lefURyNnuwxelKR6YmHVk2yK3qSd+iJKdATB9Be+PAEPakATIi8STzISVaiJ2lET4YFejIBPbmDnEy3ETmZ9REARr3l P7wAXHImU2sAU14AAAAASUVORK5CYII=`.replace(/\s/g, ""); Object.assign(button.style, { "position": "fixed", "bottom": 0, "right": 0, "height": "48px", "width": "48px", "z-index": 99999, "margin": "1rem", "cursor": "pointer", "border-radius": "50%", "box-shadow": "0px 0px 3px black", "opacity": 0.5 }); button.addEventListener("click", async () => { const current = localStorage.getItem(instanceKey) ?? defaultInstance; let instance = await showTable(current); try { new URL(instance); localStorage.setItem(instanceKey, instance); } catch (e) { alert(`The URL you entered is invalid: ${instance}`); } }); document.body.appendChild(button); }); })();