2509031254

server與歌曲路徑使用ftp
This commit is contained in:
jasonchenwork 2025-09-03 12:56:34 +08:00
parent 237e9f6c49
commit 4ca6594561
7 changed files with 210 additions and 98 deletions

View File

@ -1,17 +1,18 @@
using DualScreenDemo;
using System;
using System.IO;
using System.Net;
using System.Windows.Markup;
namespace DBObj
{
public class SongData
{
private Song basic;
private Artist A ;
private Artist B ;
private Artist A;
private Artist B;
public bool isPublicSong { get; set; }
public PlayState state;
public SongData()
{
basic = new("", "", "", "", 0, 1, "");
@ -24,46 +25,47 @@ namespace DBObj
basic = new(songNumber, song, "", filename, humanVoice, 0, "");
isPublicSong = isPublic;
}
public SongData(string songNumber, string song, string artistA, string artistB, string filename, string artistASimplified, string artistBSimplified, string songSimplified, int humanVoice,int dbChange,string situation)
public SongData(string songNumber, string song, string artistA, string artistB, string filename, string artistASimplified, string artistBSimplified, string songSimplified, int humanVoice, int dbChange, string situation)
{
basic=new(songNumber,song,songSimplified,filename,humanVoice,dbChange,situation);
basic = new(songNumber, song, songSimplified, filename, humanVoice, dbChange, situation);
A = new Artist(artistA, artistASimplified);
B = !artistB.Equals("") ? new Artist(artistB, artistBSimplified) : null;
isPublicSong = false;
}
public SongData(SongData value,PlayState s){
public SongData(SongData value, PlayState s)
{
basic = value.getBasic();
A = value.getA();
B = value.getB();
state =s;
state = s;
}
public Song getBasic() => basic;
public Artist getA() => A;
public Artist getB() => B;
public string getNumber() => basic.getNumber();
public string getName(bool IsSimplified=false) => basic.getName(IsSimplified);
public string getName(bool IsSimplified = false) => basic.getName(IsSimplified);
public string getName() => basic.getName();
public string getArtist_A(bool IsSimplified) => A.getName(IsSimplified);
public string getArtist_A() => A.getName();
public string getArtist_B(bool IsSimplified) => B.getName(IsSimplified);
public string getArtist_B() => B.getName();
public int getNameLength() =>basic.getName().Length;
public int getNameLength() => basic.getName().Length;
public string getFileName() => basic.getFileName();
public string next_song_text()
{
var str = (state == PlayState.InsertPlayback) ? GetStateTxt(false) : "";
return String.Format("下一首:{0} {1}", basic.getName(false),str);
return String.Format("下一首:{0} {1}", basic.getName(false), str);
}
public string artist_text()
{
return B!=null
return B != null
? $"{A.getName(false)} - {B.getName(false)}"
: A.getName(false);
}
public string name_text()
{
return B!=null
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));
}
@ -73,12 +75,13 @@ namespace DBObj
{
try
{
string fullPath = Path.Combine(server, basic.getFileName());
if (File.Exists(fullPath))
{
Console.WriteLine($"找到檔案: {fullPath}");
return true;
}
// string fullPath = Path.Combine(server, basic.getFileName());
Uri fullUri = new Uri(new Uri(server), basic.getFileName());
// if (File.Exists(fullPath))
// {
// Console.WriteLine($"找到檔案: {fullPath}");
return true;
// }
}
catch (Exception ex)
{
@ -89,7 +92,7 @@ namespace DBObj
Console.WriteLine($"全部 server 都找不到檔案: {basic.getFileName()}");
return false;
}
public string getFile()
{
return FindExistingPath(basic.getFileName());
@ -103,24 +106,76 @@ namespace DBObj
}
return null; // 找不到就回原本的 filename不加路徑
}
public Uri getFileUrl()
{
return GetUri(basic.getFileName());
}
private Uri GetUri(string filename)
{
foreach (var server in Utils.Env.GetSongServers())
{
var check = IsFtpUriReachable(new Uri(server).ToString());
if (!string.IsNullOrWhiteSpace(server) && check != false)
{
return new Uri(new Uri(server), filename);
}
}
// throw new InvalidOperationException("No valid server found.");
Console.WriteLine("No valid server found.");
return null;
}
public Color GetStateColor() => state switch
{
PlayState.Played => Color.Gray,
PlayState.Skipped => Color.Gray,
PlayState.NoFile => Color.Gray,
PlayState.Playing => Color.LimeGreen,
PlayState.Played => Color.Gray,
PlayState.Skipped => Color.Gray,
PlayState.NoFile => Color.Gray,
PlayState.Playing => Color.LimeGreen,
PlayState.InsertPlayback => Color.Gold,
_ => Color.White
_ => Color.White
};
public string GetStateTxt(bool IsSimplified) => (state==PlayState.NotPlayed)?"":$"({state.GetDescription(IsSimplified)})";
public string GetStateTxt(bool IsSimplified) => (state == PlayState.NotPlayed) ? "" : $"({state.GetDescription(IsSimplified)})";
public void SetState(PlayState s) => state = s;
public PlayState GetState() => state;
public int getHumanVoice() => basic.getHumanVoice();
public override string ToString()
{
return String.Format("{0} {1}", basic.getName(),state.GetDescription() );
return String.Format("{0} {1}", basic.getName(), state.GetDescription());
}
public static bool IsFtpUriReachable(string ftpUri)
{
try
{
#pragma warning disable SYSLIB0014 // 類型或成員已經過時
var request = (FtpWebRequest)WebRequest.Create(ftpUri);
#pragma warning restore SYSLIB0014 // 類型或成員已經過時
request.Method = WebRequestMethods.Ftp.ListDirectory; // Lightweight check
// request.Credentials = new NetworkCredential("svr", "svr"); // Replace with actual username/password
request.Timeout = 5000;
using var response = (FtpWebResponse)request.GetResponse();
return response.StatusCode == FtpStatusCode.OpeningData || response.StatusCode == FtpStatusCode.DataAlreadyOpen;
}
catch (WebException ex)
{
if (ex.Response is FtpWebResponse ftpResponse)
{
Console.WriteLine($"FTP error: {ftpResponse.StatusDescription}");
return false;
}
Console.WriteLine($"Connection error: {ex.Message}");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
return false;
}
}
}
}

