LLM for Unity  v2.2.5
Create characters in Unity with LLMs!
Loading...
Searching...
No Matches
ResumingWebClient.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Net;
5using System.Threading;
6using System.Threading.Tasks;
7
8namespace LLMUnity
9{
10 public class ResumingWebClient : WebClient
11 {
12 private const int timeoutMs = 30 * 1000;
13 private SynchronizationContext _context;
14 private const int DefaultDownloadBufferLength = 65536;
15 List<WebRequest> requests = new List<WebRequest>();
16
17 public ResumingWebClient()
18 {
19 _context = SynchronizationContext.Current ?? new SynchronizationContext();
20 }
21
22 public long GetURLFileSize(string address)
23 {
24 return GetURLFileSize(new Uri(address));
25 }
26
27 public long GetURLFileSize(Uri address)
28 {
29 WebRequest request = GetWebRequest(address);
30 request.Method = "HEAD";
31 WebResponse response = request.GetResponse();
32 return response.ContentLength;
33 }
34
35 public Task DownloadFileTaskAsyncResume(Uri address, string fileName, bool resume = false, Callback<float> progressCallback = null)
36 {
37 var tcs = new TaskCompletionSource<object>(address);
38 FileStream fs = null;
39 long bytesToSkip = 0;
40
41 try
42 {
43 FileMode filemode = FileMode.Create;
44 if (resume)
45 {
46 var fileInfo = new FileInfo(fileName);
47 if (fileInfo.Exists) bytesToSkip = fileInfo.Length;
48 }
49
50 WebRequest request = GetWebRequest(address);
51 if (request is HttpWebRequest webRequest && bytesToSkip > 0)
52 {
53 long remoteFileSize = GetURLFileSize(address);
54 if (bytesToSkip >= remoteFileSize)
55 {
56 LLMUnitySetup.Log($"File is already fully downloaded: {fileName}");
57 tcs.TrySetResult(true);
58 return tcs.Task;
59 }
60
61 filemode = FileMode.Append;
62 LLMUnitySetup.Log($"File exists at {fileName}, skipping {bytesToSkip} bytes");
63 webRequest.AddRange(bytesToSkip);
64 webRequest.ReadWriteTimeout = timeoutMs;
65 }
66
67 fs = new FileStream(fileName, filemode, FileAccess.Write);
68 DownloadBitsAsync(request, fs, bytesToSkip, progressCallback, tcs);
69 }
70 catch (Exception e)
71 {
72 fs?.Close();
73 tcs.TrySetException(e);
74 }
75
76 return tcs.Task;
77 }
78
79 public void CancelDownloadAsync()
80 {
81 LLMUnitySetup.Log("Cancellation requested, aborting download.");
82 foreach (WebRequest request in requests) AbortRequest(request);
83 requests.Clear();
84 }
85
86 public void AbortRequest(WebRequest request)
87 {
88 try
89 {
90 request?.Abort();
91 }
92 catch (Exception e)
93 {
94 LLMUnitySetup.LogError($"Error aborting request: {e.Message}");
95 }
96 }
97
98 private async void DownloadBitsAsync(WebRequest request, Stream writeStream, long bytesToSkip = 0, Callback<float> progressCallback = null, TaskCompletionSource<object> tcs = null)
99 {
100 try
101 {
102 requests.Add(request);
103 WebResponse response = await request.GetResponseAsync().ConfigureAwait(false);
104
105 long contentLength = response.ContentLength;
106 byte[] copyBuffer = new byte[contentLength == -1 || contentLength > DefaultDownloadBufferLength ? DefaultDownloadBufferLength : contentLength];
107
108 long TotalBytesToReceive = Math.Max(contentLength, 0) + bytesToSkip;
109 long BytesReceived = bytesToSkip;
110
111 using (writeStream)
112 using (Stream readStream = response.GetResponseStream())
113 {
114 if (readStream != null)
115 {
116 while (true)
117 {
118 int bytesRead = await readStream.ReadAsync(new Memory<byte>(copyBuffer)).ConfigureAwait(false);
119 if (bytesRead == 0)
120 {
121 break;
122 }
123
124 BytesReceived += bytesRead;
125 if (BytesReceived != TotalBytesToReceive)
126 {
127 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
128 }
129
130 await writeStream.WriteAsync(new ReadOnlyMemory<byte>(copyBuffer, 0, bytesRead)).ConfigureAwait(false);
131 }
132 }
133
134 if (TotalBytesToReceive < 0)
135 {
136 TotalBytesToReceive = BytesReceived;
137 }
138 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
139 }
140 tcs.TrySetResult(true);
141 }
142 catch (Exception e)
143 {
144 tcs.TrySetException(e);
145 LLMUnitySetup.LogError(e.Message);
146 AbortRequest(request);
147 tcs.TrySetResult(false);
148 }
149 finally
150 {
151 writeStream?.Close();
152 requests.Remove(request);
153 }
154 }
155
156 private void PostProgressChanged(Callback<float> progressCallback, long BytesReceived, long TotalBytesToReceive)
157 {
158 if (progressCallback != null && BytesReceived > 0)
159 {
160 float progressPercentage = TotalBytesToReceive < 0 ? 0 : TotalBytesToReceive == 0 ? 1 : (float)BytesReceived / TotalBytesToReceive;
161 _context.Post(_ => progressCallback?.Invoke(progressPercentage), null);
162 }
163 }
164 }
165}
Class implementing helper functions for setup and process management.