More logs, player loop moved to a thread
This commit is contained in:
parent
fbedf5878b
commit
98e2b0ce37
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user