Add filtering videos by game

This commit is contained in:
Ivan Habunek 2020-05-17 14:35:33 +02:00
parent 4241ab5d67
commit d22fd74357
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
4 changed files with 75 additions and 11 deletions

View File

@ -8,6 +8,7 @@ Twitch Downloader change log
* **Breaking**: `videos` command no longer takes the `--offset` parameter due to
API changes
* Add paging to `videos` command to replace offset
* Add `--game` option to `videos` command to filter by game
1.7.0 (2020-04-25)
------------------

View File

@ -30,16 +30,32 @@ def _continue():
return True
def videos(channel_name, limit, sort, type, **kwargs):
print_out("Loading videos...")
generator = twitch.channel_videos_generator(channel_name, limit, sort, type)
def _get_game_ids(names):
if not names:
return []
game_ids = []
for name in names:
print_out("<dim>Looking up game '{}'...</dim>".format(name))
game_id = twitch.get_game_id(name)
if not game_id:
raise ConsoleError("Game '{}' not found".format(name))
game_ids.append(int(game_id))
return game_ids
def videos(channel_name, limit, sort, type, game, **kwargs):
game_ids = _get_game_ids(game)
print_out("<dim>Loading videos...</dim>")
generator = twitch.channel_videos_generator(
channel_name, limit, sort, type, game_ids=game_ids)
first = 1
for videos, has_more in generator:
if "edges" not in videos:
break
count = len(videos["edges"]) if "edges" in videos else 0
total = videos["totalCount"]
last = first + count - 1
@ -54,6 +70,8 @@ def videos(channel_name, limit, sort, type, **kwargs):
break
first += count
else:
print_out("<yellow>No videos found</yellow>")
def _select_quality(playlists):

View File

@ -7,6 +7,7 @@ from collections import namedtuple
from twitchdl.exceptions import ConsoleError
from twitchdl.output import print_err
from twitchdl.twitch import GQLError
from . import commands, __version__
@ -54,6 +55,11 @@ COMMANDS = [
"help": "channel name",
"type": str,
}),
(["-g", "--game"], {
"help": "Show videos of given game (can be given multiple times)",
"action": "append",
"type": str,
}),
(["-l", "--limit"], {
"help": "Number of videos to fetch (default 10, max 100)",
"type": limit,
@ -163,3 +169,8 @@ def main():
except ConsoleError as e:
print_err(e)
sys.exit(1)
except GQLError as e:
print_err(e)
for err in e.errors:
print_err("*", err["message"])
sys.exit(1)

View File

@ -8,6 +8,12 @@ from twitchdl import CLIENT_ID
from twitchdl.exceptions import ConsoleError
class GQLError(Exception):
def __init__(self, errors):
super().__init__("GraphQL query failed")
self.errors = errors
def authenticated_get(url, params={}, headers={}):
headers['Client-ID'] = CLIENT_ID
@ -46,7 +52,12 @@ def kraken_get(url, params={}, headers={}):
def gql_query(query):
url = "https://gql.twitch.tv/gql"
payload = {"query": query}
return authenticated_post(url, json={"query": query}).json()
response = authenticated_post(url, json={"query": query}).json()
if "errors" in response:
raise GQLError(response["errors"])
return response
def get_video(video_id):
@ -86,13 +97,15 @@ def get_clip(slug):
return response["data"]["clip"]
def get_channel_videos(channel_id, limit, sort, type="archive", after=None):
def get_channel_videos(channel_id, limit, sort, type="archive", game_ids=[], after=None):
url = "https://gql.twitch.tv/gql"
query = """
{{
user(login: "{channel_id}") {{
videos(options: {{ }}, first: {limit}, type: {type}, sort: {sort}, after: "{after}") {{
videos(options: {{
gameIDs: {game_ids}
}}, first: {limit}, type: {type}, sort: {sort}, after: "{after}") {{
totalCount
edges {{
cursor
@ -119,6 +132,7 @@ def get_channel_videos(channel_id, limit, sort, type="archive", after=None):
query = query.format(**{
"channel_id": channel_id,
"game_ids": game_ids,
"after": after,
"limit": limit,
"sort": sort.upper(),
@ -129,10 +143,15 @@ def get_channel_videos(channel_id, limit, sort, type="archive", after=None):
return response["data"]["user"]["videos"]
def channel_videos_generator(channel_id, limit, sort, type):
def channel_videos_generator(channel_id, limit, sort, type, game_ids=None):
cursor = None
while True:
videos = get_channel_videos(channel_id, limit, sort, type, after=cursor)
videos = get_channel_videos(
channel_id, limit, sort, type, game_ids=game_ids, after=cursor)
if not videos["edges"]:
break
cursor = videos["edges"][-1]["cursor"]
yield videos, cursor is not None
@ -161,3 +180,18 @@ def get_playlists(video_id, access_token):
})
response.raise_for_status()
return response.content.decode('utf-8')
def get_game_id(name):
query = """
{{
game(name: "{}") {{
id
}}
}}
"""
response = gql_query(query.format(name.strip()))
game = response["data"]["game"]
if game:
return game["id"]