From ba7fb089d2a393a2425c0fac58735a2e351df15d Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 27 Feb 2016 02:23:47 +0100 Subject: [PATCH] rewrite started --- NadekoBot/Classes/Music/MusicControls.cs | 135 ++++++++++++++++++++++- NadekoBot/Classes/Music/StreamRequest.cs | 4 +- NadekoBot/Modules/Music.cs | 49 ++++---- 3 files changed, 159 insertions(+), 29 deletions(-) diff --git a/NadekoBot/Classes/Music/MusicControls.cs b/NadekoBot/Classes/Music/MusicControls.cs index 3ece3323..64cb008c 100644 --- a/NadekoBot/Classes/Music/MusicControls.cs +++ b/NadekoBot/Classes/Music/MusicControls.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using Discord.Commands; using MusicModule = NadekoBot.Modules.Music; +using System.Collections; +using NadekoBot.Extensions; +using System.Threading; namespace NadekoBot.Classes.Music { @@ -13,8 +16,130 @@ namespace NadekoBot.Classes.Music { Normal, Local } + public class Song { + public StreamState State { get; internal set; } - public class MusicControls { + private Song() { } + + internal Task Play(CancellationToken cancelToken) { + throw new NotImplementedException(); + } + } + public class MusicPlayer { + private IAudioClient _client { get; set; } + + private List _playlist = new List(); + public IReadOnlyCollection Playlist => _playlist; + private object playlistLock = new object(); + + public Song CurrentSong { get; set; } = default(Song); + private CancellationTokenSource SongCancelSource { get; set; } + private CancellationToken cancelToken { get; set; } + + public bool Paused { get; set; } + + public float Volume { get; private set; } + + public MusicPlayer(IAudioClient client) { + if (client == null) + throw new ArgumentNullException(nameof(client)); + _client = client; + SongCancelSource = new CancellationTokenSource(); + cancelToken = SongCancelSource.Token; + Task.Run(async () => { + while (_client?.State == ConnectionState.Connected) { + CurrentSong = GetNextSong(); + if (CurrentSong != null) { + try { + await CurrentSong.Play(cancelToken); + } + catch (OperationCanceledException) { + Console.WriteLine("Song canceled"); + } + catch (Exception ex) { + Console.WriteLine($"Exception in PlaySong: {ex}"); + } + SongCancelSource = new CancellationTokenSource(); + cancelToken = SongCancelSource.Token; + } + else { + await Task.Delay(1000); + } + } + await Stop(); + }); + } + + public void Next() { + if(!SongCancelSource.IsCancellationRequested) + SongCancelSource.Cancel(); + } + + public async Task Stop() { + + lock (_playlist) { + _playlist.Clear(); + } + try { + if (!SongCancelSource.IsCancellationRequested) + SongCancelSource.Cancel(); + } + catch { + Console.WriteLine("This shouldn't happen"); + } + await _client?.Disconnect(); + } + + public void Shuffle() { + lock (_playlist) { + _playlist.Shuffle(); + } + } + + public void SetVolume(float volume) { + if (volume < 0) + volume = 0; + if (volume > 150) + volume = 150; + + Volume = volume / 100.0f; + } + + private Song GetNextSong() { + lock (playlistLock) { + if (_playlist.Count == 0) + return null; + var toReturn = _playlist[0]; + _playlist.RemoveAt(0); + return toReturn; + } + } + + public void AddSong(Song s) { + if (s == null) + throw new ArgumentNullException(nameof(s)); + lock (playlistLock) { + _playlist.Add(s); + } + } + + public void RemoveSong(Song s) { + if (s == null) + throw new ArgumentNullException(nameof(s)); + lock (playlistLock) { + _playlist.Remove(s); + } + } + + public void RemoveSongAt(int index) { + lock (playlistLock) { + if (index < 0 || index >= _playlist.Count) + throw new ArgumentException("Invalid index"); + _playlist.RemoveAt(index); + } + } + + /* private CommandEventArgs _e; public bool NextSong { get; set; } = false; public IAudioClient Voice { get; set; } @@ -33,7 +158,7 @@ namespace NadekoBot.Classes.Music { private readonly object _voiceLock = new object(); - public MusicControls() { + public MusicPlayer() { Task.Run(async () => { while (true) { if (!Stopped) { @@ -61,7 +186,7 @@ namespace NadekoBot.Classes.Music { } } - public MusicControls(Channel voiceChannel, CommandEventArgs e, float? vol) : this() { + public MusicPlayer(Channel voiceChannel, CommandEventArgs e, float? vol) : this() { if (voiceChannel == null) throw new ArgumentNullException(nameof(voiceChannel)); if (vol != null) @@ -96,7 +221,6 @@ namespace NadekoBot.Classes.Music { catch (Exception ex) { Console.WriteLine($"Starting failed: {ex}"); CurrentSong?.Stop(); - CurrentSong = null; } } @@ -112,7 +236,7 @@ namespace NadekoBot.Classes.Music { VoiceClient?.Disconnect(); VoiceClient = null; - MusicControls throwAwayValue; + MusicPlayer throwAwayValue; MusicModule.musicPlayers.TryRemove(_e.Server, out throwAwayValue); } } @@ -127,5 +251,6 @@ namespace NadekoBot.Classes.Music { } internal bool TogglePause() => IsPaused = !IsPaused; + */ } } diff --git a/NadekoBot/Classes/Music/StreamRequest.cs b/NadekoBot/Classes/Music/StreamRequest.cs index ab7ed6ec..e1deb17a 100644 --- a/NadekoBot/Classes/Music/StreamRequest.cs +++ b/NadekoBot/Classes/Music/StreamRequest.cs @@ -38,9 +38,9 @@ namespace NadekoBot.Classes.Music { public MusicType LinkType { get; } - public MusicControls MusicControls; + public MusicPlayer MusicControls; - public StreamRequest(CommandEventArgs e, string query, MusicControls mc, MusicType musicType = MusicType.Normal) { + public StreamRequest(CommandEventArgs e, string query, MusicPlayer mc, MusicType musicType = MusicType.Normal) { if (e == null) throw new ArgumentNullException(nameof(e)); if (query == null) diff --git a/NadekoBot/Modules/Music.cs b/NadekoBot/Modules/Music.cs index e3d57f35..f13d2ffc 100644 --- a/NadekoBot/Modules/Music.cs +++ b/NadekoBot/Modules/Music.cs @@ -15,7 +15,7 @@ using Timer = System.Timers.Timer; namespace NadekoBot.Modules { class Music : DiscordModule { - public static ConcurrentDictionary musicPlayers = new ConcurrentDictionary(); + public static ConcurrentDictionary musicPlayers = new ConcurrentDictionary(); public static ConcurrentDictionary musicVolumes = new ConcurrentDictionary(); internal static string GetMusicStats() { @@ -24,7 +24,21 @@ namespace NadekoBot.Modules { return $"Playing {cnt = stats.Count()} songs".SnPl(cnt) + $", {stats.Sum(kvp => kvp.Value?.SongQueue?.Count ?? 0)} queued."; } + Timer setgameTimer => new Timer(); + + bool setgameEnabled = false; + public Music() : base() { + + setgameTimer.Interval = 20000; + setgameTimer.Elapsed += (s, e) => { + try { + int num = musicPlayers.Where(kvp => kvp.Value.CurrentSong != null).Count(); + NadekoBot.client.SetGame($"{num} songs".SnPl(num) + $", {musicPlayers.Sum(kvp => kvp.Value.SongQueue.Count())} queued"); + } + catch { } + }; + } public override void Install(ModuleManager manager) { @@ -164,17 +178,6 @@ namespace NadekoBot.Modules { player.SongQueue.Shuffle(); await e.Channel.SendMessage("🎵 `Songs shuffled.`"); }); - - bool setgameEnabled = false; - Timer setgameTimer = new Timer(); - setgameTimer.Interval = 20000; - setgameTimer.Elapsed += (s, e) => { - try { - int num = musicPlayers.Where(kvp => kvp.Value.CurrentSong != null).Count(); - NadekoBot.client.SetGame($"{num} songs".SnPl(num) + $", {musicPlayers.Sum(kvp => kvp.Value.SongQueue.Count())} queued"); - } - catch { } - }; cgb.CreateCommand("setgame") .Description("Sets the game of the bot to the number of songs playing.**Owner only**") .Do(async e => { @@ -201,7 +204,7 @@ namespace NadekoBot.Modules { //todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE var msg = await e.Channel.SendMessage($"🎵 `Attempting to queue {ids.Count} songs".SnPl(ids.Count) + "...`"); foreach (var id in ids) { - Task.Run(async () => await QueueSong(e, id, true)).ConfigureAwait(false); + Task.Run(async () => await QueueSong(e, id, true)); await Task.Delay(150); } msg?.Edit("🎵 `Playlist queue complete.`"); @@ -250,7 +253,7 @@ namespace NadekoBot.Modules { cgb.CreateCommand("mv") .Description("Moves the bot to your voice channel. (works only if music is already playing)") .Do(async e => { - MusicControls mc; + MusicPlayer mc; if (e.User.VoiceChannel == null || e.User.VoiceChannel.Server != e.Server || !musicPlayers.TryGetValue(e.Server, out mc)) return; mc.VoiceChannel = e.User.VoiceChannel; @@ -262,7 +265,7 @@ namespace NadekoBot.Modules { .Parameter("num", ParameterType.Required) .Do(async e => { var arg = e.GetArg("num"); - MusicControls mc; + MusicPlayer mc; if (!musicPlayers.TryGetValue(e.Server, out mc)) { return; } @@ -316,7 +319,7 @@ namespace NadekoBot.Modules { if (musicVolumes.TryGetValue(e.Server.Id, out throwAway)) vol = throwAway; - if (!musicPlayers.TryAdd(e.Server, new MusicControls(e.User.VoiceChannel, e, vol))) { + if (!musicPlayers.TryAdd(e.Server, new MusicPlayer(e.User.VoiceChannel, e, vol))) { await e.Channel.SendMessage("Failed to create a music player for this server."); return; } @@ -339,13 +342,15 @@ namespace NadekoBot.Modules { qmsg = await e.Channel.SendMessage("🎵 `Searching / Resolving...`"); sr.OnResolvingFailed += async (err) => { try { - await qmsg.Edit($"💢 🎵 `Resolving failed` for **{query}**"); + await qmsg.Delete(); + await e.Channel.Send($"💢 🎵 `Resolving failed` for **{query}**"); } catch { } }; sr.OnQueued += async () => { try { - await qmsg.Edit($"🎵`Queued`{sr.FullPrettyName}"); + await qmsg.Delete(); + await e.Channel.Send($"🎵`Queued`{sr.FullPrettyName}"); } catch { } }; @@ -354,7 +359,7 @@ namespace NadekoBot.Modules { } sr.OnCompleted += async () => { try { - MusicControls mc; + MusicPlayer mc; if (musicPlayers.TryGetValue(e.Server, out mc)) { if (mc.SongQueue.Count == 0) mc.Stop(); @@ -363,13 +368,13 @@ namespace NadekoBot.Modules { } catch { } }; + sr.OnStarted += async () => { try { var msgTxt = $"🎵`Playing`{sr.FullPrettyName} `Vol: {(int)(player.Volume * 100)}%`"; if (qmsg != null) - await qmsg.Edit(msgTxt); - else - await e.Channel.SendMessage(msgTxt); + await qmsg.Delete(); + await e.Channel.SendMessage(msgTxt); } catch { } };