<?php
if (!defined('FILE_CACHE_MAX_FILE_AGE')) define('FILE_CACHE_MAX_FILE_AGE', 21600);
/**
* @property string html
* @property string httpUserAgent
* @property DOMDocument doc
*/
class vkParser
{
private $httpUserAgent = 'Mozilla/5.0 Windows NT 6.1; Win64; x64 AppleWebKit/537.36 KHTML, like Gecko Chrome/61.0.3163.91 Safari/537.36';
private $html, $doc;
/**
* vkParser constructor.
* @param string $strUrl
*/
public function __construct($strUrl)
{
if (is_readable(__DIR__ . '/cookie/cookieUserVk.cook') === false) {
header('Location: /audio/auth');
exit();
}
if (is_dir(__DIR__ . '/cookie') === false) {
mkdir(__DIR__ . '/cookie');
file_put_contents(__DIR__ . '/cookie/.htaccess', 'Deny From All');
}
if (is_dir(__DIR__ . '/response') === false) {
mkdir(__DIR__ . '/response');
file_put_contents(__DIR__ . '/response/.htaccess', 'Deny From All');
}
$this->doc = new DOMDocument();
$this->cleanDirFile();
}
/**
* @param string $strUrl
* @param string $nameFile
*/
public function fetchMulti($strUrl, $nameFile)
{
$Url = array(
$strUrl . '&offset=0',
$strUrl . '&offset=50',
$strUrl . '&offset=100',
$strUrl . '&offset=150',
);
$multi = curl_multi_init();
$ch = array();
foreach ($Url As $key => $item) {
$ch[$key] = curl_init();
$options = array(
CURLOPT_URL => $item,
CURLOPT_USERAGENT => $this->httpUserAgent,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 15,
CURLOPT_FAILONERROR => true,
CURLOPT_REFERER => 'https://m.vk.com/audio',
CURLOPT_COOKIEFILE => __DIR__ . '/cookie/cookieUserVk.cook',
);
curl_setopt_array($ch[$key], $options);
curl_multi_add_handle($multi, $ch[$key]);
}
do {
$status = curl_multi_exec($multi, $active);
$info = curl_multi_info_read($multi);
if ($info === false)
continue;
if ($info['result'] !== CURLM_OK) {
curl_multi_remove_handle($multi, $info['handle']);
unset($ch[array_search($info['handle'], $ch)]);
}
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
foreach($ch as $key => $chItem) {
$this->html = curl_multi_getcontent($chItem);
$this->_newHtmlDoc();
$domXPath = new DOMXPath($this->doc);
$elements = $domXPath->query("//div[@class='audios_block audios_list _si_container']");
if($elements->length <= 0)
continue;
$elements = $elements->item(0)->childNodes;
if($elements->length <= 0)
continue;
$this->parse($elements, $nameFile);
curl_multi_remove_handle($multi, $chItem);
}
$ch = null;
curl_multi_close($multi);
}
/**
* @param DOMNodeList $elements
* @param string $nameFile
*/
private function parse(DOMNodeList $elements, $nameFile)
{
$response = array();
foreach ($elements AS $item) {
if ($item instanceof DOMElement) {
$i = substr($item->getAttribute('data-id'), 1); // data-id
$ai_info = $item->childNodes->item(1);
$cover = '';
if ($ai_info instanceof DOMElement) // ai_info
{
$cover = $ai_info->getElementsByTagName('div')->item(0)->getAttribute('style');
$cover = preg_replace('/background\-image\:url\((.*)\)/i', '$1', $cover);
$ai_info = $ai_info->childNodes->item(5);
if ($ai_info instanceof DOMElement) // ai_body
{
$ai_body = $ai_info->childNodes;
$ai_file = $ai_body->item(5)->getAttribute('value');
if (empty($ai_file))
continue;
$response[$i] = array();
$response[$i]['dur'] = $ai_body->item(1)->getAttribute('data-dur'); // ai_dur
$response[$i]['cDur'] = $ai_body->item(1)->textContent; // convert_dur
// ai_label
$response[$i]['title'] = $ai_body->item(3)->childNodes->item(1)->textContent; // ai_title
$response[$i]['artist'] = $ai_body->item(3)->childNodes->item(5)->textContent; // ai_artist
//file
$response[$i]['url'] = $ai_file; // ai_file
$response[$i]['file'] = mt_rand() . '-' . $i . '.mp3'; // ai_file
$response[$i]['cover'] = parse_url($cover, PHP_URL_PATH); // cover
if(empty($response[$i]['cover']) === false) {
$response[$i]['cover'] = "background-image: url('/audio/get/" . $this->genNameFile($nameFile) . '/' . $i . '/cover' . $response[$i]['cover'] . "')";
$response[$i]['coverServer'] = $cover;
}
}
}
}
}
$this->writeDataResponse($response, $nameFile);
unset($response);
}
/**
* @return bool
*/
private function isAjax()
{
$server = getenv('HTTP_X_REQUESTED_WITH');
$server = ($server !== false ? strtolower($server) : null);
return $server === 'xmlhttprequest';
}
/**
* Clear dir
*/
private function cleanDirFile()
{
$fileDIR = $_SERVER['DOCUMENT_ROOT'] . '/audio/file/';
if ($this->isAjax() === false) {
foreach (glob($fileDIR . $_SESSION['audioSession'] . '*.mp3') AS $item)
unlink($item);
}
if (is_readable($fileDIR . 'lastCleanTime.touch') === false)
touch($fileDIR . 'lastCleanTime.touch');
$timeAgo = time() - 4000;
if (filemtime($fileDIR . 'lastCleanTime.touch') < $timeAgo) {
foreach (glob($fileDIR . '*.mp3') AS $item)
if (filemtime($item) < $timeAgo)
unlink($item);
touch($fileDIR . 'lastCleanTime.touch');
}
}
/**
* @param string $file
* @return bool
*/
public function isDataResponse($file)
{
$file = $this->genNameFile($file);
$cacheFile = __DIR__ . '/response/' . $file . '.data.r';
$data = array();
if (is_readable($cacheFile))
$data = include $cacheFile;
if (empty($data) === true || (is_readable($cacheFile) === true && (filemtime($cacheFile) + FILE_CACHE_MAX_FILE_AGE) < time()))
return true;
return false;
}
/**
* @param string $file
* @return array
*/
public function getDataResponse($file)
{
$file = $this->genNameFile($file);
$cacheFile = __DIR__ . '/response/' . $file . '.data.r';
$r = array();
$r['nameFile'] = $file;
$r['response'] = array();
if (is_readable($cacheFile) === true)
$r['response'] = include $cacheFile;
return $r;
}
private function genNameFile($name) {
return md5(strtolower($name));
}
/**
* @param array $response
* @param string $file
* @return bool
*/
private function writeDataResponse(array $response, $file)
{
$file = $this->genNameFile($file);
$cacheFile = __DIR__ . '/response/' . $file . '.data.r';
if ($this->isDataResponse($file))
file_put_contents($cacheFile, '<?php return ' . var_export($response, true) . ';', LOCK_EX);
return true;
}
/**
* Generate html document
*/
private function _newHtmlDoc()
{
if (!empty($this->html)) {
$old_libxml_error = libxml_use_internal_errors(true);
$this->doc->loadHTML($this->html);
libxml_use_internal_errors($old_libxml_error);
}
}
public function __destruct()
{
$this->doc = null;
}
}