PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/PlayTV.php

https://github.com/tejastank/Scripts
PHP | 655 lines | 266 code | 5 blank | 384 comment | 17 complexity | 4d0bb57445c03d104ca67b513d10fc15 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. class CLI
  3. {
  4. protected static $ACCEPTED = array(
  5. 0 => array(
  6. 'help' => 'displays this help',
  7. 'list' => 'display formatted channels list and exit',
  8. 'print' => 'only print the base rtmpdump command, don\'t start anything',
  9. 'quiet' => 'disables unnecessary output'
  10. ),
  11. 1 => array(
  12. 'proxy' => 'use proxy to retrieve channel information',
  13. 'url' => 'use specified url without displaying channels list'
  14. )
  15. );
  16. var $params = array();
  17. function __construct()
  18. {
  19. global $argc, $argv;
  20. // Parse params
  21. if ($argc > 1)
  22. {
  23. $paramSwitch = false;
  24. for ($i = 1; $i < $argc; $i++)
  25. {
  26. $arg = $argv[$i];
  27. $isSwitch = preg_match('/^--/', $arg);
  28. if ($isSwitch)
  29. $arg = preg_replace('/^--/', '', $arg);
  30. if ($paramSwitch && $isSwitch)
  31. {
  32. echo "[param] expected after '$paramSwitch' switch (" . self::$ACCEPTED[1][$paramSwitch] . ")\n";
  33. exit(1);
  34. }
  35. else if (!$paramSwitch && !$isSwitch)
  36. {
  37. echo "'$arg' is an invalid switch, use --help to display valid switches\n";
  38. exit(1);
  39. }
  40. else if (!$paramSwitch && $isSwitch)
  41. {
  42. if (isset($this->params[$arg]))
  43. {
  44. echo "'$arg' switch cannot occur more than once\n";
  45. exit(1);
  46. }
  47. $this->params[$arg] = true;
  48. if (isset(self::$ACCEPTED[1][$arg]))
  49. $paramSwitch = $arg;
  50. else if (!isset(self::$ACCEPTED[0][$arg]))
  51. {
  52. echo "there's no '$arg' switch, use --help to display all switches\n";
  53. exit(1);
  54. }
  55. }
  56. else if ($paramSwitch && !$isSwitch)
  57. {
  58. $this->params[$paramSwitch] = $arg;
  59. $paramSwitch = false;
  60. }
  61. }
  62. }
  63. // Final check
  64. foreach ($this->params as $k => $v)
  65. if (isset(self::$ACCEPTED[1][$k]) && $v === true)
  66. {
  67. echo "[param] expected after '$k' switch (" . self::$ACCEPTED[1][$k] . ")\n";
  68. exit(1);
  69. }
  70. }
  71. function getParam($name)
  72. {
  73. if (isset($this->params[$name]))
  74. return $this->params[$name];
  75. else
  76. return "";
  77. }
  78. function displayHelp()
  79. {
  80. echo "You can use script with following switches: \n\n";
  81. foreach (self::$ACCEPTED[0] as $key => $value)
  82. printf(" --%-14s%s\n", $key, $value);
  83. foreach (self::$ACCEPTED[1] as $key => $value)
  84. printf(" --%-5s%-9s%s\n", $key, " [param]", $value);
  85. }
  86. }
  87. define('CRYPT_XXTEA_DELTA', 0x9E3779B9);
  88. class Crypt_XXTEA
  89. {
  90. var $_key;
  91. function setKey($key)
  92. {
  93. if (is_string($key))
  94. $k = $this->_str2long($key, false);
  95. elseif (is_array($key))
  96. $k = $key;
  97. else
  98. qecho("The secret key must be a string or long integer array\n");
  99. if (count($k) > 4)
  100. qecho("The secret key cannot be more than 16 characters or 4 long values\n");
  101. elseif (count($k) == 0)
  102. qecho("The secret key cannot be empty\n");
  103. elseif (count($k) < 4)
  104. for ($i = count($k); $i < 4; $i++)
  105. $k[$i] = 0;
  106. $this->_key = $k;
  107. return true;
  108. }
  109. function encrypt($plaintext)
  110. {
  111. if ($this->_key == null)
  112. qecho("Secret key is undefined\n");
  113. if (is_string($plaintext))
  114. return $this->_encryptString($plaintext);
  115. elseif (is_array($plaintext))
  116. return $this->_encryptArray($plaintext);
  117. else
  118. qecho("The plain text must be a string or long integer array\n");
  119. }
  120. function decrypt($ciphertext)
  121. {
  122. if ($this->_key == null)
  123. qecho("Secret key is undefined\n");
  124. if (is_string($ciphertext))
  125. return $this->_decryptString($ciphertext);
  126. elseif (is_array($ciphertext))
  127. return $this->_decryptArray($ciphertext);
  128. else
  129. qecho("The cipher text must be a string or long integer array\n");
  130. }
  131. function _encryptString($str)
  132. {
  133. if ($str == '')
  134. return '';
  135. $v = $this->_str2long($str, false);
  136. $v = $this->_encryptArray($v);
  137. return $this->_long2str($v, false);
  138. }
  139. function _encryptArray($v)
  140. {
  141. $n = count($v) - 1;
  142. $z = $v[$n];
  143. $y = $v[0];
  144. $q = floor(6 + 52 / ($n + 1));
  145. $sum = 0;
  146. while (0 < $q--)
  147. {
  148. $sum = $this->_int32($sum + CRYPT_XXTEA_DELTA);
  149. $e = $sum >> 2 & 3;
  150. for ($p = 0; $p < $n; $p++)
  151. {
  152. $y = $v[$p + 1];
  153. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  154. $z = $v[$p] = $this->_int32($v[$p] + $mx);
  155. }
  156. $y = $v[0];
  157. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  158. $z = $v[$n] = $this->_int32($v[$n] + $mx);
  159. }
  160. return $v;
  161. }
  162. function _decryptString($str)
  163. {
  164. if ($str == '')
  165. return '';
  166. $v = $this->_str2long($str, false);
  167. $v = $this->_decryptArray($v);
  168. return $this->_long2str($v, false);
  169. }
  170. function _decryptArray($v)
  171. {
  172. $n = count($v) - 1;
  173. $z = $v[$n];
  174. $y = $v[0];
  175. $q = floor(6 + 52 / ($n + 1));
  176. $sum = $this->_int32($q * CRYPT_XXTEA_DELTA);
  177. while ($sum != 0)
  178. {
  179. $e = $sum >> 2 & 3;
  180. for ($p = $n; $p > 0; $p--)
  181. {
  182. $z = $v[$p - 1];
  183. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  184. $y = $v[$p] = $this->_int32($v[$p] - $mx);
  185. }
  186. $z = $v[$n];
  187. $mx = $this->_int32((($z >> 5 & 0x07FFFFFF) ^ $y << 2) + (($y >> 3 & 0x1FFFFFFF) ^ $z << 4)) ^ $this->_int32(($sum ^ $y) + ($this->_key[$p & 3 ^ $e] ^ $z));
  188. $y = $v[0] = $this->_int32($v[0] - $mx);
  189. $sum = $this->_int32($sum - CRYPT_XXTEA_DELTA);
  190. }
  191. return $v;
  192. }
  193. function _long2str($v, $w)
  194. {
  195. $len = count($v);
  196. $s = '';
  197. for ($i = 0; $i < $len; $i++)
  198. $s .= pack('V', $v[$i]);
  199. if ($w)
  200. return substr($s, 0, $v[$len - 1]);
  201. else
  202. return $s;
  203. }
  204. function _str2long($s, $w)
  205. {
  206. $v = array_values(unpack('V*', $s . str_repeat("\0", (4 - strlen($s) % 4) & 3)));
  207. if ($w)
  208. $v[] = strlen($s);
  209. return $v;
  210. }
  211. function _int32($n)
  212. {
  213. while ($n >= 2147483648)
  214. $n -= 4294967296;
  215. while ($n <= -2147483649)
  216. $n += 4294967296;
  217. return (int) $n;
  218. }
  219. }
  220. class cURL
  221. {
  222. var $headers, $user_agent, $compression, $cookie_file;
  223. var $cert_check, $proxy;
  224. static $ref = 0;
  225. function cURL($cookies = true, $cookie = 'Cookies.txt', $compression = 'gzip', $proxy = '')
  226. {
  227. $this->headers = $this->headers();
  228. $this->user_agent = 'Mozilla/5.0 (Windows NT 5.1; rv:17.0) Gecko/20100101 Firefox/17.0';
  229. $this->compression = $compression;
  230. $this->cookies = $cookies;
  231. if ($this->cookies == true)
  232. $this->cookie($cookie);
  233. $this->cert_check = true;
  234. $this->proxy = $proxy;
  235. self::$ref++;
  236. }
  237. function __destruct()
  238. {
  239. if ((self::$ref <= 1) and file_exists($this->cookie_file))
  240. unlink($this->cookie_file);
  241. self::$ref--;
  242. }
  243. function headers()
  244. {
  245. $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
  246. $headers[] = 'Connection: Keep-Alive';
  247. return $headers;
  248. }
  249. function cookie($cookie_file)
  250. {
  251. if (file_exists($cookie_file))
  252. $this->cookie_file = $cookie_file;
  253. else
  254. {
  255. $file = fopen($cookie_file, 'w') or $this->error('The cookie file could not be opened. Make sure this directory has the correct permissions');
  256. $this->cookie_file = $cookie_file;
  257. fclose($file);
  258. }
  259. }
  260. function get($url)
  261. {
  262. $process = curl_init($url);
  263. $headers = $this->headers;
  264. $headers[] = 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8';
  265. curl_setopt($process, CURLOPT_HTTPHEADER, $headers);
  266. curl_setopt($process, CURLOPT_HEADER, 0);
  267. curl_setopt($process, CURLOPT_USERAGENT, $this->user_agent);
  268. if ($this->cookies == true)
  269. {
  270. curl_setopt($process, CURLOPT_COOKIEFILE, $this->cookie_file);
  271. curl_setopt($process, CURLOPT_COOKIEJAR, $this->cookie_file);
  272. }
  273. curl_setopt($process, CURLOPT_ENCODING, $this->compression);
  274. curl_setopt($process, CURLOPT_TIMEOUT, 30);
  275. if ($this->proxy)
  276. $this->setProxy($process, $this->proxy);
  277. curl_setopt($process, CURLOPT_RETURNTRANSFER, 1);
  278. curl_setopt($process, CURLOPT_FOLLOWLOCATION, 1);
  279. if (!$this->cert_check)
  280. curl_setopt($process, CURLOPT_SSL_VERIFYPEER, 0);
  281. $return = curl_exec($process);
  282. curl_close($process);
  283. return $return;
  284. }
  285. function post($url, $data)
  286. {
  287. $process = curl_init($url);
  288. curl_setopt($process, CURLOPT_HTTPHEADER, $this->headers);
  289. curl_setopt($process, CURLOPT_HEADER, 1);
  290. curl_setopt($process, CURLOPT_USERAGENT, $this->user_agent);
  291. if ($this->cookies == true)
  292. {
  293. curl_setopt($process, CURLOPT_COOKIEFILE, $this->cookie_file);
  294. curl_setopt($process, CURLOPT_COOKIEJAR, $this->cookie_file);
  295. }
  296. curl_setopt($process, CURLOPT_ENCODING, $this->compression);
  297. curl_setopt($process, CURLOPT_TIMEOUT, 30);
  298. if ($this->proxy)
  299. $this->setProxy($process, $this->proxy);
  300. curl_setopt($process, CURLOPT_POSTFIELDS, $data);
  301. curl_setopt($process, CURLOPT_RETURNTRANSFER, 1);
  302. curl_setopt($process, CURLOPT_FOLLOWLOCATION, 1);
  303. curl_setopt($process, CURLOPT_POST, 1);
  304. if (!$this->cert_check)
  305. curl_setopt($process, CURLOPT_SSL_VERIFYPEER, 0);
  306. $return = curl_exec($process);
  307. curl_close($process);
  308. return $return;
  309. }
  310. function setProxy(&$process, $proxy)
  311. {
  312. $type = substr($proxy, 0, stripos($proxy, "://"));
  313. if ($type)
  314. {
  315. $type = strtolower($type);
  316. $proxy = substr($proxy, stripos($proxy, "://") + 3);
  317. }
  318. switch ($type)
  319. {
  320. case "socks4":
  321. $type = CURLPROXY_SOCKS4;
  322. break;
  323. case "socks5":
  324. $type = CURLPROXY_SOCKS5;
  325. break;
  326. default:
  327. $type = CURLPROXY_HTTP;
  328. }
  329. curl_setopt($process, CURLOPT_PROXY, $proxy);
  330. curl_setopt($process, CURLOPT_PROXYTYPE, $type);
  331. }
  332. function error($error)
  333. {
  334. echo "cURL Error : $error";
  335. die;
  336. }
  337. }
  338. function runAsyncBatch($command, $filename)
  339. {
  340. $BatchFile = fopen("PlayTV.bat", 'w');
  341. fwrite($BatchFile, "@Echo off\r\n");
  342. fwrite($BatchFile, "Title $filename\r\n");
  343. fwrite($BatchFile, "$command\r\n");
  344. fwrite($BatchFile, "Del \"PlayTV.bat\"\r\n");
  345. fclose($BatchFile);
  346. $WshShell = new COM("WScript.Shell");
  347. $oExec = $WshShell->Run("PlayTV.bat", 1, false);
  348. unset($WshShell, $oExec);
  349. }
  350. function SafeFileName($filename)
  351. {
  352. $len = strlen($filename);
  353. for ($i = 0; $i < $len; $i++)
  354. {
  355. $char = ord($filename[$i]);
  356. if (($char < 32) || ($char >= 127))
  357. $filename = substr_replace($filename, ' ', $i, 1);
  358. }
  359. $filename = preg_replace('/[\/\\\?\*\:\|\<\>]/i', ' ', $filename);
  360. $filename = preg_replace('/\s\s+/i', ' ', $filename);
  361. $filename = trim($filename);
  362. return $filename;
  363. }
  364. function ShowHeader($header)
  365. {
  366. global $cli;
  367. $len = strlen($header);
  368. $width = (int) ((80 - $len) / 2) + $len;
  369. $format = "\n%" . $width . "s\n\n";
  370. if (!$cli->getParam('quiet'))
  371. printf($format, $header);
  372. }
  373. function KeyName(array $a, $pos)
  374. {
  375. $temp = array_slice($a, $pos, 1, true);
  376. return key($temp);
  377. }
  378. function ci_uksort($a, $b)
  379. {
  380. $a = strtolower($a);
  381. $b = strtolower($b);
  382. return strnatcmp($a, $b);
  383. }
  384. function Display($items, $format, $columns)
  385. {
  386. global $cli;
  387. // Display formatted channels list for external script
  388. if ($cli->getParam('list'))
  389. {
  390. foreach ($items as $name => $url)
  391. {
  392. printf("%-25.20s", preg_replace('/=/', '-', $name));
  393. printf(" = %s\n", $url);
  394. }
  395. exit(0);
  396. }
  397. $numcols = $columns;
  398. $numitems = count($items);
  399. $numrows = ceil($numitems / $numcols);
  400. for ($row = 1; $row <= $numrows; $row++)
  401. {
  402. $cell = 0;
  403. for ($col = 1; $col <= $numcols; $col++)
  404. {
  405. if ($col === 1)
  406. {
  407. $cell += $row;
  408. printf($format, $cell, KeyName($items, $cell - 1));
  409. }
  410. else
  411. {
  412. $cell += $numrows;
  413. if (isset($items[KeyName($items, $cell - 1)]))
  414. printf($format, $cell, KeyName($items, $cell - 1));
  415. }
  416. }
  417. echo "\n\n";
  418. }
  419. }
  420. function Close($message)
  421. {
  422. global $cli, $windows;
  423. if ($message)
  424. qecho($message . "\n");
  425. if ($windows)
  426. exec("chcp 1252");
  427. if (!count($cli->params))
  428. sleep(2);
  429. die();
  430. }
  431. function ShowChannel($url, $filename)
  432. {
  433. global $cc, $cli, $format, $vlc, $windows, $xxtea;
  434. qecho("Retrieving html . . .\n");
  435. $cc->headers = $cc->headers();
  436. // Retrieve channel id and primary key
  437. $timestamp = time();
  438. $player_id = $url;
  439. $init = $cc->get("http://tvplayer.playtv.fr/js/$player_id.js?_=$timestamp");
  440. preg_match("/b:[^{]*?({[^}]+})/i", $init, $init);
  441. $init = json_decode(trim($init[1]));
  442. if (!$init)
  443. Close("Unable to retrieve initialization parameters");
  444. $a = pack("H*", $init->{'a'});
  445. $b = pack("H*", $init->{'b'});
  446. $xxtea->setKey("object");
  447. $params = json_decode(trim($xxtea->decrypt($b)));
  448. if (!$params)
  449. Close("Unable to decode initialization parameters");
  450. $key = $xxtea->decrypt(pack("H*", $params->{'k'}));
  451. $xxtea->setKey($key);
  452. $params = json_decode(trim($xxtea->decrypt($a)));
  453. $channel_id = $params->{'i'};
  454. $api_url = $params->{'u'};
  455. // Generate parameter request
  456. $request = json_encode(array(
  457. 'i' => $channel_id,
  458. 't' => $timestamp,
  459. 'h' => 'playtv.fr',
  460. 'a' => 5
  461. ));
  462. $request = unpack("H*", $xxtea->encrypt($request));
  463. $request = $request[1];
  464. $cc->headers[] = "Referer: http://static.playtv.fr/swf/tvplayer.swf?r=18";
  465. $cc->headers[] = "x-flash-version: 11,4,402,265";
  466. $response = $cc->get($api_url . $request);
  467. // Decode server response
  468. $response = pack("H*", $response);
  469. $params = json_decode(trim($xxtea->decrypt($response)));
  470. if (!$params)
  471. Close("Unable to decode server response");
  472. if (isset($params->{'s'}[1]))
  473. $streams = $params->{'s'}[0]->{'bitrate'} > $params->{'s'}[1]->{'bitrate'} ? $params->{'s'}[0] : $params->{'s'}[1];
  474. else
  475. $streams = $params->{'s'}[0];
  476. $scheme = $streams->{'scheme'};
  477. $host = $streams->{'host'};
  478. $port = $streams->{'port'};
  479. $app = $streams->{'application'};
  480. $playpath = $streams->{'stream'};
  481. $token = $streams->{'token'};
  482. $title = $streams->{'title'};
  483. // Generate authentication token for rtmp server
  484. $t = $params->{'j'}->{'t'};
  485. $k = $params->{'j'}->{'k'};
  486. $xxtea->setKey("object");
  487. $key = $xxtea->decrypt(pack("H*", $k));
  488. $xxtea->setKey($key);
  489. $auth = unpack("H*", $xxtea->encrypt($t));
  490. $auth = $auth[1];
  491. if ($scheme == "http")
  492. qprintf($format, "HTTP Url", "$scheme://$host" . (isset($port) ? ":$port" : "") . "/$playpath");
  493. else
  494. qprintf($format, "RTMP Url", "$scheme://$host" . (isset($port) ? ":$port" : "") . "/$app");
  495. qprintf($format, "Playpath", $playpath);
  496. qprintf($format, "Auth", $auth);
  497. $filename = SafeFileName($filename);
  498. if (file_exists($filename . ".flv"))
  499. unlink($filename . ".flv");
  500. if ($scheme == "http")
  501. {
  502. $basecmd = "$scheme://$host" . (isset($port) ? ":$port" : "") . "/$playpath";
  503. $command = "\"$vlc\" --meta-title \"$title\" \"$basecmd\"";
  504. }
  505. else
  506. {
  507. $basecmd = "rtmpdump -r \"$scheme://$host" . (isset($port) ? ":$port" : "") . "/$app\" -a \"$app\" -s \"http://static.playtv.fr/swf/tvplayer.swf\" -p \"http://playtv.fr/television\" -C S:$auth " . (isset($token) ? "-T \"$token\" " : "") . "--live -y \"$playpath\"";
  508. $command = $basecmd . " | \"$vlc\" --meta-title \"$title\" -";
  509. }
  510. if ($cli->getParam('print'))
  511. {
  512. echo $basecmd;
  513. exit(0);
  514. }
  515. qprintf($format, "Command", $command);
  516. if ($host && $playpath && $auth)
  517. if ($windows)
  518. runAsyncBatch($command, $filename);
  519. else
  520. exec($command);
  521. }
  522. function qecho($str)
  523. {
  524. global $cli;
  525. if (!$cli->getParam('quiet'))
  526. echo $str;
  527. }
  528. function qprintf($format, $param, $arg)
  529. {
  530. global $cli;
  531. if (!$cli->getParam('quiet'))
  532. printf($format, $param, $arg);
  533. }
  534. // Global code starts here
  535. $header = "KSV PlayTV Downloader";
  536. $format = "%-8s: %s\n";
  537. $ChannelFormat = "%2d) %-22.21s";
  538. strncasecmp(php_uname('s'), "Win", 3) == 0 ? $windows = true : $windows = false;
  539. if ($windows)
  540. {
  541. exec("chcp 65001");
  542. if (file_exists("C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe"))
  543. $vlc = "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe";
  544. else
  545. $vlc = "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe";
  546. }
  547. else
  548. $vlc = "vlc";
  549. $cli = new CLI();
  550. $cc = new cURL();
  551. $xxtea = new Crypt_XXTEA();
  552. ShowHeader($header);
  553. if ($cli->getParam('help'))
  554. {
  555. $cli->displayHelp();
  556. Close("");
  557. }
  558. if ($cli->getParam('proxy'))
  559. $cc->proxy = $cli->getParam('proxy');
  560. if ($cli->getParam('url'))
  561. {
  562. $url = $cli->getParam('url');
  563. $filename = $url;
  564. ShowChannel($url, $filename);
  565. }
  566. else
  567. {
  568. $html = $cc->get("http://playtv.fr/television/");
  569. preg_match_all('/<a.*?data-channel="([^"]+).*?data-playerid="([^"]+)[^>]+>/i', $html, $links);
  570. for ($i = 0; $i < count($links[1]); $i++)
  571. $ChannelList[$links[1][$i]] = $links[2][$i];
  572. uksort($ChannelList, 'ci_uksort');
  573. $FirstRun = true;
  574. $KeepRunning = true;
  575. while ($KeepRunning)
  576. {
  577. if ($FirstRun)
  578. $FirstRun = false;
  579. else
  580. ShowHeader($header);
  581. Display($ChannelList, $ChannelFormat, 3);
  582. echo "Enter Channel Number : ";
  583. $channel = trim(fgets(STDIN));
  584. if (is_numeric($channel) && ($channel >= 1) && ($channel <= count($ChannelList)))
  585. {
  586. $url = $ChannelList[KeyName($ChannelList, $channel - 1)];
  587. $filename = KeyName($ChannelList, $channel - 1);
  588. ShowChannel($url, $filename);
  589. }
  590. else
  591. $KeepRunning = false;
  592. }
  593. }
  594. Close("Finished");
  595. ?>