site = $s->get(); $t = new Tmux(); $this->tmux = $t->get(); $this->p = new Postprocess(); $this->echooutput = $echooutput; $this->c = new ColorCLI(); $this->db = new DB(); $this->m = new Movie(); $this->consoleTools = new ConsoleTools(); $this->tmpPath = $this->site->tmpunrarpath; $this->audiofileregex = '\.(AAC|AIFF|APE|AC3|ASF|DTS|FLAC|MKA|MKS|MP2|MP3|RA|OGG|OGM|W64|WAV|WMA)'; $this->ignorebookregex = '/\b(epub|lit|mobi|pdf|sipdf|html)\b.*\.rar(?!.{20,})/i'; $this->supportfiles = '/\.(vol\d{1,3}\+\d{1,3}|par2|srs|sfv|nzb'; $this->videofileregex = '\.(AVI|F4V|IFO|M1V|M2V|M4V|MKV|MOV|MP4|MPEG|MPG|MPGV|MPV|OGV|QT|RM|RMVB|TS|VOB|WMV)'; $this->segmentstodownload = (!empty($this->tmux->segmentstodownload)) ? $this->tmux->segmentstodownload : 2; $this->passchkattempts = (!empty($this->tmux->passchkattempts)) ? $this->tmux->passchkattempts : 1; $this->partsqty = (!empty($this->tmux->maxpartsprocessed)) ? $this->tmux->maxpartsprocessed : 3; $this->rageqty = (!empty($his->tmux->maxrageprocessed)) ? $this->tmux->maxrageprocessed : 75; $this->pubkey = $this->site->amazonpubkey; $this->privkey = $this->site->amazonprivkey; $this->asstag = $this->site->amazonassociatetag; $this->addqty = (!empty($this->tmux->maxaddprocessed)) ? (int)$this->site->maxaddprocessed : 25; $this->gameqty = (!empty($this->tmux->maxgamesprocessed)) ? $this->tmux->maxgamesprocessed : 150; $this->sleeptime = (!empty($this->tmux->amazonsleep)) ? $this->tmux->amazonsleep : 1000; $this->DEBUG_ECHO = ($this->tmux->debuginfo == '0') ? false : true; if (defined('DEBUG_ECHO') && DEBUG_ECHO == true) { $this->DEBUG_ECHO = true; } $this->debug = ($this->tmux->debuginfo == "0") ? false : true; $this->compressedHeaders = ($this->site->compressedheaders == '1') ? true : false; $this->safepartrepair = (!empty($this->tmux->safepartrepair)) ? $this->tmux->safepartrepair : 0; $this->safebdate = (!empty($this->tmux->safebackfilldate)) ? $this->tmux->safebackfilldate : '2012 - 06 - 24'; $this->DoPartRepair = ($this->tmux->partrepair == '0') ? false : true; $this->messagebuffer = (!empty($this->site->maxmssgs)) ? (int)$this->site->maxmssgs : 20000; $this->NewGroupScanByDays = ($this->site->newgroupscanmethod == '1') ? true : false; $this->NewGroupMsgsToScan = (!empty($this->site->newgroupmsgstoscan)) ? (int)$this->site->newgroupmsgstoscan : 50000; $this->NewGroupDaysToScan = (!empty($this->site->newgroupdaystoscan)) ? (int)$this->site->newgroupdaystoscan : 3; $this->partrepairlimit = (!empty($this->tmux->maxpartrepair)) ? (int)$this->tmux->maxpartrepair : 15000; $this->nfo = new Info(); //\\ Paths. $this->audSavePath = www . DIR . 'covers/audiosample/'; $this->imgSavePath = WWW_DIR . 'covers/console/'; $this->jpgSavePath = WWW_DIR . 'covers/sample/'; $this->mainTmpPath = $this->site->tmpunrarpath; //\\ } /** * @var object Instance of PDO class. */ private static $pdo = null; /** * Should we use part repair? * * @var bool */ private $DoPartRepair; /** * How many headers do we download per loop? * * @var int */ public $messagebuffer; /** * How many days to go back on a new group? * * @var bool */ private $NewGroupScanByDays; /** * Path to save large jpg pictures(xxx). * * @var string */ public $jpgSavePath; // database function public function queryArray($query) { $db = new DB(); if ($query == '') return false; $result = $db->queryDirect($query); $rows = array(); foreach ($result as $row) { $rows[] = $row; } return (!isset($rows)) ? false : $rows; } // Used for deleting, updating (and inserting without needing the last insert ID). public function exec($query) { if ($query == '') return false; try { $run = self::$pdo->prepare($query); $run->execute(); return $run; } catch (PDOException $e) { // Deadlock or lock wait timeout, try 10 times. $i = 1; while (($e->errorInfo[1] == 1213 || $e->errorInfo[0] == 40001 || $e->errorInfo[1] == 1205 || $e->getMessage() == 'SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction') && $i <= 10) { echo $this->c->error("A Deadlock or lock wait timeout has occurred, sleeping.\n"); $this->consoletools->showsleep($i * $i); $run = self::$pdo->prepare($query); $run->execute(); return $run; $i++; } if ($e->errorInfo[1] == 1213 || $e->errorInfo[0] == 40001 || $e->errorInfo[1] == 1205) { //echo "Error: Deadlock or lock wait timeout."; return false; } else if ($e->errorInfo[1] == 1062 || $e->errorInfo[0] == 23000) { //echo "\nError: Update would create duplicate row, skipping\n"; return false; } else if ($e->errorInfo[1] == 1406 || $e->errorInfo[0] == 22001) { //echo "\nError: Too large to fit column length\n"; return false; } else echo $this->c->error($e->getMessage()); return false; } } public function Prepare($query, $options = array()) { try { $PDOstatement = self::$pdo->prepare($query, $options); } catch (PDOException $e) { //echo $this->c->error($e->getMessage()); $PDOstatement = false; } return $PDOstatement; } public function from_unixtime($utime, $escape = true) { if ($escape === true) { return 'FROM_UNIXTIME(' . $utime . ')'; } else return date('Y-m-d h:i:s', $utime); } // Date to unix time. // (substitute for mysql's UNIX_TIMESTAMP() function) public function unix_timestamp($date) { return strtotime($date); } public function unix_timestamp_column($column, $outputName = 'unix_time') { return 'UNIX_TIMESTAMP(' . $column . ') AS ' . $outputName; } /** * Interpretation of mysql's UUID method. * Return uuid v4 string. http://www.php.net/manual/en/function.uniqid.php#94959 * * @return string */ public function uuid() { return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); } /** * Process comments. * * @param NNTP $nntp */ public function processSharing(&$nntp) { $sharing = new Sharing($this->db, $nntp); $sharing->start(); } // gets name of category from category.php public function getNameByID($ID) { $db = new DB(); $parent = $db->queryOneRow(sprintf("SELECT title FROM category WHERE ID = %d", substr($ID, 0, 1) . "000")); $cat = $db->queryOneRow(sprintf("SELECT title FROM category WHERE ID = %d", $ID)); return $parent["title"] . " " . $cat["title"]; } public function getIDByName($name) { $db = new DB(); $res = $db->queryOneRow(sprintf("SELECT ID FROM groups WHERE name = %s", $db->escapeString($name))); return $res["ID"]; } //deletes from releases public function fastDelete($ID, $guid) { $nzb = new NZB(); $ri = new ReleaseImage(); $ri->delete($guid); // // delete from disk. // $nzbpath = $nzb->getNZBPath($guid); if (file_exists($nzbpath)) unlink($nzbpath); $this->db->exec(sprintf("delete releases, releasenfo, releasecomment, usercart, releasefiles, releaseaudio, releasesubs, releasevideo, releaseextrafull from releases LEFT OUTER JOIN releasenfo on releasenfo.releaseID = releases.ID LEFT OUTER JOIN releasecomment on releasecomment.releaseID = releases.ID LEFT OUTER JOIN usercart on usercart.releaseID = releases.ID LEFT OUTER JOIN releasefiles on releasefiles.releaseID = releases.ID LEFT OUTER JOIN releaseaudio on releaseaudio.releaseID = releases.ID LEFT OUTER JOIN releasesubs on releasesubs.releaseID = releases.ID LEFT OUTER JOIN releasevideo on releasevideo.releaseID = releases.ID LEFT OUTER JOIN releaseextrafull on releaseextrafull.releaseID = releases.ID where releases.ID = %d", $ID ) ); } //reads name of group public function getByNameByID($ID) { $db = new DB(); $res = $db->queryOneRow(sprintf("select name from groups where ID = %d ", $ID)); return $res["name"]; } // // Attempt to get a better name from a par2 file and categorize the release. // public function parsePAR2($messageID, $relID, $groupID, $nntp, $show) { $db = new DB(); $category = new Category(); $c = new ColorCLI; if (!isset($nntp)) exit($c->error("Not connected to usenet(functions->parsePAR2).\n")); if ($messageID == '') return false; $t = 'UNIX_TIMESTAMP(postdate)'; $quer = $db->queryOneRow('SELECT groupID, categoryID, searchname, ' . $t . ' as postdate, ID as releaseID FROM releases WHERE isrenamed = 0 AND ID = ' . $relID); if ($quer['categoryID'] != Category::CAT_MISC_OTHER) return false; $nntp = new Nntp(); $nntp->doConnect(); $groups = new Groups(); $functions = new Functions(); $par2 = $nntp->getMessage($this->getByNameByID($groupID), $messageID); if (PEAR::isError($par2)) { $nntp->doQuit(); $nntp->doConnect(); $par2 = $nntp->getMessage($this->getByNameByID($groupID), $messageID); if (PEAR::isError($par2)) { $nntp->doQuit(); return false; } } $par2info = new Par2Info(); $par2info->setData($par2); if ($par2info->error) return false; $files = $par2info->getFileList(); if ($files !== false && count($files) > 0) { $db = new DB(); $namefixer = new Namefixer; $rf = new ReleaseFiles(); $relfiles = 0; $foundname = false; foreach ($files as $fileID => $file) { if (!array_key_exists('name', $file)) return false; // Add to releasefiles. if (($relfiles < 11 && $db->queryOneRow(sprintf("SELECT ID FROM releasefiles WHERE releaseID = %d AND name = %s", $relID, $db->escapeString($file["name"])))) === false) { if ($rf->add($relID, $file["name"], $file["size"], $quer["postdate"], 0)) $relfiles++; } $quer["textstring"] = $file["name"]; if ($namefixer->checkName($quer, 1, 'PAR2, ', 1, $show) === true) { $foundname = true; break; } } if ($relfiles > 0) { echo $this->c->debug("Added " . $relfiles . " releasefiles from PAR2 for " . $quer["searchname"]); $cnt = $db->queryOneRow('SELECT COUNT(releaseID) AS count FROM releasefiles WHERE releaseID = ' . $relID); $count = $relfiles; if ($cnt !== false && $cnt['count'] > 0) $count = $relfiles + $cnt['count']; $db->exec(sprintf('UPDATE releases SET rarinnerfilecount = %d where ID = %d', $count, $relID)); } if ($foundname === true) return true; else return false; } else return false; } // Check if the NZB is there, returns path, else false. function NZBPath($releaseGuid, $sitenzbpath = "") { $nzb = new NZB(); $nzbfile = $nzb->getNZBPath($releaseGuid, $sitenzbpath, false); return !file_exists($nzbfile) ? false : $nzbfile; } // Sends releases back to other->misc. public function resetCategorize($where = '') { $this->db->exec('UPDATE releases SET categoryID = 8010, iscategorized = 0 ' . $where); } // Categorizes releases. // $type = name or searchname // Returns the quantity of categorized releases. public function categorizeRelease($type, $where = '', $echooutput = false) { $cat = new Category(); $relcount = 0; $resrel = $this->db->queryDirect('SELECT ID, ' . $type . ', groupID FROM releases ' . $where); $total = $resrel->rowCount(); if (count($resrel) > 0) { foreach ($resrel as $rowrel) { $catId = $cat->determineCategory($rowrel['groupID'], $rowrel[$type]); $this->db->exec(sprintf('UPDATE releases SET categoryID = %d, iscategorized = 1 WHERE ID = %d', $catId, $rowrel['ID'])); $relcount++; if ($this->echooutput) { $this->consoleTools->overWritePrimary('Categorizing: ' . $this->consoleTools->percentString($relcount, $total)); } } } if ($this->echooutput !== false && $relcount > 0) { echo "\n"; } return $relcount; } // Optimises/repairs tables on mysql. public function optimise($admin = false, $type = '') { $db = new DB(); $c = new ColorCLI(); $tablecnt = 0; if ($type === 'true' || $type === 'full' || $type === 'analyze') { $alltables = $db->query('SHOW TABLE STATUS'); } else { $alltables = $db->query('SHOW TABLE STATUS WHERE Data_free / Data_length > 0.005'); } $tablecnt = count($alltables); if ($type === 'all' || $type === 'full') { $tbls = ''; foreach ($alltables as $table) { $tbls .= $table['Name'] . ', '; } $tbls = rtrim(trim($tbls), ','); if ($admin === false) { echo $this->c->primary('Optimizing tables: ' . $tbls); } $db->queryDirect("OPTIMIZE LOCAL TABLE ${tbls}"); } else { foreach ($alltables as $table) { if ($type === 'analyze') { if ($admin === false) { echo $this->c->primary('Analyzing table: ' . $table['Name']); } $db->queryDirect('ANALYZE LOCAL TABLE `' . $table['Name'] . '`'); } else { if ($admin === false) { echo $this->c->primary('Optimizing table: ' . $table['Name']); } if (strtolower($table['engine']) == 'myisam') { $db->queryDirect('REPAIR TABLE `' . $table['Name'] . '`'); } $db->queryDirect('OPTIMIZE LOCAL TABLE `' . $table['Name'] . '`'); } } } if ($type !== 'analyze') { $db->queryDirect('FLUSH TABLES'); } return $tablecnt; } /** * Run processAdditional threaded. * * @param string $releaseToWork * @param $nntp * * @return void */ public function processAdditionalThreaded($releaseToWork = '', $nntp) { if (!isset($nntp)) { exit($this->c->error("Not connected to usenet(functions->processAdditionalThreaded).\n")); } $this->processAdditional($nntp, $releaseToWork); } /** * Check for passworded releases, RAR contents and Sample/Media info. * * @note Called externally by tmux/bin/update_per_group and update/postprocess.php * * @param NNTP $nntp Class NNTP * @param string $releaseToWork String containing SQL results. Optional. * @param string $groupID Group ID. Optional * * @return void */ public function processAdditional($nntp, $releaseToWork = '', $groupID = '') { $groupID = ($groupID === '' ? '' : 'AND groupID = ' . $groupID); // Get out all releases which have not been checked more than max attempts for password. $totResults = 0; $result = []; if ($releaseToWork === '') { $i = -6; $limit = $this->addqty; // Get releases starting from -6 password status until we reach our max limit set in site or we reach -1 password status. while (($totResults <= $limit) && ($i <= -1)) { $qResult = $this->db->query( sprintf(' SELECT r.ID, r.guid, r.name, c.disablepreview, r.size, r.groupID, r.nfostatus, r.completion, r.categoryID, r.searchname FROM releases r LEFT JOIN category c ON c.ID = r.categoryID WHERE r.size < %d %s AND r.passwordstatus = %d AND (r.haspreview = -1 AND c.disablepreview = 0) ORDER BY postdate DESC LIMIT %d', $this->maxsize * 1073741824, $groupID, $i, $limit ) ); // Get the count of rows we got from the query. $currentCount = count($qResult); if ($currentCount > 0) { // Merge the results. $result += $qResult; // Decrement so we don't get more than the max user specified value. $limit -= $currentCount; // Update the total results. $totResults += $currentCount; // Echo how many we got for this query. $this->doecho('Passwordstatus = ' . $i . ': Available to process = ' . $currentCount); } $i++; } } else { $pieces = explode(' =+= ', $releaseToWork); $result = array( array( 'ID' => $pieces[0], 'guid' => $pieces[1], 'name' => $pieces[2], 'disablepreview' => $pieces[3], 'size' => $pieces[4], 'groupID' => $pieces[5], 'nfostatus' => $pieces[6], 'categoryID' => $pieces[7], 'searchname' => $pieces[8] ) ); $totResults = 1; } $resCount = $startCount = $totResults; if ($resCount > 0) { // Start up the required objects. $this->initAdditional(); if ($this->echooutput && $resCount > 1) { $this->doecho('Additional post-processing, started at: ' . date('D M d, Y G:i a')); $this->doecho('Downloaded: (xB) = yEnc article, f= failed ;Processing: z = zip file, r = rar file'); $this->doecho('Added: s = sample image, j = jpeg image, A = audio sample, a = audio mediainfo, v = video sample'); $this->doecho('Added: m = video mediainfo, n = nfo, ^ = file details from inside the rar/zip'); } $nzb = new NZB($this->echooutput); // Loop through the releases. foreach ($result as $rel) { if ($this->echooutput) { echo $this->c->primaryOver("[" . ($releaseToWork === '' ? $startCount-- : $rel['ID'] ) . '][' . $this->readableBytesString($rel['size']) . ']' ); } $this->debugging->start('processAdditional', 'Processing ' . $rel['searchname'], 5); // Per release defaults. $this->tmpPath = $this->mainTmpPath . $rel['guid'] ./; if (!is_dir($this->tmpPath)) { $old = umask(0777); @mkdir($this->tmpPath, 0777, true); @chmod($this->tmpPath, 0777); @umask($old); if (!is_dir($this->tmpPath)) { $error = "Unable to create directory: {$this->tmpPath}"; if ($this->echooutput) { echo $this->c->error($error); } // Decrement password status. $this->db->exec('UPDATE releases SET passwordstatus = passwordstatus - 1 WHERE ID = ' . $rel['ID']); continue; } } $nzbPath = $nzb->getNZBPath($rel['guid']); if (!is_file($nzbPath)) { // The nzb was not located. decrement the password status. $this->db->exec('UPDATE releases SET passwordstatus = passwordstatus - 1 WHERE ID = ' . $rel['ID']); continue; } // Turn on output buffering. ob_start(); // Decompress the NZB. @readgzfile($nzbPath); // Read the nzb into memory. $nzbFile = ob_get_contents(); // Clean (erase) the output buffer and turn off output buffering. ob_end_clean(); // Get a list of files in the nzb. $nzbFiles = $this->nzbFileList($nzbFile); if (count($nzbFiles) === 0) { // There does not appear to be any files in the nzb, decrement password status. $this->db->exec('UPDATE releases SET passwordstatus = passwordstatus - 1 WHERE ID = ' . $rel['ID']); continue; } // Sort the files. usort($nzbFiles, 'functions::sortRAR'); // Only process for samples, previews and images if not disabled. $this->blnTookSample = ($this->processSample ? false : true); $this->blnTookSample = (($rel['disablepreview'] === '1') ? true : false); $this->blnTookVideo = ($this->processVideo ? false : true); $this->blnTookMediainfo = ($this->processMediaInfo ? false : true); $this->blnTookAudioinfo = ($this->processAudioInfo ? false : true); $this->blnTookAudioSample = ($this->processAudioSample ? false : true); $this->blnTookJPG = ($this->processJPGSample ? false : true); // Reset and set certain variables. $passStatus = array(Releases::PASSWD_NONE); $sampleMsgID = $jpgMsgID = $audioType = $mID = array(); $mediaMsgID = $audioMsgID = ''; $hasRar = $ignoredBooks = $failed = $this->filesAdded = 0; $this->password = $this->noNFO = $bookFlood = false; $groupName = $this->getByNameByID($rel['groupID']); // Make sure we don't already have an nfo. if ($rel['nfostatus'] !== '1') { $this->noNFO = true; } // Go through the nzb for this release looking for a rar, a sample etc... foreach ($nzbFiles as $nzbContents) { // Check if it's not a nfo, nzb, par2 etc... if (preg_match($this->supportFiles . "|nfo\b|inf\b|ofn\b)($|[ \")\]-])(?!.{20,})/i", $nzbContents['title'])) { continue; } // Check if it's a rar/zip. if (preg_match(" /\.(part0*1|part0+|r0+|r0*1|rar|0+|0*10?|zip)(\.rar)*($|[ \")\]-])|\"[a-f0-9]{32}\.[1-9]\d{1,2}\".*\(\d+\/\d{2,}\)$/i", $nzbContents['title'] ) ) { $hasRar = 1; } // Look for a video sample, make sure it's not an image. if ($this->processSample === true && empty($sampleMsgID) && !preg_match('/\.jpe?g/i', $nzbContents['title']) && preg_match('/sample/i', $nzbContents['title']) ) { if (isset($nzbContents['segments'])) { $sampleMsgID[] = (string)$nzbContents['segments'][0]; // Get the amount of segments for this file. $segCount = count($nzbContents['segments']); if ($segCount > 1) { // If it's more than 1 try to get up to the site specified value of segments. for ($i = 1; $i < $this->segmentsToDownload; $i++) { if ($segCount > $i) { $sampleMsgID[] = (string)$nzbContents['segments'][$i]; } else { break; } } } } } // Look for a video file, make sure it's not a sample. if ($this->processMediaInfo === true && empty($mediaMsgID) && !preg_match('/sample/i', $nzbContents['title']) && preg_match('/' . $this->videoFileRegex . '[. ")\]]/i', $nzbContents['title']) ) { if (isset($nzbContents['segments'])) { $mediaMsgID = (string)$nzbContents['segments'][0]; } } // Look for a audio file. if ($this->processAudioInfo === true && empty($audioMsgID) && preg_match('/' . $this->audioFileRegex . '[. ")\]]/i', $nzbContents['title'], $type) ) { if (isset($nzbContents['segments'])) { // Get the extension. $audioType = $type[1]; $audioMsgID = (string)$nzbContents['segments'][0]; } } // Look for a JPG picture, make sure it's not a CD cover. if ($this->processJPGSample === true && empty($jpgMsgID) && !preg_match('/flac|lossless|mp3|music|inner-sanctum|sound/i', $groupName) && preg_match('/\.jpe?g[. ")\]]/i', $nzbContents['title']) ) { if (isset($nzbContents['segments'])) { $jpgMsgID[] = (string)$nzbContents['segments'][0]; // If there's more than 1 part, get 2. if (count($nzbContents['segments']) > 1) { $jpgMsgID[] = (string)$nzbContents['segments'][1]; } } } // To see if this is book flood. if (preg_match($this->ignoreBookRegex, $nzbContents['title'])) { $ignoredBooks++; } } // Ignore massive book NZBs. $fileCount = count($nzbFiles); if ($fileCount > 40 && ($ignoredBooks * 2) >= $fileCount) { if (isset($rel['categoryID']) && substr($rel['categoryID'], 0, 1) === '8') { $this->db->exec(sprintf('UPDATE releases SET passwordstatus = 0, haspreview = 0, categoryID = 7900 WHERE ID = %d', $rel['ID'])); } $bookFlood = true; } // Separate the nzb content into the different parts (support files, archive segments and the first parts). if ($bookFlood === false && $hasRar !== 0) { if ($this->processPasswords === true || $this->processSample === true || $this->processMediaInfo === true || $this->processAudioInfo === true || $this->processVideo === true ) { $this->sum = $this->size = $this->segsize = $this->adj = $notInfinite = $failed = 0; $this->name = ''; $this->ignoreNumbered = false; // Loop through the files, attempt to find if password-ed and files. Starting with what not to process. foreach ($nzbFiles as $rarFile) { if ($this->passChkAttempts > 1) { if ($notInfinite > $this->passChkAttempts) { break; } } else { if ($notInfinite > $this->partsQTY) { if ($this->echooutput) { echo "\n"; echo $this->c->info("Ran out of tries to download yEnc articles for the RAR files."); } break; } } if ($this->password === true) { $this->debugging->start('processAdditional', 'Skipping processing of rar ' . $rarFile['title'] . ' it has a password.', 4 ); break; } // Probably not a rar/zip. if (!preg_match("/\.\b(part\d+|part00\.rar|part01\.rar|rar|r00|r01|zipr\d{2,3}|zip|zipx)($|[ \")\]-])|\"[a-f0-9]{32}\.[1-9]\d{1,2}\".*\(\d+\/\d{2,}\)$/i", $rarFile['title'])) { continue; } // Process rar contents until 1G or 85% of file size is found (smaller of the two). if ($rarFile['size'] === 0 && $rarFile['partsactual'] !== 0 && $rarFile['partstotal'] !== 0) { $this->segsize = $rarFile['size'] / ($rarFile['partsactual'] / $rarFile['partstotal']); } else { $this->segsize = 0; } $this->sum = $this->sum + $this->adj * $this->segsize; if ($this->sum > $this->size || $this->adj === 0) { // Get message-id's for the rar file. $mID = array_slice((array)$rarFile['segments'], 0, $this->segmentsToDownload); // Download the article(s) from usenet. $fetchedBinary = $nntp->getMessages($groupName, $mID, $this->alternateNNTP); if ($nntp->isError($fetchedBinary)) { $fetchedBinary = false; } if ($fetchedBinary !== false) { // Echo we downloaded rar/zip. if ($this->echooutput) { echo '(rB)'; } $notInfinite++; // Process the rar/zip file. $relFiles = $this->processReleaseFiles($fetchedBinary, $rel, $rarFile['title'], $nntp); if ($this->password === true) { $passStatus[] = Releases::PASSWD_RAR; } if ($relFiles === false) { $this->debugging->start('processAdditional', 'Error processing files ' . $rarFile['title'], 4); continue; } } else { if ($this->echooutput) { echo 'f(' . $notInfinite . ')'; } $notInfinite += 0.2; $failed++; } } } } // Get names of all files in temp dir. $files = @scandir($this->tmpPath); if ($files !== false) { // Loop over them. foreach ($files as $file) { // Check if the file exists. if (is_file($this->tmpPath . $file)) { // Check if it's a rar file. if (substr($file, -4) === '.rar') { // Load the file in archive info. $archInfo = new ArchiveInfo(); $archInfo->open($this->tmpPath . $file, true); if ($archInfo->error) { continue; } $tmpFiles = $archInfo->getArchiveFileList(); if (isset($tmpFiles[0]['name'])) { foreach ($tmpFiles as $r) { if (isset($r['range'])) { $range = $r['range']; } else { $range = mt_rand(0, 99999); } $r['range'] = $range; if (!isset($r['error'])) { if ($rel['categoryID'] !== Category::CAT_MISC) { // Check if it's a par2. if (preg_match('/\.par2/i', $r['name'])) { $par2 = $archInfo->getFileData($r['name'], $r['source']); // Try to get a release name. $this->siftPAR2($par2, $rel); } } if (preg_match( $this->supportFiles . '|part\d+|r\d{1,3}|zipr\d{2,3}|\d{2,3}|zipx|zip|rar)(\.rar)?$/i', $r['name'] ) ) { continue; } $this->addFile($r, $rel, $archInfo, $nntp); } } } } } } } } // Check if we should process these types of files. if ($this->blnTookSample === false || $this->blnTookAudioinfo === false || $this->blnTookMediainfo === false || $this->blnTookJPG === false || $this->blnTookVideo === false || $this->blnTookAudioSample === false ) { // Get all the names of the files in the temp dir. $files = @scandir($this->tmpPath); if ($files !== false) { // Loop over them. foreach ($files as $file) { // Check if it's really a file. if (is_file($this->tmpPath . $file)) { $name = ''; // Audio sample. if (($this->blnTookAudioinfo === false || $this->blnTookAudioSample === false) && preg_match('/(.*)' . $this->audioFileRegex . '$/i', $file, $name) ) { // Move the file. @rename($this->tmpPath . $name[0], $this->tmpPath . 'audiofile.' . $name[2]); // Try to get audio sample/audio media info. $this->getAudioInfo($rel['guid'], $rel['ID'], $name[2]); // Delete the file. @unlink($this->tmpPath . 'audiofile.' . $name[2]); } // JGP file sample. if ($this->blnTookJPG === false && preg_match('/\.jpe?g$/', $file)) { // Try to resize/move the image. $this->blnTookJPG = $this->saveImage( $rel['guid'] . '_thumb', $this->tmpPath . $file, $this->jpgSavePath, 650, 650 ); // If it's successful, tell the DB. if ($this->blnTookJPG !== false) { $this->db->exec(sprintf('UPDATE releases SET jpgstatus = %d WHERE ID = %d', 1, $rel['ID'])); } // Delete the old file. @unlink($this->tmpPath . $file); } // Video sample // video clip // video media info. if ($this->blnTookSample === false || $this->blnTookVideo === false || $this->blnTookMediainfo === false) { // Check if it's a video. if (preg_match('/(.*)' . $this->videoFileRegex . '$/i', $file, $name)) { // Move it. @rename($this->tmpPath . $name[0], $this->tmpPath . 'sample.avi'); // Try to get a sample with it. if ($this->blnTookSample === false) { $this->blnTookSample = $this->getSample($rel['guid']); } // Try to get a video with it. Don't get it here if $sampleMsgID is empty or has 1 message-id (Saves downloading another part). if ($this->blnTookVideo === false && count($sampleMsgID) < 2) { $this->blnTookVideo = $this->getVideo($rel['guid']); } // Try to get media info with it. if ($this->blnTookMediainfo === false) { $this->blnTookMediainfo = $this->getMediaInfo($rel['ID']); } // Delete it. @unlink($this->tmpPath . 'sample.avi'); } } // If we got it all, break out. if ($this->blnTookJPG === true && $this->blnTookAudioinfo === true && $this->blnTookAudioSample === true && $this->blnTookMediainfo === true && $this->blnTookVideo === true && $this->blnTookSample === true ) { break; } } } unset($files); } } // Download and process sample image. if ($this->blnTookSample === false || $this->blnTookVideo === false) { if (!empty($sampleMsgID)) { // Download it from usenet. $sampleBinary = $nntp->getMessages($groupName, $sampleMsgID); if ($nntp->isError($sampleBinary)) { $sampleBinary = false; } if ($sampleBinary !== false) { if ($this->echooutput) { echo '(sB)'; } // Check if it's more than 40 bytes. if (strlen($sampleBinary) > 40) { // Try to create the file. $this->addMediaFile($this->tmpPath . 'sample_' . mt_rand(0, 99999) . '.avi', $sampleBinary); // Try to get a sample picture. if ($this->blnTookSample === false) { $this->blnTookSample = $this->getSample($rel['guid']); } // Try to get a sample video. if ($this->blnTookVideo === false) { $this->blnTookVideo = $this->getVideo($rel['guid']); } // Try to get media info. Don't get it here if $mediaMsgID is not empty. if ($this->blnTookMediainfo === false && empty($mediaMsgID)) { $this->blnTookMediainfo = $this->getMediaInfo($rel['ID']); } } unset($sampleBinary); } else { if ($this->echooutput) { echo 'f'; } } } } // Download and process mediainfo. Also try to get a sample if we didn't get one yet. if ($this->blnTookMediainfo === false || $this->blnTookSample === false || $this->blnTookVideo === false) { if (!empty($mediaMsgID)) { // Try to download it from usenet. $mediaBinary = $nntp->getMessages($groupName, $mediaMsgID); if ($nntp->isError($mediaBinary)) { // If error set it to false. $mediaBinary = false; } if ($mediaBinary !== false) { if ($this->echooutput) { echo '(mB)'; } // If it's more than 40 bytes... if (strlen($mediaBinary) > 40) { // Create a file on the disk with it. $this->addMediaFile($this->tmpPath . 'media.avi', $mediaBinary); // Try to get media info. if ($this->blnTookMediainfo === false) { $this->blnTookMediainfo = $this->getMediaInfo($rel['ID']); } // Try to get a sample picture. if ($this->blnTookSample === false) { $this->blnTookSample = $this->getSample($rel['guid']); } // Try to get a sample video. if ($this->blnTookVideo === false) { $this->blnTookVideo = $this->getVideo($rel['guid']); } } unset($mediaBinary); } else { if ($this->echooutput) { echo 'f'; } } } } // Download audio file, use media info to try to get the artist / album. if (($this->blnTookAudioinfo === false || $this->blnTookAudioSample === false) && !empty($audioMsgID)) { // Try to download it from usenet. $audioBinary = $nntp->getMessages($groupName, $audioMsgID); if ($nntp->isError($audioBinary)) { $audioBinary = false; } if ($audioBinary !== false) { if ($this->echooutput) { echo '(aB)'; } // Create a file with it. $this->addMediaFile($this->tmpPath . 'audio.' . $audioType, $audioBinary); // Try to get media info / sample of the audio file. $this->getAudioInfo($rel['guid'], $rel['id'], $audioType); unset($audioBinary); } else { if ($this->echooutput) { echo 'f'; } } } // Download JPG file. if ($this->blnTookJPG === false && !empty($jpgMsgID)) { // Try to download it. $jpgBinary = $nntp->getMessages($groupName, $jpgMsgID); if ($nntp->isError($jpgBinary)) { $jpgBinary = false; } if ($jpgBinary !== false) { if ($this->echooutput) { echo '(jB)'; } // Try to create a file with it. $this->addMediaFile($this->tmpPath . 'samplepicture.jpg', $jpgBinary); // Try to resize and move it. $this->blnTookJPG = $this->releaseImage->saveImage($rel['guid'] . '_thumb', $this->tmpPath . 'samplepicture.jpg', $this->jpgSavePath, 650, 650); if ($this->blnTookJPG !== false) { // Update the DB to say we got it. $this->db->exec(sprintf('UPDATE releases SET jpgstatus = %d WHERE ID = %d', 1, $rel['ID'])); if ($this->echooutput) { echo 'j'; } } @unlink($this->tmpPath . 'samplepicture.jpg'); unset($jpgBinary); } else { if ($this->echooutput) { echo 'f'; } } } // Set up release values. $hpSQL = $iSQL = $vSQL = $jSQL = ''; if ($this->processSample === true && $this->blnTookSample !== false) { $this->db->exec(sprintf('UPDATE releases SET haspreview = 1 WHERE guid = %s', $this->db->escapeString($rel['guid']))); } else { $hpSQL = ', haspreview = 0'; } if ($failed > 0) { if ($failed / count($nzbFiles) > 0.7 || $notInfinite > $this->passChkAttempts || $notInfinite > $this->partsQTY) { $passStatus[] = Releases::BAD_FILE; } } // If samples exist from previous runs, set flags. if (file_exists($this->imgSavePath . $rel['guid'] . '_thumb.jpg')) { $iSQL = ', haspreview = 1'; } if (file_exists($this->vidSavePath . $rel['guid'] . '.ogv')) { $vSQL = ', videostatus = 1'; } if (file_exists($this->jpgSavePath . $rel['guid'] . '_thumb.jpg')) { $jSQL = ', jpgstatus = 1'; } $size = $this->db->queryOneRow('SELECT COUNT(releasefiles.releaseID) AS count, SUM(releasefiles.size) AS size FROM releasefiles WHERE releaseID = ' . $rel['ID']); $pStatus = max($passStatus); if ($this->processPasswords === true && $pStatus > 0) { $sql = sprintf('UPDATE releases SET passwordstatus = %d, rarinnerfilecount = %d %s %s %s %s WHERE ID = %d', $pStatus, $size['count'], $iSQL, $vSQL, $jSQL, $hpSQL, $rel['ID']); } else if ($hasRar && ((isset($size['size']) && (is_null($size['size']) || $size['size'] === '0')) || !isset($size['size']))) { if (!$this->blnTookSample) { $hpSQL = ''; } $sql = sprintf('UPDATE releases SET passwordstatus = passwordstatus - 1, rarinnerfilecount = %d %s %s %s %s WHERE ID = %d', $size['count'], $iSQL, $vSQL, $jSQL, $hpSQL, $rel['ID']); } else { $sql = sprintf('UPDATE releases SET passwordstatus = %s, rarinnerfilecount = %d %s %s %s %s WHERE ID = %d', Releases::PASSWD_NONE, $size['count'], $iSQL, $vSQL, $jSQL, $hpSQL, $rel['ID']); } $this->db->exec($sql); // Erase all files and directory. foreach (glob($this->tmpPath . '*') as $v) { @unlink($v); } foreach (glob($this->tmpPath . '.*') as $v) { @unlink($v); } @rmdir($this->tmpPath); } if ($this->echooutput) { echo "\n"; } } unset($rar, $nzbContents); } // Process nfo files. public function processNfos($releaseToWork = '', $nntp) { if (!isset($nntp)) exit($this->c->error("Not connected to usenet(functions->processNfos).\n")); if ($this->site->lookupnfo == 1) { $nfo = new Info($this->echooutput); $nfo->processNfoFiles($releaseToWork, $this->site->lookupimdb, $this->site->lookuptvrage, $groupID = '', $nntp); } } function doecho($str) { if ($this->echooutput) echo $this->c->header($str); } // Comparison function for usort, for sorting nzb file content. public function sortrar($a, $b) { $pos = 0; $af = $bf = false; $a = preg_replace('/\d+[- ._]?(\/|\||[o0]f)[- ._]?\d+?(?![- ._]\d)/i', ' ', $a['title']); $b = preg_replace('/\d+[- ._]?(\/|\||[o0]f)[- ._]?\d+?(?![- ._]\d)/i', ' ', $b['title']); if (preg_match("/\.(part\d+|r\d+)(\.rar)*($|[ \")\]-])/i", $a)) $af = true; if (preg_match("/\.(part\d+|r\d+)(\.rar)*($|[ \")\]-])/i", $b)) $bf = true; if (!$af && preg_match("/\.(rar)($|[ \")\]-])/i", $a)) { $a = preg_replace('/\.(rar)(?:$|[ \")\]-])/i', '.*rar', $a); $af = true; } if (!$bf && preg_match("/\.(rar)($|[ \")\]-])/i", $b)) { $b = preg_replace('/\.(rar)(?:$|[ \")\]-])/i', '.*rar', $b); $bf = true; } if (!$af && !$bf) return strnatcasecmp($a, $b); else if (!$bf) return -1; else if (!$af) return 1; if ($af && $bf) $pos = strnatcasecmp($a, $b); else if ($af) $pos = -1; else if ($bf) $pos = 1; return $pos; } // Process all TV related releases which will assign their series/episode/rage data. public function processTv($releaseToWork = '') { if ($this->site->lookuptvrage == 1) { $tvrage = new TvRage($this->echooutput); $this->processTvReleases($releaseToWork, $this->site->lookuptvrage == 1); } } public function processTvReleases($releaseToWork = '', $lookupTvRage = true, $local = false) { $ret = 0; $trakt = new TraktTv(); $tvrage = new TvRage(); // Get all releases without a rageID which are in a tv category. if ($releaseToWork == '') { $res = $this->db->query(sprintf("SELECT r.searchname, r.ID FROM releases r INNER JOIN category c ON r.categoryID = c.ID WHERE r.rageID = -1 AND c.parentID = %d ORDER BY postdate DESC LIMIT %d", Category::CAT_PARENT_TV, $this->rageqty)); $tvcount = count($res); } else { $pieces = explode(" =+= ", $releaseToWork); $res = array(array('searchname' => $pieces[0], 'ID' => $pieces[1])); $tvcount = 1; } if ($this->echooutput && $tvcount > 1) { echo $this->c->header("Processing TV for " . $tvcount . " release(s)."); } foreach ($res as $arr) { $show = $tvrage->parseNameEpSeason($arr['searchname']); if (is_array($show) && $show['name'] != '') { // Update release with season, ep, and airdate info (if available) from releasetitle. $tvrage->updateEpInfo($show, $arr['ID']); // Find the rageID. $ID = $tvrage->getByTitle($show['cleanname']); // Force local lookup only if ($local == true) { $lookupTvRage = false; } if ($ID === false && $lookupTvRage) { // If it doesnt exist locally and lookups are allowed lets try to get it. if ($this->echooutput) { echo $this->c->primaryOver("TVRage ID for ") . $this->c->headerOver($show['cleanname']) . $this->c->primary(" not found in local db, checking web."); } $tvrShow = $tvrage->getRageMatch($show); if ($tvrShow !== false && is_array($tvrShow)) { // Get all tv info and add show. $tvrage->updateRageInfo($tvrShow['showid'], $show, $tvrShow, $arr['ID']); } else if ($tvrShow === false) { // If tvrage fails, try trakt. $traktArray = $trakt->traktTVSEsummary($show['name'], $show['season'], $show['episode']); if ($traktArray !== false) { if (isset($traktArray['show']['tvrage_ID']) && $traktArray['show']['tvrage_ID'] !== 0) { if ($this->echooutput) { echo $this->c->primary('Found TVRage ID on trakt:' . $traktArray['show']['tvrage_ID']); } $this->updateRageInfoTrakt($traktArray['show']['tvrage_ID'], $show, $traktArray, $arr['ID']); } // No match, add to tvrage with rageID = -2 and $show['cleanname'] title only. else { $this->add(-2, $show['cleanname'], '', '', '', ''); } } // No match, add to tvrage with rageID = -2 and $show['cleanname'] title only. else { $this->add(-2, $show['cleanname'], '', '', '', ''); } } else { // $tvrShow probably equals -1 but we'll do this as a catchall instead of a specific else if. // Skip because we couldnt connect to tvrage.com. } } else if ($ID > 0) { //if ($this->echooutput) { // echo $this->c->AlternateOver("TV series: ") . $this->c->header($show['cleanname'] . " " . $show['seriesfull'] . (($show['year'] != '') ? ' ' . $show['year'] : '') . (($show['country'] != '') ? ' [' . $show['country'] . ']' : '')); // } $tvairdate = (isset($show['airdate']) && !empty($show['airdate'])) ? $this->db->escapeString($this->checkDate($show['airdate'])) : "NULL"; $tvtitle = "NULL"; if ($lookupTvRage) { $epinfo = $tvrage->getEpisodeInfo($ID, $show['season'], $show['episode']); if ($epinfo !== false) { if (isset($epinfo['airdate'])) { $tvairdate = $this->db->escapeString($this->checkDate($epinfo['airdate'])); } if (!empty($epinfo['title'])) { $tvtitle = $this->db->escapeString(trim($epinfo['title'])); } } } if ($tvairdate == "NULL") { $this->db->exec(sprintf('UPDATE releases SET tvtitle = %s, rageID = %d WHERE ID = %d', $tvtitle, $ID, $arr['ID'])); } else { $this->db->exec(sprintf('UPDATE releases SET tvtitle = %s, tvairdate = %s, rageID = %d WHERE ID = %d', $tvtitle, $tvairdate, $ID, $arr['ID'])); } // Cant find rageID, so set rageID to n/a. } else { $this->db->exec(sprintf('UPDATE releases SET rageID = -2 WHERE ID = %d', $arr['ID'])); } // Not a tv episode, so set rageID to n/a. } else { $this->db->exec(sprintf('UPDATE releases SET rageID = -2 WHERE ID = %d', $arr['ID'])); } $ret++; } return $ret; } public function updateRageInfoTrakt($rageid, $show, $traktArray, $relid) { $tvrage = new TvRage(); // Try and get the episode specific info from tvrage. $epinfo = $tvrage->getEpisodeInfo($rageid, $show['season'], $show['episode']); if ($epinfo !== false) { $tvairdate = (!empty($epinfo['airdate'])) ? $this->db->escapeString($epinfo['airdate']) : "NULL"; $tvtitle = (!empty($epinfo['title'])) ? $this->db->escapeString($epinfo['title']) : "NULL"; $this->db->exec(sprintf("UPDATE releases SET tvtitle = %s, tvairdate = %s, rageID = %d WHERE ID = %d", $this->db->escapeString(trim($tvtitle)), $tvairdate, $traktArray['show']['tvrage_ID'], $relid)); } else { $this->db->exec(sprintf("UPDATE releases SET rageID = %d WHERE ID = %d", $traktArray['show']['tvrage_ID'], $relid)); } $genre = ''; if (isset($traktArray['show']['genres']) && is_array($traktArray['show']['genres']) && !empty($traktArray['show']['genres'])) { $genre = $traktArray['show']['genres']['0']; } $country = ''; if (isset($traktArray['show']['country']) && !empty($traktArray['show']['country'])) { $country = $this->countryCode($traktArray['show']['country']); } $rInfo = $tvrage->getRageInfoFromPage($rageid); $desc = ''; if (isset($rInfo['desc']) && !empty($rInfo['desc'])) { $desc = $rInfo['desc']; } $imgbytes = ''; if (isset($rInfo['imgurl']) && !empty($rInfo['imgurl'])) { $img = getUrl($rInfo['imgurl']); if ($img !== false) { $im = @imagecreatefromstring($img); if ($im !== false) { $imgbytes = $img; } } } $this->add($rageid, $show['cleanname'], $desc, $genre, $country, $imgbytes); } // Convert 2012-24-07 to 2012-07-24, there is probably a better way public function checkDate($date) { if (!empty($date) && $date != null) { $chk = explode(" ", $date); $chkd = explode("-", $chk[0]); if ($chkd[1] > 12) { $date = date('Y-m-d H:i:s', strtotime($chkd[1] . " " . $chkd[2] . " " . $chkd[0])); } return $date; } return null; } public function countryCode($country) { if (!is_array($country) && strlen($country) > 2) { $code = $this->db->queryOneRow('SELECT code FROM country WHERE LOWER(name) = LOWER(' . $this->db->escapeString($country) . ')'); if (isset($code['code'])) { return $code['code']; } } return $country; } public function add($rageid, $releasename, $desc, $genre, $country, $imgbytes) { $releasename = str_replace(array('.', '_'), array(' ', ' '), $releasename); $country = $this->countryCode($country); if ($rageid != -2) { $ckid = $this->db->queryOneRow('SELECT ID FROM tvrage WHERE rageID = ' . $rageid); } else { $ckid = $this->db->queryOneRow('SELECT ID FROM tvrage WHERE releasetitle = ' . $this->db->escapeString($releasename)); } if (!isset($ckid['ID']) || $rageid == -2) { $this->db->exec(sprintf('INSERT INTO tvrage (rageID, releasetitle, description, genre, country, createddate, imgdata) VALUES (%s, %s, %s, %s, %s, NOW(), %s)', $rageid, $this->db->escapeString($releasename), $this->db->escapeString(substr($desc, 0, 10000)), $this->db->escapeString(substr($genre, 0, 64)), $this->db->escapeString($country), $this->db->escapeString($imgbytes))); } else { $this->db->exec(sprintf('UPDATE tvrage SET releasetitle = %s, description = %s, genre = %s, country = %s, createddate = NOW(), imgdata = %s WHERE rage = %d', $this->db->escapeString($releasename), $this->db->escapeString(substr($desc, 0, 10000)), $this->db->escapeString(substr($genre, 0, 64)), $this->db->escapeString($country), $this->db->escapeString($imgbytes), $rageid)); } } // Open the rar, see if it has a password, attempt to get a file. function processReleaseFiles($fetchedBinary, $release, $name, $nntp) { if (!isset($nntp)) exit($this->c->error("Not connected to usenet(postprocess->processReleaseFiles).\n")); $retval = array(); $rar = new ArchiveInfo(); $rf = new ReleaseFiles(); $this->password = false; if (preg_match("/\.(part\d+|rar|r\d{1,3})($|[ \")\]-])|\"[a-f0-9]{32}\.[1-9]\d{1,2}\".*\(\d+\/\d{2,}\)$/i", $name)) { $rar->setData($fetchedBinary, true); if ($rar->error) { $this->debug("\nError: {$rar->error}."); return false; } $tmp = $rar->getSummary(true, false); if (preg_match('/par2/i', $tmp['main_info'])) return false; if (isset($tmp['is_encrypted']) && $tmp['is_encrypted'] != 0) { $this->debug('Archive is password encrypted.'); $this->password = true; return false; } if (!empty($rar->isEncrypted)) { $this->debug('Archive is password encrypted.'); $this->password = true; return false; } $files = $rar->getArchiveFileList(); if (count($files) == 0 || !is_array($files) || !isset($files[0]['compressed'])) return false; if ($files[0]['compressed'] == 0 && $files[0]['name'] != $this->name) { $this->name = $files[0]['name']; $this->size = $files[0]['size'] * 0.95; $this->adj = $this->sum = 0; if ($this->echooutput) echo 'r'; // If archive is not stored compressed, process data foreach ($files as $file) { if (isset($file['name'])) { if (isset($file['error'])) { $this->debug("Error: {$file['error']} (in: {$file['source']})"); continue; } if ($file['pass'] == true) { $this->password = true; break; } if (preg_match($this->supportfiles . ')(?!.{20,})/i', $file['name'])) continue; if (preg_match('/\.zip$/i', $file['name'])) { $zipdata = $rar->getFileData($file['name'], $file['source']); $data = $this->processReleaseZips($zipdata, false, true, $release, $nntp); if ($data != false) { foreach ($data as $d) { if (preg_match('/\.(part\d+|r\d+|rar)(\.rar)?$/i', $d['zip']['name'])) $tmpfiles = $this->getRar($d['data']); } } } if (!isset($file['next_offset'])) $file['next_offset'] = 0; $range = mt_rand(0, 99999); if (isset($file['range'])) $range = $file['range']; $retval[] = array('name' => $file['name'], 'source' => $file['source'], 'range' => $range, 'size' => $file['size'], 'date' => $file['date'], 'pass' => $file['pass'], 'next_offset' => $file['next_offset']); $this->adj = $file['next_offset'] + $this->adj; } } $this->sum = $this->adj; if ($this->segsize != 0) $this->adj = $this->adj / $this->segsize; else $this->adj = 0; if ($this->adj < .7) $this->adj = 1; } else { $this->size = $files[0]['size'] * 0.95; if ($this->name != $files[0]['name']) { $this->name = $files[0]['name']; $this->sum = $this->segsize; $this->adj = 1; } // File is compressed, use unrar to get the content $rarfile = $this->tmpPath . 'rarfile' . mt_rand(0, 99999) . '.rar'; if (@file_put_contents($rarfile, $fetchedBinary)) { $execstring = '"' . $this->site->unrarpath . '" e -ai -ep -c- -ID -inul -kb -or -p- -r -y "' . $rarfile . '" "' . $this->tmpPath . '"'; $output = @runCmd($execstring, false, true); if (isset($files[0]['name'])) { if ($this->echooutput) echo 'r'; foreach ($files as $file) { if (isset($file['name'])) { if (!isset($file['next_offset'])) $file['next_offset'] = 0; $range = mt_rand(0, 99999); if (isset($file['range'])) $range = $file['range']; $retval[] = array('name' => $file['name'], 'source' => $file['source'], 'range' => $range, 'size' => $file['size'], 'date' => $file['date'], 'pass' => $file['pass'], 'next_offset' => $file['next_offset']); } } } } } } // Use found content to populate releasefiles, nfo, and create multimedia files. foreach ($retval as $k => $v) { if (!preg_match($this->supportfiles . '|part\d+|r\d{1,3}|zipr\d{2,3}|\d{2,3}|zipx|zip|rar)(\.rar)?$/i', $v['name']) && count($retval) > 0) $this->addfile($v, $release, $rar, $nntp); else unset($retval[$k]); } if (count($retval) == 0) $retval = false; unset($fetchedBinary, $rar, $rf, $nfo); return $retval; } /** * Open the zip, see if it has a password, attempt to get a file. * * @note Called by processReleaseFiles * * @param $fetchedBinary * @param bool $open * @param bool $data * @param $release * @param $nntp * * @return array|bool */ protected function processReleaseZips($fetchedBinary, $open = false, $data = false, $release, $nntp) { if (!isset($nntp)) { exit($this->c->error("Not connected to usenet(Functions->processReleaseZips).\n")); } // Load the ZIP file or data. $zip = new ZipInfo(); if ($open) $zip->open($fetchedBinary, true); else $zip->setData($fetchedBinary, true); if ($zip->error) { $this->c->error('processReleaseZips', 'ZIP Error: ' . $zip->error); return false; } if (!empty($zip->isEncrypted)) { $this->c->error('processReleaseZips', 'ZIP archive is password encrypted for release ' . $release['ID']); $this->password = true; return false; } $files = $zip->getFileList(); $dataArray = array(); if ($files !== false) { if ($this->echooutput) { echo 'z'; } foreach ($files as $file) { $thisData = $zip->getFileData($file['name']); $dataArray[] = array('zip' => $file, 'data' => $thisData); // Process RARs inside the ZIP. if (preg_match('/\.(r\d+|part\d+|rar)$/i', $file['name']) || preg_match('/\bRAR\b/i', $thisData)) { $tmpFiles = $this->getRar($thisData); if ($tmpFiles !== false) { $limit = 0; foreach ($tmpFiles as $f) { if ($limit++ > 11) { break; } $this->addFile($f, $release, $rar = false, $nntp); $files[] = $f; } } } //Extract a NFO from the zip. else if ($this->nonfo === true && $file['size'] < 100000 && preg_match('/\.(nfo|inf|ofn)$/i', $file['name'])) { if ($file['compressed'] !== 1) { $nfo = new Info($this->echooutput); if ($this->nfo->addAlternateNfo($thisData, $release, $nntp)) { $this->c->error('processReleaseZips', 'Added NFO from ZIP file for releaseID ' . $release['ID']); if ($this->echooutput) { echo 'n'; } $this->nonfo = false; } } else if ($this->tmux->zippath !== '' && $file['compressed'] === 1) { $zip->setExternalClient($this->tmux->zippath); $zipData = $zip->extractFile($file['name']); if ($zipData !== false && strlen($zipData) > 5) { $nfo = new Info($this->echooutput); if ($this->nfo->addAlternateNfo($zipData, $release, $nntp)) { $this->c->error('processReleaseZips', 'Added compressed NFO from ZIP file for releaseID ' . $release['ID']); if ($this->echooutput) { echo 'n'; } $this->nonfo = false; } } } } } } if ($data) { $files = $dataArray; unset($dataArray); } unset($fetchedBinary, $zip); return $files; } /** * Get contents of rar file. * * @note Called by processReleaseFiles and processReleaseZips * * @param $fetchedBinary * * @return array|bool */ protected function getRar($fetchedBinary) { $rar = new ArchiveInfo(); $files = $retVal = false; if ($rar->setData($fetchedBinary, true)) { // Useless? $files = $rar->getArchiveFileList(); } if ($rar->error) { $this->c->error('getRar', 'RAR Error: ' . $rar->error); return $retVal; } if (!empty($rar->isEncrypted)) { $this->c->error('getRar', 'Archive is password encrypted.'); $this->password = true; return $retVal; } $tmp = $rar->getSummary(true, false); if (isset($tmp['is_encrypted']) && $tmp['is_encrypted'] != 0) { $this->c->error('getRar', 'Archive is password encrypted.'); $this->password = true; return $retVal; } $files = $rar->getArchiveFileList(); if ($files !== false) { $retVal = array(); if ($this->echooutput !== false) { echo 'r'; } foreach ($files as $file) { if (isset($file['name'])) { if (isset($file['error'])) { $this->c->error('getRar', "Error: {$file['error']} (in: {$file['source']})"); continue; } if (isset($file['pass']) && $file['pass'] == true) { $this->password = true; break; } if (preg_match($this->supportFiles . ')(?!.{20,})/i', $file['name'])) { continue; } if (preg_match('/([^\/\\\\]+)(\.[a-z][a-z0-9]{2,3})$/i', $file['name'], $name)) { $rarFile = $this->tmpPath . $name[1] . mt_rand(0, 99999) . $name[2]; $fetchedBinary = $rar->getFileData($file['name'], $file['source']); if ($this->site->mediainfopath !== '') { $this->addMediaFile($rarFile, $fetchedBinary); } } if (!preg_match('/\.(r\d+|part\d+)$/i', $file['name'])) { $retVal[] = $file; } } } } if (count($retVal) === 0) return false; return $retVal; } public function updateReleaseHasPreview($guid) { $this->db->exec(sprintf('UPDATE releases SET haspreview = 1 WHERE guid = %s', $this->db->escapeString($guid))); } function addfile($v, $release, $rar = false, $nntp) { if (!isset($nntp)) exit($this->c->error("Not connected to usenet(postprocess->addfile).\n")); if (!isset($v['error']) && isset($v['source'])) { if ($rar !== false && preg_match('/\.zip$/', $v['source'])) { $zip = new ZipInfo(); $tmpdata = $zip->getFileData($v['name'], $v['source']); } else if ($rar !== false) $tmpdata = $rar->getFileData($v['name'], $v['source']); else $tmpdata = false; // Check if we already have the file or not. // Also make sure we don't add too many files, some releases have 100's of files, like PS3 releases. if ($this->filesadded < 11 && $this->db->queryOneRow(sprintf('SELECT ID FROM releasefiles WHERE releaseID = %d AND name = %s AND size = %d', $release['ID'], $this->db->escapeString($v['name']), $v['size'])) === false) { $rf = new ReleaseFiles(); if ($rf->add($release['ID'], $v['name'], $v['size'], $v['date'], $v['pass'])) { $this->filesadded++; $this->newfiles = true; if ($this->echooutput) echo '^'; } } if ($tmpdata !== false) { // Extract a NFO from the rar. if ($this->nonfo === true && $v['size'] > 100 && $v['size'] < 100000 && preg_match('/(\.(nfo|inf|ofn)|info.txt)$/i', $v['name'])) { $nfo = new Info($this->echooutput); if ($this->nfo->addAlternateNfo($tmpData, $release, $nntp)) { $this->debug('added rar nfo'); if ($this->echooutput) echo 'n'; $this->nonfo = false; } } // Extract a video file from the compressed file. else if ($this->site->mediainfopath != '' && preg_match('/' . $this->videofileregex . '$/i', $v['name'])) $this->addmediafile($this->tmpPath . 'sample_' . mt_rand(0, 99999) . '.avi', $tmpdata); // Extract an audio file from the compressed file. else if ($this->site->mediainfopath != '' && preg_match('/' . $this->audiofileregex . '$/i', $v['name'], $ext)) $this->addmediafile($this->tmpPath . 'audio_' . mt_rand(0, 99999) . $ext[0], $tmpdata); else if ($this->site->mediainfopath != '' && preg_match('/([^\/\\\r]+)(\.[a-z][a-z0-9]{2,3})$/i', $v['name'], $name)) $this->addmediafile($this->tmpPath . $name[1] . mt_rand(0, 99999) . $name[2], $tmpdata); } unset($tmpdata, $rf); } } public function debug($str) { if ($this->echooutput && $this->DEBUG_ECHO) { echo $this->c->debug($str); } } public function nzbFileList($nzb) { $num_pars = $i = 0; $result = array(); $nzb = str_replace("\x0F", '', $nzb); $xml = @simplexml_load_string($nzb); if (!$xml || strtolower($xml->getName()) != 'nzb') { return $result; } foreach ($xml->file as $file) { // Subject. $title = $file->attributes()->subject; // Amoune of pars. if (preg_match('/\.par2/i', $title)) { $num_pars++; } $result[$i]['title'] = $title; // Extensions. if (preg_match( '/\.(\d{2,3}|7z|ace|ai7|srr|srt|sub|aiff|asc|avi|audio|bin|bz2|' . 'c|cfc|cfm|chm|class|conf|cpp|cs|css|csv|cue|deb|divx|doc|dot|' . 'eml|enc|exe|file|gif|gz|hlp|htm|html|image|iso|jar|java|jpeg|' . 'jpg|js|lua|m|m3u|mm|mov|mp3|mpg|nfo|nzb|odc|odf|odg|odi|odp|' . 'ods|odt|ogg|par2|parity|pdf|pgp|php|pl|png|ppt|ps|py|r\d{2,3}|' . 'ram|rar|rb|rm|rpm|rtf|sfv|sig|sql|srs|swf|sxc|sxd|sxi|sxw|tar|' . 'tex|tgz|txt|vcf|video|vsd|wav|wma|wmv|xls|xml|xpi|xvid|zip7|zip)' . '[" ](?!(\)|\-))/i', $file->attributes()->subject, $ext ) ) { if (preg_match('/\.r\d{2,3}/i', $ext[0])) { $ext[1] = 'rar'; } $result[$i]['ext'] = strtolower($ext[1]); } else { $result[$i]['ext'] = ''; } $filesize = $numsegs = 0; // File size. foreach ($file->segments->segment as $segment) { $filesize += $segment->attributes()->bytes; $numsegs++; } $result[$i]['size'] = $filesize; // File completion. if (preg_match('/(\d+)\)$/', $title, $parts)) { $result[$i]['partstotal'] = $parts[1]; } $result[$i]['partsactual'] = $numsegs; // Groups. if (!isset($result[$i]['groups'])) { $result[$i]['groups'] = array(); } foreach ($file->groups->group as $g) { array_push($result[$i]['groups'], (string)$g); } // Parts. if (!isset($result[$i]['segments'])) { $result[$i]['segments'] = array(); } foreach ($file->segments->segment as $s) { array_push($result[$i]['segments'], (string)$s); } unset($result[$i]['segments']['@attributes']); $i++; } return $result; } function addmediafile($file, $data) { if (@file_put_contents($file, $data) !== false) { $xmlarray = @runCmd('"' . $this->site->mediainfopath . '" --Output=XML "' . $file . '"'); if (is_array($xmlarray)) { $xmlarray = implode("\n", $xmlarray); $xmlObj = @simplexml_load_string($xmlarray); $arrXml = objectsIntoArray($xmlObj); if (!isset($arrXml['File']['track'][0])) @unlink($file); } } } public function parseImdb($str) { if (preg_match('/(?:imdb.*?)?(?:tt|Title\?)(\d{5,7})/i', $str, $matches)) { return trim($matches[1]); } return false; } public function processMovies($releaseToWork = '') { if ($this->site->lookupimdb == 1) { $movie = new Film($this->echooutput); $movie->processMovieReleases($releaseToWork); } } /** * Lookup games if enabled. * * @return void */ public function processGames() { if ($this->site->lookupgames == 1) { $console = new Console($this->echooutput); $this->processConsoleReleases(); } } public function processConsoleReleases() { $db = $this->db; $res = $db->queryDirect(sprintf('SELECT r.searchname, r.ID FROM releases r INNER JOIN category c ON r.categoryID = c.ID WHERE r.consoleinfoID IS NULL AND c.parentID = %d ORDER BY r.postdate DESC LIMIT %d', Category::CAT_PARENT_GAME, $this->gameqty)); if ($res->rowCount() > 0) { if ($this->echooutput) { echo $this->c->header("\nProcessing " . $res->rowCount() . ' console release(s).'); } foreach ($res as $arr) { $startTime = microtime(true); $usedAmazon = false; $gameInfo = $this->parseTitle($arr['searchname']); if ($gameInfo !== false) { if ($this->echooutput) { echo $this->c->headerOver('Looking up: ') . $this->c->primary($gameInfo['title'] . ' (' . $gameInfo['platform'] . ')'); } // Check for existing console entry. $gameCheck = $this->getConsoleInfoByName($gameInfo['title'], $gameInfo['platform']); if ($gameCheck === false) { $gameId = $this->updateConsoleInfo($gameInfo); $usedAmazon = true; if ($gameId === false) { $gameId = -2; } } else { $gameId = $gameCheck['ID']; } // Update release. $db->exec(sprintf('UPDATE releases SET consoleinfoID = %d WHERE ID = %d', $gameId, $arr['ID'])); } else { // Could not parse release title. $db->exec(sprintf('UPDATE releases SET consoleinfoID = %d WHERE ID = %d', -2, $arr['ID'])); echo '.'; } // Sleep to not flood amazon. $diff = floor((microtime(true) - $startTime) * 1000000); if ($this->sleeptime * 1000 - $diff > 0 && $usedAmazon === true) { usleep($this->sleeptime * 1000 - $diff); } } } else if ($this->echooutput) { echo $this->c->header('No console releases to process.'); } } function parseTitle($releasename) { $matches = ''; $releasename = preg_replace('/\sMulti\d?\s/i', '', $releasename); $result = array(); // Get name of the game from name of release. preg_match('/^(.+((abgx360EFNet|EFNet\sFULL|FULL\sabgxEFNet|abgx\sFULL|abgxbox360EFNet)\s|illuminatenboard\sorg))?(?P