Skip to content

Commit

Permalink
Hello .NET 8!
Browse files Browse the repository at this point in the history
  • Loading branch information
jim60105 committed Dec 2, 2023
1 parent 3738369 commit 32f0bc0
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 103 deletions.
8 changes: 8 additions & 0 deletions GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Style", "IDE0046:轉換至條件運算式", Justification = "<暫止>", Scope = "member", Target = "~M:YoutubeLiveChatToDiscord.Helper.GetOriginalImage(System.String)~System.String")]
24 changes: 11 additions & 13 deletions Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static class ApplicationLogging
internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
}

private static readonly ILogger logger = ApplicationLogging.CreateLogger("Helper");
private static readonly ILogger _logger = ApplicationLogging.CreateLogger("Helper");

/// <summary>
/// 尋找yt-dlp程式路徑
Expand All @@ -25,14 +25,14 @@ public static string WhereIsYt_dlp()
{
// https://stackoverflow.com/a/63021455
string file = "yt-dlp";
string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? Array.Empty<string>();
string[] extensions = Environment.GetEnvironmentVariable("PATHEXT")?.Split(';') ?? Array.Empty<string>();
string[] paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [];
string[] extensions = Environment.GetEnvironmentVariable("PATHEXT")?.Split(';') ?? [];
string YtdlPath = (from p in new[] { Environment.CurrentDirectory }.Concat(paths)
from e in extensions
let path = Path.Combine(p.Trim(), file + e.ToLower())
where File.Exists(path)
select path)?.FirstOrDefault() ?? "/usr/bin/yt-dlp";
logger.LogDebug("Found yt-dlp at {path}", YtdlPath);
_logger.LogDebug("Found yt-dlp at {path}", YtdlPath);
return YtdlPath;
}

