refactor project

This commit is contained in:
Rinacm 2020-10-29 17:15:11 +08:00
parent f72390867f
commit b0b5b522a8
175 changed files with 23938 additions and 0 deletions

1354
Pixeval/Annotations.cs Normal file

File diff suppressed because it is too large Load Diff

318
Pixeval/App.xaml Normal file
View File

@ -0,0 +1,318 @@
<!--
Pixeval - A Strong, Fast and Flexible Pixiv Client
Copyright (C) 2019-2020 Dylech30th
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<Application x:Class="Pixeval.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:valueConverters="clr-namespace:Pixeval.Objects.ValueConverters"
StartupUri="UI/SignIn.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
<ResourceDictionary>
<!-- ReSharper disable Xaml.RedundantResource -->
<SolidColorBrush x:Key="PrimaryHueMidBrush" Color="#007CEE" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="MaterialDesignToolButtonWithGrayWaveEffect"
BasedOn="{StaticResource MaterialDesignToolButton}"
TargetType="Button">
<Style.Resources>
<SolidColorBrush x:Key="PrimaryHueMidBrush" Color="Gray" />
</Style.Resources>
</Style>
<system:Double x:Key="Two">
2
</system:Double>
<CubicEase x:Key="CubicEase" EasingMode="EaseOut" />
<PowerEase x:Key="QuinticEase"
EasingMode="EaseInOut"
Power="1" />
<valueConverters:EnumToStringConverter x:Key="EnumToStringConverter" />
<valueConverters:VisibleIfTrueConverter x:Key="VisibleIfTrueConverter" />
<valueConverters:VisibleIfFalseConverter x:Key="VisibleIfFalseConverter" />
<valueConverters:DoublePlusConverter x:Key="DoublePlusConverter" />
<valueConverters:IllustSubscriptConverter x:Key="IllustSubscriptConverter" />
<valueConverters:DateTimeOffsetConverter x:Key="DateTimeOffsetConverter" />
<valueConverters:DoubleDivisionConverter x:Key="DoubleDivisionConverter" />
<valueConverters:BoolToCachingPolicyConverter x:Key="BoolToCachingPolicyConverter" />
<valueConverters:IllustrationMatchConditionMaskConverter x:Key="IllustrationMatchConditionMaskConverter" />
<valueConverters:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<valueConverters:TrendsStatConverter x:Key="TrendsStatConverter" />
<valueConverters:DateTimeConverter x:Key="DateTimeConverter" />
<valueConverters:MultiCultureConverter x:Key="MultiCultureConverter" />
<valueConverters:TagMatchEnumToModelConverter x:Key="TagMatchEnumToModelConverter" />
<Style x:Key="RoundedCornerProgressBar" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueDarkBrush}" />
<Setter Property="Background" Value="{DynamicResource PrimaryHueLightBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueLightBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Height" Value="4" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<ControlTemplate.Resources>
<Storyboard x:Key="OnLoaded">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnLoadedNoAnimation">
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.Opacity)"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
To="1"
Duration="0" />
</Storyboard>
</ControlTemplate.Resources>
<Grid x:Name="TemplateRoot"
Opacity="0"
RenderTransformOrigin="0,0.5">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0" ScaleY="0" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3" />
<Rectangle x:Name="PART_Track" />
<Grid x:Name="PART_Indicator"
HorizontalAlignment="Left"
ClipToBounds="true">
<Rectangle x:Name="Indicator"
Fill="{TemplateBinding Foreground}"
RadiusX="3"
RadiusY="3" />
<Rectangle x:Name="Animation"
Fill="{TemplateBinding Foreground}"
RadiusX="3"
RadiusY="3"
RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Determinate" />
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Animation"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<EasingDoubleKeyFrame KeyTime="0" Value="0.25" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25" />
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25" />
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetName="Animation"
Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)">
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5" />
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5" />
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5" />
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="materialDesign:TransitionAssist.DisableTransitions" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Name="BeginStoryboardOnLoadedNoAnimation"
Storyboard="{StaticResource OnLoadedNoAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BeginStoryboardOnLoadedNoAnimation" />
</Trigger.ExitActions>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsVisible" Value="True" />
<Condition Property="materialDesign:TransitionAssist.DisableTransitions"
Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard Name="BeginStoryboardOnLoaded"
Storyboard="{StaticResource OnLoaded}" />
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BeginStoryboardOnLoaded" />
</MultiTrigger.ExitActions>
</MultiTrigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate" Value="true">
<Setter TargetName="Indicator" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollThumbs" TargetType="{x:Type Thumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid x:Name="Grid">
<Rectangle Width="Auto"
Height="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent" />
<Border x:Name="Rectangle1"
Width="Auto"
Height="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
CornerRadius="5" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="Horizontal">
<Setter TargetName="Rectangle1" Property="Width" Value="Auto" />
<Setter TargetName="Rectangle1" Property="Height" Value="7" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsFlicksEnabled" Value="false" />
<Setter Property="Foreground" Value="#8C8C8C" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Width" Value="8" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="GridRoot"
Width="8"
Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="0.00001*" />
</Grid.RowDefinitions>
<Track x:Name="PART_Track"
Grid.Row="0"
Focusable="false"
IsDirectionReversed="true">
<Track.Thumb>
<Thumb x:Name="Thumb"
Background="{TemplateBinding Foreground}"
Style="{DynamicResource ScrollThumbs}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton x:Name="PageUp"
Command="ScrollBar.PageDownCommand"
Focusable="false"
Opacity="0" />
</Track.IncreaseRepeatButton>
<Track.DecreaseRepeatButton>
<RepeatButton x:Name="PageDown"
Command="ScrollBar.PageUpCommand"
Focusable="false"
Opacity="0" />
</Track.DecreaseRepeatButton>
</Track>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="Thumb" Property="IsMouseOver" Value="true">
<Setter TargetName="Thumb" Property="Background"
Value="{DynamicResource ButtonSelectBrush}" />
</Trigger>
<Trigger SourceName="Thumb" Property="IsDragging" Value="true">
<Setter TargetName="Thumb" Property="Background"
Value="{DynamicResource DarkBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Thumb" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="Orientation" Value="Horizontal">
<Setter TargetName="GridRoot" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter TargetName="PART_Track" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="8" />
<Setter TargetName="Thumb" Property="Tag" Value="Horizontal" />
<Setter TargetName="PageDown" Property="Command" Value="ScrollBar.PageLeftCommand" />
<Setter TargetName="PageUp" Property="Command" Value="ScrollBar.PageRightCommand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ReleaseImageStyle" TargetType="Image">
<Style.Triggers>
<Trigger Property="IsVisible" Value="False">
<Setter Property="Source" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

128
Pixeval/App.xaml.cs Normal file
View File

@ -0,0 +1,128 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Pixeval.Core;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
#if RELEASE
using System.Net;
using Pixeval.Objects.Exceptions.Logger;
#endif
namespace Pixeval
{
public partial class App
{
private static readonly Mutex UniqueMutex = new Mutex(true, "Pixeval Mutex");
public App()
{
if (Dispatcher != null)
{
Dispatcher.UnhandledException += (sender, args) => DispatcherOnUnhandledException(args.Exception);
}
AppDomain.CurrentDomain.UnhandledException += (sender, args) => DispatcherOnUnhandledException((Exception) args.ExceptionObject);
TaskScheduler.UnobservedTaskException += (sender, args) => DispatcherOnUnhandledException(args.Exception);
}
private static void DispatcherOnUnhandledException(Exception e)
{
#if RELEASE
if (e is WebException || e is TaskCanceledException)
{
return;
}
ExceptionDumper.WriteException(e);
#elif DEBUG
throw e;
#endif
}
protected override async void OnStartup(StartupEventArgs e)
{
InitializeFolders();
CheckMultipleProcess();
await RestoreSettings();
await CheckUpdate();
base.OnStartup(e);
}
private static void InitializeFolders()
{
Directory.CreateDirectory(AppContext.ProjectFolder);
Directory.CreateDirectory(AppContext.SettingsFolder);
Directory.CreateDirectory(AppContext.ExceptionReportFolder);
}
private static void CheckMultipleProcess()
{
if (!UniqueMutex.WaitOne(0, false))
{
MessageBox.Show(AkaI18N.MultiplePixevalInstanceDetected, AkaI18N.MultiplePixevalInstanceDetectedTitle, MessageBoxButton.OK, MessageBoxImage.Error);
Environment.Exit(-1);
}
}
private static async Task CheckUpdate()
{
if (await AppContext.UpdateAvailable() && MessageBox.Show(AkaI18N.PixevalUpdateAvailable, AkaI18N.PixevalUpdateAvailableTitle, MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes)
{
Process.Start(@"updater\Pixeval.Updater.exe");
Environment.Exit(0);
}
}
private static async Task RestoreSettings()
{
await Settings.Restore();
BrowsingHistoryAccessor.GlobalLifeTimeScope = new BrowsingHistoryAccessor(200, AppContext.BrowseHistoryDatabase);
}
protected override async void OnExit(ExitEventArgs e)
{
await Settings.Global.Store();
if (Session.Current != null && !Session.Current.AccessToken.IsNullOrEmpty())
{
await Session.Current.Store();
}
if (File.Exists(AppContext.BrowseHistoryDatabase))
{
BrowsingHistoryAccessor.GlobalLifeTimeScope.SetWritable();
BrowsingHistoryAccessor.GlobalLifeTimeScope.Rewrite();
BrowsingHistoryAccessor.GlobalLifeTimeScope.Dispose();
}
else
{
BrowsingHistoryAccessor.GlobalLifeTimeScope.EmergencyRewrite();
}
base.OnExit(e);
}
}
}

68
Pixeval/AppContext.cs Normal file
View File

@ -0,0 +1,68 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Pixeval.Core;
using Pixeval.Data.ViewModel;
namespace Pixeval
{
public static class AppContext
{
public const string AppIdentifier = "Pixeval";
public const string CurrentVersion = "3.0.1";
public const string ConfigurationFileName = "pixeval_conf.json";
public static readonly string ProjectFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), AppIdentifier.ToLower());
public static readonly string ConfFolder = ProjectFolder;
public static readonly string SettingsFolder = ProjectFolder;
public static readonly string ExceptionReportFolder = Path.Combine(ProjectFolder, "crash-reports");
public static readonly string BrowseHistoryDatabase = Path.Combine(ProjectFolder, "history.db");
public static string PermanentlyFolder = Path.Combine(ProjectFolder, "permanent");
public static readonly ObservableCollection<TrendingTag> TrendingTags = new ObservableCollection<TrendingTag>();
public static readonly IQualifier<Illustration, IllustrationQualification> DefaultQualifier = new IllustrationQualifier();
public static readonly I18NOption[] AvailableCultures = { I18NOption.UsEnglish, I18NOption.ChineseSimplified };
public static int ProxyPort { get; set; }
public static int PacPort { get; set; }
public static async Task<bool> UpdateAvailable()
{
const string Url = "http://47.95.218.243/Pixeval/version.txt";
var httpClient = new HttpClient();
return await httpClient.GetStringAsync(Url) != CurrentVersion;
}
}
}

View File

@ -0,0 +1,67 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Threading;
namespace Pixeval.Core
{
/// <summary>
/// Abstract implementation of <see cref="IPixivAsyncEnumerable{T}" />, provides the default implementation of cancel a
/// running <see cref="IPixivAsyncEnumerable{T}" /> instance
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class AbstractPixivAsyncEnumerable<T> : IPixivAsyncEnumerable<T>
{
protected bool IsCancelled { get; private set; }
public abstract int RequestedPages { get; protected set; }
public abstract IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
public virtual bool VerifyRationality(T item, IList<T> collection)
{
return item != null && !collection.Contains(item);
}
public virtual void InsertionPolicy(T item, IList<T> collection)
{
if (item != null)
{
collection.Add(item);
}
}
public void Cancel()
{
IsCancelled = true;
}
public bool IsCancellationRequested()
{
return IsCancelled;
}
public void ReportRequestedPages()
{
RequestedPages++;
}
}
}

View File

@ -0,0 +1,50 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Pixeval.Core
{
/// <summary>
/// Provide a set of functions that support iterate an <see cref="IPixivAsyncEnumerable{T}" />
/// </summary>
/// <typeparam name="T">the correspond data type</typeparam>
public abstract class AbstractPixivAsyncEnumerator<T> : IAsyncEnumerator<T>
{
protected IPixivAsyncEnumerable<T> Enumerable;
protected AbstractPixivAsyncEnumerator(IPixivAsyncEnumerable<T> enumerable)
{
Enumerable = enumerable;
}
public ValueTask DisposeAsync()
{
return default;
}
public abstract ValueTask<bool> MoveNextAsync();
public abstract T Current { get; }
protected abstract void UpdateEnumerator();
}
}

View File

@ -0,0 +1,143 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Pixeval.Data.ViewModel;
using SQLite;
namespace Pixeval.Core
{
/// <summary>
/// Manages the global browsing history which lives inside application
/// the underlying implementation is sqlite database
/// </summary>
public class BrowsingHistoryAccessor : ITimelineService, IDisposable
{
public static BrowsingHistoryAccessor GlobalLifeTimeScope;
// current browsing histories
private readonly ObservableCollection<BrowsingHistory> delegation;
private readonly string path;
private readonly SQLiteConnection sqLiteConnection;
private readonly int stackLimit;
private bool writable;
public BrowsingHistoryAccessor(int stackLimit, string path)
{
this.stackLimit = stackLimit;
this.path = path;
sqLiteConnection = new SQLiteConnection(path, SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite);
sqLiteConnection.CreateTable<BrowsingHistory>();
delegation = new ObservableCollection<BrowsingHistory>(sqLiteConnection.Table<BrowsingHistory>());
}
public void Dispose()
{
sqLiteConnection?.Dispose();
}
public bool VerifyRationality(BrowsingHistory browsingHistory)
{
// if current browsing history list has elements
if (delegation.Any())
{
var prev = delegation[0];
// check if the last one in the browsing history list is the same as the one to be insert, return false if so
if (prev.Type == browsingHistory.Type && prev.BrowseObjectId == browsingHistory.BrowseObjectId)
{
return false;
}
}
return true;
}
public void Insert(BrowsingHistory browsingHistory)
{
// we can simply consider the browsing histories as a double-ended queue with limited capacity, we will pop the oldest one
// and insert a new one if the Deque is full
if (delegation.Count >= stackLimit)
{
delegation.Remove(delegation.Last());
}
delegation.Insert(0, browsingHistory);
}
/// <summary>
/// If the database file is not present, we will urgently create one and
/// write all the browsing histories that we have into it
/// </summary>
public void EmergencyRewrite()
{
if (File.Exists(path))
{
throw new InvalidOperationException();
}
using var sql = new SQLiteConnection(path);
sql.CreateTable<BrowsingHistory>();
sql.InsertAll(Get());
}
/// <summary>
/// Returns currently maintained browsing histories
/// </summary>
/// <returns></returns>
public IEnumerable<BrowsingHistory> Get()
{
return delegation;
}
/// <summary>
/// Rewrite the local database, you muse call <see cref="SetWritable" />
/// before call this method
/// </summary>
public void Rewrite()
{
if (!writable)
{
throw new InvalidOperationException();
}
sqLiteConnection.DropTable<BrowsingHistory>();
sqLiteConnection.CreateTable<BrowsingHistory>();
sqLiteConnection.InsertAll(delegation);
}
/// <summary>
/// Delete the local database
/// </summary>
public void DropDb()
{
if (File.Exists(path))
{
File.Delete(path);
}
}
public void SetWritable()
{
writable = true;
}
}
}

