├── .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 [![Nuget](https://img.shields.io/nuget/v/PixivCS.svg?style=flat-square)](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 | --------------------------------------------------------------------------------