merge #6394: [tumblr] add 'search' extractor
This commit is contained in:
@@ -934,7 +934,7 @@ Consider all listed sites to potentially be NSFW.
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Tumblr</td>
|
<td>Tumblr</td>
|
||||||
<td>https://www.tumblr.com/</td>
|
<td>https://www.tumblr.com/</td>
|
||||||
<td>Days, Likes, Posts, Tag Searches, User Profiles</td>
|
<td>Days, Likes, Posts, Search Results, Tag Searches, User Profiles</td>
|
||||||
<td><a href="https://github.com/mikf/gallery-dl#oauth">OAuth</a></td>
|
<td><a href="https://github.com/mikf/gallery-dl#oauth">OAuth</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ BASE_PATTERN = (
|
|||||||
r"([\w-]+\.tumblr\.com)))"
|
r"([\w-]+\.tumblr\.com)))"
|
||||||
)
|
)
|
||||||
|
|
||||||
POST_TYPES = frozenset((
|
POST_TYPES = frozenset(("text", "quote", "link", "answer", "video",
|
||||||
"text", "quote", "link", "answer", "video", "audio", "photo", "chat"))
|
"audio", "photo", "chat", "search"))
|
||||||
|
|
||||||
|
|
||||||
class TumblrExtractor(Extractor):
|
class TumblrExtractor(Extractor):
|
||||||
@@ -83,14 +83,21 @@ class TumblrExtractor(Extractor):
|
|||||||
return
|
return
|
||||||
if post["type"] not in self.types:
|
if post["type"] not in self.types:
|
||||||
continue
|
continue
|
||||||
if not blog:
|
|
||||||
blog = self.api.info(self.blog)
|
|
||||||
blog["uuid"] = self.blog
|
|
||||||
|
|
||||||
if self.avatar:
|
if "blog" in post:
|
||||||
url = self.api.avatar(self.blog)
|
blog = post["blog"]
|
||||||
yield Message.Directory, {"blog": blog}
|
self.blog = blog["name"] + ".tumblr.com"
|
||||||
yield self._prepare_avatar(url, post.copy(), blog)
|
else:
|
||||||
|
if not blog:
|
||||||
|
blog = self.api.info(self.blog)
|
||||||
|
blog["uuid"] = self.blog
|
||||||
|
|
||||||
|
if self.avatar:
|
||||||
|
url = self.api.avatar(self.blog)
|
||||||
|
yield Message.Directory, {"blog": blog}
|
||||||
|
yield self._prepare_avatar(url, post.copy(), blog)
|
||||||
|
|
||||||
|
post["blog"] = blog
|
||||||
|
|
||||||
reblog = "reblogged_from_id" in post
|
reblog = "reblogged_from_id" in post
|
||||||
if reblog and self._skip_reblog(post):
|
if reblog and self._skip_reblog(post):
|
||||||
@@ -99,7 +106,6 @@ class TumblrExtractor(Extractor):
|
|||||||
|
|
||||||
if "trail" in post:
|
if "trail" in post:
|
||||||
del post["trail"]
|
del post["trail"]
|
||||||
post["blog"] = blog
|
|
||||||
post["date"] = text.parse_timestamp(post["timestamp"])
|
post["date"] = text.parse_timestamp(post["timestamp"])
|
||||||
posts = []
|
posts = []
|
||||||
|
|
||||||
@@ -349,6 +355,19 @@ class TumblrLikesExtractor(TumblrExtractor):
|
|||||||
return self.api.likes(self.blog)
|
return self.api.likes(self.blog)
|
||||||
|
|
||||||
|
|
||||||
|
class TumblrSearchExtractor(TumblrExtractor):
|
||||||
|
"""Extractor for a Tumblr search"""
|
||||||
|
subcategory = "search"
|
||||||
|
pattern = (BASE_PATTERN + r"/search/([^/?#]+)"
|
||||||
|
r"(?:/([^/?#]+)(?:/([^/?#]+))?)?(?:/?\?([^#]+))?")
|
||||||
|
example = "https://www.tumblr.com/search/QUERY"
|
||||||
|
|
||||||
|
def posts(self):
|
||||||
|
_, _, _, search, mode, post_type, query = self.groups
|
||||||
|
params = text.parse_query(query)
|
||||||
|
return self.api.search(text.unquote(search), params, mode, post_type)
|
||||||
|
|
||||||
|
|
||||||
class TumblrAPI(oauth.OAuth1API):
|
class TumblrAPI(oauth.OAuth1API):
|
||||||
"""Interface for the Tumblr API v2
|
"""Interface for the Tumblr API v2
|
||||||
|
|
||||||
@@ -394,7 +413,8 @@ class TumblrAPI(oauth.OAuth1API):
|
|||||||
if self.before and params["offset"]:
|
if self.before and params["offset"]:
|
||||||
self.log.warning("'offset' and 'date-max' cannot be used together")
|
self.log.warning("'offset' and 'date-max' cannot be used together")
|
||||||
|
|
||||||
return self._pagination(blog, "/posts", params, cache=True)
|
endpoint = "/v2/blog/{}/posts".format(blog)
|
||||||
|
return self._pagination(endpoint, params, blog=blog, cache=True)
|
||||||
|
|
||||||
def likes(self, blog):
|
def likes(self, blog):
|
||||||
"""Retrieve liked posts"""
|
"""Retrieve liked posts"""
|
||||||
@@ -410,6 +430,20 @@ class TumblrAPI(oauth.OAuth1API):
|
|||||||
yield from posts
|
yield from posts
|
||||||
params["before"] = posts[-1]["liked_timestamp"]
|
params["before"] = posts[-1]["liked_timestamp"]
|
||||||
|
|
||||||
|
def search(self, query, params, mode="top", post_type=None):
|
||||||
|
"""Retrieve search results"""
|
||||||
|
endpoint = "/v2/timeline/search"
|
||||||
|
|
||||||
|
params["limit"] = "50"
|
||||||
|
params["days"] = params.pop("t", None)
|
||||||
|
params["query"] = query
|
||||||
|
params["mode"] = mode
|
||||||
|
params["reblog_info"] = "true" if self.extractor.reblogs else "false"
|
||||||
|
if post_type:
|
||||||
|
params["post_type_filter"] = post_type
|
||||||
|
|
||||||
|
return self._pagination(endpoint, params)
|
||||||
|
|
||||||
def _call(self, endpoint, params, **kwargs):
|
def _call(self, endpoint, params, **kwargs):
|
||||||
url = self.ROOT + endpoint
|
url = self.ROOT + endpoint
|
||||||
kwargs["params"] = params
|
kwargs["params"] = params
|
||||||
@@ -478,20 +512,28 @@ class TumblrAPI(oauth.OAuth1API):
|
|||||||
|
|
||||||
raise exception.StopExtraction(data)
|
raise exception.StopExtraction(data)
|
||||||
|
|
||||||
def _pagination(self, blog, endpoint, params, key="posts", cache=False):
|
def _pagination(self, endpoint, params,
|
||||||
endpoint = "/v2/blog/{}{}".format(blog, endpoint)
|
blog=None, key="posts", cache=False):
|
||||||
if self.api_key:
|
if self.api_key:
|
||||||
params["api_key"] = self.api_key
|
params["api_key"] = self.api_key
|
||||||
|
|
||||||
strategy = self.extractor.config("pagination")
|
strategy = self.extractor.config("pagination")
|
||||||
|
if not strategy and "offset" not in params:
|
||||||
|
strategy = "api"
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
data = self._call(endpoint, params)
|
data = self._call(endpoint, params)
|
||||||
|
|
||||||
if cache:
|
if "timeline" in data:
|
||||||
self.BLOG_CACHE[blog] = data["blog"]
|
data = data["timeline"]
|
||||||
cache = False
|
posts = data["elements"]
|
||||||
|
|
||||||
|
else:
|
||||||
|
if cache:
|
||||||
|
self.BLOG_CACHE[blog] = data["blog"]
|
||||||
|
cache = False
|
||||||
|
posts = data[key]
|
||||||
|
|
||||||
posts = data[key]
|
|
||||||
yield from posts
|
yield from posts
|
||||||
|
|
||||||
if strategy == "api":
|
if strategy == "api":
|
||||||
|
|||||||
@@ -360,4 +360,21 @@ __tests__ = (
|
|||||||
"#class" : tumblr.TumblrLikesExtractor,
|
"#class" : tumblr.TumblrLikesExtractor,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#url" : "https://www.tumblr.com/search/nathan fielder",
|
||||||
|
"#category": ("", "tumblr", "search"),
|
||||||
|
"#class" : tumblr.TumblrSearchExtractor,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#url" : "https://www.tumblr.com/search/nathan fielder/recent/quote?src=typed_query",
|
||||||
|
"#category": ("", "tumblr", "search"),
|
||||||
|
"#class" : tumblr.TumblrSearchExtractor,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"#url" : "https://www.tumblr.com/search/nathan%20fielder?t=90",
|
||||||
|
"#category": ("", "tumblr", "search"),
|
||||||
|
"#class" : tumblr.TumblrSearchExtractor,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user