Watch theYNC Underground videos without needing an account
Version vom
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/523012/1518004/theYNCcom%20Underground%20bypass.js
// ==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/* // @match *://archive.ph/* // @match *://archive.today/* // @include /https?:\/\/web\.archive\.org\/web\/\d+?\/https?:\/\/theync\.(?:com|org|net)/ // @require https://update.greasyfork.org/scripts/523012/1516081/WaitForKeyElement.js // @grant GM.xmlHttpRequest // @connect media.theync.com // @connect archive.org // @grant GM_addStyle // @grant GM_log // @grant GM_addElement // @version 7.4 // @supportURL https://greasyfork.org/en/scripts/520352-theync-com-underground-bypass/feedback // @license MIT // @author https://greasyfork.org/en/users/1409235-paywalldespiser // ==/UserScript== /** * 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; }); } /** * Gets the comments given a video id * * @param {number} id * @returns {Promise<string>} */ function getComments(id) { const url = new URL( 'https://theync.com/templates/theync/template.ajax_comments.php' ); url.searchParams.append('id', id); url.searchParams.append('time', new Date().getTime()); return GM_fetch(url, { method: 'GET' }) .then((response) => response.text()) .then((text) => { // Initialize the DOM parser const parser = new DOMParser(); // Parse the text return parser.parseFromString(text, 'text/html'); }); } /** * 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 a theYNC video page * * @param {Element} element * @returns {string | undefined} */ function retrieveVideoURL(element = document) { if (location.host === 'archive.ph' || location.host === 'archive.today') { const videoElement = element.querySelector( '[id="thisPlayer"] video[old-src]' ); if (videoElement) { return videoElement.getAttribute('old-src'); } } const videoSrc = element.querySelector( '.stage-video > .inner-stage video[src]' )?.src; if (videoSrc) { return videoSrc; } const playerSetupScript = element.querySelector( '[id=thisPlayer] + script' )?.textContent; if (playerSetupScript) { // TODO: Find a non-regex solution to this that doesn't involve eval for (const [, videoURL] of playerSetupScript.matchAll( /(?<=thisPlayer\.setup\(\{).*?file:\ *"(https?\:\/\/.+?.\.(?:flv|mpg|wmv|avi|3gp|qt|mp4|mov|m4v|f4v))"/gms )) { if (videoURL) { return videoURL; } } } } /** * 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 videoURL = retrieveVideoURL(doc); if (videoURL) { return videoURL; } return Promise.reject(); }); } /** * set Link of element * * @param {HTMLLinkElement} element * @param {string} url */ function setElementLink(element, url) { element.href = url; element.target = '_blank'; element.rel = 'noopener noreferrer'; } (function () { '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; } `); waitForKeyElement( '[id="content"],[id="related-videos"] .content-block' ).then((contentBlock) => { for (const element of contentBlock.querySelectorAll( '.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'; setElementLink(element, 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'; setElementLink(element, videoURL); }, () => { undergroundLogo.textContent = 'MAYBE ARCHIVED'; undergroundLogo.style.backgroundColor = 'aqua'; setElementLink(element, url); } ), () => { undergroundLogo.textContent = 'Try archive.today'; undergroundLogo.style.backgroundColor = 'red'; setElementLink( element, `https://archive.ph/${element.href}` ); GM_log( `No bypass or archive found for ${element.href}` ); } ) ) .finally(() => loadingElement.remove()); } }); waitForKeyElement('[id="stage"]:has([id="thisPlayer"])').then((stage) => { const videoURL = retrieveVideoURL(); if (videoURL) { stage.innerHTML = ''; stage.style.textAlign = 'center'; const video = GM_addElement(stage, 'video', { controls: 'controls', }); video.style.width = 'auto'; video.style.height = '100%'; const source = GM_addElement(video, 'source'); source.src = videoURL; source.type = 'video/mp4'; } }); })();