View File

@ -0,0 +1,51 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.IO;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public class CreateNewFolderForUserDownloadPathProvider : IDownloadPathProvider
{
public CreateNewFolderForUserDownloadPathProvider(string userName)
{
UserName = userName;
}
public string UserName { get; set; }
public string GetSpotlightPath(string title, DownloadOption option = null)
{
return option?.RootDirectory ?? Path.Combine(Settings.Global.DownloadLocation, "Spotlight", Strings.FormatPath(title));
}
public string GetIllustrationPath(DownloadOption option = null)
{
return option?.RootDirectory ?? Path.Combine(Settings.Global.DownloadLocation, UserName);
}
public string GetMangaPath(string id, DownloadOption option = null)
{
return option?.RootDirectory ?? Path.Combine(Settings.Global.DownloadLocation, UserName, id);
}
}
}

View File

@ -0,0 +1,44 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.IO;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public class DefaultDownloadPathProvider : IDownloadPathProvider
{
public string GetSpotlightPath(string title, DownloadOption option = null)
{
return option?.RootDirectory ?? Path.Combine(Settings.Global.DownloadLocation, "Spotlight", Strings.FormatPath(title));
}
public string GetIllustrationPath(DownloadOption option = null)
{
return option?.RootDirectory ?? Settings.Global.DownloadLocation;
}
public string GetMangaPath(string id, DownloadOption option = null)
{
return option?.RootDirectory ?? Path.Combine(Settings.Global.DownloadLocation, id);
}
}
}

View File

@ -0,0 +1,44 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.IO;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public class DefaultIllustrationFileNameFormatter : IIllustrationFileNameFormatter
{
public string Format(Illustration illustration)
{
return $"[{Strings.FormatPath(illustration.UserName)}]{illustration.Id}{Path.GetExtension(illustration.Origin.IsNullOrEmpty() ? illustration.Large : illustration.Origin)}";
}
public string FormatManga(Illustration illustration, int idx)
{
return $"[{Strings.FormatPath(illustration.UserName)}]{illustration.Id}_p{idx}{Path.GetExtension(illustration.Origin.IsNullOrEmpty() ? illustration.Large : illustration.Origin)}";
}
public string FormatGif(Illustration illustration)
{
return $"[{Strings.FormatPath(illustration.UserName)}]{illustration.Id}.gif";
}
}
}

View File

@ -0,0 +1,47 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
namespace Pixeval.Core
{
public class EnumeratingSchedule
{
private static object _currentItr;
public static void StartNewInstance<T>(IPixivAsyncEnumerable<T> itr)
{
CancelCurrent();
_currentItr = itr;
}
public static IPixivAsyncEnumerable<T> GetCurrentEnumerator<T>()
{
return _currentItr as IPixivAsyncEnumerable<T>;
}
public static void CancelCurrent()
{
var iterator = _currentItr as ICancellable;
iterator?.Cancel();
GC.Collect();
}
}
}

View File

@ -0,0 +1,166 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public abstract class AbstractGalleryAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
protected abstract string Uid { get; }
protected abstract RestrictPolicy RestrictPolicy { get; }
public override int RequestedPages { get; protected set; }
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new GalleryAsyncEnumerator(Uid, this, RestrictPolicy);
}
public static AbstractGalleryAsyncEnumerable Of(string uid, RestrictPolicy restrictPolicy)
{
return restrictPolicy switch
{
RestrictPolicy.Public => new PublicGalleryAsyncEnumerable(uid),
RestrictPolicy.Private => new PrivateGalleryAsyncEnumerable(uid),
_ => throw new ArgumentOutOfRangeException(nameof(restrictPolicy), restrictPolicy, null)
};
}
private class GalleryAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private readonly RestrictPolicy restrictPolicy;
private readonly string uid;
private GalleryResponse entity;
private IEnumerator<Illustration> illustrationsEnumerator;
public GalleryAsyncEnumerator(string uid, IPixivAsyncEnumerable<Illustration> outerInstance, RestrictPolicy restrictPolicy) : base(outerInstance)
{
this.uid = uid;
this.restrictPolicy = restrictPolicy;
}
public override Illustration Current => illustrationsEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationsEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse(restrictPolicy switch
{
RestrictPolicy.Public => $"/v1/user/bookmarks/illust?user_id={uid}&restrict=public&filter=for_ios",
RestrictPolicy.Private => $"/v1/user/bookmarks/illust?user_id={uid}&restrict=private&filter=for_ios",
_ => throw new ArgumentOutOfRangeException()
}) is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationsEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var response))
{
entity = response;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<GalleryResponse>> TryGetResponse(string url)
{
var result = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<GalleryResponse>();
if (result is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<GalleryResponse>.Wrap(true, response);
}
return HttpResponse<GalleryResponse>.Wrap(false);
}
}
}
public class PublicGalleryAsyncEnumerable : AbstractGalleryAsyncEnumerable
{
public PublicGalleryAsyncEnumerable(string uid)
{
Uid = uid;
}
protected override string Uid { get; }
protected override RestrictPolicy RestrictPolicy { get; } = RestrictPolicy.Public;
}
public class PrivateGalleryAsyncEnumerable : AbstractGalleryAsyncEnumerable
{
public PrivateGalleryAsyncEnumerable(string uid)
{
Uid = uid;
}
protected override string Uid { get; }
protected override RestrictPolicy RestrictPolicy { get; } = RestrictPolicy.Private;
}
}

View File

@ -0,0 +1,29 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Core
{
public interface ICancellable
{
void Cancel();
bool IsCancellationRequested();
}
}

View File

@ -0,0 +1,49 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Pixeval.Data.ViewModel;
namespace Pixeval.Core
{
public interface IIllustrationFileNameFormatter
{
string Format(Illustration illustration);
string FormatManga(Illustration illustration, int idx);
string FormatGif(Illustration illustration);
}
public interface IDownloadPathProvider
{
string GetSpotlightPath(string title, DownloadOption option = null);
string GetIllustrationPath(DownloadOption option = null);
string GetMangaPath(string id, DownloadOption option = null);
}
public class DownloadOption
{
public string RootDirectory { get; set; }
public bool CreateNewWhenFromUser { get; set; }
}
}

View File

@ -0,0 +1,35 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
namespace Pixeval.Core
{
public interface IPixivAsyncEnumerable<T> : IAsyncEnumerable<T>, ICancellable
{
int RequestedPages { get; }
void ReportRequestedPages();
void InsertionPolicy(T item, IList<T> collection);
bool VerifyRationality(T item, IList<T> collection);
}
}

View File

@ -0,0 +1,27 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Core
{
public interface IQualifier<in T, in P>
{
public bool Qualified(T condition, P pattern);
}
}

View File

@ -0,0 +1,43 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Pixeval.Data.ViewModel;
namespace Pixeval.Core
{
/// <summary>
/// Provides a set of functions that support a Browsing Timeline
/// </summary>
public interface ITimelineService
{
/// <summary>
/// Check if the <see cref="BrowsingHistory" /> has the rationality to be insert to timeline
/// </summary>
/// <param name="browsingHistory"></param>
/// <returns></returns>
bool VerifyRationality(BrowsingHistory browsingHistory);
/// <summary>
/// Insert a <see cref="BrowsingHistory" /> to timeline
/// </summary>
/// <param name="browsingHistory"></param>
void Insert(BrowsingHistory browsingHistory);
}
}

View File

@ -0,0 +1,56 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Text.RegularExpressions;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public class IllustrationQualification
{
public IllustrationQualification(ConditionType type, string condition)
{
Type = type;
Condition = condition;
}
public ConditionType Type { get; set; }
public string Condition { get; set; }
public static IllustrationQualification Parse(string input)
{
var isResolution = Regex.IsMatch(input, "\\d+x\\d+");
return new IllustrationQualification(input switch
{
_ when input.IsNumber() => ConditionType.Id,
_ when input.StartsWith("!") => ConditionType.ExcludeTag,
_ when !input.IsNullOrEmpty() => ConditionType.Tag,
_ when isResolution => ConditionType.Resolution,
_ => ConditionType.None
}, input);
}
}
public enum ConditionType
{
Id, Tag, ExcludeTag, Resolution, None
}
}

View File

@ -0,0 +1,61 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Linq;
using System.Text.RegularExpressions;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public class IllustrationQualifier : IQualifier<Illustration, IllustrationQualification>
{
public bool Qualified(Illustration condition, IllustrationQualification pattern)
{
if (pattern.Type == ConditionType.Resolution)
{
const string Reg = "(?<width>\\d+)x(?<height>\\d+)";
if (Regex.IsMatch(pattern.Condition, Reg))
{
return false;
}
var illustrationMatch = Regex.Match(condition.Resolution, Reg);
var conditionMatch = Regex.Match(pattern.Condition, Reg);
if (conditionMatch.Groups["width"].Value.IsNumber() && conditionMatch.Groups["height"].Value.IsNumber())
{
var illustrationWidth = int.Parse(illustrationMatch.Groups["width"].Value);
var illustrationHeight = int.Parse(illustrationMatch.Groups["height"].Value);
var desireWidth = int.Parse(conditionMatch.Groups["width"].Value);
var desireHeight = int.Parse(conditionMatch.Groups["height"].Value);
return desireWidth < illustrationWidth || desireHeight < illustrationHeight;
}
return false;
}
return pattern switch
{
{ Type: ConditionType.Id } => !condition.Id.Contains(pattern.Condition),
{ Type: ConditionType.Tag } => !condition.Title.Contains(pattern.Condition) && !(condition.Tags != null && condition.Tags.Any(tag => tag?.Name != null && tag.Name.ToLower().Contains(pattern.Condition.ToLower()) || tag?.TranslatedName != null && tag.TranslatedName.ToLower().Contains(pattern.Condition.ToLower()))),
{ Type: ConditionType.ExcludeTag } => condition.Tags != null && condition.Tags.Any(tag => tag?.Name != null && tag.Name.ToLower().Contains(pattern.Condition[1..].ToLower()) || tag?.TranslatedName != null && tag.TranslatedName.ToLower().Contains(pattern.Condition[1..].ToLower())),
_ => false
};
}
}
}

111
Pixeval/Core/Managers.cs Normal file
View File

@ -0,0 +1,111 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Pixeval.Data.ViewModel;
namespace Pixeval.Core
{
public class DownloadManager
{
public static readonly ObservableCollection<DownloadableIllustration> Downloading = new ObservableCollection<DownloadableIllustration>();
public static readonly ObservableCollection<DownloadableIllustration> Downloaded = new ObservableCollection<DownloadableIllustration>();
public static void EnqueueDownloadItem(Illustration illustration, DownloadOption option = null)
{
if (Downloading.Any(i => illustration.Id == i.DownloadContent.Id))
{
return;
}
option ??= new DownloadOption();
static DownloadableIllustration CreateDownloadableIllustration(Illustration downloadContent, bool isFromMange, DownloadOption option, int index = -1)
{
var filePathProvider = option.CreateNewWhenFromUser ? new CreateNewFolderForUserDownloadPathProvider(downloadContent.UserName) : (IDownloadPathProvider) new DefaultDownloadPathProvider();
var fileNameFormatter = new DefaultIllustrationFileNameFormatter();
var model = new DownloadableIllustration(downloadContent, fileNameFormatter, filePathProvider, isFromMange, index) { Option = option };
model.DownloadState.ValueChanged += (sender, args) => Application.Current.Dispatcher.Invoke(() =>
{
switch (args.NewValue)
{
case DownloadStateEnum.Finished:
model.Freeze();
Downloading.Remove(model);
if (Downloaded.All(i => model.DownloadContent.GetDownloadUrl() != i.DownloadContent.GetDownloadUrl()))
{
Downloaded.Add(model);
}
break;
case DownloadStateEnum.Downloading:
Downloaded.Remove(model);
Downloading.Add(model);
break;
case var stat when stat == DownloadStateEnum.Canceled || stat == DownloadStateEnum.Queue || stat == DownloadStateEnum.Exceptional:
if (stat == DownloadStateEnum.Canceled)
{
Downloading.Remove(model);
}
break;
default: throw new ArgumentOutOfRangeException();
}
});
return model;
}
if (illustration.IsManga)
{
for (var j = 0; j < illustration.MangaMetadata.Length; j++)
{
var cpy = j;
Task.Run(() => CreateDownloadableIllustration(illustration.MangaMetadata[cpy], true, option, cpy).Download());
}
}
else
{
Task.Run(() => CreateDownloadableIllustration(illustration, false, option).Download());
}
}
}
public class SearchingHistoryManager
{
private static readonly ObservableCollection<string> SearchingHistory = new ObservableCollection<string>();
public static void EnqueueSearchHistory(string keyword)
{
if (SearchingHistory.Count == 4)
{
SearchingHistory.RemoveAt(SearchingHistory.Count - 1);
}
SearchingHistory.Insert(0, keyword);
}
public static IEnumerable<string> GetSearchingHistory()
{
return SearchingHistory;
}
}
}

View File

@ -0,0 +1,45 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Core
{
public sealed class PixivClient
{
private static volatile PixivClient _instance;
private static readonly object Locker = new object();
public static PixivClient Instance
{
get
{
if (_instance == null)
{
lock (Locker)
{
_instance ??= new PixivClient();
}
}
return _instance;
}
}
}
}

View File

