From e7fbee8d41113a9ef1cbf9f1476bd0fbf738f18d Mon Sep 17 00:00:00 2001
From: Tsanie Lily <tsorgy@gmail.com>
Date: Mon, 4 May 2020 18:33:17 +0800
Subject: [PATCH] * Pixiview/Pixiview.csproj: * Pixiview/Utils/Stores.cs: *
 Pixiview/Utils/IllustData.cs: import JSON supported

* Pixiview/App.xaml.cs: add debug

* Pixiview/MainPage.xaml.cs:
---
 Pixiview/App.xaml.cs         |  14 ++-
 Pixiview/MainPage.xaml.cs    |  11 ++-
 Pixiview/Pixiview.csproj     |   1 +
 Pixiview/Utils/IllustData.cs | 106 ++++++++++++++++++++++
 Pixiview/Utils/Stores.cs     | 166 +++++++++++++++++++++++++++++++++--
 5 files changed, 287 insertions(+), 11 deletions(-)
 create mode 100644 Pixiview/Utils/IllustData.cs

diff --git a/Pixiview/App.xaml.cs b/Pixiview/App.xaml.cs
index 3da910a..d907617 100644
--- a/Pixiview/App.xaml.cs
+++ b/Pixiview/App.xaml.cs
@@ -1,4 +1,6 @@
-using Pixiview.UI;
+using System;
+using System.Diagnostics;
+using Pixiview.UI;
 using Pixiview.Utils;
 using Xamarin.Forms;
 
@@ -54,5 +56,15 @@ namespace Pixiview
         protected override void OnResume()
         {
         }
+
+        public static void DebugPrint(string message)
+        {
+            Debug.WriteLine("[{0:HH:mm:ss.ffff}] - {1}", DateTime.Now, message);
+        }
+
+        public static void DebugError(string category, string message)
+        {
+            Debug.Fail(string.Format("[{0:HH:mm:ss.ffff}] - {1} - {2}", DateTime.Now, category, message));
+        }
     }
 }
diff --git a/Pixiview/MainPage.xaml.cs b/Pixiview/MainPage.xaml.cs
index c47c903..54c18ed 100644
--- a/Pixiview/MainPage.xaml.cs
+++ b/Pixiview/MainPage.xaml.cs
@@ -1,6 +1,6 @@
 using System;
 using System.ComponentModel;
-using System.Diagnostics;
+using System.Threading.Tasks;
 using Pixiview.UI;
 using Pixiview.Utils;
 
