From d041ff1f80eee515b5f159b6b19f99dcd2adfdb7 Mon Sep 17 00:00:00 2001 From: jasonchenwork Date: Thu, 3 Jul 2025 18:15:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E6=AA=94=20=E8=AA=BF=E6=95=B4=20=E5=BF=83=E8=B7=B3=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20=E4=BF=AE=E6=AD=A3=20=E8=97=8F=E9=8F=A1=E4=BA=BA=20?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20=E6=9C=8D=E5=8B=99=E9=88=B4=2020250703?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CommandHandler.cs | 17 +- DBObj/SongListManager.cs | 4 +- DataCheck.cs | 140 --------- Env.cs | 61 ++++ HeartbeatSender.cs | 290 ++++++++---------- HttpServer.cs | 21 +- OverlayFormObj/OverlayForm.Helpers.cs | 35 +++ OverlayFormObj/OverlayForm.Labels.cs | 78 ++++- OverlayFormObj/OverlayForm.cs | 10 +- .../HotSong/PrimaryForm.HotSong.cs | 36 +-- .../HotSong/PrimaryForm.HotSongMandarinNew.cs | 3 +- .../PrimaryForm.HotSongTaiwaneseNew.cs | 4 +- .../NewSongAlert/PrimaryForm.NewSongAlert.cs | 32 +- .../PrimaryForm.NewSongAlertCantonese.cs | 12 +- .../PrimaryForm.NewSongAlertChinese.cs | 12 +- .../PrimaryForm.NewSongAlertEnglish.cs | 12 +- .../PrimaryForm.NewSongAlertJapanese.cs | 12 +- .../PrimaryForm.NewSongAlertKorean.cs | 12 +- .../PrimaryForm.NewSongAlertTaiwanese.cs | 12 +- PrimaryFormParts/PrimaryForm.Promotions.cs | 32 +- .../PrimaryForm.PromotionsAndMenuPanel.cs | 42 ++- PrimaryFormParts/PrimaryForm.SQLSearch.cs | 38 +-- PrimaryFormParts/PrimaryForm.cs | 65 +--- Program.cs | 79 ++--- TCPServer.cs | 67 +--- VideoPlayerForm.cs | 10 +- 26 files changed, 413 insertions(+), 723 deletions(-) delete mode 100644 DataCheck.cs create mode 100644 Env.cs diff --git a/CommandHandler.cs b/CommandHandler.cs index 04fd8b4..1e54917 100644 --- a/CommandHandler.cs +++ b/CommandHandler.cs @@ -70,7 +70,6 @@ namespace DualScreenDemo case "A26CA4": Console.WriteLine("ToggleVocalRemoval Invoked"); SafeInvokeAction("A26CA4",() => VideoPlayerForm.Instance.ToggleVocalRemoval()); - SafeInvokeAction("A26CA4",() => OverlayForm.MainForm.ShowOriginalSongLabel()); break; // 導唱 case "A26EA4": @@ -180,6 +179,10 @@ namespace DualScreenDemo SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.HideAllLabels()); SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調")); break; + case "A266A4": + SafeInvokeAction("A266A4",() => OverlayForm.MainForm.HideAllLabels()); + SafeInvokeAction("A266A4",() => OverlayForm.MainForm.ShowServiceBell()); + break; default: if (Regex.IsMatch(indata, @"^A23\d+A4$")) { @@ -189,20 +192,8 @@ namespace DualScreenDemo } } - private Dictionary _recentCommands = new(); - private readonly TimeSpan _debounceInterval = TimeSpan.FromMilliseconds(300); // 最短觸發間隔 private void SafeInvokeAction(string commandKey, Action action) { - /*var now = DateTime.Now; - - if (_recentCommands.TryGetValue(commandKey, out DateTime lastTime)) - { - if (now - lastTime < _debounceInterval) - return; // 忽略短時間內的重複指令 - } - - _recentCommands[commandKey] = now; - */ // 真正執行 UI 操作 if (OverlayForm.MainForm.InvokeRequired) { diff --git a/DBObj/SongListManager.cs b/DBObj/SongListManager.cs index 0e177d1..e7f97c1 100644 --- a/DBObj/SongListManager.cs +++ b/DBObj/SongListManager.cs @@ -51,11 +51,11 @@ namespace DBObj } */ public List SearchNewSongs(){ - string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {PrimaryForm.ReadNewSongLimit()};"; + string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; return PrimaryForm.Instance.SearchSongs_Mysql(query); } public List SearchHotSongs(){ - string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT {PrimaryForm.ReadHotSongLimit()};"; + string query= $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; return PrimaryForm.Instance.SearchSongs_Mysql(query); } public List SearchSongsBySinger(string keyword) diff --git a/DataCheck.cs b/DataCheck.cs deleted file mode 100644 index 68ebffd..0000000 --- a/DataCheck.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System.IO; -namespace DataCheck -{ - public class dataCheck - { - public dataCheck() - { - menu_check(); - news_check(); - } - private void menu_check() - { - string menuPath = @"\\svr01\foods"; - string menuPath_local = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foods"); - - if (!Directory.Exists(menuPath_local)) - { - Directory.CreateDirectory(menuPath_local); - } - var serverFiles = Directory.GetFiles(menuPath, "*.jpg") - .Select(f => new FileInfo(f)) - .ToDictionary(f => f.Name, f => f); - - var localFiles = Directory.GetFiles(menuPath_local, "*.jpg") - .Select(f => new FileInfo(f)) - .ToDictionary(f => f.Name, f => f); - foreach (var serverFile in serverFiles) - { - bool needsCopy = false; - string localFilePath = Path.Combine(menuPath_local, serverFile.Key); - if (!localFiles.ContainsKey(serverFile.Key)) - { - needsCopy = true; - } - else - { - var localFile = localFiles[serverFile.Key]; - if (serverFile.Value.LastWriteTime > localFile.LastWriteTime) - { - needsCopy = true; - } - } - - if (needsCopy) - { - try - { - File.Copy(serverFile.Value.FullName, localFilePath, true); - Console.WriteLine($"更新菜單: {serverFile.Key}"); - } - catch (Exception ex) - { - Console.WriteLine($"複製菜單失敗 {serverFile.Key}: {ex.Message}"); - } - } - } - // 3-2. 清除本地有但伺服器已經沒有的檔案 - foreach (var localFile in localFiles) - { - if (!serverFiles.ContainsKey(localFile.Key)) - { - try - { - File.Delete(localFile.Value.FullName); - Console.WriteLine($"刪除本地多餘菜單: {localFile.Key}"); - } - catch (Exception ex) - { - Console.WriteLine($"刪除菜單失敗 {localFile.Key}: {ex.Message}"); - } - } - } - } - private void news_check() - { - string newsPath = @"\\svr01\news"; - string newsPath_local = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "news"); - - if (!Directory.Exists(newsPath_local)) - { - Directory.CreateDirectory(newsPath_local); - } - var serverFiles = Directory.GetFiles(newsPath, "*.jpg") - .Select(f => new FileInfo(f)) - .ToDictionary(f => f.Name, f => f); - - var localFiles = Directory.GetFiles(newsPath_local, "*.jpg") - .Select(f => new FileInfo(f)) - .ToDictionary(f => f.Name, f => f); - foreach (var serverFile in serverFiles) - { - bool needsCopy = false; - string localFilePath = Path.Combine(newsPath_local, serverFile.Key); - if (!localFiles.ContainsKey(serverFile.Key)) - { - needsCopy = true; - } - else - { - var localFile = localFiles[serverFile.Key]; - if (serverFile.Value.LastWriteTime > localFile.LastWriteTime) - { - needsCopy = true; - } - } - - if (needsCopy) - { - try - { - File.Copy(serverFile.Value.FullName, localFilePath, true); - Console.WriteLine($"更新新聞: {serverFile.Key}"); - } - catch (Exception ex) - { - Console.WriteLine($"複製新聞失敗 {serverFile.Key}: {ex.Message}"); - } - } - } - // 3-2. 清除本地有但伺服器已經沒有的檔案 - foreach (var localFile in localFiles) - { - if (!serverFiles.ContainsKey(localFile.Key)) - { - try - { - File.Delete(localFile.Value.FullName); - Console.WriteLine($"刪除本地多餘新聞: {localFile.Key}"); - } - catch (Exception ex) - { - Console.WriteLine($"刪除新聞失敗 {localFile.Key}: {ex.Message}"); - } - } - } - - } - - } -} \ No newline at end of file diff --git a/Env.cs b/Env.cs new file mode 100644 index 0000000..1ac982f --- /dev/null +++ b/Env.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Utils +{ + public static class Env + { + public static readonly string KtvPath = @"\\pc101\KTVData"; + private static readonly Dictionary _values; + static Env() + { + var path = Path.Combine(KtvPath, "config.ini"); + _values = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (!File.Exists(path)) + { + Console.WriteLine($"❌ 找不到環境檔案:{path}"); + return; + } + + foreach (var line in File.ReadAllLines(path)) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed) || trimmed.StartsWith("#")) continue; + + var index = trimmed.IndexOf('='); + if (index < 0) continue; + + var key = trimmed[..index].Trim(); + var value = trimmed[(index + 1)..].Trim(); + + if (value.StartsWith("\"") && value.EndsWith("\"")) + value = value[1..^1]; // 去除引號 + + _values[key] = value; + } + } + + public static string Get(string key, string fallback = "") + { + return _values.TryGetValue(key, out var value) ? value : fallback; + } + + public static bool GetBool(string key, bool fallback = false) + { + return _values.TryGetValue(key, out var value) && bool.TryParse(value, out var result) + ? result + : fallback; + } + + public static int GetInt(string key, int fallback = 0) + { + return _values.TryGetValue(key, out var value) && int.TryParse(value, out var result) + ? result + : fallback; + } + + // 可加上 GetFloat、GetDouble、GetTimeSpan ... 視需要 + } +} diff --git a/HeartbeatSender.cs b/HeartbeatSender.cs index 19b3a93..c8d6672 100644 --- a/HeartbeatSender.cs +++ b/HeartbeatSender.cs @@ -1,182 +1,156 @@ using System.Net.Http; using System.Text; -using System.Text.Json; // 適用於 .NET Core 3.0+ / .NET 5/6/7/8 +using System.Text.Json; using System.Net; using System.Net.Sockets; using System.Diagnostics; using System.IO; +using System.Threading; +using System.Threading.Tasks; using Microsoft.VisualBasic.Devices; -namespace HeartbeatSender +namespace HeartbeatSender; + +public class heartbeatSender { + private readonly HttpClient httpClient = new(); + private string token = ""; + private string branchName = ""; + private string heartbeatDomain = ""; + private CancellationTokenSource? cancellationTokenSource; - public class heartbeatSender + public heartbeatSender() { - private readonly HttpClient httpClient = new HttpClient(); - private string token; - private string token_heartbeatUrl; - private string init_heartbeatUrl; + this.heartbeatDomain = Utils.Env.Get("HeartBeatUrl", ""); - public heartbeatSender() + // 在建構子中啟動背景心跳任務 + _ = Task.Run(() => StartAsync()); + } + public async Task StartAsync() + { + cancellationTokenSource = new CancellationTokenSource(); + + while (!cancellationTokenSource.IsCancellationRequested) { - LoadHeartbeatUrls("txt/HeartBeat.txt"); - } - /// - /// 讀取URL - /// - /// 路徑 - private void LoadHeartbeatUrls(string filePath) - { - if (!File.Exists(filePath)) - { - Console.WriteLine("找不到 HeartBeat.txt"); - return; - } - - string[] lines = File.ReadAllLines(filePath); - - foreach (string line in lines) - { - if (line.StartsWith("init:")) - { - init_heartbeatUrl = line.Substring("init:".Length).Trim(); - } - else if (line.StartsWith("token:")) - { - token_heartbeatUrl = line.Substring("token:".Length).Trim(); - } - } - - Console.WriteLine("init URL: " + init_heartbeatUrl); - Console.WriteLine("token URL: " + token_heartbeatUrl); - } - - public static string GetLocalIPv4() - { - foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) - { - if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) - { - return ip.ToString(); - } - } - return "127.0.0.1"; // fallback - } - public async Task LoginAndGetTokenAsync() - { - // init_heartbeat - //var loginUrl = "http://zqd.superstar.dnsnet.cc/api/room/receiveRegister"; - string hostName = System.Net.Dns.GetHostName(); - - var loginPayload = new - { - branch_name = "測試", - room_name = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)), - room_ip = GetLocalIPv4(), - email = "MachineKTV@gmail.com", - password = "aa147258-" - }; - - var json = JsonSerializer.Serialize(loginPayload); - var content = new StringContent(json, Encoding.UTF8, "application/json"); - try { - var response = await httpClient.PostAsync(init_heartbeatUrl, content); - response.EnsureSuccessStatusCode(); - - var responseJson = await response.Content.ReadAsStringAsync(); - // Console.WriteLine("API 回傳內容:" + responseJson); - - using var doc = JsonDocument.Parse(responseJson); - if (doc.RootElement.TryGetProperty("data", out JsonElement dataElement) && - dataElement.ValueKind == JsonValueKind.Object) + if (string.IsNullOrEmpty(token)) { - if (dataElement.TryGetProperty("token", out JsonElement tokenElement)) - { - token = tokenElement.GetString(); - } + bool loginSuccess = await LoginAndGetTokenAsync(); + Console.WriteLine(loginSuccess ? "心跳登入成功" : "心跳登入失敗"); } - // Console.WriteLine("登入成功,取得 token:" + token); - return true; + + if (!string.IsNullOrEmpty(token)) + { + await SendHeartbeatAsync(); + } + + await Task.Delay(TimeSpan.FromMinutes(1), cancellationTokenSource.Token); } catch (Exception ex) { - Console.WriteLine($"登入失敗:{ex.Message}"); - return false; + Console.WriteLine($"心跳任務錯誤:{ex.Message}"); + await Task.Delay(5000); // 錯誤後延遲再跑 } } - - public async Task SendHeartbeatAsync() - { - if (string.IsNullOrEmpty(token)) - { - Console.WriteLine("請先登入取得 token"); - return; - } - //Console.WriteLine(GetLocalIPv4()); - string hostName = System.Net.Dns.GetHostName(); - - var heartbeatData = new - { - branch_name = "測試", - hostname = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)), - ip = GetLocalIPv4(), - cpu = GetCpuUsage(), - memory = GetTotalMemoryInMB(), - disk = GetDiskTotalSizeInGB() - }; - - var json = JsonSerializer.Serialize(heartbeatData); - var content = new StringContent(json, Encoding.UTF8, "application/json"); - // token_heartbeat - // heartbeatUrl = "http://zqd.superstar.dnsnet.cc/api/room/heartbeat"; - var request = new HttpRequestMessage(HttpMethod.Post, token_heartbeatUrl); - request.Content = content; - request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); - // Console.WriteLine("送出的 JSON:"); - // Console.WriteLine(json); - var response = await httpClient.SendAsync(request); - try - { - - // Console.WriteLine($"心跳送出狀態:{response.StatusCode}"); - - response.EnsureSuccessStatusCode(); - - var responseJson = await response.Content.ReadAsStringAsync(); - - using var doc = JsonDocument.Parse(responseJson); - // Console.WriteLine("API 回傳內容:" + responseJson); - - } - catch (Exception ex) - { - var errorContent = await response.Content.ReadAsStringAsync(); - Console.WriteLine($"送出心跳錯誤:{ex.Message}"); - Console.WriteLine($"後端回應內容:{errorContent}"); - } - } - - private float GetCpuUsage() - { - using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); - cpuCounter.NextValue(); // 需要呼叫兩次 - System.Threading.Thread.Sleep(100); - return cpuCounter.NextValue(); // 回傳的是百分比,例如 25.3 - } - private float GetTotalMemoryInMB() - { - var computerInfo = new ComputerInfo(); - var totalMB = computerInfo.TotalPhysicalMemory / (1024f); - var availableMB = computerInfo.AvailablePhysicalMemory / (1024f); - var usedMB = totalMB - availableMB; - return usedMB; // 轉 MB - } - private float GetDiskTotalSizeInGB(string driveLetter = "C") - { - var drive = new DriveInfo(driveLetter); - return drive.TotalSize / (1024f * 1024f * 1024f); // 轉 GB - } + } + + public void Stop() + { + cancellationTokenSource?.Cancel(); + Console.WriteLine("心跳任務已停止"); + } + + public async Task LoginAndGetTokenAsync() + { + var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/receiveRegister"; + var loginPayload = new + { + email = "MachineKTV@gmail.com", + password = "aa147258-" + }; + + var result = await SendPostAsync(url, loginPayload); + + if (result.ValueKind == JsonValueKind.Object && + result.TryGetProperty("data", out JsonElement data)) + { + token = data.GetProperty("token").GetString()!; + branchName = data.GetProperty("branch_name").GetString()!; + return true; + } + + return false; + } + + public async Task SendHeartbeatAsync() + { + var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/heartbeat"; + string hostName = Dns.GetHostName(); + + var heartbeatPayload = new + { + branch_name = branchName, + hostname = "PC" + hostName[^3..], + ip = GetLocalIPv4(), + cpu = GetCpuUsage(), + memory = GetTotalMemoryInMB(), + disk = GetDiskTotalSizeInGB() + }; + + await SendPostAsync(url, heartbeatPayload, token); + Console.WriteLine($"心跳送出成功: {DateTime.Now:HH:mm:ss}"); + } + + private async Task SendPostAsync(string url, object payload, string? bearerToken = null) + { + var json = JsonSerializer.Serialize(payload); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var request = new HttpRequestMessage(HttpMethod.Post, url) + { + Content = content + }; + + if (!string.IsNullOrWhiteSpace(bearerToken)) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearerToken); + } + + var response = await httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + + string responseJson = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(responseJson); + } + + private static string GetLocalIPv4() + { + foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) + { + if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) + return ip.ToString(); + } + return "127.0.0.1"; + } + + private float GetCpuUsage() + { + using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + cpuCounter.NextValue(); + Thread.Sleep(100); + return cpuCounter.NextValue(); + } + + private float GetTotalMemoryInMB() + { + var ci = new ComputerInfo(); + return (ci.TotalPhysicalMemory - ci.AvailablePhysicalMemory) / 1024f; + } + + private float GetDiskTotalSizeInGB(string driveLetter = "C") + { + var drive = new DriveInfo(driveLetter); + return drive.TotalSize / (1024f * 1024f * 1024f); } } diff --git a/HttpServer.cs b/HttpServer.cs index dd3c96c..baae8a9 100644 --- a/HttpServer.cs +++ b/HttpServer.cs @@ -42,10 +42,7 @@ namespace DualScreenDemo // 安裝包更新 string localAddress = GetLocalIPAddress(); - string externalAddress = File.Exists(@"\\svr01\txt\ip.txt") - ? File.ReadAllText(@"\\svr01\txt\ip.txt").Trim() - : ""; - + string externalAddress = Utils.Env.Get("PhoneIP", "").Trim(); _listener = new HttpListener(); _listener.Prefixes.Add($"http://{localAddress}:{port}/"); string hostName = System.Net.Dns.GetHostName(); @@ -538,7 +535,7 @@ namespace DualScreenDemo // 执行服务操作 PrimaryForm.SendCommandThroughSerialPort("a2 53 a4"); OverlayForm.MainForm.Invoke(new System.Action(() => { - OverlayForm.MainForm.ShowServiceBellLabel(); + OverlayForm.MainForm.ShowServiceBell(); })); // 异步处理等待和隐藏标签 await HttpServer.HandleServiceBellAsync(); @@ -983,9 +980,19 @@ namespace DualScreenDemo string Messagelast=""; for(int j=0;j < message.Length;j++){ Messagelast += message[j]; - await Task.Delay(300); + await Task.Delay(10); // 将读取到的 "message" 字段传递给 UI 控件显示 - InvokeAction(() => OverlayForm.MainForm.ShowmessageLabel(Messagefist+Messagelast+'_')); + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.ShowmessageLabel(Messagefist + Messagelast + '_'); + })); + } + else + { + OverlayForm.MainForm.ShowmessageLabel(Messagefist + Messagelast + '_'); + } } // 真情告白顯示秒數 await Task.Delay(3000); diff --git a/OverlayFormObj/OverlayForm.Helpers.cs b/OverlayFormObj/OverlayForm.Helpers.cs index aea36e4..86a29c8 100644 --- a/OverlayFormObj/OverlayForm.Helpers.cs +++ b/OverlayFormObj/OverlayForm.Helpers.cs @@ -56,10 +56,45 @@ namespace OverlayFormObj this.Invalidate(); blackBackgroundPanel.Invalidate(); + // StartMarquee(); + } + private System.Windows.Forms.Timer marqueeTimer_detection; + private int scrollSpeed = 2; // 每次移動的像素 + + public void StartMarquee() + { + if (marqueeTimer_detection != null) + { + marqueeTimer_detection.Stop(); + marqueeTimer_detection.Dispose(); + } + + marqueeTimer_detection = new System.Windows.Forms.Timer(); + marqueeTimer_detection.Interval = 30; // 約每 30ms 更新一次 + marqueeTimer_detection.Tick += (s, e) => + { + marqueeXPos -= scrollSpeed; + int textWidth = TextRenderer.MeasureText(marqueeText, new Font("Arial", 25, FontStyle.Bold)).Width; + + if (marqueeXPos + textWidth < 0) // 跑出畫面左側 + { + marqueeTimer_detection.Stop(); + marqueeTimer_detection.Dispose(); + marqueeTimer_detection = null; + + Console.WriteLine("Marquee 跑完了!"); + } + + this.Invalidate(); + blackBackgroundPanel.Invalidate(); + }; + + marqueeTimer_detection.Start(); } public void UpdateMarqueeTextSecondLine(string newText) { + Console.WriteLine(InvokeRequired); if (InvokeRequired) { Invoke(new MethodInvoker(() => UpdateMarqueeTextSecondLine(newText))); diff --git a/OverlayFormObj/OverlayForm.Labels.cs b/OverlayFormObj/OverlayForm.Labels.cs index bdc77bd..41a7203 100644 --- a/OverlayFormObj/OverlayForm.Labels.cs +++ b/OverlayFormObj/OverlayForm.Labels.cs @@ -14,8 +14,12 @@ namespace OverlayFormObj public Label originalSongLabel; public Label nextSongLabel; private Label serviceBellLabel; + private Label BellLabel; + private Label OriginalLabel; public Label volumeUpLabel; // New volume up label public Label volumeDownLabel; // New volume down label + private System.Windows.Forms.Timer BellTimer = new System.Windows.Forms.Timer(); + private System.Windows.Forms.Timer OriginalTimer = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer volumeUpTimer = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer volumeDownTimer = new System.Windows.Forms.Timer(); private System.Windows.Forms.Timer micUpTimer = new System.Windows.Forms.Timer(); @@ -60,6 +64,10 @@ namespace OverlayFormObj InitializeMuteLabel(); // Initialize the new mute label InitializeOriginalSongLabel(); // 初始化原唱标签 InitializeServiceBellLabel(); + + InitializeBellLabel(); + InitializeOriginalLabel(); + InitializeVolumeUpLabel(); // Initialize the volume up label InitializeVolumeDownLabel(); // Initialize the volume down label InitializeMicUpLabel(); // Initialize the microphone up label @@ -89,6 +97,12 @@ namespace OverlayFormObj private void ConfigureKeyTimers() { + OriginalTimer.Interval = 1000; + OriginalTimer.Tick += (sender, e) => {OriginalLabel.Visible = false;OriginalTimer.Stop();keepshowmic();}; + + BellTimer.Interval = 1000; + BellTimer.Tick += (sender, e) => {BellLabel.Visible = false;BellTimer.Stop();keepshowmic();}; + volumeUpTimer.Interval = 1000; volumeUpTimer.Tick += (sender, e) => {volumeUpLabel.Visible = false;volumeUpTimer.Stop();keepshowmic();}; @@ -422,13 +436,47 @@ namespace OverlayFormObj volumeUpLabel.Location = new Point(blackBackgroundPanel.Width - volumeUpLabel.Width - 10, 56); }; } + private void InitializeOriginalLabel() + { + OriginalLabel = new Label + { + AutoSize = true, + ForeColor = Color.White, + Font = new Font("Microsoft JhengHei", 34, FontStyle.Bold), + BackColor = Color.Transparent, + Text = "有人聲", + Visible = false + }; + blackBackgroundPanel.Controls.Add(OriginalLabel); + blackBackgroundPanel.Resize += (s, e) => + { + OriginalLabel.Location = new Point(blackBackgroundPanel.Width - OriginalLabel.Width - 10, 56); + }; + } + private void InitializeBellLabel() + { + BellLabel = new Label + { + AutoSize = true, + ForeColor = Color.White, + Font = new Font("Microsoft JhengHei", 34, FontStyle.Bold), + BackColor = Color.Transparent, + Text = "服務鈴", + Visible = false + }; + blackBackgroundPanel.Controls.Add(BellLabel); + blackBackgroundPanel.Resize += (s, e) => + { + BellLabel.Location = new Point(blackBackgroundPanel.Width - BellLabel.Width - 10, 56); + }; + } private void InitializeVolumeDownLabel() { volumeDownLabel = new Label { AutoSize = true, ForeColor = Color.White, - Font = new Font("Microsoft JhengHei",34, FontStyle.Bold), + Font = new Font("Microsoft JhengHei", 34, FontStyle.Bold), BackColor = Color.Transparent, Text = "音量 ↓", Visible = false @@ -808,24 +856,19 @@ public void UpdateNextSongLabelFromPlaylist(bool isUserPlaylistPlaying, SongData } // 显示服务铃标签 public void ShowServiceBellLabel() - { - if (serviceBellLabel != null) - { + { whichoneshowmic(); HideAllLabels(); serviceBellLabel.Visible = true; - // Console.WriteLine("服務鈴顯示: " + serviceBellLabel.Text); // 输出标签文本内容 - } + Console.WriteLine("服務鈴顯示: " + serviceBellLabel.Text); // 输出标签文本内容 } public void HideServiceBellLabel() - { - if (serviceBellLabel != null) - { + { /* serviceBellLabel.Visible = false; - // Console.WriteLine("服務鈴隱藏: " + serviceBellLabel.Text); // 输出标签文本内容 + Console.WriteLine("服務鈴隱藏: " + serviceBellLabel.Text); // 输出标签文本内容 keepshowmic(); - } + */ } // 显示标准标签 @@ -1029,6 +1072,19 @@ public void UpdateNextSongLabelFromPlaylist(bool isUserPlaylistPlaying, SongData volumeUpLabel.Visible = true; volumeUpTimer.Start(); } + public void ShowServiceBell() + { + BellLabel.Visible = true; + BellTimer.Start(); + + } + public void ShowOriginalLabel(string text = "有人聲") + { + OriginalLabel.Text = text; + OriginalLabel.Visible = true; + OriginalTimer.Start(); + + } // 显示音量-标签 public void ShowVolumeDownLabel() { diff --git a/OverlayFormObj/OverlayForm.cs b/OverlayFormObj/OverlayForm.cs index 11a84b4..c9f44ec 100644 --- a/OverlayFormObj/OverlayForm.cs +++ b/OverlayFormObj/OverlayForm.cs @@ -613,7 +613,7 @@ private static void SongDisplayTimer_Elapsed(object sender, EventArgs e) using (Font largeFont = new Font("微軟正黑體", 34, FontStyle.Bold)) using (Brush whiteBrush = new SolidBrush(Color.White)) - using (Brush limeGreenBrush = new SolidBrush(Color.LimeGreen)) + using (Brush LimeGreen = new SolidBrush(Color.LimeGreen)) using (Brush marqueeBrush = new SolidBrush(marqueeTextColor)) using (Brush backgroundBrush = new SolidBrush(Color.FromArgb(255, 0, 0, 0))) { @@ -631,9 +631,9 @@ private static void SongDisplayTimer_Elapsed(object sender, EventArgs e) SizeF textSizeSecondLine = e.Graphics.MeasureString(marqueeTextSecondLine, largeFont); float centeredXPos = (this.Width - textSizeSecondLine.Width) / 2; - + e.Graphics.FillRectangle(backgroundBrush, centeredXPos, yPosition2, textSizeSecondLine.Width, textSizeSecondLine.Height); - e.Graphics.DrawString(marqueeTextSecondLine, largeFont, limeGreenBrush, new PointF(centeredXPos, yPosition2)); + e.Graphics.DrawString(marqueeTextSecondLine, largeFont, LimeGreen, new PointF(centeredXPos, yPosition2)); e.Graphics.Clip = originalClip; @@ -1495,11 +1495,11 @@ private void DisplayArtists(List artists, int page)//歌星點進去後 if (category == Category.NewSongs) { - sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY add_date DESC LIMIT {PrimaryForm.ReadNewSongLimit()};"; + sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; } else if (category == Category.HotSongs) { - sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY song_counts DESC LIMIT {PrimaryForm.ReadHotSongLimit()};"; + sqlQuery = $"SELECT * FROM song_library_cache WHERE language_name = '{language}' ORDER BY song_counts DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; } else { diff --git a/PrimaryFormParts/HotSong/PrimaryForm.HotSong.cs b/PrimaryFormParts/HotSong/PrimaryForm.HotSong.cs index b7b8dd7..5942f98 100644 --- a/PrimaryFormParts/HotSong/PrimaryForm.HotSong.cs +++ b/PrimaryFormParts/HotSong/PrimaryForm.HotSong.cs @@ -62,8 +62,7 @@ namespace DualScreenDemo isOnOrderedSongsPage = false; /* 清空搜尋欄 */ ResetinputBox(); - int songLimit = ReadHotSongLimit(); - string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `song_counts` DESC LIMIT {songLimit};"; + string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; var guoYuSongs = SearchSongs_Mysql(query); UpdateSongList(guoYuSongs); @@ -91,8 +90,7 @@ namespace DualScreenDemo { UpdateHotSongButtons(activeButton, activeBackground); - int songLimit = ReadHotSongLimit(); - string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY `song_counts` DESC LIMIT {songLimit};"; + string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; var selectedSongs = SearchSongs_Mysql(query); UpdateSongList(selectedSongs); @@ -235,35 +233,5 @@ namespace DualScreenDemo this.Controls.Add(button); } - public static int ReadHotSongLimit() - { - string filePath = Path.Combine(Application.StartupPath,"txt","SongLimitsSettings.txt"); - try - { - - var lines = File.ReadAllLines(filePath); - foreach (var line in lines) - { - - if (line.StartsWith("HotSongLimit:")) - { - string valuePart = line.Split(':')[1].Trim(); - int limit; - if (int.TryParse(valuePart, out limit)) - { - return limit; - } - break; - } - } - } - catch (Exception ex) - { - Console.WriteLine("Failed to read song limits from file: " + ex.Message); - return 100; - } - - return 100; - } } } \ No newline at end of file diff --git a/PrimaryFormParts/HotSong/PrimaryForm.HotSongMandarinNew.cs b/PrimaryFormParts/HotSong/PrimaryForm.HotSongMandarinNew.cs index 1f9e1c2..bafbc7a 100644 --- a/PrimaryFormParts/HotSong/PrimaryForm.HotSongMandarinNew.cs +++ b/PrimaryFormParts/HotSong/PrimaryForm.HotSongMandarinNew.cs @@ -7,8 +7,7 @@ namespace DualScreenDemo // 重置其他按钮背景 UpdateHotSongButtons(guoYuNewSongButtonHotSong, guoYuNewSongHotSongActiveBackground); - int songLimit = ReadHotSongLimit(); - string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {songLimit};"; + string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; var selectedSongs = PrimaryForm.Instance.SearchSongs_Mysql(query); currentPage = 0; diff --git a/PrimaryFormParts/HotSong/PrimaryForm.HotSongTaiwaneseNew.cs b/PrimaryFormParts/HotSong/PrimaryForm.HotSongTaiwaneseNew.cs index 3cb2601..f138d02 100644 --- a/PrimaryFormParts/HotSong/PrimaryForm.HotSongTaiwaneseNew.cs +++ b/PrimaryFormParts/HotSong/PrimaryForm.HotSongTaiwaneseNew.cs @@ -7,9 +7,7 @@ namespace DualScreenDemo // 重置其他按钮背景 UpdateHotSongButtons(taiYuNewSongButtonHotSong, taiYuNewSongHotSongActiveBackground); - int songLimit = ReadHotSongLimit(); - - string query = $"SELECT * FROM song_library_cache WHERE language_name = '台語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {songLimit};"; + string query = $"SELECT * FROM song_library_cache WHERE language_name = '台語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};"; var selectedSongs = SearchSongs_Mysql(query); currentPage = 0; diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlert.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlert.cs index b6ef384..b2161c7 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlert.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlert.cs @@ -221,38 +221,8 @@ namespace DualScreenDemo this.Controls.Add(hanYuButtonNewSong); } - public static int ReadNewSongLimit() - { - string filePath = Path.Combine(Application.StartupPath, "txt", "SongLimitsSettings.txt"); - try - { - - var lines = File.ReadAllLines(filePath); - foreach (var line in lines) - { - - if (line.StartsWith("NewSongLimit:")) - { - string valuePart = line.Split(':')[1].Trim(); - int limit; - if (int.TryParse(valuePart, out limit)) - { - return limit; - } - break; - } - } - } - catch (Exception ex) - { - Console.WriteLine("Failed to read song limits from file: " + ex.Message); - return 100; - } - - return 100; - } private string setQueryforNewSong(string category){ - string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY add_date DESC LIMIT {ReadNewSongLimit()};"; + string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};"; return query; } diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertCantonese.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertCantonese.cs index af31407..fdb89b1 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertCantonese.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertCantonese.cs @@ -11,21 +11,13 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; - int songLimit = ReadNewSongLimit(); - - /*yueYuSongs2 = allSongs.Where(song => song.Category == "粵語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("粵語"); - var yueYuSongs2 = SearchSongs_Mysql(query); + var yueYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("粵語")); currentPage = 0; - currentSongList = yueYuSongs2; totalPages = (int)Math.Ceiling((double)yueYuSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(yueYuSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertChinese.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertChinese.cs index 1c6b556..0a09c82 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertChinese.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertChinese.cs @@ -11,21 +11,13 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; - int songLimit = ReadNewSongLimit(); - - /*guoYuSongs2 = allSongs.Where(song => song.Category == "國語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("國語"); - var guoYuSongs2 = SearchSongs_Mysql(query); + var guoYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("國語")); currentPage = 0; - currentSongList = guoYuSongs2; totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(guoYuSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertEnglish.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertEnglish.cs index 7126385..187e33b 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertEnglish.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertEnglish.cs @@ -11,22 +11,14 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; - int songLimit = ReadNewSongLimit(); - - /*yingWenSongs2 = allSongs.Where(song => song.Category == "英語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("英語"); - var yingWenSongs2 = SearchSongs_Mysql(query); + var yingWenSongs2 = SearchSongs_Mysql(setQueryforNewSong("英語")); currentPage = 0; - currentSongList = yingWenSongs2; totalPages = (int)Math.Ceiling((double)yingWenSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(yingWenSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertJapanese.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertJapanese.cs index 9c8ba3d..edc7e5b 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertJapanese.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertJapanese.cs @@ -11,21 +11,13 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongActiveBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; - int songLimit = ReadNewSongLimit(); - - /*riYuSongs2 = allSongs.Where(song => song.Category == "日語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("日語"); - var riYuSongs2 = SearchSongs_Mysql(query); + var riYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("日語")); currentPage = 0; - currentSongList = riYuSongs2; totalPages = (int)Math.Ceiling((double)riYuSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(riYuSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertKorean.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertKorean.cs index 73bf731..5f7d3e7 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertKorean.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertKorean.cs @@ -11,21 +11,13 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongActiveBackground; - int songLimit = ReadNewSongLimit(); - - /*hanYuSongs2 = allSongs.Where(song => song.Category == "韓語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("韓語"); - var hanYuSongs2 = SearchSongs_Mysql(query); + var hanYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("韓語")); currentPage = 0; - currentSongList = hanYuSongs2; totalPages = (int)Math.Ceiling((double)hanYuSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(hanYuSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertTaiwanese.cs b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertTaiwanese.cs index 0785c2b..b9259c1 100644 --- a/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertTaiwanese.cs +++ b/PrimaryFormParts/NewSongAlert/PrimaryForm.NewSongAlertTaiwanese.cs @@ -11,21 +11,13 @@ namespace DualScreenDemo riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; - int songLimit = ReadNewSongLimit(); - - /*taiYuSongs2 = allSongs.Where(song => song.Category == "台語") - .OrderByDescending(song => song.AddedTime) - .Take(songLimit) - .ToList();*/ - string query = setQueryforNewSong("台語"); - var taiYuSongs2 = SearchSongs_Mysql(query); + var taiYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("台語")); currentPage = 0; - currentSongList = taiYuSongs2; totalPages = (int)Math.Ceiling((double)taiYuSongs2.Count / itemsPerPage); multiPagePanel.currentPageIndex = 0; - multiPagePanel.LoadSongs(currentSongList); + multiPagePanel.LoadSongs(taiYuSongs2); } } } \ No newline at end of file diff --git a/PrimaryFormParts/PrimaryForm.Promotions.cs b/PrimaryFormParts/PrimaryForm.Promotions.cs index 1ae60c4..e1567d9 100644 --- a/PrimaryFormParts/PrimaryForm.Promotions.cs +++ b/PrimaryFormParts/PrimaryForm.Promotions.cs @@ -74,27 +74,7 @@ namespace DualScreenDemo } } - private List LoadPromotionsImages() - { - List images = new List(); - string newsFolderPath = Path.Combine(Application.StartupPath, "news"); - - string[] imageFiles = Directory.GetFiles(newsFolderPath, "*.jpg"); - - foreach (string filePath in imageFiles) - { - try - { - images.Add(Image.FromFile(filePath)); - } - catch (Exception ex) - { - Console.WriteLine("Error loading image: " + filePath + ". Exception: " + ex.Message); - } - } - - return images; - } + private void promotionsButton_Click(object sender, EventArgs e) { @@ -137,21 +117,11 @@ namespace DualScreenDemo private void PreviousPromotionButton_Click(object sender, EventArgs e) { - - - - - promotionsAndMenuPanel.LoadPreviousPage(); } private void NextPromotionButton_Click(object sender, EventArgs e) { - - - - - promotionsAndMenuPanel.LoadNextPage(); } diff --git a/PrimaryFormParts/PrimaryForm.PromotionsAndMenuPanel.cs b/PrimaryFormParts/PrimaryForm.PromotionsAndMenuPanel.cs index 3316c48..3037932 100644 --- a/PrimaryFormParts/PrimaryForm.PromotionsAndMenuPanel.cs +++ b/PrimaryFormParts/PrimaryForm.PromotionsAndMenuPanel.cs @@ -11,32 +11,52 @@ namespace DualScreenDemo ResizeAndPositionControl(promotionsAndMenuPanel, 0, 0, 1440, 900); this.Controls.Add(promotionsAndMenuPanel); - promotions = LoadPromotionsImages(); - menu = LoadMenuImages(); + promotions = LoadImagesFromFolder("news"); + menu = LoadImagesFromFolder("foods"); + - } - - private List LoadMenuImages() - { - List images = new List(); - string foodsFolderPath = Path.Combine(Application.StartupPath, "foods"); - string[] imageFiles = Directory.GetFiles(foodsFolderPath, "*.jpg"); + /// + /// 從指定資料夾載入所有 .jpg 圖片 + /// + private List LoadImagesFromFolder(string folderName) + { + List images = new(); + string folderPath = Path.Combine(Utils.Env.KtvPath, folderName); + + if (!Directory.Exists(folderPath)) + { + Console.WriteLine($" 找不到遠端資料夾:{folderPath}"); + return images; + } + + string[] imageFiles = Directory.GetFiles(folderPath, "*.jpg"); foreach (string filePath in imageFiles) { try { - images.Add(Image.FromFile(filePath)); + using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); + images.Add(Image.FromStream(new MemoryStream(ReadFully(fs)))); } catch (Exception ex) { - Console.WriteLine("Error loading image: " + filePath + ". Exception: " + ex.Message); + Console.WriteLine($"載入圖片失敗:{filePath},原因:{ex.Message}"); } } return images; } + + /// + /// 將 Stream 轉為 byte[],用於避免 Image 檔案鎖定 + /// + private byte[] ReadFully(Stream input) + { + using MemoryStream ms = new(); + input.CopyTo(ms); + return ms.ToArray(); + } } } \ No newline at end of file diff --git a/PrimaryFormParts/PrimaryForm.SQLSearch.cs b/PrimaryFormParts/PrimaryForm.SQLSearch.cs index 1fc8f1c..acfe520 100644 --- a/PrimaryFormParts/PrimaryForm.SQLSearch.cs +++ b/PrimaryFormParts/PrimaryForm.SQLSearch.cs @@ -6,40 +6,10 @@ using System.Diagnostics; namespace DualScreenDemo{ public partial class PrimaryForm { - private static string connectionStringfortest = ""; - public static void LoadConnectionStringFromFile(string filePath) - { - filePath = Path.Combine(Application.StartupPath, "txt", filePath); - try - { - if (File.Exists(filePath)) - { - string fileContent = File.ReadAllText(filePath).Trim(); - - if (!string.IsNullOrWhiteSpace(fileContent)) - { - connectionStringfortest = fileContent; - Console.WriteLine("連線字串載入成功。"); - } - else - { - Console.WriteLine("檔案內容為空。"); - } - } - else - { - Console.WriteLine("找不到指定的檔案:" + filePath); - } - } - catch (Exception ex) - { - Console.WriteLine("讀取檔案時發生錯誤:" + ex.Message); - } - } - + private static string GetConnectionString() { - return connectionStringfortest; + return $"Server={Utils.Env.Get("DBServer", "localhost")};Port={Utils.Env.Get("DBPort", "3306")};Database={Utils.Env.Get("Database", "test")};User={Utils.Env.Get("DBUser", "root")};Password={Utils.Env.Get("DBPassword", "")};"; } public bool isLoggedIn = false; @@ -72,8 +42,8 @@ namespace DualScreenDemo{ string artistA = reader["artistA"].ToString(); string artistB = reader["artistB"].ToString(); string fileName = reader["song_filename"].ToString(); - string songFilePathHost1 = Path.Combine(@"\\SVR01\", fileName); - string songFilePathHost2 = Path.Combine(@"\\SVR02\", fileName); + string songFilePathHost1 = Path.Combine(@"\\SVR01\e\", fileName); + string songFilePathHost2 = Path.Combine(@"\\SVR02\e\", fileName); string artistASimplified = reader["artistA_simplified"].ToString(); string artistBSimplified = reader["artistB_simplified"].ToString(); string songSimplified = reader["song_simplified"].ToString(); diff --git a/PrimaryFormParts/PrimaryForm.cs b/PrimaryFormParts/PrimaryForm.cs index 9f44c96..ef0e8e0 100644 --- a/PrimaryFormParts/PrimaryForm.cs +++ b/PrimaryFormParts/PrimaryForm.cs @@ -239,7 +239,6 @@ namespace DualScreenDemo multiPagePanel.PageIndexChanged += HandlePageChanged; // 添加 Load 事件處理 this.Load += PrimaryForm_Load; - LoadConnectionStringFromFile("test.env"); } public bool IsAppResponsive() @@ -1554,23 +1553,11 @@ namespace DualScreenDemo try { // 1. 檢查是否能連接到 SVR01 - string serverVideoPath = @"\\SVR01\video"; - string serverVideoPath2 = @"\\SVR02\video"; - string selectedServerPath = null; + string serverVideoPath = Path.Combine(Utils.Env.KtvPath, "video"); string localVideoPath = @"D:\video"; - if (Directory.Exists(serverVideoPath)) - { - selectedServerPath = serverVideoPath; - Console.WriteLine("已連接到 SVR01"); - } - else if (Directory.Exists(serverVideoPath2)) - { - selectedServerPath = serverVideoPath2; - Console.WriteLine("已連接到 SVR02"); - } - else + if (!Directory.Exists(serverVideoPath)) { Console.WriteLine("未連接到 SVR,使用本地影片"); LoadLocalVideoFiles(); @@ -1584,7 +1571,7 @@ namespace DualScreenDemo } // 獲取服務器和本地的所有文件 - var serverFiles = Directory.GetFiles(selectedServerPath, "*.mpg") + var serverFiles = Directory.GetFiles(serverVideoPath, "*.mpg") .Select(f => new FileInfo(f)) .ToDictionary(f => f.Name, f => f); @@ -2351,13 +2338,13 @@ namespace DualScreenDemo SendCommandThroughSerialPort("a2 53 a4"); // 显示提示信息 - OverlayForm.MainForm.ShowServiceBellLabel(); - + OverlayForm.MainForm.HideAllLabels(); + OverlayForm.MainForm.ShowServiceBell(); // 延迟3秒 await Task.Delay(3000); // 隐藏提示信息 - OverlayForm.MainForm.HideServiceBellLabel(); + //OverlayForm.MainForm.HideServiceBellLabel(); isWaiting = false; } @@ -2565,46 +2552,14 @@ namespace DualScreenDemo { 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}"); - } + if (Program.RoomState.Equals("CLOSE")) + { + ShowSendOffScreen(); + } // 確保所有控件都已初始化完成後,再觸發熱門排行按鈕點擊 } - 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) { if (isOnOrderedSongsPage) diff --git a/Program.cs b/Program.cs index faab0be..5bda121 100644 --- a/Program.cs +++ b/Program.cs @@ -2,8 +2,7 @@ using System.IO; using Microsoft.Win32; using System.Diagnostics; using DBObj; -using DataCheck; - +using HeartbeatSender; namespace DualScreenDemo { public static class Program @@ -13,60 +12,30 @@ namespace DualScreenDemo //internal static ArtistManager artistManager; internal static SerialPortManager serialPortManager; private static PrimaryForm primaryForm; // 儲存實例的參考 + public static string RoomState = Utils.Env.Get("RoomStates", "CLOSE"); [STAThread] static void Main() { - - - Console.WriteLine("隱藏滑鼠游標"); - // Cursor.Hide(); + Console.WriteLine("Server V.1.2.0 20250703"); + if(Utils.Env.GetBool("IsCursor", true))Cursor.Hide(); AppDomain.CurrentDomain.ProcessExit += (s, e) => { Cursor.Show(); }; - Console.WriteLine("正在喚醒SVR裝置(每3分鐘呼叫一次)..."); - _ = Task.Run(async () => - { - while (true) - { - _ = Directory.Exists(@"\\svr01\video"); - _ = Directory.Exists(@"\\svr02\video"); - await Task.Delay(180000); // 每3min送一次 - } - }); + //Console.WriteLine("正在喚醒SVR裝置(每3分鐘呼叫一次)..."); + //_ = Task.Run(async () => + // { + // while (true) + // { + // _ = Directory.Exists(@"\\svr01\video"); + // _ = Directory.Exists(@"\\svr02\video"); + // await Task.Delay(180000); // 每3min送一次 + // } + // }); - // Console.WriteLine("正在與中控取得聯繫..."); - /*var sender = new HeartbeatSender.heartbeatSender(); - - // 同步呼叫非同步登入取得 token - bool loginSuccess = sender.LoginAndGetTokenAsync().GetAwaiter().GetResult(); - - if (loginSuccess) - { - // 先送一次心跳 (同步呼叫) - sender.SendHeartbeatAsync().GetAwaiter().GetResult(); - - // 背景持續每5分鐘送心跳 - _ = Task.Run(async () => - { - while (true) - { - await sender.SendHeartbeatAsync(); - await Task.Delay(300000); // 每5min送一次 - } - }); - Console.WriteLine("正在發送心跳中..."); - } - else - { - Console.WriteLine("登入失敗,無法送出心跳"); - } - */ - //之後需要做添加同步菜單+酒單+背景圖(若圖規格有做正規化) - // DataCheck.cs 有預寫好相關流程函式 - Console.WriteLine("更新菜單和酒單..."); - dataCheck Checkprocess = new dataCheck(); + //Console.WriteLine("正在與中控取得聯繫..."); + var sender = new heartbeatSender(); try { @@ -110,20 +79,8 @@ namespace DualScreenDemo primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); // 在完整初始化後檢查狀態 - string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "txt", "states.txt"); - bool isClosedState = File.Exists(stateFilePath) && - File.ReadAllText(stateFilePath).Trim().Equals("CLOSE", StringComparison.OrdinalIgnoreCase); - InitializeSecondaryScreen(); - - // 使用 Shown 事件來確保窗體完全加載後再顯示送客畫面 - if (isClosedState) - { - primaryForm.Shown += (s, e) => - { - primaryForm.ShowSendOffScreen(); - }; - } + WatchDog _watchDog = new WatchDog( () => VideoPlayerForm.Instance.GetCurrentVideoStatus(), () => primaryForm.IsAppResponsive() @@ -149,7 +106,7 @@ namespace DualScreenDemo { SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged; } -} + } private static bool IsUrlAclExists(string url) diff --git a/TCPServer.cs b/TCPServer.cs index 00928e4..f1de081 100644 --- a/TCPServer.cs +++ b/TCPServer.cs @@ -88,29 +88,7 @@ namespace DualScreenDemo // 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, () => - { - try { - foreach (Control ctrl in PrimaryForm.Instance.Controls) - { - ctrl.Enabled = false; - } - PrimaryForm.Instance.ShowSendOffScreen(); - } - catch (Exception ex) { - Console.WriteLine($"顯示送客畫面時發生錯誤: {ex.Message}"); - } - }); - } */ - - - + while (true) { Console.WriteLine("Waiting for connections..."); @@ -202,7 +180,7 @@ namespace DualScreenDemo } }); - UpdateStateFile(stateFilePath, "CLOSE"); + Program.RoomState = "CLOSE"; byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); stream.Write(okResponse, 0, okResponse.Length); @@ -218,7 +196,7 @@ namespace DualScreenDemo VideoPlayerForm.Instance.PlayPublicPlaylist(); PrimaryForm.currentSongIndexInHistory = -1; PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); - UpdateStateFile(stateFilePath, "OPEN"); + Program.RoomState = "OPEN"; PrimaryForm.Instance.HideSendOffScreen(); OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage(); @@ -238,7 +216,7 @@ namespace DualScreenDemo }); // 更新狀態檔案(可選,若你要記錄狀態) - UpdateStateFile(stateFilePath, "PAUSE"); + Program.RoomState = "PAUSE"; byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); stream.Write(okResponse, 0, okResponse.Length); @@ -271,10 +249,12 @@ namespace DualScreenDemo stream.Write(okResponse, 0, okResponse.Length); } + /* if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } + */ } Console.WriteLine("Connection closed."); @@ -315,40 +295,5 @@ namespace DualScreenDemo string hostName = Dns.GetHostName(); return hostName.Length > 3 ? hostName.Substring(hostName.Length - 3) : hostName; } - private string ReadStateFile(string filePath) - { - try - { - if (File.Exists(filePath)) - { - string state = File.ReadAllText(filePath).Trim(); - Console.WriteLine($"✅ State file read: {filePath} -> {state}"); - return state; - } - else - { - Console.WriteLine("⚠️ State file not found. Creating new file with default state: OPEN"); - UpdateStateFile(filePath, "OPEN"); - return "OPEN"; - } - } - catch (Exception ex) - { - Console.WriteLine($"❌ Failed to read state file: {ex.Message}"); - return "OPEN"; // 默認為 OPEN - } - } - private void UpdateStateFile(string filePath, string state) - { - try - { - File.WriteAllText(filePath, state); - Console.WriteLine($"✅ State file updated: {filePath} -> {state}"); - } - catch (Exception ex) - { - Console.WriteLine($"❌ Failed to update state file: {ex.Message}"); - } - } } } \ No newline at end of file diff --git a/VideoPlayerForm.cs b/VideoPlayerForm.cs index b46285b..d361183 100644 --- a/VideoPlayerForm.cs +++ b/VideoPlayerForm.cs @@ -812,9 +812,9 @@ namespace DualScreenDemo // 嘗試重新初始化並重播 try { - await Task.Delay(1000); + await Task.Delay(100); StopAndReleaseResources(); - await Task.Delay(1000); + await Task.Delay(100); // 重新初始化 COM int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED); @@ -1599,10 +1599,12 @@ namespace DualScreenDemo //OverlayForm.MainForm.ShowOriginalSongLabel(); string labelText = isVocalRemoved ? "無人聲" : "有人聲"; // 显示标签 + // 修改成新標籤 + OverlayForm.MainForm.HideAllLabels(); OverlayForm.MainForm.ShowOriginalSongLabel(labelText); - await Task.Delay(3000); + //await Task.Delay(3000); // 隐藏标签 - OverlayForm.MainForm.HideOriginalSongLabel(); + //OverlayForm.MainForm.HideOriginalSongLabel(); } } }