using System.IO; using System.Net; using System.Net.Sockets; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media.Imaging; using QRCoder; using VideoDownloader.Server; namespace VideoDownloader.App.Browser; /// M0 원격 접근 페어링: QR(주소+토큰 동시 전달) + 상태 표시. public sealed class PairingWindow : Window { public PairingWindow(ServerHost server, string deviceName) { Title = "기기 연결 - MediaBrowser"; Width = 420; Height = 560; WindowStartupLocation = WindowStartupLocation.CenterOwner; SetResourceReference(FontFamilyProperty, "UiFont"); // 암시 스타일 폰트 강제 제거 후 창 루트 상속으로 통일 Background = System.Windows.Media.Brushes.White; var lanIp = GetLanIp(); var url = $"http://{lanIp}:{server.Port}/#t={server.Token}"; var qrPayload = new QRCodeGenerator().CreateQrCode(url, QRCodeGenerator.ECCLevel.M); var qr = new QRCode(qrPayload); var bmp = qr.GetGraphic(12); var stack = new StackPanel { Margin = new Thickness(24) }; var title = new TextBlock { Text = "📱 폰 카메라로 QR 스캔", FontSize = 16, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 4) }; var sub = new TextBlock { Text = "브라우저에서 바로 열립니다(앱 설치 불필요). 같은 Wi-Fi 에 있어야 합니다.", FontSize = 12, Foreground = System.Windows.Media.Brushes.Gray, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 0, 0, 16) }; var img = new Image { Width = 300, Height = 300 }; using var ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ms.Position = 0; img.Source = BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); var urlBox = new TextBox { Text = url, IsReadOnly = true, FontSize = 11, Margin = new Thickness(0, 12, 0, 4), TextWrapping = TextWrapping.Wrap }; var copyBtn = new Button { Content = "주소 복사", Padding = new Thickness(14, 6, 14, 6), HorizontalAlignment = HorizontalAlignment.Left, Margin = new Thickness(0, 8, 0, 0) }; copyBtn.Click += (_, _) => { try { Clipboard.SetText(url); } catch { } }; var note = new TextBlock { Text = $"포트 {server.Port} · Windows 방화벽 확인이 뜨면 '허용'을 선택하세요\n토큰은 이 기기와 페어링된 폰에서만 유효합니다", FontSize = 11, Foreground = System.Windows.Media.Brushes.Gray, TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 12, 0, 0) }; stack.Children.Add(title); stack.Children.Add(sub); stack.Children.Add(img); stack.Children.Add(urlBox); stack.Children.Add(copyBtn); stack.Children.Add(note); Content = stack; } /// LAN IPv4(게이트웨이 있는 첫 후보) — 없으면 loopback. private static string GetLanIp() { try { using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0); socket.Connect("8.8.8.8", 65530); // 외부 행선지 지정 → 라우팅되는 인터페이스 IP 획득(패킷 전송 없음) return (socket.LocalEndPoint as IPEndPoint)?.Address.ToString() ?? "127.0.0.1"; } catch { return "127.0.0.1"; } } }