feat: implement initial UI structure and multi-language support for downloader interface
This commit is contained in:
+34
-23
@@ -23,10 +23,20 @@
|
||||
<span>VibeDownloader</span>
|
||||
</div>
|
||||
<div class="titlebar-controls">
|
||||
<button id="titlebar-minimize" class="control-btn" title="Simge Durumuna Getir">
|
||||
<!-- Language Dropdown -->
|
||||
<div class="language-selector-wrapper">
|
||||
<select id="language-select" class="language-select">
|
||||
<option value="tr">🇹🇷 Türkçe</option>
|
||||
<option value="en">🇺🇸 English</option>
|
||||
<option value="de">🇩🇪 Deutsch</option>
|
||||
<option value="ru">🇷🇺 Русский</option>
|
||||
<option value="ar">🇸🇦 العربية</option>
|
||||
</select>
|
||||
</div>
|
||||
<button id="titlebar-minimize" class="control-btn" title="Simge Durumuna Getir" data-i18n-title="titlebar_minimize">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
</button>
|
||||
<button id="titlebar-close" class="control-btn close" title="Kapat">
|
||||
<button id="titlebar-close" class="control-btn close" title="Kapat" data-i18n-title="titlebar_close">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -43,12 +53,12 @@
|
||||
<div class="spinner-container">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<h2 id="dep-title">Sistem Hazırlanıyor</h2>
|
||||
<p id="dep-msg">Lütfen bekleyin, gerekli bileşenler kontrol ediliyor...</p>
|
||||
<h2 id="dep-title" data-i18n="dep_title">Sistem Hazırlanıyor</h2>
|
||||
<p id="dep-msg" data-i18n="dep_msg">Lütfen bekleyin, gerekli bileşenler kontrol ediliyor...</p>
|
||||
<div class="progress-bar-container">
|
||||
<div id="dep-progress" class="progress-bar-fill" style="width: 0%"></div>
|
||||
</div>
|
||||
<div id="dep-percent" class="pct-text">0%</div>
|
||||
<div id="dep-percent" class="pct-text" data-i18n="dep_percent">0%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,28 +76,29 @@
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="glow-text">VibeDownloader</h1>
|
||||
<p class="subtitle">YouTube, X/Twitter, Instagram, TikTok ve tüm popüler sitelerden video/ses indirin</p>
|
||||
<p class="subtitle" data-i18n="app_subtitle">YouTube, X/Twitter, Instagram, TikTok ve tüm popüler sitelerden video/ses indirin</p>
|
||||
</header>
|
||||
|
||||
<section class="glass-card url-section">
|
||||
<div class="input-group">
|
||||
<input type="text" id="url-input" placeholder="Sosyal medya veya video platformu bağlantısı yapıştırın (YouTube, Instagram, TikTok, X/Twitter vb.)..." autocomplete="off">
|
||||
<input type="text" id="url-input" data-i18n-placeholder="url_placeholder" placeholder="Sosyal medya veya video platformu bağlantısı yapıştırın (YouTube, Instagram, TikTok, X/Twitter vb.)..." autocomplete="off">
|
||||
<button id="analyze-btn" class="glow-button">
|
||||
<span>Analiz Et</span>
|
||||
<span data-i18n="btn_analyze">Analiz Et</span>
|
||||
<svg class="btn-icon animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: none;">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M2 12a10 10 0 0 1 8-9.8" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="output-folder-row">
|
||||
<span class="folder-label">Kaydetme Yeri:</span>
|
||||
<span id="output-path" class="folder-path">Yükleniyor...</span>
|
||||
<button id="change-folder-btn" class="text-btn">Değiştir</button>
|
||||
<span class="folder-label" data-i18n="folder_label">Kaydetme Yeri:</span>
|
||||
<span id="output-path" class="folder-path" data-i18n="folder_loading">Yükleniyor...</span>
|
||||
<button id="change-folder-btn" class="text-btn" data-i18n="btn_change_folder">Değiştir</button>
|
||||
</div>
|
||||
<div class="cookies-row" style="margin-top: 15px; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 0.85em; opacity: 0.85; border-top: 1px solid rgba(255,255,255,0.06); padding-top: 12px; flex-wrap: wrap;">
|
||||
<input type="checkbox" id="use-cookies" style="cursor: pointer; accent-color: #7000ff; width: 15px; height: 15px;">
|
||||
<label for="use-cookies" style="cursor: pointer; color: #fff; user-select: none;">Giriş gerektiren içerikler (Örn. Instagram Hikayeleri) için tarayıcı oturumunu kullan:</label>
|
||||
<label for="use-cookies" data-i18n="use_cookies_label" style="cursor: pointer; color: #fff; user-select: none;">Giriş gerektiren içerikler (Örn. Instagram Hikayeleri) için tarayıcı oturumunu kullan:</label>
|
||||
<select id="cookie-browser" style="background: rgba(23, 20, 38, 0.8); color: #fff; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 4px; padding: 2px 8px; cursor: pointer; outline: none; font-family: inherit; font-size: 0.95em;">
|
||||
<option value="chrome">Chrome</option>
|
||||
<option value="edge">Edge</option>
|
||||
@@ -105,7 +116,7 @@
|
||||
<line x1="12" y1="8" x2="12" y2="12" />
|
||||
<line x1="12" y1="16" x2="12.01" y2="16" />
|
||||
</svg>
|
||||
<span id="error-message">Bir hata oluştu.</span>
|
||||
<span id="error-message" data-i18n="error_default">Bir hata oluştu.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -113,13 +124,13 @@
|
||||
<div id="step-2" class="wizard-step">
|
||||
<header class="step-header">
|
||||
<button id="back-btn" class="back-button">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<svg class="back-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
<span>Geri Dön</span>
|
||||
<span data-i18n="btn_back">Geri Dön</span>
|
||||
</button>
|
||||
<h2 class="step-title">İndirilecek Dosya</h2>
|
||||
<h2 class="step-title" data-i18n="step2_title">İndirilecek Dosya</h2>
|
||||
</header>
|
||||
|
||||
<!-- Scrollable Track List Area -->
|
||||
@@ -135,23 +146,23 @@
|
||||
<!-- Formats & Quality Picker -->
|
||||
<div class="options-panel">
|
||||
<div class="option-row">
|
||||
<span class="option-label">Format:</span>
|
||||
<span class="option-label" data-i18n="format_label">Format:</span>
|
||||
<div class="format-toggle">
|
||||
<button id="format-mp3-btn" class="format-btn active">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18V5l12-2v13" /><circle cx="6" cy="18" r="3" /><circle cx="18" cy="16" r="3" /></svg>
|
||||
<span>MP3 Ses</span>
|
||||
<span data-i18n="format_mp3">MP3 Ses</span>
|
||||
</button>
|
||||
<button id="format-mp4-btn" class="format-btn">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18" /><line x1="7" y1="2" x2="7" y2="22" /><line x1="17" y1="2" x2="17" y2="22" /><line x1="2" y1="12" x2="22" y2="12" /></svg>
|
||||
<span>MP4 Video</span>
|
||||
<span data-i18n="format_mp4">MP4 Video</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option-row">
|
||||
<span class="option-label">Kalite:</span>
|
||||
<span class="option-label" data-i18n="quality_label">Kalite:</span>
|
||||
<div id="quality-pills" class="quality-pills">
|
||||
<!-- Populated dynamically: 320kbps, 256kbps, 192kbps for MP3 or 1080p, 720p, 480p for MP4 -->
|
||||
<!-- Populated dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,8 +182,8 @@
|
||||
</div>
|
||||
|
||||
<button id="download-btn" class="glow-button full-width">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="8 17 12 21 16 17"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path></svg>
|
||||
<span>İndirmeyi Başlat</span>
|
||||
<svg class="dl-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="8 17 12 21 16 17"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path></svg>
|
||||
<span data-i18n="btn_download">İndirmeyi Başlat</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"app_title": "VibeDownloader",
|
||||
"app_subtitle": "قم بتنزيل الفيديو/الصوت من يوتيوب، إكس/تويتر، إنستغرام، تيك توك وجميع المواقع الشهيرة",
|
||||
"titlebar_minimize": "تصغير",
|
||||
"titlebar_close": "إغلاق",
|
||||
"dep_title": "جاري تجهيز النظام",
|
||||
"dep_msg": "يرجى الانتظار، جاري التحقق من المكونات المطلوبة...",
|
||||
"dep_percent": "0%",
|
||||
"url_placeholder": "أدخل رابط موقع التواصل الاجتماعي أو منصة الفيديو (يوتيوب، إنستغرام، تيك توك، إكس/تويتر، إلخ)...",
|
||||
"btn_analyze": "تحليل",
|
||||
"btn_analyzing": "جاري التحليل...",
|
||||
"folder_label": "مكان الحفظ:",
|
||||
"folder_loading": "جاري التحميل...",
|
||||
"btn_change_folder": "تغيير",
|
||||
"use_cookies_label": "استخدام جلسة المتصفح للمحتوى المقيد بتسجيل الدخول (مثل قصص إنستغرام):",
|
||||
"error_default": "حدث خطأ ما.",
|
||||
"btn_back": "رجوع",
|
||||
"step2_title": "الملف المراد تنزيله",
|
||||
"format_label": "الصيغة:",
|
||||
"format_mp3": "صوت MP3",
|
||||
"format_mp4": "فيديو MP4",
|
||||
"quality_label": "الجودة:",
|
||||
"btn_download": "بدء التنزيل",
|
||||
"btn_downloading": "جاري التنزيل...",
|
||||
"status_connecting": "جاري الاتصال...",
|
||||
"status_downloading": "جاري التنزيل... %{percent}",
|
||||
"status_processing": "جاري المعالجة/التحويل...",
|
||||
"status_completed": "اكتمل!",
|
||||
"subtext_mp3_download": "جاري جلب دفق الصوت...",
|
||||
"subtext_mp4_download": "جاري جلب دفق الفيديو...",
|
||||
"subtext_mp3_convert": "جاري التحويل (صوت MP3 عالي الجودة)...",
|
||||
"subtext_mp4_merge": "جاري دمج ملفات الفيديو والصوت...",
|
||||
"subtext_wait": "يرجى الانتظار...",
|
||||
"subtext_speed_note": "قد يختلف وقت المعالجة بناءً على سرعة التنزيل وحجم الملف.",
|
||||
"subtext_success": "تم حفظ ملفك بنجاح!",
|
||||
"duration_label": "المدة:",
|
||||
"duration_unknown": "غير معروف",
|
||||
"badge_ready": "جاهز",
|
||||
"badge_downloading": "جاري التنزيل",
|
||||
"badge_completed": "اكتمل",
|
||||
"badge_error": "خطأ",
|
||||
"swal_success_title": "اكتمل التنزيل بنجاح! 🎉",
|
||||
"swal_file_name": "اسم الملف:",
|
||||
"swal_platform_uploader": "المنصة / الناشر:",
|
||||
"swal_file_size": "حجم الملف:",
|
||||
"swal_duration": "مدة التنزيل:",
|
||||
"swal_format_quality": "الصيغة والجودة:",
|
||||
"swal_output_dir": "مجلد الحفظ:",
|
||||
"swal_btn_open_folder": "فتح المجلد 📂",
|
||||
"swal_btn_new_download": "تنزيل جديد 🔄",
|
||||
"err_cookies_locked": "المتصفح المحدد (كروم، إيدج، بريف، إلخ) مفتوح حالياً في الخلفية، مما يقفل قاعدة بيانات ملفات تعريف الارتباط (cookies). يرجى إغلاق جميع نوافذ المتصفح تماماً والنقر على 'تحليل' مجدداً.",
|
||||
"err_login_required": "يتطلب تسجيل الدخول للوصول إلى هذا المحتوى الخاص. يرجى تفعيل خيار ملفات تعريف الارتباط أدناه واختيار المتصفح الذي سجلت الدخول به.",
|
||||
"err_dpapi_fail": "تعذر فك تشفير مفتاح ملفات تعريف الارتباط للمتصفح (خطأ DPAPI). يحدث هذا عادةً عند فتح التطبيق أو الطرفية بوضع 'التشغيل كمسؤول' (Administrator). يرجى تشغيل التطبيق في وضع المستخدم العادي للوصول إلى ملفات تعريف الارتباط والمحاولة مرة أخرى.",
|
||||
"err_prefix": "خطأ: ",
|
||||
"quality_best": "الأعلى",
|
||||
"quality_standard": "قياسي",
|
||||
"sec_suffix": "ثانية",
|
||||
"min_suffix": "دقيقة"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"app_title": "VibeDownloader",
|
||||
"app_subtitle": "Laden Sie Videos/Audio von YouTube, X/Twitter, Instagram, TikTok und allen beliebten Websites herunter",
|
||||
"titlebar_minimize": "Minimieren",
|
||||
"titlebar_close": "Schließen",
|
||||
"dep_title": "System wird vorbereitet",
|
||||
"dep_msg": "Bitte warten Sie, erforderliche Komponenten werden überprüft...",
|
||||
"dep_percent": "0%",
|
||||
"url_placeholder": "Link der sozialen Medien oder Videoplattform einfügen (YouTube, Instagram, TikTok, X/Twitter usw.)...",
|
||||
"btn_analyze": "Analysieren",
|
||||
"btn_analyzing": "Wird analysiert...",
|
||||
"folder_label": "Speicherort:",
|
||||
"folder_loading": "Wird geladen...",
|
||||
"btn_change_folder": "Ändern",
|
||||
"use_cookies_label": "Browser-Sitzung für passwortgeschützte Inhalte verwenden (z. B. Instagram Stories):",
|
||||
"error_default": "Ein Fehler ist aufgetreten.",
|
||||
"btn_back": "Zurückgehen",
|
||||
"step2_title": "Datei zum Herunterladen",
|
||||
"format_label": "Format:",
|
||||
"format_mp3": "MP3-Audio",
|
||||
"format_mp4": "MP4-Video",
|
||||
"quality_label": "Qualität:",
|
||||
"btn_download": "Download starten",
|
||||
"btn_downloading": "Wird heruntergeladen...",
|
||||
"status_connecting": "Verbindung wird hergestellt...",
|
||||
"status_downloading": "Wird heruntergeladen... %{percent}",
|
||||
"status_processing": "Verarbeitung/Konvertierung...",
|
||||
"status_completed": "Abgeschlossen!",
|
||||
"subtext_mp3_download": "Audio-Stream wird abgerufen...",
|
||||
"subtext_mp4_download": "Video-Stream wird abgerufen...",
|
||||
"subtext_mp3_convert": "Konvertierung wird durchgeführt (MP3 in hoher Qualität)...",
|
||||
"subtext_mp4_merge": "Video- und Audiodateien werden zusammengeführt...",
|
||||
"subtext_wait": "Bitte warten...",
|
||||
"subtext_speed_note": "Die Bearbeitungszeit kann je nach Download-Geschwindigkeit und Dateigröße variieren.",
|
||||
"subtext_success": "Ihre Datei wurde erfolgreich gespeichert!",
|
||||
"duration_label": "Dauer:",
|
||||
"duration_unknown": "Unbekannt",
|
||||
"badge_ready": "Bereit",
|
||||
"badge_downloading": "Herunterladen",
|
||||
"badge_completed": "Abgeschlossen",
|
||||
"badge_error": "Fehler",
|
||||
"swal_success_title": "Download abgeschlossen! 🎉",
|
||||
"swal_file_name": "Dateiname:",
|
||||
"swal_platform_uploader": "Plattform / Uploader:",
|
||||
"swal_file_size": "Dateigröße:",
|
||||
"swal_duration": "Download-Dauer:",
|
||||
"swal_format_quality": "Format und Qualität:",
|
||||
"swal_output_dir": "Gespeichertes Verzeichnis:",
|
||||
"swal_btn_open_folder": "Ordner öffnen 📂",
|
||||
"swal_btn_new_download": "Neuer Download 🔄",
|
||||
"err_cookies_locked": "Der ausgewählte Browser (Chrome/Edge/Brave usw.) ist derzeit im Hintergrund geöffnet und sperrt die Cookie-Datenbank. Bitte schließen Sie alle Browserfenster vollständig und klicken Sie erneut auf 'Analysieren'.",
|
||||
"err_login_required": "Für den Zugriff auf diesen privaten Inhalt ist eine Anmeldung erforderlich. Bitte aktivieren Sie die unten stehende Cookie-Option und wählen Sie den Browser aus, mit dem Sie sich angemeldet haben.",
|
||||
"err_dpapi_fail": "Der Browser-Cookie-Schlüssel konnte nicht entschlüsselt werden (DPAPI-Fehler). Dies tritt normalerweise auf, wenn die Anwendung oder das Terminal im Modus 'Als Administrator ausführen' geöffnet ist. Bitte starten Sie die Anwendung im normalen Benutzermodus, um auf Cookies zuzugreifen, und versuchen Sie es erneut.",
|
||||
"err_prefix": "Fehler: ",
|
||||
"quality_best": "Höchste",
|
||||
"quality_standard": "Standard",
|
||||
"sec_suffix": "Sekunden",
|
||||
"min_suffix": "Minuten"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"app_title": "VibeDownloader",
|
||||
"app_subtitle": "Download video/audio from YouTube, X/Twitter, Instagram, TikTok and all popular sites",
|
||||
"titlebar_minimize": "Minimize",
|
||||
"titlebar_close": "Close",
|
||||
"dep_title": "System Preparing",
|
||||
"dep_msg": "Please wait, checking required components...",
|
||||
"dep_percent": "0%",
|
||||
"url_placeholder": "Paste social media or video platform link (YouTube, Instagram, TikTok, X/Twitter etc.)...",
|
||||
"btn_analyze": "Analyze",
|
||||
"btn_analyzing": "Analyzing...",
|
||||
"folder_label": "Save Location:",
|
||||
"folder_loading": "Loading...",
|
||||
"btn_change_folder": "Change",
|
||||
"use_cookies_label": "Use browser session for login-restricted content (e.g. Instagram Stories):",
|
||||
"error_default": "An error occurred.",
|
||||
"btn_back": "Go Back",
|
||||
"step2_title": "File to Download",
|
||||
"format_label": "Format:",
|
||||
"format_mp3": "MP3 Audio",
|
||||
"format_mp4": "MP4 Video",
|
||||
"quality_label": "Quality:",
|
||||
"btn_download": "Start Download",
|
||||
"btn_downloading": "Downloading...",
|
||||
"status_connecting": "Connecting...",
|
||||
"status_downloading": "Downloading... %{percent}",
|
||||
"status_processing": "Processing/Converting...",
|
||||
"status_completed": "Completed!",
|
||||
"subtext_mp3_download": "Fetching audio stream...",
|
||||
"subtext_mp4_download": "Fetching video stream...",
|
||||
"subtext_mp3_convert": "Converting (High Quality MP3)...",
|
||||
"subtext_mp4_merge": "Merging video and audio files...",
|
||||
"subtext_wait": "Please wait...",
|
||||
"subtext_speed_note": "Processing time may vary depending on download speed and file size.",
|
||||
"subtext_success": "Your file has been successfully saved!",
|
||||
"duration_label": "Duration:",
|
||||
"duration_unknown": "Unknown",
|
||||
"badge_ready": "Ready",
|
||||
"badge_downloading": "Downloading",
|
||||
"badge_completed": "Completed",
|
||||
"badge_error": "Error",
|
||||
"swal_success_title": "Download Completed! 🎉",
|
||||
"swal_file_name": "File Name:",
|
||||
"swal_platform_uploader": "Platform / Uploader:",
|
||||
"swal_file_size": "File Size:",
|
||||
"swal_duration": "Download Duration:",
|
||||
"swal_format_quality": "Format and Quality:",
|
||||
"swal_output_dir": "Saved Directory:",
|
||||
"swal_btn_open_folder": "Open Folder 📂",
|
||||
"swal_btn_new_download": "New Download 🔄",
|
||||
"err_cookies_locked": "The selected browser (Chrome/Edge/Brave etc.) is currently open in the background, locking the cookie database. Please close all browser windows completely and click 'Analyze' again.",
|
||||
"err_login_required": "Login is required to access this private content. Please activate the cookie option below and select the browser you logged in with.",
|
||||
"err_dpapi_fail": "Could not decrypt browser cookie key (DPAPI Error). This usually occurs when the application or terminal is opened in 'Run as Administrator' mode. Please start the application in normal user mode to access cookies and try again.",
|
||||
"err_prefix": "Error: ",
|
||||
"quality_best": "Highest",
|
||||
"quality_standard": "Standard",
|
||||
"sec_suffix": "seconds",
|
||||
"min_suffix": "minutes"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"app_title": "VibeDownloader",
|
||||
"app_subtitle": "Скачивайте видео/аудио с YouTube, X/Twitter, Instagram, TikTok и всех популярных сайтов",
|
||||
"titlebar_minimize": "Свернуть",
|
||||
"titlebar_close": "Закрыть",
|
||||
"dep_title": "Подготовка системы",
|
||||
"dep_msg": "Пожалуйста, подождите, проверяются необходимые компоненты...",
|
||||
"dep_percent": "0%",
|
||||
"url_placeholder": "Вставьте ссылку на социальную сеть или видеоплатформу (YouTube, Instagram, TikTok, X/Twitter и т. д.)...",
|
||||
"btn_analyze": "Анализировать",
|
||||
"btn_analyzing": "Анализ...",
|
||||
"folder_label": "Куда сохранить:",
|
||||
"folder_loading": "Загрузка...",
|
||||
"btn_change_folder": "Изменить",
|
||||
"use_cookies_label": "Использовать сессию браузера для контента с ограниченным доступом (например, историй Instagram):",
|
||||
"error_default": "Произошла ошибка.",
|
||||
"btn_back": "Назад",
|
||||
"step2_title": "Файл для скачивания",
|
||||
"format_label": "Формат:",
|
||||
"format_mp3": "MP3 Аудио",
|
||||
"format_mp4": "MP4 Видео",
|
||||
"quality_label": "Качество:",
|
||||
"btn_download": "Начать скачивание",
|
||||
"btn_downloading": "Скачивание...",
|
||||
"status_connecting": "Установка соединения...",
|
||||
"status_downloading": "Скачивание... %{percent}",
|
||||
"status_processing": "Обработка/Конвертация...",
|
||||
"status_completed": "Завершено!",
|
||||
"subtext_mp3_download": "Получение аудиопотока...",
|
||||
"subtext_mp4_download": "Получение видеопотока...",
|
||||
"subtext_mp3_convert": "Выполняется конвертация (Высокое качество MP3)...",
|
||||
"subtext_mp4_merge": "Слияние видео и аудио файлов...",
|
||||
"subtext_wait": "Пожалуйста, подождите...",
|
||||
"subtext_speed_note": "Время обработки может варьироваться в зависимости от скорости скачивания и размера файла.",
|
||||
"subtext_success": "Ваш файл успешно сохранен!",
|
||||
"duration_label": "Длительность:",
|
||||
"duration_unknown": "Неизвестно",
|
||||
"badge_ready": "Готов",
|
||||
"badge_downloading": "Скачивание",
|
||||
"badge_completed": "Завершено",
|
||||
"badge_error": "Ошибка",
|
||||
"swal_success_title": "Скачивание завершено! 🎉",
|
||||
"swal_file_name": "Имя файла:",
|
||||
"swal_platform_uploader": "Платформа / Автор:",
|
||||
"swal_file_size": "Размер файла:",
|
||||
"swal_duration": "Время скачивания:",
|
||||
"swal_format_quality": "Формат и качество:",
|
||||
"swal_output_dir": "Папка сохранения:",
|
||||
"swal_btn_open_folder": "Открыть папку 📂",
|
||||
"swal_btn_new_download": "Новое скачивание 🔄",
|
||||
"err_cookies_locked": "Выбранный браузер (Chrome/Edge/Brave и т. д.) в данный момент открыт в фоновом режиме, что блокирует базу данных файлов cookie. Пожалуйста, полностью закройте все окна браузера и нажмите кнопку 'Анализировать' еще раз.",
|
||||
"err_login_required": "Для доступа к этому приватному контенту требуется авторизация. Пожалуйста, активируйте параметр cookie ниже и выберите браузер, в котором вы авторизовались.",
|
||||
"err_dpapi_fail": "Не удалось расшифровать ключ cookie браузера (ошибка DPAPI). Обычно это происходит, когда приложение или терминал открыты в режиме 'Запуск от имени администратора'. Пожалуйста, запустите приложение в обычном режиме пользователя для доступа к файлам cookie и повторите попытку.",
|
||||
"err_prefix": "Ошибка: ",
|
||||
"quality_best": "Максимальное",
|
||||
"quality_standard": "Стандартное",
|
||||
"sec_suffix": "сек.",
|
||||
"min_suffix": "мин."
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"app_title": "VibeDownloader",
|
||||
"app_subtitle": "YouTube, X/Twitter, Instagram, TikTok ve tüm popüler sitelerden video/ses indirin",
|
||||
"titlebar_minimize": "Simge Durumuna Getir",
|
||||
"titlebar_close": "Kapat",
|
||||
"dep_title": "Sistem Hazırlanıyor",
|
||||
"dep_msg": "Lütfen bekleyin, gerekli bileşenler kontrol ediliyor...",
|
||||
"dep_percent": "0%",
|
||||
"url_placeholder": "Sosyal medya veya video platformu bağlantısı yapıştırın (YouTube, Instagram, TikTok, X/Twitter vb.)...",
|
||||
"btn_analyze": "Analiz Et",
|
||||
"btn_analyzing": "Analiz ediliyor...",
|
||||
"folder_label": "Kaydetme Yeri:",
|
||||
"folder_loading": "Yükleniyor...",
|
||||
"btn_change_folder": "Değiştir",
|
||||
"use_cookies_label": "Giriş gerektiren içerikler (Örn. Instagram Hikayeleri) için tarayıcı oturumunu kullan:",
|
||||
"error_default": "Bir hata oluştu.",
|
||||
"btn_back": "Geri Dön",
|
||||
"step2_title": "İndirilecek Dosya",
|
||||
"format_label": "Format:",
|
||||
"format_mp3": "MP3 Ses",
|
||||
"format_mp4": "MP4 Video",
|
||||
"quality_label": "Kalite:",
|
||||
"btn_download": "İndirmeyi Başlat",
|
||||
"btn_downloading": "İndiriliyor...",
|
||||
"status_connecting": "Bağlantı kuruluyor...",
|
||||
"status_downloading": "İndiriliyor... %{percent}",
|
||||
"status_processing": "İşleniyor/Dönüştürülüyor...",
|
||||
"status_completed": "Tamamlandı!",
|
||||
"subtext_mp3_download": "Ses akışı alınıyor...",
|
||||
"subtext_mp4_download": "Video akışı alınıyor...",
|
||||
"subtext_mp3_convert": "Dönüştürme işlemi yapılıyor (Yüksek Kaliteli MP3)...",
|
||||
"subtext_mp4_merge": "Video ve ses dosyaları birleştiriliyor...",
|
||||
"subtext_wait": "Lütfen bekleyin...",
|
||||
"subtext_speed_note": "İndirme hızı ve dosya boyutuna göre işlem süresi değişebilir.",
|
||||
"subtext_success": "Dosyanız başarıyla kaydedildi!",
|
||||
"duration_label": "Süre:",
|
||||
"duration_unknown": "Bilinmiyor",
|
||||
"badge_ready": "Hazır",
|
||||
"badge_downloading": "İndiriliyor",
|
||||
"badge_completed": "Tamamlandı",
|
||||
"badge_error": "Hata",
|
||||
"swal_success_title": "İndirme Tamamlandı! 🎉",
|
||||
"swal_file_name": "Dosya Adı:",
|
||||
"swal_platform_uploader": "Platform / Paylaşan:",
|
||||
"swal_file_size": "Dosya Boyutu:",
|
||||
"swal_duration": "İndirme Süresi:",
|
||||
"swal_format_quality": "Format ve Kalite:",
|
||||
"swal_output_dir": "Kaydedilen Klasör:",
|
||||
"swal_btn_open_folder": "Klasörü Aç 📂",
|
||||
"swal_btn_new_download": "Yeni İndirme 🔄",
|
||||
"err_cookies_locked": "Seçtiğiniz tarayıcı (Chrome/Edge/Brave vb.) şu anda arka planda açık olduğu için çerez veritabanı kilitlendi. Lütfen tarayıcı pencerelerinizi tamamen kapatıp ardından tekrar 'Analiz Et' butonuna basın.",
|
||||
"err_login_required": "Bu özel içeriğe erişmek için giriş yapılması gerekiyor. Lütfen aşağıdaki çerez seçeneğini aktif edip giriş yapmış olduğunuz tarayıcıyı seçin.",
|
||||
"err_dpapi_fail": "Tarayıcı çerez anahtarı çözülemedi (DPAPI Hatası). Bu durum genellikle uygulamayı veya terminali 'Yönetici Olarak Çalıştır' (Administrator) modunda açtığınızda oluşur. Çerezlerinize erişebilmek için lütfen uygulamayı normal kullanıcı modunda başlatıp tekrar deneyin.",
|
||||
"err_prefix": "Hata: ",
|
||||
"quality_best": "En Yüksek",
|
||||
"quality_standard": "Standart",
|
||||
"sec_suffix": "saniye",
|
||||
"min_suffix": "dakika"
|
||||
}
|
||||
+153
-51
@@ -31,25 +31,93 @@ 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;
|
||||
|
||||
// Quality Configuration Data
|
||||
const qualityConfigs = {
|
||||
mp3: [
|
||||
{ label: '320 kbps (En Yüksek)', 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 (Standart)', value: '480' }
|
||||
]
|
||||
};
|
||||
// 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 () => {
|
||||
@@ -61,13 +129,35 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
window.api.closeWindow();
|
||||
});
|
||||
|
||||
// Setup Language Selector Change Handler
|
||||
languageSelect.addEventListener('change', async (e) => {
|
||||
await loadTranslations(e.target.value);
|
||||
});
|
||||
|
||||
// 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 }) => {
|
||||
depMsg.textContent = message;
|
||||
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}%`;
|
||||
});
|
||||
@@ -81,17 +171,14 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
dependencyOverlay.classList.remove('active');
|
||||
}, 400);
|
||||
} else {
|
||||
depTitle.textContent = 'Hata Oluştu';
|
||||
depMsg.textContent = `Bileşenler yüklenemedi: ${result.error}`;
|
||||
depPercent.textContent = 'Hata';
|
||||
depTitle.textContent = t('badge_error');
|
||||
depMsg.textContent = `${t('error_default')} ${result.error}`;
|
||||
depPercent.textContent = t('badge_error');
|
||||
}
|
||||
} catch (err) {
|
||||
depTitle.textContent = 'Hata Oluştu';
|
||||
depTitle.textContent = t('badge_error');
|
||||
depMsg.textContent = err.message;
|
||||
}
|
||||
|
||||
// Render initial quality pills
|
||||
renderQualityPills();
|
||||
});
|
||||
|
||||
// 2. Output Path Folder Changer
|
||||
@@ -122,7 +209,8 @@ formatMp4Btn.addEventListener('click', () => {
|
||||
// 4. Quality Pills Renderer
|
||||
function renderQualityPills() {
|
||||
qualityPillsContainer.innerHTML = '';
|
||||
const options = qualityConfigs[currentFormat];
|
||||
const configs = getQualityConfigs();
|
||||
const options = configs[currentFormat];
|
||||
|
||||
options.forEach(opt => {
|
||||
const pill = document.createElement('button');
|
||||
@@ -150,6 +238,7 @@ backBtn.addEventListener('click', () => {
|
||||
// Fade out Step 2 and show Step 1
|
||||
step2.classList.remove('active');
|
||||
step1.classList.add('active');
|
||||
currentVideoData = null;
|
||||
urlInput.focus();
|
||||
});
|
||||
|
||||
@@ -165,7 +254,7 @@ analyzeBtn.addEventListener('click', async () => {
|
||||
analyzeBtn.disabled = true;
|
||||
const analyzeSpan = analyzeBtn.querySelector('span');
|
||||
const analyzeIcon = analyzeBtn.querySelector('.btn-icon');
|
||||
analyzeSpan.textContent = 'Analiz ediliyor...';
|
||||
analyzeSpan.textContent = t('btn_analyzing');
|
||||
analyzeIcon.style.display = 'inline-block';
|
||||
|
||||
try {
|
||||
@@ -188,7 +277,7 @@ analyzeBtn.addEventListener('click', async () => {
|
||||
showError(err.message);
|
||||
} finally {
|
||||
analyzeBtn.disabled = false;
|
||||
analyzeSpan.textContent = 'Analiz Et';
|
||||
analyzeSpan.textContent = t('btn_analyze');
|
||||
analyzeIcon.style.display = 'none';
|
||||
}
|
||||
});
|
||||
@@ -207,10 +296,10 @@ function renderTrackList(info) {
|
||||
<div class="track-details">
|
||||
<h4>${info.title}</h4>
|
||||
<p class="track-uploader">${info.uploader}</p>
|
||||
<span class="track-duration">Süre: ${info.duration || 'Bilinmiyor'}</span>
|
||||
<span class="track-duration">${t('duration_label')} ${info.duration || t('duration_unknown')}</span>
|
||||
</div>
|
||||
<div class="track-status">
|
||||
<span id="track-status-badge" class="status-badge ready">Hazır</span>
|
||||
<span id="track-status-badge" class="status-badge ready">${t('badge_ready')}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -224,30 +313,37 @@ downloadBtn.addEventListener('click', async () => {
|
||||
downloadProgressPanel.style.display = 'block';
|
||||
downloadBtn.disabled = true;
|
||||
backBtn.disabled = true;
|
||||
downloadStatus.textContent = 'Bağlantı kuruluyor...';
|
||||
downloadStatus.textContent = t('status_connecting');
|
||||
downloadPercent.textContent = '0%';
|
||||
downloadProgressBar.style.width = '0%';
|
||||
downloadSubtext.textContent = currentFormat === 'mp3' ? 'Ses akışı alınıyor...' : 'Video akışı alınıyor...';
|
||||
downloadSubtext.textContent = currentFormat === 'mp3' ? t('subtext_mp3_download') : t('subtext_mp4_download');
|
||||
|
||||
// Set track status badge as downloading
|
||||
const statusBadge = document.getElementById('track-status-badge');
|
||||
if (statusBadge) {
|
||||
statusBadge.textContent = 'İndiriliyor';
|
||||
statusBadge.textContent = t('badge_downloading');
|
||||
statusBadge.className = 'status-badge downloading';
|
||||
}
|
||||
|
||||
// Subscribe to progress events from the main process
|
||||
window.api.onDownloadProgress(({ percent, status }) => {
|
||||
downloadStatus.textContent = status;
|
||||
let stat = status;
|
||||
if (status.includes('İndiriliyor')) {
|
||||
stat = t('status_downloading', { percent });
|
||||
} else if (status.includes('İşleniyor') || status.includes('Dönüştürülüyor')) {
|
||||
stat = t('status_processing');
|
||||
}
|
||||
|
||||
downloadStatus.textContent = stat;
|
||||
downloadPercent.textContent = `${percent}%`;
|
||||
downloadProgressBar.style.width = `${percent}%`;
|
||||
|
||||
if (percent > 90) {
|
||||
downloadSubtext.textContent = currentFormat === 'mp3'
|
||||
? 'Dönüştürme işlemi yapılıyor (Yüksek Kaliteli MP3)...'
|
||||
: 'Video ve ses dosyaları birleştiriliyor...';
|
||||
? t('subtext_mp3_convert')
|
||||
: t('subtext_mp4_merge');
|
||||
} else {
|
||||
downloadSubtext.textContent = 'İndirme hızı ve dosya boyutuna göre işlem süresi değişebilir.';
|
||||
downloadSubtext.textContent = t('subtext_speed_note');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -255,13 +351,13 @@ downloadBtn.addEventListener('click', async () => {
|
||||
const browser = useCookiesCheckbox.checked ? cookieBrowserSelect.value : null;
|
||||
const result = await window.api.downloadMedia(analyzedUrl, currentFormat, currentQuality, browser);
|
||||
if (result.success) {
|
||||
downloadStatus.textContent = 'Tamamlandı!';
|
||||
downloadStatus.textContent = t('status_completed');
|
||||
downloadPercent.textContent = '100%';
|
||||
downloadProgressBar.style.width = '100%';
|
||||
downloadSubtext.textContent = 'Dosyanız başarıyla kaydedildi!';
|
||||
downloadSubtext.textContent = t('subtext_success');
|
||||
|
||||
if (statusBadge) {
|
||||
statusBadge.textContent = 'Tamamlandı';
|
||||
statusBadge.textContent = t('badge_completed');
|
||||
statusBadge.className = 'status-badge ready';
|
||||
statusBadge.style.borderColor = 'rgba(0, 255, 135, 0.3)';
|
||||
statusBadge.style.color = '#00ff87';
|
||||
@@ -272,25 +368,30 @@ downloadBtn.addEventListener('click', async () => {
|
||||
downloadProgressBar.style.background = 'linear-gradient(90deg, #00ff87 0%, #60efff 100%)';
|
||||
downloadProgressBar.style.boxShadow = '0 0 15px rgba(0, 255, 135, 0.6)';
|
||||
|
||||
// Translate duration to localized representation
|
||||
const localizedDuration = result.stats.duration
|
||||
.replace('saniye', t('sec_suffix'))
|
||||
.replace('dakika', t('min_suffix'));
|
||||
|
||||
// Trigger premium SweetAlert2 modal
|
||||
Swal.fire({
|
||||
title: 'İndirme Tamamlandı! 🎉',
|
||||
title: t('swal_success_title'),
|
||||
html: `
|
||||
<div style="text-align: left; font-family: 'Outfit', sans-serif; color: #fff; line-height: 1.6; font-size: 14px;">
|
||||
<p style="margin-bottom: 8px;"><strong>Dosya Adı:</strong> <span style="color: #60efff;">${currentVideoData.title}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>Platform / Paylaşan:</strong> <span style="color: #60efff;">${currentVideoData.uploader || 'Bilinmiyor'}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>Dosya Boyutu:</strong> <span style="color: #00ff87;">${result.stats.size}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>İndirme Süresi:</strong> <span style="color: #00ff87;">${result.stats.duration}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>Format ve Kalite:</strong> <span style="color: #ff4da6; text-transform: uppercase;">${currentFormat} (${currentQuality === '320' || currentQuality === '1080' ? 'En Yüksek' : currentQuality})</span></p>
|
||||
<p style="margin-top: 15px; font-size: 12px; opacity: 0.8; word-break: break-all;"><strong>Kaydedilen Klasör:</strong> <br>${result.stats.outputDir}</p>
|
||||
<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>${t('swal_file_name')}</strong> <span style="color: #60efff;">${currentVideoData.title}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>${t('swal_platform_uploader')}</strong> <span style="color: #60efff;">${currentVideoData.uploader || t('duration_unknown')}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>${t('swal_file_size')}</strong> <span style="color: #00ff87;">${result.stats.size}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>${t('swal_duration')}</strong> <span style="color: #00ff87;">${localizedDuration}</span></p>
|
||||
<p style="margin-bottom: 8px;"><strong>${t('swal_format_quality')}</strong> <span style="color: #ff4da6; text-transform: uppercase;">${currentFormat} (${currentQuality === '320' || currentQuality === '1080' ? t('quality_best') : currentQuality})</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: 'Klasörü Aç 📂',
|
||||
confirmButtonText: t('swal_btn_open_folder'),
|
||||
showCancelButton: true,
|
||||
cancelButtonText: 'Yeni İndirme 🔄',
|
||||
cancelButtonText: t('swal_btn_new_download'),
|
||||
confirmButtonColor: '#7000ff',
|
||||
cancelButtonColor: 'rgba(255, 255, 255, 0.1)',
|
||||
backdrop: 'rgba(0, 0, 0, 0.6)'
|
||||
@@ -308,7 +409,7 @@ downloadBtn.addEventListener('click', async () => {
|
||||
showError(result.error);
|
||||
downloadProgressPanel.style.display = 'none';
|
||||
if (statusBadge) {
|
||||
statusBadge.textContent = 'Hata';
|
||||
statusBadge.textContent = t('badge_error');
|
||||
statusBadge.className = 'status-badge ready';
|
||||
statusBadge.style.borderColor = 'rgba(255, 77, 77, 0.3)';
|
||||
statusBadge.style.color = '#ff4d4d';
|
||||
@@ -328,12 +429,13 @@ downloadBtn.addEventListener('click', async () => {
|
||||
function showError(msg) {
|
||||
let friendlyMsg = msg;
|
||||
if (msg.includes('Could not copy') && msg.includes('cookie database')) {
|
||||
friendlyMsg = 'Seçtiğiniz tarayıcı (Chrome/Edge/Brave vb.) şu anda arka planda açık olduğu için çerez veritabanı kilitlendi. Lütfen tarayıcı pencerelerinizi tamamen kapatıp ardından tekrar "Analiz Et" butonuna basın.';
|
||||
friendlyMsg = t('err_cookies_locked');
|
||||
} else if (msg.includes('You need to log in to access this content')) {
|
||||
friendlyMsg = 'Bu özel içeriğe erişmek için giriş yapılması gerekiyor. Lütfen aşağıdaki çerez seçeneğini aktif edip giriş yapmış olduğunuz tarayıcıyı seçin.';
|
||||
friendlyMsg = t('err_login_required');
|
||||
} else if (msg.includes('Failed to decrypt with DPAPI')) {
|
||||
friendlyMsg = 'Tarayıcı çerez anahtarı çözülemedi (DPAPI Hatası). Bu durum genellikle uygulamayı veya terminali "Yönetici Olarak Çalıştır" (Administrator) modunda açtığınızda oluşur. Çerezlerinize erişebilmek için lütfen uygulamayı normal kullanıcı modunda başlatıp tekrar deneyin.';
|
||||
friendlyMsg = t('err_dpapi_fail');
|
||||
}
|
||||
errorMessage.textContent = `Hata: ${friendlyMsg}`;
|
||||
errorMessage.textContent = `${t('err_prefix')}${friendlyMsg}`;
|
||||
errorCard.style.display = 'flex';
|
||||
}
|
||||
|
||||
|
||||
+124
@@ -775,3 +775,127 @@ input[type="text"]:focus {
|
||||
gap: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Premium Language Switcher Selector */
|
||||
.language-selector-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.language-select {
|
||||
background: rgba(23, 20, 38, 0.7);
|
||||
color: #dcd7eb;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 8px;
|
||||
padding: 4px 26px 4px 8px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
transition: all 0.25s ease;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23ff0080' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
background-size: 10px;
|
||||
}
|
||||
|
||||
.language-select:hover {
|
||||
background: rgba(23, 20, 38, 0.9);
|
||||
border-color: rgba(255, 0, 128, 0.5);
|
||||
box-shadow: 0 0 10px rgba(255, 0, 128, 0.2);
|
||||
}
|
||||
|
||||
.language-select option {
|
||||
background: #171426;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* RTL (Right to Left) Styles for Arabic Support */
|
||||
html[dir="rtl"] {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
html[dir="rtl"] * {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .text-center,
|
||||
html[dir="rtl"] .text-center * {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .custom-titlebar {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .titlebar-drag {
|
||||
padding-right: 16px;
|
||||
padding-left: 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .language-selector-wrapper {
|
||||
margin-right: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .language-select {
|
||||
padding: 4px 8px 4px 26px;
|
||||
background-position: left 8px center;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .back-button {
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .back-button:hover {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
html[dir="rtl"] .back-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
html[dir="rtl"] .track-item {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .track-details {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .track-details h4,
|
||||
html[dir="rtl"] .track-details p,
|
||||
html[dir="rtl"] .track-details span {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .progress-panel .subtext {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .option-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .format-btn {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .quality-pills {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .error-card {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user