using System.Diagnostics; using Discord.Audio; using DiscordBotCore; using DiscordBotCore.Others; namespace MusicPlayer; public class MusicPlayer { private static int defaultByteSize = 1024; public Queue MusicQueue { get; private set; } public bool isPaused { get; private set; } public bool isPlaying { get; private set; } private bool isQueueRunning; public int ByteSize { get; private set; } public MusicInfo? CurrentlyPlaying { get; private set; } public MusicPlayer() { MusicQueue = new Queue(); } public async Task PlayQueue() { if (isQueueRunning) { Application.Logger.Log("Another queue is running !", typeof(MusicPlayer), LogType.Warning); return; } if (Variables.audioClient is null) { Application.Logger.Log("Audio Client is null", typeof(MusicPlayer), LogType.Warning); return; } isQueueRunning = true; string? ffmpegPath = await Application.CurrentApplication.PluginManager.GetDependencyLocation("FFMPEG"); if(ffmpegPath is null) { Application.Logger.Log("FFMPEG is missing. Please install it and try again.", typeof(MusicPlayer), LogType.Error); isQueueRunning = false; return; } ffmpegPath = ffmpegPath.Replace("\\", "/"); ffmpegPath = Path.GetFullPath(ffmpegPath); while (MusicQueue.TryDequeue(out var dequeuedMusic)) { CurrentlyPlaying = dequeuedMusic; await using var dsAudioStream = Variables.audioClient.CreatePCMStream(AudioApplication.Mixed); using var ffmpeg = CreateStream(ffmpegPath, CurrentlyPlaying.Location); if (ffmpeg is null) { Application.Logger.Log($"Failed to start ffmpeg process. FFMPEG is missing or the {CurrentlyPlaying.Location} has an invalid format.", typeof(MusicPlayer), LogType.Error); continue; } await using var ffmpegOut = ffmpeg.StandardOutput.BaseStream; await PlayCurrentTrack(dsAudioStream, ffmpegOut, CurrentlyPlaying.ByteSize ?? defaultByteSize); } isQueueRunning = false; CurrentlyPlaying = null; } public void Loop(int numberOfTimes) { if (CurrentlyPlaying is null) return; Queue tempQueue = new(); for (var i = 0; i < numberOfTimes; i++) { tempQueue.Enqueue(CurrentlyPlaying); } foreach (var musicInfo in MusicQueue) { tempQueue.Enqueue(musicInfo); } MusicQueue = tempQueue; } private async Task PlayCurrentTrack(Stream discordVoiceChannelStream, Stream fileStreamFfmpeg, int byteSize) { if (isPlaying) return; ByteSize = byteSize; isPlaying = true; isPaused = false; while (isPlaying) { if (isPaused) continue; var bits = new byte[byteSize]; var read = await fileStreamFfmpeg.ReadAsync(bits, 0, ByteSize); if (read == 0) break; try { await discordVoiceChannelStream.WriteAsync(bits, 0, read); } catch (Exception ex) { Application.Logger.LogException(ex, this); break; } } await discordVoiceChannelStream.FlushAsync(); await fileStreamFfmpeg.FlushAsync(); isPlaying = false; isPaused = false; } public void Pause() { isPaused = true; } public void Unpause() { isPaused = false; } public bool Enqueue(MusicInfo music) { MusicQueue.Enqueue(music); return true; } public void Skip() { isPlaying = false; } private static Process? CreateStream(string? fileName, string path) { return Process.Start(new ProcessStartInfo { FileName = fileName, Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", UseShellExecute = false, RedirectStandardOutput = true } ); } public void Stop() { MusicQueue.Clear(); isPlaying = false; } }