From 98e2b0ce37bf45ada5a67fa7ed9078f35a04a5d3 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 5 Jul 2017 18:53:21 +0200 Subject: [PATCH] More logs, player loop moved to a thread --- src/NadekoBot/Services/Music/MusicPlayer.cs | 354 ++++++++++--------- src/NadekoBot/Services/Music/MusicService.cs | 4 +- 2 files changed, 182 insertions(+), 176 deletions(-) diff --git a/src/NadekoBot/Services/Music/MusicPlayer.cs b/src/NadekoBot/Services/Music/MusicPlayer.cs index ef3c53d2..688bf1c8 100644 --- a/src/NadekoBot/Services/Music/MusicPlayer.cs +++ b/src/NadekoBot/Services/Music/MusicPlayer.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Services.Music } public class MusicPlayer { - private readonly Task _player; + private readonly Thread _player; public IVoiceChannel VoiceChannel { get; private set; } private readonly Logger _log; @@ -138,188 +138,192 @@ namespace NadekoBot.Services.Music _log.Info("Initialized"); - _player = Task.Run(async () => - { - while (!Exited) - { - _bytesSent = 0; - CancellationToken cancelToken; - (int Index, SongInfo Song) data; - lock (locker) - { - data = Queue.Current; - cancelToken = SongCancelSource.Token; - manualSkip = false; - manualIndex = false; - } - if (data.Song == null) - continue; + _player = new Thread(new ThreadStart(PlayerLoop)); + _player.Start(); + _log.Info("Loop started"); + } - _log.Info("Starting"); - using (var b = new SongBuffer(await data.Song.Uri(), "")) - { - _log.Info("Created buffer, buffering..."); - AudioOutStream pcm = null; - try - { - var bufferTask = b.StartBuffering(cancelToken); - var timeout = Task.Delay(10000); - if (Task.WhenAny(bufferTask, timeout) == timeout) - { - _log.Info("Buffering failed due to a timeout."); - continue; - } - else if (!bufferTask.Result) - { - _log.Info("Buffering failed due to a cancel or error."); - continue; - } - _log.Info("Buffered. Getting audio client..."); - var ac = await GetAudioClient(); - _log.Info("Got Audio client"); - if (ac == null) - { - _log.Info("Can't join"); - await Task.Delay(900, cancelToken); - // just wait some time, maybe bot doesn't even have perms to join that voice channel, - // i don't want to spam connection attempts - continue; - } - pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 500); - _log.Info("Created pcm stream"); - OnStarted?.Invoke(this, data); + private async void PlayerLoop() + { + while (!Exited) + { + _bytesSent = 0; + CancellationToken cancelToken; + (int Index, SongInfo Song) data; + lock (locker) + { + data = Queue.Current; + cancelToken = SongCancelSource.Token; + manualSkip = false; + manualIndex = false; + } + if (data.Song == null) + continue; - byte[] buffer = new byte[3840]; - int bytesRead = 0; + _log.Info("Starting"); + using (var b = new SongBuffer(await data.Song.Uri(), "")) + { + _log.Info("Created buffer, buffering..."); + AudioOutStream pcm = null; + try + { + var bufferTask = b.StartBuffering(cancelToken); + var timeout = Task.Delay(10000); + if (Task.WhenAny(bufferTask, timeout) == timeout) + { + _log.Info("Buffering failed due to a timeout."); + continue; + } + else if (!bufferTask.Result) + { + _log.Info("Buffering failed due to a cancel or error."); + continue; + } + _log.Info("Buffered. Getting audio client..."); + var ac = await GetAudioClient(); + _log.Info("Got Audio client"); + if (ac == null) + { + _log.Info("Can't join"); + await Task.Delay(900, cancelToken); + // just wait some time, maybe bot doesn't even have perms to join that voice channel, + // i don't want to spam connection attempts + continue; + } + pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 500); + _log.Info("Created pcm stream"); + OnStarted?.Invoke(this, data); - while ((bytesRead = b.Read(buffer, 0, buffer.Length)) > 0 - && (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds)) - { - AdjustVolume(buffer, Volume); - await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); - unchecked { _bytesSent += bytesRead; } + byte[] buffer = new byte[3840]; + int bytesRead = 0; - await (pauseTaskSource?.Task ?? Task.CompletedTask); - } - } - catch (OperationCanceledException) - { - _log.Info("Song Canceled"); - } - catch (Exception ex) - { - _log.Warn(ex); - } - finally - { - if (pcm != null) - { - // flush is known to get stuck from time to time, - // just skip flushing if it takes more than 1 second - var flushCancel = new CancellationTokenSource(); - var flushToken = flushCancel.Token; - var flushDelay = Task.Delay(1000, flushToken); - await Task.WhenAny(flushDelay, pcm.FlushAsync(flushToken)); - flushCancel.Cancel(); - pcm.Dispose(); - } + while ((bytesRead = b.Read(buffer, 0, buffer.Length)) > 0 + && (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds)) + { + AdjustVolume(buffer, Volume); + await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); + unchecked { _bytesSent += bytesRead; } - OnCompleted?.Invoke(this, data.Song); - } - } - try - { - //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 + await (pauseTaskSource?.Task ?? Task.CompletedTask); + } + } + catch (OperationCanceledException) + { + _log.Info("Song Canceled"); + } + catch (Exception ex) + { + _log.Warn(ex); + } + finally + { + if (pcm != null) + { + // flush is known to get stuck from time to time, + // just skip flushing if it takes more than 1 second + var flushCancel = new CancellationTokenSource(); + var flushToken = flushCancel.Token; + var flushDelay = Task.Delay(1000, flushToken); + await Task.WhenAny(flushDelay, pcm.FlushAsync(flushToken)); + flushCancel.Cancel(); + pcm.Dispose(); + } - int queueCount; - lock (locker) - queueCount = Queue.Count; + OnCompleted?.Invoke(this, data.Song); + } + } + try + { + //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 (!manualIndex && (!RepeatCurrentSong || manualSkip)) - { - if (Shuffle) - { - _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 (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(); + int queueCount; + lock (locker) + queueCount = Queue.Count; - 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 - { - _log.Info("Waiting for something to happen"); - await Task.Delay(500); - } - while ((Queue.Count == 0 || Stopped) && !Exited); - } - }, SongCancelSource.Token); + if (!manualIndex && (!RepeatCurrentSong || manualSkip)) + { + if (Shuffle) + { + _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 (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 + { + _log.Info("Waiting for something to happen"); + await Task.Delay(500); + } + while ((Queue.Count == 0 || Stopped) && !Exited); + } } public void SetIndex(int index) diff --git a/src/NadekoBot/Services/Music/MusicService.cs b/src/NadekoBot/Services/Music/MusicService.cs index 8e68eaa8..86929a7e 100644 --- a/src/NadekoBot/Services/Music/MusicService.cs +++ b/src/NadekoBot/Services/Music/MusicService.cs @@ -44,6 +44,7 @@ namespace NadekoBot.Services.Music _sc = sc; _creds = creds; _log = LogManager.GetCurrentClassLogger(); + _yt = YouTube.Default; try { Directory.Delete(MusicDataPath, true); } catch { } @@ -329,7 +330,7 @@ namespace NadekoBot.Services.Music return (null, null); } _log.Info("Getting all videos"); - var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); + var allVideos = await Task.Run(async () => { try { return await _yt.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); var video = videos .Where(v => v.AudioBitrate < 256) @@ -358,6 +359,7 @@ namespace NadekoBot.Services.Music private readonly Regex m3uRegex = new Regex("(?^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline); private readonly Regex asxRegex = new Regex(".*?)\"", RegexOptions.Compiled); private readonly Regex xspfRegex = new Regex("(?.*?)", RegexOptions.Compiled); + private readonly YouTube _yt; private async Task HandleStreamContainers(string query) {