[pinterest] implement login support (closes #1055)
being logged allows access to secret/protected boards
This commit is contained in:
@@ -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_)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user