Advanced Audio Detector & Player

Advanced audio detection including streaming, players, and network interception, with a sorted playlist, mobile-optimized UI, draggable button, top-right close, and "Open in New Tab" snapshot.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Advanced Audio Detector & Player
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Advanced audio detection including streaming, players, and network interception, with a sorted playlist, mobile-optimized UI, draggable button, top-right close, and "Open in New Tab" snapshot.
// @author       Ifrit Raen
// @license      mit
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Audio file extensions and MIME types
    const audioExtensions = ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac', '.wma', '.opus', '.webm', '.3gp', '.amr', '.m3u8', '.mpd'];
    const primaryAudioExtensions = ['.mp3', '.m4a', '.wav', '.ogg', '.flac', '.opus', '.aac'];
    const streamExtensions = ['.m3u8', '.mpd'];
    const audioMimeTypes = [
        'audio/', 'video/mp4', 'video/webm', 'video/ogg', 'application/ogg',
        'application/vnd.apple.mpegurl', 'application/x-mpegurl', 'application/dash+xml'
    ];

    let audioFiles = new Map(); // URL -> {url, title, source, size, duration, detected}
    let floatingButton = null;
    let audioPlayer = null;
    let isExpanded = false;
    let currentAudio = null;
    let currentIndex = 0;
    let audioArray = [];
    let interceptedRequests = new Set();
// =========================
// Floating button (SMALLER, STYLISH, DRAGGABLE)  ⬇⬇⬇
// =========================
function createFloatingButton() {
    floatingButton = document.createElement('div');
    floatingButton.innerHTML = '🎵';
    floatingButton.title = 'Audio Detector';
    floatingButton.style.cssText = `
        position: fixed;
        bottom: 18px;
        right: 18px;
        width: 42px;
        height: 42px;
        background: linear-gradient(135deg, rgba(0, 123, 255, 0.95), rgba(0, 180, 200, 0.95));
        color: white;
        border: none;
        border-radius: 12px;
        cursor: grab;
        z-index: 100000;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 18px;
        box-shadow: 0 8px 20px rgba(0,0,0,0.25);
        transition: transform 0.2s ease, box-shadow 0.2s ease;
        user-select: none;
        touch-action: none;
    `;

    floatingButton.addEventListener('mouseenter', () => {
        floatingButton.style.transform = 'translateY(-1px) scale(1.06)';
        floatingButton.style.boxShadow = '0 10px 24px rgba(0,0,0,0.3)';
    });

    floatingButton.addEventListener('mouseleave', () => {
        floatingButton.style.transform = 'none';
        floatingButton.style.boxShadow = '0 8px 20px rgba(0,0,0,0.25)';
    });

    // ⚡️ no click here anymore — handled in makeDraggable (tap vs drag)
    document.body.appendChild(floatingButton);
    makeDraggable(floatingButton); // <-- NEW
    updateButtonBadge();
}

