RedGifs Copy Video Embed Link & Download

Content-aware copy button with video change detection, visual feedback, robust homepage support, enhanced download UI with beautiful hover menu, niches support, and search page support

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

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.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         RedGifs Copy Video Embed Link & Download
// @namespace    http://tampermonkey.net/
// @version      5.7
// @description  Content-aware copy button with video change detection, visual feedback, robust homepage support, enhanced download UI with beautiful hover menu, niches support, and search page support
// @author       monk3xx3
// @match        https://redgifs.com/*
// @match        https://www.redgifs.com/*
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    let copyButton = null;
    let downloadButton = null;
    let downloadDropdown = null;
    let isProcessing = false;
    let isDownloading = false;
    let currentVideoId = null;
    let bearerToken = null;
    let tokenExpiry = null;
    let activeVideoElement = null;
    let videoObserver = null;
    let highlightTimeout = null;
    let dropdownTimeout = null;
    let isDropdownVisible = false;

    // Enhanced video waiting with retry logic
    function waitForVideosOnHomepage(maxAttempts = 10, delay = 500) {
        return new Promise((resolve) => {
            let attempts = 0;
            
            function checkForVideos() {
                attempts++;
                const videos = document.querySelectorAll('video');
                const hasVideos = videos.length > 0;
                
                // Check if we're on homepage, niches, search, or other video pages
                const isValidVideoPage = window.location.pathname === '/' || 
                                        window.location.pathname === '' ||
                                        window.location.pathname === '/home' ||
                                        window.location.pathname.startsWith('/niches/') ||
                                        window.location.pathname.startsWith('/search/') ||
                                        (window.location.hostname.includes('redgifs.com') && 
                                         window.location.pathname.length <= 1);
                
                if (hasVideos || attempts >= maxAttempts) {
                    console.log(`Videos found: ${hasVideos}, Attempts: ${attempts}`);
                    resolve(hasVideos);
                    return;
                }
                
                // If no videos yet and we're on a valid page, keep trying
                if (isValidVideoPage) {
                    console.log(`Waiting for videos... Attempt ${attempts}/${maxAttempts}`);
                    setTimeout(checkForVideos, delay);
                } else {
                    resolve(false);
                }
            }
            
            checkForVideos();
        });
    }

    // Enhanced page detection including niches and search pages
    function isValidPageWithContent() {
        const path = window.location.pathname;
        const isValidPage = path === '/' || 
                           path === '' || 
                           path === '/home' ||
                           path.startsWith('/niches/') ||
                           path.startsWith('/users/') ||
                           path.startsWith('/watch/') ||
                           path.startsWith('/search/');
        
        const hasVideoContent = document.querySelector('video') || 
                               document.querySelector('[class*="video"]') ||
                               document.querySelector('[class*="gif"]') ||
                               document.querySelector('[data-testid*="video"]') ||
                               document.querySelector('[class*="post"]') ||
                               document.querySelector('[class*="content"]') ||
                               document.querySelector('[class*="search"]');
        
        return isValidPage && hasVideoContent;
    }

    // Enhanced video detection focused on content, not URL
    function detectCurrentVideo() {
        try {
            const videos = document.querySelectorAll('video');
            let bestVideo = null;
            let bestScore = 0;

            for (const video of videos) {
                const rect = video.getBoundingClientRect();
                const viewportHeight = window.innerHeight;
                const viewportWidth = window.innerWidth;

                // Calculate visibility score
                const visibleHeight = Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0);
                const visibleWidth = Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0);
                const visibleArea = Math.max(0, visibleHeight) * Math.max(0, visibleWidth);
                const totalArea = rect.width * rect.height;

                if (totalArea === 0) continue;

                const visibilityRatio = visibleArea / totalArea;
                const sizeScore = Math.min(rect.width / 100, 10) + Math.min(rect.height / 100, 10);
                const centerDistance = Math.abs(rect.top + rect.height/2 - viewportHeight/2);
                const centerScore = Math.max(0, 10 - centerDistance / 100);

                // Enhanced scoring for niches, user pages, and search pages
                const score = visibilityRatio * 20 + sizeScore + centerScore +
                             (video.readyState >= 3 ? 5 : 0) +
                             (!video.paused ? 10 : 0);

                if (score > bestScore && visibilityRatio > 0.2 && rect.width > 150) {
                    bestScore = score;
                    bestVideo = video;
                }
            }

            return bestVideo;

        } catch (error) {
            console.error('Error detecting current video:', error);
            return null;
        }
    }

    // Detect available video qualities with estimated file sizes
    function detectAvailableQualities(videoElement) {
        const qualities = { 
            sd: { available: false, size: '~2-5 MB', label: 'Standard' }, 
            hd: { available: false, size: '~8-15 MB', label: 'High Quality' } 
        };
        
        if (!videoElement) return qualities;
        
        try {
            // Check video dimensions
            const width = videoElement.videoWidth || videoElement.clientWidth;
            const height = videoElement.videoHeight || videoElement.clientHeight;
            
            // Always assume SD is available
            qualities.sd.available = true;
            
            // Check if HD is available based on dimensions
            if (width >= 1280 || height >= 720) {
                qualities.hd.available = true;
            }
            
            // Check video source URL for quality indicators
            const src = videoElement.src || videoElement.currentSrc;
            if (src && src.includes('hd')) {
                qualities.hd.available = true;
            }
            
            // Estimate file sizes based on duration if available
            if (videoElement.duration && !isNaN(videoElement.duration)) {
                const duration = videoElement.duration;
                qualities.sd.size = `~${Math.round(duration * 0.3)} MB`;
                qualities.hd.size = `~${Math.round(duration * 1.2)} MB`;
            }
            
        } catch (error) {
            console.error('Error detecting video qualities:', error);
            qualities.sd.available = true; // fallback
        }
        
        return qualities;
    }

    // Extract video ID from the active video element and surrounding context
    function extractVideoIdFromContent(videoElement) {
        if (!videoElement) return null;

        try {
            // Method 1: Check video source URLs
            const sources = [videoElement.src, videoElement.currentSrc];
            sources.push(...Array.from(videoElement.querySelectorAll('source')).map(s => s.src));

            for (const src of sources) {
                if (src && !src.startsWith('blob:')) {
                    const match = src.match(/\/([a-zA-Z0-9]{15,})(?:-silent)?\.(?:mp4|webm)/i);
                    if (match) return match[1];
                }
            }

            // Method 2: Check data attributes on video and parent elements
            let element = videoElement;
            let depth = 0;

            while (element && depth < 10) {
                // Check all data attributes
                for (const attr of element.attributes || []) {
                    if (attr.name.includes('id') || attr.name.includes('video') || attr.name.includes('gif')) {
                        const match = attr.value.match(/([a-zA-Z0-9]{15,})/);
                        if (match && match[1].length >= 15) return match[1];
                    }
                }

                // Check class names for video IDs
                if (element.className) {
                    const match = element.className.match(/([a-zA-Z0-9]{15,})/);
                    if (match && match[1].length >= 15) return match[1];
                }

                element = element.parentElement;
                depth++;
            }

            // Method 3: Look for links and video info in the container
            const container = videoElement.closest('[class*="video"], [class*="post"], [class*="content"], [class*="item"], [class*="card"]') ||
                             videoElement.parentElement;

            if (container) {
                // Find watch links
                const links = container.querySelectorAll('a[href*="watch"], a[href*="/watch/"]');
                for (const link of links) {
                    const match = link.href.match(/\/watch\/([a-zA-Z0-9]+)/);
                    if (match) return match[1];
                }

                // Find any elements with video ID patterns
                const allElements = container.querySelectorAll('*');
                for (const el of allElements) {
                    for (const attr of el.attributes || []) {
                        if (attr.value.match(/^[a-zA-Z0-9]{15,}$/)) {
                            return attr.value;
                        }
                    }
                }
            }

            // Method 4: Parse nearby script content for video data
            const nearbyScripts = document.querySelectorAll('script:not([src])');

            for (const script of nearbyScripts) {
                const content = script.textContent;

                // Look for video IDs in various JSON structures
                const patterns = [
                    /"id":\s*"([a-zA-Z0-9]{15,})"/g,
                    /"video":\s*{[^}]*"id":\s*"([^"]{15,})"/g,
                    /"gif":\s*{[^}]*"id":\s*"([^"]{15,})"/g,
                    /([a-zA-Z0-9]{15,})(?:-silent)?\.mp4/g
                ];

                for (const pattern of patterns) {
                    const matches = [...content.matchAll(pattern)];
                    if (matches.length > 0) {
                        // Return the most recent match (likely the current video)
                        return matches[matches.length - 1][1];
                    }
                }
            }

        } catch (error) {
            console.error('Error extracting video ID:', error);
        }

        return null;
    }

    // Get current video ID with enhanced detection
    function getCurrentVideoId() {
        const activeVideo = detectCurrentVideo();
        if (activeVideo !== activeVideoElement) {
            // Video changed, trigger highlight
            if (activeVideoElement !== null) {
                highlightButtonChange();
            }
            activeVideoElement = activeVideo;
        }

        if (!activeVideo) return null;

        const videoId = extractVideoIdFromContent(activeVideo);

        // Fallback: try URL-based detection only if content detection fails
        if (!videoId) {
            const urlMatch = window.location.pathname.match(/\/watch\/([^\/\?#]+)/);
            if (urlMatch) return urlMatch[1];
        }

        return videoId;
    }

    // Highlight button when video changes
    function highlightButtonChange() {
        if (!copyButton || isProcessing) return;

        clearTimeout(highlightTimeout);

        // Add highlight effect
        copyButton.style.background = 'rgba(59, 130, 246, 0.9)';
        copyButton.style.borderColor = 'rgba(59, 130, 246, 0.8)';
        copyButton.style.transform = 'translateY(-2px) scale(1.05)';
        copyButton.style.boxShadow = '0 8px 25px rgba(59, 130, 246, 0.4)';

        // Update text to show video detected
        const span = copyButton.querySelector('span');
        if (span) {
            const originalText = span.textContent;
            span.textContent = 'Video Detected';

            highlightTimeout = setTimeout(() => {
                copyButton.style.background = 'rgba(0, 0, 0, 0.85)';
                copyButton.style.borderColor = 'rgba(255, 255, 255, 0.2)';
                copyButton.style.transform = 'translateY(0) scale(1)';
                copyButton.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3)';
                span.textContent = originalText;
            }, 1500);
        }
    }

    // Extract bearer token from network requests
    function extractBearerToken() {
        return new Promise((resolve) => {
            const originalFetch = window.fetch;
            let captured = false;

            window.fetch = function(...args) {
                const result = originalFetch.apply(this, args);

                if (args[1] && args[1].headers && args[1].headers.Authorization) {
                    const token = args[1].headers.Authorization.replace('Bearer ', '');
                    if (token && !captured) {
                        bearerToken = token;
                        captured = true;
                        try {
                            const payload = JSON.parse(atob(token.split('.')[1]));
                            tokenExpiry = payload.exp * 1000;
                        } catch (e) {
                            tokenExpiry = Date.now() + (24 * 60 * 60 * 1000);
                        }
                        console.log('Bearer token captured via fetch');
                        resolve(token);
                    }
                }

                return result;
            };

            // Also monitor XHR requests
            const originalXHR = XMLHttpRequest.prototype.setRequestHeader;
            XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
                if (name.toLowerCase() === 'authorization' && value.startsWith('Bearer ') && !captured) {
                    const token = value.replace('Bearer ', '');
                    bearerToken = token;
                    captured = true;
                    try {
                        const payload = JSON.parse(atob(token.split('.')[1]));
                        tokenExpiry = payload.exp * 1000;
                    } catch (e) {
                        tokenExpiry = Date.now() + (24 * 60 * 60 * 1000);
                    }
                    console.log('Bearer token captured via XHR');
                    resolve(token);
                }
                return originalXHR.call(this, name, value);
            };

            // Timeout after 5 seconds
            setTimeout(() => {
                if (!captured) {
                    console.log('No token captured, attempting page interaction...');
                    // Try to trigger network requests
                    const videos = document.querySelectorAll('video');
                    if (videos.length > 0) {
                        videos[0].currentTime = videos[0].currentTime; // Trigger a tiny seek
                    }

                    setTimeout(() => resolve(bearerToken), 2000);
                }
            }, 5000);
        });
    }

    // Get signed URLs from RedGifs API
    async function getSignedUrls(videoId) {
        if (!bearerToken || Date.now() > tokenExpiry) {
            await extractBearerToken();
            if (!bearerToken) {
                throw new Error('Authentication failed - no bearer token');
            }
        }

        try {
            const response = await fetch(`https://api.redgifs.com/v2/gifs/${videoId}?views=yes&users=yes`, {
                headers: {
                    'Authorization': `Bearer ${bearerToken}`,
                    'User-Agent': navigator.userAgent,
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                throw new Error(`API Error: ${response.status} ${response.statusText}`);
            }

            const data = await response.json();
            const urls = data.gif?.urls;

            if (!urls) {
                throw new Error('No video URLs in API response');
            }

            return urls;

        } catch (error) {
            console.error('API request failed:', error);
            throw error;
        }
    }

    // Download video to internal storage using GM_xmlhttpRequest
    function downloadVideoFile(url, filename) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'blob',
                headers: {
                    'User-Agent': navigator.userAgent,
                    'Referer': 'https://redgifs.com/'
                },
                onload: function(response) {
                    try {
                        // Create blob from response
                        const blob = new Blob([response.response], { type: 'video/mp4' });
                        
                        // Create download link
                        const downloadUrl = URL.createObjectURL(blob);
                        const link = document.createElement('a');
                        link.href = downloadUrl;
                        link.download = filename;
                        link.style.display = 'none';
                        
                        // Trigger download
                        document.body.appendChild(link);
                        link.click();
                        document.body.removeChild(link);
                        
                        // Clean up
                        setTimeout(() => URL.revokeObjectURL(downloadUrl), 1000);
                        
                        resolve();
                    } catch (error) {
                        reject(error);
                    }
                },
                onerror: function(error) {
                    reject(new Error('Download failed: ' + error.statusText));
                },
                onprogress: function(progress) {
                    if (progress.lengthComputable) {
                        const percent = Math.round((progress.loaded / progress.total) * 100);
                        console.log(`Download progress: ${percent}%`);
                    }
                }
            });
        });
    }

    // Create beautiful download dropdown menu with enhanced UI
    function createDownloadDropdown() {
        if (downloadDropdown) return downloadDropdown;

        downloadDropdown = document.createElement('div');
        downloadDropdown.id = 'redgifs-download-dropdown';
        
        // Create dropdown content with better structure
        downloadDropdown.innerHTML = `
            <div class="dropdown-header">
                <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M8 12.793L5.646 10.44a.5.5 0 01.708-.708L7.5 10.879V1.5a.5.5 0 011 0v9.379l1.146-1.147a.5.5 0 01.708.708L8 12.793z"/>
                    <path d="M2 14.5a.5.5 0 01.5-.5h11a.5.5 0 010 1h-11a.5.5 0 01-.5-.5z"/>
                </svg>
                <span>Download Options</span>
            </div>
            <div class="dropdown-separator"></div>
            <div class="dropdown-item recommended" data-quality="sd">
                <div class="item-icon">
                    <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                        <path d="M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
                        <path d="M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z"/>
                    </svg>
                </div>
                <div class="item-content">
                    <div class="item-title">Standard Quality</div>
                    <div class="item-subtitle">Discord friendly • Fast download</div>
                    <div class="item-size">~2-5 MB</div>
                </div>
                <div class="item-badge">Recommended</div>
            </div>
            <div class="dropdown-item" data-quality="hd">
                <div class="item-icon">
                    <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                        <path d="M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
                        <path d="M1.5 2A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13zm13 1a.5.5 0 0 1 .5.5v6l-3.775-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3.5a.5.5 0 0 1 .5-.5h13z"/>
                        <circle cx="10.5" cy="8.5" r="1.5" fill="currentColor"/>
                    </svg>
                </div>
                <div class="item-content">
                    <div class="item-title">High Quality</div>
                    <div class="item-subtitle">Best resolution • Larger file</div>
                    <div class="item-size">~8-15 MB</div>
                </div>
            </div>
        `;

        downloadDropdown.style.cssText = `
            position: fixed;
            bottom: 70px;
            right: 62px;
            z-index: 10001;
            background: rgba(15, 15, 15, 0.98);
            border: 1px solid rgba(255, 255, 255, 0.12);
            border-radius: 12px;
            backdrop-filter: blur(16px);
            box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.4);
            display: none;
            flex-direction: column;
            min-width: 240px;
            max-width: 280px;
            overflow: hidden;
            transform: translateY(10px) scale(0.95);
            opacity: 0;
            transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        `;

        // Enhanced CSS styles for dropdown
        if (!document.getElementById('redgifs-dropdown-styles')) {
            const style = document.createElement('style');
            style.id = 'redgifs-dropdown-styles';
            style.textContent = `
                /* Dropdown Header */
                #redgifs-download-dropdown .dropdown-header {
                    display: flex;
                    align-items: center;
                    gap: 8px;
                    padding: 12px 16px 8px 16px;
                    color: rgba(255, 255, 255, 0.9);
                    font-size: 13px;
                    font-weight: 600;
                    letter-spacing: -0.01em;
                }
                
                /* Separator */
                #redgifs-download-dropdown .dropdown-separator {
                    height: 1px;
                    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent);
                    margin: 0 12px 8px 12px;
                }
                
                /* Dropdown Items */
                #redgifs-download-dropdown .dropdown-item {
                    display: flex;
                    align-items: center;
                    gap: 12px;
                    padding: 12px 16px;
                    color: white;
                    cursor: pointer;
                    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
                    user-select: none;
                    border: none;
                    background: none;
                    width: 100%;
                    text-align: left;
                    position: relative;
                    margin: 2px 8px;
                    border-radius: 8px;
                }
                
                #redgifs-download-dropdown .dropdown-item:hover {
                    background: rgba(255, 255, 255, 0.08);
                    transform: translateX(2px);
                }
                
                #redgifs-download-dropdown .dropdown-item.recommended {
                    background: linear-gradient(135deg, rgba(34, 197, 94, 0.12), rgba(34, 197, 94, 0.08));
                    border: 1px solid rgba(34, 197, 94, 0.2);
                }
                
                #redgifs-download-dropdown .dropdown-item.recommended:hover {
                    background: linear-gradient(135deg, rgba(34, 197, 94, 0.18), rgba(34, 197, 94, 0.12));
                    border-color: rgba(34, 197, 94, 0.3);
                    transform: translateX(2px);
                }
                
                /* Item Icons */
                #redgifs-download-dropdown .item-icon {
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    width: 32px;
                    height: 32px;
                    background: rgba(255, 255, 255, 0.08);
                    border-radius: 8px;
                    color: rgba(255, 255, 255, 0.8);
                    flex-shrink: 0;
                    transition: all 0.2s ease;
                }
                
                #redgifs-download-dropdown .dropdown-item:hover .item-icon {
                    background: rgba(255, 255, 255, 0.12);
                    color: rgba(255, 255, 255, 0.95);
                    transform: scale(1.05);
                }
                
                #redgifs-download-dropdown .dropdown-item.recommended .item-icon {
                    background: rgba(34, 197, 94, 0.15);
                    color: rgba(34, 197, 94, 0.9);
                }
                
                #redgifs-download-dropdown .dropdown-item.recommended:hover .item-icon {
                    background: rgba(34, 197, 94, 0.25);
                    color: rgb(34, 197, 94);
                }
                
                /* Item Content */
                #redgifs-download-dropdown .item-content {
                    flex: 1;
                    display: flex;
                    flex-direction: column;
                    gap: 2px;
                }
                
                #redgifs-download-dropdown .item-title {
                    font-size: 14px;
                    font-weight: 600;
                    color: rgba(255, 255, 255, 0.95);
                    line-height: 1.2;
                    letter-spacing: -0.01em;
                }
                
                #redgifs-download-dropdown .item-subtitle {
                    font-size: 12px;
                    font-weight: 400;
                    color: rgba(255, 255, 255, 0.65);
                    line-height: 1.3;
                }
                
                #redgifs-download-dropdown .item-size {
                    font-size: 11px;
                    font-weight: 500;
                    color: rgba(255, 255, 255, 0.5);
                    margin-top: 2px;
                    font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
                }
                
                /* Recommended Badge */
                #redgifs-download-dropdown .item-badge {
                    font-size: 10px;
                    font-weight: 600;
                    color: rgb(34, 197, 94);
                    background: rgba(34, 197, 94, 0.15);
                    padding: 3px 8px;
                    border-radius: 12px;
                    letter-spacing: 0.5px;
                    text-transform: uppercase;
                    border: 1px solid rgba(34, 197, 94, 0.2);
                    flex-shrink: 0;
                }
                
                /* Disabled State */
                #redgifs-download-dropdown .dropdown-item[disabled] {
                    opacity: 0.4;
                    cursor: not-allowed;
                    pointer-events: none;
                }
                
                /* Show Animation */
                #redgifs-download-dropdown.show {
                    transform: translateY(0) scale(1);
                    opacity: 1;
                }
                
                /* Responsive Adjustments */
                @media (max-width: 768px) {
                    #redgifs-download-dropdown {
                        min-width: 220px;
                        max-width: 260px;
                        border-radius: 10px;
                    }
                    
                    #redgifs-download-dropdown .dropdown-item {
                        padding: 10px 14px;
                    }
                    
                    #redgifs-download-dropdown .item-title {
                        font-size: 13px;
                    }
                    
                    #redgifs-download-dropdown .item-subtitle {
                        font-size: 11px;
                    }
                }
            `;
            document.head.appendChild(style);
        }

        // Add click handlers
        downloadDropdown.addEventListener('click', handleDropdownClick);
        
        return downloadDropdown;
    }

    // Handle dropdown item clicks
    async function handleDropdownClick(event) {
        const item = event.target.closest('.dropdown-item');
        if (!item || isDownloading || item.hasAttribute('disabled')) return;
        
        const quality = item.dataset.quality;
        hideDropdown();
        await performDownload(quality);
    }

    // Show dropdown with enhanced animations and better UX
    function showDropdown() {
        if (!downloadDropdown || isDownloading || isDropdownVisible) return;
        
        clearTimeout(dropdownTimeout);
        isDropdownVisible = true;
        
        // Update dropdown based on available qualities
        const activeVideo = detectCurrentVideo();
        const qualities = detectAvailableQualities(activeVideo);
        
        const sdItem = downloadDropdown.querySelector('[data-quality="sd"]');
        const hdItem = downloadDropdown.querySelector('[data-quality="hd"]');
        
        // Update file size information
        if (sdItem) {
            const sizeSpan = sdItem.querySelector('.item-size');
            if (sizeSpan) sizeSpan.textContent = qualities.sd.size;
            sdItem.style.display = qualities.sd.available ? 'flex' : 'none';
        }
        
        if (hdItem) {
            const sizeSpan = hdItem.querySelector('.item-size');
            if (sizeSpan) sizeSpan.textContent = qualities.hd.size;
            hdItem.style.display = qualities.hd.available ? 'flex' : 'none';
            
            if (!qualities.hd.available) {
                hdItem.setAttribute('disabled', 'true');
            } else {
                hdItem.removeAttribute('disabled');
            }
        }
        
        // Responsive positioning
        const mediaQuery = window.matchMedia('(max-width: 768px)');
        if (mediaQuery.matches) {
            downloadDropdown.style.bottom = '60px';
            downloadDropdown.style.right = '47px';
        } else {
            downloadDropdown.style.bottom = '70px';
            downloadDropdown.style.right = '62px';
        }
        
        // Show with animation
        downloadDropdown.style.display = 'flex';
        requestAnimationFrame(() => {
            downloadDropdown.classList.add('show');
        });
    }

    // Hide dropdown with smooth animation
    function hideDropdown() {
        if (!downloadDropdown || !isDropdownVisible) return;
        
        dropdownTimeout = setTimeout(() => {
            downloadDropdown.classList.remove('show');
            isDropdownVisible = false;
            
            setTimeout(() => {
                if (!isDropdownVisible) {
                    downloadDropdown.style.display = 'none';
                }
            }, 250);
        }, 150);
    }

    // Create fixed download button without conflicting elements
    function createDownloadButton() {
        if (downloadButton) return downloadButton;

        downloadButton = document.createElement('button');
        downloadButton.id = 'redgifs-download-btn';
        
        // Create icon container separately to avoid innerHTML conflicts
        const iconContainer = document.createElement('div');
        iconContainer.id = 'download-icon-container';
        iconContainer.innerHTML = `
            <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                <path d="M8 12.793L5.646 10.44a.5.5 0 01.708-.708L7.5 10.879V1.5a.5.5 0 011 0v9.379l1.146-1.147a.5.5 0 01.708.708L8 12.793z"/>
                <path d="M2 14.5a.5.5 0 01.5-.5h11a.5.5 0 010 1h-11a.5.5 0 01-.5-.5z"/>
            </svg>
        `;
        
        downloadButton.appendChild(iconContainer);

        downloadButton.style.cssText = `
            position: fixed;
            bottom: 70px;
            right: 20px;
            z-index: 10000;
            background: linear-gradient(135deg, rgba(0, 0, 0, 0.9), rgba(20, 20, 20, 0.85));
            color: white;
            border: 1px solid rgba(255, 255, 255, 0.15);
            border-radius: 10px;
            padding: 10px;
            font-size: 12px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(12px);
            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2);
            user-select: none;
            outline: none;
            width: 36px;
            height: 36px;
            min-width: 36px;
            min-height: 36px;
        `;

        // Enhanced hover effects and dropdown handling
        downloadButton.addEventListener('mouseenter', () => {
            if (!isDownloading) {
                downloadButton.style.transform = 'translateY(-2px) scale(1.05)';
                downloadButton.style.background = 'linear-gradient(135deg, rgba(20, 20, 20, 0.95), rgba(40, 40, 40, 0.9))';
                downloadButton.style.borderColor = 'rgba(255, 255, 255, 0.25)';
                downloadButton.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3)';
                
                clearTimeout(dropdownTimeout);
                setTimeout(showDropdown, 300);
            }
        });

        downloadButton.addEventListener('mouseleave', () => {
            if (!isDownloading) {
                downloadButton.style.transform = 'translateY(0) scale(1)';
                downloadButton.style.background = 'linear-gradient(135deg, rgba(0, 0, 0, 0.9), rgba(20, 20, 20, 0.85))';
                downloadButton.style.borderColor = 'rgba(255, 255, 255, 0.15)';
                downloadButton.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2)';
                
                hideDropdown();
            }
        });

        // Handle click for default SD download
        downloadButton.addEventListener('click', () => performDownload('sd'));
        
        return downloadButton;
    }

    // Update icon content during download states
    function updateDownloadButtonIcon(content) {
        const iconContainer = downloadButton?.querySelector('#download-icon-container');
        if (iconContainer) {
            iconContainer.innerHTML = content;
        }
    }

    // Enhanced download functionality with better visual feedback
    async function performDownload(quality = 'sd') {
        if (isDownloading) return;

        isDownloading = true;

        try {
            // Enhanced loading animation
            updateDownloadButtonIcon(`
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                    <circle cx="12" cy="12" r="3" fill="currentColor">
                        <animate attributeName="r" values="3;6;3" dur="1s" repeatCount="indefinite"/>
                        <animate attributeName="opacity" values="1;0.3;1" dur="1s" repeatCount="indefinite"/>
                    </circle>
                    <circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.3">
                        <animate attributeName="stroke-dasharray" values="0 50;25 25;0 50" dur="1.5s" repeatCount="indefinite"/>
                        <animateTransform attributeName="transform" type="rotate" values="0 12 12;360 12 12" dur="2s" repeatCount="indefinite"/>
                    </circle>
                </svg>
            `);
            downloadButton.style.background = 'linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(79, 70, 229, 0.8))';
            downloadButton.style.borderColor = 'rgba(59, 130, 246, 0.6)';

            const videoId = getCurrentVideoId();
            if (!videoId) {
                throw new Error('Could not detect current video');
            }

            console.log(`Downloading video ${videoId} in ${quality.toUpperCase()} quality`);

            const urls = await getSignedUrls(videoId);
            if (!urls) {
                throw new Error('Could not get video URLs from API');
            }

            // Choose URL based on requested quality with SD fallback
            let downloadUrl;
            if (quality === 'hd' && urls.hd) {
                downloadUrl = urls.hd;
            } else if (urls.sd) {
                downloadUrl = urls.sd;
            } else {
                downloadUrl = urls.hd || urls.thumbnail;
            }

            if (!downloadUrl) {
                throw new Error('No suitable video URL found');
            }

            // Generate filename
            const filename = `redgifs_${videoId}_${quality}.mp4`;
            
            // Download video to internal storage
            await downloadVideoFile(downloadUrl, filename);

            // Enhanced success feedback
            updateDownloadButtonIcon(`
                <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
                </svg>
            `);
            downloadButton.style.background = 'linear-gradient(135deg, rgba(34, 197, 94, 0.9), rgba(21, 128, 61, 0.8))';
            downloadButton.style.borderColor = 'rgba(34, 197, 94, 0.6)';
            downloadButton.style.boxShadow = '0 8px 24px rgba(34, 197, 94, 0.3), 0 2px 8px rgba(34, 197, 94, 0.2)';

            setTimeout(() => {
                updateDownloadButtonIcon(`
                    <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                        <path d="M8 12.793L5.646 10.44a.5.5 0 01.708-.708L7.5 10.879V1.5a.5.5 0 011 0v9.379l1.146-1.147a.5.5 0 01.708.708L8 12.793z"/>
                        <path d="M2 14.5a.5.5 0 01.5-.5h11a.5.5 0 010 1h-11a.5.5 0 01-.5-.5z"/>
                    </svg>
                `);
                downloadButton.style.background = 'linear-gradient(135deg, rgba(0, 0, 0, 0.9), rgba(20, 20, 20, 0.85))';
                downloadButton.style.borderColor = 'rgba(255, 255, 255, 0.15)';
                downloadButton.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2)';
                isDownloading = false;
            }, 2500);

        } catch (error) {
            console.error('Download failed:', error);

            // Enhanced error feedback
            updateDownloadButtonIcon(`
                <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
                    <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
                </svg>
            `);
            downloadButton.style.background = 'linear-gradient(135deg, rgba(239, 68, 68, 0.9), rgba(185, 28, 28, 0.8))';
            downloadButton.style.borderColor = 'rgba(239, 68, 68, 0.6)';
            downloadButton.style.boxShadow = '0 8px 24px rgba(239, 68, 68, 0.3), 0 2px 8px rgba(239, 68, 68, 0.2)';

            setTimeout(() => {
                updateDownloadButtonIcon(`
                    <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                        <path d="M8 12.793L5.646 10.44a.5.5 0 01.708-.708L7.5 10.879V1.5a.5.5 0 011 0v9.379l1.146-1.147a.5.5 0 01.708.708L8 12.793z"/>
                        <path d="M2 14.5a.5.5 0 01.5-.5h11a.5.5 0 010 1h-11a.5.5 0 01-.5-.5z"/>
                    </svg>
                `);
                downloadButton.style.background = 'linear-gradient(135deg, rgba(0, 0, 0, 0.9), rgba(20, 20, 20, 0.85))';
                downloadButton.style.borderColor = 'rgba(255, 255, 255, 0.15)';
                downloadButton.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2)';
                isDownloading = false;
            }, 3500);
        }
    }

    // Create enhanced copy button
    function createCopyButton() {
        if (copyButton) return copyButton;

        copyButton = document.createElement('button');
        copyButton.id = 'redgifs-content-copy-btn';
        copyButton.innerHTML = `
            <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
                <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
            </svg>
            <span>Copy Video</span>
        `;

        copyButton.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 10000;
            background: rgba(0, 0, 0, 0.85);
            color: white;
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 8px;
            padding: 10px 14px;
            font-size: 13px;
            font-weight: 500;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 6px;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(8px);
            box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
            user-select: none;
            outline: none;
            min-width: 110px;
            justify-content: center;
        `;

        // Responsive design for copy button only
        const mediaQuery = window.matchMedia('(max-width: 768px)');
        function applyResponsiveStyles() {
            if (mediaQuery.matches) {
                copyButton.style.bottom = '15px';
                copyButton.style.right = '15px';
                copyButton.style.padding = '8px 12px';
                copyButton.style.fontSize = '12px';
                copyButton.style.minWidth = '90px';
                // Adjust download button position on mobile
                if (downloadButton) {
                    downloadButton.style.bottom = '60px';
                    downloadButton.style.right = '15px';
                }
            } else {
                copyButton.style.bottom = '20px';
                copyButton.style.right = '20px';
                copyButton.style.padding = '10px 14px';
                copyButton.style.fontSize = '13px';
                copyButton.style.minWidth = '110px';
                // Adjust download button position on desktop
                if (downloadButton) {
                    downloadButton.style.bottom = '70px';
                    downloadButton.style.right = '20px';
                }
            }
        }
        applyResponsiveStyles();
        mediaQuery.addListener(applyResponsiveStyles);

        // Enhanced hover effects
        copyButton.addEventListener('mouseenter', () => {
            if (!isProcessing) {
                copyButton.style.background = 'rgba(0, 0, 0, 0.95)';
                copyButton.style.borderColor = 'rgba(255, 255, 255, 0.4)';
                copyButton.style.transform = 'translateY(-2px)';
                copyButton.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.4)';
            }
        });

        copyButton.addEventListener('mouseleave', () => {
            if (!isProcessing) {
                copyButton.style.background = 'rgba(0, 0, 0, 0.85)';
                copyButton.style.borderColor = 'rgba(255, 255, 255, 0.2)';
                copyButton.style.transform = 'translateY(0)';
                copyButton.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.3)';
            }
        });

        copyButton.addEventListener('click', handleCopyClick);
        return copyButton;
    }

    // Enhanced copy handler
    async function handleCopyClick(event) {
        event.preventDefault();
        event.stopPropagation();

        if (isProcessing) return;

        isProcessing = true;
        const originalContent = copyButton.innerHTML;

        try {
            copyButton.innerHTML = `
                <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                    <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
                    <path d="M12 2 A10 10 0 0 1 22 12" stroke="currentColor" stroke-width="2" fill="none">
                        <animateTransform attributeName="transform" type="rotate" values="0 12 12;360 12 12" dur="1s" repeatCount="indefinite"/>
                    </path>
                </svg>
                <span>Getting URL...</span>
            `;
            copyButton.style.background = 'rgba(64, 64, 64, 0.9)';

            const videoId = getCurrentVideoId();
            if (!videoId) {
                throw new Error('Could not detect current video');
            }

            if (videoId === currentVideoId) {
                console.log('Same video detected:', videoId);
            } else {
                console.log('New video detected:', videoId);
                currentVideoId = videoId;
            }

            const urls = await getSignedUrls(videoId);
            if (!urls) {
                throw new Error('Could not get signed URLs from API');
            }

            const signedUrl = urls.hd || urls.sd || urls.thumbnail || null;
            if (!signedUrl) {
                throw new Error('No valid signed URL found');
            }

            // Copy to clipboard
            let copySuccess = false;

            if (navigator.clipboard && window.isSecureContext) {
                try {
                    await navigator.clipboard.writeText(signedUrl);
                    copySuccess = true;
                } catch (clipboardError) {
                    console.warn('Clipboard API failed, using fallback');
                }
            }

            if (!copySuccess) {
                const textArea = document.createElement('textarea');
                textArea.value = signedUrl;
                textArea.style.cssText = 'position:fixed;top:-999px;opacity:0;pointer-events:none;';
                document.body.appendChild(textArea);
                textArea.focus();
                textArea.select();
                copySuccess = document.execCommand('copy');
                document.body.removeChild(textArea);
            }

            if (copySuccess) {
                copyButton.innerHTML = `
                    <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                        <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
                    </svg>
                    <span>Copied! (1h)</span>
                `;
                copyButton.style.background = 'rgba(34, 197, 94, 0.9)';
                copyButton.style.borderColor = 'rgba(34, 197, 94, 0.6)';

                setTimeout(() => {
                    copyButton.innerHTML = originalContent;
                    copyButton.style.background = 'rgba(0, 0, 0, 0.85)';
                    copyButton.style.borderColor = 'rgba(255, 255, 255, 0.2)';
                    isProcessing = false;
                }, 3000);
            } else {
                throw new Error('Copy to clipboard failed');
            }

        } catch (error) {
            console.error('Copy operation failed:', error);

            copyButton.innerHTML = `
                <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
                    <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
                    <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
                </svg>
                <span>Failed</span>
            `;
            copyButton.style.background = 'rgba(239, 68, 68, 0.9)';
            copyButton.style.borderColor = 'rgba(239, 68, 68, 0.6)';

            setTimeout(() => {
                copyButton.innerHTML = originalContent;
                copyButton.style.background = 'rgba(0, 0, 0, 0.85)';
                copyButton.style.borderColor = 'rgba(255, 255, 255, 0.2)';
                isProcessing = false;
            }, 4000);
        }
    }

    // Setup comprehensive video monitoring
    function setupVideoMonitoring() {
        // Intersection observer for video visibility changes
        if (videoObserver) {
            videoObserver.disconnect();
        }

        videoObserver = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting && entry.intersectionRatio > 0.3) {
                    // Video became prominently visible
                    setTimeout(() => {
                        const newVideoId = getCurrentVideoId();
                        if (newVideoId && newVideoId !== currentVideoId) {
                            currentVideoId = newVideoId;
                            console.log('Video changed via intersection:', newVideoId);
                        }
                    }, 100);
                }
            });
        }, {
            threshold: [0.3, 0.5, 0.7],
            rootMargin: '0px'
        });

        // Observe all videos
        document.querySelectorAll('video').forEach(video => {
            videoObserver.observe(video);
        });

        // Scroll monitoring with throttling
        let scrollTimeout;
        const scrollHandler = () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(() => {
                const newVideoId = getCurrentVideoId();
                if (newVideoId && newVideoId !== currentVideoId) {
                    currentVideoId = newVideoId;
                    console.log('Video changed via scroll:', newVideoId);
                }
            }, 150);
        };

        window.addEventListener('scroll', scrollHandler, { passive: true });

        // Monitor for new videos being added
        const domObserver = new MutationObserver((mutations) => {
            let hasNewVideos = false;

            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.tagName === 'VIDEO' || node.querySelector?.('video')) {
                            hasNewVideos = true;
                            if (node.tagName === 'VIDEO') {
                                videoObserver.observe(node);
                            } else {
                                node.querySelectorAll('video').forEach(v => videoObserver.observe(v));
                            }
                        }
                    }
                });
            });

            if (hasNewVideos) {
                setTimeout(() => {
                    const newVideoId = getCurrentVideoId();
                    if (newVideoId && newVideoId !== currentVideoId) {
                        currentVideoId = newVideoId;
                        console.log('Video changed via DOM update:', newVideoId);
                    }
                }, 300);
            }
        });

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

    // Enhanced button state management with niches and search support
    async function updateButtonState() {
        console.log('Updating button state...', window.location.pathname);
        
        // Enhanced page detection including niches and search pages
        const isValidPage = window.location.pathname.startsWith('/users/') ||
                           window.location.pathname.startsWith('/watch/') ||
                           window.location.pathname.startsWith('/niches/') ||
                           window.location.pathname.startsWith('/search/') ||
                           window.location.pathname === '/' ||
                           window.location.pathname === '' ||
                           window.location.pathname === '/home' ||
                           isValidPageWithContent();
        
        if (isValidPage) {
            // Wait for videos to load
            const hasVideos = await waitForVideosOnHomepage();
            
            if (hasVideos) {
                // Create and add both buttons and dropdown
                if (!copyButton || !document.body.contains(copyButton)) {
                    const copyBtn = createCopyButton();
                    document.body.appendChild(copyBtn);
                    console.log('Copy button created and added to page');
                }
                
                if (!downloadButton || !document.body.contains(downloadButton)) {
                    const downloadBtn = createDownloadButton();
                    document.body.appendChild(downloadBtn);
                    console.log('Download button created and added to page');
                }
                
                if (!downloadDropdown || !document.body.contains(downloadDropdown)) {
                    const dropdown = createDownloadDropdown();
                    document.body.appendChild(dropdown);
                    
                    // Setup dropdown hover handling
                    dropdown.addEventListener('mouseenter', () => {
                        clearTimeout(dropdownTimeout);
                        isDropdownVisible = true;
                    });
                    dropdown.addEventListener('mouseleave', hideDropdown);
                    
                    console.log('Download dropdown created and added to page');
                }
                
                copyButton.style.display = 'flex';
                downloadButton.style.display = 'flex';
                
                // Initialize video monitoring
                setupVideoMonitoring();
                
                // Get initial video ID with delay to ensure content is ready
                setTimeout(() => {
                    const initialVideoId = getCurrentVideoId();
                    if (initialVideoId) {
                        currentVideoId = initialVideoId;
                        console.log('Initial video detected:', initialVideoId);
                    } else {
                        console.log('No initial video detected, will monitor for changes');
                    }
                }, 1000);
                
            } else {
                console.log('No videos found after waiting period');
                if (copyButton) {
                    copyButton.style.display = 'none';
                }
                if (downloadButton) {
                    downloadButton.style.display = 'none';
                }
                if (downloadDropdown) {
                    downloadDropdown.style.display = 'none';
                }
            }
        } else {
            if (copyButton) {
                copyButton.style.display = 'none';
            }
            if (downloadButton) {
                downloadButton.style.display = 'none';
            }
            if (downloadDropdown) {
                downloadDropdown.style.display = 'none';
            }
        }
    }

    // Enhanced initialization with multiple attempts
    async function initialize() {
        console.log('RedGifs Content-Aware Copy Button & Download initializing...');
        
        // Extract token early with more aggressive retry
        setTimeout(extractBearerToken, 500);
        
        // Initial button state update
        await updateButtonState();
        
        // Monitor URL changes for SPA navigation with faster polling
        let currentUrl = window.location.href;
        const urlCheckInterval = setInterval(async () => {
            if (window.location.href !== currentUrl) {
                currentUrl = window.location.href;
                console.log('URL changed, updating button state');
                setTimeout(async () => await updateButtonState(), 300);
            }
        }, 250); // Faster polling for better responsiveness
        
        // Handle page visibility changes
        document.addEventListener('visibilitychange', async () => {
            if (!document.hidden) {
                setTimeout(async () => await updateButtonState(), 200);
            }
        });
        
        // Add additional MutationObserver for content changes
        const contentObserver = new MutationObserver(async (mutations) => {
            let hasNewContent = false;
            
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        if (node.tagName === 'VIDEO' || 
                            node.querySelector?.('video') ||
                            node.className?.includes('video') ||
                            node.className?.includes('gif')) {
                            hasNewContent = true;
                        }
                    }
                });
            });
            
            if (hasNewContent && (!copyButton || copyButton.style.display !== 'flex')) {
                console.log('New video content detected, updating button state');
                setTimeout(async () => await updateButtonState(), 500);
            }
        });
        
        contentObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
        
        console.log('RedGifs Content-Aware Copy Button & Download initialized');
    }

    // Enhanced startup sequence with multiple initialization attempts
    function startScript() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initialize);
        } else {
            initialize();
        }
        
        // Additional safety net - retry initialization after page load
        window.addEventListener('load', () => {
            setTimeout(async () => {
                if (!downloadButton || !document.body.contains(downloadButton)) {
                    console.log('Safety net: Re-initializing download button after page load');
                    await updateButtonState();
                }
            }, 1000);
        });
        
        // Final fallback for stubborn pages
        setTimeout(async () => {
            if (!downloadButton || !document.body.contains(downloadButton)) {
                console.log('Final fallback: Force download button creation');
                await updateButtonState();
            }
        }, 3000);
    }

    // Start the script with enhanced initialization
    startScript();
})();