@ -0,0 +1,99 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Request;
namespace Pixeval.Core
{
public static class PixivClientExtension
{
public static async void PostFavoriteAsync(this PixivClient _, Illustration illustration, RestrictPolicy restrictPolicy)
{
illustration.IsLiked = true;
await HttpClientFactory.AppApiService().AddBookmark(new AddBookmarkRequest { Id = illustration.Id, Restrict = restrictPolicy == RestrictPolicy.Public ? "public" : "private" });
}
public static async void RemoveFavoriteAsync(this PixivClient _, Illustration illustration)
{
illustration.IsLiked = false;
await HttpClientFactory.AppApiService().DeleteBookmark(new DeleteBookmarkRequest { IllustId = illustration.Id });
}
public static async Task<IEnumerable<string>> GetArticleWorks(this PixivClient _, string spotlightId)
{
var httpClient = new HttpClient();
var html = await httpClient.GetStringAsync($"https://www.pixivision.net/en/a/{spotlightId}");
var doc = await new HtmlParser().ParseDocumentAsync(html);
return doc.QuerySelectorAll(".am__body .am__work").Select(element => element.Children[1].Children[0].GetAttribute("href")).Select(url => Regex.Match(url, "https://www.pixiv.net/artworks/(?<Id>\\d+)").Groups["Id"].Value);
}
public static async Task FollowArtist(this PixivClient _, User user, RestrictPolicy policy)
{
user.IsFollowed = true;
await HttpClientFactory.AppApiService().FollowArtist(new FollowArtistRequest { Id = user.Id, Restrict = policy == RestrictPolicy.Private ? "private" : "public" });
}
public static async Task UnFollowArtist(this PixivClient _, User user)
{
user.IsFollowed = false;
await HttpClientFactory.AppApiService().UnFollowArtist(new UnFollowArtistRequest { UserId = user.Id });
}
public static async Task<List<TrendingTag>> GetTrendingTags(this PixivClient _)
{
var result = await HttpClientFactory.AppApiService().GetTrendingTags();
var list = new List<TrendingTag>();
if (result is { } res)
{
list.AddRange(res.TrendTags.Select(tag => new TrendingTag { Tag = tag.TagStr, TranslatedName = tag.TranslatedName, Thumbnail = tag.Illust.ImageUrls.SquareMedium }));
}
return list;
}
[Obsolete("reserved for Web API")]
public static async ValueTask<bool> ToggleWebApiR18State(this PixivClient _, bool isR18On)
{
try
{
var html = await HttpClientFactory.WebApiHttpClient().GetStringAsync("https://www.pixiv.net/setting_user.php");
var doc = await new HtmlParser().ParseDocumentAsync(html);
var tt = doc.QuerySelectorAll(".settingContent form input")[1].GetAttribute("value");
await HttpClientFactory.WebApiService().ToggleR18State(new ToggleR18StateRequest { R18 = isR18On ? "show" : "hide", R18G = isR18On ? "2" : "1", Tt = tt });
return true;
}
catch
{
return false;
}
}
}
}

152
Pixeval/Core/PixivHelper.cs Normal file
View File

@ -0,0 +1,152 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions.Logger;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
using Refit;
namespace Pixeval.Core
{
public class PixivHelper
{
public static async Task<Illustration> IllustrationInfo(string id)
{
SingleWorkResponse.Illust response;
try
{
response = (await HttpClientFactory.AppApiService().GetSingle(id)).IllustInfo;
}
catch (ApiException e)
{
ExceptionDumper.WriteException(e);
return null;
}
catch (Exception)
{
return null;
}
var illust = new Illustration
{
Bookmark = (int) response.TotalBookmarks,
Id = response.Id.ToString(),
IsLiked = response.IsBookmarked,
IsManga = response.PageCount != 1,
IsUgoira = response.Type == "ugoira",
Origin = response.ImageUrls.Original ?? response.MetaSinglePage.OriginalImageUrl,
Large = response.ImageUrls.Large,
Tags = response.Tags.Select(t => new Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = response.ImageUrls.Medium,
Title = response.Title,
UserName = response.User.Name,
UserId = response.User.Id.ToString(),
ViewCount = (int) response.TotalView,
Comments = (int) response.TotalComments,
Resolution = $"{response.Width}x{response.Height}",
PublishDate = response.CreateDate
};
if (illust.IsManga && response.MetaPages != null)
{
illust.MangaMetadata = response.MetaPages.Select(p =>
{
var page = (Illustration) illust.Clone();
page.Thumbnail = p.ImageUrls.Medium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
}
return illust;
}
public static async void Enumerate<T>(IPixivAsyncEnumerable<T> pixivIterator, IList<T> container, int limit = -1)
{
EnumeratingSchedule.StartNewInstance(pixivIterator);
var enumerator = EnumeratingSchedule.GetCurrentEnumerator<T>();
await foreach (var illust in enumerator)
{
if (enumerator.IsCancellationRequested() || limit != -1 && pixivIterator.RequestedPages > limit)
{
break;
}
if (pixivIterator.VerifyRationality(illust, container))
{
pixivIterator.InsertionPolicy(illust, container);
}
}
}
public static void RecordTimeline(ITimelineService service, BrowsingHistory browsingHistory)
{
if (service.VerifyRationality(browsingHistory))
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Loaded, (Action) (() => service.Insert(browsingHistory)));
}
}
public static void RecordTimelineInternal(BrowsingHistory browsingHistory)
{
RecordTimeline(BrowsingHistoryAccessor.GlobalLifeTimeScope, browsingHistory);
if (CheckWindowsVersion())
{
RecordTimeline(WindowsUserActivityManager.GlobalLifeTimeScope, browsingHistory);
}
static bool CheckWindowsVersion()
{
return Environment.OSVersion.Version >= new Version(10, 0, 17134); /* Windows 10 April 2018 Update */
}
}
public static bool VerifyIllust(ISet<string> excludeTag, ISet<string> includeTag, int minBookmark, Illustration illustration)
{
if (illustration == null)
{
return false;
}
bool excludeMatch = true, includeMatch = true;
if (!excludeTag.IsNullOrEmpty())
{
excludeMatch = excludeTag.All(x => x.IsNullOrEmpty() || illustration.Tags.All(i => !i.Name.EqualsIgnoreCase(x)));
}
if (!includeTag.IsNullOrEmpty())
{
includeMatch = includeTag.All(x => x.IsNullOrEmpty() || illustration.Tags.Any(i => i.Name.EqualsIgnoreCase(x)));
}
var minBookmarkMatch = illustration.Bookmark > minBookmark;
return excludeMatch && includeMatch && minBookmarkMatch;
}
}
}

104
Pixeval/Core/PixivIO.cs Normal file
View File

@ -0,0 +1,104 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using Pixeval.Data.Web.Delegation;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
// ReSharper disable once InconsistentNaming
public static class PixivIO
{
public static async Task<byte[]> GetBytes(string url)
{
var client = HttpClientFactory.PixivImage();
byte[] res;
try
{
res = await client.GetByteArrayAsync(url);
}
catch
{
return null;
}
return res;
}
public static async Task<BitmapImage> FromUrl(string url)
{
return await FromByteArray(await GetBytes(url));
}
public static async Task<BitmapImage> FromByteArray(byte[] bArr)
{
if (bArr == null || bArr.Length == 0)
{
return null;
}
await using var memoryStream = new MemoryStream(bArr);
return InternalIO.CreateBitmapImageFromStream(memoryStream);
}
public static async Task<string> GetResizedBase64UriOfImageFromUrl(string url, string type = null)
{
return $"data:image/{type ?? url[(url.LastIndexOf('.') + 1)..]};base64,{Convert.ToBase64String(await GetBytes(url))}";
}
public static async Task<MemoryStream> Download(string url, IProgress<double> progress, CancellationToken cancellationToken = default)
{
using var response = await HttpClientFactory.GetResponseHeader(HttpClientFactory.PixivImage().Apply(_ => _.Timeout = TimeSpan.FromSeconds(30)), url);
var contentLength = response.Content.Headers.ContentLength;
if (!contentLength.HasValue)
{
return new MemoryStream(await GetBytes(url));
}
response.EnsureSuccessStatusCode();
long bytesRead, totalRead = 0L;
var byteBuffer = ArrayPool<byte>.Shared.Rent(4096);
var memoryStream = new MemoryStream();
await using var contentStream = await response.Content.ReadAsStreamAsync();
while ((bytesRead = await contentStream.ReadAsync(byteBuffer, 0, byteBuffer.Length, cancellationToken)) != 0)
{
cancellationToken.ThrowIfCancellationRequested();
totalRead += bytesRead;
await memoryStream.WriteAsync(byteBuffer, 0, (int) bytesRead, cancellationToken);
progress.Report(totalRead / (double) contentLength);
}
cancellationToken.ThrowIfCancellationRequested();
ArrayPool<byte>.Shared.Return(byteBuffer, true);
return memoryStream;
}
}
}

View File

@ -0,0 +1,186 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public abstract class AbstractQueryAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
protected readonly bool IsPremium;
private readonly SearchTagMatchOption matchOption;
private readonly int start;
private readonly string tag;
protected AbstractQueryAsyncEnumerable(string tag, SearchTagMatchOption matchOption, bool isPremium, int start = 1)
{
this.start = start < 1 ? 1 : start;
this.tag = tag;
this.matchOption = matchOption;
IsPremium = isPremium;
}
public override int RequestedPages { get; protected set; }
public abstract override void InsertionPolicy(Illustration item, IList<Illustration> collection);
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new QueryAsyncEnumerator(this, tag, matchOption, start, IsPremium);
}
private class QueryAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private readonly int current;
private readonly bool isPremium;
private readonly string keyword;
private readonly SearchTagMatchOption matchOption;
private QueryWorksResponse entity;
private IEnumerator<Illustration> illustrationsEnumerator;
public QueryAsyncEnumerator(IPixivAsyncEnumerable<Illustration> enumerable, string keyword, SearchTagMatchOption matchOption, int current, bool isPremium) : base(enumerable)
{
this.keyword = keyword;
this.matchOption = matchOption;
this.current = current;
this.isPremium = isPremium;
}
public override Illustration Current => illustrationsEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationsEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse($"/v1/search/illust?search_target={matchOption.GetEnumAttribute<EnumAlias>().AliasAs}&sort={(isPremium ? "date_desc" : "popular_desc")}&word={keyword}&filter=for_android&offset={(current - 1) * 30}") is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationsEnumerator.MoveNext())
{
return true;
}
if (int.Parse(entity.NextUrl[(entity.NextUrl.LastIndexOf('=') + 1)..]) >= 5000)
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<QueryWorksResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<QueryWorksResponse>();
if (res is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<QueryWorksResponse>.Wrap(true, response);
}
return HttpResponse<QueryWorksResponse>.Wrap(false);
}
}
}
public class PopularityQueryAsyncEnumerable : AbstractQueryAsyncEnumerable
{
public PopularityQueryAsyncEnumerable(string tag, SearchTagMatchOption matchOption, bool isPremium, int start = 1) : base(tag, matchOption, isPremium, start)
{
}
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
if (item != null)
{
if (IsPremium)
{
collection.Add(item);
}
else
{
collection.AddSorted(item, IllustrationPopularityComparator.Instance);
}
}
}
}
public class PublishDateQueryAsyncEnumerable : AbstractQueryAsyncEnumerable
{
public PublishDateQueryAsyncEnumerable(string tag, SearchTagMatchOption matchOption, bool isPremium, int start = 1) : base(tag, matchOption, isPremium, start)
{
}
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
if (item != null)
{
if (IsPremium)
{
collection.Add(item);
}
else
{
collection.AddSorted(item, IllustrationPublishDateComparator.Instance);
}
}
}
}
}

136
Pixeval/Core/RankOption.cs Normal file
View File

@ -0,0 +1,136 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ForR18Only : Attribute
{
}
public enum RankOption
{
/// <summary>
/// 日榜
/// </summary>
[EnumAlias("day")]
[EnumLocalizedName("RankOptionDay")]
Day,
/// <summary>
/// 周榜
/// </summary>
[EnumAlias("week")]
[EnumLocalizedName("RankOptionWeek")]
Week,
/// <summary>
/// 月榜
/// </summary>
[EnumAlias("month")]
[EnumLocalizedName("RankOptionMonth")]
Month,
/// <summary>
/// 男性向日榜
/// </summary>
[EnumAlias("day_male")]
[EnumLocalizedName("RankOptionDayMale")]
DayMale,
/// <summary>
/// 女性向日榜
/// </summary>
[EnumAlias("day_female")]
[EnumLocalizedName("RankOptionDayFemale")]
DayFemale,
/// <summary>
/// 多图日榜
/// </summary>
[EnumAlias("day_manga")]
[EnumLocalizedName("RankOptionDayManga")]
DayManga,
/// <summary>
/// 多图周榜
/// </summary>
[EnumAlias("week_manga")]
[EnumLocalizedName("RankOptionWeekManga")]
WeekManga,
/// <summary>
/// 原创
/// </summary>
[EnumAlias("week_original")]
[EnumLocalizedName("RankOptionWeekOriginal")]
WeekOriginal,
/// <summary>
/// 新人
/// </summary>
[EnumAlias("week_rookie")]
[EnumLocalizedName("RankOptionWeekRookie")]
WeekRookie,
/// <summary>
/// R18日榜
/// </summary>
[ForR18Only]
[EnumAlias("day_r18")]
[EnumLocalizedName("RankOptionDayR18")]
DayR18,
/// <summary>
/// 男性向R18日榜
/// </summary>
[ForR18Only]
[EnumAlias("day_male_r18")]
[EnumLocalizedName("RankOptionDayMaleR18")]
DayMaleR18,
/// <summary>
/// 女性向R18日榜
/// </summary>
[ForR18Only]
[EnumAlias("day_female_r18")]
[EnumLocalizedName("RankOptionDayFemaleR18")]
DayFemaleR18,
/// <summary>
/// R18周榜
/// </summary>
[ForR18Only]
[EnumAlias("week_r18")]
[EnumLocalizedName("RankOptionWeekR18")]
WeekR18,
/// <summary>
/// R18G周榜
/// </summary>
[ForR18Only]
[EnumAlias("week_r18g")]
[EnumLocalizedName("RankOptionWeekR18G")]
WeekR18G
}
}

View File

@ -0,0 +1,137 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public class RankingAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
private readonly DateTime dateTime;
private readonly RankOption rankOption;
public RankingAsyncEnumerable(RankOption rankOption, DateTime dateTime)
{
this.rankOption = rankOption;
this.dateTime = dateTime;
}
public override int RequestedPages { get; protected set; }
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
collection.Add(item);
}
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new RankingAsyncEnumerator(this, rankOption, dateTime);
}
private class RankingAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private readonly string dateTimeParameter;
private readonly string rankOptionParameter;
private RankingResponse entity;
private IEnumerator<Illustration> illustrationEnumerator;
public RankingAsyncEnumerator(IPixivAsyncEnumerable<Illustration> enumerable, RankOption rankOption, DateTime dateTime) : base(enumerable)
{
rankOptionParameter = rankOption.GetEnumAttribute<EnumAlias>().AliasAs;
dateTimeParameter = dateTime.ToString("yyyy-MM-dd");
}
public override Illustration Current => illustrationEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse($"/v1/illust/ranking?filter=for_android&mode={rankOptionParameter}&date={dateTimeParameter}") is (true, var result))
{
entity = result;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var model))
{
entity = model;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<RankingResponse>> TryGetResponse(string url)
{
var result = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<RankingResponse>();
if (result is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<RankingResponse>.Wrap(true, response);
}
return HttpResponse<RankingResponse>.Wrap(false);
}
}
}
}

