DoodStream Video Hover Preview

Show video preview on hover for DoodStream video links

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey 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.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

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         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 });

})();