#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; using UnityEngine; using UnityEngine.Networking; #if UNITY_2022_1_OR_NEWER using UnityEditor; #endif #region No ReShaper // ReSharper disable All // needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C# using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; // ReSharper restore All #endregion #pragma warning restore SA1512,SA1124 // Single-line comments should not be followed by blank line namespace NugetForUnity.Helper { /// /// Helper for NuGet package icon download handling. /// internal static class NugetPackageTextureHelper { /// /// Downloads an image at the given URL and converts it to a Unity Texture2D. /// /// The URL of the image to download. /// The image as a Unity Texture2D object. [ItemCanBeNull] internal static Task DownloadImageAsync([NotNull] string url) { try { #if UNITY_2022_1_OR_NEWER if (PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed && url.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) { // if insecure http url is not allowed try to use https. url = url.Replace("http://", "https://", StringComparison.OrdinalIgnoreCase); } #endif var fromCache = false; if (url.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) { // we only cache images coming from a remote server. fromCache = true; } else if (ExistsInDiskCache(url)) { url = "file:///" + GetFilePath(url); fromCache = true; } var taskCompletionSource = new TaskCompletionSource(); var request = UnityWebRequest.Get(url); { var downloadHandler = new DownloadHandlerTexture(false); request.downloadHandler = downloadHandler; request.timeout = 1; // 1 second var operation = request.SendWebRequest(); operation.completed += _ => { try { if (!string.IsNullOrEmpty(request.error)) { #if UNITY_2020_1_OR_NEWER NugetLogger.LogVerbose( "Downloading image {0} failed! Web error: {1}, Handler error: {2}.", url, request.error, downloadHandler.error); #else NugetLogger.LogVerbose("Downloading image {0} failed! Web error: {1}.", url, request.error); #endif taskCompletionSource.TrySetResult(null); return; } var result = downloadHandler.texture; if (result != null && !fromCache) { CacheTextureOnDisk(url, downloadHandler.data); } taskCompletionSource.TrySetResult(result); } finally { request.Dispose(); } }; return taskCompletionSource.Task; } } catch (Exception exception) { NugetLogger.LogVerbose("Error while downloading image from: '{0}' got error: {1}", url, exception); return Task.FromResult(null); } } private static void CacheTextureOnDisk([NotNull] string url, [NotNull] byte[] bytes) { var diskPath = GetFilePath(url); File.WriteAllBytes(diskPath, bytes); } private static bool ExistsInDiskCache([NotNull] string url) { return File.Exists(GetFilePath(url)); } [NotNull] private static string GetFilePath([NotNull] string url) { return Path.Combine(Application.temporaryCachePath, GetHash(url)); } [SuppressMessage("Design", "CA5351", Justification = "Only use MD5 hash as cache key / not securty relevant.")] [NotNull] private static string GetHash([NotNull] string s) { if (string.IsNullOrEmpty(s)) { throw new ArgumentNullException(nameof(s)); } using (var md5 = new MD5CryptoServiceProvider()) { var data = md5.ComputeHash(Encoding.Default.GetBytes(s)); return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_').Replace("=", null); } } } }