View File

@ -0,0 +1,139 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public abstract class AbstractRecommendAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
public override int RequestedPages { get; protected set; }
public abstract override void InsertionPolicy(Illustration item, IList<Illustration> collection);
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new RecommendAsyncEnumerator(this);
}
private class RecommendAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private RecommendResponse entity;
private IEnumerator<Illustration> illustrationEnumerator;
public RecommendAsyncEnumerator(IPixivAsyncEnumerable<Illustration> enumerable) : base(enumerable)
{
}
public override Illustration Current => illustrationEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse("/v1/illust/recommended") is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<RecommendResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<RecommendResponse>();
if (res is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<RecommendResponse>.Wrap(true, response);
}
return HttpResponse<RecommendResponse>.Wrap(false);
}
}
}
public class PopularityRecommendAsyncEnumerable : AbstractRecommendAsyncEnumerable
{
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
if (item != null)
{
collection.AddSorted(item, IllustrationPopularityComparator.Instance);
}
}
}
public class PlainRecommendAsyncEnumerable : AbstractRecommendAsyncEnumerable
{
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
collection.Add(item);
}
}
}

View File

@ -0,0 +1,71 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Request;
namespace Pixeval.Core
{
public class RecommendIllustratorDeferrer
{
public static readonly RecommendIllustratorDeferrer Instance = new RecommendIllustratorDeferrer();
private readonly List<User> currentIllustrators = new List<User>();
private int index;
private int requestTimes;
public async Task<IEnumerable<User>> Acquire(int count)
{
if (30 % count != 0)
{
throw new ArgumentException("count must be divisible by 30");
}
if (currentIllustrators.Count < index + count)
{
if (requestTimes >= 9)
{
index = 0;
requestTimes = 0;
currentIllustrators.Clear();
}
await Request();
}
var illustrators = currentIllustrators.Skip(index).Take(count);
index += count;
return illustrators;
}
private async Task Request()
{
var newIllustrators = await HttpClientFactory.AppApiService().GetRecommendIllustrators(new RecommendIllustratorRequest { Offset = requestTimes++ * 30 });
currentIllustrators.AddRange(newIllustrators.UserPreviews.Select(i => i.Parse()));
}
}
}

View File

@ -0,0 +1,27 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Core
{
public enum RestrictPolicy
{
Public, Private
}
}

View File

@ -0,0 +1,48 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public enum SearchTagMatchOption
{
/// <summary>
/// 部分一致
/// </summary>
[EnumAlias("partial_match_for_tags")]
[EnumLocalizedName("TagMatchingPartialMatch")]
PartialMatchForTags,
/// <summary>
/// 完全一致
/// </summary>
[EnumAlias("exact_match_for_tags")]
[EnumLocalizedName("TagMatchingExactMatch")]
ExactMatchForTags,
/// <summary>
/// 标题和说明文
/// </summary>
[EnumAlias("title_and_caption")]
[EnumLocalizedName("TagMatchingTitleAndCaption")]
TitleAndCaption
}
}

View File

@ -0,0 +1,121 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public class SpotlightQueryAsyncEnumerable : AbstractPixivAsyncEnumerable<SpotlightArticle>
{
private readonly int start;
public SpotlightQueryAsyncEnumerable(int start)
{
this.start = start < 1 ? 1 : start;
}
public override int RequestedPages { get; protected set; }
public override IAsyncEnumerator<SpotlightArticle> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new SpotlightArticleAsyncEnumerator(this, start);
}
private class SpotlightArticleAsyncEnumerator : AbstractPixivAsyncEnumerator<SpotlightArticle>
{
private int current;
private SpotlightResponse entity;
private IEnumerator<SpotlightArticle> spotlightArticleEnumerator;
public SpotlightArticleAsyncEnumerator(IPixivAsyncEnumerable<SpotlightArticle> enumerable, int current) : base(enumerable)
{
this.current = current;
}
public override SpotlightArticle Current => spotlightArticleEnumerator.Current;
protected override void UpdateEnumerator()
{
spotlightArticleEnumerator = entity.SpotlightArticles.NonNull().GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse() is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (spotlightArticleEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse() is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private async Task<HttpResponse<SpotlightResponse>> TryGetResponse()
{
var res = await HttpClientFactory.AppApiService().GetSpotlights(current++ * 10);
if (res is { } response && !response.SpotlightArticles.IsNullOrEmpty())
{
return HttpResponse<SpotlightResponse>.Wrap(true, response);
}
return HttpResponse<SpotlightResponse>.Wrap(false);
}
}
}
}

View File

@ -0,0 +1,253 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
/// <summary>
/// This class is piece of shit
/// </summary>
public class TrendsAsyncEnumerable : AbstractPixivAsyncEnumerable<Trends>
{
public override int RequestedPages { get; protected set; }
public override IAsyncEnumerator<Trends> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new TrendsAsyncEnumerator(this);
}
private class TrendsAsyncEnumerator : AbstractPixivAsyncEnumerator<Trends>
{
private TrendsRequestContext requestContext;
private IEnumerator<Trends> trendsEnumerable;
private string tt;
public TrendsAsyncEnumerator(IPixivAsyncEnumerable<Trends> enumerable) : base(enumerable)
{
}
public override Trends Current => trendsEnumerable.Current;
public override async ValueTask<bool> MoveNextAsync()
{
if (requestContext == null)
{
if (await GetResponse(BuildRequestUrl()) is (true, var result))
{
tt = Regex.Match(result, "tt: \"(?<tt>.*)\"").Groups["tt"].Value;
trendsEnumerable = (await ParsePreloadJsonFromHtml(result)).NonNull().GetEnumerator();
requestContext = ExtractRequestParametersFromHtml(result);
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (trendsEnumerable.MoveNext())
{
return true;
}
if (requestContext.IsLastPage)
{
return false;
}
if (await GetResponse(BuildRequestUrl()) is (true, var json))
{
trendsEnumerable = (await ParseRawJson(json)).NonNull().GetEnumerator();
requestContext = ExtractRequestParametersFromRawJson(json);
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private string BuildRequestUrl()
{
return requestContext == null ? "/stacc?mode=unify" : $"/stacc/my/home/all/activity/{requestContext.Sid}/.json?mode={requestContext.Mode}&unify_token={requestContext.UnifyToken}&tt={tt}";
}
private static TrendsRequestContext ExtractRequestParametersFromHtml(string html)
{
var json = JObject.Parse(ExtractPreloadJsonSnippet(html));
return new TrendsRequestContext
{
Mode = json["param"]?["mode"]?.Value<string>(),
UnifyToken = json["param"]?["unify_token"]?.Value<string>(),
Sid = json["next_max_sid"]?.Value<long>().ToString(),
IsLastPage = json["is_last_page"]?.Value<int>() == 1
};
}
private static TrendsRequestContext ExtractRequestParametersFromRawJson(string json)
{
var obj = JObject.Parse(json);
return new TrendsRequestContext
{
Mode = obj["stacc"]?["param"]?["mode"]?.Value<string>(),
UnifyToken = obj["stacc"]?["param"]?["unify_token"]?.Value<string>(),
Sid = obj["stacc"]?["next_max_sid"]?.Value<long>().ToString(),
IsLastPage = obj["stacc"]?["is_last_page"]?.Value<int>() == 1
};
}
private static Task<IEnumerable<Trends>> ParsePreloadJsonFromHtml(string html)
{
return ParsePreloadJson(ExtractPreloadJsonSnippet(html));
}
private static string ExtractPreloadJsonSnippet(string html)
{
var match = Regex.Match(html, "pixiv\\.stacc\\.env\\.preload\\.stacc \\= (?<json>.*);");
if (!match.Success)
{
throw new QueryNotRespondingException();
}
return match.Groups["json"].Value;
}
private static Task<IEnumerable<Trends>> ParseRawJson(string json)
{
var stacc = JObject.Parse(json)["stacc"]?.ToString();
return ParsePreloadJson(stacc);
}
private static async Task<IEnumerable<Trends>> ParsePreloadJson(string json)
{
var tasks = new List<Task<Trends>>();
var stacc = JObject.Parse(json);
var status = stacc["status"];
var timeline = stacc["timeline"];
var user = stacc["user"];
var illust = stacc["illust"];
foreach (var timelineChild in timeline!)
{
var task = Task.Run(() =>
{
var timelineProp = timelineChild.First;
var statusObj = status?.FirstOrDefault(sChild => sChild.First?["id"]?.Value<string>() == timelineProp?["id"]?.Value<string>());
if (statusObj?.First == null)
{
return null;
}
var statusObjProp = statusObj.First;
var trendsObj = new Trends
{
PostDate = DateTime.Parse(statusObjProp?["post_date"]?.Value<string>()!, CultureInfo.CurrentCulture),
PostUserId = statusObjProp["post_user"]?["id"]?.Value<string>(),
TrendObjectId = statusObjProp["type"]?.Value<string>() switch
{
var type when type == "add_illust" || type == "add_bookmark" => statusObjProp["ref_illust"]?["id"]?.Value<string>(),
"add_favorite" => statusObjProp["ref_user"]?["id"]?.Value<string>(),
_ => null
}
};
var matchingPostUser = user?.FirstOrDefault(uChild => uChild.First?["id"]?.Value<string>() == trendsObj.PostUserId);
if (matchingPostUser != null)
{
trendsObj.PostUserThumbnail = matchingPostUser.First?["profile_image"]?.First?.First?["url"]?["m"]?.Value<string>();
trendsObj.PostUserName = matchingPostUser.First?["name"]?.Value<string>();
}
else
{
return null;
}
trendsObj.Type = statusObjProp["type"]?.Value<string>() switch
{
"add_illust" => TrendType.AddIllust,
"add_bookmark" => TrendType.AddBookmark,
"add_favorite" => TrendType.AddFavorite,
_ => (TrendType) (-1)
};
trendsObj.TrendObjectThumbnail = trendsObj.Type switch
{
var type when type == TrendType.AddBookmark || type == TrendType.AddIllust => illust?.FirstOrDefault(iChild => iChild.First?["id"]?.Value<string>() == trendsObj.TrendObjectId)?.First?["url"]?["m"]?.Value<string>(),
TrendType.AddFavorite => user.FirstOrDefault(uChild => uChild.First?["id"]?.Value<string>() == trendsObj.TrendObjectId)?.First?["profile_image"]?.First?.First?["url"]?["s"]?.Value<string>(),
(TrendType) (-1) => null,
_ => throw new ArgumentOutOfRangeException()
};
if (trendsObj.Type != TrendType.AddFavorite)
{
var illustration = illust.FirstOrDefault(iChild => iChild.First?["id"]?.Value<string>() == trendsObj.TrendObjectId);
if (illustration != null)
{
trendsObj.ByName = user.FirstOrDefault(uChild => uChild.First?["id"]?.Value<string>() == illustration.First?["post_user"]?["id"]?.Value<string>())?.First["name"].Value<string>();
trendsObj.TrendObjName = illustration.First["title"].Value<string>();
}
}
else
{
trendsObj.TrendObjName = user.FirstOrDefault(uChild => uChild.First?["id"]?.Value<string>() == trendsObj.TrendObjectId)?.First["name"].Value<string>();
trendsObj.IsReferToUser = true;
}
return trendsObj;
});
tasks.Add(task);
}
return await Task.WhenAll(tasks);
}
protected override void UpdateEnumerator()
{
throw new NotImplementedException();
}
private static async Task<HttpResponse<string>> GetResponse(string url)
{
var result = await HttpClientFactory.WebApiHttpClient().GetStringAsync(url);
return !result.IsNullOrEmpty() ? HttpResponse<string>.Wrap(true, result) : HttpResponse<string>.Wrap(false);
}
}
private class TrendsRequestContext
{
public string UnifyToken { get; set; }
public string Sid { get; set; }
public string Mode { get; set; }
public bool IsLastPage { get; set; }
}
}
}

View File

@ -0,0 +1,136 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public class UploadAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
private readonly string uid;
public UploadAsyncEnumerable(string uid)
{
this.uid = uid;
}
public override int RequestedPages { get; protected set; }
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
{
if (item != null)
{
collection.AddSorted(item, IllustrationPublishDateComparator.Instance);
}
}
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new UploadAsyncEnumerator(this, uid);
}
private class UploadAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private readonly string uid;
private UploadResponse entity;
private IEnumerator<Illustration> illustrationEnumerator;
public UploadAsyncEnumerator(IPixivAsyncEnumerable<Illustration> enumerable, string uid) : base(enumerable)
{
this.uid = uid;
}
public override Illustration Current => illustrationEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse($"/v1/user/illusts?user_id={uid}&filter=for_android&type=illust") is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<UploadResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<UploadResponse>();
if (res is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<UploadResponse>.Wrap(true, response);
}
return HttpResponse<UploadResponse>.Wrap(false);
}
}
}
}

View File

@ -0,0 +1,166 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public abstract class AbstractUserFollowingAsyncEnumerable : AbstractPixivAsyncEnumerable<User>
{
protected abstract string Uid { get; }
protected abstract RestrictPolicy RestrictPolicy { get; }
public override int RequestedPages { get; protected set; }
public override IAsyncEnumerator<User> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new UserFollowingAsyncEnumerator(this, Uid, RestrictPolicy);
}
public static AbstractUserFollowingAsyncEnumerable Of(string uid, RestrictPolicy restrictPolicy)
{
return restrictPolicy switch
{
RestrictPolicy.Public => new PublicUserFollowingAsyncEnumerable(uid),
RestrictPolicy.Private => new PrivateUserFollowingAsyncEnumerable(uid),
_ => throw new ArgumentOutOfRangeException(nameof(restrictPolicy), restrictPolicy, null)
};
}
private class UserFollowingAsyncEnumerator : AbstractPixivAsyncEnumerator<User>
{
private readonly RestrictPolicy restrictPolicy;
private readonly string userId;
private FollowingResponse entity;
private IEnumerator<User> followerEnumerator;
public UserFollowingAsyncEnumerator(IPixivAsyncEnumerable<User> enumerable, string userId, RestrictPolicy restrictPolicy) : base(enumerable)
{
this.userId = userId;
this.restrictPolicy = restrictPolicy;
}
public override User Current => followerEnumerator.Current;
protected override void UpdateEnumerator()
{
followerEnumerator = entity.UserPreviews.NonNull().Select(u => new User
{
Thumbnails = u.Illusts.NonNull().Select(_ => _.ImageUrls.SquareMedium).ToArray(),
Id = u.User.Id.ToString(),
Name = u.User.Name,
Avatar = u.User.ProfileImageUrls.Medium
}).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse(restrictPolicy switch
{
RestrictPolicy.Public => $"/v1/user/following?user_id={userId}&restrict=public",
RestrictPolicy.Private => $"/v1/user/following?user_id={userId}&restrict=private",
_ => throw new ArgumentOutOfRangeException()
}) is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (followerEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<FollowingResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<FollowingResponse>();
if (res is { } response && !response.UserPreviews.IsNullOrEmpty())
{
return HttpResponse<FollowingResponse>.Wrap(true, response);
}
return HttpResponse<FollowingResponse>.Wrap(false);
}
}
}
public class PublicUserFollowingAsyncEnumerable : AbstractUserFollowingAsyncEnumerable
{
public PublicUserFollowingAsyncEnumerable(string uid)
{
Uid = uid;
}
protected override string Uid { get; }
protected override RestrictPolicy RestrictPolicy { get; } = RestrictPolicy.Public;
}
public class PrivateUserFollowingAsyncEnumerable : AbstractUserFollowingAsyncEnumerable
{
public PrivateUserFollowingAsyncEnumerable(string uid)
{
Uid = uid;
}
protected override string Uid { get; }
protected override RestrictPolicy RestrictPolicy { get; } = RestrictPolicy.Private;
}
}

