Compare commits

...

2 Commits

Author SHA1 Message Date
3f272cbdf4 少傳 2025-07-03 18:13:30 +08:00
8046eb5a35 新增 設定檔
調整 心跳功能
修正 藏鏡人
修正 服務鈴
20250703
2025-07-03 18:11:43 +08:00
27 changed files with 534 additions and 838 deletions

View File

@ -70,7 +70,6 @@ namespace DualScreenDemo
case "A26CA4": case "A26CA4":
Console.WriteLine("ToggleVocalRemoval Invoked"); Console.WriteLine("ToggleVocalRemoval Invoked");
SafeInvokeAction("A26CA4",() => VideoPlayerForm.Instance.ToggleVocalRemoval()); SafeInvokeAction("A26CA4",() => VideoPlayerForm.Instance.ToggleVocalRemoval());
SafeInvokeAction("A26CA4",() => OverlayForm.MainForm.ShowOriginalSongLabel());
break; break;
// 導唱 // 導唱
case "A26EA4": case "A26EA4":
@ -180,6 +179,10 @@ namespace DualScreenDemo
SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.HideAllLabels()); SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.HideAllLabels());
SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調")); SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調"));
break; break;
case "A266A4":
SafeInvokeAction("A266A4",() => OverlayForm.MainForm.HideAllLabels());
SafeInvokeAction("A266A4",() => OverlayForm.MainForm.ShowServiceBell());
break;
default: default:
if (Regex.IsMatch(indata, @"^A23\d+A4$")) if (Regex.IsMatch(indata, @"^A23\d+A4$"))
{ {
@ -189,20 +192,8 @@ namespace DualScreenDemo
} }
} }
private Dictionary<string, DateTime> _recentCommands = new();
private readonly TimeSpan _debounceInterval = TimeSpan.FromMilliseconds(300); // 最短觸發間隔
private void SafeInvokeAction(string commandKey, Action action) 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 操作 // 真正執行 UI 操作
if (OverlayForm.MainForm.InvokeRequired) if (OverlayForm.MainForm.InvokeRequired)
{ {

View File

@ -51,11 +51,11 @@ namespace DBObj
} }
*/ */
public List<SongData> SearchNewSongs(){ public List<SongData> 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); return PrimaryForm.Instance.SearchSongs_Mysql(query);
} }
public List<SongData> SearchHotSongs(){ public List<SongData> 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); return PrimaryForm.Instance.SearchSongs_Mysql(query);
} }
public List<SongData> SearchSongsBySinger(string keyword) public List<SongData> SearchSongsBySinger(string keyword)

View File

@ -1,140 +0,0 @@
using System.IO;
namespace DataCheck
{
public class dataCheck
{
public dataCheck()
{
menu_check();
news_check();
}
private void menu_check()
{
string menuPath = @"\\JLDKTV\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 = @"\\JLDKTV\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}");
}
}
}
}
}
}

61
Env.cs Normal file
View File

@ -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<string, string> _values;
static Env()
{
var path = Path.Combine(KtvPath, "config.ini");
_values = new Dictionary<string, string>(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 ... 視需要
}
}

View File

@ -1,182 +1,156 @@
using System.Net.Http; using System.Net.Http;
using System.Text; 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;
using System.Net.Sockets; using System.Net.Sockets;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualBasic.Devices; 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(); this.heartbeatDomain = Utils.Env.Get("HeartBeatUrl", "");
private string token;
private string token_heartbeatUrl;
private string init_heartbeatUrl;
public heartbeatSender() // 在建構子中啟動背景心跳任務
_ = Task.Run(() => StartAsync());
}
public async Task StartAsync()
{
cancellationTokenSource = new CancellationTokenSource();
while (!cancellationTokenSource.IsCancellationRequested)
{ {
LoadHeartbeatUrls("txt/HeartBeat.txt");
}
/// <summary>
/// 讀取URL
/// </summary>
/// <param name="filePath">路徑</param>
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<bool> 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 try
{ {
var response = await httpClient.PostAsync(init_heartbeatUrl, content); if (string.IsNullOrEmpty(token))
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 (dataElement.TryGetProperty("token", out JsonElement tokenElement)) bool loginSuccess = await LoginAndGetTokenAsync();
{ Console.WriteLine(loginSuccess ? "心跳登入成功" : "心跳登入失敗");
token = tokenElement.GetString();
}
} }
// Console.WriteLine("登入成功,取得 token" + token);
return true; if (!string.IsNullOrEmpty(token))
{
await SendHeartbeatAsync();
}
await Task.Delay(TimeSpan.FromMinutes(1), cancellationTokenSource.Token);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"登入失敗:{ex.Message}"); Console.WriteLine($"心跳任務錯誤:{ex.Message}");
return false; await Task.Delay(5000); // 錯誤後延遲再跑
} }
} }
}
public async Task SendHeartbeatAsync()
{ public void Stop()
if (string.IsNullOrEmpty(token)) {
{ cancellationTokenSource?.Cancel();
Console.WriteLine("請先登入取得 token"); Console.WriteLine("心跳任務已停止");
return; }
}
//Console.WriteLine(GetLocalIPv4()); public async Task<bool> LoginAndGetTokenAsync()
string hostName = System.Net.Dns.GetHostName(); {
var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/receiveRegister";
var heartbeatData = new var loginPayload = new
{ {
branch_name = "測試", email = "MachineKTV@gmail.com",
hostname = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)), password = "aa147258-"
ip = GetLocalIPv4(), };
cpu = GetCpuUsage(),
memory = GetTotalMemoryInMB(), var result = await SendPostAsync<JsonElement>(url, loginPayload);
disk = GetDiskTotalSizeInGB()
}; if (result.ValueKind == JsonValueKind.Object &&
result.TryGetProperty("data", out JsonElement data))
var json = JsonSerializer.Serialize(heartbeatData); {
var content = new StringContent(json, Encoding.UTF8, "application/json"); token = data.GetProperty("token").GetString()!;
// token_heartbeat branchName = data.GetProperty("branch_name").GetString()!;
// heartbeatUrl = "http://zqd.superstar.dnsnet.cc/api/room/heartbeat"; return true;
var request = new HttpRequestMessage(HttpMethod.Post, token_heartbeatUrl); }
request.Content = content;
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); return false;
// Console.WriteLine("送出的 JSON"); }
// Console.WriteLine(json);
var response = await httpClient.SendAsync(request); public async Task SendHeartbeatAsync()
try {
{ var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/heartbeat";
string hostName = Dns.GetHostName();
// Console.WriteLine($"心跳送出狀態:{response.StatusCode}");
var heartbeatPayload = new
response.EnsureSuccessStatusCode(); {
branch_name = branchName,
var responseJson = await response.Content.ReadAsStringAsync(); hostname = "PC" + hostName[^3..],
ip = GetLocalIPv4(),
using var doc = JsonDocument.Parse(responseJson); cpu = GetCpuUsage(),
// Console.WriteLine("API 回傳內容:" + responseJson); memory = GetTotalMemoryInMB(),
disk = GetDiskTotalSizeInGB()
} };
catch (Exception ex)
{ await SendPostAsync<JsonElement>(url, heartbeatPayload, token);
var errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"心跳送出成功: {DateTime.Now:HH:mm:ss}");
Console.WriteLine($"送出心跳錯誤:{ex.Message}"); }
Console.WriteLine($"後端回應內容:{errorContent}");
} private async Task<T?> SendPostAsync<T>(string url, object payload, string? bearerToken = null)
} {
var json = JsonSerializer.Serialize(payload);
private float GetCpuUsage() var content = new StringContent(json, Encoding.UTF8, "application/json");
{
using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); var request = new HttpRequestMessage(HttpMethod.Post, url)
cpuCounter.NextValue(); // 需要呼叫兩次 {
System.Threading.Thread.Sleep(100); Content = content
return cpuCounter.NextValue(); // 回傳的是百分比,例如 25.3 };
}
private float GetTotalMemoryInMB() if (!string.IsNullOrWhiteSpace(bearerToken))
{ {
var computerInfo = new ComputerInfo(); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearerToken);
var totalMB = computerInfo.TotalPhysicalMemory / (1024f); }
var availableMB = computerInfo.AvailablePhysicalMemory / (1024f);
var usedMB = totalMB - availableMB; var response = await httpClient.SendAsync(request);
return usedMB; // 轉 MB response.EnsureSuccessStatusCode();
}
private float GetDiskTotalSizeInGB(string driveLetter = "C") string responseJson = await response.Content.ReadAsStringAsync();
{ return JsonSerializer.Deserialize<T>(responseJson);
var drive = new DriveInfo(driveLetter); }
return drive.TotalSize / (1024f * 1024f * 1024f); // 轉 GB
} 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);
} }
} }

