mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Applied codestyle to all .cs files
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using System.Reflection;
|
||||
using Ayu.Discord.Voice;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services;
|
||||
|
||||
@@ -8,10 +8,10 @@ public sealed class AyuVoiceStateService : INService
|
||||
{
|
||||
// public delegate Task VoiceProxyUpdatedDelegate(ulong guildId, IVoiceProxy proxy);
|
||||
// public event VoiceProxyUpdatedDelegate OnVoiceProxyUpdate = delegate { return Task.CompletedTask; };
|
||||
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, IVoiceProxy> _voiceProxies = new();
|
||||
private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _voiceGatewayLocks = new();
|
||||
|
||||
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly MethodInfo _sendVoiceStateUpdateMethodInfo;
|
||||
private readonly object _dnetApiClient;
|
||||
@@ -23,13 +23,17 @@ public sealed class AyuVoiceStateService : INService
|
||||
_currentUserId = _client.CurrentUser.Id;
|
||||
|
||||
var prop = _client.GetType()
|
||||
.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.First(x => x.Name == "ApiClient" && x.PropertyType.Name == "DiscordSocketApiClient");
|
||||
.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.First(x => x.Name == "ApiClient" && x.PropertyType.Name == "DiscordSocketApiClient");
|
||||
_dnetApiClient = prop.GetValue(_client, null);
|
||||
_sendVoiceStateUpdateMethodInfo = _dnetApiClient.GetType()
|
||||
.GetMethod("SendVoiceStateUpdateAsync",
|
||||
types: new[] { typeof(ulong), typeof(ulong?), typeof(bool), typeof(bool), typeof(RequestOptions) });
|
||||
|
||||
.GetMethod("SendVoiceStateUpdateAsync",
|
||||
new[]
|
||||
{
|
||||
typeof(ulong), typeof(ulong?), typeof(bool),
|
||||
typeof(bool), typeof(RequestOptions)
|
||||
});
|
||||
|
||||
_client.LeftGuild += ClientOnLeftGuild;
|
||||
}
|
||||
|
||||
@@ -44,29 +48,32 @@ public sealed class AyuVoiceStateService : INService
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task InvokeSendVoiceStateUpdateAsync(ulong guildId, ulong? channelId = null, bool isDeafened = false, bool isMuted = false)
|
||||
private Task InvokeSendVoiceStateUpdateAsync(
|
||||
ulong guildId,
|
||||
ulong? channelId = null,
|
||||
bool isDeafened = false,
|
||||
bool isMuted = false)
|
||||
// return _voiceStateUpdate(guildId, channelId, isDeafened, isMuted);
|
||||
=> (Task) _sendVoiceStateUpdateMethodInfo.Invoke(_dnetApiClient, new object[] {guildId, channelId, isMuted, isDeafened, null});
|
||||
=> (Task)_sendVoiceStateUpdateMethodInfo.Invoke(_dnetApiClient,
|
||||
new object[] { guildId, channelId, isMuted, isDeafened, null });
|
||||
|
||||
private Task SendLeaveVoiceChannelInternalAsync(ulong guildId)
|
||||
=> InvokeSendVoiceStateUpdateAsync(guildId);
|
||||
|
||||
private Task SendJoinVoiceChannelInternalAsync(ulong guildId, ulong channelId)
|
||||
=> InvokeSendVoiceStateUpdateAsync(guildId, channelId);
|
||||
|
||||
private SemaphoreSlim GetVoiceGatewayLock(ulong guildId) => _voiceGatewayLocks.GetOrAdd(guildId, new SemaphoreSlim(1, 1));
|
||||
|
||||
|
||||
private SemaphoreSlim GetVoiceGatewayLock(ulong guildId)
|
||||
=> _voiceGatewayLocks.GetOrAdd(guildId, new SemaphoreSlim(1, 1));
|
||||
|
||||
private async Task LeaveVoiceChannelInternalAsync(ulong guildId)
|
||||
{
|
||||
var complete = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
Task OnUserVoiceStateUpdated(SocketUser user, SocketVoiceState oldState, SocketVoiceState newState)
|
||||
{
|
||||
if (user is SocketGuildUser guildUser
|
||||
&& guildUser.Guild.Id == guildId
|
||||
&& newState.VoiceChannel?.Id is null)
|
||||
{
|
||||
if (user is SocketGuildUser guildUser && guildUser.Guild.Id == guildId && newState.VoiceChannel?.Id is null)
|
||||
complete.TrySetResult(true);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -89,6 +96,7 @@ public sealed class AyuVoiceStateService : INService
|
||||
_client.UserVoiceStateUpdated -= OnUserVoiceStateUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LeaveVoiceChannel(ulong guildId)
|
||||
{
|
||||
var gwLock = GetVoiceGatewayLock(guildId);
|
||||
@@ -105,8 +113,10 @@ public sealed class AyuVoiceStateService : INService
|
||||
|
||||
private async Task<IVoiceProxy> InternalConnectToVcAsync(ulong guildId, ulong channelId)
|
||||
{
|
||||
var voiceStateUpdatedSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var voiceServerUpdatedSource = new TaskCompletionSource<SocketVoiceServer>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var voiceStateUpdatedSource =
|
||||
new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var voiceServerUpdatedSource =
|
||||
new TaskCompletionSource<SocketVoiceServer>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
Task OnUserVoiceStateUpdated(SocketUser user, SocketVoiceState oldState, SocketVoiceState newState)
|
||||
{
|
||||
@@ -123,14 +133,11 @@ public sealed class AyuVoiceStateService : INService
|
||||
|
||||
Task OnVoiceServerUpdated(SocketVoiceServer data)
|
||||
{
|
||||
if (data.Guild.Id == guildId)
|
||||
{
|
||||
voiceServerUpdatedSource.TrySetResult(data);
|
||||
}
|
||||
if (data.Guild.Id == guildId) voiceServerUpdatedSource.TrySetResult(data);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
_client.VoiceServerUpdated += OnVoiceServerUpdated;
|
||||
@@ -149,10 +156,8 @@ public sealed class AyuVoiceStateService : INService
|
||||
// wait for both to end (max 1s) and check if either of them is a delay task
|
||||
var results = await Task.WhenAll(maybeUpdateTask, maybeServerTask);
|
||||
if (results[0] == delayTask || results[1] == delayTask)
|
||||
{
|
||||
// if either is delay, return null - connection unsuccessful
|
||||
return null;
|
||||
}
|
||||
|
||||
// if both are succesful, that means we can safely get
|
||||
// the values from completion sources
|
||||
@@ -164,26 +169,20 @@ public sealed class AyuVoiceStateService : INService
|
||||
return null;
|
||||
|
||||
var voiceServerData = await voiceServerUpdatedSource.Task;
|
||||
|
||||
VoiceGateway CreateVoiceGatewayLocal() =>
|
||||
new(
|
||||
guildId,
|
||||
_currentUserId,
|
||||
session,
|
||||
voiceServerData.Token,
|
||||
voiceServerData.Endpoint
|
||||
);
|
||||
|
||||
var current = _voiceProxies.AddOrUpdate(
|
||||
guildId,
|
||||
VoiceGateway CreateVoiceGatewayLocal()
|
||||
{
|
||||
return new(guildId, _currentUserId, session, voiceServerData.Token, voiceServerData.Endpoint);
|
||||
}
|
||||
|
||||
var current = _voiceProxies.AddOrUpdate(guildId,
|
||||
gid => new VoiceProxy(CreateVoiceGatewayLocal()),
|
||||
(gid, currentProxy) =>
|
||||
{
|
||||
_ = currentProxy.StopGateway();
|
||||
currentProxy.SetGateway(CreateVoiceGatewayLocal());
|
||||
return currentProxy;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
_ = current.StartGateway(); // don't await, this blocks until gateway is closed
|
||||
return current;
|
||||
@@ -212,4 +211,4 @@ public sealed class AyuVoiceStateService : INService
|
||||
|
||||
public bool TryGetProxy(ulong guildId, out IVoiceProxy proxy)
|
||||
=> _voiceProxies.TryGetValue(guildId, out proxy);
|
||||
}
|
||||
}
|
@@ -1,23 +1,23 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services;
|
||||
|
||||
public interface IMusicService : IPlaceholderProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Leave voice channel in the specified guild if it's connected to one
|
||||
/// Leave voice channel in the specified guild if it's connected to one
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild</param>
|
||||
public Task LeaveVoiceChannelAsync(ulong guildId);
|
||||
|
||||
/// <summary>
|
||||
/// Joins the voice channel with the specified id
|
||||
/// Joins the voice channel with the specified id
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the guild where the voice channel is</param>
|
||||
/// <param name="voiceChannelId">Id of the voice channel</param>
|
||||
public Task JoinVoiceChannelAsync(ulong guildId, ulong voiceChannelId);
|
||||
|
||||
|
||||
Task<IMusicPlayer?> GetOrCreateMusicPlayerAsync(ITextChannel contextChannel);
|
||||
bool TryGetMusicPlayer(ulong guildId, [MaybeNullWhen(false)] out IMusicPlayer musicPlayer);
|
||||
Task<int> EnqueueYoutubePlaylistAsync(IMusicPlayer mp, string playlistId, string queuer);
|
||||
@@ -32,4 +32,4 @@ public interface IMusicService : IPlaceholderProvider
|
||||
Task<bool> ToggleAutoDisconnectAsync(ulong guildId);
|
||||
Task<QualityPreset> GetMusicQualityAsync(ulong guildId);
|
||||
Task SetMusicQualityAsync(ulong guildId, QualityPreset preset);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Db;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services;
|
||||
|
||||
@@ -22,9 +22,17 @@ public sealed class MusicService : IMusicService
|
||||
private readonly ConcurrentDictionary<ulong, (ITextChannel Default, ITextChannel? Override)> _outputChannels;
|
||||
private readonly ConcurrentDictionary<ulong, MusicPlayerSettings> _settings;
|
||||
|
||||
public MusicService(AyuVoiceStateService voiceStateService, ITrackResolveProvider trackResolveProvider,
|
||||
DbService db, IYoutubeResolver ytResolver, ILocalTrackResolver localResolver, ISoundcloudResolver scResolver,
|
||||
DiscordSocketClient client, IBotStrings strings, IGoogleApiService googleApiService, YtLoader ytLoader,
|
||||
public MusicService(
|
||||
AyuVoiceStateService voiceStateService,
|
||||
ITrackResolveProvider trackResolveProvider,
|
||||
DbService db,
|
||||
IYoutubeResolver ytResolver,
|
||||
ILocalTrackResolver localResolver,
|
||||
ISoundcloudResolver scResolver,
|
||||
DiscordSocketClient client,
|
||||
IBotStrings strings,
|
||||
IGoogleApiService googleApiService,
|
||||
YtLoader ytLoader,
|
||||
IEmbedBuilderService eb)
|
||||
{
|
||||
_voiceStateService = voiceStateService;
|
||||
@@ -42,10 +50,10 @@ public sealed class MusicService : IMusicService
|
||||
_players = new();
|
||||
_outputChannels = new ConcurrentDictionary<ulong, (ITextChannel, ITextChannel?)>();
|
||||
_settings = new();
|
||||
|
||||
|
||||
_client.LeftGuild += ClientOnLeftGuild;
|
||||
}
|
||||
|
||||
|
||||
private void DisposeMusicPlayer(IMusicPlayer musicPlayer)
|
||||
{
|
||||
musicPlayer.Kill();
|
||||
@@ -55,10 +63,7 @@ public sealed class MusicService : IMusicService
|
||||
private void RemoveMusicPlayer(ulong guildId)
|
||||
{
|
||||
_outputChannels.TryRemove(guildId, out _);
|
||||
if (_players.TryRemove(guildId, out var mp))
|
||||
{
|
||||
DisposeMusicPlayer(mp);
|
||||
}
|
||||
if (_players.TryRemove(guildId, out var mp)) DisposeMusicPlayer(mp);
|
||||
}
|
||||
|
||||
private Task ClientOnLeftGuild(SocketGuild guild)
|
||||
@@ -73,7 +78,7 @@ public sealed class MusicService : IMusicService
|
||||
await _voiceStateService.LeaveVoiceChannel(guildId);
|
||||
}
|
||||
|
||||
public Task JoinVoiceChannelAsync(ulong guildId, ulong voiceChannelId)
|
||||
public Task JoinVoiceChannelAsync(ulong guildId, ulong voiceChannelId)
|
||||
=> _voiceStateService.JoinVoiceChannel(guildId, voiceChannelId);
|
||||
|
||||
public async Task<IMusicPlayer?> GetOrCreateMusicPlayerAsync(ITextChannel contextChannel)
|
||||
@@ -81,7 +86,7 @@ public sealed class MusicService : IMusicService
|
||||
var newPLayer = await CreateMusicPlayerInternalAsync(contextChannel.GuildId, contextChannel);
|
||||
if (newPLayer is null)
|
||||
return null;
|
||||
|
||||
|
||||
return _players.GetOrAdd(contextChannel.GuildId, newPLayer);
|
||||
}
|
||||
|
||||
@@ -109,7 +114,7 @@ public sealed class MusicService : IMusicService
|
||||
{
|
||||
if (mp.IsKilled)
|
||||
break;
|
||||
|
||||
|
||||
mp.EnqueueTrack(track, queuer);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +126,7 @@ public sealed class MusicService : IMusicService
|
||||
{
|
||||
if (mp.IsKilled)
|
||||
break;
|
||||
|
||||
|
||||
mp.EnqueueTrack(track, queuer);
|
||||
++i;
|
||||
}
|
||||
@@ -134,10 +139,7 @@ public sealed class MusicService : IMusicService
|
||||
var queue = new MusicQueue();
|
||||
var resolver = _trackResolveProvider;
|
||||
|
||||
if (!_voiceStateService.TryGetProxy(guildId, out var proxy))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!_voiceStateService.TryGetProxy(guildId, out var proxy)) return null;
|
||||
|
||||
var settings = await GetSettingsInternalAsync(guildId);
|
||||
|
||||
@@ -147,30 +149,19 @@ public sealed class MusicService : IMusicService
|
||||
overrideChannel = _client.GetGuild(guildId)?.GetTextChannel(channelId);
|
||||
|
||||
if (overrideChannel is null)
|
||||
{
|
||||
Log.Warning("Saved music output channel doesn't exist, falling back to current channel");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_outputChannels[guildId] = (defaultChannel, overrideChannel);
|
||||
|
||||
var mp = new MusicPlayer(
|
||||
queue,
|
||||
resolver,
|
||||
proxy,
|
||||
settings.QualityPreset
|
||||
);
|
||||
|
||||
var mp = new MusicPlayer(queue, resolver, proxy, settings.QualityPreset);
|
||||
|
||||
mp.SetRepeat(settings.PlayerRepeat);
|
||||
|
||||
if (settings.Volume is >= 0 and <= 100)
|
||||
{
|
||||
mp.SetVolume(settings.Volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Saved Volume is outside of valid range >= 0 && <=100 ({Volume})", settings.Volume);
|
||||
}
|
||||
|
||||
mp.OnCompleted += OnTrackCompleted(guildId);
|
||||
mp.OnStarted += OnTrackStarted(guildId);
|
||||
@@ -197,10 +188,10 @@ public sealed class MusicService : IMusicService
|
||||
{
|
||||
_ = lastFinishedMessage?.DeleteAsync();
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.finished_song), Music.MusicIconUrl)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter(trackInfo.PrettyTotalTime());
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.finished_song), Music.MusicIconUrl)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter(trackInfo.PrettyTotalTime());
|
||||
|
||||
lastFinishedMessage = await SendToOutputAsync(guildId, embed);
|
||||
};
|
||||
@@ -212,10 +203,11 @@ public sealed class MusicService : IMusicService
|
||||
return async (mp, trackInfo, index) =>
|
||||
{
|
||||
_ = lastPlayingMessage?.DeleteAsync();
|
||||
var embed = _eb.Create().WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.playing_song(index + 1)), Music.MusicIconUrl)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.playing_song(index + 1)), Music.MusicIconUrl)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
|
||||
|
||||
lastPlayingMessage = await SendToOutputAsync(guildId, embed);
|
||||
};
|
||||
@@ -225,12 +217,8 @@ public sealed class MusicService : IMusicService
|
||||
=> mp =>
|
||||
{
|
||||
if (_settings.TryGetValue(guildId, out var settings))
|
||||
{
|
||||
if (settings.AutoDisconnect)
|
||||
{
|
||||
return LeaveVoiceChannelAsync(guildId);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
@@ -238,19 +226,12 @@ public sealed class MusicService : IMusicService
|
||||
// this has to be done because dragging bot to another vc isn't supported yet
|
||||
public async Task<bool> PlayAsync(ulong guildId, ulong voiceChannelId)
|
||||
{
|
||||
if (!TryGetMusicPlayer(guildId, out var mp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!TryGetMusicPlayer(guildId, out var mp)) return false;
|
||||
|
||||
if (mp.IsStopped)
|
||||
{
|
||||
if (!_voiceStateService.TryGetProxy(guildId, out var proxy)
|
||||
if (!_voiceStateService.TryGetProxy(guildId, out var proxy)
|
||||
|| proxy.State == VoiceProxy.VoiceProxyState.Stopped)
|
||||
{
|
||||
await JoinVoiceChannelAsync(guildId, voiceChannelId);
|
||||
}
|
||||
}
|
||||
|
||||
mp.Next();
|
||||
return true;
|
||||
@@ -261,22 +242,19 @@ public sealed class MusicService : IMusicService
|
||||
var result = await _ytLoader.LoadResultsAsync(query);
|
||||
return result.Select(x => (x.Title, x.Url)).ToList();
|
||||
}
|
||||
|
||||
|
||||
private async Task<IList<(string Title, string Url)>> SearchGoogleApiVideosAsync(string query)
|
||||
{
|
||||
var result = await _googleApiService.GetVideoInfosByKeywordAsync(query, 5);
|
||||
return result.Select(x => (x.Name, x.Url)).ToList();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IList<(string Title, string Url)>> SearchVideosAsync(string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
IList<(string, string)> videos = await SearchYtLoaderVideosAsync(query);
|
||||
if (videos.Count > 0)
|
||||
{
|
||||
return videos;
|
||||
}
|
||||
if (videos.Count > 0) return videos;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -289,13 +267,14 @@ public sealed class MusicService : IMusicService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning("Failed getting video results with Google Api. " +
|
||||
"Probably google api key missing: {ErrorMessage}", ex.Message);
|
||||
Log.Warning("Failed getting video results with Google Api. "
|
||||
+ "Probably google api key missing: {ErrorMessage}",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
|
||||
return Array.Empty<(string, string)>();
|
||||
}
|
||||
|
||||
|
||||
private string GetText(ulong guildId, LocStr str)
|
||||
=> _strings.GetText(str, guildId);
|
||||
|
||||
@@ -304,11 +283,10 @@ public sealed class MusicService : IMusicService
|
||||
// random song that's playing
|
||||
yield return ("%music.playing%", () =>
|
||||
{
|
||||
var randomPlayingTrack = _players
|
||||
.Select(x => x.Value.GetCurrentTrack(out _))
|
||||
.Where(x => x is not null)
|
||||
.Shuffle()
|
||||
.FirstOrDefault();
|
||||
var randomPlayingTrack = _players.Select(x => x.Value.GetCurrentTrack(out _))
|
||||
.Where(x => x is not null)
|
||||
.Shuffle()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (randomPlayingTrack is null)
|
||||
return "-";
|
||||
@@ -319,17 +297,14 @@ public sealed class MusicService : IMusicService
|
||||
// number of servers currently listening to music
|
||||
yield return ("%music.servers%", () =>
|
||||
{
|
||||
var count = _players
|
||||
.Select(x => x.Value.GetCurrentTrack(out _))
|
||||
.Count(x => x is not null);
|
||||
var count = _players.Select(x => x.Value.GetCurrentTrack(out _)).Count(x => x is not null);
|
||||
|
||||
return count.ToString();
|
||||
});
|
||||
|
||||
|
||||
yield return ("%music.queued%", () =>
|
||||
{
|
||||
var count = _players
|
||||
.Sum(x => x.Value.GetQueuedTracks().Count);
|
||||
var count = _players.Sum(x => x.Value.GetQueuedTracks().Count);
|
||||
|
||||
return count.ToString();
|
||||
});
|
||||
@@ -348,7 +323,7 @@ public sealed class MusicService : IMusicService
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
private async Task ModifySettingsInternalAsync<TState>(
|
||||
ulong guildId,
|
||||
Action<MusicPlayerSettings, TState> action,
|
||||
@@ -360,7 +335,7 @@ public sealed class MusicService : IMusicService
|
||||
await uow.SaveChangesAsync();
|
||||
_settings[guildId] = ms;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> SetMusicChannelAsync(ulong guildId, ulong? channelId)
|
||||
{
|
||||
if (channelId is null)
|
||||
@@ -368,29 +343,31 @@ public sealed class MusicService : IMusicService
|
||||
await UnsetMusicChannelAsync(guildId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
var channel = _client.GetGuild(guildId)?.GetTextChannel(channelId.Value);
|
||||
if (channel is null)
|
||||
return false;
|
||||
|
||||
await ModifySettingsInternalAsync(guildId, (settings, chId) =>
|
||||
{
|
||||
settings.MusicChannelId = chId;
|
||||
}, channelId);
|
||||
await ModifySettingsInternalAsync(guildId,
|
||||
(settings, chId) =>
|
||||
{
|
||||
settings.MusicChannelId = chId;
|
||||
},
|
||||
channelId);
|
||||
|
||||
_outputChannels.AddOrUpdate(guildId, (channel, channel), (key, old) => (old.Default, channel));
|
||||
|
||||
_outputChannels.AddOrUpdate(guildId,
|
||||
(channel, channel),
|
||||
(key, old) => (old.Default, channel));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task UnsetMusicChannelAsync(ulong guildId)
|
||||
{
|
||||
await ModifySettingsInternalAsync(guildId, (settings, _) =>
|
||||
{
|
||||
settings.MusicChannelId = null;
|
||||
}, (ulong?)null);
|
||||
await ModifySettingsInternalAsync(guildId,
|
||||
(settings, _) =>
|
||||
{
|
||||
settings.MusicChannelId = null;
|
||||
},
|
||||
(ulong?)null);
|
||||
|
||||
if (_outputChannels.TryGetValue(guildId, out var old))
|
||||
_outputChannels[guildId] = (old.Default, null);
|
||||
@@ -398,10 +375,12 @@ public sealed class MusicService : IMusicService
|
||||
|
||||
public async Task SetRepeatAsync(ulong guildId, PlayerRepeatType repeatType)
|
||||
{
|
||||
await ModifySettingsInternalAsync(guildId, (settings, type) =>
|
||||
{
|
||||
settings.PlayerRepeat = type;
|
||||
}, repeatType);
|
||||
await ModifySettingsInternalAsync(guildId,
|
||||
(settings, type) =>
|
||||
{
|
||||
settings.PlayerRepeat = type;
|
||||
},
|
||||
repeatType);
|
||||
|
||||
if (TryGetMusicPlayer(guildId, out var mp))
|
||||
mp.SetRepeat(repeatType);
|
||||
@@ -411,12 +390,14 @@ public sealed class MusicService : IMusicService
|
||||
{
|
||||
if (value is < 0 or > 100)
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
|
||||
await ModifySettingsInternalAsync(guildId, (settings, newValue) =>
|
||||
{
|
||||
settings.Volume = newValue;
|
||||
}, value);
|
||||
|
||||
|
||||
await ModifySettingsInternalAsync(guildId,
|
||||
(settings, newValue) =>
|
||||
{
|
||||
settings.Volume = newValue;
|
||||
},
|
||||
value);
|
||||
|
||||
if (TryGetMusicPlayer(guildId, out var mp))
|
||||
mp.SetVolume(value);
|
||||
}
|
||||
@@ -424,10 +405,12 @@ public sealed class MusicService : IMusicService
|
||||
public async Task<bool> ToggleAutoDisconnectAsync(ulong guildId)
|
||||
{
|
||||
var newState = false;
|
||||
await ModifySettingsInternalAsync(guildId, (settings, _) =>
|
||||
{
|
||||
newState = settings.AutoDisconnect = !settings.AutoDisconnect;
|
||||
}, default(object));
|
||||
await ModifySettingsInternalAsync(guildId,
|
||||
(settings, _) =>
|
||||
{
|
||||
newState = settings.AutoDisconnect = !settings.AutoDisconnect;
|
||||
},
|
||||
default(object));
|
||||
|
||||
return newState;
|
||||
}
|
||||
@@ -440,10 +423,12 @@ public sealed class MusicService : IMusicService
|
||||
}
|
||||
|
||||
public Task SetMusicQualityAsync(ulong guildId, QualityPreset preset)
|
||||
=> ModifySettingsInternalAsync(guildId, (settings, _) =>
|
||||
{
|
||||
settings.QualityPreset = preset;
|
||||
}, preset);
|
||||
=> ModifySettingsInternalAsync(guildId,
|
||||
(settings, _) =>
|
||||
{
|
||||
settings.QualityPreset = preset;
|
||||
},
|
||||
preset);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@ namespace NadekoBot.Modules.Music.Services;
|
||||
|
||||
public sealed partial class YtLoader
|
||||
{
|
||||
|
||||
public class InitRange
|
||||
{
|
||||
public string Start { get; set; }
|
||||
@@ -69,4 +68,4 @@ public sealed partial class YtLoader
|
||||
_videoId = videoId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,15 +7,16 @@ namespace NadekoBot.Modules.Music.Services;
|
||||
|
||||
public sealed partial class YtLoader
|
||||
{
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private static readonly byte[] YT_RESULT_INITIAL_DATA = Encoding.UTF8.GetBytes("var ytInitialData = ");
|
||||
private static readonly byte[] YT_RESULT_JSON_END = Encoding.UTF8.GetBytes(";<");
|
||||
|
||||
private static readonly string[] durationFormats = new[]
|
||||
|
||||
private static readonly string[] durationFormats =
|
||||
{
|
||||
@"m\:ss", @"mm\:ss", @"h\:mm\:ss", @"hh\:mm\:ss", @"hhh\:mm\:ss"
|
||||
};
|
||||
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
public YtLoader(IHttpClientFactory httpFactory)
|
||||
=> _httpFactory = httpFactory;
|
||||
|
||||
@@ -54,7 +55,7 @@ public sealed partial class YtLoader
|
||||
public async Task<IList<TrackInfo>> LoadResultsAsync(string query)
|
||||
{
|
||||
query = Uri.EscapeDataString(query);
|
||||
|
||||
|
||||
using var http = _httpFactory.CreateClient();
|
||||
http.DefaultRequestHeaders.Add("Cookie", "CONSENT=YES+cb.20210530-19-p0.en+FX+071;");
|
||||
|
||||
@@ -76,20 +77,19 @@ public sealed partial class YtLoader
|
||||
var root = JsonDocument.Parse(mem).RootElement;
|
||||
|
||||
var tracksJsonItems = root
|
||||
.GetProperty("contents")
|
||||
.GetProperty("twoColumnSearchResultsRenderer")
|
||||
.GetProperty("primaryContents")
|
||||
.GetProperty("sectionListRenderer")
|
||||
.GetProperty("contents")
|
||||
[0]
|
||||
.GetProperty("itemSectionRenderer")
|
||||
.GetProperty("contents")
|
||||
.EnumerateArray();
|
||||
|
||||
.GetProperty("contents")
|
||||
.GetProperty("twoColumnSearchResultsRenderer")
|
||||
.GetProperty("primaryContents")
|
||||
.GetProperty("sectionListRenderer")
|
||||
.GetProperty("contents")[0]
|
||||
.GetProperty("itemSectionRenderer")
|
||||
.GetProperty("contents")
|
||||
.EnumerateArray();
|
||||
|
||||
var tracks = new List<TrackInfo>();
|
||||
foreach (var track in tracksJsonItems)
|
||||
{
|
||||
if(!track.TryGetProperty("videoRenderer", out var elem))
|
||||
if (!track.TryGetProperty("videoRenderer", out var elem))
|
||||
continue;
|
||||
|
||||
var videoId = elem.GetProperty("videoId").GetString();
|
||||
@@ -97,18 +97,20 @@ public sealed partial class YtLoader
|
||||
var title = elem.GetProperty("title").GetProperty("runs")[0].GetProperty("text").GetString();
|
||||
var durationString = elem.GetProperty("lengthText").GetProperty("simpleText").GetString();
|
||||
|
||||
if (!TimeSpan.TryParseExact(durationString, durationFormats, CultureInfo.InvariantCulture,
|
||||
if (!TimeSpan.TryParseExact(durationString,
|
||||
durationFormats,
|
||||
CultureInfo.InvariantCulture,
|
||||
out var duration))
|
||||
{
|
||||
Log.Warning("Cannot parse duration: {DurationString}", durationString);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
tracks.Add(new YtTrackInfo(title, videoId, duration));
|
||||
if (tracks.Count >= 5)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
@@ -120,11 +122,9 @@ public sealed partial class YtLoader
|
||||
return null; // todo future try selecting html
|
||||
startIndex += YT_RESULT_INITIAL_DATA.Length;
|
||||
|
||||
var endIndex = 140_000 + startIndex + responseSpan[(startIndex + 20_000)..].IndexOf(YT_RESULT_JSON_END) + 20_000;
|
||||
var endIndex =
|
||||
140_000 + startIndex + responseSpan[(startIndex + 20_000)..].IndexOf(YT_RESULT_JSON_END) + 20_000;
|
||||
startIndex += 140_000;
|
||||
return response.AsMemory(
|
||||
startIndex,
|
||||
endIndex - startIndex
|
||||
);
|
||||
return response.AsMemory(startIndex, endIndex - startIndex);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user