More logs, player loop moved to a thread

This commit is contained in:
Master Kwoth 2017-07-05 18:53:21 +02:00
parent fbedf5878b
commit 98e2b0ce37
2 changed files with 182 additions and 176 deletions

View File

@ -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)

View File

@ -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)
{ {