From a89785a5c80c981d855bc2a74c8d3ffe671ff5d5 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 21 Dec 2025 15:18:49 +0000 Subject: [PATCH] content_tg.js aktualisiert --- content_tg.js | 892 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 536 insertions(+), 356 deletions(-) diff --git a/content_tg.js b/content_tg.js index 890043b..da06d56 100644 --- a/content_tg.js +++ b/content_tg.js @@ -1,357 +1,537 @@ -// 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(video) { - try { - const videoUrl = video.currentSrc || video.src; - - console.log('=== FILENAME EXTRACTION ==='); - console.log('Video URL:', videoUrl); - - // 1. Suche die Message, die das Video enthält - const messageContainer = video.closest('.message, .Message, .bubble, .media-container'); - if (!messageContainer) { - console.warn('No message container found'); - return 'telegram_video_' + Date.now(); - } - - console.log('Message container found'); - - // === PRIORITÄT 1: VERBESSERTES ALBUM/SERIEN-HANDLING === - const albumContainer = messageContainer.querySelector('.Album'); - if (albumContainer) { - console.log('Album erkannt → verbessertes Serien-Naming'); - - const videoContainer = video.closest('.album-item-select-wrapper'); - if (videoContainer) { - const allVideoContainers = albumContainer.querySelectorAll('.album-item-select-wrapper'); - let videoIndex = -1; - for (let i = 0; i < allVideoContainers.length; i++) { - if (allVideoContainers[i] === videoContainer) { - videoIndex = i; - break; - } - } - - console.log('Video index in album:', videoIndex); - - if (videoIndex >= 0) { - const textContent = messageContainer.querySelector('.text-content.clearfix.with-meta, .text-content'); - if (textContent) { - const lines = textContent.innerText.split('\n') - .map(l => l.trim()) - .filter(l => l.length > 0); - - console.log('Album text lines:', lines); - - const seriesTitle = lines[0] || 'Unknown Series'; - - // Direkter Match: SxxExx - Titel - const episodeLines = lines.filter(line => /^S\d{2}E\d{2}\s*-/.test(line)); - if (episodeLines.length > videoIndex && episodeLines[videoIndex]) { - const line = episodeLines[videoIndex]; - const match = line.match(/^(S\d{2}E\d{2})\s*-\s*(.+)$/); - if (match) { - const code = match[1]; - const title = match[2].trim(); - const finalName = `${sanitizeFriendly(seriesTitle)} - ${code} - ${sanitizeFriendly(title)}`; - console.log('Perfekter Serien-Dateiname:', finalName); - return finalName; - } - } - - // Fallback: Zeile nach Serientitel als Episodentitel - if (lines.length > videoIndex + 1) { - const episodeTitle = lines[videoIndex + 1]; - if (episodeTitle.length > 5) { - const finalName = `${sanitizeFriendly(seriesTitle)} - ${sanitizeFriendly(episodeTitle)}`; - console.log('Serien-Fallback-Dateiname:', finalName); - return finalName; - } - } - - return sanitizeFriendly(seriesTitle + ' - Episode ' + (videoIndex + 1)); - } - } - } - } - - // 2. Einzelvideo: Erste Zeile aus .text-content - 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. Weitere Titel-Selektoren - const titleSelectors = [ - '.media-caption-text', - '.media-caption', - '.message-title', - '.video-title', - '.document-name', - '.file-name', - '.name', - '.title:not(.peer-title):not(.top-bar)', - '[class*="caption"]', - '[class*="title"]:not([class*="peer"]):not([class*="top"])' - ]; - - for (const selector of titleSelectors) { - const element = messageContainer.querySelector(selector); - if (element) { - const text = element.innerText?.trim() || element.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); - } - } - } - } - - // 4. Textblöcke, Text-Nodes, React Props usw. (wie in deinem Original) - const allTextElements = messageContainer.querySelectorAll('div, span, p'); - for (const el of allTextElements) { - const text = Array.from(el.childNodes) - .filter(node => node.nodeType === Node.TEXT_NODE) - .map(node => node.textContent.trim()) - .join(' ') - .trim(); - - if (text.length > 5 && text.length < 200) { - if (!text.startsWith('@') && - !text.startsWith('#') && - !text.includes('⬇') && - !text.match(/^\d{2}:\d{2}:\d{2}$/) && - /[A-Z]/.test(text)) { - console.log('Found potential filename from text element:', text); - return sanitizeFriendly(text); - } - } - } - - const walker = document.createTreeWalker( - messageContainer, - NodeFilter.SHOW_TEXT, - { - acceptNode: function(node) { - const text = node.textContent.trim(); - if (text.length < 5 || - text.match(/^\d{2}:\d{2}:\d{2}$/) || - text.startsWith('@') || - text.startsWith('#') || - text.includes('⬇')) { - return NodeFilter.FILTER_REJECT; - } - return NodeFilter.FILTER_ACCEPT; - } - }, - false - ); - - let foundTexts = []; - let node; - while (node = walker.nextNode()) { - const text = node.textContent.trim(); - if (text.length > 0) foundTexts.push(text); - } - - for (const text of foundTexts) { - if (text.length > 5 && text.length < 200 && /[A-Z]/.test(text)) { - console.log('Using text node as filename:', text); - return sanitizeFriendly(text); - } - } - - const reactKeys = Object.keys(messageContainer).filter(k => k.startsWith('__react')); - for (const key of reactKeys) { - try { - const fiber = messageContainer[key]; - const filename = searchReactTree(fiber, ['fileName', 'file_name', 'name', 'title', 'caption']); - if (filename && typeof filename === 'string' && filename.length > 3) { - console.log('Found filename in React props:', filename); - return sanitizeFriendly(filename); - } - } catch (e) {} - } - - const match = videoUrl.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_video_' + Date.now(); - - } catch (err) { - console.error('Error extracting filename:', err); - return 'telegram_video_' + Date.now(); - } -} - -// Durchsuche React Fiber Tree -function searchReactTree(node, keys, depth = 0, maxDepth = 15) { - if (depth > maxDepth || !node || typeof node !== 'object') return null; - - try { - for (const key of keys) { - if (node[key]) { - const val = node[key]; - if (typeof val === 'string' && val.length > 0) return val; - if (typeof val === 'object' && val.name) return val.name; - } - } - - if (node.memoizedProps) { - for (const key of keys) { - if (node.memoizedProps[key]) { - const val = node.memoizedProps[key]; - if (typeof val === 'string' && val.length > 0) return val; - } - } - } - - if (node.child) { - const result = searchReactTree(node.child, keys, depth + 1, maxDepth); - if (result) return result; - } - } catch (e) {} - - return null; -} - -// Download auslösen – OHNE .mp4 anhängen (für maximale Geschwindigkeit!) -function fallbackToInjectDownload(videoUrl, autoName) { - const event = new CustomEvent('downloadRequested', { - detail: { - url: videoUrl, - name: autoName, // Kein .mp4 hier → schneller Chunk-Download! - version: location.pathname, - } - }); - document.dispatchEvent(event); -} - -// Download-Button hinzufügen -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.closest('.media-inner') || 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. Bitte das Video einmal kurz abspielen.'); - return; - } - - const autoName = extractTelegramFilename(video); - - console.log('=== DOWNLOAD INFO ==='); - console.log('Final filename (ohne .mp4):', autoName); - - fallbackToInjectDownload(videoUrl, autoName); - }); - - container.appendChild(btn); -} - -// Alle Videos verarbeiten -function processVideos() { - if (isInTopicList()) return; - document.querySelectorAll('video:not([data-tg-video])').forEach(video => { - try { - video.dataset.tgVideo = 'true'; - addVideoDownloadButton(video); - } catch (err) { - console.error('Error processing video element', err); - } - }); -} - -const observer = new MutationObserver(processVideos); -observer.observe(document.body, { childList: true, subtree: true }); -setInterval(processVideos, 1200); -processVideos(); - -// Completed-Event weiterleiten -document.addEventListener('telDownloaderCompleted', (e) => { - try { - const detail = e.detail || {}; - const extId = detail.extensionId || chrome.runtime.id; - chrome.runtime.sendMessage(extId, { - action: detail.action || 'videoProgressCompleted', - videoId: detail.videoId, - clientId: detail.clientId, - name: detail.name, - version: detail.version, - locale: detail.locale - }, (res) => { - if (chrome.runtime.lastError) { - console.error('Error sending message to background:', chrome.runtime.lastError.message); - } - }); - } catch (err) { - console.error('Failed to forward telDownloaderCompleted to extension:', err); - } -}); - -// inject.js laden -const script = document.createElement('script'); -script.src = chrome.runtime.getURL('inject.js'); -script.setAttribute('data-extension-id', chrome.runtime.id); +// 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