View File

@ -42,9 +42,7 @@ namespace DualScreenDemo
// 安裝包更新 // 安裝包更新
string localAddress = GetLocalIPAddress(); string localAddress = GetLocalIPAddress();
string externalAddress = File.Exists(@"\\JLDKTV\txt\ip.txt") string externalAddress = Utils.Env.Get("PhoneIP", "").Trim();
? File.ReadAllText(@"\\JLDKTV\txt\ip.txt").Trim()
: "";
_listener = new HttpListener(); _listener = new HttpListener();
_listener.Prefixes.Add($"http://{localAddress}:{port}/"); _listener.Prefixes.Add($"http://{localAddress}:{port}/");
@ -538,7 +536,7 @@ namespace DualScreenDemo
// 执行服务操作 // 执行服务操作
PrimaryForm.SendCommandThroughSerialPort("a2 53 a4"); PrimaryForm.SendCommandThroughSerialPort("a2 53 a4");
OverlayForm.MainForm.Invoke(new System.Action(() => { OverlayForm.MainForm.Invoke(new System.Action(() => {
OverlayForm.MainForm.ShowServiceBellLabel(); OverlayForm.MainForm.ShowServiceBell();
})); }));
// 异步处理等待和隐藏标签 // 异步处理等待和隐藏标签
await HttpServer.HandleServiceBellAsync(); await HttpServer.HandleServiceBellAsync();
@ -983,9 +981,19 @@ namespace DualScreenDemo
string Messagelast=""; string Messagelast="";
for(int j=0;j < message.Length;j++){ for(int j=0;j < message.Length;j++){
Messagelast += message[j]; Messagelast += message[j];
await Task.Delay(300); await Task.Delay(10);
// 将读取到的 "message" 字段传递给 UI 控件显示 // 将读取到的 "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); await Task.Delay(3000);

View File

@ -14,8 +14,12 @@ namespace OverlayFormObj
public Label originalSongLabel; public Label originalSongLabel;
public Label nextSongLabel; public Label nextSongLabel;
private Label serviceBellLabel; private Label serviceBellLabel;
private Label BellLabel;
private Label OriginalLabel;
public Label volumeUpLabel; // New volume up label public Label volumeUpLabel; // New volume up label
public Label volumeDownLabel; // New volume down 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 volumeUpTimer = new System.Windows.Forms.Timer();
private System.Windows.Forms.Timer volumeDownTimer = 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(); private System.Windows.Forms.Timer micUpTimer = new System.Windows.Forms.Timer();
@ -60,6 +64,10 @@ namespace OverlayFormObj
InitializeMuteLabel(); // Initialize the new mute label InitializeMuteLabel(); // Initialize the new mute label
InitializeOriginalSongLabel(); // 初始化原唱标签 InitializeOriginalSongLabel(); // 初始化原唱标签
InitializeServiceBellLabel(); InitializeServiceBellLabel();
InitializeBellLabel();
InitializeOriginalLabel();
InitializeVolumeUpLabel(); // Initialize the volume up label InitializeVolumeUpLabel(); // Initialize the volume up label
InitializeVolumeDownLabel(); // Initialize the volume down label InitializeVolumeDownLabel(); // Initialize the volume down label
InitializeMicUpLabel(); // Initialize the microphone up label InitializeMicUpLabel(); // Initialize the microphone up label
@ -89,6 +97,12 @@ namespace OverlayFormObj
private void ConfigureKeyTimers() 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.Interval = 1000;
volumeUpTimer.Tick += (sender, e) => {volumeUpLabel.Visible = false;volumeUpTimer.Stop();keepshowmic();}; 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); 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() private void InitializeVolumeDownLabel()
{ {
volumeDownLabel = new Label volumeDownLabel = new Label
{ {
AutoSize = true, AutoSize = true,
ForeColor = Color.White, ForeColor = Color.White,
Font = new Font("Microsoft JhengHei",34, FontStyle.Bold), Font = new Font("Microsoft JhengHei", 34, FontStyle.Bold),
BackColor = Color.Transparent, BackColor = Color.Transparent,
Text = "音量 ↓", Text = "音量 ↓",
Visible = false Visible = false
@ -808,23 +856,18 @@ public void UpdateNextSongLabelFromPlaylist(bool isUserPlaylistPlaying, SongData
// 显示服务铃标签 // 显示服务铃标签
public void ShowServiceBellLabel() public void ShowServiceBellLabel()
{ {
if (serviceBellLabel != null)
{
whichoneshowmic(); whichoneshowmic();
HideAllLabels(); HideAllLabels();
serviceBellLabel.Visible = true; serviceBellLabel.Visible = true;
// Console.WriteLine("服務鈴顯示: " + serviceBellLabel.Text); // 输出标签文本内容 Console.WriteLine("服務鈴顯示: " + serviceBellLabel.Text); // 输出标签文本内容
}
} }
public void HideServiceBellLabel() public void HideServiceBellLabel()
{ { /*
if (serviceBellLabel != null)
{
serviceBellLabel.Visible = false; serviceBellLabel.Visible = false;
// Console.WriteLine("服務鈴隱藏: " + serviceBellLabel.Text); // 输出标签文本内容 Console.WriteLine("服務鈴隱藏: " + serviceBellLabel.Text); // 输出标签文本内容
keepshowmic(); keepshowmic();
} */
} }
// 显示标准标签 // 显示标准标签
@ -1028,6 +1071,19 @@ public void UpdateNextSongLabelFromPlaylist(bool isUserPlaylistPlaying, SongData
volumeUpLabel.Visible = true; volumeUpLabel.Visible = true;
volumeUpTimer.Start(); 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() public void ShowVolumeDownLabel()
{ {

View File

@ -444,35 +444,67 @@ private static void DisplayTimer_Tick(object sender, EventArgs e)
// 當 songDisplayTimer 計時完成時會呼叫此函式 // 當 songDisplayTimer 計時完成時會呼叫此函式
private static void SongDisplayTimer_Elapsed(object sender, EventArgs e) private static void SongDisplayTimer_Elapsed(object sender, EventArgs e)
{ {
if (MainForm == null || MainForm.IsDisposed || MainForm.Disposing)
{
Console.WriteLine("MainForm is not valid.");
return;
}
// 檢查是否需要跨執行緒操作 UI 控制項 // 檢查是否需要跨執行緒操作 UI 控制項
if (MainForm.InvokeRequired) if (MainForm.InvokeRequired)
{ {
// 如果目前不在 UI 執行緒上,必須透過 Invoke 安全執行 UI 更新邏輯 try
Console.WriteLine("SongDisplayTimer_Tick invoked on UI thread.");
MainForm.BeginInvoke(new Action(() =>
{ {
if (MainForm.songDisplayLabel != null) MainForm.BeginInvoke(new Action(() =>
MainForm.songDisplayLabel.Text = ""; {
try
{
if (MainForm.songDisplayLabel != null && !MainForm.songDisplayLabel.IsDisposed)
MainForm.songDisplayLabel.Text = "";
if (MainForm.nextSongLabel != null) if (MainForm.nextSongLabel != null && !MainForm.nextSongLabel.IsDisposed)
MainForm.nextSongLabel.Visible = true; MainForm.nextSongLabel.Visible = true;
})); }
catch (Exception ex)
{
Console.WriteLine("Invoke UI update failed: " + ex.Message);
}
}));
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("BeginInvoke failed: MainForm already disposed. " + ex.Message);
}
} }
else else
{ {
// 如果已經在 UI 執行緒,就直接更新 UI 控制項 try
Console.WriteLine("SongDisplayTimer_Tick invoked on background thread."); {
MainForm.songDisplayLabel.Text = ""; if (MainForm.songDisplayLabel != null && !MainForm.songDisplayLabel.IsDisposed)
MainForm.nextSongLabel.Visible = true; MainForm.songDisplayLabel.Text = "";
if (MainForm.nextSongLabel != null && !MainForm.nextSongLabel.IsDisposed)
MainForm.nextSongLabel.Visible = true;
}
catch (Exception ex)
{
Console.WriteLine("Direct UI update failed: " + ex.Message);
}
} }
// 停止計時器,避免重複觸發 try
songDisplayTimer.Stop(); {
songDisplayTimer.Stop();
}
catch (Exception ex)
{
Console.WriteLine("Failed to stop timer: " + ex.Message);
}
} }
private readonly object _lockObject = new object(); private readonly object _lockObject = new object();
private bool _handlingTimeout = false; private bool _handlingTimeout = false;
private async void UnifiedTimer_Elapsed(object sender, EventArgs e) private async void UnifiedTimer_Elapsed(object sender, EventArgs e)
@ -1495,11 +1527,11 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
if (category == Category.NewSongs) 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) 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 else
{ {

View File

@ -5,8 +5,8 @@ namespace DualScreenDemo
public partial class PrimaryForm public partial class PrimaryForm
{ {
private Button hotPlayButton; private Button hotPlayButton;
private Bitmap hotPlayNormalBackground; private Bitmap hotPlayNormalBackground;
private Bitmap hotPlayActiveBackground; private Bitmap hotPlayActiveBackground;
private Button guoYuButtonHotSong; private Button guoYuButtonHotSong;
private Bitmap guoYuHotSongNormalBackground; private Bitmap guoYuHotSongNormalBackground;
@ -36,18 +36,18 @@ namespace DualScreenDemo
private void SetHotSongButtonsVisibility(bool isVisible) private void SetHotSongButtonsVisibility(bool isVisible)
{ {
Button[] hotSongButtons = { guoYuButtonHotSong, taiYuButtonHotSong, taiYuNewSongButtonHotSong, guoYuNewSongButtonHotSong, yingWenButtonHotSong, riYuButtonHotSong, hanYuButtonHotSong }; Button[] hotSongButtons = { guoYuButtonHotSong, taiYuButtonHotSong, taiYuNewSongButtonHotSong, guoYuNewSongButtonHotSong, yingWenButtonHotSong, riYuButtonHotSong, hanYuButtonHotSong };
foreach (var button in hotSongButtons) foreach (var button in hotSongButtons)
{ {
button.Visible = isVisible; button.Visible = isVisible;
if (isVisible) if (isVisible)
{ {
button.BringToFront(); button.BringToFront();
@ -62,11 +62,10 @@ namespace DualScreenDemo
isOnOrderedSongsPage = false; isOnOrderedSongsPage = false;
/* 清空搜尋欄 */ /* 清空搜尋欄 */
ResetinputBox(); ResetinputBox();
int songLimit = ReadHotSongLimit(); string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};";
string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `song_counts` DESC LIMIT {songLimit};";
var guoYuSongs = SearchSongs_Mysql(query); var guoYuSongs = SearchSongs_Mysql(query);
UpdateSongList(guoYuSongs); UpdateSongList(guoYuSongs);
SetButtonsVisibility(); SetButtonsVisibility();
//HideQRCode(); //HideQRCode();
} }
@ -91,8 +90,7 @@ namespace DualScreenDemo
{ {
UpdateHotSongButtons(activeButton, activeBackground); UpdateHotSongButtons(activeButton, activeBackground);
int songLimit = ReadHotSongLimit(); string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY `song_counts` DESC LIMIT {Utils.Env.GetInt("HotSongLimit", 100)};";
string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY `song_counts` DESC LIMIT {songLimit};";
var selectedSongs = SearchSongs_Mysql(query); var selectedSongs = SearchSongs_Mysql(query);
UpdateSongList(selectedSongs); UpdateSongList(selectedSongs);
@ -113,11 +111,11 @@ namespace DualScreenDemo
private void UpdateSongList(List<SongData> songs) private void UpdateSongList(List<SongData> songs)
{ {
currentPage = 0; currentPage = 0;
currentSongList = songs; currentSongList = songs;
totalPages = (int)Math.Ceiling((double)songs.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)songs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(currentSongList);
} }
@ -148,85 +146,86 @@ namespace DualScreenDemo
} }
private void InitializeButtonsForHotSong() private void InitializeButtonsForHotSong()
{ {
InitializeHotSongButton(ref guoYuNewSongButtonHotSong, "國語新歌", 1214, 230, 209, 58, InitializeHotSongButton(ref guoYuNewSongButtonHotSong, "國語新歌", 1214, 230, 209, 58,
normalStateImageHotSong, normalStateImageHotSong,
out guoYuNewSongHotSongNormalBackground, out guoYuNewSongHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out guoYuNewSongHotSongActiveBackground, out guoYuNewSongHotSongActiveBackground,
GuoYuNewSongButtonHotSong_Click); GuoYuNewSongButtonHotSong_Click);
InitializeHotSongButton(ref taiYuNewSongButtonHotSong, "台語新歌", 1214, 293, 209, 58, InitializeHotSongButton(ref taiYuNewSongButtonHotSong, "台語新歌", 1214, 293, 209, 58,
normalStateImageHotSong, normalStateImageHotSong,
out taiYuNewSongHotSongNormalBackground, out taiYuNewSongHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out taiYuNewSongHotSongActiveBackground, out taiYuNewSongHotSongActiveBackground,
TaiYuNewSongButtonHotSong_Click); TaiYuNewSongButtonHotSong_Click);
InitializeHotSongButton(ref taiYuButtonHotSong, "台語", 1214, 418, 209, 58, InitializeHotSongButton(ref taiYuButtonHotSong, "台語", 1214, 418, 209, 58,
normalStateImageHotSong, normalStateImageHotSong,
out taiYuHotSongNormalBackground, out taiYuHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out taiYuHotSongActiveBackground, out taiYuHotSongActiveBackground,
TaiYuButtonHotSong_Click); TaiYuButtonHotSong_Click);
InitializeHotSongButton(ref yueYuButtonHotSong, "粵語", 1214, 356, 209, 58, InitializeHotSongButton(ref yueYuButtonHotSong, "粵語", 1214, 356, 209, 58,
normalStateImageHotSong, normalStateImageHotSong,
out yueYuHotSongNormalBackground, out yueYuHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out yueYuHotSongActiveBackground, out yueYuHotSongActiveBackground,
YueYuButtonHotSong_Click); YueYuButtonHotSong_Click);
InitializeHotSongButton(ref guoYuButtonHotSong, "國語", 1214, 356, 209, 59, InitializeHotSongButton(ref guoYuButtonHotSong, "國語", 1214, 356, 209, 59,
normalStateImageHotSong, normalStateImageHotSong,
out guoYuHotSongNormalBackground, out guoYuHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out guoYuHotSongActiveBackground, out guoYuHotSongActiveBackground,
GuoYuButtonHotSong_Click); GuoYuButtonHotSong_Click);
InitializeHotSongButton(ref yingWenButtonHotSong, "英文", 1214, 481, 209, 59, InitializeHotSongButton(ref yingWenButtonHotSong, "英文", 1214, 481, 209, 59,
normalStateImageHotSong, normalStateImageHotSong,
out yingWenHotSongNormalBackground, out yingWenHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out yingWenHotSongActiveBackground, out yingWenHotSongActiveBackground,
YingWenButtonHotSong_Click); YingWenButtonHotSong_Click);
InitializeHotSongButton(ref riYuButtonHotSong, "日語", 1214, 544, 209, 59, InitializeHotSongButton(ref riYuButtonHotSong, "日語", 1214, 544, 209, 59,
normalStateImageHotSong, normalStateImageHotSong,
out riYuHotSongNormalBackground, out riYuHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out riYuHotSongActiveBackground, out riYuHotSongActiveBackground,
RiYuButtonHotSong_Click); RiYuButtonHotSong_Click);
InitializeHotSongButton(ref hanYuButtonHotSong, "韓語", 1214, 607, 209, 58, InitializeHotSongButton(ref hanYuButtonHotSong, "韓語", 1214, 607, 209, 58,
normalStateImageHotSong, normalStateImageHotSong,
out hanYuHotSongNormalBackground, out hanYuHotSongNormalBackground,
mouseDownImageHotSong, mouseDownImageHotSong,
out hanYuHotSongActiveBackground, out hanYuHotSongActiveBackground,
HanYuButtonHotSong_Click); HanYuButtonHotSong_Click);
} }
private void InitializeHotSongButton(ref Button button, string buttonText, int x, int y, int width, int height, private void InitializeHotSongButton(ref Button button, string buttonText, int x, int y, int width, int height,
Image normalBackground, out Bitmap normalBackgroundOut, Image normalBackground, out Bitmap normalBackgroundOut,
Image activeBackground, out Bitmap activeBackgroundOut, Image activeBackground, out Bitmap activeBackgroundOut,
EventHandler clickEventHandler) EventHandler clickEventHandler)
{ {
button = new Button { button = new Button
{
Text = "", // 移除文字 Text = "", // 移除文字
Visible = false Visible = false
}; };
ResizeAndPositionButton(button, x, y, width, height); ResizeAndPositionButton(button, x, y, width, height);
// 修改裁剪區域,避開文字部分 // 修改裁剪區域,避開文字部分
Rectangle cropArea = new Rectangle(1214, y, 209, 58); // 使用固定的裁剪區域 Rectangle cropArea = new Rectangle(1214, y, 209, 58); // 使用固定的裁剪區域
normalBackgroundOut = new Bitmap(normalBackground).Clone(cropArea, normalBackground.PixelFormat); normalBackgroundOut = new Bitmap(normalBackground).Clone(cropArea, normalBackground.PixelFormat);
activeBackgroundOut = new Bitmap(activeBackground).Clone(cropArea, activeBackground.PixelFormat); activeBackgroundOut = new Bitmap(activeBackground).Clone(cropArea, activeBackground.PixelFormat);
button.BackgroundImage = normalBackgroundOut; button.BackgroundImage = normalBackgroundOut;
button.BackgroundImageLayout = ImageLayout.Stretch; button.BackgroundImageLayout = ImageLayout.Stretch;
button.FlatStyle = FlatStyle.Flat; button.FlatStyle = FlatStyle.Flat;
@ -234,36 +233,5 @@ namespace DualScreenDemo
button.Click += clickEventHandler; button.Click += clickEventHandler;
this.Controls.Add(button); 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;
}
} }
} }

View File

@ -7,8 +7,7 @@ namespace DualScreenDemo
// 重置其他按钮背景 // 重置其他按钮背景
UpdateHotSongButtons(guoYuNewSongButtonHotSong, guoYuNewSongHotSongActiveBackground); 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 {Utils.Env.GetInt("HotSongLimit", 100)};";
string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {songLimit};";
var selectedSongs = PrimaryForm.Instance.SearchSongs_Mysql(query); var selectedSongs = PrimaryForm.Instance.SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;

View File

@ -7,9 +7,7 @@ namespace DualScreenDemo
// 重置其他按钮背景 // 重置其他按钮背景
UpdateHotSongButtons(taiYuNewSongButtonHotSong, taiYuNewSongHotSongActiveBackground); 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 {Utils.Env.GetInt("HotSongLimit", 100)};";
string query = $"SELECT * FROM song_library_cache WHERE language_name = '台語' ORDER BY `add_date` DESC, `song_counts` DESC LIMIT {songLimit};";
var selectedSongs = SearchSongs_Mysql(query); var selectedSongs = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;

View File

@ -221,40 +221,8 @@ namespace DualScreenDemo
this.Controls.Add(hanYuButtonNewSong); 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){ private string setQueryforNewSong(string category){
string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY add_date DESC LIMIT {ReadNewSongLimit()};"; return $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};";
return query;
} }
} }
} }

View File

@ -11,21 +11,13 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit(); var yueYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("粵語"));
/*yueYuSongs2 = allSongs.Where(song => song.Category == "粵語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("粵語");
var yueYuSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = yueYuSongs2;
totalPages = (int)Math.Ceiling((double)yueYuSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)yueYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(yueYuSongs2);
} }
} }
} }

View File

@ -11,21 +11,13 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit(); var guoYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("國語"));
/*guoYuSongs2 = allSongs.Where(song => song.Category == "國語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("國語");
var guoYuSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = guoYuSongs2;
totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(guoYuSongs2);
} }
} }
} }

View File

@ -11,22 +11,14 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit(); var yingWenSongs2 = SearchSongs_Mysql(setQueryforNewSong("英語"));
/*yingWenSongs2 = allSongs.Where(song => song.Category == "英語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("英語");
var yingWenSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = yingWenSongs2;
totalPages = (int)Math.Ceiling((double)yingWenSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)yingWenSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(yingWenSongs2);
} }
} }
} }

View File

@ -11,21 +11,13 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongActiveBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongActiveBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit(); var riYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("日語"));
/*riYuSongs2 = allSongs.Where(song => song.Category == "日語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("日語");
var riYuSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = riYuSongs2;
totalPages = (int)Math.Ceiling((double)riYuSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)riYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(riYuSongs2);
} }
} }
} }

View File

@ -11,21 +11,13 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongActiveBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongActiveBackground;
int songLimit = ReadNewSongLimit(); var hanYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("韓語"));
/*hanYuSongs2 = allSongs.Where(song => song.Category == "韓語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("韓語");
var hanYuSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = hanYuSongs2;
totalPages = (int)Math.Ceiling((double)hanYuSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)hanYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(hanYuSongs2);
} }
} }
} }

View File

@ -11,21 +11,13 @@ namespace DualScreenDemo
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground; riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground; hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit(); var taiYuSongs2 = SearchSongs_Mysql(setQueryforNewSong("台語"));
/*taiYuSongs2 = allSongs.Where(song => song.Category == "台語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();*/
string query = setQueryforNewSong("台語");
var taiYuSongs2 = SearchSongs_Mysql(query);
currentPage = 0; currentPage = 0;
currentSongList = taiYuSongs2;
totalPages = (int)Math.Ceiling((double)taiYuSongs2.Count / itemsPerPage); totalPages = (int)Math.Ceiling((double)taiYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0; multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList); multiPagePanel.LoadSongs(taiYuSongs2);
} }
} }
} }

