Skip to content

Commit

Permalink
Fixed all embeds being doubled with arg --embed-missing, fixed crash …
Browse files Browse the repository at this point in the history
…when embeddedData is null, made embed fetching async
  • Loading branch information
ScrubN committed Nov 21, 2022
1 parent b2e87a5 commit 4867ddb
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 64 deletions.
2 changes: 1 addition & 1 deletion TwitchDownloaderCLI/Modes/UpdateChat.cs
Expand Up @@ -61,7 +61,7 @@ internal static void Update(ChatUpdateArgs inputOptions)
ChatUpdater chatUpdater = new(updateOptions);
Progress<ProgressReport> progress = new();
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged;
chatUpdater.ParseJson().Wait();
chatUpdater.ParseJsonAsync().Wait();
chatUpdater.UpdateAsync(progress, new CancellationToken()).Wait();
}
}
Expand Down
19 changes: 5 additions & 14 deletions TwitchDownloaderCore/ChatDownloader.cs
Expand Up @@ -325,14 +325,10 @@ public async Task DownloadAsync(IProgress<ProgressReport> 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<EmbedEmoteData> firstPartyReturnList = new List<EmbedEmoteData>();
List<EmbedEmoteData> thirdPartyReturnList = new List<EmbedEmoteData>();
List<EmbedChatBadge> badgesReturnList = new List<EmbedChatBadge>();
List<EmbedCheerEmote> bitsReturnList = new List<EmbedCheerEmote>();

List<TwitchEmote> thirdPartyEmotes = new List<TwitchEmote>();
List<TwitchEmote> firstPartyEmotes = new List<TwitchEmote>();
Expand All @@ -353,7 +349,7 @@ public async Task DownloadAsync(IProgress<ProgressReport> 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)
{
Expand All @@ -363,14 +359,14 @@ public async Task DownloadAsync(IProgress<ProgressReport> 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)
{
Expand All @@ -388,13 +384,8 @@ public async Task DownloadAsync(IProgress<ProgressReport> 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)
Expand Down
24 changes: 18 additions & 6 deletions TwitchDownloaderCore/ChatRenderer.cs
Expand Up @@ -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<ProgressReport> progress, CancellationToken cancellationToken)
{
progress.Report(new ProgressReport() { reportType = ReportType.Message, data = "Fetching Images" });
Task<List<ChatBadge>> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, offline: renderOptions.Offline));
Task<List<TwitchEmote>> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, renderOptions.TempFolder, chatRoot.embeddedData, offline: renderOptions.Offline));
Task<List<TwitchEmote>> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.BttvEmotes, renderOptions.FfzEmotes, renderOptions.StvEmotes, offline: renderOptions.Offline));
Task<List<CheerEmote>> cheerTask = Task.Run(() => TwitchHelper.GetBits(renderOptions.TempFolder, chatRoot.streamer.id.ToString(), chatRoot.embeddedData, offline: renderOptions.Offline));
Task<List<ChatBadge>> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.Offline));
Task<List<TwitchEmote>> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.Offline));
Task<List<TwitchEmote>> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, renderOptions.TempFolder, chatRoot.embeddedData, renderOptions.BttvEmotes, renderOptions.FfzEmotes, renderOptions.StvEmotes, renderOptions.Offline));
Task<List<CheerEmote>> cheerTask = Task.Run(() => TwitchHelper.GetBits(renderOptions.TempFolder, chatRoot.streamer.id.ToString(), chatRoot.embeddedData, renderOptions.Offline));
Task<Dictionary<string, SKBitmap>> emojiTask = Task.Run(() => TwitchHelper.GetTwitterEmojis(renderOptions.TempFolder));

await Task.WhenAll(badgeTask, emoteTask, emoteThirdTask, cheerTask, emojiTask);
Expand All @@ -58,6 +58,14 @@ public async Task RenderVideoAsync(IProgress<ProgressReport> 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);

Expand Down Expand Up @@ -112,9 +120,13 @@ private void FloorCommentOffsets(List<Comment> 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;
}
}
}

Expand Down
108 changes: 65 additions & 43 deletions 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;
Expand All @@ -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<TwitchEmote> firstPartyEmoteList = new();
private List<TwitchEmote> thirdPartyEmoteList = new();
private List<ChatBadge> badgeList = new();
private List<CheerEmote> 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<ProgressReport> 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<EmbedEmoteData>();
chatRoot.embeddedData.firstParty = new List<EmbedEmoteData>();
}
Console.WriteLine("Input third party emote count: " + chatRoot.embeddedData.thirdParty.Count);
List<TwitchEmote> thirdPartyEmotes = new List<TwitchEmote>();
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<EmbedEmoteData>();
chatRoot.embeddedData.thirdParty = new List<EmbedEmoteData>();
}
Console.WriteLine("Input first party emote count: " + chatRoot.embeddedData.firstParty.Count);
List<TwitchEmote> firstPartyEmotes = new List<TwitchEmote>();
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<EmbedChatBadge>();
}
Console.WriteLine("Input twitch badge count: " + chatRoot.embeddedData.twitchBadges.Count);
List<ChatBadge> twitchBadges = new List<ChatBadge>();
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<EmbedCheerEmote>();
}
Console.WriteLine("Input twitch bit count: " + chatRoot.embeddedData.twitchBits.Count);
List<CheerEmote> twitchBits = new List<CheerEmote>();
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;
Expand All @@ -110,27 +118,41 @@ public async Task UpdateAsync(IProgress<ProgressReport> 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?
if (updateOptions.FileFormat == DownloadFormat.Json)
{
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<List<TwitchEmote>> emoteTask = Task.Run(() => TwitchHelper.GetEmotes(chatRoot.comments, updateOptions.TempFolder, chatRoot.embeddedData));
Task<List<TwitchEmote>> emoteThirdTask = Task.Run(() => TwitchHelper.GetThirdPartyEmotes(chatRoot.streamer.id, updateOptions.TempFolder, chatRoot.embeddedData, updateOptions.BttvEmotes, updateOptions.FfzEmotes, updateOptions.StvEmotes));
Task<List<ChatBadge>> badgeTask = Task.Run(() => TwitchHelper.GetChatBadges(chatRoot.streamer.id, updateOptions.TempFolder, chatRoot.embeddedData));
Task<List<CheerEmote>> 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<ChatRoot> ParseJson()
public async Task<ChatRoot> ParseJsonAsync()
{
chatRoot = await ChatJsonParser.ParseJsonStatic(updateOptions.InputFile);

Expand Down

0 comments on commit 4867ddb

Please sign in to comment.