[deviantart] full resolution for non-downloadable images (#293)

Many thanks to @Ironchest337 for discovering this method
and providing a well-documented implementation.
This commit is contained in:
Mike Fährmann
2021-10-24 21:06:21 +02:00
parent a7ddb5f5fa
commit 02a247f4e5
3 changed files with 31 additions and 35 deletions

View File

@@ -1053,17 +1053,6 @@ Description
everything else (archives, etc.). everything else (archives, etc.).
extractor.deviantart.quality
----------------------------
Type
``integer``
Default
``100``
Description
JPEG quality level of newer images for which
an original file download is not available.
extractor.deviantart.refresh-token extractor.deviantart.refresh-token
---------------------------------- ----------------------------------
Type Type

View File

@@ -76,7 +76,6 @@
"mature": true, "mature": true,
"metadata": false, "metadata": false,
"original": true, "original": true,
"quality": 100,
"wait-min": 0 "wait-min": 0
}, },
"e621": "e621":

View File

@@ -14,6 +14,7 @@ from ..cache import cache, memcache
import collections import collections
import itertools import itertools
import mimetypes import mimetypes
import binascii
import time import time
import re import re
@@ -39,7 +40,6 @@ class DeviantartExtractor(Extractor):
self.offset = 0 self.offset = 0
self.flat = self.config("flat", True) self.flat = self.config("flat", True)
self.extra = self.config("extra", False) self.extra = self.config("extra", False)
self.quality = self.config("quality", "100")
self.original = self.config("original", True) self.original = self.config("original", True)
self.comments = self.config("comments", False) self.comments = self.config("comments", False)
self.user = match.group(1) or match.group(2) self.user = match.group(1) or match.group(2)
@@ -53,9 +53,6 @@ class DeviantartExtractor(Extractor):
else: else:
self.unwatch = None self.unwatch = None
if self.quality:
self.quality = ",q_{}".format(self.quality)
if self.original != "image": if self.original != "image":
self._update_content = self._update_content_default self._update_content = self._update_content_default
else: else:
@@ -104,19 +101,8 @@ class DeviantartExtractor(Extractor):
if self.original and deviation["is_downloadable"]: if self.original and deviation["is_downloadable"]:
self._update_content(deviation, content) self._update_content(deviation, content)
else:
if content["src"].startswith("https://images-wixmp-"): self._update_token(deviation, content)
if deviation["index"] <= 790677560:
# https://github.com/r888888888/danbooru/issues/4069
intermediary, count = re.subn(
r"(/f/[^/]+/[^/]+)/v\d+/.*",
r"/intermediary\1", content["src"], 1)
if count:
deviation["_fallback"] = (content["src"],)
content["src"] = intermediary
if self.quality:
content["src"] = re.sub(
r",q_\d+", self.quality, content["src"], 1)
yield self.commit(deviation, content) yield self.commit(deviation, content)
@@ -302,6 +288,32 @@ class DeviantartExtractor(Extractor):
if mtype and mtype.startswith("image/"): if mtype and mtype.startswith("image/"):
content.update(data) content.update(data)
def _update_token(self, deviation, content):
"""Replace JWT to be able to remove width/height limits
All credit goes to @Ironchest337
for discovering and implementing this method
"""
url, sep, _ = content["src"].partition("/v1/")
if not sep:
return
# header = b'{"typ":"JWT","alg":"none"}'
payload = (
b'{"sub":"urn:app:","iss":"urn:app:","obj":[[{"path":"/f/' +
url.partition("/f/")[2].encode() +
b'"}]],"aud":["urn:service:file.download"]}'
)
deviation["_fallback"] = (content["src"],)
content["src"] = (
"{}?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.{}.".format(
url,
# base64 of 'header' is precomputed as 'eyJ0eX...'
# binascii.a2b_base64(header).rstrip(b"=\n").decode(),
binascii.b2a_base64(payload).rstrip(b"=\n").decode())
)
def _limited_request(self, url, **kwargs): def _limited_request(self, url, **kwargs):
"""Limits HTTP requests to one every 2 seconds""" """Limits HTTP requests to one every 2 seconds"""
kwargs["fatal"] = None kwargs["fatal"] = None
@@ -849,12 +861,8 @@ class DeviantartDeviationExtractor(DeviantartExtractor):
}), }),
# wixmp URL rewrite # wixmp URL rewrite
(("https://www.deviantart.com/citizenfresh/art/Hverarond-789295466"), { (("https://www.deviantart.com/citizenfresh/art/Hverarond-789295466"), {
"pattern": (r"https://images-wixmp-\w+\.wixmp\.com" "pattern": (r"https://images-wixmp-\w+\.wixmp\.com/f"
r"/intermediary/f/[^/]+/[^.]+\.jpg") r"/[^/]+/[^.]+\.jpg\?token="),
}),
# wixmp URL rewrite v2 (#369)
(("https://www.deviantart.com/josephbiwald/art/Destiny-2-804940104"), {
"pattern": r"https://images-wixmp-\w+\.wixmp\.com/.*,q_100,"
}), }),
# GIF (#242) # GIF (#242)
(("https://www.deviantart.com/skatergators/art/COM-Moni-781571783"), { (("https://www.deviantart.com/skatergators/art/COM-Moni-781571783"), {