summaryrefslogtreecommitdiffstats
path: root/gallery_dl/extractor/deviantart.py
diff options
context:
space:
mode:
Diffstat (limited to 'gallery_dl/extractor/deviantart.py')
-rw-r--r--gallery_dl/extractor/deviantart.py78
1 files changed, 60 insertions, 18 deletions
diff --git a/gallery_dl/extractor/deviantart.py b/gallery_dl/extractor/deviantart.py
index 73ef20d..a0f4d1c 100644
--- a/gallery_dl/extractor/deviantart.py
+++ b/gallery_dl/extractor/deviantart.py
@@ -14,7 +14,6 @@ from ..cache import cache, memcache
import collections
import itertools
import mimetypes
-import math
import time
import re
@@ -55,6 +54,7 @@ class DeviantartExtractor(Extractor):
self._update_content = self._update_content_image
self.original = True
+ self._premium_cache = {}
self.commit_journal = {
"html": self._commit_journal_html,
"text": self._commit_journal_text,
@@ -66,6 +66,8 @@ class DeviantartExtractor(Extractor):
def items(self):
self.api = DeviantartOAuthAPI(self)
+ if not self.api.refresh_token_key:
+ self._fetch_premium = self._fetch_premium_notoken
if self.user:
profile = self.api.user_profile(self.user)
@@ -83,6 +85,10 @@ class DeviantartExtractor(Extractor):
yield Message.Queue, url, data
continue
+ if "premium_folder_data" in deviation:
+ if not self._fetch_premium(deviation):
+ continue
+
self.prepare(deviation)
yield Message.Directory, deviation
@@ -261,7 +267,9 @@ class DeviantartExtractor(Extractor):
return [(url + folder["name"], folder) for folder in folders]
def _update_content_default(self, deviation, content):
- content.update(self.api.deviation_download(deviation["deviationid"]))
+ public = "premium_folder_data" not in deviation
+ data = self.api.deviation_download(deviation["deviationid"], public)
+ content.update(data)
def _update_content_image(self, deviation, content):
data = self.api.deviation_download(deviation["deviationid"])
@@ -290,6 +298,41 @@ class DeviantartExtractor(Extractor):
return response
self.wait(seconds=180)
+ def _fetch_premium(self, deviation):
+ cache = self._premium_cache
+
+ if deviation["deviationid"] not in cache:
+
+ # check accessibility
+ dev = self.api.deviation(deviation["deviationid"], False)
+ has_access = dev["premium_folder_data"]["has_access"]
+
+ if has_access:
+ self.log.info("Fetching premium folder data")
+ else:
+ self.log.warning("Unable to access premium content (type: %s)",
+ dev["premium_folder_data"]["type"])
+ # fill cache
+ for dev in self.api.gallery(
+ deviation["author"]["username"],
+ deviation["premium_folder_data"]["gallery_id"],
+ public=False,
+ ):
+ cache[dev["deviationid"]] = dev if has_access else None
+
+ data = cache[deviation["deviationid"]]
+ if data:
+ deviation.update(data)
+ return True
+ return False
+
+ def _fetch_premium_notoken(self, deviation):
+ if not self._premium_cache:
+ self.log.warning(
+ "Unable to access premium content (no refresh-token)")
+ self._premium_cache = True
+ return False
+
class DeviantartUserExtractor(DeviantartExtractor):
"""Extractor for an artist's user profile"""
@@ -837,8 +880,7 @@ class DeviantartOAuthAPI():
self.log = extractor.log
self.headers = {}
- delay = extractor.config("wait-min", 0)
- self.delay = math.ceil(math.log2(delay)) if delay >= 1 else -1
+ self.delay = extractor.config("wait-min", 0)
self.delay_min = max(2, self.delay)
self.mature = extractor.config("mature", "true")
@@ -897,27 +939,27 @@ class DeviantartOAuthAPI():
"mature_content": self.mature}
return self._pagination_folders(endpoint, params)
- def deviation(self, deviation_id):
+ def deviation(self, deviation_id, public=True):
"""Query and return info about a single Deviation"""
endpoint = "deviation/" + deviation_id
- deviation = self._call(endpoint)
+ deviation = self._call(endpoint, public=public)
if self.metadata:
self._metadata((deviation,))
if self.folders:
self._folders((deviation,))
return deviation
- def deviation_content(self, deviation_id):
+ def deviation_content(self, deviation_id, public=False):
"""Get extended content of a single Deviation"""
endpoint = "deviation/content"
params = {"deviationid": deviation_id}
- return self._call(endpoint, params, public=False)
+ return self._call(endpoint, params, public=public)
- def deviation_download(self, deviation_id):
+ def deviation_download(self, deviation_id, public=True):
"""Get the original file download (if allowed)"""
endpoint = "deviation/download/" + deviation_id
params = {"mature_content": self.mature}
- return self._call(endpoint, params)
+ return self._call(endpoint, params, public=public)
def deviation_metadata(self, deviations):
""" Fetch deviation metadata for a set of deviations"""
@@ -930,12 +972,12 @@ class DeviantartOAuthAPI():
params = {"mature_content": self.mature}
return self._call(endpoint, params)["metadata"]
- def gallery(self, username, folder_id="", offset=0, extend=True):
+ def gallery(self, username, folder_id, offset=0, extend=True, public=True):
"""Yield all Deviation-objects contained in a gallery folder"""
endpoint = "gallery/" + folder_id
params = {"username": username, "offset": offset, "limit": 24,
"mature_content": self.mature, "mode": "newest"}
- return self._pagination(endpoint, params, extend)
+ return self._pagination(endpoint, params, extend, public)
def gallery_all(self, username, offset=0):
"""Yield all Deviation-objects of a specific user"""
@@ -993,8 +1035,8 @@ class DeviantartOAuthAPI():
"""Call an API endpoint"""
url = "https://www.deviantart.com/api/v1/oauth2/" + endpoint
while True:
- if self.delay >= 0:
- time.sleep(2 ** self.delay)
+ if self.delay:
+ time.sleep(self.delay)
self.authenticate(None if public else self.refresh_token_key)
response = self.extractor.request(
@@ -1015,15 +1057,15 @@ class DeviantartOAuthAPI():
msg = "API responded with {} {}".format(
status, response.reason)
if status == 429:
- if self.delay < 9:
+ if self.delay < 30:
self.delay += 1
- self.log.warning("%s. Using %ds delay.", msg, 2 ** self.delay)
+ self.log.warning("%s. Using %ds delay.", msg, self.delay)
else:
self.log.error(msg)
return data
- def _pagination(self, endpoint, params, extend=True):
- public = warn = True
+ def _pagination(self, endpoint, params, extend=True, public=True):
+ warn = True
while True:
data = self._call(endpoint, params, public=public)
if "results" not in data: