From c07d68fab1a2c9815825979d2643f3b0b0c505a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Thu, 28 May 2026 07:17:55 +0300 Subject: [PATCH] feat: implement automatic yt-dlp and ffmpeg dependency management and media downloading logic --- main.js | 19 +++++++++++++++---- preload.js | 5 +++-- src/index.html | 19 ++++++++++++++++--- src/renderer.js | 41 ++++++++++++++++++++++++++++++++++++++-- utils/downloader.js | 46 +++++++++++++++++++++++++++++++++------------ 5 files changed, 107 insertions(+), 23 deletions(-) diff --git a/main.js b/main.js index 68df403..b5952d1 100644 --- a/main.js +++ b/main.js @@ -70,9 +70,9 @@ ipcMain.handle('check-dependencies', async () => { }); // IPC Handler: Get Video Details -ipcMain.handle('get-video-info', async (event, url) => { +ipcMain.handle('get-video-info', async (event, { url, browser }) => { try { - const info = await downloader.getVideoInfo(url); + const info = await downloader.getVideoInfo(url, browser); return { success: true, info }; } catch (error) { return { success: false, error: error.message }; @@ -80,13 +80,24 @@ ipcMain.handle('get-video-info', async (event, url) => { }); // IPC Handler: Download Media -ipcMain.handle('download-media', async (event, { url, format, quality }) => { +ipcMain.handle('download-media', async (event, { url, format, quality, browser }) => { try { - await downloader.downloadMedia(url, format, quality, selectedOutputDir, (percent, status) => { + const stats = await downloader.downloadMedia(url, format, quality, selectedOutputDir, browser, (percent, status) => { if (mainWindow) { mainWindow.webContents.send('download-progress', { percent, status }); } }); + return { success: true, stats }; + } catch (error) { + return { success: false, error: error.message }; + } +}); + +// IPC Handler: Open Path in File Explorer +ipcMain.handle('open-path', async (event, folderPath) => { + try { + const { shell } = require('electron'); + await shell.openPath(folderPath); return { success: true }; } catch (error) { return { success: false, error: error.message }; diff --git a/preload.js b/preload.js index 737c22c..a4496a1 100644 --- a/preload.js +++ b/preload.js @@ -3,11 +3,12 @@ const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('api', { checkDependencies: () => ipcRenderer.invoke('check-dependencies'), onDependencyStatus: (callback) => ipcRenderer.on('dependency-status', (_, data) => callback(data)), - getVideoInfo: (url) => ipcRenderer.invoke('get-video-info', url), - downloadMedia: (url, format, quality) => ipcRenderer.invoke('download-media', { url, format, quality }), + getVideoInfo: (url, browser) => ipcRenderer.invoke('get-video-info', { url, browser }), + downloadMedia: (url, format, quality, browser) => ipcRenderer.invoke('download-media', { url, format, quality, browser }), onDownloadProgress: (callback) => ipcRenderer.on('download-progress', (_, data) => callback(data)), selectDirectory: () => ipcRenderer.invoke('select-directory'), getDownloadsFolder: () => ipcRenderer.invoke('get-downloads-folder'), + openPath: (path) => ipcRenderer.invoke('open-path', path), minimizeWindow: () => ipcRenderer.send('window-minimize'), closeWindow: () => ipcRenderer.send('window-close') }); diff --git a/src/index.html b/src/index.html index d0fa45e..8219765 100644 --- a/src/index.html +++ b/src/index.html @@ -3,11 +3,13 @@ - VibeDownloader - Premium YouTube & MP3 Downloader + VibeDownloader - Premium Social Media & MP3 Downloader + + @@ -64,12 +66,12 @@

VibeDownloader

-

YouTube videolarını saniyeler içinde yüksek kaliteli MP4 veya MP3 olarak indirin

+

YouTube, X/Twitter, Instagram, TikTok ve tüm popüler sitelerden video/ses indirin

