[scrolller] support albums for subreddit results (#7339)
update GraphQL queries
This commit is contained in:
@@ -76,7 +76,7 @@ class ScrolllerExtractor(Extractor):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = self._request_graphql("LoginQuery", variables)
|
data = self._request_graphql("LoginQuery", variables, False)
|
||||||
except exception.HttpError as exc:
|
except exception.HttpError as exc:
|
||||||
if exc.status == 403:
|
if exc.status == 403:
|
||||||
raise exception.AuthenticationError()
|
raise exception.AuthenticationError()
|
||||||
@@ -84,10 +84,9 @@ class ScrolllerExtractor(Extractor):
|
|||||||
|
|
||||||
return data["login"]["token"]
|
return data["login"]["token"]
|
||||||
|
|
||||||
def _request_graphql(self, opname, variables, admin=False):
|
def _request_graphql(self, opname, variables, admin=True):
|
||||||
url = "https://api.scrolller.com/api/v2/graphql"
|
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type" : "text/plain;charset=UTF-8",
|
"Content-Type" : None,
|
||||||
"Origin" : self.root,
|
"Origin" : self.root,
|
||||||
"Sec-Fetch-Dest": "empty",
|
"Sec-Fetch-Dest": "empty",
|
||||||
"Sec-Fetch-Mode": "cors",
|
"Sec-Fetch-Mode": "cors",
|
||||||
@@ -102,15 +101,19 @@ class ScrolllerExtractor(Extractor):
|
|||||||
if admin:
|
if admin:
|
||||||
url = "https://api.scrolller.com/admin"
|
url = "https://api.scrolller.com/admin"
|
||||||
headers["Content-Type"] = "application/json"
|
headers["Content-Type"] = "application/json"
|
||||||
|
else:
|
||||||
|
url = "https://api.scrolller.com/api/v2/graphql"
|
||||||
|
headers["Content-Type"] = "text/plain;charset=UTF-8"
|
||||||
|
|
||||||
return self.request(
|
return self.request(
|
||||||
url, method="POST", headers=headers, data=util.json_dumps(data),
|
url, method="POST", headers=headers, data=util.json_dumps(data),
|
||||||
).json()["data"]
|
).json()["data"]
|
||||||
|
|
||||||
def _pagination(self, opname, variables):
|
def _pagination(self, opname, variables, data=None):
|
||||||
while True:
|
if data is None:
|
||||||
data = self._request_graphql(opname, variables)
|
data = self._request_graphql(opname, variables)
|
||||||
|
|
||||||
|
while True:
|
||||||
while "items" not in data:
|
while "items" not in data:
|
||||||
data = data.popitem()[1]
|
data = data.popitem()[1]
|
||||||
yield from data["items"]
|
yield from data["items"]
|
||||||
@@ -119,6 +122,8 @@ class ScrolllerExtractor(Extractor):
|
|||||||
return
|
return
|
||||||
variables["iterator"] = data["iterator"]
|
variables["iterator"] = data["iterator"]
|
||||||
|
|
||||||
|
data = self._request_graphql(opname, variables)
|
||||||
|
|
||||||
def _sort_key(self, src):
|
def _sort_key(self, src):
|
||||||
return src["width"], not src["isOptimized"]
|
return src["width"], not src["isOptimized"]
|
||||||
|
|
||||||
@@ -132,6 +137,7 @@ class ScrolllerSubredditExtractor(ScrolllerExtractor):
|
|||||||
def posts(self):
|
def posts(self):
|
||||||
url, query = self.groups
|
url, query = self.groups
|
||||||
filter = None
|
filter = None
|
||||||
|
sort = "RANDOM"
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
params = text.parse_query(query)
|
params = text.parse_query(query)
|
||||||
@@ -139,12 +145,24 @@ class ScrolllerSubredditExtractor(ScrolllerExtractor):
|
|||||||
filter = params["filter"].upper().rstrip("S")
|
filter = params["filter"].upper().rstrip("S")
|
||||||
|
|
||||||
variables = {
|
variables = {
|
||||||
"url" : url,
|
"url" : url,
|
||||||
"iterator" : None,
|
"filter": filter,
|
||||||
"filter" : filter,
|
"sortBy": sort,
|
||||||
"hostsDown": None,
|
"limit" : 50,
|
||||||
}
|
}
|
||||||
return self._pagination("SubredditQuery", variables)
|
subreddit = self._request_graphql(
|
||||||
|
"SubredditQuery", variables)["getSubreddit"]
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"subredditId": subreddit["id"],
|
||||||
|
"iterator": None,
|
||||||
|
"filter" : filter,
|
||||||
|
"sortBy" : sort,
|
||||||
|
"limit" : 50,
|
||||||
|
"isNsfw" : subreddit["isNsfw"],
|
||||||
|
}
|
||||||
|
return self._pagination(
|
||||||
|
"SubredditChildrenQuery", variables, subreddit["children"])
|
||||||
|
|
||||||
|
|
||||||
class ScrolllerFollowingExtractor(ScrolllerExtractor):
|
class ScrolllerFollowingExtractor(ScrolllerExtractor):
|
||||||
@@ -160,11 +178,14 @@ class ScrolllerFollowingExtractor(ScrolllerExtractor):
|
|||||||
raise exception.AuthorizationError("Login required")
|
raise exception.AuthorizationError("Login required")
|
||||||
|
|
||||||
variables = {
|
variables = {
|
||||||
"iterator" : None,
|
"iterator": None,
|
||||||
"hostsDown": None,
|
"filter" : None,
|
||||||
|
"limit" : 10,
|
||||||
|
"isNsfw" : False,
|
||||||
|
"sortBy" : "RANDOM",
|
||||||
}
|
}
|
||||||
|
|
||||||
for subreddit in self._pagination("FollowingQuery", variables):
|
for subreddit in self._pagination("GetFollowingSubreddits", variables):
|
||||||
url = self.root + subreddit["url"]
|
url = self.root + subreddit["url"]
|
||||||
subreddit["_extractor"] = ScrolllerSubredditExtractor
|
subreddit["_extractor"] = ScrolllerSubredditExtractor
|
||||||
yield Message.Queue, url, subreddit
|
yield Message.Queue, url, subreddit
|
||||||
@@ -174,36 +195,62 @@ class ScrolllerPostExtractor(ScrolllerExtractor):
|
|||||||
"""Extractor for media from a single scrolller post"""
|
"""Extractor for media from a single scrolller post"""
|
||||||
subcategory = "post"
|
subcategory = "post"
|
||||||
pattern = BASE_PATTERN + r"/(?!r/|following$)([^/?#]+)"
|
pattern = BASE_PATTERN + r"/(?!r/|following$)([^/?#]+)"
|
||||||
example = "https://scrolller.com/title-slug-a1b2c3d4f5"
|
example = "https://scrolller.com/TITLE-SLUG-a1b2c3d4f5"
|
||||||
|
|
||||||
def posts(self):
|
def posts(self):
|
||||||
variables = {"url": "/" + self.groups[0]}
|
variables = {"url": "/" + self.groups[0]}
|
||||||
data = self._request_graphql("SubredditPostQuery", variables, True)
|
data = self._request_graphql("SubredditPostQuery", variables)
|
||||||
return (data["getPost"],)
|
return (data["getPost"],)
|
||||||
|
|
||||||
|
|
||||||
QUERIES = {
|
QUERIES = {
|
||||||
|
|
||||||
|
"SubredditPostQuery": """\
|
||||||
|
query SubredditPostQuery(
|
||||||
|
$url: String!
|
||||||
|
) {
|
||||||
|
getPost(
|
||||||
|
data: { url: $url }
|
||||||
|
) {
|
||||||
|
__typename id url title subredditId subredditTitle subredditUrl
|
||||||
|
redditPath isNsfw hasAudio fullLengthSource gfycatSource redgifsSource
|
||||||
|
ownerAvatar username displayName favoriteCount isPaid tags
|
||||||
|
commentsCount commentsRepliesCount isFavorite
|
||||||
|
albumContent { mediaSources { url width height isOptimized } }
|
||||||
|
mediaSources { url width height isOptimized }
|
||||||
|
blurredMediaSources { url width height isOptimized }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
|
||||||
"SubredditQuery": """\
|
"SubredditQuery": """\
|
||||||
query SubredditQuery(
|
query SubredditQuery(
|
||||||
$url: String!
|
$url: String!
|
||||||
$filter: SubredditPostFilter
|
|
||||||
$iterator: String
|
$iterator: String
|
||||||
|
$sortBy: GallerySortBy
|
||||||
|
$filter: GalleryFilter
|
||||||
|
$limit: Int!
|
||||||
) {
|
) {
|
||||||
getSubreddit(
|
getSubreddit(
|
||||||
url: $url
|
data: {
|
||||||
|
url: $url,
|
||||||
|
iterator: $iterator,
|
||||||
|
filter: $filter,
|
||||||
|
limit: $limit,
|
||||||
|
sortBy: $sortBy
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
children(
|
__typename id url title secondaryTitle description createdAt isNsfw
|
||||||
limit: 50
|
subscribers isComplete itemCount videoCount pictureCount albumCount
|
||||||
iterator: $iterator
|
isPaid username tags isFollowing
|
||||||
filter: $filter
|
banner { url width height isOptimized }
|
||||||
disabledHosts: null
|
children {
|
||||||
) {
|
|
||||||
iterator items {
|
iterator items {
|
||||||
__typename id url title subredditId subredditTitle
|
__typename id url title subredditId subredditTitle subredditUrl
|
||||||
subredditUrl redditPath isNsfw albumUrl hasAudio
|
redditPath isNsfw hasAudio fullLengthSource gfycatSource
|
||||||
fullLengthSource gfycatSource redgifsSource ownerAvatar
|
redgifsSource ownerAvatar username displayName favoriteCount
|
||||||
username displayName isPaid tags isFavorite
|
isPaid tags commentsCount commentsRepliesCount isFavorite
|
||||||
|
albumContent { mediaSources { url width height isOptimized } }
|
||||||
mediaSources { url width height isOptimized }
|
mediaSources { url width height isOptimized }
|
||||||
blurredMediaSources { url width height isOptimized }
|
blurredMediaSources { url width height isOptimized }
|
||||||
}
|
}
|
||||||
@@ -212,19 +259,59 @@ query SubredditQuery(
|
|||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
|
|
||||||
"FollowingQuery": """\
|
"SubredditChildrenQuery": """\
|
||||||
query FollowingQuery(
|
query SubredditChildrenQuery(
|
||||||
|
$subredditId: Int!
|
||||||
$iterator: String
|
$iterator: String
|
||||||
|
$filter: GalleryFilter
|
||||||
|
$sortBy: GallerySortBy
|
||||||
|
$limit: Int!
|
||||||
|
$isNsfw: Boolean
|
||||||
) {
|
) {
|
||||||
getFollowing(
|
getSubredditChildren(
|
||||||
limit: 10
|
data: {
|
||||||
iterator: $iterator
|
subredditId: $subredditId,
|
||||||
|
iterator: $iterator,
|
||||||
|
filter: $filter,
|
||||||
|
sortBy: $sortBy,
|
||||||
|
limit: $limit,
|
||||||
|
isNsfw: $isNsfw
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
iterator items {
|
||||||
|
__typename id url title subredditId subredditTitle subredditUrl
|
||||||
|
redditPath isNsfw hasAudio fullLengthSource gfycatSource
|
||||||
|
redgifsSource ownerAvatar username displayName favoriteCount isPaid
|
||||||
|
tags commentsCount commentsRepliesCount isFavorite
|
||||||
|
albumContent { mediaSources { url width height isOptimized } }
|
||||||
|
mediaSources { url width height isOptimized }
|
||||||
|
blurredMediaSources { url width height isOptimized }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
|
||||||
|
"GetFollowingSubreddits": """\
|
||||||
|
query GetFollowingSubreddits(
|
||||||
|
$iterator: String,
|
||||||
|
$limit: Int!,
|
||||||
|
$filter: GalleryFilter,
|
||||||
|
$isNsfw: Boolean,
|
||||||
|
$sortBy: GallerySortBy
|
||||||
|
) {
|
||||||
|
getFollowingSubreddits(
|
||||||
|
data: {
|
||||||
|
isNsfw: $isNsfw
|
||||||
|
limit: $limit
|
||||||
|
filter: $filter
|
||||||
|
iterator: $iterator
|
||||||
|
sortBy: $sortBy
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
iterator items {
|
iterator items {
|
||||||
__typename id url title secondaryTitle description createdAt isNsfw
|
__typename id url title secondaryTitle description createdAt isNsfw
|
||||||
subscribers isComplete itemCount videoCount pictureCount albumCount
|
subscribers isComplete itemCount videoCount pictureCount albumCount
|
||||||
isPaid username tags isFollowing
|
isFollowing
|
||||||
banner { url width height isOptimized }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,24 +339,6 @@ query ItemTypeQuery(
|
|||||||
url: $url
|
url: $url
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
""",
|
|
||||||
|
|
||||||
"SubredditPostQuery": """\
|
|
||||||
query SubredditPostQuery(
|
|
||||||
$url: String!
|
|
||||||
) {
|
|
||||||
getPost(
|
|
||||||
data: { url: $url }
|
|
||||||
) {
|
|
||||||
__typename id url title subredditId subredditTitle subredditUrl
|
|
||||||
redditPath isNsfw hasAudio fullLengthSource gfycatSource redgifsSource
|
|
||||||
ownerAvatar username displayName favoriteCount isPaid tags
|
|
||||||
commentsCount commentsRepliesCount isFavorite
|
|
||||||
albumContent { mediaSources { url width height isOptimized } }
|
|
||||||
mediaSources { url width height isOptimized }
|
|
||||||
blurredMediaSources { url width height isOptimized }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""",
|
""",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,21 +11,20 @@ __tests__ = (
|
|||||||
{
|
{
|
||||||
"#url" : "https://scrolller.com/r/AmateurPhotography",
|
"#url" : "https://scrolller.com/r/AmateurPhotography",
|
||||||
"#class" : scrolller.ScrolllerSubredditExtractor,
|
"#class" : scrolller.ScrolllerSubredditExtractor,
|
||||||
"#pattern": r"https://\w+\.scrolller\.com/(\w+/)?[\w-]+-\w+\.(jpg|png)",
|
"#pattern": r"https://static\.scrolller\.com/\w+/(\w+/)?[\w-]+-\w+\.(jpg|png|webp)",
|
||||||
"#range" : "1-100",
|
"#range" : "1-100",
|
||||||
"#count" : 100,
|
"#count" : 100,
|
||||||
|
|
||||||
"albumUrl" : None,
|
|
||||||
"displayName" : None,
|
"displayName" : None,
|
||||||
"fullLengthSource": None,
|
"fullLengthSource": None,
|
||||||
"gfycatSource" : None,
|
"gfycatSource" : None,
|
||||||
"hasAudio" : None,
|
"hasAudio" : bool,
|
||||||
"height" : int,
|
"height" : int,
|
||||||
"id" : int,
|
"id" : int,
|
||||||
"isFavorite" : False,
|
"isFavorite" : False,
|
||||||
"isNsfw" : False,
|
"isNsfw" : False,
|
||||||
"isOptimized" : bool,
|
"isOptimized" : bool,
|
||||||
"isPaid" : None,
|
"isPaid" : bool,
|
||||||
"mediaSources" : list,
|
"mediaSources" : list,
|
||||||
"ownerAvatar" : None,
|
"ownerAvatar" : None,
|
||||||
"redditPath" : r"re:/r/AmateurPhotography/comments/...",
|
"redditPath" : r"re:/r/AmateurPhotography/comments/...",
|
||||||
@@ -36,7 +35,7 @@ __tests__ = (
|
|||||||
"tags" : None,
|
"tags" : None,
|
||||||
"title" : str,
|
"title" : str,
|
||||||
"url" : str,
|
"url" : str,
|
||||||
"username" : None,
|
"username" : str,
|
||||||
"width" : int,
|
"width" : int,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user