// Draggable (mouse + touch, with tap detection)
function makeDraggable(el) {
    let startX, startY, startLeft, startTop, dragging = false, moved = false;

    const start = (clientX, clientY) => {
        dragging = true;
        moved = false;
        el.style.cursor = 'grabbing';
        const rect = el.getBoundingClientRect();
        startLeft = rect.left;
        startTop = rect.top;
        startX = clientX;
        startY = clientY;
        // detach from bottom/right to allow free move
        el.style.right = 'auto';
        el.style.bottom = 'auto';
        el.style.left = `${startLeft}px`;
        el.style.top = `${startTop}px`;
    };

    const move = (clientX, clientY) => {
        if (!dragging) return;
        const dx = clientX - startX;
        const dy = clientY - startY;
        if (Math.abs(dx) + Math.abs(dy) > 5) moved = true; // threshold
        el.style.left = `${startLeft + dx}px`;
        el.style.top = `${startTop + dy}px`;
    };

    const end = () => {
        if (!dragging) return;
        dragging = false;
        el.style.cursor = 'grab';
        if (!moved) {
            // Treat as tap / click
            toggleAudioPlayer();
        }
    };

    // Mouse
    el.addEventListener('mousedown', (e) => { start(e.clientX, e.clientY); e.preventDefault(); });
    window.addEventListener('mousemove', (e) => move(e.clientX, e.clientY));
    window.addEventListener('mouseup', end);

    // Touch
    el.addEventListener('touchstart', (e) => {
        const t = e.touches[0]; start(t.clientX, t.clientY); e.preventDefault();
    }, { passive: false });
    el.addEventListener('touchmove', (e) => {
        const t = e.touches[0]; move(t.clientX, t.clientY); e.preventDefault();
    }, { passive: false });
    el.addEventListener('touchend', end);
}

    // Update badge with count
    function updateButtonBadge() {
        if (!floatingButton) return;
        const count = audioFiles.size;
        if (count > 0) {
            floatingButton.innerHTML = `🎵<span style="
                position:absolute; top:-6px; right:-6px; background:#ff3b30; color:#fff;
                border-radius:10px; min-width:18px; height:18px; line-height:18px;
                font-size:11px; padding:0 4px; display:inline-flex; align-items:center; justify-content:center;
                box-shadow:0 2px 6px rgba(0,0,0,0.25);
            ">${count}</span>`;
        } else {
            floatingButton.innerHTML = '🎵';
        }
    }

    // Add audio file to collection
    function addAudioFile(url, metadata = {}) {
        if (!url || audioFiles.has(url)) return;

        const audioInfo = {
            url: url,
            title: metadata.title || extractFilename(url),
            source: metadata.source || 'Unknown',
            size: metadata.size || 'Unknown',
            duration: metadata.duration || 'Unknown',
            detected: new Date()
        };

        audioFiles.set(url, audioInfo);
        updateButtonBadge();

        if (isExpanded) {
            updateTrackList();
        }

        // console.log('🎵 Audio detected:', audioInfo);
    }

    // Extract filename from URL
    function extractFilename(url) {
        try {
            const urlObj = new URL(url);
            const pathname = urlObj.pathname;
            const filename = decodeURIComponent(pathname.split('/').pop());
            return filename || `audio_${Date.now()}`;
        } catch {
            return `audio_${Date.now()}`;
        }
    }

    // =========================
    // Player (CLOSE BUTTON TOP-RIGHT + OPEN IN NEW TAB) ⬇⬇⬇
    // =========================
    function createAudioPlayer() {
        audioPlayer = document.createElement('div');
        audioPlayer.style.cssText = `
            position: fixed;
            bottom: 76px;
            right: 10px;
            left: 10px;
            max-width: 95vw;
            max-height: 80vh;
            background: rgba(20, 20, 20, 0.95);
            border: 1px solid #333;
            border-radius: 12px;
            box-shadow: 0 12px 40px rgba(0,0,0,0.6);
            z-index: 99999;
            display: none;
            backdrop-filter: blur(10px);
            color: white;
            font-family: Arial, sans-serif;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        `;

        audioPlayer.innerHTML = `
            <div style="padding: 15px; border-bottom: 1px solid #333; display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; position: relative;">
                <div style="font-weight: bold; font-size: 16px;">🎵 Advanced Audio Player</div>
                <div style="display: flex; gap: 8px; align-items: center;">
                    <button id="openInNewTab" style="background:#17a2b8; color:#fff; border:none; padding:4px 8px; border-radius:6px; cursor:pointer; font-size:12px;">🗗 Open in New Tab</button>
                    <button id="refreshDetection" style="background:#28a745; color:#fff; border:none; padding:4px 8px; border-radius:6px; cursor:pointer; font-size:12px;">🔄 Scan</button>
                    <button id="clearAll" style="background:#dc3545; color:#fff; border:none; padding:4px 8px; border-radius:6px; cursor:pointer; font-size:12px;">🗑️ Clear</button>
                </div>
                <!-- CLOSE BUTTON TOP-RIGHT -->
                <button id="closeAudioPlayer" title="Close" style="
                    position:absolute; top:8px; right:10px;
                    background:transparent; border:none; color:#fff; font-size:20px; cursor:pointer; padding:0; width:28px; height:28px; line-height:28px;
                ">×</button>
            </div>

            <div id="currentTrackInfo" style="padding: 15px; border-bottom: 1px solid #333; min-height: 80px; flex-shrink: 0;">
                <div id="trackTitle" style="font-size: 14px; font-weight: bold; margin-bottom: 5px; color: #fff;">No track selected</div>
                <div id="trackSource" style="font-size: 12px; color: #4CAF50; margin-bottom: 3px;"></div>
                <div id="trackUrl" style="font-size: 11px; color: #888; word-break: break-all; max-height: 40px; overflow: hidden;"></div>
            </div>

            <div id="audioPlayerControls" style="padding: 15px; background: rgba(0,0,0,0.3); flex-shrink: 0;">
                <audio id="mainAudioElement" style="width: 100%; margin-bottom: 10px;" controls preload="metadata">
                    Your browser does not support the audio element.
                </audio>

                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                    <button id="prevBtn" style="background: #444; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">⏮️ Prev</button>
                    <div style="display: flex; gap: 10px;">
                        <button id="playPauseBtn" style="background: #007bff; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: bold;">▶️ Play</button>
                        <button id="stopBtn" style="background: #dc3545; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">⏹️ Stop</button>
                    </div>
                    <button id="nextBtn" style="background: #444; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer;">Next ⏭️</button>
                </div>

                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                    <span style="font-size: 12px; color: #ccc;">Volume:</span>
                    <input type="range" id="volumeSlider" min="0" max="100" value="50" style="flex: 1; margin: 0 10px;">
                    <button id="downloadCurrentBtn" style="background: #28a745; color: white; border: none; padding: 6px 10px; border-radius: 4px; cursor: pointer; font-size: 12px;">⬇️ Download</button>
                </div>

                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <span style="font-size: 12px; color: #ccc;">Speed:</span>
                    <select id="playbackSpeed" style="background: #333; color: white; border: 1px solid #555; padding: 4px; border-radius: 4px; margin-left: 10px;">
                        <option value="0.5">0.5x</option>
                        <option value="0.75">0.75x</option>
                        <option value="1" selected>1x</option>
                        <option value="1.25">1.25x</option>
                        <option value="1.5">1.5x</option>
                        <option value="2">2x</option>
                    </select>
                    <button id="downloadAllBtn" style="background: #6f42c1; color: white; border: none; padding: 6px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; margin-left: auto;">⬇️ Download All</button>
                </div>
            </div>

            <div style="padding: 10px 15px; border-bottom: 1px solid #333; font-size: 12px; color: #ccc; flex-shrink: 0;">
                Detected: <span id="audioCount">0</span> files | Last scan: <span id="lastScan">-</span>
            </div>

            <div id="trackList" style="overflow-y: auto; flex-grow: 1;"></div>
        `;

        document.body.appendChild(audioPlayer);
        setupPlayerControls();
    }

    // Setup enhanced player controls
    function setupPlayerControls() {
        const audioElement = document.getElementById('mainAudioElement');
        const playPauseBtn = document.getElementById('playPauseBtn');
        const stopBtn = document.getElementById('stopBtn');
        const prevBtn = document.getElementById('prevBtn');
        const nextBtn = document.getElementById('nextBtn');
        const volumeSlider = document.getElementById('volumeSlider');
        const speedSelect = document.getElementById('playbackSpeed');
        const downloadBtn = document.getElementById('downloadCurrentBtn');
        const downloadAllBtn = document.getElementById('downloadAllBtn');
        const refreshBtn = document.getElementById('refreshDetection');
        const clearBtn = document.getElementById('clearAll');
        const closeBtn = document.getElementById('closeAudioPlayer');
        const openInNewTabBtn = document.getElementById('openInNewTab');

        audioElement.volume = 0.5;

        playPauseBtn.addEventListener('click', async () => {
            if (!currentAudio) return;
            try {
                if (audioElement.paused) {
                    await audioElement.play();
                } else {
                    audioElement.pause();
                }
            } catch (error) {
                console.error('Playback error:', error);
                playPauseBtn.innerHTML = '❌ Error';
                setTimeout(() => playPauseBtn.innerHTML = '▶️ Play', 1500);
            }
        });

        stopBtn.addEventListener('click', () => {
            audioElement.pause();
            audioElement.currentTime = 0;
        });

        prevBtn.addEventListener('click', () => {
            if (audioArray.length > 0) {
                currentIndex = (currentIndex - 1 + audioArray.length) % audioArray.length;
                loadTrack(currentIndex, !audioElement.paused);
            }
        });

        nextBtn.addEventListener('click', () => {
            if (audioArray.length > 0) {
                currentIndex = (currentIndex + 1) % audioArray.length;
                loadTrack(currentIndex, !audioElement.paused);
            }
        });

        volumeSlider.addEventListener('input', (e) => {
            audioElement.volume = e.target.value / 100;
        });

        speedSelect.addEventListener('change', (e) => {
            audioElement.playbackRate = parseFloat(e.target.value);
        });

        downloadBtn.addEventListener('click', () => {
            if (currentAudio) {
                const audioInfo = audioFiles.get(currentAudio);
                downloadAudio(currentAudio, audioInfo?.title || 'audio');
            }
        });

        downloadAllBtn.addEventListener('click', downloadAllAudio);

        refreshBtn.addEventListener('click', () => {
            forceDetection();
            refreshBtn.innerHTML = '✅ Done';
            setTimeout(() => refreshBtn.innerHTML = '🔄 Scan', 1200);
        });

        clearBtn.addEventListener('click', () => {
            audioFiles.clear();
            audioArray = [];
            currentAudio = null;
            currentIndex = 0;
            const audioElement = document.getElementById('mainAudioElement');
            audioElement.src = '';
            updateButtonBadge();
            updateTrackList();
        });

        // Close btn (top-right)
        closeBtn.addEventListener('click', () => {
            audioPlayer.style.display = 'none';
            isExpanded = false;
        });

        // NEW: Open in New Tab snapshot
        openInNewTabBtn.addEventListener('click', openAudioInterfaceInNewTab);



       audioElement.addEventListener('play', () => playPauseBtn.innerHTML = '⏸️ Pause');
        audioElement.addEventListener('pause', () => playPauseBtn.innerHTML = '▶️ Play');
        audioElement.addEventListener('ended', () => {
            if (audioArray.length > 1) {
                nextBtn.click();
            } else {
                 playPauseBtn.innerHTML = '▶️ Play';
            }
        });
        audioElement.addEventListener('error', (e) => {
            console.error('Audio error:', e);
            document.getElementById('trackTitle').textContent = '❌ Error loading track';
        });
    }

    // NEW: Open the floating UI snapshot in a separate tab
    function openInNewTab() {
        const w = window.open('', '_blank');
        if (!w) return;

        const files = Array.from(audioFiles.values());
        const safe = (s) => String(s || '').replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
        const rows = files.map((f, i) => `
            <tr>
                <td>${i+1}</td>
                <td>${safe(f.title)}</td>
                <td>${safe(f.source)}</td>
                <td>${safe(f.size)}</td>
                <td>${safe(f.duration)}</td>
                <td style="max-width:420px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${safe(f.url)}">
                    <a href="${safe(f.url)}" target="_blank">${safe(f.url)}</a>
                </td>
                <td>
                    <audio controls preload="none" src="${safe(f.url)}" style="width:240px"></audio>
                </td>
                <td><a href="${safe(f.url)}" download="${safe(f.title)}" style="text-decoration:none;">⬇️</a></td>
            </tr>
        `).join('');

        w.document.write(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Detected Audio Snapshot</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;background:#0f1115;color:#e6e6e6;margin:0;padding:16px;}
h1{font-size:18px;margin:0 0 12px;}
.card{background:#141821;border:1px solid #222739;border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.35);padding:12px;}
table{width:100%;border-collapse:collapse;font-size:13px;}
th,td{border-bottom:1px solid #222739;padding:8px;vertical-align:middle;}
th{position:sticky;top:0;background:#141821;z-index:1;text-align:left;}
a{color:#6ab8ff;}
small{color:#8aa0b3}
</style>
</head>
<body>
<div class="card">
  <h1>Detected Audio Files <small>(${files.length})</small></h1>
  <table>
    <thead>
      <tr>
        <th>#</th><th>Title</th><th>Source</th><th>Size</th><th>Duration</th><th>URL</th><th>Play</th><th>DL</th>
      </tr>
    </thead>
    <tbody>
      ${rows || '<tr><td colspan="8" style="text-align:center;color:#8aa0b3;padding:24px;">No audio detected yet.</td></tr>'}
    </tbody>
  </table>
  <p style="margin-top:10px;color:#8aa0b3">This is a snapshot of what was detected on the originating tab at the moment you clicked "Open in New Tab".</p>
</div>
</body>
</html>`);
        w.document.close();
    }

    // Format duration
    function formatDuration(seconds) {
        if (isNaN(seconds) || seconds === Infinity) return 'Live';
        const mins = Math.floor(seconds / 60);
        const secs = Math.floor(seconds % 60);
        return `${mins}:${secs.toString().padStart(2, '0')}`;
    }

    // Load track with enhanced info
    function loadTrack(index, shouldPlay = false) {
        if (index < 0 || index >= audioArray.length) return;

        currentIndex = index;
        currentAudio = audioArray[index];
        const audioInfo = audioFiles.get(currentAudio);

        const audioElement = document.getElementById('mainAudioElement');
        const trackTitle = document.getElementById('trackTitle');
        const trackSource = document.getElementById('trackSource');
        const trackUrl = document.getElementById('trackUrl');

        audioElement.src = currentAudio;
        trackTitle.textContent = audioInfo.title;
        trackSource.textContent = `Source: ${audioInfo.source} | Size: ${audioInfo.size} | Duration: ${audioInfo.duration}`;
        trackUrl.textContent = currentAudio;

        audioElement.addEventListener('loadedmetadata', () => {
            if (audioInfo.duration === 'Unknown' && audioElement.duration) {
                audioInfo.duration = formatDuration(audioElement.duration);
                trackSource.textContent = `Source: ${audioInfo.source} | Size: ${audioInfo.size} | Duration: ${audioInfo.duration}`;
            }
        }, { once: true });

        if (shouldPlay) {
            audioElement.play().catch(e => console.error("Autoplay failed:", e));
        }

        updateTrackListHighlight();
    }

    // Sort priority
    function getSortPriority(url, audioInfo) {
        const lowerUrl = url.toLowerCase();
        const source = (audioInfo.source || '').toLowerCase();
        if (primaryAudioExtensions.some(ext => lowerUrl.endsWith(ext))) return 1;
        if (streamExtensions.some(ext => lowerUrl.endsWith(ext))) return 2;
        if (lowerUrl.startsWith('blob:')) return 3;
        if (['.webm', '.mp4', '.mov'].some(ext => lowerUrl.endsWith(ext))) return 4;
        if (lowerUrl.includes('audio') || lowerUrl.includes('stream') || lowerUrl.includes('music')) return 5;
        if (source.includes('embed') || source.includes('spotify') || source.includes('soundcloud') || source.includes('youtube')) return 6;
        return 7;
    }

    // Update track list (sorted)
    function updateTrackList() {
        const trackList = document.getElementById('trackList');
        const audioCount = document.getElementById('audioCount');
        const lastScan = document.getElementById('lastScan');

        if (!trackList) return;

        audioArray = Array.from(audioFiles.keys()).sort((urlA, urlB) => {
            const infoA = audioFiles.get(urlA);
            const infoB = audioFiles.get(urlB);
            const priorityA = getSortPriority(urlA, infoA);
            const priorityB = getSortPriority(urlB, infoB);
            if (priorityA !== priorityB) return priorityA - priorityB;
            return infoB.detected - infoA.detected;
        });

        audioCount.textContent = audioArray.length;
        lastScan.textContent = new Date().toLocaleTimeString();
        trackList.innerHTML = '';

        if (audioArray.length === 0) {
            trackList.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">No audio files detected</div>';
            document.getElementById('trackTitle').textContent = 'No track selected';
            document.getElementById('trackSource').textContent = '';
            document.getElementById('trackUrl').textContent = '';
            return;
        }

        audioArray.forEach((url, index) => {
            const audioInfo = audioFiles.get(url);
            const trackItem = document.createElement('div');
            trackItem.style.cssText = `
                padding: 12px 15px;
                border-bottom: 1px solid #333;
                cursor: pointer;
                transition: background-color 0.3s;
            `;

            trackItem.innerHTML = `
                <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 5px;">
                    <div style="font-size: 13px; font-weight: bold; color: #ccc; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${index + 1}. ${audioInfo.title}</div>
                    <button class="track-download" data-url="${url}" data-title="${audioInfo.title}" style="background: #28a745; color: white; border: none; padding: 2px 6px; border-radius: 3px; cursor: pointer; font-size: 10px; margin-left: 10px;">⬇️</button>
                </div>
                <div style="font-size: 11px; color: #4CAF50; margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                    ${audioInfo.source} | ${audioInfo.size} | ${audioInfo.duration} | ${audioInfo.detected.toLocaleTimeString()}
                </div>
                <div style="font-size: 10px; color: #888; word-break: break-all; max-height: 30px; overflow: hidden;">
                    ${url}
                </div>
            `;

            trackItem.addEventListener('click', (e) => {
                if (!e.target.classList.contains('track-download')) {
                    loadTrack(index, true);
                }
            });

            trackItem.addEventListener('mouseenter', () => {
                if (index !== currentIndex) trackItem.style.backgroundColor = 'rgba(255,255,255,0.08)';
            });

            trackItem.addEventListener('mouseleave', () => {
                if (index !== currentIndex) trackItem.style.backgroundColor = 'transparent';
            });

            trackList.appendChild(trackItem);
        });

        trackList.querySelectorAll('.track-download').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                downloadAudio(btn.getAttribute('data-url'), btn.getAttribute('data-title'));
            });
        });

        if (!currentAudio || !audioArray.includes(currentAudio)) {
            loadTrack(0);
        } else {
            currentIndex = audioArray.indexOf(currentAudio);
            updateTrackListHighlight();
        }
    }

    // Update highlighting
    function updateTrackListHighlight() {
        const trackList = document.getElementById('trackList');
        if (!trackList) return;
        Array.from(trackList.children).forEach((item, index) => {
            const titleDiv = item.querySelector('div div');
            if (index === currentIndex) {
                item.style.backgroundColor = 'rgba(0, 123, 255, 0.3)';
                if (titleDiv) titleDiv.style.color = '#fff';
            } else {
                item.style.backgroundColor = 'transparent';
                if (titleDiv) titleDiv.style.color = '#ccc';
            }
        });
    }

    // Toggle player
    function toggleAudioPlayer() {
        if (!audioPlayer) {
            createAudioPlayer();
        }
        if (isExpanded) {
            audioPlayer.style.display = 'none';
            isExpanded = false;
        } else {
            audioPlayer.style.display = 'flex';
            isExpanded = true;
            updateTrackList();
        }
    }













function openAudioInterfaceInNewTab() {
    const w = window.open('', '_blank');
    if (!w) return;

    const audioFilesData = JSON.stringify(Array.from(audioFiles.entries()));

    w.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Audio Player</title>
<style>
    body { margin:0; padding:20px; font-family: Arial, sans-serif; background: #f9f9f9; color: #222; }
    h1 { font-size: 22px; margin-bottom: 20px; }
    .controls, .track-info { margin-bottom: 15px; }
    button { padding:6px 12px; margin-right:6px; cursor:pointer; border-radius:4px; border:1px solid #aaa; background:#fff; }
    button:hover { background:#eee; }
    audio { width:100%; margin-bottom:10px; }
    #trackList { border-top:1px solid #ccc; }
    .trackItem { padding:10px 0; border-bottom:1px solid #ccc; display:flex; justify-content: space-between; align-items: center; }
    .trackItem div { flex:1; }
    @media(max-width:480px) { body{padding:10px;} h1{font-size:18px;} }
</style>
</head>
<body>
<h1>Detected Audio Files</h1>

<div class="track-info">
    <div id="trackTitle">No track selected</div>
    <div id="trackSource" style="font-size:14px;color:#555;"></div>
    <div id="trackUrl" style="font-size:12px;color:#888;word-break:break-all;"></div>
</div>

<div class="controls">
    <audio id="mainAudioElement" controls preload="metadata"></audio>
    <br>
    <button id="prevBtn">Prev ⏮️</button>
    <button id="playPauseBtn">Play ▶️</button>
    <button id="stopBtn">Stop ⏹️</button>
    <button id="nextBtn">Next ⏭️</button>
    <br><br>
    <label>Volume: <input type="range" id="volumeSlider" min="0" max="100" value="50"></label>
    <label>Speed:
        <select id="playbackSpeed">
            <option value="0.5">0.5x</option>
            <option value="0.75">0.75x</option>
            <option value="1" selected>1x</option>
            <option value="1.25">1.25x</option>
            <option value="1.5">1.5x</option>
            <option value="2">2x</option>
        </select>
    </label>
    <br><br>
    <button id="downloadCurrentBtn">Download Current ⬇️</button>
    <button id="downloadAllBtn">Download All ⬇️</button>
</div>

<div id="trackList"></div>

<script>
    const audioFiles = new Map(${audioFilesData});
    let audioArray = Array.from(audioFiles.keys());
    let currentIndex = 0;
    let currentAudio = audioArray[0];

    const audioElement = document.getElementById('mainAudioElement');
    const playPauseBtn = document.getElementById('playPauseBtn');
    const stopBtn = document.getElementById('stopBtn');
    const prevBtn = document.getElementById('prevBtn');
    const nextBtn = document.getElementById('nextBtn');
    const volumeSlider = document.getElementById('volumeSlider');
    const speedSelect = document.getElementById('playbackSpeed');
    const downloadBtn = document.getElementById('downloadCurrentBtn');
    const downloadAllBtn = document.getElementById('downloadAllBtn');
    const trackTitle = document.getElementById('trackTitle');
    const trackSource = document.getElementById('trackSource');
    const trackUrl = document.getElementById('trackUrl');
    const trackList = document.getElementById('trackList');

    function loadTrack(index, shouldPlay=false){
        if(index<0||index>=audioArray.length)return;
        currentIndex=index;
        currentAudio=audioArray[index];
        const info=audioFiles.get(currentAudio);
        audioElement.src=currentAudio;
        trackTitle.textContent=info.title;
        trackSource.textContent=\`Source: \${info.source} | Size: \${info.size} | Duration: \${info.duration}\`;
        trackUrl.textContent=currentAudio;
        if(shouldPlay) audioElement.play().catch(()=>{});
        highlightTrack();
    }

    function highlightTrack(){
        Array.from(trackList.children).forEach((item,i)=>{
            item.style.background=i===currentIndex?'#ddeeff':'#fff';
        });
    }

    function updateTrackList(){
        trackList.innerHTML='';
        audioArray.forEach((url,i)=>{
            const info=audioFiles.get(url);
            const item=document.createElement('div');
            item.className='trackItem';
            item.innerHTML=\`
                <div>\${i+1}. \${info.title} (\${info.source})</div>
                <button>⬇️</button>
            \`;
            item.addEventListener('click',()=>loadTrack(i,true));
            item.querySelector('button').addEventListener('click',e=>{
                e.stopPropagation();
                const a=document.createElement('a');
                a.href=url;
                a.download=info.title;
                a.click();
            });
            trackList.appendChild(item);
        });
    }

    playPauseBtn.addEventListener('click',()=>{
        if(audioElement.paused) audioElement.play(); else audioElement.pause();
    });
    stopBtn.addEventListener('click',()=>{audioElement.pause(); audioElement.currentTime=0;});
    prevBtn.addEventListener('click',()=>{currentIndex=(currentIndex-1+audioArray.length)%audioArray.length; loadTrack(currentIndex,true);});
    nextBtn.addEventListener('click',()=>{currentIndex=(currentIndex+1)%audioArray.length; loadTrack(currentIndex,true);});
    volumeSlider.addEventListener('input',e=>audioElement.volume=e.target.value/100);
    speedSelect.addEventListener('change',e=>audioElement.playbackRate=parseFloat(e.target.value));
    downloadBtn.addEventListener('click',()=>{const info=audioFiles.get(currentAudio); const a=document.createElement('a'); a.href=currentAudio; a.download=info.title; a.click();});
    downloadAllBtn.addEventListener('click',()=>{audioArray.forEach(url=>{const info=audioFiles.get(url); const a=document.createElement('a'); a.href=url; a.download=info.title; a.click();});});

    updateTrackList();
    loadTrack(0);
</script>

</body>
</html>
    `);

    w.document.close();
}









    // Enhanced download function
    async function downloadAudio(url, filename) {
        try {
            const response = await fetch(url);
            if (!response.ok) throw new Error(`Fetch failed with status ${response.status}`);
            const blob = await response.blob();
            const blobUrl = URL.createObjectURL(blob);

            const link = document.createElement('a');
            link.href = blobUrl;
            const safeFilename = (filename || 'audio').replace(/[\\/:*?"<>|]/g, '_');
            const extension = url.split('.').pop().split('?')[0] || 'mp3';
            link.download = safeFilename.endsWith('.' + extension) ? safeFilename : `${safeFilename}.${extension}`;

            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);

            setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);
        } catch (fetchError) {
            console.error('Fetch download failed, falling back to new tab:', fetchError);
            window.open(url, '_blank');
        }
    }

    // Download all audio files
    async function downloadAllAudio() {
        const downloadAllBtn = document.getElementById('downloadAllBtn');
        const originalText = downloadAllBtn.innerHTML;

        let count = 0;
        for (const url of audioArray) {
            const audioInfo = audioFiles.get(url);
            try {
                downloadAllBtn.innerHTML = `⬇️ ${++count}/${audioFiles.size}`;
                await downloadAudio(url, audioInfo.title);
                await new Promise(resolve => setTimeout(resolve, 400));
            } catch (error) {
                console.error('Failed to download:', url, error);
            }
        }

        downloadAllBtn.innerHTML = '✅ Done';
        setTimeout(() => downloadAllBtn.innerHTML = originalText, 1200);
    }

    // ADVANCED DETECTION METHODS

    // 1. Intercept Fetch API
    const originalFetch = window.fetch;
    window.fetch = function(...args) {
        const url = typeof args[0] === 'string' ? args[0] : args[0]?.url;
        if (url) {
            interceptAudioRequest(url, 'Fetch API');
        }
        return originalFetch.apply(this, args).then(response => {
            if (response.ok && url) {
                const contentType = response.headers.get('content-type') || '';
                const contentLength = response.headers.get('content-length') || 'Unknown';
                if (isAudioContent(url, contentType)) {
                    addAudioFile(url, {
                        source: 'Fetch API',
                        size: formatFileSize(contentLength),
                        title: extractFilename(url)
                    });
                }
            }
            return response.clone();
        });
    };

    // 2. Intercept XMLHttpRequest
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url, ...args) {
        this._audioDetectorUrl = url;
        return originalOpen.apply(this, [method, url, ...args]);
    };
    const originalSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function(...args) {
        this.addEventListener('load', () => {
            if (this.status >= 200 && this.status < 300 && this._audioDetectorUrl) {
                const contentType = this.getResponseHeader('content-type') || '';
                const contentLength = this.getResponseHeader('content-length') || 'Unknown';
                if (isAudioContent(this._audioDetectorUrl, contentType)) {
                    addAudioFile(this._audioDetectorUrl, {
                        source: 'XMLHttpRequest',
                        size: formatFileSize(contentLength),
                        title: extractFilename(this._audioDetectorUrl)
                    });
                }
            }
        });
        return originalSend.apply(this, args);
    };

    // 3. Monitor Audio/Video elements
    function monitorMediaElements() {
        document.querySelectorAll('audio, video').forEach(element => {
            extractMediaSources(element);
            const observer = new MutationObserver(() => extractMediaSources(element));
            observer.observe(element, { attributes: true, attributeFilter: ['src'] });
            element.addEventListener('loadstart', () => extractMediaSources(element));
            element.addEventListener('canplay', () => extractMediaSources(element));
        });
    }

    // 4. Extract media sources
    function extractMediaSources(element) {
        const tagName = element.tagName.toLowerCase();
        if (element.src && !element.src.startsWith('blob:')) {
            addAudioFile(element.src, {
                source: `${tagName} element`,
                title: element.title || element.getAttribute('alt') || extractFilename(element.src)
            });
        }
        element.querySelectorAll('source').forEach(source => {
            if (source.src) {
                addAudioFile(source.src, {
                    source: `${tagName} source`,
                    title: source.title || extractFilename(source.src)
                });
            }
        });
    }

    // 5. Intercept Web Audio API
    function interceptWebAudio() {
        if (!window.AudioContext && !window.webkitAudioContext) return;
        const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
        const newAudioContext = function(...args) {
            const context = new OriginalAudioContext(...args);
            const originalDecode = context.decodeAudioData;
            context.decodeAudioData = function(buffer, ...rest) {
                return originalDecode.call(this, buffer, ...rest);
            };
            return context;
        };
        window.AudioContext = window.webkitAudioContext = newAudioContext;
    }

    // 6. Monitor CSS and background audio
    function monitorCSSAudio() {
        const allElements = document.querySelectorAll('*');
        allElements.forEach(element => {
            const style = window.getComputedStyle(element);
            const bgImage = style.backgroundImage;
            if (bgImage && bgImage !== 'none') {
                const urls = bgImage.match(/url\(["']?([^"')]+)["']?\)/g);
                if (urls) {
                    urls.forEach(urlMatch => {
                        const url = urlMatch.match(/url\(["']?([^"')]+)["']?\)/)[1];
                        if (isAudioUrl(url)) {
                            addAudioFile(url, { source: 'CSS Background', title: extractFilename(url) });
                        }
                    });
                }
            }
        });
    }

    // 7. Detect streaming protocols
    function detectStreamingAudio() {
        if (window.Hls && window.Hls.prototype && window.Hls.prototype.loadSource) {
            const originalLoadSource = window.Hls.prototype.loadSource;
            window.Hls.prototype.loadSource = function(url) {
                if (url) {
                    addAudioFile(url, { source: 'HLS Stream', title: extractFilename(url) });
                }
                return originalLoadSource.call(this, url);
            };
        }
        if (window.dashjs && window.dashjs.MediaPlayer && window.dashjs.MediaPlayer.prototype && window.dashjs.MediaPlayer.prototype.initialize) {
            const originalInitialize = window.dashjs.MediaPlayer.prototype.initialize;
            window.dashjs.MediaPlayer.prototype.initialize = function(view, source, autoPlay) {
                if (source) {
                    addAudioFile(source, { source: 'DASH Stream', title: extractFilename(source) });
                }
                return originalInitialize.call(this, view, source, autoPlay);
            };
        }
    }

    // 8. Monitor iframe audio
    function monitorIframeAudio() {
        document.querySelectorAll('iframe').forEach(iframe => {
            try {
                if (iframe.contentDocument) {
                    iframe.contentDocument.querySelectorAll('audio, video').forEach(element => {
                        extractMediaSources(element);
                    });
                }
            } catch (e) { /* Cross-origin */ }
        });
    }

    // Helper functions
    function interceptAudioRequest(url, source) {
        if (isAudioUrl(url) && !interceptedRequests.has(url)) {
            interceptedRequests.add(url);
            setTimeout(() => {
                fetch(url, { method: 'HEAD' }).then(response => {
                    const contentType = response.headers.get('content-type') || '';
                    const contentLength = response.headers.get('content-length') || 'Unknown';
                    if (isAudioContent(url, contentType)) {
                        addAudioFile(url, {
                            source: source,
                            size: formatFileSize(contentLength),
                            title: extractFilename(url)
                        });
                    }
                }).catch(() => {
                    addAudioFile(url, { source: source, title: extractFilename(url) });
                });
            }, 200);
        }
    }

    function isAudioUrl(url) {
        if (!url || typeof url !== 'string') return false;
        const lowerUrl = url.toLowerCase().split('?')[0];
        return audioExtensions.some(ext => lowerUrl.endsWith(ext));
    }

    function isAudioContent(url, contentType) {
        return isAudioUrl(url) ||
               audioMimeTypes.some(mime => (contentType || '').toLowerCase().includes(mime));
    }

    function formatFileSize(bytes) {
        if (bytes === 'Unknown' || !bytes) return 'Unknown';
        const size = parseInt(bytes);
        if (isNaN(size) || size === 0) return 'Unknown';
        const units = ['B', 'KB', 'MB', 'GB'];
        let index = 0;
        let value = size;
        while (value >= 1024 && index < units.length - 1) {
            value /= 1024;
            index++;
        }
        return `${value.toFixed(1)} ${units[index]}`;
    }

    // 9. Monitor dynamic content and SPA routing
    function monitorDynamicContent() {
        const originalPushState = history.pushState;
        history.pushState = function(...args) {
            originalPushState.apply(this, args);
            setTimeout(forceDetection, 500);
        };
        const originalReplaceState = history.replaceState;
        history.replaceState = function(...args) {
            originalReplaceState.apply(this, args);
            setTimeout(forceDetection, 500);
        };
        window.addEventListener('popstate', () => setTimeout(forceDetection, 500));
    }

    // 10. Deep DOM scanning for embedded players
    function scanEmbeddedPlayers() {
        document.querySelectorAll('iframe[src*="spotify"], iframe[src*="soundcloud"], iframe[src*="youtube"], iframe[src*="bandcamp"]').forEach(iframe => {
            const src = iframe.src;
            let sourceName = 'Embed';
            if (src.includes('spotify')) sourceName = 'Spotify Embed';
            else if (src.includes('soundcloud')) sourceName = 'SoundCloud Embed';
            else if (src.includes('youtube')) sourceName = 'YouTube Embed';
            else if (src.includes('bandcamp')) sourceName = 'Bandcamp Embed';
            addAudioFile(src, { source: sourceName, title: iframe.title || sourceName, size: 'Stream' });
        });
    }

    // 11. Monitor JavaScript variables and objects
    function scanJavaScriptVariables() {
        // Intentionally minimal; avoid heavy scans.
    }

    // 12. Network monitoring using Performance API
    function monitorNetworkRequests() {
        try {
            const observer = new PerformanceObserver((list) => {
                list.getEntriesByType('resource').forEach(entry => {
                    if (entry.initiatorType !== 'xmlhttprequest' && entry.initiatorType !== 'fetch') {
                        if (isAudioUrl(entry.name)) {
                            addAudioFile(entry.name, {
                                source: 'Network Monitor',
                                title: extractFilename(entry.name),
                                size: entry.transferSize ? formatFileSize(entry.transferSize) : 'Unknown'
                            });
                        }
                    }
                });
            });
            observer.observe({ type: 'resource', buffered: true });
        } catch (e) {
            // Not available
        }
    }

    // 13. Blob URL detection
    function monitorBlobUrls() {
        const originalCreateObjectURL = URL.createObjectURL;
        URL.createObjectURL = function(object) {
            const url = originalCreateObjectURL.call(this, object);
            if (object instanceof Blob && object.type && audioMimeTypes.some(mime => object.type.startsWith(mime.replace('/','')))) {
                addAudioFile(url, {
                    source: 'Blob URL',
                    title: `Blob Audio (${object.type})`,
                    size: formatFileSize(object.size)
                });
            }
            return url;
        };
    }

    // 14. Streaming libs hook (ensure on late load too)
    function lateStreamingHooks() {
        detectStreamingAudio();
        const _define = Object.defineProperty;
        try {
            _define(window, 'Hls', {
                set(v){ this._hlsRef = v; detectStreamingAudio(); },
                get(){ return this._hlsRef; }
            });
        } catch {}
        try {
            _define(window, 'dashjs', {
                set(v){ this._dashRef = v; detectStreamingAudio(); },
                get(){ return this._dashRef; }
            });
        } catch {}
    }

    // Force comprehensive detection
    function forceDetection() {
        // console.log('🔍 Running comprehensive audio detection...');
        monitorMediaElements();
        scanEmbeddedPlayers();
        monitorCSSAudio();
        monitorIframeAudio();
        detectStreamingAudio();
        // console.log(`🎵 Detection complete. Found ${audioFiles.size} audio files.`);
    }

    // Initialize all detection methods
    function initializeAdvancedDetection() {
        // console.log('🚀 Initializing advanced audio detection...');
        interceptWebAudio();
        monitorBlobUrls();
        monitorNetworkRequests();
        monitorDynamicContent();
        lateStreamingHooks();

        const observer = new MutationObserver(() => {
            clearTimeout(window.audioDetectorDebounce);
            window.audioDetectorDebounce = setTimeout(forceDetection, 400);
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['src', 'href']
        });

        setTimeout(forceDetection, 1000);
    }

    // Initialize everything
    function init() {
        createFloatingButton();
        initializeAdvancedDetection();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // Global access for debugging
    window.audioDetector = {
        getDetectedFiles: () => Array.from(audioFiles.values()),
        forceDetection,
        clearFiles: () => {
            audioFiles.clear();
            updateButtonBadge();
            if (isExpanded) updateTrackList();
        }
    };

})();