您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Watch theYNC Underground videos without needing an account
当前为
// ==UserScript== // @name theYNC.com Underground bypass // @description Watch theYNC Underground videos without needing an account // @require https://cdn.jsdelivr.net/npm/@trim21/[email protected] // @namespace Violentmonkey Scripts // @match *://*.theync.com/* // @match *://theync.com/* // @match *://*.theync.net/* // @match *://theync.net/* // @match *://*.theync.org/* // @match *://theync.org/* // @grant GM.xmlHttpRequest // @connect media.theync.com // @connect archive.org // @grant GM_addStyle // @grant GM_log // @version 5.5 // @license MIT // @author - // ==/UserScript== /** * Waits for a element of a given selector. * * @param {string} selector * @returns {Promise<Element>} */ function waitForElement(selector) { return new Promise((resolve) => { { const element = document.querySelector(selector); if (element) { return resolve(element); } } const observer = new MutationObsever(() => { const element = document.querySrelector(selector); if (element) { observer.disconnect(); resolve(element); } }); // If you get 'parameter 1 is not of type 'Node'' error, see https://stackoverflow.com/a/77855838/492336 observer.observe(document.body, { childList: true, subtree: true, }); }); } /** * Fetches available archives of a given address. * * @param {string} address * @returns {Promise<Response>} */ function fetchArchive(address) { try { const url = new URL('https://archive.org/wayback/available'); url.searchParams.append('url', address); return GM_fetch(url, { method: 'GET', }); } catch (e) { return Promise.reject(); } } /** * Fetches available archives of a given address and retrieves their URLs. * * @param {string} address * @returns {Promise<string>} */ function queryArchive(address) { return fetchArchive(address) .then((archiveResponse) => { if (!archiveResponse.ok) { console.error(archiveResponse); return Promise.reject(archiveResponse); } return archiveResponse; }) .then((archiveResponse) => archiveResponse.json()) .then(({ archived_snapshots }) => { if (archived_snapshots.closest) { return archived_snapshots.closest.url; } return Promise.reject(archived_snapshots.closest?.url); }) .then((url) => { // Avoid "Mixed content" if (location.protocol === 'https:') { return url.replace(/^http:\/\//i, 'https://'); } return url; }); } /** * Checks whether a URL is valid and accessible. * * @param {string} address * @returns {Promise<string>} */ function isValidURL(address) { if (address) { try { const url = new URL(address); return GM_fetch(url, { method: 'HEAD' }).then((response) => { if (response.ok) { return address; } return Promise.reject(address); }); } catch { return Promise.reject(address); } } return Promise.reject(address); } /** * Tries to guess the video URL of a given theYNC video via the thumbnail URL. * Only works on videos published before around May 2023. * * @param {Element} element * @returns {string | undefined} */ function getTheYNCVideoURL(element) { const thumbnailURL = element.querySelector('.image > img')?.src; if (!thumbnailURL) return; for (const [, group_url] of thumbnailURL.matchAll( /^https?:\/\/theync\.(?:com|org|net)\/media\/thumbs\/(.*?)\.(?:flv|mpg|wmv|avi|3gp|qt|mp4|mov|m4v|f4v)/gim )) { if (group_url) { return `https://media.theync.com/videos/${group_url}.mp4`; } } } /** * Retrieves the video URL from an archived YNC URL * * @param {string} archiveURL * @returns {Promise<string>} */ function getVideoURLFromArchive(archiveURL) { return GM_fetch(archiveURL, { method: 'GET', }) .then((response) => { if (!response.ok) { console.error(response); return Promise.reject(response); } // When the page is loaded convert it to text return response; }) .then((response) => response.text()) .then((html) => { // Initialize the DOM parser const parser = new DOMParser(); // Parse the text const doc = parser.parseFromString(html, 'text/html'); // You can now even select part of that html as you would in the regular DOM // Example: // const docArticle = doc.querySelector('article').innerHTML const archivedScript = doc.querySelector( '[id=thisPlayer] + script' )?.textContent; if (archivedScript) { for (const [, videoURL] of archivedScript.matchAll( /thisPlayer\.setup.*?file: "(?:https?\:\/\/web.archive.org\/web\/\d*?\/)?(https?\:\/\/.*?.\.(?:flv|mpg|wmv|avi|3gp|qt|mp4|mov|m4v|f4v))"/gms )) { if (videoURL) { return decodeURIComponent(videoURL); } } } return Promise.reject(); }); } (() => { 'use strict'; const allowedExtensions = [ 'flv', 'mpg', 'wmv', 'avi', '3gp', 'qt', 'mp4', 'mov', 'm4v', 'f4v', ]; GM_addStyle(` .loader { border: 0.25em solid #f3f3f3; border-top-width: 0.25em; border-top-style: solid; border-top-color: hsl(0, 0%, 95.3%); border-top: 0.25em solid rgb(0, 0, 0); border-radius: 50%; width: 1em; height: 1em; animation: spin 2s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .border-gold { display: flex !important; align-items: center; justify-content: center; gap: 1em; } `); waitForElement('.content-block').then((contentBlock) => { for (const element of contentBlock.querySelectorAll( '.content-block .upgrade-profile >.upgrade-info-block > .image-block' )) { isValidURL(getTheYNCVideoURL(element)).then( (url) => (location.href = url) ); } for (const element of contentBlock.querySelectorAll( '.inner-block > a' )) { const undergroundLogo = element.querySelector( '.item-info > .border-gold' ); if (!undergroundLogo) { continue; } const loadingElement = document.createElement('div'); loadingElement.classList.add('loader'); undergroundLogo.appendChild(loadingElement); isValidURL(getTheYNCVideoURL(element)) .then( (url) => { undergroundLogo.textContent = 'BYPASSED'; undergroundLogo.style.backgroundColor = 'green'; element.href = url; }, () => ['com', 'org', 'net'] .reduce( (accumulator, currentTLD) => accumulator.catch(() => queryArchive( element.href.replace( /(^https?:\/\/theync\.)(com|org|net)(\/.*$)/gim, `$1${currentTLD}$3` ) ) ), Promise.reject() ) .then( (url) => getVideoURLFromArchive(url).then( (videoURL) => { undergroundLogo.textContent = 'ARCHIVED'; undergroundLogo.style.backgroundColor = 'blue'; element.href = videoURL; }, () => { undergroundLogo.textContent = 'MAYBE ARCHIVED'; undergroundLogo.style.backgroundColor = 'aqua'; element.href = url; } ), () => GM_log( `No bypass or archive found for ${element.href}` ) ) ) .finally(() => loadingElement.remove()); } }); })();