diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bda8fc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +DualScreenSetup.exe +Superstar.mdf +Superstar_log.ldf +.vs +build.bat +*.exe +bin/* +obj/* +secondary_graph.grf +primary_graph.grf diff --git a/ClickSequenceState.cs b/ClickSequenceState.cs new file mode 100644 index 0000000..212e055 --- /dev/null +++ b/ClickSequenceState.cs @@ -0,0 +1,12 @@ +namespace DualScreenDemo +{ + + public enum ClickSequenceState + { + Initial, + FirstClicked, + SecondClicked, + ThirdClicked, + Completed + } +} \ No newline at end of file diff --git a/Clsid.cs b/Clsid.cs new file mode 100644 index 0000000..5b8be7e --- /dev/null +++ b/Clsid.cs @@ -0,0 +1,13 @@ +using System; + +namespace DualScreenDemo +{ + public static class Clsid + { + public static readonly Guid LAVSplitter = new Guid("171252A0-8820-4AFE-9DF8-5C92B2D66B04"); + public static readonly Guid LAVVideoDecoder = new Guid("EE30215D-164F-4A92-A4EB-9D4C13390F9F"); + public static readonly Guid LAVAudioDecoder = new Guid("E8E73B6B-4CB3-44A4-BE99-4F7BCB96E491"); + public static readonly Guid VideoRenderer = new Guid("B87BEB7B-8D29-423F-AE4D-6582C10175AC"); + public static readonly Guid AudioSwitcher = new Guid("1367C6F9-3FDD-42E9-AE3A-BC57F4C40D6D"); + } +} \ No newline at end of file diff --git a/ComInterop.cs b/ComInterop.cs new file mode 100644 index 0000000..10263ad --- /dev/null +++ b/ComInterop.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.InteropServices; + +namespace DualScreenDemo +{ + public class ComInterop + { + [DllImport("ole32.dll")] + public static extern int CoInitializeEx(IntPtr pvReserved, int dwCoInit); + + public const int COINIT_APARTMENTTHREADED = 0x2; + public const int COINIT_MULTITHREADED = 0x0; + } +} \ No newline at end of file diff --git a/CommandHandler.cs b/CommandHandler.cs new file mode 100644 index 0000000..3d23819 --- /dev/null +++ b/CommandHandler.cs @@ -0,0 +1,931 @@ +using System; +using System.Linq; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Collections.Generic; +using System.Diagnostics; +using DBObj; +using OverlayFormObj; +namespace DualScreenDemo +{ + public class CommandHandler + { + public static bool readyForSongListInput = false; + + private readonly SongListManager songListManager; + + public CommandHandler(SongListManager songListManager) + { + this.songListManager = songListManager; + } + + public async Task ProcessData(string indata) + { + string filePath = Path.Combine(Application.StartupPath, "dataLog.txt"); + if (CheckLogForShutdown(filePath)) + { + Console.WriteLine("Shutdown condition met. Application will now close."); + ShutdownComputer(); + } + + switch (indata) + { + case "A261A4": + HandleInputA(); + break; + case "A262A4": + HandleInputB(); + break; + case "A263A4": + ClearDisplay(); + break; + case "A268A4": + OverlayForm.MainForm.currentPage = 1; + DisplaySongHistory(); + break; + case "A26AA4": + PreviousPage(); + break; + case "A26BA4": + NextPage(); + break; + case "A271A4": + HandleNewSongAnnouncements(); + break; + case "A273A4": + HandleHotSongAnnouncements(); + break; + case "A267A4": + SkipToNextSong(); + ClearDisplay(); + break; + case "A269A4": + ReplayCurrentSong(); + break; + // 原唱 + case "A26CA4": + Console.WriteLine("ToggleVocalRemoval Invoked"); + InvokeAction(() => VideoPlayerForm.Instance.ToggleVocalRemoval()); + InvokeAction(() => OverlayForm.MainForm.ShowOriginalSongLabel()); + break; + // 導唱 + case "A26EA4": + InvokeAction(() => VideoPlayerForm.Instance.ToggleVocalRemoval()); + break; + case "A26DA4": + PauseOrResumeSong(); + break; + case "A276A4": + ToggleMute(); + break; + case "A274A4": + HandleArtistAnnouncements(); + break; + case "A2B3A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowVolumeUpLabel()); + break; + case "A2B4A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowVolumeDownLabel()); + break; + case "A2B5A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowMicUpLabel()); + break; + case "A2B6A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowMicDownLabel()); + break; + case "A2C2A4": + InvokeAction(() => OverlayForm.MainForm.HidemicLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowStandardLabel()); + break; + case "A2C3A4": + InvokeAction(() => OverlayForm.MainForm.HidemicLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowProfessionalLabel()); + break; + case "A2C4A4": + InvokeAction(() => OverlayForm.MainForm.HidemicLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowSquareLabel()); + break; + case "A2C1A4": + InvokeAction(() => OverlayForm.MainForm.HidemicLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowSingDownLabel()); + break; + case "A2D5A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowBrightLabel()); + break; + case "A2D7A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowRomanticLabel()); + break; + /* case "A27CA4": + InvokeAction(() => OverlayForm.MainForm.ShowMaleKeyLabel()); + break; + case "A282A4": + InvokeAction(() => OverlayForm.MainForm.ShowFemaleKeyLabel()); + break;*/ + case "A2D6A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowSoftLabel()); + break; + case "A2D8A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowDynamicLabel()); + break; + case "A275A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowTintLabel()); + break; + case "A283A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升4調")); + break; + case "A282A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升3調")); + break; + case "A281A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升2調")); + break; + case "A280A4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升1調")); + break; + case "A27FA4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowStandardKeyLabel()); + break; + case "A27EA4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降1調")); + break; + case "A27DA4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降2調")); + break; + case "A27CA4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降3調")); + break; + case "A27BA4": + InvokeAction(() => OverlayForm.MainForm.HideAllLabels()); + InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調")); + break; + default: + if (Regex.IsMatch(indata, @"^A23\d+A4$")) + { + HandleNumberInput(indata); + } + break; + } + } + + void InvokeAction(Action action) + { + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(action); + } + else + { + action(); + } + } + + + + private static void SkipToNextSong() + { + if (PrimaryForm.Instance.InvokeRequired) + { + PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.SkipToNextSong())); + } + else + { + PrimaryForm.Instance.videoPlayerForm.SkipToNextSong(); + } + + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.ShowStandardLabel(); + })); + } + + + private static void ReplayCurrentSong() + { + if (PrimaryForm.Instance.InvokeRequired) + { + PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong())); + } + else + { + PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong(); + } + } + + private static void PauseOrResumeSong() + { + if (PrimaryForm.Instance.InvokeRequired) + { + PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.PauseOrResumeSong())); + } + else + { + PrimaryForm.Instance.videoPlayerForm.PauseOrResumeSong(); + } + } + + public static void ToggleMute() + { + if (VideoPlayerForm.Instance.InvokeRequired) + { + VideoPlayerForm.Instance.Invoke(new System.Action(ToggleMute)); + } + else + { + if (VideoPlayerForm.Instance.isMuted) + { + + VideoPlayerForm.Instance.SetVolume(VideoPlayerForm.Instance.previousVolume); + + VideoPlayerForm.Instance.isMuted = false; + OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.HideMuteLabel())); + } + else + { + + VideoPlayerForm.Instance.previousVolume = VideoPlayerForm.Instance.GetVolume(); + VideoPlayerForm.Instance.SetVolume(-10000); + + VideoPlayerForm.Instance.isMuted = true; + OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.ShowMuteLabel())); + } + } + } + + + private void HandleInputA() + { + + + OverlayForm.displayTimer.Stop(); + string input = "a"; + + + string songNumber = OverlayForm.ReadSongNumber(); + var song = songListManager.SearchSongByNumber(songNumber); + + + + if (readyForSongListInput) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.OnUserInput(input); + })); + } + else + { + OverlayForm.MainForm.OnUserInput(input); + } + } + else + { + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + if (song != null) + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song); + OverlayForm.MainForm.AddSongToPlaylist(song); + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + else + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!"; + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + })); + } + else + { + if (song != null) + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = String.Format("{0}", song); + OverlayForm.MainForm.AddSongToPlaylist(song); + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + else + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!"; + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + } + } + } + + + private void HandleInputB() + { + + OverlayForm.displayTimer.Stop(); + string input = "b"; + + + string songNumber = OverlayForm.ReadSongNumber(); + var song = songListManager.SearchSongByNumber(songNumber); + + + + if (readyForSongListInput) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.OnUserInput(input); + })); + } + else + { + OverlayForm.MainForm.OnUserInput(input); + } + } + else + { + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + if (song != null) + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = String.Format("插播歌曲{0}", song); + OverlayForm.MainForm.InsertSongToPlaylist(song); + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + else + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!"; + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + })); + } + else + { + if (song != null) + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song); + OverlayForm.MainForm.nextSongLabel.Visible = false; + } + else + { + ClearDisplay(); + OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!"; + OverlayForm.MainForm.nextSongLabel.Visible = false; + OverlayForm.displayTimer.Start(); + } + } + } + } + + private static void ClearDisplay() + { + + OverlayForm.displayTimer.Stop(); + + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + foreach (var control in OverlayForm.MainForm.Controls.OfType().ToArray()) + { + if (control != OverlayForm.MainForm.displayLabel && + control != OverlayForm.MainForm.pauseLabel && + control != OverlayForm.MainForm.muteLabel && + control != OverlayForm.MainForm.volumeUpLabel && + control != OverlayForm.MainForm.volumeDownLabel && + control != OverlayForm.MainForm.micUpLabel && + control != OverlayForm.MainForm.micDownLabel && + control != OverlayForm.MainForm.standardKeyLabel && + control != OverlayForm.MainForm.keyUpLabel && + control != OverlayForm.MainForm.keyDownLabel && + control != OverlayForm.MainForm.maleKeyLabel && + control != OverlayForm.MainForm.femaleKeyLabel && + control != OverlayForm.MainForm.squareLabel && + control != OverlayForm.MainForm.professionalLabel && + control != OverlayForm.MainForm.standardLabel && + control != OverlayForm.MainForm.singDownLabel && + control != OverlayForm.MainForm.brightLabel && + control != OverlayForm.MainForm.softLabel && + control != OverlayForm.MainForm.autoLabel && + control != OverlayForm.MainForm.romanticLabel && + control != OverlayForm.MainForm.dynamicLabel && + control != OverlayForm.MainForm.tintLabel && + control != OverlayForm.MainForm.blackBackgroundPanel && + control != OverlayForm.MainForm.nextSongLabel) + { + OverlayForm.MainForm.Controls.Remove(control); + control.Dispose(); + } + } + + OverlayForm.MainForm.displayLabel.Text = ""; + readyForSongListInput = false; + OverlayForm.SetUIState(OverlayForm.UIState.Initial); + Console.WriteLine(OverlayForm.MainForm.displayLabel.Text); + })); + } + else + { + + foreach (var control in OverlayForm.MainForm.Controls.OfType().ToArray()) + { + if (control != OverlayForm.MainForm.displayLabel && + control != OverlayForm.MainForm.pauseLabel && + control != OverlayForm.MainForm.muteLabel && + control != OverlayForm.MainForm.volumeUpLabel && + control != OverlayForm.MainForm.volumeDownLabel && + control != OverlayForm.MainForm.micUpLabel && + control != OverlayForm.MainForm.micDownLabel && + control != OverlayForm.MainForm.standardKeyLabel && + control != OverlayForm.MainForm.keyUpLabel && + control != OverlayForm.MainForm.keyDownLabel && + control != OverlayForm.MainForm.maleKeyLabel && + control != OverlayForm.MainForm.femaleKeyLabel && + control != OverlayForm.MainForm.squareLabel && + control != OverlayForm.MainForm.professionalLabel && + control != OverlayForm.MainForm.standardLabel && + control != OverlayForm.MainForm.singDownLabel && + control != OverlayForm.MainForm.brightLabel && + control != OverlayForm.MainForm.softLabel && + control != OverlayForm.MainForm.autoLabel && + control != OverlayForm.MainForm.romanticLabel && + control != OverlayForm.MainForm.dynamicLabel && + control != OverlayForm.MainForm.tintLabel && + control != OverlayForm.MainForm.blackBackgroundPanel && + control != OverlayForm.MainForm.nextSongLabel) + { + OverlayForm.MainForm.Controls.Remove(control); + control.Dispose(); + } + } + + OverlayForm.MainForm.displayLabel.Text = ""; + Console.WriteLine("ClearDisplay called."); + readyForSongListInput = false; + OverlayForm.SetUIState(OverlayForm.UIState.Initial); + Console.WriteLine(OverlayForm.MainForm.displayLabel.Text); + } + } +private static void DisplaySongHistory() +{ + ClearDisplay(); + + // 設定總歌曲數量 + OverlayForm.MainForm.totalSongs = PrimaryForm.playedSongsHistory.Count; + + // 如果播放歷史為空 + if (OverlayForm.MainForm.totalSongs == 0) + { + Console.WriteLine("No song history available."); + return; + } + + // 計算總頁數 + int totalPages = (int)Math.Ceiling(OverlayForm.MainForm.totalSongs / (double)OverlayForm.MainForm.songsPerPage); + int startIndex = (OverlayForm.MainForm.currentPage - 1) * OverlayForm.MainForm.songsPerPage; + int endIndex = Math.Min(startIndex + OverlayForm.MainForm.songsPerPage, OverlayForm.MainForm.totalSongs); + + // 準備傳遞給 UpdateHistoryLabel 的數據 + List historySongs = new List(); + List playStates = new List(); + + for (int i = startIndex; i < endIndex; i++) + { + historySongs.Add(PrimaryForm.playedSongsHistory[i]); + playStates.Add(PrimaryForm.playStates[i]); + } + + // 調用 UpdateHistoryLabel 顯示數據 + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages); + OverlayForm.MainForm.nextSongLabel.Visible = false; + })); + } + else + { + OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages); + OverlayForm.MainForm.nextSongLabel.Visible = false; + } + + // 設定 UI 狀態 + OverlayForm.SetUIState(OverlayForm.UIState.PlayHistory); +} + + + + private static void PreviousPage() + { + if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingSong) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + OverlayForm.MainForm.PreviousPage(); + })); + } + else + { + + OverlayForm.MainForm.PreviousPage(); + } + } + else if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingArtist) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + OverlayForm.MainForm.PreviousPage(); + })); + } + else + { + + OverlayForm.MainForm.PreviousPage(); + } + } + else if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory) + { + if (OverlayForm.MainForm.currentPage > 1) + { + OverlayForm.MainForm.currentPage--; + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + DisplaySongHistory(); + })); + } + else + { + DisplaySongHistory(); + } + } + } + else + { + Console.WriteLine("Page turning is not allowed in the current state."); + } + } + + private static void NextPage() + { + if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingSong) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + OverlayForm.MainForm.NextPage(); + })); + } + else + { + + OverlayForm.MainForm.NextPage(); + } + } + else if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingArtist) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + OverlayForm.MainForm.NextPage(); + })); + } + else + { + + OverlayForm.MainForm.NextPage(); + } + } + else if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory) + { + if (OverlayForm.MainForm.currentPage * OverlayForm.MainForm.songsPerPage < OverlayForm.MainForm.totalSongs) + { + OverlayForm.MainForm.currentPage++; + if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory) + { + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + DisplaySongHistory(); + })); + } + else + { + DisplaySongHistory(); + } + } + } + } + else + { + Console.WriteLine("Page turning is not allowed in the current state."); + } + } + + private static void HandleNewSongAnnouncements() + { + ClearDisplay(); + + + OverlayForm.CurrentCategory = OverlayForm.Category.NewSongs; + + + string[] messages = new string[] + { + "新歌快訊", + "1. 國語", + "2. 台語", + "3. 粵語", + "4. 英語", + "5. 日語", + "6. 韓語", + }; + + + readyForSongListInput = true; + + + OverlayForm.SetUIState(OverlayForm.UIState.SelectingLanguage); + + + UpdateDisplayLabels(messages); + } + + private static void HandleHotSongAnnouncements() + { + ClearDisplay(); + + + OverlayForm.CurrentCategory = OverlayForm.Category.HotSongs; + + + string[] messages = new string[] + { + "熱門排行", + "1. 國語", + "2. 台語", + "3. 粵語", + "4. 英語", + "5. 日語", + "6. 韓語", + }; + + + readyForSongListInput = true; + + + OverlayForm.SetUIState(OverlayForm.UIState.SelectingLanguage); + + + UpdateDisplayLabels(messages); + } + + private static void HandleArtistAnnouncements() + { + ClearDisplay(); + + + OverlayForm.CurrentCategory = OverlayForm.Category.Artists; + + + string[] messages = new string[] + { + "歌星選歌", + "1.男歌星", + "2.女歌星", + "3.團體", + "4.外語", + "5.全部", + }; + + + readyForSongListInput = true; + + + OverlayForm.SetUIState(OverlayForm.UIState.SelectingArtistCategory); + + + UpdateDisplayLabels(messages); + } + + private static void UpdateDisplayLabels(string[] messages) + { + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + + foreach (var control in OverlayForm.MainForm.Controls.OfType().ToArray()) + { + if (control != OverlayForm.MainForm.displayLabel && + control != OverlayForm.MainForm.pauseLabel && + control != OverlayForm.MainForm.muteLabel && + control != OverlayForm.MainForm.volumeUpLabel && + control != OverlayForm.MainForm.volumeDownLabel && + control != OverlayForm.MainForm.micUpLabel && + control != OverlayForm.MainForm.micDownLabel && + control != OverlayForm.MainForm.standardKeyLabel && + control != OverlayForm.MainForm.keyUpLabel && + control != OverlayForm.MainForm.keyDownLabel && + control != OverlayForm.MainForm.maleKeyLabel && + control != OverlayForm.MainForm.femaleKeyLabel && + control != OverlayForm.MainForm.squareLabel && + control != OverlayForm.MainForm.professionalLabel && + control != OverlayForm.MainForm.standardLabel && + control != OverlayForm.MainForm.singDownLabel && + control != OverlayForm.MainForm.brightLabel && + control != OverlayForm.MainForm.softLabel && + control != OverlayForm.MainForm.autoLabel && + control != OverlayForm.MainForm.romanticLabel && + control != OverlayForm.MainForm.dynamicLabel && + control != OverlayForm.MainForm.tintLabel && + control != OverlayForm.MainForm.blackBackgroundPanel && + control != OverlayForm.MainForm.nextSongLabel) + { + OverlayForm.MainForm.Controls.Remove(control); + control.Dispose(); + } + } + OverlayForm.MainForm.UpdateDisplayLabels(messages); + })); + } + else + { + + foreach (var control in OverlayForm.MainForm.Controls.OfType().ToArray()) + { + if (control != OverlayForm.MainForm.displayLabel && + control != OverlayForm.MainForm.pauseLabel && + control != OverlayForm.MainForm.muteLabel && + control != OverlayForm.MainForm.volumeUpLabel && + control != OverlayForm.MainForm.volumeDownLabel && + control != OverlayForm.MainForm.micUpLabel && + control != OverlayForm.MainForm.micDownLabel && + control != OverlayForm.MainForm.standardKeyLabel && + control != OverlayForm.MainForm.keyUpLabel && + control != OverlayForm.MainForm.keyDownLabel && + control != OverlayForm.MainForm.maleKeyLabel && + control != OverlayForm.MainForm.femaleKeyLabel && + control != OverlayForm.MainForm.squareLabel && + control != OverlayForm.MainForm.professionalLabel && + control != OverlayForm.MainForm.standardLabel && + control != OverlayForm.MainForm.singDownLabel && + control != OverlayForm.MainForm.brightLabel && + control != OverlayForm.MainForm.softLabel && + control != OverlayForm.MainForm.autoLabel && + control != OverlayForm.MainForm.romanticLabel && + control != OverlayForm.MainForm.dynamicLabel && + control != OverlayForm.MainForm.tintLabel && + control != OverlayForm.MainForm.blackBackgroundPanel && + control != OverlayForm.MainForm.nextSongLabel) + { + OverlayForm.MainForm.Controls.Remove(control); + control.Dispose(); + } + } + OverlayForm.MainForm.UpdateDisplayLabels(messages); + } + } + + private static void HandleNumberInput(string trimmedData) + { + string number = trimmedData; + + + var match = Regex.Match(trimmedData, @"^A23(\d)A4$"); + if (match.Success) + { + number = match.Groups[1].Value; + + Console.WriteLine($"Handling number: {number}"); + + } + + + if (readyForSongListInput) + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.MainForm.OnUserInput(number); + })); + } + else + { + OverlayForm.MainForm.OnUserInput(number); + } + + } + else + { + + if (OverlayForm.MainForm.InvokeRequired) + { + OverlayForm.MainForm.Invoke(new System.Action(() => + { + OverlayForm.DisplayNumberAtTopLeft(number); + OverlayForm.MainForm.HideAllLabels(); + })); + } + else + { + OverlayForm.DisplayNumberAtTopLeft(number); + OverlayForm.MainForm.HideAllLabels(); + } + } + } + + public static bool CheckLogForShutdown(string filePath) + { + if (File.Exists(filePath)) + { + + string content = File.ReadAllText(filePath).Replace(Environment.NewLine, ""); + if (content.Length >= 6 && content.Substring(content.Length - 6) == "bbbaaa") + { + return true; + } + } + return false; + } + + public static void ShutdownComputer() + { + try + { + ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/c shutdown /s /f /t 0") + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true + }; + Process process = Process.Start(processStartInfo); + process.WaitForExit(); + Console.WriteLine("Computer is shutting down..."); + } + catch (Exception ex) + { + Console.WriteLine("Error shutting down computer: " + ex.Message); + } + } + } +} \ No newline at end of file diff --git a/DBObj/Artist.cs b/DBObj/Artist.cs new file mode 100644 index 0000000..670e708 --- /dev/null +++ b/DBObj/Artist.cs @@ -0,0 +1,33 @@ +namespace DBObj +{ + // artist OOP test + public class Artist + { + + public string Name { get; set; } + + + public string Phonetic { get; set; } + + + public string Category { get; set; } + + + public int Strokes { get; set; } + + + public Artist(string name, string phonetic, string category, int strokes) + { + Name = name; + Phonetic = phonetic; + Category = category; + Strokes = strokes; + } + + + public override string ToString() + { + return $"Name: {Name}, Phonetic: {Phonetic}, Category: {Category}, Strokes: {Strokes}"; + } + } +} \ No newline at end of file diff --git a/DBObj/ArtistManager.cs b/DBObj/ArtistManager.cs new file mode 100644 index 0000000..f88d084 --- /dev/null +++ b/DBObj/ArtistManager.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using DualScreenDemo; +namespace DBObj +{ + /** + 從資料庫取資料回來 + */ + public class ArtistManager + { + private static ArtistManager _instance; + public List AllArtists { get; private set; } + + public static ArtistManager Instance + { + get + { + if (_instance == null) + { + _instance = new ArtistManager(); + } + return _instance; + } + } + + public ArtistManager() + { + AllArtists = new List(); + LoadArtists(); + + } + + private void LoadArtists() + { + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + + Console.WriteLine(databasePath); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + string sql = "SELECT 歌手姓名, 歌手注音, 歌手分類, 歌手筆畫 FROM ArtistLibrary"; + using (var command = new SQLiteCommand(sql, connection)) + { + using (SQLiteDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + string artist = reader["歌手姓名"].ToString(); + string phonetic = reader["歌手注音"].ToString(); + string category = reader["歌手分類"].ToString(); + string strokesStr = reader["歌手筆畫"].ToString(); + + + if (string.IsNullOrEmpty(strokesStr)) + { + // Console.WriteLine("歌手筆畫的值為空或無效"); + } + + if (double.TryParse(strokesStr, out double strokesDouble)) + { + int strokes = (int)Math.Round(strokesDouble); + AllArtists.Add(new Artist(artist, phonetic, category, strokes)); + + } + else + { + // Console.WriteLine($"Failed to parse '歌手筆畫' value: {strokesStr}"); + } + } + } + } + + connection.Close(); + + + + } + catch (Exception ex) + { + Console.WriteLine("Failed to load artists from SQLite database: " + ex.Message); + } + } + } + + private void PrintAllArtists() + { + Console.WriteLine("All Artists:"); + foreach (var artist in AllArtists) + { + Console.WriteLine(artist.ToString()); + } + } + + public List GetArtistsByCategoryAndStrokeCountRange(string category, int minStrokes, int maxStrokes) + { + if (category == "全部") + { + return AllArtists.Where(artist => artist.Strokes >= minStrokes && artist.Strokes <= maxStrokes).ToList(); + } + else + { + return AllArtists.Where(artist => artist.Category == category && artist.Strokes >= minStrokes && artist.Strokes <= maxStrokes).ToList(); + } + } + } +} \ No newline at end of file diff --git a/DBObj/SongData.cs b/DBObj/SongData.cs new file mode 100644 index 0000000..21c7529 --- /dev/null +++ b/DBObj/SongData.cs @@ -0,0 +1,65 @@ +using System; + +namespace DBObj +{ + public class SongData + { + public string SongNumber { get; set; } + public string Category { get; set; } + public string Song { get; set; } + public double Plays { get; set; } + public string ArtistA { get; set; } + public string ArtistB { get; set; } + public string ArtistACategory { get; set; } + public string ArtistBCategory { get; set; } + public DateTime AddedTime { get; set; } + public string SongFilePathHost1 { get; set; } + public string SongFilePathHost2 { get; set; } + public string PhoneticNotation { get; set; } + public string PinyinNotation { get; set; } + public string ArtistAPhonetic { get; set; } + public string ArtistBPhonetic { get; set; } + public string ArtistASimplified { get; set; } + public string ArtistBSimplified { get; set; } + public string SongSimplified { get; set; } + public string SongGenre { get; set; } + public string ArtistAPinyin { get; set; } + public string ArtistBPinyin { get; set; } + public int HumanVoice { get; set; } + + + public SongData(string songNumber, string category, string song, double plays, string artistA, string artistB, string artistACategory, string artistBCategory, DateTime addedTime, string songFilePathHost1, string songFilePathHost2, string phoneticNotation, string pinyinNotation, string artistAPhonetic, string artistBPhonetic, string artistASimplified, string artistBSimplified, string songSimplified, string songGenre, string artistAPinyin, string artistBPinyin, int humanVoice) + { + SongNumber = songNumber; + Category = category; + Song = song; + Plays = plays; + ArtistA = artistA; + ArtistB = artistB; + ArtistACategory = artistACategory; + ArtistBCategory = artistBCategory; + AddedTime = addedTime; + SongFilePathHost1 = songFilePathHost1; + SongFilePathHost2 = songFilePathHost2; + PhoneticNotation = phoneticNotation; + PinyinNotation = pinyinNotation; + ArtistAPhonetic = artistAPhonetic; + ArtistBPhonetic = artistBPhonetic; + ArtistASimplified = artistASimplified; + ArtistBSimplified = artistBSimplified; + SongSimplified = songSimplified; + SongGenre = songGenre; + ArtistAPinyin = artistAPinyin; + ArtistBPinyin = artistBPinyin; + HumanVoice = humanVoice; + } + + public override string ToString() + { + + return !string.IsNullOrWhiteSpace(ArtistB) + ? String.Format("{0} - {1} - {2}", ArtistA, ArtistB, Song) + : String.Format("{0} - {1}", ArtistA, Song); + } + } +} \ No newline at end of file diff --git a/DBObj/SongListManager.cs b/DBObj/SongListManager.cs new file mode 100644 index 0000000..ca8b105 --- /dev/null +++ b/DBObj/SongListManager.cs @@ -0,0 +1,662 @@ +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using System.Globalization; +using System.Diagnostics; +using DualScreenDemo; +namespace DBObj +{ + public class SongListManager + { + private static SongListManager _instance; + public List AllSongs { get; private set; } + public static Dictionary> NewSongLists { get; private set; } + public static Dictionary> HotSongLists { get; private set; } + public List FavoriteSongs { get; private set; } + public const int SongsPerPage = 9; + + public bool IsUserLoggedIn { get; set; } + public string UserPhoneNumber { get; set; } + + public SongListManager() + { + AllSongs = new List(); + NewSongLists = new Dictionary>(); + HotSongLists = new Dictionary>(); + FavoriteSongs = new List(); + + // 尝试更新数据库,但无论结果如何都继续运行 + TryUpdateDatabase(); + + // 继续使用可用的数据库(可能是更新后的或原本的本地数据库) + InitializeDatabase(); + LoadSongs(); + InitializeNewSongLists(); + InitializeHotSongLists(); + } + + + private bool TryUpdateDatabase() + { + try + { + // 1. 检查是否能连接到 SVR01 + if (!Directory.Exists(@"\\SVR01\SuperstarB")) + { + Console.WriteLine("未連接到SVR使用本地DB"); + return true; // 继续使用本地数据库 + } + + // 2. 比较本地和服务器文件 + string localDbPath = Path.Combine(Application.StartupPath, "KSongDatabase.db"); + string serverDbPath = @"\\SVR01\SuperstarB\KSongDatabase.db"; + + if (!File.Exists(localDbPath)) + { + Console.WriteLine("本地無db"); + } + else + { + FileInfo localFile = new FileInfo(localDbPath); + FileInfo serverFile = new FileInfo(serverDbPath); + + if (serverFile.LastWriteTime <= localFile.LastWriteTime) + { + Console.WriteLine("歌單已是最新"); + return true; + } + } + + // 3. 需要更新时,复制新文件 + Process copyProcess = new Process(); + copyProcess.StartInfo.FileName = "cmd.exe"; + copyProcess.StartInfo.Arguments = "/C copy /Y \\\\SVR01\\SuperstarB\\KSongDatabase.db KSongDatabase.db"; + copyProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + copyProcess.StartInfo.CreateNoWindow = true; + copyProcess.Start(); + copyProcess.WaitForExit(); + + if (copyProcess.ExitCode == 0) + { + Console.WriteLine("歌單更新成功"); + } + else + { + Console.WriteLine("歌單複製失敗,使用本地歌單"); + } + + return true; + } + catch (Exception ex) + { + Console.WriteLine($"更新歌單失敗:{ex.Message}"); + return true; // 出错时继续使用本地数据库 + } + } + + public static SongListManager Instance + { + get + { + if (_instance == null) + { + _instance = new SongListManager(); + } + return _instance; + } + } + + public List GetSongsByArtist(string artistName) + { + return AllSongs.Where(song => song.ArtistA == artistName || song.ArtistB == artistName).ToList(); + } + + + public bool CheckIfPhoneNumberExists(string phoneNumber) + { + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + string sql = "SELECT COUNT(1) FROM FavoriteSongs WHERE PhoneNumber = @PhoneNumber"; + using (var command = new SQLiteCommand(sql, connection)) + { + command.Parameters.AddWithValue("@PhoneNumber", phoneNumber); + int count = Convert.ToInt32(command.ExecuteScalar()); + return count > 0; + } + } + catch (Exception ex) + { + Console.WriteLine("Failed to check phone number in SQLite database: " + ex.Message); + return false; + } + finally + { + connection.Close(); + } + } + } + + + public void UserLogin(string phoneNumber) + { + IsUserLoggedIn = true; + UserPhoneNumber = phoneNumber; + LoadFavoriteSongs(); + Console.WriteLine(String.Format("UserLoggedIn: {0}, PhoneNumber: {1}", IsUserLoggedIn, UserPhoneNumber)); + } + + + public void UserLogout() + { + IsUserLoggedIn = false; + UserPhoneNumber = null; + FavoriteSongs.Clear(); + } + + public void InitializeDatabase() + { + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + string createTableSql = @" + CREATE TABLE IF NOT EXISTS FavoriteSongs ( + PhoneNumber TEXT NOT NULL, + SongNumber TEXT NOT NULL, + PRIMARY KEY (PhoneNumber, SongNumber) + );"; + using (var command = new SQLiteCommand(createTableSql, connection)) + { + command.ExecuteNonQuery(); + } + + connection.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Failed to initialize SQLite database: " + ex.Message); + } + } + } + + public void AddNewUser(string phoneNumber) + { + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + + string checkTableSql = "SELECT name FROM sqlite_master WHERE type='table' AND name='FavoriteSongs';"; + using (var checkCommand = new SQLiteCommand(checkTableSql, connection)) + { + var result = checkCommand.ExecuteScalar(); + if (result == null) + { + throw new Exception("Table 'FavoriteSongs' does not exist."); + } + } + + string sql = "INSERT INTO FavoriteSongs (PhoneNumber, SongNumber) VALUES (@PhoneNumber, @SongNumber)"; + using (var command = new SQLiteCommand(sql, connection)) + { + command.Parameters.AddWithValue("@PhoneNumber", phoneNumber); + command.Parameters.AddWithValue("@SongNumber", "000000"); + command.ExecuteNonQuery(); + } + connection.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Failed to add new user to SQLite database: " + ex.Message); + } + } + } + + private void LoadSongs() + { + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + + Console.WriteLine(databasePath); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + string sql = "SELECT 歌曲編號, 語別, 歌曲名稱, 點播次數, [歌星 A], [歌星 B], 新增日期, [路徑 1], [路徑 2], 歌曲檔名, 歌曲注音, 歌曲拼音, 歌星A分類, 歌星B分類, 歌星A注音, 歌星B注音, 歌星A簡體, 歌星B簡體, 歌名簡體, 分類, 歌星A拼音, 歌星B拼音, 人聲 FROM SongLibrary"; + using (var command = new SQLiteCommand(sql, connection)) + { + using (SQLiteDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + string songNumber = reader["歌曲編號"].ToString(); + string category = reader["語別"].ToString(); + string song = reader["歌曲名稱"].ToString(); + int plays = Convert.ToInt32(reader["點播次數"]); + string artistA = reader["歌星 A"].ToString(); + string artistB = reader["歌星 B"].ToString(); + string artistACategory = reader["歌星A分類"].ToString(); + string artistBCategory = reader["歌星B分類"].ToString(); + string dateValue = reader["新增日期"]?.ToString() ?? ""; + DateTime addedTime; + try + { + addedTime=DateTime.Parse(dateValue, CultureInfo.InvariantCulture).Date ; + } + catch (System.FormatException) + { + addedTime = DateTime.Today; + } + string basePathHost1 = reader["路徑 1"].ToString(); + string basePathHost2 = reader["路徑 2"].ToString(); + string fileName = reader["歌曲檔名"].ToString(); + string songFilePathHost1 = Path.Combine(basePathHost1, fileName); + string songFilePathHost2 = Path.Combine(basePathHost2, fileName); + string phoneticNotation = reader["歌曲注音"].ToString(); + string pinyinNotation = reader["歌曲拼音"].ToString(); + string artistAPhonetic = reader["歌星A注音"].ToString(); + string artistBPhonetic = reader["歌星B注音"].ToString(); + string artistASimplified = reader["歌星A簡體"].ToString(); + string artistBSimplified = reader["歌星B簡體"].ToString(); + string songSimplified = reader["歌名簡體"].ToString(); + string songGenre = reader["分類"].ToString(); + string artistAPinyin = reader["歌星A拼音"].ToString(); + string artistBPinyin = reader["歌星B拼音"].ToString(); + int humanVoice = Convert.ToInt32(reader["人聲"]); + + AllSongs.Add(new SongData(songNumber, category, song, plays, artistA, artistB, artistACategory, artistBCategory, addedTime, songFilePathHost1, songFilePathHost2, phoneticNotation, pinyinNotation, artistAPhonetic, artistBPhonetic, artistASimplified, artistBSimplified, songSimplified, songGenre, artistAPinyin, artistBPinyin, humanVoice)); + } + } + } + + connection.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Failed to load songs from SQLite database: " + ex.Message); + } + } + } + + public void LoadFavoriteSongs() + { + if (!IsUserLoggedIn || string.IsNullOrEmpty(UserPhoneNumber)) + return; + + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + + FavoriteSongs.Clear(); + FavoriteSongs.Add(new SongData("", "", UserPhoneNumber + " 的歌單", 0, "", "", "", "", DateTime.MinValue, "", "", "", "", "", "", "", "", "", "", "", "", 1)); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + string sql = @" + SELECT + sl.歌曲編號, sl.語別, sl.歌曲名稱, sl.點播次數, + sl.[歌星 A], sl.[歌星 B], sl.新增日期, sl.[路徑 1], + sl.[路徑 2], sl.歌曲檔名, sl.歌曲注音, sl.歌曲拼音, + sl.歌星A分類, sl.歌星B分類, sl.歌星A注音, sl.歌星B注音, + sl.歌星A簡體, sl.歌星B簡體, sl.歌名簡體, sl.分類, + sl.歌星A拼音, sl.歌星B拼音, sl.人聲 + FROM + FavoriteSongs fs + JOIN + SongLibrary sl + ON + fs.SongNumber = sl.歌曲編號 + WHERE + fs.PhoneNumber = @PhoneNumber"; + + using (var command = new SQLiteCommand(sql, connection)) + { + command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber); + + using (SQLiteDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + string songNumber = reader["歌曲編號"].ToString(); + string category = reader["語別"].ToString(); + string song = reader["歌曲名稱"].ToString(); + int plays = Convert.ToInt32(reader["點播次數"]); + string artistA = reader["歌星 A"].ToString(); + string artistB = reader["歌星 B"].ToString(); + string artistACategory = reader["歌星A分類"].ToString(); + string artistBCategory = reader["歌星B分類"].ToString(); + DateTime addedTime; + + try + { + addedTime = DateTime.ParseExact(reader["新增日期"].ToString(), "yyyy-MM-dd", CultureInfo.InvariantCulture); + } + catch (System.FormatException) + { + addedTime = DateTime.Today; + } + + string basePathHost1 = reader["路徑 1"].ToString(); + string basePathHost2 = reader["路徑 2"].ToString(); + string fileName = reader["歌曲檔名"].ToString(); + string songFilePathHost1 = Path.Combine(basePathHost1, fileName); + string songFilePathHost2 = Path.Combine(basePathHost2, fileName); + string phoneticNotation = reader["歌曲注音"].ToString(); + string pinyinNotation = reader["歌曲拼音"].ToString(); + string artistAPhonetic = reader["歌星A注音"].ToString(); + string artistBPhonetic = reader["歌星B注音"].ToString(); + string artistASimplified = reader["歌星A簡體"].ToString(); + string artistBSimplified = reader["歌星B簡體"].ToString(); + string songSimplified = reader["歌名簡體"].ToString(); + string songGenre = reader["分類"].ToString(); + string artistAPinyin = reader["歌星A拼音"].ToString(); + string artistBPinyin = reader["歌星B拼音"].ToString(); + int humanVoice = Convert.ToInt32(reader["人聲"]); + + FavoriteSongs.Add(new SongData( + songNumber, category, song, plays, artistA, artistB, + artistACategory, artistBCategory, addedTime, songFilePathHost1, + songFilePathHost2, phoneticNotation, pinyinNotation, + artistAPhonetic, artistBPhonetic, artistASimplified, + artistBSimplified, songSimplified, songGenre, + artistAPinyin, artistBPinyin, humanVoice)); + } + PrimaryForm.Instance.multiPagePanel.currentPageIndex = 0; + PrimaryForm.Instance.multiPagePanel.LoadSongs(FavoriteSongs); + } + } + + connection.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Failed to load favorite songs from SQLite database: " + ex.Message); + } + } + } + + public void AddToFavorite(string songNumber) + { + if (!IsUserLoggedIn || string.IsNullOrEmpty(UserPhoneNumber)) + { + Console.WriteLine("User is not logged in."); + return; + } + + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + + string checkSql = "SELECT COUNT(*) FROM FavoriteSongs WHERE PhoneNumber = @PhoneNumber AND SongNumber = @SongNumber"; + using (var checkCommand = new SQLiteCommand(checkSql, connection)) + { + checkCommand.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber); + checkCommand.Parameters.AddWithValue("@SongNumber", songNumber); + long count = (long)checkCommand.ExecuteScalar(); + + if (count > 0) + { + + Console.WriteLine(String.Format("Song {0} is already in favorites.", songNumber)); + return; + } + } + + string sql = "INSERT INTO FavoriteSongs (PhoneNumber, SongNumber) VALUES (@PhoneNumber, @SongNumber)"; + using (var command = new SQLiteCommand(sql, connection)) + { + command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber); + command.Parameters.AddWithValue("@SongNumber", songNumber); + command.ExecuteNonQuery(); + } + + connection.Close(); + + + var song = AllSongs.FirstOrDefault(s => s.SongNumber == songNumber); + if (song != null) + { + FavoriteSongs.Add(song); + Console.WriteLine(String.Format("Added song {0} to favorites.", songNumber)); + } + else + { + Console.WriteLine(String.Format("Song {0} not found in AllSongs.", songNumber)); + } + } + catch (Exception ex) + { + Console.WriteLine("Failed to add song to favorites: " + ex.Message); + } + } + } + + public List SearchSongsBySinger(string keyword) + { + + + var keywordLower = keyword.ToLower(); + return AllSongs.Where(song => song.ArtistA.ToLower().Contains(keywordLower) + || song.ArtistB.ToLower().Contains(keywordLower)) + .ToList(); + } + + public List SearchSongsByName(string keyword) + { + + + var keywordLower = keyword.ToLower(); + return AllSongs.Where(song => song.Song.ToLower().Contains(keywordLower)).ToList(); + } + + + public SongData SearchSongByNumber(string songNumber) + { + + foreach (var song in AllSongs) + { + if (song.SongNumber == songNumber) + { + return song; + } + } + return null; + } + + private void InitializeNewSongLists() + { + int songLimit = PrimaryForm.ReadNewSongLimit(); + + + string[] categories = new string[] { "國語", "台語", "粵語", "英文", "日語", "韓語" }; + + foreach (var category in categories) + { + + var songsInCategory = AllSongs + .Where(s => s.Category == category) + .OrderByDescending(s => s.AddedTime) + .Take(songLimit) + .ToList(); + + + NewSongLists.Add(category, songsInCategory); + } + } + + private void InitializeHotSongLists() + { + int songLimit = PrimaryForm.ReadHotSongLimit(); + + + string[] categories = new string[] { "國語", "台語", "英文", "日語", "韓語" }; + + foreach (var category in categories) + { + + var songsInCategory = AllSongs + .Where(s => s.Category == category) + .OrderByDescending(s => s.Plays) + .Take(songLimit) + .ToList(); + + + HotSongLists.Add(category, songsInCategory); + } + } + + public List GetNewSongsByCategory(string category) + { + + if (NewSongLists.ContainsKey(category)) + return NewSongLists[category]; + else + return new List(); + } + + public List GetHotSongsByCategory(string category) + { + + if (HotSongLists.ContainsKey(category)) + return HotSongLists[category]; + else + return new List(); + } + public List GetFavoriteSongsByPhoneNumber() + { + List favoriteSongs = new List(); + + if (string.IsNullOrEmpty(UserPhoneNumber)) + return favoriteSongs; + + string databaseFileName = "KSongDatabase.db"; + string databasePath = Path.Combine(Application.StartupPath, databaseFileName); + string connectionString = String.Format("Data Source={0};Version=3;", databasePath); + + using (var connection = new SQLiteConnection(connectionString)) + { + try + { + connection.Open(); + + string sql = @" + SELECT + sl.歌曲編號, sl.語別, sl.歌曲名稱, sl.點播次數, + sl.[歌星 A], sl.[歌星 B], sl.新增日期, sl.[路徑 1], + sl.[路徑 2], sl.歌曲檔名, sl.歌曲注音, sl.歌曲拼音, + sl.歌星A分類, sl.歌星B分類, sl.歌星A注音, sl.歌星B注音, + sl.歌星A簡體, sl.歌星B簡體, sl.歌名簡體, sl.分類, + sl.歌星A拼音, sl.歌星B拼音, sl.人聲 + FROM + FavoriteSongs fs + JOIN + SongLibrary sl + ON + fs.SongNumber = sl.歌曲編號 + WHERE + fs.PhoneNumber = @PhoneNumber"; + + using (var command = new SQLiteCommand(sql, connection)) + { + command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber); + + using (SQLiteDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + string songNumber = reader["歌曲編號"].ToString(); + string category = reader["語別"].ToString(); + string song = reader["歌曲名稱"].ToString(); + int plays = Convert.ToInt32(reader["點播次數"]); + string artistA = reader["歌星 A"].ToString(); + string artistB = reader["歌星 B"].ToString(); + string artistACategory = reader["歌星A分類"].ToString(); + string artistBCategory = reader["歌星B分類"].ToString(); + DateTime addedTime; + + try + { + addedTime = DateTime.ParseExact(reader["新增日期"].ToString(), "yyyy-MM-dd", CultureInfo.InvariantCulture); + } + catch (System.FormatException) + { + addedTime = DateTime.Now; + } + + string basePathHost1 = reader["路徑 1"].ToString(); + string basePathHost2 = reader["路徑 2"].ToString(); + string fileName = reader["歌曲檔名"].ToString(); + string songFilePathHost1 = Path.Combine(basePathHost1, fileName); + string songFilePathHost2 = Path.Combine(basePathHost2, fileName); + string phoneticNotation = reader["歌曲注音"].ToString(); + string pinyinNotation = reader["歌曲拼音"].ToString(); + string artistAPhonetic = reader["歌星A注音"].ToString(); + string artistBPhonetic = reader["歌星B注音"].ToString(); + string artistASimplified = reader["歌星A簡體"].ToString(); + string artistBSimplified = reader["歌星B簡體"].ToString(); + string songSimplified = reader["歌名簡體"].ToString(); + string songGenre = reader["分類"].ToString(); + string artistAPinyin = reader["歌星A拼音"].ToString(); + string artistBPinyin = reader["歌星B拼音"].ToString(); + int humanVoice = Convert.ToInt32(reader["人聲"]); + + favoriteSongs.Add(new SongData( + songNumber, category, song, plays, artistA, artistB, + artistACategory, artistBCategory, addedTime, songFilePathHost1, + songFilePathHost2, phoneticNotation, pinyinNotation, + artistAPhonetic, artistBPhonetic, artistASimplified, + artistBSimplified, songSimplified, songGenre, + artistAPinyin, artistBPinyin, humanVoice)); + } + } + } + + connection.Close(); + } + catch (Exception ex) + { + Console.WriteLine("Failed to load favorite songs from SQLite database: " + ex.Message); + } + } + + return favoriteSongs; + } + } +} \ No newline at end of file diff --git a/FilterEnumerator.cs b/FilterEnumerator.cs new file mode 100644 index 0000000..ed2a1c4 --- /dev/null +++ b/FilterEnumerator.cs @@ -0,0 +1,53 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using DirectShowLib; + +namespace DualScreenDemo +{ + public class FilterEnumerator + { + private static readonly Guid IID_IPropertyBag = new Guid("55272A00-42CB-11CE-8135-00AA004BB851"); + + public void EnumerateFilters() + { + ICreateDevEnum createDevEnum = (ICreateDevEnum)new CreateDevEnum(); + IEnumMoniker enumMoniker; + int hr = createDevEnum.CreateClassEnumerator(FilterCategory.LegacyAmFilterCategory, out enumMoniker, 0); + if (hr != 0 || enumMoniker == null) + { + Console.WriteLine("No filters found."); + return; + } + + IMoniker[] monikers = new IMoniker[1]; + IntPtr fetched = Marshal.AllocHGlobal(sizeof(int)); + while (enumMoniker.Next(1, monikers, fetched) == 0) + { + int fetchedCount = Marshal.ReadInt32(fetched); + if (fetchedCount > 0) + { + object objPropBag; + Guid tempGuid = IID_IPropertyBag; + monikers[0].BindToStorage(null, null, ref tempGuid, out objPropBag); + IPropertyBag propBag = objPropBag as IPropertyBag; + + object filterName = null; + if (propBag != null) + { + propBag.Read("FriendlyName", out filterName, null); + } + + if (filterName != null) + { + Console.WriteLine("Filter: " + filterName.ToString()); + } + + Marshal.ReleaseComObject(monikers[0]); + } + } + Marshal.ReleaseComObject(enumMoniker); + Marshal.FreeHGlobal(fetched); + } + } +} \ No newline at end of file diff --git a/FormatTypes.cs b/FormatTypes.cs new file mode 100644 index 0000000..bac79b5 --- /dev/null +++ b/FormatTypes.cs @@ -0,0 +1,10 @@ +using System; + +namespace DualScreenDemo +{ + public static class FormatTypes + { + public static readonly Guid VideoInfo = new Guid("05589F80-C356-11CE-BF01-00AA0055595A"); + public static readonly Guid WaveEx = new Guid("05589f81-c356-11ce-bf01-00aa0055595a"); + } +} \ No newline at end of file diff --git a/HttpServer.cs b/HttpServer.cs new file mode 100644 index 0000000..4b137c4 --- /dev/null +++ b/HttpServer.cs @@ -0,0 +1,1234 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; // For JsonConvert and JsonException +using System.Windows.Forms; // For MessageBox +using System.Drawing; // For Image +using System.Drawing.Imaging; // For ImageFormat +using ZXing; // For BarcodeWriter and BarcodeFormat +using ZXing.QrCode; // For QrCodeEncodingOptions +using ActionString = System.Action; // 现在可以在代码中使用 ActionString +using SystemAction = System.Action; +using ZXingAction = ZXing.Action; +using System.Threading; +using System.Collections.Concurrent; +using DBObj; +using OverlayFormObj; +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 _fileCache = new ConcurrentDictionary(); + private static readonly SemaphoreSlim _requestThrottle = new SemaphoreSlim(20); // 限制并发请求数 + private static readonly CancellationTokenSource _serverCts = new CancellationTokenSource(); + + + public static async Task StartServer(string baseDirectory, int port, SongListManager manager) + { + songListManager = manager; // 保存传递的SongListManager实例 + string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory); + randomFolderPath = randomFolderName; // 初始化全局变量 + + // 读取 IP 地址 + string localAddress = GetLocalIPAddress(); // 使用获取的本地 IP + string externalAddress = ""; + + // 讀取外網地址 沒有端口號 + string serverAddressFilePath = @"\\SVR01\superstarb\txt\ip.txt"; + if (File.Exists(serverAddressFilePath)) + { + externalAddress = File.ReadAllText(serverAddressFilePath).Trim(); + Console.WriteLine("External address: " + externalAddress); + } + else + { + Console.WriteLine("Warning: External address file not found. Using local address only."); + } + // 創建一個 HttpListener 來監聽 HTTP 請求 + HttpListener listener = new HttpListener(); + + // 構造本地地址的 URL 前綴(包含協議、地址和端口) + string localPrefix = String.Format("http://{0}:{1}/", localAddress, port); + + // 在控制台輸出添加的本地前綴,方便調試 + Console.WriteLine("Adding local prefix: " + localPrefix); + + // 將本地前綴添加到 HttpListener,使其監聽該 URL + listener.Prefixes.Add(localPrefix); + string hostName = System.Net.Dns.GetHostName(); + string externalPort = '1' + hostName.Substring(Math.Max(2, hostName.Length - 20)); + + // 如果有外网地址,也添加外网地址前缀 + if (!string.IsNullOrEmpty(externalAddress)) + { + // 錨點 2 + // 外網 IP 和 port 調整 + string[] parts = externalAddress.Split(':'); + string host = parts[0]; + + //int externalPort = parts.Length > 1 ? int.Parse(parts[1]) : port; + + string externalPrefix = String.Format("http://{0}:{1}/", host, externalPort); + Console.WriteLine("Adding external prefix: " + externalPrefix); + + try + { + listener.Prefixes.Add(externalPrefix); + } + catch (Exception ex) + { + Console.WriteLine($"Warning: Could not add external prefix: {ex.Message}"); + } + } + + // 生成两个二维码内容 + string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", localAddress, port, randomFolderName); + // string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", "ss.net.dnsnet.cc", 1102, randomFolderName); + + // 修改外网二维码内容生成 + + string externalQrContent = !string.IsNullOrEmpty(externalAddress) ? + String.Format("http://{0}:{1}/{2}/windows.html", externalAddress, externalPort, randomFolderName) : + localQrContent; + + Console.WriteLine("local QR Content : " + localQrContent); + Console.WriteLine("external QR Content : " + externalQrContent); + // 生成二维码(这里使用外网地址的二维码,因为通常外网地址更有用) + string qrImagePath = GenerateQRCode(externalQrContent, Path.Combine(baseDirectory, randomFolderName, "qrcode.png")); + + try + { + listener.Start(); + Console.WriteLine("Server started."); + + // 在程序关闭时删除随机文件夹 + AppDomain.CurrentDomain.ProcessExit += (s, e) => DeleteRandomFolder(baseDirectory); + } + catch (HttpListenerException ex) + { + Console.WriteLine("Error starting server: " + ex.Message); + return; + } + + while (true) + { + HttpListenerContext context = await listener.GetContextAsync(); + await ProcessRequestAsync(context, baseDirectory, randomFolderName); + } + } + + 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); + Console.WriteLine("Deleted random folder: " + fullPath); + } + catch (Exception ex) + { + Console.WriteLine("Error deleting random folder: " + ex.Message); + } + } + } + + public static string GetServerAddress() + { + return String.Format("http://{0}:{1}/", _localIP, _port); + // return String.Format("http://111.246.145.170:8080/"); + } + /// + /// 生成隨機路徑 + /// + /// + /// + 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; + } + } + + private static async Task HandleSearchRequest(HttpListenerContext context) + { + try + { + string requestBody = await ReadRequestBodyAsync(context.Request); + var searchRequest = JsonConvert.DeserializeObject(requestBody); + + List searchResults; + switch (searchRequest.Type) + { + case "new-songs": + searchResults = SongListManager.NewSongLists["國語"]; + break; + case "top-ranking": + searchResults = SongListManager.HotSongLists["國語"]; + break; + case "singer": + searchResults = songListManager.SearchSongsBySinger(searchRequest.Query); + break; + case "song": + searchResults = songListManager.SearchSongsByName(searchRequest.Query); + break; + default: + searchResults = new List(); + 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 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(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 DeterminePlayStates(List playedSongsHistory) + { + var playStates = new List(); + 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(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(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(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) + { + try + { + if (PrimaryForm.isPhoneNumberValid && !string.IsNullOrEmpty(PrimaryForm.phonenumber)) + { + string phone = PrimaryForm.phonenumber; // 直接通过类名访问 + // 登录用户 + SongListManager.Instance.UserLogin(phone); + + // 获取用户的收藏歌曲 + var favoriteSongs = SongListManager.Instance.GetFavoriteSongsByPhoneNumber(); + + // 创建响应数据 + var response = new + { + isLoggedIn = true, + favoriteSongList = favoriteSongs + .Select(song => new + { + song.Song, + song.ArtistA, + song.SongNumber, + song.Category, + song.PhoneticNotation, + song.PinyinNotation, + song.ArtistAPhonetic, + song.ArtistBPhonetic, + song.ArtistASimplified, + song.ArtistBSimplified, + song.SongSimplified, + song.SongGenre, + song.ArtistAPinyin, + song.ArtistBPinyin, + song.HumanVoice, + song.AddedTime, + 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); + } + } + + // 异步响应发送方法 + 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); + } + } + + 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; + } + } + } +} \ No newline at end of file diff --git a/HttpServerManager.cs b/HttpServerManager.cs new file mode 100644 index 0000000..b77c7fe --- /dev/null +++ b/HttpServerManager.cs @@ -0,0 +1,50 @@ +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace DualScreenDemo +{ + public static class HttpServerManager + { + public static async void StartServer() + { + int httpPort = 9090; // 你可以修改此端口 + string baseDirectory = Path.Combine(Application.StartupPath, @"themes\superstar\_www"); + + CleanUpDirectory(baseDirectory); + await HttpServer.StartServer(baseDirectory, httpPort, Program.songListManager); + } + + + private static void CleanUpDirectory(string baseDirectory) + { + string[] directoriesToKeep = { "css", "fonts", "superstar-pic", "手機點歌" }; + + var allDirectories = Directory.GetDirectories(baseDirectory); + var allFiles = Directory.GetFiles(baseDirectory); + + var filesToKeep = allFiles + .Where(file => file.EndsWith(".html")) + .Select(file => Path.GetFileName(file)) + .ToArray(); + + foreach (var dir in allDirectories) + { + var dirName = Path.GetFileName(dir); + if (!directoriesToKeep.Contains(dirName)) + { + Directory.Delete(dir, true); + } + } + + foreach (var file in allFiles) + { + var fileName = Path.GetFileName(file); + if (!filesToKeep.Contains(fileName)) + { + File.Delete(file); + } + } + } + } +} \ No newline at end of file diff --git a/ImagePanel.cs b/ImagePanel.cs new file mode 100644 index 0000000..5faddd9 --- /dev/null +++ b/ImagePanel.cs @@ -0,0 +1,32 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace DualScreenDemo +{ + public class ImagePanel : Panel + { + public ImagePanel() + { + this.DoubleBuffered = true; + this.Size = new Size(1201, 496); + this.AutoSizeMode = AutoSizeMode.GrowAndShrink; + } + + public void SetBackgroundImageFromFile(string filePath, Rectangle cropArea) + { + Image image = Image.FromFile(filePath); + Bitmap bmp = new Bitmap(cropArea.Width, cropArea.Height); + + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(image, + new Rectangle(0, 0, bmp.Width, bmp.Height), + cropArea, + GraphicsUnit.Pixel); + } + + this.BackgroundImage = bmp; + } + } +} \ No newline at end of file diff --git a/Images/ksonglover.ico b/Images/ksonglover.ico new file mode 100644 index 0000000..99cb6d9 Binary files /dev/null and b/Images/ksonglover.ico differ diff --git a/MediaSubTypes.cs b/MediaSubTypes.cs new file mode 100644 index 0000000..63d9119 --- /dev/null +++ b/MediaSubTypes.cs @@ -0,0 +1,7 @@ +using System; + +public static class MediaSubTypes +{ + public static readonly Guid YUY2 = new Guid("32595559-0000-0010-8000-00AA00389B71"); + public static readonly Guid IEEE_FLOAT = new Guid("00000003-0000-0010-8000-00AA00389B71"); +} \ No newline at end of file diff --git a/MediaTypes.cs b/MediaTypes.cs new file mode 100644 index 0000000..10ea5ec --- /dev/null +++ b/MediaTypes.cs @@ -0,0 +1,10 @@ +using System; + +namespace DualScreenDemo +{ + public static class MediaTypes + { + public static readonly Guid Video = new Guid("73646976-0000-0010-8000-00AA00389B71"); + public static readonly Guid Audio = new Guid("73647561-0000-0010-8000-00AA00389B71"); + } +} \ No newline at end of file diff --git a/OverlayFormObj/OverlayForm.Helpers.cs b/OverlayFormObj/OverlayForm.Helpers.cs new file mode 100644 index 0000000..1a6b31c --- /dev/null +++ b/OverlayFormObj/OverlayForm.Helpers.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using DualScreenDemo; +namespace OverlayFormObj +{ + public partial class OverlayForm + { + private readonly object imageLock = new object(); + + private void AdjustLabelPositions() + { + int labelHeight = displayLabels.First().Height; + int totalHeight = displayLabels.Count * labelHeight; + int startY = 100; + + for (int i = 0; i < displayLabels.Count; i++) + { + Label label = displayLabels[i]; + int centerX = (this.Width - label.Width) / 2; + int centerY = startY + i * labelHeight; + label.Location = new Point(centerX, centerY); + } + + if (pauseLabel != null) + { + + pauseLabel.Location = new Point(this.Width - pauseLabel.Width - 10, 100); + } + + if (muteLabel != null) + { + + muteLabel.Location = new Point(this.Width - muteLabel.Width - 10, 140); + } + } + + public void UpdateMarqueeText(string newText, MarqueeStartPosition startPosition, Color textColor) + { + this.marqueeText = newText; + this.marqueeTextColor = textColor; + + // 使用顯示字體進行測量 + Font displayFont = new Font("Arial", 25, FontStyle.Bold); + + using (Graphics graphics = this.CreateGraphics()) + { + SizeF textSize = graphics.MeasureString(marqueeText, displayFont); + int textWidth = (int)textSize.Width; + switch (startPosition) + { + case MarqueeStartPosition.Middle: + this.marqueeXPos = (this.Width / 2) - (textWidth / 2) - 100; + break; + case MarqueeStartPosition.Right: + this.marqueeXPos = this.Width; + break; + } + } + + this.Invalidate(); + blackBackgroundPanel.Invalidate(); + } + + public void UpdateMarqueeTextSecondLine(string newText) + { + if (InvokeRequired) + { + Invoke(new MethodInvoker(() => UpdateMarqueeTextSecondLine(newText))); + return; + } + + marqueeTextSecondLine = newText; + SplitSecondLineText(newText); + + using (Graphics graphics = this.CreateGraphics()) + { + float textWidth = MeasureDisplayStringWidth(graphics, marqueeTextSecondLine, new Font("微軟正黑體", 40, FontStyle.Bold)); + marqueeXPosSecondLine = (int)((this.Width - textWidth) / 2); + } + + if (textSegments.Count > 1) + { + segmentSwitchTimer.Start(); + } + else + { + segmentSwitchTimer.Stop(); + } + + // 重置計時器 + if (secondLineTimer != null) + { + secondLineTimer.Stop(); + secondLineTimer.Dispose(); + } + + secondLineTimer = new System.Windows.Forms.Timer(); + secondLineTimer.Interval = 100; + secondLineStartTime = DateTime.Now; + + secondLineTimer.Tick += (sender, e) => + { + if ((DateTime.Now - secondLineStartTime).TotalMilliseconds >= 30000) // 30秒 + { + marqueeTextSecondLine = ""; + textSegments.Clear(); // 清除分段文本 + if (segmentSwitchTimer != null) + { + segmentSwitchTimer.Stop(); // 停止分段切換計時器 + } + secondLineTimer.Stop(); + secondLineTimer.Dispose(); + this.Invalidate(); + blackBackgroundPanel.Invalidate(); + } + }; + + secondLineTimer.Start(); + blackBackgroundPanel.Invalidate(); + } + + public void UpdateMarqueeTextThirdLine(string newText) + { + + Console.WriteLine("UpdateMarqueeTextThirdLine called with text: " + newText); + + if (InvokeRequired) + { + Invoke(new MethodInvoker(() => UpdateMarqueeTextThirdLine(newText))); + return; + } + marqueeTextThirdLine = newText; + marqueeXPosThirdLine = this.Width; + + + Console.WriteLine("Marquee text position reset to: " + marqueeXPosThirdLine); + + + Invalidate(); + } + + private void MarqueeTimer_Tick(object sender, EventArgs e) + { + marqueeXPos -= 2; // 調整移動速度 + + // 使用與顯示相同的字體來計算文本寬度 + using (Graphics graphics = this.CreateGraphics()) + { + float textWidth = MeasureDisplayStringWidth(graphics, marqueeText, new Font("微軟正黑體", 34, FontStyle.Bold)); + + // 當文本完全移出屏幕時重置位置 + if (marqueeXPos < -textWidth) + { + marqueeXPos = this.Width; + } + } + + this.Invalidate(); + blackBackgroundPanel.Invalidate(); + } + + private float MeasureDisplayStringWidth(Graphics graphics, string text, Font font) + { + // 使用提供的字體來測量文本寬度 + SizeF textSize = graphics.MeasureString(text, font); + return textSize.Width; + } + } +} \ No newline at end of file diff --git a/OverlayFormObj/OverlayForm.Labels.cs b/OverlayFormObj/OverlayForm.Labels.cs new file mode 100644 index 0000000..02b569f --- /dev/null +++ b/OverlayFormObj/OverlayForm.Labels.cs @@ -0,0 +1,1171 @@ +// OverlayForm/OverlayForm.Labels.cs +using System.IO; +using System.Drawing; +using System.Windows.Forms; +using System.Collections.Generic; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using DBObj; +using DualScreenDemo; +namespace OverlayFormObj +{ + public partial class OverlayForm + { + public Label displayLabel; + public Label songDisplayLabel; + private List