.play help update, readded pausing when nobody is in voice channel, and also cleaning up music players if bot is kicked or leaves the server
This commit is contained in:
		| @@ -36,56 +36,62 @@ namespace NadekoBot.Modules.Music | ||||
|             _db = db; | ||||
|             _music = music; | ||||
|  | ||||
|             //_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated; | ||||
|             _client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated; | ||||
|             _client.LeftGuild += _client_LeftGuild; | ||||
|         } | ||||
|  | ||||
|         //todo when someone drags nadeko from one voice channel to another | ||||
|         private Task _client_LeftGuild(SocketGuild arg) | ||||
|         { | ||||
|             var t = _music.DestroyPlayer(arg.Id); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         //private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState) | ||||
|         //{ | ||||
|         //    var usr = iusr as SocketGuildUser; | ||||
|         //    if (usr == null || | ||||
|         //        oldState.VoiceChannel == newState.VoiceChannel) | ||||
|         //        return Task.CompletedTask; | ||||
|         private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState) | ||||
|         { | ||||
|             var t = Task.Run(() => | ||||
|             { | ||||
|                 var usr = iusr as SocketGuildUser; | ||||
|                 if (usr == null || | ||||
|                     oldState.VoiceChannel == newState.VoiceChannel) | ||||
|                     return; | ||||
|  | ||||
|         //    MusicPlayer player; | ||||
|         //    if ((player = _music.GetPlayer(usr.Guild.Id)) == null) | ||||
|         //        return Task.CompletedTask; | ||||
|                 var player = _music.GetPlayerOrDefault(usr.Guild.Id); | ||||
|  | ||||
|         //    try | ||||
|         //    { | ||||
|         //        //if bot moved | ||||
|         //        if ((player.PlaybackVoiceChannel == oldState.VoiceChannel) && | ||||
|         //                usr.Id == _client.CurrentUser.Id) | ||||
|         //        { | ||||
|         //            if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel | ||||
|         //                player.TogglePause(); | ||||
|         //            else if (!player.Paused && newState.VoiceChannel.Users.Count <= 1) // pause if there are no users in the new channel | ||||
|         //                player.TogglePause(); | ||||
|                 try | ||||
|                 { | ||||
|                     //if bot moved | ||||
|                     if ((player.VoiceChannel == oldState.VoiceChannel) && | ||||
|                             usr.Id == _client.CurrentUser.Id) | ||||
|                     { | ||||
|                         if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel | ||||
|                             player.TogglePause(); | ||||
|                         else if (!player.Paused && newState.VoiceChannel.Users.Count <= 1) // pause if there are no users in the new channel | ||||
|                             player.TogglePause(); | ||||
|  | ||||
|         //            return Task.CompletedTask; | ||||
|         //        } | ||||
|                         player.SetVoiceChannel(newState.VoiceChannel); | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|  | ||||
|         //        //if some other user moved | ||||
|         //        if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause  | ||||
|         //                player.Paused && | ||||
|         //                newState.VoiceChannel.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 && | ||||
|         //                oldState.VoiceChannel.Users.Count == 1)) | ||||
|         //        { | ||||
|         //            player.TogglePause(); | ||||
|         //            return Task.CompletedTask; | ||||
|         //        } | ||||
|  | ||||
|         //    } | ||||
|         //    catch | ||||
|         //    { | ||||
|         //        // ignored | ||||
|         //    } | ||||
|         //    return Task.CompletedTask; | ||||
|         //} | ||||
|                     //if some other user moved | ||||
|                     if ((player.VoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause  | ||||
|                             player.Paused && | ||||
|                             newState.VoiceChannel.Users.Count >= 2) ||  // keep in mind bot is in the channel (+1) | ||||
|                         (player.VoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause | ||||
|                             !player.Paused && | ||||
|                             oldState.VoiceChannel.Users.Count == 1)) | ||||
|                     { | ||||
|                         player.TogglePause(); | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     // ignored | ||||
|                 } | ||||
|             }); | ||||
|             return Task.CompletedTask; | ||||
|         } | ||||
|  | ||||
|         private async Task InternalQueue(MusicPlayer mp, SongInfo songInfo, bool silent) | ||||
|         { | ||||
| @@ -96,25 +102,24 @@ namespace NadekoBot.Modules.Music | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             (bool Success, int Index) qData; | ||||
|             int index; | ||||
|             try | ||||
|             { | ||||
|                 qData = mp.Enqueue(songInfo); | ||||
|                 index = mp.Enqueue(songInfo); | ||||
|             } | ||||
|             catch (QueueFullException) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("queue_full", mp.MaxQueueSize).ConfigureAwait(false); | ||||
|                 throw; | ||||
|             } | ||||
|             if (qData.Success) | ||||
|             if (index != -1) | ||||
|             { | ||||
|                 if (!silent) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); | ||||
|                         var queuedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||
|                                                                 .WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (qData.Index)).WithMusicIcon()) | ||||
|                                                                 .WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon()) | ||||
|                                                                 .WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ") | ||||
|                                                                 .WithThumbnailUrl(songInfo.Thumbnail) | ||||
|                                                                 .WithFooter(ef => ef.WithText(songInfo.PrettyProvider))) | ||||
| @@ -135,12 +140,26 @@ namespace NadekoBot.Modules.Music | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public Task Play([Remainder]string query = null) | ||||
|         public async Task Play([Remainder] string query = null) | ||||
|         { | ||||
|             if (!string.IsNullOrWhiteSpace(query)) | ||||
|                 try { return Queue(query); } catch (QueueFullException) { return Task.CompletedTask; } | ||||
|             var mp = await _music.GetOrCreatePlayer(Context); | ||||
|             if (string.IsNullOrWhiteSpace(query)) | ||||
|             { | ||||
|                 await Next(); | ||||
|             } | ||||
|             else if (int.TryParse(query, out var index)) | ||||
|                 if (index >= 1) | ||||
|                     mp.SetIndex(index - 1); | ||||
|                 else | ||||
|                     return; | ||||
|             else | ||||
|                 return Next(); | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     await Queue(query); | ||||
|                 } | ||||
|                 catch { } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
| @@ -218,13 +237,12 @@ namespace NadekoBot.Modules.Music | ||||
|                 page = current / itemsPerPage; | ||||
|  | ||||
|             //if page is 0 (-1 after this decrement) that means default to the page current song is playing from | ||||
|  | ||||
|             //var total = musicPlayer.TotalPlaytime; | ||||
|             //var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format", | ||||
|             //    (int)total.TotalHours, | ||||
|             //    total.Minutes, | ||||
|             //    total.Seconds); | ||||
|             //var maxPlaytime = musicPlayer.MaxPlaytimeSeconds; | ||||
|             var total = mp.TotalPlaytime; | ||||
|             var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format", | ||||
|                 (int)total.TotalHours, | ||||
|                 total.Minutes, | ||||
|                 total.Seconds); | ||||
|             var maxPlaytime = mp.MaxPlaytimeSeconds; | ||||
|             var lastPage = songs.Length / itemsPerPage; | ||||
|             Func<int, EmbedBuilder> printAction = curPage => | ||||
|             { | ||||
| @@ -255,10 +273,12 @@ namespace NadekoBot.Modules.Music | ||||
|                     add += "🔀 " + GetText("shuffling_playlist") + "\n"; | ||||
|                 else | ||||
|                 { | ||||
|                     if (mp.RepeatPlaylist) | ||||
|                         add += "🔁 " + GetText("repeating_playlist") + "\n"; | ||||
|                     if (mp.Autoplay) | ||||
|                         add += "↪ " + GetText("autoplaying") + "\n"; | ||||
|                     if (mp.FairPlay && !mp.Autoplay) | ||||
|                         add += " " + GetText("fairplay") + "\n"; | ||||
|                     else if (mp.RepeatPlaylist) | ||||
|                         add += "🔁 " + GetText("repeating_playlist") + "\n"; | ||||
|                 } | ||||
|  | ||||
|                 if (!string.IsNullOrWhiteSpace(add)) | ||||
| @@ -268,12 +288,8 @@ namespace NadekoBot.Modules.Music | ||||
|                     .WithAuthor(eab => eab.WithName(GetText("player_queue", curPage + 1, lastPage + 1)) | ||||
|                         .WithMusicIcon()) | ||||
|                     .WithDescription(desc) | ||||
|                     //.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + | ||||
|                     //                              $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " + | ||||
|                     //                              (musicPlayer.FairPlay | ||||
|                     //                                  ? "✔️" + GetText("fairplay") | ||||
|                     //                                  : "✖️" + GetText("fairplay")) + " | " + | ||||
|                     //                              (maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime)))) | ||||
|                     .WithFooter(ef => ef.WithText($"{mp.PrettyVolume} | {songs.Length} " + | ||||
|                                                   $"{("tracks".SnPl(songs.Length))} | {totalStr}")) | ||||
|                     .WithOkColor(); | ||||
|  | ||||
|                 return embed; | ||||
| @@ -517,27 +533,22 @@ namespace NadekoBot.Modules.Music | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //[NadekoCommand, Usage, Description, Aliases] | ||||
|         //[RequireContext(ContextType.Guild)] | ||||
|         //public async Task Fairplay() | ||||
|         //{ | ||||
|         //    var channel = (ITextChannel)Context.Channel; | ||||
|         //    MusicPlayer musicPlayer; | ||||
|         //    if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null) | ||||
|         //        return; | ||||
|         //    if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) | ||||
|         //        return; | ||||
|         //    var val = musicPlayer.FairPlay = !musicPlayer.FairPlay; | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task Fairplay() | ||||
|         { | ||||
|             var mp = await _music.GetOrCreatePlayer(Context); | ||||
|             var val = mp.FairPlay = !mp.FairPlay; | ||||
|  | ||||
|         //    if (val) | ||||
|         //    { | ||||
|         //        await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false); | ||||
|         //    } | ||||
|         //    else | ||||
|         //    { | ||||
|         //        await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false); | ||||
|         //    } | ||||
|         //} | ||||
|             if (val) | ||||
|             { | ||||
|                 await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
| @@ -613,58 +624,46 @@ namespace NadekoBot.Modules.Music | ||||
|                 await ReplyConfirmLocalized("songs_shuffle_disable").ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         //[NadekoCommand, Usage, Description, Aliases] | ||||
|         //[RequireContext(ContextType.Guild)] | ||||
|         //public async Task Playlist([Remainder] string playlist) | ||||
|         //{ | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|         [RequireContext(ContextType.Guild)] | ||||
|         public async Task Playlist([Remainder] string playlist) | ||||
|         { | ||||
|             if (string.IsNullOrWhiteSpace(playlist)) | ||||
|                 return; | ||||
|  | ||||
|         //    var arg = playlist; | ||||
|         //    if (string.IsNullOrWhiteSpace(arg)) | ||||
|         //        return; | ||||
|         //    if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) | ||||
|         //    { | ||||
|         //        await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false); | ||||
|         //        return; | ||||
|         //    } | ||||
|         //    var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); | ||||
|         //    if (plId == null) | ||||
|         //    { | ||||
|         //        await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); | ||||
|         //        return; | ||||
|         //    } | ||||
|         //    var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); | ||||
|         //    if (!ids.Any()) | ||||
|         //    { | ||||
|         //        await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); | ||||
|         //        return; | ||||
|         //    } | ||||
|         //    var count = ids.Count(); | ||||
|         //    var msg = await Context.Channel.SendMessageAsync("🎵 " + GetText("attempting_to_queue", | ||||
|         //        Format.Bold(count.ToString()))).ConfigureAwait(false); | ||||
|             var mp = await _music.GetOrCreatePlayer(Context); | ||||
|  | ||||
|         //    var cancelSource = new CancellationTokenSource(); | ||||
|             var plId = (await _google.GetPlaylistIdsByKeywordsAsync(playlist).ConfigureAwait(false)).FirstOrDefault(); | ||||
|             if (plId == null) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); | ||||
|             if (!ids.Any()) | ||||
|             { | ||||
|                 await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); | ||||
|                 return; | ||||
|             } | ||||
|             var count = ids.Count(); | ||||
|             var msg = await Context.Channel.SendMessageAsync("🎵 " + GetText("attempting_to_queue", | ||||
|                 Format.Bold(count.ToString()))).ConfigureAwait(false); | ||||
|              | ||||
|             foreach (var song in ids) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     if (mp.Exited) | ||||
|                         return; | ||||
|  | ||||
|         //    var gusr = (IGuildUser)Context.User; | ||||
|         //    while (ids.Any() && !cancelSource.IsCancellationRequested) | ||||
|         //    { | ||||
|         //        var tasks = Task.WhenAll(ids.Take(5).Select(async id => | ||||
|         //        { | ||||
|         //            if (cancelSource.Token.IsCancellationRequested) | ||||
|         //                return; | ||||
|         //            try | ||||
|         //            { | ||||
|         //                await _music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, id, true).ConfigureAwait(false); | ||||
|         //            } | ||||
|         //            catch (SongNotFoundException) { } | ||||
|         //            catch { try { cancelSource.Cancel(); } catch { } } | ||||
|         //        })); | ||||
|                     await Task.WhenAll(Task.Delay(100), InternalQueue(mp, await _music.ResolveSong(song, Context.User.ToString(), MusicType.YouTube), true)); | ||||
|                 } | ||||
|                 catch (SongNotFoundException) { } | ||||
|                 catch { break; } | ||||
|             } | ||||
|  | ||||
|         //        await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token)); | ||||
|         //        ids = ids.Skip(5); | ||||
|         //    } | ||||
|  | ||||
|         //    await msg.ModifyAsync(m => m.Content = "✅ " + Format.Bold(GetText("playlist_queue_complete"))).ConfigureAwait(false); | ||||
|         //} | ||||
|             await msg.ModifyAsync(m => m.Content = "✅ " + Format.Bold(GetText("playlist_queue_complete"))).ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         [NadekoCommand, Usage, Description, Aliases] | ||||
|   | ||||
| @@ -1198,7 +1198,7 @@ | ||||
|     <value>`{0}drawnew` or `{0}drawnew 5`</value> | ||||
|   </data> | ||||
|   <data name="shuffleplaylist_cmd" xml:space="preserve"> | ||||
|     <value>shuffle plsh</value> | ||||
|     <value>shuffle sh plsh</value> | ||||
|   </data> | ||||
|   <data name="shuffleplaylist_desc" xml:space="preserve"> | ||||
|     <value>Shuffles the current playlist.</value> | ||||
| @@ -1471,10 +1471,10 @@ | ||||
|     <value>play start</value> | ||||
|   </data> | ||||
|   <data name="play_desc" xml:space="preserve"> | ||||
|     <value>If no arguments are specified, acts as `{0}next 1` command. If you specify a search query, acts as a `{0}q` command</value> | ||||
|     <value>If no arguments are specified, acts as `{0}next 1` command. If you specify a song number, it will jump to that song. If you specify a search query, acts as a `{0}q` command</value> | ||||
|   </data> | ||||
|   <data name="play_usage" xml:space="preserve"> | ||||
|     <value>`{0}play` or `{0}play Dream Of Venice`</value> | ||||
|     <value>`{0}play` or `{0}play 5` or `{0}play Dream Of Venice`</value> | ||||
|   </data> | ||||
|   <data name="stop_cmd" xml:space="preserve"> | ||||
|     <value>stop s</value> | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| using Discord.WebSocket; | ||||
| using NadekoBot.DataStructures.Replacements; | ||||
| using NadekoBot.Services; | ||||
| using NadekoBot.Services.Database.Models; | ||||
| using NadekoBot.Services.Music; | ||||
| using NLog; | ||||
|   | ||||
| @@ -4,7 +4,9 @@ using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using NLog; | ||||
| using System.Diagnostics; | ||||
| using System.Linq; | ||||
| using System.Collections.Concurrent; | ||||
| using NadekoBot.Extensions; | ||||
|  | ||||
| namespace NadekoBot.Services.Music | ||||
| { | ||||
| @@ -18,7 +20,7 @@ namespace NadekoBot.Services.Music | ||||
|     public class MusicPlayer | ||||
|     { | ||||
|         private readonly Task _player; | ||||
|         private IVoiceChannel VoiceChannel { get; set; } | ||||
|         public IVoiceChannel VoiceChannel { get; private set; } | ||||
|         private readonly Logger _log; | ||||
|  | ||||
|         private MusicQueue Queue { get; } = new MusicQueue(); | ||||
| @@ -27,6 +29,7 @@ namespace NadekoBot.Services.Music | ||||
|         public bool Stopped { get; private set; } = false; | ||||
|         public float Volume { get; private set; } = 1.0f; | ||||
|         public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%"; | ||||
|         public bool Paused => pauseTaskSource != null; | ||||
|         private TaskCompletionSource<bool> pauseTaskSource { get; set; } = null; | ||||
|  | ||||
|         private CancellationTokenSource SongCancelSource { get; set; } | ||||
| @@ -48,7 +51,27 @@ namespace NadekoBot.Services.Music | ||||
|         public uint MaxQueueSize | ||||
|         { | ||||
|             get => Queue.MaxQueueSize; | ||||
|             set => Queue.MaxQueueSize = value; | ||||
|             set { lock (locker) Queue.MaxQueueSize = value; } | ||||
|         } | ||||
|         private bool _fairPlay; | ||||
|         public bool FairPlay | ||||
|         { | ||||
|             get => _fairPlay; | ||||
|             set | ||||
|             { | ||||
|                 if (value) | ||||
|                 { | ||||
|                     var cur = Queue.Current; | ||||
|                     if (cur.Song != null) | ||||
|                         RecentlyPlayedUsers.Add(cur.Song.QueuerName); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     RecentlyPlayedUsers.Clear(); | ||||
|                 } | ||||
|  | ||||
|                 _fairPlay = value; | ||||
|             } | ||||
|         } | ||||
|         public uint MaxPlaytimeSeconds { get; set; } | ||||
|  | ||||
| @@ -56,6 +79,7 @@ namespace NadekoBot.Services.Music | ||||
|         const int _frameBytes = 3840; | ||||
|         const float _miliseconds = 20.0f; | ||||
|         public TimeSpan CurrentTime => TimeSpan.FromSeconds(_bytesSent / (float)_frameBytes / (1000 / _miliseconds)); | ||||
|  | ||||
|         private int _bytesSent = 0; | ||||
|  | ||||
|         private IAudioClient _audioClient; | ||||
| @@ -63,15 +87,19 @@ namespace NadekoBot.Services.Music | ||||
|         private MusicService _musicService; | ||||
|  | ||||
|         #region events | ||||
|         public event Action<MusicPlayer, SongInfo> OnStarted; | ||||
|         public event Action<MusicPlayer, (int Index, SongInfo Song)> OnStarted; | ||||
|         public event Action<MusicPlayer, SongInfo> OnCompleted; | ||||
|         public event Action<MusicPlayer, bool> OnPauseChanged; | ||||
|         #endregion | ||||
|  | ||||
|  | ||||
|         private bool manualSkip = false; | ||||
|         private bool manualIndex = false; | ||||
|         private bool newVoiceChannel = false; | ||||
|  | ||||
|         private ConcurrentHashSet<string> RecentlyPlayedUsers { get; } = new ConcurrentHashSet<string>(); | ||||
|         public TimeSpan TotalPlaytime => TimeSpan.MaxValue; | ||||
|  | ||||
|         public MusicPlayer(MusicService musicService, IVoiceChannel vch, ITextChannel output, float volume) | ||||
|         { | ||||
|             _log = LogManager.GetCurrentClassLogger(); | ||||
| @@ -93,6 +121,7 @@ namespace NadekoBot.Services.Music | ||||
|                          data = Queue.Current; | ||||
|                          cancelToken = SongCancelSource.Token; | ||||
|                          manualSkip = false; | ||||
|                          manualIndex = false; | ||||
|                      } | ||||
|                      try | ||||
|                      { | ||||
| @@ -123,19 +152,17 @@ namespace NadekoBot.Services.Music | ||||
|                                  // i don't want to spam connection attempts | ||||
|                                  continue; | ||||
|                              } | ||||
|                              var pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 200); | ||||
|                              OnStarted?.Invoke(this, data.Song); | ||||
|                              var pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 500); | ||||
|                              OnStarted?.Invoke(this, data); | ||||
|  | ||||
|                              byte[] buffer = new byte[3840]; | ||||
|                              int bytesRead = 0; | ||||
|                              try | ||||
|                              { | ||||
|                                  while ((bytesRead = await b.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) > 0  | ||||
|                                  while ((bytesRead = await b.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) > 0 | ||||
|                                     && (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds)) | ||||
|                                  { | ||||
|                                      var vol = Volume; | ||||
|                                      if (vol != 1) | ||||
|                                          AdjustVolume(buffer, vol); | ||||
|                                      AdjustVolume(buffer, Volume); | ||||
|                                      await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); | ||||
|                                      unchecked { _bytesSent += bytesRead; } | ||||
|  | ||||
| @@ -165,60 +192,118 @@ namespace NadekoBot.Services.Music | ||||
|                      } | ||||
|                      finally | ||||
|                      { | ||||
|                          //if repeating current song, just ignore other settings,  | ||||
|                          // and play this song again (don't change the index) | ||||
|                          // ignore rcs if song is manually skipped | ||||
|                          if (!RepeatCurrentSong || manualSkip) | ||||
|                          try | ||||
|                          { | ||||
|                              if (Shuffle) | ||||
|                              //if repeating current song, just ignore other settings,  | ||||
|                              // and play this song again (don't change the index) | ||||
|                              // ignore rcs if song is manually skipped | ||||
|  | ||||
|                              int queueCount; | ||||
|                              lock (locker) | ||||
|                                  queueCount = Queue.Count; | ||||
|  | ||||
|                              if (!manualIndex && (!RepeatCurrentSong || manualSkip)) | ||||
|                              { | ||||
|                                  _log.Info("Random song"); | ||||
|                                  Queue.Random(); //if shuffle is set, set current song index to a random number | ||||
|                              } | ||||
|                              else | ||||
|                              { | ||||
|                                  //if last song, and autoplay is enabled, and if it's a youtube song | ||||
|                                  // do autplay magix | ||||
|                                  if (Queue.Count - 1 == data.Index && Autoplay && data.Song?.ProviderType == Database.Models.MusicType.YouTube) | ||||
|                                  if (Shuffle) | ||||
|                                  { | ||||
|                                      try | ||||
|                                      { | ||||
|                                          _log.Info("Loading related song"); | ||||
|                                          await _musicService.TryQueueRelatedSongAsync(data.Song.Query, OutputTextChannel, VoiceChannel); | ||||
|                                          Queue.Next(); | ||||
|                                      } | ||||
|                                      catch | ||||
|                                      { | ||||
|                                          _log.Info("Loading related song failed."); | ||||
|                                      } | ||||
|                                  } | ||||
|                                  else if (Queue.Count - 1 == data.Index && !RepeatPlaylist && !manualSkip) | ||||
|                                  { | ||||
|                                      _log.Info("Stopping because repeatplaylist is disabled"); | ||||
|                                      Stop(); | ||||
|                                      _log.Info("Random song"); | ||||
|                                      Queue.Random(); //if shuffle is set, set current song index to a random number | ||||
|                                  } | ||||
|                                  else | ||||
|                                  { | ||||
|                                      _log.Info("Next song"); | ||||
|                                      Queue.Next(); | ||||
|                                      //if last song, and autoplay is enabled, and if it's a youtube song | ||||
|                                      // do autplay magix | ||||
|                                      if (queueCount - 1 == data.Index && Autoplay && data.Song?.ProviderType == Database.Models.MusicType.YouTube) | ||||
|                                      { | ||||
|                                          try | ||||
|                                          { | ||||
|                                              _log.Info("Loading related song"); | ||||
|                                              await _musicService.TryQueueRelatedSongAsync(data.Song.Query, OutputTextChannel, VoiceChannel); | ||||
|                                              Queue.Next(); | ||||
|                                          } | ||||
|                                          catch | ||||
|                                          { | ||||
|                                              _log.Info("Loading related song failed."); | ||||
|                                          } | ||||
|                                      } | ||||
|                                      else if (FairPlay) | ||||
|                                      { | ||||
|                                          lock (locker) | ||||
|                                          { | ||||
|                                              _log.Info("Next fair song"); | ||||
|                                              var q = Queue.ToArray().Songs.Shuffle().ToArray(); | ||||
|  | ||||
|                                              bool found = false; | ||||
|                                              for (var i = 0; i < q.Length; i++) //first try to find a queuer who didn't have their song played recently | ||||
|                                              { | ||||
|                                                  var item = q[i]; | ||||
|                                                  if (RecentlyPlayedUsers.Add(item.QueuerName)) // if it's found, set current song to that index | ||||
|                                                  { | ||||
|                                                      Queue.CurrentIndex = i; | ||||
|                                                      found = true; | ||||
|                                                      break; | ||||
|                                                  } | ||||
|                                              } | ||||
|                                              if (!found) //if it's not | ||||
|                                              { | ||||
|                                                  RecentlyPlayedUsers.Clear(); //clear all recently played users (that means everyone from the playlist has had their song played) | ||||
|                                                  Queue.Random(); //go to a random song (to prevent looping on the first few songs) | ||||
|                                                  var cur = Current; | ||||
|                                                  if (cur.Current != null) // add newely scheduled song's queuer to the recently played list | ||||
|                                                      RecentlyPlayedUsers.Add(cur.Current.QueuerName); | ||||
|                                              } | ||||
|                                          } | ||||
|                                      } | ||||
|                                      else if (queueCount - 1 == data.Index && !RepeatPlaylist && !manualSkip) | ||||
|                                      { | ||||
|                                          _log.Info("Stopping because repeatplaylist is disabled"); | ||||
|                                          lock (locker) | ||||
|                                          { | ||||
|                                              Stop(); | ||||
|                                          } | ||||
|                                      } | ||||
|                                      else | ||||
|                                      { | ||||
|                                          _log.Info("Next song"); | ||||
|                                          lock (locker) | ||||
|                                          { | ||||
|                                              Queue.Next(); | ||||
|                                          } | ||||
|                                      } | ||||
|                                  } | ||||
|                              } | ||||
|                          } | ||||
|                          catch (Exception ex) | ||||
|                          { | ||||
|                              _log.Error(ex); | ||||
|                          } | ||||
|                          do | ||||
|                          { | ||||
|                              await Task.Delay(500); | ||||
|                          } | ||||
|                          while (Stopped && !Exited); | ||||
|                          while ((Queue.Count == 0 || Stopped) && !Exited); | ||||
|                      } | ||||
|                  } | ||||
|              }, SongCancelSource.Token); | ||||
|         } | ||||
|  | ||||
|         public void SetIndex(int index) | ||||
|         { | ||||
|             if (index < 0) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(index)); | ||||
|             lock (locker) | ||||
|             { | ||||
|                 Queue.CurrentIndex = index; | ||||
|                 manualIndex = true; | ||||
|                 CancelCurrentSong(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private async Task<IAudioClient> GetAudioClient(bool reconnect = false) | ||||
|         { | ||||
|             if (_audioClient == null || | ||||
|                 _audioClient.ConnectionState != ConnectionState.Connected || | ||||
|                 reconnect ||  | ||||
|                 reconnect || | ||||
|                 newVoiceChannel) | ||||
|                 try | ||||
|                 { | ||||
| @@ -240,17 +325,24 @@ namespace NadekoBot.Services.Music | ||||
|             return _audioClient; | ||||
|         } | ||||
|  | ||||
|         public (bool Success, int Index) Enqueue(SongInfo song) | ||||
|         public int Enqueue(SongInfo song) | ||||
|         { | ||||
|             _log.Info("Adding song"); | ||||
|             Queue.Add(song); | ||||
|             return (true, Queue.Count); | ||||
|             lock (locker) | ||||
|             { | ||||
|                 if (Exited) | ||||
|                     return -1; | ||||
|                 Queue.Add(song); | ||||
|                 return Queue.Count; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void Next(int skipCount = 1) | ||||
|         { | ||||
|             lock (locker) | ||||
|             { | ||||
|                 if (Exited) | ||||
|                     return; | ||||
|                 manualSkip = true; | ||||
|                 // if player is stopped, and user uses .n, it should play current song.   | ||||
|                 // It's a bit weird, but that's the least annoying solution | ||||
| @@ -277,10 +369,13 @@ namespace NadekoBot.Services.Music | ||||
|  | ||||
|         private void Unpause() | ||||
|         { | ||||
|             if (pauseTaskSource != null) | ||||
|             lock (locker) | ||||
|             { | ||||
|                 pauseTaskSource.TrySetResult(true); | ||||
|                 pauseTaskSource = null; | ||||
|                 if (pauseTaskSource != null) | ||||
|                 { | ||||
|                     pauseTaskSource.TrySetResult(true); | ||||
|                     pauseTaskSource = null; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -302,7 +397,10 @@ namespace NadekoBot.Services.Music | ||||
|         { | ||||
|             if (volume < 0 || volume > 100) | ||||
|                 throw new ArgumentOutOfRangeException(nameof(volume)); | ||||
|             Volume = ((float)volume) / 100; | ||||
|             lock (locker) | ||||
|             { | ||||
|                 Volume = ((float)volume) / 100; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public SongInfo RemoveAt(int index) | ||||
| @@ -335,7 +433,10 @@ namespace NadekoBot.Services.Music | ||||
|         } | ||||
|  | ||||
|         public (int CurrentIndex, SongInfo[] Songs) QueueArray() | ||||
|             => Queue.ToArray(); | ||||
|         { | ||||
|             lock (locker) | ||||
|                 return Queue.ToArray(); | ||||
|         } | ||||
|  | ||||
|         //aidiakapi ftw | ||||
|         public static unsafe byte[] AdjustVolume(byte[] audioSamples, float volume) | ||||
| @@ -410,366 +511,18 @@ namespace NadekoBot.Services.Music | ||||
|  | ||||
|         public void SetVoiceChannel(IVoiceChannel vch) | ||||
|         { | ||||
|             VoiceChannel = vch; | ||||
|             newVoiceChannel = true; | ||||
|             Next(); | ||||
|             lock (locker) | ||||
|             { | ||||
|                 if (Exited) | ||||
|                     return; | ||||
|                 VoiceChannel = vch; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         //private IAudioClient AudioClient { get; set; } | ||||
|  | ||||
|         ///// <summary> | ||||
|         ///// Player will prioritize different queuer name | ||||
|         ///// over the song position in the playlist | ||||
|         ///// </summary> | ||||
|         //public bool FairPlay { get; set; } = false; | ||||
|  | ||||
|         ///// <summary> | ||||
|         ///// Song will stop playing after this amount of time.  | ||||
|         ///// To prevent people queueing radio or looped songs  | ||||
|         ///// while other people want to listen to other songs too. | ||||
|         ///// </summary> | ||||
|         //public uint MaxPlaytimeSeconds { get; set; } = 0; | ||||
|  | ||||
|  | ||||
|         //// this should be written better | ||||
|         //public TimeSpan TotalPlaytime =>  | ||||
|         //    _playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ?  | ||||
|         //    TimeSpan.MaxValue :  | ||||
|         //    new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks)); | ||||
|  | ||||
|         ///// <summary> | ||||
|         ///// Users who recently got their music wish | ||||
|         ///// </summary> | ||||
|         //private ConcurrentHashSet<string> RecentlyPlayedUsers { get; } = new ConcurrentHashSet<string>(); | ||||
|  | ||||
|         //private readonly List<Song> _playlist = new List<Song>(); | ||||
|         //private readonly Logger _log; | ||||
|         //private readonly IGoogleApiService _google; | ||||
|  | ||||
|         //public IReadOnlyCollection<Song> Playlist => _playlist; | ||||
|  | ||||
|         //public Song CurrentSong { get; private set; } | ||||
|         //public CancellationTokenSource SongCancelSource { get; private set; } | ||||
|         //private CancellationToken CancelToken { get; set; } | ||||
|  | ||||
|         //public bool Paused { get; set; } | ||||
|  | ||||
|         //public float Volume { get; private set; } | ||||
|  | ||||
|         //public event Action<MusicPlayer, Song> OnCompleted = delegate { }; | ||||
|         //public event Action<MusicPlayer, Song> OnStarted = delegate { }; | ||||
|         //public event Action<bool> OnPauseChanged = delegate { }; | ||||
|  | ||||
|         //public IVoiceChannel PlaybackVoiceChannel { get; private set; } | ||||
|         //public ITextChannel OutputTextChannel { get; set; } | ||||
|  | ||||
|         //private bool Destroyed { get; set; } | ||||
|         //public bool RepeatSong { get; private set; } | ||||
|         //public bool RepeatPlaylist { get; private set; } | ||||
|         //public bool Autoplay { get; set; } | ||||
|         //public uint MaxQueueSize { get; set; } = 0; | ||||
|  | ||||
|         //private ConcurrentQueue<Action> ActionQueue { get; } = new ConcurrentQueue<Action>(); | ||||
|  | ||||
|         //public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%"; | ||||
|  | ||||
|         //public event Action<Song, int> SongRemoved = delegate { }; | ||||
|  | ||||
|         //public MusicPlayer(IVoiceChannel startingVoiceChannel, ITextChannel outputChannel, float? defaultVolume, IGoogleApiService google) | ||||
|         //{ | ||||
|         //    _log = LogManager.GetCurrentClassLogger(); | ||||
|         //    _google = google; | ||||
|  | ||||
|         //    OutputTextChannel = outputChannel; | ||||
|         //    Volume = defaultVolume ?? 1.0f; | ||||
|  | ||||
|         //    PlaybackVoiceChannel = startingVoiceChannel ?? throw new ArgumentNullException(nameof(startingVoiceChannel)); | ||||
|         //    SongCancelSource = new CancellationTokenSource(); | ||||
|         //    CancelToken = SongCancelSource.Token; | ||||
|  | ||||
|         //    Task.Run(async () => | ||||
|         //    { | ||||
|         //        try | ||||
|         //        { | ||||
|         //            while (!Destroyed) | ||||
|         //            { | ||||
|         //                try | ||||
|         //                { | ||||
|         //                    if (ActionQueue.TryDequeue(out Action action)) | ||||
|         //                    { | ||||
|         //                        action(); | ||||
|         //                    } | ||||
|         //                } | ||||
|         //                finally | ||||
|         //                { | ||||
|         //                    await Task.Delay(100).ConfigureAwait(false); | ||||
|         //                } | ||||
|         //            } | ||||
|         //        } | ||||
|         //        catch (Exception ex) | ||||
|         //        { | ||||
|         //            _log.Warn("Action queue crashed"); | ||||
|         //            _log.Warn(ex); | ||||
|         //        } | ||||
|         //    }).ConfigureAwait(false); | ||||
|  | ||||
|         //    var t = new Thread(async () => | ||||
|         //    { | ||||
|         //        while (!Destroyed) | ||||
|         //        { | ||||
|         //            try | ||||
|         //            { | ||||
|         //                CurrentSong = GetNextSong(); | ||||
|  | ||||
|         //                if (CurrentSong == null) | ||||
|         //                    continue; | ||||
|  | ||||
|         //                while (AudioClient?.ConnectionState == ConnectionState.Disconnecting ||  | ||||
|         //                    AudioClient?.ConnectionState == ConnectionState.Connecting) | ||||
|         //                { | ||||
|         //                    _log.Info("Waiting for Audio client"); | ||||
|         //                    await Task.Delay(200).ConfigureAwait(false); | ||||
|         //                } | ||||
|  | ||||
|         //                if (AudioClient == null || AudioClient.ConnectionState == ConnectionState.Disconnected) | ||||
|         //                    AudioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); | ||||
|  | ||||
|         //                var index = _playlist.IndexOf(CurrentSong); | ||||
|         //                if (index != -1) | ||||
|         //                    RemoveSongAt(index, true); | ||||
|  | ||||
|         //                OnStarted(this, CurrentSong); | ||||
|         //                try | ||||
|         //                { | ||||
|         //                    await CurrentSong.Play(AudioClient, CancelToken); | ||||
|         //                } | ||||
|         //                catch (OperationCanceledException) | ||||
|         //                { | ||||
|         //                } | ||||
|         //                finally | ||||
|         //                { | ||||
|         //                    OnCompleted(this, CurrentSong); | ||||
|         //                } | ||||
|  | ||||
|  | ||||
|         //                if (RepeatPlaylist & !RepeatSong) | ||||
|         //                    AddSong(CurrentSong, CurrentSong.QueuerName); | ||||
|  | ||||
|         //                if (RepeatSong) | ||||
|         //                    AddSong(CurrentSong, 0); | ||||
|  | ||||
|         //            } | ||||
|         //            catch (Exception ex) | ||||
|         //            { | ||||
|         //                _log.Warn("Music thread almost crashed."); | ||||
|         //                _log.Warn(ex); | ||||
|         //                await Task.Delay(3000).ConfigureAwait(false); | ||||
|         //            } | ||||
|         //            finally | ||||
|         //            { | ||||
|         //                if (!CancelToken.IsCancellationRequested) | ||||
|         //                { | ||||
|         //                    SongCancelSource.Cancel(); | ||||
|         //                } | ||||
|         //                SongCancelSource = new CancellationTokenSource(); | ||||
|         //                CancelToken = SongCancelSource.Token; | ||||
|         //                CurrentSong = null; | ||||
|         //                await Task.Delay(300).ConfigureAwait(false); | ||||
|         //            } | ||||
|         //        } | ||||
|         //    }); | ||||
|  | ||||
|         //    t.Start(); | ||||
|         //} | ||||
|  | ||||
|         //public void Next() | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        Paused = false; | ||||
|         //        SongCancelSource.Cancel(); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void Stop() | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        RepeatPlaylist = false; | ||||
|         //        RepeatSong = false; | ||||
|         //        Autoplay = false; | ||||
|         //        _playlist.Clear(); | ||||
|         //        if (!SongCancelSource.IsCancellationRequested) | ||||
|         //            SongCancelSource.Cancel(); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void TogglePause() => OnPauseChanged(Paused = !Paused); | ||||
|  | ||||
|         //public int SetVolume(int volume) | ||||
|         //{ | ||||
|         //    if (volume < 0) | ||||
|         //        volume = 0; | ||||
|         //    if (volume > 100) | ||||
|         //        volume = 100; | ||||
|  | ||||
|         //    Volume = volume / 100.0f; | ||||
|         //    return volume; | ||||
|         //} | ||||
|  | ||||
|         //private Song GetNextSong() | ||||
|         //{ | ||||
|         //    if (!FairPlay) | ||||
|         //    { | ||||
|         //        return _playlist.FirstOrDefault(); | ||||
|         //    } | ||||
|         //    var song = _playlist.FirstOrDefault(c => !RecentlyPlayedUsers.Contains(c.QueuerName)) | ||||
|         //        ?? _playlist.FirstOrDefault(); | ||||
|  | ||||
|         //    if (song == null) | ||||
|         //        return null; | ||||
|  | ||||
|         //    if (RecentlyPlayedUsers.Contains(song.QueuerName)) | ||||
|         //    { | ||||
|         //        RecentlyPlayedUsers.Clear(); | ||||
|         //    } | ||||
|  | ||||
|         //    RecentlyPlayedUsers.Add(song.QueuerName); | ||||
|         //    return song; | ||||
|         //} | ||||
|  | ||||
|         //public void Shuffle() | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        var oldPlaylist = _playlist.ToArray(); | ||||
|         //        _playlist.Clear(); | ||||
|         //        _playlist.AddRange(oldPlaylist.Shuffle()); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void AddSong(Song s, string username) | ||||
|         //{ | ||||
|         //    if (s == null) | ||||
|         //        throw new ArgumentNullException(nameof(s)); | ||||
|         //    ThrowIfQueueFull(); | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        s.MusicPlayer = this; | ||||
|         //        s.QueuerName = username.TrimTo(10); | ||||
|         //        _playlist.Add(s); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void AddSong(Song s, int index) | ||||
|         //{ | ||||
|         //    if (s == null) | ||||
|         //        throw new ArgumentNullException(nameof(s)); | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        _playlist.Insert(index, s); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void RemoveSong(Song s) | ||||
|         //{ | ||||
|         //    if (s == null) | ||||
|         //        throw new ArgumentNullException(nameof(s)); | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        _playlist.Remove(s); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void RemoveSongAt(int index, bool silent = false) | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        if (index < 0 || index >= _playlist.Count) | ||||
|         //            return; | ||||
|         //        var song = _playlist.ElementAtOrDefault(index); | ||||
|         //        if (_playlist.Remove(song) && !silent) | ||||
|         //        { | ||||
|         //            SongRemoved(song, index); | ||||
|         //        } | ||||
|  | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void ClearQueue() | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(() => | ||||
|         //    { | ||||
|         //        _playlist.Clear(); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public async Task UpdateSongDurationsAsync() | ||||
|         //{ | ||||
|         //    var curSong = CurrentSong; | ||||
|         //    var toUpdate = _playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal && | ||||
|         //                                                    s.TotalTime == TimeSpan.Zero) | ||||
|         //                                                    .ToArray(); | ||||
|         //    if (curSong != null) | ||||
|         //    { | ||||
|         //        Array.Resize(ref toUpdate, toUpdate.Length + 1); | ||||
|         //        toUpdate[toUpdate.Length - 1] = curSong; | ||||
|         //    } | ||||
|         //    var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3)) | ||||
|         //                      .Distinct(); | ||||
|  | ||||
|         //    var durations = await _google.GetVideoDurationsAsync(ids); | ||||
|  | ||||
|         //    toUpdate.ForEach(s => | ||||
|         //    { | ||||
|         //        foreach (var kvp in durations) | ||||
|         //        { | ||||
|         //            if (s.SongInfo.Query.EndsWith(kvp.Key)) | ||||
|         //            { | ||||
|         //                s.TotalTime = kvp.Value; | ||||
|         //                return; | ||||
|         //            } | ||||
|         //        } | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         //public void Destroy() | ||||
|         //{ | ||||
|         //    ActionQueue.Enqueue(async () => | ||||
|         //    { | ||||
|         //        RepeatPlaylist = false; | ||||
|         //        RepeatSong = false; | ||||
|         //        Autoplay = false; | ||||
|         //        Destroyed = true; | ||||
|         //        _playlist.Clear(); | ||||
|  | ||||
|         //        try { await AudioClient.StopAsync(); } catch { } | ||||
|         //        if (!SongCancelSource.IsCancellationRequested) | ||||
|         //            SongCancelSource.Cancel(); | ||||
|         //    }); | ||||
|         //} | ||||
|  | ||||
|         ////public async Task MoveToVoiceChannel(IVoiceChannel voiceChannel) | ||||
|         ////{ | ||||
|         ////    if (audioClient?.ConnectionState != ConnectionState.Connected) | ||||
|         ////        throw new InvalidOperationException("Can't move while bot is not connected to voice channel."); | ||||
|         ////    PlaybackVoiceChannel = voiceChannel; | ||||
|         ////    audioClient = await voiceChannel.ConnectAsync().ConfigureAwait(false); | ||||
|         ////} | ||||
|  | ||||
|         //public bool ToggleRepeatSong() => RepeatSong = !RepeatSong; | ||||
|  | ||||
|         //public bool ToggleRepeatPlaylist() => RepeatPlaylist = !RepeatPlaylist; | ||||
|  | ||||
|         //public bool ToggleAutoplay() => Autoplay = !Autoplay; | ||||
|  | ||||
|         //public void ThrowIfQueueFull() | ||||
|         //{ | ||||
|         //    if (MaxQueueSize == 0) | ||||
|         //        return; | ||||
|         //    if (_playlist.Count >= MaxQueueSize) | ||||
|         //        throw new PlaylistFullException(); | ||||
|         //} | ||||
|         //    new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks));         | ||||
|     } | ||||
| } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ namespace NadekoBot.Services.Music | ||||
|     { | ||||
|         private LinkedList<SongInfo> Songs { get; } = new LinkedList<SongInfo>(); | ||||
|         private int _currentIndex = 0; | ||||
|         private int CurrentIndex | ||||
|         public int CurrentIndex | ||||
|         { | ||||
|             get | ||||
|             { | ||||
| @@ -124,7 +124,7 @@ namespace NadekoBot.Services.Music | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public (int, SongInfo[]) ToArray() | ||||
|         public (int CurrentIndex, SongInfo[] Songs) ToArray() | ||||
|         { | ||||
|             lock (locker) | ||||
|             { | ||||
|   | ||||
| @@ -131,9 +131,9 @@ namespace NadekoBot.Services.Music | ||||
|                         playingMessage?.DeleteAfter(0); | ||||
|  | ||||
|                         playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||
|                                                     .WithAuthor(eab => eab.WithName(GetText("playing_song")).WithMusicIcon()) | ||||
|                                                     .WithDescription(song.PrettyName) | ||||
|                                                     .WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + song.PrettyInfo))) | ||||
|                                                     .WithAuthor(eab => eab.WithName(GetText("playing_song", song.Index + 1)).WithMusicIcon()) | ||||
|                                                     .WithDescription(song.Song.PrettyName) | ||||
|                                                     .WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + song.Song.PrettyInfo))) | ||||
|                                                     .ConfigureAwait(false); | ||||
|                     } | ||||
|                     catch | ||||
| @@ -288,11 +288,15 @@ namespace NadekoBot.Services.Music | ||||
|                 .FirstOrDefault(); | ||||
|  | ||||
|             if (video == null) // do something with this error | ||||
|             { | ||||
|                 _log.Info("Could not load any video elements based on the query."); | ||||
|                 return null; | ||||
|             } | ||||
|             //var m = Regex.Match(query, @"\?t=(?<t>\d*)"); | ||||
|             //int gotoTime = 0; | ||||
|             //if (m.Captures.Count > 0) | ||||
|             //    int.TryParse(m.Groups["t"].ToString(), out gotoTime); | ||||
|              | ||||
|             var song = new SongInfo | ||||
|             { | ||||
|                 Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube" | ||||
|   | ||||
| @@ -407,7 +407,7 @@ | ||||
|   "music_autoplaying": "Auto-playing.", | ||||
|   "music_defvol_set": "Default volume set to {0}%", | ||||
|   "music_dir_queue_complete": "Directory queue complete.", | ||||
|   "music_fairplay": "fairplay", | ||||
|   "music_fairplay": "Fairplay", | ||||
|   "music_finished_song": "Finished song", | ||||
|   "music_fp_disabled": "Fair play disabled.", | ||||
|   "music_fp_enabled": "Fair play enabled.", | ||||
| @@ -425,7 +425,7 @@ | ||||
|   "music_no_search_results": "No search results.", | ||||
|   "music_paused": "Music playback paused.", | ||||
|   "music_player_queue": "Player queue - Page {0}/{1}", | ||||
|   "music_playing_song": "Playing song", | ||||
|   "music_playing_song": "Playing song #{0}", | ||||
|   "music_playlists": "`#{0}` - **{1}** by *{2}* ({3} songs)", | ||||
|   "music_playlists_page": "Page {0} of saved playlists", | ||||
|   "music_playlist_deleted": "Playlist deleted.", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user