feature: parallels to download illusts

This commit is contained in:
Tsanie Lily 2020-05-25 00:44:39 +08:00
parent 7d9be29e03
commit ce31834733
9 changed files with 164 additions and 16 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0.524" package="org.tsanie.pixiview" android:versionCode="16">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0.525" package="org.tsanie.pixiview" android:versionCode="18">
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="28" />
<application android:label="Pixiview" android:icon="@mipmap/icon" android:roundIcon="@mipmap/icon_round"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@ -29,8 +29,8 @@
<string>com.apple.share-services</string>
</dict>
<key>CFBundleShortVersionString</key>
<string>1.0.524</string>
<string>1.0.525</string>
<key>CFBundleVersion</key>
<string>16</string>
<string>18</string>
</dict>
</plist>

View File

@ -79,9 +79,9 @@
</dict>
</array>
<key>CFBundleShortVersionString</key>
<string>1.0.524</string>
<string>1.0.525</string>
<key>CFBundleVersion</key>
<string>16</string>
<string>18</string>
<key>CFBundleDevelopmentRegion</key>
<string>China</string>
</dict>

View File

@ -717,9 +717,12 @@ namespace Pixiview.Illust
return true;
}, tagIndex: tag);
lock (sync)
if (task != null)
{
tasks.Enqueue(task);
lock (sync)
{
tasks.Enqueue(task);
}
}
}

View File

