2025-04-07 16:54:10 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Newtonsoft.Json; // For JsonConvert and JsonException
|
|
|
|
|
using System.Drawing.Imaging; // For ImageFormat
|
|
|
|
|
using ZXing; // For BarcodeWriter and BarcodeFormat
|
|
|
|
|
using ZXing.QrCode; // For QrCodeEncodingOptions
|
|
|
|
|
using ActionString = System.Action<string>; // 现在可以在代码中使用 ActionString
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using DBObj;
|
|
|
|
|
using OverlayFormObj;
|
2025-04-09 10:43:09 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
namespace DualScreenDemo
|
|
|
|
|
{
|
|
|
|
|
public class HttpServer
|
|
|
|
|
{
|
|
|
|
|
private static string _localIP = GetLocalIPAddress();
|
|
|
|
|
private static int _port = 9090; // 或其他方式設置
|
|
|
|
|
// 服务器类变量
|
|
|
|
|
private static SongListManager songListManager;
|
|
|
|
|
// 使用完整命名空间来避免歧义
|
|
|
|
|
public static event ActionString OnDisplayBarrage;
|
|
|
|
|
private static DateTime lastClickTime = DateTime.MinValue;
|
|
|
|
|
public static string randomFolderPath; // 声明全局变量
|
|
|
|
|
//private static OverlayForm form;
|
|
|
|
|
private static readonly ConcurrentDictionary<string, byte[]> _fileCache = new ConcurrentDictionary<string, byte[]>();
|
|
|
|
|
private static readonly SemaphoreSlim _requestThrottle = new SemaphoreSlim(20); // 限制并发请求数
|
|
|
|
|
private static readonly CancellationTokenSource _serverCts = new CancellationTokenSource();
|
2025-05-19 13:23:09 +08:00
|
|
|
|
private static HttpListener _listener;
|
|
|
|
|
private static CancellationTokenSource _cts;
|
|
|
|
|
private static Task _serverTask;
|
|
|
|
|
private static string _baseDirectory = @"themes\superstar\_www"; // 根據實際情況設定
|
2025-05-19 14:27:48 +08:00
|
|
|
|
|
2025-05-19 13:23:09 +08:00
|
|
|
|
private static TaskCompletionSource<string> _qrReadyTcs;
|
|
|
|
|
|
|
|
|
|
public static async Task StartServer(string baseDirectory, int port, SongListManager manager, CancellationToken token)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-05-19 14:27:48 +08:00
|
|
|
|
songListManager = manager;
|
2025-04-07 16:54:10 +08:00
|
|
|
|
string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory);
|
2025-05-19 13:23:09 +08:00
|
|
|
|
randomFolderPath = randomFolderName;
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
2025-05-19 13:23:09 +08:00
|
|
|
|
string localAddress = GetLocalIPAddress();
|
|
|
|
|
string externalAddress = File.Exists(@"\\SVR01\superstarb\txt\ip.txt")
|
|
|
|
|
? File.ReadAllText(@"\\SVR01\superstarb\txt\ip.txt").Trim()
|
|
|
|
|
: "";
|
|
|
|
|
|
|
|
|
|
_listener = new HttpListener();
|
|
|
|
|
_listener.Prefixes.Add($"http://{localAddress}:{port}/");
|
|
|
|
|
string hostName = System.Net.Dns.GetHostName();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
string externalPort = '1' + hostName.Substring(Math.Max(2, hostName.Length - 20));
|
|
|
|
|
if (!string.IsNullOrEmpty(externalAddress))
|
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
|
string host = externalAddress.Split(':')[0];
|
|
|
|
|
externalPort = '1' + System.Net.Dns.GetHostName().Substring(Math.Max(2, System.Net.Dns.GetHostName().Length - 20));
|
|
|
|
|
_listener.Prefixes.Add($"http://{host}:{externalPort}/");
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", localAddress, port, randomFolderName);
|
2025-05-19 13:23:09 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
|
|
|
|
// 修改外网二维码内容生成
|
|
|
|
|
|
|
|
|
|
string externalQrContent = !string.IsNullOrEmpty(externalAddress) ?
|
|
|
|
|
String.Format("http://{0}:{1}/{2}/windows.html", externalAddress, externalPort, randomFolderName) :
|
|
|
|
|
localQrContent;
|
|
|
|
|
|
2025-05-19 13:23:09 +08:00
|
|
|
|
GenerateQRCode(externalQrContent, Path.Combine(baseDirectory, randomFolderName, "qrcode.png"));
|
|
|
|
|
|
|
|
|
|
_qrReadyTcs?.TrySetResult(randomFolderName); // safe call,null-safe
|
2025-04-07 16:54:10 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
|
_listener.Start();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
Console.WriteLine("Server started.");
|
|
|
|
|
}
|
|
|
|
|
catch (HttpListenerException ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error starting server: " + ex.Message);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 13:23:09 +08:00
|
|
|
|
while (!token.IsCancellationRequested)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
HttpListenerContext context = await _listener.GetContextAsync();
|
|
|
|
|
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
|
|
|
|
|
}
|
|
|
|
|
catch (HttpListenerException)
|
|
|
|
|
{
|
|
|
|
|
// 可能是 Stop() 引起,不處理
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-05-19 13:23:09 +08:00
|
|
|
|
|
|
|
|
|
_listener.Close();
|
|
|
|
|
Console.WriteLine("Server stopped.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task RestartServer()
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Restarting server...");
|
|
|
|
|
|
|
|
|
|
_cts?.Cancel();
|
|
|
|
|
_listener?.Stop();
|
|
|
|
|
|
|
|
|
|
if (_serverTask != null)
|
|
|
|
|
{
|
|
|
|
|
try { await _serverTask; }
|
|
|
|
|
catch (OperationCanceledException) { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeleteRandomFolder(_baseDirectory);
|
|
|
|
|
|
|
|
|
|
_cts = new CancellationTokenSource();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 等 QR code 跑完
|
|
|
|
|
_qrReadyTcs = new TaskCompletionSource<string>();
|
2025-05-19 14:27:48 +08:00
|
|
|
|
_serverTask = StartServer(_baseDirectory, _port, songListManager, _cts.Token);
|
2025-05-19 13:23:09 +08:00
|
|
|
|
string readyFolder = await _qrReadyTcs.Task;
|
|
|
|
|
|
|
|
|
|
// ✅ 等 QR Code 圖片準備好之後再處理 UI 更新
|
|
|
|
|
Console.WriteLine("restart : " + readyFolder);
|
|
|
|
|
PrimaryForm.Instance.OverlayQRCodeOnImage(readyFolder);
|
|
|
|
|
OverlayForm.Instance.DisplayQRCodeOnOverlay(readyFolder);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task ProcessRequestWithTimeout(HttpListenerContext context, string baseDirectory, string randomFolderName)
|
|
|
|
|
{
|
|
|
|
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await _requestThrottle.WaitAsync(cts.Token);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_requestThrottle.Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = 408; // Request Timeout
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error processing request: {ex.Message}");
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = 500;
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void DeleteRandomFolder(string baseDirectory)
|
|
|
|
|
{
|
|
|
|
|
string fullPath = Path.Combine(baseDirectory, randomFolderPath);
|
|
|
|
|
if (Directory.Exists(fullPath))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Directory.Delete(fullPath, true);
|
2025-05-19 13:23:09 +08:00
|
|
|
|
Console.WriteLine("刪除 random folder: " + fullPath);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2025-05-19 13:23:09 +08:00
|
|
|
|
Console.WriteLine("錯誤 deleting random folder: " + ex.Message);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetServerAddress()
|
|
|
|
|
{
|
|
|
|
|
return String.Format("http://{0}:{1}/", _localIP, _port);
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 生成隨機路徑
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="baseDirectory"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private static string CreateRandomFolderAndRedirectHTML(string baseDirectory)
|
|
|
|
|
{
|
|
|
|
|
string randomFolderName = Path.GetRandomFileName().Replace(".", "");
|
|
|
|
|
string randomFolderPath = Path.Combine(baseDirectory, randomFolderName);
|
|
|
|
|
Directory.CreateDirectory(randomFolderPath);
|
|
|
|
|
Console.WriteLine(String.Format("Created random folder: {0}", randomFolderPath));
|
|
|
|
|
|
|
|
|
|
string sourceHTMLPath = Path.Combine(baseDirectory, "windows.html");
|
|
|
|
|
string targetHTMLPath = Path.Combine(randomFolderPath, "windows.html");
|
|
|
|
|
File.Copy(sourceHTMLPath, targetHTMLPath, true);
|
|
|
|
|
Console.WriteLine(String.Format("Copied windows.html to {0}", targetHTMLPath));
|
|
|
|
|
|
|
|
|
|
// 在生成的 HTML 中注入随机路径
|
|
|
|
|
string htmlContent = File.ReadAllText(sourceHTMLPath);
|
|
|
|
|
htmlContent = htmlContent.Replace("var randomFolder = '';", String.Format("var randomFolder = '{0}';", randomFolderName));
|
|
|
|
|
File.WriteAllText(targetHTMLPath, htmlContent);
|
|
|
|
|
|
|
|
|
|
return randomFolderName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成 QR 码并返回图像路径
|
|
|
|
|
public static string GenerateQRCode(string content, string savePath)
|
|
|
|
|
{
|
|
|
|
|
var writer = new BarcodeWriter
|
|
|
|
|
{
|
|
|
|
|
Format = BarcodeFormat.QR_CODE,
|
|
|
|
|
Options = new QrCodeEncodingOptions
|
|
|
|
|
{
|
|
|
|
|
Height = 165,
|
|
|
|
|
Width = 165,
|
|
|
|
|
Margin = 0
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using (var bitmap = writer.Write(content))
|
|
|
|
|
{
|
|
|
|
|
bitmap.Save(savePath, ImageFormat.Png);
|
|
|
|
|
return savePath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task ProcessRequestAsync(HttpListenerContext context, string baseDirectory, string randomFolderName)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 添加CORS头
|
|
|
|
|
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
|
|
|
|
|
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
|
|
|
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
|
|
|
|
|
|
|
|
|
|
// 处理OPTIONS请求
|
|
|
|
|
if (context.Request.HttpMethod == "OPTIONS")
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = 200;
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加基本的缓存控制
|
|
|
|
|
context.Response.Headers.Add("Cache-Control", "public, max-age=3600");
|
|
|
|
|
|
|
|
|
|
if (context.Request.HttpMethod == "POST")
|
|
|
|
|
{
|
|
|
|
|
string relativePath = context.Request.Url.AbsolutePath.Replace($"/{randomFolderName}", "");
|
|
|
|
|
Console.WriteLine("Received request for path: " + relativePath);
|
|
|
|
|
|
|
|
|
|
switch (relativePath)
|
|
|
|
|
{
|
|
|
|
|
case "/search":
|
|
|
|
|
await HandleSearchRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/signal":
|
|
|
|
|
await HandleSignalRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/sound-control":
|
|
|
|
|
await HandleSoundControlRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/send-sticker":
|
|
|
|
|
await HandleStickerRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/order-song":
|
|
|
|
|
await HandleOrderSongRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/insert-song":
|
|
|
|
|
await HandleInsertSongRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/ordered-song":
|
|
|
|
|
await HandleOrderSongListRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/message":
|
|
|
|
|
await HandlemessageRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
case "/favorite":
|
|
|
|
|
await HandleFavoriteRequest(context);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
context.Response.StatusCode = 404;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (context.Request.HttpMethod == "GET")
|
|
|
|
|
{
|
|
|
|
|
// 获取请求的完整URL路径
|
|
|
|
|
string requestPath = context.Request.Url.AbsolutePath;
|
|
|
|
|
|
|
|
|
|
// 如果是根路径访问,直接返回404
|
|
|
|
|
if (requestPath == "/" || requestPath == "/windows.html")
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string requestedFile = context.Request.Url.LocalPath.Replace($"/{randomFolderName}/", "");
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(requestedFile.Trim('/')))
|
|
|
|
|
{
|
|
|
|
|
requestedFile = "windows.html";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await HandleGetRequest(context, baseDirectory, requestedFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error processing request: {ex.Message}");
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = 500;
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task SendJsonResponseAsync(HttpListenerContext context, object data, int statusCode = 200)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(data);
|
|
|
|
|
byte[] buffer = Encoding.UTF8.GetBytes(jsonResponse);
|
|
|
|
|
|
|
|
|
|
context.Response.StatusCode = statusCode;
|
|
|
|
|
context.Response.ContentType = "application/json; charset=utf-8";
|
|
|
|
|
context.Response.ContentLength64 = buffer.Length;
|
|
|
|
|
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error sending JSON response: {ex.Message}");
|
|
|
|
|
context.Response.StatusCode = 500;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-19 14:27:48 +08:00
|
|
|
|
// 點歌問題
|
2025-04-07 16:54:10 +08:00
|
|
|
|
private static async Task HandleSearchRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string requestBody = await ReadRequestBodyAsync(context.Request);
|
|
|
|
|
var searchRequest = JsonConvert.DeserializeObject<SearchRequest>(requestBody);
|
|
|
|
|
List<SongData> searchResults;
|
|
|
|
|
switch (searchRequest.Type)
|
|
|
|
|
{
|
|
|
|
|
case "new-songs":
|
2025-04-23 11:46:55 +08:00
|
|
|
|
searchResults = songListManager.SearchNewSongs();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
break;
|
|
|
|
|
case "top-ranking":
|
2025-04-23 11:46:55 +08:00
|
|
|
|
searchResults = songListManager.SearchHotSongs();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
break;
|
|
|
|
|
case "singer":
|
2025-05-19 14:27:48 +08:00
|
|
|
|
Console.WriteLine("Query : " + searchRequest.Query);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
searchResults = songListManager.SearchSongsBySinger(searchRequest.Query);
|
|
|
|
|
break;
|
|
|
|
|
case "song":
|
|
|
|
|
searchResults = songListManager.SearchSongsByName(searchRequest.Query);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
searchResults = new List<SongData>();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await SendJsonResponseAsync(context, searchResults);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error handling search request: {ex.Message}");
|
|
|
|
|
await SendJsonResponseAsync(context, new { error = "Search failed" }, 500);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
|
|
|
|
|
{
|
|
|
|
|
using var reader = new StreamReader(request.InputStream, request.ContentEncoding);
|
|
|
|
|
return await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task HandleSignalRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Handling signal request...");
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received POST body: " + requestBody);
|
|
|
|
|
|
|
|
|
|
var responseMessage = new { status = "Signal received" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(responseMessage);
|
|
|
|
|
byte[] buffer = Encoding.UTF8.GetBytes(jsonResponse);
|
|
|
|
|
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.ContentLength64 = buffer.Length;
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task HandleSoundControlRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received sound control command: " + requestBody);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var data = JsonConvert.DeserializeObject<SoundControlRequest>(requestBody);
|
|
|
|
|
|
|
|
|
|
switch (data.Command)
|
|
|
|
|
{
|
|
|
|
|
case "pause":
|
|
|
|
|
// 执行暂停操作
|
|
|
|
|
if (VideoPlayerForm.Instance.isPaused)
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.Play();
|
|
|
|
|
PrimaryForm.Instance.pauseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.playButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.Pause();
|
|
|
|
|
PrimaryForm.Instance.pauseButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.playButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "volume_up":
|
|
|
|
|
// 执行音量增大操作
|
|
|
|
|
PrimaryForm.SendCommandThroughSerialPort("a2 b3 a4");
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowVolumeUpLabel();
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "mic_up":
|
|
|
|
|
// 执行麦克风增大操作
|
|
|
|
|
PrimaryForm.SendCommandThroughSerialPort("a2 b5 a4");
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowMicUpLabel();
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "mute":
|
|
|
|
|
// 执行静音操作
|
|
|
|
|
PrimaryForm.Instance.Invoke(new System.Action(() =>
|
|
|
|
|
{
|
|
|
|
|
if (PrimaryForm.Instance.videoPlayerForm.isMuted)
|
|
|
|
|
{
|
|
|
|
|
// 取消静音,恢复之前的音量
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.SetVolume(PrimaryForm.Instance.videoPlayerForm.previousVolume);
|
|
|
|
|
// muteButton.Text = "Mute";
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.isMuted = false;
|
|
|
|
|
OverlayForm.MainForm.HideMuteLabel();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 静音,将音量设置为-10000
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.previousVolume = PrimaryForm.Instance.videoPlayerForm.GetVolume();
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.SetVolume(-10000);
|
|
|
|
|
// muteButton.Text = "Unmute";
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.isMuted = true;
|
|
|
|
|
OverlayForm.MainForm.ShowMuteLabel();
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "volume_down":
|
|
|
|
|
// 执行音量减小操作
|
|
|
|
|
PrimaryForm.SendCommandThroughSerialPort("a2 b4 a4");
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowVolumeDownLabel();
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "mic_down":
|
|
|
|
|
// 执行麦克风减小操作
|
|
|
|
|
PrimaryForm.SendCommandThroughSerialPort("a2 b6 a4");
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowMicDownLabel();
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "original_song":
|
|
|
|
|
if (PrimaryForm.Instance.InvokeRequired)
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.Invoke(new System.Action(() =>
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.ToggleVocalRemoval();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.ToggleVocalRemoval();
|
|
|
|
|
}
|
|
|
|
|
// 执行原唱操作
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case "service":
|
|
|
|
|
// 执行服务操作
|
|
|
|
|
PrimaryForm.SendCommandThroughSerialPort("a2 53 a4");
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowServiceBellLabel();
|
|
|
|
|
}));
|
|
|
|
|
// 异步处理等待和隐藏标签
|
|
|
|
|
await HttpServer.HandleServiceBellAsync();
|
|
|
|
|
break;
|
|
|
|
|
case "replay":
|
|
|
|
|
// 执行重唱操作
|
|
|
|
|
PrimaryForm.Instance.Invoke(new System.Action(() =>
|
|
|
|
|
{
|
|
|
|
|
// 在这里执行按钮点击后的操作
|
|
|
|
|
// 比如切歌操作
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong();
|
|
|
|
|
}));
|
|
|
|
|
break;
|
|
|
|
|
case "male_key":
|
|
|
|
|
// 执行男调操作
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowMaleKeyLabel();
|
|
|
|
|
}));
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
|
|
|
|
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
|
|
|
|
|
byte[] commandBytesDecreasePitch = new byte[] { 0xA2, 0xB2, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
|
|
|
|
// MessageBox.Show("升調指令已發送。");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("串口未開啟,無法發送升調指令。");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "female_key":
|
|
|
|
|
// 执行女调操作
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowFemaleKeyLabel();
|
|
|
|
|
}));
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
|
|
|
|
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
|
|
|
|
|
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0xB1, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
|
|
|
|
// MessageBox.Show("升調指令已發送。");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("串口未開啟,無法發送升調指令。");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "cut":
|
|
|
|
|
// 执行切歌操作
|
|
|
|
|
if (PrimaryForm.Instance.InvokeRequired)
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.Invoke(new System.Action(() =>
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.SkipToNextSong();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.videoPlayerForm.SkipToNextSong();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "lower_key":
|
|
|
|
|
// 执行降调操作
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowKeyDownLabel();
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// MessageBox.Show("降調功能啟動");
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
// 假設 0xA2, 0xC2, 0xA4 是降調的指令
|
|
|
|
|
byte[] commandBytesDecreasePitch = new byte[] { 0xA2, 0xB2, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
|
|
|
|
// MessageBox.Show("降調指令已發送。");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// MessageBox.Show("串口未開啟,無法發送降調指令。");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "standard_key":
|
|
|
|
|
// 执行标准调操作
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowStandardKeyLabel();
|
|
|
|
|
}));
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
|
|
|
|
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0x7F, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
|
|
|
|
// MessageBox.Show("升調指令已發送。");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("串口未開啟,無法發送升調指令。");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "raise_key":
|
|
|
|
|
// 执行升调操作
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.ShowKeyUpLabel();
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// MessageBox.Show("升調功能啟動");
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
|
|
|
|
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0xB1, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
|
|
|
|
// MessageBox.Show("升調指令已發送。");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("串口未開啟,無法發送升調指令。");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Console.WriteLine("Unknown command: " + data.Command);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = new { status = "success" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.ContentLength64 = Encoding.UTF8.GetByteCount(jsonResponse);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
|
|
|
|
|
using (var streamWriter = new StreamWriter(context.Response.OutputStream))
|
|
|
|
|
{
|
|
|
|
|
await streamWriter.WriteAsync(jsonResponse);
|
|
|
|
|
await streamWriter.FlushAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("JSON parsing error: " + ex.Message);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(new byte[0], 0, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static async Task HandleServiceBellAsync()
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(3000); // 等待 3 秒
|
|
|
|
|
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
|
|
|
|
OverlayForm.MainForm.HideServiceBellLabel();
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
private static async Task HandleOrderSongListRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 读取请求的内容
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received order song request: " + requestBody);
|
|
|
|
|
|
|
|
|
|
// 检查 playedSongsHistory 是否存在且不为空
|
|
|
|
|
if (PrimaryForm.playedSongsHistory != null && PrimaryForm.playedSongsHistory.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Played Songs History Count: " + PrimaryForm.playedSongsHistory.Count);
|
|
|
|
|
foreach (var song in PrimaryForm.playedSongsHistory)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Song: {song.Song}, ArtistA: {song.ArtistA}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据播放历史确定每首歌的播放状态
|
|
|
|
|
var playStates = DeterminePlayStates(PrimaryForm.playedSongsHistory);
|
|
|
|
|
|
|
|
|
|
// 创建响应数据
|
|
|
|
|
var response = new
|
|
|
|
|
{
|
|
|
|
|
playingSongList = PrimaryForm.playedSongsHistory
|
|
|
|
|
.Select((song, index) => CreateSongResponse(song, playStates[index])) // 使用新的播放状态
|
|
|
|
|
.ToList(),
|
|
|
|
|
// 生成播放历史
|
|
|
|
|
currentSongIndexInHistory = PrimaryForm.currentSongIndexInHistory
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
Console.WriteLine("Serialized JSON Response: " + jsonResponse);
|
|
|
|
|
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
await SendResponseAsync(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 如果播放历史为空,回传一条消息
|
|
|
|
|
var response = new { status = "info", message = "No songs in the played history" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
Console.WriteLine("Sending empty response: " + jsonResponse);
|
|
|
|
|
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
await SendResponseAsync(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error handling order song request: " + ex.Message);
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
|
|
|
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"An error occurred\"}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确定每首歌的播放状态
|
|
|
|
|
private static List<PlayState?> DeterminePlayStates(List<SongData> playedSongsHistory)
|
|
|
|
|
{
|
|
|
|
|
var playStates = new List<PlayState?>();
|
|
|
|
|
bool foundPlaying = false; // 标记是否已找到正在播放的歌曲
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < playedSongsHistory.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
// 这里可以根据您的业务逻辑来确定每首歌的播放状态
|
|
|
|
|
if (i == PrimaryForm.currentSongIndexInHistory)
|
|
|
|
|
{
|
|
|
|
|
playStates.Add(PlayState.Playing);
|
|
|
|
|
foundPlaying = true; // 找到正在播放的歌曲
|
|
|
|
|
}
|
|
|
|
|
else if (foundPlaying)
|
|
|
|
|
{
|
|
|
|
|
playStates.Add(null); // 找到播放中的歌曲后,后面的状态设置为 null
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
playStates.Add(PlayState.NotPlayed); // 未播放状态
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return playStates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用于创建歌曲响应对象,包括播放状态
|
|
|
|
|
private static object CreateSongResponse(SongData song, PlayState? playState)
|
|
|
|
|
{
|
|
|
|
|
return new
|
|
|
|
|
{
|
|
|
|
|
song.Song,
|
|
|
|
|
song.ArtistA,
|
|
|
|
|
song.SongFilePathHost1,
|
|
|
|
|
song.SongFilePathHost2,
|
|
|
|
|
PlayState = playState.HasValue ? (playState.Value == PlayState.Playing ? "播放中" : "播放完畢") : null // 如果状态为 null,不返回状态信息
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成播放状态
|
|
|
|
|
|
|
|
|
|
private static async Task HandleOrderSongRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received order song request: " + requestBody);
|
|
|
|
|
|
|
|
|
|
// 解析 JSON 为 Song 对象
|
|
|
|
|
var song = JsonConvert.DeserializeObject<SongData>(requestBody);
|
|
|
|
|
|
|
|
|
|
if (song != null)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Ordering Song: {song.Song} by {song.ArtistA}");
|
|
|
|
|
// 这里可以添加处理逻辑,例如将歌曲加入到播放列表或数据库中
|
|
|
|
|
OverlayForm.MainForm.AddSongToPlaylist(song);
|
|
|
|
|
|
|
|
|
|
var response = new { status = "success", message = "Song ordered successfully" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
await SendResponseAsync(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid song data\"}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("JSON parsing error: " + ex.Message);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid JSON format\"}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task HandleInsertSongRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received insert song request: " + requestBody);
|
|
|
|
|
|
|
|
|
|
// 解析 JSON 为 Song 对象
|
|
|
|
|
var song = JsonConvert.DeserializeObject<SongData>(requestBody);
|
|
|
|
|
|
|
|
|
|
if (song != null)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Inserting Song: {song.Song} by {song.ArtistA}");
|
|
|
|
|
// 这里可以添加插播歌曲的处理逻辑
|
|
|
|
|
OverlayForm.MainForm.InsertSongToPlaylist(song);
|
|
|
|
|
|
|
|
|
|
var response = new { status = "success", message = "Song inserted successfully" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
await SendResponseAsync(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid song data\"}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("JSON parsing error: " + ex.Message);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid JSON format\"}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class SoundControlRequest
|
|
|
|
|
{
|
|
|
|
|
public string Command { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task HandleStickerRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
string requestBody;
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
requestBody = await reader.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Received sticker ID: " + requestBody);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var data = JsonConvert.DeserializeObject<StickerRequest>(requestBody);
|
|
|
|
|
string stickerId = data.StickerId;
|
|
|
|
|
|
|
|
|
|
// 处理 stickerId 的逻辑,例如显示贴图
|
|
|
|
|
if (OverlayForm.MainForm != null)
|
|
|
|
|
{
|
|
|
|
|
OverlayForm.MainForm.DisplaySticker(stickerId);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("MainForm is null.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = new { status = "success" };
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.ContentLength64 = Encoding.UTF8.GetByteCount(jsonResponse);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
|
|
|
|
|
using (var streamWriter = new StreamWriter(context.Response.OutputStream))
|
|
|
|
|
{
|
|
|
|
|
await streamWriter.WriteAsync(jsonResponse);
|
|
|
|
|
await streamWriter.FlushAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("JSON parsing error: " + ex.Message);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(new byte[0], 0, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class StickerRequest
|
|
|
|
|
{
|
|
|
|
|
public string StickerId { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 封装响应代码以避免重复
|
|
|
|
|
async static Task SendResponseAsync(HttpListenerContext context, string jsonResponse) {
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.ContentLength64 = Encoding.UTF8.GetByteCount(jsonResponse);
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
using (var streamWriter = new StreamWriter(context.Response.OutputStream)) {
|
|
|
|
|
await streamWriter.WriteAsync(jsonResponse);
|
|
|
|
|
await streamWriter.FlushAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void InvokeAction(System.Action action)
|
|
|
|
|
{
|
|
|
|
|
if (OverlayForm.MainForm.InvokeRequired)
|
|
|
|
|
{
|
|
|
|
|
OverlayForm.MainForm.Invoke(action);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
action();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static async Task HandlemessageRequest(HttpListenerContext context)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 初始化 form(如果未初始化
|
|
|
|
|
|
|
|
|
|
// 读取请求体中的消息
|
|
|
|
|
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
|
|
|
{
|
|
|
|
|
// 异步读取整个请求体内容
|
|
|
|
|
string json = await reader.ReadToEndAsync();
|
|
|
|
|
|
|
|
|
|
// 确保 JSON 内容不为空
|
|
|
|
|
if (!string.IsNullOrEmpty(json))
|
|
|
|
|
{
|
|
|
|
|
// 返回成功响应
|
|
|
|
|
context.Response.StatusCode = 200;
|
|
|
|
|
context.Response.ContentType = "application/json"; // 确保返回 JSON 格式
|
|
|
|
|
int startIndex = json.IndexOf("\"message\":\"") + 11; // 11 是 "message\":\"" 的长度
|
|
|
|
|
int endIndex = json.IndexOf("\"", startIndex);
|
|
|
|
|
string Messagefist ="藏鏡人:";
|
|
|
|
|
|
|
|
|
|
// 如果找到了 "message" 字段
|
|
|
|
|
if (startIndex >= 0 && endIndex >= 0)
|
|
|
|
|
{
|
|
|
|
|
// 提取 "message" 字段的值
|
|
|
|
|
string message = json.Substring(startIndex, endIndex - startIndex);
|
|
|
|
|
for(int i=0;i<3;i++)
|
|
|
|
|
{
|
|
|
|
|
string Messagelast="";
|
|
|
|
|
for(int j=0;j < message.Length;j++){
|
|
|
|
|
Messagelast += message[j];
|
|
|
|
|
await Task.Delay(300);
|
|
|
|
|
// 将读取到的 "message" 字段传递给 UI 控件显示
|
|
|
|
|
InvokeAction(() => OverlayForm.MainForm.ShowmessageLabel(Messagefist+Messagelast+'_'));
|
|
|
|
|
}
|
|
|
|
|
await Task.Delay(3000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 如果没有有效的 JSON 数据,返回错误响应
|
|
|
|
|
context.Response.StatusCode = 400; // Bad Request
|
|
|
|
|
context.Response.StatusDescription = "Invalid JSON data.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//catch (JsonException ex)
|
|
|
|
|
//{
|
|
|
|
|
// context.Response.StatusCode = 400;
|
|
|
|
|
// Console.WriteLine("解析留言数据时出错");
|
|
|
|
|
//}
|
|
|
|
|
//catch (Exception ex)
|
|
|
|
|
//{
|
|
|
|
|
// context.Response.StatusCode = 500;
|
|
|
|
|
// Console.WriteLine("服务器内部错误");
|
|
|
|
|
//}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
context.Response.Close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task OutputMessageAsync(string message, Label messageLabel)
|
|
|
|
|
{
|
|
|
|
|
messageLabel.Text = ""; // 清空现有的文本
|
|
|
|
|
|
|
|
|
|
foreach (char c in message)
|
|
|
|
|
{
|
|
|
|
|
messageLabel.Text += c; // 逐字显示消息
|
|
|
|
|
await Task.Delay(500); // 每个字符间隔 0.5 秒
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟等待 5 秒后继续其他操作
|
|
|
|
|
await Task.Delay(5000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetMimeType(string filePath)
|
|
|
|
|
{
|
|
|
|
|
// 获取MIME类型的逻辑
|
|
|
|
|
string mimeType = "application/octet-stream"; // Default MIME type
|
|
|
|
|
string extension = Path.GetExtension(filePath).ToLowerInvariant();
|
|
|
|
|
|
|
|
|
|
// MIME type lookup based on file extension
|
|
|
|
|
switch (extension)
|
|
|
|
|
{
|
|
|
|
|
case ".html":
|
|
|
|
|
case ".htm":
|
|
|
|
|
mimeType = "text/html";
|
|
|
|
|
break;
|
|
|
|
|
case ".css":
|
|
|
|
|
mimeType = "text/css";
|
|
|
|
|
break;
|
|
|
|
|
case ".js":
|
|
|
|
|
mimeType = "application/javascript";
|
|
|
|
|
break;
|
|
|
|
|
case ".png":
|
|
|
|
|
mimeType = "image/png";
|
|
|
|
|
break;
|
|
|
|
|
case ".jpg":
|
|
|
|
|
case ".jpeg":
|
|
|
|
|
mimeType = "image/jpeg";
|
|
|
|
|
break;
|
|
|
|
|
case ".gif":
|
|
|
|
|
mimeType = "image/gif";
|
|
|
|
|
break;
|
|
|
|
|
case ".svg":
|
|
|
|
|
mimeType = "image/svg+xml";
|
|
|
|
|
break;
|
|
|
|
|
case ".json":
|
|
|
|
|
mimeType = "application/json";
|
|
|
|
|
break;
|
|
|
|
|
// Add more cases for other file types as needed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mimeType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetLocalIPAddress()
|
|
|
|
|
{
|
|
|
|
|
var host = Dns.GetHostEntry(Dns.GetHostName());
|
|
|
|
|
foreach (var ip in host.AddressList)
|
|
|
|
|
{
|
|
|
|
|
if (ip.AddressFamily == AddressFamily.InterNetwork)
|
|
|
|
|
{
|
|
|
|
|
return ip.ToString(); // 返回找到的 IPv4 地址
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw new Exception("No network adapters with an IPv4 address in the system!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 启动服务器的公共方法
|
|
|
|
|
private static async Task HandleFavoriteRequest(HttpListenerContext context)
|
|
|
|
|
{
|
2025-05-02 10:29:50 +08:00
|
|
|
|
try
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-05-02 10:29:50 +08:00
|
|
|
|
if (PrimaryForm.isPhoneNumberValid && !string.IsNullOrEmpty(PrimaryForm.Instance.userPhone))
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-05-02 10:29:50 +08:00
|
|
|
|
//string phone = PrimaryForm.phonenumber; // 直接通过类名访问
|
2025-04-07 16:54:10 +08:00
|
|
|
|
// 登录用户
|
2025-05-02 10:29:50 +08:00
|
|
|
|
//SongListManager.Instance.UserLogin(phone);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
|
|
|
|
// 获取用户的收藏歌曲
|
2025-05-02 10:29:50 +08:00
|
|
|
|
var favoriteSongs=new List<SongData>(); //= SongListManager.Instance.GetFavoriteSongsByPhoneNumber();
|
|
|
|
|
|
|
|
|
|
string query =PrimaryForm.Instance.SearchFavoriteSongs_Mysql();
|
|
|
|
|
var searchResults = PrimaryForm.Instance.SearchSongs_Mysql(query);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
// 创建响应数据
|
|
|
|
|
var response = new
|
|
|
|
|
{
|
|
|
|
|
isLoggedIn = true,
|
2025-05-02 10:29:50 +08:00
|
|
|
|
|
|
|
|
|
favoriteSongList = searchResults
|
2025-04-07 16:54:10 +08:00
|
|
|
|
.Select(song => new
|
|
|
|
|
{
|
|
|
|
|
song.Song,
|
|
|
|
|
song.ArtistA,
|
|
|
|
|
song.SongNumber,
|
2025-05-02 10:29:50 +08:00
|
|
|
|
//song.Category,
|
|
|
|
|
//song.PhoneticNotation,
|
|
|
|
|
//song.PinyinNotation,
|
|
|
|
|
//song.ArtistAPhonetic,
|
|
|
|
|
//song.ArtistBPhonetic,
|
2025-04-07 16:54:10 +08:00
|
|
|
|
song.ArtistASimplified,
|
|
|
|
|
song.ArtistBSimplified,
|
|
|
|
|
song.SongSimplified,
|
2025-05-02 10:29:50 +08:00
|
|
|
|
//song.SongGenre,
|
|
|
|
|
//song.ArtistAPinyin,
|
|
|
|
|
//song.ArtistBPinyin,
|
2025-04-07 16:54:10 +08:00
|
|
|
|
song.HumanVoice,
|
2025-05-02 10:29:50 +08:00
|
|
|
|
//song.AddedTime,
|
2025-04-07 16:54:10 +08:00
|
|
|
|
song.SongFilePathHost1,
|
|
|
|
|
song.SongFilePathHost2 // 例如语言类别等信息
|
|
|
|
|
})
|
|
|
|
|
.ToList(),
|
|
|
|
|
status = "success",
|
|
|
|
|
message = "Favorites retrieved successfully"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
Console.WriteLine("Serialized JSON Response: " + jsonResponse);
|
|
|
|
|
|
|
|
|
|
// 返回响应
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
|
|
|
await SendResponseAsyncs(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 如果手机号无效,返回未登录的状态
|
|
|
|
|
var response = new
|
|
|
|
|
{
|
|
|
|
|
isLoggedIn = false,
|
|
|
|
|
status = "error",
|
|
|
|
|
message = "Invalid mobile number or user not logged in"
|
|
|
|
|
};
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(response);
|
|
|
|
|
Console.WriteLine("Sending not logged in response: " + jsonResponse);
|
|
|
|
|
|
|
|
|
|
// 返回错误响应
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
|
|
|
|
await SendResponseAsyncs(context, jsonResponse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error handling favorite song request: {ex.Message}");
|
|
|
|
|
|
|
|
|
|
// 增强异常信息,返回详细的错误
|
|
|
|
|
var errorResponse = new
|
|
|
|
|
{
|
|
|
|
|
status = "error",
|
|
|
|
|
message = "An error occurred while processing your request",
|
|
|
|
|
errorDetails = ex.Message // 可以把错误信息传递给前端,帮助调试
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
string jsonResponse = JsonConvert.SerializeObject(errorResponse);
|
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
|
|
|
await SendResponseAsyncs(context, jsonResponse);
|
2025-05-02 10:29:50 +08:00
|
|
|
|
}
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 异步响应发送方法
|
2025-05-02 10:29:50 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
private static async Task SendResponseAsyncs(HttpListenerContext context, string responseContent)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (var writer = new StreamWriter(context.Response.OutputStream))
|
|
|
|
|
{
|
|
|
|
|
await writer.WriteAsync(responseContent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error sending response: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-02 10:29:50 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
private static async Task HandleGetRequest(HttpListenerContext context, string baseDirectory, string requestedFile)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 获取请求的完整URL路径
|
|
|
|
|
string requestPath = context.Request.Url.AbsolutePath;
|
|
|
|
|
|
|
|
|
|
// 检查URL是否包含随机文件夹名
|
|
|
|
|
if (!requestPath.Contains("/" + randomFolderPath + "/"))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Access denied: Request path {requestPath} does not contain valid random folder");
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string filePath = Path.Combine(baseDirectory, requestedFile.TrimStart('/'));
|
|
|
|
|
|
|
|
|
|
if (!File.Exists(filePath))
|
|
|
|
|
{
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string contentType = GetMimeType(filePath);
|
|
|
|
|
context.Response.ContentType = contentType;
|
|
|
|
|
|
|
|
|
|
// 对于静态文件使用缓存
|
|
|
|
|
if (_fileCache.TryGetValue(filePath, out byte[] cachedContent))
|
|
|
|
|
{
|
|
|
|
|
context.Response.ContentLength64 = cachedContent.Length;
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(cachedContent, 0, cachedContent.Length);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 读取文件内容
|
|
|
|
|
byte[] buffer;
|
|
|
|
|
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
buffer = new byte[fileStream.Length];
|
|
|
|
|
await fileStream.ReadAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 缓存静态文件
|
|
|
|
|
if (contentType.StartsWith("text/") || contentType.Contains("javascript") || contentType.Contains("json"))
|
|
|
|
|
{
|
|
|
|
|
_fileCache.TryAdd(filePath, buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context.Response.ContentLength64 = buffer.Length;
|
|
|
|
|
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Error serving file: {ex.Message}");
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|