mirror of
https://github.com/Pixeval/Pixeval.git
synced 2025-01-08 11:47:34 +08:00
fix
This commit is contained in:
parent
4a349f82e5
commit
3995577133
4
.idea/.idea.Pixeval/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.Pixeval/.idea/encodings.xml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
8
.idea/.idea.Pixeval/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.Pixeval/.idea/indexLayout.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ContentModelUserStore">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
8
.idea/.idea.Pixeval/.idea/modules.xml
generated
Normal file
8
.idea/.idea.Pixeval/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.Pixeval/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.Pixeval/riderModule.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/.idea.Pixeval/.idea/projectSettingsUpdater.xml
generated
Normal file
6
.idea/.idea.Pixeval/.idea/projectSettingsUpdater.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RiderProjectSettingsUpdater">
|
||||
<option name="vcsConfiguration" value="1" />
|
||||
</component>
|
||||
</project>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
36
.idea/.idea.Pixeval/riderModule.iml
generated
Normal file
36
.idea/.idea.Pixeval/riderModule.iml
generated
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="RIDER_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/../.." />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/README.txt" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/cef.pak" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/cef_100_percent.pak" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/cef_200_percent.pak" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/cef_extensions.pak" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/chrome_elf.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/d3dcompiler_47.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/devtools_resources.pak" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/icudtl.dat" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/libEGL.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/libGLESv2.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/libcef.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/locales" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/snapshot_blob.bin" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/swiftshader" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cef.redist.x64/83.4.2/CEF/v8_context_snapshot.bin" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.BrowserSubprocess.Core.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.BrowserSubprocess.Core.pdb" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.BrowserSubprocess.exe" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.BrowserSubprocess.pdb" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.Core.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.Core.pdb" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.Core.xml" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.XML" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.common/83.4.20/CefSharp/x64/CefSharp.pdb" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.wpf/83.4.20/CefSharp/x64/CefSharp.Wpf.XML" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.wpf/83.4.20/CefSharp/x64/CefSharp.Wpf.dll" />
|
||||
<content url="file://$USER_HOME$/.nuget/packages/cefsharp.wpf/83.4.20/CefSharp/x64/CefSharp.Wpf.pdb" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -1,9 +1,17 @@
|
||||
<Window x:Class="Pixeval.Updater.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
<Window x:Class="Pixeval.Updater.MainWindow"
|
||||
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" Title="MainWindow" Width="600"
|
||||
Height="100" AllowsTransparency="True" Background="Transparent"
|
||||
Icon="Resources/pxlogo.ico" Loaded="MainWindow_OnLoaded" MouseDown="MainWindow_OnMouseDown"
|
||||
ResizeMode="NoResize" WindowStyle="None">
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
Title="MainWindow"
|
||||
Width="600"
|
||||
Height="100"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
Icon="Resources/pxlogo.ico"
|
||||
Loaded="MainWindow_OnLoaded"
|
||||
MouseDown="MainWindow_OnMouseDown"
|
||||
ResizeMode="NoResize"
|
||||
WindowStyle="None">
|
||||
<Window.Resources>
|
||||
<Style x:Key="RoundedCornerProgressBar" TargetType="{x:Type ProgressBar}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueDarkBrush}" />
|
||||
@ -34,7 +42,8 @@
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="OnLoadedNoAnimation">
|
||||
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
|
||||
Storyboard.TargetProperty="(UIElement.Opacity)" To="1"
|
||||
Storyboard.TargetProperty="(UIElement.Opacity)"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="TemplateRoot"
|
||||
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
|
||||
@ -46,7 +55,9 @@
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
<Grid x:Name="TemplateRoot" Opacity="0" RenderTransformOrigin="0,0.5">
|
||||
<Grid x:Name="TemplateRoot"
|
||||
Opacity="0"
|
||||
RenderTransformOrigin="0,0.5">
|
||||
<Grid.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform ScaleX="0" ScaleY="0" />
|
||||
@ -60,13 +71,18 @@
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="3" />
|
||||
<Rectangle x:Name="PART_Track" />
|
||||
<Grid x:Name="PART_Indicator" HorizontalAlignment="Left" ClipToBounds="true">
|
||||
<Grid x:Name="PART_Indicator"
|
||||
HorizontalAlignment="Left"
|
||||
ClipToBounds="true">
|
||||
<Rectangle x:Name="Indicator"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
RadiusX="3" RadiusY="3" />
|
||||
RadiusX="3"
|
||||
RadiusY="3" />
|
||||
<Rectangle x:Name="Animation"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
RadiusX="3" RadiusY="3" RenderTransformOrigin="0.5,0.5">
|
||||
RadiusX="3"
|
||||
RadiusY="3"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
@ -139,27 +155,46 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth4" ClipToBounds="True" UniformCornerRadius="10">
|
||||
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth4"
|
||||
ClipToBounds="True"
|
||||
UniformCornerRadius="10">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="20,0,0,0" VerticalAlignment="Bottom" FontFamily="Microsoft YaHei Light"
|
||||
<TextBlock Margin="20,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
FontFamily="Microsoft YaHei Light"
|
||||
Text="正在下载更新..." />
|
||||
<StackPanel Grid.Row="1" FlowDirection="LeftToRight" Orientation="Horizontal">
|
||||
<ProgressBar x:Name="DownloadProgressIndicator" Width="520" Margin="20,0,0,0"
|
||||
Background="#ABCCEA" BorderThickness="0" Foreground="#3386F4"
|
||||
Maximum="1" Minimum="0"
|
||||
<StackPanel Grid.Row="1"
|
||||
FlowDirection="LeftToRight"
|
||||
Orientation="Horizontal">
|
||||
<ProgressBar x:Name="DownloadProgressIndicator"
|
||||
Width="520"
|
||||
Margin="20,0,0,0"
|
||||
Background="#ABCCEA"
|
||||
BorderThickness="0"
|
||||
Foreground="#007CEE"
|
||||
Maximum="1"
|
||||
Minimum="0"
|
||||
Style="{StaticResource RoundedCornerProgressBar}"
|
||||
ValueChanged="DownloadProgressIndicator_OnValueChanged" />
|
||||
<materialDesign:PackIcon x:Name="CancelButton" Margin="10,0,0,0" VerticalAlignment="Center"
|
||||
Cursor="Hand" Foreground="Gray" Kind="CloseCircle"
|
||||
MouseLeftButtonDown="CancelButton_OnMouseLeftButtonDown" ToolTip="取消" />
|
||||
<materialDesign:PackIcon x:Name="CancelButton"
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Cursor="Hand"
|
||||
Foreground="Gray"
|
||||
Kind="CloseCircle"
|
||||
MouseLeftButtonDown="CancelButton_OnMouseLeftButtonDown"
|
||||
ToolTip="取消" />
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="ProgressIndicatorText" Grid.Row="1" Grid.RowSpan="2"
|
||||
Margin="20,0,0,0" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="ProgressIndicatorText"
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="2"
|
||||
Margin="20,0,0,0"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</Window>
|
@ -40,20 +40,23 @@ namespace Pixeval.Updater
|
||||
/// </summary>
|
||||
public partial class MainWindow
|
||||
{
|
||||
private static readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
private static readonly string _currentDir = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase!).LocalPath);
|
||||
private static readonly string CurrentDir = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase!).LocalPath);
|
||||
|
||||
private static readonly DirectoryInfo _pixevalDirectory = Directory.GetParent(_currentDir);
|
||||
private static readonly DirectoryInfo PixevalDirectory = Directory.GetParent(CurrentDir);
|
||||
|
||||
private static readonly string _zipTmpPath = Path.Combine(_pixevalDirectory.FullName, "Pixeval.zip");
|
||||
private static readonly string ZipTmpPath = Path.Combine(PixevalDirectory.FullName, "Pixeval.zip");
|
||||
|
||||
private static readonly string _extractedTmpPath = Path.Combine(_pixevalDirectory.FullName, "Pixeval");
|
||||
private static readonly string ExtractedTmpPath = Path.Combine(PixevalDirectory.FullName, "Pixeval");
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
if (!CheckIsPixevalDirectory(_pixevalDirectory)) Exit("Pixeval更新器必须位于Pixeval目录中");
|
||||
if (!CheckIsPixevalDirectory(PixevalDirectory))
|
||||
{
|
||||
Exit("Pixeval更新器必须位于Pixeval目录中");
|
||||
}
|
||||
}
|
||||
|
||||
private void MainWindow_OnMouseDown(object sender, MouseButtonEventArgs e)
|
||||
@ -65,7 +68,7 @@ namespace Pixeval.Updater
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var memory = await Download(RelevantUrl.ZipArchive, new Progress<double>(p => DownloadProgressIndicator.Value = p), _cancellationTokenSource.Token);
|
||||
await using var memory = await Download(RelevantUrl.ZipArchive, new Progress<double>(p => DownloadProgressIndicator.Value = p), CancellationTokenSource.Token);
|
||||
memory.Position = 0L;
|
||||
if (memory.Checksum<SHA256Managed>() != (await GetRemoteChecksum()).ToLower())
|
||||
{
|
||||
@ -74,17 +77,17 @@ namespace Pixeval.Updater
|
||||
}
|
||||
|
||||
RmFiles();
|
||||
await using (var fileStream = new FileStream(_zipTmpPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
|
||||
await using (var fileStream = new FileStream(ZipTmpPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
memory.WriteTo(fileStream);
|
||||
}
|
||||
|
||||
Extract();
|
||||
File.Delete(_zipTmpPath);
|
||||
Directory.Delete(Path.Combine(_extractedTmpPath, "updater"), true);
|
||||
FileSystem.CopyDirectory(_extractedTmpPath, _pixevalDirectory.FullName);
|
||||
Directory.Delete(_extractedTmpPath, true);
|
||||
Process.Start(Path.Combine(_pixevalDirectory.FullName, "Pixeval.exe"));
|
||||
File.Delete(ZipTmpPath);
|
||||
Directory.Delete(Path.Combine(ExtractedTmpPath, "updater"), true);
|
||||
FileSystem.CopyDirectory(ExtractedTmpPath, PixevalDirectory.FullName);
|
||||
Directory.Delete(ExtractedTmpPath, true);
|
||||
Process.Start(Path.Combine(PixevalDirectory.FullName, "Pixeval.exe"));
|
||||
Exit();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
@ -104,22 +107,31 @@ namespace Pixeval.Updater
|
||||
|
||||
private static void RmFiles()
|
||||
{
|
||||
var rmList = _pixevalDirectory.GetFileSystemInfos().Where(fs => fs.FullName != _currentDir);
|
||||
var rmList = PixevalDirectory.GetFileSystemInfos().Where(fs => fs.FullName != CurrentDir);
|
||||
foreach (var fileSystemInfo in rmList)
|
||||
{
|
||||
if (File.GetAttributes(fileSystemInfo.FullName).HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
Directory.Delete(fileSystemInfo.FullName, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileSystemInfo.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Extract()
|
||||
{
|
||||
ZipFile.ExtractToDirectory(_zipTmpPath, _pixevalDirectory.FullName);
|
||||
ZipFile.ExtractToDirectory(ZipTmpPath, PixevalDirectory.FullName);
|
||||
}
|
||||
|
||||
private void Exit(string message = null)
|
||||
{
|
||||
if (message != null) MessageBox.Show(message);
|
||||
if (message != null)
|
||||
{
|
||||
MessageBox.Show(message);
|
||||
}
|
||||
|
||||
Close();
|
||||
Environment.Exit(0);
|
||||
@ -132,7 +144,7 @@ namespace Pixeval.Updater
|
||||
|
||||
private void CancelButton_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
private static bool CheckIsPixevalDirectory(DirectoryInfo dir)
|
||||
@ -145,7 +157,10 @@ namespace Pixeval.Updater
|
||||
using var response = await GetResponseHeader(new HttpClient(), url);
|
||||
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
if (!contentLength.HasValue) throw new InvalidOperationException("cannot retrieve the content length of the request uri");
|
||||
if (!contentLength.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("cannot retrieve the content length of the request uri");
|
||||
}
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
18
Pixeval.sln
18
Pixeval.sln
@ -3,11 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30104.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval", "src\Pixeval\Pixeval.csproj", "{3F5E9BB6-B8F1-453F-89E9-F719342D1E16}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval", "Pixeval\Pixeval.csproj", "{3F5E9BB6-B8F1-453F-89E9-F719342D1E16}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval.Updater", "src\Pixeval.Updater\Pixeval.Updater.csproj", "{D328EB05-39B1-46A9-B874-113F362DD589}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval.Interchange", "src\Pixeval.Interchange\Pixeval.Interchange.csproj", "{AF5AF2B2-4C1D-4103-AA97-D219C4768564}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval.Updater", "Pixeval.Updater\Pixeval.Updater.csproj", "{D328EB05-39B1-46A9-B874-113F362DD589}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D044DAED-2EF7-43B0-9C75-7714DC021F3B}"
|
||||
EndProject
|
||||
@ -42,18 +40,6 @@ Global
|
||||
{D328EB05-39B1-46A9-B874-113F362DD589}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D328EB05-39B1-46A9-B874-113F362DD589}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D328EB05-39B1-46A9-B874-113F362DD589}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AF5AF2B2-4C1D-4103-AA97-D219C4768564}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -32,7 +32,7 @@ namespace Pixeval
|
||||
{
|
||||
public const string AppIdentifier = "Pixeval";
|
||||
|
||||
public const string CurrentVersion = "3.0.1";
|
||||
public const string CurrentVersion = "3.0.2";
|
||||
|
||||
public const string ConfigurationFileName = "pixeval_conf.json";
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
<PropertyGroup>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Version>3.0.2</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
@ -1,61 +0,0 @@
|
||||
#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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pixeval.Interchange
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private const string IllustRegex = "pixeval://(www\\.)?pixiv\\.net/artworks/(?<id>\\d+)";
|
||||
private const string UserRegex = "pixeval://(www\\.)?pixiv\\.net/users/(?<id>\\d+)";
|
||||
private const string SpotlightRegex = "pixeval://(www\\.)?pixivision\\.net/\\w{2}/a/(?<id>\\d+)";
|
||||
|
||||
private static readonly HttpClient _httpClient = new HttpClient();
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
using (_httpClient)
|
||||
{
|
||||
if (!args.Any()) return;
|
||||
var url = args[0];
|
||||
// check protocol rationality
|
||||
if (!(Regex.IsMatch(url, IllustRegex) || Regex.IsMatch(url, UserRegex) || Regex.IsMatch(url, SpotlightRegex))) return;
|
||||
// check if there's an instance is running
|
||||
if (PixevalInstanceRunning())
|
||||
// send Pixeval custom pluggable protocol if so
|
||||
await _httpClient.PostAsync("http://127.0.0.1:12547/open", new StringContent(url, Encoding.UTF8));
|
||||
else
|
||||
// otherwise execute Pixeval and pass the protocol as argument
|
||||
Process.Start("Pixeval", url);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool PixevalInstanceRunning()
|
||||
{
|
||||
return Process.GetProcessesByName("Pixeval").Length > 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Pixeval.Interchange": {
|
||||
"commandName": "Project",
|
||||
"nativeDebugging": false
|
||||
},
|
||||
"newProfile1": {
|
||||
"commandName": "Executable"
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
dotnet publish /p:Platform=x64 Pixeval.Interchange.csproj -c Release -f netcoreapp3.1 --no-self-contained -r win-x64
|
File diff suppressed because it is too large
Load Diff
@ -1,307 +0,0 @@
|
||||
<!--
|
||||
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.MergedDictionaries>
|
||||
<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>
|
@ -1,281 +0,0 @@
|
||||
#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
|
||||
|
||||
#if RELEASE
|
||||
using Pixeval.Objects.Exceptions.Logger;
|
||||
#endif
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using CefSharp;
|
||||
using CefSharp.Wpf;
|
||||
using Microsoft.Win32;
|
||||
using Pixeval.Core;
|
||||
using Pixeval.Objects.I18n;
|
||||
using Pixeval.Objects.Primitive;
|
||||
using Pixeval.Persisting;
|
||||
using Pixeval.Persisting.WebApi;
|
||||
|
||||
namespace Pixeval
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
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
|
||||
ExceptionDumper.WriteException(e);
|
||||
#elif DEBUG
|
||||
throw e;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// These initializations ensure Pixeval to run properly, they MUST be initialized before everything start
|
||||
WriteToCurrentUserPathVariable();
|
||||
InitializeFolders();
|
||||
CheckCppRedistributable();
|
||||
CheckMultipleProcess();
|
||||
await InstallFakeCaCertificate();
|
||||
|
||||
// These initializations are for WEB API LOGIN
|
||||
PortScan(out var proxy, out var pac);
|
||||
AppContext.ProxyPort = proxy;
|
||||
AppContext.PacPort = pac;
|
||||
await WritePac();
|
||||
CefSharpInitialize();
|
||||
|
||||
// These initializations handle USER SESSION AND PIXEVAL UPDATE
|
||||
await RestoreSettings();
|
||||
await CheckUpdate();
|
||||
|
||||
// These initializations are for PROCESS COMMUNICATION AND PLUGGABLE PROTOCOL
|
||||
await InstallPluggableProtocolHandler();
|
||||
CreatePluggableProtocolRegistry();
|
||||
PluggableProtocolListener.StartServer();
|
||||
|
||||
|
||||
if (e.Args.Any()) await PluggableProtocolParser.Parse(e.Args[0]);
|
||||
base.OnStartup(e);
|
||||
}
|
||||
|
||||
private static void PortScan(out int proxy, out int pac)
|
||||
{
|
||||
var unavailablePorts = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections().Select(t => t.LocalEndPoint.Port).ToArray();
|
||||
|
||||
var rd = new Random();
|
||||
proxy = RandPorts();
|
||||
pac = RandPorts();
|
||||
while (Array.BinarySearch(unavailablePorts, proxy) >= 0) proxy = RandPorts();
|
||||
|
||||
while (Array.BinarySearch(unavailablePorts, pac) >= 0) pac = RandPorts();
|
||||
|
||||
int RandPorts()
|
||||
{
|
||||
return rd.Next(3000, 65536);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task InstallPluggableProtocolHandler()
|
||||
{
|
||||
var files = Directory.GetFiles(AppContext.InterchangeFolder);
|
||||
if (!(files.Any(f => f[(f.LastIndexOf(@"\", StringComparison.Ordinal) + 1)..] == "Pixeval.Interchange.runtimeconfig.json") && files.Any(f => f[(f.LastIndexOf(@"\", StringComparison.Ordinal) + 1)..] == "Pixeval.Interchange.exe") && files.Any(f => f[(f.LastIndexOf(@"\", StringComparison.Ordinal) + 1)..] == "Pixeval.Interchange.dll")))
|
||||
{
|
||||
foreach (var file in files) File.Delete(file);
|
||||
if (GetResourceStream(new Uri("pack://application:,,,/Pixeval;component/Resources/interchange.zip")) is { } streamResourceInfo)
|
||||
{
|
||||
var interchange = Path.Combine(AppContext.InterchangeFolder, "interchange.zip");
|
||||
await using (var fs = new FileStream(interchange, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
await streamResourceInfo.Stream.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
ZipFile.ExtractToDirectory(interchange, AppContext.InterchangeFolder, true);
|
||||
File.Delete(Path.Combine(AppContext.InterchangeFolder, "interchange.zip"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteToCurrentUserPathVariable()
|
||||
{
|
||||
var pathOld = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User);
|
||||
if (pathOld == null)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("PATH", "", EnvironmentVariableTarget.User);
|
||||
pathOld = "";
|
||||
}
|
||||
|
||||
var paths = pathOld.Split(';').ToList();
|
||||
var location = Path.GetDirectoryName(typeof(App).Assembly.Location);
|
||||
if (paths.All(p => p != location))
|
||||
{
|
||||
paths.RemoveAll(p => p.ToLower().Contains("pixeval"));
|
||||
var newPath = paths.Join(';') + ';' + location;
|
||||
Environment.SetEnvironmentVariable("PATH", newPath, EnvironmentVariableTarget.User);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeFolders()
|
||||
{
|
||||
Directory.CreateDirectory(AppContext.ProjectFolder);
|
||||
Directory.CreateDirectory(AppContext.SettingsFolder);
|
||||
Directory.CreateDirectory(AppContext.ExceptionReportFolder);
|
||||
Directory.CreateDirectory(AppContext.ResourceFolder);
|
||||
Directory.CreateDirectory(AppContext.PermanentlyFolder);
|
||||
Directory.CreateDirectory(AppContext.InterchangeFolder);
|
||||
}
|
||||
|
||||
private static void CreatePluggableProtocolRegistry()
|
||||
{
|
||||
var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\pixeval");
|
||||
if (regKey == null)
|
||||
{
|
||||
using var rootKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\pixeval");
|
||||
if (rootKey == null) throw new InvalidOperationException(nameof(rootKey));
|
||||
rootKey.SetValue("", "URL:Pixeval Protocol");
|
||||
rootKey.SetValue("URL Protocol", "");
|
||||
using var shellKey = rootKey.CreateSubKey("shell\\open\\command");
|
||||
if (shellKey == null) throw new InvalidOperationException(nameof(shellKey));
|
||||
shellKey.SetValue("", $"{Path.Combine(AppContext.InterchangeFolder, "Pixeval.Interchange.exe")} %1");
|
||||
}
|
||||
|
||||
regKey?.Dispose();
|
||||
}
|
||||
|
||||
private static void CheckMultipleProcess()
|
||||
{
|
||||
if (Process.GetProcessesByName(AppContext.AppIdentifier).Length > 1)
|
||||
{
|
||||
MessageBox.Show(AkaI18N.MultiplePixevalInstanceDetected, AkaI18N.MultiplePixevalInstanceDetectedTitle, MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckCppRedistributable()
|
||||
{
|
||||
if (!CppRedistributableInstalled())
|
||||
{
|
||||
MessageBox.Show(AkaI18N.CppRedistributableRequired, AkaI18N.CppRedistributableRequiredTitle, MessageBoxButton.OK, MessageBoxImage.Exclamation);
|
||||
Clipboard.SetDataObject("https://support.microsoft.com/zh-cn/help/2977003/the-latest-supported-visual-c-downloads");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the required Visual C++ Redistributable is installed on the computer
|
||||
/// </summary>
|
||||
/// <returns>Cpp redistributable is installed</returns>
|
||||
private static bool CppRedistributableInstalled()
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64");
|
||||
if (key == null) return false;
|
||||
|
||||
var success = int.TryParse(key.GetValue("Bld").ToString(), out var version);
|
||||
// visual C++ redistributable Bld table:
|
||||
// version v2015 v2017 v2019
|
||||
// ----------------------------------
|
||||
// Bid 23026 26020 27820
|
||||
const int vc2015Bld = 23026;
|
||||
return success && version >= vc2015Bld;
|
||||
}
|
||||
|
||||
private static void CefSharpInitialize()
|
||||
{
|
||||
Cef.Initialize(new CefSettings {CefCommandLineArgs = {{"proxy-pac-url", $"http://127.0.0.1:{AppContext.PacPort}/pixeval_pac.pac"}}}, true, browserProcessHandler: null);
|
||||
}
|
||||
|
||||
private static async Task InstallFakeCaCertificate()
|
||||
{
|
||||
var certificateManager = new CertificateManager(await CertificateManager.GetFakeCaRootCertificate());
|
||||
if (!certificateManager.Query(StoreName.Root, StoreLocation.CurrentUser))
|
||||
{
|
||||
if (MessageBox.Show(AkaI18N.CertificateInstallationIsRequired, AkaI18N.CertificateInstallationIsRequiredTitle, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
|
||||
certificateManager.Install(StoreName.Root, StoreLocation.CurrentUser);
|
||||
else
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task RestoreSettings()
|
||||
{
|
||||
await Settings.Restore();
|
||||
BrowsingHistoryAccessor.GlobalLifeTimeScope = new BrowsingHistoryAccessor(200, AppContext.BrowseHistoryDatabase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write Proxy-Auto-Configuration file to ..\{Directory to Pixeval.dll}\Resource\pixeval_pac.pac,
|
||||
/// this method is for login usage only, USE AT YOUR OWN RISK
|
||||
/// </summary>
|
||||
private static async Task WritePac()
|
||||
{
|
||||
var scriptBuilder = new StringBuilder();
|
||||
scriptBuilder.AppendLine("function FindProxyForURL(url, host) {");
|
||||
scriptBuilder.AppendLine(" if (shExpMatch(host, \"*.pixiv.net\")) {");
|
||||
scriptBuilder.AppendLine($" return 'PROXY 127.0.0.1:{AppContext.ProxyPort}';");
|
||||
scriptBuilder.AppendLine(" }");
|
||||
scriptBuilder.AppendLine(" return \"DIRECT\";");
|
||||
scriptBuilder.AppendLine("}");
|
||||
await File.WriteAllTextAsync(Path.Combine(AppContext.ResourceFolder, "pixeval_pac.pac"), scriptBuilder.ToString());
|
||||
}
|
||||
|
||||
protected override async void OnExit(ExitEventArgs e)
|
||||
{
|
||||
CertificateManager.GetFakeCaRootCertificate().Dispose();
|
||||
CertificateManager.GetFakeServerCertificate().Dispose();
|
||||
await Settings.Global.Store();
|
||||
if (Session.Current != null && !Session.Current.AccessToken.IsNullOrEmpty() && !Session.Current.PhpSessionId.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
#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 = "2.2.5";
|
||||
|
||||
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 InterchangeFolder = Path.Combine(ProjectFolder, "interchange");
|
||||
|
||||
public static readonly string CacheFolder = Path.Combine(ProjectFolder, "cache");
|
||||
|
||||
public static readonly string ResourceFolder = Path.Combine(Path.GetDirectoryName(typeof(App).Assembly.Location)!, "Resource");
|
||||
|
||||
public static readonly string PermanentlyFolder = Path.Combine(Path.GetDirectoryName(typeof(App).Assembly.Location)!, "Permanent");
|
||||
|
||||
public static readonly string BrowseHistoryDatabase = Path.Combine(ProjectFolder, "history.db");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
#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++;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_stackLimit = stackLimit;
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#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";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
#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.VerifyIllustRational(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)
|
||||
{
|
||||
_uid = uid;
|
||||
_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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).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;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#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
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class provides several functions to browse the Pixiv content
|
||||
/// </summary>
|
||||
/// <typeparam name="T">the correspond data type</typeparam>
|
||||
public interface IPixivAsyncEnumerable<T> : IAsyncEnumerable<T>, ICancellable
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates how many pages have been requested
|
||||
/// </summary>
|
||||
int RequestedPages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Basically, this method SHOULD increase the value of <see cref="RequestedPages" />
|
||||
/// </summary>
|
||||
void ReportRequestedPages();
|
||||
|
||||
/// <summary>
|
||||
/// Tell the <see cref="IPixivAsyncEnumerable{T}" /> how to insert <see cref="item" /> to a <see cref="IList{T}" />
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="collection"></param>
|
||||
void InsertionPolicy(T item, IList<T> collection);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the <see cref="item" /> has the rationality to be inserted to the <see cref="IList{T}" />
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <returns></returns>
|
||||
bool VerifyRationality(T item, IList<T> collection);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#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 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)
|
||||
{
|
||||
return new IllustrationQualification(input switch
|
||||
{
|
||||
_ when input.IsNumber() => ConditionType.Id,
|
||||
_ when input.StartsWith("!") => ConditionType.ExcludeTag,
|
||||
_ when !input.IsNullOrEmpty() => ConditionType.Tag,
|
||||
_ => ConditionType.None
|
||||
}, input);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConditionType
|
||||
{
|
||||
Id, Tag, ExcludeTag, None
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#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 Pixeval.Data.ViewModel;
|
||||
|
||||
namespace Pixeval.Core
|
||||
{
|
||||
public class IllustrationQualifier : IQualifier<Illustration, IllustrationQualification>
|
||||
{
|
||||
public bool Qualified(Illustration condition, IllustrationQualification pattern)
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
#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.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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
#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.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)) 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
|
||||
>= /* Windows 10 April 2018 Update(a.k.a. Redstone 4), which is the version that release Windows Timeline feature */
|
||||
new Version(10, 0, 17134);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool VerifyIllustRational(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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#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 EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using Pixeval.Objects.Primitive;
|
||||
using Pixeval.UI;
|
||||
|
||||
namespace Pixeval.Core
|
||||
{
|
||||
public class TransferController : WebApiController
|
||||
{
|
||||
[Route(HttpVerbs.Post, "/open")]
|
||||
public async Task DoOpen()
|
||||
{
|
||||
MainWindow.Instance.Dispatcher.Invoke(() => MainWindow.Instance.GlobalActivate());
|
||||
await PluggableProtocolParser.Parse(await HttpContext.GetRequestBodyAsStringAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public class PluggableProtocolListener
|
||||
{
|
||||
private static WebServer _server;
|
||||
|
||||
public static void StartServer()
|
||||
{
|
||||
_server = new WebServer(o => o.WithUrlPrefix("http://127.0.0.1:12547").WithMode(HttpListenerMode.Microsoft)).WithWebApi("/", m => m.WithController<TransferController>());
|
||||
_server.Start();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
#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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using MahApps.Metro.Controls;
|
||||
using Pixeval.Data.ViewModel;
|
||||
using Pixeval.Data.Web.Delegation;
|
||||
using Pixeval.Objects.Generic;
|
||||
using Pixeval.UI;
|
||||
using Refit;
|
||||
|
||||
namespace Pixeval.Core
|
||||
{
|
||||
public class PluggableProtocolParser
|
||||
{
|
||||
private const string IllustRegex = "pixeval://(www\\.)?pixiv\\.net/artworks/(?<id>\\d+)";
|
||||
private const string UserRegex = "pixeval://(www\\.)?pixiv\\.net/users/(?<id>\\d+)";
|
||||
private const string SpotlightRegex = "pixeval://(www\\.)?pixivision\\.net/\\w{2}/a/(?<id>\\d+)";
|
||||
|
||||
public static async Task Parse(string url)
|
||||
{
|
||||
Match match;
|
||||
if ((match = Regex.Match(url, IllustRegex)).Success)
|
||||
{
|
||||
var i = MainWindow.Instance;
|
||||
await i.Invoke(async () =>
|
||||
{
|
||||
if (i.IllustBrowserDialogHost.IsOpen) i.IllustBrowserDialogHost.CurrentSession.Close();
|
||||
i.OpenIllustBrowser(await PixivHelper.IllustrationInfo(match.Groups["id"].Value), false);
|
||||
});
|
||||
}
|
||||
else if ((match = Regex.Match(url, UserRegex)).Success)
|
||||
{
|
||||
var i = MainWindow.Instance;
|
||||
i.Invoke(() =>
|
||||
{
|
||||
i.SetUserBrowserContext(new User {Id = match.Groups["id"].Value});
|
||||
i.OpenUserBrowser();
|
||||
});
|
||||
}
|
||||
else if ((match = Regex.Match(url, SpotlightRegex)).Success)
|
||||
{
|
||||
var articleId = match.Groups["id"].Value;
|
||||
string title;
|
||||
|
||||
// if the current culture is set to en-XX, then we will simply analyze the html and get the title
|
||||
if (CultureInfo.CurrentCulture.Name.ToLower().StartsWith("en"))
|
||||
title = await TryGetSpotlightEnTitle(articleId);
|
||||
else // otherwise, we will try to access the spotlight web API, and analyze html if failed
|
||||
try
|
||||
{
|
||||
title = (await HttpClientFactory.WebApiService().GetSpotlightArticles(articleId)).BodyList[0].Title;
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.NotFound)
|
||||
title = await TryGetSpotlightEnTitle(articleId);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
||||
var tasks = await Tasks<string, Illustration>.Of(await PixivClient.Instance.GetArticleWorks(articleId)).Mapping(PixivHelper.IllustrationInfo).Construct().WhenAll();
|
||||
var result = tasks.Peek(i =>
|
||||
{
|
||||
i.IsManga = true;
|
||||
i.FromSpotlight = true;
|
||||
i.SpotlightTitle = title;
|
||||
}).ToArray();
|
||||
|
||||
MainWindow.Instance.Invoke(() => MainWindow.Instance.OpenIllustBrowser(result[0].Apply(r => r.MangaMetadata = result.ToArray()), false));
|
||||
|
||||
static async Task<string> TryGetSpotlightEnTitle(string id)
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
var html = await httpClient.GetStringAsync($"https://www.pixivision.net/en/a/{id}");
|
||||
var doc = await new HtmlParser().ParseDocumentAsync(html);
|
||||
return doc.QuerySelectorAll(".am__title")[0].InnerHtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
#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>
|
||||
{
|
||||
private readonly SearchTagMatchOption _matchOption;
|
||||
private readonly int _start;
|
||||
private readonly string _tag;
|
||||
protected readonly bool IsPremium;
|
||||
|
||||
protected AbstractQueryAsyncEnumerable(string tag, SearchTagMatchOption matchOption, bool isPremium, int start = 1)
|
||||
{
|
||||
_start = start < 1 ? 1 : start;
|
||||
_tag = tag;
|
||||
_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.VerifyIllustRational(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)
|
||||
{
|
||||
_keyword = keyword;
|
||||
_matchOption = matchOption;
|
||||
_current = current;
|
||||
_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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).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, IllustrationPopularityComparator.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_rankOption = rankOption;
|
||||
_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.VerifyIllustRational(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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).GetStringAsync(url)).FromJson<RankingResponse>();
|
||||
|
||||
if (result is { } response && !response.Illusts.IsNullOrEmpty()) return HttpResponse<RankingResponse>.Wrap(true, response);
|
||||
return HttpResponse<RankingResponse>.Wrap(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
#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.VerifyIllustRational(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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).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 PublishDateRecommendAsyncEnumerable : AbstractRecommendAsyncEnumerable
|
||||
{
|
||||
public override void InsertionPolicy(Illustration item, IList<Illustration> collection)
|
||||
{
|
||||
if (item != null) collection.AddSorted(item, IllustrationPublishDateComparator.Instance);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
#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()));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
#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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).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; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_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.VerifyIllustRational(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)
|
||||
{
|
||||
_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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).GetStringAsync(url)).FromJson<UploadResponse>();
|
||||
if (res is { } response && !response.Illusts.IsNullOrEmpty()) return HttpResponse<UploadResponse>.Wrap(true, response);
|
||||
|
||||
return HttpResponse<UploadResponse>.Wrap(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_userId = userId;
|
||||
_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;
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
#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.VerifyIllustRational(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().Apply(h => h.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage())).GetStringAsync(url)).FromJson<UserUpdateResponse>();
|
||||
if (res is { } response && !response.Illusts.IsNullOrEmpty()) return HttpResponse<UserUpdateResponse>.Wrap(true, response);
|
||||
|
||||
return HttpResponse<UserUpdateResponse>.Wrap(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
#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>
|
||||
/// represents the string form of the <see cref="BrowsingHistory" />, by default, it is the name/title
|
||||
/// </summary>
|
||||
public string BrowseObjectState { get; set; }
|
||||
|
||||
public string BrowseObjectThumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// get the illustrator of the illust which the current BrowsingHistory is representing,
|
||||
/// this property will be valid only if the <see cref="IsReferToIllust" /> property is set
|
||||
/// to true
|
||||
/// </summary>
|
||||
public string IllustratorName { get; set; }
|
||||
|
||||
public bool IsReferToUser { get; set; }
|
||||
|
||||
public bool IsReferToIllust { get; set; }
|
||||
|
||||
public bool IsReferToSpotlight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// represents the string form of the identification of current BrowsingHistory, by default
|
||||
/// it is illust id/user id/spotlight id
|
||||
/// </summary>
|
||||
public string BrowseObjectId { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
#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();
|
||||
|
||||
// reverse-ordered(a.k.a. descend order)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#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];
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
#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)
|
||||
{
|
||||
_requestHandler = requestHandler;
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#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");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#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(PixivAuthenticationHttpRequestHandler.Instance, directConnect)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DnsResolver DnsResolver { get; set; } = PixivApiDnsResolver.Instance;
|
||||
|
||||
public static HttpMessageHandler Instance(bool directConnect)
|
||||
{
|
||||
return new PixivApiHttpClientHandler(directConnect);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
#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.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 PixivAuthenticationHttpRequestHandler : IHttpRequestHandler
|
||||
{
|
||||
public static readonly PixivAuthenticationHttpRequestHandler Instance = new PixivAuthenticationHttpRequestHandler();
|
||||
|
||||
protected PixivAuthenticationHttpRequestHandler()
|
||||
{
|
||||
}
|
||||
|
||||
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":
|
||||
if (Session.Current.PhpSessionId.IsNullOrEmpty()) throw new TokenNotFoundException($"{nameof(Session.Current.PhpSessionId)} 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.TryAddWithoutValidation("Cookie", $"PHPSESSID={Session.Current.PhpSessionId}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!httpRequestMessage.Headers.Contains("Accept-Language")) httpRequestMessage.Headers.TryAddWithoutValidation("Accept-Language", AkaI18N.GetCultureAcceptLanguage());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#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(PixivAuthenticationHttpRequestHandler.Instance, false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DnsResolver DnsResolver { get; set; } = null;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#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";
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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";
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#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";
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user