2025-04-07 16:54:10 +08:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using DBObj;
|
|
|
|
|
using OverlayFormObj;
|
2025-08-07 18:34:45 +08:00
|
|
|
|
using DualScreenDemo.Services;
|
2025-08-27 12:59:37 +08:00
|
|
|
|
using LibVLCSharp;
|
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
namespace DualScreenDemo
|
|
|
|
|
{
|
|
|
|
|
public class VideoPlayerForm : Form
|
|
|
|
|
{
|
|
|
|
|
#region 防止閃屏
|
2025-07-14 18:08:12 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
protected override CreateParams CreateParams
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
CreateParams cp = base.CreateParams;
|
|
|
|
|
cp.ExStyle |= 0x02000000;
|
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-20 13:12:10 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
|
|
|
|
// 单例实例
|
|
|
|
|
public static VideoPlayerForm Instance { get; private set; }
|
2025-06-20 13:12:10 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
// 导入user32.dll API
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
|
|
|
static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
|
|
|
|
|
|
|
|
|
// Windows API 函數
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
static extern IntPtr GetDesktopWindow();
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
|
|
|
|
|
|
|
|
|
|
[DllImport("user32.dll")]
|
|
|
|
|
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
|
|
|
|
|
|
|
|
|
|
// MONITORINFO 結構
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
struct MONITORINFO
|
|
|
|
|
{
|
|
|
|
|
public int cbSize;
|
|
|
|
|
public RECT rcMonitor;
|
|
|
|
|
public RECT rcWork;
|
|
|
|
|
public uint dwFlags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RECT 結構
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
struct RECT
|
|
|
|
|
{
|
|
|
|
|
public int Left;
|
|
|
|
|
public int Top;
|
|
|
|
|
public int Right;
|
|
|
|
|
public int Bottom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private const int GWL_EXSTYLE = -20;
|
|
|
|
|
private const int WS_EX_TOPMOST = 0x00000008;
|
|
|
|
|
private const uint SWP_NOZORDER = 0x0004;
|
2025-08-18 10:29:00 +08:00
|
|
|
|
private MediaService _mediaService0 = new MediaService();
|
|
|
|
|
private MediaService _mediaService1 = new MediaService();
|
2025-08-15 12:15:33 +08:00
|
|
|
|
|
2025-08-25 17:32:20 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
public static OverlayForm overlayForm;
|
|
|
|
|
public bool isMuted = false;
|
2025-08-13 09:51:11 +08:00
|
|
|
|
public int previousVolume = 100;
|
2025-04-07 16:54:10 +08:00
|
|
|
|
public bool isPaused = false;
|
|
|
|
|
private bool isSyncToPrimaryMonitor = false;
|
|
|
|
|
public bool IsSyncToPrimaryMonitor
|
|
|
|
|
{
|
|
|
|
|
get { return isSyncToPrimaryMonitor; }
|
|
|
|
|
set { isSyncToPrimaryMonitor = value; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Screen secondMonitor;
|
|
|
|
|
|
|
|
|
|
public VideoPlayerForm()
|
|
|
|
|
{
|
|
|
|
|
Instance = this;
|
|
|
|
|
// this.DoubleBuffered = true;
|
|
|
|
|
this.Load += VideoPlayerForm_Load;
|
|
|
|
|
this.Shown += VideoPlayerForm_Shown;
|
|
|
|
|
this.FormClosing += VideoPlayerForm_FormClosing;
|
|
|
|
|
InitializeOverlayForm(secondMonitor);
|
|
|
|
|
BringOverlayToFront();
|
|
|
|
|
HttpServer.OnDisplayBarrage += DisplayBarrageOnOverlay;
|
|
|
|
|
MonitorMediaEvents();
|
|
|
|
|
}
|
2025-08-18 10:29:00 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
|
|
|
|
|
private void VideoPlayerForm_Load(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
secondMonitor = ScreenHelper.GetSecondMonitor();
|
|
|
|
|
if (secondMonitor != null)
|
|
|
|
|
{
|
|
|
|
|
this.FormBorderStyle = FormBorderStyle.None; // 设置窗体没有边框
|
|
|
|
|
this.StartPosition = FormStartPosition.Manual;
|
|
|
|
|
this.Location = secondMonitor.Bounds.Location;
|
|
|
|
|
this.Size = secondMonitor.Bounds.Size;
|
2025-08-22 16:54:20 +08:00
|
|
|
|
this.DoubleBuffered = true;
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-08-07 18:34:45 +08:00
|
|
|
|
Screen screen = Screen.FromHandle(this.Handle);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-07-21 18:36:09 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
private void VideoPlayerForm_Shown(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
|
2025-08-07 18:34:45 +08:00
|
|
|
|
if (hr < 0)
|
|
|
|
|
{
|
2025-04-07 16:54:10 +08:00
|
|
|
|
Console.WriteLine("Failed to initialize COM library.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-07 18:34:45 +08:00
|
|
|
|
else
|
2025-08-15 11:42:56 +08:00
|
|
|
|
{
|
2025-08-25 17:32:20 +08:00
|
|
|
|
this.Controls.Add(PrimaryForm.Instance.videoView1);
|
|
|
|
|
PrimaryForm.Instance.videoView1.MediaPlayer = _mediaService1.Player;
|
|
|
|
|
PrimaryForm.Instance.videoView0.MediaPlayer = _mediaService0.Player;
|
2025-09-03 17:34:33 +08:00
|
|
|
|
//同步畫面比例
|
|
|
|
|
_mediaService0.Player.AspectRatio = "8:5";
|
2025-09-02 15:03:59 +08:00
|
|
|
|
_mediaService0._zero = true;
|
2025-08-07 18:34:45 +08:00
|
|
|
|
PlayNextSong();
|
|
|
|
|
}
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void VideoPlayerForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
|
|
|
{
|
2025-08-15 17:53:55 +08:00
|
|
|
|
_mediaService0.Dispose();
|
|
|
|
|
_mediaService1.Dispose();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
// 清理COM
|
|
|
|
|
CoUninitialize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// COM API函数声明
|
|
|
|
|
[DllImport("ole32.dll")]
|
|
|
|
|
private static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
|
|
|
|
|
|
|
|
|
|
[DllImport("ole32.dll")]
|
|
|
|
|
private static extern void CoUninitialize();
|
|
|
|
|
|
|
|
|
|
// CoInitializeEx() 可以选择的参数
|
|
|
|
|
private enum COINIT : int
|
|
|
|
|
{
|
|
|
|
|
APARTMENTTHREADED = 0x2,
|
|
|
|
|
MULTITHREADED = 0x0
|
|
|
|
|
}
|
2025-08-18 10:29:00 +08:00
|
|
|
|
|
2025-05-08 16:49:32 +08:00
|
|
|
|
// 同步畫面事件
|
2025-04-07 16:54:10 +08:00
|
|
|
|
public void SyncToPrimaryMonitor()
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.primaryScreenPanel.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.primaryScreenPanel.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncServiceBellButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncServiceBellButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncCutSongButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncCutSongButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncReplayButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncReplayButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncOriginalSongButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncOriginalSongButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncMuteButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncMuteButton.BringToFront();
|
|
|
|
|
if (isPaused)
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.BringToFront();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.BringToFront();
|
|
|
|
|
}
|
|
|
|
|
PrimaryForm.Instance.syncVolumeUpButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncVolumeUpButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncVolumeDownButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncVolumeDownButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncMicUpButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncMicUpButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncMicDownButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncMicDownButton.BringToFront();
|
|
|
|
|
PrimaryForm.Instance.syncCloseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncCloseButton.BringToFront();
|
2025-05-08 16:49:32 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-06-20 13:12:10 +08:00
|
|
|
|
public void ClosePrimaryScreenPanel()
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.primaryScreenPanel.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncServiceBellButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncCutSongButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncReplayButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncOriginalSongButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncMuteButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncVolumeUpButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncVolumeDownButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncMicUpButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncMicDownButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncCloseButton.Visible = false;
|
|
|
|
|
IsSyncToPrimaryMonitor = false;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(String.Format("Error closing primary screen panel: {0}", ex.Message));
|
|
|
|
|
MessageBox.Show(String.Format("Error closing primary screen panel: {0}", ex.Message));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[DllImport("gdi32.dll", ExactSpelling = true)]
|
|
|
|
|
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
|
|
|
|
|
IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
|
|
|
|
|
|
|
|
|
|
public enum TernaryRasterOperations : uint
|
|
|
|
|
{
|
|
|
|
|
SRCCOPY = 0x00CC0020,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void DisplayBarrageOnOverlay(string text)
|
|
|
|
|
{
|
|
|
|
|
if (overlayForm.InvokeRequired)
|
|
|
|
|
overlayForm.Invoke(new System.Action(() => overlayForm.DisplayBarrage(text)));
|
|
|
|
|
else
|
|
|
|
|
overlayForm.DisplayBarrage(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task PlayNextSong()
|
|
|
|
|
{
|
2025-08-19 17:50:37 +08:00
|
|
|
|
try
|
2025-08-07 18:34:45 +08:00
|
|
|
|
{
|
2025-08-19 17:50:37 +08:00
|
|
|
|
// 等待初始化完成(例如播放器、串口等資源尚未就緒時)
|
|
|
|
|
Console.WriteLine("開始播放下一首歌曲...");
|
|
|
|
|
var songToPlay = SongList.Next();
|
|
|
|
|
if (!songToPlay.isPublicSong)
|
2025-07-14 18:08:12 +08:00
|
|
|
|
{
|
2025-08-19 17:50:37 +08:00
|
|
|
|
// 若是使用者點播模式,先送出升Key的串口指令
|
|
|
|
|
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
|
|
|
|
|
{
|
|
|
|
|
byte[] commandBytesIncreasePitch1 = new byte[] { 0xA2, 0x7F, 0xA4 };
|
|
|
|
|
SerialPortManager.mySerialPort.Write(commandBytesIncreasePitch1, 0, commandBytesIncreasePitch1.Length);
|
|
|
|
|
}
|
2025-07-14 18:08:12 +08:00
|
|
|
|
}
|
2025-08-19 17:50:37 +08:00
|
|
|
|
// 更新畫面上顯示的下一首歌資訊
|
|
|
|
|
SongList.UpdateNextSongLabel();
|
|
|
|
|
|
|
|
|
|
// 顯示 QRCode(可能是點歌頁用)
|
|
|
|
|
overlayForm.DisplayQRCodeOnOverlay(HttpServer.randomFolderPath);
|
2025-05-08 10:09:14 +08:00
|
|
|
|
|
2025-08-19 17:50:37 +08:00
|
|
|
|
// 隱藏「暫停中」標籤
|
|
|
|
|
overlayForm.HidePauseLabel();
|
2025-05-08 10:09:14 +08:00
|
|
|
|
|
2025-08-19 17:50:37 +08:00
|
|
|
|
await _InitializeAndPlayMedia(songToPlay);
|
2025-05-08 10:09:14 +08:00
|
|
|
|
|
2025-08-19 17:50:37 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(ex.Message);
|
|
|
|
|
}
|
2025-08-18 10:29:00 +08:00
|
|
|
|
|
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-08-15 12:15:33 +08:00
|
|
|
|
public async Task ReplayCurrentSong()
|
|
|
|
|
{
|
|
|
|
|
if (Program.room.IsClose())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
2025-08-18 10:29:00 +08:00
|
|
|
|
{
|
2025-08-15 12:15:33 +08:00
|
|
|
|
await _InitializeAndPlayMedia(SongList.Current());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2025-08-18 10:29:00 +08:00
|
|
|
|
|
2025-08-11 17:36:01 +08:00
|
|
|
|
private async Task _InitializeAndPlayMedia(SongData song)
|
2025-08-07 18:34:45 +08:00
|
|
|
|
{
|
2025-04-07 16:54:10 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
if (InvokeRequired)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-11 17:36:01 +08:00
|
|
|
|
await InvokeAsync(() => InitializeAndPlayMedia(song));
|
2025-08-07 18:34:45 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-08-11 17:36:01 +08:00
|
|
|
|
await InitializeAndPlayMedia(song);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
Console.WriteLine($"播放時發生錯誤: {ex.Message}");
|
2025-08-11 17:36:01 +08:00
|
|
|
|
await RetryInitializeAndPlayMedia(song);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-11 17:36:01 +08:00
|
|
|
|
private async Task RetryInitializeAndPlayMedia(SongData song)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
int hr = CoInitializeEx(IntPtr.Zero, COINIT.APARTMENTTHREADED);
|
|
|
|
|
if (hr >= 0)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-11 17:36:01 +08:00
|
|
|
|
await InitializeAndPlayMedia(song);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-08-07 18:34:45 +08:00
|
|
|
|
else
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
Console.WriteLine("CoInitializeEx 失敗,無法重新初始化 COM。");
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-07 18:34:45 +08:00
|
|
|
|
catch (Exception retryEx)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
Console.WriteLine($"重試播放時發生錯誤: {retryEx.Message}");
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 18:34:45 +08:00
|
|
|
|
// 通用的 async invoke 方法(避免重複寫)
|
|
|
|
|
private Task InvokeAsync(Func<Task> func)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
var tcs = new TaskCompletionSource<bool>();
|
|
|
|
|
BeginInvoke(async () =>
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await func();
|
|
|
|
|
tcs.SetResult(true);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2025-08-25 09:27:39 +08:00
|
|
|
|
Program.WriteLog(ex.ToString());
|
2025-08-07 18:34:45 +08:00
|
|
|
|
tcs.SetException(ex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return tcs.Task;
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-08-11 17:36:01 +08:00
|
|
|
|
private Task InitializeAndPlayMedia(SongData song)
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-25 09:27:39 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string pathToPlay = song.getFile();
|
2025-09-03 17:34:33 +08:00
|
|
|
|
Uri url = song.getFileUrl();
|
2025-08-28 13:30:59 +08:00
|
|
|
|
//影片畫面播放器載入media設置聲道
|
2025-09-03 17:34:33 +08:00
|
|
|
|
if (pathToPlay == null)
|
|
|
|
|
{
|
|
|
|
|
_mediaService0.LoadMedia(url, 0);
|
|
|
|
|
_mediaService1.LoadMedia(url, song.isPublicSong ? 0 : 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_mediaService0.LoadMedia(pathToPlay, 0);
|
|
|
|
|
_mediaService1.LoadMedia(pathToPlay, song.isPublicSong ? 0 : 1);
|
|
|
|
|
}
|
|
|
|
|
// _mediaService0.Player.Media.AddOption(":no-audio");
|
|
|
|
|
// _mediaService0.Player.AspectRatio = "8:5";
|
|
|
|
|
|
2025-08-27 12:59:37 +08:00
|
|
|
|
isVocalRemoved = true;
|
|
|
|
|
if (isMuted) { Mute(true); }
|
|
|
|
|
|
2025-08-25 17:32:20 +08:00
|
|
|
|
// 音量處理
|
2025-08-25 09:27:39 +08:00
|
|
|
|
SetVolume(100 + song.getBasic().getDbChange());
|
2025-08-25 17:32:20 +08:00
|
|
|
|
Task.Run(() => Thread.Sleep(100));
|
2025-08-25 09:27:39 +08:00
|
|
|
|
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
|
2025-09-03 17:34:33 +08:00
|
|
|
|
//ftp抓不到檔案跳下一首歌
|
|
|
|
|
if (url == null && !song.isPublicSong) PlayNextSong();
|
2025-09-03 16:38:39 +08:00
|
|
|
|
PrimaryForm.Instance.standardKeyButton.PerformClick();
|
2025-08-25 09:27:39 +08:00
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2025-08-27 12:59:37 +08:00
|
|
|
|
catch (Exception ex)
|
2025-08-25 09:27:39 +08:00
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(ex.Message);
|
|
|
|
|
Program.WriteLog(ex.ToString());
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2025-08-18 10:29:00 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeOverlayForm(Screen secondaryScreen)
|
|
|
|
|
{
|
|
|
|
|
overlayForm = new OverlayForm();
|
|
|
|
|
Screen secondMonitor = ScreenHelper.GetSecondMonitor();
|
|
|
|
|
if (secondMonitor != null)
|
|
|
|
|
{
|
|
|
|
|
overlayForm.Location = secondMonitor.WorkingArea.Location;
|
|
|
|
|
overlayForm.StartPosition = FormStartPosition.Manual;
|
|
|
|
|
overlayForm.Size = new Size(secondMonitor.WorkingArea.Width, secondMonitor.WorkingArea.Height);
|
|
|
|
|
}
|
|
|
|
|
overlayForm.ShowInTaskbar = false;
|
|
|
|
|
overlayForm.Owner = this;
|
|
|
|
|
overlayForm.Show();
|
|
|
|
|
this.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MonitorMediaEvents()
|
|
|
|
|
{
|
|
|
|
|
Task.Run(async () =>
|
|
|
|
|
{
|
2025-08-11 17:36:01 +08:00
|
|
|
|
await Task.Delay(10000);
|
2025-08-07 18:34:45 +08:00
|
|
|
|
Console.WriteLine("開始監聽媒體事件...");
|
2025-04-07 16:54:10 +08:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-08-25 17:32:20 +08:00
|
|
|
|
if (_mediaService1.IsAtEndTime())
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
BeginInvoke(new Action(async () =>
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-08-07 18:34:45 +08:00
|
|
|
|
await PlayNextSong();
|
2025-09-03 12:56:34 +08:00
|
|
|
|
if (PrimaryForm.Instance.isOnOrderedSongsPage) PrimaryForm.Instance.orderedSongsButton.PerformClick();
|
2025-08-07 18:34:45 +08:00
|
|
|
|
}));
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"監控媒體事件時發生錯誤: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-07 18:34:45 +08:00
|
|
|
|
await Task.Delay(1000);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void BringOverlayToFront()
|
|
|
|
|
{
|
|
|
|
|
if (overlayForm != null)
|
|
|
|
|
{
|
|
|
|
|
if (!overlayForm.Visible)
|
|
|
|
|
{
|
|
|
|
|
overlayForm.Show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overlayForm.BringToFront();
|
|
|
|
|
overlayForm.TopMost = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Play()
|
|
|
|
|
{
|
2025-08-15 17:53:55 +08:00
|
|
|
|
_mediaService0.Play();
|
|
|
|
|
_mediaService1.Play();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
isPaused = false;
|
|
|
|
|
OverlayForm.MainForm.HidePauseLabel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
2025-08-15 17:53:55 +08:00
|
|
|
|
_mediaService0.Stop();
|
|
|
|
|
_mediaService1.Stop();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Pause()
|
|
|
|
|
{
|
2025-08-15 17:53:55 +08:00
|
|
|
|
_mediaService0.Pause();
|
|
|
|
|
_mediaService1.Pause();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
isPaused = true;
|
|
|
|
|
OverlayForm.MainForm.ShowPauseLabel();
|
|
|
|
|
}
|
|
|
|
|
public void PauseOrResumeSong()
|
|
|
|
|
{
|
|
|
|
|
if (isPaused)
|
|
|
|
|
{
|
|
|
|
|
Play();
|
|
|
|
|
PrimaryForm.Instance.pauseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.playButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Pause();
|
|
|
|
|
PrimaryForm.Instance.pauseButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.playButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = true;
|
|
|
|
|
OverlayForm.MainForm.ShowPauseLabel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private void UpdateSyncButtons()
|
|
|
|
|
{
|
|
|
|
|
if (isPaused)
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = true;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PrimaryForm.Instance.syncPlayButton.Visible = false;
|
|
|
|
|
PrimaryForm.Instance.syncPauseButton.Visible = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-13 09:51:11 +08:00
|
|
|
|
public bool Mute(bool isMuted)
|
|
|
|
|
{
|
2025-08-18 10:29:00 +08:00
|
|
|
|
if (isMuted)
|
|
|
|
|
{
|
2025-08-18 10:58:58 +08:00
|
|
|
|
_mediaService1.Mute(true);
|
2025-08-18 10:29:00 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-08-27 12:59:37 +08:00
|
|
|
|
_mediaService1.Mute(false);
|
2025-08-15 17:53:55 +08:00
|
|
|
|
}
|
|
|
|
|
return isMuted;
|
2025-08-13 09:51:11 +08:00
|
|
|
|
}
|
2025-04-07 16:54:10 +08:00
|
|
|
|
public void SetVolume(int volume)
|
|
|
|
|
{
|
2025-08-13 09:51:11 +08:00
|
|
|
|
Console.WriteLine($"SetVolume: {volume}");
|
2025-08-15 17:53:55 +08:00
|
|
|
|
_mediaService0.SetVolume(volume);
|
|
|
|
|
_mediaService1.SetVolume(volume);
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
|
|
|
|
public int GetVolume()
|
|
|
|
|
{
|
2025-08-15 17:53:55 +08:00
|
|
|
|
return _mediaService1.GetVolume();
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-08-27 12:59:37 +08:00
|
|
|
|
|
2025-08-15 17:53:55 +08:00
|
|
|
|
private bool isVocalRemoved = true;
|
2025-07-14 18:08:12 +08:00
|
|
|
|
public void ToggleVocalRemoval()
|
2025-04-07 16:54:10 +08:00
|
|
|
|
{
|
2025-09-02 11:46:50 +08:00
|
|
|
|
if (SongList.Current().isPublicSong == false)
|
2025-08-27 12:59:37 +08:00
|
|
|
|
{
|
2025-09-02 11:46:50 +08:00
|
|
|
|
if (isVocalRemoved)
|
|
|
|
|
{
|
|
|
|
|
_mediaService1.Player.Select(TrackType.Audio, "audio/2");
|
2025-08-27 12:59:37 +08:00
|
|
|
|
|
2025-09-02 11:46:50 +08:00
|
|
|
|
isVocalRemoved = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_mediaService1.Player.Select(TrackType.Audio, "audio/1,audio/3");
|
|
|
|
|
// _mediaService1.Player.Select(TrackType.Audio, "audio/3");
|
|
|
|
|
isVocalRemoved = true;
|
|
|
|
|
}
|
|
|
|
|
OverlayForm.MainForm.ShowTopRightLabelTime(isVocalRemoved ? "無人聲" : "有人聲");
|
2025-08-27 12:59:37 +08:00
|
|
|
|
}
|
2025-09-02 11:46:50 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-08-27 12:59:37 +08:00
|
|
|
|
|
2025-04-07 16:54:10 +08:00
|
|
|
|
}
|
2025-06-20 13:12:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|