1
0
Files
Userscripts/get-chatgpt-audio.user.js
2025-11-11 00:58:22 +01:00

226 lines
9.0 KiB
JavaScript

// ==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 = `
<h3 style="margin: 0 0 16px 0; font-size: 18px; font-weight: 600; color: ${textColor};">Audio herunterladen</h3>
<p style="margin: 0 0 20px 0; font-size: 14px; color: ${secondaryTextColor};">Audiogröße: ${(audioData.blob.size / 1024).toFixed(1)} KB</p>
<div style="display: flex; gap: 8px; justify-content: flex-end;">
<button id="download-cancel-btn" style="padding: 8px 16px; border: 1px solid ${cancelBtnBorder}; border-radius: 6px; background: ${cancelBtnBg}; color: ${cancelBtnText}; cursor: pointer; font-size: 14px;">Abbrechen</button>
<button id="download-confirm-btn" style="padding: 8px 16px; border: none; border-radius: 6px; background: #10a37f; color: white; cursor: pointer; font-size: 14px; font-weight: 500;">Herunterladen</button>
</div>
`;
// 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
})();