// ==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();
}
};
})();