mirror of
https://github.com/ihabunek/twitch-dl
synced 2024-08-30 18:32:25 +00:00
Use graphql to fetch channel videos
The old helix endpoint returns HTTP 401 fixes #18
This commit is contained in:
parent
6c28dd2f5e
commit
2118cd8825
@ -1,6 +1,13 @@
|
|||||||
Twitch Downloader change log
|
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)
|
1.7.0 (2020-04-25)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -16,26 +16,23 @@ from twitchdl.exceptions import ConsoleError
|
|||||||
from twitchdl.output import print_out, print_video
|
from twitchdl.output import print_out, print_video
|
||||||
|
|
||||||
|
|
||||||
def videos(channel_name, limit, offset, sort, type, **kwargs):
|
def videos(channel_name, limit, 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))
|
|
||||||
|
|
||||||
print_out("Loading videos...")
|
print_out("Loading videos...")
|
||||||
videos = twitch.get_channel_videos(user["id"], limit, offset, sort, type)
|
videos = twitch.get_channel_videos(channel_name, limit, sort, type)
|
||||||
count = len(videos['videos'])
|
count = len(videos["edges"])
|
||||||
|
total = videos["totalCount"]
|
||||||
|
|
||||||
if not count:
|
if not count:
|
||||||
print_out("No videos found")
|
print_out("No videos found")
|
||||||
return
|
return
|
||||||
|
|
||||||
first = offset + 1
|
# TODO: paging
|
||||||
last = offset + len(videos['videos'])
|
first = 1
|
||||||
total = videos["_total"]
|
last = count
|
||||||
print_out("<yellow>Showing videos {}-{} of {}</yellow>".format(first, last, total))
|
print_out("<yellow>Showing videos {}-{} of {}</yellow>".format(first, last, total))
|
||||||
|
|
||||||
for video in videos['videos']:
|
for video in videos["edges"]:
|
||||||
print_video(video)
|
print_video(video["node"])
|
||||||
|
|
||||||
|
|
||||||
def _select_quality(playlists):
|
def _select_quality(playlists):
|
||||||
|
@ -32,6 +32,19 @@ def time(value):
|
|||||||
return hours * 3600 + minutes * 60 + seconds
|
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 = [
|
COMMANDS = [
|
||||||
Command(
|
Command(
|
||||||
name="videos",
|
name="videos",
|
||||||
@ -43,14 +56,9 @@ COMMANDS = [
|
|||||||
}),
|
}),
|
||||||
(["-l", "--limit"], {
|
(["-l", "--limit"], {
|
||||||
"help": "Number of videos to fetch (default 10, max 100)",
|
"help": "Number of videos to fetch (default 10, max 100)",
|
||||||
"type": int,
|
"type": limit,
|
||||||
"default": 10,
|
"default": 10,
|
||||||
}),
|
}),
|
||||||
(["-o", "--offset"], {
|
|
||||||
"help": "Offset for pagination of results. (default 0)",
|
|
||||||
"type": int,
|
|
||||||
"default": 0,
|
|
||||||
}),
|
|
||||||
(["-s", "--sort"], {
|
(["-s", "--sort"], {
|
||||||
"help": "Sorting order of videos. (default: time)",
|
"help": "Sorting order of videos. (default: time)",
|
||||||
"type": str,
|
"type": str,
|
||||||
|
@ -57,12 +57,16 @@ def print_err(*args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def print_video(video):
|
def print_video(video):
|
||||||
published_at = video['published_at'].replace('T', ' @ ').replace('Z', '')
|
published_at = video["publishedAt"].replace("T", " @ ").replace("Z", "")
|
||||||
length = utils.format_duration(video['length'])
|
length = utils.format_duration(video["lengthSeconds"])
|
||||||
name = video['channel']['display_name']
|
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("<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("Published <cyan>{}</cyan> Length: <cyan>{}</cyan> ".format(published_at, length))
|
||||||
print_out("<i>{}</i>".format(video["url"]))
|
print_out("<i>{}</i>".format(url))
|
||||||
|
@ -43,18 +43,6 @@ def kraken_get(url, params={}, headers={}):
|
|||||||
return authenticated_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):
|
def get_video(video_id):
|
||||||
"""
|
"""
|
||||||
https://dev.twitch.tv/docs/v5/reference/videos#get-video
|
https://dev.twitch.tv/docs/v5/reference/videos#get-video
|
||||||
@ -93,18 +81,46 @@ def get_clip(slug):
|
|||||||
return data["data"]["clip"]
|
return data["data"]["clip"]
|
||||||
|
|
||||||
|
|
||||||
def get_channel_videos(channel_id, limit, offset, sort, type="archive"):
|
def get_channel_videos(channel_id, limit, sort, type="archive"):
|
||||||
"""
|
url = "https://gql.twitch.tv/gql"
|
||||||
https://dev.twitch.tv/docs/v5/reference/channels#get-channel-videos
|
|
||||||
"""
|
|
||||||
url = "https://api.twitch.tv/kraken/channels/{}/videos".format(channel_id)
|
|
||||||
|
|
||||||
return kraken_get(url, {
|
query = """
|
||||||
"broadcast_type": type,
|
{{
|
||||||
|
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,
|
"limit": limit,
|
||||||
"offset": offset,
|
"type": type.upper(),
|
||||||
"sort": sort,
|
"sort": sort.upper(),
|
||||||
}).json()
|
})
|
||||||
|
|
||||||
|
response = authenticated_post(url, json={"query": query}).json()
|
||||||
|
return response["data"]["user"]["videos"]
|
||||||
|
|
||||||
|
|
||||||
def get_access_token(video_id):
|
def get_access_token(video_id):
|
||||||
|
Loading…
Reference in New Issue
Block a user