537 lines
20 KiB
JavaScript
537 lines
20 KiB
JavaScript
// 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 <audio> Element
|
|
document.querySelectorAll('.Audio, .audio').forEach(container => {
|
|
try {
|
|
if (!container.querySelector('.tg-audio-btn')) {
|
|
const audio = container.querySelector('audio');
|
|
if (audio) addAudioDownloadButton(container);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error processing audio', err);
|
|
}
|
|
});
|
|
|
|
// Documents (.mp3, .zip, .pdf, etc.) - erkennbar an .document Container MIT .File Element
|
|
document.querySelectorAll('.document, .Document').forEach(container => {
|
|
try {
|
|
// Prüfe ob es ein .File Element gibt (das ist der Indikator für Documents)
|
|
const fileEl = container.querySelector('.File');
|
|
const fileTitle = container.querySelector('.file-title');
|
|
|
|
if ((fileEl || fileTitle) && !container.querySelector('.tg-doc-btn')) {
|
|
addDocumentDownloadButton(container);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error processing document', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
const observer = new MutationObserver(processMedia);
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
setInterval(processMedia, 1200);
|
|
processMedia();
|
|
|
|
console.log('✅ TG Downloader loaded - Video, Image & Audio support enabled');
|
|
|
|
// Event-Weiterleitung
|
|
document.addEventListener('telDownloaderCompleted', (e) => {
|
|
try {
|
|
const detail = e.detail || {};
|
|
chrome.runtime.sendMessage(detail.extensionId || chrome.runtime.id, {
|
|
action: detail.action || 'videoProgressCompleted',
|
|
videoId: detail.videoId,
|
|
clientId: detail.clientId,
|
|
name: detail.name,
|
|
version: detail.version,
|
|
locale: detail.locale
|
|
}, () => {
|
|
if (chrome.runtime.lastError) {
|
|
console.error('Error sending message:', chrome.runtime.lastError.message);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
console.error('Failed to forward event:', err);
|
|
}
|
|
});
|
|
|
|
// inject.js laden
|
|
const script = document.createElement('script');
|
|
script.src = chrome.runtime.getURL('inject.js');
|
|
script.setAttribute('data-extension-id', chrome.runtime.id);
|
|
(document.head || document.documentElement).appendChild(script); |