View File

@ -0,0 +1,126 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Core
{
public class UserPreviewAsyncEnumerable : AbstractPixivAsyncEnumerable<User>
{
private readonly string keyword;
public UserPreviewAsyncEnumerable(string keyword)
{
this.keyword = keyword;
}
public override int RequestedPages { get; protected set; }
public override IAsyncEnumerator<User> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new UserPreviewAsyncEnumerator(this, keyword);
}
private class UserPreviewAsyncEnumerator : AbstractPixivAsyncEnumerator<User>
{
private readonly string keyword;
private UserNavResponse entity;
private IEnumerator<User> userPreviewEnumerator;
public UserPreviewAsyncEnumerator(IPixivAsyncEnumerable<User> enumerable, string keyword) : base(enumerable)
{
this.keyword = keyword;
}
public override User Current => userPreviewEnumerator.Current;
protected override void UpdateEnumerator()
{
userPreviewEnumerator = entity.UserPreviews.NonNull().Select(u => new User
{
Avatar = u.User.ProfileImageUrls.Medium,
Thumbnails = u.Illusts.NonNull().Select(_ => _.ImageUrl.SquareMedium).ToArray(),
Id = u.User.Id.ToString(),
Name = u.User.Name
}).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse($"https://app-api.pixiv.net/v1/search/user?filter=for_android&word={keyword}") is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (userPreviewEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<UserNavResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<UserNavResponse>();
if (res is { } response && !response.UserPreviews.IsNullOrEmpty())
{
return HttpResponse<UserNavResponse>.Wrap(true, response);
}
return HttpResponse<UserNavResponse>.Wrap(false);
}
}
}
}

View File

@ -0,0 +1,118 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.ViewModel;
using Pixeval.Data.Web;
using Pixeval.Data.Web.Delegation;
using Pixeval.Data.Web.Response;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Core
{
public class UserUpdateAsyncEnumerable : AbstractPixivAsyncEnumerable<Illustration>
{
public override int RequestedPages { get; protected set; }
public override IAsyncEnumerator<Illustration> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new UserUpdateAsyncEnumerator(this);
}
public override bool VerifyRationality(Illustration item, IList<Illustration> collection)
{
return item != null && collection.All(t => t.Id != item.Id) && PixivHelper.VerifyIllust(Settings.Global.ExcludeTag, Settings.Global.IncludeTag, Settings.Global.MinBookmark, item);
}
private class UserUpdateAsyncEnumerator : AbstractPixivAsyncEnumerator<Illustration>
{
private UserUpdateResponse entity;
private IEnumerator<Illustration> illustrationEnumerator;
public UserUpdateAsyncEnumerator(IPixivAsyncEnumerable<Illustration> enumerable) : base(enumerable)
{
}
public override Illustration Current => illustrationEnumerator.Current;
protected override void UpdateEnumerator()
{
illustrationEnumerator = entity.Illusts.NonNull().Select(_ => _.Parse()).GetEnumerator();
}
public override async ValueTask<bool> MoveNextAsync()
{
if (entity == null)
{
if (await TryGetResponse("https://app-api.pixiv.net/v2/illust/follow?restrict=public") is (true, var model))
{
entity = model;
UpdateEnumerator();
}
else
{
throw new QueryNotRespondingException();
}
Enumerable.ReportRequestedPages();
}
if (illustrationEnumerator.MoveNext())
{
return true;
}
if (entity.NextUrl.IsNullOrEmpty())
{
return false;
}
if (await TryGetResponse(entity.NextUrl) is (true, var res))
{
entity = res;
UpdateEnumerator();
Enumerable.ReportRequestedPages();
return true;
}
return false;
}
private static async Task<HttpResponse<UserUpdateResponse>> TryGetResponse(string url)
{
var res = (await HttpClientFactory.AppApiHttpClient().GetStringAsync(url)).FromJson<UserUpdateResponse>();
if (res is { } response && !response.Illusts.IsNullOrEmpty())
{
return HttpResponse<UserUpdateResponse>.Wrap(true, response);
}
return HttpResponse<UserUpdateResponse>.Wrap(false);
}
}
}
}

View File

@ -0,0 +1,137 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.UserActivities;
using Windows.UI.Shell;
using AdaptiveCards;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
namespace Pixeval.Core
{
/// <summary>
/// Provides a set of functions to create a Windows 10 Timeline Activity,
/// for more information and underlying implementation, see
/// <a href="https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/modernize-wpf-tutorial-4" />
/// </summary>
public class WindowsUserActivityManager : ITimelineService
{
public static readonly WindowsUserActivityManager GlobalLifeTimeScope = new WindowsUserActivityManager();
private readonly Uri iconUri = new Uri("http://qa23pqcql.bkt.clouddn.com/pxlogo.ico");
private UserActivitySession userActivitySession;
public bool VerifyRationality(BrowsingHistory browsingHistory)
{
return true;
}
public async void Insert(BrowsingHistory browsingHistory)
{
var userActivityChannel = UserActivityChannel.GetDefault();
var model = await GetPixevalTimelineModel(browsingHistory);
var userActivity = await userActivityChannel.GetOrCreateUserActivityAsync($"Pixeval-{model.Id}-{DateTime.Now:s}");
userActivity.VisualElements.DisplayText = model.Title;
userActivity.VisualElements.Content = AdaptiveCardBuilder.CreateAdaptiveCardFromJson(BuildAdaptiveCard(model));
userActivity.VisualElements.Attribution = new UserActivityAttribution(iconUri);
userActivity.VisualElements.AttributionDisplayText = "Pixeval";
userActivity.ActivationUri = new Uri(browsingHistory.Type switch
{
"illust" => $"pixeval://www.pixiv.net/artworks/{model.Id}",
"user" => $"pixeval://www.pixiv.net/users/{model.Id}",
"spotlight" => $"pixeval://www.pixivision.net/en/a/{model.Id}",
_ => throw new ArgumentException(nameof(browsingHistory.Type))
});
await userActivity.SaveAsync();
userActivitySession?.Dispose();
userActivitySession = userActivity.CreateSession();
}
private static async Task<PixevalTimelineModel> GetPixevalTimelineModel(BrowsingHistory history)
{
var p = new PixevalTimelineModel { Background = await PixivIO.GetResizedBase64UriOfImageFromUrl(history.BrowseObjectThumbnail), Id = history.BrowseObjectId, Title = history.BrowseObjectState };
switch (history.Type)
{
case "illust":
p.Author = history.IllustratorName;
return p;
case "user":
case "spotlight": return p;
}
throw new ArgumentException(nameof(history.Type));
}
private static string BuildAdaptiveCard(PixevalTimelineModel pixevalTimelineModel)
{
var card = new StringifyBackgroundAdaptiveCard("1.0") { StringifyUrl = pixevalTimelineModel.Background };
card.Body.Add(new AdaptiveTextBlock
{
Text = pixevalTimelineModel.Title,
Weight = AdaptiveTextWeight.Bolder,
Wrap = true,
Size = AdaptiveTextSize.Large,
MaxLines = 3
});
if (!pixevalTimelineModel.Author.IsNullOrEmpty())
{
card.Body.Add(new AdaptiveTextBlock
{
Text = pixevalTimelineModel.Author,
Weight = AdaptiveTextWeight.Bolder,
Wrap = true,
Size = AdaptiveTextSize.Small,
MaxLines = 3,
Spacing = AdaptiveSpacing.Small
});
}
return card.ToJson();
}
private class PixevalTimelineModel
{
public string Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Background { get; set; }
}
// https://stackoverflow.com/questions/55663963/adaptive-cards-serving-images-in-bytes
private class StringifyBackgroundAdaptiveCard : AdaptiveCard
{
public StringifyBackgroundAdaptiveCard(AdaptiveSchemaVersion schemaVersion) : base(schemaVersion)
{
}
public StringifyBackgroundAdaptiveCard(string schemaVersion) : base(schemaVersion)
{
}
[JsonProperty("backgroundImage")]
public string StringifyUrl { get; set; }
}
}
}

27
Pixeval/Data/IParser.cs Normal file
View File

@ -0,0 +1,27 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Data
{
public interface IParser<out T>
{
T Parse();
}
}

View File

@ -0,0 +1,32 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class AutoCompletion
{
public string Tag { get; set; }
public string TranslatedName { get; set; }
}
}

View File

@ -0,0 +1,58 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
using SQLite;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class BrowsingHistory
{
[PrimaryKey]
[AutoIncrement]
public int Index { get; set; }
/// <summary>
/// 提供当前<see cref="BrowsingHistory"/>的<see cref="string"/>视图,默认的值是名称/标题
/// </summary>
public string BrowseObjectState { get; set; }
public string BrowseObjectThumbnail { get; set; }
/// <summary>
/// <see cref="IsReferToIllust"/>有效仅当此属性有效
/// </summary>
public string IllustratorName { get; set; }
public bool IsReferToUser { get; set; }
public bool IsReferToIllust { get; set; }
public bool IsReferToSpotlight { get; set; }
/// <summary>
/// 提供当前<see cref="BrowsingHistory"/>的id的<see cref="string"/>视图默认的值是作品ID/用户ID/特辑ID
/// </summary>
public string BrowseObjectId { get; set; }
public string Type { get; set; }
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class ConditionString
{
[DoNotNotify]
public static ConditionString Shared = new ConditionString();
public string Condition { get; set; }
}
}

View File

