新增 設定檔
調整 心跳功能 修正 藏鏡人 修正 服務鈴 20250703
This commit is contained in:
parent
b3261306eb
commit
d041ff1f80
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
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.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
|
public class heartbeatSender
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient = new HttpClient();
|
private readonly HttpClient httpClient = new();
|
||||||
private string token;
|
private string token = "";
|
||||||
private string token_heartbeatUrl;
|
private string branchName = "";
|
||||||
private string init_heartbeatUrl;
|
private string heartbeatDomain = "";
|
||||||
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
public heartbeatSender()
|
public heartbeatSender()
|
||||||
{
|
{
|
||||||
LoadHeartbeatUrls("txt/HeartBeat.txt");
|
this.heartbeatDomain = Utils.Env.Get("HeartBeatUrl", "");
|
||||||
|
|
||||||
|
// 在建構子中啟動背景心跳任務
|
||||||
|
_ = Task.Run(() => StartAsync());
|
||||||
}
|
}
|
||||||
/// <summary>
|
public async Task StartAsync()
|
||||||
/// 讀取URL
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filePath">路徑</param>
|
|
||||||
private void LoadHeartbeatUrls(string filePath)
|
|
||||||
{
|
{
|
||||||
if (!File.Exists(filePath))
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
while (!cancellationTokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Console.WriteLine("找不到 HeartBeat.txt");
|
try
|
||||||
return;
|
{
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
bool loginSuccess = await LoginAndGetTokenAsync();
|
||||||
|
Console.WriteLine(loginSuccess ? "心跳登入成功" : "心跳登入失敗");
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] lines = File.ReadAllLines(filePath);
|
if (!string.IsNullOrEmpty(token))
|
||||||
|
|
||||||
foreach (string line in lines)
|
|
||||||
{
|
{
|
||||||
if (line.StartsWith("init:"))
|
await SendHeartbeatAsync();
|
||||||
{
|
|
||||||
init_heartbeatUrl = line.Substring("init:".Length).Trim();
|
|
||||||
}
|
}
|
||||||
else if (line.StartsWith("token:"))
|
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(1), cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
token_heartbeatUrl = line.Substring("token:".Length).Trim();
|
Console.WriteLine($"心跳任務錯誤:{ex.Message}");
|
||||||
|
await Task.Delay(5000); // 錯誤後延遲再跑
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("init URL: " + init_heartbeatUrl);
|
public void Stop()
|
||||||
Console.WriteLine("token URL: " + token_heartbeatUrl);
|
{
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
Console.WriteLine("心跳任務已停止");
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
public async Task<bool> LoginAndGetTokenAsync()
|
||||||
{
|
{
|
||||||
// init_heartbeat
|
var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/receiveRegister";
|
||||||
//var loginUrl = "http://zqd.superstar.dnsnet.cc/api/room/receiveRegister";
|
|
||||||
string hostName = System.Net.Dns.GetHostName();
|
|
||||||
|
|
||||||
var loginPayload = new
|
var loginPayload = new
|
||||||
{
|
{
|
||||||
branch_name = "測試",
|
|
||||||
room_name = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)),
|
|
||||||
room_ip = GetLocalIPv4(),
|
|
||||||
email = "MachineKTV@gmail.com",
|
email = "MachineKTV@gmail.com",
|
||||||
password = "aa147258-"
|
password = "aa147258-"
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(loginPayload);
|
var result = await SendPostAsync<JsonElement>(url, loginPayload);
|
||||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
||||||
|
|
||||||
try
|
if (result.ValueKind == JsonValueKind.Object &&
|
||||||
|
result.TryGetProperty("data", out JsonElement data))
|
||||||
{
|
{
|
||||||
var response = await httpClient.PostAsync(init_heartbeatUrl, content);
|
token = data.GetProperty("token").GetString()!;
|
||||||
response.EnsureSuccessStatusCode();
|
branchName = data.GetProperty("branch_name").GetString()!;
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
token = tokenElement.GetString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Console.WriteLine("登入成功,取得 token:" + token);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"登入失敗:{ex.Message}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendHeartbeatAsync()
|
public async Task SendHeartbeatAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token))
|
var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/heartbeat";
|
||||||
{
|
string hostName = Dns.GetHostName();
|
||||||
Console.WriteLine("請先登入取得 token");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Console.WriteLine(GetLocalIPv4());
|
|
||||||
string hostName = System.Net.Dns.GetHostName();
|
|
||||||
|
|
||||||
var heartbeatData = new
|
var heartbeatPayload = new
|
||||||
{
|
{
|
||||||
branch_name = "測試",
|
branch_name = branchName,
|
||||||
hostname = "PC" + hostName.Substring(Math.Max(0, hostName.Length - 3)),
|
hostname = "PC" + hostName[^3..],
|
||||||
ip = GetLocalIPv4(),
|
ip = GetLocalIPv4(),
|
||||||
cpu = GetCpuUsage(),
|
cpu = GetCpuUsage(),
|
||||||
memory = GetTotalMemoryInMB(),
|
memory = GetTotalMemoryInMB(),
|
||||||
disk = GetDiskTotalSizeInGB()
|
disk = GetDiskTotalSizeInGB()
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(heartbeatData);
|
await SendPostAsync<JsonElement>(url, heartbeatPayload, token);
|
||||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
Console.WriteLine($"心跳送出成功: {DateTime.Now:HH:mm:ss}");
|
||||||
// token_heartbeat
|
}
|
||||||
// heartbeatUrl = "http://zqd.superstar.dnsnet.cc/api/room/heartbeat";
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Post, token_heartbeatUrl);
|
private async Task<T?> SendPostAsync<T>(string url, object payload, string? bearerToken = null)
|
||||||
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
|
|
||||||
{
|
{
|
||||||
|
var json = JsonSerializer.Serialize(payload);
|
||||||
|
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
// Console.WriteLine($"心跳送出狀態:{response.StatusCode}");
|
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();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var responseJson = await response.Content.ReadAsStringAsync();
|
string responseJson = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonSerializer.Deserialize<T>(responseJson);
|
||||||
using var doc = JsonDocument.Parse(responseJson);
|
|
||||||
// Console.WriteLine("API 回傳內容:" + responseJson);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
private static string GetLocalIPv4()
|
||||||
{
|
{
|
||||||
var errorContent = await response.Content.ReadAsStringAsync();
|
foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
|
||||||
Console.WriteLine($"送出心跳錯誤:{ex.Message}");
|
{
|
||||||
Console.WriteLine($"後端回應內容:{errorContent}");
|
if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip))
|
||||||
|
return ip.ToString();
|
||||||
}
|
}
|
||||||
|
return "127.0.0.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetCpuUsage()
|
private float GetCpuUsage()
|
||||||
{
|
{
|
||||||
using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
||||||
cpuCounter.NextValue(); // 需要呼叫兩次
|
cpuCounter.NextValue();
|
||||||
System.Threading.Thread.Sleep(100);
|
Thread.Sleep(100);
|
||||||
return cpuCounter.NextValue(); // 回傳的是百分比,例如 25.3
|
return cpuCounter.NextValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetTotalMemoryInMB()
|
private float GetTotalMemoryInMB()
|
||||||
{
|
{
|
||||||
var computerInfo = new ComputerInfo();
|
var ci = new ComputerInfo();
|
||||||
var totalMB = computerInfo.TotalPhysicalMemory / (1024f);
|
return (ci.TotalPhysicalMemory - ci.AvailablePhysicalMemory) / 1024f;
|
||||||
var availableMB = computerInfo.AvailablePhysicalMemory / (1024f);
|
|
||||||
var usedMB = totalMB - availableMB;
|
|
||||||
return usedMB; // 轉 MB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetDiskTotalSizeInGB(string driveLetter = "C")
|
private float GetDiskTotalSizeInGB(string driveLetter = "C")
|
||||||
{
|
{
|
||||||
var drive = new DriveInfo(driveLetter);
|
var drive = new DriveInfo(driveLetter);
|
||||||
return drive.TotalSize / (1024f * 1024f * 1024f); // 轉 GB
|
return drive.TotalSize / (1024f * 1024f * 1024f);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,7 @@ namespace DualScreenDemo
|
|||||||
|
|
||||||
// 安裝包更新
|
// 安裝包更新
|
||||||
string localAddress = GetLocalIPAddress();
|
string localAddress = GetLocalIPAddress();
|
||||||
string externalAddress = File.Exists(@"\\svr01\txt\ip.txt")
|
string externalAddress = Utils.Env.Get("PhoneIP", "").Trim();
|
||||||
? File.ReadAllText(@"\\svr01\txt\ip.txt").Trim()
|
|
||||||
: "";
|
|
||||||
|
|
||||||
_listener = new HttpListener();
|
_listener = new HttpListener();
|
||||||
_listener.Prefixes.Add($"http://{localAddress}:{port}/");
|
_listener.Prefixes.Add($"http://{localAddress}:{port}/");
|
||||||
string hostName = System.Net.Dns.GetHostName();
|
string hostName = System.Net.Dns.GetHostName();
|
||||||
@ -538,7 +535,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 +980,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);
|
||||||
|
@ -56,10 +56,45 @@ namespace OverlayFormObj
|
|||||||
|
|
||||||
this.Invalidate();
|
this.Invalidate();
|
||||||
blackBackgroundPanel.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)
|
public void UpdateMarqueeTextSecondLine(string newText)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(InvokeRequired);
|
||||||
if (InvokeRequired)
|
if (InvokeRequired)
|
||||||
{
|
{
|
||||||
Invoke(new MethodInvoker(() => UpdateMarqueeTextSecondLine(newText)));
|
Invoke(new MethodInvoker(() => UpdateMarqueeTextSecondLine(newText)));
|
||||||
|
@ -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,6 +436,40 @@ 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
|
||||||
@ -808,24 +856,19 @@ 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();
|
||||||
}
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示标准标签
|
// 显示标准标签
|
||||||
@ -1029,6 +1072,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()
|
||||||
{
|
{
|
||||||
|
@ -613,7 +613,7 @@ private static void SongDisplayTimer_Elapsed(object sender, EventArgs e)
|
|||||||
|
|
||||||
using (Font largeFont = new Font("微軟正黑體", 34, FontStyle.Bold))
|
using (Font largeFont = new Font("微軟正黑體", 34, FontStyle.Bold))
|
||||||
using (Brush whiteBrush = new SolidBrush(Color.White))
|
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 marqueeBrush = new SolidBrush(marqueeTextColor))
|
||||||
using (Brush backgroundBrush = new SolidBrush(Color.FromArgb(255, 0, 0, 0)))
|
using (Brush backgroundBrush = new SolidBrush(Color.FromArgb(255, 0, 0, 0)))
|
||||||
{
|
{
|
||||||
@ -633,7 +633,7 @@ private static void SongDisplayTimer_Elapsed(object sender, EventArgs e)
|
|||||||
float centeredXPos = (this.Width - textSizeSecondLine.Width) / 2;
|
float centeredXPos = (this.Width - textSizeSecondLine.Width) / 2;
|
||||||
|
|
||||||
e.Graphics.FillRectangle(backgroundBrush, centeredXPos, yPosition2, textSizeSecondLine.Width, textSizeSecondLine.Height);
|
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;
|
e.Graphics.Clip = originalClip;
|
||||||
|
|
||||||
@ -1495,11 +1495,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
|
||||||
{
|
{
|
||||||
|
@ -62,8 +62,7 @@ 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);
|
||||||
|
|
||||||
@ -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);
|
||||||
@ -235,35 +233,5 @@ namespace DualScreenDemo
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -221,38 +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()};";
|
string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY add_date DESC LIMIT {Utils.Env.GetInt("NewSongLimit", 100)};";
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
private void promotionsButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@ -137,21 +117,11 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,32 +11,52 @@ 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()
|
/// <summary>
|
||||||
|
/// 從指定資料夾載入所有 .jpg 圖片
|
||||||
|
/// </summary>
|
||||||
|
private List<Image> LoadImagesFromFolder(string folderName)
|
||||||
{
|
{
|
||||||
List<Image> images = new List<Image>();
|
List<Image> images = new();
|
||||||
string foodsFolderPath = Path.Combine(Application.StartupPath, "foods");
|
string folderPath = Path.Combine(Utils.Env.KtvPath, folderName);
|
||||||
|
|
||||||
string[] imageFiles = Directory.GetFiles(foodsFolderPath, "*.jpg");
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,40 +6,10 @@ 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;
|
||||||
@ -72,8 +42,8 @@ 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();
|
||||||
|
@ -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()
|
||||||
@ -1554,23 +1553,11 @@ 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 +1571,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 +2338,13 @@ 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");
|
|
||||||
string initialState = ReadStateFile(stateFilePath);
|
|
||||||
|
|
||||||
if (initialState.Equals("CLOSE", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
foreach (Control ctrl in this.Controls)
|
|
||||||
{
|
|
||||||
ctrl.Enabled = false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
ShowSendOffScreen();
|
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)
|
||||||
|
73
Program.cs
73
Program.cs
@ -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,60 +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.2.0 20250703");
|
||||||
|
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\video");
|
||||||
_ = Directory.Exists(@"\\svr02\video");
|
// _ = Directory.Exists(@"\\svr02\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
|
||||||
{
|
{
|
||||||
@ -110,20 +79,8 @@ namespace DualScreenDemo
|
|||||||
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()
|
||||||
|
65
TCPServer.cs
65
TCPServer.cs
@ -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...");
|
||||||
@ -202,7 +180,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);
|
||||||
|
|
||||||
@ -218,7 +196,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();
|
||||||
|
|
||||||
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
||||||
@ -238,7 +216,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);
|
||||||
|
|
||||||
@ -271,10 +249,12 @@ namespace DualScreenDemo
|
|||||||
stream.Write(okResponse, 0, okResponse.Length);
|
stream.Write(okResponse, 0, okResponse.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
|
if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Connection closed.");
|
Console.WriteLine("Connection closed.");
|
||||||
@ -315,40 +295,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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -812,9 +812,9 @@ namespace DualScreenDemo
|
|||||||
// 嘗試重新初始化並重播
|
// 嘗試重新初始化並重播
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
await Task.Delay(100);
|
||||||
StopAndReleaseResources();
|
StopAndReleaseResources();
|
||||||
await Task.Delay(1000);
|
await Task.Delay(100);
|
||||||
|
|
||||||
// 重新初始化 COM
|
// 重新初始化 COM
|
||||||
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
|
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
|
||||||
@ -1599,10 +1599,12 @@ namespace DualScreenDemo
|
|||||||
//OverlayForm.MainForm.ShowOriginalSongLabel();
|
//OverlayForm.MainForm.ShowOriginalSongLabel();
|
||||||
string labelText = isVocalRemoved ? "無人聲" : "有人聲";
|
string labelText = isVocalRemoved ? "無人聲" : "有人聲";
|
||||||
// 显示标签
|
// 显示标签
|
||||||
|
// 修改成新標籤
|
||||||
|
OverlayForm.MainForm.HideAllLabels();
|
||||||
OverlayForm.MainForm.ShowOriginalSongLabel(labelText);
|
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