From 3882b6451ed908216ea574b27c4730c7569794ef Mon Sep 17 00:00:00 2001 From: "Romerio J." Date: Sat, 14 Mar 2026 09:50:29 -0300 Subject: [PATCH] Upgrade Davinci library to version 2.0 Refactoring of the DaVinci image download library to version 2.0 with improvements in request handling and cache management. Added function to obtain the size of the cache folder. Added support for multiple requests. Added support for UnityWebRequest. Added downloaded sprite name based on the URL. Modified ".start()" to ".Init()" in the constructor. Revised code removing redundancies, obsolete code, and incorrect naming conventions. The DaVinci object is no longer destroyed at the end of each request. DaVinci persists after the first request, eliminating garbage collection with instantiate and destroy. --- Davinci/Assets/Davinci/Scripts/Davinci.cs | 715 +++++----------------- 1 file changed, 163 insertions(+), 552 deletions(-) diff --git a/Davinci/Assets/Davinci/Scripts/Davinci.cs b/Davinci/Assets/Davinci/Scripts/Davinci.cs index 3932a00..9ccce7c 100644 --- a/Davinci/Assets/Davinci/Scripts/Davinci.cs +++ b/Davinci/Assets/Davinci/Scripts/Davinci.cs @@ -2,658 +2,269 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Security.Cryptography; using System.Text; using UnityEngine; using UnityEngine.Events; -using UnityEngine.UI; -#if UNITY_2018_3_OR_NEWER using UnityEngine.Networking; -#endif +using UnityEngine.UI; /// /// Davinci - A powerful, esay-to-use image downloading and caching library for Unity in Run-Time -/// v 1.2 +/// v 2.0 /// Developed by ShamsDEV.com +/// Modified by NyanHeavy.app /// copyright (c) ShamsDEV.com All Rights Reserved. /// Licensed under the MIT License. /// https://github.com/shamsdev/davinci /// public class Davinci : MonoBehaviour { - private static bool ENABLE_GLOBAL_LOGS = true; - - private bool enableLog = false; - private float fadeTime = 1; - private bool cached = true; + private static Davinci _instance; + private static string filePath; - private enum RendererType + public static Davinci Get() { - none, - uiImage, - renderer, - sprite - } - - private RendererType rendererType = RendererType.none; - private GameObject targetObj; - private string url = null; - - private Texture2D loadingPlaceholder, errorPlaceholder; - - private UnityAction onStartAction, - onDownloadedAction, - OnLoadedAction, - onEndAction; - - private UnityAction onDownloadProgressChange; - private UnityAction onErrorAction; - - private static Dictionary underProcessDavincies - = new Dictionary(); - - private string uniqueHash; - private int progress; - - private bool success = false; - - static string filePath = Application.persistentDataPath + "/" + - "davinci" + "/"; - + if (_instance == null) + { + GameObject go = new("Davinci"); + _instance = go.AddComponent(); + DontDestroyOnLoad(go); + filePath = Path.Combine(Application.persistentDataPath, "davinci"); + if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath); + } - /// - /// Get instance of davinci class - /// - public static Davinci get() - { - return new GameObject("Davinci").AddComponent(); + //Return a NEW request instead of resetting a single instance + return _instance; } /// - /// Set image url for download. + /// Entry point for creating a new individual request. /// - /// Image Url - /// - public Davinci load(string url) + public DavinciRequest Download(string url) { - if (enableLog) - Debug.Log("[Davinci] Url set : " + url); - - this.url = url; - return this; + return new DavinciRequest(this, url, filePath); } - /// - /// Set fading animation time. - /// - /// Fade animation time. Set 0 for disable fading. - /// - public Davinci setFadeTime(float fadeTime) - { - if (enableLog) - Debug.Log("[Davinci] Fading time set : " + fadeTime); + // Shortcut to maintain compatibility with your old code if desired. + public DavinciRequest Load(string url) => Download(url); - this.fadeTime = fadeTime; - return this; + public static string CreateMD5(string input) + { + using MD5 md5 = MD5.Create(); + byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); + StringBuilder sb = new(); + for (int i = 0; i < hashBytes.Length; i++) + { + sb.Append(hashBytes[i].ToString("x2")); + } + return sb.ToString(); } - /// - /// Set target Image component. - /// - /// target Unity UI image component - /// - public Davinci into(Image image) + public static void ClearAllCachedFiles() { - if (enableLog) - Debug.Log("[Davinci] Target as UIImage set : " + image); - - rendererType = RendererType.uiImage; - this.targetObj = image.gameObject; - return this; + if (Directory.Exists(filePath)) Directory.Delete(filePath, true); } /// - /// Set target Renderer component. + /// Return size of Davinci Cache files /// - /// target renderer component - /// - public Davinci into(Renderer renderer) + public static string GetCacheSize() { - if (enableLog) - Debug.Log("[Davinci] Target as Renderer set : " + renderer); - - rendererType = RendererType.renderer; - this.targetObj = renderer.gameObject; - return this; + filePath = Path.Combine(Application.persistentDataPath, "davinci"); + if (!Directory.Exists(filePath)) return "0 KBs"; + long bytes = GetDirectorySize(new DirectoryInfo(filePath)); + return FormatBytes(bytes); } - public Davinci into(SpriteRenderer spriteRenderer) + private static long GetDirectorySize(DirectoryInfo dir) { - if (enableLog) - Debug.Log("[Davinci] Target as SpriteRenderer set : " + spriteRenderer); - - rendererType = RendererType.sprite; - this.targetObj = spriteRenderer.gameObject; - return this; - } - - #region Actions - public Davinci withStartAction(UnityAction action) - { - this.onStartAction = action; - - if (enableLog) - Debug.Log("[Davinci] On start action set : " + action); - - return this; + long size = 0; + foreach (FileInfo file in dir.GetFiles()) + { + size += file.Length; + } + return size; } - public Davinci withDownloadedAction(UnityAction action) + private static string FormatBytes(long bytes) { - this.onDownloadedAction = action; + string[] Suffix = { "Bytes", "KB", "MB", "GB", "TB" }; + int i; + double dblSByte = bytes; - if (enableLog) - Debug.Log("[Davinci] On downloaded action set : " + action); + for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024) + { + dblSByte = bytes / 1024.0; + } - return this; + return string.Format("{0:0.##} {1}", dblSByte, Suffix[i]); } +} - public Davinci withDownloadProgressChangedAction(UnityAction action) - { - this.onDownloadProgressChange = action; - - if (enableLog) - Debug.Log("[Davinci] On download progress changed action set : " + action); - - return this; - } +/// +/// A class that stores the data for a specific request. +/// +public class DavinciRequest +{ + private Davinci manager; + private string url; + private string cachePath; - public Davinci withLoadedAction(UnityAction action) - { - this.OnLoadedAction = action; + private bool enableLog = false; + private float fadeTime = 0f; + private bool cached = true; + private GameObject targetObj; + private Texture2D loadingPlaceholder, errorPlaceholder; + private RendererType rendererType = RendererType.none; - if (enableLog) - Debug.Log("[Davinci] On loaded action set : " + action); + private UnityAction onStartAction, onDownloadedAction, onLoadedAction, onEndAction; + private UnityAction onDownloadProgressChange; + private UnityAction onErrorAction; - return this; - } + private enum RendererType { none, uiImage, renderer } - public Davinci withErrorAction(UnityAction action) + public DavinciRequest(Davinci manager, string url, string cacheFolder) { - this.onErrorAction = action; - - if (enableLog) - Debug.Log("[Davinci] On error action set : " + action); - - return this; + this.manager = manager; + this.url = url; + this.cachePath = cacheFolder; } - public Davinci withEndAction(UnityAction action) - { - this.onEndAction = action; - - if (enableLog) - Debug.Log("[Davinci] On end action set : " + action); - - return this; - } + #region Settings + public DavinciRequest Into(Image image) { rendererType = RendererType.uiImage; targetObj = image.gameObject; return this; } + public DavinciRequest Into(Renderer renderer) { rendererType = RendererType.renderer; targetObj = renderer.gameObject; return this; } + public DavinciRequest SetFadeTime(float time) { fadeTime = time; return this; } + public DavinciRequest SetCached(bool enable) { cached = enable; return this; } + public DavinciRequest SetEnableLog(bool enable) { enableLog = enable; return this; } + public DavinciRequest SetLoadingPlaceholder(Texture2D p) { loadingPlaceholder = p; return this; } + public DavinciRequest SetErrorPlaceholder(Texture2D p) { errorPlaceholder = p; return this; } + public DavinciRequest WithStartAction(UnityAction a) { onStartAction = a; return this; } + public DavinciRequest WithLoadedAction(UnityAction a) { onLoadedAction = a; return this; } + public DavinciRequest WithErrorAction(UnityAction a) { onErrorAction = a; return this; } + public DavinciRequest WithEndAction(UnityAction a) { onEndAction = a; return this; } #endregion - /// - /// Show or hide logs in console. - /// - /// 'true' for show logs in console. - /// - public Davinci setEnableLog(bool enableLog) - { - this.enableLog = enableLog; - - if (enableLog) - Debug.Log("[Davinci] Logging enabled : " + enableLog); - - return this; - } - - /// - /// Set the sprite of image when davinci is downloading and loading image - /// - /// loading texture - /// - public Davinci setLoadingPlaceholder(Texture2D loadingPlaceholder) + public void Init() { - this.loadingPlaceholder = loadingPlaceholder; - - if (enableLog) - Debug.Log("[Davinci] Loading placeholder has been set."); + if (string.IsNullOrEmpty(url)) { Error("Empty URL."); return; } + if (targetObj == null) { Error("Target null."); return; } - return this; - } - - /// - /// Set image sprite when some error occurred during downloading or loading image - /// - /// error texture - /// - public Davinci setErrorPlaceholder(Texture2D errorPlaceholder) - { - this.errorPlaceholder = errorPlaceholder; - - if (enableLog) - Debug.Log("[Davinci] Error placeholder has been set."); - - return this; - } - - /// - /// Enable cache - /// - /// - public Davinci setCached(bool cached) - { - this.cached = cached; + string uniqueHash = Davinci.CreateMD5(url); + string fullPath = Path.Combine(cachePath, uniqueHash); - if (enableLog) - Debug.Log("[Davinci] Cache enabled : " + cached); + if (loadingPlaceholder != null) SetPlaceholder(loadingPlaceholder); + onStartAction?.Invoke(); - return this; + // Initiates coroutine in the global instance of Da Vinci, allowing multiple instances. + manager.StartCoroutine(ProcessImage(fullPath)); } - /// - /// Start davinci process. - /// - public void start() + private IEnumerator ProcessImage(string fullPath) { - if (url == null) - { - error("Url has not been set. Use 'load' funtion to set image url."); - return; - } - - try - { - Uri uri = new Uri(url); - this.url = uri.AbsoluteUri; - } - catch (Exception ex) - { - error("Url is not correct."); - return; - } - - if (rendererType == RendererType.none || targetObj == null) - { - error("Target has not been set. Use 'into' function to set target component."); - return; - } - - if (enableLog) - Debug.Log("[Davinci] Start Working."); - - if (loadingPlaceholder != null) - SetLoadingImage(); - - if (onStartAction != null) - onStartAction.Invoke(); - - if (!Directory.Exists(filePath)) - { - Directory.CreateDirectory(filePath); - } - - uniqueHash = CreateMD5(url); - - if (underProcessDavincies.ContainsKey(uniqueHash)) + yield return new WaitForEndOfFrame(); + if (File.Exists(fullPath) && cached) { - Davinci sameProcess = underProcessDavincies[uniqueHash]; - sameProcess.onDownloadedAction += () => - { - if (onDownloadedAction != null) - onDownloadedAction.Invoke(); - - loadSpriteToImage(); - }; + if (enableLog) Debug.Log($"[Davinci] Cache found: {url}"); + yield return LoadFromDisk(fullPath); } else { - if (File.Exists(filePath + uniqueHash)) - { - if (onDownloadedAction != null) - onDownloadedAction.Invoke(); - - loadSpriteToImage(); - } - else - { - underProcessDavincies.Add(uniqueHash, this); - StopAllCoroutines(); - StartCoroutine("Downloader"); - } + yield return DownloadFromWeb(fullPath); } } - private IEnumerator Downloader() + private IEnumerator DownloadFromWeb(string savePath) { - if (enableLog) - Debug.Log("[Davinci] Download started."); + using UnityWebRequest www = UnityWebRequestTexture.GetTexture(url); + UnityWebRequestAsyncOperation operation = www.SendWebRequest(); -#if UNITY_2018_3_OR_NEWER - UnityWebRequest www = UnityWebRequestTexture.GetTexture(url); - yield return www.SendWebRequest(); -#else - var www = new WWW(url); -#endif - - while (!www.isDone) + while (!operation.isDone) { - if (www.error != null) - { - error("Error while downloading the image : " + www.error); - yield break; - } - -#if UNITY_2018_3_OR_NEWER - progress = Mathf.FloorToInt(www.downloadProgress * 100); -#else - progress = Mathf.FloorToInt(www.progress * 100); -#endif - if (onDownloadProgressChange != null) - onDownloadProgressChange.Invoke(progress); - - if (enableLog) - Debug.Log("[Davinci] Downloading progress : " + progress + "%"); - + int progress = Mathf.FloorToInt(www.downloadProgress * 100); + onDownloadProgressChange?.Invoke(progress); yield return null; } -#if UNITY_2018_3_OR_NEWER - if (www.error == null) - File.WriteAllBytes(filePath + uniqueHash, www.downloadHandler.data); -#else - if (www.error == null) - File.WriteAllBytes(filePath + uniqueHash, www.bytes); -#endif - - www.Dispose(); - www = null; - - if (onDownloadedAction != null) - onDownloadedAction.Invoke(); - - loadSpriteToImage(); - - underProcessDavincies.Remove(uniqueHash); - } - - private void loadSpriteToImage() - { - progress = 100; - if (onDownloadProgressChange != null) - onDownloadProgressChange.Invoke(progress); - - if (enableLog) - Debug.Log("[Davinci] Downloading progress : " + progress + "%"); - - if (!File.Exists(filePath + uniqueHash)) + if (www.result != UnityWebRequest.Result.Success) { - error("Loading image file has been failed."); - return; + Error($"Download Error: {www.error}"); + yield break; } - StopAllCoroutines(); - StartCoroutine(ImageLoader()); + if (cached) File.WriteAllBytes(savePath, www.downloadHandler.data); + onDownloadedAction?.Invoke(); + Texture2D tex = DownloadHandlerTexture.GetContent(www); + yield return ApplyTexture(tex); } - private void SetLoadingImage() + private IEnumerator LoadFromDisk(string path) { - switch (rendererType) - { - case RendererType.renderer: - Renderer renderer = targetObj.GetComponent(); - renderer.material.mainTexture = loadingPlaceholder; - break; - - case RendererType.uiImage: - Image image = targetObj.GetComponent(); - Sprite sprite = Sprite.Create(loadingPlaceholder, - new Rect(0, 0, loadingPlaceholder.width, loadingPlaceholder.height), - new Vector2(0.5f, 0.5f)); - image.sprite = sprite; - - break; - - case RendererType.sprite: - SpriteRenderer spriteRenderer = targetObj.GetComponent(); - Sprite spriteImage = Sprite.Create(loadingPlaceholder, - new Rect(0, 0, loadingPlaceholder.width, loadingPlaceholder.height), - new Vector2(0.5f, 0.5f)); - - spriteRenderer.sprite = spriteImage; - break; - } - + byte[] data = File.ReadAllBytes(path); + Texture2D tex = new(2, 2); + tex.LoadImage(data); + yield return ApplyTexture(tex); } - private IEnumerator ImageLoader(Texture2D texture = null) + private IEnumerator ApplyTexture(Texture2D tex) { - if (enableLog) - Debug.Log("[Davinci] Start loading image."); + if (targetObj == null) yield break; - if (texture == null) + if (rendererType == RendererType.uiImage && targetObj.TryGetComponent(out var img)) { - byte[] fileData; - fileData = File.ReadAllBytes(filePath + uniqueHash); - texture = new Texture2D(2, 2); - //ImageConversion.LoadImage(texture, fileData); - texture.LoadImage(fileData); //..this will auto-resize the texture dimensions. + img.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); + img.sprite.name = GetFileNameFromUrl(url); + onLoadedAction?.Invoke(); + if (fadeTime > 0) yield return FadeEffect(img); } - - Color color; - - if (targetObj != null) - switch (rendererType) - { - case RendererType.renderer: - Renderer renderer = targetObj.GetComponent(); - - if (renderer == null || renderer.material == null) - break; - - renderer.material.mainTexture = texture; - float maxAlpha; - - if (fadeTime > 0 && renderer.material.HasProperty("_Color")) - { - color = renderer.material.color; - maxAlpha = color.a; - - color.a = 0; - - renderer.material.color = color; - float time = Time.time; - while (color.a < maxAlpha) - { - color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); - - if (renderer != null) - renderer.material.color = color; - - yield return null; - } - } - - break; - - case RendererType.uiImage: - Image image = targetObj.GetComponent(); - - if (image == null) - break; - - Sprite sprite = Sprite.Create(texture, - new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); - - image.sprite = sprite; - color = image.color; - maxAlpha = color.a; - - if (fadeTime > 0) - { - color.a = 0; - image.color = color; - - float time = Time.time; - while (color.a < maxAlpha) - { - color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); - - if (image != null) - image.color = color; - yield return null; - } - } - break; - - case RendererType.sprite: - SpriteRenderer spriteRenderer = targetObj.GetComponent(); - - if (spriteRenderer == null) - break; - - Sprite spriteImage = Sprite.Create(texture, - new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); - - spriteRenderer.sprite = spriteImage; - color = spriteRenderer.color; - maxAlpha = color.a; - - if (fadeTime > 0) - { - color.a = 0; - spriteRenderer.color = color; - - float time = Time.time; - while (color.a < maxAlpha) - { - color.a = Mathf.Lerp(0, maxAlpha, (Time.time - time) / fadeTime); - - if (spriteRenderer != null) - spriteRenderer.color = color; - yield return null; - } - } - break; - } - - if (OnLoadedAction != null) - OnLoadedAction.Invoke(); - - if (enableLog) - Debug.Log("[Davinci] Image has been loaded."); - - success = true; - finish(); - } - - public static string CreateMD5(string input) - { - // Use input string to calculate MD5 hash - using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) + else if (rendererType == RendererType.renderer && targetObj.TryGetComponent(out var rend)) { - byte[] inputBytes = Encoding.ASCII.GetBytes(input); - byte[] hashBytes = md5.ComputeHash(inputBytes); - - // Convert the byte array to hexadecimal string - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < hashBytes.Length; i++) - { - sb.Append(hashBytes[i].ToString("X2")); - } - return sb.ToString(); + rend.material.mainTexture = tex; + onLoadedAction?.Invoke(); + if (fadeTime > 0) yield return FadeEffect(rend); } - } - - private void error(string message) - { - success = false; - - if (enableLog) - Debug.LogError("[Davinci] Error : " + message); - - if (onErrorAction != null) - onErrorAction.Invoke(message); - - if (errorPlaceholder != null) - StartCoroutine(ImageLoader(errorPlaceholder)); - else finish(); - } - - private void finish() - { - if (enableLog) - Debug.Log("[Davinci] Operation has been finished."); - - if (!cached) + else { - try - { - File.Delete(filePath + uniqueHash); - } - catch (Exception ex) - { - if (enableLog) - Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); - } + onLoadedAction?.Invoke(); } - if (onEndAction != null) - onEndAction.Invoke(); - - Invoke("destroyer", 0.5f); + onEndAction?.Invoke(); } - private void destroyer() + private string GetFileNameFromUrl(string url) { - Destroy(gameObject); + return Path.GetFileNameWithoutExtension(url); } - - /// - /// Clear a certain cached file with its url - /// - /// Cached file url. - /// - public static void ClearCache(string url) + private IEnumerator FadeEffect(object component) { - try + float elapsed = 0; + while (elapsed < fadeTime) { - File.Delete(filePath + CreateMD5(url)); + if (targetObj == null) yield break; + elapsed += Time.deltaTime; + float alpha = Mathf.Lerp(0, 1, elapsed / fadeTime); - if (ENABLE_GLOBAL_LOGS) - Debug.Log($"[Davinci] Cached file has been cleared: {url}"); - } - catch (Exception ex) - { - if (ENABLE_GLOBAL_LOGS) - Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); + if (component is Image img) { Color c = img.color; c.a = alpha; img.color = c; } + else if (component is Renderer rend) { Color c = rend.material.color; c.a = alpha; rend.material.color = c; } + yield return null; } } - /// - /// Clear all davinci cached files - /// - /// - public static void ClearAllCachedFiles() + private void SetPlaceholder(Texture2D tex) { - try - { - Directory.Delete(filePath, true); + if (targetObj == null) return; + if (rendererType == RendererType.uiImage) + targetObj.GetComponent().sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); + else if (rendererType == RendererType.renderer) + targetObj.GetComponent().material.mainTexture = tex; + } - if (ENABLE_GLOBAL_LOGS) - Debug.Log("[Davinci] All Davinci cached files has been cleared."); - } - catch (Exception ex) - { - if (ENABLE_GLOBAL_LOGS) - Debug.LogError($"[Davinci] Error while removing cached file: {ex.Message}"); - } + private void Error(string msg) + { + if (enableLog) Debug.LogError($"[Davinci] {msg}"); + onErrorAction?.Invoke(msg); + if (errorPlaceholder != null) SetPlaceholder(errorPlaceholder); + onEndAction?.Invoke(); } -} \ No newline at end of file +}