View File

@ -74,28 +74,6 @@ namespace DualScreenDemo
} }
} }
private List<Image> LoadPromotionsImages()
{
List<Image> images = new List<Image>();
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) private void promotionsButton_Click(object sender, EventArgs e)
{ {
promotionsAndMenuPanel.currentPageIndex=0; promotionsAndMenuPanel.currentPageIndex=0;
@ -137,27 +115,16 @@ namespace DualScreenDemo
private void PreviousPromotionButton_Click(object sender, EventArgs e) private void PreviousPromotionButton_Click(object sender, EventArgs e)
{ {
promotionsAndMenuPanel.LoadPreviousPage(); promotionsAndMenuPanel.LoadPreviousPage();
} }
private void NextPromotionButton_Click(object sender, EventArgs e) private void NextPromotionButton_Click(object sender, EventArgs e)
{ {
promotionsAndMenuPanel.LoadNextPage(); promotionsAndMenuPanel.LoadNextPage();
} }
private void ClosePromotionsButton_Click(object sender, EventArgs e) private void ClosePromotionsButton_Click(object sender, EventArgs e)
{ {
promotionsAndMenuPanel.Visible = false; promotionsAndMenuPanel.Visible = false;
previousPromotionButton.Visible = false; previousPromotionButton.Visible = false;
nextPromotionButton.Visible = false; nextPromotionButton.Visible = false;

View File

@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO; using System.IO;
namespace DualScreenDemo namespace DualScreenDemo
{ {
public partial class PrimaryForm public partial class PrimaryForm
@ -11,32 +15,50 @@ namespace DualScreenDemo
ResizeAndPositionControl(promotionsAndMenuPanel, 0, 0, 1440, 900); ResizeAndPositionControl(promotionsAndMenuPanel, 0, 0, 1440, 900);
this.Controls.Add(promotionsAndMenuPanel); this.Controls.Add(promotionsAndMenuPanel);
promotions = LoadPromotionsImages(); promotions = LoadImagesFromFolder("news");
menu = LoadMenuImages(); menu = LoadImagesFromFolder("foods");
} }
private List<Image> LoadMenuImages()
{
List<Image> images = new List<Image>();
string foodsFolderPath = Path.Combine(Application.StartupPath, "foods");
string[] imageFiles = Directory.GetFiles(foodsFolderPath, "*.jpg"); /// <summary>
/// 從指定資料夾載入所有 .jpg 圖片
/// </summary>
private List<Image> LoadImagesFromFolder(string folderName)
{
List<Image> 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) foreach (string filePath in imageFiles)
{ {
try 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) catch (Exception ex)
{ {
Console.WriteLine("Error loading image: " + filePath + ". Exception: " + ex.Message); Console.WriteLine($"載入圖片失敗:{filePath},原因:{ex.Message}");
} }
} }
return images; return images;
} }
/// <summary>
/// 將 Stream 轉為 byte[],用於避免 Image 檔案鎖定
/// </summary>
private byte[] ReadFully(Stream input)
{
using MemoryStream ms = new();
input.CopyTo(ms);
return ms.ToArray();
}
} }
} }

