225 lines
8.9 KiB
JavaScript
225 lines
8.9 KiB
JavaScript
// ==UserScript==
|
|
// @name ChatGPT Audio Downloader
|
|
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
|
// @version 1.0
|
|
// @description Lädt ChatGPT Audio-Antworten herunter
|
|
// @author Akamaru
|
|
// @match https://chatgpt.com/*
|
|
// @match https://chat.openai.com/*
|
|
// @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
|
|
|
|
})();
|