// Topic-Übersicht erkennen (keine Buttons dort) function isInTopicList() { return document.querySelector('.topics-list, .topic-list, .forums-list, .chat-list .topic') !== null || document.body.classList.contains('topics-mode'); } // Kleine Helfer-Funktion: freundlich sanitisieren (erhalte Leerzeichen, Klammern, Punkte) function sanitizeFriendly(name) { if (!name) return 'Telegram'; return name.toString().replace(/[\/\\\?\%\*\:\|\"\<\>]/g, "_").trim(); } // Extrahiere den Original-Dateinamen aus Telegram-Metadaten function extractTelegramFilename(element) { try { const elementUrl = element.currentSrc || element.src; console.log('=== FILENAME EXTRACTION ==='); console.log('Element URL:', elementUrl); // 1. Suche die Message, die das Element enthält const messageContainer = element.closest('.message, .Message, .bubble, .media-container'); if (!messageContainer) { console.warn('No message container found'); return 'telegram_media_' + Date.now(); } console.log('Message container found'); // 2. Zuerst: Suche nach .text-content (funktioniert für einzelne Medien) const textContent = messageContainer.querySelector('.text-content, .text-content.clearfix'); if (textContent) { const fullText = textContent.innerText || textContent.textContent; const firstLine = fullText.split('\n')[0].trim(); if (firstLine.length > 5 && !firstLine.startsWith('@') && !firstLine.startsWith('#') && !firstLine.includes('⬇') && !firstLine.match(/^\d{2}:\d{2}:\d{2}$/)) { console.log('Found filename from .text-content:', firstLine); return sanitizeFriendly(firstLine); } } // 3. Bei Alben/Serien const albumContainer = messageContainer.querySelector('.album, .Album, [class*="album"]'); if (albumContainer) { console.log('Album/Series detected'); let elementContainer = element.closest('.album-item-select-wrapper, .album-item, .media-item'); if (elementContainer) { const allContainers = albumContainer.querySelectorAll('.album-item-select-wrapper, .album-item'); let elementIndex = -1; for (let i = 0; i < allContainers.length; i++) { if (allContainers[i] === elementContainer || allContainers[i].contains(element)) { elementIndex = i; break; } } console.log('Element index in album:', elementIndex); const contentInner = messageContainer.querySelector('.content-inner, .text, .message-text'); if (contentInner && elementIndex >= 0) { const fullText = contentInner.innerText || contentInner.textContent; const episodePattern = /S\d{2}E\d{2}\s*-\s*[^\n]+/gi; const episodes = fullText.match(episodePattern); if (episodes && episodes[elementIndex]) { const episodeName = episodes[elementIndex].trim(); console.log('Found episode name for index', elementIndex, ':', episodeName); return sanitizeFriendly(episodeName); } const lines = fullText.split('\n').map(l => l.trim()).filter(l => l.length > 5); const episodeLines = lines.filter(line => /S\d{2}E\d{2}/.test(line) || (line.includes('-') && line.length > 10 && line.length < 150) ); if (episodeLines[elementIndex]) { console.log('Found episode from lines:', episodeLines[elementIndex]); return sanitizeFriendly(episodeLines[elementIndex]); } } } } // 4. Weitere Selektoren const titleSelectors = [ '.media-caption-text', '.media-caption', '.message-title', '.video-title', '.document-name', '.file-name', '.name', '.title:not(.peer-title):not(.top-bar)' ]; for (const selector of titleSelectors) { const el = messageContainer.querySelector(selector); if (el) { const text = el.innerText?.trim() || el.textContent?.trim(); if (text && text.length > 0 && text.length < 300) { if (!text.includes('⬇') && !text.toLowerCase().includes('download') && !text.match(/^\d{2}:\d{2}:\d{2}$/)) { console.log('Found filename from selector', selector, ':', text); return sanitizeFriendly(text); } } } } // 5. Fallbacks const match = elementUrl?.match(/document(\d+)/); if (match) { console.warn('Using document ID as fallback'); return 'telegram_doc_' + match[1]; } console.warn('Could not extract filename, using timestamp fallback'); return 'telegram_media_' + Date.now(); } catch (err) { console.error('Error extracting filename:', err); return 'telegram_media_' + Date.now(); } } // Video-Button function addVideoDownloadButton(video) { if (isInTopicList()) return; if (!video || video.tagName !== "VIDEO" || video.classList.contains("sticker-media") || video.classList.contains("media-sticker")) return; if (!video.currentSrc && !video.src) return; let container = video.parentElement || video.closest('div') || video; if (!container || container.querySelector('.tg-video-btn')) return; const computed = window.getComputedStyle(container); if (computed.position === 'static') container.style.position = 'relative'; const btn = document.createElement('button'); btn.className = 'tg-video-btn'; btn.innerText = '⬇ Download'; btn.title = 'Video herunterladen'; btn.style.cssText = ` position: absolute !important; top: 10px !important; right: 10px !important; z-index: 100000 !important; background: #e63946 !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 8px 12px !important; font-size: 14px !important; font-weight: bold !important; cursor: pointer !important; box-shadow: 0 4px 12px rgba(0,0,0,0.6) !important; `; btn.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); const videoUrl = video.currentSrc || video.src; if (!videoUrl) { alert('Video-URL nicht verfügbar.'); return; } const autoName = extractTelegramFilename(video); console.log('=== VIDEO DOWNLOAD ===', autoName); const event = new CustomEvent('downloadRequested', { detail: { url: videoUrl, name: autoName, version: location.pathname } }); document.dispatchEvent(event); }); container.appendChild(btn); } // Bild-Button function addImageDownloadButton(img) { if (isInTopicList()) return; if (!img || img.tagName !== "IMG") return; if (img.classList.contains('emoji') || img.classList.contains('thumbnail') || img.width < 100 || img.height < 100) return; let container = img.closest('.media-inner') || img.closest('.Message') || img.parentElement; if (!container || container.querySelector('.tg-img-btn')) return; if (window.getComputedStyle(container).position === 'static') { container.style.position = 'relative'; } const btn = document.createElement('button'); btn.className = 'tg-img-btn'; btn.innerText = '⬇'; btn.title = 'Bild herunterladen'; btn.style.cssText = ` position: absolute !important; top: 5px !important; left: 5px !important; z-index: 999999 !important; background: rgba(42, 157, 143, 0.95) !important; color: white !important; border: 2px solid white !important; border-radius: 50% !important; width: 44px !important; height: 44px !important; font-size: 22px !important; cursor: pointer !important; box-shadow: 0 2px 8px rgba(0,0,0,0.8) !important; display: flex !important; align-items: center !important; justify-content: center !important; pointer-events: auto !important; `; btn.onmouseenter = () => btn.style.transform = 'scale(1.15)'; btn.onmouseleave = () => btn.style.transform = 'scale(1)'; btn.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); const imgUrl = img.src || img.currentSrc; if (!imgUrl || imgUrl.startsWith('data:')) { alert('Bild-URL nicht verfügbar.'); return; } const autoName = extractTelegramFilename(img) || 'telegram_image'; console.log('=== IMAGE DOWNLOAD ===', autoName); const event = new CustomEvent('imageDownloadRequested', { detail: { url: imgUrl, name: autoName } }); document.dispatchEvent(event); }); container.appendChild(btn); console.log('✅ Image button added'); } // Audio-Button function addAudioDownloadButton(audioContainer) { if (isInTopicList()) return; if (!audioContainer || audioContainer.querySelector('.tg-audio-btn')) return; const audio = audioContainer.querySelector('audio'); if (!audio) return; if (window.getComputedStyle(audioContainer).position === 'static') { audioContainer.style.position = 'relative'; } const btn = document.createElement('button'); btn.className = 'tg-audio-btn'; btn.innerText = '⬇ Download'; btn.title = 'Audio herunterladen'; btn.style.cssText = ` position: absolute !important; top: 10px !important; right: 10px !important; z-index: 999999 !important; background: rgba(244, 162, 97, 0.95) !important; color: white !important; border: 2px solid white !important; border-radius: 50% !important; width: 40px !important; height: 40px !important; font-size: 20px !important; cursor: pointer !important; box-shadow: 0 2px 8px rgba(0,0,0,0.8) !important; display: flex !important; align-items: center !important; justify-content: center !important; pointer-events: auto !important; `; btn.onmouseenter = () => btn.style.transform = 'scale(1.15)'; btn.onmouseleave = () => btn.style.transform = 'scale(1)'; btn.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); const audioUrl = audio.currentSrc || audio.src; if (!audioUrl) { alert('Audio-URL nicht verfügbar.'); return; } const autoName = extractTelegramFilename(audioContainer) || 'telegram_audio'; console.log('=== AUDIO DOWNLOAD ===', autoName); const event = new CustomEvent('audioDownloadRequested', { detail: { url: audioUrl, name: autoName } }); document.dispatchEvent(event); }); audioContainer.appendChild(btn); console.log('✅ Audio button added'); } // Document-Button (für .mp3, .zip, .pdf, etc.) function addDocumentDownloadButton(docContainer) { if (isInTopicList()) return; if (!docContainer || docContainer.querySelector('.tg-doc-btn')) return; // Suche nach dem Dateinamen - verschiedene mögliche Selektoren const docNameEl = docContainer.querySelector('.file-title, .document-name, .file-name'); const docName = docNameEl?.innerText?.trim() || docNameEl?.title || ''; // Suche nach File-Extension const extEl = docContainer.querySelector('.file-ext'); const fileExt = extEl?.innerText?.trim()?.toLowerCase() || ''; // Wenn kein Name gefunden wurde, ignoriere if (!docName && !fileExt) return; // Prüfe Dateityp basierend auf Extension oder Name const isAudio = /\.(mp3|ogg|wav|m4a|flac|aac|opus)$/i.test(docName) || ['mp3', 'ogg', 'wav', 'm4a', 'flac', 'aac', 'opus'].includes(fileExt); const isArchive = /\.(zip|rar|7z|tar|gz)$/i.test(docName) || ['zip', 'rar', '7z', 'tar', 'gz'].includes(fileExt); const isDocument = /\.(pdf|doc|docx|txt|xlsx|xls|ppt)$/i.test(docName) || ['pdf', 'doc', 'docx', 'txt', 'xlsx', 'xls', 'ppt'].includes(fileExt); if (window.getComputedStyle(docContainer).position === 'static') { docContainer.style.position = 'relative'; } // Icon und Farbe basierend auf Dateityp let icon = '⬇'; let bgColor = 'rgba(168, 85, 247, 0.95)'; let title = 'Datei herunterladen'; if (isAudio) { icon = '⬇'; bgColor = 'rgba(244, 162, 97, 0.95)'; title = 'Audio herunterladen'; } else if (isArchive) { icon = '⬇'; bgColor = 'rgba(59, 130, 246, 0.95)'; title = 'Archiv herunterladen'; } else if (isDocument) { icon = '⬇'; bgColor = 'rgba(168, 85, 247, 0.95)'; title = 'Dokument herunterladen'; } const btn = document.createElement('button'); btn.className = 'tg-doc-btn'; btn.innerText = icon; btn.title = title + (docName ? ': ' + docName : ''); btn.style.cssText = ` position: absolute !important; top: 10px !important; right: 10px !important; z-index: 999999 !important; background: ${bgColor} !important; color: white !important; border: 2px solid white !important; border-radius: 50% !important; width: 40px !important; height: 40px !important; font-size: 20px !important; cursor: pointer !important; box-shadow: 0 2px 8px rgba(0,0,0,0.8) !important; display: flex !important; align-items: center !important; justify-content: center !important; pointer-events: auto !important; `; btn.onmouseenter = () => btn.style.transform = 'scale(1.15)'; btn.onmouseleave = () => btn.style.transform = 'scale(1)'; btn.addEventListener('click', (e) => { e.stopPropagation(); e.preventDefault(); // Versuche Download-URL zu finden let downloadUrl = null; // Methode 1: Suche nach data-doc-id im File-Element oder Container const fileEl = docContainer.querySelector('.File'); const docId = fileEl?.dataset?.docId || docContainer.dataset?.docId || docContainer.closest('[data-doc-id]')?.dataset?.docId; if (docId) { // Konstruiere die Telegram progressive URL downloadUrl = `${location.origin}/a/progressive/document${docId}`; } // Methode 2: Suche nach existierendem Download-Link if (!downloadUrl) { const downloadLink = docContainer.querySelector('a[href*="document"], a[download]'); if (downloadLink) { downloadUrl = downloadLink.href; } } // Methode 3: Suche im parent Message nach document-ID if (!downloadUrl) { const message = docContainer.closest('.Message'); const messageId = message?.id?.replace('message-', ''); if (messageId) { // Versuche document-ID aus verschiedenen Quellen const allDataIds = message.querySelectorAll('[data-doc-id]'); if (allDataIds.length > 0) { const firstDocId = allDataIds[0].dataset.docId; downloadUrl = `${location.origin}/a/progressive/document${firstDocId}`; } } } if (!downloadUrl) { alert('Download-URL nicht gefunden. Bitte klicke zuerst auf die Datei um sie zu laden.'); return; } const finalName = docName || 'telegram_file'; console.log('=== DOCUMENT DOWNLOAD ==='); console.log('Name:', finalName); console.log('URL:', downloadUrl); const eventName = isAudio ? 'audioDownloadRequested' : 'documentDownloadRequested'; const event = new CustomEvent(eventName, { detail: { url: downloadUrl, name: finalName } }); document.dispatchEvent(event); }); docContainer.appendChild(btn); console.log('✅ Document button added:', docName || fileExt); } // Alle Medien verarbeiten function processMedia() { if (isInTopicList()) return; // Videos document.querySelectorAll('video:not([data-tg-video])').forEach(video => { try { video.dataset.tgVideo = 'true'; addVideoDownloadButton(video); } catch (err) { console.error('Error processing video', err); } }); // Bilder document.querySelectorAll('img.full-media').forEach(img => { try { const container = img.closest('.media-inner, .Message') || img.parentElement; if (container && !container.querySelector('.tg-img-btn')) { addImageDownloadButton(img); } } catch (err) { console.error('Error processing image', err); } }); // Audio mit