自動刷新QRCODE,修正開關台元件問題
This commit is contained in:
parent
2dda501294
commit
706daa0080
137
HttpServer.cs
137
HttpServer.cs
@ -27,86 +27,50 @@ namespace DualScreenDemo
|
||||
private static readonly ConcurrentDictionary<string, byte[]> _fileCache = new ConcurrentDictionary<string, byte[]>();
|
||||
private static readonly SemaphoreSlim _requestThrottle = new SemaphoreSlim(20); // 限制并发请求数
|
||||
private static readonly CancellationTokenSource _serverCts = new CancellationTokenSource();
|
||||
|
||||
|
||||
public static async Task StartServer(string baseDirectory, int port, SongListManager manager)
|
||||
private static HttpListener _listener;
|
||||
private static CancellationTokenSource _cts;
|
||||
private static Task _serverTask;
|
||||
private static string _baseDirectory = @"themes\superstar\_www"; // 根據實際情況設定
|
||||
private static SongListManager _songListManager;
|
||||
private static TaskCompletionSource<string> _qrReadyTcs;
|
||||
|
||||
public static async Task StartServer(string baseDirectory, int port, SongListManager manager, CancellationToken token)
|
||||
{
|
||||
songListManager = manager; // 保存传递的SongListManager实例
|
||||
_songListManager = manager;
|
||||
string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory);
|
||||
randomFolderPath = randomFolderName; // 初始化全局变量
|
||||
|
||||
// 读取 IP 地址
|
||||
string localAddress = GetLocalIPAddress(); // 使用获取的本地 IP
|
||||
string externalAddress = "";
|
||||
randomFolderPath = randomFolderName;
|
||||
|
||||
// 讀取外網地址 沒有端口號
|
||||
string serverAddressFilePath = @"\\SVR01\superstarb\txt\ip.txt";
|
||||
if (File.Exists(serverAddressFilePath))
|
||||
{
|
||||
externalAddress = File.ReadAllText(serverAddressFilePath).Trim();
|
||||
Console.WriteLine("External address: " + externalAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Warning: External address file not found. Using local address only.");
|
||||
}
|
||||
// 創建一個 HttpListener 來監聽 HTTP 請求
|
||||
HttpListener listener = new HttpListener();
|
||||
string localAddress = GetLocalIPAddress();
|
||||
string externalAddress = File.Exists(@"\\SVR01\superstarb\txt\ip.txt")
|
||||
? File.ReadAllText(@"\\SVR01\superstarb\txt\ip.txt").Trim()
|
||||
: "";
|
||||
|
||||
// 構造本地地址的 URL 前綴(包含協議、地址和端口)
|
||||
string localPrefix = String.Format("http://{0}:{1}/", localAddress, port);
|
||||
|
||||
// 在控制台輸出添加的本地前綴,方便調試
|
||||
Console.WriteLine("Adding local prefix: " + localPrefix);
|
||||
|
||||
// 將本地前綴添加到 HttpListener,使其監聽該 URL
|
||||
listener.Prefixes.Add(localPrefix);
|
||||
string hostName = System.Net.Dns.GetHostName();
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add($"http://{localAddress}:{port}/");
|
||||
string hostName = System.Net.Dns.GetHostName();
|
||||
string externalPort = '1' + hostName.Substring(Math.Max(2, hostName.Length - 20));
|
||||
|
||||
// 如果有外网地址,也添加外网地址前缀
|
||||
if (!string.IsNullOrEmpty(externalAddress))
|
||||
{
|
||||
string[] parts = externalAddress.Split(':');
|
||||
string host = parts[0];
|
||||
|
||||
//int externalPort = parts.Length > 1 ? int.Parse(parts[1]) : port;
|
||||
|
||||
string externalPrefix = String.Format("http://{0}:{1}/", host, externalPort);
|
||||
Console.WriteLine("Adding external prefix: " + externalPrefix);
|
||||
|
||||
try
|
||||
{
|
||||
listener.Prefixes.Add(externalPrefix);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Warning: Could not add external prefix: {ex.Message}");
|
||||
}
|
||||
string host = externalAddress.Split(':')[0];
|
||||
externalPort = '1' + System.Net.Dns.GetHostName().Substring(Math.Max(2, System.Net.Dns.GetHostName().Length - 20));
|
||||
_listener.Prefixes.Add($"http://{host}:{externalPort}/");
|
||||
}
|
||||
|
||||
// 生成两个二维码内容
|
||||
string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", localAddress, port, randomFolderName);
|
||||
// string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", "ss.net.dnsnet.cc", 1102, randomFolderName);
|
||||
|
||||
|
||||
// 修改外网二维码内容生成
|
||||
|
||||
string externalQrContent = !string.IsNullOrEmpty(externalAddress) ?
|
||||
String.Format("http://{0}:{1}/{2}/windows.html", externalAddress, externalPort, randomFolderName) :
|
||||
localQrContent;
|
||||
|
||||
Console.WriteLine("local QR Content : " + localQrContent);
|
||||
Console.WriteLine("external QR Content : " + externalQrContent);
|
||||
// 生成二维码(这里使用外网地址的二维码,因为通常外网地址更有用)
|
||||
string qrImagePath = GenerateQRCode(externalQrContent, Path.Combine(baseDirectory, randomFolderName, "qrcode.png"));
|
||||
|
||||
GenerateQRCode(externalQrContent, Path.Combine(baseDirectory, randomFolderName, "qrcode.png"));
|
||||
|
||||
_qrReadyTcs?.TrySetResult(randomFolderName); // safe call,null-safe
|
||||
try
|
||||
{
|
||||
listener.Start();
|
||||
_listener.Start();
|
||||
Console.WriteLine("Server started.");
|
||||
|
||||
// 在程序关闭时删除随机文件夹
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => DeleteRandomFolder(baseDirectory);
|
||||
}
|
||||
catch (HttpListenerException ex)
|
||||
{
|
||||
@ -114,11 +78,51 @@ namespace DualScreenDemo
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
HttpListenerContext context = await listener.GetContextAsync();
|
||||
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
|
||||
try
|
||||
{
|
||||
HttpListenerContext context = await _listener.GetContextAsync();
|
||||
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{
|
||||
// 可能是 Stop() 引起,不處理
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_listener.Close();
|
||||
Console.WriteLine("Server stopped.");
|
||||
}
|
||||
|
||||
public static async Task RestartServer()
|
||||
{
|
||||
Console.WriteLine("Restarting server...");
|
||||
|
||||
_cts?.Cancel();
|
||||
_listener?.Stop();
|
||||
|
||||
if (_serverTask != null)
|
||||
{
|
||||
try { await _serverTask; }
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
DeleteRandomFolder(_baseDirectory);
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
// ✅ 等 QR code 跑完
|
||||
_qrReadyTcs = new TaskCompletionSource<string>();
|
||||
_serverTask = StartServer(_baseDirectory, _port, _songListManager, _cts.Token);
|
||||
string readyFolder = await _qrReadyTcs.Task;
|
||||
|
||||
// ✅ 等 QR Code 圖片準備好之後再處理 UI 更新
|
||||
Console.WriteLine("restart : " + readyFolder);
|
||||
PrimaryForm.Instance.OverlayQRCodeOnImage(readyFolder);
|
||||
OverlayForm.Instance.DisplayQRCodeOnOverlay(readyFolder);
|
||||
}
|
||||
|
||||
private static async Task ProcessRequestWithTimeout(HttpListenerContext context, string baseDirectory, string randomFolderName)
|
||||
@ -161,11 +165,11 @@ namespace DualScreenDemo
|
||||
try
|
||||
{
|
||||
Directory.Delete(fullPath, true);
|
||||
Console.WriteLine("Deleted random folder: " + fullPath);
|
||||
Console.WriteLine("刪除 random folder: " + fullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error deleting random folder: " + ex.Message);
|
||||
Console.WriteLine("錯誤 deleting random folder: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,7 +177,6 @@ namespace DualScreenDemo
|
||||
public static string GetServerAddress()
|
||||
{
|
||||
return String.Format("http://{0}:{1}/", _localIP, _port);
|
||||
// return String.Format("http://111.246.145.170:8080/");
|
||||
}
|
||||
/// <summary>
|
||||
/// 生成隨機路徑
|
||||
|
@ -4,13 +4,17 @@ namespace DualScreenDemo
|
||||
{
|
||||
public static class HttpServerManager
|
||||
{
|
||||
private static CancellationTokenSource _cts;
|
||||
|
||||
public static async void StartServer()
|
||||
{
|
||||
int httpPort = 9090; // 你可以修改此端口
|
||||
string baseDirectory = Path.Combine(Application.StartupPath, @"themes\superstar\_www");
|
||||
|
||||
CleanUpDirectory(baseDirectory);
|
||||
await HttpServer.StartServer(baseDirectory, httpPort, Program.songListManager);
|
||||
// await HttpServer.StartServer(baseDirectory, httpPort, Program.songListManager);
|
||||
_cts = new CancellationTokenSource();
|
||||
await HttpServer.StartServer(baseDirectory, httpPort, Program.songListManager, _cts.Token);
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,11 +62,12 @@ namespace OverlayFormObj
|
||||
get { return _mainForm; }
|
||||
private set { _mainForm = value; }
|
||||
}
|
||||
|
||||
public static OverlayForm Instance { get; private set; }
|
||||
public OverlayForm()
|
||||
{
|
||||
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
|
||||
MainForm = this;
|
||||
Instance = this;
|
||||
InitializeFormSettings();
|
||||
ConfigureTimers();
|
||||
InitializeLabels();
|
||||
@ -1512,7 +1513,6 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
ClearDisplay();
|
||||
DisplaySongs(currentPage);
|
||||
}
|
||||
/* 秒數偵測BUG 測試 */
|
||||
public void AddSongToPlaylist(SongData songData)
|
||||
{
|
||||
try
|
||||
|
@ -66,7 +66,7 @@ namespace DualScreenDemo
|
||||
string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `song_id` DESC LIMIT {songLimit}";
|
||||
var guoYuSongs = SearchSongs_Mysql(query);
|
||||
UpdateSongList(guoYuSongs);
|
||||
|
||||
|
||||
SetButtonsVisibility();
|
||||
HideQRCode();
|
||||
}
|
||||
@ -84,7 +84,6 @@ namespace DualScreenDemo
|
||||
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
|
||||
promotionsButton.BackgroundImage = promotionsNormalBackground;
|
||||
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
|
||||
|
||||
activeButton.BackgroundImage = activeBackground;
|
||||
}
|
||||
|
||||
@ -144,13 +143,8 @@ namespace DualScreenDemo
|
||||
|
||||
private void HideQRCode()
|
||||
{
|
||||
if (pictureBoxQRCode != null)
|
||||
{
|
||||
pictureBoxQRCode.Visible = false;
|
||||
}
|
||||
if(closeQRCodeButton != null){
|
||||
closeQRCodeButton.Visible = false;
|
||||
}
|
||||
pictureBoxQRCode.Visible = false;
|
||||
closeQRCodeButton.Visible = false;
|
||||
}
|
||||
|
||||
private void InitializeButtonsForHotSong()
|
||||
|
@ -3,10 +3,10 @@ namespace DualScreenDemo
|
||||
{
|
||||
public partial class PrimaryForm : Form
|
||||
{
|
||||
private PictureBox pictureBoxQRCode;
|
||||
private Button closeQRCodeButton;
|
||||
public PictureBox pictureBoxQRCode;
|
||||
public Button closeQRCodeButton;
|
||||
|
||||
private void OverlayQRCodeOnImage(string randomFolderPath)
|
||||
public void OverlayQRCodeOnImage(string randomFolderPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -40,17 +40,19 @@ namespace DualScreenDemo
|
||||
string qrImagePath = Path.Combine(Application.StartupPath, "themes/superstar/_www", randomFolderPath, "qrcode.png");
|
||||
if (!File.Exists(qrImagePath))
|
||||
{
|
||||
Console.WriteLine("QR code image not found: " + qrImagePath);
|
||||
Console.WriteLine("布局 image not found: " + qrImagePath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string tempPath = Path.GetTempFileName();
|
||||
File.Copy(qrImagePath, tempPath, true); // 複製一份再讀,避開快取
|
||||
|
||||
Image qrCodeImage = null;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fs = new FileStream(qrImagePath, FileMode.Open, FileAccess.Read))
|
||||
using (var fs = new FileStream(tempPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
qrCodeImage = Image.FromStream(fs);
|
||||
}
|
||||
@ -59,9 +61,10 @@ namespace DualScreenDemo
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error loading QR code image: " + ex.Message);
|
||||
System.Threading.Thread.Sleep(100);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
File.Delete(tempPath); // 用完記得刪除
|
||||
|
||||
if (qrCodeImage == null)
|
||||
{
|
||||
@ -78,15 +81,10 @@ namespace DualScreenDemo
|
||||
{
|
||||
|
||||
g.DrawImage(baseImage, 0, 0);
|
||||
|
||||
|
||||
|
||||
Rectangle qrCodeRect = new Rectangle(32, 39, 165, 165);
|
||||
|
||||
g.DrawImage(qrCodeImage, qrCodeRect);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
pictureBoxQRCode.Image = new Bitmap(bitmap);
|
||||
}
|
||||
}
|
||||
|
@ -339,10 +339,14 @@ namespace DualScreenDemo
|
||||
buttonTopLeft.BringToFront();
|
||||
buttonThanks.BringToFront();
|
||||
}
|
||||
|
||||
// 修正螢幕初始化關鍵
|
||||
public void HideSendOffScreen()
|
||||
{
|
||||
sendOffPanel.Visible = false;
|
||||
foreach (Control ctrl in this.Controls)
|
||||
{
|
||||
ctrl.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HideControlsRecursively(Control parent)
|
||||
@ -2457,12 +2461,48 @@ namespace DualScreenDemo
|
||||
|
||||
// 添加 Form Load 事件處理方法
|
||||
private void PrimaryForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
// 確保所有控件都已初始化完成後,再觸發熱門排行按鈕點擊
|
||||
{
|
||||
if (hotPlayButton != null)
|
||||
{
|
||||
HotPlayButton_Click(null, EventArgs.Empty);
|
||||
}
|
||||
try
|
||||
{
|
||||
string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "txt", "states.txt");
|
||||
string initialState = ReadStateFile(stateFilePath);
|
||||
|
||||
if (initialState.Equals("CLOSE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (Control ctrl in this.Controls)
|
||||
{
|
||||
ctrl.Enabled = false;
|
||||
}
|
||||
|
||||
ShowSendOffScreen();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[PrimaryForm_Load] 初始化狀態錯誤: {ex.Message}");
|
||||
}
|
||||
// 確保所有控件都已初始化完成後,再觸發熱門排行按鈕點擊
|
||||
|
||||
}
|
||||
|
||||
private string ReadStateFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
return File.ReadAllText(filePath).Trim();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("讀取 states.txt 時發生錯誤: " + ex.Message);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private void AutoRefreshTimer_Tick(object sender, EventArgs e)
|
||||
|
17
TCPServer.cs
17
TCPServer.cs
@ -87,9 +87,10 @@ namespace DualScreenDemo
|
||||
//sender.Start();
|
||||
// Console.WriteLine($"heart beat for server{sender.RemoteEndPoint.Address}:{sender.RemoteEndPoint.Port}");
|
||||
try {
|
||||
|
||||
string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"txt","states.txt");
|
||||
string initialState = ReadStateFile(stateFilePath);
|
||||
|
||||
/*
|
||||
if (initialState.Equals("CLOSE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_ = SafeInvoke(PrimaryForm.Instance, () =>
|
||||
@ -106,6 +107,7 @@ namespace DualScreenDemo
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -139,11 +141,13 @@ namespace DualScreenDemo
|
||||
|
||||
if (command.Trim().Equals("X", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
PrimaryForm.Instance.pictureBoxQRCode.Visible = false;
|
||||
PrimaryForm.Instance.closeQRCodeButton.Visible = false;
|
||||
_ = SafeInvoke(VideoPlayerForm.Instance, async () =>
|
||||
{
|
||||
if (IsFormReady(PrimaryForm.Instance))
|
||||
{
|
||||
await SafeInvoke(PrimaryForm.Instance, () =>
|
||||
await SafeInvoke(PrimaryForm.Instance, async () =>
|
||||
{
|
||||
PrimaryForm.Instance.ShowSendOffScreen();
|
||||
|
||||
@ -178,7 +182,14 @@ namespace DualScreenDemo
|
||||
VideoPlayerForm.Instance.PlayNextSong();
|
||||
PrimaryForm.Instance.logout();
|
||||
Console.WriteLine("已設置新的播放列表,包含當前歌曲和 CLOSE.MPG");
|
||||
|
||||
try
|
||||
{
|
||||
await HttpServer.RestartServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("RestartServer 發生錯誤: " + ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user