165 lines
5.4 KiB
C#
165 lines
5.4 KiB
C#
|
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<MusicInfo> 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<MusicInfo>();
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|