Last active
November 22, 2025 20:43
-
-
Save minanagehsalalma/569a536695e40c76cebf956f56dcbf9c to your computer and use it in GitHub Desktop.
Facebook Messenger Audio Downloader (v12.0) - One-click download. Auto-plays/pauses to capture source, converts to real MP3 (with WAV fallback).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ============================================================================ | |
| // AUDIO SNIPPET DOWNLOADER v12.0 (Robust Auto-Pause) | |
| // ============================================================================ | |
| // FIX: Uses the 'playing' event listener to ensure pause commands are respected | |
| // by the browser's media engine. | |
| // ============================================================================ | |
| (function() { | |
| console.log("Audio Downloader v12: Robust Auto-Pause Active."); | |
| let activeAutoDownloadBtn = null; | |
| // --------------------------------------------------------- | |
| // 1. INJECT LAMEJS (MP3 Library) | |
| // --------------------------------------------------------- | |
| let lameLoaded = false; | |
| const lameScript = document.createElement('script'); | |
| lameScript.src = "https://cdn.jsdelivr.net/npm/[email protected]/lame.min.js"; | |
| lameScript.crossOrigin = "anonymous"; | |
| lameScript.onload = () => { lameLoaded = true; console.log("MP3 Library loaded."); }; | |
| lameScript.onerror = () => { lameLoaded = false; console.warn("MP3 Library blocked. Fallback enabled."); }; | |
| document.head.appendChild(lameScript); | |
| // --------------------------------------------------------- | |
| // 2. ENCODING LOGIC | |
| // --------------------------------------------------------- | |
| function floatTo16BitPCM(input) { | |
| const output = new Int16Array(input.length); | |
| for (let i = 0; i < input.length; i++) { | |
| const s = Math.max(-1, Math.min(1, input[i])); | |
| output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF; | |
| } | |
| return output; | |
| } | |
| async function encodeMp3(audioBuffer) { | |
| if (!lameLoaded || !window.lamejs) throw new Error("LameJS missing"); | |
| const mp3Encoder = new lamejs.Mp3Encoder(1, audioBuffer.sampleRate, 128); | |
| const samples = floatTo16BitPCM(audioBuffer.getChannelData(0)); | |
| const sampleBlockSize = 1152; | |
| const mp3Data = []; | |
| let remaining = samples.length; | |
| let i = 0; | |
| while (remaining >= sampleBlockSize) { | |
| const mp3buf = mp3Encoder.encodeBuffer(samples.subarray(i, i + sampleBlockSize)); | |
| if (mp3buf.length > 0) mp3Data.push(mp3buf); | |
| remaining -= sampleBlockSize; | |
| i += sampleBlockSize; | |
| } | |
| const mp3buf = mp3Encoder.flush(); | |
| if (mp3buf.length > 0) mp3Data.push(mp3buf); | |
| return new Blob(mp3Data, { type: 'audio/mp3' }); | |
| } | |
| function encodeWav(audioBuffer) { | |
| const samples = audioBuffer.getChannelData(0); | |
| const buffer = new ArrayBuffer(44 + samples.length * 2); | |
| const view = new DataView(buffer); | |
| const writeString = (view, offset, string) => { | |
| for (let i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i)); | |
| }; | |
| writeString(view, 0, 'RIFF'); | |
| view.setUint32(4, 36 + samples.length * 2, true); | |
| writeString(view, 8, 'WAVE'); | |
| writeString(view, 12, 'fmt '); | |
| view.setUint32(16, 16, true); | |
| view.setUint16(20, 1, true); | |
| view.setUint16(22, 1, true); | |
| view.setUint32(24, audioBuffer.sampleRate, true); | |
| view.setUint32(28, audioBuffer.sampleRate * 2, true); | |
| view.setUint16(32, 2, true); | |
| view.setUint16(34, 16, true); | |
| writeString(view, 36, 'data'); | |
| view.setUint32(40, samples.length * 2, true); | |
| const output = new Int16Array(buffer, 44); | |
| for (let i = 0; i < samples.length; i++) { | |
| const s = Math.max(-1, Math.min(1, samples[i])); | |
| output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF; | |
| } | |
| return new Blob([view], { type: 'audio/wav' }); | |
| } | |
| // --------------------------------------------------------- | |
| // 3. CORE DOWNLOAD PROCESSOR | |
| // --------------------------------------------------------- | |
| async function processAudioDownload(btn, srcUrl) { | |
| if (!btn) return; | |
| btn.innerText = "⏳ Downloading..."; | |
| btn.style.backgroundColor = "#e6b800"; | |
| try { | |
| const response = await fetch(srcUrl); | |
| const blob = await response.blob(); | |
| const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); | |
| const arrayBuffer = await blob.arrayBuffer(); | |
| const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer); | |
| let finalBlob = null; | |
| let ext = ""; | |
| if (lameLoaded) { | |
| try { | |
| btn.innerText = "⏳ Encoding MP3..."; | |
| finalBlob = await encodeMp3(audioBuffer); | |
| ext = ".mp3"; | |
| } catch (e) { console.warn("MP3 failed", e); } | |
| } | |
| if (!finalBlob) { | |
| btn.innerText = "⏳ Encoding WAV..."; | |
| finalBlob = encodeWav(audioBuffer); | |
| ext = ".wav"; | |
| } | |
| const durationTag = btn.dataset.duration ? `_${btn.dataset.duration.replace(':','m')}` : ''; | |
| const filename = `messenger_audio${durationTag}${ext}`; | |
| const a = document.createElement('a'); | |
| a.href = URL.createObjectURL(finalBlob); | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| btn.innerText = `✔ SAVED ${ext.toUpperCase()}`; | |
| btn.style.backgroundColor = "#31a24c"; | |
| setTimeout(() => { | |
| btn.innerText = "⬇ GET AUDIO"; | |
| btn.style.backgroundColor = "#20cef5"; | |
| }, 3000); | |
| } catch (err) { | |
| console.error(err); | |
| btn.innerText = "❌ ERROR"; | |
| alert("Download failed. See console."); | |
| } | |
| } | |
| // --------------------------------------------------------- | |
| // 4. SPY & AUTOMATION LOGIC | |
| // --------------------------------------------------------- | |
| function getTimeVariations(seconds) { | |
| if (!seconds) return []; | |
| const candidates = [Math.floor(seconds), Math.round(seconds), Math.ceil(seconds)]; | |
| return candidates.map(sec => { | |
| const m = Math.floor(sec / 60); | |
| const s = Math.floor(sec % 60); | |
| return `${m}:${s.toString().padStart(2, '0')}`; | |
| }); | |
| } | |
| const originalAudioPlay = HTMLAudioElement.prototype.play; | |
| // We attach this globally to handle paused events correctly | |
| function attachPauseListener(mediaElement) { | |
| if (mediaElement.dataset.hasPauseListener) return; | |
| mediaElement.addEventListener('playing', function() { | |
| if (activeAutoDownloadBtn) { | |
| console.log("[AutoSpy] Audio started. Pausing immediately."); | |
| mediaElement.pause(); | |
| // Process the download | |
| const btn = activeAutoDownloadBtn; | |
| activeAutoDownloadBtn = null; | |
| // Use currentSrc as the most reliable source | |
| const src = mediaElement.currentSrc || mediaElement.src; | |
| processAudioDownload(btn, src); | |
| } | |
| }); | |
| mediaElement.dataset.hasPauseListener = "true"; | |
| } | |
| function handlePlay(mediaElement) { | |
| attachPauseListener(mediaElement); | |
| // Manual play support | |
| const checkDuration = () => { | |
| const duration = mediaElement.duration; | |
| if (!duration || duration === Infinity) return; | |
| const timeStrings = getTimeVariations(duration); | |
| document.querySelectorAll('.custom-audio-dl-btn').forEach(btn => { | |
| if (timeStrings.includes(btn.dataset.duration) && btn.innerText.includes("GET AUDIO")) { | |
| btn.innerText = "✔ CLICK TO DOWNLOAD"; | |
| btn.style.backgroundColor = "#31a24c"; | |
| btn.onclick = (e) => { | |
| e.stopPropagation(); | |
| processAudioDownload(btn, mediaElement.src || mediaElement.currentSrc); | |
| }; | |
| } | |
| }); | |
| }; | |
| // Run check if metadata already exists, otherwise wait | |
| if (mediaElement.readyState >= 1) checkDuration(); | |
| mediaElement.addEventListener('loadedmetadata', checkDuration); | |
| } | |
| HTMLAudioElement.prototype.play = function() { | |
| handlePlay(this); | |
| return originalAudioPlay.apply(this, arguments); | |
| }; | |
| // --------------------------------------------------------- | |
| // 5. UI INJECTION | |
| // --------------------------------------------------------- | |
| const style = document.createElement('style'); | |
| style.innerHTML = ` | |
| .custom-audio-dl-btn { | |
| background-color: #20cef5; | |
| color: white; | |
| border: none; | |
| padding: 6px 10px; | |
| border-radius: 20px; | |
| font-size: 10px; | |
| font-weight: 800; | |
| cursor: pointer; | |
| margin-left: 10px; | |
| z-index: 10000; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.3); | |
| font-family: sans-serif; | |
| transition: all 0.2s; | |
| min-width: 80px; | |
| } | |
| .custom-audio-dl-btn:hover { opacity: 0.9; transform: scale(1.05); } | |
| `; | |
| document.head.appendChild(style); | |
| function addDownloadButtons() { | |
| const playButtons = document.querySelectorAll('[aria-label="Play"]'); | |
| playButtons.forEach(playBtn => { | |
| const rowContainer = playBtn.closest('[role="gridcell"]') || playBtn.closest('.html-div'); | |
| if (!rowContainer) return; | |
| if (rowContainer.querySelector('.custom-audio-dl-btn')) return; | |
| const timer = rowContainer.querySelector('[role="timer"]'); | |
| let durationText = "0:00"; | |
| if (timer) { | |
| durationText = timer.innerText; | |
| } else { | |
| const textNodes = rowContainer.innerText.match(/\d+:\d{2}/); | |
| if (textNodes) durationText = textNodes[0]; | |
| } | |
| const dlBtn = document.createElement('button'); | |
| dlBtn.innerText = "⬇ GET AUDIO"; | |
| dlBtn.className = "custom-audio-dl-btn"; | |
| dlBtn.dataset.duration = durationText; | |
| // --- AUTOMATION TRIGGER --- | |
| dlBtn.onclick = (e) => { | |
| e.stopPropagation(); | |
| activeAutoDownloadBtn = dlBtn; | |
| dlBtn.innerText = "⏳ Connecting..."; | |
| playBtn.click(); | |
| setTimeout(() => { | |
| if (activeAutoDownloadBtn === dlBtn) { | |
| dlBtn.innerText = "❌ Timeout"; | |
| activeAutoDownloadBtn = null; | |
| alert("Could not trigger audio. Try playing it manually once."); | |
| } | |
| }, 5000); | |
| }; | |
| const insertionPoint = playBtn.parentElement.parentElement; | |
| if (insertionPoint) { | |
| insertionPoint.appendChild(dlBtn); | |
| } else { | |
| playBtn.parentNode.appendChild(dlBtn); | |
| } | |
| }); | |
| } | |
| addDownloadButtons(); | |
| setInterval(addDownloadButtons, 2000); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment