using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using Discord; using Discord.Audio; using Discord.WebSocket; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace KasinoBot { public class Music { private IAudioClient AudioClient { get; set; } private ISocketMessageChannel TextChannel { get; set; } private Queue Queue { get; } private Stream? CurrentMusicStream { get; set; } private Process? CurrentFFmpeg { get; set; } private AudioOutStream? CurrentOuAudio { get; set; } public Music(SocketGuild guild, ISocketMessageChannel channel, IAudioClient audioClient) { Queue = new Queue(); TextChannel = channel; AudioClient = audioClient; } public async Task Play(string query, SocketUser user, SocketUserMessage message) { if (message.Channel != null) TextChannel = message.Channel; MusicInfo musicInfo = new MusicInfo(query, user); //await message.AddReactionAsync(Emoji.Parse("👌")); await TextChannel.SendMessageAsync($"Enqueued **\"{musicInfo.Title}\"** *({musicInfo.Duration})*"); Queue.Enqueue(musicInfo); if (Queue.Count == 1) await PlayNextMusic(); } public async Task Stop() { Queue.Clear(); if (CurrentMusicStream != null) CurrentMusicStream.Close(); await Task.Delay(10); } public async Task Skip() { Console.WriteLine("Skipping"); if (CurrentMusicStream != null) CurrentMusicStream.Close(); await Task.Delay(10); } private async Task PlayNextMusic() { if (Queue.Count == 0) return; try { var musicInfo = Queue.Peek(); using (var input = AudioClient.CreatePCMStream(AudioApplication.Music)) using (CurrentFFmpeg = CreateAudioStream(musicInfo.MediaURL)) using (CurrentMusicStream = CurrentFFmpeg.StandardOutput.BaseStream) { try { await CurrentMusicStream.CopyToAsync(input); } catch (Exception ex) { Console.WriteLine("Error CopyToAsync: " + ex.ToString()); } finally { Queue.Dequeue(); await input.FlushAsync(); } } } catch (Exception e) { await TextChannel.SendMessageAsync($"Error: {e.Message}"); Console.WriteLine(e); } await PlayNextMusic(); } private Process CreateAudioStream(string path) { var p = Process.Start(new ProcessStartInfo { FileName = "ffmpeg", Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", UseShellExecute = false, RedirectStandardOutput = true, }); if (p == null) throw new Exception("Could not start ffmpeg. Please make sure ffmpeg is available."); else if (p.HasExited) throw new Exception("ffmpeg exited with code " + p.ExitCode); return p; } } public class MusicInfo { public string Query { get; } public SocketUser User { get; } public string Title { get; set; } public string Duration { get; set; } public string MediaURL { get; set; } public string YoutubeURL { get; set; } public MusicInfo(string query, SocketUser user) { Query = query; User = user; var p = Process.Start(new ProcessStartInfo { FileName = "youtube-dl", Arguments = $"-f bestaudio --dump-json --default-search \"ytsearch\" -g \"{this.Query}\"", UseShellExecute = false, RedirectStandardOutput = true, }); if (p == null) throw new Exception("Could not start youtube-dl. Please make sure youtube-dl is available."); else if (p.HasExited) throw new Exception("youtube-dl exited with code " + p.ExitCode); var output = p.StandardOutput.ReadToEnd(); p.WaitForExit(); try { JToken json = JObject.Parse(output.Split('\n')[1]); if (json == null) throw new Exception("Could not parse youtube-dl output."); int duration = int.Parse(json["duration"].ToString()); this.MediaURL = output.Split('\n')[0]; this.Title = json["title"].ToString(); this.YoutubeURL = json["webpage_url"].ToString(); this.Duration = $"{Math.Floor((float)duration / 60)}m {duration % 60}s"; } catch (Exception e) { throw new Exception("Error parsing information from Youtube-DL: " + e.Message); } } } }