| | 1 | | using System; |
| | 2 | | using System.Collections; |
| | 3 | | using System.Collections.Generic; |
| | 4 | | using System.Threading; |
| | 5 | | using Cysharp.Threading.Tasks; |
| | 6 | | using DCL.Helpers; |
| | 7 | | using NFTShape_Internal; |
| | 8 | | using UnityEngine; |
| | 9 | | using UnityEngine.Networking; |
| | 10 | |
|
| | 11 | | namespace DCL |
| | 12 | | { |
| | 13 | | public interface INFTAssetRetriever : IDisposable |
| | 14 | | { |
| | 15 | | IEnumerator LoadNFTAsset(string url, Action<INFTAsset> OnSuccess, Action<Exception> OnFail); |
| | 16 | | UniTask<INFTAsset> LoadNFTAsset(string url); |
| | 17 | | } |
| | 18 | |
|
| | 19 | | public class NFTAssetRetriever : INFTAssetRetriever |
| | 20 | | { |
| | 21 | | private const string CONTENT_TYPE = "Content-Type"; |
| | 22 | | private const string CONTENT_LENGTH = "Content-Length"; |
| | 23 | | private const string CONTENT_TYPE_GIF = "image/gif"; |
| | 24 | | private const long PREVIEW_IMAGE_SIZE_LIMIT = 500000; |
| | 25 | |
|
| | 26 | | protected AssetPromise_Texture imagePromise = null; |
| | 27 | | protected AssetPromise_Gif gifPromise = null; |
| | 28 | | private CancellationTokenSource tokenSource; |
| | 29 | |
|
| | 30 | | public async UniTask<INFTAsset> LoadNFTAsset(string url) |
| | 31 | | { |
| 0 | 32 | | tokenSource = new CancellationTokenSource(); |
| 0 | 33 | | tokenSource.Token.ThrowIfCancellationRequested(); |
| 0 | 34 | | INFTAsset result = null; |
| 0 | 35 | | await LoadNFTAsset(url, (nftAsset) => |
| | 36 | | { |
| 0 | 37 | | result = nftAsset; |
| 0 | 38 | | }, (exception) => |
| | 39 | | { |
| 0 | 40 | | Debug.Log("Fail to load nft " + url + " due to " + exception.Message); |
| 0 | 41 | | }).WithCancellation(tokenSource.Token); |
| | 42 | |
|
| 0 | 43 | | return result; |
| 0 | 44 | | } |
| | 45 | |
|
| | 46 | | public IEnumerator LoadNFTAsset(string url, Action<INFTAsset> OnSuccess, Action<Exception> OnFail) |
| | 47 | | { |
| 6 | 48 | | if (string.IsNullOrEmpty(url)) |
| | 49 | | { |
| 0 | 50 | | OnFail?.Invoke(new Exception($"Image url is null!")); |
| 0 | 51 | | yield break; |
| | 52 | | } |
| | 53 | |
|
| 6 | 54 | | HashSet<string> headers = new HashSet<string>() {CONTENT_TYPE, CONTENT_LENGTH}; |
| 6 | 55 | | Dictionary<string, string> responseHeaders = new Dictionary<string, string>(); |
| 6 | 56 | | string headerRequestError = string.Empty; |
| | 57 | |
|
| 12 | 58 | | yield return GetHeaders(url, headers, result => responseHeaders = result, (x) => headerRequestError = x); |
| | 59 | |
|
| 6 | 60 | | if (!string.IsNullOrEmpty(headerRequestError)) |
| | 61 | | { |
| 0 | 62 | | OnFail?.Invoke(new Exception($"Error fetching headers! ({headerRequestError})")); |
| 0 | 63 | | yield break; |
| | 64 | | } |
| | 65 | |
|
| 6 | 66 | | string contentType = responseHeaders[CONTENT_TYPE]; |
| 6 | 67 | | long.TryParse(responseHeaders[CONTENT_LENGTH], out long contentLength); |
| 6 | 68 | | bool isGif = contentType == CONTENT_TYPE_GIF; |
| | 69 | |
|
| 6 | 70 | | if (isGif) |
| | 71 | | { |
| 2 | 72 | | yield return FetchGif(url, |
| | 73 | | OnSuccess: (promise) => |
| | 74 | | { |
| 2 | 75 | | UnloadPromises(); |
| 2 | 76 | | this.gifPromise = promise; |
| 2 | 77 | | OnSuccess?.Invoke(new NFTAsset_Gif(promise.asset)); |
| 2 | 78 | | }, |
| 0 | 79 | | OnFail: (exception) => { OnFail?.Invoke(exception); } |
| | 80 | | ); |
| | 81 | |
|
| 2 | 82 | | yield break; |
| | 83 | | } |
| | 84 | |
|
| 4 | 85 | | if (contentLength > PREVIEW_IMAGE_SIZE_LIMIT) |
| | 86 | | { |
| 1 | 87 | | OnFail?.Invoke(new System.Exception($"Image is too big! {contentLength} > {PREVIEW_IMAGE_SIZE_LIMIT}")); |
| 1 | 88 | | yield break; |
| | 89 | | } |
| | 90 | |
|
| 3 | 91 | | yield return FetchImage(url, |
| | 92 | | OnSuccess: (promise) => |
| | 93 | | { |
| 3 | 94 | | UnloadPromises(); |
| 3 | 95 | | this.imagePromise = promise; |
| 3 | 96 | | OnSuccess?.Invoke(new NFTAsset_Image(promise.asset)); |
| 3 | 97 | | }, |
| 0 | 98 | | OnFail: (exc) => { OnFail?.Invoke(exc); }); |
| 3 | 99 | | } |
| | 100 | |
|
| | 101 | | public void Dispose() |
| | 102 | | { |
| 14 | 103 | | UnloadPromises(); |
| 14 | 104 | | tokenSource?.Cancel(); |
| 14 | 105 | | tokenSource?.Dispose(); |
| 0 | 106 | | } |
| | 107 | |
|
| | 108 | | protected virtual IEnumerator GetHeaders(string url, HashSet<string> headerField, |
| | 109 | | Action<Dictionary<string, string>> OnSuccess, Action<string> OnFail) |
| | 110 | | { |
| 0 | 111 | | using (var request = UnityWebRequest.Head(url)) |
| | 112 | | { |
| 0 | 113 | | yield return request.SendWebRequest(); |
| | 114 | |
|
| 0 | 115 | | if (request.WebRequestSucceded()) |
| | 116 | | { |
| 0 | 117 | | var result = new Dictionary<string, string>(); |
| | 118 | |
|
| 0 | 119 | | foreach (var key in headerField) |
| | 120 | | { |
| 0 | 121 | | result.Add(key, request.GetResponseHeader(key)); |
| | 122 | | } |
| | 123 | |
|
| 0 | 124 | | OnSuccess?.Invoke(result); |
| 0 | 125 | | } |
| | 126 | | else |
| | 127 | | { |
| 0 | 128 | | OnFail?.Invoke(request.error); |
| | 129 | | } |
| 0 | 130 | | } |
| 0 | 131 | | } |
| | 132 | |
|
| | 133 | | protected virtual IEnumerator FetchGif(string url, Action<AssetPromise_Gif> OnSuccess, |
| | 134 | | Action<Exception> OnFail = null) |
| | 135 | | { |
| 0 | 136 | | AssetPromise_Gif gifPromise = new AssetPromise_Gif(url); |
| 0 | 137 | | gifPromise.OnSuccessEvent += texture => { OnSuccess?.Invoke(gifPromise); }; |
| 0 | 138 | | gifPromise.OnFailEvent += (x, error) => OnFail?.Invoke(error); |
| | 139 | |
|
| 0 | 140 | | AssetPromiseKeeper_Gif.i.Keep(gifPromise); |
| | 141 | |
|
| 0 | 142 | | yield return gifPromise; |
| 0 | 143 | | } |
| | 144 | |
|
| | 145 | | protected virtual IEnumerator FetchImage(string url, Action<AssetPromise_Texture> OnSuccess, |
| | 146 | | Action<Exception> OnFail = null) |
| | 147 | | { |
| 0 | 148 | | AssetPromise_Texture texturePromise = new AssetPromise_Texture(url); |
| 0 | 149 | | texturePromise.OnSuccessEvent += texture => { OnSuccess?.Invoke(texturePromise); }; |
| 0 | 150 | | texturePromise.OnFailEvent += (x, error) => OnFail?.Invoke(error); |
| | 151 | |
|
| 0 | 152 | | AssetPromiseKeeper_Texture.i.Keep(texturePromise); |
| | 153 | |
|
| 0 | 154 | | yield return texturePromise; |
| 0 | 155 | | } |
| | 156 | |
|
| | 157 | | protected virtual void UnloadPromises() |
| | 158 | | { |
| 19 | 159 | | if (gifPromise != null) |
| 2 | 160 | | AssetPromiseKeeper_Gif.i.Forget(gifPromise); |
| | 161 | |
|
| 19 | 162 | | if (imagePromise != null) |
| 3 | 163 | | AssetPromiseKeeper_Texture.i.Forget(imagePromise); |
| | 164 | |
|
| 19 | 165 | | gifPromise = null; |
| 19 | 166 | | imagePromise = null; |
| 19 | 167 | | } |
| | 168 | | } |
| | 169 | | } |