.play help update, readded pausing when nobody is in voice channel, and also cleaning up music players if bot is kicked or leaves the server
This commit is contained in:
parent
3c9b68e739
commit
a609e17717
@ -36,56 +36,62 @@ namespace NadekoBot.Modules.Music
|
|||||||
_db = db;
|
_db = db;
|
||||||
_music = music;
|
_music = music;
|
||||||
|
|
||||||
//_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||||
|
_client.LeftGuild += _client_LeftGuild;
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo when someone drags nadeko from one voice channel to another
|
private Task _client_LeftGuild(SocketGuild arg)
|
||||||
|
{
|
||||||
|
var t = _music.DestroyPlayer(arg.Id);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
//private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState)
|
private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState)
|
||||||
//{
|
{
|
||||||
// var usr = iusr as SocketGuildUser;
|
var t = Task.Run(() =>
|
||||||
// if (usr == null ||
|
{
|
||||||
// oldState.VoiceChannel == newState.VoiceChannel)
|
var usr = iusr as SocketGuildUser;
|
||||||
// return Task.CompletedTask;
|
if (usr == null ||
|
||||||
|
oldState.VoiceChannel == newState.VoiceChannel)
|
||||||
|
return;
|
||||||
|
|
||||||
// MusicPlayer player;
|
var player = _music.GetPlayerOrDefault(usr.Guild.Id);
|
||||||
// if ((player = _music.GetPlayer(usr.Guild.Id)) == null)
|
|
||||||
// return Task.CompletedTask;
|
|
||||||
|
|
||||||
// try
|
try
|
||||||
// {
|
{
|
||||||
// //if bot moved
|
//if bot moved
|
||||||
// if ((player.PlaybackVoiceChannel == oldState.VoiceChannel) &&
|
if ((player.VoiceChannel == oldState.VoiceChannel) &&
|
||||||
// usr.Id == _client.CurrentUser.Id)
|
usr.Id == _client.CurrentUser.Id)
|
||||||
// {
|
{
|
||||||
// if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel
|
if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel
|
||||||
// player.TogglePause();
|
player.TogglePause();
|
||||||
// else if (!player.Paused && newState.VoiceChannel.Users.Count <= 1) // pause if there are no users in the new channel
|
else if (!player.Paused && newState.VoiceChannel.Users.Count <= 1) // pause if there are no users in the new channel
|
||||||
// player.TogglePause();
|
player.TogglePause();
|
||||||
|
|
||||||
// return Task.CompletedTask;
|
player.SetVoiceChannel(newState.VoiceChannel);
|
||||||
// }
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// //if some other user moved
|
//if some other user moved
|
||||||
// if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause
|
if ((player.VoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause
|
||||||
// player.Paused &&
|
player.Paused &&
|
||||||
// newState.VoiceChannel.Users.Count == 2) || // keep in mind bot is in the channel (+1)
|
newState.VoiceChannel.Users.Count >= 2) || // keep in mind bot is in the channel (+1)
|
||||||
// (player.PlaybackVoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause
|
(player.VoiceChannel == oldState.VoiceChannel && // if left last, and player unpaused, pause
|
||||||
// !player.Paused &&
|
!player.Paused &&
|
||||||
// oldState.VoiceChannel.Users.Count == 1))
|
oldState.VoiceChannel.Users.Count == 1))
|
||||||
// {
|
{
|
||||||
// player.TogglePause();
|
player.TogglePause();
|
||||||
// return Task.CompletedTask;
|
return;
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
// }
|
catch
|
||||||
// catch
|
{
|
||||||
// {
|
// ignored
|
||||||
// // ignored
|
}
|
||||||
// }
|
});
|
||||||
// return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
//}
|
}
|
||||||
|
|
||||||
private async Task InternalQueue(MusicPlayer mp, SongInfo songInfo, bool silent)
|
private async Task InternalQueue(MusicPlayer mp, SongInfo songInfo, bool silent)
|
||||||
{
|
{
|
||||||
@ -96,25 +102,24 @@ namespace NadekoBot.Modules.Music
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(bool Success, int Index) qData;
|
int index;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
qData = mp.Enqueue(songInfo);
|
index = mp.Enqueue(songInfo);
|
||||||
}
|
}
|
||||||
catch (QueueFullException)
|
catch (QueueFullException)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("queue_full", mp.MaxQueueSize).ConfigureAwait(false);
|
await ReplyErrorLocalized("queue_full", mp.MaxQueueSize).ConfigureAwait(false);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
if (qData.Success)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
if (!silent)
|
if (!silent)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
|
|
||||||
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (qData.Index)).WithMusicIcon())
|
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon())
|
||||||
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
|
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
|
||||||
.WithThumbnailUrl(songInfo.Thumbnail)
|
.WithThumbnailUrl(songInfo.Thumbnail)
|
||||||
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider)))
|
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider)))
|
||||||
@ -135,12 +140,26 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public Task Play([Remainder]string query = null)
|
public async Task Play([Remainder] string query = null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(query))
|
var mp = await _music.GetOrCreatePlayer(Context);
|
||||||
try { return Queue(query); } catch (QueueFullException) { return Task.CompletedTask; }
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
{
|
||||||
|
await Next();
|
||||||
|
}
|
||||||
|
else if (int.TryParse(query, out var index))
|
||||||
|
if (index >= 1)
|
||||||
|
mp.SetIndex(index - 1);
|
||||||
|
else
|
||||||
|
return;
|
||||||
else
|
else
|
||||||
return Next();
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Queue(query);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -218,13 +237,12 @@ namespace NadekoBot.Modules.Music
|
|||||||
page = current / itemsPerPage;
|
page = current / itemsPerPage;
|
||||||
|
|
||||||
//if page is 0 (-1 after this decrement) that means default to the page current song is playing from
|
//if page is 0 (-1 after this decrement) that means default to the page current song is playing from
|
||||||
|
var total = mp.TotalPlaytime;
|
||||||
//var total = musicPlayer.TotalPlaytime;
|
var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format",
|
||||||
//var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format",
|
(int)total.TotalHours,
|
||||||
// (int)total.TotalHours,
|
total.Minutes,
|
||||||
// total.Minutes,
|
total.Seconds);
|
||||||
// total.Seconds);
|
var maxPlaytime = mp.MaxPlaytimeSeconds;
|
||||||
//var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
|
|
||||||
var lastPage = songs.Length / itemsPerPage;
|
var lastPage = songs.Length / itemsPerPage;
|
||||||
Func<int, EmbedBuilder> printAction = curPage =>
|
Func<int, EmbedBuilder> printAction = curPage =>
|
||||||
{
|
{
|
||||||
@ -255,10 +273,12 @@ namespace NadekoBot.Modules.Music
|
|||||||
add += "🔀 " + GetText("shuffling_playlist") + "\n";
|
add += "🔀 " + GetText("shuffling_playlist") + "\n";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mp.RepeatPlaylist)
|
|
||||||
add += "🔁 " + GetText("repeating_playlist") + "\n";
|
|
||||||
if (mp.Autoplay)
|
if (mp.Autoplay)
|
||||||
add += "↪ " + GetText("autoplaying") + "\n";
|
add += "↪ " + GetText("autoplaying") + "\n";
|
||||||
|
if (mp.FairPlay && !mp.Autoplay)
|
||||||
|
add += " " + GetText("fairplay") + "\n";
|
||||||
|
else if (mp.RepeatPlaylist)
|
||||||
|
add += "🔁 " + GetText("repeating_playlist") + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(add))
|
if (!string.IsNullOrWhiteSpace(add))
|
||||||
@ -268,12 +288,8 @@ namespace NadekoBot.Modules.Music
|
|||||||
.WithAuthor(eab => eab.WithName(GetText("player_queue", curPage + 1, lastPage + 1))
|
.WithAuthor(eab => eab.WithName(GetText("player_queue", curPage + 1, lastPage + 1))
|
||||||
.WithMusicIcon())
|
.WithMusicIcon())
|
||||||
.WithDescription(desc)
|
.WithDescription(desc)
|
||||||
//.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
|
.WithFooter(ef => ef.WithText($"{mp.PrettyVolume} | {songs.Length} " +
|
||||||
// $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
|
$"{("tracks".SnPl(songs.Length))} | {totalStr}"))
|
||||||
// (musicPlayer.FairPlay
|
|
||||||
// ? "✔️" + GetText("fairplay")
|
|
||||||
// : "✖️" + GetText("fairplay")) + " | " +
|
|
||||||
// (maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime))))
|
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
return embed;
|
return embed;
|
||||||
@ -517,27 +533,22 @@ namespace NadekoBot.Modules.Music
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
//[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
//public async Task Fairplay()
|
public async Task Fairplay()
|
||||||
//{
|
{
|
||||||
// var channel = (ITextChannel)Context.Channel;
|
var mp = await _music.GetOrCreatePlayer(Context);
|
||||||
// MusicPlayer musicPlayer;
|
var val = mp.FairPlay = !mp.FairPlay;
|
||||||
// if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
|
||||||
// return;
|
|
||||||
// if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
|
||||||
// return;
|
|
||||||
// var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
|
|
||||||
|
|
||||||
// if (val)
|
if (val)
|
||||||
// {
|
{
|
||||||
// await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false);
|
await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false);
|
||||||
// }
|
}
|
||||||
// else
|
else
|
||||||
// {
|
{
|
||||||
// await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false);
|
await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false);
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
@ -613,58 +624,46 @@ namespace NadekoBot.Modules.Music
|
|||||||
await ReplyConfirmLocalized("songs_shuffle_disable").ConfigureAwait(false);
|
await ReplyConfirmLocalized("songs_shuffle_disable").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
//[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
//public async Task Playlist([Remainder] string playlist)
|
public async Task Playlist([Remainder] string playlist)
|
||||||
//{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(playlist))
|
||||||
|
return;
|
||||||
|
|
||||||
// var arg = playlist;
|
var mp = await _music.GetOrCreatePlayer(Context);
|
||||||
// if (string.IsNullOrWhiteSpace(arg))
|
|
||||||
// return;
|
|
||||||
// if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
|
|
||||||
// {
|
|
||||||
// await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
|
||||||
// if (plId == null)
|
|
||||||
// {
|
|
||||||
// await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
|
||||||
// if (!ids.Any())
|
|
||||||
// {
|
|
||||||
// await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// var count = ids.Count();
|
|
||||||
// var msg = await Context.Channel.SendMessageAsync("🎵 " + GetText("attempting_to_queue",
|
|
||||||
// Format.Bold(count.ToString()))).ConfigureAwait(false);
|
|
||||||
|
|
||||||
// var cancelSource = new CancellationTokenSource();
|
var plId = (await _google.GetPlaylistIdsByKeywordsAsync(playlist).ConfigureAwait(false)).FirstOrDefault();
|
||||||
|
if (plId == null)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
||||||
|
if (!ids.Any())
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var count = ids.Count();
|
||||||
|
var msg = await Context.Channel.SendMessageAsync("🎵 " + GetText("attempting_to_queue",
|
||||||
|
Format.Bold(count.ToString()))).ConfigureAwait(false);
|
||||||
|
|
||||||
// var gusr = (IGuildUser)Context.User;
|
foreach (var song in ids)
|
||||||
// while (ids.Any() && !cancelSource.IsCancellationRequested)
|
{
|
||||||
// {
|
try
|
||||||
// var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
|
{
|
||||||
// {
|
if (mp.Exited)
|
||||||
// if (cancelSource.Token.IsCancellationRequested)
|
return;
|
||||||
// return;
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// await _music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// catch (SongNotFoundException) { }
|
|
||||||
// catch { try { cancelSource.Cancel(); } catch { } }
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
|
await Task.WhenAll(Task.Delay(100), InternalQueue(mp, await _music.ResolveSong(song, Context.User.ToString(), MusicType.YouTube), true));
|
||||||
// ids = ids.Skip(5);
|
}
|
||||||
// }
|
catch (SongNotFoundException) { }
|
||||||
|
catch { break; }
|
||||||
|
}
|
||||||
|
|
||||||
// await msg.ModifyAsync(m => m.Content = "✅ " + Format.Bold(GetText("playlist_queue_complete"))).ConfigureAwait(false);
|
await msg.ModifyAsync(m => m.Content = "✅ " + Format.Bold(GetText("playlist_queue_complete"))).ConfigureAwait(false);
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
@ -1198,7 +1198,7 @@
|
|||||||
<value>`{0}drawnew` or `{0}drawnew 5`</value>
|
<value>`{0}drawnew` or `{0}drawnew 5`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="shuffleplaylist_cmd" xml:space="preserve">
|
<data name="shuffleplaylist_cmd" xml:space="preserve">
|
||||||
<value>shuffle plsh</value>
|
<value>shuffle sh plsh</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="shuffleplaylist_desc" xml:space="preserve">
|
<data name="shuffleplaylist_desc" xml:space="preserve">
|
||||||
<value>Shuffles the current playlist.</value>
|
<value>Shuffles the current playlist.</value>
|
||||||
@ -1471,10 +1471,10 @@
|
|||||||
<value>play start</value>
|
<value>play start</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="play_desc" xml:space="preserve">
|
<data name="play_desc" xml:space="preserve">
|
||||||
<value>If no arguments are specified, acts as `{0}next 1` command. If you specify a search query, acts as a `{0}q` command</value>
|
<value>If no arguments are specified, acts as `{0}next 1` command. If you specify a song number, it will jump to that song. If you specify a search query, acts as a `{0}q` command</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="play_usage" xml:space="preserve">
|
<data name="play_usage" xml:space="preserve">
|
||||||
<value>`{0}play` or `{0}play Dream Of Venice`</value>
|
<value>`{0}play` or `{0}play 5` or `{0}play Dream Of Venice`</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="stop_cmd" xml:space="preserve">
|
<data name="stop_cmd" xml:space="preserve">
|
||||||
<value>stop s</value>
|
<value>stop s</value>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using NadekoBot.DataStructures.Replacements;
|
using NadekoBot.DataStructures.Replacements;
|
||||||
using NadekoBot.Services;
|
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NadekoBot.Services.Music;
|
using NadekoBot.Services.Music;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -4,7 +4,9 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System.Diagnostics;
|
using System.Linq;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
@ -18,7 +20,7 @@ namespace NadekoBot.Services.Music
|
|||||||
public class MusicPlayer
|
public class MusicPlayer
|
||||||
{
|
{
|
||||||
private readonly Task _player;
|
private readonly Task _player;
|
||||||
private IVoiceChannel VoiceChannel { get; set; }
|
public IVoiceChannel VoiceChannel { get; private set; }
|
||||||
private readonly Logger _log;
|
private readonly Logger _log;
|
||||||
|
|
||||||
private MusicQueue Queue { get; } = new MusicQueue();
|
private MusicQueue Queue { get; } = new MusicQueue();
|
||||||
@ -27,6 +29,7 @@ namespace NadekoBot.Services.Music
|
|||||||
public bool Stopped { get; private set; } = false;
|
public bool Stopped { get; private set; } = false;
|
||||||
public float Volume { get; private set; } = 1.0f;
|
public float Volume { get; private set; } = 1.0f;
|
||||||
public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
|
public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
|
||||||
|
public bool Paused => pauseTaskSource != null;
|
||||||
private TaskCompletionSource<bool> pauseTaskSource { get; set; } = null;
|
private TaskCompletionSource<bool> pauseTaskSource { get; set; } = null;
|
||||||
|
|
||||||
private CancellationTokenSource SongCancelSource { get; set; }
|
private CancellationTokenSource SongCancelSource { get; set; }
|
||||||
@ -48,7 +51,27 @@ namespace NadekoBot.Services.Music
|
|||||||
public uint MaxQueueSize
|
public uint MaxQueueSize
|
||||||
{
|
{
|
||||||
get => Queue.MaxQueueSize;
|
get => Queue.MaxQueueSize;
|
||||||
set => Queue.MaxQueueSize = value;
|
set { lock (locker) Queue.MaxQueueSize = value; }
|
||||||
|
}
|
||||||
|
private bool _fairPlay;
|
||||||
|
public bool FairPlay
|
||||||
|
{
|
||||||
|
get => _fairPlay;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
var cur = Queue.Current;
|
||||||
|
if (cur.Song != null)
|
||||||
|
RecentlyPlayedUsers.Add(cur.Song.QueuerName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RecentlyPlayedUsers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_fairPlay = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public uint MaxPlaytimeSeconds { get; set; }
|
public uint MaxPlaytimeSeconds { get; set; }
|
||||||
|
|
||||||
@ -56,6 +79,7 @@ namespace NadekoBot.Services.Music
|
|||||||
const int _frameBytes = 3840;
|
const int _frameBytes = 3840;
|
||||||
const float _miliseconds = 20.0f;
|
const float _miliseconds = 20.0f;
|
||||||
public TimeSpan CurrentTime => TimeSpan.FromSeconds(_bytesSent / (float)_frameBytes / (1000 / _miliseconds));
|
public TimeSpan CurrentTime => TimeSpan.FromSeconds(_bytesSent / (float)_frameBytes / (1000 / _miliseconds));
|
||||||
|
|
||||||
private int _bytesSent = 0;
|
private int _bytesSent = 0;
|
||||||
|
|
||||||
private IAudioClient _audioClient;
|
private IAudioClient _audioClient;
|
||||||
@ -63,15 +87,19 @@ namespace NadekoBot.Services.Music
|
|||||||
private MusicService _musicService;
|
private MusicService _musicService;
|
||||||
|
|
||||||
#region events
|
#region events
|
||||||
public event Action<MusicPlayer, SongInfo> OnStarted;
|
public event Action<MusicPlayer, (int Index, SongInfo Song)> OnStarted;
|
||||||
public event Action<MusicPlayer, SongInfo> OnCompleted;
|
public event Action<MusicPlayer, SongInfo> OnCompleted;
|
||||||
public event Action<MusicPlayer, bool> OnPauseChanged;
|
public event Action<MusicPlayer, bool> OnPauseChanged;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
private bool manualSkip = false;
|
private bool manualSkip = false;
|
||||||
|
private bool manualIndex = false;
|
||||||
private bool newVoiceChannel = false;
|
private bool newVoiceChannel = false;
|
||||||
|
|
||||||
|
private ConcurrentHashSet<string> RecentlyPlayedUsers { get; } = new ConcurrentHashSet<string>();
|
||||||
|
public TimeSpan TotalPlaytime => TimeSpan.MaxValue;
|
||||||
|
|
||||||
public MusicPlayer(MusicService musicService, IVoiceChannel vch, ITextChannel output, float volume)
|
public MusicPlayer(MusicService musicService, IVoiceChannel vch, ITextChannel output, float volume)
|
||||||
{
|
{
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
@ -93,6 +121,7 @@ namespace NadekoBot.Services.Music
|
|||||||
data = Queue.Current;
|
data = Queue.Current;
|
||||||
cancelToken = SongCancelSource.Token;
|
cancelToken = SongCancelSource.Token;
|
||||||
manualSkip = false;
|
manualSkip = false;
|
||||||
|
manualIndex = false;
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -123,8 +152,8 @@ namespace NadekoBot.Services.Music
|
|||||||
// i don't want to spam connection attempts
|
// i don't want to spam connection attempts
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 200);
|
var pcm = ac.CreatePCMStream(AudioApplication.Music, bufferMillis: 500);
|
||||||
OnStarted?.Invoke(this, data.Song);
|
OnStarted?.Invoke(this, data);
|
||||||
|
|
||||||
byte[] buffer = new byte[3840];
|
byte[] buffer = new byte[3840];
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
@ -133,9 +162,7 @@ namespace NadekoBot.Services.Music
|
|||||||
while ((bytesRead = await b.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) > 0
|
while ((bytesRead = await b.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) > 0
|
||||||
&& (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds))
|
&& (MaxPlaytimeSeconds <= 0 || MaxPlaytimeSeconds >= CurrentTime.TotalSeconds))
|
||||||
{
|
{
|
||||||
var vol = Volume;
|
AdjustVolume(buffer, Volume);
|
||||||
if (vol != 1)
|
|
||||||
AdjustVolume(buffer, vol);
|
|
||||||
await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
|
await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
|
||||||
unchecked { _bytesSent += bytesRead; }
|
unchecked { _bytesSent += bytesRead; }
|
||||||
|
|
||||||
@ -165,55 +192,113 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
//if repeating current song, just ignore other settings,
|
try
|
||||||
// and play this song again (don't change the index)
|
|
||||||
// ignore rcs if song is manually skipped
|
|
||||||
if (!RepeatCurrentSong || manualSkip)
|
|
||||||
{
|
{
|
||||||
if (Shuffle)
|
//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
|
||||||
|
|
||||||
|
int queueCount;
|
||||||
|
lock (locker)
|
||||||
|
queueCount = Queue.Count;
|
||||||
|
|
||||||
|
if (!manualIndex && (!RepeatCurrentSong || manualSkip))
|
||||||
{
|
{
|
||||||
_log.Info("Random song");
|
if (Shuffle)
|
||||||
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 (Queue.Count - 1 == data.Index && Autoplay && data.Song?.ProviderType == Database.Models.MusicType.YouTube)
|
|
||||||
{
|
{
|
||||||
try
|
_log.Info("Random song");
|
||||||
{
|
Queue.Random(); //if shuffle is set, set current song index to a random number
|
||||||
_log.Info("Loading related song");
|
|
||||||
await _musicService.TryQueueRelatedSongAsync(data.Song.Query, OutputTextChannel, VoiceChannel);
|
|
||||||
Queue.Next();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_log.Info("Loading related song failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Queue.Count - 1 == data.Index && !RepeatPlaylist && !manualSkip)
|
|
||||||
{
|
|
||||||
_log.Info("Stopping because repeatplaylist is disabled");
|
|
||||||
Stop();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_log.Info("Next song");
|
//if last song, and autoplay is enabled, and if it's a youtube song
|
||||||
Queue.Next();
|
// 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
|
do
|
||||||
{
|
{
|
||||||
await Task.Delay(500);
|
await Task.Delay(500);
|
||||||
}
|
}
|
||||||
while (Stopped && !Exited);
|
while ((Queue.Count == 0 || Stopped) && !Exited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, SongCancelSource.Token);
|
}, SongCancelSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetIndex(int index)
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
lock (locker)
|
||||||
|
{
|
||||||
|
Queue.CurrentIndex = index;
|
||||||
|
manualIndex = true;
|
||||||
|
CancelCurrentSong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<IAudioClient> GetAudioClient(bool reconnect = false)
|
private async Task<IAudioClient> GetAudioClient(bool reconnect = false)
|
||||||
{
|
{
|
||||||
if (_audioClient == null ||
|
if (_audioClient == null ||
|
||||||
@ -240,17 +325,24 @@ namespace NadekoBot.Services.Music
|
|||||||
return _audioClient;
|
return _audioClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (bool Success, int Index) Enqueue(SongInfo song)
|
public int Enqueue(SongInfo song)
|
||||||
{
|
{
|
||||||
_log.Info("Adding song");
|
_log.Info("Adding song");
|
||||||
Queue.Add(song);
|
lock (locker)
|
||||||
return (true, Queue.Count);
|
{
|
||||||
|
if (Exited)
|
||||||
|
return -1;
|
||||||
|
Queue.Add(song);
|
||||||
|
return Queue.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Next(int skipCount = 1)
|
public void Next(int skipCount = 1)
|
||||||
{
|
{
|
||||||
lock (locker)
|
lock (locker)
|
||||||
{
|
{
|
||||||
|
if (Exited)
|
||||||
|
return;
|
||||||
manualSkip = true;
|
manualSkip = true;
|
||||||
// if player is stopped, and user uses .n, it should play current song.
|
// if player is stopped, and user uses .n, it should play current song.
|
||||||
// It's a bit weird, but that's the least annoying solution
|
// It's a bit weird, but that's the least annoying solution
|
||||||
@ -277,10 +369,13 @@ namespace NadekoBot.Services.Music
|
|||||||
|
|
||||||
private void Unpause()
|
private void Unpause()
|
||||||
{
|
{
|
||||||
if (pauseTaskSource != null)
|
lock (locker)
|
||||||
{
|
{
|
||||||
pauseTaskSource.TrySetResult(true);
|
if (pauseTaskSource != null)
|
||||||
pauseTaskSource = null;
|
{
|
||||||
|
pauseTaskSource.TrySetResult(true);
|
||||||
|
pauseTaskSource = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +397,10 @@ namespace NadekoBot.Services.Music
|
|||||||
{
|
{
|
||||||
if (volume < 0 || volume > 100)
|
if (volume < 0 || volume > 100)
|
||||||
throw new ArgumentOutOfRangeException(nameof(volume));
|
throw new ArgumentOutOfRangeException(nameof(volume));
|
||||||
Volume = ((float)volume) / 100;
|
lock (locker)
|
||||||
|
{
|
||||||
|
Volume = ((float)volume) / 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SongInfo RemoveAt(int index)
|
public SongInfo RemoveAt(int index)
|
||||||
@ -335,7 +433,10 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
|
|
||||||
public (int CurrentIndex, SongInfo[] Songs) QueueArray()
|
public (int CurrentIndex, SongInfo[] Songs) QueueArray()
|
||||||
=> Queue.ToArray();
|
{
|
||||||
|
lock (locker)
|
||||||
|
return Queue.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
//aidiakapi ftw
|
//aidiakapi ftw
|
||||||
public static unsafe byte[] AdjustVolume(byte[] audioSamples, float volume)
|
public static unsafe byte[] AdjustVolume(byte[] audioSamples, float volume)
|
||||||
@ -410,366 +511,18 @@ namespace NadekoBot.Services.Music
|
|||||||
|
|
||||||
public void SetVoiceChannel(IVoiceChannel vch)
|
public void SetVoiceChannel(IVoiceChannel vch)
|
||||||
{
|
{
|
||||||
VoiceChannel = vch;
|
lock (locker)
|
||||||
newVoiceChannel = true;
|
{
|
||||||
Next();
|
if (Exited)
|
||||||
|
return;
|
||||||
|
VoiceChannel = vch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//private IAudioClient AudioClient { get; set; }
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Player will prioritize different queuer name
|
|
||||||
///// over the song position in the playlist
|
|
||||||
///// </summary>
|
|
||||||
//public bool FairPlay { get; set; } = false;
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Song will stop playing after this amount of time.
|
|
||||||
///// To prevent people queueing radio or looped songs
|
|
||||||
///// while other people want to listen to other songs too.
|
|
||||||
///// </summary>
|
|
||||||
//public uint MaxPlaytimeSeconds { get; set; } = 0;
|
|
||||||
|
|
||||||
|
|
||||||
//// this should be written better
|
//// this should be written better
|
||||||
//public TimeSpan TotalPlaytime =>
|
//public TimeSpan TotalPlaytime =>
|
||||||
// _playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ?
|
// _playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ?
|
||||||
// TimeSpan.MaxValue :
|
// TimeSpan.MaxValue :
|
||||||
// new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks));
|
// new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks));
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Users who recently got their music wish
|
|
||||||
///// </summary>
|
|
||||||
//private ConcurrentHashSet<string> RecentlyPlayedUsers { get; } = new ConcurrentHashSet<string>();
|
|
||||||
|
|
||||||
//private readonly List<Song> _playlist = new List<Song>();
|
|
||||||
//private readonly Logger _log;
|
|
||||||
//private readonly IGoogleApiService _google;
|
|
||||||
|
|
||||||
//public IReadOnlyCollection<Song> Playlist => _playlist;
|
|
||||||
|
|
||||||
//public Song CurrentSong { get; private set; }
|
|
||||||
//public CancellationTokenSource SongCancelSource { get; private set; }
|
|
||||||
//private CancellationToken CancelToken { get; set; }
|
|
||||||
|
|
||||||
//public bool Paused { get; set; }
|
|
||||||
|
|
||||||
//public float Volume { get; private set; }
|
|
||||||
|
|
||||||
//public event Action<MusicPlayer, Song> OnCompleted = delegate { };
|
|
||||||
//public event Action<MusicPlayer, Song> OnStarted = delegate { };
|
|
||||||
//public event Action<bool> OnPauseChanged = delegate { };
|
|
||||||
|
|
||||||
//public IVoiceChannel PlaybackVoiceChannel { get; private set; }
|
|
||||||
//public ITextChannel OutputTextChannel { get; set; }
|
|
||||||
|
|
||||||
//private bool Destroyed { get; set; }
|
|
||||||
//public bool RepeatSong { get; private set; }
|
|
||||||
//public bool RepeatPlaylist { get; private set; }
|
|
||||||
//public bool Autoplay { get; set; }
|
|
||||||
//public uint MaxQueueSize { get; set; } = 0;
|
|
||||||
|
|
||||||
//private ConcurrentQueue<Action> ActionQueue { get; } = new ConcurrentQueue<Action>();
|
|
||||||
|
|
||||||
//public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
|
|
||||||
|
|
||||||
//public event Action<Song, int> SongRemoved = delegate { };
|
|
||||||
|
|
||||||
//public MusicPlayer(IVoiceChannel startingVoiceChannel, ITextChannel outputChannel, float? defaultVolume, IGoogleApiService google)
|
|
||||||
//{
|
|
||||||
// _log = LogManager.GetCurrentClassLogger();
|
|
||||||
// _google = google;
|
|
||||||
|
|
||||||
// OutputTextChannel = outputChannel;
|
|
||||||
// Volume = defaultVolume ?? 1.0f;
|
|
||||||
|
|
||||||
// PlaybackVoiceChannel = startingVoiceChannel ?? throw new ArgumentNullException(nameof(startingVoiceChannel));
|
|
||||||
// SongCancelSource = new CancellationTokenSource();
|
|
||||||
// CancelToken = SongCancelSource.Token;
|
|
||||||
|
|
||||||
// Task.Run(async () =>
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// while (!Destroyed)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// if (ActionQueue.TryDequeue(out Action action))
|
|
||||||
// {
|
|
||||||
// action();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// finally
|
|
||||||
// {
|
|
||||||
// await Task.Delay(100).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// _log.Warn("Action queue crashed");
|
|
||||||
// _log.Warn(ex);
|
|
||||||
// }
|
|
||||||
// }).ConfigureAwait(false);
|
|
||||||
|
|
||||||
// var t = new Thread(async () =>
|
|
||||||
// {
|
|
||||||
// while (!Destroyed)
|
|
||||||
// {
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// CurrentSong = GetNextSong();
|
|
||||||
|
|
||||||
// if (CurrentSong == null)
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// while (AudioClient?.ConnectionState == ConnectionState.Disconnecting ||
|
|
||||||
// AudioClient?.ConnectionState == ConnectionState.Connecting)
|
|
||||||
// {
|
|
||||||
// _log.Info("Waiting for Audio client");
|
|
||||||
// await Task.Delay(200).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (AudioClient == null || AudioClient.ConnectionState == ConnectionState.Disconnected)
|
|
||||||
// AudioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
// var index = _playlist.IndexOf(CurrentSong);
|
|
||||||
// if (index != -1)
|
|
||||||
// RemoveSongAt(index, true);
|
|
||||||
|
|
||||||
// OnStarted(this, CurrentSong);
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// await CurrentSong.Play(AudioClient, CancelToken);
|
|
||||||
// }
|
|
||||||
// catch (OperationCanceledException)
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
// finally
|
|
||||||
// {
|
|
||||||
// OnCompleted(this, CurrentSong);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// if (RepeatPlaylist & !RepeatSong)
|
|
||||||
// AddSong(CurrentSong, CurrentSong.QueuerName);
|
|
||||||
|
|
||||||
// if (RepeatSong)
|
|
||||||
// AddSong(CurrentSong, 0);
|
|
||||||
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// _log.Warn("Music thread almost crashed.");
|
|
||||||
// _log.Warn(ex);
|
|
||||||
// await Task.Delay(3000).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// finally
|
|
||||||
// {
|
|
||||||
// if (!CancelToken.IsCancellationRequested)
|
|
||||||
// {
|
|
||||||
// SongCancelSource.Cancel();
|
|
||||||
// }
|
|
||||||
// SongCancelSource = new CancellationTokenSource();
|
|
||||||
// CancelToken = SongCancelSource.Token;
|
|
||||||
// CurrentSong = null;
|
|
||||||
// await Task.Delay(300).ConfigureAwait(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// t.Start();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Next()
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// Paused = false;
|
|
||||||
// SongCancelSource.Cancel();
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Stop()
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// RepeatPlaylist = false;
|
|
||||||
// RepeatSong = false;
|
|
||||||
// Autoplay = false;
|
|
||||||
// _playlist.Clear();
|
|
||||||
// if (!SongCancelSource.IsCancellationRequested)
|
|
||||||
// SongCancelSource.Cancel();
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void TogglePause() => OnPauseChanged(Paused = !Paused);
|
|
||||||
|
|
||||||
//public int SetVolume(int volume)
|
|
||||||
//{
|
|
||||||
// if (volume < 0)
|
|
||||||
// volume = 0;
|
|
||||||
// if (volume > 100)
|
|
||||||
// volume = 100;
|
|
||||||
|
|
||||||
// Volume = volume / 100.0f;
|
|
||||||
// return volume;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private Song GetNextSong()
|
|
||||||
//{
|
|
||||||
// if (!FairPlay)
|
|
||||||
// {
|
|
||||||
// return _playlist.FirstOrDefault();
|
|
||||||
// }
|
|
||||||
// var song = _playlist.FirstOrDefault(c => !RecentlyPlayedUsers.Contains(c.QueuerName))
|
|
||||||
// ?? _playlist.FirstOrDefault();
|
|
||||||
|
|
||||||
// if (song == null)
|
|
||||||
// return null;
|
|
||||||
|
|
||||||
// if (RecentlyPlayedUsers.Contains(song.QueuerName))
|
|
||||||
// {
|
|
||||||
// RecentlyPlayedUsers.Clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// RecentlyPlayedUsers.Add(song.QueuerName);
|
|
||||||
// return song;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Shuffle()
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// var oldPlaylist = _playlist.ToArray();
|
|
||||||
// _playlist.Clear();
|
|
||||||
// _playlist.AddRange(oldPlaylist.Shuffle());
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void AddSong(Song s, string username)
|
|
||||||
//{
|
|
||||||
// if (s == null)
|
|
||||||
// throw new ArgumentNullException(nameof(s));
|
|
||||||
// ThrowIfQueueFull();
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// s.MusicPlayer = this;
|
|
||||||
// s.QueuerName = username.TrimTo(10);
|
|
||||||
// _playlist.Add(s);
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void AddSong(Song s, int index)
|
|
||||||
//{
|
|
||||||
// if (s == null)
|
|
||||||
// throw new ArgumentNullException(nameof(s));
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// _playlist.Insert(index, s);
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void RemoveSong(Song s)
|
|
||||||
//{
|
|
||||||
// if (s == null)
|
|
||||||
// throw new ArgumentNullException(nameof(s));
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// _playlist.Remove(s);
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void RemoveSongAt(int index, bool silent = false)
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// if (index < 0 || index >= _playlist.Count)
|
|
||||||
// return;
|
|
||||||
// var song = _playlist.ElementAtOrDefault(index);
|
|
||||||
// if (_playlist.Remove(song) && !silent)
|
|
||||||
// {
|
|
||||||
// SongRemoved(song, index);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void ClearQueue()
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(() =>
|
|
||||||
// {
|
|
||||||
// _playlist.Clear();
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public async Task UpdateSongDurationsAsync()
|
|
||||||
//{
|
|
||||||
// var curSong = CurrentSong;
|
|
||||||
// var toUpdate = _playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal &&
|
|
||||||
// s.TotalTime == TimeSpan.Zero)
|
|
||||||
// .ToArray();
|
|
||||||
// if (curSong != null)
|
|
||||||
// {
|
|
||||||
// Array.Resize(ref toUpdate, toUpdate.Length + 1);
|
|
||||||
// toUpdate[toUpdate.Length - 1] = curSong;
|
|
||||||
// }
|
|
||||||
// var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3))
|
|
||||||
// .Distinct();
|
|
||||||
|
|
||||||
// var durations = await _google.GetVideoDurationsAsync(ids);
|
|
||||||
|
|
||||||
// toUpdate.ForEach(s =>
|
|
||||||
// {
|
|
||||||
// foreach (var kvp in durations)
|
|
||||||
// {
|
|
||||||
// if (s.SongInfo.Query.EndsWith(kvp.Key))
|
|
||||||
// {
|
|
||||||
// s.TotalTime = kvp.Value;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Destroy()
|
|
||||||
//{
|
|
||||||
// ActionQueue.Enqueue(async () =>
|
|
||||||
// {
|
|
||||||
// RepeatPlaylist = false;
|
|
||||||
// RepeatSong = false;
|
|
||||||
// Autoplay = false;
|
|
||||||
// Destroyed = true;
|
|
||||||
// _playlist.Clear();
|
|
||||||
|
|
||||||
// try { await AudioClient.StopAsync(); } catch { }
|
|
||||||
// if (!SongCancelSource.IsCancellationRequested)
|
|
||||||
// SongCancelSource.Cancel();
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
|
|
||||||
////public async Task MoveToVoiceChannel(IVoiceChannel voiceChannel)
|
|
||||||
////{
|
|
||||||
//// if (audioClient?.ConnectionState != ConnectionState.Connected)
|
|
||||||
//// throw new InvalidOperationException("Can't move while bot is not connected to voice channel.");
|
|
||||||
//// PlaybackVoiceChannel = voiceChannel;
|
|
||||||
//// audioClient = await voiceChannel.ConnectAsync().ConfigureAwait(false);
|
|
||||||
////}
|
|
||||||
|
|
||||||
//public bool ToggleRepeatSong() => RepeatSong = !RepeatSong;
|
|
||||||
|
|
||||||
//public bool ToggleRepeatPlaylist() => RepeatPlaylist = !RepeatPlaylist;
|
|
||||||
|
|
||||||
//public bool ToggleAutoplay() => Autoplay = !Autoplay;
|
|
||||||
|
|
||||||
//public void ThrowIfQueueFull()
|
|
||||||
//{
|
|
||||||
// if (MaxQueueSize == 0)
|
|
||||||
// return;
|
|
||||||
// if (_playlist.Count >= MaxQueueSize)
|
|
||||||
// throw new PlaylistFullException();
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ namespace NadekoBot.Services.Music
|
|||||||
{
|
{
|
||||||
private LinkedList<SongInfo> Songs { get; } = new LinkedList<SongInfo>();
|
private LinkedList<SongInfo> Songs { get; } = new LinkedList<SongInfo>();
|
||||||
private int _currentIndex = 0;
|
private int _currentIndex = 0;
|
||||||
private int CurrentIndex
|
public int CurrentIndex
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -124,7 +124,7 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public (int, SongInfo[]) ToArray()
|
public (int CurrentIndex, SongInfo[] Songs) ToArray()
|
||||||
{
|
{
|
||||||
lock (locker)
|
lock (locker)
|
||||||
{
|
{
|
||||||
|
@ -131,9 +131,9 @@ namespace NadekoBot.Services.Music
|
|||||||
playingMessage?.DeleteAfter(0);
|
playingMessage?.DeleteAfter(0);
|
||||||
|
|
||||||
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||||
.WithAuthor(eab => eab.WithName(GetText("playing_song")).WithMusicIcon())
|
.WithAuthor(eab => eab.WithName(GetText("playing_song", song.Index + 1)).WithMusicIcon())
|
||||||
.WithDescription(song.PrettyName)
|
.WithDescription(song.Song.PrettyName)
|
||||||
.WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + song.PrettyInfo)))
|
.WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + song.Song.PrettyInfo)))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -288,11 +288,15 @@ namespace NadekoBot.Services.Music
|
|||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (video == null) // do something with this error
|
if (video == null) // do something with this error
|
||||||
|
{
|
||||||
_log.Info("Could not load any video elements based on the query.");
|
_log.Info("Could not load any video elements based on the query.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
//var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
//var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
||||||
//int gotoTime = 0;
|
//int gotoTime = 0;
|
||||||
//if (m.Captures.Count > 0)
|
//if (m.Captures.Count > 0)
|
||||||
// int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
// int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
||||||
|
|
||||||
var song = new SongInfo
|
var song = new SongInfo
|
||||||
{
|
{
|
||||||
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
||||||
|
@ -407,7 +407,7 @@
|
|||||||
"music_autoplaying": "Auto-playing.",
|
"music_autoplaying": "Auto-playing.",
|
||||||
"music_defvol_set": "Default volume set to {0}%",
|
"music_defvol_set": "Default volume set to {0}%",
|
||||||
"music_dir_queue_complete": "Directory queue complete.",
|
"music_dir_queue_complete": "Directory queue complete.",
|
||||||
"music_fairplay": "fairplay",
|
"music_fairplay": "Fairplay",
|
||||||
"music_finished_song": "Finished song",
|
"music_finished_song": "Finished song",
|
||||||
"music_fp_disabled": "Fair play disabled.",
|
"music_fp_disabled": "Fair play disabled.",
|
||||||
"music_fp_enabled": "Fair play enabled.",
|
"music_fp_enabled": "Fair play enabled.",
|
||||||
@ -425,7 +425,7 @@
|
|||||||
"music_no_search_results": "No search results.",
|
"music_no_search_results": "No search results.",
|
||||||
"music_paused": "Music playback paused.",
|
"music_paused": "Music playback paused.",
|
||||||
"music_player_queue": "Player queue - Page {0}/{1}",
|
"music_player_queue": "Player queue - Page {0}/{1}",
|
||||||
"music_playing_song": "Playing song",
|
"music_playing_song": "Playing song #{0}",
|
||||||
"music_playlists": "`#{0}` - **{1}** by *{2}* ({3} songs)",
|
"music_playlists": "`#{0}` - **{1}** by *{2}* ({3} songs)",
|
||||||
"music_playlists_page": "Page {0} of saved playlists",
|
"music_playlists_page": "Page {0} of saved playlists",
|
||||||
"music_playlist_deleted": "Playlist deleted.",
|
"music_playlist_deleted": "Playlist deleted.",
|
||||||
|
Loading…
Reference in New Issue
Block a user