| 1 |
using System; |
| 2 |
using System.Collections.Generic; |
| 3 |
using System.IO; |
| 4 |
using System.Linq; |
| 5 |
using System.Net; |
| 6 |
using System.Text; |
| 7 |
using System.Reflection; |
| 8 |
|
| 9 |
namespace Aqua877.WinApp.IronLivetube |
| 10 |
{ |
| 11 |
public static class Extensions |
| 12 |
{ |
| 13 |
public static void ForEach<T>(this IEnumerable<T> arr, Action<T> action) |
| 14 |
{ |
| 15 |
foreach (T item in arr) |
| 16 |
{ |
| 17 |
action(item); |
| 18 |
} |
| 19 |
} |
| 20 |
|
| 21 |
public static void ForEach<T>(this IEnumerable<T> arr, Action<T, int> action) |
| 22 |
{ |
| 23 |
int count = 0; |
| 24 |
foreach (T item in arr) |
| 25 |
{ |
| 26 |
action(item, count); |
| 27 |
count++; |
| 28 |
} |
| 29 |
} |
| 30 |
|
| 31 |
public static TResult Let<TArgument, TResult>(this TArgument obj, Func<TArgument, TResult> action) |
| 32 |
{ |
| 33 |
return action(obj); |
| 34 |
} |
| 35 |
|
| 36 |
public static T DeepCopy<T>(this T obj) where T : class |
| 37 |
{ |
| 38 |
return typeof(T).InvokeMember("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, obj, null) as T; |
| 39 |
} |
| 40 |
} |
| 41 |
|
| 42 |
public static class Progress |
| 43 |
{ |
| 44 |
public static Progress<T> Create<T>(T value, double currentLength, double totalLength) |
| 45 |
{ |
| 46 |
return new Progress<T>(value, currentLength, totalLength); |
| 47 |
} |
| 48 |
} |
| 49 |
|
| 50 |
public class Progress<T> |
| 51 |
{ |
| 52 |
public T Value { get; private set; } |
| 53 |
public double TotalLength { get; private set; } |
| 54 |
public double CurrentLength { get; private set; } |
| 55 |
public int Percentage |
| 56 |
{ |
| 57 |
get |
| 58 |
{ |
| 59 |
return (TotalLength < 0 || CurrentLength < 0) |
| 60 |
? 0 |
| 61 |
: (int)((CurrentLength / TotalLength) * 100); |
| 62 |
} |
| 63 |
} |
| 64 |
|
| 65 |
public Progress(T value, double currentLength, double totalLength) |
| 66 |
{ |
| 67 |
Value = value; |
| 68 |
TotalLength = totalLength; |
| 69 |
CurrentLength = currentLength; |
| 70 |
} |
| 71 |
} |
| 72 |
|
| 73 |
public static class WebRequestExtensions |
| 74 |
{ |
| 75 |
public static IObservable<WebResponse> GetResponseAsObservable(this WebRequest request) |
| 76 |
{ |
| 77 |
return Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)(); |
| 78 |
} |
| 79 |
|
| 80 |
public static IObservable<Stream> GetRequestStreamAsObservable(this WebRequest request) |
| 81 |
{ |
| 82 |
return Observable.FromAsyncPattern<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream)(); |
| 83 |
} |
| 84 |
|
| 85 |
public static IObservable<byte[]> DownloadDataAsync(this WebRequest request) |
| 86 |
{ |
| 87 |
return Observable.Defer(() => request.GetResponseAsObservable()).SelectMany(r => r.DownloadDataAsync()); |
| 88 |
} |
| 89 |
|
| 90 |
public static IObservable<Progress<byte[]>> DownloadDataAsyncWithProgress(this WebRequest request, int chunkSize = 65536) |
| 91 |
{ |
| 92 |
return Observable.Defer(() => request.GetResponseAsObservable()).SelectMany(r => r.DownloadDataAsyncWithProgress(chunkSize)); |
| 93 |
} |
| 94 |
|
| 95 |
public static IObservable<string> DownloadStringAsync(this WebRequest request) |
| 96 |
{ |
| 97 |
return DownloadStringAsync(request, Encoding.UTF8); |
| 98 |
} |
| 99 |
|
| 100 |
public static IObservable<string> DownloadStringAsync(this WebRequest request, Encoding encoding) |
| 101 |
{ |
| 102 |
return Observable.Defer(() => request.GetResponseAsObservable()).SelectMany(r => r.DownloadStringAsync(encoding)); |
| 103 |
} |
| 104 |
|
| 105 |
public static IObservable<string> DownloadStringLineAsync(this WebRequest request) |
| 106 |
{ |
| 107 |
return DownloadStringLineAsync(request, Encoding.UTF8); |
| 108 |
} |
| 109 |
|
| 110 |
public static IObservable<string> DownloadStringLineAsync(this WebRequest request, Encoding encoding) |
| 111 |
{ |
| 112 |
return Observable.Defer(() => request.GetResponseAsObservable()).SelectMany(r => r.DownloadStringLineAsync(encoding)); |
| 113 |
} |
| 114 |
|
| 115 |
public static IObservable<WebResponse> UploadStringAsync(this WebRequest request, string data) |
| 116 |
{ |
| 117 |
var bytes = Encoding.UTF8.GetBytes(data); |
| 118 |
return request.UploadDataAsync(bytes); |
| 119 |
} |
| 120 |
|
| 121 |
public static IObservable<Progress<Unit>> UploadStringAsyncWithProgress(this WebRequest request, string data, int chunkSize = 65536) |
| 122 |
{ |
| 123 |
var bytes = Encoding.UTF8.GetBytes(data); |
| 124 |
return request.UploadDataAsyncWithProgress(bytes, chunkSize); |
| 125 |
} |
| 126 |
|
| 127 |
public static IObservable<WebResponse> UploadValuesAsync(this WebRequest request, IDictionary<string, string> parameters) |
| 128 |
{ |
| 129 |
var parameter = parameters.Select(kvp => Uri.EscapeDataString(kvp.Key) + "=" + Uri.EscapeDataString(kvp.Value)) |
| 130 |
.Aggregate(new StringBuilder(), (sb, x) => sb.Append(x)).ToString(); |
| 131 |
var bytes = Encoding.UTF8.GetBytes(parameter); |
| 132 |
|
| 133 |
return request.UploadDataAsync(bytes); |
| 134 |
} |
| 135 |
|
| 136 |
public static IObservable<Progress<Unit>> UploadValuesAsyncWithProgress(this WebRequest request, IDictionary<string, string> parameters, int chunkSize = 65536) |
| 137 |
{ |
| 138 |
var parameter = parameters.Select(kvp => Uri.EscapeDataString(kvp.Key) + "=" + Uri.EscapeDataString(kvp.Value)) |
| 139 |
.Aggregate(new StringBuilder(), (sb, x) => sb.Append(x)).ToString(); |
| 140 |
var bytes = Encoding.UTF8.GetBytes(parameter); |
| 141 |
|
| 142 |
return request.UploadDataAsyncWithProgress(bytes, chunkSize); |
| 143 |
} |
| 144 |
|
| 145 |
public static IObservable<WebResponse> UploadDataAsync(this WebRequest request, byte[] data) |
| 146 |
{ |
| 147 |
return Observable.Defer(() => request.GetRequestStreamAsObservable()) |
| 148 |
.SelectMany(stream => stream.WriteAsObservable(data, 0, data.Length).Finally(() => stream.Close())) |
| 149 |
.TakeLast(1) |
| 150 |
.SelectMany(_ => request.GetResponseAsObservable()); |
| 151 |
} |
| 152 |
|
| 153 |
public static IObservable<Progress<Unit>> UploadDataAsyncWithProgress(this WebRequest request, byte[] data, int chunkSize = 65536) |
| 154 |
{ |
| 155 |
return Observable.Defer(() => request.GetRequestStreamAsObservable()) |
| 156 |
.SelectMany(stream => stream.WriteAsync(data, chunkSize)) |
| 157 |
.Scan(0, (i, _) => i + 1) |
| 158 |
.Select(i => |
| 159 |
{ |
| 160 |
var currentLength = i * chunkSize; |
| 161 |
if (currentLength > data.Length) currentLength = data.Length; |
| 162 |
return Progress.Create(new Unit(), currentLength, data.Length); |
| 163 |
}); |
| 164 |
} |
| 165 |
} |
| 166 |
|
| 167 |
public static class WebResponseExtensions |
| 168 |
{ |
| 169 |
public static IObservable<byte[]> DownloadDataAsync(this WebResponse response) |
| 170 |
{ |
| 171 |
return Observable.Defer(() => response.GetResponseStream().ReadAsync()) |
| 172 |
.Finally(() => response.Close()) |
| 173 |
.Aggregate(new List<byte>(), (list, bytes) => { list.AddRange(bytes); return list; }) |
| 174 |
.Select(x => x.ToArray()); |
| 175 |
} |
| 176 |
|
| 177 |
public static IObservable<Progress<byte[]>> DownloadDataAsyncWithProgress(this WebResponse response, int chunkSize = 65536) |
| 178 |
{ |
| 179 |
return Observable.Defer(() => response.GetResponseStream().ReadAsync(chunkSize)) |
| 180 |
.Finally(() => response.Close()) |
| 181 |
.Scan(Progress.Create(new byte[0], 0, 0), |
| 182 |
(p, bytes) => Progress.Create(bytes, p.CurrentLength + bytes.Length, response.ContentLength)); |
| 183 |
} |
| 184 |
|
| 185 |
public static IObservable<string> DownloadStringAsync(this WebResponse response) |
| 186 |
{ |
| 187 |
return DownloadStringAsync(response, Encoding.UTF8); |
| 188 |
} |
| 189 |
|
| 190 |
public static IObservable<string> DownloadStringAsync(this WebResponse response, Encoding encoding) |
| 191 |
{ |
| 192 |
return response.DownloadDataAsync().Select(x => encoding.GetString(x, 0, x.Length)); |
| 193 |
} |
| 194 |
|
| 195 |
public static IObservable<string> DownloadStringLineAsync(this WebResponse response) |
| 196 |
{ |
| 197 |
return DownloadStringLineAsync(response, Encoding.UTF8); |
| 198 |
} |
| 199 |
|
| 200 |
public static IObservable<string> DownloadStringLineAsync(this WebResponse response, Encoding encoding) |
| 201 |
{ |
| 202 |
return Observable.Defer(() => response.GetResponseStream().ReadLineAsync(encoding)) |
| 203 |
.Finally(() => response.Close()); |
| 204 |
} |
| 205 |
} |
| 206 |
|
| 207 |
public static class StreamExtensions |
| 208 |
{ |
| 209 |
public static IObservable<Unit> WriteAsObservable(this Stream stream, byte[] buffer, int offset, int count) |
| 210 |
{ |
| 211 |
return Observable.FromAsyncPattern((ac, o) => stream.BeginWrite(buffer, offset, count, ac, o), stream.EndWrite)(); |
| 212 |
} |
| 213 |
|
| 214 |
public static IObservable<int> ReadAsObservable(this Stream stream, byte[] buffer, int offset, int count) |
| 215 |
{ |
| 216 |
return Observable.FromAsyncPattern<int>((ac, o) => stream.BeginRead(buffer, offset, count, ac, o), stream.EndRead)(); |
| 217 |
} |
| 218 |
|
| 219 |
public static IObservable<Unit> WriteAsync(this Stream stream, string data) |
| 220 |
{ |
| 221 |
return WriteAsync(stream, data, Encoding.UTF8); |
| 222 |
} |
| 223 |
|
| 224 |
public static IObservable<Unit> WriteAsync(this Stream stream, string data, Encoding encoding) |
| 225 |
{ |
| 226 |
return WriteAsync(stream, encoding.GetBytes(data)); |
| 227 |
} |
| 228 |
|
| 229 |
public static IObservable<Unit> WriteAsync(this Stream stream, IEnumerable<byte> data, int chunkSize = 65536) |
| 230 |
{ |
| 231 |
return WriteAsync(stream, data.ToObservable(), chunkSize); |
| 232 |
} |
| 233 |
|
| 234 |
public static IObservable<Unit> WriteAsync(this Stream stream, IObservable<byte> data, int chunkSize = 65536) |
| 235 |
{ |
| 236 |
return Observable.Defer(() => data) |
| 237 |
.BufferWithCount(chunkSize) |
| 238 |
.SelectMany(l => stream.WriteAsObservable(l.ToArray(), 0, l.Count)) |
| 239 |
.Finally(() => stream.Close()); |
| 240 |
} |
| 241 |
|
| 242 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, string data) |
| 243 |
{ |
| 244 |
return WriteLineAsync(stream, data, Encoding.UTF8); |
| 245 |
} |
| 246 |
|
| 247 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, string data, Encoding encoding) |
| 248 |
{ |
| 249 |
return WriteAsync(stream, data + Environment.NewLine, encoding); |
| 250 |
} |
| 251 |
|
| 252 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, IEnumerable<string> data) |
| 253 |
{ |
| 254 |
return WriteLineAsync(stream, data, Encoding.UTF8); |
| 255 |
} |
| 256 |
|
| 257 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, IObservable<string> data) |
| 258 |
{ |
| 259 |
return WriteLineAsync(stream, data, Encoding.UTF8); |
| 260 |
} |
| 261 |
|
| 262 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, IEnumerable<string> data, Encoding encoding) |
| 263 |
{ |
| 264 |
return WriteLineAsync(stream, data.ToObservable(), encoding); |
| 265 |
} |
| 266 |
|
| 267 |
public static IObservable<Unit> WriteLineAsync(this Stream stream, IObservable<string> data, Encoding encoding) |
| 268 |
{ |
| 269 |
return WriteAsync(stream, data.SelectMany(s => encoding.GetBytes(s + Environment.NewLine))); |
| 270 |
} |
| 271 |
|
| 272 |
public static IObservable<byte[]> ReadAsync(this Stream stream, int chunkSize = 65536) |
| 273 |
{ |
| 274 |
return Observable.Defer(() => Observable.Return(new byte[chunkSize])) |
| 275 |
.SelectMany(buffer => stream.ReadAsObservable(buffer, 0, chunkSize), |
| 276 |
(buffer, readCount) => new { buffer, readCount }) |
| 277 |
.Repeat() |
| 278 |
.TakeWhile(a => a.readCount != 0) |
| 279 |
.Select(a => |
| 280 |
{ |
| 281 |
if (a.readCount == chunkSize) return a.buffer; |
| 282 |
|
| 283 |
var newBuffer = new byte[a.readCount]; |
| 284 |
Array.Copy(a.buffer, newBuffer, a.readCount); |
| 285 |
return newBuffer; |
| 286 |
}) |
| 287 |
.Finally(() => stream.Close()); |
| 288 |
} |
| 289 |
|
| 290 |
public static IObservable<string> ReadLineAsync(this Stream stream, int chunkSize = 65536) |
| 291 |
{ |
| 292 |
return ReadLineAsync(stream, Encoding.UTF8, chunkSize); |
| 293 |
} |
| 294 |
|
| 295 |
public static IObservable<string> ReadLineAsync(this Stream stream, Encoding encoding, int chunkSize = 65536) |
| 296 |
{ |
| 297 |
return Observable.CreateWithDisposable<string>(observer => |
| 298 |
{ |
| 299 |
var decoder = encoding.GetDecoder(); |
| 300 |
var bom = encoding.GetChars(encoding.GetPreamble()).FirstOrDefault(); |
| 301 |
var sb = new StringBuilder(); |
| 302 |
var prev = default(char); |
| 303 |
|
| 304 |
return stream.ReadAsync(chunkSize) |
| 305 |
.SelectMany(bytes => |
| 306 |
{ |
| 307 |
var charBuffer = new char[encoding.GetMaxCharCount(bytes.Length)]; |
| 308 |
var count = decoder.GetChars(bytes, 0, bytes.Length, charBuffer, 0); |
| 309 |
return charBuffer.Take(count); |
| 310 |
}) |
| 311 |
.Subscribe( |
| 312 |
c => |
| 313 |
{ |
| 314 |
if (c == bom) { } // skip bom |
| 315 |
else if (prev == '\r' && c == '\n') { } // when \r\n do nothing |
| 316 |
else if (c == '\r' || c == '\n') // reach at EndOfLine |
| 317 |
{ |
| 318 |
var str = sb.ToString(); |
| 319 |
sb.Length = 0; |
| 320 |
observer.OnNext(str); |
| 321 |
} |
| 322 |
else sb.Append(c); // normally char |
| 323 |
|
| 324 |
prev = c; |
| 325 |
}, |
| 326 |
observer.OnError, |
| 327 |
() => |
| 328 |
{ |
| 329 |
var str = sb.ToString(); |
| 330 |
if (str != "") observer.OnNext(str); |
| 331 |
observer.OnCompleted(); |
| 332 |
}); |
| 333 |
}); |
| 334 |
} |
| 335 |
} |
| 336 |
} |