From 4867ddb8b26a40efd9ddd2aab3a6e6eea129326c Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Mon, 21 Nov 2022 03:01:16 -0500 Subject: [PATCH] Fixed all embeds being doubled with arg --embed-missing, fixed crash when embeddedData is null, made embed fetching async --- TwitchDownloaderCLI/Modes/UpdateChat.cs | 2 +- TwitchDownloaderCore/ChatDownloader.cs | 19 ++--- TwitchDownloaderCore/ChatRenderer.cs | 24 ++++-- TwitchDownloaderCore/ChatUpdater.cs | 108 ++++++++++++++---------- 4 files changed, 89 insertions(+), 64 deletions(-) diff --git a/TwitchDownloaderCLI/Modes/UpdateChat.cs b/TwitchDownloaderCLI/Modes/UpdateChat.cs index 71f8617e..75887f64 100644 --- a/TwitchDownloaderCLI/Modes/UpdateChat.cs +++ b/TwitchDownloaderCLI/Modes/UpdateChat.cs @@ -61,7 +61,7 @@ internal static void Update(ChatUpdateArgs inputOptions) ChatUpdater chatUpdater = new(updateOptions); Progress progress = new(); progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged; - chatUpdater.ParseJson().Wait(); + chatUpdater.ParseJsonAsync().Wait(); chatUpdater.UpdateAsync(progress, new CancellationToken()).Wait(); } } diff --git a/TwitchDownloaderCore/ChatDownloader.cs b/TwitchDownloaderCore/ChatDownloader.cs index 975ff01b..083a8cd0 100644 --- a/TwitchDownloaderCore/ChatDownloader.cs +++ b/TwitchDownloaderCore/ChatDownloader.cs @@ -325,14 +325,10 @@ public async Task DownloadAsync(IProgress progress, Cancellation comments = commentsSet.DistinctBy(x => x._id).ToList(); chatRoot.comments = comments; - if (downloadOptions.EmbedData && (downloadOptions.DownloadFormat == DownloadFormat.Json || downloadOptions.DownloadFormat == DownloadFormat.Html)) + if (downloadOptions.EmbedData && (downloadOptions.DownloadFormat is DownloadFormat.Json or DownloadFormat.Html)) { progress.Report(new ProgressReport() { reportType = ReportType.Message, data = "Downloading + Embedding Images" }); chatRoot.embeddedData = new EmbeddedData(); - List firstPartyReturnList = new List(); - List thirdPartyReturnList = new List(); - List badgesReturnList = new List(); - List bitsReturnList = new List(); List thirdPartyEmotes = new List(); List firstPartyEmotes = new List(); @@ -353,7 +349,7 @@ public async Task DownloadAsync(IProgress progress, Cancellation newEmote.name = emote.Name; newEmote.width = emote.Width / emote.ImageScale; newEmote.height = emote.Height / emote.ImageScale; - thirdPartyReturnList.Add(newEmote); + chatRoot.embeddedData.thirdParty.Add(newEmote); } foreach (TwitchEmote emote in firstPartyEmotes) { @@ -363,14 +359,14 @@ public async Task DownloadAsync(IProgress progress, Cancellation newEmote.data = emote.ImageData; newEmote.width = emote.Width / emote.ImageScale; newEmote.height = emote.Height / emote.ImageScale; - firstPartyReturnList.Add(newEmote); + chatRoot.embeddedData.firstParty.Add(newEmote); } foreach (ChatBadge badge in twitchBadges) { EmbedChatBadge newBadge = new EmbedChatBadge(); newBadge.name = badge.Name; newBadge.versions = badge.VersionsData; - badgesReturnList.Add(newBadge); + chatRoot.embeddedData.twitchBadges.Add(newBadge); } foreach (CheerEmote bit in twitchBits) { @@ -388,13 +384,8 @@ public async Task DownloadAsync(IProgress progress, Cancellation newEmote.height = emotePair.Value.Height / emotePair.Value.ImageScale; newBit.tierList.Add(emotePair.Key, newEmote); } - bitsReturnList.Add(newBit); + chatRoot.embeddedData.twitchBits.Add(newBit); } - - chatRoot.embeddedData.thirdParty = thirdPartyReturnList; - chatRoot.embeddedData.firstParty = firstPartyReturnList; - chatRoot.embeddedData.twitchBadges = badgesReturnList; - chatRoot.embeddedData.twitchBits = bitsReturnList; } if (downloadOptions.DownloadFormat == DownloadFormat.Json) diff --git a/TwitchDownloaderCore/ChatRenderer.cs b/TwitchDownloaderCore/ChatRenderer.cs index 2d82bcd3..12f23b43 100644 --- a/TwitchDownloaderCore/ChatRenderer.cs +++ b/TwitchDownloaderCore/ChatRenderer.cs @@ -38,16 +38,16 @@ public class ChatRenderer public ChatRenderer(ChatRenderOptions chatRenderOptions) { renderOptions = chatRenderOptions; - renderOptions.TempFolder = string.IsNullOrWhiteSpace(renderOptions.TempFolder) ? Path.Combine(Path.GetTempPath(), "TwitchDownloader") : Path.Combine(renderOptions.TempFolder, "TwitchDownloader"); + renderOptions.TempFolder = Path.Combine(string.IsNullOrWhiteSpace(renderOptions.TempFolder) ? Path.GetTempPath() : renderOptions.TempFolder, "TwitchDownloader"); } public async Task RenderVideoAsync(IProgress progress, CancellationToken cancellationToken) { progress.Report(new ProgressReport() { reportType = ReportType.Message, data = "Fetching Images" }); - Task> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, offline: renderOptions.Offline)); - Task> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, renderOptions.TempFolder, chatRoot.embeddedData, offline: renderOptions.Offline)); - Task> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.BttvEmotes, renderOptions.FfzEmotes, renderOptions.StvEmotes, offline: renderOptions.Offline)); - Task> cheerTask = Task.Run(() => TwitchHelper.GetBits(renderOptions.TempFolder, chatRoot.streamer.id.ToString(), chatRoot.embeddedData, offline: renderOptions.Offline)); + Task> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.Offline)); + Task> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.Offline)); + Task> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.BttvEmotes, renderOptions.FfzEmotes, renderOptions.StvEmotes, renderOptions.Offline)); + Task> cheerTask = Task.Run(() => TwitchHelper.GetBits(renderOptions.TempFolder, chatRoot.streamer.id.ToString(), chatRoot.embeddedData, renderOptions.Offline)); Task> emojiTask = Task.Run(() => TwitchHelper.GetTwitterEmojis(renderOptions.TempFolder)); await Task.WhenAll(badgeTask, emoteTask, emoteThirdTask, cheerTask, emojiTask); @@ -58,6 +58,14 @@ public async Task RenderVideoAsync(IProgress progress, Cancellat cheermotesList = cheerTask.Result; emojiCache = emojiTask.Result; + // Dispose of the tasks to free up the memory now + // TODO: move the tasks to a dedicated function so they are disposed by the scope instead + badgeTask.Dispose(); + emoteTask.Dispose(); + emoteThirdTask.Dispose(); + cheerTask.Dispose(); + emojiTask.Dispose(); + await Task.Run(ScaleImages); FloorCommentOffsets(chatRoot.comments); @@ -112,9 +120,13 @@ private void FloorCommentOffsets(List comments) foreach (var comment in comments) { if (renderOptions.UpdateRate > 1) + { comment.content_offset_seconds = Math.Floor(comment.content_offset_seconds); + } else - comment.content_offset_seconds = Math.Floor(comment.content_offset_seconds * (1 / renderOptions.UpdateRate)) / (1 / renderOptions.UpdateRate); + { + comment.content_offset_seconds = Math.Floor(comment.content_offset_seconds / renderOptions.UpdateRate) * renderOptions.UpdateRate; + } } } diff --git a/TwitchDownloaderCore/ChatUpdater.cs b/TwitchDownloaderCore/ChatUpdater.cs index d4777809..2db9179c 100644 --- a/TwitchDownloaderCore/ChatUpdater.cs +++ b/TwitchDownloaderCore/ChatUpdater.cs @@ -1,4 +1,6 @@ -using System; +using Newtonsoft.Json; +using SkiaSharp; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -12,89 +14,95 @@ namespace TwitchDownloaderCore { public class ChatUpdater { - public ChatRoot chatRoot { get; set; } = new ChatRoot(); - private readonly ChatUpdateOptions updateOptions; + public ChatRoot chatRoot = new(); + private readonly ChatUpdateOptions updateOptions = new(); + private List firstPartyEmoteList = new(); + private List thirdPartyEmoteList = new(); + private List badgeList = new(); + private List bitList = new(); public ChatUpdater(ChatUpdateOptions UpdateOptions) { updateOptions = UpdateOptions; + updateOptions.TempFolder = Path.Combine(string.IsNullOrWhiteSpace(updateOptions.TempFolder) ? Path.GetTempPath() : updateOptions.TempFolder, "TwitchDownloader"); } public async Task UpdateAsync(IProgress progress, CancellationToken cancellationToken) { - string cacheFolder = Path.Combine(string.IsNullOrWhiteSpace(updateOptions.TempFolder) ? Path.GetTempPath() : updateOptions.TempFolder, "TwitchDownloader", "chatupdatecache"); + GetDataToEmbed().Wait(cancellationToken); - // Clear working directory if it already exists - if (Directory.Exists(cacheFolder)) - Directory.Delete(cacheFolder, true); + chatRoot.embeddedData ??= new EmbeddedData(); - // Thirdparty emotes - if (chatRoot.embeddedData.thirdParty == null || updateOptions.UpdateOldEmbeds) + // Firstparty emotes + if (chatRoot.embeddedData.firstParty == null || updateOptions.UpdateOldEmbeds) { - chatRoot.embeddedData.thirdParty = new List(); + chatRoot.embeddedData.firstParty = new List(); } - Console.WriteLine("Input third party emote count: " + chatRoot.embeddedData.thirdParty.Count); - List thirdPartyEmotes = new List(); - thirdPartyEmotes = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, cacheFolder, bttv: updateOptions.BttvEmotes, ffz: updateOptions.FfzEmotes, stv: inputOptions.StvEmotes, embeddedData: chatRoot.embeddedData)).Result; - foreach (TwitchEmote emote in thirdPartyEmotes) + int inputCount = chatRoot.embeddedData.firstParty.Count; + foreach (TwitchEmote emote in firstPartyEmoteList) { EmbedEmoteData newEmote = new EmbedEmoteData(); newEmote.id = emote.Id; newEmote.imageScale = emote.ImageScale; newEmote.data = emote.ImageData; - newEmote.name = emote.Name; newEmote.width = emote.Width / emote.ImageScale; newEmote.height = emote.Height / emote.ImageScale; - chatRoot.embeddedData.thirdParty.Add(newEmote); + + if (!chatRoot.embeddedData.firstParty.Any(x => x.id.Equals(newEmote.id))) + { + chatRoot.embeddedData.firstParty.Add(newEmote); + } } - Console.WriteLine("Output third party emote count: " + chatRoot.embeddedData.thirdParty.Count); + progress.Report(new ProgressReport() { reportType = ReportType.Message, data = string.Format("Input firsty party emote count: {0}. Output count: {1}", inputCount, chatRoot.embeddedData.firstParty.Count) }); - // Firstparty emotes - if (chatRoot.embeddedData.firstParty == null || updateOptions.UpdateOldEmbeds) + // Thirdparty emotes + if (chatRoot.embeddedData.thirdParty == null || updateOptions.UpdateOldEmbeds) { - chatRoot.embeddedData.firstParty = new List(); + chatRoot.embeddedData.thirdParty = new List(); } - Console.WriteLine("Input first party emote count: " + chatRoot.embeddedData.firstParty.Count); - List firstPartyEmotes = new List(); - firstPartyEmotes = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, cacheFolder, embeddedData: chatRoot.embeddedData)).Result; - foreach (TwitchEmote emote in firstPartyEmotes) + inputCount = chatRoot.embeddedData.thirdParty.Count; + foreach (TwitchEmote emote in thirdPartyEmoteList) { EmbedEmoteData newEmote = new EmbedEmoteData(); newEmote.id = emote.Id; newEmote.imageScale = emote.ImageScale; newEmote.data = emote.ImageData; + newEmote.name = emote.Name; newEmote.width = emote.Width / emote.ImageScale; newEmote.height = emote.Height / emote.ImageScale; - chatRoot.embeddedData.firstParty.Add(newEmote); + + if (!chatRoot.embeddedData.thirdParty.Any(x => x.id.Equals(newEmote.id))) + { + chatRoot.embeddedData.thirdParty.Add(newEmote); + } } - Console.WriteLine("Output third party emote count: " + chatRoot.embeddedData.firstParty.Count); + progress.Report(new ProgressReport() { reportType = ReportType.Message, data = string.Format("Input third party emote count: {0}. Output count: {1}", inputCount, chatRoot.embeddedData.thirdParty.Count) }); // Twitch badges if (chatRoot.embeddedData.twitchBadges == null || updateOptions.UpdateOldEmbeds) { chatRoot.embeddedData.twitchBadges = new List(); } - Console.WriteLine("Input twitch badge count: " + chatRoot.embeddedData.twitchBadges.Count); - List twitchBadges = new List(); - twitchBadges = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, cacheFolder, embeddedData: chatRoot.embeddedData)).Result; - foreach (ChatBadge badge in twitchBadges) + inputCount = chatRoot.embeddedData.twitchBadges.Count; + foreach (ChatBadge badge in badgeList) { EmbedChatBadge newBadge = new EmbedChatBadge(); newBadge.name = badge.Name; newBadge.versions = badge.VersionsData; - chatRoot.embeddedData.twitchBadges.Add(newBadge); + if (!chatRoot.embeddedData.twitchBadges.Any(x => x.name.Equals(newBadge.name))) + { + chatRoot.embeddedData.twitchBadges.Add(newBadge); + } } - Console.WriteLine("Output twitch badge count: " + chatRoot.embeddedData.twitchBadges.Count); + progress.Report(new ProgressReport() { reportType = ReportType.Message, data = string.Format("Input badge count: {0}. Output count: {1}", inputCount, chatRoot.embeddedData.twitchBadges.Count) }); // Twitch bits / cheers if (chatRoot.embeddedData.twitchBits == null || updateOptions.UpdateOldEmbeds) { chatRoot.embeddedData.twitchBits = new List(); } - Console.WriteLine("Input twitch bit count: " + chatRoot.embeddedData.twitchBits.Count); - List twitchBits = new List(); - twitchBits = Task.Run(() => TwitchHelper.GetBits(cacheFolder, chatRoot.streamer.id.ToString(), embeddedData: chatRoot.embeddedData)).Result; - foreach (CheerEmote bit in twitchBits) + inputCount = chatRoot.embeddedData.twitchBits.Count; + foreach (CheerEmote bit in bitList) { EmbedCheerEmote newBit = new EmbedCheerEmote(); newBit.prefix = bit.prefix; @@ -110,9 +118,12 @@ public async Task UpdateAsync(IProgress progress, CancellationTo newEmote.height = emotePair.Value.Height / emotePair.Value.ImageScale; newBit.tierList.Add(emotePair.Key, newEmote); } - chatRoot.embeddedData.twitchBits.Add(newBit); + if (!chatRoot.embeddedData.twitchBits.Any(x => x.prefix.Equals(newBit.prefix))) + { + chatRoot.embeddedData.twitchBits.Add(newBit); + } } - Console.WriteLine("Input twitch bit count: " + chatRoot.embeddedData.twitchBits.Count); + progress.Report(new ProgressReport() { reportType = ReportType.Message, data = string.Format("Input cheermote emote count: {0}. Output count: {1}", inputCount, chatRoot.embeddedData.twitchBits.Count) }); // Finally save the output to file! // TODO: maybe in the future we could also export as HTML here too? @@ -120,17 +131,28 @@ public async Task UpdateAsync(IProgress progress, CancellationTo { using (TextWriter writer = File.CreateText(updateOptions.OutputFile)) { - var serializer = new Newtonsoft.Json.JsonSerializer(); + var serializer = new JsonSerializer(); serializer.Serialize(writer, chatRoot); } } + } + + public async Task GetDataToEmbed() + { + Task> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, updateOptions.TempFolder, chatRoot.embeddedData)); + Task> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, updateOptions.TempFolder, chatRoot.embeddedData, updateOptions.BttvEmotes, updateOptions.FfzEmotes, updateOptions.StvEmotes)); + Task> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, updateOptions.TempFolder, chatRoot.embeddedData)); + Task> bitTask = Task.Run(() => TwitchHelper.GetBits(updateOptions.TempFolder, chatRoot.streamer.id.ToString(), chatRoot.embeddedData)); + + await Task.WhenAll(emoteTask, emoteThirdTask, badgeTask, bitTask); - // Clear our working directory, it's highly unlikely we would reuse it anyways - if (Directory.Exists(cacheFolder)) - Directory.Delete(cacheFolder, true); + firstPartyEmoteList = emoteTask.Result; + thirdPartyEmoteList = emoteThirdTask.Result; + badgeList = badgeTask.Result; + bitList = bitTask.Result; } - public async Task ParseJson() + public async Task ParseJsonAsync() { chatRoot = await ChatJsonParser.ParseJsonStatic(updateOptions.InputFile);