class PixivApiClient API_VERSION = "1" CLIENT_ID = "bYGKuGVw91e0NMfPGp44euvGt59s" CLIENT_SECRET = "HP3RmkgAmEGro0gn1x9ioawQE8WMfvLXDz3ZqxpK" # Tools to not include in the tags list. We don't tag digital media, so # including these results in bad translated tags suggestions. TOOLS_BLACKLIST = %w[ Photoshop Illustrator Fireworks Flash Painter PaintShopPro pixiv\ Sketch CLIP\ STUDIO\ PAINT IllustStudio ComicStudio RETAS\ STUDIO SAI PhotoStudio Pixia NekoPaint PictBear openCanvas ArtRage Expression Inkscape GIMP CGillust COMICWORKS MS_Paint EDGE AzPainter AzPainter2 AzDrawing PicturePublisher SketchBookPro Processing 4thPaint GraphicsGale mdiapp Paintgraphic AfterEffects drawr CLIP\ PAINT\ Lab FireAlpaca Pixelmator AzDrawing2 MediBang\ Paint Krita ibisPaint Procreate Live2D Lightwave3D Shade Poser STRATA AnimationMaster XSI CARRARA CINEMA4D Maya 3dsMax Blender ZBrush Metasequoia Sunny3D Bryce Vue Hexagon\ King SketchUp VistaPro Sculptris Comi\ Po! modo DAZ\ Studio 3D-Coat ] class Error < Exception ; end class WorksResponse attr_reader :json, :pages, :name, :moniker, :user_id, :page_count, :tags attr_reader :artist_commentary_title, :artist_commentary_desc def initialize(json) # Sample response: # { # "status": "success", # "response": [ # { # "id": 49270482, # "title": "ツイログ", # "caption": null, # "tags": [ # "神崎蘭子", # "双葉杏", # "アイドルマスターシンデレラガールズ", # "Star!!", # "アイマス5000users入り" # ], # "tools": [ # "CLIP STUDIO PAINT" # ], # "image_urls": { # "large": "http://i3.pixiv.net/img-original/img/2015/03/14/17/53/32/49270482_p0.jpg" # }, # "width": 1200, # "height": 951, # "stats": { # "scored_count": 8247, # "score": 81697, # "views_count": 191630, # "favorited_count": { # "public": 7804, # "private": 745 # }, # "commented_count": 182 # }, # "publicity": 0, # "age_limit": "all-age", # "created_time": "2015-03-14 17:53:32", # "reuploaded_time": "2015-03-14 17:53:32", # "user": { # "id": 341433, # "account": "nardack", # "name": "Nardack", # "is_following": false, # "is_follower": false, # "is_friend": false, # "is_premium": null, # "profile_image_urls": { # "px_50x50": "http://i1.pixiv.net/img19/profile/nardack/846482_s.jpg" # }, # "stats": null, # "profile": null # }, # "is_manga": true, # "is_liked": false, # "favorite_id": 0, # "page_count": 2, # "book_style": "none", # "type": "illustration", # "metadata": { # "pages": [ # { # "image_urls": { # "large": "http://i3.pixiv.net/img-original/img/2015/03/14/17/53/32/49270482_p0.jpg", # "medium": "http://i3.pixiv.net/c/1200x1200/img-master/img/2015/03/14/17/53/32/49270482_p0_master1200.jpg" # } # }, # { # "image_urls": { # "large": "http://i3.pixiv.net/img-original/img/2015/03/14/17/53/32/49270482_p1.jpg", # "medium": "http://i3.pixiv.net/c/1200x1200/img-master/img/2015/03/14/17/53/32/49270482_p1_master1200.jpg" # } # } # ] # }, # "content_type": null # } # ], # "count": 1 # } @json = json @name = json["user"]["name"] @user_id = json["user"]["id"] @moniker = json["user"]["account"] @page_count = json["page_count"].to_i @artist_commentary_title = json["title"].to_s @artist_commentary_desc = json["caption"].to_s @tags = json["tags"].reject {|x| x =~ /^http:/} @tags += json["tools"] - TOOLS_BLACKLIST if json["metadata"]["zip_urls"] @pages = json["metadata"]["zip_urls"] elsif page_count > 1 @pages = json["metadata"]["pages"].map {|x| x["image_urls"]["large"]} else @pages = [json["image_urls"]["large"]] end end end def works(illust_id) headers = Danbooru.config.http_headers.merge( "Referer" => "http://www.pixiv.net", "Content-Type" => "application/x-www-form-urlencoded", "Authorization" => "Bearer #{access_token}" ) params = { "image_sizes" => "large", "include_stats" => "true" } url = "https://public-api.secure.pixiv.net/v#{API_VERSION}/works/#{illust_id.to_i}.json" resp = HTTParty.get(url, Danbooru.config.httparty_options.deep_merge(query: params, headers: headers)) if resp.success? json = parse_api_json(resp.body) WorksResponse.new(json["response"][0]) else raise Error.new("Pixiv API call failed (status=#{resp.code} body=#{resp.body})") end end private def parse_api_json(body) json = JSON.parse(body) if json["status"] != "success" raise Error.new("Pixiv API call failed (status=#{json['status']} body=#{body})") end json end def access_token Cache.get("pixiv-papi-access-token", 3000) do access_token = nil headers = { "Referer" => "http://www.pixiv.net" } params = { "username" => Danbooru.config.pixiv_login, "password" => Danbooru.config.pixiv_password, "grant_type" => "password", "client_id" => CLIENT_ID, "client_secret" => CLIENT_SECRET } url = "https://oauth.secure.pixiv.net/auth/token" resp = HTTParty.post(url, Danbooru.config.httparty_options.deep_merge(body: params, headers: headers)) if resp.success? json = JSON.parse(resp.body) access_token = json["response"]["access_token"] else raise Error.new("Pixiv API access token call failed (status=#{resp.code} body=#{resp.body})") end access_token end end end