diff --git a/inject.js b/inject.js index 1ac612f..a5631d5 100644 --- a/inject.js +++ b/inject.js @@ -1,396 +1,558 @@ -(function () { - const scriptElement = document.currentScript; - const extensionId = scriptElement ? scriptElement.getAttribute("data-extension-id") : null; - - console.log('inject.js running, extensionId=', extensionId); - - // Empfang von "downloadRequested" (kommt vom content script) - document.addEventListener("downloadRequested", function(e) { - try { - let t = e.detail.url; - let l = e.detail.name; - let r = e.detail.version; - let o = e.detail.clientId; - let n = e.detail.locale; - tel_download_video(t, l, r, o, n); - } catch (err) { - console.error('Error in downloadRequested handler', err); - } - }); - - document.addEventListener("imageDownloadRequested", function(e) { - try { - let t = e.detail.url; - let l = e.detail.name; - tel_download_image(t, l); - } catch (err) { - console.error('Error in imageDownloadRequested handler', err); - } - }); - - // Hilfsfunktion: progress-bar-container - function ensureProgressContainer() { - let container = document.getElementById("tel-downloader-progress-bar-container"); - if (!container) { - container = document.createElement("div"); - container.id = "tel-downloader-progress-bar-container"; - container.style.position = "fixed"; - container.style.bottom = "0"; - container.style.right = "0"; - container.style.display = "flex"; - container.style.flexDirection = "column"; - container.style.gap = "0.4rem"; - container.style.padding = "0.4rem"; - container.style.zIndex = "1600"; - document.body.appendChild(container); - } - return container; - } - - const createProgressBar = (id, filename) => { - const dark = document.querySelector("html")?.classList?.contains("night") || - document.querySelector("html")?.classList?.contains("theme-dark"); - const container = ensureProgressContainer(); - container.style.zIndex = location.pathname.startsWith("/k/") ? "4" : "1600"; - - const n = document.createElement("div"); - n.id = "tel-downloader-progress-" + id; - n.style.width = "20rem"; - n.style.marginTop = "0.4rem"; - n.style.padding = "0.6rem"; - n.style.backgroundColor = dark ? "rgba(0,0,0,0.6)" : "rgba(0,0,0,0.3)"; - n.style.borderRadius = "8px"; - n.style.boxShadow = "0 4px 12px rgba(0,0,0,0.2)"; - n.style.color = "white"; - n.style.fontFamily = "sans-serif"; - - const s = document.createElement("div"); - s.style.display = "flex"; - s.style.justifyContent = "space-between"; - s.style.alignItems = "center"; - - const i = document.createElement("p"); - i.className = "filename"; - i.style.margin = 0; - i.style.color = "white"; - i.style.fontSize = "0.9rem"; - i.style.overflow = "hidden"; - i.style.textOverflow = "ellipsis"; - i.style.whiteSpace = "nowrap"; - i.innerText = filename || "video"; - - const d = document.createElement("div"); - d.style.cursor = "pointer"; - d.style.fontSize = "1.2rem"; - d.style.color = dark ? "#8a8a8a" : "white"; - d.innerHTML = "×"; - d.onclick = function() { - if (n.parentElement) n.parentElement.removeChild(n); - }; - - const a = document.createElement("div"); - a.className = "progress"; - a.style.backgroundColor = "#e2e2e2"; - a.style.position = "relative"; - a.style.width = "100%"; - a.style.height = "1.6rem"; - a.style.borderRadius = "2rem"; - a.style.overflow = "hidden"; - a.style.marginTop = "0.6rem"; - - const c = document.createElement("p"); - c.style.position = "absolute"; - c.style.zIndex = 5; - c.style.left = "50%"; - c.style.top = "50%"; - c.style.transform = "translate(-50%, -50%)"; - c.style.margin = 0; - c.style.color = "black"; - c.style.fontSize = "0.85rem"; - c.innerText = "0%"; - - const p = document.createElement("div"); - p.style.position = "absolute"; - p.style.height = "100%"; - p.style.width = "0%"; - p.style.backgroundColor = "#3390ec"; - p.style.left = "0"; - p.style.top = "0"; - - a.appendChild(c); - a.appendChild(p); - s.appendChild(i); - s.appendChild(d); - n.appendChild(s); - n.appendChild(a); - container.appendChild(n); - }; - - const updateProgress = (id, filename, percent) => { - let r = document.getElementById("tel-downloader-progress-" + id); - if (!r) return; - - const fname = filename || r.querySelector("p.filename")?.innerText || "video"; - r.querySelector("p.filename").innerText = fname; - - const prog = r.querySelector("div.progress"); - if (!prog) return; - - const txt = prog.querySelector("p"); - const bar = prog.querySelector("div"); - if (txt) txt.innerText = (percent || 0) + "%"; - if (bar) bar.style.width = (percent || 0) + "%"; - }; - - const completeProgress = (id, clientId, filename, version, locale) => { - let n = document.getElementById("tel-downloader-progress-" + id); - if (!n) return; - - let prog = n.querySelector("div.progress"); - if (prog) { - let txt = prog.querySelector("p"); - if (txt) txt.innerText = "Completed"; - let bar = prog.querySelector("div"); - if (bar) { - bar.style.backgroundColor = "#40DCA5"; - bar.style.width = "100%"; - } - } - - document.dispatchEvent(new CustomEvent('telDownloaderCompleted', { - detail: { - extensionId: extensionId, - action: "videoProgressCompleted", - videoId: id, - clientId: clientId, - name: filename, - version: version, - locale: locale - } - })); - }; - - const AbortProgress = id => { - let t = document.getElementById("tel-downloader-progress-" + id); - if (!t) return; - - let prog = t.querySelector("div.progress"); - let txt = prog?.querySelector("p"); - if (txt) { - txt.innerText = "Download failed. Try again"; - txt.style.fontSize = "12px"; - } - let bar = prog?.querySelector("div"); - if (bar) { - bar.style.backgroundColor = "#D16666"; - bar.style.width = "100%"; - } - }; - - // Filename helper - function sanitizeFilenamePreserveFriendly(name) { - if (!name) return 'video'; - return name.toString().replace(/[\/\\\?\%\*\:\|\"\<\>]/g, "_").trim(); - } - - // Download-Funktion mit automatischem Dateinamen - OPTIMIERT FÜR GESCHWINDIGKEIT - function tel_download_video(e, t, l, r, o) { - if (!e) { - console.error('No url provided to tel_download_video'); - return; - } - - let baseName = sanitizeFilenamePreserveFriendly(t || 'video'); - const hasExt = /\.\w{1,5}$/.test(baseName); - const id = (Math.random() + 1).toString(36).substring(2, 10) + "_" + Date.now().toString(); - - const finishDownload = (blob, finalName) => { - try { - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - document.body.appendChild(link); - link.href = url; - link.download = finalName; - link.click(); - document.body.removeChild(link); - setTimeout(() => URL.revokeObjectURL(url), 2000); - } catch (err) { - console.error('Error finishing download', err); - AbortProgress(id); - } - }; - - createProgressBar(id, baseName + (hasExt ? '' : '.mp4')); - updateProgress(id, baseName + (hasExt ? '' : '.mp4'), 0); - - // GESCHWINDIGKEITS-OPTIMIERUNG: Größere Chunks + parallele Downloads - const chunkSize = 10 * 1024 * 1024; // 10MB chunks (statt 1MB) - const maxParallel = 3; // 3 parallele Downloads - let chunks = []; - let totalSize = null; - let detectedExt = "mp4"; - let activeDownloads = 0; - let downloadComplete = false; - - // Zuerst: Hole die Gesamtgröße - fetch(e, { - method: "HEAD", - credentials: "include" - }).then(res => { - const contentLength = res.headers.get("Content-Length"); - const contentType = res.headers.get("Content-Type") || ""; - const mime = contentType.split(";")[0] || ""; - if (mime.startsWith("video/")) { - detectedExt = mime.split("/")[1] || detectedExt; - } - - if (contentLength) { - totalSize = parseInt(contentLength, 10); - console.log('Total file size:', (totalSize / 1024 / 1024).toFixed(2), 'MB'); - - // Berechne wie viele Chunks wir brauchen - const numChunks = Math.ceil(totalSize / chunkSize); - chunks = new Array(numChunks).fill(null); - - // Starte parallele Downloads - for (let i = 0; i < Math.min(maxParallel, numChunks); i++) { - downloadChunk(i); - } - } else { - // Fallback: sequentieller Download ohne Größeninfo - sequentialDownload(); - } - }).catch(err => { - console.warn('HEAD request failed, using sequential download'); - sequentialDownload(); - }); - - function downloadChunk(chunkIndex) { - if (downloadComplete || chunkIndex >= chunks.length) return; - - activeDownloads++; - const start = chunkIndex * chunkSize; - const end = Math.min(start + chunkSize - 1, totalSize - 1); - - fetch(e, { - method: "GET", - headers: { Range: `bytes=${start}-${end}` }, - credentials: "include" - }).then(res => { - if (![200, 206].includes(res.status)) { - throw Error("Non 200/206 response: " + res.status); - } - return res.blob(); - }).then(blob => { - chunks[chunkIndex] = blob; - activeDownloads--; - - // Update Progress - const downloaded = chunks.filter(c => c !== null).length; - const percent = Math.round(100 * downloaded / chunks.length); - updateProgress(id, baseName + (hasExt ? "" : "." + detectedExt), percent); - - // Check ob fertig - if (chunks.every(c => c !== null)) { - downloadComplete = true; - const finalName = hasExt ? baseName : (baseName + "." + detectedExt); - const merged = new Blob(chunks, { type: "video/" + detectedExt }); - finishDownload(merged, finalName); - completeProgress(id, r, finalName, l, o); - } else { - // Starte nächsten Chunk - const nextChunk = chunks.findIndex(c => c === null); - if (nextChunk !== -1) { - downloadChunk(nextChunk); - } - } - }).catch(err => { - console.error('Chunk download error:', err); - activeDownloads--; - // Retry nach kurzer Pause - setTimeout(() => downloadChunk(chunkIndex), 500); - }); - } - - function sequentialDownload() { - // Fallback: Sequentieller Download für Server ohne Range-Support - let blobs = [], s = 0; - - const rangedFetch = () => { - fetch(e, { - method: "GET", - headers: { Range: `bytes=${s}-` }, - credentials: "include" - }).then(res2 => { - if (![200, 206].includes(res2.status)) { - throw Error("Non 200/206 response: " + res2.status); - } - - const contentType2 = res2.headers.get("Content-Type") || ""; - const mime2 = contentType2.split(";")[0] || ""; - if (mime2.startsWith("video/")) { - detectedExt = mime2.split("/")[1] || detectedExt; - } - - const cr = res2.headers.get("Content-Range"); - if (cr) { - const match = cr.match(/^bytes (\d+)-(\d+)\/(\d+)$/); - if (match) { - const start = parseInt(match[1], 10); - const end = parseInt(match[2], 10); - const total = parseInt(match[3], 10); - - s = end + 1; - totalSize = total; - - const percent = Math.min(100, Math.round(100 * s / totalSize)); - updateProgress(id, baseName + (hasExt ? "" : "." + detectedExt), percent); - } - } else if (res2.status === 200) { - const contentLength = res2.headers.get("Content-Length"); - if (contentLength) { - totalSize = parseInt(contentLength, 10); - } - } - - return res2.blob(); - }).then(chunkBlob => { - blobs.push(chunkBlob); - - if (totalSize && s >= totalSize) { - const finalName = hasExt ? baseName : (baseName + "." + detectedExt); - const merged = new Blob(blobs, { type: "video/" + detectedExt }); - finishDownload(merged, finalName); - completeProgress(id, r, finalName, l, o); - } else { - setTimeout(rangedFetch, 10); - } - }).catch(err2 => { - console.error('Sequential download error', err2); - AbortProgress(id); - }); - }; - - rangedFetch(); - } - } - - const tel_download_image = (src, suggestedName) => { - try { - const safe = sanitizeFilenamePreserveFriendly(suggestedName || 'image') + "_" + Date.now() + ".jpg"; - const a = document.createElement("a"); - document.body.appendChild(a); - a.href = src; - a.download = safe; - a.click(); - document.body.removeChild(a); - } catch (err) { - console.error('tel_download_image error', err); - } - }; - - window.telDownloader = { - startVideo: tel_download_video, - startImage: tel_download_image - }; +(function () { + const scriptElement = document.currentScript; + const extensionId = scriptElement ? scriptElement.getAttribute("data-extension-id") : null; + + console.log('inject.js running, extensionId=', extensionId); + + // Empfang von "downloadRequested" (kommt vom content script) + document.addEventListener("downloadRequested", function(e) { + try { + let t = e.detail.url; + let l = e.detail.name; + let r = e.detail.version; + let o = e.detail.clientId; + let n = e.detail.locale; + tel_download_video(t, l, r, o, n); + } catch (err) { + console.error('Error in downloadRequested handler', err); + } + }); + + document.addEventListener("imageDownloadRequested", function(e) { + try { + let t = e.detail.url; + let l = e.detail.name; + tel_download_image(t, l); + } catch (err) { + console.error('Error in imageDownloadRequested handler', err); + } + }); + + document.addEventListener("audioDownloadRequested", function(e) { + try { + let t = e.detail.url; + let l = e.detail.name; + tel_download_audio(t, l); + } catch (err) { + console.error('Error in audioDownloadRequested handler', err); + } + }); + + document.addEventListener("documentDownloadRequested", function(e) { + try { + let t = e.detail.url; + let l = e.detail.name; + tel_download_document(t, l); + } catch (err) { + console.error('Error in documentDownloadRequested handler', err); + } + }); + + // Hilfsfunktion: progress-bar-container + function ensureProgressContainer() { + let container = document.getElementById("tel-downloader-progress-bar-container"); + if (!container) { + container = document.createElement("div"); + container.id = "tel-downloader-progress-bar-container"; + container.style.position = "fixed"; + container.style.bottom = "0"; + container.style.right = "0"; + container.style.display = "flex"; + container.style.flexDirection = "column"; + container.style.gap = "0.4rem"; + container.style.padding = "0.4rem"; + container.style.zIndex = "1600"; + document.body.appendChild(container); + } + return container; + } + + const createProgressBar = (id, filename) => { + const dark = document.querySelector("html")?.classList?.contains("night") || + document.querySelector("html")?.classList?.contains("theme-dark"); + const container = ensureProgressContainer(); + container.style.zIndex = location.pathname.startsWith("/k/") ? "4" : "1600"; + + const n = document.createElement("div"); + n.id = "tel-downloader-progress-" + id; + n.style.width = "20rem"; + n.style.marginTop = "0.4rem"; + n.style.padding = "0.6rem"; + n.style.backgroundColor = dark ? "rgba(0,0,0,0.6)" : "rgba(0,0,0,0.3)"; + n.style.borderRadius = "8px"; + n.style.boxShadow = "0 4px 12px rgba(0,0,0,0.2)"; + n.style.color = "white"; + n.style.fontFamily = "sans-serif"; + + const s = document.createElement("div"); + s.style.display = "flex"; + s.style.justifyContent = "space-between"; + s.style.alignItems = "center"; + + const i = document.createElement("p"); + i.className = "filename"; + i.style.margin = 0; + i.style.color = "white"; + i.style.fontSize = "0.9rem"; + i.style.overflow = "hidden"; + i.style.textOverflow = "ellipsis"; + i.style.whiteSpace = "nowrap"; + i.innerText = filename || "video"; + + const d = document.createElement("div"); + d.style.cursor = "pointer"; + d.style.fontSize = "1.2rem"; + d.style.color = dark ? "#8a8a8a" : "white"; + d.innerHTML = "×"; + d.onclick = function() { + if (n.parentElement) n.parentElement.removeChild(n); + }; + + const a = document.createElement("div"); + a.className = "progress"; + a.style.backgroundColor = "#e2e2e2"; + a.style.position = "relative"; + a.style.width = "100%"; + a.style.height = "1.6rem"; + a.style.borderRadius = "2rem"; + a.style.overflow = "hidden"; + a.style.marginTop = "0.6rem"; + + const c = document.createElement("p"); + c.style.position = "absolute"; + c.style.zIndex = 5; + c.style.left = "50%"; + c.style.top = "50%"; + c.style.transform = "translate(-50%, -50%)"; + c.style.margin = 0; + c.style.color = "black"; + c.style.fontSize = "0.85rem"; + c.innerText = "0%"; + + const p = document.createElement("div"); + p.style.position = "absolute"; + p.style.height = "100%"; + p.style.width = "0%"; + p.style.backgroundColor = "#3390ec"; + p.style.left = "0"; + p.style.top = "0"; + + a.appendChild(c); + a.appendChild(p); + s.appendChild(i); + s.appendChild(d); + n.appendChild(s); + n.appendChild(a); + container.appendChild(n); + }; + + const updateProgress = (id, filename, percent) => { + let r = document.getElementById("tel-downloader-progress-" + id); + if (!r) return; + + const fname = filename || r.querySelector("p.filename")?.innerText || "video"; + r.querySelector("p.filename").innerText = fname; + + const prog = r.querySelector("div.progress"); + if (!prog) return; + + const txt = prog.querySelector("p"); + const bar = prog.querySelector("div"); + if (txt) txt.innerText = (percent || 0) + "%"; + if (bar) bar.style.width = (percent || 0) + "%"; + }; + + const completeProgress = (id, clientId, filename, version, locale) => { + let n = document.getElementById("tel-downloader-progress-" + id); + if (!n) return; + + let prog = n.querySelector("div.progress"); + if (prog) { + let txt = prog.querySelector("p"); + if (txt) txt.innerText = "Completed"; + let bar = prog.querySelector("div"); + if (bar) { + bar.style.backgroundColor = "#40DCA5"; + bar.style.width = "100%"; + } + } + + document.dispatchEvent(new CustomEvent('telDownloaderCompleted', { + detail: { + extensionId: extensionId, + action: "videoProgressCompleted", + videoId: id, + clientId: clientId, + name: filename, + version: version, + locale: locale + } + })); + }; + + const AbortProgress = id => { + let t = document.getElementById("tel-downloader-progress-" + id); + if (!t) return; + + let prog = t.querySelector("div.progress"); + let txt = prog?.querySelector("p"); + if (txt) { + txt.innerText = "Download failed. Try again"; + txt.style.fontSize = "12px"; + } + let bar = prog?.querySelector("div"); + if (bar) { + bar.style.backgroundColor = "#D16666"; + bar.style.width = "100%"; + } + }; + + // Filename helper + function sanitizeFilenamePreserveFriendly(name) { + if (!name) return 'video'; + return name.toString().replace(/[\/\\\?\%\*\:\|\"\<\>]/g, "_").trim(); + } + + // Download-Funktion mit automatischem Dateinamen - OPTIMIERT FÜR GESCHWINDIGKEIT + function tel_download_video(e, t, l, r, o) { + if (!e) { + console.error('No url provided to tel_download_video'); + return; + } + + let baseName = sanitizeFilenamePreserveFriendly(t || 'video'); + const hasExt = /\.\w{1,5}$/.test(baseName); + const id = (Math.random() + 1).toString(36).substring(2, 10) + "_" + Date.now().toString(); + + const finishDownload = (blob, finalName) => { + try { + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + document.body.appendChild(link); + link.href = url; + link.download = finalName; + link.click(); + document.body.removeChild(link); + setTimeout(() => URL.revokeObjectURL(url), 2000); + } catch (err) { + console.error('Error finishing download', err); + AbortProgress(id); + } + }; + + createProgressBar(id, baseName + (hasExt ? '' : '.mp4')); + updateProgress(id, baseName + (hasExt ? '' : '.mp4'), 0); + + // GESCHWINDIGKEITS-OPTIMIERUNG: Größere Chunks + parallele Downloads + const chunkSize = 10 * 1024 * 1024; // 10MB chunks (statt 1MB) + const maxParallel = 3; // 3 parallele Downloads + let chunks = []; + let totalSize = null; + let detectedExt = "mp4"; + let activeDownloads = 0; + let downloadComplete = false; + + // Zuerst: Hole die Gesamtgröße + fetch(e, { + method: "HEAD", + credentials: "include" + }).then(res => { + const contentLength = res.headers.get("Content-Length"); + const contentType = res.headers.get("Content-Type") || ""; + const mime = contentType.split(";")[0] || ""; + if (mime.startsWith("video/")) { + detectedExt = mime.split("/")[1] || detectedExt; + } + + if (contentLength) { + totalSize = parseInt(contentLength, 10); + console.log('Total file size:', (totalSize / 1024 / 1024).toFixed(2), 'MB'); + + // Berechne wie viele Chunks wir brauchen + const numChunks = Math.ceil(totalSize / chunkSize); + chunks = new Array(numChunks).fill(null); + + // Starte parallele Downloads + for (let i = 0; i < Math.min(maxParallel, numChunks); i++) { + downloadChunk(i); + } + } else { + // Fallback: sequentieller Download ohne Größeninfo + sequentialDownload(); + } + }).catch(err => { + console.warn('HEAD request failed, using sequential download'); + sequentialDownload(); + }); + + function downloadChunk(chunkIndex) { + if (downloadComplete || chunkIndex >= chunks.length) return; + + activeDownloads++; + const start = chunkIndex * chunkSize; + const end = Math.min(start + chunkSize - 1, totalSize - 1); + + fetch(e, { + method: "GET", + headers: { Range: `bytes=${start}-${end}` }, + credentials: "include" + }).then(res => { + if (![200, 206].includes(res.status)) { + throw Error("Non 200/206 response: " + res.status); + } + return res.blob(); + }).then(blob => { + chunks[chunkIndex] = blob; + activeDownloads--; + + // Update Progress + const downloaded = chunks.filter(c => c !== null).length; + const percent = Math.round(100 * downloaded / chunks.length); + updateProgress(id, baseName + (hasExt ? "" : "." + detectedExt), percent); + + // Check ob fertig + if (chunks.every(c => c !== null)) { + downloadComplete = true; + const finalName = hasExt ? baseName : (baseName + "." + detectedExt); + const merged = new Blob(chunks, { type: "video/" + detectedExt }); + finishDownload(merged, finalName); + completeProgress(id, r, finalName, l, o); + } else { + // Starte nächsten Chunk + const nextChunk = chunks.findIndex(c => c === null); + if (nextChunk !== -1) { + downloadChunk(nextChunk); + } + } + }).catch(err => { + console.error('Chunk download error:', err); + activeDownloads--; + // Retry nach kurzer Pause + setTimeout(() => downloadChunk(chunkIndex), 500); + }); + } + + function sequentialDownload() { + // Fallback: Sequentieller Download für Server ohne Range-Support + let blobs = [], s = 0; + + const rangedFetch = () => { + fetch(e, { + method: "GET", + headers: { Range: `bytes=${s}-` }, + credentials: "include" + }).then(res2 => { + if (![200, 206].includes(res2.status)) { + throw Error("Non 200/206 response: " + res2.status); + } + + const contentType2 = res2.headers.get("Content-Type") || ""; + const mime2 = contentType2.split(";")[0] || ""; + if (mime2.startsWith("video/")) { + detectedExt = mime2.split("/")[1] || detectedExt; + } + + const cr = res2.headers.get("Content-Range"); + if (cr) { + const match = cr.match(/^bytes (\d+)-(\d+)\/(\d+)$/); + if (match) { + const start = parseInt(match[1], 10); + const end = parseInt(match[2], 10); + const total = parseInt(match[3], 10); + + s = end + 1; + totalSize = total; + + const percent = Math.min(100, Math.round(100 * s / totalSize)); + updateProgress(id, baseName + (hasExt ? "" : "." + detectedExt), percent); + } + } else if (res2.status === 200) { + const contentLength = res2.headers.get("Content-Length"); + if (contentLength) { + totalSize = parseInt(contentLength, 10); + } + } + + return res2.blob(); + }).then(chunkBlob => { + blobs.push(chunkBlob); + + if (totalSize && s >= totalSize) { + const finalName = hasExt ? baseName : (baseName + "." + detectedExt); + const merged = new Blob(blobs, { type: "video/" + detectedExt }); + finishDownload(merged, finalName); + completeProgress(id, r, finalName, l, o); + } else { + setTimeout(rangedFetch, 10); + } + }).catch(err2 => { + console.error('Sequential download error', err2); + AbortProgress(id); + }); + }; + + rangedFetch(); + } + } + + const tel_download_image = (src, suggestedName) => { + try { + console.log('=== IMAGE DOWNLOAD START ==='); + console.log('Image URL:', src); + console.log('Suggested name:', suggestedName); + + const baseName = sanitizeFilenamePreserveFriendly(suggestedName || 'telegram_image'); + const hasExt = /\.(jpg|jpeg|png|gif|webp)$/i.test(baseName); + const finalName = hasExt ? baseName : (baseName + '.jpg'); + + fetch(src, { + method: "GET", + credentials: "include" + }).then(res => { + if (!res.ok) throw new Error("Fetch failed: " + res.status); + + // Detect image type + const contentType = res.headers.get("Content-Type") || ""; + let ext = "jpg"; + if (contentType.includes("png")) ext = "png"; + else if (contentType.includes("gif")) ext = "gif"; + else if (contentType.includes("webp")) ext = "webp"; + + const finalNameWithExt = hasExt ? baseName : (baseName + '.' + ext); + + return res.blob().then(blob => { + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + document.body.appendChild(a); + a.href = url; + a.download = finalNameWithExt; + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 2000); + console.log('Image downloaded:', finalNameWithExt); + }); + }).catch(err => { + console.error('Image download error:', err); + alert('Bild-Download fehlgeschlagen. Versuche es erneut.'); + }); + } catch (err) { + console.error('tel_download_image error', err); + } + }; + + const tel_download_audio = (src, suggestedName) => { + try { + console.log('=== AUDIO DOWNLOAD START ==='); + console.log('Audio URL:', src); + console.log('Suggested name:', suggestedName); + + const baseName = sanitizeFilenamePreserveFriendly(suggestedName || 'telegram_audio'); + const hasExt = /\.(mp3|ogg|wav|m4a|opus)$/i.test(baseName); + + const id = (Math.random() + 1).toString(36).substring(2, 10) + "_" + Date.now().toString(); + + createProgressBar(id, baseName + (hasExt ? '' : '.mp3')); + updateProgress(id, baseName + (hasExt ? '' : '.mp3'), 0); + + fetch(src, { + method: "GET", + credentials: "include" + }).then(res => { + if (!res.ok) throw new Error("Fetch failed: " + res.status); + + const contentType = res.headers.get("Content-Type") || ""; + let ext = "mp3"; + if (contentType.includes("ogg")) ext = "ogg"; + else if (contentType.includes("wav")) ext = "wav"; + else if (contentType.includes("m4a")) ext = "m4a"; + else if (contentType.includes("opus")) ext = "opus"; + + const finalName = hasExt ? baseName : (baseName + '.' + ext); + + return res.blob().then(blob => { + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + document.body.appendChild(a); + a.href = url; + a.download = finalName; + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 2000); + + updateProgress(id, finalName, 100); + completeProgress(id, null, finalName, null, null); + console.log('Audio downloaded:', finalName); + }); + }).catch(err => { + console.error('Audio download error:', err); + AbortProgress(id); + }); + } catch (err) { + console.error('tel_download_audio error', err); + } + }; + + const tel_download_document = (src, suggestedName) => { + try { + console.log('=== DOCUMENT DOWNLOAD START ==='); + console.log('Document URL:', src); + console.log('Suggested name:', suggestedName); + + const baseName = sanitizeFilenamePreserveFriendly(suggestedName || 'telegram_file'); + const hasExt = /\.\w{2,5}$/i.test(baseName); + + const id = (Math.random() + 1).toString(36).substring(2, 10) + "_" + Date.now().toString(); + + createProgressBar(id, baseName); + updateProgress(id, baseName, 0); + + fetch(src, { + method: "GET", + credentials: "include" + }).then(res => { + if (!res.ok) throw new Error("Fetch failed: " + res.status); + + const contentType = res.headers.get("Content-Type") || ""; + let ext = ""; + + // Erkenne Extension aus Content-Type + if (contentType.includes("zip")) ext = "zip"; + else if (contentType.includes("rar")) ext = "rar"; + else if (contentType.includes("pdf")) ext = "pdf"; + else if (contentType.includes("msword")) ext = "doc"; + else if (contentType.includes("spreadsheet")) ext = "xlsx"; + else if (contentType.includes("audio/mpeg")) ext = "mp3"; + else if (contentType.includes("audio/")) ext = "mp3"; + + const finalName = hasExt ? baseName : (baseName + (ext ? '.' + ext : '')); + + return res.blob().then(blob => { + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + document.body.appendChild(a); + a.href = url; + a.download = finalName; + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 2000); + + updateProgress(id, finalName, 100); + completeProgress(id, null, finalName, null, null); + console.log('Document downloaded:', finalName); + }); + }).catch(err => { + console.error('Document download error:', err); + AbortProgress(id); + }); + } catch (err) { + console.error('tel_download_document error', err); + } + }; + + window.telDownloader = { + startVideo: tel_download_video, + startImage: tel_download_image, + startAudio: tel_download_audio, + startDocument: tel_download_document + }; })(); \ No newline at end of file