Greasy Fork

Greasy Fork is available in English.

Universal Video Share Button with M3U8 Support

Share button with M3U8 support. Downloads and converts to MP4. Hold 8s for debug console..... HOYL SHITBU IT WORKS I GOT ENCODING IN THE BROWSR FAST>>:FEnfiSENGEGYIEWhfgawyriughawup

当前为 2025-11-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Universal Video Share Button with M3U8 Support
// @namespace    http://tampermonkey.net/
// @version      6.4
// @description  Share button with M3U8 support. Downloads and converts to MP4. Hold 8s for debug console..... HOYL SHITBU IT WORKS I GOT ENCODING IN THE BROWSR FAST>>:FEnfiSENGEGYIEWhfgawyriughawup
// @author       Minoa
// @license      MIT
// @match        *://*/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/m3u8-parser.min.js
// @require      https://cdn.jsdelivr.net/npm/@warren-bank/[email protected]/dist/umd/ffmpeg.js
// @resource     classWorkerURL  https://cdn.jsdelivr.net/npm/@warren-bank/[email protected]/dist/umd/258.ffmpeg.js
// @resource     coreURL         https://cdn.jsdelivr.net/npm/@ffmpeg/[email protected]/dist/umd/ffmpeg-core.js
// @resource     wasmURL         https://cdn.jsdelivr.net/npm/@ffmpeg/[email protected]/dist/umd/ffmpeg-core.wasm
// @grant        GM_getResourceURL
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var floatingButton = null;
    var pressTimer = null;
    var isLongPress = false;
    var checkInterval = null;
    var detectedM3U8s = [];
    var detectedM3U8Urls = [];
    var allDetectedVideos = new Map();
    var processedVideos = new Map();
    var downloadedBlobs = new Map();
    var debugMode = false;
    var debugConsole = null;
    var debugLogs = [];
    var longPressStartTime = 0;
    var ffmpegInstance = null;
    var ffmpegLoaded = false;
    var wasmBinaryCache = null;

    // Detect iOS
    const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
    const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
    const isMobile = isIOS || /Android|webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

    // Color scheme
    const COLORS = {
        button: '#55423d',
        buttonHover: '#6b5651',
        icon: '#ffc0ad',
        text: '#fff3ec'
    };

    // Common video selectors
    var VIDEO_SELECTORS = [
        'video',
        '.video-player video',
        '.player video',
        '#player video',
        '.video-container video',
        '[class*="video"] video',
        '[class*="player"] video',
        'iframe[src*="youtube.com"]',
        'iframe[src*="vimeo.com"]',
        'iframe[src*="dailymotion.com"]',
        'iframe[src*="twitch.tv"]'
    ];

    var VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.wmv', '.flv', '.mkv', '.m4v', '.3gp'];

    // FFmpeg helpers
    const getWasmBinary = async () => {
        if (wasmBinaryCache) {
            return wasmBinaryCache.slice(0);
        }

        const wasmURL = GM_getResourceURL('wasmURL', false);
        debugLog('[FFMPEG] Loading WASM from: ' + wasmURL.substring(0, 50) + '...');

        let wasmBinary = null;

        if (wasmURL.startsWith('data:')) {
            const index = wasmURL.indexOf(',');
            const base64 = wasmURL.substring(index + 1);
            const binaryString = atob(base64);
            const bytes = new Uint8Array(binaryString.length);
            for (let i = 0; i < binaryString.length; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            wasmBinary = bytes.buffer;
        } else if (wasmURL.startsWith('blob:')) {
            wasmBinary = await fetch(wasmURL).then(res => res.arrayBuffer());
        }

        if (wasmBinary) {
            debugLog('[FFMPEG] WASM loaded: ' + (wasmBinary.byteLength / 1024 / 1024).toFixed(2) + 'MB');
            wasmBinaryCache = wasmBinary.slice(0);
        }

        return wasmBinary;
    };

    const getFFmpegLoadConfig = async () => ({
        classWorkerURL: GM_getResourceURL('classWorkerURL', false),
        coreURL: GM_getResourceURL('coreURL', false),
        wasmBinary: await getWasmBinary(),
        createTrustedTypePolicy: true
    });

    async function initFFmpeg() {
        if (ffmpegLoaded && ffmpegInstance) return ffmpegInstance;

        debugLog('[FFMPEG] Initializing FFmpeg...');
        showNotification('⚙️ Loading FFmpeg...', 'info');

        try {
            ffmpegInstance = new window.FFmpegWASM.FFmpeg();

            ffmpegInstance.on('log', ({ message }) => {
                debugLog('[FFMPEG LOG] ' + message);
            });

            ffmpegInstance.on('progress', ({ progress }) => {
                const percent = Math.round(progress * 100);
                updateProgress(percent);
                debugLog('[FFMPEG] Encoding progress: ' + percent + '%');
            });

            await ffmpegInstance.load(await getFFmpegLoadConfig());
            ffmpegLoaded = true;
            debugLog('[FFMPEG] ✅ FFmpeg ready!');
            showNotification('✅ FFmpeg loaded', 'success');

            return ffmpegInstance;
        } catch(e) {
            debugLog('[ERROR] FFmpeg init failed: ' + e.message);
            showNotification('❌ FFmpeg failed to load', 'error');
            throw e;
        }
    }

    async function convertTStoMP4(tsBlob, filename) {
        debugLog('[CONVERT] Starting TS to MP4 conversion...');
        debugLog('[CONVERT] Input size: ' + (tsBlob.size / 1024 / 1024).toFixed(2) + 'MB');

        try {
            showNotification('⚙️ Loading FFmpeg encoder...', 'info');

            const ffmpeg = await initFFmpeg();

            const inputName = 'input.ts';
            const outputName = filename.replace('.ts', '.mp4');

            debugLog('[CONVERT] Writing input file to FFmpeg...');
            showNotification('📝 Preparing conversion...', 'info');

            const inputData = new Uint8Array(await tsBlob.arrayBuffer());
            await ffmpeg.writeFile(inputName, inputData);

            debugLog('[CONVERT] Starting FFmpeg conversion...');
            debugLog('[CONVERT] Command: -i input.ts -c copy -movflags faststart output.mp4');
            showNotification('🔄 Converting to MP4...', 'info');

            // Fast remux (no re-encoding)
            await ffmpeg.exec(['-i', inputName, '-c', 'copy', '-movflags', 'faststart', outputName]);

            debugLog('[CONVERT] Reading output file...');
            const data = await ffmpeg.readFile(outputName, 'binary');

            debugLog('[CONVERT] Cleaning up FFmpeg files...');
            await ffmpeg.deleteFile(inputName);
            await ffmpeg.deleteFile(outputName);

            const mp4Blob = new Blob([data.buffer], { type: 'video/mp4' });
            debugLog('[CONVERT] ✅ Conversion complete!');
            debugLog('[CONVERT] Output size: ' + (mp4Blob.size / 1024 / 1024).toFixed(2) + 'MB');

            showNotification('✅ Converted to MP4!', 'success');

            return { blob: mp4Blob, filename: outputName };

        } catch(e) {
            debugLog('[ERROR] Conversion failed: ' + e.message);
            debugLog('[ERROR] Stack: ' + e.stack);
            showNotification('❌ Conversion failed', 'error');
            throw e;
        }
    }

    // Helper function to check if URL is M3U8
    function isM3U8Url(url) {
        if (!url) return false;
        const lowerUrl = url.toLowerCase();
        return lowerUrl.includes('.m3u8') || lowerUrl.includes('.m3u');
    }

    // Debug console functions
    function createDebugConsole() {
        if (debugConsole) return;

        debugConsole = document.createElement('div');
        debugConsole.id = 'debug-console';
        debugConsole.style.cssText = `
            position: fixed;
            top: 70px;
            left: 10px;
            right: 10px;
            bottom: 10px;
            background: rgba(0, 0, 0, 0.95);
            border: 2px solid ${COLORS.icon};
            border-radius: 12px;
            z-index: 99999999;
            display: flex;
            flex-direction: column;
            font-family: monospace;
            font-size: 11px;
            color: ${COLORS.text};
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8);
        `;

        var header = document.createElement('div');
        header.style.cssText = `
            padding: 12px;
            background: ${COLORS.button};
            border-bottom: 1px solid ${COLORS.icon};
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-radius: 10px 10px 0 0;
        `;
        header.innerHTML = `<strong style="color: ${COLORS.icon};">🐛 DEBUG CONSOLE</strong>`;

        var buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = 'display: flex; gap: 8px;';

        var copyBtn = document.createElement('button');
        copyBtn.textContent = '📋 Copy';
        copyBtn.style.cssText = `
            background: ${COLORS.icon};
            color: #000;
            border: none;
            padding: 6px 12px;
            border-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            font-size: 11px;
        `;
        copyBtn.onclick = function() {
            var logText = debugLogs.join('\n');
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(logText).then(function() {
                    copyBtn.textContent = '✅ Copied!';
                    setTimeout(function() {
                        copyBtn.textContent = '📋 Copy';
                    }, 2000);
                });
            } else {
                var textArea = document.createElement('textarea');
                textArea.value = logText;
                textArea.style.position = 'fixed';
                textArea.style.left = '-999999px';
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                copyBtn.textContent = '✅ Copied!';
                setTimeout(function() {
                    copyBtn.textContent = '📋 Copy';
                }, 2000);
            }
        };

        var clearBtn = document.createElement('button');
        clearBtn.textContent = '🗑️ Clear';
        clearBtn.style.cssText = `
            background: rgba(239, 68, 68, 0.8);
            color: ${COLORS.text};
            border: none;
            padding: 6px 12px;
            border-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            font-size: 11px;
        `;
        clearBtn.onclick = function() {
            debugLogs = [];
            updateDebugConsole();
        };

        var closeBtn = document.createElement('button');
        closeBtn.textContent = '✕';
        closeBtn.style.cssText = `
            background: rgba(255, 255, 255, 0.2);
            color: ${COLORS.text};
            border: none;
            padding: 6px 10px;
            border-radius: 6px;
            font-weight: bold;
            cursor: pointer;
            font-size: 11px;
        `;
        closeBtn.onclick = function() {
            debugMode = false;
            debugConsole.remove();
            debugConsole = null;
        };

        buttonContainer.appendChild(copyBtn);
        buttonContainer.appendChild(clearBtn);
        buttonContainer.appendChild(closeBtn);
        header.appendChild(buttonContainer);

        var logContainer = document.createElement('div');
        logContainer.id = 'debug-log-container';
        logContainer.style.cssText = `
            flex: 1;
            overflow-y: auto;
            padding: 12px;
            line-height: 1.5;
        `;

        debugConsole.appendChild(header);
        debugConsole.appendChild(logContainer);
        document.body.appendChild(debugConsole);

        updateDebugConsole();
    }

    function updateDebugConsole() {
        if (!debugConsole) return;
        var logContainer = document.getElementById('debug-log-container');
        if (!logContainer) return;

        logContainer.innerHTML = '';

        debugLogs.forEach(function(log) {
            var logLine = document.createElement('div');
            logLine.style.cssText = 'margin-bottom: 4px; word-wrap: break-word;';

            var color = COLORS.text;
            if (log.includes('[ERROR]') || log.includes('❌')) {
                color = '#ef4444';
            } else if (log.includes('[SUCCESS]') || log.includes('✅')) {
                color = '#4ade80';
            } else if (log.includes('[INFO]') || log.includes('📥') || log.includes('🔄')) {
                color = '#3b82f6';
            } else if (log.includes('[M3U8]') || log.includes('[FFMPEG]')) {
                color = COLORS.icon;
            }

            logLine.style.color = color;
            logLine.textContent = log;
            logContainer.appendChild(logLine);
        });

        logContainer.scrollTop = logContainer.scrollHeight;
    }

    function debugLog(message) {
        var timestamp = new Date().toLocaleTimeString();
        var logMessage = `[${timestamp}] ${message}`;
        debugLogs.push(logMessage);
        console.log(message);

        if (debugMode && debugConsole) {
            updateDebugConsole();
        }
    }

    // M3U8 Detection
    (function setupNetworkDetection() {
        const originalFetch = window.fetch;
        window.fetch = function(...args) {
            const promise = originalFetch.apply(this, args);
            const url = typeof args[0] === 'string' ? args[0] : args[0]?.url;

            promise.then(response => {
                if (url) {
                    if (isM3U8Url(url)) {
                        detectM3U8(url);
                    } else if (!url.match(/seg-\d+-.*\.ts/i) && !url.endsWith('.ts')) {
                        checkUrlForVideo(url);
                    }
                }
                return response;
            }).catch(e => {
                throw e;
            });
            return promise;
        };

        const originalOpen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function(...args) {
            this.addEventListener("load", function() {
                try {
                    const url = args[1];
                    if (url) {
                        if (isM3U8Url(url)) {
                            detectM3U8(url);
                        } else if (!url.match(/seg-\d+-.*\.ts/i) && !url.endsWith('.ts')) {
                            checkUrlForVideo(url);
                        }
                        if (this.responseText && this.responseText.trim().startsWith("#EXTM3U")) {
                            detectM3U8(url);
                        }
                    }
                } catch(e) {}
            });
            return originalOpen.apply(this, args);
        };

        const originalText = Response.prototype.text;
        Response.prototype.text = function() {
            return originalText.call(this).then(text => {
                if (text.trim().startsWith("#EXTM3U")) {
                    detectM3U8(this.url);
                }
                return text;
            });
        };

        const originalSetAttribute = Element.prototype.setAttribute;
        Element.prototype.setAttribute = function(name, value) {
            if (this.tagName === 'VIDEO' || this.tagName === 'SOURCE') {
                if (name === 'src' && value) {
                    if (isM3U8Url(value)) {
                        detectM3U8(value);
                    } else {
                        checkUrlForVideo(value);
                    }
                }
            }
            return originalSetAttribute.call(this, name, value);
        };
    })();

    function checkUrlForVideo(url) {
        try {
            if (url.startsWith('blob:')) return;
            if (url.match(/seg-\d+-.*\.ts/i)) return;
            if (url.endsWith('.ts')) return;
            if (isM3U8Url(url)) {
                debugLog('[VIDEO CHECK] URL contains m3u8, skipping: ' + url);
                return;
            }

            const lowerUrl = url.toLowerCase();
            const isVideo = VIDEO_EXTENSIONS.some(ext => lowerUrl.includes(ext));

            if (isVideo) {
                const fullUrl = new URL(url, location.href).href;
                if (!allDetectedVideos.has(fullUrl)) {
                    debugLog('[VIDEO] Found video file: ' + fullUrl);
                    allDetectedVideos.set(fullUrl, {
                        url: fullUrl,
                        type: 'video',
                        timestamp: Date.now(),
                        title: 'Video - ' + getFilenameFromUrl(fullUrl)
                    });
                    checkForVideos();
                }
            }
        } catch(e) {
            debugLog('[ERROR] checkUrlForVideo: ' + e.message);
        }
    }

    function getFilenameFromUrl(url) {
        try {
            const pathname = new URL(url).pathname;
            const filename = pathname.split('/').pop();
            return filename || 'Unknown';
        } catch(e) {
            return 'Unknown';
        }
    }

    function getTimeAgo(timestamp) {
        const seconds = Math.floor((Date.now() - timestamp) / 1000);
        if (seconds < 60) return 'just now';
        const minutes = Math.floor(seconds / 60);
        if (minutes < 60) return minutes + 'm ago';
        const hours = Math.floor(minutes / 60);
        if (hours < 24) return hours + 'h ago';
        const days = Math.floor(hours / 24);
        return days + 'd ago';
    }

    function getBaseUrlFromSegment(segmentUrl) {
        try {
            const url = new URL(segmentUrl);
            const path = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);
            return url.origin + path;
        } catch(e) {
            return null;
        }
    }

    function isSegmentOfPlaylist(videoUrl) {
        if (!videoUrl.endsWith('.ts')) return false;
        if (!videoUrl.match(/seg-\d+-/i)) return false;

        const baseUrl = getBaseUrlFromSegment(videoUrl);
        if (!baseUrl) return false;

        for (const [m3u8Url, data] of allDetectedVideos.entries()) {
            if (data.type === 'm3u8') {
                const m3u8Base = getBaseUrlFromSegment(m3u8Url);
                if (m3u8Base && baseUrl.startsWith(m3u8Base)) {
                    return true;
                }
            }
        }

        return false;
    }

    function hasM3U8Playlist() {
        return Array.from(allDetectedVideos.values()).some(v => v.type === 'm3u8');
    }

    function isMasterPlaylist(url, manifest) {
        if (url.includes('master.m3u8')) return true;
        if (manifest.playlists && manifest.playlists.length > 0 && (!manifest.segments || manifest.segments.length === 0)) {
            return true;
        }
        return false;
    }

    function shouldFilterM3U8(url, manifest) {
        if (!isMasterPlaylist(url, manifest)) return false;

        const baseUrl = getBaseUrlFromSegment(url);
        if (!baseUrl) return false;

        for (const [otherUrl, data] of allDetectedVideos.entries()) {
            if (data.type === 'm3u8' && otherUrl !== url) {
                const otherBase = getBaseUrlFromSegment(otherUrl);
                if (otherBase === baseUrl && otherUrl.includes('index-')) {
                    debugLog('[M3U8] Filtering master playlist: ' + url);
                    return true;
                }
            }
        }

        return false;
    }

    async function detectM3U8(url) {
        try {
            if (url.startsWith('blob:')) return;

            url = new URL(url, location.href).href;

            const urlWithoutQuery = url.split('?')[0];

            let alreadyHasBetter = false;
            for (const [existingUrl, existingData] of allDetectedVideos.entries()) {
                if (existingData.type === 'm3u8') {
                    const existingWithoutQuery = existingUrl.split('?')[0];
                    if (existingWithoutQuery === urlWithoutQuery) {
                        if (existingData.m3u8Data && existingData.m3u8Data.manifest &&
                            existingData.m3u8Data.manifest.segments &&
                            existingData.m3u8Data.manifest.segments.length > 0) {
                            debugLog('[M3U8] Already have better version with segments: ' + existingUrl);
                            alreadyHasBetter = true;
                            break;
                        }
                    }
                }
            }

            if (alreadyHasBetter) return;

            if (detectedM3U8Urls.includes(url)) {
                debugLog('[M3U8] Already detected: ' + url);
                return;
            }
            detectedM3U8Urls.push(url);

            debugLog('[M3U8] *** DETECTED M3U8 URL ***: ' + url);
            debugLog('[M3U8] Fetching manifest...');

            const response = await fetch(url);
            const content = await response.text();

            const parser = new m3u8Parser.Parser();
            parser.push(content);
            parser.end();
            const manifest = parser.manifest;

            let duration = 0;

            if (manifest.playlists && manifest.playlists.length > 0 && (!manifest.segments || manifest.segments.length === 0)) {
                debugLog('[M3U8] This is a MASTER playlist with ' + manifest.playlists.length + ' variants');

                const bestVariant = manifest.playlists[manifest.playlists.length - 1];
                debugLog('[M3U8] Selected variant: ' + JSON.stringify(bestVariant));

                const baseUrl = url.substring(0, url.lastIndexOf('/') + 1);
                const variantUrl = bestVariant.uri.startsWith('http')
                    ? bestVariant.uri
                    : baseUrl + bestVariant.uri;

                debugLog('[M3U8] Fetching variant playlist: ' + variantUrl);

                const masterIndex = detectedM3U8Urls.indexOf(url);
                if (masterIndex > -1) {
                    detectedM3U8Urls.splice(masterIndex, 1);
                }

                for (const [existingUrl, existingData] of allDetectedVideos.entries()) {
                    if (existingData.type === 'm3u8') {
                        const existingWithoutQuery = existingUrl.split('?')[0];
                        if (existingWithoutQuery === urlWithoutQuery) {
                            debugLog('[M3U8] Removing existing master playlist: ' + existingUrl);
                            allDetectedVideos.delete(existingUrl);
                        }
                    }
                }

                detectM3U8(variantUrl);
                return;
            }

            if (manifest.segments) {
                for (var s = 0; s < manifest.segments.length; s++) {
                    duration += manifest.segments[s].duration;
                }
                debugLog('[M3U8] Found ' + manifest.segments.length + ' segments, duration: ' + duration + 's');
            } else {
                debugLog('[M3U8] No segments found!');
            }

            const m3u8Data = {
                url: url,
                manifest: manifest,
                content: content,
                duration: duration,
                title: 'M3U8 - ' + (duration ? Math.ceil(duration / 60) + 'min' : 'Unknown'),
                timestamp: Date.now()
            };

            detectedM3U8s.push(m3u8Data);

            for (const [existingUrl, existingData] of allDetectedVideos.entries()) {
                if (existingData.type === 'm3u8') {
                    const existingWithoutQuery = existingUrl.split('?')[0];
                    if (existingWithoutQuery === urlWithoutQuery) {
                        if ((!existingData.m3u8Data.manifest.segments || existingData.m3u8Data.manifest.segments.length === 0) &&
                            manifest.segments && manifest.segments.length > 0) {
                            debugLog('[M3U8] Replacing master with media playlist: ' + existingUrl + ' -> ' + url);
                            allDetectedVideos.delete(existingUrl);
                        }
                    }
                }
            }

            debugLog('[M3U8] *** ADDING TO MAP AS TYPE m3u8 ***');
            allDetectedVideos.set(url, {
                url: url,
                type: 'm3u8',
                timestamp: Date.now(),
                title: m3u8Data.title,
                m3u8Data: m3u8Data
            });

            checkForVideos();
        } catch(e) {
            debugLog('[ERROR] M3U8 parse failed: ' + e.message);
        }
    }

    function getVideoUrl(videoElement) {
        if (videoElement.tagName === 'VIDEO') {
            if (videoElement.currentSrc && !videoElement.currentSrc.startsWith('blob:'))
                return videoElement.currentSrc;
            if (videoElement.src && !videoElement.src.startsWith('blob:'))
                return videoElement.src;

            var sources = videoElement.querySelectorAll('source');
            for (var i = 0; i < sources.length; i++) {
                if (sources[i].src && !sources[i].src.startsWith('blob:'))
                    return sources[i].src;
            }
        }

        if (videoElement.tagName === 'IFRAME') {
            return videoElement.src;
        }

        return null;
    }

    function getUniqueVideos() {
        var videos = [];
        var seenUrls = new Set();

        debugLog('[GET VIDEOS] Checking allDetectedVideos map...');
        debugLog('[GET VIDEOS] Map size: ' + allDetectedVideos.size);

        allDetectedVideos.forEach(function(videoData, url) {
            debugLog('[GET VIDEOS] Map entry: ' + videoData.type + ' - ' + url);

            if (hasM3U8Playlist() && isSegmentOfPlaylist(videoData.url)) {
                debugLog('[GET VIDEOS] Skipping segment: ' + url);
                return;
            }

            if (videoData.type === 'm3u8' && shouldFilterM3U8(videoData.url, videoData.m3u8Data.manifest)) {
                debugLog('[GET VIDEOS] Filtering master playlist: ' + url);
                return;
            }

            if (!seenUrls.has(videoData.url)) {
                seenUrls.add(videoData.url);
                videos.push(videoData);
                debugLog('[GET VIDEOS] Added to final list: ' + videoData.type + ' - ' + url);
            }
        });

        debugLog('[GET VIDEOS] Checking video elements on page...');

        for (var s = 0; s < VIDEO_SELECTORS.length; s++) {
            var elements = document.querySelectorAll(VIDEO_SELECTORS[s]);

            for (var i = 0; i < elements.length; i++) {
                var element = elements[i];
                var rect = element.getBoundingClientRect();

                if (rect.width > 100 && rect.height > 100) {
                    var url = getVideoUrl(element);

                    if (url && isM3U8Url(url)) {
                        debugLog('[GET VIDEOS] Found M3U8 in video element, triggering detection: ' + url);
                        detectM3U8(url);
                        continue;
                    }

                    if (url && !seenUrls.has(url) && !allDetectedVideos.has(url)) {
                        seenUrls.add(url);
                        const videoData = {
                            type: 'video',
                            element: element,
                            url: url,
                            title: element.title || element.alt || ('Video ' + (videos.length + 1)),
                            timestamp: Date.now()
                        };
                        videos.push(videoData);
                        allDetectedVideos.set(url, videoData);
                        debugLog('[GET VIDEOS] Added from element: video - ' + url);
                    } else if (url && allDetectedVideos.has(url)) {
                        debugLog('[GET VIDEOS] Already in map, skipping: ' + url);
                    }
                }
            }
        }

        var iframes = document.querySelectorAll('iframe');
        for (var i = 0; i < iframes.length; i++) {
            try {
                var iframeDoc = iframes[i].contentDocument || iframes[i].contentWindow.document;
                if (iframeDoc) {
                    var iframeVideos = iframeDoc.querySelectorAll('video');
                    for (var v = 0; v < iframeVideos.length; v++) {
                        var url = getVideoUrl(iframeVideos[v]);

                        if (url && isM3U8Url(url)) {
                            debugLog('[GET VIDEOS] Found M3U8 in iframe, triggering detection: ' + url);
                            detectM3U8(url);
                            continue;
                        }

                        if (url && !seenUrls.has(url) && !allDetectedVideos.has(url)) {
                            seenUrls.add(url);
                            const videoData = {
                                type: 'video',
                                element: iframeVideos[v],
                                url: url,
                                title: 'Iframe Video ' + (videos.length + 1),
                                timestamp: Date.now()
                            };
                            videos.push(videoData);
                            allDetectedVideos.set(url, videoData);
                        }
                    }
                }
            } catch(e) {}
        }

        debugLog('[GET VIDEOS] Final video count: ' + videos.length);

        return videos;
    }

    function getButtonIcon(videos) {
        if (videos.length > 1) return '⇓';
        if (videos.length === 1) {
            return videos[0].type === 'm3u8' ? '⇣' : '↯';
        }
        return '▶';
    }

    function createFloatingButton() {
        if (floatingButton) return floatingButton;

        var container = document.createElement('div');
        container.id = 'universal-video-share-container';
        container.style.cssText = 'position: fixed; top: 15px; left: 15px; width: 40px; height: 40px; z-index: 999999;';

        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('width', '40');
        svg.setAttribute('height', '40');
        svg.style.cssText = 'position: absolute; top: 0; left: 0; transform: rotate(-90deg);';

        var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('cx', '20');
        circle.setAttribute('cy', '20');
        circle.setAttribute('r', '18');
        circle.setAttribute('fill', 'none');
        circle.setAttribute('stroke', '#4ade80');
        circle.setAttribute('stroke-width', '3');
        circle.setAttribute('stroke-dasharray', '113');
        circle.setAttribute('stroke-dashoffset', '113');
        circle.setAttribute('stroke-linecap', 'round');
        circle.style.cssText = 'transition: stroke-dashoffset 0.3s ease;';
        circle.id = 'progress-circle';

        svg.appendChild(circle);
        container.appendChild(svg);

        floatingButton = document.createElement('div');
        floatingButton.innerHTML = '▶';
        floatingButton.id = 'universal-video-share-float';

        floatingButton.style.cssText = `position: absolute; top: 2px; left: 2px; width: 36px; height: 36px; background: ${COLORS.button}; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); color: ${COLORS.icon}; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 50%; font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; user-select: none; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);`;

        container.appendChild(floatingButton);
        floatingButton.progressCircle = circle;

        floatingButton.addEventListener('mouseenter', function() {
            this.style.background = COLORS.buttonHover;
            this.style.transform = 'scale(1.05)';
        });

        floatingButton.addEventListener('mouseleave', function() {
            this.style.background = COLORS.button;
            this.style.transform = 'scale(1)';
        });

        // FIXED: Mouse events with 8 second threshold
        floatingButton.addEventListener('mousedown', function(e) {
            e.preventDefault();
            isLongPress = false;
            longPressStartTime = Date.now();

            pressTimer = setTimeout(function() {
                isLongPress = true;
                var duration = Date.now() - longPressStartTime;

                // Only activate debug at 8 seconds
                if (duration >= 8000) {
                    debugMode = true;
                    floatingButton.style.background = 'rgba(239, 68, 68, 0.8)';
                    floatingButton.innerHTML = '🐛';
                    debugLog('='.repeat(50));
                    debugLog('[INIT] DEBUG MODE ACTIVATED');
                    debugLog('[INIT] Platform - iOS: ' + isIOS + ', Safari: ' + isSafari);
                    debugLog('[INIT] Debug mode: Hold button for 8 seconds');
                    debugLog('='.repeat(50));
                } else {
                    // Just long press for copy (not debug)
                    floatingButton.style.background = 'rgba(74, 222, 128, 0.8)';
                    floatingButton.innerHTML = '⎘';
                }
            }, 500);
        });

        floatingButton.addEventListener('mouseup', function(e) {
            e.preventDefault();
            clearTimeout(pressTimer);

            var pressDuration = Date.now() - longPressStartTime;

            var videos = getUniqueVideos();
            floatingButton.style.background = COLORS.button;
            floatingButton.innerHTML = getButtonIcon(videos);

            // Only open debug console if held for 8+ seconds
            if (pressDuration >= 8000) {
                debugMode = true;
                createDebugConsole();
            } else if (isLongPress) {
                // Regular long press (copy)
                handleCopy();
            } else {
                // Short press (share)
                handleShare();
            }
        });

        floatingButton.addEventListener('mouseleave', function() {
            clearTimeout(pressTimer);
            var videos = getUniqueVideos();
            floatingButton.style.background = COLORS.button;
            floatingButton.innerHTML = getButtonIcon(videos);
        });

        // FIXED: Touch events with 8 second threshold
        floatingButton.addEventListener('touchstart', function(e) {
            e.preventDefault();
            isLongPress = false;
            longPressStartTime = Date.now();

            pressTimer = setTimeout(function() {
                isLongPress = true;
                var duration = Date.now() - longPressStartTime;

                if (duration >= 8000) {
                    debugMode = true;
                    floatingButton.style.background = 'rgba(239, 68, 68, 0.8)';
                    floatingButton.innerHTML = '🐛';
                    navigator.vibrate && navigator.vibrate(200);
                    debugLog('='.repeat(50));
                    debugLog('[INIT] DEBUG MODE ACTIVATED');
                    debugLog('[INIT] Platform - iOS: ' + isIOS + ', Safari: ' + isSafari);
                    debugLog('[INIT] Debug mode: Hold button for 8 seconds');
                    debugLog('='.repeat(50));
                } else {
                    floatingButton.style.background = 'rgba(74, 222, 128, 0.8)';
                    floatingButton.innerHTML = '⎘';
                    navigator.vibrate && navigator.vibrate(100);
                }
            }, 500);
        });

        floatingButton.addEventListener('touchend', function(e) {
            e.preventDefault();
            clearTimeout(pressTimer);

            var pressDuration = Date.now() - longPressStartTime;

            var videos = getUniqueVideos();
            floatingButton.style.background = COLORS.button;
            floatingButton.innerHTML = getButtonIcon(videos);

            if (pressDuration >= 8000) {
                debugMode = true;
                createDebugConsole();
            } else if (isLongPress) {
                handleCopy();
            } else {
                handleShare();
            }
        });

        document.body.appendChild(container);
        return floatingButton;
    }

    function updateProgress(percent) {
        if (!floatingButton || !floatingButton.progressCircle) return;
        var offset = 113 - (113 * percent / 100);
        floatingButton.progressCircle.setAttribute('stroke-dashoffset', offset);
    }

    function resetProgress() {
        if (!floatingButton || !floatingButton.progressCircle) return;
        floatingButton.progressCircle.setAttribute('stroke-dashoffset', '113');
    }

    function handleShare() {
        debugLog('[SHARE] Button clicked');
        showNotification('🔍 Checking videos...', 'info');

        var videos = getUniqueVideos();

        debugLog('[SHARE] Videos found: ' + videos.length);
        videos.forEach(v => debugLog('[SHARE] - ' + v.type + ': ' + v.url));

        if (videos.length === 0) {
            showNotification('❌ No videos found', 'error');
            debugLog('[ERROR] No videos found');
            return;
        }

        if (videos.length === 1) {
            debugLog('[SHARE] Single video, calling shareVideo');
            shareVideo(videos[0]);
        } else {
            debugLog('[SHARE] Multiple videos, showing selector');
            showVideoSelector(videos, 'share');
        }
    }

    function handleCopy() {
        debugLog('[COPY] Long press detected');
        showNotification('📋 Long press detected...', 'info');

        var videos = getUniqueVideos();

        debugLog('[COPY] Videos found: ' + videos.length);

        if (videos.length === 0) {
            showNotification('❌ No videos found', 'error');
            debugLog('[ERROR] No videos found');
            return;
        }

        if (videos.length === 1) {
            if (videos[0].type === 'm3u8') {
                debugLog('[COPY] M3U8 detected, forcing download');
                showNotification('📥 Downloading M3U8...', 'info');
                downloadM3U8(videos[0], true);
            } else {
                copyVideoUrl(videos[0]);
            }
        } else {
            showVideoSelector(videos, 'copy');
        }
    }

    function showVideoSelector(videos, action) {
        var existingSelector = document.querySelector('#video-selector-popup');
        if (existingSelector) {
            existingSelector.remove();
        }

        var popup = document.createElement('div');
        popup.id = 'video-selector-popup';
        popup.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); z-index: 9999999; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box;';

        var container = document.createElement('div');
        container.style.cssText = 'background: rgba(20, 20, 20, 0.95); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 24px; max-width: 600px; max-height: 70%; overflow-y: auto; position: relative; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);';

        var closeButton = document.createElement('button');
        closeButton.innerHTML = '✕';
        closeButton.style.cssText = `position: absolute; top: 12px; right: 12px; background: rgba(255, 255, 255, 0.1); border: none; font-size: 16px; cursor: pointer; color: ${COLORS.text}; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background 0.2s;`;

        closeButton.addEventListener('click', function() {
            popup.remove();
        });

        closeButton.addEventListener('mouseenter', function() {
            this.style.background = 'rgba(255, 255, 255, 0.2)';
        });

        closeButton.addEventListener('mouseleave', function() {
            this.style.background = 'rgba(255, 255, 255, 0.1)';
        });

        var title = document.createElement('h3');
        title.textContent = 'Select Video to ' + (action.charAt(0).toUpperCase() + action.slice(1));
        title.style.cssText = `margin: 0 0 16px 0; color: ${COLORS.text}; font-size: 16px; font-weight: 600; text-align: center;`;

        container.appendChild(closeButton);
        container.appendChild(title);

        videos.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));

        for (var i = 0; i < videos.length; i++) {
            var videoData = videos[i];
            var videoItem = document.createElement('div');
            videoItem.style.cssText = 'margin-bottom: 12px; padding: 12px; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px; cursor: pointer; transition: all 0.2s ease; background: rgba(255, 255, 255, 0.05);';

            (function(currentVideoData) {
                videoItem.addEventListener('mouseenter', function() {
                    this.style.borderColor = 'rgba(255, 255, 255, 0.3)';
                    this.style.background = 'rgba(255, 255, 255, 0.1)';
                });

                videoItem.addEventListener('mouseleave', function() {
                    this.style.borderColor = 'rgba(255, 255, 255, 0.1)';
                    this.style.background = 'rgba(255, 255, 255, 0.05)';
                });

                var headerDiv = document.createElement('div');
                headerDiv.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;';

                var typeBadge = document.createElement('span');
                typeBadge.textContent = currentVideoData.type === 'm3u8' ? 'M3U8' : 'VIDEO';
                typeBadge.style.cssText = 'display: inline-block; background: ' + (currentVideoData.type === 'm3u8' ? 'rgba(239, 68, 68, 0.8)' : 'rgba(59, 130, 246, 0.8)') + `; color: ${COLORS.text}; padding: 3px 8px; border-radius: 4px; font-size: 10px; font-weight: 600;`;

                var timeAgo = document.createElement('span');
                timeAgo.textContent = getTimeAgo(currentVideoData.timestamp || Date.now());
                timeAgo.style.cssText = `color: ${COLORS.text}; opacity: 0.6; font-size: 10px;`;

                headerDiv.appendChild(typeBadge);
                headerDiv.appendChild(timeAgo);
                videoItem.appendChild(headerDiv);

                var videoInfo = document.createElement('div');
                videoInfo.innerHTML = `<div style="color: ${COLORS.text}; font-size: 13px; font-weight: 500; margin-bottom: 4px;">` + currentVideoData.title + `</div><div style="color: ${COLORS.text}; opacity: 0.5; font-size: 11px; word-break: break-all; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">` + currentVideoData.url + '</div>';
                videoItem.appendChild(videoInfo);

                videoItem.addEventListener('click', function() {
                    popup.remove();
                    if (action === 'share') {
                        shareVideo(currentVideoData);
                    } else {
                        if (currentVideoData.type === 'm3u8') {
                            downloadM3U8(currentVideoData, true);
                        } else {
                            copyVideoUrl(currentVideoData);
                        }
                    }
                });
            })(videoData);

            container.appendChild(videoItem);
        }

        popup.appendChild(container);

        popup.addEventListener('click', function(e) {
            if (e.target === popup) {
                popup.remove();
            }
        });

        document.body.appendChild(popup);
    }

    async function downloadM3U8(videoData, forceDownload = false) {
        debugLog('='.repeat(50));
        debugLog('[M3U8] STARTING M3U8 DOWNLOAD & CONVERSION');
        debugLog('[M3U8] URL: ' + videoData.url);
        debugLog('[M3U8] Force download: ' + forceDownload);

        const cachedBlob = downloadedBlobs.get(videoData.url);
        if (cachedBlob && !forceDownload) {
            debugLog('[M3U8] Using cached blob');
            showNotification('♻️ Using cached video...', 'info');
            await shareOrDownloadBlob(cachedBlob.blob, cachedBlob.filename, videoData, forceDownload);
            return;
        }

        showNotification('📥 Starting M3U8 download...', 'info');
        resetProgress();

        try {
            const manifest = videoData.m3u8Data.manifest;
            const baseUrl = videoData.url.substring(0, videoData.url.lastIndexOf('/') + 1);
            const segments = manifest.segments;

            if (!segments || segments.length === 0) {
                throw new Error("No segments in playlist!");
            }

            debugLog('[M3U8] Found ' + segments.length + ' segments');
            showNotification(`📥 Downloading ${segments.length} segments...`, 'info');

            var segmentData = [];
            var totalSize = 0;

            for (var i = 0; i < segments.length; i++) {
                var segUrl = segments[i].uri.startsWith('http')
                    ? segments[i].uri
                    : baseUrl + segments[i].uri;

                var response = await fetch(segUrl);
                if (!response.ok) {
                    throw new Error('Segment ' + (i+1) + ' failed: ' + response.status);
                }

                var data = await response.arrayBuffer();
                totalSize += data.byteLength;
                segmentData.push(data);

                var percent = Math.floor((i + 1) / segments.length * 100);
                updateProgress(percent);

                if (percent % 10 === 0 || i === segments.length - 1) {
                    showNotification(`📥 ${percent}% (${(totalSize/1024/1024).toFixed(1)}MB)`, 'info');
                }
            }

            debugLog('[M3U8] All segments downloaded, merging...');
            showNotification('🔄 Merging segments...', 'info');

            var merged = new Blob(segmentData, { type: 'video/mp2t' });
            debugLog('[M3U8] Merged TS blob size: ' + (merged.size/1024/1024).toFixed(2) + 'MB');

            // CONVERT TO MP4
            var urlPath = new URL(videoData.url).pathname;
            var baseName = urlPath.split('/').pop().replace(/\.(m3u8?|ts)$/i, '') || 'video';
            var tsFilename = baseName + '.ts';

            debugLog('[M3U8] Converting TS to MP4...');
            const converted = await convertTStoMP4(merged, tsFilename);

            debugLog('[M3U8] MP4 conversion complete!');
            downloadedBlobs.set(videoData.url, { blob: converted.blob, filename: converted.filename });

            await shareOrDownloadBlob(converted.blob, converted.filename, videoData, forceDownload);

        } catch(e) {
            debugLog('[ERROR] M3U8 download/conversion failed: ' + e.message);
            showNotification('❌ Failed: ' + e.message, 'error');
            resetProgress();
        }
    }

    async function shareOrDownloadBlob(blob, filename, videoData, forceDownload = false) {
        debugLog('='.repeat(50));
        debugLog('[SHARE/DL] SHARE OR DOWNLOAD BLOB');
        debugLog('[SHARE/DL] Blob size: ' + (blob.size/1024/1024).toFixed(2) + 'MB');
        debugLog('[SHARE/DL] Filename: ' + filename);

        const processed = processedVideos.get(videoData.url);

        if (processed && !forceDownload) {
            debugLog('[SHARE/DL] Already processed, forcing download');
            forceDownload = true;
        }

        if (!forceDownload && navigator.share) {
            try {
                var file = new File([blob], filename, { type: 'video/mp4' });
                var canShareFiles = navigator.canShare ? navigator.canShare({ files: [file] }) : true;

                if (canShareFiles) {
                    showNotification('📤 Opening share...', 'info');
                    var shareStartTime = Date.now();

                    try {
                        await navigator.share({
                            files: [file],
                            title: filename,
                            text: 'Video file'
                        });

                        processedVideos.set(videoData.url, { action: 'shared', timestamp: Date.now() });
                        showNotification('✅ Shared!', 'success');
                        resetProgress();
                        return;

                    } catch(e) {
                        var elapsed = Date.now() - shareStartTime;
                        if (elapsed > 2000) {
                            processedVideos.set(videoData.url, { action: 'shared', timestamp: Date.now() });
                            showNotification('✅ Share completed', 'success');
                            resetProgress();
                            return;
                        }
                    }
                }
            } catch(e) {
                debugLog('[ERROR] Share error: ' + e.message);
            }
        }

        debugLog('[SHARE/DL] Initiating download...');
        showNotification('💾 Downloading...', 'info');

        var blobUrl = URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = blobUrl;
        a.download = filename;
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();

        setTimeout(function() {
            document.body.removeChild(a);
            URL.revokeObjectURL(blobUrl);
        }, 1000);

        processedVideos.set(videoData.url, { action: 'downloaded', timestamp: Date.now() });
        showNotification('✅ Downloaded: ' + filename, 'success');
        resetProgress();
    }

    function shareVideo(videoData) {
        debugLog('='.repeat(50));
        debugLog('[SHARE] shareVideo() called');
        debugLog('[SHARE] Type: ' + videoData.type);

        if (videoData.type === 'm3u8') {
            debugLog('[SHARE] M3U8 detected - downloading and converting');
            const processed = processedVideos.get(videoData.url);
            const forceDownload = processed && (processed.action === 'shared' || processed.action === 'downloaded');
            downloadM3U8(videoData, forceDownload);
            return;
        }

        const processed = processedVideos.get(videoData.url);
        if (processed && (processed.action === 'shared' || processed.action === 'copied')) {
            downloadVideo(videoData);
            return;
        }

        if (navigator.share) {
            var shareStartTime = Date.now();
            navigator.share({
                title: document.title,
                url: videoData.url
            }).then(function() {
                processedVideos.set(videoData.url, { action: 'shared', timestamp: Date.now() });
                showNotification('✅ Shared!', 'success');
            }).catch(function(error) {
                if (Date.now() - shareStartTime > 2000) {
                    processedVideos.set(videoData.url, { action: 'shared', timestamp: Date.now() });
                    showNotification('✅ Share completed', 'success');
                } else {
                    copyVideoUrl(videoData);
                }
            });
        } else {
            copyVideoUrl(videoData);
        }
    }

    function downloadVideo(videoData) {
        showNotification('📂 Opening video...', 'info');
        window.open(videoData.url, '_blank');
        processedVideos.set(videoData.url, { action: 'downloaded', timestamp: Date.now() });
    }

    function copyVideoUrl(videoData) {
        const processed = processedVideos.get(videoData.url);
        if (processed && processed.action === 'copied') {
            downloadVideo(videoData);
            return;
        }

        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(videoData.url).then(function() {
                processedVideos.set(videoData.url, { action: 'copied', timestamp: Date.now() });
                showNotification('✅ URL copied', 'success');
            }).catch(function() {
                fallbackCopy(videoData.url, videoData);
            });
        } else {
            fallbackCopy(videoData.url, videoData);
        }
    }

    function fallbackCopy(url, videoData) {
        try {
            var textArea = document.createElement('textarea');
            textArea.value = url;
            textArea.style.position = 'fixed';
            textArea.style.left = '-999999px';
            document.body.appendChild(textArea);
            textArea.select();

            if (document.execCommand('copy')) {
                processedVideos.set(videoData.url, { action: 'copied', timestamp: Date.now() });
                showNotification('✅ URL copied', 'success');
            } else {
                showNotification('❌ Copy failed', 'error');
            }

            document.body.removeChild(textArea);
        } catch (err) {
            showNotification('❌ Copy failed', 'error');
        }
    }

    function showNotification(message, type) {
        if (type === undefined) type = 'success';

        debugLog('[NOTIF] ' + message);

        var existingNotifications = document.querySelectorAll('.universal-video-notification');
        for (var i = 0; i < existingNotifications.length; i++) {
            existingNotifications[i].remove();
        }

        var notification = document.createElement('div');
        notification.textContent = message;
        notification.className = 'universal-video-notification';

        var bgColor = type === 'success' ? 'rgba(74, 222, 128, 0.9)' :
                      type === 'error' ? 'rgba(239, 68, 68, 0.9)' :
                      'rgba(59, 130, 246, 0.9)';

        notification.style.cssText = `position: fixed; top: 65px; left: 15px; background: ${bgColor}; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); color: ${COLORS.text}; padding: 10px 14px; border-radius: 8px; z-index: 999998; font-weight: 600; font-size: 13px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); max-width: 280px; word-wrap: break-word; transform: translateX(-350px); transition: transform 0.3s ease;`;

        document.body.appendChild(notification);

        setTimeout(function() {
            notification.style.transform = 'translateX(0)';
        }, 100);

        setTimeout(function() {
            if (notification.parentNode) {
                notification.style.transform = 'translateX(-350px)';
                setTimeout(function() {
                    notification.remove();
                }, 300);
            }
        }, 4000);
    }

    function checkForVideos() {
        var videos = getUniqueVideos();

        if (videos.length > 0) {
            if (!floatingButton) {
                createFloatingButton();
            }
            if (floatingButton) {
                floatingButton.innerHTML = getButtonIcon(videos);
            }
        } else {
            if (floatingButton && floatingButton.parentNode) {
                floatingButton.parentNode.remove();
                floatingButton = null;
            }
        }
    }

    function init() {
        debugLog('='.repeat(50));
        debugLog('[INIT] Universal Video Share v6.4 - MULTI-METHOD REMUX (FIXED)');
        debugLog('[INIT] Methods: muxjs, ffmpeg, simple');
        debugLog('[INIT] Preferred: muxjs');
        debugLog('[INIT] Platform - iOS: ' + isIOS + ', Safari: ' + isSafari);
        debugLog('[INIT] Debug mode: Hold button for 8 seconds');
        debugLog('='.repeat(50));

        setTimeout(checkForVideos, 1000);
        checkInterval = setInterval(checkForVideos, 5000);
    }

    init();

})();