@ -0,0 +1,227 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Core;
using Pixeval.Data.Web.Delegation;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class DownloadableIllustration
{
[DoNotNotify]
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private bool modifiable = true;
private bool retried;
public DownloadableIllustration(Illustration downloadContent, IIllustrationFileNameFormatter fileNameFormatter, IDownloadPathProvider downloadPathProvider, bool isFromManga, int mangaIndex = -1)
{
DownloadContent = downloadContent;
FileNameFormatter = fileNameFormatter;
DownloadPathProvider = downloadPathProvider;
IsFromManga = isFromManga;
MangaIndex = mangaIndex;
}
public Illustration DownloadContent { get; set; }
public IIllustrationFileNameFormatter FileNameFormatter { get; set; }
public IDownloadPathProvider DownloadPathProvider { get; set; }
public bool IsFromManga { get; set; }
public int MangaIndex { get; set; }
public bool DownloadFailed { get; set; }
public double Progress { get; set; }
public string ReasonPhase { get; set; }
public Observable<DownloadStateEnum> DownloadState { get; set; } = new Observable<DownloadStateEnum>(DownloadStateEnum.Queue);
public DownloadOption Option { get; set; }
public string GetPath()
{
if (DownloadContent.IsUgoira)
{
return Path.Combine(Directory.CreateDirectory(DownloadPathProvider.GetIllustrationPath(Option)).FullName, FileNameFormatter.FormatGif(DownloadContent));
}
if (DownloadContent.FromSpotlight)
{
return IsFromManga ? Path.Combine(Directory.CreateDirectory(DownloadPathProvider.GetSpotlightPath(DownloadContent.SpotlightTitle, Option)).FullName, DownloadContent.Id, FileNameFormatter.FormatManga(DownloadContent, MangaIndex)) : Path.Combine(Directory.CreateDirectory(DownloadPathProvider.GetSpotlightPath(DownloadContent.SpotlightTitle, Option)).FullName, FileNameFormatter.Format(DownloadContent));
}
return IsFromManga ? Path.Combine(Directory.CreateDirectory(DownloadPathProvider.GetMangaPath(DownloadContent.Id, Option)).FullName, FileNameFormatter.FormatManga(DownloadContent, MangaIndex)) : Path.Combine(Directory.CreateDirectory(DownloadPathProvider.GetIllustrationPath(Option)).FullName, FileNameFormatter.Format(DownloadContent));
}
public void Freeze()
{
modifiable = false;
}
public void Cancel()
{
if (modifiable)
{
cancellationTokenSource.Cancel();
cancellationTokenSource = new CancellationTokenSource();
Progress = 0;
ReasonPhase = null;
DownloadFailed = false;
DownloadState.Value = DownloadStateEnum.Canceled;
}
}
public void Restart()
{
if (modifiable)
{
cancellationTokenSource.Cancel();
cancellationTokenSource = new CancellationTokenSource();
Progress = 0;
ReasonPhase = null;
DownloadFailed = false;
Download();
}
}
public async void Download()
{
if (!modifiable)
{
return;
}
DownloadState.Value = DownloadStateEnum.Downloading;
var downloadPath = GetPath();
if (DownloadContent.IsUgoira)
{
DownloadGif();
return;
}
try
{
await using var memory = await PixivIO.Download(DownloadContent.GetDownloadUrl(), new Progress<double>(d => Progress = d), cancellationTokenSource.Token);
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
await using var fileStream = new FileStream(downloadPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
memory.WriteTo(fileStream);
DownloadState.Value = DownloadStateEnum.Finished;
}
catch (OperationCanceledException)
{
if (downloadPath != null && File.Exists(downloadPath))
{
File.Delete(downloadPath);
}
}
catch (Exception e)
{
if (!retried)
{
Restart();
retried = true;
}
else
{
HandleError(e, downloadPath);
}
}
}
private async void DownloadGif()
{
var downloadPath = GetPath();
try
{
var metadata = await HttpClientFactory.AppApiService().GetUgoiraMetadata(DownloadContent.Id);
var ugoiraUrl = metadata.UgoiraMetadataInfo.ZipUrls.Medium;
ugoiraUrl = !ugoiraUrl.EndsWith("ugoira1920x1080.zip") ? Regex.Replace(ugoiraUrl, "ugoira(\\d+)x(\\d+).zip", "ugoira1920x1080.zip") : ugoiraUrl;
var delay = metadata.UgoiraMetadataInfo.Frames.Select(f => f.Delay / 10).ToArray();
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
await using var memory = await PixivIO.Download(ugoiraUrl, new Progress<double>(d => Progress = d), cancellationTokenSource.Token);
await using var gifStream = (MemoryStream) InternalIO.MergeGifStream(InternalIO.ReadGifZipEntries(memory), delay);
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
await using var fileStream = new FileStream(downloadPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
gifStream.WriteTo(fileStream);
DownloadState.Value = DownloadStateEnum.Finished;
}
catch (TaskCanceledException)
{
if (downloadPath != null && File.Exists(downloadPath))
{
File.Delete(downloadPath);
}
}
catch (Exception e)
{
if (!retried)
{
Restart();
retried = true;
}
else
{
HandleError(e, downloadPath);
}
}
}
private void HandleError(Exception e, string path)
{
DownloadState.Value = DownloadStateEnum.Exceptional;
DownloadFailed = true;
ReasonPhase = e.Message;
if (path != null && File.Exists(path))
{
File.Delete(path);
}
}
}
[Flags]
public enum DownloadStateEnum
{
Queue, Downloading, Exceptional, Finished, Canceled
}
}

View File

@ -0,0 +1,46 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class I18NOption
{
public static readonly I18NOption UsEnglish = new I18NOption("English(US)", "en-us");
public static readonly I18NOption ChineseSimplified = new I18NOption("简体中文(中国)", "zh-cn");
public I18NOption(string localizedName, string name)
{
LocalizedName = localizedName;
Name = name;
}
public I18NOption()
{
}
public string LocalizedName { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,119 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Pixeval.Objects.Primitive;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class Illustration : ICloneable
{
public string Id { get; set; }
public bool IsUgoira { get; set; }
public bool IsR18 => Tags?.Any(x => Regex.IsMatch(x?.Name ?? string.Empty, "[Rr][-]?18[Gg]?") || Regex.IsMatch(x?.TranslatedName ?? string.Empty, "[Rr][-]?18[Gg]?")) ?? false;
public string Origin { get; set; }
public string Large { get; set; }
public string Thumbnail { get; set; }
public int Bookmark { get; set; }
public bool IsLiked { get; set; }
public bool IsManga { get; set; }
public string Title { get; set; }
public string UserName { get; set; }
public string UserId { get; set; }
public IEnumerable<Tag> Tags { get; set; }
public Illustration[] MangaMetadata { get; set; }
public DateTimeOffset PublishDate { get; set; }
public int ViewCount { get; set; }
public string Resolution { get; set; }
public int Comments { get; set; }
public bool FromSpotlight { get; set; }
public string SpotlightTitle { get; set; }
public object Clone()
{
return MemberwiseClone();
}
public string GetDownloadUrl()
{
return Origin.IsNullOrEmpty() ? Large : Origin;
}
}
public class Tag
{
public string Name { get; set; }
public string TranslatedName { get; set; }
}
public class IllustrationPopularityComparator : IComparer<Illustration>
{
public static readonly IllustrationPopularityComparator Instance = new IllustrationPopularityComparator();
public int Compare(Illustration x, Illustration y)
{
if (x == null || y == null)
{
return 0;
}
return x.Bookmark < y.Bookmark ? 1 : x.Bookmark == y.Bookmark ? 0 : -1;
}
}
public class IllustrationPublishDateComparator : IComparer<Illustration>
{
public static readonly IllustrationPublishDateComparator Instance = new IllustrationPublishDateComparator();
public int Compare(Illustration x, Illustration y)
{
if (x == null || y == null)
{
return 0;
}
return x.PublishDate < y.PublishDate ? 1 : x.PublishDate == y.PublishDate ? 0 : -1;
}
}
}

View File

@ -0,0 +1,60 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Linq;
using Pixeval.Core;
using Pixeval.Objects.Generic;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class RankOptionModel
{
[DoNotNotify]
public static readonly RankOptionModel[] RegularRankOptions = Enum.GetValues(typeof(RankOption)).Cast<RankOption>().Select(rank => new RankOptionModel(rank)).ToArray().Apply(models => models[0].IsSelected = true);
[DoNotNotify]
public static readonly DateTime MaxRankDateTime = DateTime.Today - TimeSpan.FromDays(2);
[DoNotNotify]
public static readonly DateTime InvalidRankDateTimeStart = DateTime.Today - TimeSpan.FromDays(1);
public RankOptionModel(RankOption option)
{
Corresponding = option;
Name = AkaI18N.GetResource(option.GetEnumAttribute<EnumLocalizedName>().Name);
}
public string Name { get; set; }
public bool IsSelected { get; set; }
public RankOption Corresponding { get; set; }
public override string ToString()
{
return Name;
}
}
}

View File

@ -0,0 +1,50 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Pixeval.Core;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class SearchTagMatchOptionModel
{
public static readonly SearchTagMatchOptionModel PartialMatchModel = new SearchTagMatchOptionModel(SearchTagMatchOption.PartialMatchForTags);
public static readonly SearchTagMatchOptionModel ExactMatchModel = new SearchTagMatchOptionModel(SearchTagMatchOption.ExactMatchForTags);
public static readonly SearchTagMatchOptionModel TitleAndCaptionModel = new SearchTagMatchOptionModel(SearchTagMatchOption.TitleAndCaption);
public static readonly IEnumerable<SearchTagMatchOptionModel> AllPossibleMatchOptions = new[] { PartialMatchModel, ExactMatchModel, TitleAndCaptionModel };
public SearchTagMatchOptionModel(SearchTagMatchOption corresponding)
{
Description = AkaI18N.GetResource(corresponding.GetEnumAttribute<EnumLocalizedName>().Name);
Corresponding = corresponding;
}
public string Description { get; set; }
public SearchTagMatchOption Corresponding { get; set; }
}
}

View File

@ -0,0 +1,72 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using Newtonsoft.Json;
using Pixeval.Core;
using Pixeval.Objects.Generic;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class SpotlightArticle
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("pure_title")]
public string PureTitle { get; set; }
[JsonProperty("thumbnail")]
public string Thumbnail { get; set; }
[JsonProperty("article_url")]
public string ArticleUrl { get; set; }
[JsonProperty("publish_date")]
public DateTimeOffset PublishDate { get; set; }
[JsonProperty("category")]
public string Category { get; set; }
[JsonProperty("subcategory_label")]
public string SubcategoryLabel { get; set; }
public async void Download()
{
var result = await Tasks<string, Illustration>.Of(await PixivClient.Instance.GetArticleWorks(Id.ToString())).Mapping(async i =>
{
var res = await PixivHelper.IllustrationInfo(i);
res.SpotlightTitle = Title;
res.FromSpotlight = true;
return res;
}).Construct().WhenAll();
foreach (var illustration in result)
{
DownloadManager.EnqueueDownloadItem(illustration);
}
}
}
}

View File

@ -0,0 +1,34 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class TrendingTag
{
public string Tag { get; set; }
public string TranslatedName { get; set; }
public string Thumbnail { get; set; }
}
}

View File

@ -0,0 +1,67 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class Trends
{
public string PostUserId { get; set; }
public string PostUserName { get; set; }
public string TrendObjectId { get; set; }
public DateTime PostDate { get; set; }
public TrendType Type { get; set; }
public string ByName { get; set; }
public bool IsReferToUser { get; set; }
public string TrendObjName { get; set; }
public string TrendObjectThumbnail { get; set; }
public string PostUserThumbnail { get; set; }
}
public enum TrendType
{
/// <summary>
/// Bookmark
/// </summary>
AddBookmark,
/// <summary>
/// New illust
/// </summary>
AddIllust,
/// <summary>
/// New follow
/// </summary>
AddFavorite
}
}

View File

@ -0,0 +1,46 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using PropertyChanged;
namespace Pixeval.Data.ViewModel
{
[AddINotifyPropertyChangedInterface]
public class User
{
public string Name { get; set; }
public string Id { get; set; }
public bool IsFollowed { get; set; }
public string Avatar { get; set; }
public string Introduction { get; set; }
public string Background { get; set; }
public int Follows { get; set; }
public bool IsPremium { get; set; }
public string[] Thumbnails { get; set; } = new string[3];
}
}

View File

@ -0,0 +1,95 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Persisting;
namespace Pixeval.Data.Web.Delegation
{
public abstract class DnsResolvedHttpClientHandler : HttpClientHandler
{
private readonly bool directConnect;
private readonly IHttpRequestHandler requestHandler;
static DnsResolvedHttpClientHandler()
{
System.AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
}
protected DnsResolvedHttpClientHandler(IHttpRequestHandler requestHandler = null, bool directConnect = true)
{
this.requestHandler = requestHandler;
this.directConnect = directConnect;
ServerCertificateCustomValidationCallback = DangerousAcceptAnyServerCertificateValidator;
}
protected abstract DnsResolver DnsResolver { get; set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
requestHandler?.Handle(request);
if (directConnect)
{
var host = request.RequestUri.DnsSafeHost;
var isSslSession = request.RequestUri.ToString().StartsWith("https://");
request.RequestUri = new Uri($"{(isSslSession ? "https://" : "http://")}{DnsResolver.Lookup()[0]}{request.RequestUri.PathAndQuery}");
request.Headers.Host = host;
}
HttpResponseMessage result;
try
{
result = await base.SendAsync(request, cancellationToken);
}
catch (HttpRequestException e)
{
if (e.InnerException != null && e.InnerException.Message.ToLower().Contains("winhttp"))
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
throw;
}
if (result.StatusCode == HttpStatusCode.BadRequest && (await result.Content.ReadAsStringAsync()).Contains("OAuth"))
{
using var semaphore = new SemaphoreSlim(1);
await semaphore.WaitAsync(cancellationToken);
await Authentication.AppApiAuthenticate(Session.Current.Account, Session.Current.Password);
var token = request.Headers.Authorization;
if (token != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, Session.Current.AccessToken);
}
return await base.SendAsync(request, cancellationToken);
}
return result;
}
}
}

View File

@ -0,0 +1,138 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using Pixeval.Data.Web.Protocol;
using Pixeval.Data.Web.Request;
using Pixeval.Data.Web.Response;
using Refit;
namespace Pixeval.Data.Web.Delegation
{
public abstract class DnsResolver
{
public static readonly ThreadLocal<Dictionary<string, List<IPAddress>>> DnsCache = new ThreadLocal<Dictionary<string, List<IPAddress>>>(() => new Dictionary<string, List<IPAddress>>());
protected async Task<DnsResolveResponse> GetDnsJson(string hostname)
{
return await RestService.For<IResolveDnsProtocol>(new HttpClient(new HttpClientHandler { SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls }) { BaseAddress = new Uri(ProtocolBase.DnsServer), Timeout = TimeSpan.FromSeconds(5) }).ResolveDns(new DnsResolveRequest
{
Ct = "application/dns-json",
Cd = "false",
Do = "false",
Name = hostname,
Type = "A"
});
}
/*public async Task<IReadOnlyList<IPAddress>> Lookup(string hostname)
{
const string OAuthUrl = "oauth.secure.pixiv.net";
if (hostname == OAuthUrl)
{
CacheDns(hostname, UseDefaultDns());
return DnsCache.Value[hostname];
}
if (DnsCache.Value.ContainsKey(hostname)) return DnsCache.Value[hostname].ToImmutableList();
var ipList = new HashSet<IPAddress>(new IpAddressEqualityComparer());
if (_dnsQueryFailed)
{
CacheDns(hostname, UseDefaultDns());
return DnsCache.Value[hostname];
}
DnsResolveResponse response;
try
{
response = await GetDnsJson(hostname);
}
catch (Exception)
{
_dnsQueryFailed = true;
CacheDns(hostname, UseDefaultDns());
return DnsCache.Value[hostname];
}
if (response != null)
{
var answer = response.Answers;
if (!answer.IsNullOrEmpty())
{
foreach (var queriedIp in answer)
if (IPAddress.TryParse(queriedIp.Data, out var address))
ipList.Add(address);
}
else
{
ipList.AddRange(await Dns.GetHostAddressesAsync(hostname));
if (ipList.IsNullOrEmpty()) ipList.AddRange(UseDefaultDns());
}
CacheDns(hostname, ipList);
return ipList.ToImmutableList();
}
ipList.AddRange(UseDefaultDns());
CacheDns(hostname, ipList);
return ipList.ToImmutableList();
}*/
public IReadOnlyList<IPAddress> Lookup()
{
return UseDefaultDns().ToImmutableList();
}
// private static void CacheDns(string hostname, IEnumerable<IPAddress> ipList)
// {
// if (DnsCache.Value.ContainsKey(hostname))
// DnsCache.Value[hostname].AddRange(ipList);
// else
// DnsCache.Value[hostname] = new List<IPAddress>(ipList);
// }
protected abstract IEnumerable<IPAddress> UseDefaultDns();
private class IpAddressEqualityComparer : IEqualityComparer<IPAddress>
{
public bool Equals(IPAddress x, IPAddress y)
{
if (x == null || y == null)
{
return false;
}
return x.ToString() == y.ToString();
}
public int GetHashCode(IPAddress obj)
{
return obj.GetHashCode();
}
}
}
}

View File

@ -0,0 +1,72 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Pixeval.Data.Web.Protocol;
using Pixeval.Objects.Generic;
using Pixeval.Persisting;
using Refit;
namespace Pixeval.Data.Web.Delegation
{
public class HttpClientFactory
{
public static HttpClient AppApiHttpClient()
{
return PixivApi(ProtocolBase.AppApiBaseUrl, Settings.Global.DirectConnect).Apply(h => h.DefaultRequestHeaders.Add("Authorization", "Bearer"));
}
public static HttpClient WebApiHttpClient()
{
return PixivApi(ProtocolBase.WebApiBaseUrl, Settings.Global.DirectConnect);
}
public static IAppApiProtocol AppApiService()
{
return RestService.For<IAppApiProtocol>(PixivApi(ProtocolBase.AppApiBaseUrl, Settings.Global.DirectConnect));
}
public static IWebApiProtocol WebApiService()
{
return RestService.For<IWebApiProtocol>(PixivApi(ProtocolBase.WebApiBaseUrl, Settings.Global.DirectConnect));
}
public static HttpClient PixivApi(string baseAddress, bool directConnect)
{
return new HttpClient(PixivApiHttpClientHandler.Instance(directConnect)) { BaseAddress = new Uri(baseAddress) };
}
public static HttpClient PixivImage()
{
return new HttpClient(PixivImageHttpClientHandler.Instance).Apply(client =>
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Referer", "http://www.pixiv.net");
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "PixivIOSApp/5.8.7");
});
}
public static Task<HttpResponseMessage> GetResponseHeader(HttpClient client, string uri)
{
return client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
}
}
}

View File

@ -0,0 +1,29 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Net.Http;
namespace Pixeval.Data.Web.Delegation
{
public interface IHttpRequestHandler
{
void Handle(HttpRequestMessage httpRequestMessage);
}
}

View File

