Th3-822

[rapidleech][d] youtube_com.php

Sep 14th, 2012 (edited)
4,686
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 25.72 KB | None | 0 0
  1. <?php
  2.  
  3. if (!defined('RAPIDLEECH')) {
  4.     require_once('index.html');
  5.     exit();
  6. }
  7.  
  8. class youtube_com extends DownloadClass {
  9.     private $page, $cookie, $fmtmaps, $vid, $sts = -1, $js, $playerJs, $sigJs, $jsVars, $cookieFile,
  10.         $fmts = array(22 => '0|720|0|192', 45 => '1|720|1|192', 44 => '1|480|1|192', 35 => '2|480|0|128', 43 => '1|360|1|128', 34 => '2|360|0|128', 18 => '0|360|0|96', 6 => '2|270|3|64', 5 => '2|240|3|64', 36 => '3|240|0|36', 17 => '3|144|0|24');
  11.  
  12.     public function Download($link) {
  13.         $this->cookieFile = DOWNLOAD_DIR.'YT_cookie.txt';
  14.         $this->cookie = isset($_POST['step']) && !empty($_POST['cookie']) ? StrToCookies(decrypt(urldecode($_POST['cookie']))) : $this->loadCookie();
  15.         $url = parse_url($link);
  16.         $this->vid = array();
  17.  
  18.         if (host_matches('youtu.be', $url['host'])) preg_match('@/([\w\-\.]{11})@i', $url['path'], $this->vid);
  19.         else if (empty($url['query']) || ($this->vid[1] = cut_str('&'.$url['query'].'&', '&v=', '&')) === false || !preg_match('@^[\w\-\.]{11}$@i', $this->vid[1])) preg_match('@/(?:v|(?:embed))/([\w\-\.]{11})@i', $url['path'], $this->vid);
  20.  
  21.         if (empty($this->vid[1])) html_error('Video ID not found.');
  22.         $this->vid = $this->vid[1];
  23.         $this->link = 'https://www.youtube.com/watch?v='.$this->vid;
  24.  
  25.         if (empty($_POST['step'])) $this->getFmtMaps();
  26.         else $this->captcha();
  27.  
  28.         $yt_fmt = empty($_REQUEST['yt_fmt']) ? '' : $_REQUEST['yt_fmt'];
  29.         if (empty($yt_fmt) && !isset($_GET['audl'])) return $this->QSelector();
  30.         else if (isset($_REQUEST['ytube_mp4']) && $_REQUEST['ytube_mp4'] == 'on' && !empty($yt_fmt)) {
  31.             //look for and download the highest quality we can find?
  32.             if ($yt_fmt == 'highest') {
  33.                 foreach (array_keys($this->fmts) as $itag) if (!empty($this->fmtmaps[$itag])) break;
  34.             } else if (!empty($this->fmtmaps[$yt_fmt])) {
  35.                 $itag = $yt_fmt;
  36.             } else html_error('Specified video format not found');
  37.         } else { //just get the one Youtube plays by default (in some cases it could also be the highest quality format)
  38.             $itag = key($this->fmtmaps);
  39.         }
  40.         $fmt = $this->fmtmaps[$itag];
  41.         $is_dash = empty($this->fmts[$itag]);
  42.  
  43.         if (empty($this->response['title'])) {
  44.             if (empty($this->response['player_response']['videoDetails']['title'])) html_error('No video title found! Download halted.');
  45.             else $this->response['title'] = $this->response['player_response']['videoDetails']['title'];
  46.         }
  47.         $filename = str_replace(str_split('\\\:*?"<>|=;'."\t\r\n\f"), '_', html_entity_decode(trim($this->response['title']), ENT_QUOTES));
  48.         if (!empty($_REQUEST['cleanname'])) $filename = preg_replace('@[^ A-Za-z_\-\d\.,\(\)\[\]\{\}&\!\'\@\%\#]@u', '_', $filename);
  49.         if (!empty($fmt['qualityLabel'])) $filename .= " [YT-{$fmt['qualityLabel']}]";
  50.         else if (!empty($fmt['quality_label'])) $filename .= " [YT-{$fmt['quality_label']}]";
  51.         else if (!$is_dash) {
  52.             $v = explode('|', $this->fmts[$itag]);
  53.             $filename .= " [YT-{$v[1]}p]";
  54.         } else $filename .= " [YT-Audio]";
  55.         $filename .= sprintf('[%s].%s', $this->vid, ($is_dash ? 'dash.' : '') . $fmt['_codecs']['container']);
  56.  
  57.         $this->RedirectDownload($fmt['url'], $filename, $this->cookie, 0, ($is_dash ? $this->link . "\r\nRange: bytes=0-" : 0), $filename);
  58.     }
  59.  
  60.     private function FormToArr($content, $v1 = '&', $v2 = '=') {
  61.         $rply = array();
  62.         if (strpos($content, $v1) === false || strpos($content, $v2) === false) return $rply;
  63.         foreach (array_filter(array_map('trim', explode($v1, $content))) as $v) {
  64.             $v = array_map('trim', explode($v2, $v, 2));
  65.             if ($v[0] != '') $rply[$v[0]] = $v[1];
  66.         }
  67.         return $rply;
  68.     }
  69.  
  70.     private function captcha() {
  71.         $url = 'https://www.youtube.com/das_captcha';
  72.         if (!empty($_POST['step']) && $_POST['step'] == '1') {
  73.             $post = $this->verifyReCaptchav2();
  74.             $post['action_recaptcha_verify2'] = '1';
  75.             $post['session_token'] = $_POST['session_token'];
  76.  
  77.             $page = $this->GetPage($url, $this->cookie, $post, $url);
  78.             is_present($page, 'The verification code was invalid', 'The verification code was invalid or has timed out, please try again.');
  79.             is_present($page, "\r\n\r\nAuthorization Error.", 'Error sending captcha.');
  80.             is_notpresent($page, 'Set-Cookie: goojf=', 'Cannot get captcha cookie.');
  81.  
  82.             $this->cookie = GetCookiesArr($page, $this->cookie);
  83.             unset($_POST['step']);
  84.             $this->getFmtMaps();
  85.         } else {
  86.             $page = $this->GetPage($url, $this->cookie);
  87.             $this->cookie = GetCookiesArr($page, $this->cookie);
  88.             if (!preg_match('@class="g-recaptcha" data-sitekey="([\w\.\-]+)"@i', $page, $pid)) html_error('Error: reCAPTCHA2 not found.');
  89.  
  90.             $data = $this->DefaultParamArr($this->link, $this->cookie, 1, 1);
  91.             $data['session_token'] = urlencode(cut_str($page, 'name="session_token" value="', '"'));
  92.             if (isset($_REQUEST['ytube_mp4'])) $data['ytube_mp4'] = $_REQUEST['ytube_mp4'];
  93.             if (isset($_REQUEST['ytdirect'])) $data['ytdirect'] = $_REQUEST['ytdirect'];
  94.             if (isset($_REQUEST['yt_fmt'])) $data['yt_fmt'] = $_REQUEST['yt_fmt'];
  95.             $data['step'] = '1';
  96.  
  97.             $this->reCAPTCHAv2($pid[1], $data);
  98.         }
  99.     }
  100.  
  101.     private function loadCookie() {
  102.         if (@file_exists($this->cookieFile) && ($file = file_get_contents($this->cookieFile)) && ($saved = @unserialize($file)) && is_array($saved) && !empty($saved['hash']) && !empty($saved['cookie']) && ($saved['cookie'] = decrypt(base64_decode($saved['cookie']))) && sha1($saved['cookie']) == $saved['hash']) {
  103.             return StrToCookies($saved['cookie']);
  104.         }
  105.         return array();
  106.     }
  107.  
  108.     private function saveCookie() {
  109.         if (!empty($this->cookie)) {
  110.             $data = array('cookie' => CookiesToStr($this->cookie));
  111.             $data['hash'] = sha1($data['cookie']);
  112.             $data['cookie'] = base64_encode(encrypt($data['cookie']));
  113.             file_put_contents($this->cookieFile, serialize($data));
  114.         }
  115.     }
  116.  
  117.     // Special Function Called by verifyReCaptchav2 When Captcha Is Incorrect, To Allow Retry. - Required
  118.     protected function retryReCaptchav2() {
  119.         $data = $this->DefaultParamArr($this->link, $this->cookie, 1, 1);
  120.         foreach (array('step', 'action_recaptcha_verify2', 'session_token', 'ytube_mp4', 'ytdirect', 'yt_fmt') as $name) {
  121.             if (!empty($_POST[$name])) $data[$name] = $_POST[$name];
  122.         }
  123.         return $this->reCAPTCHAv2($_POST['recaptcha2_public_key'], $data);
  124.     }
  125.  
  126.     private function queryVideo() {
  127.         $this->page = $this->GetPage('https://www.youtube.com/get_video_info?hl=en_US&video_id=' . $this->vid . '&eurl=https%3A%2F%2Fgoogle.com%2F&html5=1' . ($this->sts > 0 ? '&sts=' . $this->sts : ''), $this->cookie);
  128.         $this->cookie = GetCookiesArr($this->page, $this->cookie);
  129.         $this->response = array_map('urldecode', $this->FormToArr(substr($this->page, strpos($this->page, "\r\n\r\n") + 4)));
  130.         if (!empty($this->response['requires_purchase'])) html_error('[Unsupported Video] This Video or Channel Requires a Payment to Watch.');
  131.         $this->response['player_response'] = json_decode($this->response['player_response'], true);
  132.     }
  133.  
  134.     private function getFmtMaps() {
  135.         $this->queryVideo();
  136.  
  137.         if (!empty($this->response['reason'])) html_error('['.htmlspecialchars($this->response['errorcode']).'] '.htmlspecialchars($this->response['reason']));
  138.  
  139.         if (!empty($this->response['player_response']['playabilityStatus']['reason'])) html_error('['.htmlspecialchars($this->response['player_response']['playabilityStatus']['reason']).'] '.htmlspecialchars($this->response['player_response']['playabilityStatus']['errorScreen']['playerErrorMessageRenderer']['subreason']['runs'][0]['text']));
  140.  
  141.         if (!empty($this->response['player_response']['streamingData']['formats'])) {
  142.             $this->response['_formats'] = $this->response['player_response']['streamingData']['formats'];
  143.         }
  144.         if (!empty($this->response['player_response']['streamingData']['adaptiveFormats'])) {
  145.             $this->response['_adaptiveFormats'] = $this->response['player_response']['streamingData']['adaptiveFormats'];
  146.         }
  147.  
  148.         if (in_array(substr($this->page, 9, 3), array('402', '429')) || preg_match('@Location: https?://(www\.)?youtube\.com/das_captcha@i', $this->page)) return $this->captcha();
  149.  
  150.         if (!empty($this->cookie['goojf'])) $this->saveCookie();
  151.  
  152.         $this->fmtmaps = array();
  153.         $keys2Merge = array('cipher', 'signatureCipher');
  154.         foreach (array('url_encoded_fmt_stream_map', 'adaptive_fmts', '_formats', '_adaptiveFormats') as $map) {
  155.             if (empty($this->response[$map])) continue;
  156.             if (!is_array($this->response[$map])) $this->response[$map] = explode(',', $this->response[$map]);
  157.             foreach ($this->response[$map] as $fmt) {
  158.                 if (!is_array($fmt)) $fmt = array_map('urldecode', $this->FormToArr($fmt));
  159.                 foreach($keys2Merge as $key) if (!empty($fmt[$key])) {
  160.                     $fmt += array_map('urldecode', $this->FormToArr($fmt[$key]));
  161.                 }
  162.  
  163.                 if (empty($fmt['itag']) || empty($fmt['url'])) continue;
  164.                 if (!empty($fmt['s']) && empty($this->encS)) {
  165.                     if ($this->sts < 1) return $this->getCipher();
  166.                     else html_error('[' . $this->sts . '] No decoded steps');
  167.                 }
  168.                 $fmt['url'] = parse_url($fmt['url']);
  169.                 $fmt['url']['query'] = array_map('urldecode', $this->FormToArr($fmt['url']['query']));
  170.                 if (!empty($fmt['s']) && !empty($fmt['sp']))
  171.                 {
  172.                     $fmt['url']['query']["{$fmt['sp']}"] = $this->sigDecode($fmt['s']);
  173.                 }
  174.                 else if (empty($fmt['s']) && !empty($fmt['sig']))
  175.                 {
  176.                     if (empty($fmt['url']['query']['signature'])) $fmt['url']['query']['signature'] = $fmt['sig'];
  177.                 }
  178.                 else if (empty($fmt['url']['query']['signature']) && empty($fmt['url']['query']['sig'])) html_error("Cannot get signature key name");
  179.  
  180.                 if (empty($fmt['url']['query']['ratebypass'])) $fmt['url']['query']['ratebypass'] = 'yes'; // Fix for Slow Downloads of DASH Formats
  181.  
  182.                 ksort($fmt['url']['query']);
  183.                 $fmt['url']['query'] = http_build_query($fmt['url']['query']);
  184.                 $fmt['url'] = rebuild_url($fmt['url']);
  185.  
  186.                 if (!empty($fmt['mimeType']) && preg_match('@(audio|video)/(?:x-)?(\w+); codecs="(\w+)(?:\.\w+)*(?:, (\w+)(?:\.\w+)*)?"@', $fmt['mimeType'], $mimeType)) {
  187.                     $fmt['_codecs'] = array();
  188.                     $fmt['_codecs']['type'] = $mimeType[1];
  189.                     $fmt['_codecs']['container'] = $mimeType[2];
  190.                     if (!empty($mimeType[4])) {
  191.                         $fmt['_codecs']['type'] = 'dual';
  192.                         $fmt['_codecs']['video'] = $mimeType[3];
  193.                         $fmt['_codecs']['audio'] = $mimeType[4];
  194.                     } else if ($mimeType[1] == 'video') {
  195.                         $fmt['_codecs']['video'] = $mimeType[3];
  196.                     } else {
  197.                         $fmt['_codecs']['container'] = str_replace(array('mp4a', 'vorbis'), array('m4a', 'ogg'), $mimeType[3]);
  198.                         $fmt['_codecs']['audio'] = $mimeType[3];
  199.                     }
  200.                     if (!empty($fmt['_codecs']['video'])) $fmt['_codecs']['video'] = strtoupper(str_replace(array('av01', 'avc1'), array('AV1', 'H264'), $fmt['_codecs']['video']));
  201.                     if (!empty($fmt['_codecs']['audio'])) $fmt['_codecs']['audio'] = ucfirst(str_replace('mp4a', 'AAC', $fmt['_codecs']['audio']));
  202.                 }
  203.  
  204.                 $this->fmtmaps[$fmt['itag']] = $fmt;
  205.             }
  206.         }
  207.  
  208.         if (empty($this->fmtmaps)) html_error('[' . $this->sts . '] Video links not found.');
  209.     }
  210.  
  211.     private function decError($msg) {
  212.         html_error("Error while decoding [{$this->sts}][{$this->js[2]}]: $msg");
  213.     }
  214.  
  215.     private function findFunction($fName, $num) {
  216.         if ($fName == 'T8') return "w$num";
  217.         $obj = explode('.', $fName, 3);
  218.         if (count($obj) > 2) $this->decError("Cannot search function: '$fName'");
  219.         if (count($obj) > 1) {
  220.             $fName = $obj[1];
  221.             $obj = $obj[0];
  222.             if (empty($this->jsVars[$obj]['src'])) {
  223.                 if (($spos = strpos($this->playerJs, "var $obj={")) === false || ($epos = strpos($this->playerJs, '};', $spos)) === false) $this->decError("Cannot find object '$obj'");
  224.                 $spos += strlen("var $obj={");
  225.                 $this->jsVars[$obj] = array('src' => substr($this->playerJs, $spos, $epos - $spos), 'fn' => array());
  226.             }
  227.             if (empty($this->jsVars[$obj]['fn'][$fName]['step'])) {
  228.                 $v = '[\$_A-Za-z][\$\w]*';
  229.                 if (!preg_match("@(?<=^|,)\s*$fName:function\($v(?:,($v))?\)\{([^}]+)\}\s*(?=,|$)@", $this->jsVars[$obj]['src'], $src)) $this->decError("Cannot find function '$obj.$fName'");
  230.                 $src[0] = trim($src[0]);
  231.                 $this->jsVars[$obj]['fn'][$fName] = array('src' => $src);
  232.  
  233.                 if (empty($src[1])) return $this->jsVars[$obj]['fn'][$fName]['step'] = 'r';
  234.                 else if (preg_match("@var\s+($v)=($v)\[0\];\\2\[0\]=\\2\[{$src[1]}%\\2(?:\.length|\[$v\])\];\\2\[{$src[1]}(?:%\\2(?:\.length|\[$v\]))?\]=\\1@", $src[2])) {
  235.                     $this->jsVars[$obj]['fn'][$fName]['step'] = 'w%d';
  236.                     return "w$num";
  237.                 } else if (preg_match("@(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,){$src[1]}\)@", $src[2])) {
  238.                     $this->jsVars[$obj]['fn'][$fName]['step'] = 's%d';
  239.                     return "s$num";
  240.                 } else if (preg_match("@(?:$v=)?$v(?:\.reverse|\[$v\])\(\)@", $src[2])) return $this->jsVars[$obj]['fn'][$fName]['step'] = 'r';
  241.                 else $this->decError("Error parsing function '$obj.$fName'");
  242.             } else return sprintf($this->jsVars[$obj]['fn'][$fName]['step'], $num);
  243.         }
  244.         if (empty($this->jsVars[$fName]['step'])) {
  245.             if (($spos = strpos($this->playerJs, "function $fName(")) === false || ($epos = strpos($this->playerJs, '};', $spos)) === false) $this->decError("Cannot find function '$fName'");
  246.             $this->jsVars[$fName] = array('src' => substr($this->playerJs, $spos, $epos - $spos));
  247.             $v = '[\$_A-Za-z][\$\w]*';
  248.             if (!preg_match("@^function\s+$fName\($v(?:,($v))?\)\{(.*)$@", $this->jsVars[$fName]['src'], $pars)) $this->decError("Cannot parse function '$fName'");
  249.             if (empty($pars[1])) return $this->jsVars[$fName]['step'] = 'r';
  250.             else if (preg_match("@var\s+($v)=($v)\[0\];\\2\[0\]=\\2\[{$pars[1]}%\\2(?:\.length|\[$v\])\];\\2\[{$pars[1]}(?:%\\2(?:\.length|\[$v\]))?\]=\\1@", $src[2])) {
  251.                 $this->jsVars[$fName]['step'] = 'w%d';
  252.                 return "w$num";
  253.             } else if (preg_match("@(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,){$src[1]}\)@", $src[2])) {
  254.                 $this->jsVars[$fName]['step'] = 's%d';
  255.                 return "s$num";
  256.             } else if (preg_match("@(?:$v=)?$v(?:\.reverse|\[$v\])\(\)@", $src[2])) return $this->jsVars[$fName]['step'] = 'r';
  257.             else $this->decError("Error parsing function '$fName'");
  258.         } else return sprintf($this->jsVars[$fName]['step'], $num);
  259.     }
  260.  
  261.     // getCipher & sigDecode are based on jwz's youtubedown code.
  262.     private function getCipher() {
  263.         $this->changeMesg('<br />Video with ciphered signature, trying to decode it.', 1);
  264.         $page = $this->GetPage('https://www.youtube.com/embed/'.$this->vid, $this->cookie);
  265.         $this->cookie = GetCookiesArr($page, $this->cookie);
  266.  
  267.         if (preg_match('@"sts"\s*:\s*(\d+)@i', $page, $this->sts) && intval($this->sts[1])) {
  268.             $this->sts = intval($this->sts[1]);
  269.         }
  270.  
  271.         $savefile = DOWNLOAD_DIR.'YT_lastjs.txt';
  272.         if (!preg_match('@(?<=[\"\'])(?:(?:https?:?)?//((?:[\w\-]+\.)+[\w\-]+(?:\:\d+)?))?(/(?:[^\"\'/]+/)+?(?:html5)?player[-_][\w\-\.]+(?:(?:/\w+)?/[\w\-\.]+)?)\.js@i', str_replace('\\/', '/', $page), $this->js)) html_error('YT\'s player javascript not found.');
  273.  
  274.         $this->changeMesg(sprintf('<br />Found player: %s.js', $this->js[2]), 1);
  275.  
  276.         if (@file_exists($savefile) && ($file = file_get_contents($savefile, false, NULL, 0, 822)) && ($saved = @unserialize($file)) && is_array($saved) && !empty($saved['js']) && !empty($saved['sts']) && !empty($saved['steps']) && ((!$this->sts && $saved['js'] == $this->js[2]) || $saved['sts'] == $this->sts) && preg_match('@^\s*([ws]\d+|r)( ([ws]\d+|r))*\s*$@', $saved['steps'])) {
  277.             $this->changeMesg(sprintf('<br />Using cached decoder: [%d] %s.', $saved['sts'], $saved['steps']), 1);
  278.             $this->encS = explode(' ', trim($saved['steps']));
  279.             if (empty($this->sts)) $this->sts = $saved['sts'];
  280.         } else {
  281.             $this->changeMesg('<br />Loading video player data.', 1);
  282.             $this->playerJs = $this->GetPage('https://' . (!empty($this->js[1]) ? $this->js[1] : 'www.youtube.com') . $this->js[2] . '.js', $this->cookie, 0, 'https://www.youtube.com/embed/'.$this->vid);
  283.             $v = '[\$_A-Za-z][\$\w]*';
  284.             $v3 = '[\$_A-Za-z][\$\w]{3,}';
  285.             if (empty($this->sts)) {
  286.                 if (preg_match('@\b(?:sts|signatureTimestamp)\s*[:=]\s*(\d+)@i', $this->playerJs, $sts)) {
  287.                     $this->sts = intval($sts[1]);
  288.                 } else if (preg_match("@[:=]($v+(?:\.$v+)?\(\s*\"STS\"[^()?:]+?\))\s*\?\s*\1\s*:\s*(\d\d+)\b@i", $this->playerJs, $sts)) {
  289.                     $this->sts = intval($sts[2]);
  290.                 } else if (preg_match("@\b(?:sts|signatureTimestamp)\s*[:=]\s*($v)@", $this->playerJs, $sts, PREG_OFFSET_CAPTURE) && preg_match("@(?:var\s+|[,{};])\s*{$sts[1][0]}\s*[=:]\s*(\d\d+)@", $this->playerJs, $sts2, 0, strrpos($this->playerJs, "\n", $sts[0][1] - strlen($this->playerJs)))) {
  291.                     $this->sts = intval($sts2[1]);
  292.                 }
  293.                 if (empty($this->sts)) html_error('Signature TimeStamp not found.');
  294.             }
  295.             //if (($spos = strpos($this->playerJs, '.sig||')) === false) $this->decError('Not found (".sig||")');
  296.             //if (($cut1 = cut_str(substr($this->playerJs, $spos), '{', '}')) == false) $this->decError('Cannot get inner content of "if(X.sig||X.s)"');
  297.             if (!preg_match("@(?:\.sig\|\||\.set\(\"signature\",|\|\"signature\",|$v\.sp,|$v=)(?:\(0,$v(?:\.$v)*\)\(|$v3\()?($v)\((?:\(0,$v(?:\.$v)*\)\(|$v3\()?$v\.s\)@", $this->playerJs, $fn)) $this->decError('Cannot get decoder function name');
  298.             $fn = preg_quote($fn[1], '@');
  299.             if (!preg_match("@(?:function\s+$fn\s*\(|var\s+$fn\s*=\s*function\s*\(|(?<=(?:{|,|;))\s*$fn\s*=\s*function\s*\()@", $this->playerJs, $fpos, PREG_OFFSET_CAPTURE)) $this->decError('Cannot find decoder function');
  300.             $fpos = $fpos[0][1];
  301.             if (($cut2 = cut_str(substr($this->playerJs, $fpos), '{', '}')) == false) $this->decError('Cannot get decoder function contents');
  302.             $this->sigJs = preg_replace("@var $v=$v\[0\];$v\[0\]=($v)\[(\d+)%$v(?:\.length|\[$v\])\];$v\[\d+\]=$v;@", '$1=T8($1,$2);', trim($cut2));
  303.             $this->encS = array();
  304.             foreach (array_map('trim', explode(';', '{'.$this->sigJs.'}')) as $step) {
  305.                 if (($step[0] == '{' || substr($step, strlen($step) - 1, 1) == '}') && (preg_match("@^\{(?:var\s+)?$v=$v(?:\.split|\[$v\])\(\"\"\)$@", $step) || preg_match("@^return\s+$v(?:\.join|\[$v\])\(\"\"\);?\}$@", $step))) continue;
  306.                 else if (preg_match("@^(?:$v=)?((?:$v.)*$v)\($v\,(\d+)\)$@", $step, $s)) $this->encS[] = $this->findFunction($s[1], $s[2]);
  307.                 else if (preg_match("@^(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,)(\d+)\)$@", $step, $s)) $this->encS[] = 's'.$s[2];
  308.                 else if (preg_match("@^(?:$v=)?$v(?:\.reverse|\[$v\])\(\)$@", $step)) $this->encS[] = 'r';
  309.                 else $this->decError($step.' | Unknown step on decoder function.');
  310.             }
  311.  
  312.             if (empty($this->encS)) $this->decError('Empty decoded result');
  313.             $this->changeMesg(sprintf('<br />Decoder: [%d] %s.', $this->sts, implode(' ', $this->encS)), 1);
  314.             file_put_contents($savefile, serialize(array('js' => $this->js[2], 'sts' => $this->sts, 'steps' => implode(' ', $this->encS))));
  315.         }
  316.  
  317.         // Request video fmts with the current sts
  318.         $this->getFmtMaps();
  319.     }
  320.  
  321.     private function sigDecode($sig) {
  322.         if (empty($this->encS)) $this->decError('sigDecode() can\'t be called before getCipher()');
  323.         $_sig = $sig;
  324.         $sig = str_split($sig);
  325.         foreach ($this->encS as $_step) {
  326.             if (!preg_match('@^\s*([wrs])(\d*)\s*$@', $_step, $step) || ($step[1] != 'r' && !array_key_exists(2, $step))) $this->decError("Unknown decoding step \"$_step\"");
  327.             switch ($step[1]) {
  328.                 case 'w': $step[2] = (int)$step[2];$x = $sig[0];$sig[0] = $sig[$step[2] % count($sig)];$sig[$step[2]] = $x; break;
  329.                 case 's': $step[2] = (int)$step[2];$sig = array_slice($sig, $step[2]); break;
  330.                 case 'r': $sig = array_reverse($sig); break;
  331.             }
  332.         }
  333.         return implode($sig);
  334.     }
  335.  
  336.     private function bitrate2KMG($bitrate) {
  337.         if (!is_numeric($bitrate)) return 'Unknown';
  338.         $s = array('', 'K', 'M', 'G');
  339.         $e = min(floor(log($bitrate) / log(1000)), count($s) - 1);
  340.         return sprintf("%.2f {$s[$e]}bps", ($bitrate / pow(1000, $e)));
  341.     }
  342.  
  343.     private function QSelector() {
  344.         $C = array('V' => array('MP4', 'WebM', 'FLV', '3GP'), 'A' => array('AAC', 'Vorbis', 'Opus', 'MP3'));
  345.  
  346.         $sizes = array();
  347.         /* Add a // at the start of this line for enable this code.
  348.         if (extension_loaded('curl') && function_exists('curl_init') && function_exists('curl_exec') && !trim(ini_get('open_basedir'))) {
  349.             $sizes = array();
  350.             $opt = array(CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_NOBODY => true);
  351.             foreach ($this->fmtmaps as $itag => $fmt) {
  352.                 if (empty($this->fmts[$itag]) || !empty($fmt['contentLength']) || !empty($fmt['clen'])) continue;
  353.                 $headers = explode("\r\n\r\n", cURL($fmt['url'], $this->cookie, 0, 0, 0, $opt));
  354.                 $headers = ((count($headers) > 2) ? $headers[count($headers) - 2] : $headers[0]) . "\r\n\r\n";
  355.                 if (substr($headers, 9, 3) == '200' && ($CL = cut_str($headers, "\nContent-Length: ", "\n")) && $CL > 1024) $sizes[$itag] = bytesToKbOrMbOrGb(trim($CL));
  356.                 if (substr($headers, 9, 3) == '403') {
  357.                     $this->changeMesg('<br />Cannot Ask Video Filesize of (' . $itag . ') (Signature Error?)');
  358.                     break;
  359.                 }
  360.             }
  361.             unset($headers, $CL);
  362.         } //*/
  363.  
  364.         echo "\n<br /><br /><h3 style='text-align: center;'>".lang(216).".</h4>";
  365.         echo "\n<center><form name='YT_QS' action='{$_SERVER['SCRIPT_NAME']}' method='POST'>\n";
  366.         echo "<input type='hidden' name='yt_QS' value='on' />\n";
  367.         echo '<label><input type="checkbox" name="cleanname" checked="checked" value="1" /><small>&nbsp;Remove non-supported characters from filename</small></label><br />';
  368.         echo "<select name='yt_fmt' id='QS_fmt'>\n";
  369.         foreach ($this->fmtmaps as $itag => $fmt) {
  370.             if (!empty($this->fmts[$itag])) {
  371.                 // Classic Formats
  372.                 $size = (!empty($fmt['contentLength']) ? ' ('.bytesToKbOrMbOrGb($fmt['contentLength']).')' : (!empty($fmt['clen']) ? ' ('.bytesToKbOrMbOrGb($fmt['clen']).')' : (!empty($sizes[$itag]) ? ' ('.$sizes[$itag].')' : '')));
  373.                 if (($I = explode('|', $this->fmts[$itag]))) printf('<option value="%d"' . ($itag == '22' ? ' selected="selected"' : '') . ">[%1\$d] Video: %s %dp | Audio: %s ~%d Kbps%s</option>\n", $itag, $C['V'][$I[0]], $I[1], $C['A'][$I[2]], $I[3], $size);
  374.             } else if (!empty($fmt['_codecs'])) {
  375.                 // DASH Streams
  376.                 $size = (!empty($fmt['contentLength']) ? ' ('.bytesToKbOrMbOrGb($fmt['contentLength']).')' : (!empty($fmt['clen']) ? ' ('.bytesToKbOrMbOrGb($fmt['clen']).')' : ''));
  377.                 switch ($fmt['_codecs']['type']) {
  378.                     case 'video':
  379.                         printf("<option value='%d'>[%1\$d] Video only: %s (%s) @ %s%s</option>\n", $itag, str_replace('WEBM', 'WebM', strtoupper($fmt['_codecs']['container'])), $fmt['_codecs']['video'], (!empty($fmt['qualityLabel']) ? $fmt['qualityLabel'] : $fmt['quality_label']), $size);
  380.                         break;
  381.                     case 'audio':
  382.                         printf("<option value='%d'>[%1\$d] Audio only: %s @ ~%s%s</option>\n", $itag, $fmt['_codecs']['audio'], $this->bitrate2KMG((!empty($fmt['averageBitrate']) ? $fmt['averageBitrate'] : $fmt['bitrate'])), $size);
  383.                         break;
  384.                 }
  385.             }
  386.         }
  387.         echo "</select>\n";
  388.  
  389.         $data = $this->DefaultParamArr($this->link);
  390.         $data['ytube_mp4'] = 'on';
  391.         foreach ($data as $n => $v) echo("<input type='hidden' name='$n' id='QS_$n' value='$v' />\n");
  392.  
  393.         echo "<input type='submit' name='Th3-822' value='".lang(209)."' />\n";
  394.         echo "</form></center>\n</body>\n</html>";
  395.         exit;
  396.     }
  397. }
  398.  
  399. //re-written by szal based on original plugin by eqbal
  400. //updated 07 June 2010
  401. // [28-03-2011]  Fixed (!$video_id) regex. - Th3-822
  402. // [29-03-2011]  Added support for captcha. - Th3-822
  403. // [02-04-2011]  Fixed redirect error. [26-04-2011]  Added error msgs.  - Th3-822
  404. // [04-8-2011]  Fixed for recent changes in fmt_stream_map content & some edits maded for work fine. (Redirect is needed yet) - Th3-822
  405. // [12-8-2011]  Added support for videos that need login for verify age & Changed fmt order by quality & Fixed regexps for fileext. - Th3-822
  406. // [13-8-2011]  Some fixes & removed not working code & fixed verify_age function. - Th3-822
  407. // [17-9-2011]  Added function for skip 'verify_controversy' on youtube && Fixed cookies after captcha && Little changes. - Th3-822
  408. // [26-1-2012]  Fixed regexp for get title, added a quality selector (if the one in template is removed) and some changes in the code. - Th3-822
  409. // [17-5-2012]  Fixed captcha (Now uses reCaptcha). - Th3-822
  410. // [14-9-2012]  Fixed Download links & small changes. - Th3-822
  411. // [07-10-2012]  Fixed for redirect at link. - Th3-822
  412. // [02-1-2013]  Using new way for getting links and video info, now it doesn't need login for restricted videos. - Th3-822
  413. // [02-10-2013]  Fixed issues with videos with ciphered signature & Rewritten quality selector (Now it doesn't use lang) & Remove direct-link option & Added option for sanitize filenames & small changes. - Th3-822
  414. // [04-3-2014]  Re-Added Support for 3GP quality. - Th3-822
  415. // [16-3-2014]  Added functions for decoding ciphered signatures. - Th3-822
  416. // [28-7-2014]  Fixed signature decoding functions. - Th3-822
  417. // [17-12-2014]  Forced https on all the requests for avoid redirect errors. - Th3-822
  418. // [14-1-2015]  Fixed Age Restrictions. (Please, do not annoy my inbox when a plugin fails, go to the forum) - Th3-822
  419. // [21-1-2015]  Fixed backslash in filename when cleanname is off. - Th3-822
  420. // [13-4-2015]  Fixed captcha detection. - Th3-822
  421. // [05-2-2016]  Fixed captcha (Now uses reCaptcha2) & Added cookie storage for it. - Th3-822
  422. // [08-6-2016]  Added support to download DASH formats & Revised video formats handling. - Th3-822
  423. // [30-8-2016]  Fixed slow speed while downloading DASH streams. - Th3-822
  424. // [30-4-2017]  Fixed signature decoding functions. - Th3-822
  425. // [25-1-2018]  Fixed get_video_info. - Th3-822
  426. // [03-2-2019]  Fixed signature decoding functions. - Th3-822
  427. // [25-5-2019]  Fixed embed JS regex. - Th3-822
  428. // [19-6-2019]  Fixed signature key name. - Th3-822
  429. // [10-7-2019]  Fixed signature search and related functions. - Th3-822
  430. // [27-8-2019]  Fixed video title code. - Th3-822
  431. // [04-1-2020]  Fixed fmts handling & Fixed signature search. - Th3-822
  432. // [02-2-2020]  Fixed signature search. - Th3-822
  433. // [15-3-2020]  Fixed embed JS regex. - Th3-822
  434. // [31-5-2020]  Fixed signature search & Rewrote quality selector to parse and show all available formats. - Th3-822
  435. // [17-8-2020]  Fixed signature timestamp search. - Th3-822
  436. // [30-5-2021]  Fixed get_video_info & Now MP4 @ 720p is default (if available) on the selector. - Th3-822
Add Comment
Please, Sign In to add comment