Applied codestyle to all .cs files

This commit is contained in:
Kwoth
2021-12-29 06:07:16 +01:00
parent 723447c7d4
commit 82000c97a4
543 changed files with 13221 additions and 14059 deletions

View File

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

View File

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

View File

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

View File

@@ -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;
}
}
}
}

View File

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