Th3-822 icon

[rapidleech][d] youtube_com.php

Th3-822 | PRO | 05/30/21 10:59:12 PM UTC (Edited) | 0 ⭐ | 4836 👁️ | Never ⏰ | []
PHP |

25.72 KB

|

None

|

0 👍

/

0 👎

<?php
 
if (!defined('RAPIDLEECH')) {
    require_once('index.html');
    exit();
}
 
class youtube_com extends DownloadClass {
    private $page, $cookie, $fmtmaps, $vid, $sts = -1, $js, $playerJs, $sigJs, $jsVars, $cookieFile,
        $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');
 
    public function Download($link) {
        $this->cookieFile = DOWNLOAD_DIR.'YT_cookie.txt';
        $this->cookie = isset($_POST['step']) && !empty($_POST['cookie']) ? StrToCookies(decrypt(urldecode($_POST['cookie']))) : $this->loadCookie();
        $url = parse_url($link);
        $this->vid = array();
 
        if (host_matches('youtu.be', $url['host'])) preg_match('@/([\w\-\.]{11})@i', $url['path'], $this->vid);
        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);
 
        if (empty($this->vid[1])) html_error('Video ID not found.');
        $this->vid = $this->vid[1];
        $this->link = 'https://www.youtube.com/watch?v='.$this->vid;
 
        if (empty($_POST['step'])) $this->getFmtMaps();
        else $this->captcha();
 
        $yt_fmt = empty($_REQUEST['yt_fmt']) ? '' : $_REQUEST['yt_fmt'];
        if (empty($yt_fmt) && !isset($_GET['audl'])) return $this->QSelector();
        else if (isset($_REQUEST['ytube_mp4']) && $_REQUEST['ytube_mp4'] == 'on' && !empty($yt_fmt)) {
            //look for and download the highest quality we can find?
            if ($yt_fmt == 'highest') {
                foreach (array_keys($this->fmts) as $itag) if (!empty($this->fmtmaps[$itag])) break;
            } else if (!empty($this->fmtmaps[$yt_fmt])) {
                $itag = $yt_fmt;
            } else html_error('Specified video format not found');
        } else { //just get the one Youtube plays by default (in some cases it could also be the highest quality format)
            $itag = key($this->fmtmaps);
        }
        $fmt = $this->fmtmaps[$itag];
        $is_dash = empty($this->fmts[$itag]);
 
        if (empty($this->response['title'])) {
            if (empty($this->response['player_response']['videoDetails']['title'])) html_error('No video title found! Download halted.');
            else $this->response['title'] = $this->response['player_response']['videoDetails']['title'];
        }
        $filename = str_replace(str_split('\\\:*?"<>|=;'."\t\r\n\f"), '_', html_entity_decode(trim($this->response['title']), ENT_QUOTES));
        if (!empty($_REQUEST['cleanname'])) $filename = preg_replace('@[^ A-Za-z_\-\d\.,\(\)\[\]\{\}&\!\'\@\%\#]@u', '_', $filename);
        if (!empty($fmt['qualityLabel'])) $filename .= " [YT-{$fmt['qualityLabel']}]";
        else if (!empty($fmt['quality_label'])) $filename .= " [YT-{$fmt['quality_label']}]";
        else if (!$is_dash) {
            $v = explode('|', $this->fmts[$itag]);
            $filename .= " [YT-{$v[1]}p]";
        } else $filename .= " [YT-Audio]";
        $filename .= sprintf('[%s].%s', $this->vid, ($is_dash ? 'dash.' : '') . $fmt['_codecs']['container']);
 
