theYNC.com Underground bypass

Watch theYNC Underground videos without needing an account

От 01.01.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==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.6
// @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(
            '.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());
        }
    });
})();