自動刷新QRCODE,修正開關台元件問題

This commit is contained in:
jasonchenwork 2025-05-19 13:23:09 +08:00
parent 2dda501294
commit 706daa0080
7 changed files with 148 additions and 98 deletions

View File

@ -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 callnull-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>
/// 生成隨機路徑

View File

@ -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);
}

View File

@ -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

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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
{