Skip to content

Commit

Permalink
WPF Chat Updater Fatal Error Fixes (#803)
Browse files Browse the repository at this point in the history
* Do not enable chatupdater buttons until chat json is fully loaded & catch deserialization exceptions.
Fixes a crash when loading large chats

* Catch potential exceptions when setting up chatupdater video/clip info & fix potential for thumbnail image to not update between chats

* Fix potential false positive success from TryGetThumb

* Document exceptions from ChatJson.DeserializeAsync
  • Loading branch information
ScrubN authored Aug 30, 2023
1 parent eb601f8 commit e6f957c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 64 deletions.
2 changes: 2 additions & 0 deletions TwitchDownloaderCore/Chat/ChatJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public static class ChatJson
/// Asynchronously deserializes a chat json file.
/// </summary>
/// <returns>A <see cref="ChatRoot"/> representation the deserialized chat json file.</returns>
/// <exception cref="IOException">The file does not exist.</exception>
/// <exception cref="NotSupportedException">The file is not a valid chat format.</exception>
public static async Task<ChatRoot> DeserializeAsync(string filePath, bool getComments = true, bool getEmbeds = true, CancellationToken cancellationToken = new())
{
ArgumentNullException.ThrowIfNull(filePath, nameof(filePath));
Expand Down
139 changes: 78 additions & 61 deletions TwitchDownloaderWPF/PageChatUpdate.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,35 @@ private async void btnBrowse_Click(object sender, RoutedEventArgs e)

textJson.Text = openFileDialog.FileName;
InputFile = openFileDialog.FileName;
SetEnabled(true);
ChatJsonInfo = null;
imgThumbnail.Source = null;
SetEnabled(false);

if (Path.GetExtension(InputFile)!.ToLower() is not ".json" and not ".gz")
{
textJson.Text = "";
InputFile = "";
return;
}

try
{
ChatJsonInfo = await ChatJson.DeserializeAsync(InputFile, true, false, CancellationToken.None);
ChatJsonInfo.comments.RemoveRange(1, ChatJsonInfo.comments.Count - 2);
GC.Collect();
}
catch (Exception ex)
{
AppendLog(Translations.Strings.ErrorLog + ex.Message);
if (Settings.Default.VerboseErrors)
{
MessageBox.Show(ex.ToString(), Translations.Strings.VerboseErrorOutput, MessageBoxButton.OK, MessageBoxImage.Error);
}

return;
}

ChatJsonInfo = await ChatJson.DeserializeAsync(InputFile, true, false, CancellationToken.None);
ChatJsonInfo.comments.RemoveRange(1, ChatJsonInfo.comments.Count - 2);
GC.Collect();
SetEnabled(true);

var videoCreatedAt = ChatJsonInfo.video.created_at == default
? ChatJsonInfo.comments[0].created_at - TimeSpan.FromSeconds(ChatJsonInfo.comments[0].content_offset_seconds)
Expand Down Expand Up @@ -92,85 +111,83 @@ private async void btnBrowse_Click(object sender, RoutedEventArgs e)
ViewCount = ChatJsonInfo.video.viewCount;
Game = ChatJsonInfo.video.game ?? ChatJsonInfo.video.chapters.FirstOrDefault()?.gameDisplayName ?? "Unknown";

if (VideoId.All(char.IsDigit))
try
{
GqlVideoResponse videoInfo = await TwitchHelper.GetVideoInfo(int.Parse(VideoId));
if (videoInfo.data.video == null)
if (VideoId.All(char.IsDigit))
{
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail + ": " + Translations.Strings.VodExpiredOrIdCorrupt);
var (success, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
if (success)
GqlVideoResponse videoInfo = await TwitchHelper.GetVideoInfo(int.Parse(VideoId));
if (videoInfo.data.video == null)
{
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail + ": " + Translations.Strings.VodExpiredOrIdCorrupt);
var (_, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
imgThumbnail.Source = image;

numStartHour.Maximum = 48;
numEndHour.Maximum = 48;
}
numStartHour.Maximum = 48;
numEndHour.Maximum = 48;
}
else
{
VideoLength = TimeSpan.FromSeconds(videoInfo.data.video.lengthSeconds);
labelLength.Text = VideoLength.ToString("c");
numStartHour.Maximum = (int)VideoLength.TotalHours;
numEndHour.Maximum = (int)VideoLength.TotalHours;
ViewCount = videoInfo.data.video.viewCount;
Game = videoInfo.data.video.game?.displayName;

try
{
string thumbUrl = videoInfo.data.video.thumbnailURLs.FirstOrDefault();
imgThumbnail.Source = await ThumbnailService.GetThumb(thumbUrl);
}
catch
else
{
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail);
var (success, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
if (success)
VideoLength = TimeSpan.FromSeconds(videoInfo.data.video.lengthSeconds);
labelLength.Text = VideoLength.ToString("c");
numStartHour.Maximum = (int)VideoLength.TotalHours;
numEndHour.Maximum = (int)VideoLength.TotalHours;
ViewCount = videoInfo.data.video.viewCount;
Game = videoInfo.data.video.game?.displayName;

var thumbUrl = videoInfo.data.video.thumbnailURLs.FirstOrDefault();
var (success, image) = await ThumbnailService.TryGetThumb(thumbUrl);
if (!success)
{
imgThumbnail.Source = image;
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail);
(_, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
}
}
}
}
else
{
if (VideoId != "-1")
{
numStartHour.Maximum = 0;
numEndHour.Maximum = 0;
}
GqlClipResponse videoInfo = await TwitchHelper.GetClipInfo(VideoId);
if (videoInfo.data.clip.video == null)
{
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail + ": " + Translations.Strings.VodExpiredOrIdCorrupt);
var (success, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
if (success)
{

imgThumbnail.Source = image;
}
}
else
{
VideoLength = TimeSpan.FromSeconds(videoInfo.data.clip.durationSeconds);
labelLength.Text = VideoLength.ToString("c");
ViewCount = videoInfo.data.clip.viewCount;
Game = videoInfo.data.clip.game?.displayName;
if (VideoId != "-1")
{
numStartHour.Maximum = 0;
numEndHour.Maximum = 0;
}

try
GqlClipResponse videoInfo = await TwitchHelper.GetClipInfo(VideoId);
if (videoInfo.data.clip.video == null)
{
string thumbUrl = videoInfo.data.clip.thumbnailURL;
imgThumbnail.Source = await ThumbnailService.GetThumb(thumbUrl);
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail + ": " + Translations.Strings.VodExpiredOrIdCorrupt);
var (_, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
imgThumbnail.Source = image;
}
catch
else
{
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail);
var (success, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
if (success)
VideoLength = TimeSpan.FromSeconds(videoInfo.data.clip.durationSeconds);
labelLength.Text = VideoLength.ToString("c");
ViewCount = videoInfo.data.clip.viewCount;
Game = videoInfo.data.clip.game?.displayName;

var thumbUrl = videoInfo.data.clip.thumbnailURL;
var (success, image) = await ThumbnailService.TryGetThumb(thumbUrl);
if (!success)
{
imgThumbnail.Source = image;
AppendLog(Translations.Strings.ErrorLog + Translations.Strings.UnableToFindThumbnail);
(_, image) = await ThumbnailService.TryGetThumb(ThumbnailService.THUMBNAIL_MISSING_URL);
}

imgThumbnail.Source = image;
}
}
}
catch (Exception ex)
{
MessageBox.Show(Translations.Strings.UnableToGetInfoMessage, Translations.Strings.UnableToGetInfo, MessageBoxButton.OK, MessageBoxImage.Error);
AppendLog(Translations.Strings.ErrorLog + ex.Message);
if (Settings.Default.VerboseErrors)
{
MessageBox.Show(ex.ToString(), Translations.Strings.VerboseErrorOutput, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}

private void UpdateActionButtons(bool isUpdating)
Expand Down
12 changes: 9 additions & 3 deletions TwitchDownloaderWPF/Services/ThumbnailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ namespace TwitchDownloaderWPF.Services
public static class ThumbnailService
{
public const string THUMBNAIL_MISSING_URL = @"https://vod-secure.twitch.tv/_404/404_processing_320x180.png";
private static readonly HttpClient _httpClient = new();
private static readonly HttpClient HttpClient = new();

public static async Task<BitmapImage> GetThumb(string thumbUrl)
{
if (string.IsNullOrWhiteSpace(thumbUrl))
{
return null;
}

BitmapImage img = new BitmapImage();
img.CacheOption = BitmapCacheOption.OnLoad;
img.BeginInit();
img.StreamSource = await _httpClient.GetStreamAsync(thumbUrl);
img.StreamSource = await HttpClient.GetStreamAsync(thumbUrl);
img.EndInit();
return img;
}
Expand All @@ -23,7 +28,8 @@ public static async Task<BitmapImage> GetThumb(string thumbUrl)
{
try
{
return (true, await GetThumb(thumbUrl));
var thumb = await GetThumb(thumbUrl);
return (thumb != null, thumb);
}
catch
{
Expand Down

0 comments on commit e6f957c

Please sign in to comment.