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 +}