Merged projects with plugins and modules
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
using Discord;
|
||||
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Interfaces;
|
||||
using DiscordBotCore.Online;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer.Commands;
|
||||
|
||||
public class AddMelody: DBCommand
|
||||
{
|
||||
public string Command => "add_melody";
|
||||
|
||||
public List<string>? Aliases => new()
|
||||
{
|
||||
"madd"
|
||||
};
|
||||
|
||||
public string Description => "Add a custom melody to the database";
|
||||
public string Usage => "add_melody [title],[description?],[aliases],[byteSize]";
|
||||
public bool requireAdmin => false;
|
||||
|
||||
public async void ExecuteServer(DbCommandExecutingArguments args)
|
||||
{
|
||||
var arguments = string.Join(" ", args.Arguments);
|
||||
string[] split = arguments.Split(',');
|
||||
|
||||
if (split.Length < 4)
|
||||
{
|
||||
var message = "";
|
||||
message += "Invalid arguments given. Please use the following format:\n";
|
||||
message += "add_melody [title],[description?],[aliases],[byteSize]\n";
|
||||
message += "title: The title of the melody\n";
|
||||
message += "description: The description of the melody\n";
|
||||
message += "aliases: The aliases of the melody. Use | to separate them\n";
|
||||
message += "byteSize: The byte size of the melody. Default is 1024. ( & will use default)\n";
|
||||
|
||||
await args.Context.Channel.SendMessageAsync(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Context.Message.Attachments.Count == 0)
|
||||
{
|
||||
await args.Context.Channel.SendMessageAsync("You must upload a valid .mp3 audio or .mp4 video file !!");
|
||||
return;
|
||||
}
|
||||
|
||||
var file = args.Context.Message.Attachments.FirstOrDefault();
|
||||
if (!(file.Filename.EndsWith(".mp3") || file.Filename.EndsWith(".mp4")))
|
||||
{
|
||||
await args.Context.Channel.SendMessageAsync("Invalid file format !!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var title = split[0];
|
||||
var description = split[1];
|
||||
string[]? aliases = split[2]?.Split('|') ?? null;
|
||||
var byteSize = split[3];
|
||||
int bsize;
|
||||
if (!int.TryParse(byteSize, out bsize))
|
||||
bsize = 1024;
|
||||
|
||||
|
||||
var msg = await args.Context.Channel.SendMessageAsync("Saving melody ...");
|
||||
Console.WriteLine("Saving melody");
|
||||
|
||||
IProgress<float> downloadProgress = new Progress<float>();
|
||||
|
||||
|
||||
var location = Application.GetResourceFullPath($"Music/Melodies/{title}.mp3");
|
||||
Directory.CreateDirectory(Application.GetResourceFullPath("Music/Melodies"));
|
||||
await ServerCom.DownloadFileAsync(file.Url, location, downloadProgress);
|
||||
|
||||
Console.WriteLine($"Done. Saved at {location}");
|
||||
|
||||
await msg.ModifyAsync(a => a.Content = "Done");
|
||||
|
||||
|
||||
var info =
|
||||
MusicInfoExtensions.CreateMusicInfo(title, location, description ?? "Unknown", aliases.ToList(), bsize);
|
||||
|
||||
Variables._MusicDatabase?.Add(title, info);
|
||||
|
||||
var builder = new EmbedBuilder();
|
||||
builder.Title = "A new music was successfully added !";
|
||||
builder.AddField("Title", info.Title);
|
||||
builder.AddField("Description", info.Description);
|
||||
builder.AddField("Aliases", string.Join(" | ", aliases));
|
||||
await args.Context.Channel.SendMessageAsync(embed: builder.Build());
|
||||
|
||||
await Variables._MusicDatabase.SaveToFile();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Interfaces;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer.Commands;
|
||||
|
||||
public class AddMelodyYoutube: DBCommand
|
||||
{
|
||||
public string Command => "add_melody_youtube";
|
||||
|
||||
public List<string>? Aliases => new()
|
||||
{
|
||||
"madd-yt"
|
||||
};
|
||||
|
||||
public string Description => "Add melody to the database from a youtube link";
|
||||
public string Usage => "add_melody_youtube [URL] <alias1|alias2|...>";
|
||||
public bool requireAdmin => true;
|
||||
|
||||
public async void ExecuteServer(DbCommandExecutingArguments args)
|
||||
{
|
||||
if (args.Arguments is null)
|
||||
{
|
||||
await args.Context.Channel.SendMessageAsync("Invalid arguments given. Please use the following format:\nadd_melody_youtube [URL]");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var URL = args.Arguments[0];
|
||||
|
||||
if (!URL.StartsWith("https://www.youtube.com/watch?v=") && !URL.StartsWith("https://youtu.be/"))
|
||||
{
|
||||
await args.Context.Channel.SendMessageAsync("Invalid URL given. Please use the following format:\nadd_melody_youtube [URL]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Arguments.Length <= 1)
|
||||
{
|
||||
await args.Channel.SendMessageAsync("Please specify at least one alias for the melody !");
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = await args.Context.Channel.SendMessageAsync("Saving melody ...");
|
||||
|
||||
var title = await YoutubeDLP.DownloadMelody(URL);
|
||||
|
||||
if (title == null)
|
||||
{
|
||||
await msg.ModifyAsync(x => x.Content = "Failed to download melody !");
|
||||
return;
|
||||
}
|
||||
|
||||
var joinedAliases = string.Join(" ", args.Arguments.Skip(1));
|
||||
List<string> aliases = joinedAliases.Split('|').ToList();
|
||||
|
||||
|
||||
if (Variables._MusicDatabase.ContainsMelodyWithNameOrAlias(title))
|
||||
Variables._MusicDatabase.Remove(title);
|
||||
|
||||
Variables._MusicDatabase.Add(title, new MusicInfo()
|
||||
{
|
||||
Aliases = aliases,
|
||||
ByteSize = 1024,
|
||||
Description = "Melody added from youtube link",
|
||||
Location = Application.GetResourceFullPath($"Music/Melodies/{title}.mp3"),
|
||||
Title = title
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
await Variables._MusicDatabase.SaveToFile();
|
||||
await msg.ModifyAsync(x => x.Content = "Melody saved !");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Discord;
|
||||
using DiscordBotCore.Interfaces;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer.Commands;
|
||||
|
||||
public class SearchMelody: DBCommand
|
||||
{
|
||||
|
||||
public string Command => "search_melody";
|
||||
public List<string>? Aliases => null;
|
||||
public string Description => "Search for a melody in the database";
|
||||
public string Usage => "search_melody [melody name OR one of its aliases]";
|
||||
public bool requireAdmin => false;
|
||||
|
||||
public void ExecuteServer(DbCommandExecutingArguments args)
|
||||
{
|
||||
var title = string.Join(" ", args.Arguments);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
args.Context.Channel.SendMessageAsync("You need to specify a melody name");
|
||||
return;
|
||||
}
|
||||
|
||||
List<MusicInfo>? info = Variables._MusicDatabase.GetMusicInfoList(title);
|
||||
if (info == null || info.Count == 0)
|
||||
{
|
||||
args.Context.Channel.SendMessageAsync("No melody with that name or alias was found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.Count > 1)
|
||||
args.Context.Channel.SendMessageAsync(embed: info.ToEmbed(Color.DarkOrange));
|
||||
else
|
||||
args.Context.Channel.SendMessageAsync(embed: info[0].ToEmbed(Color.DarkOrange));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Discord.WebSocket;
|
||||
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Interfaces;
|
||||
|
||||
namespace MusicPlayer.Events;
|
||||
|
||||
public class OnLoad: DBEvent
|
||||
{
|
||||
private static readonly string _DefaultMusicPath = "Music/";
|
||||
private static readonly string _DefaultSaveLocation = "Music/Melodies/";
|
||||
private static readonly string _DefaultMusicDB = "Music/music_db.json";
|
||||
public string Name => "Music Commands";
|
||||
public string Description => "The default music commands event loader";
|
||||
public bool RequireOtherThread => false;
|
||||
|
||||
public async void Start(DiscordSocketClient client)
|
||||
{
|
||||
var path1 = Application.GetResourceFullPath(_DefaultMusicPath);
|
||||
var path2 = Application.GetResourceFullPath(_DefaultSaveLocation);
|
||||
var path3 = Application.GetResourceFullPath(_DefaultMusicDB);
|
||||
Directory.CreateDirectory(path1);
|
||||
Directory.CreateDirectory(path2);
|
||||
Variables._MusicDatabase = new MusicDatabase(path3);
|
||||
await Variables._MusicDatabase.LoadFromFile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Discord.WebSocket;
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Interfaces;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer.Events;
|
||||
|
||||
public class OnVoiceRemoved: DBEvent
|
||||
{
|
||||
|
||||
public string Name => "Event: OnVoiceRemoved";
|
||||
public string Description => "Called when bot leaves a voice channel";
|
||||
|
||||
public bool RequireOtherThread => false;
|
||||
|
||||
public void Start(DiscordSocketClient client)
|
||||
{
|
||||
client.UserVoiceStateUpdated += async (user, oldState, newState) =>
|
||||
{
|
||||
if (user.Id == client.CurrentUser.Id && newState.VoiceChannel == null)
|
||||
{
|
||||
Variables._MusicPlayer?.MusicQueue.Clear();
|
||||
Variables._MusicPlayer?.Skip();
|
||||
Variables._MusicPlayer?.Stop();
|
||||
await Variables.audioClient!.StopAsync();
|
||||
Variables.audioClient = null;
|
||||
Variables._MusicPlayer = null;
|
||||
|
||||
Application.CurrentApplication.Logger.Log("Bot left voice channel.", this, LogType.Info);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer;
|
||||
|
||||
public class MusicDatabase: SettingsDictionary<string, MusicInfo>
|
||||
{
|
||||
public MusicDatabase(string file): base(file)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the database contains a melody with the specified name or alias
|
||||
/// </summary>
|
||||
/// <param name="melodyName">The name (alias) of the melody</param>
|
||||
/// <returns></returns>
|
||||
public bool ContainsMelodyWithNameOrAlias(string melodyName)
|
||||
{
|
||||
return ContainsKey(melodyName) || Values.Any(elem => elem.Aliases.Contains(melodyName, StringComparer.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the music info of a melody with the specified name or alias. Returns the first match or null if no match was found.
|
||||
/// </summary>
|
||||
/// <param name="searchQuery">The music name or one of its aliases.</param>
|
||||
/// <returns></returns>
|
||||
public MusicInfo? GetMusicInfo(string searchQuery)
|
||||
{
|
||||
return FirstOrDefault(kvp => kvp.Key.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase) ||
|
||||
kvp.Value.Aliases.Any(alias => alias.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase))
|
||||
).Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of music info that match the search query. Returns null if an error occurred, or empty list if no match was found.
|
||||
/// </summary>
|
||||
/// <param name="searchQuery">The search query</param>
|
||||
/// <returns>null if an error occured, otherwise a list with songs that match the search query. If no song match the list is empty</returns>
|
||||
public List<MusicInfo>? GetMusicInfoList(string searchQuery)
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.Where(kvp =>
|
||||
kvp.Key.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase) ||
|
||||
kvp.Value.Aliases.Any(alias => alias.Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase))
|
||||
)
|
||||
.Select(item => item.Value).ToList();
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry to the database based on the music info. It uses the title as the key.
|
||||
/// </summary>
|
||||
/// <param name="musicInfo">The music to add to</param>
|
||||
public void AddNewEntryBasedOnMusicInfo(MusicInfo musicInfo)
|
||||
{
|
||||
Add(musicInfo.Title, musicInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace MusicPlayer;
|
||||
|
||||
public class MusicInfo
|
||||
{
|
||||
public string Title { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public string Location { get; init; }
|
||||
public List<string>? Aliases { get; init; }
|
||||
public int? ByteSize { get; init; } = 1024;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Discord;
|
||||
|
||||
namespace MusicPlayer;
|
||||
|
||||
public static class MusicInfoExtensions
|
||||
{
|
||||
public static void AddAlias(this MusicInfo musicInfo, string alias)
|
||||
{
|
||||
musicInfo.Aliases.Add(alias);
|
||||
}
|
||||
|
||||
public static void RemoveAlias(this MusicInfo musicInfo, string alias)
|
||||
{
|
||||
musicInfo.Aliases.Remove(alias);
|
||||
}
|
||||
|
||||
public static MusicInfo CreateMusicInfo(string title, string fileLocation, string? Description = "Unknown", List<string>? aliases = null, int? byteSize = 1024)
|
||||
{
|
||||
return new MusicInfo()
|
||||
{
|
||||
Title = title,
|
||||
Aliases = aliases,
|
||||
Description = Description,
|
||||
Location = fileLocation,
|
||||
ByteSize = byteSize
|
||||
};
|
||||
}
|
||||
|
||||
public static Embed ToEmbed(this MusicInfo musicInfo, Color? embedColor = null)
|
||||
{
|
||||
var builder = new EmbedBuilder();
|
||||
builder.Color = embedColor ?? Color.Default;
|
||||
builder.WithTitle(musicInfo.Title);
|
||||
builder.WithDescription(musicInfo.Description);
|
||||
if (musicInfo.Aliases != null)
|
||||
builder.AddField("Aliases", string.Join(", ", musicInfo.Aliases));
|
||||
else
|
||||
builder.AddField("Aliases", "None");
|
||||
builder.AddField("Location", musicInfo.Location);
|
||||
builder.AddField("ByteSize", musicInfo.ByteSize);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static Embed ToEmbed(this List<MusicInfo> musicInfo, Color? embedColor = null)
|
||||
{
|
||||
var builder = new EmbedBuilder();
|
||||
builder.Color = embedColor ?? Color.Default;
|
||||
builder.WithTitle("Search results");
|
||||
builder.WithDescription("Found " + musicInfo.Count + " results");
|
||||
builder.AddField("Results", string.Join("\n", musicInfo.Select(item => item.Title)));
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System.Diagnostics;
|
||||
using Discord.Audio;
|
||||
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer;
|
||||
|
||||
public class MusicPlayer
|
||||
{
|
||||
private static int defaultByteSize = 1024;
|
||||
|
||||
public Queue<MusicInfo> 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<MusicInfo>();
|
||||
}
|
||||
|
||||
public async Task PlayQueue()
|
||||
{
|
||||
if (isQueueRunning)
|
||||
{
|
||||
Application.CurrentApplication.Logger.Log("Another queue is running !", typeof(MusicPlayer), LogType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Variables.audioClient is null)
|
||||
{
|
||||
Application.CurrentApplication.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.CurrentApplication.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);
|
||||
|
||||
Console.WriteLine("FFMPEG Path: " + 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.CurrentApplication.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<MusicInfo> 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.CurrentApplication.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(string musicName)
|
||||
{
|
||||
var minfo = Variables._MusicDatabase.GetMusicInfo(musicName);
|
||||
if (minfo is null) return false;
|
||||
|
||||
MusicQueue.Enqueue(minfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Skip()
|
||||
{
|
||||
isPlaying = false;
|
||||
}
|
||||
|
||||
public void SetVolume(float volume)
|
||||
{
|
||||
// set volume
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="libs\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="libs\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="libs\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,54 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using DiscordBotCore.Interfaces;
|
||||
|
||||
namespace MusicPlayer.SlashCommands;
|
||||
|
||||
public class Loop: DBSlashCommand
|
||||
{
|
||||
|
||||
public string Name => "loop";
|
||||
public string Description => "Loop the current song for a certain amount of times. If no times are specified, it will loop once";
|
||||
public bool canUseDM => false;
|
||||
public bool HasInteraction => false;
|
||||
|
||||
public List<SlashCommandOptionBuilder> Options => new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Type = ApplicationCommandOptionType.Integer,
|
||||
Name = "times",
|
||||
Description = "How many times to loop the song",
|
||||
IsRequired = false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
if (Variables._MusicPlayer.CurrentlyPlaying == null)
|
||||
{
|
||||
context.RespondAsync("There is nothing playing right now");
|
||||
return;
|
||||
}
|
||||
|
||||
var times = context.Data.Options.FirstOrDefault()?.Value.ToString() ?? "1";
|
||||
|
||||
if (!int.TryParse(times, out var timesToLoop))
|
||||
{
|
||||
context.RespondAsync("Invalid number");
|
||||
return;
|
||||
}
|
||||
|
||||
if (timesToLoop < 1)
|
||||
{
|
||||
context.RespondAsync("You need to specify a number greater than 0");
|
||||
return;
|
||||
}
|
||||
|
||||
Variables._MusicPlayer.Loop(timesToLoop);
|
||||
|
||||
context.RespondAsync($"Looping {Variables._MusicPlayer.CurrentlyPlaying.Title} {timesToLoop} times. Check the queue to see the progress");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
|
||||
using DiscordBotCore;
|
||||
using DiscordBotCore.Interfaces;
|
||||
using DiscordBotCore.Others;
|
||||
|
||||
namespace MusicPlayer.SlashCommands;
|
||||
|
||||
public class Play: DBSlashCommand
|
||||
{
|
||||
public string Name => "play";
|
||||
public string Description => "Play music command";
|
||||
public bool canUseDM => false;
|
||||
public bool HasInteraction => false;
|
||||
|
||||
public List<SlashCommandOptionBuilder> Options => new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
IsRequired = true,
|
||||
Description = "The music name to be played",
|
||||
Name = "music-name",
|
||||
Type = ApplicationCommandOptionType.String
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
public async void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
var melodyName = context.Data.Options.First().Value as string;
|
||||
|
||||
if (melodyName is null)
|
||||
{
|
||||
await context.RespondAsync("Failed to retrieve melody with name " + melodyName);
|
||||
return;
|
||||
}
|
||||
|
||||
var melody = Variables._MusicDatabase.GetMusicInfo(melodyName);
|
||||
if (melody is null)
|
||||
{
|
||||
await context.RespondAsync("The searched melody does not exists in the database. Sorry :(");
|
||||
return;
|
||||
}
|
||||
|
||||
var user = context.User as IGuildUser;
|
||||
if (user is null)
|
||||
{
|
||||
await context.RespondAsync("Failed to get user data from channel ! Check error log at " + DateTime.Now.ToLongTimeString());
|
||||
Application.CurrentApplication.Logger.Log("User is null while trying to convert from context.User to IGuildUser.", typeof(Play), LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var voiceChannel = user.VoiceChannel;
|
||||
if (voiceChannel is null)
|
||||
{
|
||||
await context.RespondAsync("Unknown voice channel. Maybe I do not have permission to join it ?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Variables.audioClient is null)
|
||||
{
|
||||
Variables.audioClient = await voiceChannel.ConnectAsync(true); // self deaf
|
||||
}
|
||||
|
||||
Variables._MusicPlayer ??= new MusicPlayer();
|
||||
|
||||
if (!Variables._MusicPlayer.Enqueue(melodyName))
|
||||
{
|
||||
await context.RespondAsync("Failed to enqueue your request. Something went wrong !");
|
||||
return;
|
||||
}
|
||||
await context.RespondAsync("Enqueued your request");
|
||||
|
||||
await Variables._MusicPlayer.PlayQueue(); //start queue
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using DiscordBotCore.Interfaces;
|
||||
|
||||
namespace MusicPlayer.SlashCommands;
|
||||
|
||||
public class Queue: DBSlashCommand
|
||||
{
|
||||
public string Name => "queue";
|
||||
public string Description => "Queue a melody to play";
|
||||
public bool canUseDM => false;
|
||||
public bool HasInteraction => false;
|
||||
public List<SlashCommandOptionBuilder> Options => null;
|
||||
|
||||
|
||||
public async void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
if (Variables._MusicPlayer is null)
|
||||
{
|
||||
await context.RespondAsync("No music is currently playing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Variables._MusicPlayer.MusicQueue.Count == 0 && Variables._MusicPlayer.CurrentlyPlaying == null)
|
||||
{
|
||||
await context.RespondAsync("No music is currently playing");
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new EmbedBuilder()
|
||||
{
|
||||
Title = "Music Queue",
|
||||
Description = "Here is the current music queue",
|
||||
Color = Color.Blue
|
||||
};
|
||||
|
||||
if (Variables._MusicPlayer.CurrentlyPlaying != null)
|
||||
builder.AddField("Current music", Variables._MusicPlayer.CurrentlyPlaying.Title);
|
||||
|
||||
var i = 1;
|
||||
foreach (var melody in Variables._MusicPlayer.MusicQueue)
|
||||
{
|
||||
builder.AddField($"#{i}", melody.Title);
|
||||
i++;
|
||||
}
|
||||
|
||||
await context.RespondAsync(embed: builder.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using DiscordBotCore.Interfaces;
|
||||
|
||||
namespace MusicPlayer.SlashCommands;
|
||||
|
||||
public class Skip: DBSlashCommand
|
||||
{
|
||||
public string Name => "skip";
|
||||
public string Description => "Skip the current melody";
|
||||
public bool canUseDM => false;
|
||||
public bool HasInteraction => false;
|
||||
public List<SlashCommandOptionBuilder> Options => null;
|
||||
|
||||
public async void ExecuteServer(SocketSlashCommand context)
|
||||
{
|
||||
if (Variables._MusicPlayer is null)
|
||||
{
|
||||
await context.RespondAsync("No music is currently playing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Variables._MusicPlayer.MusicQueue.Count == 0 && Variables._MusicPlayer.CurrentlyPlaying == null)
|
||||
{
|
||||
await context.RespondAsync("No music is currently playing");
|
||||
return;
|
||||
}
|
||||
|
||||
var melodyTitle = Variables._MusicPlayer.CurrentlyPlaying.Title;
|
||||
|
||||
await context.RespondAsync($"Skipping {melodyTitle} ...");
|
||||
Variables._MusicPlayer.Skip();
|
||||
await context.ModifyOriginalResponseAsync(x => x.Content = $"Skipped {melodyTitle}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Discord.Audio;
|
||||
|
||||
namespace MusicPlayer;
|
||||
|
||||
public class Variables
|
||||
{
|
||||
public static MusicDatabase? _MusicDatabase;
|
||||
public static MusicPlayer? _MusicPlayer;
|
||||
|
||||
public static IAudioClient? audioClient;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace MusicPlayer;
|
||||
|
||||
public class YoutubeDLP
|
||||
{
|
||||
public static async Task<string?> DownloadMelody(string url)
|
||||
{
|
||||
Console.WriteLine("Downloading melody: " + url);
|
||||
var process = new Process();
|
||||
process.StartInfo.FileName = await DiscordBotCore.Application.CurrentApplication.PluginManager.GetDependencyLocation("yt-dlp");
|
||||
process.StartInfo.Arguments = $"-x --force-overwrites -o \"{DiscordBotCore.Application.GetResourceFullPath("/Music/Melodies")}/%(title)s.%(ext)s\" --audio-format mp3 {url}";
|
||||
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
var title = "";
|
||||
process.OutputDataReceived += (sender, args) =>
|
||||
{
|
||||
if (args.Data != null)
|
||||
{
|
||||
if (args.Data.StartsWith("[ExtractAudio] Destination: "))
|
||||
{
|
||||
title = args.Data.Replace("[ExtractAudio] Destination: ", "").Replace(".mp3", "");
|
||||
title = title.Replace("\\", "/");
|
||||
title = title.Split('/').Last().Replace(".mp3", "").TrimEnd();
|
||||
|
||||
Console.WriteLine("Output title: " + title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(args.Data);
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
Console.WriteLine("Waiting for process to exit ...");
|
||||
process.BeginOutputReadLine();
|
||||
await process.WaitForExitAsync();
|
||||
|
||||
return title ?? null;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user