diff --git a/README.rst b/README.rst index bba884c8..5bb73eb1 100644 --- a/README.rst +++ b/README.rst @@ -194,7 +194,7 @@ OAuth ----- *gallery-dl* supports user authentication via OAuth_ for -``deviantart``, ``flickr``, ``pinterest``, ``reddit`` and ``tumblr``. +``deviantart``, ``flickr``, ``reddit`` and ``tumblr``. This is entirely optional, but grants *gallery-dl* the ability to issue requests on your account's behalf and enables it to access resources which would otherwise be unavailable to a public user. diff --git a/docs/configuration.rst b/docs/configuration.rst index 3d7cb301..6c464d41 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -820,13 +820,26 @@ How To =========== ===== +extractor.pinterest.access-token +-------------------------------- +=========== ===== +Type ``string`` +How To - register a Pinterest application and use its client-id and + client-secret (see `extractor.pinterest.client-id & .secret`_) + - run `gallery-dl oauth:pinterest` and authenticate access with + (preferably) the same account that registered the application +Notes Access tokens currently only allow for 10 requests per hour. +=========== ===== + + extractor.pinterest.client-id & .secret --------------------------------------- =========== ===== Type ``string`` How To - login and visit Pinterest's `Apps `__ section - - click "Create app" + - agree to "Pinterest Developer Terms and the API Policy" + and click "Create app" - choose a random name and description and click "Create" - scroll down and set a Site URL (e.g. https://example.org/) and allow https://mikf.github.io/gallery-dl/oauth-redirect.html diff --git a/docs/supportedsites.rst b/docs/supportedsites.rst index 602251dc..e55ba8b9 100644 --- a/docs/supportedsites.rst +++ b/docs/supportedsites.rst @@ -55,7 +55,7 @@ Niconico Seiga http://seiga.nicovideo.jp Images from Users, indi nijie https://nijie.info/ |Images from Use-3| Required Nyafuu Archive https://archive.nyafuu.org/ Threads Pawoo https://pawoo.net Images from Users, Images from Statuses -Pinterest https://www.pinterest.com Boards, Pins, pin.it Links Optional (OAuth) +Pinterest https://www.pinterest.com Boards, Pins, pin.it Links Pixiv https://www.pixiv.net/ |Images from Use-4| Required PowerManga https://powermanga.org/ Chapters, Manga Pure Mashiro http://reader.puremashiro.moe/ Chapters, Manga diff --git a/gallery_dl/extractor/pinterest.py b/gallery_dl/extractor/pinterest.py index 60c4ac1c..99d97f52 100644 --- a/gallery_dl/extractor/pinterest.py +++ b/gallery_dl/extractor/pinterest.py @@ -48,6 +48,9 @@ class PinterestPinExtractor(PinterestExtractor): ("https://www.pinterest.com/pin/858146903966145188/", { "exception": exception.StopExtraction, }), + ("https://www.pinterest.com/pin/85814690396614518/", { + "exception": exception.NotFoundError, + }), ] def __init__(self, match): @@ -134,59 +137,58 @@ class PinterestPinitExtractor(PinterestExtractor): class PinterestAPI(): """Minimal interface for the pinterest API""" - CLIENT_ID = "4959725425749142746" - CLIENT_SECRET = ("2ea77dc64ca02974a728e46c5a9d2adf" - "cdd42f4d4ffb40ad064072165ad4b10d") - def __init__(self, extractor, access_token="AfyIXxi1MJ6et0NlIl_vBchHbex-" - "FSWylPyr2GJE2uu3W8A97QAAAAA"): - access_token = extractor.config("access-token", access_token) - self.session = extractor.session - self.session.params["access_token"] = access_token + def __init__(self, extractor, access_token=None): self.log = extractor.log + self.session = extractor.session + self.access_token = ( + access_token or + extractor.config("access-token") or + "AfyIXxi1MJ6et0NlIl_vBchHbex-FSWylPyr2GJE2uu3W8A97QAAAAA" + ) def pin(self, pin_id, fields="id,image,note"): """Query information about a pin""" + endpoint = "pins/{}/".format(pin_id) params = {"fields": fields} - response = self.session.get( - "https://api.pinterest.com/v1/pins/{pin}/".format(pin=pin_id), - params=params - ) - return self._parse(response)["data"] + return self._call(endpoint, params)["data"] def board(self, user, board, fields="id,name,counts"): """Query information about a board""" + endpoint = "boards/{}/{}/".format(user, board) params = {"fields": fields} - response = self.session.get( - "https://api.pinterest.com/v1/boards/{user}/{board}/" - .format(user=user, board=board), params=params - ) - return self._parse(response)["data"] + return self._call(endpoint, params)["data"] - def board_pins(self, user, board, fields="id,image,note"): + def board_pins(self, user, board, fields="id,image,note", limit=100): """Yield all pins of a specific board""" - params = {"fields": fields, "limit": 100} - url = ("https://api.pinterest.com/v1/boards/{user}/{board}/pins/" - .format(user=user, board=board)) + endpoint = "boards/{}/{}/pins/".format(user, board) + params = {"fields": fields, "limit": limit} + return self._pagination(endpoint, params) + + def _call(self, endpoint, params): + params["access_token"] = self.access_token + url = "https://api.pinterest.com/v1/" + endpoint + + response = self.session.get(url, params=params) + status = response.status_code + data = response.json() + + if 200 <= status < 400 and data.get("data"): + return data + + msg = data.get("message", "") + if status == 404: + msg = msg.partition(" ")[0].lower() + raise exception.NotFoundError(msg) + self.log.error("API request failed: %s", msg or "") + raise exception.StopExtraction() + + def _pagination(self, endpoint, params): while True: - response = self._parse(self.session.get(url, params=params)) + response = self._call(endpoint, params) yield from response["data"] cursor = response["page"]["cursor"] if not cursor: return params["cursor"] = cursor - - def _parse(self, response): - """Parse an API response""" - data = response.json() - if 200 <= response.status_code < 400 and data.get("data"): - return data - - msg = data.get("message") - if response.status_code == 404: - msg = msg.partition(" ")[0].lower() - raise exception.NotFoundError(msg) - else: - self.log.error("API request failed: %s", msg or "") - raise exception.StopExtraction() diff --git a/scripts/build_supportedsites.py b/scripts/build_supportedsites.py index cd7253ce..398d3623 100755 --- a/scripts/build_supportedsites.py +++ b/scripts/build_supportedsites.py @@ -88,7 +88,6 @@ AUTH_MAP = { "flickr" : "Optional (OAuth)", "idolcomplex": "Optional", "nijie" : "Required", - "pinterest" : "Optional (OAuth)", "pixiv" : "Required", "reddit" : "Optional (OAuth)", "sankaku" : "Optional", diff --git a/test/test_results.py b/test/test_results.py index 7f3fb94d..2a1933f8 100644 --- a/test/test_results.py +++ b/test/test_results.py @@ -22,6 +22,7 @@ TRAVIS_SKIP = { # temporary issues, etc. BROKEN = { "luscious", # layout change + "pinterest", # access tokens have been set to 10 requests per hour "puremashiro", # online reader down } @@ -40,8 +41,6 @@ class TestExtractorResults(unittest.TestCase): config.set(("extractor", "deviantart", "client-id"), "7777") config.set(("extractor", "deviantart", "client-secret"), "ff14994c744d9208e5caeec7aab4a026") - config.set(("extractor", "pinterest", "access-token"), - "Ab1gUJFF5TFoWXRbX0p7_ue7jOHeFSX8iOrCIOZE24bOp0A6TQAAAAA") config.set(("extractor", "tumblr", "api-key"), "0cXoHfIqVzMQcc3HESZSNsVlulGxEXGDTTZCDrRrjaa0jmuTc6")