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

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
    }
})();