From 96601ee958e77504ca5dcfdec8d0a94a35b52c1b Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Fri, 26 Feb 2021 17:08:05 -0700 Subject: [PATCH] Optimize the gallery image loaders --- .../ModListRegistry/ModListMetadata.cs | 15 ++------- Wabbajack/Util/UIUtils.cs | 32 ++++++++++++++++--- .../View Models/Gallery/ModListMetadataVM.cs | 2 +- Wabbajack/Views/ModListTileView.xaml.cs | 10 +++--- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Wabbajack.Lib/ModListRegistry/ModListMetadata.cs b/Wabbajack.Lib/ModListRegistry/ModListMetadata.cs index 7504a877..e9444a90 100644 --- a/Wabbajack.Lib/ModListRegistry/ModListMetadata.cs +++ b/Wabbajack.Lib/ModListRegistry/ModListMetadata.cs @@ -56,19 +56,8 @@ namespace Wabbajack.Lib.ModListRegistry [JsonName("Links")] public class LinksObject { - [JsonProperty("image")] public string ImageUri { get; set; } = string.Empty; - - [JsonIgnore] - public string ImageUrlFast - { - get - { - if (ImageUri.StartsWith("https://raw.githubusercontent.com/wabbajack-tools/mod-lists/")) - return ImageUri.Replace("https://raw.githubusercontent.com/wabbajack-tools/mod-lists/", - "https://mod-lists.wabbajack.org/"); - return ImageUri; - } - } + [JsonProperty("image")] + public string ImageUri { get; set; } = string.Empty; [JsonProperty("readme")] public string Readme { get; set; } = string.Empty; diff --git a/Wabbajack/Util/UIUtils.cs b/Wabbajack/Util/UIUtils.cs index fd135202..e845083c 100644 --- a/Wabbajack/Util/UIUtils.cs +++ b/Wabbajack/Util/UIUtils.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Media.Imaging; +using SharpDX.Text; using Wabbajack.Common; namespace Wabbajack @@ -85,14 +86,19 @@ namespace Wabbajack { try { + var (found, mstream) = await FindCachedImage(url); + if (found) return mstream; + var ret = new MemoryStream(); using (var client = new HttpClient()) - using (var stream = await client.GetStreamAsync(url)) + await using (var stream = await client.GetStreamAsync(url)) { - stream.CopyTo(ret); + await stream.CopyToAsync(ret); } ret.Seek(0, SeekOrigin.Begin); + + await WriteCachedImage(url, ret.ToArray()); return ret; } catch (Exception ex) @@ -101,7 +107,6 @@ namespace Wabbajack return default; } }) - .ObserveOnGuiThread() .Select(memStream => { if (memStream == null) return default; @@ -118,7 +123,26 @@ namespace Wabbajack { memStream.Dispose(); } - }); + }) + .ObserveOnGuiThread(); + } + + private static async Task WriteCachedImage(string url, byte[] data) + { + var folder = Consts.LocalAppDataPath.Combine("ModListImages"); + if (!folder.Exists) folder.CreateDirectory(); + + var path = folder.Combine(Encoding.UTF8.GetBytes(url).xxHash().ToHex()); + await path.WriteAllBytesAsync(data); + } + + private static async Task<(bool Found, MemoryStream data)> FindCachedImage(string uri) + { + var folder = Consts.LocalAppDataPath.Combine("ModListImages"); + if (!folder.Exists) folder.CreateDirectory(); + + var path = folder.Combine(Encoding.UTF8.GetBytes(uri).xxHash().ToHex()); + return path.Exists ? (true, new MemoryStream(await path.ReadAllBytesAsync())) : (false, default); } /// diff --git a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs index 13ef795c..bfa3f715 100644 --- a/Wabbajack/View Models/Gallery/ModListMetadataVM.cs +++ b/Wabbajack/View Models/Gallery/ModListMetadataVM.cs @@ -161,7 +161,7 @@ namespace Wabbajack }) .ToGuiProperty(this, nameof(Exists)); - var imageObs = Observable.Return(Metadata.Links.ImageUrlFast) + var imageObs = Observable.Return(Metadata.Links.ImageUri) .DownloadBitmapImage((ex) => Utils.Log($"Error downloading modlist image {Metadata.Title}")); _Image = imageObs diff --git a/Wabbajack/Views/ModListTileView.xaml.cs b/Wabbajack/Views/ModListTileView.xaml.cs index 787e6a35..a12d04c3 100644 --- a/Wabbajack/Views/ModListTileView.xaml.cs +++ b/Wabbajack/Views/ModListTileView.xaml.cs @@ -37,13 +37,15 @@ namespace Wabbajack .BindToStrict(this, x => x.DownloadProgressBar.Value) .DisposeWith(dispose); this.WhenAny(x => x.ViewModel.Metadata) - .Where(x => !x.ImageContainsTitle) - .Select(x => x.Title) + .CombineLatest(this.WhenAny(x => x.ViewModel.IsBroken)) + .Where(x => !x.First.ImageContainsTitle || x.Second) + .Select(x => x.First.Title) .BindToStrict(this, x => x.DescriptionTextShadow.Text) .DisposeWith(dispose); this.WhenAny(x => x.ViewModel.Metadata) - .Where(x => !x.ImageContainsTitle) - .Select(x => x.Title) + .CombineLatest(this.WhenAny(x => x.ViewModel.IsBroken)) + .Where(x => !x.First.ImageContainsTitle || x.Second) + .Select(x => x.First.Title) .BindToStrict(this, x => x.ModListTitleShadow.Text) .DisposeWith(dispose);