        $this->RedirectDownload($fmt['url'], $filename, $this->cookie, 0, ($is_dash ? $this->link . "\r\nRange: bytes=0-" : 0), $filename);
    }
 
    private function FormToArr($content, $v1 = '&', $v2 = '=') {
        $rply = array();
        if (strpos($content, $v1) === false || strpos($content, $v2) === false) return $rply;
        foreach (array_filter(array_map('trim', explode($v1, $content))) as $v) {
            $v = array_map('trim', explode($v2, $v, 2));
            if ($v[0] != '') $rply[$v[0]] = $v[1];
        }
        return $rply;
    }
 
    private function captcha() {
        $url = 'https://www.youtube.com/das_captcha';
        if (!empty($_POST['step']) && $_POST['step'] == '1') {
            $post = $this->verifyReCaptchav2();
            $post['action_recaptcha_verify2'] = '1';
            $post['session_token'] = $_POST['session_token'];
 
            $page = $this->GetPage($url, $this->cookie, $post, $url);
            is_present($page, 'The verification code was invalid', 'The verification code was invalid or has timed out, please try again.');
            is_present($page, "\r\n\r\nAuthorization Error.", 'Error sending captcha.');
            is_notpresent($page, 'Set-Cookie: goojf=', 'Cannot get captcha cookie.');
 
            $this->cookie = GetCookiesArr($page, $this->cookie);
            unset($_POST['step']);
            $this->getFmtMaps();
        } else {
            $page = $this->GetPage($url, $this->cookie);
            $this->cookie = GetCookiesArr($page, $this->cookie);
            if (!preg_match('@class="g-recaptcha" data-sitekey="([\w\.\-]+)"@i', $page, $pid)) html_error('Error: reCAPTCHA2 not found.');
 
            $data = $this->DefaultParamArr($this->link, $this->cookie, 1, 1);
            $data['session_token'] = urlencode(cut_str($page, 'name="session_token" value="', '"'));
            if (isset($_REQUEST['ytube_mp4'])) $data['ytube_mp4'] = $_REQUEST['ytube_mp4'];
            if (isset($_REQUEST['ytdirect'])) $data['ytdirect'] = $_REQUEST['ytdirect'];
            if (isset($_REQUEST['yt_fmt'])) $data['yt_fmt'] = $_REQUEST['yt_fmt'];
            $data['step'] = '1';
 
            $this->reCAPTCHAv2($pid[1], $data);
        }
    }
 
    private function loadCookie() {
        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']) {
            return StrToCookies($saved['cookie']);
        }
        return array();
    }
 
    private function saveCookie() {
        if (!empty($this->cookie)) {
            $data = array('cookie' => CookiesToStr($this->cookie));
            $data['hash'] = sha1($data['cookie']);
            $data['cookie'] = base64_encode(encrypt($data['cookie']));
            file_put_contents($this->cookieFile, serialize($data));
        }
    }
 
    // Special Function Called by verifyReCaptchav2 When Captcha Is Incorrect, To Allow Retry. - Required
    protected function retryReCaptchav2() {
        $data = $this->DefaultParamArr($this->link, $this->cookie, 1, 1);
        foreach (array('step', 'action_recaptcha_verify2', 'session_token', 'ytube_mp4', 'ytdirect', 'yt_fmt') as $name) {
            if (!empty($_POST[$name])) $data[$name] = $_POST[$name];
        }
        return $this->reCAPTCHAv2($_POST['recaptcha2_public_key'], $data);
    }
 
    private function queryVideo() {
        $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);
        $this->cookie = GetCookiesArr($this->page, $this->cookie);
        $this->response = array_map('urldecode', $this->FormToArr(substr($this->page, strpos($this->page, "\r\n\r\n") + 4)));
        if (!empty($this->response['requires_purchase'])) html_error('[Unsupported Video] This Video or Channel Requires a Payment to Watch.');
        $this->response['player_response'] = json_decode($this->response['player_response'], true);
    }
 
    private function getFmtMaps() {
        $this->queryVideo();
 
        if (!empty($this->response['reason'])) html_error('['.htmlspecialchars($this->response['errorcode']).'] '.htmlspecialchars($this->response['reason']));
 
        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']));
 
        if (!empty($this->response['player_response']['streamingData']['formats'])) {
            $this->response['_formats'] = $this->response['player_response']['streamingData']['formats'];
        }
        if (!empty($this->response['player_response']['streamingData']['adaptiveFormats'])) {
            $this->response['_adaptiveFormats'] = $this->response['player_response']['streamingData']['adaptiveFormats'];
        }
 
        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();
 
        if (!empty($this->cookie['goojf'])) $this->saveCookie();
 
        $this->fmtmaps = array();
        $keys2Merge = array('cipher', 'signatureCipher');
        foreach (array('url_encoded_fmt_stream_map', 'adaptive_fmts', '_formats', '_adaptiveFormats') as $map) {
            if (empty($this->response[$map])) continue;
            if (!is_array($this->response[$map])) $this->response[$map] = explode(',', $this->response[$map]);
            foreach ($this->response[$map] as $fmt) {
                if (!is_array($fmt)) $fmt = array_map('urldecode', $this->FormToArr($fmt));
                foreach($keys2Merge as $key) if (!empty($fmt[$key])) {
                    $fmt += array_map('urldecode', $this->FormToArr($fmt[$key]));
                }
 
                if (empty($fmt['itag']) || empty($fmt['url'])) continue;
                if (!empty($fmt['s']) && empty($this->encS)) {
                    if ($this->sts < 1) return $this->getCipher();
                    else html_error('[' . $this->sts . '] No decoded steps');
                }
                $fmt['url'] = parse_url($fmt['url']);
                $fmt['url']['query'] = array_map('urldecode', $this->FormToArr($fmt['url']['query']));
                if (!empty($fmt['s']) && !empty($fmt['sp']))
                {
                    $fmt['url']['query']["{$fmt['sp']}"] = $this->sigDecode($fmt['s']);
                }
                else if (empty($fmt['s']) && !empty($fmt['sig']))
                {
                    if (empty($fmt['url']['query']['signature'])) $fmt['url']['query']['signature'] = $fmt['sig'];
                }
                else if (empty($fmt['url']['query']['signature']) && empty($fmt['url']['query']['sig'])) html_error("Cannot get signature key name");
 
                if (empty($fmt['url']['query']['ratebypass'])) $fmt['url']['query']['ratebypass'] = 'yes'; // Fix for Slow Downloads of DASH Formats
 
                ksort($fmt['url']['query']);
                $fmt['url']['query'] = http_build_query($fmt['url']['query']);
                $fmt['url'] = rebuild_url($fmt['url']);
 
                if (!empty($fmt['mimeType']) && preg_match('@(audio|video)/(?:x-)?(\w+); codecs="(\w+)(?:\.\w+)*(?:, (\w+)(?:\.\w+)*)?"@', $fmt['mimeType'], $mimeType)) {
                    $fmt['_codecs'] = array();
                    $fmt['_codecs']['type'] = $mimeType[1];
                    $fmt['_codecs']['container'] = $mimeType[2];
                    if (!empty($mimeType[4])) {
                        $fmt['_codecs']['type'] = 'dual';
                        $fmt['_codecs']['video'] = $mimeType[3];
                        $fmt['_codecs']['audio'] = $mimeType[4];
                    } else if ($mimeType[1] == 'video') {
                        $fmt['_codecs']['video'] = $mimeType[3];
                    } else {
                        $fmt['_codecs']['container'] = str_replace(array('mp4a', 'vorbis'), array('m4a', 'ogg'), $mimeType[3]);
                        $fmt['_codecs']['audio'] = $mimeType[3];
                    }
                    if (!empty($fmt['_codecs']['video'])) $fmt['_codecs']['video'] = strtoupper(str_replace(array('av01', 'avc1'), array('AV1', 'H264'), $fmt['_codecs']['video']));
                    if (!empty($fmt['_codecs']['audio'])) $fmt['_codecs']['audio'] = ucfirst(str_replace('mp4a', 'AAC', $fmt['_codecs']['audio']));
                }
 
                $this->fmtmaps[$fmt['itag']] = $fmt;
            }
        }
 
        if (empty($this->fmtmaps)) html_error('[' . $this->sts . '] Video links not found.');
    }
 
    private function decError($msg) {
        html_error("Error while decoding [{$this->sts}][{$this->js[2]}]: $msg");
    }
 
    private function findFunction($fName, $num) {
        if ($fName == 'T8') return "w$num";
        $obj = explode('.', $fName, 3);
        if (count($obj) > 2) $this->decError("Cannot search function: '$fName'");
        if (count($obj) > 1) {
            $fName = $obj[1];
            $obj = $obj[0];
            if (empty($this->jsVars[$obj]['src'])) {
                if (($spos = strpos($this->playerJs, "var $obj={")) === false || ($epos = strpos($this->playerJs, '};', $spos)) === false) $this->decError("Cannot find object '$obj'");
                $spos += strlen("var $obj={");
                $this->jsVars[$obj] = array('src' => substr($this->playerJs, $spos, $epos - $spos), 'fn' => array());
            }
            if (empty($this->jsVars[$obj]['fn'][$fName]['step'])) {
                $v = '[\$_A-Za-z][\$\w]*';
                if (!preg_match("@(?<=^|,)\s*$fName:function\($v(?:,($v))?\)\{([^}]+)\}\s*(?=,|$)@", $this->jsVars[$obj]['src'], $src)) $this->decError("Cannot find function '$obj.$fName'");
                $src[0] = trim($src[0]);
                $this->jsVars[$obj]['fn'][$fName] = array('src' => $src);
 
                if (empty($src[1])) return $this->jsVars[$obj]['fn'][$fName]['step'] = 'r';
                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])) {
                    $this->jsVars[$obj]['fn'][$fName]['step'] = 'w%d';
                    return "w$num";
                } else if (preg_match("@(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,){$src[1]}\)@", $src[2])) {
                    $this->jsVars[$obj]['fn'][$fName]['step'] = 's%d';
                    return "s$num";
                } else if (preg_match("@(?:$v=)?$v(?:\.reverse|\[$v\])\(\)@", $src[2])) return $this->jsVars[$obj]['fn'][$fName]['step'] = 'r';
                else $this->decError("Error parsing function '$obj.$fName'");
            } else return sprintf($this->jsVars[$obj]['fn'][$fName]['step'], $num);
        }
        if (empty($this->jsVars[$fName]['step'])) {
            if (($spos = strpos($this->playerJs, "function $fName(")) === false || ($epos = strpos($this->playerJs, '};', $spos)) === false) $this->decError("Cannot find function '$fName'");
            $this->jsVars[$fName] = array('src' => substr($this->playerJs, $spos, $epos - $spos));
            $v = '[\$_A-Za-z][\$\w]*';
            if (!preg_match("@^function\s+$fName\($v(?:,($v))?\)\{(.*)$@", $this->jsVars[$fName]['src'], $pars)) $this->decError("Cannot parse function '$fName'");
            if (empty($pars[1])) return $this->jsVars[$fName]['step'] = 'r';
            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])) {
                $this->jsVars[$fName]['step'] = 'w%d';
                return "w$num";
            } else if (preg_match("@(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,){$src[1]}\)@", $src[2])) {
                $this->jsVars[$fName]['step'] = 's%d';
                return "s$num";
            } else if (preg_match("@(?:$v=)?$v(?:\.reverse|\[$v\])\(\)@", $src[2])) return $this->jsVars[$fName]['step'] = 'r';
            else $this->decError("Error parsing function '$fName'");
        } else return sprintf($this->jsVars[$fName]['step'], $num);
    }
 
    // getCipher & sigDecode are based on jwz's youtubedown code.
    private function getCipher() {
        $this->changeMesg('<br />Video with ciphered signature, trying to decode it.', 1);
        $page = $this->GetPage('https://www.youtube.com/embed/'.$this->vid, $this->cookie);
        $this->cookie = GetCookiesArr($page, $this->cookie);
 
        if (preg_match('@"sts"\s*:\s*(\d+)@i', $page, $this->sts) && intval($this->sts[1])) {
            $this->sts = intval($this->sts[1]);
        }
 
        $savefile = DOWNLOAD_DIR.'YT_lastjs.txt';
        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.');
 
        $this->changeMesg(sprintf('<br />Found player: %s.js', $this->js[2]), 1);
 
        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'])) {
            $this->changeMesg(sprintf('<br />Using cached decoder: [%d] %s.', $saved['sts'], $saved['steps']), 1);
            $this->encS = explode(' ', trim($saved['steps']));
            if (empty($this->sts)) $this->sts = $saved['sts'];
        } else {
            $this->changeMesg('<br />Loading video player data.', 1);
            $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);
            $v = '[\$_A-Za-z][\$\w]*';
            $v3 = '[\$_A-Za-z][\$\w]{3,}';
            if (empty($this->sts)) {
                if (preg_match('@\b(?:sts|signatureTimestamp)\s*[:=]\s*(\d+)@i', $this->playerJs, $sts)) {
                    $this->sts = intval($sts[1]);
                } else if (preg_match("@[:=]($v+(?:\.$v+)?\(\s*\"STS\"[^()?:]+?\))\s*\?\s*\1\s*:\s*(\d\d+)\b@i", $this->playerJs, $sts)) {
                    $this->sts = intval($sts[2]);
                } 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)))) {
                    $this->sts = intval($sts2[1]);
                }
                if (empty($this->sts)) html_error('Signature TimeStamp not found.');
            }
            //if (($spos = strpos($this->playerJs, '.sig||')) === false) $this->decError('Not found (".sig||")');
            //if (($cut1 = cut_str(substr($this->playerJs, $spos), '{', '}')) == false) $this->decError('Cannot get inner content of "if(X.sig||X.s)"');
            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');
            $fn = preg_quote($fn[1], '@');
            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');
            $fpos = $fpos[0][1];
            if (($cut2 = cut_str(substr($this->playerJs, $fpos), '{', '}')) == false) $this->decError('Cannot get decoder function contents');
            $this->sigJs = preg_replace("@var $v=$v\[0\];$v\[0\]=($v)\[(\d+)%$v(?:\.length|\[$v\])\];$v\[\d+\]=$v;@", '$1=T8($1,$2);', trim($cut2));
            $this->encS = array();
            foreach (array_map('trim', explode(';', '{'.$this->sigJs.'}')) as $step) {
                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;
                else if (preg_match("@^(?:$v=)?((?:$v.)*$v)\($v\,(\d+)\)$@", $step, $s)) $this->encS[] = $this->findFunction($s[1], $s[2]);
                else if (preg_match("@^(?:$v=)?$v(?:\.s(p)?lice|\[$v\])\((?(1)0,)(\d+)\)$@", $step, $s)) $this->encS[] = 's'.$s[2];
                else if (preg_match("@^(?:$v=)?$v(?:\.reverse|\[$v\])\(\)$@", $step)) $this->encS[] = 'r';
                else $this->decError($step.' | Unknown step on decoder function.');
            }
 
            if (empty($this->encS)) $this->decError('Empty decoded result');
            $this->changeMesg(sprintf('<br />Decoder: [%d] %s.', $this->sts, implode(' ', $this->encS)), 1);
            file_put_contents($savefile, serialize(array('js' => $this->js[2], 'sts' => $this->sts, 'steps' => implode(' ', $this->encS))));
        }
 
        // Request video fmts with the current sts
        $this->getFmtMaps();
    }
 
    private function sigDecode($sig) {
        if (empty($this->encS)) $this->decError('sigDecode() can\'t be called before getCipher()');
        $_sig = $sig;
        $sig = str_split($sig);
        foreach ($this->encS as $_step) {
            if (!preg_match('@^\s*([wrs])(\d*)\s*$@', $_step, $step) || ($step[1] != 'r' && !array_key_exists(2, $step))) $this->decError("Unknown decoding step \"$_step\"");
            switch ($step[1]) {
                case 'w': $step[2] = (int)$step[2];$x = $sig[0];$sig[0] = $sig[$step[2] % count($sig)];$sig[$step[2]] = $x; break;
                case 's': $step[2] = (int)$step[2];$sig = array_slice($sig, $step[2]); break;
                case 'r': $sig = array_reverse($sig); break;
            }
        }
        return implode($sig);
    }
 
    private function bitrate2KMG($bitrate) {
        if (!is_numeric($bitrate)) return 'Unknown';
        $s = array('', 'K', 'M', 'G');
        $e = min(floor(log($bitrate) / log(1000)), count($s) - 1);
        return sprintf("%.2f {$s[$e]}bps", ($bitrate / pow(1000, $e)));
    }
 
    private function QSelector() {
        $C = array('V' => array('MP4', 'WebM', 'FLV', '3GP'), 'A' => array('AAC', 'Vorbis', 'Opus', 'MP3'));
 
        $sizes = array();
        /* Add a // at the start of this line for enable this code.
        if (extension_loaded('curl') && function_exists('curl_init') && function_exists('curl_exec') && !trim(ini_get('open_basedir'))) {
            $sizes = array();
            $opt = array(CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_NOBODY => true);
            foreach ($this->fmtmaps as $itag => $fmt) {
                if (empty($this->fmts[$itag]) || !empty($fmt['contentLength']) || !empty($fmt['clen'])) continue;
                $headers = explode("\r\n\r\n", cURL($fmt['url'], $this->cookie, 0, 0, 0, $opt));
                $headers = ((count($headers) > 2) ? $headers[count($headers) - 2] : $headers[0]) . "\r\n\r\n";
                if (substr($headers, 9, 3) == '200' && ($CL = cut_str($headers, "\nContent-Length: ", "\n")) && $CL > 1024) $sizes[$itag] = bytesToKbOrMbOrGb(trim($CL));
                if (substr($headers, 9, 3) == '403') {
                    $this->changeMesg('<br />Cannot Ask Video Filesize of (' . $itag . ') (Signature Error?)');
                    break;
                }
            }
            unset($headers, $CL);
        } //*/
 
        echo "\n<br /><br /><h3 style='text-align: center;'>".lang(216).".</h4>";
        echo "\n<center><form name='YT_QS' action='{$_SERVER['SCRIPT_NAME']}' method='POST'>\n";
        echo "<input type='hidden' name='yt_QS' value='on' />\n";
        echo '<label><input type="checkbox" name="cleanname" checked="checked" value="1" /><small>&nbsp;Remove non-supported characters from filename</small></label><br />';
        echo "<select name='yt_fmt' id='QS_fmt'>\n";
        foreach ($this->fmtmaps as $itag => $fmt) {
            if (!empty($this->fmts[$itag])) {
                // Classic Formats
                $size = (!empty($fmt['contentLength']) ? ' ('.bytesToKbOrMbOrGb($fmt['contentLength']).')' : (!empty($fmt['clen']) ? ' ('.bytesToKbOrMbOrGb($fmt['clen']).')' : (!empty($sizes[$itag]) ? ' ('.$sizes[$itag].')' : '')));
                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);
            } else if (!empty($fmt['_codecs'])) {
                // DASH Streams
                $size = (!empty($fmt['contentLength']) ? ' ('.bytesToKbOrMbOrGb($fmt['contentLength']).')' : (!empty($fmt['clen']) ? ' ('.bytesToKbOrMbOrGb($fmt['clen']).')' : ''));
                switch ($fmt['_codecs']['type']) {
                    case 'video':
                        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);
                        break;
                    case 'audio':
                        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);
                        break;
                }
            }
        }
        echo "</select>\n";
 
        $data = $this->DefaultParamArr($this->link);
        $data['ytube_mp4'] = 'on';
        foreach ($data as $n => $v) echo("<input type='hidden' name='$n' id='QS_$n' value='$v' />\n");
 
        echo "<input type='submit' name='Th3-822' value='".lang(209)."' />\n";
        echo "</form></center>\n</body>\n</html>";
        exit;
    }
}
 