- +
+
+ + + +
diff --git a/src/renderer.js b/src/renderer.js index 6f09434..5b00151 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -9,6 +9,8 @@ const urlInput = document.getElementById('url-input'); const analyzeBtn = document.getElementById('analyze-btn'); const outputPath = document.getElementById('output-path'); const changeFolderBtn = document.getElementById('change-folder-btn'); +const useCookiesCheckbox = document.getElementById('use-cookies'); +const cookieBrowserSelect = document.getElementById('cookie-browser'); const errorCard = document.getElementById('error-card'); const errorMessage = document.getElementById('error-message'); @@ -167,7 +169,8 @@ analyzeBtn.addEventListener('click', async () => { analyzeIcon.style.display = 'inline-block'; try { - const result = await window.api.getVideoInfo(url); + const browser = useCookiesCheckbox.checked ? cookieBrowserSelect.value : null; + const result = await window.api.getVideoInfo(url, browser); if (result.success) { analyzedUrl = url; currentVideoData = result.info; @@ -245,7 +248,8 @@ downloadBtn.addEventListener('click', async () => { }); try { - const result = await window.api.downloadMedia(analyzedUrl, currentFormat, currentQuality); + const browser = useCookiesCheckbox.checked ? cookieBrowserSelect.value : null; + const result = await window.api.downloadMedia(analyzedUrl, currentFormat, currentQuality, browser); if (result.success) { downloadStatus.textContent = 'Tamamlandı!'; downloadPercent.textContent = '100%'; @@ -263,6 +267,39 @@ downloadBtn.addEventListener('click', async () => { // Success gradient transitions downloadProgressBar.style.background = 'linear-gradient(90deg, #00ff87 0%, #60efff 100%)'; downloadProgressBar.style.boxShadow = '0 0 15px rgba(0, 255, 135, 0.6)'; + + // Trigger premium SweetAlert2 modal + Swal.fire({ + title: 'İndirme Tamamlandı! 🎉', + html: ` +
+

Dosya Adı: ${currentVideoData.title}

+

Platform / Paylaşan: ${currentVideoData.uploader || 'Bilinmiyor'}

+

Dosya Boyutu: ${result.stats.size}

+

İndirme Süresi: ${result.stats.duration}

+

Format ve Kalite: ${currentFormat} (${currentQuality === '320' || currentQuality === '1080' ? 'En Yüksek' : currentQuality})

+

Kaydedilen Klasör:
${result.stats.outputDir}

+
+ `, + icon: 'success', + background: 'rgba(23, 20, 38, 0.95)', + color: '#fff', + confirmButtonText: 'Klasörü Aç 📂', + showCancelButton: true, + cancelButtonText: 'Yeni İndirme 🔄', + confirmButtonColor: '#7000ff', + cancelButtonColor: 'rgba(255, 255, 255, 0.1)', + backdrop: 'rgba(0, 0, 0, 0.6)' + }).then((swalResult) => { + if (swalResult.isConfirmed) { + window.api.openPath(result.stats.outputDir); + } else { + // Go back to step 1 + backBtn.click(); + urlInput.value = ''; + downloadProgressPanel.style.display = 'none'; + } + }); } else { showError(result.error); downloadProgressPanel.style.display = 'none'; diff --git a/utils/downloader.js b/utils/downloader.js index 694a68f..6463355 100644 --- a/utils/downloader.js +++ b/utils/downloader.js @@ -169,12 +169,17 @@ async function checkDependencies(onStatusUpdate) { } // Get video metadata (title, thumbnail, duration, etc.) -function getVideoInfo(url) { +function getVideoInfo(url, browser) { console.log(`[DEBUG] Fetching info for URL: ${url}`); return new Promise((resolve, reject) => { - // Adding '--no-playlist' prevents yt-dlp from crawling the entire playlist (which hangs/takes extremely long) - console.log(`[DEBUG] Spawning process: ${YTDLP_PATH} --no-playlist -j ${url}`); - const proc = spawn(YTDLP_PATH, ['--no-playlist', '-j', url]); + let args = ['--no-playlist', '-j']; + if (browser) { + args.push('--cookies-from-browser', browser); + } + args.push(url); + + console.log(`[DEBUG] Spawning process: ${YTDLP_PATH} ${args.join(' ')}`); + const proc = spawn(YTDLP_PATH, args); let stdout = ''; let stderr = ''; @@ -206,11 +211,13 @@ function getVideoInfo(url) { } // Download video or convert to MP3 -function downloadMedia(url, format, quality, outputDir, onProgress) { - console.log(`[DEBUG] Media download requested. Format: ${format}, Quality: ${quality}, Target Dir: ${outputDir}`); +function downloadMedia(url, format, quality, outputDir, browser, onProgress) { + console.log(`[DEBUG] Media download requested. Format: ${format}, Quality: ${quality}, Target Dir: ${outputDir}, Browser Cookies: ${browser || 'none'}`); return new Promise((resolve, reject) => { let args = []; const outputTemplate = path.join(outputDir, '%(title)s.%(ext)s'); + const startTime = Date.now(); + let totalSize = 'Bilinmiyor'; if (format === 'mp3') { const q = quality ? `${quality}K` : '320K'; // '320K', '256K', '192K' @@ -220,25 +227,35 @@ function downloadMedia(url, format, quality, outputDir, onProgress) { '-x', '--audio-format', 'mp3', '--audio-quality', q, - '-o', outputTemplate, - url + '-o', outputTemplate ]; } else { const res = quality || '1080'; // '1080', '720', '480' args = [ '--no-playlist', '--ffmpeg-location', FFMPEG_PATH, - '-f', `bv*[height<=${res}][ext=mp4]+ba[ext=m4a]/b[height<=${res}][ext=mp4] / bv*[height<=${res}]+ba/b[height<=${res}]`, - '-o', outputTemplate, - url + '-f', `bv*[height<=${res}][ext=mp4]+ba[ext=m4a]/b[height<=${res}][ext=mp4]/bv*[height<=${res}]+ba/b[height<=${res}]/bestvideo+bestaudio/best`, + '-o', outputTemplate ]; } + if (browser) { + args.push('--cookies-from-browser', browser); + } + args.push(url); + console.log(`[DEBUG] Spawning downloader process: ${YTDLP_PATH} ${args.join(' ')}`); const proc = spawn(YTDLP_PATH, args); proc.stdout.on('data', (data) => { const line = data.toString(); + + // Parse total size: e.g. [download] 1.2% of 8.37MiB at... + const sizeMatch = line.match(/of\s+(\d+(?:\.\d+)?\s*[a-zA-Z]+)/); + if (sizeMatch) { + totalSize = sizeMatch[1]; + } + const match = line.match(/\[download\]\s+(\d+\.\d+)%/); if (match && onProgress) { const percent = parseFloat(match[1]); @@ -256,7 +273,12 @@ function downloadMedia(url, format, quality, outputDir, onProgress) { proc.on('close', (code) => { console.log(`[DEBUG] Downloader process exited with code ${code}`); if (code === 0) { - resolve(); + const durationSec = ((Date.now() - startTime) / 1000).toFixed(1); + resolve({ + size: totalSize, + duration: `${durationSec} saniye`, + outputDir: outputDir + }); } else { reject(new Error(`Download exited with code ${code}`)); }