@@ -11,6 +11,8 @@ namespace Pixiview
     [DesignTimeVisible(false)]
     public partial class MainPage : AdaptedPage
     {
+        private IllustData illustData;
+
         public MainPage()
         {
             InitializeComponent();
@@ -19,7 +21,12 @@ namespace Pixiview
 
         public override void OnLoad()
         {
-            Debug.WriteLine(Stores.PersonalFolder);
+            App.DebugPrint($"folder: {Stores.PersonalFolder}");
+            Task.Run(async () =>
+            {
+                illustData = await Stores.LoadIllustData();
+                App.DebugPrint(illustData.message);
+            });
         }
 
         void NavigationTitle_RightButtonClicked(object sender, EventArgs e)
diff --git a/Pixiview/Pixiview.csproj b/Pixiview/Pixiview.csproj
index 776110c..d5405d3 100644
--- a/Pixiview/Pixiview.csproj
+++ b/Pixiview/Pixiview.csproj
@@ -13,6 +13,7 @@
   <ItemGroup>
     <PackageReference Include="Xamarin.Forms" Version="4.6.0.726" />
     <PackageReference Include="Xamarin.Essentials" Version="1.5.3.1" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
   </ItemGroup>
   <ItemGroup>
     <Folder Include="UI\" />
diff --git a/Pixiview/Utils/IllustData.cs b/Pixiview/Utils/IllustData.cs
new file mode 100644
index 0000000..60ddaef
--- /dev/null
+++ b/Pixiview/Utils/IllustData.cs
@@ -0,0 +1,106 @@
+using Newtonsoft.Json;
+
+namespace Pixiview.Utils
+{
+    public class IllustData
+    {
+        public bool error;
+        public string message;
+        public Body body;
+
+        public class Body
+        {
+            public Page page;
+            public Thumbnail thumbnails;
+            public User[] users;
+
+            public class Page
+            {
+                public int[] follow;
+                public string[] recommend;
+                public RecommendByTag[] recommendByTags;
+                public Ranking ranking;
+                public RecommendUser[] recommendUser;
+                public EditorRecommend[] editorRecommend;
+                public string[] newPost;
+
+                public class RecommendByTag
+                {
+                    public string tag;
+                    public string[] ids;
+                }
+
+                public class Ranking
+                {
+                    public RankingItem[] items;
+                    public string date;
+
+                    public class RankingItem
+                    {
+                        public string rank;
+                        public string id;
+                    }
+                }
+
+                public class RecommendUser
+                {
+                    public int id;
+                    public string[] illustIds;
+                }
+
+                public class EditorRecommend
+                {
+                    public string illustId;
+                    public string comment;
+                }
+            }
+
+            public class Thumbnail
+            {
+                public Illust[] illust;
+
+                public class Illust
+                {
+                    public string illustId;
+                    public string illustTitle;
+                    public string id;
+                    public string title;
+                    public string url;
+                    public string description;
+                    public string[] tags;
+                    public string userId;
+                    public string userName;
+                    public int width;
+                    public int height;
+                    public string alt;
+                    public IllustUrls urls;
+                    public string seriesId;
+                    public string seriesTitle;
+                    public string profileImageUrl;
+
+                    public class IllustUrls
+                    {
+                        [JsonProperty("250x250")]
+                        public string x250;
+                        [JsonProperty("360x360")]
+                        public string x360;
+                        [JsonProperty("540x540")]
+                        public string x540;
+                    }
+                }
+            }
+
+            public class User
+            {
+                public string userId;
+                public string name;
+                public string image;
+                public string imageBig;
+                public bool premium;
+                public bool isFollowed;
+                public string background;
+                public int partial;
+            }
+        }
+    }
+}
diff --git a/Pixiview/Utils/Stores.cs b/Pixiview/Utils/Stores.cs
index 140fbc0..74d04d4 100644
--- a/Pixiview/Utils/Stores.cs
+++ b/Pixiview/Utils/Stores.cs
@@ -1,8 +1,11 @@
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Net;
 using System.Net.Http;
+using System.Text;
 using System.Threading.Tasks;
+using Newtonsoft.Json;
 
 namespace Pixiview.Utils
 {
@@ -10,14 +13,137 @@ namespace Pixiview.Utils
     {
         public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
         //public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);
-        public const string ImageFolder = "images";
+        private const string imageFolder = "img-master";
+        private const string userFolder = "user-profile";
+        private const string illustFile = "illust.json";
 
-        public static async Task DownloadImage(string url)
+        public static async Task<IllustData> LoadIllustData()
         {
+            var file = Path.Combine(PersonalFolder, illustFile);
+            string content = null;
+            if (File.Exists(file))
+            {
+                try
+                {
+                    content = File.ReadAllText(file);
+                }
+                catch (Exception ex)
+                {
+                    App.DebugError("illust.load", $"failed to read file: {file}, error: {ex.Message}");
+                }
+            }
+            if (content == null)
+            {
+                var response = Download(Configs.UrlIllust, headers: new[]
+                {
+                    ("cookie",    Configs.Cookie),
+                    ("x-user-id", Configs.UserId)
+                });
+                if (response == null)
+                {
+                    return null;
+                }
+                using (response)
+                {
+                    content = await response.Content.ReadAsStringAsync();
+                    try
+                    {
+                        File.WriteAllText(file, content, Encoding.UTF8);
+                    }
+                    catch (Exception ex)
+                    {
+                        App.DebugError("illust.save", $"failed to save illust JSON object, error: {ex.Message}");
+                    }
+                }
+            }
+            try
+            {
+                return JsonConvert.DeserializeObject<IllustData>(content);
+            }
+            catch (Exception ex)
+            {
+                App.DebugError("illust.load", $"failed to parse illust JSON object, error: {ex.Message}");
+                return null;
+            }
+        }
+
+        public static string LoadIllustImage(string url)
+        {
+            return LoadImage(url, imageFolder);
+        }
+
+        public static string LoadUserProfileImage(string url)
+        {
+            return LoadImage(url, userFolder);
+        }
+
+        public static string LoadImage(string url, string folder)
+        {
+            var file = Path.Combine(PersonalFolder, imageFolder, Path.GetFileName(url));
+            if (File.Exists(file))
+            {
+                return file;
+            }
+            else
+            {
+                file = DownloadImage(url, folder).Result;
+                return file;
+            }
+        }
+
+        public static async Task<string> DownloadImage(string url, string folder)
+        {
+            try
+            {
+                var file = Path.Combine(PersonalFolder, folder, Path.GetFileName(url));
+                var response = Download(url);
+                if (response == null)
+                {
+                    return null;
+                }
+                using (response)
+                using (var fs = File.OpenWrite(file))
+                {
+                    await response.Content.CopyToAsync(fs);
+                    if (response.Headers.Date != null)
+                    {
+                        File.SetLastWriteTimeUtc(file, response.Headers.Date.Value.UtcDateTime);
+                    }
+                }
+                return file;
+            }
+            catch (Exception ex)
+            {
+                App.DebugError("image.download", ex.Message);
+                return null;
+            }
+        }
+
+        private static T TryCount<T>(Func<T> func, int tryCount = 3)
+        {
+            int tries = 0;
+            while (tries < tryCount)
+            {
+                try
+                {
+                    return func();
+                }
+                catch (Exception ex)
+                {
+                    tries++;
+                    App.DebugError("try.do", $"tries: {tries}, error: {ex.Message}");
+                }
+            }
+            return default;
+        }
+
+        private static HttpResponseMessage Download(string url, string referer = null, IEnumerable<(string header, string value)> headers = null)
+        {
+            App.DebugPrint($"GET: {url}");
             var uri = new Uri(url);
             var handler = new HttpClientHandler
             {
-                Proxy = new WebProxy("10.0.10.100", 8088),
+                Proxy = Configs.Proxy,
                 UseProxy = true
             };
             var client = new HttpClient(handler)
@@ -29,14 +155,38 @@ namespace Pixiview.Utils
                 Version = new Version(2, 0)
             })
             {
-                request.Headers.Referrer = new Uri("https://www.pixiv.net/");
-                var file = Path.Combine(PersonalFolder, ImageFolder, Path.GetFileName(uri.LocalPath));
-                using (var response = await client.SendAsync(request))
-                using (var fs = File.OpenWrite(file))
+                request.Headers.Referrer = referer == null ? Configs.Referer : new Uri(referer);
+                if (headers != null)
                 {
-                    await response.Content.CopyToAsync(fs);
+                    foreach (var (header, value) in headers)
+                    {
+                        request.Headers.Add(header, value);
+                    }
                 }
+                return TryCount(() => client.SendAsync(request).Result);
             }
         }
     }
+
+    public static class Configs
+    {
+        public static readonly WebProxy Proxy = new WebProxy("10.0.10.100", 8088);
+        public static readonly Uri Referer = new Uri("https://www.pixiv.net/");
+
+        public const string UrlIllust = "https://www.pixiv.net/ajax/top/illust?mode=all&lang=zh";
+        public const string Cookie = "first_visit_datetime_pc=2019-10-29+22%3A05%3A30; p_ab_id=2; p_ab_id_2=6;" +
+            " p_ab_d_id=1155161977; a_type=0; b_type=1; d_type=2; module_orders_mypage=%5B%7B%22name%22%3A%22s" +
+            "ketch_live%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22tag_follow%22%2C%22visible%22%3Atrue" +
+            "%7D%2C%7B%22name%22%3A%22recommended_illusts%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22ev" +
+            "eryone_new_illusts%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22following_new_illusts%22%2C%" +
+            "22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22mypixiv_new_illusts%22%2C%22visible%22%3Atrue%7D%2C%7" +
+            "B%22name%22%3A%22spotlight%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22fanbox%22%2C%22visib" +
+            "le%22%3Atrue%7D%2C%7B%22name%22%3A%22featured_tags%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3" +
+            "A%22contests%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22user_events%22%2C%22visible%22%3At" +
+            "rue%7D%2C%7B%22name%22%3A%22sensei_courses%22%2C%22visible%22%3Atrue%7D%2C%7B%22name%22%3A%22boot" +
+            "h_follow_items%22%2C%22visible%22%3Atrue%7D%5D; yuid_b=NgcXQWQ; login_ever=yes; c_type=31; PHPSES" +
+            "SID=2603358_VHyGPeRaz7LpeoFkRsHvjXIpApCMb56a; __cfduid=d9fa2d4d1ddd30db85ebb519f9855d256158780674" +
+            "7; privacy_policy_agreement=2";
+        public const string UserId = "2603358";
+    }
 }