Expand All @@ -42,33 +42,31 @@ where File.Exists(path)
/// <param name="arg"></param>
/// <returns></returns>
internal static Task DiscordWebhookClient_Log(LogMessage arg)
{
return Task.Run(() =>
=> Task.Run(() =>
{
switch (arg.Severity)
{
case LogSeverity.Critical:
logger.LogCritical("{message}", arg);
_logger.LogCritical("{message}", arg);
break;
case LogSeverity.Error:
logger.LogError("{message}", arg);
_logger.LogError("{message}", arg);
break;
case LogSeverity.Warning:
logger.LogWarning("{message}", arg);
_logger.LogWarning("{message}", arg);
break;
case LogSeverity.Info:
logger.LogInformation("{message}", arg);
_logger.LogInformation("{message}", arg);
break;
case LogSeverity.Verbose:
logger.LogTrace("{message}", arg);
_logger.LogTrace("{message}", arg);
break;
case LogSeverity.Debug:
default:
logger.LogDebug("{message}", arg);
_logger.LogDebug("{message}", arg);
break;
}
});
}

/// <summary>
/// 處理Youtube的圖片url,取得原始尺寸圖片
Expand Down
107 changes: 52 additions & 55 deletions LiveChatMonitorWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ namespace YoutubeLiveChatToDiscord
{
public class LiveChatMonitorWorker : BackgroundService
{
private readonly ILogger<LiveChatMonitorWorker> logger;
private readonly string id;
private readonly DiscordWebhookClient client;
private readonly FileInfo liveChatFileInfo;
private long position = 0;
private readonly LiveChatDownloadService liveChatDownloadService;
private readonly ILogger<LiveChatMonitorWorker> _logger;
private readonly string _id;
private readonly DiscordWebhookClient _client;
private readonly FileInfo _liveChatFileInfo;
private long _position = 0;
private readonly LiveChatDownloadService _liveChatDownloadService;

public LiveChatMonitorWorker(
ILogger<LiveChatMonitorWorker> _logger,
DiscordWebhookClient _client,
LiveChatDownloadService _liveChatDownloadService
ILogger<LiveChatMonitorWorker> logger,
DiscordWebhookClient client,
LiveChatDownloadService liveChatDownloadService
)
{
(logger, client, liveChatDownloadService) = (_logger, _client, _liveChatDownloadService);
client.Log += Helper.DiscordWebhookClient_Log;
(_logger, _client, _liveChatDownloadService) = (logger, client, liveChatDownloadService);
_client.Log += Helper.DiscordWebhookClient_Log;

id = Environment.GetEnvironmentVariable("VIDEO_ID") ?? "";
if (string.IsNullOrEmpty(id)) throw new ArgumentException(nameof(id));
_id = Environment.GetEnvironmentVariable("VIDEO_ID") ?? "";
if (string.IsNullOrEmpty(_id)) throw new ArgumentException(nameof(_id));

liveChatFileInfo = new($"{id}.live_chat.json");
_liveChatFileInfo = new($"{_id}.live_chat.json");
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
Expand All @@ -36,38 +36,35 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
if (liveChatDownloadService.DownloadProcess.IsCompleted)
if (_liveChatDownloadService.downloadProcess.IsCompleted)
{
_ = liveChatDownloadService.ExecuteAsync(stoppingToken)
.ContinueWith((_) =>
{
logger.LogInformation("yt-dlp is stopped.");
}, stoppingToken);
_ = _liveChatDownloadService.ExecuteAsync(stoppingToken)
.ContinueWith((_) => _logger.LogInformation("yt-dlp is stopped."), stoppingToken);
}

logger.LogInformation("Wait 10 seconds.");
_logger.LogInformation("Wait 10 seconds.");
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
liveChatFileInfo.Refresh();
_liveChatFileInfo.Refresh();

try
{
if (!liveChatFileInfo.Exists)
if (!_liveChatFileInfo.Exists)
{
throw new FileNotFoundException(null, liveChatFileInfo.FullName);
throw new FileNotFoundException(null, _liveChatFileInfo.FullName);
}

await Monitoring(stoppingToken);
}
catch (FileNotFoundException e)
{
logger.LogWarning("Json file not found. {FileName}", e.FileName);
_logger.LogWarning("Json file not found. {FileName}", e.FileName);
}
}
}
catch (TaskCanceledException) { }
finally
{
logger.LogError("Wait 10 seconds before closing the program. This is to prevent a restart loop from hanging the machine.");
_logger.LogError("Wait 10 seconds before closing the program. This is to prevent a restart loop from hanging the machine.");
#pragma warning disable CA2016 // 將 'CancellationToken' 參數轉送給方法
await Task.Delay(TimeSpan.FromSeconds(10));
#pragma warning restore CA2016 // 將 'CancellationToken' 參數轉送給方法
Expand All @@ -87,32 +84,32 @@ private async Task Monitoring(CancellationToken stoppingToken)
#if !DEBUG
if (null == Environment.GetEnvironmentVariable("SKIP_STARTUP_WAITING"))
{
logger.LogInformation("Wait 1 miunute to skip old chats");
_logger.LogInformation("Wait 1 miunute to skip old chats");
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
liveChatFileInfo.Refresh();
_liveChatFileInfo.Refresh();
}
#endif

position = liveChatFileInfo.Length;
logger.LogInformation("Start at position: {position}", position);
logger.LogInformation("Start Monitoring!");
_position = _liveChatFileInfo.Length;
_logger.LogInformation("Start at position: {position}", _position);
_logger.LogInformation("Start Monitoring!");

while (!stoppingToken.IsCancellationRequested)
{
liveChatFileInfo.Refresh();
if (liveChatFileInfo.Length > position)
_liveChatFileInfo.Refresh();
if (_liveChatFileInfo.Length > _position)
{
await ProcessChats(stoppingToken);
}
else if (liveChatDownloadService.DownloadProcess.IsCompleted)
else if (_liveChatDownloadService.downloadProcess.IsCompleted)
{
logger.LogInformation("Download process is stopped. Restart monitoring.");
_logger.LogInformation("Download process is stopped. Restart monitoring.");
return;
}
else
{
position = liveChatFileInfo.Length;
logger.LogTrace("No new chat. Wait 10 seconds.");
_position = _liveChatFileInfo.Length;
_logger.LogTrace("No new chat. Wait 10 seconds.");
// 每10秒檢查一次json檔
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
Expand All @@ -127,14 +124,14 @@ private async Task Monitoring(CancellationToken stoppingToken)
/// <exception cref="FileNotFoundException"></exception>
private async Task GetVideoInfo(CancellationToken stoppingToken)
{
FileInfo videoInfo = new($"{id}.info.json");
FileInfo videoInfo = new($"{_id}.info.json");
if (!videoInfo.Exists)
{
// Chat json file 在 VideoInfo json file之後被產生,理論上這段不會進來
throw new FileNotFoundException(null, videoInfo.FullName);
}

Info? info = JsonConvert.DeserializeObject<Info>(await new StreamReader(videoInfo.OpenRead()).ReadToEndAsync());
Info? info = JsonConvert.DeserializeObject<Info>(await new StreamReader(videoInfo.OpenRead()).ReadToEndAsync(stoppingToken));
string? Title = info?.title;
string? ChannelId = info?.channel_id;
string? thumb = info?.thumbnail;
Expand All @@ -155,17 +152,17 @@ private async Task ProcessChats(CancellationToken stoppingToken)
// 這是.NET Core在Linux、Windows上關於鎖定設計的描述: https://github.com/dotnet/runtime/pull/55256
// 如果要繞過這個問題,從.NET 6開始,可以加上環境變數「DOTNET_SYSTEM_IO_DISABLEFILELOCKING」讓FileStream「不」遵守鎖定。
// (本專案已在Dockerfile加上此環境變數)
using FileStream fs = new(liveChatFileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using FileStream fs = new(_liveChatFileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs);

sr.BaseStream.Seek(position, SeekOrigin.Begin);
while (position < sr.BaseStream.Length)
sr.BaseStream.Seek(_position, SeekOrigin.Begin);
while (_position < sr.BaseStream.Length)
{
string? str = "";
try
{
str = await sr.ReadLineAsync();
position = sr.BaseStream.Position;
str = await sr.ReadLineAsync(stoppingToken);
_position = sr.BaseStream.Position;
if (string.IsNullOrEmpty(str)) continue;

Chat? chat = JsonConvert.DeserializeObject<Chat>(str);
Expand All @@ -175,17 +172,17 @@ private async Task ProcessChats(CancellationToken stoppingToken)
}
catch (JsonSerializationException e)
{
logger.LogError("{error}", e.Message);
logger.LogError("{originalString}", str);
_logger.LogError("{error}", e.Message);
_logger.LogError("{originalString}", str);
}
catch (ArgumentException e)
{
logger.LogError("{error}", e.Message);
logger.LogError("{originalString}", str);
_logger.LogError("{error}", e.Message);
_logger.LogError("{originalString}", str);
}
catch (IOException e)
{
logger.LogError("{error}", e.Message);
_logger.LogError("{error}", e.Message);
break;
}
}
Expand All @@ -202,7 +199,7 @@ private async Task BuildRequestAndSendToDiscord(Chat chat, CancellationToken sto
{
EmbedBuilder eb = new();
eb.WithTitle(Environment.GetEnvironmentVariable("TITLE") ?? "")
.WithUrl($"https://youtu.be/{id}")
.WithUrl($"https://youtu.be/{_id}")
.WithThumbnailUrl(Helper.GetOriginalImage(Environment.GetEnvironmentVariable("VIDEO_THUMB")));
string author = "";

Expand Down Expand Up @@ -397,13 +394,13 @@ private async Task BuildRequestAndSendToDiscord(Chat chat, CancellationToken sto
) { return; }
else
{
logger.LogWarning("Message type not supported, skip sending to discord.");
_logger.LogWarning("Message type not supported, skip sending to discord.");
throw new ArgumentException("Message type not supported", nameof(chat));
}

if (stoppingToken.IsCancellationRequested) return;

logger.LogDebug("Sending Request to Discord: {author}: {message}", author, eb.Description);
_logger.LogDebug("Sending Request to Discord: {author}: {message}", author, eb.Description);

try
{
Expand All @@ -420,15 +417,15 @@ private async Task BuildRequestAndSendToDiscord(Chat chat, CancellationToken sto

// The rate for Discord webhooks are 30 requests/minute per channel.
// Be careful when you run multiple instances in the same channel!
logger.LogTrace("Wait 2 seconds for discord webhook rate limit");
_logger.LogTrace("Wait 2 seconds for discord webhook rate limit");
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);

Task SendMessage()
=> client.SendMessageAsync(embeds: new Embed[] { eb.Build() })
=> _client.SendMessageAsync(embeds: new Embed[] { eb.Build() })
.ContinueWith(async p =>
{
ulong messageId = await p;
logger.LogDebug("Message sent to discord, message id: {messageId}", messageId);
_logger.LogDebug("Message sent to discord, message id: {messageId}", messageId);
}, stoppingToken);
}
}
Expand Down
3 changes: 1 addition & 2 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
{
services.AddHostedService<LiveChatMonitorWorker>()
.AddSingleton<LiveChatDownloadService>()
.AddSingleton<DiscordWebhookClient>((service) =>
new DiscordWebhookClient(Environment.GetEnvironmentVariable("WEBHOOK")));
.AddSingleton((service) => new DiscordWebhookClient(Environment.GetEnvironmentVariable("WEBHOOK")));
})
.Build();

Expand Down
Loading

0 comments on commit 32f0bc0

Please sign in to comment.