View File

@ -6,40 +6,9 @@ using System.Diagnostics;
namespace DualScreenDemo{ namespace DualScreenDemo{
public partial class PrimaryForm 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() 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; public bool isLoggedIn = false;
@ -55,7 +24,7 @@ namespace DualScreenDemo{
Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch = new Stopwatch();
connection.Open(); connection.Open();
stopwatch.Start(); stopwatch.Start();
Console.WriteLine("MyDB 連線成功!"); Console.WriteLine("MyDB 連線成功!");
@ -72,15 +41,15 @@ namespace DualScreenDemo{
string artistA = reader["artistA"].ToString(); string artistA = reader["artistA"].ToString();
string artistB = reader["artistB"].ToString(); string artistB = reader["artistB"].ToString();
string fileName = reader["song_filename"].ToString(); string fileName = reader["song_filename"].ToString();
string songFilePathHost1 = Path.Combine(@"\\SVR01\", fileName); string songFilePathHost1 = Path.Combine(@"\\SVR01\e\", fileName);
string songFilePathHost2 = Path.Combine(@"\\SVR02\", fileName); string songFilePathHost2 = Path.Combine(@"\\SVR02\e\", fileName);
string artistASimplified = reader["artistA_simplified"].ToString(); string artistASimplified = reader["artistA_simplified"].ToString();
string artistBSimplified = reader["artistB_simplified"].ToString(); string artistBSimplified = reader["artistB_simplified"].ToString();
string songSimplified = reader["song_simplified"].ToString(); string songSimplified = reader["song_simplified"].ToString();
int humanVoice = Convert.ToInt32(reader["vocal"]); int humanVoice = Convert.ToInt32(reader["vocal"]);
searchResults.Add(new SongData( searchResults.Add(new SongData(
songNumber, song, artistA, artistB,fileName, songNumber, song, artistA, artistB, fileName,
songFilePathHost1, songFilePathHost2, songFilePathHost1, songFilePathHost2,
artistASimplified, artistBSimplified, artistASimplified, artistBSimplified,
songSimplified, humanVoice songSimplified, humanVoice
@ -118,8 +87,8 @@ namespace DualScreenDemo{
while (reader.Read()) while (reader.Read())
{ {
string artist = reader["name"].ToString(); string artist = reader["name"].ToString();
string artistSimplified = reader ["simplified"].ToString(); string artistSimplified = reader["simplified"].ToString();
searchResults.Add(new Artist(artist,artistSimplified)); searchResults.Add(new Artist(artist, artistSimplified));
} }
} }
} }
@ -140,7 +109,7 @@ namespace DualScreenDemo{
string connectionString = GetConnectionString(); string connectionString = GetConnectionString();
using (var connection = new MySqlConnection(connectionString)) using (var connection = new MySqlConnection(connectionString))
{ {
connection.Open(); connection.Open();
Console.WriteLine("MyDB 連線成功!"); Console.WriteLine("MyDB 連線成功!");
@ -168,7 +137,7 @@ namespace DualScreenDemo{
string connectionString = GetConnectionString(); string connectionString = GetConnectionString();
using (var connection = new MySqlConnection(connectionString)) using (var connection = new MySqlConnection(connectionString))
{ {
connection.Open(); connection.Open();
Console.WriteLine("MyDB 連線成功!"); Console.WriteLine("MyDB 連線成功!");
@ -254,7 +223,7 @@ namespace DualScreenDemo{
string connectionString = GetConnectionString(); string connectionString = GetConnectionString();
using (var connection = new MySqlConnection(connectionString)) using (var connection = new MySqlConnection(connectionString))
{ {
connection.Open(); connection.Open();
Console.WriteLine("MyDB 連線成功!"); Console.WriteLine("MyDB 連線成功!");

View File

@ -28,14 +28,32 @@ namespace DualScreenDemo
private void ConfigureImageButton(Button button, int posX, int posY, int width, int height, private void ConfigureImageButton(Button button, int posX, int posY, int width, int height,
string imagePath, EventHandler clickEventHandler) string imagePath, EventHandler clickEventHandler)
{ {
Bitmap image = new Bitmap(imagePath); // 你的基準解析度(設計時以這個為標準)
button.SetBounds(posX, posY, image.Width, image.Height); const float baseWidth = 1920f;
const float baseHeight = 1080f;
// 實際螢幕解析度
float actualWidth = Screen.PrimaryScreen.Bounds.Width;
float actualHeight = Screen.PrimaryScreen.Bounds.Height;
// 計算縮放比例
float scaleX = actualWidth / baseWidth;
float scaleY = actualHeight / baseHeight;
// 套用縮放比例到位置與大小
int scaledX = (int)(posX * scaleX);
int scaledY = (int)(posY * scaleY);
int scaledWidth = (int)(width * scaleX);
int scaledHeight = (int)(height * scaleY);
// 載入圖片並調整按鈕尺寸
Bitmap image = new Bitmap(imagePath);
button.SetBounds(scaledX, scaledY, scaledWidth, scaledHeight);
// 載入圖片
button.BackgroundImage = image; button.BackgroundImage = image;
button.BackgroundImageLayout = ImageLayout.Stretch; button.BackgroundImageLayout = ImageLayout.Stretch;
// 按鈕樣式設定 // 按鈕樣式
button.FlatStyle = FlatStyle.Flat; button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0; button.FlatAppearance.BorderSize = 0;
button.FlatAppearance.MouseDownBackColor = Color.Transparent; button.FlatAppearance.MouseDownBackColor = Color.Transparent;
@ -165,20 +183,28 @@ namespace DualScreenDemo
if (File.Exists(imagePath)) if (File.Exists(imagePath))
{ {
// 直接載入完整圖 try
Bitmap image = new Bitmap(imagePath); {
// 使用 Image.FromFile 載入後 clone避免檔案 lock & GDI 錯誤
using (Image original = Image.FromFile(imagePath))
{
Bitmap image = new Bitmap(original);
pictureBoxSceneSoundEffects.Image = image;
}
// 顯示在 PictureBox 上 ResizeAndPositionPictureBox(pictureBoxSceneSoundEffects, 850, 450,
pictureBoxSceneSoundEffects.Image = image; pictureBoxSceneSoundEffects.Image.Width, pictureBoxSceneSoundEffects.Image.Height);
// 設定 PictureBox 的大小與位置(依你的需要調整) pictureBoxSceneSoundEffects.Visible = true;
ResizeAndPositionPictureBox(pictureBoxSceneSoundEffects, 850, 450, image.Width , image.Height); }
catch (Exception ex)
pictureBoxSceneSoundEffects.Visible = true; {
Console.WriteLine("圖片載入失敗:" + ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} }
else else
{ {
Console.WriteLine("圖片檔案不存在:" + imagePath); Console.WriteLine("圖片檔案不存在:" + imagePath, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning);
} }
} }

View File

@ -239,7 +239,6 @@ namespace DualScreenDemo
multiPagePanel.PageIndexChanged += HandlePageChanged; multiPagePanel.PageIndexChanged += HandlePageChanged;
// 添加 Load 事件處理 // 添加 Load 事件處理
this.Load += PrimaryForm_Load; this.Load += PrimaryForm_Load;
LoadConnectionStringFromFile("test.env");
} }
public bool IsAppResponsive() public bool IsAppResponsive()
@ -504,7 +503,7 @@ namespace DualScreenDemo
string selectedTheme = ReadSelectedThemePath(); string selectedTheme = ReadSelectedThemePath();
if (!string.IsNullOrEmpty(selectedTheme)) if (!string.IsNullOrEmpty(selectedTheme))
{ {
string backgroundImagePath = Path.Combine(Application.StartupPath, "themes\\superstar\\555009.jpg"); string backgroundImagePath = Path.Combine(Utils.Env.KtvPath, "themes","superstar","555009.jpg");
try try
{ {
using (Image originalImage = Image.FromFile(backgroundImagePath)) using (Image originalImage = Image.FromFile(backgroundImagePath))
@ -1554,23 +1553,10 @@ namespace DualScreenDemo
try try
{ {
// 1. 檢查是否能連接到 SVR01 // 1. 檢查是否能連接到 SVR01
string serverVideoPath = @"\\SVR01\video"; string serverVideoPath = Path.Combine(Utils.Env.KtvPath, "video");
string serverVideoPath2 = @"\\SVR02\video";
string selectedServerPath = null;
string localVideoPath = @"D:\video"; string localVideoPath = @"D:\video";
if (Directory.Exists(serverVideoPath)) if (!Directory.Exists(serverVideoPath))
{
selectedServerPath = serverVideoPath;
Console.WriteLine("已連接到 SVR01");
}
else if (Directory.Exists(serverVideoPath2))
{
selectedServerPath = serverVideoPath2;
Console.WriteLine("已連接到 SVR02");
}
else
{ {
Console.WriteLine("未連接到 SVR使用本地影片"); Console.WriteLine("未連接到 SVR使用本地影片");
LoadLocalVideoFiles(); LoadLocalVideoFiles();
@ -1584,7 +1570,7 @@ namespace DualScreenDemo
} }
// 獲取服務器和本地的所有文件 // 獲取服務器和本地的所有文件
var serverFiles = Directory.GetFiles(selectedServerPath, "*.mpg") var serverFiles = Directory.GetFiles(serverVideoPath, "*.mpg")
.Select(f => new FileInfo(f)) .Select(f => new FileInfo(f))
.ToDictionary(f => f.Name, f => f); .ToDictionary(f => f.Name, f => f);
@ -2351,13 +2337,14 @@ namespace DualScreenDemo
SendCommandThroughSerialPort("a2 53 a4"); SendCommandThroughSerialPort("a2 53 a4");
// 显示提示信息 // 显示提示信息
OverlayForm.MainForm.ShowServiceBellLabel(); OverlayForm.MainForm.HideAllLabels();
OverlayForm.MainForm.ShowServiceBell();
// 延迟3秒 // 延迟3秒
await Task.Delay(3000); await Task.Delay(3000);
// 隐藏提示信息 // 隐藏提示信息
OverlayForm.MainForm.HideServiceBellLabel(); //OverlayForm.MainForm.HideServiceBellLabel();
isWaiting = false; isWaiting = false;
} }
@ -2565,46 +2552,14 @@ namespace DualScreenDemo
{ {
HotPlayButton_Click(null, EventArgs.Empty); HotPlayButton_Click(null, EventArgs.Empty);
} }
try if (Program.RoomState.Equals("CLOSE"))
{ {
string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "txt", "states.txt"); ShowSendOffScreen();
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) private void AutoRefreshTimer_Tick(object sender, EventArgs e)
{ {
if (isOnOrderedSongsPage) if (isOnOrderedSongsPage)

View File

@ -2,8 +2,7 @@ using System.IO;
using Microsoft.Win32; using Microsoft.Win32;
using System.Diagnostics; using System.Diagnostics;
using DBObj; using DBObj;
using DataCheck; using HeartbeatSender;
namespace DualScreenDemo namespace DualScreenDemo
{ {
public static class Program public static class Program
@ -13,59 +12,30 @@ namespace DualScreenDemo
//internal static ArtistManager artistManager; //internal static ArtistManager artistManager;
internal static SerialPortManager serialPortManager; internal static SerialPortManager serialPortManager;
private static PrimaryForm primaryForm; // 儲存實例的參考 private static PrimaryForm primaryForm; // 儲存實例的參考
public static string RoomState = Utils.Env.Get("RoomStates", "CLOSE");
[STAThread] [STAThread]
static void Main() static void Main()
{ {
Console.WriteLine("Server V.1.1.0 20250701");
if(Utils.Env.GetBool("IsCursor", true))Cursor.Hide();
Console.WriteLine("隱藏滑鼠游標");
Cursor.Hide();
AppDomain.CurrentDomain.ProcessExit += (s, e) => AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{ {
Cursor.Show(); Cursor.Show();
}; };
Console.WriteLine("正在喚醒SVR裝置(每3分鐘呼叫一次)..."); //Console.WriteLine("正在喚醒SVR裝置(每3分鐘呼叫一次)...");
_ = Task.Run(async () => //_ = Task.Run(async () =>
{ // {
while (true) // while (true)
{ // {
_ = Directory.Exists(@"\\svr01\video"); // _ = Directory.Exists(@"\\svr01\e\video");
_ = Directory.Exists(@"\\svr02\video"); // _ = Directory.Exists(@"\\svr02\e\video");
await Task.Delay(180000); // 每3min送一次 // await Task.Delay(180000); // 每3min送一次
} // }
}); // });
// Console.WriteLine("正在與中控取得聯繫..."); // Console.WriteLine("正在與中控取得聯繫...");
var sender = new HeartbeatSender.heartbeatSender(); var sender = new 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();
try try
{ {
@ -102,27 +72,13 @@ namespace DualScreenDemo
// 創建主窗體 // 創建主窗體
primaryForm = new PrimaryForm(); primaryForm = new PrimaryForm();
//primaryForm.allSongs = songListManager.AllSongs;
//primaryForm.allArtists = artistManager.AllArtists;
primaryForm.StartPosition = FormStartPosition.Manual; primaryForm.StartPosition = FormStartPosition.Manual;
primaryForm.Location = new Point(0, 0); primaryForm.Location = new Point(0, 0);
primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); 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(); InitializeSecondaryScreen();
// 使用 Shown 事件來確保窗體完全加載後再顯示送客畫面
if (isClosedState)
{
primaryForm.Shown += (s, e) =>
{
primaryForm.ShowSendOffScreen();
};
}
WatchDog _watchDog = new WatchDog( WatchDog _watchDog = new WatchDog(
() => VideoPlayerForm.Instance.GetCurrentVideoStatus(), () => VideoPlayerForm.Instance.GetCurrentVideoStatus(),
() => primaryForm.IsAppResponsive() () => primaryForm.IsAppResponsive()
@ -133,7 +89,6 @@ namespace DualScreenDemo
{ {
_watchDog.Stop(); _watchDog.Stop();
}; };
primaryForm.Show(); primaryForm.Show();
Application.Run(primaryForm); Application.Run(primaryForm);
@ -148,7 +103,7 @@ namespace DualScreenDemo
{ {
SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged; SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;
} }
} }
private static bool IsUrlAclExists(string url) private static bool IsUrlAclExists(string url)

View File

@ -89,28 +89,6 @@ namespace DualScreenDemo
try { 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) while (true)
{ {
Console.WriteLine("Waiting for connections..."); Console.WriteLine("Waiting for connections...");
@ -201,7 +179,7 @@ namespace DualScreenDemo
} }
}); });
UpdateStateFile(stateFilePath, "CLOSE"); Program.RoomState = "CLOSE";
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
stream.Write(okResponse, 0, okResponse.Length); stream.Write(okResponse, 0, okResponse.Length);
@ -217,7 +195,7 @@ namespace DualScreenDemo
VideoPlayerForm.Instance.PlayPublicPlaylist(); VideoPlayerForm.Instance.PlayPublicPlaylist();
PrimaryForm.currentSongIndexInHistory = -1; PrimaryForm.currentSongIndexInHistory = -1;
PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty); PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty);
UpdateStateFile(stateFilePath, "OPEN"); Program.RoomState = "OPEN";
PrimaryForm.Instance.HideSendOffScreen(); PrimaryForm.Instance.HideSendOffScreen();
@ -236,7 +214,7 @@ namespace DualScreenDemo
}); });
// 更新狀態檔案(可選,若你要記錄狀態) // 更新狀態檔案(可選,若你要記錄狀態)
UpdateStateFile(stateFilePath, "PAUSE"); Program.RoomState = "PAUSE";
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
stream.Write(okResponse, 0, okResponse.Length); stream.Write(okResponse, 0, okResponse.Length);
@ -266,12 +244,18 @@ namespace DualScreenDemo
OverlayForm.MainForm.UpdateMarqueeTextSecondLine(marqueeMessage); OverlayForm.MainForm.UpdateMarqueeTextSecondLine(marqueeMessage);
} }
}); });
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
stream.Write(okResponse, 0, okResponse.Length);
continue;
} }
/*
if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase)) if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
{ {
break; break;
} }
*/
} }
Console.WriteLine("Connection closed."); Console.WriteLine("Connection closed.");
@ -312,40 +296,5 @@ namespace DualScreenDemo
string hostName = Dns.GetHostName(); string hostName = Dns.GetHostName();
return hostName.Length > 3 ? hostName.Substring(hostName.Length - 3) : hostName; 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}");
}
}
} }
} }