@ -315,7 +315,7 @@ namespace Pixiview.Illust
if (illust.ImageUrl != null)
{
var url = Configs.GetThumbnailUrl(illust.ImageUrl);
var image = Stores.LoadPreviewImage(url, true, force: false);
var image = Stores.LoadPreviewImage(url, false);
if (image == null)
{
image = Stores.LoadThumbnailImage(url, true);

View File

@ -400,7 +400,7 @@ namespace Pixiview.Illust
task.Dispose();
task = null;
}
task = ParallelTask.Start("view.illusts", 0, items.Length, Configs.MaxPageThreads, i =>
task = ParallelTask.Start("view.illusts", 0, items.Length, 2, i =>
{
DoLoadImage(i);
return true;
@ -439,7 +439,7 @@ namespace Pixiview.Illust
}
}
item.Loading = true;
var image = Stores.LoadPreviewImage(item.PreviewUrl, true, force: force);
var image = Stores.LoadPreviewImage(item.PreviewUrl, true, IllustItem.Id, force: force);
if (image != null)
{
item.Image = image;

View File

@ -107,7 +107,8 @@ namespace Pixiview.Utils
return null;
}
var task = new ParallelTask(tag, from, toExclusive, maxCount, action, tagIndex, complete);
return task.Start();
task.Start();
return task;
}
private readonly object sync = new object();
@ -141,10 +142,9 @@ namespace Pixiview.Utils
this.complete = complete;
}
public ParallelTask Start()
public void Start()
{
_ = ThreadPool.QueueUserWorkItem(DoStart);
return this;
}
private void DoStart(object state)

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
@ -6,6 +7,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Pixiview.Utils
@ -204,6 +206,112 @@ namespace Pixiview.Utils
}
}
public static Task<string> DownloadImageAsync(string url, string id, string working, string folder)
{
try
{
var directory = Path.Combine(working, folder);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var file = Path.Combine(directory, Path.GetFileName(url));
var proxy = Configs.Proxy;
var referer = new Uri(string.Format(Configs.UrlIllust, id));
var handler = new HttpClientHandler
{
UseCookies = false
};
if (proxy != null)
{
handler.Proxy = proxy;
handler.UseProxy = true;
}
var client = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(30)
};
long size;
DateTimeOffset lastModified;
using (var request = new HttpRequestMessage(HttpMethod.Head, url))
{
var headers = request.Headers;
headers.Add("Accept", Configs.AcceptPureImage);
headers.Add("Accept-Language", Configs.AcceptLanguage);
headers.Referrer = referer;
headers.Add("User-Agent", Configs.UserAgent);
using (var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result)
{
size = response.Content.Headers.ContentLength.Value;
lastModified = response.Content.Headers.LastModified.Value;
#if DEBUG
App.DebugPrint($"content length: {size:n0} bytes, last modified: {lastModified}");
#endif
}
}
// segments
const int SIZE = 50000;
var list = new List<(long from, long to)>();
for (var i = 0L; i < size; i += SIZE)
{
long to;
if (i + SIZE >= size)
{
to = size - 1;
}
else
{
to = i + SIZE - 1;
}
list.Add((i, to));
}
var data = new byte[size];
var task = new TaskCompletionSource<string>();
ParallelTask.Start($"download.async.{id}", 0, list.Count, 6, i =>
{
var (from, to) = list[i];
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
{
var headers = request.Headers;
headers.Add("Accept", Configs.AcceptPureImage);
headers.Add("Accept-Language", Configs.AcceptLanguage);
headers.Add("Accept-Encoding", "identity");
headers.IfRange = new RangeConditionHeaderValue(lastModified);
headers.Range = new RangeHeaderValue(from, to);
headers.Referrer = referer;
headers.Add("User-Agent", Configs.UserAgent);
using (var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result)
using (var ms = new MemoryStream(data, (int)from, (int)(to - from + 1)))
{
response.Content.CopyToAsync(ms).Wait();
#if DEBUG
App.DebugPrint($"downloaded range: from ({from:n0}) to ({to:n0})");
#endif
}
}
return true;
},
complete: () =>
{
using (var fs = File.OpenWrite(file))
{
fs.Write(data, 0, data.Length);
}
task.SetResult(file);
});
return task.Task;
}
catch (Exception ex)
{
App.DebugError("image.download.async", ex.Message);
return Task.FromResult<string>(null);
}
}
private static HttpResponseMessage Download(string url, Action<HttpRequestHeaders> headerAction, HttpContent post = null)
{
#if DEBUG

View File

@ -6,6 +6,7 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Pixiview.Illust;
using Xamarin.Essentials;
@ -590,8 +591,12 @@ namespace Pixiview.Utils
return LoadImage(url, PersonalFolder, imageFolder, true);
}
public static ImageSource LoadPreviewImage(string url, bool downloading, bool force = false)
public static ImageSource LoadPreviewImage(string url, bool downloading, string id = null, bool force = false)
{
if (downloading)
{
return LoadImageAsync(url, id, PersonalFolder, previewFolder, force).Result;
}
return LoadImage(url, PersonalFolder, previewFolder, downloading, force);
}
@ -656,6 +661,37 @@ namespace Pixiview.Utils
}
return image;
}
private static Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool force = false)
{
var file = Path.Combine(working, folder, Path.GetFileName(url));
ImageSource image;
if (!force && File.Exists(file))
{
try
{
image = ImageSource.FromFile(file);
}
catch (Exception ex)
{
App.DebugError("image.load", $"failed to load image from file: {file}, error: {ex.Message}");
image = null;
}
}
else
{
image = null;
}
if (image == null)
{
file = HttpUtility.DownloadImageAsync(url, id, working, folder).Result;
if (file != null)
{
image = ImageSource.FromFile(file);
}
}
return Task.FromResult(image);
}
}
public class IllustFavorite
@ -772,9 +808,9 @@ namespace Pixiview.Utils
}
#if __IOS__
public static System.Threading.Tasks.Task<bool> RequestCookieContainer(WebKit.WKHttpCookieStore cookieStore)
public static Task<bool> RequestCookieContainer(WebKit.WKHttpCookieStore cookieStore)
{
var task = new System.Threading.Tasks.TaskCompletionSource<bool>();
var task = new TaskCompletionSource<bool>();
cookieStore.GetAllCookies(cookies =>
{
var list = new List<string>();
@ -825,6 +861,7 @@ namespace Pixiview.Utils
public const string UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36";
public const string AcceptImage = "image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5";
public const string AcceptPureImage = "image/*,*/*;q=0.8";
public const string AcceptJson = "application/json";
public const string AcceptUrlEncoded = "application/x-www-form-urlencoded";
//public const string AcceptEncoding = "gzip, deflate";