SysTools
<?php
/*
Twitch.tv live streams playlist downloader
(HTML5 and Flash players lags so much)
(c) SysTools 2017-2024
http://systools.losthost.org/
https://github.com/systoolz/miscsoft/
Windows native application "TwitchXP.exe" can be downloaded here:
http://systools.losthost.org/?misc#twitchxp
Changelog:
2024.08.05 updated JSON code in playlist request
2022.12.28 old API has been removed, new implementation
2021.10.02 included JSON data validation
2021.07.08 replaced fsockopen() with stream_socket_client() because of PHP 5.6.0+:
fsockopen(): Peer certificate CN=`usher.ttvnw.net' did not match
expected CN=`usher.twitch.tv' in twitchtv.php on line 41
2019.01.06 add rawurlencode() since it was required for some args;
removed replace_pattern() and replaced with sprintf()
2018.05.23 Twitch.tv switched protocols from HTTP to HTTPS
2017.01.08 first publice release
AGDQ/SGDQ
https://gamesdonequick.com/schedule
https://player.twitch.tv/?volume=1&channel=gamesdonequick
*/
function get_page_from_web($link, $data = '', $auth = array()) {
$CRLF = chr(13).chr(10);
$type = 'tcp://';
$port = '80';
if (!strcasecmp(substr($link, 0, 7), 'http://')) {
$link = substr($link, 7);
}
if (!strcasecmp(substr($link, 0, 8), 'https://')) {
$link = substr($link, 8);
$type = 'ssl://';
$port = '443';
}
$host = substr($link, 0, strpos($link, '/'));
$link = substr($link, strpos($link, '/'));
$wp = '';
$context = stream_context_create(
array('ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
))
);
$fp = stream_socket_client($type.$host.':'.$port, $errno, $errstr, 15, STREAM_CLIENT_CONNECT, $context);
if ($fp) {
$out =
(empty($data) ? 'GET' : 'POST').' '.$link.' HTTP/1.0'.$CRLF.
'Host: '.$host.$CRLF.
(empty($auth) ? '' : implode($CRLF, $auth).$CRLF).
(empty($data) ? '' :
'Content-Length: '.strlen($data).$CRLF.
'Content-Type: text/plain; charset=UTF-8'.$CRLF
).
'Connection: Close'.$CRLF.
$CRLF.
$data;
fwrite($fp, $out);
while (!feof($fp)) {
$wp .= fgets($fp, 10 * 1024);
}
fclose($fp);
$fp = $CRLF.$CRLF;
$out = strpos($wp, $fp);
if ($out !== false) {
$wp = substr($wp, $out + strlen($fp));
}
}
return($wp);
}
function get_twich_playlist($channel) {
$result = '';
if (!empty($channel)) {
$channel = rawurlencode($channel);
// get clientId
$token = get_page_from_web('https://www.twitch.tv/'.$channel);
$auth = array();
preg_match('/clientId\s*=\s*"([0-9a-z]+)/is', $token, $auth);
$auth = array(
'Client-ID: '.array_pop($auth), // required
'Device-ID: '.substr(md5(strval(time())), 0, 16) // anything
);
// get query string
$link = array();
preg_match('/var\s+query\s*=\s*\'([^\']+)\'/is', $token, $link);
$link = html_entity_decode(array_pop($link), ENT_NOQUOTES, 'UTF-8');
$token = array(
'operationName' => 'PlaybackAccessToken_Template',
'query' => $link,
'variables' => array(
'isLive' => true,
'login' => $channel,
'isVod' => false,
'vodID' => '',
'playerType' => 'site',
'platform' => 'web'
)
);
// get token
$token = trim(get_page_from_web('https://gql.twitch.tv/gql', json_encode($token), $auth));
if (!empty($token)) {
$token = json_decode($token, true);
if (
(json_last_error() == JSON_ERROR_NONE) && (is_array($token)) &&
(array_key_exists('data', $token)) && (is_array($token['data'])) &&
(array_key_exists('streamPlaybackAccessToken', $token['data']))
) {
// usher.twitch.tv
$link = sprintf(
'https://usher.ttvnw.net/api/channel/hls/%s.m3u8?'.
'acmb=%s'.
'&allow_source=true'.
'&fast_bread=true'.
'&p=%u'.
'&play_session_id=%s'.
'&player_backend=mediaplayer'.
'&playlist_include_framerate=true'.
'&reassignments_supported=true'.
'&sig=%s'.
'&supported_codecs=avc1'.
'&token=%s'.
'&cdm=wv'.
'&player_version=1.16.0',
$channel,
rawurlencode(base64_encode('{}')), // empty array
time(),
md5(strval(time())), // anything
rawurlencode($token['data']['streamPlaybackAccessToken']['signature']),
rawurlencode($token['data']['streamPlaybackAccessToken']['value'])
);
$result = get_page_from_web($link);
}
}
}
return($result);
}
/*
// example of usage:
file_put_contents('playlist.m3u', get_twich_playlist('gamesdonequick'));
*/
2017.01.09