AagMaal Streamtape Inline Resolver (Safe Buttons with Play & Download)

Add 'Resolve & Play' buttons with Play & Download options after resolving Streamtape link

Verzia zo dňa 30.04.2025. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         AagMaal Streamtape Inline Resolver (Safe Buttons with Play & Download)
// @namespace    
// @version      1.4
// @description  Add 'Resolve & Play' buttons with Play & Download options after resolving Streamtape link
// @match        https://aagmaal.boo/*
// @grant        GM_xmlhttpRequest
// @connect      *
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const STREAMTAPE_HOSTS = [
        'streamtape.com', 'strtape.cloud', 'streamtape.net', 'streamta.pe', 'streamtape.site',
        'strcloud.link', 'strcloud.club', 'strtpe.link', 'streamtape.cc', 'scloud.online',
        'stape.fun', 'streamadblockplus.com', 'shavetape.cash', 'streamtape.to', 'streamta.site',
        'streamadblocker.xyz', 'tapewithadblock.org', 'adblocktape.wiki', 'antiadtape.com',
        'streamtape.xyz', 'tapeblocker.com', 'streamnoads.com', 'tapeadvertisement.com',
        'tapeadsenjoyer.com', 'watchadsontape.com'
    ];

    function isStreamtapeLink(href) {
        return STREAMTAPE_HOSTS.some(host => href.includes(host));
    }

    function resolveStreamtape(url) {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                headers: {
                    'User-Agent': navigator.userAgent,
                    'Referer': new URL(url).origin + '/'
                },
                onload: function (response) {
                    const html = response.responseText;
                    const matches = html.match(/ById\('.+?=\s*(["']\/\/[^;<]+)/);
                    if (!matches) return resolve(null);

                    let src_url = '';
                    const parts = matches[1].replace(/'/g, '"').split('+');
                    for (const part of parts) {
                        const p1Match = part.match(/"([^"]*)/);
                        if (!p1Match) continue;
                        let p1 = p1Match[1];
                        let p2 = 0;
                        if (part.includes('substring')) {
                            const subst = part.match(/substring\((\d+)/g);
                            if (subst) {
                                for (const sub of subst) {
                                    p2 += parseInt(sub.replace('substring(', ''), 10);
                                }
                            }
                        }
                        src_url += p1.substring(p2);
                    }

                    src_url += '&stream=1';
                    const finalUrl = src_url.startsWith('//') ? 'https:' + src_url : src_url;

                    GM_xmlhttpRequest({
                        method: 'HEAD',
                        url: finalUrl,
                        headers: {
                            'User-Agent': navigator.userAgent,
                            'Referer': url
                        },
                        onload: function (redirectResponse) {
                            const redirectUrl = redirectResponse.finalUrl || finalUrl;
                            resolve(redirectUrl);
                        },
                        onerror: () => resolve(finalUrl)
                    });
                },
                onerror: () => resolve(null)
            });
        });
    }

    function fetchVideoPage(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                headers: { 'User-Agent': navigator.userAgent },
                onload: function (response) {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    resolve(doc);
                },
                onerror: () => reject(new Error('Page fetch failed'))
            });
        });
    }

    function playInModal(url) {
        const modal = document.createElement('div');
        modal.style.position = 'fixed';
        modal.style.top = '0';
        modal.style.left = '0';
        modal.style.width = '100%';
        modal.style.height = '100%';
        modal.style.background = 'rgba(0,0,0,0.8)';
        modal.style.zIndex = '10001';
        modal.style.display = 'flex';
        modal.style.flexDirection = 'column';
        modal.style.justifyContent = 'center';
        modal.style.alignItems = 'center';

        const closeModalBtn = document.createElement('button');
        closeModalBtn.textContent = 'Close';
        styleButton(closeModalBtn, '#f44336');
        closeModalBtn.style.position = 'absolute';
        closeModalBtn.style.top = '20px';
        closeModalBtn.style.right = '20px';
        closeModalBtn.onclick = () => document.body.removeChild(modal);

        const player = document.createElement('video');
        player.controls = true;
        player.autoplay = true;
        player.style.maxWidth = '90%';
        player.style.maxHeight = '80%';
        player.src = url;

        modal.appendChild(closeModalBtn);
        modal.appendChild(player);
        document.body.appendChild(modal);
    }

    function downloadVideo(url, title) {
        const a = document.createElement('a');
        a.href = url;
        a.download = title || 'download.mp4'; // Use title if available, otherwise 'download.mp4'
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    function styleButton(btn, bg, size = 'normal') {
        btn.style.padding = size === 'small' ? '2px 8px' : '4px 10px';
        btn.style.fontSize = size === 'small' ? '11px' : '13px';
        btn.style.background = bg;
        btn.style.color = 'white';
        btn.style.border = 'none';
        btn.style.borderRadius = '4px';
        btn.style.cursor = 'pointer';
        btn.style.marginTop = '8px';
        btn.style.display = 'inline-block';
    }

    function addSafeResolveButtons() {
        const articles = document.querySelectorAll('article[data-video-uid]');

        for (const article of articles) {
            const existing = article.querySelector('.inline-resolve-btn');
            if (existing) continue;

            const anchor = article.querySelector('a[href]');
            if (!anchor) continue;

            const videoUrl = anchor.href;
            const title = article.querySelector('.entry-header')?.textContent.trim() || 'Untitled';

            // Create button wrapper separate from <a>
            const btn = document.createElement('button');
            btn.className = 'inline-resolve-btn';
            btn.textContent = '▶ Resolve & Play';
            styleButton(btn, '#9c27b0');

            btn.addEventListener('click', async (e) => {
                e.preventDefault();
                e.stopPropagation();

                btn.disabled = true;
                btn.textContent = '⏳ Resolving...';

                try {
                    const doc = await fetchVideoPage(videoUrl);
                    const links = doc.querySelectorAll('a[href]');
                    for (const link of links) {
                        const href = link.href;
                        if (isStreamtapeLink(href)) {
                            const directUrl = await resolveStreamtape(href);
                            if (!directUrl) throw new Error('Resolution failed');

                            // Remove previous button and add new buttons (Play & Download)
                            article.removeChild(btn);

                            const playBtn = document.createElement('button');
                            playBtn.textContent = '▶ Play';
                            styleButton(playBtn, '#4caf50');
                            playBtn.onclick = () => playInModal(directUrl);
                            article.appendChild(playBtn);

                            const downloadBtn = document.createElement('button');
                            downloadBtn.textContent = '📥 Download';
                            styleButton(downloadBtn, '#1976d2');
                            downloadBtn.onclick = () => downloadVideo(directUrl, title);
                            article.appendChild(downloadBtn);

                            return;
                        }
                    }
                    alert('No Streamtape link found.');
                } catch (err) {
                    alert('Error: ' + err.message);
                } finally {
                    btn.disabled = false;
                    btn.textContent = '▶ Resolve & Play';
                }
            });

            // Insert the button after the <a> element
            anchor.parentElement.appendChild(btn);
        }
    }

    function init() {
        addSafeResolveButtons();

        // Optional: observe DOM in case videos are dynamically added
        const observer = new MutationObserver(() => addSafeResolveButtons());
        observer.observe(document.body, { childList: true, subtree: true });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();