新增 設定檔
調整 心跳功能 修正 藏鏡人 修正 服務鈴 20250703
This commit is contained in:
parent
b3261306eb
commit
d041ff1f80
@ -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<string, DateTime> _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)
|
||||
{
|
||||
|
@ -51,11 +51,11 @@ namespace DBObj
|
||||
}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
public List<SongData> SearchSongsBySinger(string keyword)
|
||||
|
140
DataCheck.cs
140
DataCheck.cs
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
61
Env.cs
Normal file
61
Env.cs
Normal 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 ... 視需要
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
/// <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
|
||||
{
|
||||
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<bool> LoginAndGetTokenAsync()
|
||||
{
|
||||
var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/receiveRegister";
|
||||
var loginPayload = new
|
||||
{
|
||||
email = "MachineKTV@gmail.com",
|
||||
password = "aa147258-"
|
||||
};
|
||||
|
||||
var result = await SendPostAsync<JsonElement>(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<JsonElement>(url, heartbeatPayload, token);
|
||||
Console.WriteLine($"心跳送出成功: {DateTime.Now:HH:mm:ss}");
|
||||
}
|
||||
|
||||
private async Task<T?> SendPostAsync<T>(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<T>(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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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<Artist> 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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -74,27 +74,7 @@ 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)
|
||||
{
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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<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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 將 Stream 轉為 byte[],用於避免 Image 檔案鎖定
|
||||
/// </summary>
|
||||
private byte[] ReadFully(Stream input)
|
||||
{
|
||||
using MemoryStream ms = new();
|
||||
input.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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)
|
||||
|
79
Program.cs
79
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)
|
||||
|
67
TCPServer.cs
67
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user