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,14 +1,15 @@
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;
@ -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)
{
@ -103,16 +106,37 @@ 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;
@ -120,7 +144,38 @@ namespace DBObj
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

@ -84,7 +84,7 @@ namespace DualScreenDemo
{
try
{
HttpListenerContext context = await _listener.GetContextAsync();
HttpListenerContext context = await _listener.GetContextAsync();
await ProcessRequestAsync(context, baseDirectory, randomFolderName);
}
catch (HttpListenerException)
@ -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 { }
@ -377,7 +399,7 @@ namespace DualScreenDemo
.Select(song => new
{
Song = song.getName(),
ArtistA =song.getArtist_A(),
ArtistA = song.getArtist_A(),
SongNumber = song.getNumber(),
})
.ToList();
@ -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,14 +523,14 @@ 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":
@ -528,8 +550,9 @@ namespace DualScreenDemo
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":
@ -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,8 +691,8 @@ 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()
@ -704,9 +727,10 @@ namespace DualScreenDemo
Console.WriteLine("Received order song request: " + requestBody);
// 解析 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($"Ordering Song: {song.getName()} by {song.getArtist_A()}");
// 这里可以添加处理逻辑,例如将歌曲加入到播放列表或数据库中
@ -716,7 +740,9 @@ namespace DualScreenDemo
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();
}
@ -856,7 +887,7 @@ namespace DualScreenDemo
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
{
@ -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,9 +1047,9 @@ namespace DualScreenDemo
//SongListManager.Instance.UserLogin(phone);
// 获取用户的收藏歌曲
var favoriteSongs=new List<SongData>(); //= SongListManager.Instance.GetFavoriteSongsByPhoneNumber();
var favoriteSongs = new List<SongData>(); //= SongListManager.Instance.GetFavoriteSongsByPhoneNumber();
string query =PrimaryForm.Instance.SearchFavoriteSongs_Mysql();
string query = PrimaryForm.Instance.SearchFavoriteSongs_Mysql();
var searchResults = PrimaryForm.Instance.SearchSongs_Mysql(query);
// 创建响应数据
var response = new
@ -1029,12 +1060,12 @@ namespace DualScreenDemo
.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(),

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();
}));
}
}