// ==UserScript== // @name ChatGPT Audio Downloader // @namespace https://git.ponywave.de/Akamaru/Userscripts // @version 1.1 // @description Lädt ChatGPT Audio-Antworten herunter // @author Akamaru // @match https://chatgpt.com/* // @match https://chat.openai.com/* // @icon https://www.google.com/s2/favicons?domain=chatgpt.com&sz=32 // @grant none // @run-at document-start // @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/get-chatgpt-audio.user.js // @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/get-chatgpt-audio.user.js // ==/UserScript== (function() { 'use strict'; console.log('[ChatGPT Audio] Skript geladen'); // Cache für Audio-Blobs const audioCache = new Map(); // Fange fetch-Aufrufe ab (vor ChatGPT-Initialisierung) const originalFetch = window.fetch; window.fetch = async function(...args) { const url = typeof args[0] === 'string' ? args[0] : args[0]?.url; // Prüfe ob es ein Audio-Synthesize-Request ist if (url && (url.includes('synthesize') || url.includes('/backend-api/synthesize'))) { console.log('[ChatGPT Audio] ===== SYNTHESIZE-ANFRAGE ERKANNT ====='); console.log('[ChatGPT Audio] URL:', url); const response = await originalFetch.apply(this, args); console.log('[ChatGPT Audio] Antwort erhalten:', response.status, response.type); // Klone Response um sie mehrfach zu lesen const responseClone = response.clone(); try { const blob = await responseClone.blob(); console.log('[ChatGPT Audio] Blob erfasst! Größe:', blob.size, 'Typ:', blob.type); // Extrahiere message_id aus URL const urlObj = new URL(url, window.location.origin); const messageId = urlObj.searchParams.get('message_id'); console.log('[ChatGPT Audio] Message-ID:', messageId); if (blob.size > 0) { // Speichere das echte Blob! audioCache.set(messageId || 'latest', { blob: blob, messageId: messageId, timestamp: Date.now(), url: url }); console.log('[ChatGPT Audio] ✓ Audio im Cache gespeichert! Message-ID:', messageId, 'Größe:', blob.size); // Zeige Popup nach 2 Sekunden automatisch setTimeout(() => { console.log('[ChatGPT Audio] Zeige Download-Popup...'); zeigeDownloadPopup(messageId || 'latest'); }, 2000); } } catch (err) { console.error('[ChatGPT Audio] Fehler beim Verarbeiten des Blobs:', err); } return response; } return originalFetch.apply(this, args); }; console.log('[ChatGPT Audio] Fetch-Interceptor installiert'); // Zeige Download-Popup function zeigeDownloadPopup(messageId) { console.log('[ChatGPT Audio] Zeige Download-Popup für Nachricht:', messageId); // Prüfe ob document.body existiert if (!document.body) { console.warn('[ChatGPT Audio] document.body noch nicht bereit, warte...'); setTimeout(() => zeigeDownloadPopup(messageId), 100); return; } // Hole gecachtes Audio const audioData = audioCache.get(messageId); if (!audioData || !audioData.blob) { console.error('[ChatGPT Audio] Keine Audiodaten im Cache für Nachricht:', messageId); console.log('[ChatGPT Audio] Cache-Inhalt:', Array.from(audioCache.keys())); return; } console.log('[ChatGPT Audio] Gecachtes Audio gefunden, Blob-Größe:', audioData.blob.size); // Entferne altes Popup falls vorhanden const altesPopup = document.getElementById('chatgpt-audio-download-popup'); if (altesPopup) { altesPopup.remove(); } const altesOverlay = document.getElementById('chatgpt-audio-download-overlay'); if (altesOverlay) { altesOverlay.remove(); } console.log('[ChatGPT Audio] Erstelle Popup-Elemente...'); // Erkenne Dark Mode const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; const bgColor = isDarkMode ? '#2f2f2f' : '#ffffff'; const textColor = isDarkMode ? '#ececec' : '#000000'; const borderColor = isDarkMode ? '#565656' : '#ddd'; const secondaryTextColor = isDarkMode ? '#b4b4b4' : '#666'; const cancelBtnBg = isDarkMode ? '#424242' : '#ffffff'; const cancelBtnBorder = isDarkMode ? '#565656' : '#ddd'; const cancelBtnText = isDarkMode ? '#ececec' : '#000000'; // Erstelle Popup const popup = document.createElement('div'); popup.id = 'chatgpt-audio-download-popup'; popup.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: ${bgColor}; border: 1px solid ${borderColor}; border-radius: 12px; padding: 24px; box-shadow: 0 4px 24px rgba(0,0,0,0.3); z-index: 10000; min-width: 300px; color: ${textColor}; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; `; popup.innerHTML = `

Audio herunterladen

Audiogröße: ${(audioData.blob.size / 1024).toFixed(1)} KB

`; // Overlay (Hintergrund) const overlay = document.createElement('div'); overlay.id = 'chatgpt-audio-download-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9999; `; document.body.appendChild(overlay); document.body.appendChild(popup); console.log('[ChatGPT Audio] Popup angezeigt'); // Event-Listener für Abbrechen-Button document.getElementById('download-cancel-btn').addEventListener('click', () => { popup.remove(); overlay.remove(); }); // Event-Listener für Download-Button document.getElementById('download-confirm-btn').addEventListener('click', () => { try { console.log('[ChatGPT Audio] Download-Button geklickt'); // Verwende das bereits gecachte Blob direkt! const blob = audioData.blob; const fileName = `chatgpt-audio-${messageId || Date.now()}.aac`; console.log('[ChatGPT Audio] Erstelle Download-Link für Blob, Größe:', blob.size); // Erstelle temporäre Download-URL const downloadUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); // Aufräumen setTimeout(() => URL.revokeObjectURL(downloadUrl), 100); console.log('[ChatGPT Audio] ✓ Download gestartet:', fileName); popup.remove(); overlay.remove(); } catch (error) { console.error('[ChatGPT Audio] Download-Fehler:', error); alert('Fehler beim Download: ' + error.message); } }); // Klick auf Overlay schließt Popup overlay.addEventListener('click', () => { popup.remove(); overlay.remove(); }); } // Cleanup alter Audio-Blobs (nach 1 Stunde) setInterval(() => { const now = Date.now(); const oneHour = 60 * 60 * 1000; for (const [messageId, audioData] of audioCache.entries()) { if (now - audioData.timestamp > oneHour) { audioCache.delete(messageId); console.log('[ChatGPT Audio] Altes Audio bereinigt:', messageId); } } }, 10 * 60 * 1000); // Alle 10 Minuten prüfen })();