Compare commits
No commits in common. "superstar_1.2.1" and "main" have entirely different histories.
superstar_
...
main
12
ClickSequenceState.cs
Normal file
12
ClickSequenceState.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
|
||||
public enum ClickSequenceState
|
||||
{
|
||||
Initial,
|
||||
FirstClicked,
|
||||
SecondClicked,
|
||||
ThirdClicked,
|
||||
Completed
|
||||
}
|
||||
}
|
@ -25,9 +25,8 @@ namespace DualScreenDemo
|
||||
/// <summary>
|
||||
/// 遙控器接收資料
|
||||
/// </summary>
|
||||
public Task ProcessData(string indata)
|
||||
{
|
||||
AddToHistory(indata);
|
||||
public async Task ProcessData(string indata)
|
||||
{ AddToHistory(indata);
|
||||
// 遙控器測試
|
||||
// Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 遙控器: {indata}");
|
||||
switch (indata)
|
||||
@ -87,73 +86,102 @@ namespace DualScreenDemo
|
||||
HandleArtistAnnouncements();
|
||||
break;
|
||||
case "A2B3A4":
|
||||
SafeInvokeAction("A2B3A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↑ "));
|
||||
SafeInvokeAction("A2B3A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2B3A4",() => OverlayForm.MainForm.ShowVolumeUpLabel());
|
||||
break;
|
||||
case "A2B4A4":
|
||||
SafeInvokeAction("A2B4A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↓ "));
|
||||
SafeInvokeAction("A2B4A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2B4A4",() => OverlayForm.MainForm.ShowVolumeDownLabel());
|
||||
break;
|
||||
case "A2B5A4":
|
||||
SafeInvokeAction("A2B5A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↑ "));
|
||||
SafeInvokeAction("A2B5A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2B5A4",() => OverlayForm.MainForm.ShowMicUpLabel());
|
||||
break;
|
||||
case "A2B6A4":
|
||||
SafeInvokeAction("A2B6A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↓ "));
|
||||
SafeInvokeAction("A2B6A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2B6A4",() => OverlayForm.MainForm.ShowMicDownLabel());
|
||||
break;
|
||||
case "A2C2A4":
|
||||
SafeInvokeAction("A2C2A4",() => OverlayForm.MainForm.ShowTopRightEchoLabel("標準迴音"));
|
||||
SafeInvokeAction("A2C2A4",() => OverlayForm.MainForm.HidemicLabels());
|
||||
SafeInvokeAction("A2C2A4",() => OverlayForm.MainForm.ShowStandardLabel());
|
||||
break;
|
||||
case "A2C3A4":
|
||||
SafeInvokeAction("A2C3A4",() => OverlayForm.MainForm.ShowTopRightEchoLabel("專業迴音"));
|
||||
SafeInvokeAction("A2C3A4",() => OverlayForm.MainForm.HidemicLabels());
|
||||
SafeInvokeAction("A2C3A4",() => OverlayForm.MainForm.ShowProfessionalLabel());
|
||||
break;
|
||||
case "A2C4A4":
|
||||
SafeInvokeAction("A2C4A4",() => OverlayForm.MainForm.ShowTopRightEchoLabel("廣場迴音"));
|
||||
SafeInvokeAction("A2C4A4",() => OverlayForm.MainForm.HidemicLabels());
|
||||
SafeInvokeAction("A2C4A4",() => OverlayForm.MainForm.ShowSquareLabel());
|
||||
break;
|
||||
case "A2C1A4":
|
||||
SafeInvokeAction("A2C1A4",() => OverlayForm.MainForm.ShowTopRightEchoLabel("唱將迴音"));
|
||||
SafeInvokeAction("A2C1A4",() => OverlayForm.MainForm.HidemicLabels());
|
||||
SafeInvokeAction("A2C1A4",() => OverlayForm.MainForm.ShowSingDownLabel());
|
||||
break;
|
||||
case "A2D5A4":
|
||||
SafeInvokeAction("A2D5A4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 明亮 "));
|
||||
SafeInvokeAction("A2D5A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2D5A4",() => OverlayForm.MainForm.ShowBrightLabel());
|
||||
break;
|
||||
case "A2D7A4":
|
||||
SafeInvokeAction("A2D7A4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 浪漫 "));
|
||||
SafeInvokeAction("A2D7A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2D7A4",() => OverlayForm.MainForm.ShowRomanticLabel());
|
||||
break;
|
||||
/* case "A27CA4":
|
||||
InvokeAction(() => OverlayForm.MainForm.ShowMaleKeyLabel());
|
||||
break;
|
||||
case "A282A4":
|
||||
InvokeAction(() => OverlayForm.MainForm.ShowFemaleKeyLabel());
|
||||
break;*/
|
||||
case "A2D6A4":
|
||||
SafeInvokeAction("A2D6A4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 柔和 "));
|
||||
SafeInvokeAction("A2D6A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2D6A4",() => OverlayForm.MainForm.ShowSoftLabel());
|
||||
break;
|
||||
case "A2D8A4":
|
||||
SafeInvokeAction("A2D8A4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 動感 "));
|
||||
SafeInvokeAction("A2D8A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A2D8A4",() => OverlayForm.MainForm.ShowDynamicLabel());
|
||||
break;
|
||||
case "A275A4":
|
||||
SafeInvokeAction("A275A4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 調色 "));
|
||||
SafeInvokeAction("A275A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A275A4",() => OverlayForm.MainForm.ShowTintLabel());
|
||||
break;
|
||||
case "A283A4":
|
||||
SafeInvokeAction("A283A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↑升4調 "));
|
||||
SafeInvokeAction("A283A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A283A4",() => OverlayForm.MainForm.ShowKeyUpLabel("↑升4調"));
|
||||
break;
|
||||
case "A282A4":
|
||||
SafeInvokeAction("A282A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↑升3調 "));
|
||||
SafeInvokeAction("A282A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A282A4",() => OverlayForm.MainForm.ShowKeyUpLabel("↑升3調"));
|
||||
break;
|
||||
case "A281A4":
|
||||
SafeInvokeAction("A281A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↑升2調 "));
|
||||
SafeInvokeAction("A281A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A281A4",() => OverlayForm.MainForm.ShowKeyUpLabel("↑升2調"));
|
||||
break;
|
||||
case "A280A4":
|
||||
SafeInvokeAction("A280A4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↑升1調 "));
|
||||
SafeInvokeAction("A280A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A280A4",() => OverlayForm.MainForm.ShowKeyUpLabel("↑升1調"));
|
||||
break;
|
||||
case "A27FA4":
|
||||
SafeInvokeAction("A27FA4",() => OverlayForm.MainForm.ShowTopRightLabelTime(" 標準調 "));
|
||||
SafeInvokeAction("A27FA4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A27FA4",() => OverlayForm.MainForm.ShowStandardKeyLabel());
|
||||
break;
|
||||
case "A27EA4":
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↓降1調 "));
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降1調"));
|
||||
break;
|
||||
case "A27DA4":
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↓降2調 "));
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A27EA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降2調"));
|
||||
break;
|
||||
case "A27CA4":
|
||||
SafeInvokeAction("A27CA4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↓降3調 "));
|
||||
SafeInvokeAction("A27CA4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A27CA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降3調"));
|
||||
break;
|
||||
case "A27BA4":
|
||||
SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.ShowTopRightLabelTime("↓降4調 "));
|
||||
SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A27BA4",() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調"));
|
||||
break;
|
||||
case "A266A4":
|
||||
SafeInvokeAction("A266A4",() => OverlayForm.MainForm.ShowTopRightLabel("服務鈴"));
|
||||
SafeInvokeAction("A266A4",() => OverlayForm.MainForm.HideAllLabels());
|
||||
SafeInvokeAction("A266A4",() => OverlayForm.MainForm.ShowServiceBell());
|
||||
break;
|
||||
default:
|
||||
if (Regex.IsMatch(indata, @"^A23\d+A4$"))
|
||||
@ -162,8 +190,7 @@ namespace DualScreenDemo
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
}
|
||||
private void SafeInvokeAction(string commandKey, Action action)
|
||||
{
|
||||
@ -216,11 +243,18 @@ namespace DualScreenDemo
|
||||
private static void SkipToNextSong()
|
||||
{
|
||||
if (PrimaryForm.Instance.InvokeRequired)
|
||||
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.PlayNextSong()));
|
||||
{
|
||||
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.SkipToNextSong()));
|
||||
}
|
||||
else
|
||||
PrimaryForm.Instance.videoPlayerForm.PlayNextSong();
|
||||
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() => { OverlayForm.MainForm.ShowTopRightEchoLabel("標準迴音"); } ) );
|
||||
{
|
||||
PrimaryForm.Instance.videoPlayerForm.SkipToNextSong();
|
||||
}
|
||||
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowStandardLabel();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -318,8 +352,8 @@ namespace DualScreenDemo
|
||||
{
|
||||
ClearDisplay();
|
||||
OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song);
|
||||
SongList.Add(song);
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.AddSongToPlaylist(song);
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
else
|
||||
@ -337,7 +371,7 @@ namespace DualScreenDemo
|
||||
_indataHistory.Clear(); // 清空歷史紀錄
|
||||
_wrongInputCountfor61 = 0; // 重置計數器
|
||||
}
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
}));
|
||||
@ -348,8 +382,8 @@ namespace DualScreenDemo
|
||||
{
|
||||
ClearDisplay();
|
||||
OverlayForm.MainForm.displayLabel.Text = String.Format("{0}", song);
|
||||
SongList.Add(song);
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.AddSongToPlaylist(song);
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
else
|
||||
@ -367,7 +401,7 @@ namespace DualScreenDemo
|
||||
_indataHistory.Clear(); // 清空歷史紀錄
|
||||
_wrongInputCountfor61 = 0; // 重置計數器
|
||||
}
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
}
|
||||
@ -375,12 +409,12 @@ namespace DualScreenDemo
|
||||
}
|
||||
private string check_control(string old){
|
||||
foreach(string item in _indataHistory)
|
||||
{
|
||||
if(item == "A261A4")
|
||||
old += "#";
|
||||
else if(item == "A262A4")
|
||||
old += "*";
|
||||
}
|
||||
{
|
||||
if(item == "A261A4")
|
||||
old += "#";
|
||||
else if(item == "A262A4")
|
||||
old += "*";
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
@ -424,8 +458,8 @@ namespace DualScreenDemo
|
||||
{
|
||||
ClearDisplay();
|
||||
OverlayForm.MainForm.displayLabel.Text = String.Format("插播歌曲{0}", song);
|
||||
SongList.Insert(song);
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.InsertSongToPlaylist(song);
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
else
|
||||
@ -443,7 +477,7 @@ namespace DualScreenDemo
|
||||
_indataHistory.Clear(); // 清空歷史紀錄
|
||||
_wrongInputCountfor62 = 0; // 重置計數器
|
||||
}
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
}));
|
||||
@ -454,7 +488,7 @@ namespace DualScreenDemo
|
||||
{
|
||||
ClearDisplay();
|
||||
OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song);
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -471,7 +505,7 @@ namespace DualScreenDemo
|
||||
_indataHistory.Clear(); // 清空歷史紀錄
|
||||
_wrongInputCountfor62 = 0; // 重置計數器
|
||||
}
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
OverlayForm.displayTimer.Start();
|
||||
}
|
||||
}
|
||||
@ -508,9 +542,27 @@ namespace DualScreenDemo
|
||||
OverlayForm.MainForm.displayLabel,
|
||||
OverlayForm.MainForm.pauseLabel,
|
||||
OverlayForm.MainForm.muteLabel,
|
||||
OverlayForm.MainForm.topRightLabel,
|
||||
OverlayForm.MainForm.volumeUpLabel,
|
||||
OverlayForm.MainForm.volumeDownLabel,
|
||||
OverlayForm.MainForm.micUpLabel,
|
||||
OverlayForm.MainForm.micDownLabel,
|
||||
OverlayForm.MainForm.standardKeyLabel,
|
||||
OverlayForm.MainForm.keyUpLabel,
|
||||
OverlayForm.MainForm.keyDownLabel,
|
||||
OverlayForm.MainForm.maleKeyLabel,
|
||||
OverlayForm.MainForm.femaleKeyLabel,
|
||||
OverlayForm.MainForm.squareLabel,
|
||||
OverlayForm.MainForm.professionalLabel,
|
||||
OverlayForm.MainForm.standardLabel,
|
||||
OverlayForm.MainForm.singDownLabel,
|
||||
OverlayForm.MainForm.brightLabel,
|
||||
OverlayForm.MainForm.softLabel,
|
||||
OverlayForm.MainForm.autoLabel,
|
||||
OverlayForm.MainForm.romanticLabel,
|
||||
OverlayForm.MainForm.dynamicLabel,
|
||||
OverlayForm.MainForm.tintLabel,
|
||||
OverlayForm.MainForm.blackBackgroundPanel,
|
||||
OverlayForm.MainForm.topLeftLabel
|
||||
OverlayForm.MainForm.nextSongLabel
|
||||
};
|
||||
|
||||
// 將所有不在保留清單內的控制項移除並釋放資源
|
||||
@ -540,9 +592,7 @@ namespace DualScreenDemo
|
||||
ClearDisplay();
|
||||
|
||||
// 設定歌曲總數為已播放歌曲的數量
|
||||
List<SongData> history= SongList.GetHistory();
|
||||
List<SongData> historySongs = new List<SongData>();
|
||||
OverlayForm.MainForm.totalSongs = SongList.GetHistory().Count;
|
||||
OverlayForm.MainForm.totalSongs = PrimaryForm.playedSongsHistory.Count;
|
||||
|
||||
// 若無任何播放紀錄,直接顯示訊息並返回,不執行後續動作
|
||||
if (OverlayForm.MainForm.totalSongs == 0)
|
||||
@ -559,11 +609,45 @@ namespace DualScreenDemo
|
||||
int endIndex = Math.Min(startIndex + OverlayForm.MainForm.songsPerPage, OverlayForm.MainForm.totalSongs);
|
||||
|
||||
// 準備要傳給 UpdateHistoryLabel 的兩個清單:歌曲資料與播放狀態
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
historySongs.Add(history[i]);
|
||||
}
|
||||
|
||||
List<SongData> historySongs = new List<SongData>();
|
||||
List<PlayState> playStates = new List<PlayState>();
|
||||
// 錨點 遙控器
|
||||
// 從播放紀錄中取出當前頁面應顯示的歌曲與狀態
|
||||
int completedCount = PrimaryForm.currentSongIndexInHistory;
|
||||
// 判斷是否正在播放公播歌單 (若用戶點播歌單為空,則播放公播歌單)
|
||||
bool isPlayingPublicList = PrimaryForm.userRequestedSongs.Count == 0 ||
|
||||
(PrimaryForm.currentSongIndexInHistory >= PrimaryForm.userRequestedSongs.Count - 1 && PrimaryForm.Instance.videoPlayerForm.IsPlayingPublicSong);
|
||||
if (isPlayingPublicList)
|
||||
{
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
historySongs.Add(PrimaryForm.playedSongsHistory[i]); // 加入歌曲
|
||||
playStates.Add(PlayState.Played);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
|
||||
historySongs.Add(PrimaryForm.playedSongsHistory[i]); // 加入歌曲
|
||||
if (i < completedCount)
|
||||
{
|
||||
playStates.Add(PlayState.Played);
|
||||
}
|
||||
else if (i == completedCount)
|
||||
{
|
||||
playStates.Add(PlayState.Playing);
|
||||
}
|
||||
else
|
||||
{
|
||||
playStates.Add(PlayState.NotPlayed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// 安全更新 UI:若非 UI 執行緒則使用 Invoke 切換
|
||||
if (OverlayForm.MainForm.InvokeRequired)
|
||||
@ -571,17 +655,17 @@ namespace DualScreenDemo
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
// 更新主畫面的歷史播放顯示區
|
||||
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, OverlayForm.MainForm.currentPage, totalPages);
|
||||
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages);
|
||||
|
||||
// 隱藏下一首提示
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 若已在 UI 執行緒,直接操作
|
||||
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, OverlayForm.MainForm.currentPage, totalPages);
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages);
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
}
|
||||
|
||||
// 切換 UI 狀態為播放歷史模式(可做為內部狀態管理用途)
|
||||
@ -813,9 +897,27 @@ namespace DualScreenDemo
|
||||
if (control != OverlayForm.MainForm.displayLabel &&
|
||||
control != OverlayForm.MainForm.pauseLabel &&
|
||||
control != OverlayForm.MainForm.muteLabel &&
|
||||
control != OverlayForm.MainForm.topRightLabel &&
|
||||
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.topLeftLabel)
|
||||
control != OverlayForm.MainForm.nextSongLabel)
|
||||
{
|
||||
OverlayForm.MainForm.Controls.Remove(control);
|
||||
control.Dispose();
|
||||
@ -832,9 +934,27 @@ namespace DualScreenDemo
|
||||
if (control != OverlayForm.MainForm.displayLabel &&
|
||||
control != OverlayForm.MainForm.pauseLabel &&
|
||||
control != OverlayForm.MainForm.muteLabel &&
|
||||
control != OverlayForm.MainForm.topRightLabel &&
|
||||
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.topLeftLabel)
|
||||
control != OverlayForm.MainForm.nextSongLabel)
|
||||
{
|
||||
OverlayForm.MainForm.Controls.Remove(control);
|
||||
control.Dispose();
|
||||
@ -884,14 +1004,14 @@ namespace DualScreenDemo
|
||||
{
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.HideTopRightLabels();
|
||||
OverlayForm.DisplayNumberAtTopLeft(number);
|
||||
OverlayForm.DisplayNumberAtTopLeft(number);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
OverlayForm.MainForm.HideTopRightLabels();
|
||||
OverlayForm.DisplayNumberAtTopLeft(number);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ namespace DBObj
|
||||
// artist OOP test
|
||||
public class Artist
|
||||
{
|
||||
public string Name ;
|
||||
public string NameSimplified ;
|
||||
public string Name { get; set; }
|
||||
public string NameSimplified{get;set;}
|
||||
//public string Phonetic { get; set; }
|
||||
//public string Category { get; set; }
|
||||
//public int Strokes { get; set; }
|
||||
@ -14,8 +14,6 @@ namespace DBObj
|
||||
Name = name;
|
||||
NameSimplified=nameSimplified;
|
||||
}
|
||||
public string getName(bool IsSimplified=false) { return IsSimplified ? NameSimplified : Name; }
|
||||
public string getName() {return Name;}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace DBObj
|
||||
{
|
||||
public class MyDB : IDisposable
|
||||
{
|
||||
private readonly string connectionString = Utils.Env.GetDBConnection();
|
||||
private MySqlConnection conn;
|
||||
|
||||
public MyDB()
|
||||
{
|
||||
conn = new MySqlConnection(connectionString);
|
||||
conn.Open();
|
||||
Console.WriteLine("MyDB 連線成功!");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (conn != null)
|
||||
{
|
||||
conn.Close();
|
||||
Console.WriteLine("MyDB 連線已關閉!");
|
||||
}
|
||||
}
|
||||
|
||||
// SELECT 方法
|
||||
public DataTable Select(string query, MySqlParameter[] parameters)
|
||||
{
|
||||
using (var cmd = new MySqlCommand(query, conn))
|
||||
{
|
||||
if (parameters != null)
|
||||
{
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
}
|
||||
using (var adapter = new MySqlDataAdapter(cmd))
|
||||
{
|
||||
DataTable result = new DataTable();
|
||||
adapter.Fill(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INSERT / UPDATE / DELETE 方法
|
||||
public int ExecuteNonQuery(string query, MySqlParameter[] parameters)
|
||||
{
|
||||
using (var cmd = new MySqlCommand(query, conn))
|
||||
{
|
||||
if (parameters != null)
|
||||
{
|
||||
cmd.Parameters.AddRange(parameters);
|
||||
}
|
||||
return cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace DBObj
|
||||
{
|
||||
// song OOP test
|
||||
public class Song
|
||||
{
|
||||
private string Number;
|
||||
private string Name;
|
||||
private string Name_Simplified;
|
||||
private string FileName;
|
||||
private int HumanVoice;
|
||||
|
||||
|
||||
public Song(string num,string name,string name_s,string filename,int humanVoice) {
|
||||
Number=num;
|
||||
Name=name;
|
||||
Name_Simplified=name_s;
|
||||
FileName=filename;
|
||||
HumanVoice =humanVoice;
|
||||
}
|
||||
public string getNumber() {return Number;}
|
||||
public string getName(bool IsSimplified) {return IsSimplified ? Name_Simplified : Name;}
|
||||
public string getName() { return Name;}
|
||||
public string getFileName() { return FileName;}
|
||||
public int getHumanVoice() { return HumanVoice;}
|
||||
}
|
||||
}
|
@ -1,96 +1,120 @@
|
||||
using DualScreenDemo;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Markup;
|
||||
namespace DBObj
|
||||
{
|
||||
public class SongData
|
||||
{
|
||||
private Song basic;
|
||||
private Artist A ;
|
||||
private Artist B ;
|
||||
public string Number { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Artist_A { get; set; }
|
||||
private string Artist_B { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string Artist_A_Simplified { get; set; }
|
||||
public string Artist_B_Simplified { get; set; }
|
||||
public string Name_Simplified { get; set; }
|
||||
public int HumanVoice { get; set; }
|
||||
public bool isPublicSong { get; set; }
|
||||
|
||||
public PlayState state;
|
||||
|
||||
/*
|
||||
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 SongData(string songNumber, string song, string filename, int humanVoice, bool isPublic)
|
||||
{
|
||||
basic=new(songNumber,song,"",filename,humanVoice);
|
||||
Number = songNumber;
|
||||
Name = song;
|
||||
FileName = filename;
|
||||
HumanVoice = humanVoice;
|
||||
isPublicSong = isPublic;
|
||||
}
|
||||
public SongData(string songNumber, string song, string artistA, string artistB, string filename, string artistASimplified, string artistBSimplified, string songSimplified, int humanVoice)
|
||||
{
|
||||
basic=new(songNumber,song,songSimplified,filename,humanVoice);
|
||||
A= new Artist(artistA, artistASimplified);
|
||||
if(artistB!=null){
|
||||
B = new Artist(artistB, artistBSimplified);
|
||||
}
|
||||
Number = songNumber;
|
||||
Name = song;
|
||||
Artist_A = artistA;
|
||||
Artist_B = artistB;
|
||||
FileName = filename;
|
||||
Artist_A_Simplified = artistASimplified;
|
||||
Artist_B_Simplified = artistBSimplified;
|
||||
Name_Simplified = songSimplified;
|
||||
HumanVoice = humanVoice;
|
||||
isPublicSong = false;
|
||||
}
|
||||
public SongData(SongData value,PlayState s){
|
||||
basic = value.getBasic();
|
||||
A = value.getA();
|
||||
B = value.getB();
|
||||
state =s;
|
||||
}
|
||||
public Song getBasic() => basic;
|
||||
public Artist getA() => A;
|
||||
public Artist getB() => B;
|
||||
|
||||
public string getNumber() {return basic.getNumber();}
|
||||
public string getName(bool IsSimplified=false) { return basic.getName(IsSimplified); }
|
||||
public string getName() { return basic.getName();}
|
||||
public string getArtist_A(bool IsSimplified) { return A.getName(IsSimplified); }
|
||||
public string getArtist_A() { return A.getName();}
|
||||
public string getArtist_B(bool IsSimplified) { return B.getName(IsSimplified); }
|
||||
public string getArtist_B() { return B.getName();}
|
||||
public int getNameLength() { return basic.getName().Length; }
|
||||
public string getFileName() {return basic.getFileName();}
|
||||
public string next_song_text()
|
||||
public string getName(bool IsSimplified=false)
|
||||
{
|
||||
var str = (state == PlayState.InsertPlayback) ? GetStateTxt(false) : "";
|
||||
return String.Format("下一首:{0} {1}", basic.getName(false),str);
|
||||
return IsSimplified ? Name_Simplified : Name;
|
||||
}
|
||||
public int getNameLength()
|
||||
{
|
||||
return Name.Length;
|
||||
}
|
||||
public string next_artist_text()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Artist_B)
|
||||
? String.Format("下一首:{0} {1} {2}", Artist_A, Artist_B, Name)
|
||||
: String.Format("下一首:{0} {1}", Artist_A, Name);
|
||||
}
|
||||
public string artist_text()
|
||||
{
|
||||
return B!=null
|
||||
? $"{A.getName(false)} - {B.getName(false)}"
|
||||
: A.getName(false);
|
||||
return !string.IsNullOrWhiteSpace(Artist_B)
|
||||
? $"{Artist_A} - {Artist_B}"
|
||||
: Artist_A;
|
||||
}
|
||||
public string name_text()
|
||||
{
|
||||
return B!=null
|
||||
? String.Format("{0} - {1} - {2}", A.getName(false), B.getName(false), basic.getName(false))
|
||||
: String.Format("{0} - {1}", A.getName(false), basic.getName(false));
|
||||
return !string.IsNullOrWhiteSpace(Artist_B)
|
||||
? String.Format("{0} - {1} - {2}", Artist_A, Artist_B, Name)
|
||||
: String.Format("{0} - {1}", Artist_A, Name);
|
||||
}
|
||||
public string getArtist_A(bool IsSimplified)
|
||||
{
|
||||
return IsSimplified ? Artist_A_Simplified : Artist_A;
|
||||
}
|
||||
public string getArtist_B(bool IsSimplified)
|
||||
{
|
||||
return IsSimplified ? Artist_B_Simplified : Artist_B;
|
||||
}
|
||||
|
||||
public string getFile()
|
||||
{
|
||||
return FindExistingPath(basic.getFileName());
|
||||
return FindExistingPath(FileName);
|
||||
}
|
||||
private string FindExistingPath(string filename)
|
||||
{
|
||||
foreach (var server in Utils.Env.GetSongServers())
|
||||
var servers = new[] { @"\\svr01\", @"\\svr02\", @"\\svr01\e", @"\\svr02\e" };
|
||||
foreach (var server in servers)
|
||||
{
|
||||
Console.WriteLine($"伺服器路徑: '{server}'");
|
||||
string fullPath = Path.Combine(server, filename);
|
||||
if (File.Exists(fullPath)) return fullPath;
|
||||
}
|
||||
return null; // 找不到就回原本的 filename,不加路徑
|
||||
}
|
||||
public Color GetStateColor(){
|
||||
Color c = Color.White;
|
||||
if (state == PlayState.Played){
|
||||
c = Color.Gray;//Color.FromArgb(200, 75, 125); // 播畢顏色:紫紅色
|
||||
}else if (state == PlayState.Playing){
|
||||
c = Color.LimeGreen;
|
||||
}else if (state == PlayState.InsertPlayback){
|
||||
c = Color.Gold;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
public string GetStateTxt(bool IsSimplified){
|
||||
return (state==PlayState.NotPlayed)?"":$"({state.GetDescription(IsSimplified)})";
|
||||
}
|
||||
public void SetState(PlayState s) => state = s;
|
||||
|
||||
public PlayState GetState()
|
||||
@ -98,13 +122,13 @@ namespace DBObj
|
||||
return state;
|
||||
}
|
||||
|
||||
public int getHumanVoice() { return basic.getHumanVoice(); }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
return B!=null
|
||||
? String.Format("{0} - {1} - {2} - {3}", state, A.getName(), B.getName(), basic.getName())
|
||||
: String.Format("{0} - {1} - {2}", state, A.getName(), basic.getName());
|
||||
return !string.IsNullOrWhiteSpace(Artist_B)
|
||||
? String.Format("{0} - {1} - {2}", Artist_A, Artist_B, Name)
|
||||
: String.Format("{0} - {1}", Artist_A, Name);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
using DualScreenDemo;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Navigation;
|
||||
namespace DBObj
|
||||
{
|
||||
public class SongList
|
||||
{
|
||||
private static bool isWelcome = true;
|
||||
public static SongData welcome;
|
||||
public static SongData close ;
|
||||
private static SongData publicPlaying=null;
|
||||
private static List<SongData> publicSong = new();
|
||||
private static SongData playing=null;
|
||||
private static List<SongData> not_played = new List<SongData>();
|
||||
private static List<SongData> played = new List<SongData>();
|
||||
public static List<SongData> PublicSong() => publicSong;
|
||||
|
||||
public static SongData Current()
|
||||
{
|
||||
Console.WriteLine(not_played.Count + " Current " + playing);
|
||||
return (playing ==null) ? publicPlaying : playing;
|
||||
}
|
||||
public static SongData Next()
|
||||
{
|
||||
if (playing != null)
|
||||
{
|
||||
playing.SetState(PlayState.Played);
|
||||
played.Add(playing);
|
||||
playing = null;
|
||||
}
|
||||
if (not_played.Count <= 0)
|
||||
return NextPublicSong();
|
||||
else
|
||||
return NextUserSong();
|
||||
}
|
||||
private static SongData NextUserSong()
|
||||
{
|
||||
playing = not_played[0];
|
||||
not_played.RemoveAt(0);
|
||||
playing.SetState(PlayState.Playing);
|
||||
UpdateNextSongLabel();
|
||||
return playing;
|
||||
}
|
||||
private static SongData NextPublicSong()
|
||||
{
|
||||
if (Room.IsClose()) {
|
||||
publicPlaying = close;
|
||||
} else if(Room.IsOpen() && isWelcome){
|
||||
isWelcome = false;
|
||||
publicPlaying = welcome;
|
||||
} else {
|
||||
publicPlaying = publicSong[0];
|
||||
publicSong.RemoveAt(0);
|
||||
publicSong.Add(publicPlaying);
|
||||
}
|
||||
|
||||
return publicPlaying;
|
||||
}
|
||||
|
||||
public static List<SongData> GetHistory()
|
||||
{
|
||||
List<SongData> History = [.. played];
|
||||
if (playing != null) History.Add(playing);
|
||||
History.AddRange(not_played);
|
||||
return History;
|
||||
}
|
||||
|
||||
public static void Add(SongData song)
|
||||
{
|
||||
not_played.Add(new SongData(song,PlayState.NotPlayed));
|
||||
// PrimaryForm.Instance.AddSongCount(songData.Number);
|
||||
chkCut();
|
||||
}
|
||||
public static void Insert(SongData song)
|
||||
{
|
||||
not_played.Insert(0, new SongData(song,PlayState.InsertPlayback));
|
||||
chkCut();
|
||||
}
|
||||
private static void chkCut()
|
||||
{
|
||||
if (playing == null)
|
||||
{
|
||||
if (PrimaryForm.Instance.InvokeRequired)
|
||||
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.PlayNextSong()));
|
||||
else
|
||||
PrimaryForm.Instance.videoPlayerForm.PlayNextSong();
|
||||
} else {
|
||||
UpdateNextSongLabel();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateNextSongLabel()
|
||||
{
|
||||
VideoPlayerForm.overlayForm.UpdateTopLeftLabel(
|
||||
(not_played.Count > 0) ? not_played[0].next_song_text() : "目前沒有下一首,請踴躍點歌!!!"
|
||||
);
|
||||
}
|
||||
public static void clearSong()
|
||||
{
|
||||
|
||||
isWelcome = true;
|
||||
not_played.Clear();
|
||||
played.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
using System.Data;
|
||||
using DualScreenDemo;
|
||||
namespace DBObj
|
||||
{
|
||||
public class SongListManager
|
||||
{
|
||||
private MyDB db = new MyDB();
|
||||
public List<SongData> FavoriteSongs { get; private set; }
|
||||
public List<SongData> FavoriteSongs { get; private set; }
|
||||
//public bool IsUserLoggedIn { get; set; }
|
||||
//public string UserPhoneNumber { get; set; }
|
||||
public SongListManager()
|
||||
@ -78,32 +76,6 @@ namespace DBObj
|
||||
var searchResults =PrimaryForm.Instance.SearchSongs_Mysql(query);
|
||||
return searchResults.FirstOrDefault();
|
||||
}
|
||||
private List<SongData> select_Mysql(string query)
|
||||
{
|
||||
List<SongData> searchResults = new List<SongData>();
|
||||
Console.WriteLine(query);
|
||||
using (db)
|
||||
{
|
||||
DataTable result = db.Select(query, null);
|
||||
foreach (DataRow row in result.Rows)
|
||||
{
|
||||
searchResults.Add(new SongData(
|
||||
row["song_id"].ToString(),
|
||||
row["song_name"].ToString(),
|
||||
row["artistA"].ToString(),
|
||||
row["artistB"].ToString(),
|
||||
row["song_filename"].ToString(),
|
||||
row["artistA_simplified"].ToString(),
|
||||
row["artistB_simplified"].ToString(),
|
||||
row["song_simplified"].ToString(),
|
||||
Convert.ToInt32(row["vocal"])
|
||||
));
|
||||
}
|
||||
Console.WriteLine($"查詢到 {searchResults.Count} 筆資料。");
|
||||
}
|
||||
|
||||
return searchResults;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
10
DualScreenDemo.Shared.cs
Normal file
10
DualScreenDemo.Shared.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace DualScreenDemo.Shared
|
||||
{
|
||||
public class VideoStatus
|
||||
{
|
||||
public bool IsGraphOk { get; set; }
|
||||
public string LastError { get; set; }
|
||||
public double PositionSeconds { get; set; }
|
||||
public string PlayState { get; set; }
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public static class EnumExtensions
|
||||
{
|
||||
public static string GetDescription(this Enum value, bool isSimplified = false)
|
||||
{
|
||||
var field = value.GetType().GetField(value.ToString());
|
||||
|
||||
var attr = field?.GetCustomAttribute<LocalizedDescriptionAttribute>();
|
||||
if (attr != null)
|
||||
return isSimplified ? attr.Simplified : attr.Traditional;
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
7
Env.cs
7
Env.cs
@ -49,13 +49,6 @@ namespace Utils
|
||||
|
||||
Console.WriteLine("所有指定目錄都找不到 config.ini");
|
||||
}
|
||||
public static string[] GetSongServers()
|
||||
{
|
||||
return Get("songServer", "")
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Select(s => s.Trim('\'', '"').TrimEnd('\\')) // 清理
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static string Get(string key, string fallback = "") =>
|
||||
_values.TryGetValue(key, out var value) ? value : fallback;
|
||||
|
52
FilterEnumerator.cs
Normal file
52
FilterEnumerator.cs
Normal file
@ -0,0 +1,52 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
8
FormatTypes.cs
Normal file
8
FormatTypes.cs
Normal file
@ -0,0 +1,8 @@
|
||||
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");
|
||||
}
|
||||
}
|
288
HttpServer.cs
288
HttpServer.cs
@ -10,7 +10,6 @@ using ActionString = System.Action<string>; // 现在可以在代码中使用 Ac
|
||||
using System.Collections.Concurrent;
|
||||
using DBObj;
|
||||
using OverlayFormObj;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
@ -272,7 +271,7 @@ namespace DualScreenDemo
|
||||
await HandleOrderSongListRequest(context);
|
||||
break;
|
||||
case "/message":
|
||||
await HandleMessageRequest(context);
|
||||
await HandlemessageRequest(context);
|
||||
break;
|
||||
case "/favorite":
|
||||
await HandleFavoriteRequest(context);
|
||||
@ -370,16 +369,8 @@ namespace DualScreenDemo
|
||||
searchResults = new List<SongData>();
|
||||
break;
|
||||
}
|
||||
var response = searchResults
|
||||
.Select(song => new
|
||||
{
|
||||
Song = song.getName(),
|
||||
ArtistA =song.getArtist_A(),
|
||||
SongNumber = song.getNumber(),
|
||||
})
|
||||
.ToList();
|
||||
|
||||
await SendJsonResponseAsync(context, response);
|
||||
await SendJsonResponseAsync(context, searchResults);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -460,16 +451,24 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "volume_up":
|
||||
// 执行音量增大操作
|
||||
PrimaryForm.SendCommandThroughSerialPort("a2 b3 a4");
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↑","a2 b3 a4");
|
||||
OverlayForm.MainForm.ShowVolumeUpLabel();
|
||||
PrimaryForm.Instance.volumeUpTimer.Start();
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
PrimaryForm.Instance.volumeUpTimer.Stop();
|
||||
}));
|
||||
break;
|
||||
case "mic_up":
|
||||
// 执行麦克风增大操作
|
||||
PrimaryForm.SendCommandThroughSerialPort("a2 b5 a4");
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↑","a2 b5 a4");
|
||||
OverlayForm.MainForm.ShowMicUpLabel();
|
||||
PrimaryForm.Instance.micControlTimer.Start();
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
PrimaryForm.Instance.micControlTimer.Stop();
|
||||
}));
|
||||
break;
|
||||
case "mute":
|
||||
@ -497,16 +496,24 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "volume_down":
|
||||
// 执行音量减小操作
|
||||
PrimaryForm.SendCommandThroughSerialPort("a2 b4 a4");
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↓","a2 b4 a4");
|
||||
OverlayForm.MainForm.ShowVolumeDownLabel();
|
||||
PrimaryForm.Instance.volumeDownTimer.Start();
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
PrimaryForm.Instance.volumeDownTimer.Stop();
|
||||
}));
|
||||
break;
|
||||
case "mic_down":
|
||||
// 执行麦克风减小操作
|
||||
PrimaryForm.SendCommandThroughSerialPort("a2 b6 a4");
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↓","a2 b6 a4");
|
||||
OverlayForm.MainForm.ShowMicDownLabel();
|
||||
PrimaryForm.Instance.micControlTimer.Start();
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
PrimaryForm.Instance.micControlTimer.Stop();
|
||||
}));
|
||||
break;
|
||||
case "original_song":
|
||||
@ -526,9 +533,12 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "service":
|
||||
// 执行服务操作
|
||||
PrimaryForm.SendCommandThroughSerialPort("a2 53 a4");
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
||||
OverlayForm.MainForm.ShowTopRightLabel("服務鈴","a2 53 a4");
|
||||
OverlayForm.MainForm.ShowServiceBell();
|
||||
}));
|
||||
// 异步处理等待和隐藏标签
|
||||
await HttpServer.HandleServiceBellAsync();
|
||||
break;
|
||||
case "replay":
|
||||
// 执行重唱操作
|
||||
@ -541,6 +551,9 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "male_key":
|
||||
// 执行男调操作
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
||||
OverlayForm.MainForm.ShowMaleKeyLabel();
|
||||
}));
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
||||
@ -558,6 +571,9 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "female_key":
|
||||
// 执行女调操作
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
||||
OverlayForm.MainForm.ShowFemaleKeyLabel();
|
||||
}));
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
||||
@ -576,12 +592,24 @@ namespace DualScreenDemo
|
||||
case "cut":
|
||||
// 执行切歌操作
|
||||
if (PrimaryForm.Instance.InvokeRequired)
|
||||
PrimaryForm.Instance.Invoke(new System.Action(() => { PrimaryForm.Instance.videoPlayerForm.PlayNextSong();}));
|
||||
{
|
||||
PrimaryForm.Instance.Invoke(new System.Action(() =>
|
||||
{
|
||||
PrimaryForm.Instance.videoPlayerForm.SkipToNextSong();
|
||||
}));
|
||||
}
|
||||
else
|
||||
PrimaryForm.Instance.videoPlayerForm.PlayNextSong();
|
||||
{
|
||||
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 是降調的指令
|
||||
@ -596,6 +624,9 @@ namespace DualScreenDemo
|
||||
break;
|
||||
case "standard_key":
|
||||
// 执行标准调操作
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() => {
|
||||
OverlayForm.MainForm.ShowStandardKeyLabel();
|
||||
}));
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
// 假設 0xA2, 0xC1, 0xA4 是升調的指令
|
||||
@ -610,6 +641,11 @@ namespace DualScreenDemo
|
||||
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 是升調的指令
|
||||
@ -646,9 +682,15 @@ namespace DualScreenDemo
|
||||
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)
|
||||
{
|
||||
string jsonResponse = "{\"status\": \"error\", \"message\": \"An error occurred\"}";
|
||||
try
|
||||
{
|
||||
// 读取请求的内容
|
||||
@ -659,32 +701,94 @@ namespace DualScreenDemo
|
||||
}
|
||||
|
||||
Console.WriteLine("Received order song request: " + requestBody);
|
||||
|
||||
// 检查 playedSongsHistory 是否存在且不为空
|
||||
// 创建响应数据
|
||||
var response = new
|
||||
if (PrimaryForm.playedSongsHistory != null && PrimaryForm.playedSongsHistory.Count > 0)
|
||||
{
|
||||
playingSongList = SongList.GetHistory()
|
||||
.Select((song, index) => new
|
||||
{
|
||||
Song=song.getName(),
|
||||
ArtistA=song.getArtist_A(),
|
||||
PlayState = song.GetState().GetDescription()
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
|
||||
jsonResponse = JsonConvert.SerializeObject(response);
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
|
||||
Console.WriteLine("Played Songs History Count: " + PrimaryForm.playedSongsHistory.Count);
|
||||
foreach (var song in PrimaryForm.playedSongsHistory)
|
||||
{
|
||||
Console.WriteLine($"Song: {song.Name}, ArtistA: {song.Artist_A}");
|
||||
}
|
||||
|
||||
// 根据播放历史确定每首歌的播放状态
|
||||
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\"}");
|
||||
}
|
||||
context.Response.ContentType = "application/json";
|
||||
await SendResponseAsync(context, jsonResponse);
|
||||
}
|
||||
|
||||
// 确定每首歌的播放状态
|
||||
private static List<PlayState?> DeterminePlayStates(List<SongData> playedSongsHistory)
|
||||
{
|
||||
var playStates = new List<PlayState?>();
|
||||
bool foundPlaying = false; // 标记是否已找到正在播放的歌曲
|
||||
|
||||
for (int i = 0; i < playedSongsHistory.Count; i++)
|
||||
{
|
||||
// 这里可以根据您的业务逻辑来确定每首歌的播放状态
|
||||
if (i == PrimaryForm.currentSongIndexInHistory)
|
||||
{
|
||||
playStates.Add(PlayState.Playing);
|
||||
foundPlaying = true; // 找到正在播放的歌曲
|
||||
}
|
||||
else if (foundPlaying)
|
||||
{
|
||||
playStates.Add(null); // 找到播放中的歌曲后,后面的状态设置为 null
|
||||
}
|
||||
else
|
||||
{
|
||||
playStates.Add(PlayState.NotPlayed); // 未播放状态
|
||||
}
|
||||
}
|
||||
|
||||
return playStates;
|
||||
}
|
||||
|
||||
// 用于创建歌曲响应对象,包括播放状态
|
||||
private static object CreateSongResponse(SongData song, PlayState? playState)
|
||||
{
|
||||
return new
|
||||
{
|
||||
song.Name,
|
||||
song.Artist_A,
|
||||
song.FileName,
|
||||
PlayState = playState.HasValue ? (playState.Value == PlayState.Playing ? "播放中" : "播放完畢") : null // 如果状态为 null,不返回状态信息
|
||||
};
|
||||
}
|
||||
|
||||
// 生成播放状态
|
||||
@ -700,20 +804,22 @@ namespace DualScreenDemo
|
||||
}
|
||||
|
||||
Console.WriteLine("Received order song request: " + requestBody);
|
||||
|
||||
// 解析 JSON 为 Song 对象
|
||||
var json = JObject.Parse(requestBody);
|
||||
SongData song =songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
|
||||
var song = JsonConvert.DeserializeObject<SongData>(requestBody);
|
||||
|
||||
if (song != null) {
|
||||
Console.WriteLine($"Ordering Song: {song.getName()} by {song.getArtist_A()}");
|
||||
|
||||
if (song != null)
|
||||
{
|
||||
Console.WriteLine($"Ordering Song: {song.Name} by {song.Artist_A}");
|
||||
// 这里可以添加处理逻辑,例如将歌曲加入到播放列表或数据库中
|
||||
SongList.Add(song);
|
||||
OverlayForm.MainForm.AddSongToPlaylist(song);
|
||||
|
||||
var response = new { status = "success", message = "Song ordered successfully" };
|
||||
string jsonResponse = JsonConvert.SerializeObject(response);
|
||||
await SendResponseAsync(context, jsonResponse);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid song data\"}");
|
||||
}
|
||||
@ -739,17 +845,20 @@ namespace DualScreenDemo
|
||||
Console.WriteLine("Received insert song request: " + requestBody);
|
||||
|
||||
// 解析 JSON 为 Song 对象
|
||||
var json = JObject.Parse(requestBody);
|
||||
SongData song =songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
|
||||
var song = JsonConvert.DeserializeObject<SongData>(requestBody);
|
||||
|
||||
if (song != null){
|
||||
Console.WriteLine($"Inserting Song: {song.getName()} by {song.getArtist_A()}");
|
||||
if (song != null)
|
||||
{
|
||||
Console.WriteLine($"Inserting Song: {song.Name} by {song.Artist_A}");
|
||||
// 这里可以添加插播歌曲的处理逻辑
|
||||
SongList.Insert(song);
|
||||
OverlayForm.MainForm.InsertSongToPlaylist(song);
|
||||
|
||||
var response = new { status = "success", message = "Song inserted successfully" };
|
||||
string jsonResponse = JsonConvert.SerializeObject(response);
|
||||
await SendResponseAsync(context, jsonResponse);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
await SendResponseAsync(context, "{\"status\": \"error\", \"message\": \"Invalid song data\"}");
|
||||
}
|
||||
@ -838,37 +947,7 @@ namespace DualScreenDemo
|
||||
action();
|
||||
}
|
||||
}
|
||||
private static async Task HandleMessageRequest(HttpListenerContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
||||
{
|
||||
string json = await reader.ReadToEndAsync();
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.ContentType = "application/json";
|
||||
int startIndex = json.IndexOf("\"message\":\"") + 11;
|
||||
int endIndex = json.IndexOf("\"", startIndex);
|
||||
if (startIndex >= 0 && endIndex >= 0)
|
||||
OnDisplayBarrage?.Invoke( json.Substring(startIndex, endIndex - startIndex) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有有效的 JSON 数据,返回错误响应
|
||||
context.Response.StatusCode = 400; // Bad Request
|
||||
context.Response.StatusDescription = "Invalid JSON data.";
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Response.Close();
|
||||
}
|
||||
}
|
||||
/*
|
||||
private static async Task HandleMessageRequest2(HttpListenerContext context)
|
||||
private static async Task HandlemessageRequest(HttpListenerContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -888,18 +967,17 @@ namespace DualScreenDemo
|
||||
context.Response.ContentType = "application/json"; // 确保返回 JSON 格式
|
||||
int startIndex = json.IndexOf("\"message\":\"") + 11; // 11 是 "message\":\"" 的长度
|
||||
int endIndex = json.IndexOf("\"", startIndex);
|
||||
string Messagefist = "藏鏡人:";
|
||||
string Messagefist ="藏鏡人:";
|
||||
|
||||
// 如果找到了 "message" 字段
|
||||
if (startIndex >= 0 && endIndex >= 0)
|
||||
{
|
||||
// 提取 "message" 字段的值
|
||||
string message = json.Substring(startIndex, endIndex - startIndex);
|
||||
for (int i = 0; i < 3; i++)
|
||||
for(int i=0;i<3;i++)
|
||||
{
|
||||
string Messagelast = "";
|
||||
for (int j = 0; j < message.Length; j++)
|
||||
{
|
||||
string Messagelast="";
|
||||
for(int j=0;j < message.Length;j++){
|
||||
Messagelast += message[j];
|
||||
await Task.Delay(10);
|
||||
// 将读取到的 "message" 字段传递给 UI 控件显示
|
||||
@ -907,17 +985,17 @@ namespace DualScreenDemo
|
||||
{
|
||||
OverlayForm.MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
OverlayForm.MainForm.ShowMessageLabel(Messagefist + Messagelast + '_');
|
||||
OverlayForm.MainForm.ShowmessageLabel(Messagefist + Messagelast + '_');
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
OverlayForm.MainForm.ShowMessageLabel(Messagefist + Messagelast + '_');
|
||||
OverlayForm.MainForm.ShowmessageLabel(Messagefist + Messagelast + '_');
|
||||
}
|
||||
}
|
||||
// 真情告白顯示秒數
|
||||
await Task.Delay(3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -942,9 +1020,21 @@ namespace DualScreenDemo
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -1024,14 +1114,14 @@ namespace DualScreenDemo
|
||||
favoriteSongList = searchResults
|
||||
.Select(song => new
|
||||
{
|
||||
Name = song.getName(),
|
||||
ArtistA =song.getArtist_A(),
|
||||
Number = song.getNumber(),
|
||||
ArtistAFull = song.getArtist_A(true),
|
||||
ArtistBFull = song.getArtist_B(true),
|
||||
NameFull = song.getName(true),
|
||||
HumanVoice =song.getHumanVoice(),
|
||||
FileName = song.getFileName()
|
||||
song.Name,
|
||||
song.Artist_A,
|
||||
song.Number,
|
||||
song.Artist_A_Simplified,
|
||||
song.Artist_B_Simplified,
|
||||
song.Name_Simplified,
|
||||
song.HumanVoice,
|
||||
song.FileName
|
||||
})
|
||||
.ToList(),
|
||||
status = "success",
|
||||
|
@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class LocalizedDescriptionAttribute : Attribute
|
||||
{
|
||||
public string Traditional { get; }
|
||||
public string Simplified { get; }
|
||||
|
||||
public LocalizedDescriptionAttribute(string traditional, string simplified)
|
||||
{
|
||||
Traditional = traditional;
|
||||
Simplified = simplified;
|
||||
}
|
||||
}
|
||||
}
|
5
MediaSubTypes.cs
Normal file
5
MediaSubTypes.cs
Normal file
@ -0,0 +1,5 @@
|
||||
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");
|
||||
}
|
8
MediaTypes.cs
Normal file
8
MediaTypes.cs
Normal file
@ -0,0 +1,8 @@
|
||||
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");
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,16 @@ namespace OverlayFormObj
|
||||
public static System.Timers.Timer unifiedTimer;
|
||||
private System.Windows.Forms.Timer stickerTimer1 = new System.Windows.Forms.Timer();
|
||||
private System.Windows.Forms.Timer stickerTimer2 = new System.Windows.Forms.Timer();
|
||||
private System.Windows.Forms.Timer keyUpTimer = new System.Windows.Forms.Timer();
|
||||
private System.Windows.Forms.Timer keyDownTimer = new System.Windows.Forms.Timer();
|
||||
public Label standardKeyLabel;
|
||||
private System.Windows.Forms.Timer standardKeyTimer = new System.Windows.Forms.Timer();
|
||||
private System.Windows.Forms.Timer messageTimer = new System.Windows.Forms.Timer();
|
||||
public Label maleKeyLabel;
|
||||
public Label messageLabel;
|
||||
private System.Windows.Forms.Timer maleKeyTimer;
|
||||
public Label femaleKeyLabel;
|
||||
private System.Windows.Forms.Timer femaleKeyTimer;
|
||||
private System.Windows.Forms.Timer secondLineTimer;
|
||||
private DateTime secondLineStartTime;
|
||||
private const int secondLineDuration = 20000;
|
||||
@ -99,159 +109,182 @@ namespace OverlayFormObj
|
||||
currentSegmentIndex = 0;
|
||||
}
|
||||
|
||||
private void AddCenteredPicture(Bitmap bitmap, int y)
|
||||
|
||||
|
||||
private void InitializeMaleKeyTimer()
|
||||
{
|
||||
int x = (this.Width - bitmap.Width) / 2;
|
||||
AddPicture(bitmap, x, y);
|
||||
}
|
||||
private void AddPicture(Bitmap bitmap, int x, int y)
|
||||
{
|
||||
PictureBox pictureBox = new PictureBox
|
||||
maleKeyTimer = new System.Windows.Forms.Timer();
|
||||
maleKeyTimer.Interval = 3000;
|
||||
maleKeyTimer.Tick += (s, e) =>
|
||||
{
|
||||
Image = bitmap,
|
||||
Size = bitmap.Size,
|
||||
BackColor = Color.Transparent,
|
||||
Location = new Point(x, y)
|
||||
HideAllLabels();
|
||||
};
|
||||
this.Controls.Add(pictureBox);
|
||||
pictureBox.BringToFront();
|
||||
}
|
||||
private Bitmap GenerateTextImage(string text, Font font, Color textColor, Color backgroundColor)
|
||||
|
||||
|
||||
private void InitializeFemaleKeyTimer()
|
||||
{
|
||||
SizeF textSize;
|
||||
using (Bitmap tempBitmap = new Bitmap(1, 1))
|
||||
using (Graphics tempGraphics = Graphics.FromImage(tempBitmap))
|
||||
femaleKeyTimer = new System.Windows.Forms.Timer();
|
||||
femaleKeyTimer.Interval = 3000;
|
||||
femaleKeyTimer.Tick += (s, e) =>
|
||||
{
|
||||
textSize = tempGraphics.MeasureString(text, font);
|
||||
}
|
||||
|
||||
// 創建一個稍大的位圖尺寸,以容納描邊
|
||||
int padding = 4; // 描邊寬度
|
||||
Bitmap bitmap = new Bitmap((int)textSize.Width + padding * 2, (int)textSize.Height + padding * 2);
|
||||
|
||||
using (Graphics graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
graphics.Clear(backgroundColor);
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||
|
||||
// 先繪製黑色描邊
|
||||
using (Brush outlineBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
for (int x = -2; x <= 2; x++)
|
||||
{
|
||||
for (int y = -2; y <= 2; y++)
|
||||
{
|
||||
if (x != 0 || y != 0)
|
||||
{
|
||||
graphics.DrawString(text, font, outlineBrush,
|
||||
new PointF(padding + x, padding + y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 繪製中心文字
|
||||
using (Brush textBrush = new SolidBrush(textColor))
|
||||
{
|
||||
graphics.DrawString(text, font, textBrush,
|
||||
new PointF(padding, padding));
|
||||
}
|
||||
}
|
||||
|
||||
// 修剪圖片邊緣空白
|
||||
return TrimBitmap(bitmap);
|
||||
HideAllLabels();
|
||||
};
|
||||
}
|
||||
|
||||
private Bitmap TrimBitmap(Bitmap source)
|
||||
private void AddCenteredPicture(Bitmap bitmap, int y)
|
||||
{
|
||||
int x = (this.Width - bitmap.Width) / 2;
|
||||
AddPicture(bitmap, x, y);
|
||||
}
|
||||
private void AddPicture(Bitmap bitmap, int x, int y)
|
||||
{
|
||||
PictureBox pictureBox = new PictureBox
|
||||
{
|
||||
Image = bitmap,
|
||||
Size = bitmap.Size,
|
||||
BackColor = Color.Transparent,
|
||||
Location = new Point(x, y)
|
||||
};
|
||||
this.Controls.Add(pictureBox);
|
||||
pictureBox.BringToFront();
|
||||
}
|
||||
private Bitmap GenerateTextImage(string text, Font font, Color textColor, Color backgroundColor)
|
||||
{
|
||||
SizeF textSize;
|
||||
using (Bitmap tempBitmap = new Bitmap(1, 1))
|
||||
using (Graphics tempGraphics = Graphics.FromImage(tempBitmap))
|
||||
{
|
||||
textSize = tempGraphics.MeasureString(text, font);
|
||||
}
|
||||
|
||||
// 創建一個稍大的位圖尺寸,以容納描邊
|
||||
int padding = 4; // 描邊寬度
|
||||
Bitmap bitmap = new Bitmap((int)textSize.Width + padding * 2, (int)textSize.Height + padding * 2);
|
||||
|
||||
using (Graphics graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
graphics.Clear(backgroundColor);
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
||||
|
||||
// 先繪製黑色描邊
|
||||
using (Brush outlineBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
Rectangle rect = FindContentBounds(source);
|
||||
|
||||
if (rect.Width == 0 || rect.Height == 0)
|
||||
return source; // 防止圖片完全空白時崩潰
|
||||
|
||||
Bitmap trimmedBitmap = new Bitmap(rect.Width, rect.Height);
|
||||
using (Graphics g = Graphics.FromImage(trimmedBitmap))
|
||||
for (int x = -2; x <= 2; x++)
|
||||
{
|
||||
g.DrawImage(source, 0, 0, rect, GraphicsUnit.Pixel);
|
||||
for (int y = -2; y <= 2; y++)
|
||||
{
|
||||
if (x != 0 || y != 0)
|
||||
{
|
||||
graphics.DrawString(text, font, outlineBrush,
|
||||
new PointF(padding + x, padding + y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trimmedBitmap;
|
||||
}
|
||||
|
||||
private Rectangle FindContentBounds(Bitmap bmp)
|
||||
// 繪製中心文字
|
||||
using (Brush textBrush = new SolidBrush(textColor))
|
||||
{
|
||||
int width = bmp.Width;
|
||||
int height = bmp.Height;
|
||||
|
||||
int top = 0, left = 0, bottom = height - 1, right = width - 1;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// 找到頂邊界
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
top = y;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到底邊界
|
||||
for (int y = height - 1; y >= 0; y--)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
bottom = y;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到左邊界
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
left = x;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到右邊界
|
||||
for (int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
right = x;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
return Rectangle.FromLTRB(left, top, right + 1, bottom + 1);
|
||||
graphics.DrawString(text, font, textBrush,
|
||||
new PointF(padding, padding));
|
||||
}
|
||||
}
|
||||
|
||||
// 修剪圖片邊緣空白
|
||||
return TrimBitmap(bitmap);
|
||||
}
|
||||
|
||||
private Bitmap TrimBitmap(Bitmap source)
|
||||
{
|
||||
Rectangle rect = FindContentBounds(source);
|
||||
|
||||
if (rect.Width == 0 || rect.Height == 0)
|
||||
return source; // 防止圖片完全空白時崩潰
|
||||
|
||||
Bitmap trimmedBitmap = new Bitmap(rect.Width, rect.Height);
|
||||
using (Graphics g = Graphics.FromImage(trimmedBitmap))
|
||||
{
|
||||
g.DrawImage(source, 0, 0, rect, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
return trimmedBitmap;
|
||||
}
|
||||
|
||||
private Rectangle FindContentBounds(Bitmap bmp)
|
||||
{
|
||||
int width = bmp.Width;
|
||||
int height = bmp.Height;
|
||||
|
||||
int top = 0, left = 0, bottom = height - 1, right = width - 1;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// 找到頂邊界
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
top = y;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到底邊界
|
||||
for (int y = height - 1; y >= 0; y--)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
bottom = y;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到左邊界
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
left = x;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
found = false;
|
||||
// 找到右邊界
|
||||
for (int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (bmp.GetPixel(x, y).A > 0)
|
||||
{
|
||||
right = x;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
return Rectangle.FromLTRB(left, top, right + 1, bottom + 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -263,7 +296,7 @@ namespace OverlayFormObj
|
||||
{
|
||||
|
||||
string currentText = MainForm.displayLabel.Text;
|
||||
MainForm.topLeftLabel.Visible = false;
|
||||
MainForm.nextSongLabel.Visible = false;
|
||||
string combinedText = currentText + newText;
|
||||
|
||||
|
||||
@ -275,7 +308,7 @@ namespace OverlayFormObj
|
||||
|
||||
|
||||
MainForm.displayLabel.Text = combinedText;
|
||||
MainForm.topLeftLabel.Visible = false;
|
||||
MainForm.nextSongLabel.Visible = false;
|
||||
|
||||
|
||||
displayTimer.Stop();
|
||||
@ -323,6 +356,7 @@ namespace OverlayFormObj
|
||||
secondLineTimer = new System.Windows.Forms.Timer();
|
||||
secondLineTimer.Interval = 100;
|
||||
secondLineTimer.Tick += SecondLineTimer_Tick;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
@ -336,6 +370,12 @@ namespace OverlayFormObj
|
||||
Console.WriteLine("Error reading marquee text from file: " + ex.Message);
|
||||
marqueeText = "這是跑馬燈文字示例 - 歡迎使用MediaPlayerForm!";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.DoubleBuffered = true;
|
||||
}
|
||||
|
||||
@ -382,51 +422,54 @@ namespace OverlayFormObj
|
||||
};
|
||||
}
|
||||
|
||||
private static void DisplayTimer_Tick(object sender, EventArgs e)
|
||||
private static void DisplayTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (MainForm.InvokeRequired)
|
||||
{
|
||||
MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
if (MainForm.InvokeRequired)
|
||||
{
|
||||
MainForm.Invoke(new System.Action(() =>
|
||||
{
|
||||
MainForm.displayLabel.Text = "";
|
||||
MainForm.topLeftLabel.Visible = true; // 显示 topLeftLabel
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
MainForm.displayLabel.Text = "";
|
||||
MainForm.topLeftLabel.Visible = true; // 显示 topLeftLabel
|
||||
}
|
||||
MainForm.displayLabel.Text = "";
|
||||
MainForm.nextSongLabel.Visible = true; // 显示 nextSongLabel
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
MainForm.displayLabel.Text = "";
|
||||
MainForm.nextSongLabel.Visible = true; // 显示 nextSongLabel
|
||||
}
|
||||
|
||||
displayTimer.Stop(); // 停止计时器
|
||||
}
|
||||
displayTimer.Stop(); // 停止计时器
|
||||
}
|
||||
|
||||
// 當 songDisplayTimer 計時完成時會呼叫此函式
|
||||
private static void SongDisplayTimer_Elapsed(object sender, EventArgs e)
|
||||
// 當 songDisplayTimer 計時完成時會呼叫此函式
|
||||
private static void SongDisplayTimer_Elapsed(object sender, EventArgs e)
|
||||
{
|
||||
// 檢查是否需要跨執行緒操作 UI 控制項
|
||||
if (MainForm.InvokeRequired)
|
||||
{
|
||||
// 如果目前不在 UI 執行緒上,必須透過 Invoke 安全執行 UI 更新邏輯
|
||||
Console.WriteLine("SongDisplayTimer_Tick invoked on UI thread.");
|
||||
|
||||
MainForm.BeginInvoke(new Action(() =>
|
||||
{
|
||||
// 檢查是否需要跨執行緒操作 UI 控制項
|
||||
if (MainForm.InvokeRequired)
|
||||
{
|
||||
// 如果目前不在 UI 執行緒上,必須透過 Invoke 安全執行 UI 更新邏輯
|
||||
Console.WriteLine("SongDisplayTimer_Tick invoked on UI thread.");
|
||||
if (MainForm.songDisplayLabel != null)
|
||||
MainForm.songDisplayLabel.Text = "";
|
||||
|
||||
MainForm.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (MainForm.topLeftLabel != null)
|
||||
MainForm.topLeftLabel.Visible = true;
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果已經在 UI 執行緒,就直接更新 UI 控制項
|
||||
Console.WriteLine("SongDisplayTimer_Tick invoked on background thread.");
|
||||
//MainForm.songDisplayLabel.Text = "";
|
||||
MainForm.topLeftLabel.Visible = true;
|
||||
}
|
||||
if (MainForm.nextSongLabel != null)
|
||||
MainForm.nextSongLabel.Visible = true;
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果已經在 UI 執行緒,就直接更新 UI 控制項
|
||||
Console.WriteLine("SongDisplayTimer_Tick invoked on background thread.");
|
||||
MainForm.songDisplayLabel.Text = "";
|
||||
MainForm.nextSongLabel.Visible = true;
|
||||
}
|
||||
|
||||
// 停止計時器,避免重複觸發
|
||||
songDisplayTimer.Stop();
|
||||
}
|
||||
// 停止計時器,避免重複觸發
|
||||
songDisplayTimer.Stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -478,7 +521,7 @@ namespace OverlayFormObj
|
||||
|
||||
private void DisplayMessage(string message, int duration)
|
||||
{
|
||||
MainForm.topLeftLabel.Visible = false;
|
||||
MainForm.nextSongLabel.Visible = false;
|
||||
displayLabel.Text = message;
|
||||
unifiedTimer.Interval = duration;
|
||||
unifiedTimer.Start();
|
||||
@ -518,6 +561,27 @@ namespace OverlayFormObj
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSongDisplayLabel(string newText)
|
||||
{
|
||||
|
||||
songDisplayLabel.Text = newText;
|
||||
|
||||
|
||||
songDisplayLabel.Font = new Font("Arial", 125);
|
||||
|
||||
songDisplayLabel.ForeColor = Color.White;
|
||||
|
||||
|
||||
songDisplayTimer.Stop();
|
||||
|
||||
|
||||
songDisplayTimer.Start();
|
||||
|
||||
|
||||
this.Invalidate();
|
||||
}
|
||||
|
||||
|
||||
private void SecondLineTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if ((DateTime.Now - secondLineStartTime).TotalMilliseconds >= secondLineDuration)
|
||||
@ -527,7 +591,19 @@ namespace OverlayFormObj
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ResetMarqueeTextToWelcomeMessage()
|
||||
{
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(Application.StartupPath,"txt","WelcomeMessage.txt");
|
||||
string welcomeMessage = File.ReadAllText(filePath);
|
||||
this.UpdateMarqueeText(welcomeMessage, MarqueeStartPosition.Right, Color.White);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error reading marquee text from file: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
@ -606,6 +682,69 @@ namespace OverlayFormObj
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void DisplayBarrage(string text)
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
{
|
||||
this.Invoke(new System.Action(() => DisplayBarrage(text)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Random rnd = new Random();
|
||||
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
Label lblBarrage = new Label
|
||||
{
|
||||
Text = text,
|
||||
AutoSize = true,
|
||||
ForeColor = Color.FromArgb(rnd.Next(256), rnd.Next(256), rnd.Next(256)),
|
||||
Font = new Font("Arial", rnd.Next(10, 50)),
|
||||
Location = new Point(rnd.Next(0, this.Width), rnd.Next(0, this.Height))
|
||||
};
|
||||
|
||||
|
||||
this.Controls.Add(lblBarrage);
|
||||
lblBarrage.BringToFront();
|
||||
|
||||
|
||||
System.Windows.Forms.Timer moveTimer = new System.Windows.Forms.Timer { Interval = 50 };
|
||||
moveTimer.Tick += (sender, e) =>
|
||||
{
|
||||
lblBarrage.Left -= 5;
|
||||
|
||||
if (lblBarrage.Right < 0)
|
||||
{
|
||||
lblBarrage.Dispose();
|
||||
moveTimer.Dispose();
|
||||
}
|
||||
};
|
||||
moveTimer.Start();
|
||||
|
||||
|
||||
int duration = rnd.Next(3000, 7000);
|
||||
System.Windows.Forms.Timer durationTimer = new System.Windows.Forms.Timer { Interval = duration };
|
||||
durationTimer.Tick += (sender, e) =>
|
||||
{
|
||||
if (moveTimer.Enabled)
|
||||
{
|
||||
moveTimer.Stop();
|
||||
moveTimer.Dispose();
|
||||
}
|
||||
|
||||
this.Controls.Remove(lblBarrage);
|
||||
lblBarrage.Dispose();
|
||||
durationTimer.Stop();
|
||||
durationTimer.Dispose();
|
||||
};
|
||||
durationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void DisplaySticker(string stickerId)
|
||||
{
|
||||
//Console.WriteLine("Attempting to display sticker.");
|
||||
@ -661,7 +800,7 @@ namespace OverlayFormObj
|
||||
public static string ReadSongNumber()
|
||||
{
|
||||
string songNumber = MainForm.displayLabel.Text;
|
||||
MainForm.topLeftLabel.Visible = false;
|
||||
MainForm.nextSongLabel.Visible = false;
|
||||
return songNumber;
|
||||
}
|
||||
|
||||
@ -712,9 +851,27 @@ namespace OverlayFormObj
|
||||
if (control != MainForm.displayLabel &&
|
||||
control != MainForm.pauseLabel &&
|
||||
control != MainForm.muteLabel &&
|
||||
control != MainForm.topRightLabel &&
|
||||
control != MainForm.volumeUpLabel &&
|
||||
control != MainForm.volumeDownLabel &&
|
||||
control != MainForm.micUpLabel &&
|
||||
control != MainForm.micDownLabel &&
|
||||
control != MainForm.standardKeyLabel &&
|
||||
control != MainForm.keyUpLabel &&
|
||||
control != MainForm.keyDownLabel &&
|
||||
control != MainForm.maleKeyLabel &&
|
||||
control != MainForm.femaleKeyLabel &&
|
||||
control != MainForm.squareLabel &&
|
||||
control != MainForm.professionalLabel &&
|
||||
control != MainForm.standardLabel &&
|
||||
control != MainForm.singDownLabel &&
|
||||
control != MainForm.brightLabel &&
|
||||
control != MainForm.softLabel &&
|
||||
control != MainForm.autoLabel &&
|
||||
control != MainForm.romanticLabel &&
|
||||
control != MainForm.dynamicLabel &&
|
||||
control != MainForm.tintLabel &&
|
||||
control != MainForm.blackBackgroundPanel &&
|
||||
control != MainForm.topLeftLabel)
|
||||
control != MainForm.nextSongLabel)
|
||||
{
|
||||
MainForm.Controls.Remove(control);
|
||||
// control.Dispose();
|
||||
@ -847,8 +1004,13 @@ namespace OverlayFormObj
|
||||
if (songIndex >= 0 && songIndex < totalSongs)
|
||||
{
|
||||
selectedSong = LanguageSongList[songIndex];
|
||||
Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].getName() + " " + selectedSong.getFileName());
|
||||
SongList.Add(selectedSong);
|
||||
Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].Name + " " + selectedSong.FileName);
|
||||
|
||||
|
||||
// DisplayActionWithSong(currentPage, songIndex, "點播");
|
||||
|
||||
|
||||
AddSongToPlaylist(selectedSong);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -874,8 +1036,13 @@ namespace OverlayFormObj
|
||||
if (songIndex < totalSongs)
|
||||
{
|
||||
selectedSong = LanguageSongList[songIndex];
|
||||
Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].getName() + " " + selectedSong.getFileName() );
|
||||
SongList.Insert(selectedSong);
|
||||
Console.WriteLine("Adding song to playlist: " + LanguageSongList[songIndex].Name + " " + selectedSong.FileName );
|
||||
|
||||
|
||||
// DisplayActionWithSong(currentPage, songIndex, "插播");
|
||||
|
||||
|
||||
InsertSongToPlaylist(selectedSong);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -953,7 +1120,7 @@ namespace OverlayFormObj
|
||||
if (songIndex < totalSongs)
|
||||
{
|
||||
selectedSong = LanguageSongList[songIndex];
|
||||
Console.WriteLine("Adding song to playlist C: " + LanguageSongList[songIndex].getName() + " " + selectedSong.getFileName());
|
||||
Console.WriteLine("Adding song to playlist C: " + LanguageSongList[songIndex].Name + " " + selectedSong.FileName);
|
||||
|
||||
|
||||
DisplaySongsWithArrows(currentPage, songIndex);
|
||||
@ -1006,16 +1173,34 @@ namespace OverlayFormObj
|
||||
if (control != displayLabel &&
|
||||
control != pauseLabel &&
|
||||
control != muteLabel &&
|
||||
control != topRightLabel &&
|
||||
control != volumeUpLabel &&
|
||||
control != volumeDownLabel &&
|
||||
control != micUpLabel &&
|
||||
control != micDownLabel &&
|
||||
control != standardKeyLabel &&
|
||||
control != keyUpLabel &&
|
||||
control != keyDownLabel &&
|
||||
control != maleKeyLabel &&
|
||||
control != femaleKeyLabel &&
|
||||
control != squareLabel &&
|
||||
control != professionalLabel &&
|
||||
control != standardLabel &&
|
||||
control != singDownLabel &&
|
||||
control != brightLabel &&
|
||||
control != softLabel &&
|
||||
control != autoLabel &&
|
||||
control != romanticLabel &&
|
||||
control != dynamicLabel &&
|
||||
control != tintLabel &&
|
||||
control != blackBackgroundPanel &&
|
||||
control != topLeftLabel)
|
||||
control != nextSongLabel)
|
||||
{
|
||||
this.Controls.Remove(control);
|
||||
control.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void UpdateHistoryLabel(List<SongData> historySongs, int currentPage, int totalPages)
|
||||
public void UpdateHistoryLabel(List<SongData> historySongs, List<PlayState> playStates, int currentPage, int totalPages)
|
||||
{
|
||||
|
||||
// 移除畫面上所有現有的 PictureBox(用於刷新內容)
|
||||
@ -1042,15 +1227,37 @@ namespace OverlayFormObj
|
||||
int rightMargin = this.Width - 100;
|
||||
|
||||
// 逐一繪製歷史歌曲列表
|
||||
|
||||
for (int i = 0; i < historySongs.Count; i++)
|
||||
{
|
||||
var song = historySongs[i]; // 歌曲資料
|
||||
string status=song.GetStateTxt(false); // 播放狀態文字(如「播放中」)
|
||||
Color textColor=song.GetStateColor(); // 對應的顏色
|
||||
var song = historySongs[i]; // 歌曲資料
|
||||
var state = playStates[i]; // 對應的播放狀態
|
||||
|
||||
string status; // 播放狀態文字(如「播放中」)
|
||||
Color textColor; // 對應的顏色
|
||||
|
||||
// 根據播放狀態決定狀態文字與顏色
|
||||
switch (state)
|
||||
{
|
||||
case PlayState.Played:
|
||||
status = "(播畢)";
|
||||
textColor = Color.FromArgb(200, 75, 125); // 播畢顏色:紫紅色
|
||||
break;
|
||||
case PlayState.Playing:
|
||||
status = "(播放中)";
|
||||
textColor = Color.LimeGreen; // 播放中顏色:亮綠
|
||||
break;
|
||||
case PlayState.NotPlayed:
|
||||
status = "";
|
||||
textColor = Color.White; // 尚未播放:白色
|
||||
break;
|
||||
default:
|
||||
status = "";
|
||||
textColor = Color.White;
|
||||
break;
|
||||
}
|
||||
|
||||
// 建立顯示的歌曲文字,例如 "1. 我的歌"
|
||||
string songText = $"{i + 1}. {song.getName()}";
|
||||
string songText = $"{i + 1}. {song.Name}";
|
||||
Font songFont = new Font("Microsoft JhengHei", 50, FontStyle.Bold);
|
||||
|
||||
// 將歌名轉成圖片
|
||||
@ -1319,7 +1526,100 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
ClearDisplay();
|
||||
DisplaySongs(currentPage);
|
||||
}
|
||||
|
||||
// 歌庫路徑調整
|
||||
public void AddSongToPlaylist(SongData songData)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 之後還要設計成本地的資料夾位置
|
||||
string pathToPlay = songData.getFile();
|
||||
if (!string.IsNullOrEmpty(pathToPlay))
|
||||
{
|
||||
bool wasEmpty = PrimaryForm.userRequestedSongs.Count == 0;
|
||||
|
||||
PrimaryForm.userRequestedSongs.Add(songData);
|
||||
PrimaryForm.playedSongsHistory.Add(songData);
|
||||
PrimaryForm.playStates.Add(wasEmpty ? PlayState.Playing : PlayState.NotPlayed);
|
||||
// 刷新頁面
|
||||
|
||||
if (PrimaryForm.Instance.multiPagePanel.get_currentSongList() == PrimaryForm.playedSongsHistory)
|
||||
PrimaryForm.Instance.multiPagePanel.LoadSongs(PrimaryForm.Instance.currentSongList);
|
||||
if (wasEmpty)
|
||||
{
|
||||
VideoPlayerForm.Instance.SetPlayingSongList(PrimaryForm.userRequestedSongs);
|
||||
PrimaryForm.currentSongIndexInHistory += 1;
|
||||
}
|
||||
VideoPlayerForm.Instance.UpdateNextSongFromPlaylist();
|
||||
PrimaryForm.PrintPlayingSongList();
|
||||
|
||||
// 點播次數+1
|
||||
PrimaryForm.Instance.AddSongCount(songData.Number);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error occurred: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void InsertSongToPlaylist(SongData songData)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 從 songData 中取得兩個可能的檔案路徑(主機1與主機2)
|
||||
var pathToPlay = songData.getFile();
|
||||
|
||||
// 檢查兩個主機上的檔案是否皆不存在
|
||||
if (!string.IsNullOrEmpty(pathToPlay))
|
||||
{
|
||||
// 若其中一個存在,就用第一個存在的那個檔案
|
||||
|
||||
Console.WriteLine("path to play" + pathToPlay);
|
||||
|
||||
// 檢查目前使用者歌單是否為空
|
||||
bool wasEmpty = PrimaryForm.userRequestedSongs.Count == 0;
|
||||
|
||||
if (wasEmpty)
|
||||
{
|
||||
// 若是空的,代表是第一首歌,直接加入並設為正在播放
|
||||
PrimaryForm.userRequestedSongs.Add(songData); // 加入播放清單
|
||||
VideoPlayerForm.Instance.SetPlayingSongList(PrimaryForm.userRequestedSongs); // 傳給播放器
|
||||
PrimaryForm.playedSongsHistory.Add(songData); // 加入播放歷史
|
||||
PrimaryForm.playStates.Add(PlayState.Playing); // 設定狀態為正在播放
|
||||
PrimaryForm.currentSongIndexInHistory += 1; // 歷史索引 +1
|
||||
}
|
||||
else if (PrimaryForm.userRequestedSongs.Count == 1)
|
||||
{
|
||||
// 若清單中已有一首,插入新歌在 index 1 (即目前播放之後的位置)
|
||||
PrimaryForm.userRequestedSongs.Insert(1, songData);
|
||||
PrimaryForm.playedSongsHistory.Insert(PrimaryForm.currentSongIndexInHistory + 1, songData);
|
||||
PrimaryForm.playStates.Insert(PrimaryForm.currentSongIndexInHistory + 1, PlayState.NotPlayed); // 尚未播放
|
||||
}
|
||||
else
|
||||
{
|
||||
// 若清單中超過一首,同樣插入在 index 1,也是在當前歌曲之後的位置
|
||||
PrimaryForm.userRequestedSongs.Insert(1, songData);
|
||||
PrimaryForm.playedSongsHistory.Insert(PrimaryForm.currentSongIndexInHistory + 1, songData);
|
||||
PrimaryForm.playStates.Insert(PrimaryForm.currentSongIndexInHistory + 1, PlayState.NotPlayed);
|
||||
}
|
||||
|
||||
// 更新下一首即將播放的資訊(畫面或播放器邏輯用)
|
||||
VideoPlayerForm.Instance.UpdateNextSongFromPlaylist();
|
||||
|
||||
// 印出目前播放清單資訊到畫面或 console
|
||||
PrimaryForm.PrintPlayingSongList();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 捕捉所有例外並印出錯誤訊息(避免整個流程當掉)
|
||||
Console.WriteLine("Error occurred: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 測試
|
||||
public int currentPage = 1;
|
||||
public int songsPerPage = 5;
|
||||
@ -1381,7 +1681,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].getName(false)}";
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
maxSongLength = Math.Max(maxSongLength, songText.Length);
|
||||
@ -1401,7 +1701,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
// 計算行高
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].getName(false)}";
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
Font songFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold);
|
||||
@ -1418,7 +1718,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
{
|
||||
int songNumber = i - startIndex + 1;
|
||||
|
||||
string songText = $"{songNumber}. {LanguageSongList[i].getName(false)}";
|
||||
string songText = $"{songNumber}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
Font songFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold);
|
||||
@ -1491,7 +1791,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
int maxArtistLength = 0;
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].getName()}";
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
maxSongLength = Math.Max(maxSongLength, songText.Length);
|
||||
@ -1507,7 +1807,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
int rowHeight = 0;
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].getName(false)}";
|
||||
string songText = $"{i - startIndex + 1}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
Font tempSongFont = new Font("Microsoft JhengHei", songFontSize, FontStyle.Bold);
|
||||
@ -1524,7 +1824,7 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
{
|
||||
int songNumber = i - startIndex + 1;
|
||||
|
||||
string songText = $"{songNumber}. {LanguageSongList[i].getName(false)}";
|
||||
string songText = $"{songNumber}. {LanguageSongList[i].Name}";
|
||||
string artistText = LanguageSongList[i].artist_text();
|
||||
|
||||
// 設定顏色,選中的索引顯示為亮綠色
|
||||
@ -1547,6 +1847,67 @@ private void DisplayArtists(List<Artist> artists, int page)//歌星點進去後
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void DisplayActionWithSong(int page, int songIndex, string actionType)
|
||||
{
|
||||
// try
|
||||
// {
|
||||
// if (LanguageSongList == null || LanguageSongList.Count == 0)
|
||||
// {
|
||||
// Console.WriteLine("Error: LanguageSongList is null or empty.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// SongData song = LanguageSongList[songIndex];
|
||||
|
||||
// this.Controls.OfType<PictureBox>().ToList().ForEach(p => this.Controls.Remove(p));
|
||||
|
||||
// int songsPerColumn = 5;
|
||||
// int startIndex = (page - 1) * songsPerPage;
|
||||
// int endIndex = Math.Min(startIndex + songsPerPage, LanguageSongList.Count);
|
||||
|
||||
// int totalPages = (int)Math.Ceiling((double)LanguageSongList.Count / songsPerPage);
|
||||
|
||||
// string headerText = $"{actionType}: {song.ArtistA} - {song.Song} ({page} / {totalPages})";
|
||||
// Font headerFont = new Font("Microsoft JhengHei", 40, FontStyle.Bold);
|
||||
// Color headerColor = actionType == "點播" ? Color.LimeGreen : Color.Yellow;
|
||||
// Bitmap headerBitmap = GenerateTextImage(headerText, headerFont, headerColor, Color.Transparent);
|
||||
// AddCenteredPicture(headerBitmap, 150);
|
||||
|
||||
// int startY = 250;
|
||||
// int verticalSpacing = 10;
|
||||
// int leftColumnX = 200;
|
||||
// int rightColumnX = this.Width / 2 + 150;
|
||||
|
||||
// for (int i = startIndex; i < endIndex; i++)
|
||||
// {
|
||||
// int songNumber = i - startIndex + 1;
|
||||
// string songText = $"{songNumber}. {LanguageSongList[i].Song}";
|
||||
// string artistText = !string.IsNullOrWhiteSpace(LanguageSongList[i].ArtistB)
|
||||
// ? $"{LanguageSongList[i].ArtistA} - {LanguageSongList[i].ArtistB}"
|
||||
// : LanguageSongList[i].ArtistA;
|
||||
|
||||
// Font songFont = new Font("Microsoft JhengHei", 40, FontStyle.Bold);
|
||||
// Bitmap songBitmap = GenerateTextImage(songText, songFont, Color.White, Color.Transparent);
|
||||
|
||||
// Font artistFont = new Font("Microsoft JhengHei", 30, FontStyle.Bold);
|
||||
// Bitmap artistBitmap = GenerateTextImage(artistText, artistFont, Color.White, Color.Transparent);
|
||||
|
||||
// int x = (i - startIndex) < songsPerColumn ? leftColumnX : rightColumnX;
|
||||
// int y = startY + ((i - startIndex) % songsPerColumn) * (songBitmap.Height + verticalSpacing);
|
||||
|
||||
// AddPicture(songBitmap, x, y);
|
||||
// AddPicture(artistBitmap, x + songBitmap.Width + 20, y);
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Console.WriteLine($"Error in DisplayActionWithSong: {ex.Message}");
|
||||
// Console.WriteLine(ex.StackTrace);
|
||||
// }
|
||||
}
|
||||
|
||||
public void NextPage()
|
||||
{
|
||||
|
||||
|
25
PlayState.cs
25
PlayState.cs
@ -1,26 +1,11 @@
|
||||
using System.ComponentModel;
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
|
||||
|
||||
public enum PlayState
|
||||
{
|
||||
[LocalizedDescription("", "")]//未播放
|
||||
NotPlayed,
|
||||
|
||||
[LocalizedDescription("播放中", "播放中")]
|
||||
Playing,
|
||||
|
||||
[LocalizedDescription("播畢", "播毕")]
|
||||
Played,
|
||||
|
||||
[LocalizedDescription("插播", "插播")]
|
||||
InsertPlayback,
|
||||
|
||||
[LocalizedDescription("無文件", "无文件")]
|
||||
NoFile,
|
||||
|
||||
[LocalizedDescription("已跳過", "已跳过")]
|
||||
Skipped
|
||||
|
||||
Playing,
|
||||
Played,
|
||||
NotPlayed,
|
||||
Skipped
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("F1"); // Set the query for SQL to filter by genre "F1"
|
||||
var chinaSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = chinaSongs;
|
||||
totalPages = (int)Math.Ceiling((double)chinaSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(chinaSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("A1"); // Set the query for SQL to filter by genre "F1"
|
||||
var loveDuetSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = loveDuetSongs;
|
||||
totalPages = (int)Math.Ceiling((double)loveDuetSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(loveDuetSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("C1"); // Set the query for SQL to filter by genre "F1"
|
||||
var medleyDanceSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = medleyDanceSongs;
|
||||
totalPages = (int)Math.Ceiling((double)medleyDanceSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(medleyDanceSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("D1"); // Set the query for SQL to filter by genre "F1"
|
||||
var ninetiesSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = ninetiesSongs;
|
||||
totalPages = (int)Math.Ceiling((double)ninetiesSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(ninetiesSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("E1"); // Set the query for SQL to filter by genre "F1"
|
||||
var nostalgicSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = nostalgicSongs;
|
||||
totalPages = (int)Math.Ceiling((double)nostalgicSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(nostalgicSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("B1"); // Set the query for SQL to filter by genre "F1"
|
||||
var talentShowSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = talentShowSongs;
|
||||
totalPages = (int)Math.Ceiling((double)talentShowSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(talentShowSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("G1"); // Set the query for SQL to filter by genre "F1"
|
||||
var vietnameseSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = vietnameseSongs;
|
||||
totalPages = (int)Math.Ceiling((double)vietnameseSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(vietnameseSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -185,11 +185,12 @@ namespace DualScreenDemo
|
||||
string query = SetQueryforSQL("A1");
|
||||
var loveDuetSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = loveDuetSongs;
|
||||
totalPages = (int)Math.Ceiling((double)loveDuetSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(loveDuetSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
SetHotSongButtonsVisibility(false);
|
||||
|
@ -17,11 +17,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '國語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var guoYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = guoYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(guoYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,11 +17,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '韓語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var hanYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = hanYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)hanYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(hanYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,11 +18,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '日語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var riYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = riYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)riYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(riYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,11 +17,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '台語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var taiYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = taiYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)taiYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(taiYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,11 +17,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '英語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var yingWenSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = yingWenSongs;
|
||||
totalPages = (int)Math.Ceiling((double)yingWenSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(yingWenSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,11 +18,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '粵語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var yueYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = yueYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)yueYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(yueYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -136,11 +136,12 @@ namespace DualScreenDemo
|
||||
string query = "SELECT * FROM song_library_cache WHERE language_name = '國語' AND (`artistA_category` = '團' OR `artistB_category` = '團') ORDER BY song_counts DESC;";
|
||||
var guoYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = guoYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(guoYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
SetHotSongButtonsVisibility(false);
|
||||
|
@ -112,11 +112,12 @@ namespace DualScreenDemo
|
||||
private void UpdateSongList(List<SongData> songs)
|
||||
{
|
||||
currentPage = 0;
|
||||
currentSongList = songs;
|
||||
totalPages = (int)Math.Ceiling((double)songs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(songs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
|
||||
private void SetButtonsVisibility()
|
||||
|
@ -11,10 +11,11 @@ namespace DualScreenDemo
|
||||
var selectedSongs = PrimaryForm.Instance.SearchSongs_Mysql(query);
|
||||
|
||||
currentPage = 0;
|
||||
currentSongList = selectedSongs;
|
||||
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(selectedSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,10 +11,11 @@ namespace DualScreenDemo
|
||||
var selectedSongs = SearchSongs_Mysql(query);
|
||||
|
||||
currentPage = 0;
|
||||
currentSongList = selectedSongs;
|
||||
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(selectedSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,10 +78,13 @@ namespace DualScreenDemo
|
||||
string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' ORDER BY song_counts DESC LIMIT 100";
|
||||
var guoYuSongs = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = guoYuSongs;
|
||||
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(guoYuSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
SetHotSongButtonsVisibility(false);
|
||||
SetNewSongButtonsVisibility(false);
|
||||
@ -122,12 +125,16 @@ namespace DualScreenDemo
|
||||
activeButton.BackgroundImage = activeBackground;
|
||||
string query = $"SELECT * FROM song_library_cache WHERE language_name = '{category}' ORDER BY song_counts DESC LIMIT 100";
|
||||
var selectedSongs = SearchSongs_Mysql(query);
|
||||
|
||||
/*var selectedSongs = allSongs.Where(song => song.Category == category)
|
||||
.OrderByDescending(song => song.Plays)
|
||||
.ToList();*/
|
||||
currentPage = 0;
|
||||
currentSongList = selectedSongs;
|
||||
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(selectedSongs);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
|
||||
private void SetPictureBoxLanguageButtonsVisibility(bool isVisible)
|
||||
|
@ -89,11 +89,14 @@ namespace DualScreenDemo
|
||||
string query = setQueryforNewSong("國語");
|
||||
var guoYuSongs2 = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = guoYuSongs2;
|
||||
totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(guoYuSongs2);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
SetHotSongButtonsVisibility(false);
|
||||
SetSingerSearchButtonsVisibility(false);
|
||||
SetSongSearchButtonsVisibility(false);
|
||||
|
@ -221,10 +221,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(SearchFavoriteSongs_Mysql());
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
ToggleFavoritePictureBoxButtonsVisibility();
|
||||
}
|
||||
@ -256,10 +257,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(SearchFavoriteSongs_Mysql());
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
ToggleFavoritePictureBoxButtonsVisibility();
|
||||
}
|
||||
@ -272,10 +274,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(SearchFavoriteSongs_Mysql());
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
ToggleFavoritePictureBoxButtonsVisibility();
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ namespace DualScreenDemo
|
||||
RefreshDisplayBase_Singer();
|
||||
}
|
||||
|
||||
public void LoadPlayedSongs(List<SongData> songs)
|
||||
public void LoadPlayedSongs(List<SongData> songs, List<PlayState> states)
|
||||
{
|
||||
_isShowingSinger = false;
|
||||
currentSongList = songs;
|
||||
@ -376,20 +376,76 @@ namespace DualScreenDemo
|
||||
{
|
||||
// 創建歌曲標籤
|
||||
Label songLabel = new Label();
|
||||
string statusText;
|
||||
Color color;
|
||||
string statusText = ""; // 狀態文字
|
||||
|
||||
bool isCurrentlyPlaying = false; // 是否正在播放
|
||||
bool hasBeenPlayed = false; // 是否已播放完成
|
||||
bool isLatestInstance = true; // 是否是最新的歌曲
|
||||
|
||||
// 僅在 "已點歌曲" 頁面顯示播放狀態
|
||||
if (PrimaryForm.Instance.isOnOrderedSongsPage)
|
||||
{
|
||||
statusText = song.GetStateTxt(IsSimplified); // 狀態文字
|
||||
color = song.GetStateColor();
|
||||
// 判斷是否正在播放公播歌單 (若用戶點播歌單為空,則播放公播歌單)
|
||||
bool isPlayingPublicList = userRequestedSongs.Count == 0 ||
|
||||
(currentSongIndexInHistory >= userRequestedSongs.Count - 1 && PrimaryForm.Instance.videoPlayerForm.IsPlayingPublicSong);
|
||||
if (isPlayingPublicList)
|
||||
{
|
||||
// 若播放公播歌單,代表已點歌曲皆已播放完畢
|
||||
hasBeenPlayed = true;
|
||||
songLabel.ForeColor = Color.Gray;
|
||||
statusText = IsSimplified ? "(播毕)" : "(播畢)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 計算已完成播放的歌曲數量
|
||||
int completedCount = currentSongIndexInHistory;
|
||||
//Console.WriteLine("currentSongIndexInHistory:" + currentSongIndexInHistory);
|
||||
// 遍歷已點歌曲歷史
|
||||
/*for (int i = 0; i <= currentSongIndexInHistory && i < playedSongsHistory.Count; i++) {
|
||||
if (i == currentSongIndexInHistory) {
|
||||
completedCount = i + 1; // 当前播放的歌曲
|
||||
break;
|
||||
}
|
||||
// 检查播放状态
|
||||
if (i < playStates.Count) {
|
||||
if (playStates[i] == PlayState.Played || playStates[i] == PlayState.Playing) {
|
||||
completedCount++;
|
||||
}
|
||||
// 如果是切歌状态,不增加计数
|
||||
}
|
||||
}*/
|
||||
// 計算歌曲在歷史中的位置
|
||||
int songPosition = pageOffset + index;
|
||||
// 判斷歌曲狀態
|
||||
if (songPosition < completedCount)
|
||||
{
|
||||
// 已播放完成
|
||||
hasBeenPlayed = true;
|
||||
songLabel.ForeColor = Color.Gray;
|
||||
statusText = IsSimplified ? "(播毕)" : "(播畢)";
|
||||
}
|
||||
else if (songPosition == completedCount)
|
||||
{
|
||||
// 正在播放
|
||||
isCurrentlyPlaying = true;
|
||||
songLabel.ForeColor = Color.LimeGreen;
|
||||
statusText = IsSimplified ? "(播放中)" : "(播放中)";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 未播放
|
||||
songLabel.ForeColor = Color.White;
|
||||
statusText = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.White;
|
||||
// 未在 "已點歌曲" 頁面顯示白色
|
||||
songLabel.ForeColor = Color.White;
|
||||
statusText = string.Empty;
|
||||
}
|
||||
|
||||
songLabel.ForeColor = color;
|
||||
// 根據簡繁體設置選擇要顯示的文字
|
||||
string songText = song.getName(IsSimplified);
|
||||
|
||||
@ -451,7 +507,7 @@ namespace DualScreenDemo
|
||||
int artistWidth = (int)(this.Width * ArtistWidth);
|
||||
int artistX = songX + songWidth + 10;
|
||||
// 添加人聲標籤
|
||||
if (song.getHumanVoice() == 1)
|
||||
if (song.HumanVoice == 1)
|
||||
{
|
||||
PictureBox icon = new PictureBox()
|
||||
{
|
||||
@ -486,12 +542,20 @@ namespace DualScreenDemo
|
||||
};
|
||||
|
||||
// 根據文字長度設置字體大小
|
||||
int fold = 14;
|
||||
if (artistText.Length > 6)
|
||||
fold = 10;
|
||||
else if (artistText.Length > 3)
|
||||
fold = 12;
|
||||
artistLabel.Font = new Font("微軟正黑體", fold, FontStyle.Bold);
|
||||
{
|
||||
artistLabel.Font = new Font("微軟正黑體", 10, FontStyle.Bold);
|
||||
}
|
||||
else if (artistText.Length > 3)
|
||||
{
|
||||
artistLabel.Font = new Font("微軟正黑體", 12, FontStyle.Bold);
|
||||
}
|
||||
else
|
||||
{
|
||||
artistLabel.Font = new Font("微軟正黑體", 14, FontStyle.Bold);
|
||||
}
|
||||
|
||||
//artistLabel.Font = new Font("微軟正黑體", 16, FontStyle.Bold);
|
||||
|
||||
artistLabel.ForeColor = Color.FromArgb(30,144,255);
|
||||
songLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||||
@ -501,15 +565,48 @@ namespace DualScreenDemo
|
||||
artistLabel.BackColor = Color.Transparent;
|
||||
// 定義滑鼠進入 (MouseEnter) 事件的處理程序
|
||||
EventHandler mouseEnter = (sender, e) => {
|
||||
songLabel.ForeColor = Color.Yellow;
|
||||
artistLabel.ForeColor = Color.Yellow;
|
||||
separatorPanel.BackColor = Color.FromArgb(120, 255, 255, 255);
|
||||
|
||||
// 當不在「已點歌曲」頁面,或者該歌曲不是當前正在播放的歌曲,
|
||||
// 且(該歌曲未播放過或不是該歌曲的最新實例)時,改變顯示樣式
|
||||
if (!PrimaryForm.Instance.isOnOrderedSongsPage ||
|
||||
(!isCurrentlyPlaying && (!hasBeenPlayed || !isLatestInstance)))
|
||||
{
|
||||
// 當滑鼠移到歌曲上時,變更歌曲名稱為黃色
|
||||
songLabel.ForeColor = Color.Yellow;
|
||||
// 變更歌手名稱為黃色
|
||||
artistLabel.ForeColor = Color.Yellow;
|
||||
// 增強分隔線的亮度,使其更明顯
|
||||
separatorPanel.BackColor = Color.FromArgb(120, 255, 255, 255);
|
||||
}
|
||||
};
|
||||
// 定義滑鼠離開 (MouseLeave) 事件的處理程序
|
||||
EventHandler mouseLeave = (sender, e) => {
|
||||
songLabel.ForeColor = color;
|
||||
// 判斷是否處於「已點歌曲」頁面
|
||||
if (PrimaryForm.Instance.isOnOrderedSongsPage)
|
||||
{
|
||||
// 如果當前歌曲正在播放,則維持綠色顯示
|
||||
if (isCurrentlyPlaying)
|
||||
{
|
||||
songLabel.ForeColor = Color.LimeGreen;
|
||||
}
|
||||
// 如果該歌曲已播放完成,且是該歌曲的最新實例,則變為灰色
|
||||
else if (hasBeenPlayed && isLatestInstance)
|
||||
{
|
||||
songLabel.ForeColor = Color.Gray;
|
||||
}
|
||||
// 其他情況,則維持白色
|
||||
else
|
||||
{
|
||||
songLabel.ForeColor = Color.White;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 若不在「已點歌曲」頁面,則默認為白色
|
||||
songLabel.ForeColor = Color.White;
|
||||
}
|
||||
// 恢復歌手名稱的顏色為預設的藍色
|
||||
artistLabel.ForeColor = Color.FromArgb(30, 144, 255);
|
||||
// 恢復分隔線的顏色為半透明白色
|
||||
separatorPanel.BackColor = Color.FromArgb(80, 255, 255, 255);
|
||||
};
|
||||
// 添加事件处理
|
||||
|
@ -20,6 +20,23 @@ namespace DualScreenDemo
|
||||
|
||||
using (Image baseImage = Image.FromFile(imagePath))
|
||||
{
|
||||
|
||||
string serverAddressFilePath = Path.Combine(Application.StartupPath, "txt", "ip.txt");
|
||||
if (!File.Exists(serverAddressFilePath))
|
||||
{
|
||||
Console.WriteLine("Server address file not found: " + serverAddressFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
string serverAddress = File.ReadAllText(serverAddressFilePath).Trim();
|
||||
|
||||
// 根据地址格式生成不同的URL
|
||||
string qrContent = serverAddress.Contains(":") ?
|
||||
String.Format("http://{0}/{1}/windows.html", serverAddress, randomFolderPath) :
|
||||
String.Format("http://{0}:{1}/{2}/windows.html", serverAddress, 9090, randomFolderPath);
|
||||
// Console.WriteLine("QR Content: " + qrContent);
|
||||
|
||||
|
||||
string qrImagePath = Path.Combine(Application.StartupPath, "themes/superstar/_www", randomFolderPath, "qrcode.png");
|
||||
if (!File.Exists(qrImagePath))
|
||||
{
|
||||
|
@ -34,36 +34,77 @@ namespace DualScreenDemo
|
||||
this.syncCloseButton = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ResizeAndPositionControl(this.primaryScreenPanel, 0, 0, 1440, 900);
|
||||
this.primaryScreenPanel.TabIndex = 0;
|
||||
this.primaryScreenPanel.BorderStyle = BorderStyle.FixedSingle;
|
||||
this.primaryScreenPanel.BackColor = System.Drawing.Color.Black;
|
||||
|
||||
|
||||
|
||||
// 同步畫面 服務鈴
|
||||
ConfigureButton(this.syncServiceBellButton, 1240, 17, 161, 161,
|
||||
resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen,
|
||||
//(sender, e) => SendCommandThroughSerialPort("a2 53 a4"));
|
||||
(sender,e)=>OnServiceBellButtonClick(sender,e));
|
||||
|
||||
ConfigureButton(this.syncCutSongButton, 1218, 195, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, (sender, e) => videoPlayerForm.PlayNextSong());
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncCutSongButton, 1218, 195, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, (sender, e) => videoPlayerForm.SkipToNextSong());
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncReplayButton, 1218, 265, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, ReplayButton_Click);
|
||||
|
||||
|
||||
|
||||
// 有人聲入口位置
|
||||
ConfigureButton(this.syncOriginalSongButton, 1218, 335, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, (sender,e) => videoPlayerForm.ToggleVocalRemoval());
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncMuteButton, 1218, 406, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, MuteUnmuteButton_Click);
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncPauseButton, 1218, 475, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, SyncPauseButton_Click);
|
||||
ConfigureButton(this.syncPlayButton, 1218, 475, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, SyncPlayButton_Click);
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncVolumeUpButton, 1218, 546, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
|
||||
this.syncVolumeUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("音量 ↑ ","a2 b3 a4"); };
|
||||
this.syncVolumeUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
this.syncVolumeUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeUpLabel(); volumeUpTimer.Start(); };
|
||||
this.syncVolumeUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeUpTimer.Stop(); };
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncVolumeDownButton, 1218, 616, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
|
||||
this.syncVolumeDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("音量 ↓ ","a2 b4 a4");};
|
||||
this.syncVolumeDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel();};
|
||||
this.syncVolumeDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeDownLabel(); volumeDownTimer.Start(); };
|
||||
this.syncVolumeDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeDownTimer.Stop(); };
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncMicUpButton, 1218, 686, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
|
||||
this.syncMicUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("麥克風 ↑ ","a2 b5 a4");};
|
||||
this.syncMicUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
this.syncMicUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicUpLabel(); micControlTimer.Tag = "a2 b5 a4"; micControlTimer.Start(); };
|
||||
this.syncMicUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigureButton(this.syncMicDownButton, 1218, 756, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
|
||||
this.syncMicDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("麥克風 ↓ ","a2 b6 a4");};
|
||||
this.syncMicDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
this.syncMicDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicDownLabel(); micControlTimer.Tag = "a2 b6 a4"; micControlTimer.Start(); };
|
||||
this.syncMicDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System.IO;
|
||||
using DBObj;
|
||||
|
||||
using OverlayFormObj;
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public partial class PrimaryForm
|
||||
@ -114,21 +114,26 @@ namespace DualScreenDemo
|
||||
this.Controls.Add(favoriteButton);
|
||||
this.Controls.Add(vodScreenCloseButton);
|
||||
}
|
||||
private void VodButton_Click(object sender, EventArgs e)
|
||||
|
||||
|
||||
private async void VodButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetVodScreenPictureBoxAndButtonsVisibility(false);
|
||||
SongList.Add(currentSelectedSong);
|
||||
await Task.Delay(1000);
|
||||
OverlayForm.MainForm.AddSongToPlaylist(currentSelectedSong);
|
||||
|
||||
}
|
||||
|
||||
private void InsertButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
SetVodScreenPictureBoxAndButtonsVisibility(false);
|
||||
SongList.Insert(currentSelectedSong);
|
||||
OverlayForm.MainForm.InsertSongToPlaylist(currentSelectedSong);
|
||||
|
||||
}
|
||||
|
||||
private void AlbumButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
string name = currentSelectedSong.getArtist_A(false);
|
||||
string name = currentSelectedSong.Artist_A;
|
||||
string query = $"SELECT * FROM song_library_cache WHERE artistA = '{name}' ORDER BY add_date DESC;";
|
||||
var selectedSongs = SearchSongs_Mysql(query);
|
||||
/*var selectedSongs = allSongs.Where(song => song.ArtistA == currentSelectedSong.ArtistA)
|
||||
@ -141,21 +146,24 @@ namespace DualScreenDemo
|
||||
|
||||
private void FavoriteButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
Console.WriteLine("Favorite Button Clicked");
|
||||
InsertNewFavoriteSong(currentSelectedSong.getNumber());
|
||||
InsertNewFavoriteSong(currentSelectedSong.Number);
|
||||
var searchResults = SearchSongs_Mysql(SearchFavoriteSongs_Mysql());
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
SetVodScreenPictureBoxAndButtonsVisibility(false);
|
||||
}
|
||||
|
||||
private void VodScreenCloseButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
SetVodScreenPictureBoxAndButtonsVisibility(false);
|
||||
}
|
||||
|
||||
@ -163,23 +171,38 @@ namespace DualScreenDemo
|
||||
|
||||
private void SetVodScreenPictureBoxAndButtonsVisibility(bool isVisible)
|
||||
{
|
||||
|
||||
overlayPanel.Visible = isVisible;
|
||||
VodScreenPictureBox.Visible = isVisible;
|
||||
|
||||
|
||||
vodButton.Visible = isVisible;
|
||||
insertButton.Visible = isVisible;
|
||||
albumButton.Visible = isVisible;
|
||||
favoriteButton.Visible = isVisible;
|
||||
vodScreenCloseButton.Visible = isVisible;
|
||||
if (isVisible){
|
||||
if (isLoggedIn){
|
||||
|
||||
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
|
||||
if (isLoggedIn)
|
||||
{
|
||||
favoriteButton.Enabled = true;
|
||||
favoriteButton.Controls.Remove(disabledPanel);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
favoriteButton.Enabled = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
overlayPanel.BringToFront();
|
||||
VodScreenPictureBox.BringToFront();
|
||||
|
||||
|
||||
vodButton.BringToFront();
|
||||
insertButton.BringToFront();
|
||||
albumButton.BringToFront();
|
||||
|
@ -118,7 +118,12 @@ namespace DualScreenDemo
|
||||
private static Bitmap normalStateImageForLightControl;
|
||||
private static Bitmap resizedNormalStateImageForLightControl;
|
||||
public VideoPlayerForm videoPlayerForm;
|
||||
public List<SongData> currentSongList;
|
||||
public List<Artist> currentArtistList;
|
||||
public static List<SongData> userRequestedSongs;
|
||||
public static List<SongData> playedSongsHistory;
|
||||
public static List<PlayState> playStates;
|
||||
public static int currentSongIndexInHistory = -1;
|
||||
public MultiPagePanel multiPagePanel;
|
||||
private List<Label> songLabels = new List<Label>();
|
||||
private int _currentPage { get; set; }= 0;
|
||||
@ -150,7 +155,10 @@ namespace DualScreenDemo
|
||||
private const int PanelEndTop = 739; // 修改為實際需要的下邊界
|
||||
|
||||
private Timer lightControlTimer;
|
||||
|
||||
public Timer volumeUpTimer;
|
||||
public Timer volumeDownTimer;
|
||||
public Timer micControlTimer;
|
||||
|
||||
private SequenceManager sequenceManager = new SequenceManager();
|
||||
private PictureBox buttonMiddle;
|
||||
private PictureBox buttonTopRight;
|
||||
@ -198,9 +206,19 @@ namespace DualScreenDemo
|
||||
lightControlTimer = new Timer();
|
||||
lightControlTimer.Interval = 5;
|
||||
lightControlTimer.Tick += LightControlTimer_Tick;
|
||||
|
||||
volumeUpTimer = new Timer();
|
||||
volumeUpTimer.Interval = 100;
|
||||
volumeUpTimer.Tick += VolumeUpTimer_Tick;
|
||||
volumeDownTimer = new Timer();
|
||||
volumeDownTimer.Interval = 100;
|
||||
volumeDownTimer.Tick += VolumeDownTimer_Tick;
|
||||
micControlTimer = new Timer();
|
||||
micControlTimer.Interval = 100;
|
||||
micControlTimer.Tick += MicControlTimer_Tick;
|
||||
|
||||
InitializeRecording();
|
||||
InitializeMediaPlayer();
|
||||
LoadSongData();
|
||||
LoadImages();
|
||||
InitializeFormAndControls();
|
||||
InitializeMultiPagePanel();
|
||||
@ -222,6 +240,23 @@ namespace DualScreenDemo
|
||||
this.Load += PrimaryForm_Load;
|
||||
|
||||
}
|
||||
public bool IsAppResponsive()
|
||||
{
|
||||
try
|
||||
{
|
||||
var form = this;
|
||||
if (form != null)
|
||||
{
|
||||
bool dummy = form.InvokeRequired; // 如果 Invoke 卡死,會丟錯
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 添加 DPI 感知支持
|
||||
[DllImport("user32.dll")]
|
||||
@ -850,7 +885,7 @@ namespace DualScreenDemo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LightControlTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if(lightControlTimer.Tag != null)
|
||||
@ -859,6 +894,56 @@ namespace DualScreenDemo
|
||||
}
|
||||
}
|
||||
|
||||
private void VolumeUpTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
SendCommandThroughSerialPort("a2 b3 a4");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to send command: " + ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void VolumeDownTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
SendCommandThroughSerialPort("a2 b4 a4");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to send command: " + ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void MicControlTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if(micControlTimer.Tag != null)
|
||||
{
|
||||
SendCommandThroughSerialPort(micControlTimer.Tag.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to send command: " + ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void SendCommandThroughSerialPort(string command)
|
||||
{
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
@ -904,6 +989,10 @@ namespace DualScreenDemo
|
||||
|
||||
if (status == RecognitionStatus.NoError)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
List<string> candidates = new List<string>();
|
||||
foreach (RecognitionAlternate alternate in result.GetAlternatesFromSelection())
|
||||
{
|
||||
@ -1013,7 +1102,7 @@ namespace DualScreenDemo
|
||||
qieGeButton.Name = "qieGeButton";
|
||||
ConfigureButton(qieGeButton, 28, 755, 92, 132,
|
||||
resizedNormalStateImage, resizedNormalStateImage, resizedNormalStateImage,
|
||||
(sender, e) => videoPlayerForm.PlayNextSong());
|
||||
(sender, e) => videoPlayerForm.SkipToNextSong());
|
||||
this.Controls.Add(qieGeButton);
|
||||
|
||||
|
||||
@ -1025,8 +1114,8 @@ namespace DualScreenDemo
|
||||
musicUpButton.BackgroundImageLayout = ImageLayout.Stretch;
|
||||
musicUpButton.FlatStyle = FlatStyle.Flat;
|
||||
musicUpButton.FlatAppearance.BorderSize = 0;
|
||||
musicUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("音量 ↑","a2 b3 a4"); };
|
||||
musicUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
musicUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeUpLabel(); volumeUpTimer.Start(); };
|
||||
musicUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeUpTimer.Stop(); };
|
||||
this.Controls.Add(musicUpButton);
|
||||
|
||||
|
||||
@ -1038,8 +1127,8 @@ namespace DualScreenDemo
|
||||
musicDownButton.BackgroundImageLayout = ImageLayout.Stretch;
|
||||
musicDownButton.FlatStyle = FlatStyle.Flat;
|
||||
musicDownButton.FlatAppearance.BorderSize = 0;
|
||||
musicDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("音量 ↓","a2 b4 a4"); };
|
||||
musicDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
musicDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeDownLabel(); volumeDownTimer.Start(); };
|
||||
musicDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeDownTimer.Stop(); };
|
||||
this.Controls.Add(musicDownButton);
|
||||
|
||||
|
||||
@ -1051,8 +1140,8 @@ namespace DualScreenDemo
|
||||
micUpButton.BackgroundImageLayout = ImageLayout.Stretch;
|
||||
micUpButton.FlatStyle = FlatStyle.Flat;
|
||||
micUpButton.FlatAppearance.BorderSize = 0;
|
||||
micUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowTopRightLabel("麥克風 ↑","a2 b5 a4"); };
|
||||
micUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
micUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicUpLabel(); micControlTimer.Tag = "a2 b5 a4"; micControlTimer.Start(); };
|
||||
micUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
|
||||
this.Controls.Add(micUpButton);
|
||||
|
||||
|
||||
@ -1064,8 +1153,8 @@ namespace DualScreenDemo
|
||||
micDownButton.BackgroundImageLayout = ImageLayout.Stretch;
|
||||
micDownButton.FlatStyle = FlatStyle.Flat;
|
||||
micDownButton.FlatAppearance.BorderSize = 0;
|
||||
micDownButton.MouseDown += (sender, e) =>{ OverlayForm.MainForm.ShowTopRightLabel("麥克風 ↓","a2 b6 a4"); };
|
||||
micDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.RedisplayTopRigthLabel(); };
|
||||
micDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicDownLabel(); micControlTimer.Tag = "a2 b6 a4"; micControlTimer.Start(); };
|
||||
micDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
|
||||
this.Controls.Add(micDownButton);
|
||||
|
||||
|
||||
@ -1349,6 +1438,8 @@ namespace DualScreenDemo
|
||||
}
|
||||
this.Controls.Add(button);
|
||||
}
|
||||
|
||||
|
||||
private void InitializeMultiPagePanel()
|
||||
{
|
||||
// 獲取螢幕尺寸
|
||||
@ -1405,6 +1496,15 @@ namespace DualScreenDemo
|
||||
}
|
||||
}
|
||||
|
||||
public static void PrintPlayingSongList()
|
||||
{
|
||||
Console.WriteLine("當前播放列表:");
|
||||
foreach (var song in userRequestedSongs)
|
||||
{
|
||||
Console.WriteLine(song.name_text());
|
||||
}
|
||||
}
|
||||
|
||||
private void NextPageButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
multiPagePanel.LoadNextPage();
|
||||
@ -1436,6 +1536,14 @@ namespace DualScreenDemo
|
||||
{
|
||||
multiPagePanel.LoadPreviousPage();
|
||||
}
|
||||
|
||||
private void LoadSongData()
|
||||
{
|
||||
userRequestedSongs = new List<SongData>();
|
||||
playedSongsHistory = new List<SongData>();
|
||||
playStates = new List<PlayState>();
|
||||
|
||||
}
|
||||
|
||||
private Bitmap ResizeImage(Image image, int width, int height)
|
||||
{
|
||||
@ -1593,7 +1701,7 @@ namespace DualScreenDemo
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private void DrawTextOnVodScreenPictureBox(string imagePath, SongData SongDetail)
|
||||
private void DrawTextOnVodScreenPictureBox(string imagePath, SongData songData)
|
||||
{
|
||||
Bitmap originalImage = new Bitmap(imagePath);
|
||||
|
||||
@ -1616,11 +1724,11 @@ namespace DualScreenDemo
|
||||
Font font = new Font("微軟正黑體", points, FontStyle.Bold);
|
||||
// 根據文字長度設置字體大小
|
||||
|
||||
if (SongDetail.getNameLength() > 18)
|
||||
if (songData.getNameLength() > 18)
|
||||
{
|
||||
font = new Font("微軟正黑體", 17, FontStyle.Bold);
|
||||
}
|
||||
else if (SongDetail.getNameLength() > 13)
|
||||
else if (songData.getNameLength() > 13)
|
||||
{
|
||||
font = new Font("微軟正黑體", 21, FontStyle.Bold);
|
||||
}
|
||||
@ -1633,7 +1741,7 @@ namespace DualScreenDemo
|
||||
|
||||
Brush textBrush = Brushes.Black;
|
||||
|
||||
string songInfo = SongDetail.getName() ?? "未提供歌曲信息";
|
||||
string songInfo = songData.Name ?? "未提供歌曲信息";
|
||||
|
||||
g.DrawString(songInfo, font, textBrush, new PointF(201, 29));
|
||||
|
||||
@ -1827,7 +1935,7 @@ namespace DualScreenDemo
|
||||
foreach(var song in userRequestedSongs){
|
||||
Console.WriteLine(song.ToString());
|
||||
}*/
|
||||
if( multiPagePanel.get_currentSongList() == SongList.GetHistory())
|
||||
if( multiPagePanel.get_currentSongList() == playedSongsHistory)
|
||||
return;
|
||||
autoRefreshTimer.Stop(); // 停止自动刷新
|
||||
FindFirstNonEmptyText(inputBoxZhuYinSingers
|
||||
@ -1938,45 +2046,65 @@ namespace DualScreenDemo
|
||||
|
||||
private void MaleKeyButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
|
||||
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
byte[] commandBytesDecreasePitch = new byte[] { 0xA2, 0xB2, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("串口未開啟,無法發送降調指令。");
|
||||
}
|
||||
|
||||
OverlayForm.MainForm.ShowMaleKeyLabel();
|
||||
}
|
||||
|
||||
private void FemaleKeyButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
|
||||
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
|
||||
|
||||
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0xB1, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
MessageBox.Show("串口未開啟,無法發送升調指令。");
|
||||
}
|
||||
|
||||
OverlayForm.MainForm.ShowFemaleKeyLabel();
|
||||
}
|
||||
|
||||
private void StandardKeyButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
OverlayForm.MainForm.ShowStandardKeyLabel();
|
||||
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
|
||||
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0x7F, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1986,10 +2114,15 @@ namespace DualScreenDemo
|
||||
|
||||
private void PitchUpButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
OverlayForm.MainForm.ShowKeyUpLabel();
|
||||
|
||||
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
{
|
||||
|
||||
byte[] commandBytesIncreasePitch = new byte[] { 0xA2, 0xB1, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
||||
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch, 0, commandBytesIncreasePitch.Length);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1999,10 +2132,15 @@ namespace DualScreenDemo
|
||||
|
||||
private void PitchDownButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
OverlayForm.MainForm.ShowKeyDownLabel();
|
||||
|
||||
|
||||
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
||||
{
|
||||
|
||||
byte[] commandBytesDecreasePitch = new byte[] { 0xA2, 0xB2, 0xA4 };
|
||||
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
||||
SerialPortManager.mySerialPort.Write(commandBytesDecreasePitch, 0, commandBytesDecreasePitch.Length);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2022,12 +2160,18 @@ namespace DualScreenDemo
|
||||
if (isWaiting) return;
|
||||
|
||||
isWaiting = true;
|
||||
|
||||
// 发送串口命令
|
||||
SendCommandThroughSerialPort("a2 53 a4");
|
||||
|
||||
// 显示提示信息
|
||||
//OverlayForm.MainForm.HideAllLabels();
|
||||
OverlayForm.MainForm.ShowTopRightLabel("服務鈴","a2 53 a4");
|
||||
OverlayForm.MainForm.HideAllLabels();
|
||||
OverlayForm.MainForm.ShowServiceBell();
|
||||
// 延迟3秒
|
||||
await Task.Delay(3000);
|
||||
|
||||
// 隐藏提示信息
|
||||
//OverlayForm.MainForm.HideServiceBellLabel();
|
||||
|
||||
isWaiting = false;
|
||||
}
|
||||
@ -2235,7 +2379,7 @@ namespace DualScreenDemo
|
||||
{
|
||||
HotPlayButton_Click(null, EventArgs.Empty);
|
||||
}
|
||||
if (Room.IsClose())
|
||||
if (Program.RoomState.Equals("CLOSE"))
|
||||
{
|
||||
ShowSendOffScreen();
|
||||
}
|
||||
@ -2268,8 +2412,8 @@ namespace DualScreenDemo
|
||||
isOnOrderedSongsPage = true;
|
||||
autoRefreshTimer.Start(); // 开始自动刷新
|
||||
// 已點歌曲錨點
|
||||
var List = SongList.GetHistory();
|
||||
totalPages = (int)Math.Ceiling((double)List.Count / itemsPerPage);
|
||||
currentSongList = playedSongsHistory;
|
||||
totalPages = (int)Math.Ceiling((double)playedSongsHistory.Count / itemsPerPage);
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
|
||||
SetHotSongButtonsVisibility(false);
|
||||
@ -2294,7 +2438,7 @@ namespace DualScreenDemo
|
||||
closeQRCodeButton.Visible = false;
|
||||
}
|
||||
|
||||
multiPagePanel.LoadPlayedSongs(List);
|
||||
multiPagePanel.LoadPlayedSongs(playedSongsHistory, playStates);
|
||||
}
|
||||
}
|
||||
}
|
@ -317,10 +317,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,10 +236,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
}
|
||||
|
@ -294,10 +294,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
}
|
||||
|
@ -557,10 +557,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
|
||||
}
|
||||
|
@ -271,10 +271,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化拼音輸入框 (RichTextBox),並從 config.ini 讀取相關設定。
|
||||
|
@ -490,10 +490,11 @@ namespace DualScreenDemo
|
||||
var searchResults = SearchSongs_Mysql(query);
|
||||
// 重置分頁
|
||||
currentPage = 0;
|
||||
currentSongList = searchResults;
|
||||
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
|
||||
// 更新多頁面面板的內容
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResults);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
}
|
||||
|
||||
|
||||
|
@ -52,11 +52,12 @@ namespace DualScreenDemo
|
||||
string query = $"SELECT * FROM song_library_cache WHERE language_name = '國語' LIMIT 100;";
|
||||
var searchResult = SearchSongs_Mysql(query);
|
||||
currentPage = 0;
|
||||
currentSongList = searchResult;
|
||||
totalPages = (int)Math.Ceiling((double)searchResult.Count / itemsPerPage);
|
||||
|
||||
|
||||
multiPagePanel.currentPageIndex = 0;
|
||||
multiPagePanel.LoadSongs(searchResult);
|
||||
multiPagePanel.LoadSongs(currentSongList);
|
||||
|
||||
// 隱藏其他 UI
|
||||
SetHotSongButtonsVisibility(false);
|
||||
|
146
Program.cs
146
Program.cs
@ -1,28 +1,44 @@
|
||||
using System.IO;
|
||||
using Microsoft.Win32;
|
||||
using System.Diagnostics;
|
||||
using DBObj;
|
||||
using HeartbeatSender;
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
// 定义全局变量
|
||||
internal static DBObj.SongListManager songListManager;
|
||||
internal static SongListManager songListManager;
|
||||
//internal static ArtistManager artistManager;
|
||||
internal static SerialPortManager serialPortManager;
|
||||
private static PrimaryForm primaryForm; // 儲存實例的參考
|
||||
public static Room room = new Room();
|
||||
|
||||
public static string RoomState = Utils.Env.Get("RoomStates", "CLOSE");
|
||||
public static DataCheck.PublicSongChecker cherker;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Console.WriteLine("Server V.1.2.3 202507211828");
|
||||
if (Utils.Env.GetBool("IsCursor", true)) Cursor.Hide();
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) => Cursor.Show();
|
||||
Console.WriteLine("Server V.1.2.0 20250703");
|
||||
if(Utils.Env.GetBool("IsCursor", true))Cursor.Hide();
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
|
||||
{
|
||||
Cursor.Show();
|
||||
};
|
||||
//Console.WriteLine("正在喚醒SVR裝置(每3分鐘呼叫一次)...");
|
||||
//_ = Task.Run(async () =>
|
||||
// {
|
||||
// while (true)
|
||||
// {
|
||||
// _ = Directory.Exists(@"\\svr01\video");
|
||||
// _ = Directory.Exists(@"\\svr02\video");
|
||||
// await Task.Delay(180000); // 每3min送一次
|
||||
// }
|
||||
// });
|
||||
|
||||
//Console.WriteLine("正在與中控取得聯繫...");
|
||||
var sender = new heartbeatSender();
|
||||
var cherker=new DataCheck.PublicSongChecker(DBObj.SongList.PublicSong());
|
||||
cherker =new DataCheck.PublicSongChecker();
|
||||
|
||||
try
|
||||
{
|
||||
// COM 初始化
|
||||
@ -34,7 +50,7 @@ namespace DualScreenDemo
|
||||
}
|
||||
// 初始化管理器
|
||||
|
||||
songListManager = new DBObj.SongListManager(); // 使用单例
|
||||
songListManager = new SongListManager(); // 使用单例
|
||||
//artistManager = new ArtistManager();
|
||||
|
||||
var commandHandler = new CommandHandler(songListManager);
|
||||
@ -58,46 +74,86 @@ namespace DualScreenDemo
|
||||
|
||||
// 創建主窗體
|
||||
primaryForm = new PrimaryForm();
|
||||
//primaryForm.allSongs = songListManager.AllSongs;
|
||||
//primaryForm.allArtists = artistManager.AllArtists;
|
||||
primaryForm.StartPosition = FormStartPosition.Manual;
|
||||
primaryForm.Location = new Point(0, 0);
|
||||
primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
|
||||
|
||||
// 在完整初始化後檢查狀態
|
||||
if (Screen.AllScreens.Length > 1)
|
||||
{
|
||||
var secondaryScreen = Screen.AllScreens.FirstOrDefault(s => !s.Primary);
|
||||
if (secondaryScreen != null)
|
||||
{
|
||||
// 确保 primaryForm 和 videoPlayerForm 已经正确初始化
|
||||
if (primaryForm.videoPlayerForm == null)
|
||||
{
|
||||
primaryForm.videoPlayerForm = new VideoPlayerForm();
|
||||
}
|
||||
InitializeSecondaryScreen();
|
||||
|
||||
// 设置 videoPlayerForm 的位置和大小
|
||||
// primaryForm.videoPlayerForm.StartPosition = FormStartPosition.Manual;
|
||||
// primaryForm.videoPlayerForm.Location = secondaryScreen.WorkingArea.Location;
|
||||
// primaryForm.videoPlayerForm.Size = secondaryScreen.WorkingArea.Size;
|
||||
|
||||
// 显示 videoPlayerForm 在第二显示器
|
||||
primaryForm.videoPlayerForm.Show();
|
||||
|
||||
// 初始化公共播放列表
|
||||
primaryForm.videoPlayerForm.PlayNextSong();
|
||||
}
|
||||
}
|
||||
primaryForm.Show();
|
||||
Application.Run(primaryForm);
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLog(ex.ToString());
|
||||
//} finally {
|
||||
//SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;
|
||||
}
|
||||
finally
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool IsUrlAclExists(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = "http show urlacl",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
using (StreamReader reader = process.StandardOutput)
|
||||
{
|
||||
string output = reader.ReadToEnd();
|
||||
return output.Contains(url); // 检查是否包含指定 URL
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("检查 URL ACL 时出错: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void InitializeSecondaryScreen()
|
||||
{
|
||||
if (Screen.AllScreens.Length > 1)
|
||||
{
|
||||
var secondaryScreen = Screen.AllScreens.FirstOrDefault(s => !s.Primary);
|
||||
if (secondaryScreen != null)
|
||||
{
|
||||
// 确保 primaryForm 和 videoPlayerForm 已经正确初始化
|
||||
if (primaryForm.videoPlayerForm == null)
|
||||
{
|
||||
primaryForm.videoPlayerForm = new VideoPlayerForm();
|
||||
}
|
||||
|
||||
// 设置 videoPlayerForm 的位置和大小
|
||||
// primaryForm.videoPlayerForm.StartPosition = FormStartPosition.Manual;
|
||||
// primaryForm.videoPlayerForm.Location = secondaryScreen.WorkingArea.Location;
|
||||
// primaryForm.videoPlayerForm.Size = secondaryScreen.WorkingArea.Size;
|
||||
|
||||
// 显示 videoPlayerForm 在第二显示器
|
||||
primaryForm.videoPlayerForm.Show();
|
||||
|
||||
// 初始化公共播放列表
|
||||
primaryForm.videoPlayerForm.InitializePublicPlaylist(cherker.GetSongs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnDisplaySettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
@ -118,7 +174,7 @@ namespace DualScreenDemo
|
||||
{
|
||||
primaryForm.videoPlayerForm = new VideoPlayerForm();
|
||||
// primaryForm.primaryMediaPlayerForm = new PrimaryMediaPlayerForm(primaryForm, primaryForm.secondaryMediaPlayerForm);
|
||||
//primaryForm.videoPlayerForm.PlayNextSong();
|
||||
primaryForm.videoPlayerForm.InitializePublicPlaylist(cherker.GetSongs());
|
||||
primaryForm.videoPlayerForm.Show();
|
||||
}
|
||||
}
|
||||
@ -157,6 +213,26 @@ namespace DualScreenDemo
|
||||
Console.WriteLine(String.Format("Error writing to log file: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Form CreatePrimaryForm()
|
||||
{
|
||||
return new Form
|
||||
{
|
||||
WindowState = FormWindowState.Maximized,
|
||||
FormBorderStyle = FormBorderStyle.None
|
||||
};
|
||||
}
|
||||
|
||||
private static Form CreateSecondaryForm(Screen screen)
|
||||
{
|
||||
return new Form
|
||||
{
|
||||
Text = "Secondary Screen Form",
|
||||
StartPosition = FormStartPosition.Manual,
|
||||
Bounds = screen.Bounds,
|
||||
WindowState = FormWindowState.Maximized,
|
||||
FormBorderStyle = FormBorderStyle.None
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -10,19 +10,22 @@ namespace DataCheck
|
||||
public class PublicSongChecker
|
||||
{
|
||||
|
||||
private List<SongData> publicList;
|
||||
public PublicSongChecker(List<SongData> publicSongList)
|
||||
private List<SongData> publicSongList = new();
|
||||
public PublicSongChecker()
|
||||
{
|
||||
publicList = publicSongList;
|
||||
string serverPath = Utils.Env.GetPath("video", "");
|
||||
string localPath = @"D:\video";
|
||||
// 加入你要同步的資料夾
|
||||
SyncFolder(serverPath, localPath, "video", new[] { ".mpg" });
|
||||
Console.WriteLine($"publicList:{publicList.Count}");
|
||||
}
|
||||
public List<SongData> GetSongs()
|
||||
{
|
||||
return publicSongList;
|
||||
}
|
||||
|
||||
private void SyncFolder(string serverPath, string localPath, string label, string[] extensions)
|
||||
{
|
||||
|
||||
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
|
||||
if (!Directory.Exists(serverPath)) {
|
||||
Console.WriteLine($"找不到伺服器資料夾:{serverPath}");
|
||||
@ -54,15 +57,11 @@ namespace DataCheck
|
||||
}
|
||||
}
|
||||
string fileName = Path.GetFileNameWithoutExtension(serverFile.Key);
|
||||
if (fileName == "welcome") {
|
||||
DBObj.SongList.welcome=new SongData("0", "歡迎光臨", @"D:\video\welcome.mpg", 1 ,true);
|
||||
} else if (fileName == "CLOSE") {
|
||||
DBObj.SongList.close =new SongData("0", "結束播放", @"D:\video\CLOSE.MPG", 1 ,true);
|
||||
} else {
|
||||
//Console.WriteLine($"{fileName} {Path.Combine(localPath, serverFile.Key)}");
|
||||
publicList.Add(new SongData(
|
||||
publicList.Count.ToString(), // songNumber
|
||||
fileName, // song
|
||||
Match match = Regex.Match(fileName, @"^(?<songNumber>\d+)-.*?-(?<songName>[^-]+)-");
|
||||
if (match.Success) {
|
||||
publicSongList.Add(new SongData(
|
||||
match.Groups["songNumber"].Value, // songNumber
|
||||
match.Groups["songName"].Value, // song
|
||||
Path.Combine(localPath, serverFile.Key), // songFilePathHost1
|
||||
1, // priority
|
||||
true
|
||||
|
12
RemoteCommand.cs
Normal file
12
RemoteCommand.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public enum RemoteCommand
|
||||
{
|
||||
NextPage,
|
||||
PreviousPage,
|
||||
Play,
|
||||
Pause,
|
||||
Stop
|
||||
|
||||
}
|
||||
}
|
7
RemoteControlEventArgs.cs
Normal file
7
RemoteControlEventArgs.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public class RemoteControlEventArgs : EventArgs
|
||||
{
|
||||
public RemoteCommand Command { get; set; }
|
||||
}
|
||||
}
|
8
RequestData.cs
Normal file
8
RequestData.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public class RequestData
|
||||
{
|
||||
public string buttonId { get; set; }
|
||||
public string text { get; set; }
|
||||
}
|
||||
}
|
60
Room.cs
60
Room.cs
@ -1,60 +0,0 @@
|
||||
using OverlayFormObj;
|
||||
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public class Room
|
||||
{
|
||||
private static string State=Utils.Env.Get("RoomStates", "CLOSE");
|
||||
public Room(){}
|
||||
public static void set(string value)
|
||||
{
|
||||
string marqueeMessage = "歡迎使用超級巨星歡唱,與你共度美好時光。";
|
||||
Color c = Color.White;
|
||||
State = value;
|
||||
if (value.Equals("PAUSE"))
|
||||
{
|
||||
PrimaryForm.Instance.ShowSendOffScreen();
|
||||
VideoPlayerForm.Instance.Pause();
|
||||
marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!";
|
||||
c = Color.Red;
|
||||
}
|
||||
else if (value.Equals("OPEN"))
|
||||
{
|
||||
DBObj.SongList.clearSong();
|
||||
PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty);
|
||||
PrimaryForm.Instance.HideSendOffScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBObj.SongList.clearSong();
|
||||
PrimaryForm.Instance.pictureBoxQRCode.Visible = false;
|
||||
PrimaryForm.Instance.closeQRCodeButton.Visible = false;
|
||||
PrimaryForm.Instance.ShowSendOffScreen();
|
||||
|
||||
|
||||
OverlayForm.MainForm.topLeftLabel.Visible = false;
|
||||
|
||||
VideoPlayerForm.Instance.PlayNextSong();
|
||||
PrimaryForm.Instance.logout();
|
||||
|
||||
}
|
||||
|
||||
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, c);
|
||||
|
||||
}
|
||||
public static bool IsClose()
|
||||
{
|
||||
return State.Equals("CLOSE");
|
||||
}
|
||||
public static bool IsOpen()
|
||||
{
|
||||
return State.Equals("OPEN");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
26
SampleGrabberCallback.cs
Normal file
26
SampleGrabberCallback.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using DirectShowLib;
|
||||
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public class SampleGrabberCallback : ISampleGrabberCB
|
||||
{
|
||||
private VideoPlayerForm form;
|
||||
|
||||
public SampleGrabberCallback(VideoPlayerForm form)
|
||||
{
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int SampleCB(double SampleTime, IMediaSample pSample)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
127
SongChecker.cs
Normal file
127
SongChecker.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DBObj;
|
||||
|
||||
namespace DataCheck
|
||||
{
|
||||
public class SongChecker
|
||||
{
|
||||
private static List<SongData> playingSongList= new();
|
||||
//public static List<PlayState> playStates;
|
||||
private static List<SongData> publicSongList = new();
|
||||
private SongData welcome = new SongData("0", "歡迎光臨", @"D:\video\welcome.mpg", 1 ,true);
|
||||
private SongData close = new SongData("0", "結束播放", @"D:\video\CLOSE.MPG", 1 ,true);
|
||||
|
||||
public SongChecker()
|
||||
{
|
||||
string serverPath = Utils.Env.GetPath("video", "");
|
||||
string localPath = @"D:\video";
|
||||
// 加入你要同步的資料夾
|
||||
SyncFolder(serverPath, localPath, "video", new[] { ".mpg" });
|
||||
}
|
||||
public static bool AddSongToPlayList(SongData song)
|
||||
{
|
||||
playingSongList.Add(song);
|
||||
return true;
|
||||
}
|
||||
public static bool isPlayingUserSong()
|
||||
{
|
||||
return playingSongList.Count == 0;
|
||||
}
|
||||
public static SongData GetCurrentSong()
|
||||
{
|
||||
SongData song = null;
|
||||
int count = playingSongList.Count;
|
||||
if (count == 0)
|
||||
{
|
||||
song = publicSongList[0];
|
||||
publicSongList.RemoveAt(0);
|
||||
publicSongList.Add(song);
|
||||
}
|
||||
else
|
||||
{
|
||||
song = playingSongList[0];
|
||||
playingSongList.RemoveAt(0);
|
||||
}
|
||||
return song;
|
||||
}
|
||||
public static SongData GetNextSong()
|
||||
{
|
||||
return playingSongList.Count > 1 ? playingSongList[1] : null;
|
||||
}
|
||||
|
||||
private void SyncFolder(string serverPath, string localPath, string label, string[] extensions)
|
||||
{
|
||||
|
||||
if (!Directory.Exists(localPath)) Directory.CreateDirectory(localPath);
|
||||
if (!Directory.Exists(serverPath))
|
||||
{
|
||||
Console.WriteLine($"找不到伺服器資料夾:{serverPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var serverFiles = Directory.GetFiles(serverPath)
|
||||
.Where(f => extensions.Contains(Path.GetExtension(f), StringComparer.OrdinalIgnoreCase))
|
||||
.Select(f => new FileInfo(f))
|
||||
.ToDictionary(f => f.Name, f => f);
|
||||
|
||||
var localFiles = Directory.GetFiles(localPath)
|
||||
.Where(f => extensions.Contains(Path.GetExtension(f), StringComparer.OrdinalIgnoreCase))
|
||||
.Select(f => new FileInfo(f))
|
||||
.ToDictionary(f => f.Name, f => f);
|
||||
|
||||
// 1. 複製或更新檔案
|
||||
foreach (var serverFile in serverFiles)
|
||||
{
|
||||
string dest = Path.Combine(localPath, serverFile.Key);
|
||||
bool needsCopy = !localFiles.ContainsKey(serverFile.Key) ||
|
||||
serverFile.Value.LastWriteTime > localFiles[serverFile.Key].LastWriteTime;
|
||||
if (needsCopy)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(serverFile.Value.FullName, dest, true);
|
||||
Console.WriteLine($"✅ 更新 {label}: {serverFile.Key}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ 複製 {label} 失敗 {serverFile.Key}: {ex.Message}");
|
||||
continue; // 如果複製失敗就不加入 SongData
|
||||
}
|
||||
}
|
||||
string fileName = Path.GetFileNameWithoutExtension(serverFile.Key);
|
||||
Match match = Regex.Match(fileName, @"^(?<songNumber>\d+)-.*?-(?<songName>[^-]+)-");
|
||||
if (match.Success)
|
||||
{
|
||||
publicSongList.Add(new SongData(
|
||||
match.Groups["songNumber"].Value, // songNumber
|
||||
match.Groups["songName"].Value, // song
|
||||
Path.Combine(localPath, serverFile.Key), // songFilePathHost1
|
||||
1, // priority
|
||||
true
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 刪除多餘的本地檔案
|
||||
foreach (var localFile in localFiles)
|
||||
{
|
||||
if (!serverFiles.ContainsKey(localFile.Key))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(localFile.Value.FullName);
|
||||
Console.WriteLine($"刪除多餘{label}: {localFile.Key}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"刪除{label}失敗 {localFile.Key}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
TCPServer.cs
83
TCPServer.cs
@ -5,6 +5,7 @@ using System.Text.RegularExpressions;
|
||||
using System.IO; // 為 Path 和 File 提供支持
|
||||
using DBObj;
|
||||
using OverlayFormObj;
|
||||
using HeartbeatSender;
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public class TCPServer
|
||||
@ -122,17 +123,62 @@ namespace DualScreenDemo
|
||||
|
||||
if (command.Trim().Equals("X", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
PrimaryForm.Instance.pictureBoxQRCode.Visible = false;
|
||||
PrimaryForm.Instance.closeQRCodeButton.Visible = false;
|
||||
_ = SafeInvoke(VideoPlayerForm.Instance, async () =>
|
||||
{
|
||||
Room.set("CLOSE");
|
||||
try{
|
||||
await HttpServer.RestartServer();
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine("RestartServer 發生錯誤: " + ex.Message);
|
||||
if (IsFormReady(PrimaryForm.Instance))
|
||||
{
|
||||
await SafeInvoke(PrimaryForm.Instance, async () =>
|
||||
{
|
||||
PrimaryForm.Instance.ShowSendOffScreen();
|
||||
Console.WriteLine("開始設置新的播放列表");
|
||||
|
||||
|
||||
string closePath = @"D:\video\CLOSE.MPG";
|
||||
if (File.Exists(closePath))
|
||||
{
|
||||
SongData closeSong = new SongData("0", "結束播放",closePath, 1,true);
|
||||
VideoPlayerForm.publicPlaylist = new List<SongData>();
|
||||
VideoPlayerForm.playingSongList = new List<SongData>();
|
||||
PrimaryForm.playedSongsHistory = new List<SongData>();
|
||||
if (VideoPlayerForm.Instance.currentPlayingSong != null)
|
||||
{
|
||||
VideoPlayerForm.playingSongList.Add(VideoPlayerForm.Instance.currentPlayingSong);
|
||||
}
|
||||
|
||||
VideoPlayerForm.playingSongList.Add(closeSong);
|
||||
VideoPlayerForm.publicPlaylist.Add(closeSong);
|
||||
|
||||
PrimaryForm.userRequestedSongs = new List<SongData>();
|
||||
|
||||
if (IsFormReady(OverlayForm.MainForm))
|
||||
{
|
||||
OverlayForm.MainForm.nextSongLabel.Visible = false;
|
||||
}
|
||||
|
||||
VideoPlayerForm.Instance.PlayNextSong();
|
||||
PrimaryForm.Instance.logout();
|
||||
Console.WriteLine("已設置新的播放列表,包含當前歌曲和 CLOSE.MPG");
|
||||
try
|
||||
{
|
||||
await HttpServer.RestartServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("RestartServer 發生錯誤: " + ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"錯誤: 找不到檔案 {closePath}");
|
||||
}
|
||||
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
Program.RoomState = "CLOSE";
|
||||
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
||||
stream.Write(okResponse, 0, okResponse.Length);
|
||||
|
||||
@ -142,7 +188,17 @@ namespace DualScreenDemo
|
||||
if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 開台時跳至首頁
|
||||
Room.set("OPEN");
|
||||
|
||||
VideoPlayerForm.publicPlaylist = new List<SongData>();
|
||||
VideoPlayerForm.playingSongList = new List<SongData>();
|
||||
VideoPlayerForm.Instance.PlayPublicPlaylist();
|
||||
PrimaryForm.currentSongIndexInHistory = -1;
|
||||
PrimaryForm.Instance.HotPlayButton_Click(null, EventArgs.Empty);
|
||||
Program.RoomState = "OPEN";
|
||||
PrimaryForm.Instance.HideSendOffScreen();
|
||||
|
||||
OverlayForm.Instance.ResetMarqueeTextToWelcomeMessage();
|
||||
|
||||
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
||||
stream.Write(okResponse, 0, okResponse.Length);
|
||||
continue;
|
||||
@ -151,10 +207,17 @@ namespace DualScreenDemo
|
||||
{
|
||||
_ = SafeInvoke(PrimaryForm.Instance, () =>
|
||||
{
|
||||
Room.set("PAUSE");
|
||||
PrimaryForm.Instance.ShowSendOffScreen();
|
||||
VideoPlayerForm.Instance.Pause();
|
||||
string marqueeMessage = "發生火災,請跟隨引導至逃生出口!!!";
|
||||
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, Color.Red);
|
||||
});
|
||||
|
||||
// 更新狀態檔案(可選,若你要記錄狀態)
|
||||
Program.RoomState = "PAUSE";
|
||||
byte[] okResponse = Encoding.UTF8.GetBytes("OK\n");
|
||||
stream.Write(okResponse, 0, okResponse.Length);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -192,7 +255,7 @@ namespace DualScreenDemo
|
||||
*/
|
||||
}
|
||||
|
||||
//Console.WriteLine("Connection closed.");
|
||||
Console.WriteLine("Connection closed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
8
WindowStyles.cs
Normal file
8
WindowStyles.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace DualScreenDemo
|
||||
{
|
||||
public static class WindowStyles
|
||||
{
|
||||
public const int WS_CHILD = 0x40000000;
|
||||
public const int WS_CLIPSIBLINGS = 0x04000000;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ApplicationIcon>Images/superstar.ico</ApplicationIcon>
|
||||
<RootNamespace>superstar_1.2</RootNamespace>
|
||||
<RootNamespace>superstar</RootNamespace>
|
||||
<AssemblyName>superstar</AssemblyName>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
@ -389,6 +389,8 @@
|
||||
Song: row.cells[0].textContent,
|
||||
ArtistA: row.cells[1].textContent,
|
||||
SongNumber: row.cells[2].textContent,
|
||||
SongFilePathHost1: row.cells[3].textContent,
|
||||
SongFilePathHost2: row.cells[4].textContent
|
||||
};
|
||||
|
||||
window.selectedSong = song;
|
||||
@ -541,6 +543,8 @@
|
||||
<td>${song.Song}</td>
|
||||
<td>${song.ArtistA}</td>
|
||||
<td>${song.SongNumber}</td>
|
||||
<td style="display:none;">${song.SongFilePathHost1}</td>
|
||||
<td style="display:none;">${song.SongFilePathHost2}</td>
|
||||
`;
|
||||
row.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
Loading…
x
Reference in New Issue
Block a user