From dffe0bb3a4bf4a3aeb811797085547efabd7192f Mon Sep 17 00:00:00 2001 From: Tsanie Lily Date: Sat, 9 May 2020 17:39:01 +0800 Subject: [PATCH] feature: open with URL --- Pixiview.iOS/AppDelegate.cs | 6 ++ Pixiview.iOS/Info.plist | 15 ++++ Pixiview/App.cs | 25 ++++++ Pixiview/Illust/FavoritesPage.xaml.cs | 4 + Pixiview/Illust/IllustCollectionPage.cs | 17 ++-- Pixiview/Illust/RankingPage.xaml.cs | 3 +- Pixiview/Illust/ViewIllustPage.xaml.cs | 26 ++++-- Pixiview/Resources/Languages/zh-CN.xml | 1 + Pixiview/Resources/ResourceHelper.cs | 1 + Pixiview/Utils/IllustData.cs | 105 +++++++++++++++++++++--- Pixiview/Utils/Stores.cs | 82 +++++++++++++++--- 11 files changed, 251 insertions(+), 34 deletions(-) diff --git a/Pixiview.iOS/AppDelegate.cs b/Pixiview.iOS/AppDelegate.cs index 14d3c98..47ab490 100644 --- a/Pixiview.iOS/AppDelegate.cs +++ b/Pixiview.iOS/AppDelegate.cs @@ -23,5 +23,11 @@ namespace Pixiview.iOS return base.FinishedLaunching(app, options); } + + public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options) + { + App.DebugPrint($"url: {url}."); + return App.OpenUrl(url.AbsoluteString); + } } } diff --git a/Pixiview.iOS/Info.plist b/Pixiview.iOS/Info.plist index 1d65878..3861c37 100644 --- a/Pixiview.iOS/Info.plist +++ b/Pixiview.iOS/Info.plist @@ -50,5 +50,20 @@ UIFileSharingEnabled + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLSchemes + + pixiview + + CFBundleURLName + org.tsanie.pixiview + CFBundleURLIconFile + Assets.xcassets/AppIcon.appiconset/Icon58 + + diff --git a/Pixiview/App.cs b/Pixiview/App.cs index e3fea30..c414be8 100644 --- a/Pixiview/App.cs +++ b/Pixiview/App.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Text.RegularExpressions; +using Pixiview.Illust; using Pixiview.Resources; using Pixiview.UI.Theme; using Pixiview.Utils; @@ -96,5 +98,28 @@ namespace Pixiview { Debug.Fail(string.Format("[{0:HH:mm:ss.ffff}] - {1} - {2}", DateTime.Now, category, message)); } + + public static bool OpenUrl(string url) + { + var current = Current.MainPage; + if (current != null) + { + var m = Regex.Match(url, "/artworks/([0-9]+)", RegexOptions.IgnoreCase); + if (m.Success) + { + var illust = new IllustItem { Id = m.Groups[1].Value }; + var page = new ViewIllustPage(illust, true); + MainThread.BeginInvokeOnMainThread(() => current.Navigation.PushAsync(page)); + } + else + { + MainThread.BeginInvokeOnMainThread(() => current.DisplayAlert( + url, + ResourceHelper.InvalidUrl, + ResourceHelper.Ok)); + } + } + return true; + } } } diff --git a/Pixiview/Illust/FavoritesPage.xaml.cs b/Pixiview/Illust/FavoritesPage.xaml.cs index 7bd5c05..8e62a95 100644 --- a/Pixiview/Illust/FavoritesPage.xaml.cs +++ b/Pixiview/Illust/FavoritesPage.xaml.cs @@ -15,6 +15,10 @@ namespace Pixiview.Illust protected override bool IsFavoriteVisible => false; + public override void OnUnload() + { + } + protected override void OnAppearing() { //base.OnAppearing(); diff --git a/Pixiview/Illust/IllustCollectionPage.cs b/Pixiview/Illust/IllustCollectionPage.cs index 09453ed..43ba8f5 100644 --- a/Pixiview/Illust/IllustCollectionPage.cs +++ b/Pixiview/Illust/IllustCollectionPage.cs @@ -13,10 +13,7 @@ using Xamarin.Forms; namespace Pixiview.Illust { - public abstract class FavoriteIllustCollectionPage : IllustCollectionPage - { - public override void OnUnload() { } - } + public abstract class FavoriteIllustCollectionPage : IllustCollectionPage { } public abstract class IllustDataCollectionPage : IllustCollectionPage { } public abstract class IllustUserDataCollectionPage : IllustCollectionPage { } @@ -138,7 +135,7 @@ namespace Pixiview.Illust protected abstract IEnumerable DoGetIllustList(T data, ICommand command); protected virtual void OnIllustImageTapped(IllustItem illust) { - var page = new ViewIllustPage(illust, this); + var page = new ViewIllustPage(illust, IsFavoriteVisible); Navigation.PushAsync(page); } @@ -452,6 +449,8 @@ namespace Pixiview.Illust [JsonObject(MemberSerialization.OptIn)] public class IllustItem : BindableObject { + public static readonly BindableProperty TitleProperty = BindableProperty.Create( + nameof(Title), typeof(string), typeof(IllustItem)); public static readonly BindableProperty ImageProperty = BindableProperty.Create( nameof(Image), typeof(ImageSource), typeof(IllustItem)); public static readonly BindableProperty ProfileImageProperty = BindableProperty.Create( @@ -461,6 +460,12 @@ namespace Pixiview.Illust public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create( nameof(IsFavorite), typeof(bool), typeof(IllustItem)); + [JsonProperty] + public string Title + { + get => (string)GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } public ImageSource Image { get => (ImageSource)GetValue(ImageProperty); @@ -488,8 +493,6 @@ namespace Pixiview.Illust [JsonProperty] public string ImageUrl { get; set; } [JsonProperty] - public string Title { get; set; } - [JsonProperty] public bool IsRestrict { get; set; } [JsonProperty] public string ProfileUrl { get; set; } diff --git a/Pixiview/Illust/RankingPage.xaml.cs b/Pixiview/Illust/RankingPage.xaml.cs index cc2b849..b0b65b9 100644 --- a/Pixiview/Illust/RankingPage.xaml.cs +++ b/Pixiview/Illust/RankingPage.xaml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Windows.Input; using Pixiview.UI; using Pixiview.Utils; +using Xamarin.Essentials; using Xamarin.Forms; namespace Pixiview.Illust @@ -69,7 +70,7 @@ namespace Pixiview.Illust { date = date.Substring(0, 4) + "-" + date.Substring(4, 2) + "-" + date.Substring(6, 2); } - Device.BeginInvokeOnMainThread(() => Title = date); + MainThread.BeginInvokeOnMainThread(() => Title = date); } return data; } diff --git a/Pixiview/Illust/ViewIllustPage.xaml.cs b/Pixiview/Illust/ViewIllustPage.xaml.cs index ee75910..7cba37a 100644 --- a/Pixiview/Illust/ViewIllustPage.xaml.cs +++ b/Pixiview/Illust/ViewIllustPage.xaml.cs @@ -55,15 +55,15 @@ namespace Pixiview.Illust public int CurrentPage { get; private set; } - private readonly IIllustCollectionPage collectionPage; + private readonly bool saveFavorites; private readonly ICommand longPressed; private readonly ImageSource fontIconLove; private readonly ImageSource fontIconNotLove; - public ViewIllustPage(IllustItem illust, IIllustCollectionPage page) + public ViewIllustPage(IllustItem illust, bool save) { IllustItem = illust; - collectionPage = page; + saveFavorites = save; longPressed = new Command(Illust_LongPressed); BindingContext = this; @@ -97,7 +97,7 @@ namespace Pixiview.Illust { base.OnDisappearing(); - if (collectionPage is IllustDataCollectionPage) + if (saveFavorites) { Stores.SaveFavoritesIllusts(); } @@ -161,7 +161,7 @@ namespace Pixiview.Illust LongPressed = longPressed }; } - items = tmp; + Illusts = items = tmp; } for (var i = 0; i < items.Length; i++) @@ -170,6 +170,20 @@ namespace Pixiview.Illust var p = pages.body[i]; item.PreviewUrl = p.urls.regular; item.OriginalUrl = p.urls.original; + + if (i == 0 && illustItem.ImageUrl == null) + { + // maybe open from a link + var preload = Stores.LoadIllustPreloadData(illustItem.Id); + if (preload != null && preload.illust.TryGetValue(illustItem.Id, out var illust)) + { + illust.CopyToItem(illustItem); + if (preload.user.TryGetValue(illust.userId, out var user)) + { + illustItem.ProfileUrl = user.image; + } + } + } } DoLoadImage(0, true); @@ -329,7 +343,7 @@ namespace Pixiview.Illust var image = Stores.LoadIllustImage(item.OriginalUrl); if (image != null) { - Device.BeginInvokeOnMainThread(async () => + MainThread.BeginInvokeOnMainThread(async () => { var service = DependencyService.Get(); var result = await service.SaveImageToGalleryAsync(image); diff --git a/Pixiview/Resources/Languages/zh-CN.xml b/Pixiview/Resources/Languages/zh-CN.xml index 5bf800d..a45cc3f 100644 --- a/Pixiview/Resources/Languages/zh-CN.xml +++ b/Pixiview/Resources/Languages/zh-CN.xml @@ -20,4 +20,5 @@ 浏览用户 成功保存图片到照片库。 原图已保存,是否继续? + 无法识别该 URL。 \ No newline at end of file diff --git a/Pixiview/Resources/ResourceHelper.cs b/Pixiview/Resources/ResourceHelper.cs index 28b0b1f..d4936d6 100644 --- a/Pixiview/Resources/ResourceHelper.cs +++ b/Pixiview/Resources/ResourceHelper.cs @@ -22,6 +22,7 @@ namespace Pixiview.Resources public static string UserDetail => GetResource(nameof(UserDetail)); public static string SaveSuccess => GetResource(nameof(SaveSuccess)); public static string AlreadySavedQuestion => GetResource(nameof(AlreadySavedQuestion)); + public static string InvalidUrl => GetResource(nameof(InvalidUrl)); static readonly Dictionary dict = new Dictionary(); diff --git a/Pixiview/Utils/IllustData.cs b/Pixiview/Utils/IllustData.cs index 0be7284..e3537d1 100644 --- a/Pixiview/Utils/IllustData.cs +++ b/Pixiview/Utils/IllustData.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Newtonsoft.Json; using Pixiview.Illust; @@ -47,8 +48,8 @@ namespace Pixiview.Utils return new IllustItem { Id = illustId, - ImageUrl = urls?.x360 ?? url, Title = illustTitle, + ImageUrl = urls?.x360 ?? url, IsRestrict = xRestrict == 1, ProfileUrl = profileImageUrl, UserId = userId, @@ -60,6 +61,18 @@ namespace Pixiview.Utils } } + 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; + } + public class IllustData : IllustResponse { } public class IllustBody { @@ -112,20 +125,92 @@ namespace Pixiview.Utils { public Illust[] illust; } + } - public class User + public class IllustPreloadBody + { + public Dictionary illust; + public Dictionary user; + + public class Illust { + public string illustId; + public string illustTitle; + public string illustComment; + public string id; + public string title; + public string description; + public int illustType; + public DateTime createDate; + public DateTime uploadDate; + public int xRestrict; + public IllustUrls urls; + public IllustTag tags; + public string alt; public string userId; - public string name; - public string image; - public string imageBig; - public bool premium; - public bool isFollowed; - public string background; - public int partial; + public string userName; + public string userAccount; + //public Dictionary userIllusts; + public int width; + public int height; + public int pageCount; + public int bookmarkCount; + public int likeCount; + public int commentCount; + public int responseCount; + public int viewCount; + public bool isOriginal; + + public IllustItem CopyToItem(IllustItem item) + { + item.Title = illustTitle; + item.ImageUrl = urls?.regular; + item.IsRestrict = xRestrict == 1; + item.UserId = userId; + item.UserName = userName; + item.Width = width; + item.Height = height; + item.PageCount = pageCount; + return item; + } + + [JsonIgnore] + public string Url => urls.regular; + + public class IllustUrls + { + public string mini; + public string thumb; + public string small; + public string regular; + public string original; + } + + public class IllustTag + { + public string authorId; + public bool isLocked; + public IllustTagItem[] tags; + public bool writable; + + public class IllustTagItem + { + public string tag; + public bool locked; + public bool deletable; + public string userId; + public IllustTranslate translation; + public string userName; + } + } } } + public class IllustTranslate + { + public string en; + } + public class IllustPageData : IllustResponse { } public class IllustPageBody { diff --git a/Pixiview/Utils/Stores.cs b/Pixiview/Utils/Stores.cs index abdb0f5..24ffeac 100644 --- a/Pixiview/Utils/Stores.cs +++ b/Pixiview/Utils/Stores.cs @@ -18,15 +18,16 @@ namespace Pixiview.Utils public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache); - private const string pagesFolder = "pages"; private const string imageFolder = "img-original"; private const string previewFolder = "img-master"; - private const string thumbFolder = "img-thumb"; - private const string userFolder = "user-profile"; - private const string illustFile = "illust.json"; private const string favoriteFile = "favorites.json"; + private const string pagesFolder = "pages"; + private const string preloadsFolder = "preloads"; + private const string thumbFolder = "img-thumb"; + private const string userFolder = "user-profile"; + private static readonly object sync = new object(); public static bool NetworkAvailable @@ -93,7 +94,7 @@ namespace Pixiview.Utils } } - private static T LoadObject(string file, string url, string referer = null, bool force = false) + private static T LoadObject(string file, string url, string referer, bool force = false, Func action = null) { string content = null; if (!force && file != null && File.Exists(file)) @@ -111,7 +112,10 @@ namespace Pixiview.Utils { var response = Download(url, headers => { - headers.Referrer = referer == null ? Configs.Referer : new Uri(referer); + if (referer != null) + { + headers.Referrer = new Uri(referer); + } headers.Add("user-agent", Configs.UserAgent); headers.Add("accept", Configs.AcceptJson); headers.Add("cookie", Configs.Cookie); @@ -123,7 +127,20 @@ namespace Pixiview.Utils } using (response) { - content = response.Content.ReadAsStringAsync().Result; + try + { + content = response.Content.ReadAsStringAsync().Result; + if (action != null) + { + content = action(content); + } + } + catch (Exception ex) + { + App.DebugError("load.strea", $"failed to read stream, error: {ex.Message}"); + return default; + } + if (file != null) { try @@ -217,18 +234,52 @@ namespace Pixiview.Utils var result = LoadObject( file, Configs.UrlIllustList, + Configs.Referer, force: force); + if (result.error) + { + App.DebugPrint($"error when load illust data: {result.message} ({force})"); + } + return result; + } + + public static IllustPreloadBody LoadIllustPreloadData(string id, bool force = false) + { + var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json"); + var result = LoadObject( + 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; + }); return result; } public static IllustPageData LoadIllustPageData(string id, bool force = false) { - var file = Path.Combine(PersonalFolder, pagesFolder, $"{id}.json"); + var file = Path.Combine(CacheFolder, pagesFolder, $"{id}.json"); var result = LoadObject( file, string.Format(Configs.UrlIllustPage, id), string.Format(Configs.UrlIllust, id), force: force); + if (result.error) + { + App.DebugPrint($"error when load page data: {result.message} ({force})"); + } return result; } @@ -239,7 +290,12 @@ namespace Pixiview.Utils string.Format(Configs.UrlIllustUserAll, userId), string.Format(Configs.UrlIllustUser, userId), force: force); + if (list.error) + { + App.DebugPrint($"error when load user data: {list.message} ({force})"); + } + // TODO var ids = string.Join("&ids%5B%5D=", list.body.illusts.Keys.Take(20)); var result = LoadObject( @@ -247,6 +303,10 @@ namespace Pixiview.Utils string.Format(Configs.UrlIllustUserArtworks, userId, ids, 1), string.Format(Configs.UrlIllustUser, userId), force: force); + if (result.error) + { + App.DebugPrint($"error when load user illust data: {result.message} ({force})"); + } return result; } @@ -330,7 +390,7 @@ namespace Pixiview.Utils App.DebugPrint($"download, url: {url}"); var response = Download(url, headers => { - headers.Referrer = Configs.Referer; + headers.Referrer = new Uri(Configs.Referer); headers.Add("user-agent", Configs.UserAgent); headers.Add("accept", Configs.AcceptImage); }); @@ -409,11 +469,13 @@ namespace Pixiview.Utils public static class Configs { public static readonly WebProxy Proxy = new WebProxy("router.tsanie.us", 8088); - public static readonly Uri Referer = new Uri("https://www.pixiv.net/"); public const int MaxThreads = 3; + public const string Referer = "https://www.pixiv.net/"; public const string UrlIllustList = "https://www.pixiv.net/ajax/top/illust?mode=all&lang=zh"; public const string UrlIllust = "https://www.pixiv.net/artworks/{0}"; + public const string SuffixPreload = " id=\"meta-preload-data\" content='"; + public const int SuffixPreloadLength = 33; // SuffixPreload.Length public const string UrlIllustUserAll = "https://www.pixiv.net/ajax/user/{0}/profile/all?lang=zh"; public const string UrlIllustUserArtworks = "https://www.pixiv.net/ajax/user/{0}/profile/illusts?ids%5B%5D={1}&work_category=illustManga&is_first_page={2}&lang=zh"; public const string UrlIllustUser = "https://www.pixiv.net/users/{0}/artworks";