您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.
// ==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 Akhlak Ur Rahman // @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 => ({'&':'&','<':'<','>':'>','"':'"'}[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(); } }; })();