Pixiview/Pixiview/Utils/Stores.cs

556 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Pixiview.Illust;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Pixiview.Utils
{
public static class Stores
{
public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
#if __IOS__
public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);
#else
public static readonly string CacheFolder = FileSystem.CacheDirectory;
#endif
private const string favoriteFile = "favorites.json";
private const string imageFolder = "img-original";
private const string previewFolder = "img-master";
private const string ugoiraFolder = "img-zip-ugoira";
private const string illustFile = "illust.json";
private const string pagesFolder = "pages";
private const string preloadsFolder = "preloads";
private const string thumbFolder = "img-thumb";
private const string userFolder = "user-profile";
private const string recommendsFolder = "recommends";
private static readonly object sync = new object();
public static bool NetworkAvailable
{
get
{
try
{
return Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.Internet;
}
catch
{
return false;
}
}
}
public static List<IllustItem> Favorites => GetFavoriteObject().Illusts;
public static string FavoritesPath => Path.Combine(PersonalFolder, favoriteFile);
private static IllustFavorite favoriteObject;
public static IllustFavorite GetFavoriteObject(bool force = false)
{
lock (sync)
{
if (force || favoriteObject == null)
{
var favorites = LoadFavoritesIllusts();
if (favorites != null)
{
favoriteObject = favorites;
}
else
{
favoriteObject = new IllustFavorite
{
Illusts = new List<IllustItem>()
};
}
}
return favoriteObject;
}
}
public static IllustFavorite LoadFavoritesIllusts(string file = null)
{
if (file == null)
{
file = FavoritesPath;
}
lock (sync)
{
return ReadObject<IllustFavorite>(file);
}
}
public static void SaveFavoritesIllusts()
{
var file = FavoritesPath;
lock (sync)
{
var data = GetFavoriteObject();
data.LastFavoriteUtc = DateTime.UtcNow;
WriteObject(file, data);
}
}
public static string LoadUgoiraImage(string zip, string frame)
{
var file = Path.Combine(PersonalFolder, ugoiraFolder, zip, frame);
if (File.Exists(file))
{
//try
//{
return file;
//}
//catch (Exception ex)
//{
// App.DebugError("load.ugoira", $"failed to load ugoira frame: {zip}/{frame}, error: {ex.Message}");
//}
}
return null;
}
public static string SaveUgoiraImage(string zip, string frame, byte[] data)
{
try
{
var directory = Path.Combine(PersonalFolder, ugoiraFolder, zip);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var file = Path.Combine(directory, frame);
File.WriteAllBytes(file, data);
return file;
}
catch (Exception ex)
{
App.DebugError("save.ugoira", $"failed to save ugoira frame: {zip}/{frame}, error: {ex.Message}");
return null;
}
}
public static string GetUgoiraPath(string url, string ext)
{
return Path.Combine(PersonalFolder, ugoiraFolder, Path.GetFileNameWithoutExtension(url) + ext);
}
private static T ReadObject<T>(string file)
{
string content = null;
if (File.Exists(file))
{
try
{
content = File.ReadAllText(file);
}
catch (Exception ex)
{
App.DebugError("read", $"failed to read file: {file}, error: {ex.Message}");
}
}
else
{
//App.DebugError("read", $"file not found: {file}");
return default;
}
try
{
return JsonConvert.DeserializeObject<T>(content);
}
catch (Exception ex)
{
App.DebugError("read", $"failed to parse illust JSON object, error: {ex.Message}");
return default;
}
}
private static void WriteObject(string file, object obj)
{
var dir = Path.GetDirectoryName(file);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
string content;
try
{
content = JsonConvert.SerializeObject(obj, Formatting.None);
}
catch (Exception ex)
{
App.DebugError("write", $"failed to serialize object, error: {ex.Message}");
return;
}
try
{
File.WriteAllText(file, content, Encoding.UTF8);
}
catch (Exception ex)
{
App.DebugError("write", $"failed to write file: {file}, error: {ex.Message}");
}
}
public static IllustData LoadIllustData(bool force = false)
{
var file = Path.Combine(PersonalFolder, illustFile);
var result = HttpUtility.LoadObject<IllustData>(
file,
Configs.UrlIllustList,
Configs.Referer,
force: force);
if (result == null || result.error)
{
App.DebugPrint($"error when load illust data: {result?.message}, force({force})");
}
return result;
}
public static IllustRankingData LoadIllustRankingData(string mode, string date, int page, bool force = false)
{
var file = Path.Combine(CacheFolder, mode, $"{date}_{page}.json");
string query = $"mode={mode}";
if (mode != "male" && mode != "male_r18")
{
query += "&content=illust";
}
if (date != null)
{
query += $"&date={date}";
}
var referer = string.Format(Configs.RefererIllustRanking, query);
if (page > 1)
{
query += $"&p={page}";
}
query += "&format=json";
var result = HttpUtility.LoadObject<IllustRankingData>(
file,
string.Format(Configs.UrlIllustRanking, query),
referer,
namehandler: rst =>
{
return Path.Combine(CacheFolder, mode, $"{rst.date}_{page}.json");
},
header: headers =>
{
headers.Add("X-Requested-With", "XMLHttpRequest");
},
force: force);
if (result == null)
{
App.DebugPrint($"error when load ranking data: mode({mode}), date({date}), page({page}), force({force})");
}
return result;
}
public static IllustRecommendsData LoadIllustRecommendsInitData(string id, bool force = false)
{
var file = Path.Combine(CacheFolder, recommendsFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustRecommendsData>(
file,
string.Format(Configs.UrlIllustRecommendsInit, id),
string.Format(Configs.RefererIllust, id),
force: force);
if (result == null || result.error)
{
App.DebugPrint($"error when load recommends init data: {result?.message}, force({force})");
}
return result;
}
public static IllustRecommendsData LoadIllustRecommendsListData(string id, string[] ids)
{
if (ids == null || ids.Length == 0)
{
return null;
}
var ps = string.Concat(ids.Select(i => $"illust_ids%5B%5D={i}&"));
var result = HttpUtility.LoadObject<IllustRecommendsData>(
null,
string.Format(Configs.UrlIllustRecommendsList, ps),
string.Format(Configs.RefererIllust, id));
if (result == null || result.error)
{
App.DebugPrint($"error when load recommends list data: {result?.message}");
}
return result;
}
public static IllustPreloadBody LoadIllustPreloadData(string id, bool force = false)
{
var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustPreloadBody>(
file,
string.Format(Configs.UrlIllust, id),
null,
force: force,
action: content =>
{
var index = content.IndexOf(Configs.SuffixPreload);
if (index > 0)
{
index += Configs.SuffixPreloadLength;
var end = content.IndexOf('\'', index);
if (end > index)
{
content = content.Substring(index, end - index);
}
}
return content;
});
if (result == null)
{
App.DebugPrint($"error when load preload data: force({force})");
}
return result;
}
public static IllustPageData LoadIllustPageData(string id, bool force = false)
{
var file = Path.Combine(CacheFolder, pagesFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustPageData>(
file,
string.Format(Configs.UrlIllustPage, id),
string.Format(Configs.RefererIllust, id),
force: force);
if (result == null || result.error)
{
App.DebugPrint($"error when load page data: {result?.message}, force({force})");
}
return result;
}
public static IllustUgoiraData LoadIllustUgoiraData(string id, bool force = false)
{
var file = Path.Combine(PersonalFolder, ugoiraFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustUgoiraData>(
file,
string.Format(Configs.UrlIllustUgoira, id),
string.Format(Configs.RefererIllust, id),
force: force);
if (result == null || result.error)
{
App.DebugPrint($"error when load ugoira data: {result?.message}, force({force})");
}
return result;
}
public static IllustUserListData LoadIllustUserInitData(string userId)
{
var list = HttpUtility.LoadObject<IllustUserListData>(
null,
string.Format(Configs.UrlIllustUserAll, userId),
string.Format(Configs.RefererIllustUser, userId));
if (list == null || list.error)
{
App.DebugPrint($"error when load user data: {list?.message}");
}
return list;
}
public static IllustUserData LoadIllustUserData(string userId, string[] ids, bool firstPage)
{
if (ids == null || ids.Length == 0)
{
return null;
}
var ps = string.Concat(ids.Select(i => $"ids%5B%5D={i}&"));
var result = HttpUtility.LoadObject<IllustUserData>(
null,
string.Format(Configs.UrlIllustUserArtworks, userId, ps, firstPage ? 1 : 0),
string.Format(Configs.RefererIllustUser, userId));
if (result == null || result.error)
{
App.DebugPrint($"error when load user illust data: {result?.message}");
}
return result;
}
public static ImageSource LoadIllustImage(string url)
{
return LoadImage(url, PersonalFolder, imageFolder);
}
public static ImageSource LoadPreviewImage(string url, bool downloading = true)
{
return LoadImage(url, PersonalFolder, previewFolder, downloading);
}
public static ImageSource LoadThumbnailImage(string url)
{
return LoadImage(url, CacheFolder, thumbFolder);
}
public static ImageSource LoadUserProfileImage(string url)
{
return LoadImage(url, CacheFolder, userFolder);
}
public static bool CheckIllustImage(string url)
{
var file = Path.Combine(PersonalFolder, imageFolder, Path.GetFileName(url));
return File.Exists(file);
}
public static bool CheckUgoiraVideo(string url)
{
var file = Path.Combine(PersonalFolder, ugoiraFolder, Path.GetFileNameWithoutExtension(url) + ".mp4");
return File.Exists(file);
}
public static string GetPreviewImagePath(string url)
{
var file = Path.Combine(PersonalFolder, previewFolder, Path.GetFileName(url));
if (File.Exists(file))
{
return file;
}
return null;
}
private static ImageSource LoadImage(string url, string working, string folder, bool downloading = true)
{
var file = Path.Combine(working, folder, Path.GetFileName(url));
ImageSource image;
if (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 (downloading && image == null)
{
file = HttpUtility.DownloadImage(url, working, folder);
if (file != null)
{
return ImageSource.FromFile(file);
}
}
return image;
}
}
public static class Configs
{
public const string IsOnR18Key = "is_on_r18";
public const string IsProxiedKey = "is_proxied";
public const string HostKey = "host";
public const string PortKey = "port";
public const string QueryModeKey = "query_mode";
public const string QueryTypeKey = "query_type";
public const string QueryDateKey = "query_date";
public const int MaxPageThreads = 3;
public const int MaxThreads = 6;
public const string Referer = "https://www.pixiv.net/";
public const string RefererIllust = "https://www.pixiv.net/artworks/{0}";
public const string RefererIllustRanking = "https://www.pixiv.net/ranking.php?{0}";
public const string RefererIllustUser = "https://www.pixiv.net/users/{0}/illustrations";
public static bool IsOnR18;
public static WebProxy Proxy;
private static string Prefix => Proxy == null ?
"https://hk.tsanie.us/reverse/" :
"https://www.pixiv.net/";
public const string SuffixPreload = " id=\"meta-preload-data\" content='";
public const int SuffixPreloadLength = 33; // SuffixPreload.Length
public static string UrlIllustList => Prefix + "ajax/top/illust?mode=all&lang=zh";
public static string UrlIllust => Prefix + "artworks/{0}";
public static string UrlIllustRanking => Prefix + "ranking.php?{0}";
public static string UrlIllustUserAll => Prefix + "ajax/user/{0}/profile/all?lang=zh";
public static string UrlIllustUserArtworks => Prefix + "ajax/user/{0}/profile/illusts?{1}work_category=illust&is_first_page={2}&lang=zh";
public static string UrlIllustPage => Prefix + "ajax/illust/{0}/pages?lang=zh";
public static string UrlIllustUgoira => Prefix + "ajax/illust/{0}/ugoira_meta?lang=zh";
public static string UrlIllustRecommendsInit => Prefix + "ajax/illust/{0}/recommend/init?limit=18&lang=zh";
public static string UrlIllustRecommendsList => Prefix + "ajax/illust/recommend/illusts?{0}lang=zh";
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 AcceptJson = "application/json";
//public const string AcceptEncoding = "gzip, deflate";
public const string AcceptLanguage = "zh-cn";
#if __ANDROID__
public const string UserId = "53887721";
public const string Cookie =
"PHPSESSID=5sn8n049j5c18l0tlj91qrjhesgddhjv; " +
"a_type=0; b_type=1; c_type=29; d_type=2; " +
"p_ab_d_id=1021624041; p_ab_id=2; p_ab_id_2=0; " +
"privacy_policy_agreement=2; " +
"login_ever=yes; " +
"__cfduid=d84153bf70ae67315a8bc297299d39eb61588856027; " +
"first_visit_datetime_pc=2020-05-07+21%3A53%3A47; " +
"yuid_b=MYkIJXc";
#else
public const string UserId = "2603358";
public const string Cookie =
"PHPSESSID=2603358_VHyGPeRaz7LpeoFkRsHvjXIpApCMb56a; " +
"a_type=0; b_type=1; c_type=31; d_type=2; " +
"p_ab_id=2; p_ab_id_2=6; p_ab_d_id=1155161977; " +
"privacy_policy_agreement=2; " +
"login_ever=yes; " +
"__cfduid=d9fa2d4d1ddd30db85ebb519f9855d2561587806747; " +
"first_visit_datetime_pc=2019-10-29+22%3A05%3A30; " +
"yuid_b=NgcXQWQ";
#endif
private const string URL_PREVIEW = "https://i.pximg.net/c/360x360_70";
public static string GetThumbnailUrl(string url)
{
if (url == null)
{
return null;
}
url = url.ToLower().Replace("/custom-thumb/", "/img-master/");
var index = url.LastIndexOf("_square1200.jpg");
if (index < 0)
{
index = url.LastIndexOf("_custom1200.jpg");
}
if (index > 0)
{
url = url.Substring(0, index) + "_master1200.jpg";
var start = url.IndexOf("/img-master/");
if (start > 0)
{
url = URL_PREVIEW + url.Substring(start);
}
}
return url;
}
}
public static class Routes
{
public const string Illust = "illust";
public const string Detail = "detail";
public const string Follow = "follow";
public const string Recommends = "recommends";
public const string ByUser = "byuser";
public const string Ranking = "ranking";
public const string Favorites = "favorites";
public const string Option = "option";
}
}