More logs, player loop moved to a thread
This commit is contained in:
		| @@ -20,7 +20,7 @@ namespace NadekoBot.Services.Music | |||||||
|     } |     } | ||||||
|     public class MusicPlayer |     public class MusicPlayer | ||||||
|     { |     { | ||||||
|         private readonly Task _player; |         private readonly Thread _player; | ||||||
|         public IVoiceChannel VoiceChannel { get; private set; } |         public IVoiceChannel VoiceChannel { get; private set; } | ||||||
|         private readonly Logger _log; |         private readonly Logger _log; | ||||||
|  |  | ||||||
| @@ -138,188 +138,192 @@ namespace NadekoBot.Services.Music | |||||||
|  |  | ||||||
|             _log.Info("Initialized"); |             _log.Info("Initialized"); | ||||||
|  |  | ||||||
|             _player = Task.Run(async () => |             _player = new Thread(new ThreadStart(PlayerLoop)); | ||||||
|              { |             _player.Start(); | ||||||
|                  while (!Exited) |             _log.Info("Loop started"); | ||||||
|                  { |         } | ||||||
|                      _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; |  | ||||||
|  |  | ||||||
|                      _log.Info("Starting"); |         private async void PlayerLoop() | ||||||
|                      using (var b = new SongBuffer(await data.Song.Uri(), "")) |         { | ||||||
|                      { |             while (!Exited) | ||||||
|                          _log.Info("Created buffer, buffering..."); |             { | ||||||
|                          AudioOutStream pcm = null; |                 _bytesSent = 0; | ||||||
|                          try |                 CancellationToken cancelToken; | ||||||
|                          { |                 (int Index, SongInfo Song) data; | ||||||
|                              var bufferTask = b.StartBuffering(cancelToken); |                 lock (locker) | ||||||
|                              var timeout = Task.Delay(10000); |                 { | ||||||
|                              if (Task.WhenAny(bufferTask, timeout) == timeout) |                     data = Queue.Current; | ||||||
|                              { |                     cancelToken = SongCancelSource.Token; | ||||||
|                                  _log.Info("Buffering failed due to a timeout."); |                     manualSkip = false; | ||||||
|                                  continue; |                     manualIndex = false; | ||||||
|                              } |                 } | ||||||
|                              else if (!bufferTask.Result) |                 if (data.Song == null) | ||||||
|                              { |                     continue; | ||||||
|                                  _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); |  | ||||||
|  |  | ||||||
|                              byte[] buffer = new byte[3840]; |                 _log.Info("Starting"); | ||||||
|                              int bytesRead = 0; |                 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 |                         byte[] buffer = new byte[3840]; | ||||||
|                              && (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds)) |                         int bytesRead = 0; | ||||||
|                              { |  | ||||||
|                                  AdjustVolume(buffer, Volume); |  | ||||||
|                                  await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); |  | ||||||
|                                  unchecked { _bytesSent += bytesRead; } |  | ||||||
|  |  | ||||||
|                                  await (pauseTaskSource?.Task ?? Task.CompletedTask); |                         while ((bytesRead = b.Read(buffer, 0, buffer.Length)) > 0 | ||||||
|                              } |                         && (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds)) | ||||||
|                          } |                         { | ||||||
|                          catch (OperationCanceledException) |                             AdjustVolume(buffer, Volume); | ||||||
|                          { |                             await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false); | ||||||
|                              _log.Info("Song Canceled"); |                             unchecked { _bytesSent += bytesRead; } | ||||||
|                          } |  | ||||||
|                          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(); |  | ||||||
|                              } |  | ||||||
|  |  | ||||||
|                              OnCompleted?.Invoke(this, data.Song); |                             await (pauseTaskSource?.Task ?? Task.CompletedTask); | ||||||
|                          } |                         } | ||||||
|                      } |                     } | ||||||
|                      try |                     catch (OperationCanceledException) | ||||||
|                      { |                     { | ||||||
|                          //if repeating current song, just ignore other settings,  |                         _log.Info("Song Canceled"); | ||||||
|                          // and play this song again (don't change the index) |                     } | ||||||
|                          // ignore rcs if song is manually skipped |                     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; |                         OnCompleted?.Invoke(this, data.Song); | ||||||
|                          lock (locker) |                     } | ||||||
|                              queueCount = Queue.Count; |                 } | ||||||
|  |                 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)) |                     int queueCount; | ||||||
|                          { |                     lock (locker) | ||||||
|                              if (Shuffle) |                         queueCount = Queue.Count; | ||||||
|                              { |  | ||||||
|                                  _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; |                     if (!manualIndex && (!RepeatCurrentSong || manualSkip)) | ||||||
|                                          for (var i = 0; i < q.Length; i++) //first try to find a queuer who didn't have their song played recently |                     { | ||||||
|                                          { |                         if (Shuffle) | ||||||
|                                              var item = q[i]; |                         { | ||||||
|                                              if (RecentlyPlayedUsers.Add(item.QueuerName)) // if it's found, set current song to that index |                             _log.Info("Random song"); | ||||||
|                                              { |                             Queue.Random(); //if shuffle is set, set current song index to a random number | ||||||
|                                                  Queue.CurrentIndex = i; |                         } | ||||||
|                                                  found = true; |                         else | ||||||
|                                                  break; |                         { | ||||||
|                                              } |                             //if last song, and autoplay is enabled, and if it's a youtube song | ||||||
|                                          } |                             // do autplay magix | ||||||
|                                          if (!found) //if it's not |                             if (queueCount - 1 == data.Index && Autoplay && data.Song?.ProviderType == Database.Models.MusicType.YouTube) | ||||||
|                                          { |                             { | ||||||
|                                              RecentlyPlayedUsers.Clear(); //clear all recently played users (that means everyone from the playlist has had their song played) |                                 try | ||||||
|                                              Queue.Random(); //go to a random song (to prevent looping on the first few songs) |                                 { | ||||||
|                                              var cur = Current; |                                     _log.Info("Loading related song"); | ||||||
|                                              if (cur.Current != null) // add newely scheduled song's queuer to the recently played list |                                     await _musicService.TryQueueRelatedSongAsync(data.Song.Query, OutputTextChannel, VoiceChannel); | ||||||
|                                                  RecentlyPlayedUsers.Add(cur.Current.QueuerName); |                                     Queue.Next(); | ||||||
|                                          } |                                 } | ||||||
|                                      } |                                 catch | ||||||
|                                  } |                                 { | ||||||
|                                  else if (queueCount - 1 == data.Index && !RepeatPlaylist && !manualSkip) |                                     _log.Info("Loading related song failed."); | ||||||
|                                  { |                                 } | ||||||
|                                      _log.Info("Stopping because repeatplaylist is disabled"); |                             } | ||||||
|                                      lock (locker) |                             else if (FairPlay) | ||||||
|                                      { |                             { | ||||||
|                                          Stop(); |                                 lock (locker) | ||||||
|                                      } |                                 { | ||||||
|                                  } |                                     _log.Info("Next fair song"); | ||||||
|                                  else |                                     var q = Queue.ToArray().Songs.Shuffle().ToArray(); | ||||||
|                                  { |  | ||||||
|                                      _log.Info("Next song"); |                                     bool found = false; | ||||||
|                                      lock (locker) |                                     for (var i = 0; i < q.Length; i++) //first try to find a queuer who didn't have their song played recently | ||||||
|                                      { |                                     { | ||||||
|                                          Queue.Next(); |                                         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; | ||||||
|                      catch (Exception ex) |                                         } | ||||||
|                      { |                                     } | ||||||
|                          _log.Error(ex); |                                     if (!found) //if it's not | ||||||
|                      } |                                     { | ||||||
|                      do |                                         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) | ||||||
|                          _log.Info("Waiting for something to happen"); |                                         var cur = Current; | ||||||
|                          await Task.Delay(500); |                                         if (cur.Current != null) // add newely scheduled song's queuer to the recently played list | ||||||
|                      } |                                             RecentlyPlayedUsers.Add(cur.Current.QueuerName); | ||||||
|                      while ((Queue.Count == 0 || Stopped) && !Exited); |                                     } | ||||||
|                  } |                                 } | ||||||
|              }, SongCancelSource.Token); |                             } | ||||||
|  |                             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) |         public void SetIndex(int index) | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ namespace NadekoBot.Services.Music | |||||||
|             _sc = sc; |             _sc = sc; | ||||||
|             _creds = creds; |             _creds = creds; | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _yt = YouTube.Default; | ||||||
|  |  | ||||||
|             try { Directory.Delete(MusicDataPath, true); } catch { } |             try { Directory.Delete(MusicDataPath, true); } catch { } | ||||||
|  |  | ||||||
| @@ -329,7 +330,7 @@ namespace NadekoBot.Services.Music | |||||||
|                 return (null, null); |                 return (null, null); | ||||||
|             } |             } | ||||||
|             _log.Info("Getting all videos"); |             _log.Info("Getting all videos"); | ||||||
|             var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false); |             var allVideos = await Task.Run(async () => { try { return await _yt.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false); | ||||||
|             var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); |             var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); | ||||||
|             var video = videos |             var video = videos | ||||||
|                 .Where(v => v.AudioBitrate < 256) |                 .Where(v => v.AudioBitrate < 256) | ||||||
| @@ -358,6 +359,7 @@ namespace NadekoBot.Services.Music | |||||||
|         private readonly Regex m3uRegex = new Regex("(?<url>^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline); |         private readonly Regex m3uRegex = new Regex("(?<url>^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline); | ||||||
|         private readonly Regex asxRegex = new Regex("<ref href=\"(?<url>.*?)\"", RegexOptions.Compiled); |         private readonly Regex asxRegex = new Regex("<ref href=\"(?<url>.*?)\"", RegexOptions.Compiled); | ||||||
|         private readonly Regex xspfRegex = new Regex("<location>(?<url>.*?)</location>", RegexOptions.Compiled); |         private readonly Regex xspfRegex = new Regex("<location>(?<url>.*?)</location>", RegexOptions.Compiled); | ||||||
|  |         private readonly YouTube _yt; | ||||||
|  |  | ||||||
|         private async Task<string> HandleStreamContainers(string query) |         private async Task<string> HandleStreamContainers(string query) | ||||||
|         { |         { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user