@ -0,0 +1,37 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using System.Net;
namespace Pixeval.Data.Web.Delegation
{
public class PixivApiDnsResolver : DnsResolver
{
public static readonly DnsResolver Instance = new PixivApiDnsResolver();
protected override IEnumerable<IPAddress> UseDefaultDns()
{
yield return IPAddress.Parse("210.140.131.219");
yield return IPAddress.Parse("210.140.131.223");
yield return IPAddress.Parse("210.140.131.226");
}
}
}

View File

@ -0,0 +1,38 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Net.Http;
namespace Pixeval.Data.Web.Delegation
{
public class PixivApiHttpClientHandler : DnsResolvedHttpClientHandler
{
private PixivApiHttpClientHandler(bool directConnect) : base(PixivHttpRequestHandler.Instance, directConnect)
{
}
protected override DnsResolver DnsResolver { get; set; } = PixivApiDnsResolver.Instance;
public static HttpMessageHandler Instance(bool directConnect)
{
return new PixivApiHttpClientHandler(directConnect);
}
}
}

View File

@ -0,0 +1,72 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.I18n;
using Pixeval.Objects.Primitive;
using Pixeval.Persisting;
namespace Pixeval.Data.Web.Delegation
{
public class PixivHttpRequestHandler : IHttpRequestHandler
{
public static readonly PixivHttpRequestHandler Instance = new PixivHttpRequestHandler();
protected PixivHttpRequestHandler()
{
}
public virtual void Handle(HttpRequestMessage httpRequestMessage)
{
switch (httpRequestMessage.RequestUri.DnsSafeHost)
{
case "app-api.pixiv.net":
var token = httpRequestMessage.Headers.Authorization;
if (token != null)
{
if (Session.Current.AccessToken.IsNullOrEmpty())
{
throw new TokenNotFoundException($"{nameof(Session.Current.AccessToken)} is empty, this exception should never be thrown, if you see this message, please send issue on github or contact me (decem0730@gmail.com)");
}
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(token.Scheme, Session.Current.AccessToken);
}
break;
case var x when x == "pixiv.net" || x == "www.pixiv.net":
httpRequestMessage.Headers.TryAddWithoutValidation("Cookie", Settings.Global.Cookie);
break;
}
if (httpRequestMessage.RequestUri.DnsSafeHost == "i.pximg.net" && !Settings.Global.MirrorServer.IsNullOrEmpty())
{
httpRequestMessage.RequestUri = new Uri(httpRequestMessage.RequestUri.ToString().Replace("i.pximg.net", Settings.Global.MirrorServer));
}
if (!httpRequestMessage.Headers.Contains("Accept-Language"))
{
httpRequestMessage.Headers.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage());
}
}
}
}

View File

@ -0,0 +1,35 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Net.Http;
namespace Pixeval.Data.Web.Delegation
{
public class PixivImageHttpClientHandler : DnsResolvedHttpClientHandler
{
public static readonly HttpMessageHandler Instance = new PixivImageHttpClientHandler();
private PixivImageHttpClientHandler() : base(PixivHttpRequestHandler.Instance, false)
{
}
protected override DnsResolver DnsResolver { get; set; } = null;
}
}

View File

@ -0,0 +1,47 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
namespace Pixeval.Data.Web
{
public class HttpResponse<T> : Tuple<bool, T>
{
private HttpResponse(bool status, T response) : base(status, response)
{
}
public static HttpResponse<T> Wrap(bool status)
{
return new HttpResponse<T>(status, default);
}
public static HttpResponse<T> Wrap(bool status, T response)
{
return new HttpResponse<T>(status, response);
}
public void Deconstruct(out bool status, out T response)
{
status = Item1;
response = Item2;
}
}
}

View File

@ -0,0 +1,64 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Threading.Tasks;
using Pixeval.Data.Web.Request;
using Pixeval.Data.Web.Response;
using Refit;
namespace Pixeval.Data.Web.Protocol
{
[Headers("Authorization: Bearer")]
public interface IAppApiProtocol
{
[Post("/v1/illust/bookmark/delete")]
Task DeleteBookmark([Body(BodySerializationMethod.UrlEncoded)] DeleteBookmarkRequest deleteBookmarkRequest);
[Get("/v1/user/detail")]
Task<UserInformationResponse> GetUserInformation(UserInformationRequest userInformationRequest);
[Get("/v1/spotlight/articles?category=all")]
Task<SpotlightResponse> GetSpotlights(int offset);
[Post("/v1/user/follow/add")]
Task FollowArtist([Body(BodySerializationMethod.UrlEncoded)] FollowArtistRequest followArtistRequest);
[Post("/v1/user/follow/delete")]
Task UnFollowArtist([Body(BodySerializationMethod.UrlEncoded)] UnFollowArtistRequest unFollowArtistRequest);
[Get("/v1/ugoira/metadata")]
Task<UgoiraMetadataResponse> GetUgoiraMetadata([AliasAs("illust_id")] string id);
[Post("/v2/illust/bookmark/add")]
Task AddBookmark([Body(BodySerializationMethod.UrlEncoded)] AddBookmarkRequest addBookmarkRequest);
[Get("/v2/search/autocomplete")]
Task<AutoCompletionResponse> GetAutoCompletion(AutoCompletionRequest autoCompletionRequest);
[Get("/v1/illust/detail")]
Task<SingleWorkResponse> GetSingle([AliasAs("illust_id")] string id);
[Get("/v1/user/recommended?filter=for_android")]
Task<RecommendIllustratorResponse> GetRecommendIllustrators(RecommendIllustratorRequest recommendIllustratorRequest);
[Get("/v1/trending-tags/illust?filter=for_android")]
Task<TrendingTagResponse> GetTrendingTags();
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Threading.Tasks;
using Pixeval.Data.Web.Request;
using Pixeval.Data.Web.Response;
using Refit;
namespace Pixeval.Data.Web.Protocol
{
public interface IResolveDnsProtocol
{
[Get("/dns-query")]
Task<DnsResolveResponse> ResolveDns(DnsResolveRequest dnsResolverRequest);
}
}

View File

@ -0,0 +1,34 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Net.Http;
using System.Threading.Tasks;
using Refit;
namespace Pixeval.Data.Web.Protocol
{
// ReSharper disable once InconsistentNaming
public interface ISauceNAOProtocol
{
[Multipart]
[Post("/search.php")]
Task<HttpResponseMessage> GetSauce([AliasAs("file")] StreamPart stream);
}
}

View File

@ -0,0 +1,37 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Threading.Tasks;
using Pixeval.Data.Web.Request;
using Pixeval.Data.Web.Response;
using Refit;
namespace Pixeval.Data.Web.Protocol
{
[Headers("User-Agent: PixivAndroidApp/5.0.64 (Android 6.0)", "Content-Type: application/x-www-form-urlencoded")]
public interface ITokenProtocol
{
[Post("/auth/token")]
Task<TokenResponse> GetTokenByPassword([Body(BodySerializationMethod.UrlEncoded)] PasswordTokenRequest body, [Header("X-Client-Time")] string clientTime, [Header("X-Client-Hash")] string clientHash);
[Post("/auth/token")]
Task<TokenResponse> RefreshToken([Body(BodySerializationMethod.UrlEncoded)] RefreshTokenRequest body);
}
}

View File

@ -0,0 +1,41 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Net.Http;
using System.Threading.Tasks;
using Pixeval.Data.Web.Request;
using Pixeval.Data.Web.Response;
using Refit;
namespace Pixeval.Data.Web.Protocol
{
[Headers("User-Agent: PixivAndroidApp/5.0.64 (Android 6.0)", "Content-Type: application/x-www-form-urlencoded")]
public interface IWebApiProtocol
{
[Post("/setting_user.php")]
Task<HttpResponseMessage> ToggleR18State([Body(BodySerializationMethod.UrlEncoded)] ToggleR18StateRequest toggleR18StateRequest);
[Get("/ajax/showcase/article")]
Task<SpotlightArticleResponse> GetSpotlightArticles([AliasAs("article_id")] string articleId);
[Get("/touch/ajax/user/details")]
Task<WebApiUserDetailResponse> GetWebApiUserDetail(string id);
}
}

View File

@ -0,0 +1,37 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Data.Web
{
public class ProtocolBase
{
public const string PublicApiBaseUrl = "https://public-api.secure.pixiv.net/v1";
public const string AppApiBaseUrl = "https://app-api.pixiv.net";
public const string DnsServer = "https://1.0.0.1";
public const string SauceNaoUrl = "https://saucenao.com/";
public const string OAuthBaseUrl = "https://oauth.secure.pixiv.net";
public const string WebApiBaseUrl = "https://www.pixiv.net";
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class AddBookmarkRequest
{
[AliasAs("restrict")]
public string Restrict { get; set; } = "public";
[AliasAs("illust_id")]
public string Id { get; set; }
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class AutoCompletionRequest
{
[AliasAs("merge_plain_keyword_results=true")]
public bool MergePlainKeywordResult { get; set; } = true;
[AliasAs("word")]
public string Word { get; set; }
}
}

View File

@ -0,0 +1,30 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class DeleteBookmarkRequest
{
[AliasAs("illust_id")]
public string IllustId { get; set; }
}
}

View File

@ -0,0 +1,42 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class DnsResolveRequest
{
[AliasAs("ct")]
public string Ct { get; set; }
[AliasAs("name")]
public string Name { get; set; }
[AliasAs("type")]
public string Type { get; set; }
[AliasAs("do")]
public string Do { get; set; }
[AliasAs("cd")]
public string Cd { get; set; }
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class FollowArtistRequest
{
[AliasAs("user_id")]
public string Id { get; set; }
[AliasAs("restrict")]
public string Restrict { get; set; } = "public";
}
}

View File

@ -0,0 +1,45 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class PasswordTokenRequest
{
[AliasAs("username")]
public string Name { get; set; }
[AliasAs("password")]
public string Password { get; set; }
[AliasAs("grant_type")]
public string GrantType => "password";
[AliasAs("client_id")]
public string ClientId => "MOBrBDS8blbauoSck0ZfDbtuzpyT";
[AliasAs("client_secret")]
public string ClientSecret => "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
[AliasAs("get_secure_url")]
public string GetSecureUrl => "1";
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class RecommendIllustratorRequest
{
[AliasAs("filter")]
public string Filter { get; } = "for_android";
[AliasAs("offset")]
public int Offset { get; set; } = 0;
}
}

View File

@ -0,0 +1,42 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class RefreshTokenRequest
{
[AliasAs("refresh_token")]
public string RefreshToken { get; set; }
[AliasAs("grant_type")]
public string GrantType => "refresh_token";
[AliasAs("client_id")]
public string ClientId => "MOBrBDS8blbauoSck0ZfDbtuzpyT";
[AliasAs("client_secret")]
public string ClientSecret => "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
[AliasAs("get_secure_url")]
public string GetSecureUrl => "1";
}
}

View File

@ -0,0 +1,45 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class ToggleR18StateRequest
{
[AliasAs("mode")]
public string Mode { get; } = "mod";
[AliasAs("user_language")]
public string UserLang { get; } = "zh";
[AliasAs("r18")]
public string R18 { get; set; }
[AliasAs("r18g")]
public string R18G { get; set; }
[AliasAs("submit")]
public string Submit { get; } = "保存";
[AliasAs("tt")]
public string Tt { get; set; }
}
}

View File

@ -0,0 +1,30 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class UnFollowArtistRequest
{
[AliasAs("user_id")]
public string UserId { get; set; }
}
}

View File

@ -0,0 +1,33 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Refit;
namespace Pixeval.Data.Web.Request
{
public class UserInformationRequest
{
[AliasAs("user_id")]
public string Id { get; set; }
[AliasAs("filter")]
public string Filter { get; set; } = "for_android";
}
}

View File

@ -0,0 +1,40 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class AutoCompletionResponse
{
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
}
}

View File

@ -0,0 +1,76 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class DnsResolveResponse
{
[JsonProperty("Status")]
public long Status { get; set; }
[JsonProperty("TC")]
public bool Tc { get; set; }
[JsonProperty("RD")]
public bool Rd { get; set; }
[JsonProperty("RA")]
public bool Ra { get; set; }
[JsonProperty("AD")]
public bool Ad { get; set; }
[JsonProperty("CD")]
public bool Cd { get; set; }
[JsonProperty("Question")]
public List<Question> Questions { get; set; }
[JsonProperty("Answer")]
public List<Answer> Answers { get; set; }
public class Answer
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public long Type { get; set; }
[JsonProperty("TTL")]
public long Ttl { get; set; }
[JsonProperty("data")]
public string Data { get; set; }
}
public class Question
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public long Type { get; set; }
}
}
}

View File

@ -0,0 +1,263 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class FollowingResponse
{
[JsonProperty("user_previews")]
public List<UserPreview> UserPreviews { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class UserPreview
{
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("novels")]
public List<Novel> Novels { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
}
public class Illust
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<IllustTag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("series")]
public Series Series { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public Uri Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public Uri OriginalImageUrl { get; set; }
}
public class Series
{
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public long? Id { get; set; }
[JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
public string Title { get; set; }
}
public class IllustTag
{
[JsonProperty("name")]
public string Name { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
public class Novel
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("is_original")]
public bool IsOriginal { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("tags")]
public List<NovelTag> Tags { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("text_length")]
public long TextLength { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("series")]
public Series Series { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("total_comments")]
public long TotalComments { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
[JsonProperty("is_mypixiv_only")]
public bool IsMypixivOnly { get; set; }
[JsonProperty("is_x_restricted")]
public bool IsXRestricted { get; set; }
}
public class NovelTag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("added_by_uploaded_user")]
public bool AddedByUploadedUser { get; set; }
}
}
}

View File

