Use graphql to fetch channel videos

The old helix endpoint returns HTTP 401

fixes #18
This commit is contained in:
Ivan Habunek 2020-05-17 11:56:21 +02:00
parent 6c28dd2f5e
commit 2118cd8825
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
5 changed files with 79 additions and 47 deletions

View File

@ -1,6 +1,13 @@
Twitch Downloader change log
============================
1.8.0 (2020-05-17)
------------------
* Fix videos command (#18)
* **Breaking**: `videos` command no longer takes the `--offset` parameter due to
API changes
1.7.0 (2020-04-25)
------------------

View File

@ -16,26 +16,23 @@ from twitchdl.exceptions import ConsoleError
from twitchdl.output import print_out, print_video
def videos(channel_name, limit, offset, sort, type, **kwargs):
print_out("Looking up user...")
user = twitch.get_user(channel_name)
if not user:
raise ConsoleError("User {} not found.".format(channel_name))
def videos(channel_name, limit, sort, type, **kwargs):
print_out("Loading videos...")
videos = twitch.get_channel_videos(user["id"], limit, offset, sort, type)
count = len(videos['videos'])
videos = twitch.get_channel_videos(channel_name, limit, sort, type)
count = len(videos["edges"])
total = videos["totalCount"]
if not count:
print_out("No videos found")
return
first = offset + 1
last = offset + len(videos['videos'])
total = videos["_total"]
# TODO: paging
first = 1
last = count
print_out("<yellow>Showing videos {}-{} of {}</yellow>".format(first, last, total))
for video in videos['videos']:
print_video(video)
for video in videos["edges"]:
print_video(video["node"])
def _select_quality(playlists):

View File

@ -32,6 +32,19 @@ def time(value):
return hours * 3600 + minutes * 60 + seconds
def limit(value):
"""Validates the number of videos to fetch."""
try:
value = int(value)
except ValueError:
raise ArgumentTypeError("must be an integer")
if not 1 <= int(value) <= 100:
raise ArgumentTypeError("must be between 1 and 100")
return value
COMMANDS = [
Command(
name="videos",
@ -43,14 +56,9 @@ COMMANDS = [
}),
(["-l", "--limit"], {
"help": "Number of videos to fetch (default 10, max 100)",
"type": int,
"type": limit,
"default": 10,
}),
(["-o", "--offset"], {
"help": "Offset for pagination of results. (default 0)",
"type": int,
"default": 0,
}),
(["-s", "--sort"], {
"help": "Sorting order of videos. (default: time)",
"type": str,

View File

@ -57,12 +57,16 @@ def print_err(*args, **kwargs):
def print_video(video):
published_at = video['published_at'].replace('T', ' @ ').replace('Z', '')
length = utils.format_duration(video['length'])
name = video['channel']['display_name']
published_at = video["publishedAt"].replace("T", " @ ").replace("Z", "")
length = utils.format_duration(video["lengthSeconds"])
channel = video["creator"]["channel"]["displayName"]
game = video["game"]["name"]
print_out("\n<bold>{}</bold>".format(video['_id'][1:]))
# Can't find URL in video object, strange
url = "https://twitch.tv/{}".format(video["id"])
print_out("\n<bold>{}</bold>".format(video["id"]))
print_out("<green>{}</green>".format(video["title"]))
print_out("<cyan>{}</cyan> playing <cyan>{}</cyan>".format(name, video['game']))
print_out("<cyan>{}</cyan> playing <cyan>{}</cyan>".format(channel, game))
print_out("Published <cyan>{}</cyan> Length: <cyan>{}</cyan> ".format(published_at, length))
print_out("<i>{}</i>".format(video["url"]))
print_out("<i>{}</i>".format(url))

View File

@ -43,18 +43,6 @@ def kraken_get(url, params={}, headers={}):
return authenticated_get(url, params, headers)
def get_user(login):
"""
https://dev.twitch.tv/docs/api/reference/#get-users
"""
response = authenticated_get("https://api.twitch.tv/helix/users", {
"login": login
})
users = response.json()["data"]
return users[0] if users else None
def get_video(video_id):
"""
https://dev.twitch.tv/docs/v5/reference/videos#get-video
@ -93,18 +81,46 @@ def get_clip(slug):
return data["data"]["clip"]
def get_channel_videos(channel_id, limit, offset, sort, type="archive"):
"""
https://dev.twitch.tv/docs/v5/reference/channels#get-channel-videos
"""
url = "https://api.twitch.tv/kraken/channels/{}/videos".format(channel_id)
def get_channel_videos(channel_id, limit, sort, type="archive"):
url = "https://gql.twitch.tv/gql"
return kraken_get(url, {
"broadcast_type": type,
query = """
{{
user(login: "{channel_id}") {{
videos(options: {{ }}, first: {limit}, type: {type}, sort: {sort}, after: "opaqueCursor") {{
totalCount
edges {{
cursor
node {{
id
title
publishedAt
broadcastType
lengthSeconds
game {{
name
}}
creator {{
channel {{
displayName
}}
}}
}}
}}
}}
}}
}}
"""
query = query.format(**{
"channel_id": channel_id,
"limit": limit,
"offset": offset,
"sort": sort,
}).json()
"type": type.upper(),
"sort": sort.upper(),
})
response = authenticated_post(url, json={"query": query}).json()
return response["data"]["user"]["videos"]
def get_access_token(video_id):