SysTools Logo SysTools


PHP: Twitch.tv live streams playlist downloader


<?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


[ Код ]