2
Env.cs
View File

@ -60,7 +60,7 @@ namespace Utils
// .Select(s => s.Trim('\'', '"').TrimEnd('\\')) // 清理
// .ToArray();
return new string[] { @"\\svr01", @"\\svr02" };
return new string[] { @"ftp://svr:svr@svr",@"ftp://song1:song1@svr01", @"ftp://song2:song2@svr02",@"ftp://song3:song3@svr03" };
}

View File

@ -34,13 +34,13 @@ namespace DualScreenDemo
private static string _baseDirectory = @"themes\superstar\_www"; // 根據實際情況設定
private static TaskCompletionSource<string> _qrReadyTcs;
public static async Task StartServer(string baseDirectory, int port, SQLManager manager, CancellationToken token)
{
songListManager = manager;
string randomFolderName = CreateRandomFolderAndRedirectHTML(baseDirectory);
randomFolderPath = randomFolderName;
// 安裝包更新
string localAddress = GetLocalIPAddress();
string externalAddress = Utils.Env.Get("PhoneIP", "").Trim();
@ -58,16 +58,16 @@ namespace DualScreenDemo
//_listener.Prefixes.Add($"http://{host}:{externalPort}/");
}
string localQrContent = String.Format("http://{0}:{1}/{2}/windows.html", localAddress, port, randomFolderName);
// 修改外网二维码内容生成
string externalQrContent = !string.IsNullOrEmpty(externalAddress) ?
string externalQrContent = !string.IsNullOrEmpty(externalAddress) ?
String.Format("http://{0}:{1}/{2}/windows.html", externalAddress, externalPort, randomFolderName) :
localQrContent;
GenerateQRCode(externalQrContent, Path.Combine(baseDirectory, randomFolderName, "qrcode.png"));
_qrReadyTcs?.TrySetResult(randomFolderName); // safe callnull-safe
try
{
@ -84,7 +84,7 @@ namespace DualScreenDemo
{
try
{
HttpListenerContext context = await _listener.GetContextAsync();
HttpListenerContext context = await _listener.GetContextAsync();
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
}
catch (HttpListenerException)
@ -97,7 +97,7 @@ namespace DualScreenDemo
_listener.Close();
Console.WriteLine("Server stopped.");
}
public static async Task RestartServer()
{
Console.WriteLine("Restarting server...");
@ -115,7 +115,7 @@ namespace DualScreenDemo
_cts = new CancellationTokenSource();
// ✅ 等 QR code 跑完
_qrReadyTcs = new TaskCompletionSource<string>();
_serverTask = StartServer(_baseDirectory, _port, songListManager, _cts.Token);
@ -245,7 +245,7 @@ namespace DualScreenDemo
// 添加基本的缓存控制
context.Response.Headers.Add("Cache-Control", "public, max-age=3600");
if (context.Request.HttpMethod == "POST")
{
string relativePath = context.Request.Url.AbsolutePath.Replace($"/{randomFolderName}", "");
@ -289,7 +289,7 @@ namespace DualScreenDemo
{
// 获取请求的完整URL路径
string requestPath = context.Request.Url.AbsolutePath;
// 如果是根路径访问直接返回404
if (requestPath == "/" || requestPath == "/windows.html")
{
@ -298,7 +298,7 @@ namespace DualScreenDemo
}
string requestedFile = context.Request.Url.LocalPath.Replace($"/{randomFolderName}/", "");
if (string.IsNullOrEmpty(requestedFile.Trim('/')))
{
requestedFile = "windows.html";
@ -321,6 +321,28 @@ namespace DualScreenDemo
{
try
{
var request = context.Request;
var user = context.User;
Console.WriteLine("Headers:");
foreach (string key in request.Headers.AllKeys)
{
Console.WriteLine($" {key}: {request.Headers[key]}");
}
Console.WriteLine("Query String:");
foreach (string key in request.QueryString.AllKeys)
{
Console.WriteLine($" {key}: {request.QueryString[key]}");
}
Console.WriteLine("Body:");
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
string body = reader.ReadToEnd();
Console.WriteLine(body);
}
context.Response.Close();
}
catch { }
@ -337,7 +359,7 @@ namespace DualScreenDemo
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json; charset=utf-8";
context.Response.ContentLength64 = buffer.Length;
await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
}
catch (Exception ex)
@ -377,7 +399,7 @@ namespace DualScreenDemo
.Select(song => new
{
Song = song.getName(),
ArtistA =song.getArtist_A(),
ArtistA = song.getArtist_A(),
SongNumber = song.getNumber(),
})
.ToList();
@ -421,7 +443,7 @@ namespace DualScreenDemo
private static async Task HandleSoundControlRequest(HttpListenerContext context)
{
string requestBody;
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
{
@ -465,14 +487,14 @@ namespace DualScreenDemo
// 执行音量增大操作
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↑","a2 b3 a4");
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↑", "a2 b3 a4");
}));
break;
case "mic_up":
// 执行麦克风增大操作
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↑","a2 b5 a4");
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↑", "a2 b5 a4");
}));
break;
case "mute":
@ -501,20 +523,20 @@ namespace DualScreenDemo
// 执行音量减小操作
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↓","a2 b4 a4");
OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↓", "a2 b4 a4");
}));
break;
case "mic_down":
// 执行麦克风减小操作
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↓","a2 b6 a4");
OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↓", "a2 b6 a4");
}));
break;
case "original_song":
if (PrimaryForm.Instance.InvokeRequired)
{
PrimaryForm.Instance.Invoke(new System.Action(() =>
PrimaryForm.Instance.Invoke(new System.Action(() =>
{
PrimaryForm.Instance.videoPlayerForm.ToggleVocalRemoval();
}));
@ -524,12 +546,13 @@ namespace DualScreenDemo
PrimaryForm.Instance.videoPlayerForm.ToggleVocalRemoval();
}
// 执行原唱操作
break;
case "service":
// 执行服务操作
OverlayForm.MainForm.Invoke(new System.Action(() => {
OverlayForm.MainForm.ShowTopRightLabel("服務鈴","a2 53 a4");
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowTopRightLabel("服務鈴", "a2 53 a4");
}));
break;
case "replay":
@ -539,7 +562,7 @@ namespace DualScreenDemo
// 在这里执行按钮点击后的操作
// 比如切歌操作
PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong();
}));
}));
break;
case "male_key":
// 执行男调操作
@ -578,7 +601,7 @@ 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.PlayNextSong(); }));
else
PrimaryForm.Instance.videoPlayerForm.PlayNextSong();
break;
@ -668,16 +691,16 @@ namespace DualScreenDemo
playingSongList = SongList.GetHistory()
.Select((song, index) => new
{
Song=song.getName(),
ArtistA=song.getArtist_A(),
Song = song.getName(),
ArtistA = song.getArtist_A(),
PlayState = song.GetState().GetDescription()
})
.ToList()
};
jsonResponse = JsonConvert.SerializeObject(response);
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
catch (Exception ex)
{
@ -690,7 +713,7 @@ namespace DualScreenDemo
}
// 生成播放状态
private static async Task HandleOrderSongRequest(HttpListenerContext context)
{
try
@ -704,19 +727,22 @@ namespace DualScreenDemo
Console.WriteLine("Received order song request: " + requestBody);
// 解析 JSON 为 Song 对象
var json = JObject.Parse(requestBody);
SongData song =songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
if (song != null) {
SongData song = songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
if (song != null)
{
Console.WriteLine($"Ordering Song: {song.getName()} by {song.getArtist_A()}");
// 这里可以添加处理逻辑,例如将歌曲加入到播放列表或数据库中
song.getBasic().setNameTest("*");
SongList.Add(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\"}");
}
@ -743,9 +769,10 @@ namespace DualScreenDemo
// 解析 JSON 为 Song 对象
var json = JObject.Parse(requestBody);
SongData song =songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
SongData song = songListManager.SearchSongByNumber(json["SongNumber"]?.ToString());
if (song != null){
if (song != null)
{
Console.WriteLine($"Inserting Song: {song.getName()} by {song.getArtist_A()}");
// 这里可以添加插播歌曲的处理逻辑
song.getBasic().setNameTest("*");
@ -753,7 +780,9 @@ namespace DualScreenDemo
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\"}");
}
@ -822,11 +851,13 @@ namespace DualScreenDemo
}
// 封装响应代码以避免重复
async static Task SendResponseAsync(HttpListenerContext context, string jsonResponse) {
async static Task SendResponseAsync(HttpListenerContext context, string jsonResponse)
{
context.Response.ContentType = "application/json";
context.Response.ContentLength64 = Encoding.UTF8.GetByteCount(jsonResponse);
context.Response.StatusCode = (int)HttpStatusCode.OK;
using (var streamWriter = new StreamWriter(context.Response.OutputStream)) {
using (var streamWriter = new StreamWriter(context.Response.OutputStream))
{
await streamWriter.WriteAsync(jsonResponse);
await streamWriter.FlushAsync();
}
@ -843,7 +874,7 @@ namespace DualScreenDemo
}
}
private static async Task HandleMessageRequest(HttpListenerContext context)
{
{
try
{
using (var reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
@ -852,11 +883,11 @@ namespace DualScreenDemo
if (!string.IsNullOrEmpty(json))
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
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) );
OnDisplayBarrage?.Invoke(json.Substring(startIndex, endIndex - startIndex));
}
else
{
@ -948,7 +979,7 @@ namespace DualScreenDemo
}
}*/
private static string GetMimeType(string filePath)
{
@ -985,7 +1016,7 @@ namespace DualScreenDemo
case ".json":
mimeType = "application/json";
break;
// Add more cases for other file types as needed
// Add more cases for other file types as needed
}
return mimeType;
@ -1016,25 +1047,25 @@ namespace DualScreenDemo
//SongListManager.Instance.UserLogin(phone);
// 获取用户的收藏歌曲
var favoriteSongs=new List<SongData>(); //= SongListManager.Instance.GetFavoriteSongsByPhoneNumber();
string query =PrimaryForm.Instance.SearchFavoriteSongs_Mysql();
var favoriteSongs = new List<SongData>(); //= SongListManager.Instance.GetFavoriteSongsByPhoneNumber();
string query = PrimaryForm.Instance.SearchFavoriteSongs_Mysql();
var searchResults = PrimaryForm.Instance.SearchSongs_Mysql(query);
// 创建响应数据
var response = new
{
isLoggedIn = true,
favoriteSongList = searchResults
.Select(song => new
{
Name = song.getName(),
ArtistA =song.getArtist_A(),
ArtistA = song.getArtist_A(),
Number = song.getNumber(),
ArtistAFull = song.getArtist_A(true),
ArtistBFull = song.getArtist_B(true),
NameFull = song.getName(true),
HumanVoice =song.getHumanVoice(),
HumanVoice = song.getHumanVoice(),
FileName = song.getFileName()
})
.ToList(),
@ -1088,7 +1119,7 @@ namespace DualScreenDemo
}
// 异步响应发送方法
private static async Task SendResponseAsyncs(HttpListenerContext context, string responseContent)
{
try
@ -1103,14 +1134,14 @@ namespace DualScreenDemo
Console.WriteLine("Error sending response: " + ex.Message);
}
}
private static async Task HandleGetRequest(HttpListenerContext context, string baseDirectory, string requestedFile)
{
try
{
// 获取请求的完整URL路径
string requestPath = context.Request.Url.AbsolutePath;
// 检查URL是否包含随机文件夹名
if (!requestPath.Contains("/" + randomFolderPath + "/"))
{
@ -1120,7 +1151,7 @@ namespace DualScreenDemo
}
string filePath = Path.Combine(baseDirectory, requestedFile.TrimStart('/'));
if (!File.Exists(filePath))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
@ -1129,7 +1160,7 @@ namespace DualScreenDemo
string contentType = GetMimeType(filePath);
context.Response.ContentType = contentType;
// 对于静态文件使用缓存
if (_fileCache.TryGetValue(filePath, out byte[] cachedContent))
{
@ -1145,7 +1176,7 @@ namespace DualScreenDemo
buffer = new byte[fileStream.Length];
await fileStream.ReadAsync(buffer, 0, buffer.Length);
}
// 缓存静态文件
if (contentType.StartsWith("text/") || contentType.Contains("javascript") || contentType.Contains("json"))
{

View File

@ -2,7 +2,7 @@ namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button orderedSongsButton;
public Button orderedSongsButton;
private Bitmap orderedSongsNormalBackground;
private Bitmap orderedSongsActiveBackground;
}

View File

@ -14,7 +14,7 @@ namespace DualScreenDemo
private static PrimaryForm primaryForm; // 儲存實例的參考
public static Room room = new Room();
public static string verSion = "Server V2.10 202509010938";
public static string verSion = "Server V2.10 202509031254";
[STAThread]
static void Main()

View File

@ -85,15 +85,16 @@ namespace DualScreenDemo.Services
_media?.Dispose();
_media = new Media(filePath, FromType.FromPath);
_media.ParseAsync(_libVLC);
_media.ParsedChanged += (sender, args) =>
{
// if (args.ParsedStatus != MediaParsedStatus.Failed)
// {
_media.ParseAsync(_libVLC);
_media.ParsedChanged += (sender, args) =>
{
// if (args.ParsedStatus != MediaParsedStatus.Failed)
// {
_mediaPlayer.Media = addMediaOption(_media, audioTrackIndex);
_mediaPlayer.Play();
// }
};
// }
};
_mediaPlayer.Fullscreen = true;
_mediaPlayer.Mute = false;
}
@ -103,7 +104,27 @@ namespace DualScreenDemo.Services
Program.WriteLog(ex.ToString());
}
}
public void LoadMedia(Uri url, int audioTrackIndex = 0)
{
try
{
_mediaPlayer.Pause();
_media?.Dispose();
_media = new Media(url);
_mediaPlayer.Media = addMediaOption(_media, audioTrackIndex);
_mediaPlayer.Play();
_mediaPlayer.Fullscreen = true;
_mediaPlayer.Mute = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Program.WriteLog(ex.ToString());
}
}
private Media addMediaOption(Media media, int audioTrackIndex)
{
media.AddOption(":avcodec-hw=dxva2");

View File

@ -347,12 +347,15 @@ namespace DualScreenDemo
try
{
string pathToPlay = song.getFile();
Uri url = song.getFileUrl();
if (pathToPlay == null) _mediaService0.LoadMedia(url, 0);
//同步畫面播放器載入media設置參數
_mediaService0.LoadMedia(pathToPlay, 0);
else _mediaService0.LoadMedia(pathToPlay, 0);
// _mediaService0.Player.Media.AddOption(":no-audio");
_mediaService0.Player.AspectRatio = "8:5";
if(pathToPlay==null)_mediaService1.LoadMedia(url, song.isPublicSong ? 0 : 1);
//影片畫面播放器載入media設置聲道
_mediaService1.LoadMedia(pathToPlay, song.isPublicSong ? 0 : 1);
else _mediaService1.LoadMedia(pathToPlay, song.isPublicSong ? 0 : 1);
isVocalRemoved = true;
if (isMuted) { Mute(true); }
@ -361,6 +364,7 @@ namespace DualScreenDemo
SetVolume(100 + song.getBasic().getDbChange());
Task.Run(() => Thread.Sleep(100));
if (isSyncToPrimaryMonitor) SyncToPrimaryMonitor();
if (url == null&&!song.isPublicSong) PlayNextSong();
return Task.CompletedTask;
}
catch (Exception ex)
@ -403,6 +407,7 @@ namespace DualScreenDemo
BeginInvoke(new Action(async () =>
{
await PlayNextSong();
if (PrimaryForm.Instance.isOnOrderedSongsPage) PrimaryForm.Instance.orderedSongsButton.PerformClick();
}));
}
}