mirror of
https://github.com/ihabunek/twitch-dl
synced 2024-08-30 18:32:25 +00:00
parent
0dd04a7e2d
commit
a49dcab419
@ -3,6 +3,7 @@ import re
|
|||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from twitchdl import twitch, utils
|
from twitchdl import twitch, utils
|
||||||
|
from twitchdl.commands.download import get_clip_authenticated_url
|
||||||
from twitchdl.download import download_file
|
from twitchdl.download import download_file
|
||||||
from twitchdl.exceptions import ConsoleError
|
from twitchdl.exceptions import ConsoleError
|
||||||
from twitchdl.output import print_out, print_clip, print_json
|
from twitchdl.output import print_out, print_clip, print_json
|
||||||
@ -79,7 +80,7 @@ def _clips_download(args):
|
|||||||
for clips, _ in generator:
|
for clips, _ in generator:
|
||||||
for clip in clips["edges"]:
|
for clip in clips["edges"]:
|
||||||
clip = clip["node"]
|
clip = clip["node"]
|
||||||
url = clip["videoQualities"][0]["sourceURL"]
|
url = get_clip_authenticated_url(clip["slug"], "source")
|
||||||
target = _clip_target_filename(clip)
|
target = _clip_target_filename(clip)
|
||||||
|
|
||||||
if path.exists(target):
|
if path.exists(target):
|
||||||
|
@ -7,7 +7,7 @@ import tempfile
|
|||||||
|
|
||||||
from os import path
|
from os import path
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse, urlencode
|
||||||
|
|
||||||
from twitchdl import twitch, utils
|
from twitchdl import twitch, utils
|
||||||
from twitchdl.download import download_file, download_files
|
from twitchdl.download import download_file, download_files
|
||||||
@ -139,21 +139,21 @@ def download(args):
|
|||||||
raise ConsoleError("Invalid input: {}".format(args.video))
|
raise ConsoleError("Invalid input: {}".format(args.video))
|
||||||
|
|
||||||
|
|
||||||
def _get_clip_url(clip, args):
|
def _get_clip_url(clip, quality):
|
||||||
qualities = clip["videoQualities"]
|
qualities = clip["videoQualities"]
|
||||||
|
|
||||||
# Quality given as an argument
|
# Quality given as an argument
|
||||||
if args.quality:
|
if quality:
|
||||||
if args.quality == "source":
|
if quality == "source":
|
||||||
return qualities[0]["sourceURL"]
|
return qualities[0]["sourceURL"]
|
||||||
|
|
||||||
selected_quality = args.quality.rstrip("p") # allow 720p as well as 720
|
selected_quality = quality.rstrip("p") # allow 720p as well as 720
|
||||||
for q in qualities:
|
for q in qualities:
|
||||||
if q["quality"] == selected_quality:
|
if q["quality"] == selected_quality:
|
||||||
return q["sourceURL"]
|
return q["sourceURL"]
|
||||||
|
|
||||||
available = ", ".join([str(q["quality"]) for q in qualities])
|
available = ", ".join([str(q["quality"]) for q in qualities])
|
||||||
msg = "Quality '{}' not found. Available qualities are: {}".format(args.quality, available)
|
msg = "Quality '{}' not found. Available qualities are: {}".format(quality, available)
|
||||||
raise ConsoleError(msg)
|
raise ConsoleError(msg)
|
||||||
|
|
||||||
# Ask user to select quality
|
# Ask user to select quality
|
||||||
@ -167,6 +167,23 @@ def _get_clip_url(clip, args):
|
|||||||
return selected_quality["sourceURL"]
|
return selected_quality["sourceURL"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_clip_authenticated_url(slug, quality):
|
||||||
|
print_out("<dim>Fetching access token...</dim>")
|
||||||
|
access_token = twitch.get_clip_access_token(slug)
|
||||||
|
|
||||||
|
if not access_token:
|
||||||
|
raise ConsoleError("Access token not found for slug '{}'".format(slug))
|
||||||
|
|
||||||
|
url = _get_clip_url(access_token, quality)
|
||||||
|
|
||||||
|
query = urlencode({
|
||||||
|
"sig": access_token["playbackAccessToken"]["signature"],
|
||||||
|
"token": access_token["playbackAccessToken"]["value"],
|
||||||
|
})
|
||||||
|
|
||||||
|
return "{}?{}".format(url, query)
|
||||||
|
|
||||||
|
|
||||||
def _download_clip(slug, args):
|
def _download_clip(slug, args):
|
||||||
print_out("<dim>Looking up clip...</dim>")
|
print_out("<dim>Looking up clip...</dim>")
|
||||||
clip = twitch.get_clip(slug)
|
clip = twitch.get_clip(slug)
|
||||||
@ -174,6 +191,12 @@ def _download_clip(slug, args):
|
|||||||
if not clip:
|
if not clip:
|
||||||
raise ConsoleError("Clip '{}' not found".format(slug))
|
raise ConsoleError("Clip '{}' not found".format(slug))
|
||||||
|
|
||||||
|
print_out("<dim>Fetching access token...</dim>")
|
||||||
|
access_token = twitch.get_clip_access_token(slug)
|
||||||
|
|
||||||
|
if not access_token:
|
||||||
|
raise ConsoleError("Access token not found for slug '{}'".format(slug))
|
||||||
|
|
||||||
print_out("Found: <green>{}</green> by <yellow>{}</yellow>, playing <blue>{}</blue> ({})".format(
|
print_out("Found: <green>{}</green> by <yellow>{}</yellow>, playing <blue>{}</blue> ({})".format(
|
||||||
clip["title"],
|
clip["title"],
|
||||||
clip["broadcaster"]["displayName"],
|
clip["broadcaster"]["displayName"],
|
||||||
@ -181,7 +204,7 @@ def _download_clip(slug, args):
|
|||||||
utils.format_duration(clip["durationSeconds"])
|
utils.format_duration(clip["durationSeconds"])
|
||||||
))
|
))
|
||||||
|
|
||||||
url = _get_clip_url(clip, args)
|
url = get_clip_authenticated_url(slug, args.quality)
|
||||||
print_out("<dim>Selected URL: {}</dim>".format(url))
|
print_out("<dim>Selected URL: {}</dim>".format(url))
|
||||||
|
|
||||||
target = _clip_target_filename(clip)
|
target = _clip_target_filename(clip)
|
||||||
|
@ -51,6 +51,16 @@ def kraken_get(url, params={}, headers={}):
|
|||||||
return authenticated_get(url, params, headers)
|
return authenticated_get(url, params, headers)
|
||||||
|
|
||||||
|
|
||||||
|
def gql_post(query):
|
||||||
|
url = "https://gql.twitch.tv/gql"
|
||||||
|
response = authenticated_post(url, data=query).json()
|
||||||
|
|
||||||
|
if "errors" in response:
|
||||||
|
raise GQLError(response["errors"])
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def gql_query(query):
|
def gql_query(query):
|
||||||
url = "https://gql.twitch.tv/gql"
|
url = "https://gql.twitch.tv/gql"
|
||||||
response = authenticated_post(url, json={"query": query}).json()
|
response = authenticated_post(url, json={"query": query}).json()
|
||||||
@ -133,6 +143,26 @@ def get_clip(slug):
|
|||||||
return response["data"]["clip"]
|
return response["data"]["clip"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_clip_access_token(slug):
|
||||||
|
query = """
|
||||||
|
{{
|
||||||
|
"operationName": "VideoAccessToken_Clip",
|
||||||
|
"variables": {{
|
||||||
|
"slug": "{slug}"
|
||||||
|
}},
|
||||||
|
"extensions": {{
|
||||||
|
"persistedQuery": {{
|
||||||
|
"version": 1,
|
||||||
|
"sha256Hash": "36b89d2507fce29e5ca551df756d27c1cfe079e2609642b4390aa4c35796eb11"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = gql_post(query.format(slug=slug).strip())
|
||||||
|
return response["data"]["clip"]
|
||||||
|
|
||||||
|
|
||||||
def get_channel_clips(channel_id, period, limit, after=None):
|
def get_channel_clips(channel_id, period, limit, after=None):
|
||||||
"""
|
"""
|
||||||
List channel clips.
|
List channel clips.
|
||||||
|
Loading…
Reference in New Issue
Block a user