//re-written by szal based on original plugin by eqbal
//updated 07 June 2010
// [28-03-2011]  Fixed (!$video_id) regex. - Th3-822
// [29-03-2011]  Added support for captcha. - Th3-822
// [02-04-2011]  Fixed redirect error. [26-04-2011]  Added error msgs.  - Th3-822
// [04-8-2011]  Fixed for recent changes in fmt_stream_map content & some edits maded for work fine. (Redirect is needed yet) - Th3-822
// [12-8-2011]  Added support for videos that need login for verify age & Changed fmt order by quality & Fixed regexps for fileext. - Th3-822
// [13-8-2011]  Some fixes & removed not working code & fixed verify_age function. - Th3-822
// [17-9-2011]  Added function for skip 'verify_controversy' on youtube && Fixed cookies after captcha && Little changes. - Th3-822
// [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
// [17-5-2012]  Fixed captcha (Now uses reCaptcha). - Th3-822
// [14-9-2012]  Fixed Download links & small changes. - Th3-822
// [07-10-2012]  Fixed for redirect at link. - Th3-822
// [02-1-2013]  Using new way for getting links and video info, now it doesn't need login for restricted videos. - Th3-822
// [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
// [04-3-2014]  Re-Added Support for 3GP quality. - Th3-822
// [16-3-2014]  Added functions for decoding ciphered signatures. - Th3-822
// [28-7-2014]  Fixed signature decoding functions. - Th3-822
// [17-12-2014]  Forced https on all the requests for avoid redirect errors. - Th3-822
// [14-1-2015]  Fixed Age Restrictions. (Please, do not annoy my inbox when a plugin fails, go to the forum) - Th3-822
// [21-1-2015]  Fixed backslash in filename when cleanname is off. - Th3-822
// [13-4-2015]  Fixed captcha detection. - Th3-822
// [05-2-2016]  Fixed captcha (Now uses reCaptcha2) & Added cookie storage for it. - Th3-822
// [08-6-2016]  Added support to download DASH formats & Revised video formats handling. - Th3-822
// [30-8-2016]  Fixed slow speed while downloading DASH streams. - Th3-822
// [30-4-2017]  Fixed signature decoding functions. - Th3-822
// [25-1-2018]  Fixed get_video_info. - Th3-822
// [03-2-2019]  Fixed signature decoding functions. - Th3-822
// [25-5-2019]  Fixed embed JS regex. - Th3-822
// [19-6-2019]  Fixed signature key name. - Th3-822
// [10-7-2019]  Fixed signature search and related functions. - Th3-822
// [27-8-2019]  Fixed video title code. - Th3-822
// [04-1-2020]  Fixed fmts handling & Fixed signature search. - Th3-822
// [02-2-2020]  Fixed signature search. - Th3-822
// [15-3-2020]  Fixed embed JS regex. - Th3-822
// [31-5-2020]  Fixed signature search & Rewrote quality selector to parse and show all available formats. - Th3-822
// [17-8-2020]  Fixed signature timestamp search. - Th3-822
// [30-5-2021]  Fixed get_video_info & Now MP4 @ 720p is default (if available) on the selector. - Th3-822

Comments