favorite & view page & etc.
This commit is contained in:
30
Gallery.Share/Util/Consts.cs
Normal file
30
Gallery.Share/Util/Consts.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Gallery.Util
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
public const string DownloadThreadsKey = "download_threads";
|
||||
public const string IsProxiedKey = "is_proxied";
|
||||
public const string ProxyHostKey = "proxy_host";
|
||||
public const string ProxyPortKey = "proxy_port";
|
||||
|
||||
public const int MaxThreads = 4;
|
||||
public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36";
|
||||
public const string AcceptLanguage = "zh-cn";
|
||||
public const string AcceptImage = "image/png,image/*,*/*;q=0.8";
|
||||
|
||||
public static int DownloadThreads;
|
||||
public static WebProxy Proxy;
|
||||
}
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public const string Gallery = "gallery";
|
||||
public const string Favorite = "favorite";
|
||||
public const string Option = "option";
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Gallery.Util.Model;
|
||||
|
||||
namespace Gallery.Util
|
||||
{
|
||||
@ -63,5 +64,14 @@ namespace Gallery.Util
|
||||
var ticks = datetime.Ticks;
|
||||
return (ticks - 621355968000000000L) / 10000000;
|
||||
}
|
||||
|
||||
public static bool SourceEquals(this GalleryItem a, GalleryItem b)
|
||||
{
|
||||
if (a == null || b == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return a.Id == b.Id && a.Source == b.Source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Gallery.Util.Model;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@ -7,17 +8,28 @@ namespace Gallery.Util.Interface
|
||||
public interface IGallerySource
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
string Route { get; }
|
||||
|
||||
string FlyoutIconKey { get; }
|
||||
|
||||
string HomePage { get; }
|
||||
bool IsScrollable { get; }
|
||||
|
||||
void SetCookie();
|
||||
|
||||
void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark);
|
||||
Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page);
|
||||
Task<string> ResolveImageUrl(GalleryItem item);
|
||||
}
|
||||
|
||||
Task<GalleryItem[]> GetRecentItemsAsync(int page);
|
||||
public abstract class GallerySourceBase : IGallerySource
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract string Route { get; }
|
||||
public abstract string FlyoutIconKey { get; }
|
||||
public abstract string HomePage { get; }
|
||||
public virtual bool IsScrollable => true;
|
||||
|
||||
public abstract void SetCookie();
|
||||
public abstract void InitDynamicResources(string family, ResourceDictionary light, ResourceDictionary dark);
|
||||
public abstract Task<IEnumerable<GalleryItem>> GetRecentItemsAsync(int page);
|
||||
public virtual Task<string> ResolveImageUrl(GalleryItem item) => Task.FromResult(item.RawUrl);
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ namespace Gallery.Util.Model
|
||||
[JsonProperty]
|
||||
public string Source { get; set; }
|
||||
[JsonProperty]
|
||||
public string SourceUrl { get; set; }
|
||||
[JsonProperty]
|
||||
public string PreviewUrl { get; set; }
|
||||
[JsonProperty]
|
||||
public string RawUrl { get; set; }
|
||||
@ -124,7 +126,7 @@ namespace Gallery.Util.Model
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var source = string.IsNullOrEmpty(Source) ? RawUrl : Source;
|
||||
var source = string.IsNullOrEmpty(SourceUrl) ? RawUrl : SourceUrl;
|
||||
return $"{Id}, {source}";
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ namespace Gallery.Util
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> DownloadImageAsync(string url, string id, string working, string folder)
|
||||
public static async Task<string> DownloadImageAsync(string url, string id, string working, string folder, Action<(int loc, int size)> action = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -149,7 +149,7 @@ namespace Gallery.Util
|
||||
{
|
||||
Timeout = Config.Timeout
|
||||
};
|
||||
long size;
|
||||
int size;
|
||||
DateTimeOffset lastModified;
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Head, url))
|
||||
{
|
||||
@ -158,7 +158,7 @@ namespace Gallery.Util
|
||||
headers.Add("Accept-Language", Config.AcceptLanguage);
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
size = response.Content.Headers.ContentLength.Value;
|
||||
size = (int)response.Content.Headers.ContentLength.Value;
|
||||
lastModified = response.Content.Headers.LastModified.Value;
|
||||
#if DEBUG
|
||||
Log.Print($"content length: {size:n0} bytes, last modified: {lastModified}");
|
||||
@ -167,10 +167,10 @@ namespace Gallery.Util
|
||||
|
||||
// segments
|
||||
const int SIZE = 150000;
|
||||
var list = new List<(long from, long to)>();
|
||||
for (long i = 0; i < size; i += SIZE)
|
||||
var list = new List<(int from, int to)>();
|
||||
for (var i = 0; i < size; i += SIZE)
|
||||
{
|
||||
long to;
|
||||
int to;
|
||||
if (i + SIZE >= size)
|
||||
{
|
||||
to = size - 1;
|
||||
@ -198,11 +198,12 @@ namespace Gallery.Util
|
||||
headers.Range = new RangeHeaderValue(from, to);
|
||||
headers.Add("User-Agent", Config.UserAgent);
|
||||
using var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
|
||||
using var ms = new MemoryStream(data, (int)from, (int)(to - from + 1));
|
||||
using var ms = new MemoryStream(data, from, to - from + 1);
|
||||
response.Content.CopyToAsync(ms).Wait();
|
||||
#if DEBUG
|
||||
Log.Print($"downloaded range: from({from:n0}) to ({to:n0})");
|
||||
#endif
|
||||
action?.Invoke((to, size));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Gallery.Util.Model;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@ -12,17 +15,120 @@ namespace Gallery.Util
|
||||
public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
|
||||
public static readonly string CacheFolder = FileSystem.CacheDirectory;
|
||||
|
||||
private const string favoriteFile = "favorites.json";
|
||||
private const string imageFolder = "img-original";
|
||||
private const string previewFolder = "img-preview";
|
||||
|
||||
public static async Task<ImageSource> LoadRawImage(string url)
|
||||
public static FavoriteList FavoriteList => GetFavoriteList();
|
||||
public static string FavoriteListPath => Path.Combine(PersonalFolder, favoriteFile);
|
||||
private static FavoriteList favoriteList;
|
||||
|
||||
public static FavoriteList GetFavoriteList(bool force = false)
|
||||
{
|
||||
return await LoadImageAsync(url, null, PersonalFolder, imageFolder, force: true);
|
||||
if (force || favoriteList == null)
|
||||
{
|
||||
var favorites = LoadFavoriteList();
|
||||
if (favorites != null)
|
||||
{
|
||||
favoriteList = favorites;
|
||||
}
|
||||
else
|
||||
{
|
||||
favoriteList = new FavoriteList();
|
||||
}
|
||||
}
|
||||
return favoriteList;
|
||||
}
|
||||
|
||||
public static async Task<ImageSource> LoadPreviewImage(string url, bool downloading, bool force = false)
|
||||
private static FavoriteList LoadFavoriteList(string file = null)
|
||||
{
|
||||
return await LoadImage(url, CacheFolder, previewFolder, downloading, force: force);
|
||||
if (file == null)
|
||||
{
|
||||
file = FavoriteListPath;
|
||||
}
|
||||
return LoadObject<FavoriteList>(file);
|
||||
}
|
||||
|
||||
public static void SaveFavoriteList()
|
||||
{
|
||||
var file = FavoriteListPath;
|
||||
WriteObject(file, FavoriteList);
|
||||
}
|
||||
|
||||
private static T LoadObject<T>(string file)
|
||||
{
|
||||
string content = null;
|
||||
if (File.Exists(file))
|
||||
{
|
||||
try
|
||||
{
|
||||
content = File.ReadAllText(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("file.read", $"failed to read file: {file}, error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return default;
|
||||
}
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(content);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("file.deserialize", $"failed to parse JSON object, error: {ex.Message}");
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteObject(string file, object obj)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(file);
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
string content;
|
||||
try
|
||||
{
|
||||
content = JsonConvert.SerializeObject(obj, Formatting.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("object.serialize", $"failed to serialize object, error: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
File.WriteAllText(file, content, Encoding.UTF8);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error("file.write", $"failed to write file: {file}, error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<ImageSource> LoadRawImage(GalleryItem item, bool force = false, Action<(int loc, int size)> action = null)
|
||||
{
|
||||
return await LoadImageAsync(item.RawUrl, null, PersonalFolder, Path.Combine(imageFolder, item.Source), force, action);
|
||||
}
|
||||
|
||||
public static string GetRawImagePath(GalleryItem item)
|
||||
{
|
||||
var file = Path.Combine(PersonalFolder, imageFolder, item.Source, Path.GetFileName(item.RawUrl));
|
||||
if (File.Exists(file))
|
||||
{
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<ImageSource> LoadPreviewImage(GalleryItem item, bool downloading, bool force = false)
|
||||
{
|
||||
return await LoadImage(item.PreviewUrl, CacheFolder, Path.Combine(previewFolder, item.Source), downloading, force: force);
|
||||
}
|
||||
|
||||
private static async Task<ImageSource> LoadImage(string url, string working, string folder, bool downloading, bool force = false)
|
||||
@ -48,7 +154,7 @@ namespace Gallery.Util
|
||||
return image;
|
||||
}
|
||||
|
||||
private static async Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool force = false)
|
||||
private static async Task<ImageSource> LoadImageAsync(string url, string id, string working, string folder, bool force, Action<(int loc, int size)> action)
|
||||
{
|
||||
var file = Path.Combine(working, folder, Path.GetFileName(url));
|
||||
ImageSource image;
|
||||
@ -62,7 +168,7 @@ namespace Gallery.Util
|
||||
}
|
||||
if (image == null)
|
||||
{
|
||||
file = await NetHelper.DownloadImageAsync(url, id, working, folder);
|
||||
file = await NetHelper.DownloadImageAsync(url, id, working, folder, action);
|
||||
if (file != null)
|
||||
{
|
||||
image = ImageSource.FromFile(file);
|
||||
@ -72,27 +178,33 @@ namespace Gallery.Util
|
||||
}
|
||||
}
|
||||
|
||||
public static class Config
|
||||
public class FavoriteList : List<GalleryItem>
|
||||
{
|
||||
public static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
|
||||
public bool Changed { get; private set; }
|
||||
|
||||
public const string DownloadThreadsKey = "download_threads";
|
||||
public const string IsProxiedKey = "is_proxied";
|
||||
public const string ProxyHostKey = "proxy_host";
|
||||
public const string ProxyPortKey = "proxy_port";
|
||||
public FavoriteList() : base() { }
|
||||
public FavoriteList(IEnumerable<GalleryItem> collection) : base(collection) { }
|
||||
|
||||
public const int MaxThreads = 2;
|
||||
public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36";
|
||||
public const string AcceptLanguage = "zh-cn";
|
||||
public const string AcceptImage = "image/png,image/*,*/*;q=0.8";
|
||||
public new void Insert(int index, GalleryItem item)
|
||||
{
|
||||
base.Insert(index, item);
|
||||
Changed = true;
|
||||
}
|
||||
public new void InsertRange(int index, IEnumerable<GalleryItem> collection)
|
||||
{
|
||||
base.InsertRange(index, collection);
|
||||
Changed = true;
|
||||
}
|
||||
public new void RemoveAt(int index)
|
||||
{
|
||||
base.RemoveAt(index);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
public static int DownloadThreads;
|
||||
public static WebProxy Proxy;
|
||||
}
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public const string Gallery = "gallery";
|
||||
public const string Option = "option";
|
||||
public FavoriteList Reload()
|
||||
{
|
||||
Changed = false;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user