[pinterest] implement login support (closes #1055)

being logged allows access to secret/protected boards
This commit is contained in:
Mike Fährmann
2020-10-15 00:51:53 +02:00
parent 1b1cf01d0d
commit b8daabc3ca
5 changed files with 61 additions and 13 deletions

View File

@@ -214,8 +214,8 @@ a username & password pair. This is necessary for
``pixiv``, ``nijie``, and ``seiga`` ``pixiv``, ``nijie``, and ``seiga``
and optional for and optional for
``aryion``, ``danbooru``, ``e621``, ``exhentai``, ``idolcomplex``, ``inkbunny``, ``aryion``, ``danbooru``, ``e621``, ``exhentai``, ``idolcomplex``, ``inkbunny``,
``instagram``, ``luscious``, ``sankaku``, ``subscribestar``, ``tsumino``, ``instagram``, ``luscious``, ``pinterest``, ``sankaku``, ``subscribestar``,
and ``twitter``. ``tsumino``, and ``twitter``.
You can set the necessary information in your configuration file You can set the necessary information in your configuration file
(cf. gallery-dl.conf_) (cf. gallery-dl.conf_)

View File

@@ -267,17 +267,18 @@ Description
* ``inkbunny`` * ``inkbunny``
* ``instagram`` * ``instagram``
* ``luscious`` * ``luscious``
* ``pinterest``
* ``sankaku`` * ``sankaku``
* ``subscribestar`` * ``subscribestar``
* ``tsumino`` * ``tsumino``
* ``twitter`` * ``twitter``
These values can also be set via the ``-u/--username`` and These values can also be specified via the
``-p/--password`` command-line options or by using a |.netrc|_ file. ``-u/--username`` and ``-p/--password`` command-line options or
(see Authentication_) by using a |.netrc|_ file. (see Authentication_)
Note: The password values for ``danbooru`` and ``e621`` should be Note: The password values for ``danbooru`` and ``e621`` should be
the API keys found in your user profile, not your actual account the API keys found in your user profile, not the actual account
password. password.

View File

@@ -94,7 +94,7 @@ Patreon https://www.patreon.com/ Creators, Posts, User P
Pawoo https://pawoo.net/ Images from Statuses, User Profiles Optional (`OAuth <https://github.com/mikf/gallery-dl#oauth>`__) Pawoo https://pawoo.net/ Images from Statuses, User Profiles Optional (`OAuth <https://github.com/mikf/gallery-dl#oauth>`__)
Photobucket https://photobucket.com/ Albums, individual Images Photobucket https://photobucket.com/ Albums, individual Images
Piczel https://piczel.tv/ Folders, individual Images, User Profiles Piczel https://piczel.tv/ Folders, individual Images, User Profiles
Pinterest https://www.pinterest.com/ Boards, Pins, pin.it Links, related Pins, Sections Pinterest https://www.pinterest.com/ Boards, Pins, pin.it Links, related Pins, Sections Optional
Pixiv https://www.pixiv.net/ |pixiv-C| Required Pixiv https://www.pixiv.net/ |pixiv-C| Required
Pixnet https://www.pixnet.net/ Folders, individual Images, Sets, User Profiles Pixnet https://www.pixnet.net/ Folders, individual Images, Sets, User Profiles
Plurk https://www.plurk.com/ Posts, Timelines Plurk https://www.plurk.com/ Posts, Timelines

View File

@@ -9,7 +9,8 @@
"""Extractors for https://www.pinterest.com/""" """Extractors for https://www.pinterest.com/"""
from .common import Extractor, Message from .common import Extractor, Message
from .. import text, exception from .. import text, util, exception
from ..cache import cache
import itertools import itertools
import json import json
@@ -28,6 +29,7 @@ class PinterestExtractor(Extractor):
self.api = PinterestAPI(self) self.api = PinterestAPI(self)
def items(self): def items(self):
self.api.login()
data = self.metadata() data = self.metadata()
yield Message.Version, 1 yield Message.Version, 1
yield Message.Directory, data yield Message.Directory, data
@@ -98,6 +100,10 @@ class PinterestBoardExtractor(PinterestExtractor):
"options": (("sections", True),), "options": (("sections", True),),
"count": 5, "count": 5,
}), }),
# secret board (#1055)
("https://www.pinterest.de/g1952849/secret/", {
"count": 2,
}),
("https://www.pinterest.com/g1952848/test/", { ("https://www.pinterest.com/g1952848/test/", {
"exception": exception.GalleryDLException, "exception": exception.GalleryDLException,
}), }),
@@ -230,16 +236,22 @@ class PinterestAPI():
"Accept" : "application/json, text/javascript, " "Accept" : "application/json, text/javascript, "
"*/*, q=0.01", "*/*, q=0.01",
"Accept-Language" : "en-US,en;q=0.5", "Accept-Language" : "en-US,en;q=0.5",
"X-Pinterest-AppState": "active",
"X-APP-VERSION" : "b00dd49",
"X-Requested-With" : "XMLHttpRequest",
"Origin" : BASE_URL,
"Referer" : BASE_URL + "/", "Referer" : BASE_URL + "/",
"X-Requested-With" : "XMLHttpRequest",
"X-APP-VERSION" : "7a20185",
"X-CSRFToken" : None,
"X-Pinterest-AppState": "active",
"Origin" : BASE_URL,
} }
def __init__(self, extractor): def __init__(self, extractor):
self.extractor = extractor self.extractor = extractor
csrf_token = util.generate_csrf_token()
self.headers = self.HEADERS.copy()
self.headers["X-CSRFToken"] = csrf_token
self.cookies = {"csrftoken": csrf_token}
def pin(self, pin_id): def pin(self, pin_id):
"""Query information about a pin""" """Query information about a pin"""
options = {"id": pin_id, "field_set_key": "detailed"} options = {"id": pin_id, "field_set_key": "detailed"}
@@ -282,12 +294,45 @@ class PinterestAPI():
options = {"board_id": board_id, "add_vase": True} options = {"board_id": board_id, "add_vase": True}
return self._pagination("BoardRelatedPixieFeed", options) return self._pagination("BoardRelatedPixieFeed", options)
def login(self):
"""Login and obtain session cookies"""
username, password = self.extractor._get_auth_info()
if username:
self.cookies.update(self._login_impl(username, password))
@cache(maxage=180*24*3600, keyarg=1)
def _login_impl(self, username, password):
self.extractor.log.info("Logging in as %s", username)
url = self.BASE_URL + "/resource/UserSessionResource/create/"
options = {
"username_or_email": username,
"password" : password,
}
data = {"data": json.dumps({"options": options}), "source_url": ""}
try:
response = self.extractor.request(
url, method="POST", headers=self.headers,
cookies=self.cookies, data=data)
resource = response.json()["resource_response"]
except (exception.HttpError, ValueError, KeyError):
raise exception.AuthenticationError()
if resource["status"] != "success":
raise exception.AuthenticationError()
return {
cookie.name: cookie.value
for cookie in response.cookies
}
def _call(self, resource, options): def _call(self, resource, options):
url = "{}/resource/{}Resource/get/".format(self.BASE_URL, resource) url = "{}/resource/{}Resource/get/".format(self.BASE_URL, resource)
params = {"data": json.dumps({"options": options}), "source_url": ""} params = {"data": json.dumps({"options": options}), "source_url": ""}
response = self.extractor.request( response = self.extractor.request(
url, params=params, headers=self.HEADERS, fatal=False) url, params=params, headers=self.headers,
cookies=self.cookies, fatal=False)
try: try:
data = response.json() data = response.json()

View File

@@ -296,6 +296,7 @@ class TestFormatter(util.Formatter):
def setup_test_config(): def setup_test_config():
name = "gallerydl" name = "gallerydl"
email = "gallerydl@openaliasbox.org" email = "gallerydl@openaliasbox.org"
email2 = "gallerydl@protonmail.com"
config.clear() config.clear()
config.set(("cache",), "file", None) config.set(("cache",), "file", None)
@@ -307,6 +308,7 @@ def setup_test_config():
config.set(("extractor", "nijie") , "username", email) config.set(("extractor", "nijie") , "username", email)
config.set(("extractor", "seiga") , "username", email) config.set(("extractor", "seiga") , "username", email)
config.set(("extractor", "pinterest") , "username", email2)
config.set(("extractor", "newgrounds"), "username", "d1618111") config.set(("extractor", "newgrounds"), "username", "d1618111")
config.set(("extractor", "newgrounds"), "password", "d1618111") config.set(("extractor", "newgrounds"), "password", "d1618111")