mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Gambling moved to a separate project. Project builds
This commit is contained in:
@@ -60,9 +60,16 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
CredsExamplePath);
|
||||
}
|
||||
|
||||
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
|
||||
.AddEnvironmentVariables("NadekoBot_")
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
|
||||
.AddEnvironmentVariables("NadekoBot_")
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
_changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
|
||||
Reload();
|
||||
|
@@ -1,60 +0,0 @@
|
||||
#nullable disable
|
||||
using SixLabors.Fonts;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class FontProvider : INService
|
||||
{
|
||||
public FontFamily DottyFont { get; }
|
||||
|
||||
public FontFamily UniSans { get; }
|
||||
|
||||
public FontFamily NotoSans { get; }
|
||||
//public FontFamily Emojis { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Font used for .rip command
|
||||
/// </summary>
|
||||
public Font RipFont { get; }
|
||||
|
||||
public List<FontFamily> FallBackFonts { get; }
|
||||
private readonly FontCollection _fonts;
|
||||
|
||||
public FontProvider()
|
||||
{
|
||||
_fonts = new();
|
||||
|
||||
NotoSans = _fonts.Add("data/fonts/NotoSans-Bold.ttf");
|
||||
UniSans = _fonts.Add("data/fonts/Uni Sans.ttf");
|
||||
|
||||
FallBackFonts = new();
|
||||
|
||||
//FallBackFonts.Add(_fonts.Install("data/fonts/OpenSansEmoji.ttf"));
|
||||
|
||||
// try loading some emoji and jap fonts on windows as fallback fonts
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fontsfolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
|
||||
FallBackFonts.Add(_fonts.Add(Path.Combine(fontsfolder, "seguiemj.ttf")));
|
||||
FallBackFonts.AddRange(_fonts.AddCollection(Path.Combine(fontsfolder, "msgothic.ttc")));
|
||||
FallBackFonts.AddRange(_fonts.AddCollection(Path.Combine(fontsfolder, "segoe.ttc")));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// any fonts present in data/fonts should be added as fallback fonts
|
||||
// this will allow support for special characters when drawing text
|
||||
foreach (var font in Directory.GetFiles(@"data/fonts"))
|
||||
{
|
||||
if (font.EndsWith(".ttf"))
|
||||
FallBackFonts.Add(_fonts.Add(font));
|
||||
else if (font.EndsWith(".ttc"))
|
||||
FallBackFonts.AddRange(_fonts.AddCollection(font));
|
||||
}
|
||||
|
||||
RipFont = NotoSans.CreateFont(20, FontStyle.Bold);
|
||||
DottyFont = FallBackFonts.First(x => x.Name == "dotty");
|
||||
}
|
||||
}
|
@@ -1,38 +1,4 @@
|
||||
using NadekoBot.Common.Configs;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public sealed class ImagesConfig : ConfigServiceBase<ImageUrls>
|
||||
{
|
||||
private const string PATH = "data/images.yml";
|
||||
|
||||
private static readonly TypedKey<ImageUrls> _changeKey =
|
||||
new("config.images.updated");
|
||||
|
||||
public override string Name
|
||||
=> "images";
|
||||
|
||||
public ImagesConfig(IConfigSeria serializer, IPubSub pubSub)
|
||||
: base(PATH, serializer, pubSub, _changeKey)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface IImageCache
|
||||
{
|
||||
Task<byte[]?> GetHeadsImageAsync();
|
||||
Task<byte[]?> GetTailsImageAsync();
|
||||
Task<byte[]?> GetCurrencyImageAsync();
|
||||
Task<byte[]?> GetXpBackgroundImageAsync();
|
||||
Task<byte[]?> GetRategirlBgAsync();
|
||||
Task<byte[]?> GetRategirlDotAsync();
|
||||
Task<byte[]?> GetDiceAsync(int num);
|
||||
Task<byte[]?> GetSlotEmojiAsync(int number);
|
||||
Task<byte[]?> GetSlotBgAsync();
|
||||
Task<byte[]?> GetRipBgAsync();
|
||||
Task<byte[]?> GetRipOverlayAsync();
|
||||
Task<byte[]?> GetImageDataAsync(Uri url);
|
||||
}
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public sealed class ImageCache : IImageCache, INService
|
||||
{
|
||||
@@ -114,4 +80,4 @@ public sealed class ImageCache : IImageCache, INService
|
||||
|
||||
public Task<byte[]?> GetRipOverlayAsync()
|
||||
=> GetImageDataAsync(_ic.Data.Rip.Overlay);
|
||||
}
|
||||
}
|
27
src/NadekoBot/Services/Impl/PubSub/JsonSeria.cs
Normal file
27
src/NadekoBot/Services/Impl/PubSub/JsonSeria.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using NadekoBot.Common.JsonConverters;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class JsonSeria : ISeria
|
||||
{
|
||||
private readonly JsonSerializerOptions _serializerOptions = new()
|
||||
{
|
||||
Converters =
|
||||
{
|
||||
new Rgba32Converter(),
|
||||
new CultureInfoConverter()
|
||||
}
|
||||
};
|
||||
|
||||
public byte[] Serialize<T>(T data)
|
||||
=> JsonSerializer.SerializeToUtf8Bytes(data, _serializerOptions);
|
||||
|
||||
public T? Deserialize<T>(byte[]? data)
|
||||
{
|
||||
if (data is null)
|
||||
return default;
|
||||
|
||||
return JsonSerializer.Deserialize<T>(data, _serializerOptions);
|
||||
}
|
||||
}
|
52
src/NadekoBot/Services/Impl/PubSub/RedisPubSub.cs
Normal file
52
src/NadekoBot/Services/Impl/PubSub/RedisPubSub.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public sealed class RedisPubSub : IPubSub
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly ConnectionMultiplexer _multi;
|
||||
private readonly ISeria _serializer;
|
||||
|
||||
public RedisPubSub(ConnectionMultiplexer multi, ISeria serializer, IBotCredentials creds)
|
||||
{
|
||||
_multi = multi;
|
||||
_serializer = serializer;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public Task Pub<TData>(in TypedKey<TData> key, TData data)
|
||||
where TData : notnull
|
||||
{
|
||||
var serialized = _serializer.Serialize(data);
|
||||
return _multi.GetSubscriber()
|
||||
.PublishAsync($"{_creds.RedisKey()}:{key.Key}", serialized, CommandFlags.FireAndForget);
|
||||
}
|
||||
|
||||
public Task Sub<TData>(in TypedKey<TData> key, Func<TData, ValueTask> action)
|
||||
where TData : notnull
|
||||
{
|
||||
var eventName = key.Key;
|
||||
|
||||
async void OnSubscribeHandler(RedisChannel _, RedisValue data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataObj = _serializer.Deserialize<TData>(data);
|
||||
if (dataObj is not null)
|
||||
await action(dataObj);
|
||||
else
|
||||
{
|
||||
Log.Warning("Publishing event {EventName} with a null value. This is not allowed",
|
||||
eventName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("Error handling the event {EventName}: {ErrorMessage}", eventName, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return _multi.GetSubscriber().SubscribeAsync($"{_creds.RedisKey()}:{eventName}", OnSubscribeHandler);
|
||||
}
|
||||
}
|
39
src/NadekoBot/Services/Impl/PubSub/YamlSeria.cs
Normal file
39
src/NadekoBot/Services/Impl/PubSub/YamlSeria.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using NadekoBot.Common.Configs;
|
||||
using NadekoBot.Common.Yml;
|
||||
using System.Text.RegularExpressions;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public class YamlSeria : IConfigSeria
|
||||
{
|
||||
private static readonly Regex _codePointRegex =
|
||||
new(@"(\\U(?<code>[a-zA-Z0-9]{8})|\\u(?<code>[a-zA-Z0-9]{4})|\\x(?<code>[a-zA-Z0-9]{2}))",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private readonly IDeserializer _deserializer;
|
||||
private readonly ISerializer _serializer;
|
||||
|
||||
public YamlSeria()
|
||||
{
|
||||
_serializer = Yaml.Serializer;
|
||||
_deserializer = Yaml.Deserializer;
|
||||
}
|
||||
|
||||
public string Serialize<T>(T obj)
|
||||
where T : notnull
|
||||
{
|
||||
var escapedOutput = _serializer.Serialize(obj);
|
||||
var output = _codePointRegex.Replace(escapedOutput,
|
||||
me =>
|
||||
{
|
||||
var str = me.Groups["code"].Value;
|
||||
var newString = YamlHelper.UnescapeUnicodeCodePoint(str);
|
||||
return newString;
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string data)
|
||||
=> _deserializer.Deserialize<T>(data);
|
||||
}
|
119
src/NadekoBot/Services/Impl/RedisBotCache.cs
Normal file
119
src/NadekoBot/Services/Impl/RedisBotCache.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using OneOf;
|
||||
using OneOf.Types;
|
||||
using StackExchange.Redis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public sealed class RedisBotCache : IBotCache
|
||||
{
|
||||
private static readonly Type[] _supportedTypes = new []
|
||||
{
|
||||
typeof(bool), typeof(int), typeof(uint), typeof(long),
|
||||
typeof(ulong), typeof(float), typeof(double),
|
||||
typeof(string), typeof(byte[]), typeof(ReadOnlyMemory<byte>), typeof(Memory<byte>),
|
||||
typeof(RedisValue),
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions _opts = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
AllowTrailingCommas = true,
|
||||
IgnoreReadOnlyProperties = false,
|
||||
};
|
||||
private readonly ConnectionMultiplexer _conn;
|
||||
|
||||
public RedisBotCache(ConnectionMultiplexer conn)
|
||||
{
|
||||
_conn = conn;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> AddAsync<T>(TypedKey<T> key, T value, TimeSpan? expiry = null, bool overwrite = true)
|
||||
{
|
||||
// if a null value is passed, remove the key
|
||||
if (value is null)
|
||||
{
|
||||
await RemoveAsync(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
var db = _conn.GetDatabase();
|
||||
RedisValue val = IsSupportedType(typeof(T))
|
||||
? RedisValue.Unbox(value)
|
||||
: JsonSerializer.Serialize(value, _opts);
|
||||
|
||||
var success = await db.StringSetAsync(key.Key,
|
||||
val,
|
||||
expiry: expiry,
|
||||
when: overwrite ? When.Always : When.NotExists);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool IsSupportedType(Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var typeDef = type.GetGenericTypeDefinition();
|
||||
if (typeDef == typeof(Nullable<>))
|
||||
return IsSupportedType(type.GenericTypeArguments[0]);
|
||||
}
|
||||
|
||||
foreach (var t in _supportedTypes)
|
||||
{
|
||||
if (type == t)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask<OneOf<T, None>> GetAsync<T>(TypedKey<T> key)
|
||||
{
|
||||
var db = _conn.GetDatabase();
|
||||
var val = await db.StringGetAsync(key.Key);
|
||||
if (val == default)
|
||||
return new None();
|
||||
|
||||
if (IsSupportedType(typeof(T)))
|
||||
return (T)((IConvertible)val).ToType(typeof(T), null);
|
||||
|
||||
return JsonSerializer.Deserialize<T>(val.ToString(), _opts)!;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> RemoveAsync<T>(TypedKey<T> key)
|
||||
{
|
||||
var db = _conn.GetDatabase();
|
||||
|
||||
return await db.KeyDeleteAsync(key.Key);
|
||||
}
|
||||
|
||||
public async ValueTask<T?> GetOrAddAsync<T>(TypedKey<T> key, Func<Task<T?>> createFactory, TimeSpan? expiry = null)
|
||||
{
|
||||
var result = await GetAsync(key);
|
||||
|
||||
return await result.Match<Task<T?>>(
|
||||
v => Task.FromResult<T?>(v),
|
||||
async _ =>
|
||||
{
|
||||
var factoryValue = await createFactory();
|
||||
|
||||
if (factoryValue is null)
|
||||
return default;
|
||||
|
||||
await AddAsync(key, factoryValue, expiry);
|
||||
|
||||
// get again to make sure it's the cached value
|
||||
// and not the late factory value, in case there's a race condition
|
||||
|
||||
var newResult = await GetAsync(key);
|
||||
|
||||
// it's fine to do this, it should blow up if something went wrong.
|
||||
return newResult.Match<T?>(
|
||||
v => v,
|
||||
_ => default);
|
||||
});
|
||||
}
|
||||
}
|
79
src/NadekoBot/Services/Impl/RedisBotStringsProvider.cs
Normal file
79
src/NadekoBot/Services/Impl/RedisBotStringsProvider.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
#nullable disable
|
||||
using StackExchange.Redis;
|
||||
using System.Web;
|
||||
using Nadeko.Common;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Uses <see cref="IStringsSource" /> to load strings into redis hash (only on Shard 0)
|
||||
/// and retrieves them from redis via <see cref="GetText" />
|
||||
/// </summary>
|
||||
public class RedisBotStringsProvider : IBotStringsProvider
|
||||
{
|
||||
private readonly ConnectionMultiplexer _redis;
|
||||
private readonly IStringsSource _source;
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public RedisBotStringsProvider(
|
||||
ConnectionMultiplexer redis,
|
||||
DiscordSocketClient discordClient,
|
||||
IStringsSource source,
|
||||
IBotCredentials creds)
|
||||
{
|
||||
_redis = redis;
|
||||
_source = source;
|
||||
_creds = creds;
|
||||
|
||||
if (discordClient.ShardId == 0)
|
||||
Reload();
|
||||
}
|
||||
|
||||
public string GetText(string localeName, string key)
|
||||
{
|
||||
var value = _redis.GetDatabase().HashGet($"{_creds.RedisKey()}:responses:{localeName}", key);
|
||||
return value;
|
||||
}
|
||||
|
||||
public CommandStrings GetCommandStrings(string localeName, string commandName)
|
||||
{
|
||||
string argsStr = _redis.GetDatabase()
|
||||
.HashGet($"{_creds.RedisKey()}:commands:{localeName}", $"{commandName}::args");
|
||||
if (argsStr == default)
|
||||
return null;
|
||||
|
||||
var descStr = _redis.GetDatabase()
|
||||
.HashGet($"{_creds.RedisKey()}:commands:{localeName}", $"{commandName}::desc");
|
||||
if (descStr == default)
|
||||
return null;
|
||||
|
||||
var args = argsStr.Split('&').Map(HttpUtility.UrlDecode);
|
||||
return new()
|
||||
{
|
||||
Args = args,
|
||||
Desc = descStr
|
||||
};
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
var redisDb = _redis.GetDatabase();
|
||||
foreach (var (localeName, localeStrings) in _source.GetResponseStrings())
|
||||
{
|
||||
var hashFields = localeStrings.Select(x => new HashEntry(x.Key, x.Value)).ToArray();
|
||||
|
||||
redisDb.HashSet($"{_creds.RedisKey()}:responses:{localeName}", hashFields);
|
||||
}
|
||||
|
||||
foreach (var (localeName, localeStrings) in _source.GetCommandStrings())
|
||||
{
|
||||
var hashFields = localeStrings
|
||||
.Select(x => new HashEntry($"{x.Key}::args",
|
||||
string.Join('&', x.Value.Args.Map(HttpUtility.UrlEncode))))
|
||||
.Concat(localeStrings.Select(x => new HashEntry($"{x.Key}::desc", x.Value.Desc)))
|
||||
.ToArray();
|
||||
|
||||
redisDb.HashSet($"{_creds.RedisKey()}:commands:{localeName}", hashFields);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public static class RedisImageExtensions
|
||||
{
|
||||
private const string OLD_CDN_URL = "nadeko-pictures.nyc3.digitaloceanspaces.com";
|
||||
private const string NEW_CDN_URL = "cdn.nadeko.bot";
|
||||
|
||||
public static Uri ToNewCdn(this Uri uri)
|
||||
=> new(uri.ToString().Replace(OLD_CDN_URL, NEW_CDN_URL));
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
#nullable disable
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class SingleProcessCoordinator : ICoordinator
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public SingleProcessCoordinator(IBotCredentials creds, DiscordSocketClient client)
|
||||
{
|
||||
_creds = creds;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public bool RestartBot()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_creds.RestartCommand?.Cmd)
|
||||
|| string.IsNullOrWhiteSpace(_creds.RestartCommand?.Args))
|
||||
{
|
||||
Log.Error("You must set RestartCommand.Cmd and RestartCommand.Args in creds.yml");
|
||||
return false;
|
||||
}
|
||||
|
||||
Process.Start(_creds.RestartCommand.Cmd, _creds.RestartCommand.Args);
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
Die();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Die(bool graceful = false)
|
||||
=> Environment.Exit(5);
|
||||
|
||||
public bool RestartShard(int shardId)
|
||||
=> RestartBot();
|
||||
|
||||
public IList<ShardStatus> GetAllShardStatuses()
|
||||
=> new[]
|
||||
{
|
||||
new ShardStatus
|
||||
{
|
||||
ConnectionState = _client.ConnectionState,
|
||||
GuildCount = _client.Guilds.Count,
|
||||
LastUpdate = DateTime.UtcNow,
|
||||
ShardId = _client.ShardId
|
||||
}
|
||||
};
|
||||
|
||||
public int GetGuildCount()
|
||||
=> _client.Guilds.Count;
|
||||
|
||||
public Task Reload()
|
||||
=> Task.CompletedTask;
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
#nullable disable
|
||||
using System.Collections;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class StartingGuildsService : IEnumerable<ulong>, INService
|
||||
{
|
||||
private readonly IReadOnlyList<ulong> _guilds;
|
||||
|
||||
public StartingGuildsService(DiscordSocketClient client)
|
||||
=> _guilds = client.Guilds.Select(x => x.Id).ToList();
|
||||
|
||||
public IEnumerator<ulong> GetEnumerator()
|
||||
=> _guilds.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> _guilds.GetEnumerator();
|
||||
}
|
@@ -7,14 +7,11 @@ namespace NadekoBot.Services;
|
||||
|
||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||
{
|
||||
public const string BOT_VERSION = "4.3.13";
|
||||
public const string BOT_VERSION = "5.0.0-alpha1";
|
||||
|
||||
public string Author
|
||||
=> "Kwoth#2452";
|
||||
|
||||
public string Library
|
||||
=> "Discord.Net";
|
||||
|
||||
public double MessagesPerSecond
|
||||
=> MessageCounter / GetUptime().TotalSeconds;
|
||||
|
||||
|
@@ -1,78 +0,0 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Nadeko.Common;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class YtdlOperation
|
||||
{
|
||||
private readonly string _baseArgString;
|
||||
private readonly bool _isYtDlp;
|
||||
|
||||
public YtdlOperation(string baseArgString, bool isYtDlp = false)
|
||||
{
|
||||
_baseArgString = baseArgString;
|
||||
_isYtDlp = isYtDlp;
|
||||
}
|
||||
|
||||
private Process CreateProcess(string[] args)
|
||||
{
|
||||
var newArgs = args.Map(arg => (object)arg.Replace("\"", ""));
|
||||
return new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
FileName = _isYtDlp ? "yt-dlp" : "youtube-dl",
|
||||
Arguments = string.Format(_baseArgString, newArgs),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> GetDataAsync(params string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = CreateProcess(args);
|
||||
|
||||
Log.Debug("Executing {FileName} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||
process.Start();
|
||||
|
||||
var str = await process.StandardOutput.ReadToEndAsync();
|
||||
var err = await process.StandardError.ReadToEndAsync();
|
||||
if (!string.IsNullOrEmpty(err))
|
||||
Log.Warning("YTDL warning: {YtdlWarning}", err);
|
||||
|
||||
return str;
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
Log.Error("youtube-dl is likely not installed. " + "Please install it before running the command again");
|
||||
return default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception running youtube-dl: {ErrorMessage}", ex.Message);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<string> EnumerateDataAsync(params string[] args)
|
||||
{
|
||||
using var process = CreateProcess(args);
|
||||
|
||||
Log.Debug("Executing {FileName} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||
process.Start();
|
||||
|
||||
string line;
|
||||
while ((line = await process.StandardOutput.ReadLineAsync()) is not null)
|
||||
yield return line;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user