More logs, player loop moved to a thread
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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<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 video = videos | ||||
|                 .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 asxRegex = new Regex("<ref href=\"(?<url>.*?)\"", RegexOptions.Compiled); | ||||
|         private readonly Regex xspfRegex = new Regex("<location>(?<url>.*?)</location>", RegexOptions.Compiled); | ||||
|         private readonly YouTube _yt; | ||||
|  | ||||
|         private async Task<string> HandleStreamContainers(string query) | ||||
|         { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user