releases/VideoDownloader.App/SettingsWindow.xaml.cs

180 lines
8.4 KiB
C#

using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using Microsoft.Win32;
using VideoDownloader.App.Browser;
using VideoDownloader.Core.Config;
using VideoDownloader.Core.Social;
namespace VideoDownloader.App;
public partial class SettingsWindow : Window
{
public AppConfig Config { get; }
private readonly string _origTheme;
private readonly Func<Task>? _clearBrowsingData;
public bool ThemeChanged { get; private set; }
public SettingsWindow(AppConfig config, Func<Task>? clearBrowsingData = null)
{
InitializeComponent();
Config = config;
_origTheme = config.Theme;
_clearBrowsingData = clearBrowsingData;
FolderBox.Text = config.OutputFolder;
HomeBox.Text = config.HomeUrl;
ConcurrencyBox.Text = config.Concurrency.ToString();
MinSizeBox.Text = config.MinSizeMB.ToString();
MaxConcurrentBox.Text = config.MaxConcurrentDownloads.ToString();
ThemeLightBox.IsChecked = config.Theme.Equals("light", StringComparison.OrdinalIgnoreCase);
FfmpegBox.Text = config.FfmpegPath ?? "";
FmtHls.IsChecked = config.EnabledFormats.Contains("hls");
FmtDash.IsChecked = config.EnabledFormats.Contains("dash");
FmtMp4.IsChecked = config.EnabledFormats.Contains("mp4");
FmtWebM.IsChecked = config.EnabledFormats.Contains("webm");
FmtMkv.IsChecked = config.EnabledFormats.Contains("mkv");
FmtM4v.IsChecked = config.EnabledFormats.Contains("m4v");
FmtMov.IsChecked = config.EnabledFormats.Contains("mov");
FmtAvi.IsChecked = config.EnabledFormats.Contains("avi");
FmtAudio.IsChecked = config.EnabledFormats.Contains("audio");
YtDlpBox.Text = config.YtDlpPath ?? "";
UseCookiesBox.IsChecked = config.UseCookiesForSocial;
AutoInstallBox.IsChecked = config.AutoInstallYtDlp;
AutoUpdateBox.IsChecked = config.AutoUpdateYtDlp;
AutoInstallDenoBox.IsChecked = config.AutoInstallDeno;
DefaultQualityBox.SelectedValue = config.DefaultSocialQuality;
if (DefaultQualityBox.SelectedIndex < 0) DefaultQualityBox.SelectedIndex = 0;
YoutubeClientsBox.Text = config.YoutubePlayerClients ?? "";
ProfilePathBox.Text = MainWindow.ProfileFolder;
ProfileModeText.Text = config.PrivateMode
? "개인정보 보호 모드(InPrivate) — 데이터를 저장하지 않습니다"
: "일반 모드 — 쿠키·세션·localStorage 를 프로필에 보존합니다";
SearchEngineBox.ItemsSource = SearchEngines.Presets.Select(e => e.Name).ToList();
SearchEngineBox.SelectedItem = SearchEngines.FindByName(config.SearchEngine)?.Name ?? "Google";
if (SearchEngineBox.SelectedIndex < 0) SearchEngineBox.SelectedIndex = 0;
}
private void SearchEngineBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (SearchEngineHint == null) return;
var engine = SearchEngines.FindByName(SearchEngineBox.SelectedItem as string);
SearchEngineHint.Text = engine == null
? "주소창에 검색어를 입력하면 검색합니다"
: $"주소창에 검색어 입력 시 {engine.Name}(으)로 검색합니다. `g 검색어`처럼 키워드로 즉시 전환도 가능해요";
}
private void BrowseFolder(object sender, RoutedEventArgs e)
{
var dlg = new OpenFolderDialog();
if (Directory.Exists(FolderBox.Text)) dlg.InitialDirectory = FolderBox.Text;
if (dlg.ShowDialog() == true)
FolderBox.Text = dlg.FolderName;
}
private void BrowseFfmpeg(object sender, RoutedEventArgs e)
{
var dlg = new OpenFileDialog { Filter = "ffmpeg|ffmpeg.exe|실행 파일|*.exe|모든 파일|*.*" };
if (dlg.ShowDialog() == true)
FfmpegBox.Text = dlg.FileName;
}
private void BrowseYtDlp(object sender, RoutedEventArgs e)
{
var dlg = new OpenFileDialog { Filter = "yt-dlp|yt-dlp.exe|실행 파일|*.exe|모든 파일|*.*" };
if (dlg.ShowDialog() == true)
YtDlpBox.Text = dlg.FileName;
}
private async void ReinstallYtDlp_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
if (btn != null) btn.IsEnabled = false;
try
{
var path = await YtDlpRunner.DownloadAsync(Config, null, CancellationToken.None);
MessageBox.Show(path != null ? $"yt-dlp 설치 완료:\n{path}" : "yt-dlp 다운로드에 실패했습니다. 네트워크를 확인하세요.",
"yt-dlp", MessageBoxButton.OK, path != null ? MessageBoxImage.Information : MessageBoxImage.Warning);
if (path != null) YtDlpBox.Text = "";
}
finally { if (btn != null) btn.IsEnabled = true; }
}
private async void ClearData_Click(object sender, RoutedEventArgs e)
{
if (_clearBrowsingData == null) return;
if (MessageBox.Show("쿠키·localStorage·캐시 등 브라우저 데이터를 삭제합니다.\n로그인이 풀릴 수 있습니다. 계속할까요?",
"데이터 삭제", MessageBoxButton.OKCancel, MessageBoxImage.Warning) != MessageBoxResult.OK) return;
var btn = sender as Button;
if (btn != null) btn.IsEnabled = false;
try { await _clearBrowsingData(); }
finally { if (btn != null) btn.IsEnabled = true; }
}
private void Ok_Click(object sender, RoutedEventArgs e)
{
Config.OutputFolder = FolderBox.Text?.Trim() ?? "";
Config.HomeUrl = string.IsNullOrWhiteSpace(HomeBox.Text) ? "https://www.google.com" : HomeBox.Text.Trim();
if (int.TryParse(ConcurrencyBox.Text, out var c)) Config.Concurrency = Math.Max(1, c);
if (long.TryParse(MinSizeBox.Text, out var m) && m >= 0) Config.MinSizeMB = m;
Config.FfmpegPath = string.IsNullOrWhiteSpace(FfmpegBox.Text) ? null : FfmpegBox.Text.Trim();
Config.Theme = ThemeLightBox.IsChecked == true ? "light" : "dark";
if (int.TryParse(MaxConcurrentBox.Text, out var mc)) Config.MaxConcurrentDownloads = Math.Max(1, mc);
ThemeChanged = !Config.Theme.Equals(_origTheme, StringComparison.OrdinalIgnoreCase);
var fmts = new List<string>();
if (FmtHls.IsChecked == true) fmts.Add("hls");
if (FmtDash.IsChecked == true) fmts.Add("dash");
if (FmtMp4.IsChecked == true) fmts.Add("mp4");
if (FmtWebM.IsChecked == true) fmts.Add("webm");
if (FmtMkv.IsChecked == true) fmts.Add("mkv");
if (FmtM4v.IsChecked == true) fmts.Add("m4v");
if (FmtMov.IsChecked == true) fmts.Add("mov");
if (FmtAvi.IsChecked == true) fmts.Add("avi");
if (FmtAudio.IsChecked == true) fmts.Add("audio");
Config.EnabledFormats = fmts;
Config.YtDlpPath = string.IsNullOrWhiteSpace(YtDlpBox.Text) ? null : YtDlpBox.Text.Trim();
Config.UseCookiesForSocial = UseCookiesBox.IsChecked == true;
Config.AutoInstallYtDlp = AutoInstallBox.IsChecked == true;
Config.AutoUpdateYtDlp = AutoUpdateBox.IsChecked == true;
Config.AutoInstallDeno = AutoInstallDenoBox.IsChecked == true;
Config.DefaultSocialQuality = (DefaultQualityBox.SelectedValue as string) ?? "best";
Config.YoutubePlayerClients = string.IsNullOrWhiteSpace(YoutubeClientsBox.Text) ? null : YoutubeClientsBox.Text.Trim();
Config.SearchEngine = SearchEngineBox.SelectedItem as string ?? "Google";
DialogResult = true;
}
// 보더리스: Win11 어두운 테두리 + 둥근 모서리
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
try
{
var hwnd = new WindowInteropHelper(this).Handle;
int dark = App.IsLightTheme ? 0 : 1; // DWMWA_USE_IMMERSIVE_DARK_MODE (라이트 테마면 밝게)
DwmSetWindowAttribute(hwnd, 20, ref dark, sizeof(int));
int round = 2; // DWMWCP_ROUND
DwmSetWindowAttribute(hwnd, 33, ref round, sizeof(int));
}
catch { }
}
private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) { try { DragMove(); } catch { } }
}
private void Close_Click(object sender, RoutedEventArgs e) => Close();
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int pvAttribute, int cbAttribute);
}