ytclass.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <?php
  2. class YTDownloader {
  3. private $cache_dir;
  4. private $cookie_dir;
  5. private $itag_info = array(
  6. // Full Video
  7. 18 => "MP4[640x360]",
  8. 22 => "HD MP4[1280x720]",
  9. 36 => "3GP[320x180]",
  10. 43 => "WEBM[640x360]",
  11. 17 => "3GP[176x144]",
  12. // DASH videos
  13. 137 => "(Video Only) MP4[1920x1080]",
  14. 248 => "(Video Only) WEBM[1920x1080]",
  15. 136 => "(Video Only) MP4[1280x720]",
  16. 247 => "(Video Only) WEBM[1280x720]",
  17. 135 => "(Video Only) MP4[854x480]",
  18. 244 => "(Video Only) WEBM[854x480]",
  19. 134 => "(Video Only) MP4[640x360]",
  20. 243 => "(Video Only) WEBM[640x360]",
  21. 133 => "(Video Only) MP4[320x240]",
  22. 242 => "(Video Only) WEBM[320x240]",
  23. 160 => "(Video Only) MP4[176x144]",
  24. 278 => "(Video Only) WEBM[176x144]",
  25. // Dash Audios
  26. 140 => "(Audio Only) M4A[128Kbps]",
  27. 171 => "(Audio Only) WEBM[128Kbps]",
  28. 249 => "(Audio Only) WEBM[50Kbps]",
  29. 250 => "(Audio Only) WEBM[70Kbps]",
  30. 251 => "(Audio Only) WEBM[160Kbps]"
  31. );
  32. private $itag_ext = array(
  33. // Full Video
  34. 18 => ".mp4",
  35. 22 => ".mp4",
  36. 36 => ".3gp",
  37. 43 => ".webm",
  38. 17 => ".3gp",
  39. // DASH videos
  40. 137 => ".mp4",
  41. 248 => ".webm",
  42. 136 => ".mp4",
  43. 247 => ".webm",
  44. 135 => ".mp4",
  45. 244 => ".webm",
  46. 134 => ".mp4",
  47. 243 => ".webm",
  48. 133 => ".mp4",
  49. 242 => ".webm",
  50. 160 => ".mp4",
  51. 278 => ".webm",
  52. // Dash Audios
  53. 140 => ".mp4",
  54. 171 => ".webm",
  55. 249 => ".webm",
  56. 250 => ".webm",
  57. 251 => ".webm"
  58. );
  59. function __construct(){
  60. $this->cache_dir = dirname(__FILE__).'/.cache';
  61. $this->cookie_dir = sys_get_temp_dir();
  62. if(!file_exists($this->cache_dir)) {
  63. mkdir($this->cache_dir,0755);
  64. }
  65. }
  66. public function getDownloadLinks($id)
  67. {
  68. $returnData = FALSE;
  69. $videoID = $this->extractId($id);
  70. $webPage = $this->curlGet('https://www.youtube.com/watch?v='.$videoID);
  71. $sts = null;
  72. if(preg_match('|"sts":([0-9]{4,}),"|i', $webPage, $matches)) {
  73. $sts = $matches[1];
  74. }
  75. foreach(array('vevo', 'embedded', 'detailpage') as $elKey) {
  76. $query = http_build_query(array(
  77. 'c' => 'web',
  78. 'el' => $elKey,
  79. 'hl' => 'en_US',
  80. 'sts' => $sts,
  81. 'cver' => 'html5',
  82. 'eurl' => "https://youtube.googleapis.com/v/{$videoID}",
  83. 'html5' => '1',
  84. 'iframe' => '1',
  85. 'authuser' => '1',
  86. 'video_id' => $videoID,
  87. ));
  88. if($this->is_Ok($videoData = $this->curlGet("http://www.youtube.com/get_video_info?{$query}"))) {
  89. parse_str($videoData, $videoData);
  90. break;
  91. }
  92. }
  93. if(isset($videoData['status']) && $videoData['status'] !== 'fail') {
  94. $vInfo['Title'] = $videoData['title'];
  95. $vInfo['ChannelName'] = $videoData['author'];
  96. $vInfo['ChannelId'] = $videoData['ucid'];
  97. $vInfo['Thumbnail'] = str_replace('default', 'maxresdefault', $videoData['thumbnail_url']);
  98. $vInfo['Duration'] = $videoData['length_seconds'];
  99. $vInfo['Rating'] = $videoData['avg_rating'];
  100. }
  101. if (isset($videoData['url_encoded_fmt_stream_map']) && isset($videoData['adaptive_fmts'])) {
  102. $draft1 = explode(',',$videoData['url_encoded_fmt_stream_map']);
  103. $draft2 = explode(',',$videoData['adaptive_fmts']);
  104. foreach ($draft1 as $key) {
  105. $draftLink[] = $key;
  106. }
  107. foreach ($draft2 as $key) {
  108. $draftLink[] = $key;
  109. }
  110. foreach($draftLink as $dlink) {
  111. parse_str($dlink,$mLink[]);
  112. }
  113. if (isset($mLink[0]['s'])) {
  114. $instructions = $this->get_instructions($webPage);
  115. }
  116. foreach($mLink as $linker) {
  117. if(isset($linker['s'])) {
  118. $linkData[] = array(
  119. 'url' => preg_replace('@(https\:\/\/)[^\.]+(\.googlevideo\.com)@', 'https://redirector$2', $linker['url']).'&signature='.$this->sig_decipher($linker['s'], $instructions).'&title='.$this->clean_name($videoData['title']),
  120. 'itag' => $linker['itag'],
  121. 'type' => isset($this->itag_info[$linker['itag']]) ? $this->itag_info[$linker['itag']] : 'Unknown'
  122. );
  123. } else {
  124. $linkData[] = array(
  125. 'url' => preg_replace('@(https\:\/\/)[^\.]+(\.googlevideo\.com)@', 'https://redirector$2', $linker['url']).'&title='.$this->clean_name($videoData['title']),
  126. 'itag' => $linker['itag'],
  127. 'type' => isset($this->itag_info[$linker['itag']]) ? $this->itag_info[$linker['itag']] : 'Unknown'
  128. );
  129. }
  130. }
  131. }
  132. if (!empty($vInfo)) {
  133. $returnData['info'] = $vInfo;
  134. }
  135. if (!empty($linkData)) {
  136. $returnData['dl'] = $linkData;
  137. }
  138. return $returnData;
  139. }
  140. protected function curlGet($url)
  141. {
  142. if(in_array('curl', get_loaded_extensions())){
  143. $ch = curl_init($url);
  144. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0');
  145. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  146. curl_setopt($ch, CURLOPT_HEADER, 0);
  147. //curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  148. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  149. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  150. $result = curl_exec($ch);
  151. curl_close($ch);
  152. return $result;
  153. }
  154. if(ini_get('allow_url_fopen')){
  155. return @file_get_contents($url);
  156. }
  157. return FALSE;
  158. }
  159. private function is_Ok($var) {
  160. if(!preg_match('|status=fail|i',$var)) {
  161. return true;
  162. }
  163. }
  164. private function extractId($str)
  165. {
  166. if(preg_match('/[a-z0-9_-]{11}/i', $str, $matches)){
  167. return $matches[0];
  168. }
  169. return FALSE;
  170. }
  171. private function get_instructions($html) {
  172. $playerPattern = '/"assets":.+?"js":\s*("[^"]+")/';
  173. if(preg_match($playerPattern, $html, $matches) && is_string($_player = json_decode($matches[1])) && strlen($_player) >= 1) {
  174. $playerLink = substr($_player, 0, 2) == '//' ? "https:{$_player}" : "https://www.youtube.com{$_player}";
  175. $cache_player = $this->cache_dir.'/.ht-'.md5($_player);
  176. if(file_exists($cache_player)) {
  177. return unserialize(file_get_contents($cache_player));
  178. } else {
  179. $js_code = $this->curlGet($playerLink);
  180. $instructions = $this->sig_js_decode($js_code);
  181. if($instructions){
  182. file_put_contents($cache_player, serialize($instructions));
  183. return $instructions;
  184. }
  185. }
  186. }
  187. return false;
  188. }
  189. private function clean_name($name)
  190. {
  191. $special_chars = array(".","?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
  192. $filename = str_replace($special_chars,' ',$name);
  193. $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename );
  194. $filename = str_replace( array( '%20', '+', ' '), '-', $filename );
  195. $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename );
  196. $filename = trim( $filename, '.-_' );
  197. return $filename;
  198. }
  199. private function sig_decipher($signature, $instructions)
  200. {
  201. foreach($instructions as $opt){
  202. $command = $opt[0];
  203. $value = $opt[1];
  204. if($command == 'swap'){
  205. $temp = $signature[0];
  206. $signature[0] = $signature[$value % strlen($signature)];
  207. $signature[$value] = $temp;
  208. } elseif($command == 'splice'){
  209. $signature = substr($signature, $value);
  210. } elseif($command == 'reverse'){
  211. $signature = strrev($signature);
  212. }
  213. }
  214. return trim($signature);
  215. }
  216. private function sig_js_decode($file){
  217. $script = $this->getBetween($file, 'a=a.split("");', ';return a.join("")');
  218. $script = str_replace(array("a,","\n"), array(',',''), $script);
  219. $script2 = $this->getBetween($file, 'var ' . substr($script, 0, 2).'={', '};');
  220. $script2 = str_replace('a,b', 'a', $script2);
  221. $script = str_replace(substr($script, 0, 2).'.', '', $script);
  222. $script = str_replace('(', '', $script);
  223. $script = str_replace(')', '', $script);
  224. $script_ex = explode(";", $script);
  225. $script2_ex = explode("\n", $script2);
  226. for($i = 0; $i < count($script2_ex); $i++) {
  227. $tmp = isset($script2_ex[$i]) ? explode(':', $script2_ex[$i]) : [];
  228. $n = isset($tmp[0]) ? $tmp[0] : '';
  229. $m = isset($tmp[1]) ? $tmp[1] : '';
  230. $tempS[$n] = $m;
  231. }
  232. for($i = 0; $i < count($script_ex); $i++) {
  233. $tmp = isset($script_ex[$i]) ? explode(',', $script_ex[$i]) : [];
  234. $a = isset($tmp[0]) ? $tmp[0] : '';
  235. $b = isset($tmp[1]) ? $tmp[1] : '';
  236. $deKey[] = $this->createCommad($a, $b, $tempS);
  237. }
  238. return $deKey;
  239. }
  240. private function createCommad($value, $num, $source)
  241. {
  242. $result = '';
  243. if (isset($source[$value]) && mb_strpos($source[$value], 'reverse')) {
  244. $result = array('reverse', '');
  245. } elseif (isset($source[$value]) && mb_strpos($source[$value], 'a.splice')) {
  246. $result = array('splice', $num);
  247. } else {
  248. $result = array('swap', $num);
  249. }
  250. return $result;
  251. }
  252. private function getBetween($content, $start, $end)
  253. {
  254. $r = explode($start, $content);
  255. if (isset($r[1])) {
  256. $r = explode($end, $r[1]);
  257. return $r[0];
  258. }
  259. return '';
  260. }
  261. }