NadekoBot/src/NadekoBot/Modules/Music/Music.cs

912 lines
38 KiB
C#
Raw Normal View History

using Discord.Commands;
using NadekoBot.Modules.Music.Classes;
using System.Collections.Concurrent;
using Discord.WebSocket;
using NadekoBot.Services;
using System.IO;
using Discord;
using System.Threading.Tasks;
using NadekoBot.Attributes;
using System;
using System.Linq;
using NadekoBot.Extensions;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using NadekoBot.Services.Database.Models;
2016-12-14 19:29:01 +00:00
using System.Text.RegularExpressions;
2016-12-21 12:14:24 +00:00
using System.Threading;
namespace NadekoBot.Modules.Music
{
2016-12-17 00:16:14 +00:00
[NadekoModule("Music", "!!")]
[DontAutoLoad]
2016-08-21 22:47:06 +00:00
public partial class Music : DiscordModule
{
2016-12-08 17:46:08 +00:00
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
public const string MusicDataPath = "data/musicdata";
2017-01-01 12:57:38 +00:00
static Music()
{
//it can fail if its currenctly opened or doesn't exist. Either way i don't care
try { Directory.Delete(MusicDataPath, true); } catch { }
2016-12-21 12:14:24 +00:00
NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
Directory.CreateDirectory(MusicDataPath);
}
2016-12-21 12:14:24 +00:00
2017-01-01 12:57:38 +00:00
private static async void Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState)
2016-12-14 19:48:44 +00:00
{
2016-12-17 00:16:14 +00:00
var usr = iusr as SocketGuildUser;
2016-12-14 19:48:44 +00:00
if (usr == null ||
oldState.VoiceChannel == newState.VoiceChannel)
2016-12-16 19:45:46 +00:00
return;
2016-12-14 19:48:44 +00:00
MusicPlayer player;
if (!MusicPlayers.TryGetValue(usr.Guild.Id, out player))
2016-12-16 19:45:46 +00:00
return;
try
{
var users = await player.PlaybackVoiceChannel.GetUsersAsync().Flatten().ConfigureAwait(false);
if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause
player.Paused &&
users.Count() == 2) || // keep in mind bot is in the channel (+1)
(player.PlaybackVoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause
!player.Paused &&
users.Count() == 1))
{
player.TogglePause();
}
}
2017-01-01 12:57:38 +00:00
catch { }
2016-12-14 19:48:44 +00:00
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public Task Next(int skipCount = 1)
{
if (skipCount < 1)
return Task.CompletedTask;
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask;
2016-12-16 18:43:57 +00:00
if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)Context.User).VoiceChannel)
{
while (--skipCount > 0)
{
musicPlayer.RemoveSongAt(0);
}
musicPlayer.Next();
}
return Task.CompletedTask;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public Task Stop()
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
{
musicPlayer.Autoplay = false;
musicPlayer.Stop();
}
return Task.CompletedTask;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public Task Destroy()
{
2017-01-04 12:23:30 +00:00
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask;
2017-01-04 12:23:30 +00:00
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
if (MusicPlayers.TryRemove(Context.Guild.Id, out musicPlayer))
2016-10-15 23:37:29 +00:00
musicPlayer.Destroy();
2017-01-04 12:23:30 +00:00
return Task.CompletedTask;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public Task Pause()
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
2016-12-15 15:51:09 +00:00
return Task.CompletedTask;
musicPlayer.TogglePause();
2016-12-15 15:51:09 +00:00
return Task.CompletedTask;
}
2016-12-25 05:39:04 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-31 10:55:12 +00:00
public async Task Fairplay()
2016-12-25 05:39:04 +00:00
{
2016-12-31 10:55:12 +00:00
var channel = (ITextChannel)Context.Channel;
2016-12-25 05:39:04 +00:00
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return;
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
2016-12-25 05:39:04 +00:00
return;
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Queue([Remainder] string query)
{
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query).ConfigureAwait(false);
2016-12-17 04:09:04 +00:00
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
{
2016-12-31 10:55:12 +00:00
Context.Message.DeleteAfter(10);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task SoundCloudQueue([Remainder] string query)
{
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
2016-12-17 04:09:04 +00:00
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
{
Context.Message.DeleteAfter(10);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task ListQueue(int page = 1)
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
return;
}
if (page <= 0)
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
2016-10-19 10:40:43 +00:00
{
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
return;
2016-10-19 10:40:43 +00:00
}
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
const int itemsPerPage = 10;
2016-12-27 14:18:12 +00:00
var total = musicPlayer.TotalPlaytime;
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
var lastPage = musicPlayer.Playlist.Count / itemsPerPage;
Func<int, EmbedBuilder> printAction = (curPage) =>
{
int startAt = itemsPerPage * (curPage - 1);
var number = 0 + startAt;
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue")
.WithMusicIcon())
.WithDescription(string.Join("\n", musicPlayer.Playlist
.Skip(startAt)
.Take(itemsPerPage)
.Select(v => $"`{++number}.` {v.PrettyFullName}")))
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " +
(musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
.WithOkColor();
if (musicPlayer.RepeatSong)
{
embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}");
}
else if (musicPlayer.RepeatPlaylist)
{
embed.WithTitle("🔁 Repeating Playlist");
}
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
{
embed.WithTitle("🎵 Song queue is full!");
}
return embed;
};
await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public async Task NowPlaying()
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
return;
2016-12-22 22:29:25 +00:00
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
2016-10-19 10:40:43 +00:00
2016-12-27 13:32:58 +00:00
var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
.WithDescription(currentSong.PrettyName)
.WithThumbnailUrl(currentSong.Thumbnail)
2016-12-27 14:18:12 +00:00
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
2016-12-27 13:32:58 +00:00
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Volume(int val)
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
if (val < 0)
return;
var volume = musicPlayer.SetVolume(val);
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"🎵 Volume set to {volume}%").ConfigureAwait(false);
}
2016-08-30 01:22:15 +00:00
[NadekoCommand, Usage, Description, Aliases]
2016-08-30 01:22:15 +00:00
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Defvol([Remainder] int val)
2016-08-30 01:22:15 +00:00
{
2016-12-21 12:14:24 +00:00
2016-08-30 01:22:15 +00:00
if (val < 0 || val > 100)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false);
2016-08-30 01:22:15 +00:00
return;
}
using (var uow = DbHandler.UnitOfWork())
{
2016-12-17 00:16:14 +00:00
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
2016-08-30 01:22:15 +00:00
uow.Complete();
}
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false);
2016-08-30 01:22:15 +00:00
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public async Task ShufflePlaylist()
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
if (musicPlayer.Playlist.Count < 2)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false);
return;
}
musicPlayer.Shuffle();
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Playlist([Remainder] string playlist)
{
2016-12-21 12:14:24 +00:00
var arg = playlist;
if (string.IsNullOrWhiteSpace(arg))
return;
2016-12-17 00:16:14 +00:00
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
{
2017-01-10 12:44:26 +00:00
await Context.Channel.SendErrorAsync($"💢 You need to be in a **voice channel** on this server.").ConfigureAwait(false);
return;
}
2016-12-08 17:46:08 +00:00
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
if (plId == null)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("No search results for that query.");
return;
}
2016-12-08 17:46:08 +00:00
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
if (!ids.Any())
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
return;
}
var count = ids.Count();
var msg = await Context.Channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
2016-12-27 00:35:46 +00:00
2016-12-21 12:14:24 +00:00
var cancelSource = new CancellationTokenSource();
var gusr = (IGuildUser)Context.User;
2016-12-27 00:35:46 +00:00
while (ids.Any() && !cancelSource.IsCancellationRequested)
{
var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
{
if (cancelSource.Token.IsCancellationRequested)
2016-12-27 00:35:46 +00:00
return;
try
{
await QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
2016-12-27 00:35:46 +00:00
}
catch (SongNotFoundException) { }
catch { try { cancelSource.Cancel(); } catch { } }
}));
2016-12-21 12:14:24 +00:00
2016-12-27 00:35:46 +00:00
await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
ids = ids.Skip(5);
}
2016-12-21 12:14:24 +00:00
2016-12-14 19:29:01 +00:00
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task SoundCloudPl([Remainder] string pl)
{
2016-12-21 12:14:24 +00:00
pl = pl?.Trim();
if (string.IsNullOrWhiteSpace(pl))
return;
using (var http = new HttpClient())
{
var scvids = JObject.Parse(await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Credentials.SoundCloudClientId}").ConfigureAwait(false))["tracks"].ToObject<SoundCloudVideo[]>();
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false);
MusicPlayer mp;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out mp))
return;
foreach (var svideo in scvids.Skip(1))
{
try
{
mp.AddSong(new Song(new Classes.SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = MusicType.Normal,
Query = svideo.TrackLink,
2016-12-16 18:43:57 +00:00
}), ((IGuildUser)Context.User).Username);
}
catch (PlaylistFullException) { break; }
}
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
2016-12-16 19:45:46 +00:00
public async Task LocalPl([Remainder] string directory)
{
2016-12-21 12:14:24 +00:00
var arg = directory;
if (string.IsNullOrWhiteSpace(arg))
return;
try
{
var dir = new DirectoryInfo(arg);
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)Context.User;
foreach (var file in fileEnum)
{
try
{
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
}
catch (PlaylistFullException)
{
break;
}
catch { }
}
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
}
catch { }
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Radio(string radio_link)
{
2016-12-21 12:14:24 +00:00
2016-12-17 00:16:14 +00:00
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining it.").ConfigureAwait(false);
return;
}
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
2016-12-17 04:09:04 +00:00
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
{
2016-12-31 10:55:12 +00:00
Context.Message.DeleteAfter(10);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
2016-12-16 19:45:46 +00:00
public async Task Local([Remainder] string path)
{
2016-12-21 12:14:24 +00:00
var arg = path;
if (string.IsNullOrWhiteSpace(arg))
return;
2016-12-17 00:16:14 +00:00
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, path, musicType: MusicType.Local).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public async Task Move()
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-16 18:43:57 +00:00
var voiceChannel = ((IGuildUser)Context.User).VoiceChannel;
2016-12-17 00:16:14 +00:00
if (voiceChannel == null || voiceChannel.Guild != Context.Guild || !MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
await musicPlayer.MoveToVoiceChannel(voiceChannel);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-10-19 08:10:57 +00:00
[Priority(0)]
public Task Remove(int num)
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return Task.CompletedTask;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return Task.CompletedTask;
2017-01-03 15:02:02 +00:00
musicPlayer.RemoveSongAt(num - 1);
return Task.CompletedTask;
}
2016-09-03 13:04:07 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-10-19 08:10:57 +00:00
[Priority(1)]
2016-12-16 19:45:46 +00:00
public async Task Remove(string all)
{
if (all.Trim().ToUpperInvariant() != "ALL")
return;
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return;
musicPlayer.ClearQueue();
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false);
return;
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task MoveSong([Remainder] string fromto)
{
2016-12-17 00:16:14 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
{
return;
}
fromto = fromto?.Trim();
var fromtoArr = fromto.Split('>');
int n1;
int n2;
var playlist = musicPlayer.Playlist as List<Song> ?? musicPlayer.Playlist.ToList();
if (fromtoArr.Length != 2 || !int.TryParse(fromtoArr[0], out n1) ||
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
n1 > playlist.Count || n2 > playlist.Count)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("Invalid input.").ConfigureAwait(false);
return;
}
var s = playlist[n1 - 1];
playlist.Insert(n2 - 1, s);
var nn1 = n2 < n1 ? n1 : n1 - 1;
playlist.RemoveAt(nn1);
var embed = new EmbedBuilder()
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
.WithUrl($"{s.SongInfo.Query}")
.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
2016-12-14 19:29:01 +00:00
//await channel.SendConfirmAsync($"🎵Moved {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task SetMaxQueue(uint size)
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
musicPlayer.MaxQueueSize = size;
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-31 10:55:12 +00:00
public async Task SetMaxPlaytime(uint seconds)
2016-12-27 13:32:58 +00:00
{
2016-12-27 14:18:12 +00:00
if (seconds < 15 && seconds != 0)
2016-12-27 13:32:58 +00:00
return;
2016-12-31 10:55:12 +00:00
var channel = (ITextChannel)Context.Channel;
2016-12-27 13:32:58 +00:00
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
musicPlayer.MaxPlaytimeSeconds = seconds;
if (seconds == 0)
2016-12-27 13:32:58 +00:00
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
else
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ReptCurSong()
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
return;
var currentValue = musicPlayer.ToggleRepeatSong();
if (currentValue)
await Context.Channel.EmbedAsync(new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
2016-12-24 19:49:29 +00:00
.WithDescription(currentSong.PrettyName)
2016-12-31 10:55:12 +00:00
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false);
else
2016-12-31 10:55:12 +00:00
await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 18:43:57 +00:00
public async Task RepeatPl()
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
var currentValue = musicPlayer.ToggleRepeatPlaylist();
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"🔁 Repeat playlist {(currentValue ? "**enabled**." : "**disabled**.")}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Save([Remainder] string name)
{
2016-12-21 12:14:24 +00:00
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
var curSong = musicPlayer.CurrentSong;
var songs = musicPlayer.Playlist.Append(curSong)
2016-12-21 12:14:24 +00:00
.Select(s => new PlaylistSong()
{
Provider = s.SongInfo.Provider,
ProviderType = s.SongInfo.ProviderType,
Title = s.SongInfo.Title,
Uri = s.SongInfo.Uri,
Query = s.SongInfo.Query,
}).ToList();
MusicPlaylist playlist;
using (var uow = DbHandler.UnitOfWork())
{
playlist = new MusicPlaylist
{
Name = name,
2016-12-16 18:43:57 +00:00
Author = Context.User.Username,
AuthorId = Context.User.Id,
Songs = songs,
};
uow.MusicPlaylists.Add(playlist);
await uow.CompleteAsync().ConfigureAwait(false);
}
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync(($"🎵 Saved playlist as **{name}**, ID: {playlist.Id}.")).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Load([Remainder] int id)
{
MusicPlaylist mpl;
using (var uow = DbHandler.UnitOfWork())
{
mpl = uow.MusicPlaylists.GetWithSongs(id);
}
if (mpl == null)
{
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("Can't find playlist with that ID.").ConfigureAwait(false);
return;
}
2016-10-05 05:01:19 +00:00
IUserMessage msg = null;
2016-12-17 00:16:14 +00:00
try { msg = await Context.Channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
foreach (var item in mpl.Songs)
{
2016-12-16 18:43:57 +00:00
var usr = (IGuildUser)Context.User;
try
{
2016-12-17 00:16:14 +00:00
await QueueSong(usr, (ITextChannel)Context.Channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false);
}
catch (SongNotFoundException) { }
catch { break; }
}
2016-10-05 05:01:19 +00:00
if (msg != null)
2016-12-14 19:29:01 +00:00
await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Playlists([Remainder] int num = 1)
{
2016-12-21 12:14:24 +00:00
if (num <= 0)
return;
List<MusicPlaylist> playlists;
using (var uow = DbHandler.UnitOfWork())
{
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
}
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon())
2016-12-24 19:49:29 +00:00
.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)")))
.WithOkColor();
2016-12-31 10:55:12 +00:00
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
//todo only author or owner
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task DeletePlaylist([Remainder] int id)
{
2016-12-21 12:14:24 +00:00
bool success = false;
MusicPlaylist pl = null;
2016-10-02 01:00:03 +00:00
try
{
2016-10-02 01:00:03 +00:00
using (var uow = DbHandler.UnitOfWork())
{
2016-10-02 01:00:03 +00:00
pl = uow.MusicPlaylists.Get(id);
if (pl != null)
{
2016-12-16 18:43:57 +00:00
if (NadekoBot.Credentials.IsOwner(Context.User) || pl.AuthorId == Context.User.Id)
2016-10-02 01:00:03 +00:00
{
uow.MusicPlaylists.Remove(pl);
await uow.CompleteAsync().ConfigureAwait(false);
success = true;
}
else
success = false;
}
}
2016-10-02 01:00:03 +00:00
if (!success)
2016-12-17 00:16:14 +00:00
await Context.Channel.SendErrorAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false);
2016-10-02 01:00:03 +00:00
else
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false);
2016-10-02 01:00:03 +00:00
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-12-16 19:45:46 +00:00
public async Task Goto(int time)
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
2016-12-16 18:43:57 +00:00
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
if (time < 0)
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
return;
//currentSong.PrintStatusMessage = false;
var gotoSong = currentSong.Clone();
gotoSong.SkipTo = time;
musicPlayer.AddSong(gotoSong, 0);
musicPlayer.Next();
var minutes = (time / 60).ToString();
var seconds = (time % 60).ToString();
if (minutes.Length == 1)
minutes = "0" + minutes;
if (seconds.Length == 1)
seconds = "0" + seconds;
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Autoplay()
{
MusicPlayer musicPlayer;
2016-12-17 00:16:14 +00:00
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
if (!musicPlayer.ToggleAutoplay())
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false);
else
2016-12-17 00:16:14 +00:00
await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false);
}
public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
{
if (voiceCh == null || voiceCh.Guild != textCh.Guild)
{
if (!silent)
2016-12-17 00:16:14 +00:00
await textCh.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining.").ConfigureAwait(false);
throw new ArgumentNullException(nameof(voiceCh));
}
if (string.IsNullOrWhiteSpace(query) || query.Length < 3)
throw new ArgumentException("💢 Invalid query for queue song.", nameof(query));
var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server =>
{
2016-08-26 01:25:55 +00:00
float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
2016-08-30 01:11:25 +00:00
using (var uow = DbHandler.UnitOfWork())
{
vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
2016-08-30 01:11:25 +00:00
}
var mp = new MusicPlayer(voiceCh, vol);
IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null;
mp.OnCompleted += async (s, song) =>
{
try
{
if (lastFinishedMessage != null)
lastFinishedMessage.DeleteAfter(0);
2016-12-27 00:35:46 +00:00
lastFinishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
.ConfigureAwait(false);
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
{
await QueueSong(await queuer.Guild.GetCurrentUserAsync(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
}
}
catch { }
};
2016-12-27 00:35:46 +00:00
mp.OnStarted += async (player, song) =>
{
2016-12-23 07:06:11 +00:00
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var sender = player as MusicPlayer;
2016-12-23 03:50:49 +00:00
if (sender == null)
2016-12-27 00:35:46 +00:00
return;
2016-12-22 04:32:34 +00:00
try
{
2016-12-22 04:32:34 +00:00
if (playingMessage != null)
playingMessage.DeleteAfter(0);
playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
2016-12-24 19:49:29 +00:00
.WithDescription(song.PrettyName)
2016-12-31 10:55:12 +00:00
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
2016-12-22 04:32:34 +00:00
.ConfigureAwait(false);
}
2016-12-22 04:32:34 +00:00
catch { }
};
mp.OnPauseChanged += async (paused) =>
{
try
{
IUserMessage msg;
if (paused)
msg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
else
msg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
if (msg != null)
msg.DeleteAfter(10);
}
catch { }
};
2017-01-05 20:54:49 +00:00
mp.SongRemoved += async (song, index) =>
{
try
{
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Removed song #" + (index + 1)).WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))
.WithErrorColor();
await textCh.EmbedAsync(embed).ConfigureAwait(false);
}
catch { }
};
return mp;
});
Song resolvedSong;
try
{
musicPlayer.ThrowIfQueueFull();
resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
if (resolvedSong == null)
throw new SongNotFoundException();
musicPlayer.AddSong(resolvedSong, queuer.Username);
}
catch (PlaylistFullException)
{
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
throw;
}
if (!silent)
{
2016-10-05 05:01:19 +00:00
try
{
2016-12-21 23:52:52 +00:00
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
2016-12-27 16:06:53 +00:00
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\nQueue ")
.WithThumbnailUrl(resolvedSong.Thumbnail)
2016-12-31 10:55:12 +00:00
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)))
2016-12-21 23:52:52 +00:00
.ConfigureAwait(false);
2016-12-22 04:32:34 +00:00
if (queuedMessage != null)
queuedMessage.DeleteAfter(10);
2016-10-05 05:01:19 +00:00
}
catch { } // if queued message sending fails, don't attempt to delete it
}
}
}
2016-12-23 03:50:49 +00:00
}