├── .gitattributes
├── .gitignore
├── ImgurNaoAPI.cs
├── LICENSE
├── Objects
└── AllJsonObjects.cs
├── PixivAppAPI.cs
├── PixivBaseAPI.cs
├── PixivCS.csproj
├── PixivCS.sln
├── README.md
├── SauceNAOJsonObjectReturn.txt
└── Utilities.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/ImgurNaoAPI.cs:
--------------------------------------------------------------------------------
1 | //using System;
2 | //using System.Collections.Generic;
3 | //using System.Linq;
4 | //using System.Text;
5 | //using System.Threading.Tasks;
6 | //using System.Collections.Specialized;
7 | //using System.Net;
8 |
9 | //namespace PixivCS
10 | //{
11 | // public class ImgurNaoAPI
12 | // {
13 | // private string SauceNAO_API;
14 | // private string Imgur_API;
15 |
16 | // ///
17 | // /// 构造方法
18 | // ///
19 | // /// SauceNAO Api
20 | // /// Imgur Api
21 | // public ImgurNaoAPI(string SauceNAOApiKey, string ImgurApiKey)
22 | // {
23 | // SauceNAO_API = SauceNAOApiKey;
24 | // Imgur_API = ImgurApiKey;
25 | // }
26 |
27 | // ///
28 | // /// 上传图像
29 | // ///
30 | // /// 图像字节数组
31 | // /// 包含图像链接的Json对象
32 | // public JsonObject UpLoad(byte[] Image)
33 | // {
34 | // WebClient WebClient;
35 | // WebClient = new WebClient();
36 | // WebClient.Headers.Add("Authorization: Client-ID " + Imgur_API);
37 | // var values = new NameValueCollection
38 | // {
39 | // { "image", Convert.ToBase64String(Image) }
40 | // };
41 | // return JsonObject.Parse(Encoding.UTF8.GetString(WebClient.UploadValues("https://api.imgur.com/3/upload", values))).GetNamedObject("data");
42 | // }
43 |
44 | // ///
45 | // /// 下载Json对象
46 | // ///
47 | // /// 图像链接
48 | // /// 经过处理的包含Pixiv ID , title , member_name , member_id , ext_urls 的Json对象
49 | // public JsonObject DownLoad(string url)
50 | // {
51 | // WebClient WebClient = new WebClient();
52 | // WebClient.QueryString.Add("db", "999");
53 | // WebClient.QueryString.Add("output_type", "2");
54 | // WebClient.QueryString.Add("numres", "16");
55 | // WebClient.QueryString.Add("api_key", SauceNAO_API);
56 | // WebClient.QueryString.Add("url", url);
57 | // try
58 | // {
59 | // string response = WebClient.DownloadString("https://saucenao.com/search.php");
60 | // JsonObject dynObj = JsonObject.Parse(response);
61 | // return dynObj.GetNamedArray("results")[0].GetObject().GetNamedObject("data");
62 | // }
63 | // catch (Exception e)
64 | // {
65 | // System.Diagnostics.Debug.WriteLine(e.Message);
66 | // }
67 | // return null;
68 | // }
69 | // }
70 | //}
71 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Communist Fish
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Objects/AllJsonObjects.cs:
--------------------------------------------------------------------------------
1 | //此文件由app.quicktype.io生成
2 | //我吹爆!
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Globalization;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Converters;
9 |
10 | namespace PixivCS.Objects
11 | {
12 |
13 | public partial class ShowcaseArticle
14 | {
15 | [JsonProperty("error")]
16 | public bool Error { get; set; }
17 |
18 | [JsonProperty("message")]
19 | public string Message { get; set; }
20 |
21 | [JsonProperty("body")]
22 | public Body[] Body { get; set; }
23 | }
24 |
25 | public partial class Body
26 | {
27 | [JsonProperty("id")]
28 | public string Id { get; set; }
29 |
30 | [JsonProperty("lang")]
31 | public string Lang { get; set; }
32 |
33 | [JsonProperty("entry")]
34 | public Entry Entry { get; set; }
35 |
36 | [JsonProperty("tags")]
37 | public EntryTag[] Tags { get; set; }
38 |
39 | [JsonProperty("thumbnailUrl")]
40 | public Uri ThumbnailUrl { get; set; }
41 |
42 | [JsonProperty("title")]
43 | public string Title { get; set; }
44 |
45 | [JsonProperty("publishDate")]
46 | public long PublishDate { get; set; }
47 |
48 | [JsonProperty("category")]
49 | public string Category { get; set; }
50 |
51 | [JsonProperty("subCategory")]
52 | public string SubCategory { get; set; }
53 |
54 | [JsonProperty("subCategoryLabel")]
55 | public string SubCategoryLabel { get; set; }
56 |
57 | [JsonProperty("subCategoryIntroduction")]
58 | public string SubCategoryIntroduction { get; set; }
59 |
60 | [JsonProperty("introduction")]
61 | public string Introduction { get; set; }
62 |
63 | [JsonProperty("footer")]
64 | public string Footer { get; set; }
65 |
66 | [JsonProperty("illusts")]
67 | public BodyIllust[] Illusts { get; set; }
68 |
69 | [JsonProperty("relatedArticles")]
70 | public RelatedArticle[] RelatedArticles { get; set; }
71 |
72 | [JsonProperty("followingUserIds")]
73 | public dynamic[] FollowingUserIds { get; set; }
74 |
75 | [JsonProperty("isOnlyOneUser")]
76 | public bool IsOnlyOneUser { get; set; }
77 | }
78 |
79 | public partial class Entry
80 | {
81 | [JsonProperty("id")]
82 | public string Id { get; set; }
83 |
84 | [JsonProperty("title")]
85 | public string Title { get; set; }
86 |
87 | [JsonProperty("pure_title")]
88 | public string PureTitle { get; set; }
89 |
90 | [JsonProperty("catchphrase")]
91 | public string Catchphrase { get; set; }
92 |
93 | [JsonProperty("header")]
94 | public string Header { get; set; }
95 |
96 | [JsonProperty("body")]
97 | public string Body { get; set; }
98 |
99 | [JsonProperty("footer")]
100 | public string Footer { get; set; }
101 |
102 | [JsonProperty("sidebar")]
103 | public string Sidebar { get; set; }
104 |
105 | [JsonProperty("publish_date")]
106 | public long PublishDate { get; set; }
107 |
108 | [JsonProperty("language")]
109 | public string Language { get; set; }
110 |
111 | [JsonProperty("pixivision_category_slug")]
112 | public string PixivisionCategorySlug { get; set; }
113 |
114 | [JsonProperty("pixivision_category")]
115 | public PixivisionCategory PixivisionCategory { get; set; }
116 |
117 | [JsonProperty("pixivision_subcategory_slug")]
118 | public string PixivisionSubcategorySlug { get; set; }
119 |
120 | [JsonProperty("pixivision_subcategory")]
121 | public PixivisionSubcategory PixivisionSubcategory { get; set; }
122 |
123 | [JsonProperty("tags")]
124 | public EntryTag[] Tags { get; set; }
125 |
126 | [JsonProperty("article_url")]
127 | public Uri ArticleUrl { get; set; }
128 |
129 | [JsonProperty("intro")]
130 | public string Intro { get; set; }
131 |
132 | [JsonProperty("facebook_count")]
133 | public string FacebookCount { get; set; }
134 |
135 | [JsonProperty("twitter_count")]
136 | public string TwitterCount { get; set; }
137 | }
138 |
139 | public partial class PixivisionCategory
140 | {
141 | [JsonProperty("label")]
142 | public string Label { get; set; }
143 |
144 | [JsonProperty("introduction")]
145 | public string Introduction { get; set; }
146 | }
147 |
148 | public partial class PixivisionSubcategory
149 | {
150 | [JsonProperty("label")]
151 | public string Label { get; set; }
152 |
153 | [JsonProperty("label_en")]
154 | public string LabelEn { get; set; }
155 |
156 | [JsonProperty("title")]
157 | public string Title { get; set; }
158 |
159 | [JsonProperty("introduction")]
160 | public string Introduction { get; set; }
161 |
162 | [JsonProperty("image_url")]
163 | public string ImageUrl { get; set; }
164 |
165 | [JsonProperty("big_image_url")]
166 | public string BigImageUrl { get; set; }
167 | }
168 |
169 | public partial class EntryTag
170 | {
171 | [JsonProperty("id")]
172 | public string Id { get; set; }
173 |
174 | [JsonProperty("name")]
175 | public string Name { get; set; }
176 | }
177 |
178 | public partial class BodyIllust
179 | {
180 | [JsonProperty("spotlight_article_id")]
181 | public long SpotlightArticleId { get; set; }
182 |
183 | [JsonProperty("illust_id")]
184 | public long IllustId { get; set; }
185 |
186 | [JsonProperty("description")]
187 | public string Description { get; set; }
188 |
189 | [JsonProperty("language")]
190 | public string Language { get; set; }
191 |
192 | [JsonProperty("illust_user_id")]
193 | public string IllustUserId { get; set; }
194 |
195 | [JsonProperty("illust_title")]
196 | public string IllustTitle { get; set; }
197 |
198 | [JsonProperty("illust_ext")]
199 | public string IllustExt { get; set; }
200 |
201 | [JsonProperty("illust_width")]
202 | public string IllustWidth { get; set; }
203 |
204 | [JsonProperty("illust_height")]
205 | public string IllustHeight { get; set; }
206 |
207 | [JsonProperty("illust_restrict")]
208 | public string IllustRestrict { get; set; }
209 |
210 | [JsonProperty("illust_x_restrict")]
211 | public string IllustXRestrict { get; set; }
212 |
213 | [JsonProperty("illust_create_date")]
214 | public string IllustCreateDate { get; set; }
215 |
216 | [JsonProperty("illust_upload_date")]
217 | public string IllustUploadDate { get; set; }
218 |
219 | [JsonProperty("illust_server_id")]
220 | public string IllustServerId { get; set; }
221 |
222 | [JsonProperty("illust_hash")]
223 | public string IllustHash { get; set; }
224 |
225 | [JsonProperty("illust_type")]
226 | public string IllustType { get; set; }
227 |
228 | [JsonProperty("illust_sanity_level")]
229 | public long IllustSanityLevel { get; set; }
230 |
231 | [JsonProperty("illust_book_style")]
232 | public string IllustBookStyle { get; set; }
233 |
234 | [JsonProperty("illust_page_count")]
235 | public string IllustPageCount { get; set; }
236 |
237 | [JsonProperty("illust_custom_thumbnail_upload_datetime")]
238 | public dynamic IllustCustomThumbnailUploadDatetime { get; set; }
239 |
240 | [JsonProperty("illust_comment")]
241 | public string IllustComment { get; set; }
242 |
243 | [JsonProperty("user_account")]
244 | public string UserAccount { get; set; }
245 |
246 | [JsonProperty("user_name")]
247 | public string UserName { get; set; }
248 |
249 | [JsonProperty("user_comment")]
250 | public string UserComment { get; set; }
251 |
252 | [JsonProperty("url")]
253 | public Url Url { get; set; }
254 |
255 | [JsonProperty("ugoira_meta")]
256 | public dynamic UgoiraMeta { get; set; }
257 |
258 | [JsonProperty("user_icon")]
259 | public Uri UserIcon { get; set; }
260 | }
261 |
262 | public partial class Url
263 | {
264 | [JsonProperty("1200x1200")]
265 | public Uri The1200X1200 { get; set; }
266 |
267 | [JsonProperty("768x1200")]
268 | public Uri The768X1200 { get; set; }
269 |
270 | [JsonProperty("ugoira600x600")]
271 | public string Ugoira600X600 { get; set; }
272 |
273 | [JsonProperty("ugoira1920x1080")]
274 | public string Ugoira1920X1080 { get; set; }
275 | }
276 |
277 | public partial class RelatedArticle
278 | {
279 | [JsonProperty("id")]
280 | public string Id { get; set; }
281 |
282 | [JsonProperty("ja")]
283 | public PrivacyPolicy Ja { get; set; }
284 |
285 | [JsonProperty("en")]
286 | public PrivacyPolicy En { get; set; }
287 |
288 | [JsonProperty("zh")]
289 | public PrivacyPolicy Zh { get; set; }
290 |
291 | [JsonProperty("zh_tw")]
292 | public PrivacyPolicy ZhTw { get; set; }
293 |
294 | [JsonProperty("publish_date")]
295 | public long PublishDate { get; set; }
296 |
297 | [JsonProperty("category")]
298 | public string Category { get; set; }
299 |
300 | [JsonProperty("pixivision_category_slug")]
301 | public string PixivisionCategorySlug { get; set; }
302 |
303 | [JsonProperty("pixivision_subcategory_slug")]
304 | public string PixivisionSubcategorySlug { get; set; }
305 |
306 | [JsonProperty("thumbnail")]
307 | public string Thumbnail { get; set; }
308 |
309 | [JsonProperty("thumbnail_illust_id")]
310 | public string ThumbnailIllustId { get; set; }
311 |
312 | [JsonProperty("has_body")]
313 | public string HasBody { get; set; }
314 |
315 | [JsonProperty("is_pr")]
316 | public string IsPr { get; set; }
317 |
318 | [JsonProperty("pr_client_name")]
319 | public string PrClientName { get; set; }
320 |
321 | [JsonProperty("edit_status")]
322 | public string EditStatus { get; set; }
323 |
324 | [JsonProperty("translation_status")]
325 | public string TranslationStatus { get; set; }
326 |
327 | [JsonProperty("is_sample")]
328 | public string IsSample { get; set; }
329 |
330 | [JsonProperty("illusts")]
331 | public dynamic[] Illusts { get; set; }
332 |
333 | [JsonProperty("novel_ids")]
334 | public dynamic[] NovelIds { get; set; }
335 |
336 | [JsonProperty("memo")]
337 | public string Memo { get; set; }
338 |
339 | [JsonProperty("facebook_count")]
340 | public string FacebookCount { get; set; }
341 |
342 | [JsonProperty("tweet_count")]
343 | public string TweetCount { get; set; }
344 |
345 | [JsonProperty("tweet_max_count")]
346 | public string TweetMaxCount { get; set; }
347 |
348 | [JsonProperty("tags")]
349 | public dynamic[] Tags { get; set; }
350 |
351 | [JsonProperty("tag_ids")]
352 | public dynamic TagIds { get; set; }
353 |
354 | [JsonProperty("numbered_tags")]
355 | public dynamic[] NumberedTags { get; set; }
356 |
357 | [JsonProperty("main_abtest_pattern_id")]
358 | public string MainAbtestPatternId { get; set; }
359 |
360 | [JsonProperty("advertisement_id")]
361 | public string AdvertisementId { get; set; }
362 | }
363 |
364 | public partial class PrivacyPolicy
365 | {
366 | }
367 |
368 | public partial class UgoiraMetadata
369 | {
370 | [JsonProperty("ugoira_metadata")]
371 | public UgoiraMetadataClass UgoiraMetadataUgoiraMetadata { get; set; }
372 | }
373 |
374 | public partial class UgoiraMetadataClass
375 | {
376 | [JsonProperty("zip_urls")]
377 | public Urls ZipUrls { get; set; }
378 |
379 | [JsonProperty("frames")]
380 | public Frame[] Frames { get; set; }
381 | }
382 |
383 | public partial class Frame
384 | {
385 | [JsonProperty("file")]
386 | public string File { get; set; }
387 |
388 | [JsonProperty("delay")]
389 | public long Delay { get; set; }
390 | }
391 |
392 | public partial class Urls
393 | {
394 | [JsonProperty("medium")]
395 | public Uri Medium { get; set; }
396 | }
397 |
398 | public partial class UserList
399 | {
400 | [JsonProperty("users")]
401 | public dynamic[] Users { get; set; }
402 | }
403 |
404 | public partial class UserFollowList
405 | {
406 | [JsonProperty("user_previews")]
407 | public UserPreview[] UserPreviews { get; set; }
408 |
409 | [JsonProperty("next_url")]
410 | public Uri NextUrl { get; set; }
411 | }
412 |
413 | public partial class UserPreview
414 | {
415 | [JsonProperty("user")]
416 | public IllustUser User { get; set; }
417 |
418 | [JsonProperty("illusts")]
419 | public UserPreviewIllust[] Illusts { get; set; }
420 |
421 | [JsonProperty("novels")]
422 | public dynamic[] Novels { get; set; }
423 |
424 | [JsonProperty("is_muted")]
425 | public bool IsMuted { get; set; }
426 | }
427 |
428 | public partial class UserPreviewIllust
429 | {
430 | [JsonProperty("id")]
431 | public long Id { get; set; }
432 |
433 | [JsonProperty("title")]
434 | public string Title { get; set; }
435 |
436 | [JsonProperty("type")]
437 | public string Type { get; set; }
438 |
439 | [JsonProperty("image_urls")]
440 | public ImageUrls ImageUrls { get; set; }
441 |
442 | [JsonProperty("caption")]
443 | public string Caption { get; set; }
444 |
445 | [JsonProperty("restrict")]
446 | public long Restrict { get; set; }
447 |
448 | [JsonProperty("user")]
449 | public IllustUser User { get; set; }
450 |
451 | [JsonProperty("tags")]
452 | public IllustTag[] Tags { get; set; }
453 |
454 | [JsonProperty("tools")]
455 | public string[] Tools { get; set; }
456 |
457 | [JsonProperty("create_date")]
458 | public string CreateDate { get; set; }
459 |
460 | [JsonProperty("page_count")]
461 | public long PageCount { get; set; }
462 |
463 | [JsonProperty("width")]
464 | public long Width { get; set; }
465 |
466 | [JsonProperty("height")]
467 | public long Height { get; set; }
468 |
469 | [JsonProperty("sanity_level")]
470 | public long SanityLevel { get; set; }
471 |
472 | [JsonProperty("x_restrict")]
473 | public long XRestrict { get; set; }
474 |
475 | [JsonProperty("series")]
476 | public Series Series { get; set; }
477 |
478 | [JsonProperty("meta_single_page")]
479 | public MetaSinglePage MetaSinglePage { get; set; }
480 |
481 | [JsonProperty("meta_pages")]
482 | public MetaPage[] MetaPages { get; set; }
483 |
484 | [JsonProperty("total_view")]
485 | public long TotalView { get; set; }
486 |
487 | [JsonProperty("total_bookmarks")]
488 | public long TotalBookmarks { get; set; }
489 |
490 | [JsonProperty("is_bookmarked")]
491 | public bool IsBookmarked { get; set; }
492 |
493 | [JsonProperty("visible")]
494 | public bool Visible { get; set; }
495 |
496 | [JsonProperty("is_muted")]
497 | public bool IsMuted { get; set; }
498 |
499 | [JsonProperty("total_comments", NullValueHandling = NullValueHandling.Ignore)]
500 | public long? TotalComments { get; set; }
501 | }
502 |
503 | public partial class ImageUrls
504 | {
505 | [JsonProperty("square_medium")]
506 | public Uri SquareMedium { get; set; }
507 |
508 | [JsonProperty("medium")]
509 | public Uri Medium { get; set; }
510 |
511 | [JsonProperty("large")]
512 | public Uri Large { get; set; }
513 |
514 | [JsonProperty("original", NullValueHandling = NullValueHandling.Ignore)]
515 | public Uri Original { get; set; }
516 | }
517 |
518 | public partial class MetaPage
519 | {
520 | [JsonProperty("image_urls")]
521 | public ImageUrls ImageUrls { get; set; }
522 | }
523 |
524 | public partial class MetaSinglePage
525 | {
526 | [JsonProperty("original_image_url", NullValueHandling = NullValueHandling.Ignore)]
527 | public Uri OriginalImageUrl { get; set; }
528 | }
529 |
530 | public partial class Series
531 | {
532 | [JsonProperty("id")]
533 | public long Id { get; set; }
534 |
535 | [JsonProperty("title")]
536 | public string Title { get; set; }
537 | }
538 |
539 | public partial class IllustTag
540 | {
541 | [JsonProperty("name")]
542 | public string Name { get; set; }
543 |
544 | [JsonProperty("translated_name")]
545 | public string TranslatedName { get; set; }
546 | }
547 |
548 | public partial class IllustUser
549 | {
550 | [JsonProperty("id")]
551 | public long Id { get; set; }
552 |
553 | [JsonProperty("name")]
554 | public string Name { get; set; }
555 |
556 | [JsonProperty("account")]
557 | public string Account { get; set; }
558 |
559 | [JsonProperty("profile_image_urls")]
560 | public Urls ProfileImageUrls { get; set; }
561 |
562 | [JsonProperty("is_followed", NullValueHandling = NullValueHandling.Ignore)]
563 | public bool? IsFollowed { get; set; }
564 |
565 | [JsonProperty("comment", NullValueHandling = NullValueHandling.Ignore)]
566 | public string Comment { get; set; }
567 | }
568 |
569 | public partial class UserBookmarkTags
570 | {
571 | [JsonProperty("bookmark_tags")]
572 | public dynamic[] BookmarkTags { get; set; }
573 |
574 | [JsonProperty("next_url")]
575 | public Uri NextUrl { get; set; }
576 | }
577 |
578 | public partial class IllustBookmarkDetail
579 | {
580 | [JsonProperty("bookmark_detail")]
581 | public BookmarkDetail BookmarkDetail { get; set; }
582 | }
583 |
584 | public partial class BookmarkDetail
585 | {
586 | [JsonProperty("is_bookmarked")]
587 | public bool IsBookmarked { get; set; }
588 |
589 | [JsonProperty("tags")]
590 | public BookmarkDetailTag[] Tags { get; set; }
591 |
592 | [JsonProperty("restrict")]
593 | public string Restrict { get; set; }
594 | }
595 |
596 | public partial class BookmarkDetailTag
597 | {
598 | [JsonProperty("name")]
599 | public string Name { get; set; }
600 |
601 | [JsonProperty("is_registered")]
602 | public bool IsRegistered { get; set; }
603 | }
604 |
605 | public partial class SearchIllustResult
606 | {
607 | [JsonProperty("illusts")]
608 | public UserPreviewIllust[] Illusts { get; set; }
609 |
610 | [JsonProperty("next_url")]
611 | public Uri NextUrl { get; set; }
612 |
613 | [JsonProperty("search_span_limit")]
614 | public long SearchSpanLimit { get; set; }
615 | }
616 |
617 | public partial class TrendingTagsIllust
618 | {
619 | [JsonProperty("trend_tags")]
620 | public TrendTag[] TrendTags { get; set; }
621 | }
622 |
623 | public partial class TrendTag
624 | {
625 | [JsonProperty("tag")]
626 | public string Tag { get; set; }
627 |
628 | [JsonProperty("translated_name")]
629 | public string TranslatedName { get; set; }
630 |
631 | [JsonProperty("illust")]
632 | public UserPreviewIllust Illust { get; set; }
633 | }
634 |
635 | public partial class UserIllusts
636 | {
637 | [JsonProperty("illusts")]
638 | public UserPreviewIllust[] Illusts { get; set; }
639 |
640 | [JsonProperty("next_url")]
641 | public Uri NextUrl { get; set; }
642 | }
643 |
644 | public partial class IllustRecommended
645 | {
646 | [JsonProperty("illusts")]
647 | public UserPreviewIllust[] Illusts { get; set; }
648 |
649 | [JsonProperty("ranking_illusts")]
650 | public dynamic[] RankingIllusts { get; set; }
651 |
652 | [JsonProperty("contest_exists")]
653 | public bool ContestExists { get; set; }
654 |
655 | [JsonProperty("privacy_policy")]
656 | public PrivacyPolicy PrivacyPolicy { get; set; }
657 |
658 | [JsonProperty("next_url")]
659 | public Uri NextUrl { get; set; }
660 | }
661 |
662 | public partial class IllustCommentAddResult
663 | {
664 | [JsonProperty("comment")]
665 | public Comment Comment { get; set; }
666 | }
667 |
668 | public partial class Comment
669 | {
670 | [JsonProperty("id")]
671 | public long Id { get; set; }
672 |
673 | [JsonProperty("comment")]
674 | public string CommentComment { get; set; }
675 |
676 | [JsonProperty("date")]
677 | public string Date { get; set; }
678 |
679 | [JsonProperty("user")]
680 | public IllustUser User { get; set; }
681 |
682 | [JsonProperty("has_replies", NullValueHandling = NullValueHandling.Ignore)]
683 | public bool? HasReplies { get; set; }
684 |
685 | [JsonProperty("parent_comment", NullValueHandling = NullValueHandling.Ignore)]
686 | public Comment ParentComment { get; set; }
687 | }
688 |
689 | public partial class IllustComments
690 | {
691 | [JsonProperty("total_comments")]
692 | public long TotalComments { get; set; }
693 |
694 | [JsonProperty("comments")]
695 | public Comment[] Comments { get; set; }
696 |
697 | [JsonProperty("next_url")]
698 | public Uri NextUrl { get; set; }
699 | }
700 |
701 | public partial class IllustDetail
702 | {
703 | [JsonProperty("illust")]
704 | public UserPreviewIllust Illust { get; set; }
705 | }
706 |
707 | public partial class UserDetail
708 | {
709 | [JsonProperty("user")]
710 | public IllustUser User { get; set; }
711 |
712 | [JsonProperty("profile")]
713 | public Profile Profile { get; set; }
714 |
715 | [JsonProperty("profile_publicity")]
716 | public ProfilePublicity ProfilePublicity { get; set; }
717 |
718 | [JsonProperty("workspace")]
719 | public Workspace Workspace { get; set; }
720 | }
721 |
722 | public partial class Profile
723 | {
724 | [JsonProperty("webpage")]
725 | public dynamic Webpage { get; set; }
726 |
727 | [JsonProperty("gender")]
728 | public string Gender { get; set; }
729 |
730 | [JsonProperty("birth")]
731 | public string Birth { get; set; }
732 |
733 | [JsonProperty("birth_day")]
734 | public string BirthDay { get; set; }
735 |
736 | [JsonProperty("birth_year")]
737 | public long BirthYear { get; set; }
738 |
739 | [JsonProperty("region")]
740 | public string Region { get; set; }
741 |
742 | [JsonProperty("address_id")]
743 | public long AddressId { get; set; }
744 |
745 | [JsonProperty("country_code")]
746 | public string CountryCode { get; set; }
747 |
748 | [JsonProperty("job")]
749 | public string Job { get; set; }
750 |
751 | [JsonProperty("job_id")]
752 | public long JobId { get; set; }
753 |
754 | [JsonProperty("total_follow_users")]
755 | public long TotalFollowUsers { get; set; }
756 |
757 | [JsonProperty("total_mypixiv_users")]
758 | public long TotalMypixivUsers { get; set; }
759 |
760 | [JsonProperty("total_illusts")]
761 | public long TotalIllusts { get; set; }
762 |
763 | [JsonProperty("total_manga")]
764 | public long TotalManga { get; set; }
765 |
766 | [JsonProperty("total_novels")]
767 | public long TotalNovels { get; set; }
768 |
769 | [JsonProperty("total_illust_bookmarks_public")]
770 | public long TotalIllustBookmarksPublic { get; set; }
771 |
772 | [JsonProperty("total_illust_series")]
773 | public long TotalIllustSeries { get; set; }
774 |
775 | [JsonProperty("total_novel_series")]
776 | public long TotalNovelSeries { get; set; }
777 |
778 | [JsonProperty("background_image_url")]
779 | public Uri BackgroundImageUrl { get; set; }
780 |
781 | [JsonProperty("twitter_account")]
782 | public string TwitterAccount { get; set; }
783 |
784 | [JsonProperty("twitter_url")]
785 | public Uri TwitterUrl { get; set; }
786 |
787 | [JsonProperty("pawoo_url")]
788 | public Uri PawooUrl { get; set; }
789 |
790 | [JsonProperty("is_premium")]
791 | public bool IsPremium { get; set; }
792 |
793 | [JsonProperty("is_using_custom_profile_image")]
794 | public bool IsUsingCustomProfileImage { get; set; }
795 | }
796 |
797 | public partial class ProfilePublicity
798 | {
799 | [JsonProperty("gender")]
800 | public string Gender { get; set; }
801 |
802 | [JsonProperty("region")]
803 | public string Region { get; set; }
804 |
805 | [JsonProperty("birth_day")]
806 | public string BirthDay { get; set; }
807 |
808 | [JsonProperty("birth_year")]
809 | public string BirthYear { get; set; }
810 |
811 | [JsonProperty("job")]
812 | public string Job { get; set; }
813 |
814 | [JsonProperty("pawoo")]
815 | public bool Pawoo { get; set; }
816 | }
817 |
818 | public partial class Workspace
819 | {
820 | [JsonProperty("pc")]
821 | public string Pc { get; set; }
822 |
823 | [JsonProperty("monitor")]
824 | public string Monitor { get; set; }
825 |
826 | [JsonProperty("tool")]
827 | public string Tool { get; set; }
828 |
829 | [JsonProperty("scanner")]
830 | public string Scanner { get; set; }
831 |
832 | [JsonProperty("tablet")]
833 | public string Tablet { get; set; }
834 |
835 | [JsonProperty("mouse")]
836 | public string Mouse { get; set; }
837 |
838 | [JsonProperty("printer")]
839 | public string Printer { get; set; }
840 |
841 | [JsonProperty("desktop")]
842 | public string Desktop { get; set; }
843 |
844 | [JsonProperty("music")]
845 | public string Music { get; set; }
846 |
847 | [JsonProperty("desk")]
848 | public string Desk { get; set; }
849 |
850 | [JsonProperty("chair")]
851 | public string Chair { get; set; }
852 |
853 | [JsonProperty("comment")]
854 | public string Comment { get; set; }
855 |
856 | [JsonProperty("workspace_image_url")]
857 | public Uri WorkspaceImageUrl { get; set; }
858 | }
859 |
860 | public partial class AuthResult
861 | {
862 | [JsonProperty("response")]
863 | public Response Response { get; set; }
864 | }
865 |
866 | public partial class Response
867 | {
868 | [JsonProperty("access_token")]
869 | public string AccessToken { get; set; }
870 |
871 | [JsonProperty("expires_in")]
872 | public long ExpiresIn { get; set; }
873 |
874 | [JsonProperty("token_type")]
875 | public string TokenType { get; set; }
876 |
877 | [JsonProperty("scope")]
878 | public string Scope { get; set; }
879 |
880 | [JsonProperty("refresh_token")]
881 | public string RefreshToken { get; set; }
882 |
883 | [JsonProperty("user")]
884 | public ResponseUser User { get; set; }
885 |
886 | [JsonProperty("device_token")]
887 | public string DeviceToken { get; set; }
888 | }
889 |
890 | public partial class ResponseUser
891 | {
892 | [JsonProperty("profile_image_urls")]
893 | public ProfileImageUrls ProfileImageUrls { get; set; }
894 |
895 | [JsonProperty("id")]
896 | public string Id { get; set; }
897 |
898 | [JsonProperty("name")]
899 | public string Name { get; set; }
900 |
901 | [JsonProperty("account")]
902 | public string Account { get; set; }
903 |
904 | [JsonProperty("mail_address")]
905 | public string MailAddress { get; set; }
906 |
907 | [JsonProperty("is_premium")]
908 | public bool IsPremium { get; set; }
909 |
910 | [JsonProperty("x_restrict")]
911 | public long XRestrict { get; set; }
912 |
913 | [JsonProperty("is_mail_authorized")]
914 | public bool IsMailAuthorized { get; set; }
915 | }
916 |
917 | public partial class ProfileImageUrls
918 | {
919 | [JsonProperty("px_16x16")]
920 | public Uri Px16X16 { get; set; }
921 |
922 | [JsonProperty("px_50x50")]
923 | public Uri Px50X50 { get; set; }
924 |
925 | [JsonProperty("px_170x170")]
926 | public Uri Px170X170 { get; set; }
927 | }
928 |
929 | public partial class ShowcaseArticle
930 | {
931 | public static ShowcaseArticle FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
932 | }
933 |
934 | public partial class UgoiraMetadata
935 | {
936 | public static UgoiraMetadata FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
937 | }
938 |
939 | public partial class UserList
940 | {
941 | public static UserList FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
942 | }
943 |
944 | public partial class UserMyPixiv
945 | {
946 | public static UserFollowList FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
947 | }
948 |
949 | public partial class UserFollower
950 | {
951 | public static UserFollowList FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
952 | }
953 |
954 | public partial class UserFollowList
955 | {
956 | public static UserFollowList FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
957 | }
958 |
959 | public partial class UserBookmarkTags
960 | {
961 | public static UserBookmarkTags FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
962 | }
963 |
964 | public partial class IllustBookmarkDetail
965 | {
966 | public static IllustBookmarkDetail FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
967 | }
968 |
969 | public partial class SearchIllustResult
970 | {
971 | public static SearchIllustResult FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
972 | }
973 |
974 | public partial class TrendingTagsIllust
975 | {
976 | public static TrendingTagsIllust FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
977 | }
978 |
979 | public partial class IllustRanking
980 | {
981 | public static UserIllusts FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
982 | }
983 |
984 | public partial class IllustRecommended
985 | {
986 | public static IllustRecommended FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
987 | }
988 |
989 | public partial class IllustRelated
990 | {
991 | public static UserIllusts FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
992 | }
993 |
994 | public partial class IllustCommentAddResult
995 | {
996 | public static IllustCommentAddResult FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
997 | }
998 |
999 | public partial class IllustComments
1000 | {
1001 | public static IllustComments FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1002 | }
1003 |
1004 | public partial class IllustDetail
1005 | {
1006 | public static IllustDetail FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1007 | }
1008 |
1009 | public partial class IllustFollow
1010 | {
1011 | public static UserIllusts FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1012 | }
1013 |
1014 | public partial class UserBookmarksIllust
1015 | {
1016 | public static UserIllusts FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1017 | }
1018 |
1019 | public partial class UserIllusts
1020 | {
1021 | public static UserIllusts FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1022 | }
1023 |
1024 | public partial class UserDetail
1025 | {
1026 | public static UserDetail FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1027 | }
1028 |
1029 | public partial class AuthResult
1030 | {
1031 | public static AuthResult FromJson(string json) => JsonConvert.DeserializeObject(json, PixivCS.Objects.Converter.Settings);
1032 | }
1033 |
1034 | public static class Serialize
1035 | {
1036 | public static string ToJson(this ShowcaseArticle self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1037 | public static string ToJson(this UgoiraMetadata self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1038 | public static string ToJson(this UserList self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1039 | public static string ToJson(this UserFollowList self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1040 | public static string ToJson(this UserBookmarkTags self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1041 | public static string ToJson(this IllustBookmarkDetail self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1042 | public static string ToJson(this SearchIllustResult self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1043 | public static string ToJson(this TrendingTagsIllust self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1044 | public static string ToJson(this UserIllusts self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1045 | public static string ToJson(this IllustRecommended self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1046 | public static string ToJson(this IllustCommentAddResult self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1047 | public static string ToJson(this IllustComments self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1048 | public static string ToJson(this IllustDetail self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1049 | public static string ToJson(this UserDetail self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1050 | public static string ToJson(this AuthResult self) => JsonConvert.SerializeObject(self, PixivCS.Objects.Converter.Settings);
1051 | }
1052 |
1053 | internal static class Converter
1054 | {
1055 | public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
1056 | {
1057 | MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
1058 | DateParseHandling = DateParseHandling.None,
1059 | Converters =
1060 | {
1061 | new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
1062 | },
1063 | };
1064 | }
1065 | }
1066 |
--------------------------------------------------------------------------------
/PixivAppAPI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Web;
8 |
9 | namespace PixivCS
10 | {
11 | public class PixivAppAPI : PixivBaseAPI
12 | {
13 | public PixivAppAPI(string AccessToken, string RefreshToken, string UserID, bool ExperimentalConnection = false) :
14 | base(AccessToken, RefreshToken, UserID, ExperimentalConnection)
15 | { }
16 |
17 | public PixivAppAPI() : base() { }
18 |
19 | public PixivAppAPI(PixivBaseAPI BaseAPI) : base(BaseAPI) { }
20 |
21 | public async Task RequestCall(string Method, string Url,
22 | Dictionary Headers = null, List<(string, string)> Query = null,
23 | HttpContent Body = null, bool RequireAuth = true)
24 | {
25 | var headers = Headers ?? new Dictionary();
26 | if (!(headers.ContainsKey("User-Agent") || headers.ContainsKey("user-agent")))
27 | {
28 | headers.Add("App-OS", "ios");
29 | headers.Add("App-OS-Version", "10.3.1");
30 | headers.Add("App-Version", "6.7.1");
31 | headers.Add("User-Agent", "PixivIOSApp/6.7.1 (iOS 10.3.1; iPhone8,1)");
32 | }
33 | if (RequireAuth) headers.Add("Authorization", string.Format("Bearer {0}", AccessToken));
34 | return await base.RequestCall(Method, Url, headers, Query, Body);
35 | }
36 |
37 | //用户详情
38 | public async Task GetUserDetailAsync(string UserID, string Filter = "for_ios",
39 | bool RequireAuth = true)
40 | {
41 | string url = "https://app-api.pixiv.net/v1/user/detail";
42 | List<(string, string)> query = new List<(string, string)>
43 | {
44 | ("user_id", UserID),
45 | ("filter", Filter)
46 | };
47 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
48 | return Objects.UserDetail.FromJson(await GetResponseString(res));
49 | }
50 |
51 | //用户作品
52 | public async Task GetUserIllustsAsync(string UserID, string IllustType = "illust",
53 | string Filter = "for_ios", string Offset = null, bool RequireAuth = true)
54 | {
55 | string url = "https://app-api.pixiv.net/v1/user/illusts";
56 | List<(string, string)> query = new List<(string, string)>
57 | {
58 | ("user_id", UserID),
59 | ("filter", Filter)
60 | };
61 | if (!string.IsNullOrEmpty(IllustType)) query.Add(("type", IllustType));
62 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
63 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
64 | return Objects.UserIllusts.FromJson(await GetResponseString(res));
65 | }
66 |
67 | //用户收藏
68 | public async Task GetUserBookmarksIllustAsync(string UserID, string Restrict = "public",
69 | string Filter = "for_ios", string MaxBookmarkID = null, string Tag = null,
70 | bool RequireAuth = true)
71 | {
72 | string url = "https://app-api.pixiv.net/v1/user/bookmarks/illust";
73 | List<(string, string)> query = new List<(string, string)>
74 | {
75 | ("user_id", UserID),
76 | ("restrict", Restrict),
77 | ("filter", Filter)
78 | };
79 | if (!string.IsNullOrEmpty(MaxBookmarkID)) query.Add(("max_bookmark_id", MaxBookmarkID));
80 | if (!string.IsNullOrEmpty(Tag)) query.Add(("tag", Tag));
81 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
82 | return Objects.UserBookmarksIllust.FromJson(await GetResponseString(res));
83 | }
84 |
85 | //关注者的新作品
86 | public async Task GetIllustFollowAsync(string Restrict = "public", string Offset = null,
87 | bool RequireAuth = true)
88 | {
89 | string url = "https://app-api.pixiv.net/v2/illust/follow";
90 | List<(string, string)> query = new List<(string, string)>
91 | {
92 | ("restrict", Restrict)
93 | };
94 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
95 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
96 | return Objects.IllustFollow.FromJson(await GetResponseString(res));
97 | }
98 |
99 | //作品详情
100 | public async Task GetIllustDetailAsync(string IllustID, bool RequireAuth = true)
101 | {
102 | string url = "https://app-api.pixiv.net/v1/illust/detail";
103 | List<(string, string)> query = new List<(string, string)>
104 | {
105 | ("illust_id", IllustID)
106 | };
107 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
108 | return Objects.IllustDetail.FromJson(await GetResponseString(res));
109 | }
110 |
111 | //作品评论
112 | //IncludeTotalComments决定是否在返回的JSON中包含总评论数
113 | public async Task GetIllustCommentsAsync(string IllustID, string Offset = null,
114 | bool? IncludeTotalComments = null, bool RequireAuth = true)
115 | {
116 | string url = "https://app-api.pixiv.net/v1/illust/comments";
117 | List<(string, string)> query = new List<(string, string)>
118 | {
119 | ("illust_id", IllustID)
120 | };
121 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
122 | if (IncludeTotalComments != null) query.Add(("include_total_comments",
123 | IncludeTotalComments.Value ? "true" : "false"));
124 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
125 | return Objects.IllustComments.FromJson(await GetResponseString(res));
126 | }
127 |
128 | //发表评论
129 | public async Task PostIllustCommentAddAsync(string IllustID, string Comment,
130 | string ParentCommentID = null, bool RequireAuth = true)
131 | {
132 | string url = "https://app-api.pixiv.net/v1/illust/comment/add";
133 | Dictionary data = new Dictionary
134 | {
135 | { "illust_id", IllustID },
136 | { "comment", Comment }
137 | };
138 | if (!string.IsNullOrWhiteSpace(ParentCommentID))
139 | data.Add("parent_comment_id", ParentCommentID);
140 | var res = await RequestCall("POST", url, Body: new FormUrlEncodedContent(data),
141 | RequireAuth: RequireAuth);
142 | return Objects.IllustCommentAddResult.FromJson(await GetResponseString(res));
143 | }
144 |
145 | //相关作品
146 | public async Task GetIllustRelatedAsync(string IllustID, string Filter = "for_ios",
147 | List SeedIllustIDs = null, bool RequireAuth = true)
148 | {
149 | string url = "https://app-api.pixiv.net/v2/illust/related";
150 | List<(string, string)> query = new List<(string, string)>
151 | {
152 | ("illust_id", IllustID),
153 | ("filter", Filter)
154 | };
155 | if (SeedIllustIDs != null)
156 | {
157 | foreach (var i in SeedIllustIDs)
158 | query.Add(("seed_illust_ids[]", i));
159 | }
160 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
161 | return Objects.IllustRelated.FromJson(await GetResponseString(res));
162 | }
163 |
164 | //首页推荐
165 | //content_type: [illust, manga]
166 | public async Task GetIllustRecommendedAsync(string ContentType = "illust",
167 | bool IncludeRankingLabel = true, string Filter = "for_ios",
168 | string MaxBookmarkIDForRecommended = null,
169 | string MinBookmarkIDForRecentIllust = null, string Offset = null,
170 | bool? IncludeRankingIllusts = null, List BookmarkIllustIDs = null,
171 | string IncludePrivacyPolicy = null, bool RequireAuth = true)
172 | {
173 | string url = RequireAuth ? "https://app-api.pixiv.net/v1/illust/recommended" :
174 | "https://app-api.pixiv.net/v1/illust/recommended-nologin";
175 | List<(string, string)> query = new List<(string, string)>
176 | {
177 | ("content_type", ContentType),
178 | ("include_ranking_label", IncludeRankingLabel ? "true" : "false"),
179 | ("filter", Filter)
180 | };
181 | if (!string.IsNullOrEmpty(MaxBookmarkIDForRecommended))
182 | query.Add(("max_bookmark_id_for_recommend", MaxBookmarkIDForRecommended));
183 | if (!string.IsNullOrEmpty(MinBookmarkIDForRecentIllust))
184 | query.Add(("min_bookmark_id_for_recent_illust", MinBookmarkIDForRecentIllust));
185 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
186 | if (IncludeRankingIllusts != null)
187 | query.Add(("include_ranking_illusts", IncludeRankingIllusts.Value ? "true" : "false"));
188 | string ids = "";
189 | if (BookmarkIllustIDs != null)
190 | foreach (var i in BookmarkIllustIDs)
191 | ids += (i + ",");
192 | if (ids != "")
193 | {
194 | ids.TrimEnd(',');
195 | query.Add(("bookmark_illust_ids", ids));
196 | }
197 | if (!string.IsNullOrEmpty(IncludePrivacyPolicy))
198 | query.Add(("include_privacy_policy", IncludePrivacyPolicy));
199 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
200 | return Objects.IllustRecommended.FromJson(await GetResponseString(res));
201 | }
202 |
203 | //作品排行
204 | //mode: [day, week, month, day_male, day_female, week_original, week_rookie, day_manga]
205 | //date: yyyy-mm-dd
206 | public async Task GetIllustRankingAsync(string Mode = "day", string Filter = "for_ios",
207 | string Date = null, string Offset = null, bool RequireAuth = true)
208 | {
209 | string url = "https://app-api.pixiv.net/v1/illust/ranking";
210 | List<(string, string)> query = new List<(string, string)>
211 | {
212 | ("mode", Mode),
213 | ("filter", Filter)
214 | };
215 | if (!string.IsNullOrEmpty(Date)) query.Add(("date", Date));
216 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
217 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
218 | return Objects.IllustRanking.FromJson(await GetResponseString(res));
219 | }
220 |
221 | //趋势标签
222 | public async Task GetTrendingTagsIllustAsync(string Filter = "for_ios", bool RequireAuth = true)
223 | {
224 | string url = "https://app-api.pixiv.net/v1/trending-tags/illust";
225 | List<(string, string)> query = new List<(string, string)>
226 | {
227 | ("filter", Filter)
228 | };
229 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
230 | return Objects.TrendingTagsIllust.FromJson(await GetResponseString(res));
231 | }
232 |
233 | //搜索
234 | //search_target - 搜索类型
235 | // partial_match_for_tags - 标签部分一致
236 | // exact_match_for_tags - 标签完全一致
237 | // title_and_caption - 标题说明文
238 | //sort: [date_desc, date_asc]
239 | //duration: [within_last_day, within_last_week, within_last_month]
240 | public async Task GetSearchIllustAsync(string Word, string SearchTarget = "partial_match_for_tags",
241 | string Sort = "date_desc", string Duration = null, string Filter = "for_ios", string Offset = null,
242 | bool RequireAuth = true)
243 | {
244 | string url = "https://app-api.pixiv.net/v1/search/illust";
245 | List<(string, string)> query = new List<(string, string)>
246 | {
247 | ("word", Word),
248 | ("search_target", SearchTarget),
249 | ("sort", Sort),
250 | ("filter", Filter)
251 | };
252 | if (!string.IsNullOrEmpty(Duration)) query.Add(("duration", Duration));
253 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
254 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
255 | return Objects.SearchIllustResult.FromJson(await GetResponseString(res));
256 | }
257 |
258 | //作品收藏详情
259 | public async Task GetIllustBookmarkDetailAsync(string IllustID, bool RequireAuth = true)
260 | {
261 | string url = "https://app-api.pixiv.net/v2/illust/bookmark/detail";
262 | List<(string, string)> query = new List<(string, string)>
263 | {
264 | ("illust_id", IllustID)
265 | };
266 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
267 | return Objects.IllustBookmarkDetail.FromJson(await GetResponseString(res));
268 | }
269 |
270 | //新增收藏
271 | public async Task PostIllustBookmarkAddAsync(string IllustID, string Restrict = "public",
272 | List Tags = null, bool RequireAuth = true)
273 | {
274 | string url = "https://app-api.pixiv.net/v2/illust/bookmark/add";
275 | Dictionary data = new Dictionary
276 | {
277 | { "illust_id", IllustID },
278 | { "restrict", Restrict }
279 | };
280 | string tags = "";
281 | if (Tags != null)
282 | foreach (var i in Tags)
283 | tags += (i + " ");
284 | tags = tags.Trim();
285 | if (tags != "")
286 | data.Add("tags", HttpUtility.UrlEncode(tags));
287 | await RequestCall("POST", url, Body: new FormUrlEncodedContent(data),
288 | RequireAuth: RequireAuth);
289 | }
290 |
291 | //删除收藏
292 | public async Task PostIllustBookmarkDeleteAsync(string IllustID, bool RequireAuth = true)
293 | {
294 | string url = "https://app-api.pixiv.net/v1/illust/bookmark/delete";
295 | Dictionary data = new Dictionary
296 | {
297 | { "illust_id", IllustID }
298 | };
299 | await RequestCall("POST", url, Body: new FormUrlEncodedContent(data),
300 | RequireAuth: RequireAuth);
301 | }
302 |
303 | //用户收藏标签列表
304 | public async Task GetUserBookmarkTagsIllustAsync(string Restrict = "public", string Offset = null,
305 | bool RequireAuth = true)
306 | {
307 | string url = "https://app-api.pixiv.net/v1/user/bookmark-tags/illust";
308 | List<(string, string)> query = new List<(string, string)>
309 | {
310 | ("restrict", Restrict)
311 | };
312 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
313 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
314 | return Objects.UserBookmarkTags.FromJson(await GetResponseString(res));
315 | }
316 |
317 | //Following用户列表
318 | public async Task GetUserFollowingAsync(string UserID, string Restrict = "public",
319 | string Offset = null, bool RequireAuth = true)
320 | {
321 | string url = "https://app-api.pixiv.net/v1/user/following";
322 | List<(string, string)> query = new List<(string, string)>
323 | {
324 | ("user_id", UserID),
325 | ("restrict", Restrict)
326 | };
327 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
328 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
329 | return Objects.UserFollowList.FromJson(await GetResponseString(res));
330 | }
331 |
332 | //Followers用户列表
333 | public async Task GetUserFollowerAsync(string UserID, string Restrict = "public",
334 | string Offset = null, bool RequireAuth = true)
335 | {
336 | string url = "https://app-api.pixiv.net/v1/user/follower";
337 | List<(string, string)> query = new List<(string, string)>
338 | {
339 | ("user_id", UserID),
340 | ("restrict", Restrict)
341 | };
342 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
343 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
344 | return Objects.UserFollower.FromJson(await GetResponseString(res));
345 | }
346 |
347 | //关注用户
348 | public async Task PostUserFollowAddAsync(string UserID, string Restrict = "public",
349 | bool RequireAuth = true)
350 | {
351 | string url = "https://app-api.pixiv.net/v1/user/follow/add";
352 | Dictionary data = new Dictionary
353 | {
354 | { "user_id", UserID },
355 | { "restrict", Restrict }
356 | };
357 | await RequestCall("POST", url, Body: new FormUrlEncodedContent(data),
358 | RequireAuth: RequireAuth);
359 | }
360 |
361 | //取关用户
362 | public async Task PostUserFollowDeleteAsync(string UserID, string Restrict = "public",
363 | bool RequireAuth = true)
364 | {
365 | string url = "https://app-api.pixiv.net/v1/user/follow/delete";
366 | Dictionary data = new Dictionary
367 | {
368 | { "user_id", UserID },
369 | { "restrict", Restrict }
370 | };
371 | await RequestCall("POST", url, Body: new FormUrlEncodedContent(data),
372 | RequireAuth: RequireAuth);
373 | }
374 |
375 | //好P友
376 | public async Task GetUserMyPixivAsync(string UserID, string Offset = null,
377 | bool RequireAuth = true)
378 | {
379 | string url = "https://app-api.pixiv.net/v1/user/mypixiv";
380 | List<(string, string)> query = new List<(string, string)>
381 | {
382 | ("user_id", UserID)
383 | };
384 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
385 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
386 | return Objects.UserMyPixiv.FromJson(await GetResponseString(res));
387 | }
388 |
389 | //黑名单用户
390 | public async Task GetUserListAsync(string UserID, string Filter = "for_ios",
391 | string Offset = null, bool RequireAuth = true)
392 | {
393 | string url = "https://app-api.pixiv.net/v2/user/list";
394 | List<(string, string)> query = new List<(string, string)>
395 | {
396 | ("user_id", UserID),
397 | ("filter", Filter)
398 | };
399 | if (!string.IsNullOrEmpty(Offset)) query.Add(("offset", Offset));
400 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
401 | return Objects.UserList.FromJson(await GetResponseString(res));
402 | }
403 |
404 | //Ugoira信息
405 | public async Task GetUgoiraMetadataAsync(string IllustID, bool RequireAuth = true)
406 | {
407 | string url = "https://app-api.pixiv.net/v1/ugoira/metadata";
408 | List<(string, string)> query = new List<(string, string)>
409 | {
410 | ("illust_id", IllustID)
411 | };
412 | var res = await RequestCall("GET", url, Query: query, RequireAuth: RequireAuth);
413 | return Objects.UgoiraMetadata.FromJson(await GetResponseString(res));
414 | }
415 |
416 | //特辑详情(伪装成Chrome)
417 | public async Task GetShowcaseArticleAsync(string ShowcaseID)
418 | {
419 | string url = "https://www.pixiv.net/ajax/showcase/article";
420 | Dictionary headers = new Dictionary
421 | {
422 | { "User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" },
423 | { "Referer", "https://www.pixiv.net" }
424 | };
425 | List<(string, string)> query = new List<(string, string)>()
426 | {
427 | ("article_id", ShowcaseID)
428 | };
429 | var res = await RequestCall("GET", url, headers, Query: query, RequireAuth: false);
430 | return Objects.ShowcaseArticle.FromJson(await GetResponseString(res));
431 | }
432 | }
433 | }
434 |
--------------------------------------------------------------------------------
/PixivBaseAPI.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Specialized;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Linq;
7 | using System.Net;
8 | using System.Net.Http;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Web;
12 | using System.Security.Cryptography;
13 | using System.Timers;
14 |
15 | namespace PixivCS
16 | {
17 | public class PixivException : Exception
18 | {
19 | public PixivException() { }
20 | public PixivException(string msg) : base(msg) { }
21 | }
22 |
23 | public class RefreshEventArgs : EventArgs
24 | {
25 | public string NewAccessToken { get; }
26 | public string NewRefreshToken { get; }
27 | public bool IsSuccessful { get; }
28 |
29 | public RefreshEventArgs(string NewAccessToken, string NewRefreshToken, bool IsSuccessful)
30 | {
31 | this.NewAccessToken = NewAccessToken;
32 | this.NewRefreshToken = NewRefreshToken;
33 | this.IsSuccessful = IsSuccessful;
34 | }
35 | }
36 |
37 | public class PixivBaseAPI
38 | {
39 |
40 | // 参考自下面的链接
41 | // https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client#create-and-initialize-httpclient
42 | // https://stackoverflow.com/questions/15705092/do-httpclient-and-httpclienthandler-have-to-be-disposed
43 | private static HttpClient _client = new HttpClient();
44 |
45 | //允许设置代理
46 | public static void SetProxy(IWebProxy Proxy)
47 | {
48 | HttpClientHandler handler = new HttpClientHandler()
49 | {
50 | Proxy = Proxy
51 | };
52 | _client = new HttpClient(handler, true);
53 | }
54 |
55 | //清空代理
56 | public static void ClearProxy()
57 | {
58 | _client = new HttpClient();
59 | }
60 |
61 | internal string clientID = "MOBrBDS8blbauoSck0ZfDbtuzpyT";
62 | internal string clientSecret = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
63 | internal string hashSecret = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c";
64 |
65 | public Dictionary TargetIPs { get; set; } = new Dictionary()
66 | {
67 | {"oauth.secure.pixiv.net","210.140.131.188" },
68 | {"www.pixiv.net","210.140.131.188" },
69 | {"app-api.pixiv.net","210.140.131.188" }
70 | };
71 |
72 | public Dictionary TargetSubjects { get; set; } = new Dictionary()
73 | {
74 | {"210.140.131.188","CN=*.pixiv.net, O=pixiv Inc., OU=Development department, L=Shibuya-ku, S=Tokyo, C=JP" },
75 | {"210.140.92.142","CN=*.pximg.net, OU=Domain Control Validated" }
76 | };
77 | public Dictionary TargetSNs { get; set; } = new Dictionary()
78 | {
79 | {"210.140.131.188","346B03F05A00DD2FFAE58853" },
80 | {"210.140.92.142","2387DB20E84EFCF82492545C" }
81 | };
82 | public Dictionary TargetTPs { get; set; } = new Dictionary()
83 | {
84 | {"210.140.131.188","07954CC4735FA33B629899E1DC2591500B090FB5" },
85 | {"210.140.92.142","F4A431620F42E4D10EB42621C6948E3CD5014FB0" }
86 | };
87 |
88 | public string AccessToken { get; internal set; }
89 | public string RefreshToken { get; internal set; }
90 | public string UserID { get; internal set; }
91 | public bool ExperimentalConnection { get; set; }
92 |
93 | public PixivBaseAPI(string AccessToken, string RefreshToken, string UserID,
94 | bool ExperimentalConnection = false)
95 | {
96 | this.AccessToken = AccessToken;
97 | this.RefreshToken = RefreshToken;
98 | this.UserID = UserID;
99 | this.ExperimentalConnection = ExperimentalConnection;
100 | }
101 |
102 | public PixivBaseAPI() : this(null, null, null) { }
103 |
104 | public PixivBaseAPI(PixivBaseAPI BaseAPI) :
105 | this(BaseAPI.AccessToken, BaseAPI.RefreshToken, BaseAPI.UserID, BaseAPI.ExperimentalConnection)
106 | { }
107 |
108 | //用于生成带参数的url
109 | private static string GetQueryString(List<(string, string)> query)
110 | {
111 | var array = (from i in query
112 | select string.Format("{0}={1}", HttpUtility.UrlEncode(i.Item1),
113 | HttpUtility.UrlEncode(i.Item2)))
114 | .ToArray();
115 | return "?" + string.Join("&", array);
116 | }
117 |
118 | public void RequireAuth()
119 | {
120 | if (AccessToken == null) throw new PixivException("Authentication required!");
121 | }
122 |
123 | public async Task RequestCall(string Method, string Url,
124 | Dictionary Headers = null, List<(string, string)> Query = null,
125 | HttpContent Body = null)
126 | {
127 | string queryUrl = Url + ((Query != null) ? GetQueryString(Query) : "");
128 | if (ExperimentalConnection && TargetIPs.ContainsKey(new Uri(queryUrl).Host))
129 | {
130 | #region 无 底 深 坑
131 | var targetIP = TargetIPs[new Uri(queryUrl).Host];
132 | var targetSubject = TargetSubjects[targetIP];
133 | var targetSN = TargetSNs[targetIP];
134 | var targetTP = TargetTPs[targetIP];
135 | using (var connection = await Utilities.CreateConnectionAsync(targetIP, (cert) =>
136 | cert.Subject == targetSubject && cert.SerialNumber == targetSN && cert.Thumbprint == targetTP))
137 | {
138 | var httpRequest = await Utilities.ConstructHTTPAsync(Method, queryUrl, Headers, Body);
139 | await connection.WriteAsync(httpRequest, 0, httpRequest.Length);
140 | using (var memory = new MemoryStream())
141 | {
142 | await connection.CopyToAsync(memory);
143 | memory.Position = 0;
144 | var data = memory.ToArray();
145 | var index = Utilities.BinaryMatch(data, Encoding.UTF8.GetBytes("\r\n\r\n")) + 4;
146 | var headers = Encoding.UTF8.GetString(data, 0, index);
147 | memory.Position = index;
148 | byte[] result;
149 | HttpStatusCode statusCode;
150 | Dictionary headersDictionary = new Dictionary();
151 | foreach (var header in headers.Split(new[] { "\r\n" }, StringSplitOptions.None))
152 | {
153 | if (string.IsNullOrWhiteSpace(header)) break;
154 | if (!header.Contains(": "))
155 | {
156 | var status = header.Split(new[] { " " }, StringSplitOptions.None);
157 | statusCode = (HttpStatusCode)Convert.ToInt32(status[1]);
158 | }
159 | else
160 | {
161 | var pair = header.Split(new[] { ": " }, StringSplitOptions.None);
162 | headersDictionary.Add(pair[0], pair[1]);
163 | }
164 | }
165 | if (headersDictionary.ContainsKey("Content-Encoding") &&
166 | headersDictionary["Content-Encoding"].Contains("gzip"))
167 | {
168 | using (GZipStream decompressionStream = new GZipStream(memory, CompressionMode.Decompress))
169 | using (var decompressedMemory = new MemoryStream())
170 | {
171 | await decompressionStream.CopyToAsync(decompressedMemory);
172 | decompressedMemory.Position = 0;
173 | result = decompressedMemory.ToArray();
174 | }
175 | }
176 | else
177 | {
178 | using (var resultMemory = new MemoryStream())
179 | {
180 | await memory.CopyToAsync(resultMemory);
181 | result = resultMemory.ToArray();
182 | }
183 | }
184 | if (headersDictionary.ContainsKey("Transfer-Encoding") &&
185 | headersDictionary["Transfer-Encoding"].Contains("chunked"))
186 | {
187 | //处理分块传输
188 | using (MemoryStream parsedChunckedResult = new MemoryStream())
189 | {
190 | parsedChunckedResult.Position = 0;
191 | int position = 0;
192 | bool lengthOrContent = false;
193 | int chunkLength = 0;
194 | List lengthList = new List();
195 | while (position < result.Length)
196 | {
197 | if (!lengthOrContent)
198 | {
199 | //分块长度信息
200 | if (result[position] == '\r')
201 | {
202 | position += 2;
203 | lengthOrContent = true;
204 | var lengthArray = lengthList.ToArray();
205 | chunkLength = Convert.ToInt32(Encoding.UTF8.GetString(lengthArray), 16);
206 | lengthList.Clear();
207 | }
208 | else
209 | {
210 | lengthList.Add(result[position]);
211 | position++;
212 | }
213 | }
214 | else
215 | {
216 | //末端
217 | if (chunkLength == 0) break;
218 | //分块内容
219 | await parsedChunckedResult.WriteAsync(result, position, chunkLength);
220 | position += chunkLength + 2;
221 | lengthOrContent = false;
222 | }
223 | }
224 | result = parsedChunckedResult.ToArray();
225 | }
226 | }
227 | var res = new HttpResponseMessage();
228 | res.Content = new ByteArrayContent(result);
229 | foreach (var pair in headersDictionary)
230 | {
231 | var added = res.Headers.TryAddWithoutValidation(pair.Key, pair.Value);
232 | if (!added) res.Content.Headers.Add(pair.Key, pair.Value);
233 | }
234 | return res;
235 | }
236 | }
237 | #endregion
238 | }
239 | else //传统手段
240 | {
241 | var allowMethods = new string[] { "get", "post" };
242 | if (!allowMethods.Any(m => m.Equals(Method, StringComparison.OrdinalIgnoreCase)))
243 | throw new PixivException("Unsupported method");
244 |
245 | var request = new HttpRequestMessage(new HttpMethod(Method), queryUrl);
246 | if (Headers != null)
247 | foreach (var pair in Headers)
248 | request.Headers.Add(pair.Key, pair.Value);
249 |
250 | if (Body != null)
251 | request.Content = Body;
252 |
253 | return await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
254 | }
255 | }
256 |
257 | //以字符串形式拿回Response
258 | public static async Task GetResponseString(HttpResponseMessage Response)
259 | {
260 | return await Response.Content.ReadAsStringAsync();
261 | }
262 |
263 | //以流形式拿回Response
264 | public static async Task GetResponseStream(HttpResponseMessage Response)
265 | {
266 | return await Response.Content.ReadAsStreamAsync();
267 | }
268 |
269 | public void SetClient(string ClientID, string ClientSecret, string HashSecret)
270 | {
271 | clientID = ClientID;
272 | clientSecret = ClientSecret;
273 | hashSecret = HashSecret;
274 | }
275 |
276 | //用户名和密码登录
277 | public async Task AuthAsync(string Username, string Password)
278 | {
279 | string MD5Hash(string Input)
280 | {
281 | if (string.IsNullOrEmpty(Input)) return null;
282 | using (var md5 = MD5.Create())
283 | {
284 | var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(Input.Trim()));
285 | StringBuilder builder = new StringBuilder();
286 | for (int i = 0; i < bytes.Length; i++)
287 | builder.Append(bytes[i].ToString("x2"));
288 | return builder.ToString();
289 | }
290 | }
291 | string url = "https://oauth.secure.pixiv.net/auth/token";
292 | string time = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss+00:00");
293 | Dictionary headers = new Dictionary
294 | {
295 | { "User-Agent", "PixivAndroidApp/5.0.64 (Android 6.0)" },
296 | { "X-Client-Time", time },
297 | { "X-Client-Hash", MD5Hash(time+hashSecret) }
298 | };
299 | Dictionary data = new Dictionary
300 | {
301 | { "get_secure_url", "1" },
302 | { "client_id", clientID },
303 | { "client_secret", clientSecret },
304 | { "grant_type", "password" },
305 | { "username", Username },
306 | { "password", Password }
307 | };
308 | var res = await RequestCall("POST", url, headers, Body: new FormUrlEncodedContent(data));
309 | int status = (int)res.StatusCode;
310 | if (!(status == 200 || status == 301 || status == 302))
311 | throw new PixivException("[ERROR] Auth() failed! Check Username and Password.");
312 | var resJSON = Objects.AuthResult.FromJson(await GetResponseString(res));
313 | AccessToken = resJSON.Response.AccessToken;
314 | UserID = resJSON.Response.User.Id;
315 | RefreshToken = resJSON.Response.RefreshToken;
316 | return resJSON;
317 | }
318 |
319 | //RefreshToken登录
320 | public async Task AuthAsync(string RefreshToken)
321 | {
322 | string url = "https://oauth.secure.pixiv.net/auth/token";
323 | Dictionary headers = new Dictionary
324 | {
325 | { "User-Agent", "PixivAndroidApp/5.0.64 (Android 6.0)" }
326 | };
327 | Dictionary data = new Dictionary
328 | {
329 | { "get_secure_url", "1" },
330 | { "client_id", clientID },
331 | { "client_secret", clientSecret },
332 | { "grant_type", "refresh_token" },
333 | { "refresh_token", RefreshToken }
334 | };
335 | var res = await RequestCall("POST", url, headers, Body: new FormUrlEncodedContent(data));
336 | int status = (int)res.StatusCode;
337 | if (!(status == 200 || status == 301 || status == 302))
338 | throw new PixivException("[ERROR] Auth() failed! Check Username and Password.");
339 | var resJSON = Objects.AuthResult.FromJson(await GetResponseString(res));
340 | AccessToken = resJSON.Response.AccessToken;
341 | UserID = resJSON.Response.User.Id;
342 | this.RefreshToken = resJSON.Response.RefreshToken;
343 | return resJSON;
344 | }
345 | }
346 | }
--------------------------------------------------------------------------------
/PixivCS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | true
5 | 0.5.12
6 | TobiichiAmane
7 |
8 | C# version of Pixiv API in .NET Standard 2.0. Used by Pixiv UWP.
9 | GPL-2.0-only
10 | https://github.com/tobiichiamane/pixivcs
11 | https://github.com/tobiichiamane/pixivcs
12 | git
13 | Pixiv PixivAPI
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/PixivCS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28803.156
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixivCS", "PixivCS.csproj", "{FE779F07-B97A-41E1-BBC6-731C2EB24E72}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixivCSTest", "..\PixivCSTest\PixivCSTest.csproj", "{ACAE5B30-730B-48CF-A153-999CB1EA83C6}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixivCSDesktopTest", "..\PixivCSDesktopTest\PixivCSDesktopTest.csproj", "{C56FAACD-5969-4737-915B-DD25DA796CB9}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|ARM = Debug|ARM
16 | Debug|ARM64 = Debug|ARM64
17 | Debug|x64 = Debug|x64
18 | Debug|x86 = Debug|x86
19 | Release|Any CPU = Release|Any CPU
20 | Release|ARM = Release|ARM
21 | Release|ARM64 = Release|ARM64
22 | Release|x64 = Release|x64
23 | Release|x86 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|ARM.ActiveCfg = Debug|Any CPU
29 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|ARM.Build.0 = Debug|Any CPU
30 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|ARM64.ActiveCfg = Debug|Any CPU
31 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|ARM64.Build.0 = Debug|Any CPU
32 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|x64.ActiveCfg = Debug|Any CPU
33 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|x64.Build.0 = Debug|Any CPU
34 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|x86.ActiveCfg = Debug|Any CPU
35 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Debug|x86.Build.0 = Debug|Any CPU
36 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|ARM.ActiveCfg = Release|Any CPU
39 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|ARM.Build.0 = Release|Any CPU
40 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|ARM64.ActiveCfg = Release|Any CPU
41 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|ARM64.Build.0 = Release|Any CPU
42 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|x64.ActiveCfg = Release|Any CPU
43 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|x64.Build.0 = Release|Any CPU
44 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|x86.ActiveCfg = Release|Any CPU
45 | {FE779F07-B97A-41E1-BBC6-731C2EB24E72}.Release|x86.Build.0 = Release|Any CPU
46 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|Any CPU.ActiveCfg = Debug|x86
47 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|Any CPU.Build.0 = Debug|x86
48 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|Any CPU.Deploy.0 = Debug|x86
49 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM.ActiveCfg = Debug|ARM
50 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM.Build.0 = Debug|ARM
51 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM.Deploy.0 = Debug|ARM
52 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM64.ActiveCfg = Debug|ARM64
53 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM64.Build.0 = Debug|ARM64
54 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|ARM64.Deploy.0 = Debug|ARM64
55 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x64.ActiveCfg = Debug|x64
56 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x64.Build.0 = Debug|x64
57 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x64.Deploy.0 = Debug|x64
58 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x86.ActiveCfg = Debug|x86
59 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x86.Build.0 = Debug|x86
60 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Debug|x86.Deploy.0 = Debug|x86
61 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|Any CPU.ActiveCfg = Release|x86
62 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM.ActiveCfg = Release|ARM
63 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM.Build.0 = Release|ARM
64 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM.Deploy.0 = Release|ARM
65 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM64.ActiveCfg = Release|ARM64
66 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM64.Build.0 = Release|ARM64
67 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|ARM64.Deploy.0 = Release|ARM64
68 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x64.ActiveCfg = Release|x64
69 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x64.Build.0 = Release|x64
70 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x64.Deploy.0 = Release|x64
71 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x86.ActiveCfg = Release|x86
72 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x86.Build.0 = Release|x86
73 | {ACAE5B30-730B-48CF-A153-999CB1EA83C6}.Release|x86.Deploy.0 = Release|x86
74 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
76 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
77 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|ARM.Build.0 = Debug|Any CPU
78 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|ARM64.ActiveCfg = Debug|Any CPU
79 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|ARM64.Build.0 = Debug|Any CPU
80 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|x64.ActiveCfg = Debug|Any CPU
81 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|x64.Build.0 = Debug|Any CPU
82 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|x86.ActiveCfg = Debug|Any CPU
83 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Debug|x86.Build.0 = Debug|Any CPU
84 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
85 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|Any CPU.Build.0 = Release|Any CPU
86 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|ARM.ActiveCfg = Release|Any CPU
87 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|ARM.Build.0 = Release|Any CPU
88 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|ARM64.ActiveCfg = Release|Any CPU
89 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|ARM64.Build.0 = Release|Any CPU
90 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|x64.ActiveCfg = Release|Any CPU
91 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|x64.Build.0 = Release|Any CPU
92 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|x86.ActiveCfg = Release|Any CPU
93 | {C56FAACD-5969-4737-915B-DD25DA796CB9}.Release|x86.Build.0 = Release|Any CPU
94 | EndGlobalSection
95 | GlobalSection(SolutionProperties) = preSolution
96 | HideSolutionNode = FALSE
97 | EndGlobalSection
98 | GlobalSection(ExtensibilityGlobals) = postSolution
99 | SolutionGuid = {1C63651D-AA55-45C0-AB69-CC7A8CD6F42D}
100 | EndGlobalSection
101 | EndGlobal
102 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PixivCS [](https://www.nuget.org/packages/PixivCS)
2 |
3 | This project contains a set of Pixiv APIs implemented using C# and can be used on .NET Standard 2.0 platforms. Code of this project is heavily based on [pixivpy](https://github.com/upbit/pixivpy).
--------------------------------------------------------------------------------
/SauceNAOJsonObjectReturn.txt:
--------------------------------------------------------------------------------
1 | DownLoad函数返回
2 | {
3 | "ext_urls": [
4 | "P站链接"
5 | ],
6 | "title": "图像标题",
7 | "pixiv_id": P ID(数字)
8 | "member_name": "作者名",
9 | "member_id": 作者ID(数字)
10 | }
11 | UpLoad函数返回
12 | {
13 | "id": "UFXVN6s",
14 | "title": null,
15 | "description": null,
16 | "datetime": 1570458561,
17 | "type": "image/jpeg",
18 | "animated": false,
19 | "width": 800,
20 | "height": 1022,
21 | "size": 530332,
22 | "views": 0,
23 | "bandwidth": 0,
24 | "vote": null,
25 | "favorite": false,
26 | "nsfw": null,
27 | "section": null,
28 | "account_url": null,
29 | "account_id": 0,
30 | "is_ad": false,
31 | "in_most_viral": false,
32 | "has_sound": false,
33 | "tags": [],
34 | "ad_type": 0,
35 | "ad_url": "",
36 | "edited": "0",
37 | "in_gallery": false,
38 | "deletehash": "cizJz0AAKoH4dUJ",
39 | "name": "",
40 | "link": "最终需要的链接"
41 | }
--------------------------------------------------------------------------------
/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Net.Security;
6 | using System.Net.Sockets;
7 | using System.Security.Cryptography.X509Certificates;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace PixivCS
12 | {
13 | internal static class Utilities
14 | {
15 | //不携带SNI的连接
16 | public static async Task CreateConnectionAsync(string TargetIP, Func CertValidation)
17 | {
18 | TcpClient client = new TcpClient();
19 | await client.ConnectAsync(TargetIP, 443);
20 | var networkStream = client.GetStream();
21 | var sslStream = new SslStream(networkStream, false, (sender, certificate, chain, errors) => CertValidation((X509Certificate2)certificate));
22 | try
23 | {
24 | await sslStream.AuthenticateAsClientAsync("");
25 | return sslStream;
26 | }
27 | catch (Exception e)
28 | {
29 | sslStream.Dispose();
30 | throw new PixivException(e.Message);
31 | }
32 | }
33 |
34 | //用于构造一个Http报文
35 | public static async Task ConstructHTTPAsync(string Method, string Url,
36 | Dictionary Headers, HttpContent Body)
37 | {
38 | StringBuilder builder = new StringBuilder();
39 | switch (Method.ToUpper())
40 | {
41 | case "GET":
42 | builder.AppendLine(string.Format("GET {0} HTTP/1.1", Url));
43 | foreach (var pair in Headers)
44 | builder.AppendLine(string.Format("{0}: {1}", pair.Key, pair.Value));
45 | if (!Headers.ContainsKey("Host"))
46 | builder.AppendLine(string.Format("Host: {0}", new Uri(Url).Host));
47 | if (!Headers.ContainsKey("Connection"))
48 | builder.AppendLine("Connection: Keep-Alive");
49 | if (!Headers.ContainsKey("Cache-Control"))
50 | builder.AppendLine("Cache-Control: no-cache");
51 | builder.AppendLine();
52 | break;
53 | case "POST":
54 | string bodyStr = "";
55 | builder.AppendLine(string.Format("POST {0} HTTP/1.1", Url));
56 | foreach (var pair in Headers)
57 | builder.AppendLine(string.Format("{0}: {1}", pair.Key, pair.Value));
58 | switch (Body)
59 | {
60 | case FormUrlEncodedContent form:
61 | bodyStr = await form.ReadAsStringAsync();
62 | if (!Headers.ContainsKey("Content-Length"))
63 | builder.AppendLine(string.Format("Content-Length: {0}", Encoding.UTF8.GetByteCount(bodyStr)));
64 | if (!Headers.ContainsKey("Content-Type"))
65 | builder.AppendLine("Content-Type: application/x-www-form-urlencoded");
66 | break;
67 | default:
68 | throw new PixivException("Unsupported content type");
69 | }
70 | if (!Headers.ContainsKey("Host"))
71 | builder.AppendLine(string.Format("Host: {0}", new Uri(Url).Host));
72 | if (!Headers.ContainsKey("Connection"))
73 | builder.AppendLine("Connection: Keep-Alive");
74 | if (!Headers.ContainsKey("Cache-Control"))
75 | builder.AppendLine("Cache-Control: no-cache");
76 | builder.AppendLine();
77 | builder.Append(bodyStr);
78 | break;
79 | default:
80 | throw new PixivException("Unsupported method");
81 | }
82 | return Encoding.UTF8.GetBytes(builder.ToString());
83 | }
84 |
85 | public static int BinaryMatch(byte[] input, byte[] pattern)
86 | {
87 | int sLen = input.Length - pattern.Length + 1;
88 | for (int i = 0; i < sLen; ++i)
89 | {
90 | bool match = true;
91 | for (int j = 0; j < pattern.Length; ++j)
92 | {
93 | if (input[i + j] != pattern[j])
94 | {
95 | match = false;
96 | break;
97 | }
98 | }
99 | if (match)
100 | {
101 | return i;
102 | }
103 | }
104 | return -1;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------