gelbooru source
This commit is contained in:
@ -15,6 +15,7 @@ namespace Gallery
|
|||||||
public static AppTheme CurrentTheme { get; private set; }
|
public static AppTheme CurrentTheme { get; private set; }
|
||||||
public static PlatformCulture CurrentCulture { get; private set; }
|
public static PlatformCulture CurrentCulture { get; private set; }
|
||||||
|
|
||||||
|
public static Dictionary<string, System.DateTime> RefreshTimes { get; } = new();
|
||||||
public static List<IGallerySource> GallerySources { get; } = new()
|
public static List<IGallerySource> GallerySources { get; } = new()
|
||||||
{
|
{
|
||||||
new Yandere.GallerySource(), // https://yande.re
|
new Yandere.GallerySource(), // https://yande.re
|
||||||
|
@ -12,12 +12,7 @@ namespace Gallery.Resources.UI
|
|||||||
{
|
{
|
||||||
public abstract class GalleryCollectionPage : GalleryScrollableCollectionPage<GalleryItem[]>
|
public abstract class GalleryCollectionPage : GalleryScrollableCollectionPage<GalleryItem[]>
|
||||||
{
|
{
|
||||||
protected readonly IGallerySource source;
|
public GalleryCollectionPage(IGallerySource source) : base(source) { }
|
||||||
|
|
||||||
public GalleryCollectionPage(IGallerySource source)
|
|
||||||
{
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IGalleryCollectionPage
|
public interface IGalleryCollectionPage
|
||||||
@ -59,15 +54,46 @@ namespace Gallery.Resources.UI
|
|||||||
set => SetValue(IsBottomLoadingProperty, value);
|
set => SetValue(IsBottomLoadingProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IGallerySource Source { get; }
|
||||||
public GalleryCollection GalleryCollection { get; set; }
|
public GalleryCollection GalleryCollection { get; set; }
|
||||||
|
public DateTime LastUpdated
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (App.RefreshTimes.TryGetValue(Source.Route, out var time))
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Log.Print($"get last updated time for: {Source.Route}, {time}");
|
||||||
|
#endif
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
Log.Print($"cannot get last updated time for: {Source.Route}");
|
||||||
|
#endif
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Log.Print($"set last updated time for: {Source.Route} to {value}");
|
||||||
|
#endif
|
||||||
|
App.RefreshTimes[Source.Route] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual ActivityIndicator LoadingIndicator => null;
|
protected virtual ActivityIndicator LoadingIndicator => null;
|
||||||
protected virtual double IndicatorMarginTop => 16;
|
protected virtual double IndicatorMarginTop => 16;
|
||||||
|
|
||||||
protected bool Expired => lastUpdated == default || (DateTime.Now - lastUpdated).TotalMinutes > EXPIRED_MINUTES;
|
protected bool Expired
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var lastUpdated = LastUpdated;
|
||||||
|
return lastUpdated == default || (DateTime.Now - lastUpdated).TotalMinutes > EXPIRED_MINUTES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected readonly Command<GalleryItem> commandGalleryItemTapped;
|
protected readonly Command<GalleryItem> commandGalleryItemTapped;
|
||||||
protected DateTime lastUpdated;
|
|
||||||
protected double topOffset;
|
protected double topOffset;
|
||||||
protected string lastError;
|
protected string lastError;
|
||||||
|
|
||||||
@ -75,8 +101,9 @@ namespace Gallery.Resources.UI
|
|||||||
private readonly Stack<ParallelTask> tasks = new();
|
private readonly Stack<ParallelTask> tasks = new();
|
||||||
private T galleryData;
|
private T galleryData;
|
||||||
|
|
||||||
public GalleryCollectionPage()
|
public GalleryCollectionPage(IGallerySource source)
|
||||||
{
|
{
|
||||||
|
Source = source;
|
||||||
commandGalleryItemTapped = new Command<GalleryItem>(OnGalleryItemTapped);
|
commandGalleryItemTapped = new Command<GalleryItem>(OnGalleryItemTapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +134,14 @@ namespace Gallery.Resources.UI
|
|||||||
}
|
}
|
||||||
InvalidateCollection();
|
InvalidateCollection();
|
||||||
Gallery = null;
|
Gallery = null;
|
||||||
lastUpdated = default;
|
LastUpdated = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
||||||
if (lastUpdated == default)
|
if (Expired)
|
||||||
{
|
{
|
||||||
StartLoading();
|
StartLoading();
|
||||||
}
|
}
|
||||||
@ -391,9 +418,9 @@ namespace Gallery.Resources.UI
|
|||||||
Log.Error("gallery.load", "failed to load gallery data.");
|
Log.Error("gallery.load", "failed to load gallery data.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (force)
|
if (force || Expired)
|
||||||
{
|
{
|
||||||
lastUpdated = DateTime.Now;
|
LastUpdated = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = DoGetGalleryList(galleryData, out int tag).Where(i => i != null);
|
var data = DoGetGalleryList(galleryData, out int tag).Where(i => i != null);
|
||||||
@ -469,6 +496,8 @@ namespace Gallery.Resources.UI
|
|||||||
private double lastRefreshY = double.MinValue;
|
private double lastRefreshY = double.MinValue;
|
||||||
private double offset;
|
private double offset;
|
||||||
|
|
||||||
|
public GalleryScrollableCollectionPage(IGallerySource source) : base(source) { }
|
||||||
|
|
||||||
protected bool IsScrollingDown(double y)
|
protected bool IsScrollingDown(double y)
|
||||||
{
|
{
|
||||||
if (y > lastScrollY)
|
if (y > lastScrollY)
|
||||||
|
@ -33,7 +33,7 @@ namespace Gallery.Views
|
|||||||
|
|
||||||
protected override async Task<GalleryItem[]> DoloadGalleryData(bool force)
|
protected override async Task<GalleryItem[]> DoloadGalleryData(bool force)
|
||||||
{
|
{
|
||||||
var result = await source.GetRecentItemsAsync(currentPage);
|
var result = await Source.GetRecentItemsAsync(currentPage);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ namespace Gallery.Util
|
|||||||
case nameof(GalleryItem.RawUrl): item.RawUrl = reader.GetString(); break;
|
case nameof(GalleryItem.RawUrl): item.RawUrl = reader.GetString(); break;
|
||||||
case nameof(GalleryItem.Width): item.Width = reader.GetInt32(); break;
|
case nameof(GalleryItem.Width): item.Width = reader.GetInt32(); break;
|
||||||
case nameof(GalleryItem.Height): item.Height = reader.GetInt32(); break;
|
case nameof(GalleryItem.Height): item.Height = reader.GetInt32(); break;
|
||||||
|
case nameof(GalleryItem.BookmarkId): item.BookmarkId = reader.GetString(); break;
|
||||||
|
case nameof(GalleryItem.IsRawPage): item.IsRawPage = reader.GetBoolean(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,6 +93,8 @@ namespace Gallery.Util
|
|||||||
writer.WriteString(nameof(GalleryItem.RawUrl), value.RawUrl);
|
writer.WriteString(nameof(GalleryItem.RawUrl), value.RawUrl);
|
||||||
writer.WriteNumber(nameof(GalleryItem.Width), value.Width);
|
writer.WriteNumber(nameof(GalleryItem.Width), value.Width);
|
||||||
writer.WriteNumber(nameof(GalleryItem.Height), value.Height);
|
writer.WriteNumber(nameof(GalleryItem.Height), value.Height);
|
||||||
|
writer.WriteString(nameof(GalleryItem.BookmarkId), value.BookmarkId);
|
||||||
|
writer.WriteBoolean(nameof(GalleryItem.IsRawPage), value.IsRawPage);
|
||||||
|
|
||||||
writer.WriteEndObject();
|
writer.WriteEndObject();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<SignAssembly>true</SignAssembly>
|
|
||||||
<AssemblyOriginatorKeyFile>..\Ref\Tsanie.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>..\Ref\Tsanie.snk</AssemblyOriginatorKeyFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -19,31 +19,26 @@ namespace Gallery.Util.Model
|
|||||||
public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(nameof(IsFavorite), typeof(bool), typeof(GalleryItem));
|
public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(nameof(IsFavorite), typeof(bool), typeof(GalleryItem));
|
||||||
public static readonly BindableProperty BookmarkIdProperty = BindableProperty.Create(nameof(BookmarkId), typeof(string), typeof(GalleryItem));
|
public static readonly BindableProperty BookmarkIdProperty = BindableProperty.Create(nameof(BookmarkId), typeof(string), typeof(GalleryItem));
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string TagDescription
|
public string TagDescription
|
||||||
{
|
{
|
||||||
get => (string)GetValue(TagDescriptionProperty);
|
get => (string)GetValue(TagDescriptionProperty);
|
||||||
set => SetValue(TagDescriptionProperty, value);
|
set => SetValue(TagDescriptionProperty, value);
|
||||||
}
|
}
|
||||||
[JsonIgnore]
|
|
||||||
public ImageSource PreviewImage
|
public ImageSource PreviewImage
|
||||||
{
|
{
|
||||||
get => (ImageSource)GetValue(PreviewImageProperty);
|
get => (ImageSource)GetValue(PreviewImageProperty);
|
||||||
set => SetValue(PreviewImageProperty, value);
|
set => SetValue(PreviewImageProperty, value);
|
||||||
}
|
}
|
||||||
[JsonIgnore]
|
|
||||||
public GridLength ImageHeight
|
public GridLength ImageHeight
|
||||||
{
|
{
|
||||||
get => (GridLength)GetValue(ImageHeightProperty);
|
get => (GridLength)GetValue(ImageHeightProperty);
|
||||||
set => SetValue(ImageHeightProperty, value);
|
set => SetValue(ImageHeightProperty, value);
|
||||||
}
|
}
|
||||||
[JsonIgnore]
|
|
||||||
public bool IsFavorite
|
public bool IsFavorite
|
||||||
{
|
{
|
||||||
get => (bool)GetValue(IsFavoriteProperty);
|
get => (bool)GetValue(IsFavoriteProperty);
|
||||||
set => SetValue(IsFavoriteProperty, value);
|
set => SetValue(IsFavoriteProperty, value);
|
||||||
}
|
}
|
||||||
[JsonIgnore]
|
|
||||||
public string BookmarkId
|
public string BookmarkId
|
||||||
{
|
{
|
||||||
get => (string)GetValue(BookmarkIdProperty);
|
get => (string)GetValue(BookmarkIdProperty);
|
||||||
@ -75,6 +70,7 @@ namespace Gallery.Util.Model
|
|||||||
public string Source { get; set; }
|
public string Source { get; set; }
|
||||||
public string PreviewUrl { get; set; }
|
public string PreviewUrl { get; set; }
|
||||||
public string RawUrl { get; set; }
|
public string RawUrl { get; set; }
|
||||||
|
public bool IsRawPage { get; set; }
|
||||||
|
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
|
@ -18,7 +18,7 @@ namespace Gallery.Danbooru
|
|||||||
public async Task<GalleryItem[]> GetRecentItemsAsync(int page)
|
public async Task<GalleryItem[]> GetRecentItemsAsync(int page)
|
||||||
{
|
{
|
||||||
var url = $"https://danbooru.donmai.us/posts?page={page}";
|
var url = $"https://danbooru.donmai.us/posts?page={page}";
|
||||||
var (result, error) = await NetHelper.RequestObject<GalleryItem[]>(url, contentHandler: ContentHandler);
|
var (result, error) = await NetHelper.RequestObject(url, @return: content => ResolveGalleryItems(content));
|
||||||
|
|
||||||
if (result == null || !string.IsNullOrEmpty(error))
|
if (result == null || !string.IsNullOrEmpty(error))
|
||||||
{
|
{
|
||||||
@ -29,18 +29,30 @@ namespace Gallery.Danbooru
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ContentHandler(string content)
|
private GalleryItem[] ResolveGalleryItems(string content)
|
||||||
{
|
{
|
||||||
var regex = new Regex(@"<article id=""post_\d+"".+?data-id=""(\d+)"".+?data-tags=""([^""]+?)"".+?data-width=""(\d+?)"" data-height=""(\d+?)"".+?data-source=""([^""]+?)"" data-uploader-id=""(\d+?)"" data-normalized-source=""([^""]+?)"".+?data-file-url=""([^""]+?)"".+?data-preview-file-url=""([^""]+?)""", RegexOptions.Multiline);
|
var regex = new Regex(
|
||||||
|
@"<article id=""post_\d+"".+?data-id=""(\d+)"".+?data-tags=""([^""]+?)"".+?" +
|
||||||
|
@"data-width=""(\d+?)"" data-height=""(\d+?)"".+?data-source=""([^""]+?)"".+?" +
|
||||||
|
@"data-uploader-id=""(\d+?)"".+?data-normalized-source=""([^""]+?)"".+?" +
|
||||||
|
@"data-file-url=""([^""]+?)"".+?data-large-file-url=""([^""]+?)""", RegexOptions.Multiline);
|
||||||
var matches = regex.Matches(content);
|
var matches = regex.Matches(content);
|
||||||
var array = new string[matches.Count];
|
var items = new GalleryItem[matches.Count];
|
||||||
for (var i = 0; i < array.Length; i++)
|
for (var i = 0; i < items.Length; i++)
|
||||||
{
|
{
|
||||||
var g = matches[i].Groups;
|
var g = matches[i].Groups;
|
||||||
var tags = g[2].Value.Replace(" ", "\",\"");
|
items[i] = new GalleryItem(int.Parse(g[1].Value))
|
||||||
array[i] = $"{{\"Id\":{g[1].Value},\"Tags\":[\"{tags}\"],\"Width\":{g[3].Value},\"Height\":{g[4].Value},\"Source\":\"{g[7].Value}\",\"UserId\":\"{g[6].Value}\",\"RawUrl\":\"{g[8].Value}\",\"PreviewUrl\":\"{g[9].Value}\"}}";
|
{
|
||||||
|
Tags = g[2].Value.Split(' '),
|
||||||
|
Width = int.Parse(g[3].Value),
|
||||||
|
Height = int.Parse(g[4].Value),
|
||||||
|
UserId = g[6].Value,
|
||||||
|
Source = g[7].Value,
|
||||||
|
RawUrl = g[8].Value,
|
||||||
|
PreviewUrl = g[9].Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return $"[{string.Join(',', array)}]";
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCookie()
|
public void SetCookie()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Gallery.Util;
|
||||||
using Gallery.Util.Interface;
|
using Gallery.Util.Interface;
|
||||||
using Gallery.Util.Model;
|
using Gallery.Util.Model;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -13,9 +15,40 @@ namespace Gallery.Gelbooru
|
|||||||
public string FlyoutIconKey => "Gelbooru";
|
public string FlyoutIconKey => "Gelbooru";
|
||||||
public string HomePage => "https://gelbooru.com";
|
public string HomePage => "https://gelbooru.com";
|
||||||
|
|
||||||
public Task<GalleryItem[]> GetRecentItemsAsync(int page)
|
public async Task<GalleryItem[]> GetRecentItemsAsync(int page)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
var offset = (page - 1) * 42;
|
||||||
|
var url = $"https://gelbooru.com/index.php?page=post&s=list&tags=all&pid={offset}";
|
||||||
|
var (result, error) = await NetHelper.RequestObject(url, @return: content => ResolveGalleryItems(content));
|
||||||
|
|
||||||
|
if (result == null || !string.IsNullOrEmpty(error))
|
||||||
|
{
|
||||||
|
Log.Error("gelbooru.content.load", $"failed to load content array, error: {error}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GalleryItem[] ResolveGalleryItems(string content)
|
||||||
|
{
|
||||||
|
var regex = new Regex(
|
||||||
|
@"<article(.|\n)+?<a id=""p(\d+?)"" href=""([^""]+?)"">(.|\n)+?<img src=""([^""]+?)"" title=""([^""]+?)""",
|
||||||
|
RegexOptions.Multiline);
|
||||||
|
var matches = regex.Matches(content);
|
||||||
|
var items = new GalleryItem[matches.Count];
|
||||||
|
for (var i = 0; i < items.Length; i++)
|
||||||
|
{
|
||||||
|
var g = matches[i].Groups;
|
||||||
|
items[i] = new GalleryItem(int.Parse(g[2].Value))
|
||||||
|
{
|
||||||
|
RawUrl = g[3].Value,
|
||||||
|
PreviewUrl = g[5].Value,
|
||||||
|
Tags = g[6].Value.Split(' '),
|
||||||
|
IsRawPage = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCookie()
|
public void SetCookie()
|
||||||
|
Reference in New Issue
Block a user