722 lines
24 KiB
JavaScript
722 lines
24 KiB
JavaScript
// DOM Elements
|
|
const dependencyOverlay = document.getElementById('dependency-overlay');
|
|
const depTitle = document.getElementById('dep-title');
|
|
const depMsg = document.getElementById('dep-msg');
|
|
const depProgress = document.getElementById('dep-progress');
|
|
const depPercent = document.getElementById('dep-percent');
|
|
|
|
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');
|
|
|
|
const step1 = document.getElementById('step-1');
|
|
const step2 = document.getElementById('step-2');
|
|
const backBtn = document.getElementById('back-btn');
|
|
const trackList = document.getElementById('track-list');
|
|
|
|
const formatMp3Btn = document.getElementById('format-mp3-btn');
|
|
const formatMp4Btn = document.getElementById('format-mp4-btn');
|
|
const qualityPillsContainer = document.getElementById('quality-pills');
|
|
|
|
const downloadBtn = document.getElementById('download-btn');
|
|
const downloadProgressPanel = document.getElementById('download-progress-panel');
|
|
const downloadStatus = document.getElementById('download-status');
|
|
const downloadPercent = document.getElementById('download-percent');
|
|
const downloadProgressBar = document.getElementById('download-progress-bar');
|
|
const downloadSubtext = document.getElementById('download-subtext');
|
|
|
|
const languageSelect = document.getElementById('language-select');
|
|
|
|
// State Variables
|
|
let analyzedUrl = '';
|
|
let currentFormat = 'mp3'; // 'mp3' or 'mp4'
|
|
let currentQuality = '320'; // Selected quality value
|
|
let currentVideoData = null;
|
|
|
|
// Playlist State Variables
|
|
const playlistHeader = document.getElementById('playlist-header');
|
|
const selectAllCheckbox = document.getElementById('select-all-checkbox');
|
|
const bulkMp3Btn = document.getElementById('bulk-mp3-btn');
|
|
const bulkMp4Btn = document.getElementById('bulk-mp4-btn');
|
|
let playlistTracks = [];
|
|
let focusedTrackId = null;
|
|
let isPlaylistMode = false;
|
|
|
|
// Localization State
|
|
let currentLang = 'tr';
|
|
let translations = {};
|
|
|
|
// 0. Translation Engine Functions
|
|
async function loadTranslations(lang) {
|
|
try {
|
|
const res = await fetch(`locales/${lang}.json`);
|
|
translations = await res.json();
|
|
currentLang = lang;
|
|
localStorage.setItem('vibe_lang', lang);
|
|
|
|
// Toggle direction attribute for Arabic (RTL)
|
|
if (lang === 'ar') {
|
|
document.documentElement.setAttribute('dir', 'rtl');
|
|
document.documentElement.setAttribute('lang', 'ar');
|
|
} else {
|
|
document.documentElement.removeAttribute('dir');
|
|
document.documentElement.setAttribute('lang', lang);
|
|
}
|
|
|
|
// Apply translations to data-i18n attributes
|
|
applyTranslations();
|
|
} catch (err) {
|
|
console.error('Error loading language file:', err);
|
|
}
|
|
}
|
|
|
|
function t(key, vars = {}) {
|
|
let text = translations[key] || key;
|
|
for (const [k, v] of Object.entries(vars)) {
|
|
text = text.replace(`%{${k}}`, v);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
function applyTranslations() {
|
|
// Elements with data-i18n attribute
|
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
const key = el.dataset.i18n;
|
|
el.textContent = t(key);
|
|
});
|
|
|
|
// Elements with data-i18n-placeholder attribute
|
|
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
|
const key = el.dataset.i18nPlaceholder;
|
|
el.placeholder = t(key);
|
|
});
|
|
|
|
// Elements with data-i18n-title attribute
|
|
document.querySelectorAll('[data-i18n-title]').forEach(el => {
|
|
const key = el.dataset.i18nTitle;
|
|
el.title = t(key);
|
|
});
|
|
|
|
// Dynamically redraw active quality pills based on current locale
|
|
renderQualityPills();
|
|
|
|
// If track is rendered, translate its status and duration labels
|
|
if (currentVideoData) {
|
|
renderTrackList(currentVideoData);
|
|
}
|
|
}
|
|
|
|
// Quality Configuration Generator (Dynamic for Localization)
|
|
function getQualityConfigs() {
|
|
return {
|
|
mp3: [
|
|
{ label: `320 kbps (${t('quality_best')})`, value: '320' },
|
|
{ label: '256 kbps', value: '256' },
|
|
{ label: '192 kbps', value: '192' }
|
|
],
|
|
mp4: [
|
|
{ label: '1080p (Full HD)', value: '1080' },
|
|
{ label: '720p (HD)', value: '720' },
|
|
{ label: `480p (${t('quality_standard')})`, value: '480' }
|
|
]
|
|
};
|
|
}
|
|
|
|
// 1. Initial Load & Dependency Verification
|
|
window.addEventListener('DOMContentLoaded', async () => {
|
|
// Setup Custom Titlebar Controls
|
|
document.getElementById('titlebar-minimize').addEventListener('click', () => {
|
|
window.api.minimizeWindow();
|
|
});
|
|
document.getElementById('titlebar-close').addEventListener('click', () => {
|
|
window.api.closeWindow();
|
|
});
|
|
|
|
// Setup Language Selector Change Handler
|
|
languageSelect.addEventListener('change', async (e) => {
|
|
await loadTranslations(e.target.value);
|
|
});
|
|
|
|
// Select All Checkbox Handler
|
|
selectAllCheckbox.addEventListener('change', () => {
|
|
const checked = selectAllCheckbox.checked;
|
|
playlistTracks.forEach(track => {
|
|
track.checked = checked;
|
|
});
|
|
renderTrackList(currentVideoData);
|
|
});
|
|
|
|
// Bulk MP3 Button Handler
|
|
bulkMp3Btn.addEventListener('click', () => {
|
|
playlistTracks.forEach(track => {
|
|
if (track.checked) {
|
|
track.format = 'mp3';
|
|
track.quality = '320';
|
|
}
|
|
});
|
|
const focusedTrack = playlistTracks.find(t => t.id === focusedTrackId);
|
|
if (focusedTrack) syncBottomPanelWithTrack(focusedTrack);
|
|
renderTrackList(currentVideoData);
|
|
});
|
|
|
|
// Bulk MP4 Button Handler
|
|
bulkMp4Btn.addEventListener('click', () => {
|
|
playlistTracks.forEach(track => {
|
|
if (track.checked) {
|
|
track.format = 'mp4';
|
|
track.quality = '1080';
|
|
}
|
|
});
|
|
const focusedTrack = playlistTracks.find(t => t.id === focusedTrackId);
|
|
if (focusedTrack) syncBottomPanelWithTrack(focusedTrack);
|
|
renderTrackList(currentVideoData);
|
|
});
|
|
|
|
// Get and load persisted or default language
|
|
const savedLang = localStorage.getItem('vibe_lang') || 'tr';
|
|
languageSelect.value = savedLang;
|
|
await loadTranslations(savedLang);
|
|
|
|
// Get and display default downloads folder path
|
|
const defaultPath = await window.api.getDownloadsFolder();
|
|
outputPath.textContent = defaultPath;
|
|
|
|
// Listen to dependency checking progress
|
|
window.api.onDependencyStatus(({ message, progress }) => {
|
|
let msg = message;
|
|
if (message.includes('Downloading yt-dlp')) {
|
|
const pctMatch = message.match(/(\d+)%/);
|
|
msg = pctMatch ? `${t('btn_downloading')} yt-dlp... ${pctMatch[1]}%` : `${t('btn_downloading')} yt-dlp...`;
|
|
} else if (message.includes('Downloading ffmpeg')) {
|
|
const pctMatch = message.match(/(\d+)%/);
|
|
msg = pctMatch ? `${t('btn_downloading')} ffmpeg... ${pctMatch[1]}%` : `${t('btn_downloading')} ffmpeg...`;
|
|
} else if (message.includes('Extracting ffmpeg')) {
|
|
msg = `${t('status_processing')} ffmpeg...`;
|
|
} else if (message.includes('Dependencies ready')) {
|
|
msg = t('status_completed');
|
|
}
|
|
depMsg.textContent = msg;
|
|
depProgress.style.width = `${progress}%`;
|
|
depPercent.textContent = `${progress}%`;
|
|
});
|
|
|
|
try {
|
|
const result = await window.api.checkDependencies();
|
|
if (result.success) {
|
|
// Transition out the loading overlay
|
|
dependencyOverlay.style.opacity = '0';
|
|
setTimeout(() => {
|
|
dependencyOverlay.classList.remove('active');
|
|
}, 400);
|
|
} else {
|
|
depTitle.textContent = t('badge_error');
|
|
depMsg.textContent = `${t('error_default')} ${result.error}`;
|
|
depPercent.textContent = t('badge_error');
|
|
}
|
|
} catch (err) {
|
|
depTitle.textContent = t('badge_error');
|
|
depMsg.textContent = err.message;
|
|
}
|
|
});
|
|
|
|
// 2. Output Path Folder Changer
|
|
changeFolderBtn.addEventListener('click', async () => {
|
|
const newPath = await window.api.selectDirectory();
|
|
outputPath.textContent = newPath;
|
|
});
|
|
|
|
// 3. Format Toggle Event Listeners
|
|
formatMp3Btn.addEventListener('click', () => {
|
|
if (currentFormat === 'mp3') return;
|
|
currentFormat = 'mp3';
|
|
formatMp3Btn.classList.add('active');
|
|
formatMp4Btn.classList.remove('active');
|
|
currentQuality = '320'; // Reset to default best quality for MP3
|
|
renderQualityPills();
|
|
applyBottomSettingsToSelected();
|
|
});
|
|
|
|
formatMp4Btn.addEventListener('click', () => {
|
|
if (currentFormat === 'mp4') return;
|
|
currentFormat = 'mp4';
|
|
formatMp4Btn.classList.add('active');
|
|
formatMp3Btn.classList.remove('active');
|
|
currentQuality = '1080'; // Reset to default best quality for MP4
|
|
renderQualityPills();
|
|
applyBottomSettingsToSelected();
|
|
});
|
|
|
|
// 4. Quality Pills Renderer
|
|
function renderQualityPills() {
|
|
qualityPillsContainer.innerHTML = '';
|
|
const configs = getQualityConfigs();
|
|
const options = configs[currentFormat];
|
|
|
|
options.forEach(opt => {
|
|
const pill = document.createElement('button');
|
|
pill.className = 'quality-pill';
|
|
if (opt.value === currentQuality) {
|
|
pill.classList.add('active');
|
|
}
|
|
pill.textContent = opt.label;
|
|
pill.dataset.value = opt.value;
|
|
|
|
pill.addEventListener('click', () => {
|
|
document.querySelectorAll('.quality-pill').forEach(p => p.classList.remove('active'));
|
|
pill.classList.add('active');
|
|
currentQuality = opt.value;
|
|
applyBottomSettingsToSelected();
|
|
});
|
|
|
|
qualityPillsContainer.appendChild(pill);
|
|
});
|
|
}
|
|
|
|
function syncBottomPanelWithTrack(track) {
|
|
currentFormat = track.format;
|
|
currentQuality = track.quality;
|
|
|
|
if (currentFormat === 'mp3') {
|
|
formatMp3Btn.classList.add('active');
|
|
formatMp4Btn.classList.remove('active');
|
|
} else {
|
|
formatMp4Btn.classList.add('active');
|
|
formatMp3Btn.classList.remove('active');
|
|
}
|
|
renderQualityPills();
|
|
}
|
|
|
|
function applyBottomSettingsToSelected() {
|
|
if (!currentVideoData) return;
|
|
const checkedTracks = playlistTracks.filter(t => t.checked);
|
|
if (checkedTracks.length > 0) {
|
|
checkedTracks.forEach(track => {
|
|
track.format = currentFormat;
|
|
track.quality = currentQuality;
|
|
});
|
|
} else if (focusedTrackId) {
|
|
const focusedTrack = playlistTracks.find(t => t.id === focusedTrackId);
|
|
if (focusedTrack) {
|
|
focusedTrack.format = currentFormat;
|
|
focusedTrack.quality = currentQuality;
|
|
}
|
|
}
|
|
renderTrackList(currentVideoData);
|
|
}
|
|
|
|
function updateSelectAllCheckboxState() {
|
|
const checkedCount = playlistTracks.filter(t => t.checked).length;
|
|
selectAllCheckbox.checked = checkedCount === playlistTracks.length;
|
|
}
|
|
|
|
// 5. Back Button (Step 2 to Step 1 Transition)
|
|
backBtn.addEventListener('click', () => {
|
|
// Reset any error card
|
|
errorCard.style.display = 'none';
|
|
// Fade out Step 2 and show Step 1
|
|
step2.classList.remove('active');
|
|
step1.classList.add('active');
|
|
currentVideoData = null;
|
|
playlistTracks = [];
|
|
focusedTrackId = null;
|
|
urlInput.focus();
|
|
});
|
|
|
|
// 6. Link Analysis Handler
|
|
analyzeBtn.addEventListener('click', async () => {
|
|
const url = urlInput.value.trim();
|
|
if (!url) return;
|
|
|
|
// Reset error displays
|
|
errorCard.style.display = 'none';
|
|
|
|
// Toggle button loading states
|
|
analyzeBtn.disabled = true;
|
|
const analyzeSpan = analyzeBtn.querySelector('span');
|
|
const analyzeIcon = analyzeBtn.querySelector('.btn-icon');
|
|
analyzeSpan.textContent = t('btn_analyzing');
|
|
analyzeIcon.style.display = 'inline-block';
|
|
|
|
try {
|
|
const browser = useCookiesCheckbox.checked ? cookieBrowserSelect.value : null;
|
|
const result = await window.api.getVideoInfo(url, browser);
|
|
if (result.success) {
|
|
analyzedUrl = url;
|
|
currentVideoData = result.info;
|
|
playlistTracks = [];
|
|
focusedTrackId = null;
|
|
|
|
// Render the fetched video details inside the scrollable list container
|
|
renderTrackList(result.info);
|
|
|
|
// Wizard transition: Step 1 -> Step 2
|
|
step1.classList.remove('active');
|
|
step2.classList.add('active');
|
|
} else {
|
|
showError(result.error);
|
|
}
|
|
} catch (err) {
|
|
showError(err.message);
|
|
} finally {
|
|
analyzeBtn.disabled = false;
|
|
analyzeSpan.textContent = t('btn_analyze');
|
|
analyzeIcon.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// 7. Render Track List Function
|
|
function renderTrackList(info) {
|
|
trackList.innerHTML = '';
|
|
|
|
if (info.isPlaylist) {
|
|
isPlaylistMode = true;
|
|
playlistHeader.style.display = 'flex';
|
|
} else {
|
|
isPlaylistMode = false;
|
|
playlistHeader.style.display = 'none';
|
|
}
|
|
|
|
if (info.entries) {
|
|
info.entries.forEach(entry => {
|
|
const existing = playlistTracks.find(t => t.id === entry.id);
|
|
if (!existing) {
|
|
playlistTracks.push({
|
|
id: entry.id,
|
|
title: entry.title,
|
|
thumbnail: entry.thumbnail,
|
|
duration: entry.duration,
|
|
uploader: entry.uploader,
|
|
url: entry.url,
|
|
format: currentFormat,
|
|
quality: currentQuality,
|
|
status: 'ready',
|
|
progress: 0,
|
|
checked: true
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
if (playlistTracks.length > 0 && !focusedTrackId) {
|
|
focusedTrackId = playlistTracks[0].id;
|
|
}
|
|
|
|
const focusedTrack = playlistTracks.find(t => t.id === focusedTrackId);
|
|
if (focusedTrack) {
|
|
syncBottomPanelWithTrack(focusedTrack);
|
|
}
|
|
|
|
playlistTracks.forEach(track => {
|
|
const itemEl = document.createElement('div');
|
|
itemEl.className = `track-item ${track.id === focusedTrackId ? 'focused' : ''}`;
|
|
itemEl.dataset.id = track.id;
|
|
|
|
const checkboxWrapper = document.createElement('div');
|
|
checkboxWrapper.className = 'track-checkbox-wrapper';
|
|
|
|
const checkbox = document.createElement('input');
|
|
checkbox.type = 'checkbox';
|
|
checkbox.className = 'track-checkbox';
|
|
checkbox.checked = track.checked;
|
|
|
|
checkbox.addEventListener('change', (e) => {
|
|
e.stopPropagation();
|
|
track.checked = checkbox.checked;
|
|
updateSelectAllCheckboxState();
|
|
});
|
|
|
|
checkboxWrapper.appendChild(checkbox);
|
|
itemEl.appendChild(checkboxWrapper);
|
|
|
|
const thumbEl = document.createElement('div');
|
|
thumbEl.className = 'track-thumb';
|
|
thumbEl.innerHTML = `
|
|
<svg class="track-thumb-fallback" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polygon points="23 7 16 12 23 17 23 7"></polygon>
|
|
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
|
|
</svg>
|
|
<img src="${track.thumbnail || ''}" onerror="this.style.display='none';" alt="Thumbnail">
|
|
`;
|
|
itemEl.appendChild(thumbEl);
|
|
|
|
const detailsEl = document.createElement('div');
|
|
detailsEl.className = 'track-details';
|
|
detailsEl.innerHTML = `
|
|
<h4>${track.title}</h4>
|
|
<p class="track-uploader">${track.uploader}</p>
|
|
<span class="track-duration">${t('duration_label')} ${track.duration || t('duration_unknown')}</span>
|
|
`;
|
|
itemEl.appendChild(detailsEl);
|
|
|
|
const formatSelectorEl = document.createElement('div');
|
|
formatSelectorEl.className = 'track-format-selector';
|
|
|
|
const formatBadgeBtn = document.createElement('button');
|
|
formatBadgeBtn.className = `format-badge-btn ${track.format}`;
|
|
formatBadgeBtn.textContent = track.format.toUpperCase();
|
|
|
|
formatBadgeBtn.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
track.format = track.format === 'mp3' ? 'mp4' : 'mp3';
|
|
track.quality = track.format === 'mp3' ? '320' : '1080';
|
|
|
|
if (track.id === focusedTrackId) {
|
|
syncBottomPanelWithTrack(track);
|
|
}
|
|
|
|
renderTrackList(currentVideoData);
|
|
});
|
|
|
|
formatSelectorEl.appendChild(formatBadgeBtn);
|
|
itemEl.appendChild(formatSelectorEl);
|
|
|
|
const statusEl = document.createElement('div');
|
|
statusEl.className = 'track-status';
|
|
|
|
let statusText = t('badge_ready');
|
|
let badgeClass = 'ready';
|
|
|
|
if (track.status === 'downloading') {
|
|
statusText = track.progress > 0 ? `${t('badge_downloading')} %${track.progress}` : t('badge_downloading');
|
|
badgeClass = 'downloading';
|
|
} else if (track.status === 'completed') {
|
|
statusText = t('badge_completed');
|
|
badgeClass = 'completed';
|
|
} else if (track.status === 'error') {
|
|
statusText = t('badge_error');
|
|
badgeClass = 'error';
|
|
} else if (track.status === 'waiting') {
|
|
statusText = t('status_connecting');
|
|
badgeClass = 'ready';
|
|
}
|
|
|
|
const statusBadge = document.createElement('span');
|
|
statusBadge.id = `badge-${track.id}`;
|
|
statusBadge.className = `status-badge ${badgeClass}`;
|
|
|
|
if (track.status === 'completed') {
|
|
statusBadge.style.borderColor = 'rgba(0, 255, 135, 0.3)';
|
|
statusBadge.style.color = '#00ff87';
|
|
statusBadge.style.background = 'rgba(0, 255, 135, 0.1)';
|
|
} else if (track.status === 'error') {
|
|
statusBadge.style.borderColor = 'rgba(255, 77, 77, 0.3)';
|
|
statusBadge.style.color = '#ff4d4d';
|
|
statusBadge.style.background = 'rgba(255, 77, 77, 0.1)';
|
|
}
|
|
|
|
statusBadge.textContent = statusText;
|
|
statusEl.appendChild(statusBadge);
|
|
itemEl.appendChild(statusEl);
|
|
|
|
itemEl.addEventListener('click', () => {
|
|
focusedTrackId = track.id;
|
|
syncBottomPanelWithTrack(track);
|
|
renderTrackList(currentVideoData);
|
|
});
|
|
|
|
trackList.appendChild(itemEl);
|
|
});
|
|
}
|
|
|
|
// 8. Media Download Executor
|
|
downloadBtn.addEventListener('click', async () => {
|
|
if (!analyzedUrl || playlistTracks.length === 0) return;
|
|
|
|
const selectedItems = playlistTracks.filter(t => t.checked);
|
|
if (selectedItems.length === 0) {
|
|
Swal.fire({
|
|
title: t('badge_error'),
|
|
text: t('err_no_items_selected') || 'Lütfen indirmek için en az bir dosya seçin.',
|
|
icon: 'warning',
|
|
background: 'rgba(23, 20, 38, 0.95)',
|
|
color: '#fff',
|
|
confirmButtonColor: '#7000ff'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Setup download progress panel UI states
|
|
downloadProgressPanel.style.display = 'block';
|
|
downloadBtn.disabled = true;
|
|
backBtn.disabled = true;
|
|
downloadStatus.textContent = t('status_connecting');
|
|
downloadPercent.textContent = '0%';
|
|
downloadProgressBar.style.width = '0%';
|
|
downloadSubtext.textContent = t('subtext_wait');
|
|
|
|
// Disable UI interactive components during download
|
|
document.querySelectorAll('.track-checkbox, .format-badge-btn, .quality-pill, .format-btn').forEach(el => {
|
|
el.disabled = true;
|
|
el.style.pointerEvents = 'none';
|
|
});
|
|
|
|
// Mark selected items as waiting
|
|
selectedItems.forEach(track => {
|
|
track.status = 'waiting';
|
|
track.progress = 0;
|
|
|
|
const badge = document.getElementById(`badge-${track.id}`);
|
|
if (badge) {
|
|
badge.textContent = t('status_connecting');
|
|
badge.className = 'status-badge ready';
|
|
}
|
|
});
|
|
|
|
// Subscribe to progress events from the main process
|
|
window.api.onDownloadProgress(({ itemId, index, total, percent, status, title, failed }) => {
|
|
const track = playlistTracks.find(t => t.id === itemId);
|
|
if (track) {
|
|
if (percent === 100) {
|
|
track.status = 'completed';
|
|
track.progress = 100;
|
|
} else if (failed) {
|
|
track.status = 'error';
|
|
} else {
|
|
track.status = 'downloading';
|
|
track.progress = percent;
|
|
}
|
|
}
|
|
|
|
// Dynamic row updates
|
|
const badge = document.getElementById(`badge-${itemId}`);
|
|
if (badge) {
|
|
if (percent === 100) {
|
|
badge.textContent = t('badge_completed');
|
|
badge.className = 'status-badge completed';
|
|
badge.style.borderColor = 'rgba(0, 255, 135, 0.3)';
|
|
badge.style.color = '#00ff87';
|
|
badge.style.background = 'rgba(0, 255, 135, 0.1)';
|
|
} else if (failed) {
|
|
badge.textContent = t('badge_error');
|
|
badge.className = 'status-badge error';
|
|
badge.style.borderColor = 'rgba(255, 77, 77, 0.3)';
|
|
badge.style.color = '#ff4d4d';
|
|
badge.style.background = 'rgba(255, 77, 77, 0.1)';
|
|
} else {
|
|
badge.textContent = `${t('badge_downloading')} %${percent}`;
|
|
badge.className = 'status-badge downloading';
|
|
}
|
|
}
|
|
|
|
// Update main footer progress panel
|
|
let stat = status;
|
|
if (status.includes('İndiriliyor')) {
|
|
stat = `${t('btn_downloading')}... %${percent}`;
|
|
} else if (status.includes('İşleniyor') || status.includes('Dönüştürülüyor')) {
|
|
stat = t('status_processing');
|
|
}
|
|
|
|
downloadStatus.textContent = `[${index + 1}/${total}] ${title}`;
|
|
downloadPercent.textContent = `${percent}%`;
|
|
downloadProgressBar.style.width = `${percent}%`;
|
|
downloadSubtext.textContent = `${t('swal_file_size')}: ${stat}`;
|
|
});
|
|
|
|
try {
|
|
const browser = useCookiesCheckbox.checked ? cookieBrowserSelect.value : null;
|
|
|
|
// Map selected items into a simplified API task schema
|
|
const downloadTasks = selectedItems.map(t => ({
|
|
id: t.id,
|
|
title: t.title,
|
|
url: t.url,
|
|
format: t.format,
|
|
quality: t.quality
|
|
}));
|
|
|
|
const result = await window.api.downloadMedia(downloadTasks, browser);
|
|
|
|
if (result.success) {
|
|
downloadStatus.textContent = t('status_completed');
|
|
downloadPercent.textContent = '100%';
|
|
downloadProgressBar.style.width = '100%';
|
|
downloadSubtext.textContent = t('subtext_success');
|
|
|
|
// 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)';
|
|
|
|
const displayTitle = isPlaylistMode
|
|
? `${currentVideoData.playlistTitle || 'Playlist'} (${result.stats.completed}/${result.stats.totalItems})`
|
|
: selectedItems[0].title;
|
|
|
|
const displayUploader = isPlaylistMode
|
|
? 'Playlist Queue'
|
|
: (selectedItems[0].uploader || t('duration_unknown'));
|
|
|
|
const displaySize = isPlaylistMode
|
|
? `${result.stats.completed} ${t('badge_completed').toLowerCase()}`
|
|
: (result.stats.results[0]?.size || 'Bilinmiyor');
|
|
|
|
const localizedDuration = result.stats.duration
|
|
.replace('saniye', t('sec_suffix'))
|
|
.replace('dakika', t('min_suffix'));
|
|
|
|
// Trigger premium SweetAlert2 modal
|
|
Swal.fire({
|
|
title: t('swal_success_title'),
|
|
html: `
|
|
<div style="text-align: ${currentLang === 'ar' ? 'right' : 'left'}; direction: ${currentLang === 'ar' ? 'rtl' : 'ltr'}; font-family: 'Outfit', sans-serif; color: #fff; line-height: 1.6; font-size: 14px;">
|
|
<p style="margin-bottom: 8px;"><strong>${isPlaylistMode ? t('playlist_title_label') || 'Çalma Listesi:' : t('swal_file_name')}</strong> <span style="color: #60efff;">${displayTitle}</span></p>
|
|
<p style="margin-bottom: 8px;"><strong>${t('swal_platform_uploader')}</strong> <span style="color: #60efff;">${displayUploader}</span></p>
|
|
<p style="margin-bottom: 8px;"><strong>${isPlaylistMode ? t('downloaded_files_label') || 'İndirilen Dosyalar:' : t('swal_file_size')}</strong> <span style="color: #00ff87;">${displaySize}</span></p>
|
|
<p style="margin-bottom: 8px;"><strong>${t('swal_duration')}</strong> <span style="color: #00ff87;">${localizedDuration}</span></p>
|
|
<p style="margin-top: 15px; font-size: 12px; opacity: 0.8; word-break: break-all;"><strong>${t('swal_output_dir')}</strong> <br>${result.stats.outputDir}</p>
|
|
</div>
|
|
`,
|
|
icon: 'success',
|
|
background: 'rgba(23, 20, 38, 0.95)',
|
|
color: '#fff',
|
|
confirmButtonText: t('swal_btn_open_folder'),
|
|
showCancelButton: true,
|
|
cancelButtonText: t('swal_btn_new_download'),
|
|
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 {
|
|
backBtn.click();
|
|
urlInput.value = '';
|
|
downloadProgressPanel.style.display = 'none';
|
|
}
|
|
});
|
|
} else {
|
|
showError(result.error);
|
|
downloadProgressPanel.style.display = 'none';
|
|
}
|
|
} catch (err) {
|
|
showError(err.message);
|
|
downloadProgressPanel.style.display = 'none';
|
|
} finally {
|
|
downloadBtn.disabled = false;
|
|
backBtn.disabled = false;
|
|
document.querySelectorAll('.track-checkbox, .format-badge-btn, .quality-pill, .format-btn').forEach(el => {
|
|
el.disabled = false;
|
|
el.style.pointerEvents = 'auto';
|
|
});
|
|
}
|
|
});
|
|
|
|
// Helper: Show Error Alert inside Step 1
|
|
function showError(msg) {
|
|
let friendlyMsg = msg;
|
|
if (msg.includes('Could not copy') && msg.includes('cookie database')) {
|
|
friendlyMsg = t('err_cookies_locked');
|
|
} else if (msg.includes('You need to log in to access this content')) {
|
|
friendlyMsg = t('err_login_required');
|
|
} else if (msg.includes('Failed to decrypt with DPAPI')) {
|
|
friendlyMsg = t('err_dpapi_fail');
|
|
}
|
|
errorMessage.textContent = `${t('err_prefix')}${friendlyMsg}`;
|
|
errorCard.style.display = 'flex';
|
|
}
|
|
|