開源專案 · 自由透明
MIT License
100% 開源透明 · 自主託管
本專案完全開源並由自建伺服器託管,不依賴 GitHub 或第三方代管平台。所有核心腳本與安裝邏輯皆開放檢閱,無隱藏代碼、無廣告、無隱私追蹤。
100% 透明無毒
純文字腳本直接可見,不收集任何個人隱私、下載記錄或裝置識別資訊。
基於開源成熟標準
核心奠基於 yt-dlp 與 FFmpeg,享有全世界最強大且持續更新的影音解析力。
MIT 自由授權協議
遵循 MIT 授權條款,允許個人自由學習研究、修改程式碼、二次封裝或私有備份。
自主託管 · 免平台限制
不綁定 GitHub 或任何第三方專案代管平台,源代碼、校驗碼與安裝包皆由全球 CDN 直發。
核心程式碼線上檢視
Python 3.10+
259 行
·
9.5 KB
import os
import sys
import subprocess
from pathlib import Path
# 自動檢查並安裝 yt-dlp
try:
import yt_dlp
except ImportError:
print("正在安裝 yt-dlp 套件...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"])
import yt_dlp
def get_safe_download_dir():
"""
取得具備寫入權限的安全下載路徑:
1. 優先嘗試在腳本同層建立 downloads 資料夾
2. 若遭遇 WinError 5 權限不足,自動切換至 Windows 系統使用者「下載」資料夾
"""
# 取得當前腳本本體所在的絕對路徑目錄
try:
script_dir = Path(__file__).resolve().parent
except NameError:
script_dir = Path.cwd()
target_dir = script_dir / "downloads"
try:
target_dir.mkdir(parents=True, exist_ok=True)
# 測試寫入權限
test_file = target_dir / ".perm_test"
test_file.touch()
test_file.unlink()
return str(target_dir)
except (PermissionError, OSError):
# 降級方案:改用 Windows 使用者個人「下載」資料夾
fallback_dir = Path.home() / "Downloads" / "YT_Downloads"
fallback_dir.mkdir(parents=True, exist_ok=True)
print(f"\n[提示] 腳本所在目錄無寫入權限,已自動改用系統下載路徑:\n {fallback_dir}")
return str(fallback_dir)
SUPPORTED_VIDEO_CONTAINERS = {
"1": ("mp4", "MP4 格式 (相容性最高,支援絕大多數裝置與剪輯軟體)"),
"2": ("mkv", "MKV 格式 (支援多音軌與多字幕封裝,容錯率高)"),
"3": ("webm", "WebM 格式 (VP9/AV1 + Opus 開源架構,適合網頁播放)"),
"4": ("mov", "MOV 格式 (QuickTime 封裝,適合 macOS 與 Final Cut Pro)"),
"5": ("avi", "AVI 格式 (傳統多媒體格式)"),
"6": ("flv", "FLV 格式 (Flash 串流視訊相容封裝)"),
}
SUPPORTED_AUDIO_CODECS = {
"1": ("mp3", "MP3 (通用度最高,預設 320kbps 高音質)"),
"2": ("m4a", "M4A (AAC 編碼,Apple 裝置最佳相容格式)"),
"3": ("aac", "AAC 原生音訊串流"),
"4": ("flac", "FLAC (無損壓縮音訊,音質完全不失真)"),
"5": ("wav", "WAV (無壓縮 PCM 原始音訊,檔案較大)"),
"6": ("opus", "Opus (低延遲高壓縮比,YouTube 原生最佳音訊編碼)"),
"7": ("alac", "ALAC (Apple 無損音訊編碼)"),
}
def format_bytes(size):
if not size:
return "未知大小"
for unit in ['B', 'KB', 'MB', 'GB']:
if size < 1024.0:
return f"{size:.2f} {unit}"
size /= 1024.0
return f"{size:.2f} TB"
def get_video_info(url):
ydl_opts = {'quiet': True, 'no_warnings': True}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
return ydl.extract_info(url, download=False)
def handle_video_download(url, info, download_dir):
print("\n" + "=" * 50)
print("【可選的影片輸出格式】")
for key, (fmt, desc) in SUPPORTED_VIDEO_CONTAINERS.items():
print(f" [{key}] {fmt.upper()} - {desc}")
print("=" * 50)
choice = input("請選擇目標格式編號 (預設 1 - MP4): ").strip()
target_format = SUPPORTED_VIDEO_CONTAINERS.get(choice, ("mp4", ""))[0]
ydl_opts = {
'format': 'bestvideo+bestaudio/best',
'merge_output_format': target_format,
'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
'noplaylist': True,
}
if target_format in ['avi', 'flv', 'mov']:
ydl_opts['postprocessors'] = [{
'key': 'FFmpegVideoConvertor',
'preferedformat': target_format,
}]
print(f"\n[執行下載] 畫質:最高畫質 + 最佳音軌 | 目標格式:{target_format.upper()}")
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
def handle_audio_download(url, info, download_dir):
print("\n" + "=" * 50)
print("【可選的音訊轉換格式】")
for key, (codec, desc) in SUPPORTED_AUDIO_CODECS.items():
print(f" [{key}] {codec.upper()} - {desc}")
print("=" * 50)
choice = input("請選擇目標音訊格式編號 (預設 1 - MP3): ").strip()
target_codec = SUPPORTED_AUDIO_CODECS.get(choice, ("mp3", ""))[0]
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
'noplaylist': True,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': target_codec,
'preferredquality': '320' if target_codec == 'mp3' else None,
}],
}
print(f"\n[執行下載] 擷取最佳音軌並轉碼為:{target_codec.upper()}")
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
def handle_raw_stream_selection(url, info, download_dir):
formats = info.get('formats', [])
if not formats:
print("未偵測到串流軌道資訊。")
return
print("\n" + "=" * 90)
print(f"{'Format ID':<12} {'副檔名':<8} {'類型':<10} {'解析度/頻寬':<18} {'FPS':<6} {'編碼格式':<18} {'預估大小'}")
print("-" * 90)
for f in formats:
fid = f.get('format_id', 'N/A')
ext = f.get('ext', 'N/A')
vcodec = f.get('vcodec', 'none')
acodec = f.get('acodec', 'none')
fps = str(f.get('fps', '')) if f.get('fps') else '-'
filesize = format_bytes(f.get('filesize') or f.get('filesize_approx'))
if vcodec != 'none' and acodec != 'none':
stream_type = "影音合成"
detail = f"{f.get('width', '?')}x{f.get('height', '?')}"
codec_str = f"{vcodec[:7]}+{acodec[:7]}"
elif vcodec != 'none':
stream_type = "純影像軌"
detail = f"{f.get('width', '?')}x{f.get('height', '?')} ({f.get('format_note', '')})"
codec_str = vcodec
elif acodec != 'none':
stream_type = "純音訊軌"
abr = f.get('abr', '?')
detail = f"{abr} kbps"
codec_str = acodec
else:
stream_type = "其他"
detail = f.get('format_note', '-')
codec_str = "unknown"
print(f"{fid:<12} {ext:<8} {stream_type:<10} {detail:<18} {fps:<6} {codec_str:<18} {filesize}")
print("=" * 90)
print("【輸入範例】")
print(" - 單軌下載:直接輸入編號,如 `137`")
print(" - 合併軌道:輸入 `影像編號+音訊編號`,如 `137+140`(將自動以 FFmpeg 合成)")
format_selection = input("\n請輸入想要下載的 Format ID 組合: ").strip()
if not format_selection:
print("未輸入編號,取消操作。")
return
ydl_opts = {
'format': format_selection,
'merge_output_format': 'mp4',
'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
}
print(f"\n[執行下載] 正在抓取指定串流:{format_selection}")
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
def main():
print("=" * 60)
print(" yt-dlp 全格式自選進階下載器(權限加強版)")
print("=" * 60)
# 1. 取得絕對安全且有權限的儲存目錄
download_dir = get_safe_download_dir()
print(f"檔案儲存路徑已設定為:{download_dir}\n")
url = input("請輸入 YouTube 影片網址: ").strip()
if not url:
print("未輸入網址,程式結束。")
return
print("\n正在向 YouTube 解析影片資訊與可用串流軌道,請稍候...")
try:
info = get_video_info(url)
except Exception as e:
print(f"\n[解析失敗]:{e}")
return
title = info.get('title', '未知標題')
duration = info.get('duration', 0)
uploader = info.get('uploader', '未知上傳者')
print(f"\n影片標題:{title}")
print(f"發布頻道:{uploader}")
print(f"影片長度:{duration // 60} 分 {duration % 60} 秒")
print("\n請選擇下載處理模式:")
print(" [1] 影片下載(自動選最佳畫質音訊,並由您挑選輸出格式:MP4 / MKV / WebM / MOV / AVI 等)")
print(" [2] 純音訊下載(自動提取最高音質,並轉碼為:MP3 / M4A / FLAC / WAV / OPUS 等)")
print(" [3] 進階串流手動模式(列出伺服器上所有 Format ID 軌道供您自訂搭配)")
mode = input("\n請選擇模式 (1-3,預設 1): ").strip()
try:
if mode == '2':
handle_audio_download(url, info, download_dir)
elif mode == '3':
handle_raw_stream_selection(url, info, download_dir)
else:
handle_video_download(url, info, download_dir)
print(f"\n[成功] 處理完成!檔案儲存於:{download_dir}")
except Exception as e:
print(f"\n[下載處理失敗]:{e}")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "--cli":
try:
main()
except Exception as general_err:
print(f"\n程式異常終止:{general_err}")
finally:
input("\n請按 Enter 鍵退出程式...")
else:
try:
import YT_GUI
YT_GUI.main()
except Exception as e:
print(f"[提示] 無法啟動圖形介面 ({e}),切換至命令列模式:\n")
try:
main()
except Exception as general_err:
print(f"\n程式異常終止:{general_err}")
finally:
input("\n請按 Enter 鍵退出程式...")
PowerShell
178 行
·
7.9 KB
$ErrorActionPreference = 'Stop'
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host " YouTube Downloader - Auto Installer " -ForegroundColor Cyan
Write-Host "==========================================================" -ForegroundColor Cyan
Write-Host ""
$appName = "YouTube Downloader"
$targetDir = Join-Path $env:LOCALAPPDATA "Programs\YouTube Downloader"
$targetExe = Join-Path $targetDir "YouTube_Downloader.exe"
$targetIcon = Join-Path $targetDir "app_icon.ico"
$tempZip = Join-Path $env:TEMP ("yt_install_" + [Guid]::NewGuid().ToString("N") + ".zip")
$baseUrl = "https://youtube-downloader-7g5.pages.dev/assets/"
try {
# 1. Prepare installation directory and stop existing process
Write-Host "[1/4] Preparing installation directory..." -ForegroundColor Gray
try {
Get-Process -Name "YouTube_Downloader" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 500
} catch {}
if (-not (Test-Path $targetDir)) {
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
}
# 2. Download package chunks from Cloudflare CDN
Write-Host "[2/4] Downloading core components from Cloudflare CDN..." -ForegroundColor Yellow
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor 3072
$wc = New-Object System.Net.WebClient
$wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
# Fetch chunk list (supports dynamic manifest.json)
$chunks = @()
try {
$manifestJson = $wc.DownloadString($baseUrl + "manifest.json")
$manifest = $manifestJson | ConvertFrom-Json
$chunks = $manifest.chunks | ForEach-Object { $_.name }
} catch {
$chunks = @("payload.zip.001", "payload.zip.002")
}
$fs = New-Object System.IO.FileStream($tempZip, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
try {
$idx = 1
foreach ($c in $chunks) {
Write-Host " Fetching chunk ($idx/$($chunks.Count)): $c..." -ForegroundColor Gray
$bytes = $wc.DownloadData($baseUrl + $c)
$fs.Write($bytes, 0, $bytes.Length)
$idx++
}
} finally {
$fs.Close()
$fs.Dispose()
}
# 3. Extract components to target directory
Write-Host "[3/4] Extracting components to target directory..." -ForegroundColor Gray
Add-Type -AssemblyName System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::OpenRead($tempZip)
try {
foreach ($entry in $zip.Entries) {
$dest = [System.IO.Path]::Combine($targetDir, $entry.FullName)
$pDir = [System.IO.Path]::GetDirectoryName($dest)
if (-not [string]::IsNullOrEmpty($pDir) -and -not (Test-Path $pDir)) {
New-Item -ItemType Directory -Path $pDir -Force | Out-Null
}
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $dest, $true)
}
} finally {
$zip.Dispose()
}
# Remove Mark-of-the-Web zone identifier to prevent Windows SmartScreen blocks
try {
Get-ChildItem -Path $targetDir -Recurse | Unblock-File -ErrorAction SilentlyContinue
} catch {}
# 4. Create shortcuts and register application in Windows Settings
Write-Host "[4/4] Creating shortcuts and registering application..." -ForegroundColor Gray
$ws = New-Object -ComObject WScript.Shell
# Desktop shortcut
$desktop = [Environment]::GetFolderPath('Desktop')
$desktopLnkPath = Join-Path $desktop "$appName.lnk"
$desktopLnk = $ws.CreateShortcut($desktopLnkPath)
$desktopLnk.TargetPath = $targetExe
$desktopLnk.WorkingDirectory = $targetDir
if (Test-Path $targetIcon) { $desktopLnk.IconLocation = "$targetIcon,0" }
$desktopLnk.Save()
try { Unblock-File -Path $desktopLnkPath -ErrorAction SilentlyContinue } catch {}
# Start Menu shortcut
$startMenu = [Environment]::GetFolderPath('Programs')
$startLnkPath = Join-Path $startMenu "$appName.lnk"
$startLnk = $ws.CreateShortcut($startLnkPath)
$startLnk.TargetPath = $targetExe
$startLnk.WorkingDirectory = $targetDir
if (Test-Path $targetIcon) { $startLnk.IconLocation = "$targetIcon,0" }
$startLnk.Save()
try { Unblock-File -Path $startLnkPath -ErrorAction SilentlyContinue } catch {}
# Register in Windows Settings Installed apps
$regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\YouTubeDownloader"
if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
Set-ItemProperty -Path $regPath -Name "DisplayName" -Value "YouTube Downloader"
Set-ItemProperty -Path $regPath -Name "DisplayVersion" -Value "1.2.0"
Set-ItemProperty -Path $regPath -Name "Publisher" -Value "Ryan"
if (Test-Path $targetIcon) { Set-ItemProperty -Path $regPath -Name "DisplayIcon" -Value $targetIcon }
Set-ItemProperty -Path $regPath -Name "InstallLocation" -Value $targetDir
Set-ItemProperty -Path $regPath -Name "UninstallString" -Value "`"$targetExe`" --uninstall"
Set-ItemProperty -Path $regPath -Name "QuietUninstallString" -Value "`"$targetExe`" --uninstall"
Set-ItemProperty -Path $regPath -Name "EstimatedSize" -Value 42000
Set-ItemProperty -Path $regPath -Name "NoModify" -Value 1
Set-ItemProperty -Path $regPath -Name "NoRepair" -Value 1
# 5. Save UI language setting to setup_config.ini
try {
$uiLang = [System.Globalization.CultureInfo]::CurrentUICulture.Name
$langCode = "en"
if ($uiLang -like "zh-TW*" -or $uiLang -like "zh-HK*" -or $uiLang -like "zh-MO*") { $langCode = "zh-TW" }
elseif ($uiLang -like "zh*") { $langCode = "zh-CN" }
elseif ($uiLang -like "ja*") { $langCode = "ja" }
elseif ($uiLang -like "ko*") { $langCode = "ko" }
elseif ($uiLang -like "es*") { $langCode = "es" }
elseif ($uiLang -like "fr*") { $langCode = "fr" }
elseif ($uiLang -like "de*") { $langCode = "de" }
elseif ($uiLang -like "ru*") { $langCode = "ru" }
elseif ($uiLang -like "pt*") { $langCode = "pt" }
elseif ($uiLang -like "it*") { $langCode = "it" }
elseif ($uiLang -like "ar*") { $langCode = "ar" }
$cfgPath = Join-Path $targetDir "setup_config.ini"
if (-not (Test-Path $cfgPath)) {
Set-Content -Path $cfgPath -Value "lang=$langCode`r`n" -Encoding UTF8
}
} catch {}
Write-Host ""
Write-Host "==========================================================" -ForegroundColor Green
Write-Host " Installation completed successfully! " -ForegroundColor Green
Write-Host "==========================================================" -ForegroundColor Green
Write-Host "Created shortcut: Desktop\$appName.lnk" -ForegroundColor Green
Write-Host "You can uninstall anytime from Windows Settings > Installed apps." -ForegroundColor Cyan
Write-Host ""
} catch {
Write-Host "Installation failed: $($_.Exception.Message)" -ForegroundColor Red
} finally {
if (Test-Path $tempZip) {
Remove-Item -Path $tempZip -Force -ErrorAction SilentlyContinue
}
}
# 6. Launch main application via Windows Shell (isolated from install status)
Write-Host "Starting YouTube Downloader..." -ForegroundColor Green
$started = $false
try {
Start-Process -FilePath "explorer.exe" -ArgumentList "`"$desktopLnkPath`"" -ErrorAction Stop
$started = $true
} catch {
try {
Start-Process -FilePath "explorer.exe" -ArgumentList "`"$targetExe`"" -ErrorAction Stop
$started = $true
} catch {
try {
$ws = New-Object -ComObject WScript.Shell
$ws.Run("`"$targetExe`"", 1, $false)
$started = $true
} catch {}
}
}
if (-not $started) {
Write-Host "Notice: Please open YouTube Downloader from your Desktop shortcut." -ForegroundColor Yellow
}
C# .NET
1778 行
·
81.8 KB
using System;
using System.IO;
using System.Reflection;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO.Compression;
using System.Net;
using System.Text.RegularExpressions;
using System.Collections.Generic;
[assembly: AssemblyTitle("YouTube Downloader Setup")]
[assembly: AssemblyDescription("YouTube Video and Audio Downloader Installer")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("YouTube Downloader Project")]
[assembly: AssemblyProduct("YouTube Downloader")]
[assembly: AssemblyCopyright("Copyright © 2026 YouTube Downloader")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
public class InstallLang
{
public string Code;
public string DisplayName;
public bool IsRtl;
public string AppTitle;
public string WizardTitle;
public string HeaderSubtitle;
public string DestFolder;
public string Browse;
public string BrowseDesc;
public string ShortcutDesktop;
public string ShortcutStart;
public string LaunchAfter;
public string InstallNow;
public string Cancel;
public string Ok;
public string SelectLangTitle;
public string SelectLangPrompt;
public string Preparing;
public string Connecting;
public string CreatingFolder;
public string ExtractingLocal;
public string CopyingLocal;
public string Downloading;
public string DownloadingChunk;
public string ExtractingDeploying;
public string CreatingShortcuts;
public string ShortcutsDetail;
public string Registering;
public string RegisterDetail;
public string InstallDone;
public string InstallDoneDetail;
public string Finish;
public string ErrorTitle;
public string ErrorPrompt;
public string UninstallWizardTitle;
public string UninstallHeaderTitle;
public string UninstallHeaderSub;
public string UninstallPrompt;
public string UninstallDetail;
public string UninstallAction;
public string UninstallProgressTitle;
public string UninstallProgressDetail;
public string UninstallDoneTitle;
public string UninstallDoneDetail;
public override string ToString()
{
return DisplayName;
}
}
public static class I18n
{
private static readonly Dictionary<string, InstallLang> _langs = new Dictionary<string, InstallLang>(StringComparer.OrdinalIgnoreCase);
public static readonly List<InstallLang> All = new List<InstallLang>();
static I18n()
{
Add(new InstallLang {
Code = "zh-TW",
DisplayName = "繁體中文 (Traditional Chinese)",
IsRtl = false,
AppTitle = "YouTube 影音下載器",
WizardTitle = "{0} - 安裝精靈",
HeaderSubtitle = "超輕量線上安裝器 | 支援 4K 影片與 MP3 音訊下載",
DestFolder = "安裝目的地資料夾:",
Browse = "瀏覽...",
BrowseDesc = "選擇 YouTube 影音下載器 的安裝目錄",
ShortcutDesktop = "在桌面建立捷徑",
ShortcutStart = "在開始功能表建立捷徑",
LaunchAfter = "安裝完成後立即啟動程式",
InstallNow = "立即安裝",
Cancel = "取消",
Ok = "確定",
SelectLangTitle = "選擇安裝語言",
SelectLangPrompt = "請選擇安裝時使用的語言:",
Preparing = "正在準備安裝環境...",
Connecting = "正在連接伺服器...",
CreatingFolder = "正在建立安裝資料夾...",
ExtractingLocal = "正在從本機離線檔案解壓縮組件...",
CopyingLocal = "正在複製本機組件...",
Downloading = "正在自雲端下載核心組件...",
DownloadingChunk = "正在自雲端下載核心組件 ({0}/{1})...",
ExtractingDeploying = "正在解壓縮與部署核心檔案...",
CreatingShortcuts = "正在建立捷徑與系統關聯...",
ShortcutsDetail = "桌面與開始功能表",
Registering = "正在向 Windows 註冊應用程式資訊...",
RegisterDetail = "支援 Windows 設定直接解除安裝",
InstallDone = "安裝完成!",
InstallDoneDetail = "所有組件已配置完畢,已就緒可供使用。",
Finish = "完成",
ErrorTitle = "安裝錯誤",
ErrorPrompt = "安裝過程發生錯誤:\n{0}\n\n提示:若無法自雲端下載,可將 payload.zip 置於安裝檔同目錄下進行離線安裝。",
UninstallWizardTitle = "{0} - 解除安裝精靈",
UninstallHeaderTitle = "解除安裝 {0}",
UninstallHeaderSub = "從您的電腦完全移除此應用程式及其桌面捷徑",
UninstallPrompt = "您確定要從這部電腦完整解除安裝「{0}」嗎?",
UninstallDetail = "此操作將會永久清理所有安裝的程式檔案、桌面與開始功能表捷徑,以及系統設定清單中的記錄。",
UninstallAction = "確定解除安裝",
UninstallProgressTitle = "正在解除安裝中,請稍候...",
UninstallProgressDetail = "正在結束處理程序並清理檔案捷徑與系統註冊表記錄...",
UninstallDoneTitle = "解除安裝已順利完成!",
UninstallDoneDetail = "「{0}」已完全從您的電腦中移除。感謝您的使用!"
});
Add(new InstallLang {
Code = "zh-CN",
DisplayName = "简体中文 (Simplified Chinese)",
IsRtl = false,
AppTitle = "YouTube 影音下载器",
WizardTitle = "{0} - 安装向导",
HeaderSubtitle = "超轻量在线安装器 | 支持 4K 视频与 MP3 音频下载",
DestFolder = "安装目标文件夹:",
Browse = "浏览...",
BrowseDesc = "选择 YouTube 影音下载器 的安装目录",
ShortcutDesktop = "在桌面创建快捷方式",
ShortcutStart = "在开始菜单创建快捷方式",
LaunchAfter = "安装完成后立即启动程序",
InstallNow = "立即安装",
Cancel = "取消",
Ok = "确定",
SelectLangTitle = "选择安装语言",
SelectLangPrompt = "请选择安装时使用的语言:",
Preparing = "正在准备安装环境...",
Connecting = "正在连接服务器...",
CreatingFolder = "正在创建安装文件夹...",
ExtractingLocal = "正在从本地离线文件解压缩组件...",
CopyingLocal = "正在复制本地组件...",
Downloading = "正在自云端下载核心组件...",
DownloadingChunk = "正在自云端下载核心组件 ({0}/{1})...",
ExtractingDeploying = "正在解压缩与部署核心文件...",
CreatingShortcuts = "正在创建快捷方式与系统关联...",
ShortcutsDetail = "桌面与开始菜单",
Registering = "正在向 Windows 注册应用程序信息...",
RegisterDetail = "支持 Windows 设置直接卸载",
InstallDone = "安装完成!",
InstallDoneDetail = "所有组件已配置完毕,已就绪可供使用。",
Finish = "完成",
ErrorTitle = "安装错误",
ErrorPrompt = "安装过程发生错误:\n{0}\n\n提示:若无法自云端下载,可将 payload.zip 置于安装包同目录下进行离线安装。",
UninstallWizardTitle = "{0} - 卸载向导",
UninstallHeaderTitle = "卸载 {0}",
UninstallHeaderSub = "从您的电脑完全移除此应用程序及其桌面快捷方式",
UninstallPrompt = "您确定要从这台电脑完整卸载「{0}」吗?",
UninstallDetail = "此操作将会永久清理所有安装的程序文件、桌面与开始菜单快捷方式,以及系统设置列表中的记录。",
UninstallAction = "确定卸载",
UninstallProgressTitle = "正在卸载中,请稍候...",
UninstallProgressDetail = "正在结束进程并清理文件快捷方式与系统注册表记录...",
UninstallDoneTitle = "卸载已顺利完成!",
UninstallDoneDetail = "「{0}」已完全从您的电脑中移除。感谢您的使用!"
});
Add(new InstallLang {
Code = "en",
DisplayName = "English",
IsRtl = false,
AppTitle = "YouTube Video Downloader",
WizardTitle = "{0} - Setup",
HeaderSubtitle = "Ultra-lightweight Online Installer | Supports 4K Video & MP3 Audio",
DestFolder = "Destination Folder:",
Browse = "Browse...",
BrowseDesc = "Select Installation Folder for YouTube Video Downloader",
ShortcutDesktop = "Create desktop shortcut",
ShortcutStart = "Create Start Menu shortcut",
LaunchAfter = "Launch application after installation",
InstallNow = "Install Now",
Cancel = "Cancel",
Ok = "OK",
SelectLangTitle = "Select Setup Language",
SelectLangPrompt = "Select the language to use during the installation:",
Preparing = "Preparing installation environment...",
Connecting = "Connecting to server...",
CreatingFolder = "Creating installation folder...",
ExtractingLocal = "Extracting components from local package...",
CopyingLocal = "Copying local components...",
Downloading = "Downloading core components from cloud...",
DownloadingChunk = "Downloading core components ({0}/{1})...",
ExtractingDeploying = "Extracting and deploying core files...",
CreatingShortcuts = "Creating shortcuts and system associations...",
ShortcutsDetail = "Desktop and Start Menu",
Registering = "Registering application with Windows...",
RegisterDetail = "Enables uninstall via Windows Settings",
InstallDone = "Installation Complete!",
InstallDoneDetail = "All components have been configured and are ready to use.",
Finish = "Finish",
ErrorTitle = "Installation Error",
ErrorPrompt = "An error occurred during installation:\n{0}\n\nTip: You can place payload.zip in the same directory for offline installation.",
UninstallWizardTitle = "{0} - Uninstall Wizard",
UninstallHeaderTitle = "Uninstall {0}",
UninstallHeaderSub = "Completely remove this application and shortcuts from your computer",
UninstallPrompt = "Are you sure you want to completely uninstall \"{0}\"?",
UninstallDetail = "This will permanently remove all program files, shortcuts, and system entries.",
UninstallAction = "Uninstall",
UninstallProgressTitle = "Uninstalling, please wait...",
UninstallProgressDetail = "Closing processes and cleaning up files, shortcuts, and registry entries...",
UninstallDoneTitle = "Uninstallation Complete",
UninstallDoneDetail = "\"{0}\" has been completely removed from your computer. Thank you for using it!"
});
Add(new InstallLang {
Code = "ja",
DisplayName = "日本語 (Japanese)",
IsRtl = false,
AppTitle = "YouTube 動画ダウンローダー",
WizardTitle = "{0} - セットアップ",
HeaderSubtitle = "超軽量オンラインインストーラー | 4K 動画&MP3 音声保存対応",
DestFolder = "インストール先フォルダー:",
Browse = "参照...",
BrowseDesc = "YouTube 動画ダウンローダーのインストール先を選択",
ShortcutDesktop = "デスクトップ ショートカットを作成する",
ShortcutStart = "スタートメニューに登録する",
LaunchAfter = "インストール完了後に起動する",
InstallNow = "今すぐインストール",
Cancel = "キャンセル",
Ok = "OK",
SelectLangTitle = "セットアップ言語の選択",
SelectLangPrompt = "インストール中に使用する言語を選択してください:",
Preparing = "インストール環境を準備しています...",
Connecting = "サーバーに接続しています...",
CreatingFolder = "インストール先フォルダーを作成しています...",
ExtractingLocal = "ローカルパッケージから展開しています...",
CopyingLocal = "ローカルコンポーネントをコピーしています...",
Downloading = "クラウドからコンポーネントをダウンロードしています...",
DownloadingChunk = "クラウドからコンポーネントをダウンロード中 ({0}/{1})...",
ExtractingDeploying = "ファイルを展開して配置しています...",
CreatingShortcuts = "ショートカットと関連付けを作成しています...",
ShortcutsDetail = "デスクトップとスタートメニュー",
Registering = "Windows システムに登録しています...",
RegisterDetail = "Windows 設定からのアンインストールに対応",
InstallDone = "インストール完了!",
InstallDoneDetail = "すべてのコンポーネントの配置が完了し、使用可能です。",
Finish = "完了",
ErrorTitle = "インストールエラー",
ErrorPrompt = "インストール中にエラーが発生しました:\n{0}\n\nヒント: 同じフォルダーに payload.zip を配置するとオフラインインストールが可能です。",
UninstallWizardTitle = "{0} - アンインストール",
UninstallHeaderTitle = "{0} のアンインストール",
UninstallHeaderSub = "このアプリケーションとショートカットを完全に削除します",
UninstallPrompt = "コンピューターから「{0}」を完全にアンインストールしますか?",
UninstallDetail = "プログラムファイル、デスクトップおよびスタートメニューのショートカット、システム登録情報が削除されます。",
UninstallAction = "アンインストール",
UninstallProgressTitle = "アンインストールしています。しばらくお待ちください...",
UninstallProgressDetail = "プロセスを終了し、ファイル、ショートカット、レジストリをクリーンアップしています...",
UninstallDoneTitle = "アンインストール完了",
UninstallDoneDetail = "「{0}」はコンピューターから完全に削除されました。ご利用ありがとうございました!"
});
Add(new InstallLang {
Code = "ko",
DisplayName = "한국어 (Korean)",
IsRtl = false,
AppTitle = "YouTube 비디오 다운로더",
WizardTitle = "{0} - 설치 마법사",
HeaderSubtitle = "초경량 온라인 설치 프로그램 | 4K 비디오 및 MP3 오디오 지원",
DestFolder = "설치 위치 폴더:",
Browse = "찾아보기...",
BrowseDesc = "YouTube 비디오 다운로더 설치 폴더 선택",
ShortcutDesktop = "바탕화면에 바로가기 만들기",
ShortcutStart = "시작 메뉴에 등록",
LaunchAfter = "설치 완료 후 프로그램 실행",
InstallNow = "지금 설치",
Cancel = "취소",
Ok = "확인",
SelectLangTitle = "설치 언어 선택",
SelectLangPrompt = "설치 중 사용할 언어를 선택하세요:",
Preparing = "설치 환경을 준비하는 중입니다...",
Connecting = "서버에 연결하는 중입니다...",
CreatingFolder = "설치 폴더를 생성하는 중입니다...",
ExtractingLocal = "로컬 오프라인 패키지에서 압축 해제 중...",
CopyingLocal = "로컬 구성 요소를 복사하는 중...",
Downloading = "클라우드에서 핵심 구성 요소를 다운로드하는 중...",
DownloadingChunk = "클라우드에서 구성 요소 다운로드 중 ({0}/{1})...",
ExtractingDeploying = "핵심 파일을 압축 해제하고 배치하는 중...",
CreatingShortcuts = "바로가기 및 시스템 연결을 생성하는 중...",
ShortcutsDetail = "바탕화면 및 시작 메뉴",
Registering = "Windows 시스템에 애플리케이션 등록 중...",
RegisterDetail = "Windows 설정에서 직접 제거 가능",
InstallDone = "설치 완료!",
InstallDoneDetail = "모든 구성 요소가 구성되었으며 사용할 준비가 되었습니다.",
Finish = "완료",
ErrorTitle = "설치 오류",
ErrorPrompt = "설치 중 오류가 발생했습니다:\n{0}\n\n팁: 동일한 디렉터리에 payload.zip을 두면 오프라인으로 설치할 수 있습니다.",
UninstallWizardTitle = "{0} - 제거 마법사",
UninstallHeaderTitle = "{0} 제거",
UninstallHeaderSub = "컴퓨터에서 이 응용 프로그램과 바로가기를 완전히 제거합니다"
,
UninstallPrompt = "컴퓨터에서 \"{0}\"을(를) 완전히 제거하시겠습니까?",
UninstallDetail = "설치된 모든 프로그램 파일, 바로가기 및 시스템 설정 목록의 기록이 영구적으로 정리됩니다.",
UninstallAction = "제거",
UninstallProgressTitle = "제거하는 중입니다. 잠시 기다려 주세요...",
UninstallProgressDetail = "프로세스를 종료하고 파일, 바로가기 및 레지스트리 항목을 정리하는 중입니다...",
UninstallDoneTitle = "제거 완료",
UninstallDoneDetail = "\"{0}\"이(가) 컴퓨터에서 완전히 제거되었습니다. 이용해 주셔서 감사합니다!"
});
Add(new InstallLang {
Code = "es",
DisplayName = "Español (Spanish)",
IsRtl = false,
AppTitle = "Descargador de YouTube",
WizardTitle = "{0} - Asistente de instalación",
HeaderSubtitle = "Instalador ultraligero | Soporta vídeo 4K y audio MP3",
DestFolder = "Carpeta de destino:",
Browse = "Examinar...",
BrowseDesc = "Seleccionar carpeta de instalación de Descargador de YouTube",
ShortcutDesktop = "Crear acceso directo en el escritorio",
ShortcutStart = "Crear acceso directo en el menú Inicio",
LaunchAfter = "Iniciar la aplicación al completar la instalación",
InstallNow = "Instalar ahora",
Cancel = "Cancelar",
Ok = "Aceptar",
SelectLangTitle = "Seleccionar idioma de instalación",
SelectLangPrompt = "Seleccione el idioma que se utilizará durante la instalación:",
Preparing = "Preparando el entorno de instalación...",
Connecting = "Conectando al servidor...",
CreatingFolder = "Creando carpeta de instalación...",
ExtractingLocal = "Extrayendo componentes del paquete local...",
CopyingLocal = "Copiando componentes locales...",
Downloading = "Descargando componentes principales desde la nube...",
DownloadingChunk = "Descargando componentes ({0}/{1})...",
ExtractingDeploying = "Extrayendo e implementando archivos principales...",
CreatingShortcuts = "Creando accesos directos y asociaciones del sistema...",
ShortcutsDetail = "Escritorio y menú Inicio",
Registering = "Registrando la aplicación en Windows...",
RegisterDetail = "Permite desinstalar desde Configuración de Windows",
InstallDone = "¡Instalación completada!",
InstallDoneDetail = "Todos los componentes se han configurado y están listos para usar.",
Finish = "Finalizar",
ErrorTitle = "Error de instalación",
ErrorPrompt = "Ocurrió un error durante la instalación:\n{0}\n\nConsejo: Puede colocar payload.zip en el mismo directorio para instalación sin conexión.",
UninstallWizardTitle = "{0} - Asistente de desinstalación",
UninstallHeaderTitle = "Desinstalar {0}",
UninstallHeaderSub = "Eliminar completamente esta aplicación y sus accesos directos del equipo",
UninstallPrompt = "¿Está seguro de que desea desinstalar \"{0}\" de este equipo?",
UninstallDetail = "Esto eliminará permanentemente todos los archivos del programa, accesos directos y entradas del sistema.",
UninstallAction = "Desinstalar",
UninstallProgressTitle = "Desinstalando, por favor espere...",
UninstallProgressDetail = "Cerrando procesos y limpiando archivos, accesos directos y registros...",
UninstallDoneTitle = "Desinstalación completada",
UninstallDoneDetail = "\"{0}\" se ha eliminado por completo de su equipo. ¡Gracias por usarlo!"
});
Add(new InstallLang {
Code = "fr",
DisplayName = "Français (French)",
IsRtl = false,
AppTitle = "Téléchargeur YouTube",
WizardTitle = "{0} - Assistant d'installation",
HeaderSubtitle = "Installateur ultra-léger | Prise en charge vidéo 4K et audio MP3",
DestFolder = "Dossier de destination :",
Browse = "Parcourir...",
BrowseDesc = "Sélectionner le dossier d'installation de Téléchargeur YouTube",
ShortcutDesktop = "Créer un raccourci sur le bureau",
ShortcutStart = "Créer un raccourci dans le menu Démarrer",
LaunchAfter = "Lancer l'application après l'installation",
InstallNow = "Installer maintenant",
Cancel = "Annuler",
Ok = "OK",
SelectLangTitle = "Sélectionner la langue de l'installation",
SelectLangPrompt = "Sélectionnez la langue à utiliser pendant l'installation :",
Preparing = "Préparation de l'environnement d'installation...",
Connecting = "Connexion au serveur...",
CreatingFolder = "Création du dossier d'installation...",
ExtractingLocal = "Extraction des composants locaux...",
CopyingLocal = "Copie des composants locaux...",
Downloading = "Téléchargement des composants depuis le cloud...",
DownloadingChunk = "Téléchargement des composants ({0}/{1})...",
ExtractingDeploying = "Extraction et déploiement des fichiers...",
CreatingShortcuts = "Création des raccourcis et associations système...",
ShortcutsDetail = "Bureau et menu Démarrer",
Registering = "Enregistrement de l'application dans Windows...",
RegisterDetail = "Permet la désinstallation via les paramètres Windows",
InstallDone = "Installation terminée !",
InstallDoneDetail = "Tous les composants ont été configurés et sont prêts à être utilisés.",
Finish = "Terminer",
ErrorTitle = "Erreur d'installation",
ErrorPrompt = "Une erreur est survenue pendant l'installation :\n{0}\n\nAstuce : Vous pouvez placer payload.zip dans le même dossier pour une installation hors ligne.",
UninstallWizardTitle = "{0} - Assistant de désinstallation",
UninstallHeaderTitle = "Désinstaller {0}",
UninstallHeaderSub = "Supprimer complètement cette application et ses raccourcis de votre ordinateur",
UninstallPrompt = "Voulez-vous vraiment désinstaller \"{0}\" de cet ordinateur ?",
UninstallDetail = "Cette opération supprimera définitivement tous les fichiers du programme, les raccourcis et les entrées système.",
UninstallAction = "Désinstaller",
UninstallProgressTitle = "Désinstallation en cours, veuillez patienter...",
UninstallProgressDetail = "Fermeture des processus et nettoyage des fichiers, raccourcis et entrées de registre...",
UninstallDoneTitle = "Désinstallation terminée",
UninstallDoneDetail = "\"{0}\" a été complètement supprimé de votre ordinateur. Merci de votre confiance !"
});
Add(new InstallLang {
Code = "de",
DisplayName = "Deutsch (German)",
IsRtl = false,
AppTitle = "YouTube Video Downloader",
WizardTitle = "{0} - Setup-Assistent",
HeaderSubtitle = "Ultraleichter Online-Installer | Unterstützt 4K-Video & MP3-Audio",
DestFolder = "Zielordner:",
Browse = "Durchsuchen...",
BrowseDesc = "Installationsordner für YouTube Video Downloader auswählen",
ShortcutDesktop = "Desktop-Verknüpfung erstellen",
ShortcutStart = "Startmenü-Verknüpfung erstellen",
LaunchAfter = "Anwendung nach der Installation starten",
InstallNow = "Jetzt installieren",
Cancel = "Abbrechen",
Ok = "OK",
SelectLangTitle = "Setup-Sprache auswählen",
SelectLangPrompt = "Wählen Sie die Sprache für die Installation aus:",
Preparing = "Installationsumgebung wird vorbereitet...",
Connecting = "Verbindung zum Server wird hergestellt...",
CreatingFolder = "Installationsordner wird erstellt...",
ExtractingLocal = "Komponenten aus lokalem Paket werden entpackt...",
CopyingLocal = "Lokale Komponenten werden kopiert...",
Downloading = "Kernkomponenten werden aus der Cloud heruntergeladen...",
DownloadingChunk = "Komponenten werden heruntergeladen ({0}/{1})...",
ExtractingDeploying = "Dateien werden entpackt und bereitgestellt...",
CreatingShortcuts = "Verknüpfungen und Systemzuordnungen werden erstellt...",
ShortcutsDetail = "Desktop und Startmenü",
Registering = "Anwendung wird bei Windows registriert...",
RegisterDetail = "Ermöglicht Deinstallation über Windows-Einstellungen",
InstallDone = "Installation abgeschlossen!",
InstallDoneDetail = "Alle Komponenten wurden konfiguriert und sind einsatzbereit.",
Finish = "Fertigstellen",
ErrorTitle = "Installationsfehler",
ErrorPrompt = "Während der Installation ist ein Fehler aufgetreten:\n{0}\n\nTipp: Sie können payload.zip in denselben Ordner legen, um offline zu installieren.",
UninstallWizardTitle = "{0} - Deinstallations-Assistent",
UninstallHeaderTitle = "{0} deinstallieren",
UninstallHeaderSub = "Entfernt diese Anwendung und Verknüpfungen vollständig von Ihrem Computer",
UninstallPrompt = "Möchten Sie \"{0}\" wirklich von diesem Computer deinstallieren?",
UninstallDetail = "Dadurch werden alle Programmdateien, Verknüpfungen und Systemeinträge dauerhaft bereinigt.",
UninstallAction = "Deinstallieren",
UninstallProgressTitle = "Deinstallation läuft, bitte warten...",
UninstallProgressDetail = "Prozesse werden beendet, Dateien, Verknüpfungen und Registrierungseinträge werden bereinigt...",
UninstallDoneTitle = "Deinstallation abgeschlossen",
UninstallDoneDetail = "\"{0}\" wurde vollständig von Ihrem Computer entfernt. Vielen Dank für die Nutzung!"
});
Add(new InstallLang {
Code = "ru",
DisplayName = "Русский (Russian)",
IsRtl = false,
AppTitle = "Загрузчик видео с YouTube",
WizardTitle = "{0} - Мастер установки",
HeaderSubtitle = "Легкий онлайн-установщик | Поддержка 4K видео и MP3 аудио",
DestFolder = "Папка назначения:",
Browse = "Обзор...",
BrowseDesc = "Выберите папку для установки Загрузчика видео с YouTube",
ShortcutDesktop = "Создать ярлык на рабочем столе",
ShortcutStart = "Создать ярлык в меню «Пуск»",
LaunchAfter = "Запустить программу после завершения установки",
InstallNow = "Установить сейчас",
Cancel = "Отмена",
Ok = "ОК",
SelectLangTitle = "Выбор языка установки",
SelectLangPrompt = "Выберите язык для процесса установки:",
Preparing = "Подготовка среды установки...",
Connecting = "Подключение к серверу...",
CreatingFolder = "Создание папки установки...",
ExtractingLocal = "Распаковка компонентов из локального архива...",
CopyingLocal = "Копирование локальных компонентов...",
Downloading = "Загрузка компонентов из облака...",
DownloadingChunk = "Загрузка компонентов ({0}/{1})...",
ExtractingDeploying = "Распаковка и развертывание файлов...",
CreatingShortcuts = "Создание ярлыков и ассоциаций системы...",
ShortcutsDetail = "Рабочий стол и меню «Пуск»",
Registering = "Регистрация приложения в Windows...",
RegisterDetail = "Поддержка удаления через параметры Windows",
InstallDone = "Установка завершена!",
InstallDoneDetail = "Все компоненты настроены и готовы к использованию.",
Finish = "Завершить",
ErrorTitle = "Ошибка установки",
ErrorPrompt = "Во время установки произошла ошибка:\n{0}\n\nПодсказка: Вы можете поместить payload.zip в ту же папку для офлайн-установки.",
UninstallWizardTitle = "{0} - Мастер удаления",
UninstallHeaderTitle = "Удаление {0}",
UninstallHeaderSub = "Полное удаление этого приложения и ярлыков с вашего компьютера",
UninstallPrompt = "Вы действительно хотите удалить «{0}» с этого компьютера?",
UninstallDetail = "Это действие безвозвратно удалит все файлы программы, ярлыки и записи в реестре системы.",
UninstallAction = "Удалить",
UninstallProgressTitle = "Идет удаление, пожалуйста подождите...",
UninstallProgressDetail = "Завершение процессов и очистка файлов, ярлыков и записей реестра...",
UninstallDoneTitle = "Удаление завершено",
UninstallDoneDetail = "«{0}» успешно удален с вашего компьютера. Спасибо за использование!"
});
Add(new InstallLang {
Code = "pt",
DisplayName = "Português (Portuguese)",
IsRtl = false,
AppTitle = "Baixador do YouTube",
WizardTitle = "{0} - Assistente de Instalação",
HeaderSubtitle = "Instalador online ultraleve | Suporte a vídeo 4K e áudio MP3",
DestFolder = "Pasta de destino:",
Browse = "Procurar...",
BrowseDesc = "Selecione a pasta de instalação do Baixador do YouTube",
ShortcutDesktop = "Criar atalho na área de trabalho",
ShortcutStart = "Criar atalho no menu Iniciar",
LaunchAfter = "Iniciar o aplicativo após a instalação",
InstallNow = "Instalar Agora",
Cancel = "Cancelar",
Ok = "OK",
SelectLangTitle = "Selecionar Idioma de Instalação",
SelectLangPrompt = "Selecione o idioma a ser usado durante a instalação:",
Preparing = "Preparando o ambiente de instalação...",
Connecting = "Conectando ao servidor...",
CreatingFolder = "Criando pasta de instalação...",
ExtractingLocal = "Extraindo componentes do pacote local...",
CopyingLocal = "Copiando componentes locais...",
Downloading = "Baixando componentes principais da nuvem...",
DownloadingChunk = "Baixando componentes ({0}/{1})...",
ExtractingDeploying = "Extraindo e implantando arquivos principais...",
CreatingShortcuts = "Criando atalhos e associações do sistema...",
ShortcutsDetail = "Área de trabalho e Menu Iniciar",
Registering = "Registrando o aplicativo no Windows...",
RegisterDetail = "Permite desinstalação pelas Configurações do Windows",
InstallDone = "Instalação Concluída!",
InstallDoneDetail = "Todos os componentes foram configurados e estão prontos para uso.",
Finish = "Concluir",
ErrorTitle = "Erro de Instalação",
ErrorPrompt = "Ocorreu um erro durante a instalação:\n{0}\n\nDica: Você pode colocar o arquivo payload.zip no mesmo diretório para instalação offline.",
UninstallWizardTitle = "{0} - Assistente de Desinstalação",
UninstallHeaderTitle = "Desinstalar {0}",
UninstallHeaderSub = "Remover completamente este aplicativo e atalhos do seu computador",
UninstallPrompt = "Tem certeza de que deseja desinstalar \"{0}\" deste computador?",
UninstallDetail = "Isso removerá permanentemente todos os arquivos do programa, atalhos e registros das configurações do sistema.",
UninstallAction = "Desinstalar",
UninstallProgressTitle = "Desinstalando, por favor aguarde...",
UninstallProgressDetail = "Encerrando processos e limpando arquivos, atalhos e entradas do registro...",
UninstallDoneTitle = "Desinstalação Concluída",
UninstallDoneDetail = "\"{0}\" foi removido com sucesso do seu computador. Obrigado por usar!"
});
Add(new InstallLang {
Code = "it",
DisplayName = "Italiano (Italian)",
IsRtl = false,
AppTitle = "Downloader per YouTube",
WizardTitle = "{0} - Installazione guidata",
HeaderSubtitle = "Installer online ultraleggero | Supporta video 4K e audio MP3",
DestFolder = "Cartella di destinazione:",
Browse = "Sfoglia...",
BrowseDesc = "Seleziona la cartella di installazione di Downloader per YouTube",
ShortcutDesktop = "Crea collegamento sul desktop",
ShortcutStart = "Crea collegamento nel menu Start",
LaunchAfter = "Avvia l'applicazione al termine dell'installazione",
InstallNow = "Installa ora",
Cancel = "Annulla",
Ok = "OK",
SelectLangTitle = "Seleziona lingua di installazione",
SelectLangPrompt = "Seleziona la lingua da utilizzare durante l'installazione:",
Preparing = "Preparazione dell'ambiente di installazione...",
Connecting = "Connessione al server...",
CreatingFolder = "Creazione della cartella di installazione...",
ExtractingLocal = "Estrazione dei componenti dal pacchetto locale...",
CopyingLocal = "Copia dei componenti locali...",
Downloading = "Download dei componenti principali dal cloud...",
DownloadingChunk = "Download dei componenti ({0}/{1})...",
ExtractingDeploying = "Estrazione e distribuzione dei file...",
CreatingShortcuts = "Creazione di collegamenti e associazioni di sistema...",
ShortcutsDetail = "Desktop e Menu Start",
Registering = "Registrazione dell'applicazione in Windows...",
RegisterDetail = "Supporta la disinstallazione da Impostazioni di Windows",
InstallDone = "Installazione completata!",
InstallDoneDetail = "Tutti i componenti sono stati configurati e sono pronti all'uso.",
Finish = "Fine",
ErrorTitle = "Errore di installazione",
ErrorPrompt = "Si è verificato un errore durante l'installazione:\n{0}\n\nSuggerimento: È possibile inserire payload.zip nella stessa cartella per l'installazione offline.",
UninstallWizardTitle = "{0} - Disinstallazione guidata",
UninstallHeaderTitle = "Disinstalla {0}",
UninstallHeaderSub = "Rimuovi completamente questa applicazione e i suoi collegamenti dal computer",
UninstallPrompt = "Sei sicuro di voler disinstallare \"{0}\" da questo computer?",
UninstallDetail = "Questa operazione rimuoverà definitivamente tutti i file del programma, i collegamenti e le voci di sistema.",
UninstallAction = "Disinstalla",
UninstallProgressTitle = "Disinstallazione in corso, attendere...",
UninstallProgressDetail = "Chiusura dei processi e pulizia dei file, dei collegamenti e delle voci di registro...",
UninstallDoneTitle = "Disinstallazione completata",
UninstallDoneDetail = "\"{0}\" è stato rimosso completamente dal computer. Grazie per averlo utilizzato!"
});
Add(new InstallLang {
Code = "ar",
DisplayName = "العربية (Arabic)",
IsRtl = true,
AppTitle = "أداة تنزيل يوتيوب",
WizardTitle = "{0} - معالج التثبيت",
HeaderSubtitle = "مثبت فائق الخفة عبر الإنترنت | يدعم فيديو 4K وصوت MP3",
DestFolder = "مجلد التثبيت:",
Browse = "استعراض...",
BrowseDesc = "حدد مجلد تثبيت أداة تنزيل يوتيوب",
ShortcutDesktop = "إنشاء اختصار على سطح المكتب",
ShortcutStart = "إنشاء اختصار في قائمة ابدأ",
LaunchAfter = "تشغيل التطبيق فور اكتمال التثبيت",
InstallNow = "تثبيت الآن",
Cancel = "إلغاء",
Ok = "موافق",
SelectLangTitle = "اختيار لغة التثبيت",
SelectLangPrompt = "يرجى تحديد اللغة التي تريد استخدامها أثناء التثبيت:",
Preparing = "جارٍ تجهيز بيئة التثبيت...",
Connecting = "جارٍ الاتصال بالخادم...",
CreatingFolder = "جارٍ إنشاء مجلد التثبيت...",
ExtractingLocal = "جارٍ استخراج المكونات من الحزمة المحلية...",
CopyingLocal = "جارٍ نسخ المكونات المحلية...",
Downloading = "جارٍ تنزيل المكونات الأساسية من السحابة...",
DownloadingChunk = "جارٍ تنزيل المكونات ({0}/{1})...",
ExtractingDeploying = "جارٍ استخراج وتوزيع الملفات الأساسية...",
CreatingShortcuts = "جارٍ إنشاء الاختصارات وربط النظام...",
ShortcutsDetail = "سطح المكتب وقائمة ابدأ",
Registering = "جارٍ تسجيل التطبيق في نظام Windows...",
RegisterDetail = "يدعم إلغاء التثبيت المباشر من إعدادات Windows",
InstallDone = "اكتمل التثبيت!",
InstallDoneDetail = "تم تكوين جميع المكونات وهي جاهزة للاستخدام الآن.",
Finish = "إنهاء",
ErrorTitle = "خطأ في التثبيت",
ErrorPrompt = "حدث خطأ أثناء التثبيت:\n{0}\n\nتلميح: يمكنك وضع payload.zip في نفس الدليل للتثبيت دون إنترنت.",
UninstallWizardTitle = "{0} - معالج إلغاء التثبيت",
UninstallHeaderTitle = "إلغاء تثبيت {0}",
UninstallHeaderSub = "إزالة هذا التطبيق واختصاراته تمامًا من جهاز الكمبيوتر الخاص بك",
UninstallPrompt = "هل أنت متأكد من رغبتك في إلغاء تثبيت \"{0}\" بالكامل من هذا الكمبيوتر؟",
UninstallDetail = "ستؤدي هذه العملية إلى إزالة جميع ملفات البرنامج والاختصارات وسجلات إعدادات النظام نهائيًا.",
UninstallAction = "إلغاء التثبيت",
UninstallProgressTitle = "جارٍ إلغاء التثبيت، يرجى الانتظار...",
UninstallProgressDetail = "جارٍ إنهاء العمليات وتنظيف الملفات والاختصارات وإدخالات السجل...",
UninstallDoneTitle = "اكتمل إلغاء التثبيت",
UninstallDoneDetail = "تمت إزالة \"{0}\" تمامًا من جهاز الكمبيوتر الخاص بك. شكرًا لك على استخدامه!"
});
}
private static void Add(InstallLang l)
{
_langs[l.Code] = l;
All.Add(l);
}
public static InstallLang Get(string code)
{
if (string.IsNullOrEmpty(code)) return _langs["en"];
InstallLang lang;
if (_langs.TryGetValue(code, out lang)) return lang;
if (code.Contains("-"))
{
string prefix = code.Split('-')[0];
foreach (var kv in _langs)
{
if (kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return kv.Value;
}
}
return _langs["en"];
}
public static string DetectLanguageCode()
{
string uiName = "";
try { uiName = System.Globalization.CultureInfo.CurrentUICulture.Name; } catch {}
if (string.IsNullOrEmpty(uiName))
{
try { uiName = System.Globalization.CultureInfo.InstalledUICulture.Name; } catch {}
}
if (string.IsNullOrEmpty(uiName)) return "en";
uiName = uiName.ToLowerInvariant();
if (uiName.StartsWith("zh-tw") || uiName.StartsWith("zh-hk") || uiName.StartsWith("zh-mo") || uiName.Contains("hant"))
return "zh-TW";
if (uiName.StartsWith("zh"))
return "zh-CN";
if (uiName.StartsWith("ja"))
return "ja";
if (uiName.StartsWith("ko"))
return "ko";
if (uiName.StartsWith("es"))
return "es";
if (uiName.StartsWith("fr"))
return "fr";
if (uiName.StartsWith("de"))
return "de";
if (uiName.StartsWith("ru"))
return "ru";
if (uiName.StartsWith("pt"))
return "pt";
if (uiName.StartsWith("it"))
return "it";
if (uiName.StartsWith("ar"))
return "ar";
return "en";
}
public static InstallLang GetSavedOrDetectedLanguage()
{
string code = null;
try
{
string curDir = AppDomain.CurrentDomain.BaseDirectory;
string iniFile = Path.Combine(curDir, "setup_config.ini");
if (!File.Exists(iniFile))
{
string defDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "YouTube Downloader");
iniFile = Path.Combine(defDir, "setup_config.ini");
}
if (File.Exists(iniFile))
{
foreach (string line in File.ReadAllLines(iniFile))
{
if (line.StartsWith("lang=", StringComparison.OrdinalIgnoreCase))
{
code = line.Substring(5).Trim();
break;
}
}
}
}
catch {}
if (string.IsNullOrEmpty(code)) code = DetectLanguageCode();
return Get(code);
}
}
class Program
{
public static string CustomUrl = null;
public static string CustomLang = null;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 啟用 TLS 1.2 及 TLS 1.3
try
{
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
}
catch {}
string exeName = Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName);
bool isUninstall = exeName.IndexOf("uninstall", StringComparison.OrdinalIgnoreCase) >= 0;
foreach (string arg in args)
{
if (arg.Equals("/UNINSTALL", StringComparison.OrdinalIgnoreCase) ||
arg.Equals("--uninstall", StringComparison.OrdinalIgnoreCase) ||
arg.Equals("-uninstall", StringComparison.OrdinalIgnoreCase))
{
isUninstall = true;
}
else if (arg.StartsWith("/URL=", StringComparison.OrdinalIgnoreCase) ||
arg.StartsWith("--url=", StringComparison.OrdinalIgnoreCase))
{
int eq = arg.IndexOf('=');
if (eq > 0 && eq < arg.Length - 1)
{
CustomUrl = arg.Substring(eq + 1).Trim('"', '\'');
}
}
else if (arg.StartsWith("/LANG=", StringComparison.OrdinalIgnoreCase) ||
arg.StartsWith("--lang=", StringComparison.OrdinalIgnoreCase))
{
int eq = arg.IndexOf('=');
if (eq > 0 && eq < arg.Length - 1)
{
CustomLang = arg.Substring(eq + 1).Trim('"', '\'');
}
}
}
if (isUninstall)
{
InstallLang uLang = null;
if (!string.IsNullOrEmpty(CustomLang)) uLang = I18n.Get(CustomLang);
else uLang = I18n.GetSavedOrDetectedLanguage();
Application.Run(new ModernUninstaller(uLang));
}
else
{
InstallLang selectedLang = null;
using (var dlg = new LanguageSelectDialog(CustomLang))
{
if (dlg.ShowDialog() != DialogResult.OK)
{
return; // 使用者按取消或關閉
}
selectedLang = dlg.SelectedLanguage;
}
if (selectedLang == null) selectedLang = I18n.Get(I18n.DetectLanguageCode());
Application.Run(new ModernInstaller(selectedLang));
}
}
public static void CreateLnk(string lnk, string target, string icon, string dir)
{
try
{
Type t = Type.GetTypeFromProgID("WScript.Shell");
dynamic s = Activator.CreateInstance(t);
dynamic c = s.CreateShortcut(lnk);
c.TargetPath = target;
c.WorkingDirectory = dir;
if (File.Exists(icon)) c.IconLocation = icon + ",0";
c.Save();
}
catch {}
}
public static void RegisterApp(string targetDir, string targetIcon, string targetSetup, string appName)
{
try
{
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\YouTubeDownloader"))
{
if (key != null)
{
key.SetValue("DisplayName", appName);
key.SetValue("DisplayVersion", "1.2.0");
key.SetValue("Publisher", "Ryan");
key.SetValue("DisplayIcon", targetIcon);
key.SetValue("InstallLocation", targetDir);
key.SetValue("UninstallString", "\"" + targetSetup + "\" /UNINSTALL");
key.SetValue("QuietUninstallString", "\"" + targetSetup + "\" /UNINSTALL");
key.SetValue("EstimatedSize", 42000, Microsoft.Win32.RegistryValueKind.DWord);
key.SetValue("NoModify", 1, Microsoft.Win32.RegistryValueKind.DWord);
key.SetValue("NoRepair", 1, Microsoft.Win32.RegistryValueKind.DWord);
}
}
}
catch {}
}
public static void RegDel(string subKey)
{
try
{
Microsoft.Win32.Registry.CurrentUser.DeleteSubKeyTree(subKey, false);
}
catch {}
}
}
public class LanguageSelectDialog : Form
{
private PictureBox picIcon;
private Label lblPrompt;
private ComboBox cmbLangs;
private Button btnOk;
private Button btnCancel;
public InstallLang SelectedLanguage { get; private set; }
public LanguageSelectDialog(string preselectCode = null)
{
this.Size = new Size(430, 200);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.ShowInTaskbar = true;
this.BackColor = Color.FromArgb(249, 250, 252);
this.Font = new Font("Microsoft JhengHei UI", 9F);
this.TopMost = true;
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream sIco = asm.GetManifestResourceStream("app_icon.ico"))
{
if (sIco != null) this.Icon = new Icon(sIco);
}
}
catch {}
picIcon = new PictureBox {
Size = new Size(42, 42),
Location = new Point(22, 22),
SizeMode = PictureBoxSizeMode.Zoom
};
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream sLogo = asm.GetManifestResourceStream("app_logo.png"))
{
if (sLogo != null) picIcon.Image = Image.FromStream(sLogo);
}
}
catch {}
this.Controls.Add(picIcon);
lblPrompt = new Label {
Location = new Point(78, 20),
Size = new Size(320, 36),
ForeColor = Color.FromArgb(31, 41, 55),
Font = new Font("Microsoft JhengHei UI", 9F)
};
this.Controls.Add(lblPrompt);
cmbLangs = new ComboBox {
Location = new Point(78, 60),
Size = new Size(314, 26),
DropDownStyle = ComboBoxStyle.DropDownList,
Font = new Font("Microsoft JhengHei UI", 9.5F),
BackColor = Color.White,
ForeColor = Color.FromArgb(31, 41, 55)
};
foreach (var l in I18n.All)
{
cmbLangs.Items.Add(l);
}
string targetCode = preselectCode;
if (string.IsNullOrEmpty(targetCode)) targetCode = I18n.DetectLanguageCode();
int selectIdx = 0;
for (int i = 0; i < cmbLangs.Items.Count; i++)
{
var l = (InstallLang)cmbLangs.Items[i];
if (l.Code.Equals(targetCode, StringComparison.OrdinalIgnoreCase))
{
selectIdx = i;
break;
}
}
cmbLangs.SelectedIndex = selectIdx;
cmbLangs.SelectedIndexChanged += (s, e) => UpdateDialogLang();
this.Controls.Add(cmbLangs);
btnOk = new Button {
Size = new Size(88, 30),
Location = new Point(216, 110),
FlatStyle = FlatStyle.Flat,
BackColor = Color.FromArgb(0, 103, 192),
ForeColor = Color.White,
Cursor = Cursors.Hand,
Font = new Font("Microsoft JhengHei UI", 9F, FontStyle.Bold)
};
btnOk.FlatAppearance.BorderSize = 0;
btnOk.Click += (s, e) => {
this.SelectedLanguage = (InstallLang)cmbLangs.SelectedItem;
this.DialogResult = DialogResult.OK;
this.Close();
};
this.Controls.Add(btnOk);
btnCancel = new Button {
Size = new Size(88, 30),
Location = new Point(312, 110),
FlatStyle = FlatStyle.Flat,
BackColor = Color.White,
ForeColor = Color.FromArgb(55, 65, 81),
Cursor = Cursors.Hand
};
btnCancel.FlatAppearance.BorderColor = Color.FromArgb(209, 213, 219);
btnCancel.Click += (s, e) => {
this.DialogResult = DialogResult.Cancel;
this.Close();
};
this.Controls.Add(btnCancel);
this.AcceptButton = btnOk;
this.CancelButton = btnCancel;
UpdateDialogLang();
}
private void UpdateDialogLang()
{
var lang = (InstallLang)cmbLangs.SelectedItem;
if (lang == null) return;
this.SelectedLanguage = lang;
this.Text = lang.SelectLangTitle;
lblPrompt.Text = lang.SelectLangPrompt;
btnOk.Text = lang.Ok;
btnCancel.Text = lang.Cancel;
if (lang.IsRtl)
{
this.RightToLeft = RightToLeft.Yes;
this.RightToLeftLayout = true;
}
else
{
this.RightToLeft = RightToLeft.No;
this.RightToLeftLayout = false;
}
}
}
public class ModernInstaller : Form
{
// 預設 Cloudflare Pages 下載端點 (已對接專案網址)
public const string DEFAULT_CF_URL = "https://youtube-downloader-7g5.pages.dev/assets/";
private readonly InstallLang _lang;
private Panel panelHeader;
private Panel panelContent;
private Panel panelFooter;
private PictureBox picIcon;
private Label lblTitle;
private Label lblSub;
// View 1: Welcome & Settings
private Panel pnlWelcome;
private Label lblDest;
private TextBox txtPath;
private Button btnBrowse;
private CheckBox chkDesktop;
private CheckBox chkStart;
private CheckBox chkLaunch;
// View 2: Progress
private Panel pnlProgress;
private Label lblProgTitle;
private ProgressBar progressBar;
private Label lblProgDetail;
// View 3: Complete
private Panel pnlDone;
private Label lblDoneTitle;
private Label lblDoneDesc;
private Button btnAction;
private Button btnCancel;
public ModernInstaller(InstallLang lang = null)
{
_lang = lang ?? I18n.Get(I18n.DetectLanguageCode());
this.Text = string.Format(_lang.WizardTitle, _lang.AppTitle);
this.Size = new Size(540, 360);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.BackColor = Color.FromArgb(249, 250, 252);
this.Font = new Font("Microsoft JhengHei UI", 9F);
if (_lang.IsRtl)
{
this.RightToLeft = RightToLeft.Yes;
this.RightToLeftLayout = true;
}
// Header
panelHeader = new Panel { Dock = DockStyle.Top, Height = 84, BackColor = Color.White };
panelHeader.Paint += (s, e) => {
using (Pen p = new Pen(Color.FromArgb(229, 231, 235), 1)) {
e.Graphics.DrawLine(p, 0, 83, panelHeader.Width, 83);
}
};
picIcon = new PictureBox { Size = new Size(52, 52), Location = new Point(24, 16), SizeMode = PictureBoxSizeMode.Zoom };
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream sLogo = asm.GetManifestResourceStream("app_logo.png"))
{
if (sLogo != null) picIcon.Image = Image.FromStream(sLogo);
}
using (Stream sIco = asm.GetManifestResourceStream("app_icon.ico"))
{
if (sIco != null) this.Icon = new Icon(sIco);
}
}
catch {}
lblTitle = new Label {
Text = _lang.AppTitle,
Font = new Font("Microsoft JhengHei UI", 13.5F, FontStyle.Bold),
ForeColor = Color.FromArgb(24, 24, 27),
Location = new Point(88, 16),
AutoSize = true
};
lblSub = new Label {
Text = "v1.2.0 | " + _lang.HeaderSubtitle,
Font = new Font("Microsoft JhengHei UI", 8.8F),
ForeColor = Color.FromArgb(113, 113, 122),
Location = new Point(89, 46),
AutoSize = true
};
panelHeader.Controls.Add(picIcon);
panelHeader.Controls.Add(lblTitle);
panelHeader.Controls.Add(lblSub);
// Footer
panelFooter = new Panel { Dock = DockStyle.Bottom, Height = 64, BackColor = Color.FromArgb(244, 245, 247) };
panelFooter.Paint += (s, e) => {
using (Pen p = new Pen(Color.FromArgb(229, 231, 235), 1)) {
e.Graphics.DrawLine(p, 0, 0, panelFooter.Width, 0);
}
};
btnCancel = new Button {
Text = _lang.Cancel,
Size = new Size(88, 34),
Location = new Point(426, 15),
FlatStyle = FlatStyle.Flat,
BackColor = Color.White,
ForeColor = Color.FromArgb(55, 65, 81),
Cursor = Cursors.Hand
};
btnCancel.FlatAppearance.BorderColor = Color.FromArgb(209, 213, 219);
btnCancel.Click += (s, e) => this.Close();
btnAction = new Button {
Text = _lang.InstallNow,
Size = new Size(110, 34),
Location = new Point(306, 15),
FlatStyle = FlatStyle.Flat,
BackColor = Color.FromArgb(0, 103, 192),
ForeColor = Color.White,
Cursor = Cursors.Hand,
Font = new Font("Microsoft JhengHei UI", 9F, FontStyle.Bold)
};
btnAction.FlatAppearance.BorderSize = 0;
btnAction.Click += BtnAction_Click;
panelFooter.Controls.Add(btnCancel);
panelFooter.Controls.Add(btnAction);
// Content
panelContent = new Panel { Dock = DockStyle.Fill, BackColor = Color.FromArgb(249, 250, 252) };
// ------------------ View 1: Welcome & Settings ------------------
pnlWelcome = new Panel { Dock = DockStyle.Fill };
lblDest = new Label {
Text = _lang.DestFolder,
Font = new Font("Microsoft JhengHei UI", 9F, FontStyle.Bold),
ForeColor = Color.FromArgb(55, 65, 81),
Location = new Point(28, 16),
AutoSize = true
};
pnlWelcome.Controls.Add(lblDest);
string defPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "YouTube Downloader");
txtPath = new TextBox {
Text = defPath,
Location = new Point(30, 40),
Size = new Size(385, 23),
BackColor = Color.White,
BorderStyle = BorderStyle.FixedSingle
};
pnlWelcome.Controls.Add(txtPath);
btnBrowse = new Button {
Text = _lang.Browse,
Location = new Point(423, 39),
Size = new Size(77, 25),
FlatStyle = FlatStyle.Flat,
BackColor = Color.White,
Cursor = Cursors.Hand
};
btnBrowse.FlatAppearance.BorderColor = Color.FromArgb(209, 213, 219);
btnBrowse.Click += (s, e) => {
using (FolderBrowserDialog fbd = new FolderBrowserDialog())
{
fbd.Description = _lang.BrowseDesc;
fbd.SelectedPath = txtPath.Text;
if (fbd.ShowDialog() == DialogResult.OK)
{
txtPath.Text = fbd.SelectedPath;
}
}
};
pnlWelcome.Controls.Add(btnBrowse);
chkDesktop = new CheckBox { Text = _lang.ShortcutDesktop, Checked = true, Location = new Point(30, 80), AutoSize = true, Cursor = Cursors.Hand };
chkStart = new CheckBox { Text = _lang.ShortcutStart, Checked = true, Location = new Point(30, 108), AutoSize = true, Cursor = Cursors.Hand };
chkLaunch = new CheckBox { Text = _lang.LaunchAfter, Checked = true, Location = new Point(30, 136), AutoSize = true, Cursor = Cursors.Hand };
pnlWelcome.Controls.Add(chkDesktop);
pnlWelcome.Controls.Add(chkStart);
pnlWelcome.Controls.Add(chkLaunch);
// ------------------ View 2: Progress ------------------
pnlProgress = new Panel { Dock = DockStyle.Fill, Visible = false };
lblProgTitle = new Label {
Text = _lang.Preparing,
Font = new Font("Microsoft JhengHei UI", 10.5F, FontStyle.Bold),
ForeColor = Color.FromArgb(31, 41, 55),
Location = new Point(28, 40),
AutoSize = true
};
progressBar = new ProgressBar {
Location = new Point(30, 75),
Size = new Size(470, 22),
Style = ProgressBarStyle.Continuous,
Value = 0
};
lblProgDetail = new Label {
Text = _lang.Connecting,
Font = new Font("Microsoft JhengHei UI", 8.5F),
ForeColor = Color.FromArgb(107, 114, 128),
Location = new Point(30, 108),
Size = new Size(470, 45)
};
pnlProgress.Controls.Add(lblProgTitle);
pnlProgress.Controls.Add(progressBar);
pnlProgress.Controls.Add(lblProgDetail);
// ------------------ View 3: Complete ------------------
pnlDone = new Panel { Dock = DockStyle.Fill, Visible = false };
PictureBox picDone = new PictureBox {
Size = new Size(48, 48),
Location = new Point(30, 36),
SizeMode = PictureBoxSizeMode.Zoom
};
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream sLogo = asm.GetManifestResourceStream("app_logo.png"))
{
if (sLogo != null) picDone.Image = Image.FromStream(sLogo);
}
}
catch {}
lblDoneTitle = new Label {
Text = _lang.InstallDone,
Font = new Font("Microsoft JhengHei UI", 12F, FontStyle.Bold),
ForeColor = Color.FromArgb(22, 101, 52),
Location = new Point(90, 36),
AutoSize = true
};
lblDoneDesc = new Label {
Text = _lang.InstallDoneDetail,
Font = new Font("Microsoft JhengHei UI", 9F),
ForeColor = Color.FromArgb(75, 85, 99),
Location = new Point(92, 66),
Size = new Size(410, 45)
};
pnlDone.Controls.Add(picDone);
pnlDone.Controls.Add(lblDoneTitle);
pnlDone.Controls.Add(lblDoneDesc);
panelContent.Controls.Add(pnlWelcome);
panelContent.Controls.Add(pnlProgress);
panelContent.Controls.Add(pnlDone);
this.Controls.Add(panelContent);
this.Controls.Add(panelHeader);
this.Controls.Add(panelFooter);
}
private void UpdateProgress(int pct, string title, string detail)
{
if (this.InvokeRequired)
{
this.BeginInvoke((Action)(() => UpdateProgress(pct, title, detail)));
return;
}
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
progressBar.Value = pct;
if (!string.IsNullOrEmpty(title)) lblProgTitle.Text = title;
if (!string.IsNullOrEmpty(detail)) lblProgDetail.Text = detail;
}
private void BtnAction_Click(object sender, EventArgs e)
{
string targetDir = txtPath.Text.Trim();
if (string.IsNullOrEmpty(targetDir))
{
MessageBox.Show(this, "請指定有效的安裝資料夾路徑。", "路徑錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (btnAction.Text == _lang.Finish)
{
if (chkLaunch.Checked)
{
string exe = Path.Combine(targetDir, "YouTube_Downloader.exe");
if (File.Exists(exe))
{
try { Process.Start(new ProcessStartInfo(exe) { WorkingDirectory = targetDir }); } catch {}
}
}
this.Close();
return;
}
pnlWelcome.Visible = false;
pnlProgress.Visible = true;
btnAction.Enabled = false;
btnCancel.Enabled = false;
string serverUrl = GetServerUrl();
bool createDesk = chkDesktop.Checked;
bool createStart = chkStart.Checked;
System.Threading.ThreadPool.QueueUserWorkItem((state) => {
try
{
ExecuteInstall(targetDir, serverUrl, createDesk, createStart);
}
catch (Exception ex)
{
this.BeginInvoke((Action)(() => {
pnlProgress.Visible = false;
pnlWelcome.Visible = true;
btnAction.Enabled = true;
btnCancel.Enabled = true;
MessageBox.Show(this, string.Format(_lang.ErrorPrompt, ex.Message), _lang.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
}));
}
});
}
private string GetServerUrl()
{
string url = Program.CustomUrl;
if (string.IsNullOrEmpty(url))
{
string cfgFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "setup_config.ini");
if (File.Exists(cfgFile))
{
try
{
foreach (string line in File.ReadAllLines(cfgFile))
{
if (line.StartsWith("url=", StringComparison.OrdinalIgnoreCase))
{
url = line.Substring(4).Trim();
break;
}
}
}
catch {}
}
}
if (string.IsNullOrEmpty(url)) url = DEFAULT_CF_URL;
return url.Trim().TrimEnd('/') + "/";
}
private static void ExtractZipSafe(string zipPath, string targetDir)
{
using (var archive = ZipFile.OpenRead(zipPath))
{
foreach (var entry in archive.Entries)
{
string fullPath = Path.Combine(targetDir, entry.FullName);
string entryDir = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(entryDir) && !Directory.Exists(entryDir))
Directory.CreateDirectory(entryDir);
if (!string.IsNullOrEmpty(entry.Name))
entry.ExtractToFile(fullPath, true);
}
}
}
private void ExecuteInstall(string targetDir, string serverUrl, bool createDesk, bool createStart)
{
try
{
foreach (var proc in Process.GetProcessesByName("YouTube_Downloader"))
{
try { proc.Kill(); proc.WaitForExit(1000); } catch {}
}
}
catch {}
UpdateProgress(5, _lang.CreatingFolder, targetDir);
if (!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
string targetExe = Path.Combine(targetDir, "YouTube_Downloader.exe");
string targetIcon = Path.Combine(targetDir, "app_icon.ico");
string targetSetup = Path.Combine(targetDir, "YouTube_Downloader_Setup.exe");
string appName = _lang.AppTitle;
string localDir = AppDomain.CurrentDomain.BaseDirectory;
string localZip = Path.Combine(localDir, "payload.zip");
string localExe = Path.Combine(localDir, "YouTube_Downloader.exe");
bool acquired = false;
// 1. 優先檢查本機離線檔案
if (File.Exists(localZip))
{
UpdateProgress(30, _lang.ExtractingLocal, localZip);
ExtractZipSafe(localZip, targetDir);
acquired = true;
}
else if (File.Exists(localExe))
{
UpdateProgress(30, _lang.CopyingLocal, localExe);
File.Copy(localExe, targetExe, true);
string localIco = Path.Combine(localDir, "app_icon.ico");
if (File.Exists(localIco)) File.Copy(localIco, targetIcon, true);
acquired = true;
}
// 2. 若本機無檔案,則透過 Cloudflare Pages 雲端 CDN 自動下載
if (!acquired)
{
UpdateProgress(10, _lang.Connecting, serverUrl);
string tempZip = Path.Combine(Path.GetTempPath(), "yt_payload_" + Guid.NewGuid().ToString("N") + ".zip");
try
{
using (WebClient wc = new WebClient())
{
wc.Headers.Add("User-Agent", "YouTubeDownloaderSetup/1.0");
// 檢查是否存在 manifest.json (分段壓縮包架構)
string manifestUrl = serverUrl + "manifest.json";
string manifestJson = null;
try
{
manifestJson = wc.DownloadString(manifestUrl);
}
catch {}
List<string> chunkFiles = new List<string>();
long totalExpectedSize = 0;
if (!string.IsNullOrEmpty(manifestJson))
{
var chunkMatches = Regex.Matches(manifestJson, "\"name\"\\s*:\\s*\"([^\"]+)\"");
foreach (Match m in chunkMatches)
{
chunkFiles.Add(m.Groups[1].Value);
}
var sizeMatch = Regex.Match(manifestJson, "\"total_size\"\\s*:\\s*(\\d+)");
if (sizeMatch.Success) long.TryParse(sizeMatch.Groups[1].Value, out totalExpectedSize);
}
if (chunkFiles.Count > 0)
{
// 分段平行/依序下載並合併
using (FileStream fsOut = new FileStream(tempZip, FileMode.Create, FileAccess.Write))
{
for (int i = 0; i < chunkFiles.Count; i++)
{
string cName = chunkFiles[i];
string cUrl = serverUrl + cName;
int curIdx = i + 1;
int totIdx = chunkFiles.Count;
int basePct = 10 + (int)(70.0 * i / totIdx);
UpdateProgress(basePct, string.Format(_lang.DownloadingChunk, curIdx, totIdx), cName);
byte[] chunkBytes = wc.DownloadData(cUrl);
fsOut.Write(chunkBytes, 0, chunkBytes.Length);
}
}
}
else
{
// 單一 payload.zip 回退模式
string fullZipUrl = serverUrl + "payload.zip";
wc.DownloadProgressChanged += (s, e) => {
int pct = 10 + (int)(e.ProgressPercentage * 0.7);
UpdateProgress(pct, _lang.Downloading, string.Format("{0:F1} MB / {1:F1} MB", e.BytesReceived / 1048576.0, e.TotalBytesToReceive / 1048576.0));
};
wc.DownloadFile(new Uri(fullZipUrl), tempZip);
}
}
UpdateProgress(80, _lang.ExtractingDeploying, tempZip);
ExtractZipSafe(tempZip, targetDir);
}
finally
{
if (File.Exists(tempZip))
{
try { File.Delete(tempZip); } catch {}
}
}
}
// 3. 複製安裝程式自身作為解除安裝程式
string selfExe = Application.ExecutablePath;
if (File.Exists(selfExe))
{
try { File.Copy(selfExe, targetSetup, true); } catch {}
}
// 保存語系與設定
try
{
string cfgFile = Path.Combine(targetDir, "setup_config.ini");
File.WriteAllText(cfgFile, "lang=" + _lang.Code + "\r\nurl=" + serverUrl + "\r\n", System.Text.Encoding.UTF8);
}
catch {}
// 4. 建立捷徑
if (!File.Exists(targetIcon))
{
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream s = asm.GetManifestResourceStream("app_icon.ico"))
{
if (s != null)
{
using (FileStream fs = new FileStream(targetIcon, FileMode.Create, FileAccess.Write))
{
s.CopyTo(fs);
}
}
}
}
catch {}
}
UpdateProgress(90, _lang.CreatingShortcuts, _lang.ShortcutsDetail);
if (createDesk)
{
string desk = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
Program.CreateLnk(Path.Combine(desk, appName + ".lnk"), targetExe, targetIcon, targetDir);
}
if (createStart)
{
string progs = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
Program.CreateLnk(Path.Combine(progs, appName + ".lnk"), targetExe, targetIcon, targetDir);
}
// 5. 註冊 Windows 11 設定「已安裝的應用程式」
UpdateProgress(96, _lang.Registering, _lang.RegisterDetail);
Program.RegisterApp(targetDir, targetIcon, targetSetup, appName);
// 6. 完成
UpdateProgress(100, _lang.InstallDone, _lang.InstallDoneDetail);
this.BeginInvoke((Action)(() => {
pnlProgress.Visible = false;
pnlDone.Visible = true;
btnAction.Text = _lang.Finish;
btnAction.BackColor = Color.FromArgb(22, 163, 74);
btnAction.Enabled = true;
btnCancel.Visible = false;
}));
}
}
public class ModernUninstaller : Form
{
private readonly InstallLang _lang;
private Panel panelHeader;
private Panel panelFooter;
private Panel panelContent;
private PictureBox picIcon;
private Label lblTitle;
private Label lblSub;
private Label lblPrompt;
private Label lblDetail;
private ProgressBar progressBar;
private Button btnAction;
private Button btnCancel;
public ModernUninstaller(InstallLang lang = null)
{
_lang = lang ?? I18n.GetSavedOrDetectedLanguage();
this.Text = string.Format(_lang.UninstallWizardTitle, _lang.AppTitle);
this.Size = new Size(500, 310);
this.StartPosition = FormStartPosition.CenterScreen;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.BackColor = Color.FromArgb(249, 250, 252);
this.Font = new Font("Microsoft JhengHei UI", 9F);
this.TopMost = true;
if (_lang.IsRtl)
{
this.RightToLeft = RightToLeft.Yes;
this.RightToLeftLayout = true;
}
// Header
panelHeader = new Panel { Dock = DockStyle.Top, Height = 75, BackColor = Color.White };
panelHeader.Paint += (s, e) => {
using (Pen p = new Pen(Color.FromArgb(229, 231, 235), 1)) {
e.Graphics.DrawLine(p, 0, 74, panelHeader.Width, 74);
}
};
picIcon = new PictureBox { Size = new Size(44, 44), Location = new Point(24, 16), SizeMode = PictureBoxSizeMode.Zoom };
try
{
var asm = Assembly.GetExecutingAssembly();
using (Stream sLogo = asm.GetManifestResourceStream("app_logo.png"))
{
if (sLogo != null) picIcon.Image = Image.FromStream(sLogo);
}
using (Stream sIco = asm.GetManifestResourceStream("app_icon.ico"))
{
if (sIco != null) this.Icon = new Icon(sIco);
}
}
catch {}
lblTitle = new Label {
Text = string.Format(_lang.UninstallHeaderTitle, _lang.AppTitle),
Font = new Font("Microsoft JhengHei UI", 12.5F, FontStyle.Bold),
ForeColor = Color.FromArgb(24, 24, 27),
Location = new Point(80, 16),
AutoSize = true
};
lblSub = new Label {
Text = _lang.UninstallHeaderSub,
ForeColor = Color.FromArgb(113, 113, 122),
Location = new Point(81, 42),
AutoSize = true
};
panelHeader.Controls.Add(picIcon);
panelHeader.Controls.Add(lblTitle);
panelHeader.Controls.Add(lblSub);
// Footer
panelFooter = new Panel { Dock = DockStyle.Bottom, Height = 60, BackColor = Color.FromArgb(244, 245, 247) };
panelFooter.Paint += (s, e) => {
using (Pen p = new Pen(Color.FromArgb(229, 231, 235), 1)) {
e.Graphics.DrawLine(p, 0, 0, panelFooter.Width, 0);
}
};
btnCancel = new Button {
Text = _lang.Cancel,
Size = new Size(80, 32),
Location = new Point(390, 14),
FlatStyle = FlatStyle.Flat,
BackColor = Color.White,
ForeColor = Color.FromArgb(55, 65, 81),
Cursor = Cursors.Hand
};
btnCancel.FlatAppearance.BorderColor = Color.FromArgb(209, 213, 219);
btnCancel.Click += (s, e) => this.Close();
btnAction = new Button {
Text = _lang.UninstallAction,
Size = new Size(115, 32),
Location = new Point(265, 14),
FlatStyle = FlatStyle.Flat,
BackColor = Color.FromArgb(220, 38, 38),
ForeColor = Color.White,
Cursor = Cursors.Hand,
Font = new Font("Microsoft JhengHei UI", 9F, FontStyle.Bold)
};
btnAction.FlatAppearance.BorderSize = 0;
btnAction.Click += BtnAction_Click;
panelFooter.Controls.Add(btnCancel);
panelFooter.Controls.Add(btnAction);
// Content
panelContent = new Panel { Dock = DockStyle.Fill, BackColor = Color.FromArgb(249, 250, 252) };
lblPrompt = new Label {
Text = string.Format(_lang.UninstallPrompt, _lang.AppTitle),
Font = new Font("Microsoft JhengHei UI", 10F, FontStyle.Bold),
ForeColor = Color.FromArgb(24, 24, 27),
Location = new Point(28, 26),
AutoSize = true
};
lblDetail = new Label {
Text = _lang.UninstallDetail,
Font = new Font("Microsoft JhengHei UI", 9F),
ForeColor = Color.FromArgb(100, 116, 139),
Location = new Point(30, 56),
Size = new Size(430, 45)
};
progressBar = new ProgressBar {
Location = new Point(30, 105),
Size = new Size(430, 16),
Style = ProgressBarStyle.Marquee,
Visible = false
};
panelContent.Controls.Add(lblPrompt);
panelContent.Controls.Add(lblDetail);
panelContent.Controls.Add(progressBar);
this.Controls.Add(panelContent);
this.Controls.Add(panelHeader);
this.Controls.Add(panelFooter);
}
private void BtnAction_Click(object sender, EventArgs e)
{
if (btnAction.Text == _lang.Finish)
{
this.Close();
return;
}
btnAction.Enabled = false;
btnCancel.Enabled = false;
progressBar.Visible = true;
lblPrompt.Text = _lang.UninstallProgressTitle;
lblDetail.Text = _lang.UninstallProgressDetail;
Timer t = new Timer { Interval = 600 };
t.Tick += (ts, te) => {
t.Stop();
try
{
foreach (Process p in Process.GetProcessesByName("YouTube_Downloader"))
{
try { p.Kill(); p.WaitForExit(1500); } catch {}
}
// 清理所有支援語系的可能桌面與開始功能表捷徑
string desk = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string progs = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
foreach (var l in I18n.All)
{
string dLnk = Path.Combine(desk, l.AppTitle + ".lnk");
if (File.Exists(dLnk)) { try { File.Delete(dLnk); } catch {} }
string sLnk = Path.Combine(progs, l.AppTitle + ".lnk");
if (File.Exists(sLnk)) { try { File.Delete(sLnk); } catch {} }
}
Program.RegDel(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\YouTubeDownloader");
Program.RegDel(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{D3F982A1-5E4C-4B71-9C3E-812C45F20E77}_is1");
string curDir = AppDomain.CurrentDomain.BaseDirectory.TrimEnd('\\', '/');
string targetDirToDelete = null;
if (curDir.IndexOf("YouTube Downloader", StringComparison.OrdinalIgnoreCase) >= 0)
{
targetDirToDelete = curDir;
}
else
{
string defDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "YouTube Downloader");
if (Directory.Exists(defDir)) targetDirToDelete = defDir;
}
if (!string.IsNullOrEmpty(targetDirToDelete))
{
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c timeout /t 1 /nobreak >nul & rd /s /q \"" + targetDirToDelete + "\"");
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
Process.Start(psi);
}
}
catch {}
progressBar.Visible = false;
lblPrompt.Text = _lang.UninstallDoneTitle;
lblDetail.Text = string.Format(_lang.UninstallDoneDetail, _lang.AppTitle);
btnAction.Text = _lang.Finish;
btnAction.BackColor = Color.FromArgb(22, 163, 74);
btnAction.Enabled = true;
btnCancel.Visible = false;
};
t.Start();
}
}
本機編譯與執行教學
1. 安裝 Python 3.10 或以上版本,並將 FFmpeg 加入系統環境變數 PATH。
確保在終端機輸入 python --version 及 ffmpeg -version 均能正常輸出版本編號。
2. 安裝核心音視頻解析依賴庫:
pip install --upgrade yt-dlp
3. 執行腳本即可啟動影音下載引擎:
python YT.py
python YT_GUI.py
4. 可選:使用 PyInstaller 將 GUI 打包為獨立執行檔:
pip install pyinstaller && pyinstaller --onefile --windowed YT_GUI.py
檔案完整性校驗 (SHA-256)
| 檔案名稱 | 檔案大小 | SHA-256 安全校驗碼 |
|---|---|---|
| source-code.zip | 69.0 KB | a3c5e6d86ffc9cbd2241463afcf3aff5d954d8f56d470d9f05ed18b1e53466d1 |
| YT.py | 9.5 KB | 867acfecc24c51010df2c4400c1c58497dac98b524b48b1d9c098309c6776915 |
| install.ps1 | 7.9 KB | 8f8e3501e98f57dcc379dc52ebda29bcd9e8e7ae38c7ae20c6e87a0c9c08c172 |
| Setup.cs | 81.8 KB | 4611c95a63ba1b52af81993a10a228c5b01c702c349abc2146d7b6de1e16e03e |
| YouTube_Downloader_Setup.exe | 98.0 KB | 371c28039fb67243957c2ba561fbf18e3fe8140b094e775ae0be5129d18acd6f |
| YouTube_Downloader_Setup.zip | 53.5 KB | aac9ecce7497ef429ed835ad67f4068ffce028f8c5c40bcc64c3766a2449d5fb |
開源授權聲明 (MIT License)
本專案採用全球通行的 MIT 開源授權協議,保障任何人享有完全自由的使用、學習、修改、私有備份與再分發權利。
MIT License Copyright (c) 2026 YouTube Downloader Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.