Baseline: WPF/Avalonia/Cli/Core + plans (BROWSER_FEATURES_PLAN v2, MOBILE_PLAN v1)
This commit is contained in:
commit
21b7c4d8bd
60 changed files with 6581 additions and 0 deletions
165
VideoDownloader.App/SettingsWindow.xaml.cs
Normal file
165
VideoDownloader.App/SettingsWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using Microsoft.Win32;
|
||||
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 를 프로필에 보존합니다";
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue