DoodStream Video Hover Preview

Show video preview on hover for DoodStream video links

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         DoodStream Video Hover Preview
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Show video preview on hover for DoodStream video links
// @author       You
// @match        *://doodstream.com/*
// @match        *://dsvplay.com/*
// @match        *://nobodyhome.ws/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    // Base configuration for most domains
    const BASE_CONFIG = {
        embedPath: '/e/',
        videoPath: '/d/',
        thumbnailPatterns: [
            /https:\/\/img\.doodcdn\.io\/splash\/[a-zA-Z0-9]+\.jpg/,
            /https:\/\/[^"'\s]+\.jpg/,
            /https:\/\/[^"'\s]+\.png/,
            /https:\/\/[^"'\s]+\.webp/
        ]
    };

    // Function to create tooltip
    function createTooltip() {
        const tooltip = document.createElement('div');
        tooltip.id = 'video-tooltip';
        tooltip.style.position = 'fixed';
        tooltip.style.background = 'rgba(0,0,0,0.8)';
        tooltip.style.color = 'white';
        tooltip.style.padding = '10px';
        tooltip.style.borderRadius = '5px';
        tooltip.style.zIndex = '10000';
        tooltip.style.display = 'none';
        tooltip.style.pointerEvents = 'none';
        document.body.appendChild(tooltip);
        return tooltip;
    }

    // Function to create "Copy All Links" button
    function createCopyAllButton() {
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'btn btn-success ml-2';
        button.innerHTML = '<i class="fad fa-copy"></i> Copy All Links';

        button.addEventListener('click', () => {
            copyAllLinks(button);
        });

        // Find the btn-group div and append the button
        const btnGroup = document.querySelector('.btn-group');
        if (btnGroup) {
            btnGroup.appendChild(button);
        } else {
            // Fallback to fixed position if btn-group not found
            button.style.position = 'fixed';
            button.style.top = '20px';
            button.style.right = '20px';
            button.style.zIndex = '10001';
            document.body.appendChild(button);
        }

        return button;
    }

    // Function to extract file code from DoodStream URL
    function extractFileCode(url) {
        const match = url.match(/\/[de]\/([a-z0-9]+)/i);
        return match ? match[1] : null;
    }

    // Function to copy all links
    async function copyAllLinks(button) {
        const originalHTML = button.innerHTML;
        button.innerHTML = '<i class="fad fa-spinner fa-spin"></i> Loading...';
        button.disabled = true;

        const videoLinks = document.querySelectorAll('a[href*="/d/"], a[href*="/e/"], a[href*="dood.to"], a[href*="dood.so"], a[href*="dood.la"], a[href*="doodstream.com"]');

        if (videoLinks.length === 0) {
            button.innerHTML = '<i class="fad fa-times"></i> No links found';
            setTimeout(() => {
                button.innerHTML = originalHTML;
                button.disabled = false;
            }, 2000);
            return;
        }

        const results = [];
        let processed = 0;

        for (const link of videoLinks) {
            const fileCode = extractFileCode(link.href);
            if (!fileCode) continue;

            // Check cache first
            if (previewUrlCache.has(fileCode)) {
                const previewUrl = previewUrlCache.get(fileCode);
                results.push(`[img]${previewUrl}[/img]\n${link.href}`);
                processed++;
                button.innerHTML = `<i class="fad fa-spinner fa-spin"></i> ${processed}/${videoLinks.length}`;
                continue;
            }

            // Fetch preview URL
            await new Promise((resolve) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://dsvplay.com/e/${fileCode}`,
                    onload: function(response) {
                        let previewUrl = null;
                        for (const pattern of BASE_CONFIG.thumbnailPatterns) {
                            const match = response.responseText.match(pattern);
                            if (match) {
                                previewUrl = match[0];
                                break;
                            }
                        }
                        if (previewUrl) {
                            previewUrlCache.set(fileCode, previewUrl);
                            results.push(`[img]${previewUrl}[/img]\n${link.href}`);
                        } else {
                            results.push(link.href);
                        }
                        processed++;
                        button.innerHTML = `<i class="fad fa-spinner fa-spin"></i> ${processed}/${videoLinks.length}`;
                        resolve();
                    },
                    onerror: function() {
                        results.push(link.href);
                        processed++;
                        button.innerHTML = `<i class="fad fa-spinner fa-spin"></i> ${processed}/${videoLinks.length}`;
                        resolve();
                    }
                });
            });

            // Small delay to avoid overwhelming the server
            await new Promise(resolve => setTimeout(resolve, 100));
        }

        // Copy to clipboard
        const text = results.join('\n\n');
        navigator.clipboard.writeText(text).then(() => {
            button.innerHTML = `<i class="fad fa-check"></i> Copied ${results.length} links!`;
            setTimeout(() => {
                button.innerHTML = originalHTML;
                button.disabled = false;
            }, 2000);
        }).catch(() => {
            button.innerHTML = '<i class="fad fa-times"></i> Copy failed';
            setTimeout(() => {
                button.innerHTML = originalHTML;
                button.disabled = false;
            }, 2000);
        });
    }

    // Get all DoodStream video links
    const videoLinks = document.querySelectorAll('a[href*="/d/"], a[href*="/e/"], a[href*="dood.to"], a[href*="dood.so"], a[href*="dood.la"], a[href*="doodstream.com"]');

    if (videoLinks.length === 0) {
        console.log('No DoodStream video links found');
        return;
    }

    const tooltip = createTooltip();

    // Wait a bit for the page to load before adding the button
    setTimeout(() => {
        createCopyAllButton();
    }, 1000);

    // Cache successful preview src by fileCode to avoid reloading
    const previewCache = new Map();
    const previewUrlCache = new Map();

    // Function to add hover listeners to links
    function addHoverListeners(links) {
        links.forEach(link => {
            const fileCode = extractFileCode(link.href);
            if (!fileCode || link.hasAttribute('data-hover-added')) return;

            link.setAttribute('data-hover-added', 'true');
            console.log('Adding hover to:', link.href, 'fileCode:', fileCode);

            link.addEventListener('mouseenter', (e) => {
                console.log('Hovering on:', link.href);
                const rect = e.target.getBoundingClientRect();
                tooltip.style.left = rect.left + 'px';
                tooltip.style.top = (rect.top - 200) + 'px'; // Position above

                // Show embed iframe for preview
                const embedUrl = `https://dsvplay.com/e/${fileCode}`;
                const iframe = document.createElement('iframe');
                iframe.src = embedUrl;
                iframe.style.width = '320px';
                iframe.style.height = '180px';
                iframe.style.border = '0';
                iframe.style.display = 'block';
                iframe.style.pointerEvents = 'none';

                tooltip.innerHTML = '';
                tooltip.appendChild(iframe);
                tooltip.style.display = 'block';
            });

            link.addEventListener('mouseleave', () => {
                tooltip.style.display = 'none';
            });

            // Add copy button
            const copyLinkBtn = document.createElement('button');
            copyLinkBtn.textContent = 'Copy Link';
            copyLinkBtn.style.fontSize = '12px';
            copyLinkBtn.style.padding = '2px 5px';
            copyLinkBtn.style.marginLeft = '10px';
            link.parentNode.insertBefore(copyLinkBtn, link.nextSibling);

            copyLinkBtn.addEventListener('click', () => {
                const cached = previewUrlCache.get(fileCode);
                if (cached) {
                    const text = `[img]${cached}[/img]\n${link.href}`;
                    navigator.clipboard.writeText(text).then(() => {
                        console.log('Copied link');
                    });
                } else {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: `https://dsvplay.com/e/${fileCode}`,
                        onload: function(response) {
                            let previewUrl = null;
                            for (const pattern of BASE_CONFIG.thumbnailPatterns) {
                                const match = response.responseText.match(pattern);
                                if (match) {
                                    previewUrl = match[0];
                                    break;
                                }
                            }
                            if (previewUrl) {
                                previewUrlCache.set(fileCode, previewUrl);
                                const text = `[img]${previewUrl}[/img]\n${link.href}`;
                                navigator.clipboard.writeText(text).then(() => {
                                    console.log('Copied link');
                                });
                            }
                        }
                    });
                }
            });
        });
    }

    // Initial links
    addHoverListeners(videoLinks);

    // Watch for new links (for dynamic content)
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach((node) => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    const newLinks = node.querySelectorAll ? node.querySelectorAll('a[href*="/d/"], a[href*="/e/"], a[href*="dood.to"], a[href*="dood.so"], a[href*="dood.la"], a[href*="doodstream.com"]') : [];
                    addHoverListeners(newLinks);
                    if (node.tagName === 'A' && (node.href.includes('/d/') || node.href.includes('/e/') || node.href.includes('dood.to') || node.href.includes('dood.so') || node.href.includes('dood.la') || node.href.includes('doodstream.com'))) {
                        addHoverListeners([node]);
                    }
                }
            });
        });
    });

    observer.observe(document.body, { childList: true, subtree: true });

})();