View File

@ -1596,14 +1596,15 @@ namespace DualScreenDemo
{ {
streamSelect.Enable(audioTrack1, AMStreamSelectEnableFlags.Enable); streamSelect.Enable(audioTrack1, AMStreamSelectEnableFlags.Enable);
isVocalRemoved = false; isVocalRemoved = false;
} }
//OverlayForm.MainForm.ShowOriginalSongLabel();
string labelText = isVocalRemoved ? "無人聲" : "有人聲"; string labelText = isVocalRemoved ? "無人聲" : "有人聲";
// 显示标签 // 显示标签
OverlayForm.MainForm.ShowOriginalSongLabel(labelText); // 修改成新標籤
await Task.Delay(3000); OverlayForm.MainForm.HideAllLabels();
OverlayForm.MainForm.ShowOriginalLabel(labelText);
// await Task.Delay(3000);
// 隐藏标签 // 隐藏标签
OverlayForm.MainForm.HideOriginalSongLabel(); // OverlayForm.MainForm.HideOriginalSongLabel();
} }
} }
} }

View File

@ -71,16 +71,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- 複製資料夾 foods -->
<Content Include="foods\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- 複製資料夾 db -->
<Content Include="db\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- 複製資料夾 txt --> <!-- 複製資料夾 txt -->
<Content Include="txt\**\*.*"> <Content Include="txt\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -90,11 +80,6 @@
<Content Include="themes\**\*.*"> <Content Include="themes\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<!-- 複製資料夾 news -->
<Content Include="news\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- 複製資料夾 sounds --> <!-- 複製資料夾 sounds -->
<Content Include="sounds\**\*.*"> <Content Include="sounds\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>