@ -0,0 +1,207 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class GalleryResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsManga = PageCount != 1,
IsUgoira = Type == "ugoira",
Origin = MetaSinglePage.OriginalImageUrl,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium.IsNullOrEmpty() ? ImageUrls.SquareMedium : ImageUrls.Medium,
Title = Title,
UserId = User.Id.ToString(),
UserName = User.Name,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,206 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
namespace Pixeval.Data.Web.Response
{
public class QueryWorksResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsUgoira = Type == "ugoira",
IsManga = !MetaPages.IsNullOrEmpty(),
Origin = MetaSinglePage.OriginalImageUrl ?? ImageUrls.Large,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium ?? ImageUrls.SquareMedium,
Title = Title,
UserId = User.Id.ToString(),
UserName = User.Name,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium ?? p.ImageUrls.SquareMedium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,207 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class RankingResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsManga = PageCount != 1,
IsUgoira = Type == "ugoira",
Origin = MetaSinglePage.OriginalImageUrl,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium.IsNullOrEmpty() ? ImageUrls.SquareMedium : ImageUrls.Medium,
Title = Title,
UserId = User.Id.ToString(),
UserName = User.Name,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,185 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class RecommendIllustratorResponse
{
[JsonProperty("user_previews")]
public UserPreview[] UserPreviews { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class UserPreview : IParser<ViewModel.User>
{
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("illusts")]
public Illust[] Illusts { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public ViewModel.User Parse()
{
return new ViewModel.User
{
Avatar = Regex.Replace(User.ProfileImageUrls.Medium, "_170\\.", "_50."),
Id = User.Id.ToString(),
Name = User.Name,
Thumbnails = Illusts.Select(i => i.ImageUrls.SquareMedium).ToArray()
};
}
}
public class Illust
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public Tag[] Tags { get; set; }
[JsonProperty("tools")]
public string[] Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public MetaPage[] MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,210 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class RecommendResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("contest_exists")]
public bool ContestExists { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsManga = PageCount != 1,
IsUgoira = Type == "ugoira",
Origin = MetaSinglePage.OriginalImageUrl.IsNullOrEmpty() ? MetaPages[0].ImageUrls.Original : MetaSinglePage.OriginalImageUrl,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium,
Title = Title,
UserId = User.Id.ToString(),
UserName = User.Name,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,164 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class SingleWorkResponse
{
[JsonProperty("illust")]
public Illust IllustInfo { get; set; }
public class Illust
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
[JsonProperty("total_comments")]
public long TotalComments { get; set; }
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url")]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,49 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class SpotlightArticleResponse
{
[JsonProperty("body")]
public List<Body> BodyList { get; set; }
public class Body
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
}
public class Illust
{
[JsonProperty("illust_id")]
public long IllustId { get; set; }
}
}
}

View File

@ -0,0 +1,35 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
namespace Pixeval.Data.Web.Response
{
public class SpotlightResponse
{
[JsonProperty("spotlight_articles")]
public List<SpotlightArticle> SpotlightArticles { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
}
}

View File

@ -0,0 +1,136 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using Newtonsoft.Json;
using Pixeval.Objects.Exceptions;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class TokenResponse
{
[JsonProperty("response")]
public Response ToResponse { get; set; }
public override string ToString()
{
return this.ToJson();
}
public class Response
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public long ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("device_token")]
public string DeviceToken { get; set; }
}
public class User
{
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("id")]
[JsonConverter(typeof(ParseStringConverter))]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("mail_address")]
public string MailAddress { get; set; }
[JsonProperty("is_premium")]
public bool IsPremium { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("is_mail_authorized")]
public bool IsMailAuthorized { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("px_16x16")]
public string Px16X16 { get; set; }
[JsonProperty("px_50x50")]
public string Px50X50 { get; set; }
[JsonProperty("px_170x170")]
public string Px170X170 { get; set; }
}
private class ParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t)
{
return t == typeof(long) || t == typeof(long?);
}
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<string>(reader);
if (long.TryParse(value, out var l))
{
return l;
}
throw new TypeMismatchException("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (long) untypedValue;
serializer.Serialize(writer, value.ToString());
}
}
}
}

View File

@ -0,0 +1,173 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class TrendingTagResponse
{
[JsonProperty("trend_tags")]
public List<TrendTag> TrendTags { get; set; }
public class TrendTag
{
[JsonProperty("tag")]
public string TagStr { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
[JsonProperty("illust")]
public Illust Illust { get; set; }
}
public class Illust
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,55 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class UgoiraMetadataResponse
{
[JsonProperty("ugoira_metadata")]
public UgoiraMetadata UgoiraMetadataInfo { get; set; }
public class UgoiraMetadata
{
[JsonProperty("zip_urls")]
public ZipUrls ZipUrls { get; set; }
[JsonProperty("frames")]
public List<Frame> Frames { get; set; }
}
public class Frame
{
[JsonProperty("file")]
public string File { get; set; }
[JsonProperty("delay")]
public long Delay { get; set; }
}
public class ZipUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,209 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
namespace Pixeval.Data.Web.Response
{
public class UploadResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
[JsonProperty("total_comments")]
public long TotalComments { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsUgoira = Type == "ugoira",
IsManga = !MetaPages.IsNullOrEmpty(),
Origin = MetaSinglePage.OriginalImageUrl ?? ImageUrls.Large,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium ?? ImageUrls.SquareMedium,
Title = Title,
UserId = User.Id.ToString(),
UserName = User.Name,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium ?? p.ImageUrls.SquareMedium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,198 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class UserInformationResponse
{
[JsonProperty("user")]
public User UserEntity { get; set; }
[JsonProperty("profile")]
public Profile UserProfile { get; set; }
[JsonProperty("profile_publicity")]
public ProfilePublicity UserProfilePublicity { get; set; }
[JsonProperty("workspace")]
public Workspace UserWorkspace { get; set; }
public class Profile
{
[JsonProperty("webpage")]
public string Webpage { get; set; }
[JsonProperty("gender")]
public string Gender { get; set; }
[JsonProperty("birth")]
public string Birth { get; set; }
[JsonProperty("birth_day")]
public string BirthDay { get; set; }
[JsonProperty("birth_year")]
public long BirthYear { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("address_id")]
public long AddressId { get; set; }
[JsonProperty("country_code")]
public string CountryCode { get; set; }
[JsonProperty("job")]
public string Job { get; set; }
[JsonProperty("job_id")]
public long JobId { get; set; }
[JsonProperty("total_follow_users")]
public long TotalFollowUsers { get; set; }
[JsonProperty("total_mypixiv_users")]
public long TotalMypixivUsers { get; set; }
[JsonProperty("total_illusts")]
public long TotalIllusts { get; set; }
[JsonProperty("total_manga")]
public long TotalManga { get; set; }
[JsonProperty("total_novels")]
public long TotalNovels { get; set; }
[JsonProperty("total_illust_bookmarks_public")]
public long TotalIllustBookmarksPublic { get; set; }
[JsonProperty("total_illust_series")]
public long TotalIllustSeries { get; set; }
[JsonProperty("total_novel_series")]
public long TotalNovelSeries { get; set; }
[JsonProperty("background_image_url")]
public string BackgroundImageUrl { get; set; }
[JsonProperty("twitter_account")]
public string TwitterAccount { get; set; }
[JsonProperty("twitter_url")]
public string TwitterUrl { get; set; }
[JsonProperty("is_premium")]
public bool IsPremium { get; set; }
[JsonProperty("is_using_custom_profile_image")]
public bool IsUsingCustomProfileImage { get; set; }
}
public class ProfilePublicity
{
[JsonProperty("gender")]
public string Gender { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("birth_day")]
public string BirthDay { get; set; }
[JsonProperty("birth_year")]
public string BirthYear { get; set; }
[JsonProperty("job")]
public string Job { get; set; }
[JsonProperty("pawoo")]
public bool Pawoo { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("comment")]
public string Comment { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
public class Workspace
{
[JsonProperty("pc")]
public string Pc { get; set; }
[JsonProperty("monitor")]
public string Monitor { get; set; }
[JsonProperty("tool")]
public string Tool { get; set; }
[JsonProperty("scanner")]
public string Scanner { get; set; }
[JsonProperty("tablet")]
public string Tablet { get; set; }
[JsonProperty("mouse")]
public string Mouse { get; set; }
[JsonProperty("printer")]
public string Printer { get; set; }
[JsonProperty("desktop")]
public string Desktop { get; set; }
[JsonProperty("music")]
public string Music { get; set; }
[JsonProperty("desk")]
public string Desk { get; set; }
[JsonProperty("chair")]
public string Chair { get; set; }
[JsonProperty("comment")]
public string Comment { get; set; }
}
}
}

View File

@ -0,0 +1,106 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System.Collections.Generic;
using Newtonsoft.Json;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class UserNavResponse
{
[JsonProperty("user_previews")]
public List<UserPreview> UserPreviews { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public override string ToString()
{
return this.ToJson();
}
public class UserPreview
{
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
}
public class Illust
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrl { get; set; }
[JsonProperty("tools")]
public string[] Tools { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,207 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Pixeval.Data.ViewModel;
using Pixeval.Objects.Generic;
using Pixeval.Objects.Primitive;
namespace Pixeval.Data.Web.Response
{
public class UserUpdateResponse
{
[JsonProperty("illusts")]
public List<Illust> Illusts { get; set; }
[JsonProperty("next_url")]
public string NextUrl { get; set; }
public class Illust : IParser<Illustration>
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("restrict")]
public long Restrict { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("tags")]
public List<Tag> Tags { get; set; }
[JsonProperty("tools")]
public List<string> Tools { get; set; }
[JsonProperty("create_date")]
public DateTimeOffset CreateDate { get; set; }
[JsonProperty("page_count")]
public long PageCount { get; set; }
[JsonProperty("width")]
public long Width { get; set; }
[JsonProperty("height")]
public long Height { get; set; }
[JsonProperty("sanity_level")]
public long SanityLevel { get; set; }
[JsonProperty("x_restrict")]
public long XRestrict { get; set; }
[JsonProperty("meta_single_page")]
public MetaSinglePage MetaSinglePage { get; set; }
[JsonProperty("meta_pages")]
public List<MetaPage> MetaPages { get; set; }
[JsonProperty("total_view")]
public long TotalView { get; set; }
[JsonProperty("total_bookmarks")]
public long TotalBookmarks { get; set; }
[JsonProperty("is_bookmarked")]
public bool IsBookmarked { get; set; }
[JsonProperty("visible")]
public bool Visible { get; set; }
[JsonProperty("is_muted")]
public bool IsMuted { get; set; }
public Illustration Parse()
{
return new Illustration
{
Bookmark = (int) TotalBookmarks,
Id = Id.ToString(),
IsLiked = IsBookmarked,
IsManga = PageCount != 1,
IsUgoira = Type == "ugoira",
Origin = MetaSinglePage.OriginalImageUrl.IsNullOrEmpty() ? ImageUrls.Large : MetaSinglePage.OriginalImageUrl,
Large = ImageUrls.Large,
Tags = Tags.Select(t => new ViewModel.Tag { Name = t.Name, TranslatedName = t.TranslatedName }),
Thumbnail = ImageUrls.Medium,
UserId = User.Id.ToString(),
UserName = User.Name,
Title = Title,
Resolution = $"{Width}x{Height}",
ViewCount = (int) TotalView,
PublishDate = CreateDate
}.Apply(i =>
{
if (i.IsManga)
{
i.MangaMetadata = MetaPages.Select(p =>
{
var page = (Illustration) i.Clone();
page.Thumbnail = p.ImageUrls.Medium;
page.Origin = p.ImageUrls.Original;
page.Large = p.ImageUrls.Large;
return page;
}).ToArray();
foreach (var illustration in i.MangaMetadata)
{
illustration.MangaMetadata = i.MangaMetadata;
}
}
});
}
}
public class ImageUrls
{
[JsonProperty("square_medium")]
public string SquareMedium { get; set; }
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
public string Original { get; set; }
}
public class MetaPage
{
[JsonProperty("image_urls")]
public ImageUrls ImageUrls { get; set; }
}
public class MetaSinglePage
{
[JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
public string OriginalImageUrl { get; set; }
}
public class Tag
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("translated_name")]
public string TranslatedName { get; set; }
}
public class User
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
[JsonProperty("profile_image_urls")]
public ProfileImageUrls ProfileImageUrls { get; set; }
[JsonProperty("is_followed")]
public bool IsFollowed { get; set; }
}
public class ProfileImageUrls
{
[JsonProperty("medium")]
public string Medium { get; set; }
}
}
}

View File

@ -0,0 +1,54 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using Newtonsoft.Json;
namespace Pixeval.Data.Web.Response
{
public class WebApiUserDetailResponse
{
[JsonProperty("body")]
public Body ResponseBody { get; set; }
public class Body
{
[JsonProperty("user_details")]
public UserDetails UserDetails { get; set; }
}
public class UserDetails
{
[JsonProperty("cover_image")]
public CoverImage CoverImage { get; set; }
}
public class CoverImage
{
[JsonProperty("profile_cover_image")]
public ProfileCoverImage ProfileCoverImage { get; set; }
}
public class ProfileCoverImage
{
[JsonProperty("720x360")]
public string The720X360 { get; set; }
}
}
}

3
Pixeval/FodyWeavers.xml Normal file
View File

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

64
Pixeval/FodyWeavers.xsd Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEquality" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,27 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
namespace Pixeval.Objects.Caching
{
public enum CachingPolicy
{
Memory, File
}
}

View File

@ -0,0 +1,117 @@
#region Copyright (C) 2019-2020 Dylech30th. All rights reserved.
// Pixeval - A Strong, Fast and Flexible Pixiv Client
// Copyright (C) 2019-2020 Dylech30th
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Pixeval.Objects.Caching
{
public class FileCache<T, THash> : IWeakCacheProvider<T, THash>, IEnumerable<KeyValuePair<THash, string>> where T : class
{
private readonly Func<T, Stream> cachingPolicy;
private readonly ConcurrentDictionary<THash, string> fileMapping = new ConcurrentDictionary<THash, string>();
private readonly string initDirectory;
private readonly Func<Stream, T> restorePolicy;
public FileCache(string initDirectory, Func<T, Stream> cachingPolicy, Func<Stream, T> restorePolicy)
{
this.cachingPolicy = cachingPolicy;
this.restorePolicy = restorePolicy;
this.initDirectory = initDirectory;
Directory.CreateDirectory(initDirectory);
}
public IEnumerator<KeyValuePair<THash, string>> GetEnumerator()
{
return fileMapping.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Attach(ref T key, THash associateWith)
{
if (associateWith == null || key == null)
{
return;
}
var path = Path.Combine(initDirectory, IWeakCacheProvider<T, THash>.HashKey(associateWith) + ".tmp");
if (!File.Exists(path))
{
var s = cachingPolicy(key);
key = null;
Task.Run(() =>
{
fileMapping.TryAdd(associateWith, path);
WriteFile(path, s);
});
}
}
public void Detach(THash associateWith)
{
using var sem = new SemaphoreSlim(1);
var path = Path.Combine(initDirectory, IWeakCacheProvider<T, THash>.HashKey(associateWith) + ".");
if (File.Exists(path))
{
fileMapping.TryRemove(associateWith, out _);
File.Delete(path);
}
}
public async Task<(bool, T)> TryGet([NotNull] THash key)
{
if (fileMapping.TryGetValue(key, out var file) && File.Exists(file))
{
await using var fileStream = File.OpenRead(file);
fileStream.Position = 0L;
await using Stream memoStream = new MemoryStream();
await fileStream.CopyToAsync(memoStream);
return (true, restorePolicy(memoStream));
}
return (false, null);
}
public void Clear()
{
using var sem = new SemaphoreSlim(1);
foreach (var file in Directory.GetFiles(initDirectory))
{
File.Delete(file);
}
}
private static async void WriteFile(string path, Stream src)
{
await using var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
src.Position = 0L;
await src.CopyToAsync(fileStream);
}
}
}

Some files were not shown because too many files have changed in this diff Show More