From 596a74c22d2c2f3d8ff3560e8f7bd0cf5fb4e8ce Mon Sep 17 00:00:00 2001 From: jonnyboy Date: Sat, 5 Jan 2013 06:18:54 -0500 Subject: [PATCH] thanks to kevin123 lots of changes --- README.md | 19 +- bin/cleanup_scripts.sh | 2 +- bin/lib/postprocess2.php | 975 ++++++++++++++++++ bin/lib/postprocess3.php | 975 ++++++++++++++++++ bin/lib/releases2.php | 2016 +++++++++++++++++++++++++++++++++++++ bin/lib/releases3.php | 2016 +++++++++++++++++++++++++++++++++++++ bin/lib/thetvdb2.php | 331 ++++++ bin/lib/thetvdb3.php | 331 ++++++ bin/monitor.php | 138 ++- bin/pid.php | 32 - bin/postProcessing1.sh | 14 + bin/postProcessing2.sh | 14 + bin/postProcessing3.sh | 14 + bin/postProcessing4.sh | 14 + bin/postProcessing5.sh | 15 + bin/postProcessing6.sh | 17 + bin/postprocess_nfo.php | 13 +- bin/postprocessing.php | 56 -- bin/processAlternate.php | 21 - bin/processAlternate2.php | 41 + bin/processAlternate3.php | 39 + bin/processBooks.php | 15 + bin/processGames.php | 15 + bin/processMovies.php | 15 + bin/processMusic.php | 15 + bin/processOthers.php | 23 + bin/processTv.php | 15 + bin/processUnwanted.php | 15 + bin/workhorse.sh | 3 +- convert.php | 22 + edit_these.sh | 20 +- revert.sh | 2 + set_perms.sh | 9 + start.sh | 34 +- 34 files changed, 7117 insertions(+), 179 deletions(-) create mode 100755 bin/lib/postprocess2.php create mode 100755 bin/lib/postprocess3.php create mode 100755 bin/lib/releases2.php create mode 100755 bin/lib/releases3.php create mode 100755 bin/lib/thetvdb2.php create mode 100755 bin/lib/thetvdb3.php delete mode 100755 bin/pid.php create mode 100755 bin/postProcessing1.sh create mode 100755 bin/postProcessing2.sh create mode 100755 bin/postProcessing3.sh create mode 100755 bin/postProcessing4.sh create mode 100755 bin/postProcessing5.sh create mode 100755 bin/postProcessing6.sh delete mode 100755 bin/postprocessing.php delete mode 100755 bin/processAlternate.php create mode 100755 bin/processAlternate2.php create mode 100755 bin/processAlternate3.php create mode 100755 bin/processBooks.php create mode 100755 bin/processGames.php create mode 100755 bin/processMovies.php create mode 100755 bin/processMusic.php create mode 100755 bin/processOthers.php create mode 100755 bin/processTv.php create mode 100755 bin/processUnwanted.php create mode 100755 convert.php diff --git a/README.md b/README.md index 9ef5fedd3..3c77ad840 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,20 @@ `mysqldump --opt -u root -p newznab > ~/newznab_backup.sql` - * This first step will modify your setup using [andrewit's github](https://github.com/itandrew/Newznab-InnoDB-Dropin.git), if you want. If you do not convert, then use then use myisam when editing. + * The first step is to decide whether or not you will convert your database to the InnoDB engine. The InnoDB has a lot of benefits, too many to list here, but more ram is required. How much exactly, depends on too many things to list here. + + * If you decide to convert your database, I recommend using [kevein123's github](https://github.com/kevinlekiller/Newznab-Barracuda.git). I recommend only converting the binaries a parts table, using compressed tables. But, there are many choices. I suggest you read his README and follow his recommendations. Or, simply: + + `cd to a location to store the git git repository.` + + `git clone https://github.com/kevinlekiller/Newznab-Barracuda.git` + + `cd Newznab-Barracuda/B\ +\ P/` + + `php bpinnodb_compressed.php` + + * Or, you can use [andrewit's github](https://github.com/itandrew/Newznab-InnoDB-Dropin.git). If you are using Innodb, this script uses some of [andrewit's](https://github.com/itandrew/Newznab-InnoDB-Dropin.git), so you will need to follow at least the next 2 commands. + * convert database to innodb, edit path if necessary `cd /var/www/newznab/misc/testing/` @@ -47,9 +60,11 @@ * Now all panes should be running smoothly. If any of the scriptns in the top 3 panes crash, the panes should remain open and you should be able to view the error messages. I hope this helps. + * Join in the converstion at irc://moonlight.se.eu.synirc.net/newznab-tmux. + * Thanks go to all who offered their assistance and improvement to these scripts.
- * Donations help to pay my hosting fees and server costs. Thank you + * IOf, you find these scripts useful and would like to offer a donation. Danotions are greatly appreciated. Thank you PayPal - The safer, easier way to pay online!Click here to lend your support to: Newznab-tmux and make a donation at www.pledgie.com ! diff --git a/bin/cleanup_scripts.sh b/bin/cleanup_scripts.sh index 574fca005..c7db99aa2 100755 --- a/bin/cleanup_scripts.sh +++ b/bin/cleanup_scripts.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -e source ../edit_these.sh @@ -69,3 +68,4 @@ sleep $NEWZNAB_POST_SLEEP_TIME done +exit diff --git a/bin/lib/postprocess2.php b/bin/lib/postprocess2.php new file mode 100755 index 000000000..1428a3a59 --- /dev/null +++ b/bin/lib/postprocess2.php @@ -0,0 +1,975 @@ +echooutput = $echooutput; + $s = new Sites(); + $this->site = $s->get(); + + $this->mediafileregex = 'AVI|VOB|MKV|MP4|TS|WMV|MOV|M4V|F4V|MPG|MPEG'; + $this->audiofileregex = 'MP3|AAC|OGG'; + $this->mp3SavePath = WWW_DIR.'covers/audio/'; + } + + /** + * Perform all post processing. + */ + public function processAll() + { + //$this->>processAdditional2(); + //$this->processNfos(); + //$this->processUnwanted(); + //$this->processMovies(); + //$this->processMusic(); + //$this->processBooks(); + //$this->processGames(); + //$this->processTv(); + //$this->processMusicFromMediaInfo(); + //$this->processOtherMiscCategory(); + //$this->processUnknownCategory(); + } + + public function processUnwanted() + { + $r = new Releases; + $db = new DB; + $currTime_ori = $db->queryOneRow("SELECT NOW() as now"); + + // + // Delete any passworded releases + // + if($this->site->deletepasswordedrelease == 1) + { + echo "PostPrc : Removing unwanted releases\n"; + $result = $db->query("select ID from releases where passwordstatus > 0"); + foreach ($result as $row) + $r->delete($row["ID"]); + } + + // + // Delete any releases which are older than site's release retention days + // + if($this->site->releaseretentiondays != 0) + { + echo "PostPrc : Deleting releases older than ".$this->site->releaseretentiondays." days\n"; + + $result = $db->query(sprintf("select ID from releases where postdate < %s - interval %d day", $db->escapeString($currTime_ori["now"]), $this->site->releaseretentiondays)); + foreach ($result as $row) + $r->delete($row["ID"]); + } + + // + // Delete any audiopreviews older than site->audiopreviewprune days + // + if($this->site->audiopreviewprune > 0) + { + $result = $db->query(sprintf("select guid from releases where categoryID like '3%%' and haspreview = 2 and adddate < %s - interval %d day", $db->escapeString($currTime_ori["now"]), $this->site->audiopreviewprune)); + + echo "PostPrc : Deleting ".count($result)." audio previews older than ".$this->site->audiopreviewprune." days\n"; + foreach ($result as $row) + { + $r->updateHasPreview($row["guid"], 0); + $this->deleteAudioSample($row["guid"]); + } + } + + // + // Delete any releases suspected of being spam/virus + // + if($this->site->removespam != 0) + { + $spamIDs = array(); + + // + // all releases where the only file inside the rars is *.exe and they are not in the PC category + // + $sql = "select releasefiles.releaseID as ID from releasefiles inner join ( select releaseID, count(*) as totnum from releasefiles group by releaseID ) x on x.releaseID = releasefiles.releaseID and x.totnum = 1 inner join releases on releases.ID = releasefiles.releaseID left join releasenfo on releasenfo.releaseID = releases.ID where (releasefiles.name like '%.exe' or releasefiles.name like '%.scr') and (releases.categoryID not in (4000,4010,4020,4030,4040,4050) or (releases.categoryID in (4000,4010,4020,4030,4040,4050) and releasenfo.ID is null)) group by releasefiles.releaseID"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all releases containing exe not in permitted categories + // + if ($this->site->exepermittedcategories != '') + { + $sql = sprintf("select releasefiles.releaseID as ID from releasefiles inner join releases on releases.ID = releasefiles.releaseID left join releasenfo on releasenfo.releaseID = releases.ID where releasefiles.name like '%%.exe' and releases.categoryID not in (%s) group by releasefiles.releaseID", $this->site->exepermittedcategories); + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + } + + // + // delete all releases which contain a file with password.url in it + // + $sql = "select distinct releasefiles.releaseID as ID from releasefiles where name = 'password.url'"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all releases where the only file inside the rars is *.rar + // + $sql = "select releasefiles.releaseID as ID from releasefiles inner join ( select releaseID, count(*) as totnum from releasefiles group by releaseID ) x on x.releaseID = releasefiles.releaseID and x.totnum = 1 inner join releases on releases.ID = releasefiles.releaseID where releasefiles.name like '%.rar' group by releasefiles.releaseID"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all audio which contains a file with .exe in + // + $sql = "select distinct r.ID from releasefiles rf inner join releases r on r.id = rf.releaseID and r.categoryID like '3%' where rf.name like '%.exe'"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + if (count($spamIDs) > 0) + { + echo "PostPrc : Deleting ".count($spamIDs)." spam releases\n" ; + foreach ($spamIDs as $row) + $r->delete($row["ID"]); + } + } + } + + /** + * Process nfo files + */ + public function processNfos() + { + if ($this->site->lookupnfo == 1) + { + $nfo = new Nfo($this->echooutput); + $nfo->processNfoFiles($this->site->lookupimdb, $this->site->lookuptvrage); + } + } + + /** + * Lookup imdb if enabled + */ + public function processMovies() + { + if ($this->site->lookupimdb == 1) + { + $movie = new Movie($this->echooutput); + $movie->processMovieReleases(); + } + } + + /** + * Lookup music if enabled + */ + public function processMusic() + { + if ($this->site->lookupmusic == 1) + { + $music = new Music($this->echooutput); + $music->processMusicReleases(); + } + } + + /** + * Lookup book if enabled + */ + public function processBooks() + { + if ($this->site->lookupbooks == 1) + { + $book = new Book($this->echooutput); + $book->processBookReleases(); + } + } + + /** + * Lookup games if enabled + */ + public function processGames() + { + if ($this->site->lookupgames == 1) + { + $console = new Console($this->echooutput); + $console->processConsoleReleases(); + } + } + + /** + * Work out any categories which were not assigned by regex or determinecategory + * Done in post process as releasevideo/audio will have been performed by now. + */ + public function processUnknownCategory() + { + $db = new DB; + $sql = sprintf("select ID from releases where categoryID = %d", Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + $rescount = sizeof($result); + if ($rescount > 0) + { + echo "PostPrc : Attempting to fix ".$rescount." uncategorised release(s)\n"; + + $sql = sprintf("update releases inner join releasevideo rv on rv.releaseID = releases.ID set releases.categoryID = %d where imdbid is not null and categoryid = %d and videocodec = 'XVID'", Category::CAT_MOVIE_SD, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + + $sql = sprintf("update releases inner join releasevideo rv on rv.releaseID = releases.ID set releases.categoryID = %d where imdbid is not null and categoryid = %d and videocodec = 'V_MPEG4/ISO/AVC'", Category::CAT_MOVIE_HD, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + + $sql = sprintf("update releases set categoryID = %d where categoryID = %d", Category::CAT_MISC, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + } + } + + /** + * Process all TV related releases which will assign their series/episode/rage data + */ + public function processTv() + { + if ($this->site->lookupanidb == 1) + { + $anidb = new AniDB($this->echooutput); + $anidb->animetitlesUpdate(); + $anidb->processAnimeReleases(); + } + + if ($this->site->lookuptvrage == 1) + { + $tvrage = new TVRage($this->echooutput); + $tvrage->processTvReleases(($this->site->lookuptvrage==1)); + } + + if ($this->site->lookupthetvdb == 1) + { + $thetvdb = new TheTVDB($this->echooutput); + $thetvdb->processReleases(); + } + } + + /** + * Process releases without a proper name and try to look it up in the nfo + */ + public function processOtherMiscCategory($numToProcess = 10) + { + $db = new DB(); + + $res = $db->query(sprintf("select r.searchname, r.ID, r.guid, g.name as groupname from releases r inner join releasenfo rn on rn.releaseID = r.ID left join groups g on g.ID = r.groupID where (r.categoryID = %d or r.categoryID = %d) order by r.ID desc limit %d", Category::CAT_MISC, Category::CAT_NOT_DETERMINED, $numToProcess)); + + if ($res) + { + $rescount = sizeof($res); + + if ($this->echooutput) + echo "PostPrc : Attempting to categorise ".$rescount." Other-Misc releases\n"; + + foreach($res as $rel) + { + $filenameRes = $db->query(sprintf("select dirname from predb where filename = %s limit 2", $db->escapeString($rel['searchname']))); + if (count($filenameRes) == 1) + $foundName = $filenameRes[0]['dirname']; + else + { + $nfoRes = $db->queryOneRow(sprintf("select uncompress(nfo) as nfo from releasenfo where releaseID = %d", $rel['ID'])); + $nfo = $nfoRes['nfo']; + + $foundName = ''; + //Typical scene regex + if (preg_match('/(?PSource\s*?:|fix fornuke)?(?:\s|\]|\[)?(?P[a-z0-9\']+(?:\.|_)[a-z0-9\.\-_\'&]+\-[a-z0-9&]+)(?:\s|\[|\])/i', $nfo, $matches)) + { + if (empty($matches['source'])) + $foundName = $matches['name']; + } + //IMAGiNE releases + elseif(preg_match('/\*\s+([a-z0-9]+(?:\.|_| )[a-z0-9\.\_\- ]+ \- imagine)\s+\*/i', $nfo, $matches)) + { + $foundName = $matches[1]; + } + //SANTi releases + elseif(preg_match('/\b([a-z0-9]+(?:\.|_| )[a-z0-9\.\_\- \']+\-santi)\b/i', $nfo, $matches)) + { + $foundName = $matches[1]; + } + } + + if ($foundName != '') + { + $category = new Category(); + $categoryID = $category->determineCategory($rel['groupname'], $foundName); + $name = str_replace(' ', '_', $foundName); + $searchname = str_replace('_', ' ', $foundName); + + $db->query(sprintf("UPDATE releases SET name = %s, searchname = %s, categoryID = %d WHERE ID = %d", $db->escapeString($name), $db->escapeString($searchname), $categoryID, $rel['ID'])); + } + } + } + } + + /** + * Check for passworded releases, RAR contents and Sample/Media info + */ + public function processAdditional2() + { + require_once(WWW_DIR."/lib/nntp.php"); + + $maxattemptstocheckpassworded = 5; + $numtoProcess = 100; + $processVideoSample = ($this->site->ffmpegpath != '') ? true : false; + $processMediainfo = ($this->site->mediainfopath != '') ? true : false; + $processPasswords = ($this->site->unrarpath != '') ? true : false; + $processAudioSample = ($this->site->saveaudiopreview == 1) ? true : false; + + $tmpPath = $this->site->tmpunrarpath; + $tmpPath .= '2'; + if (substr($tmpPath, -strlen( '/' ) ) != '/') + { + $tmpPath = $tmpPath.'/'; + } + + if (!file_exists($tmpPath)) + mkdir($tmpPath, 0766, true); + + $db = new DB; + $nntp = new Nntp; + $nzb = new Nzb; + + // + // Get out all releases which have not been checked more than max attempts for password. + // + $result = $db->query(sprintf("select r.ID, r.guid, r.name, c.disablepreview from releases r + left join category c on c.ID = r.categoryID + where (r.passwordstatus between %d and -1) + or (r.haspreview = -1 and c.disablepreview = 0) order by r.guid desc limit %d ", ($maxattemptstocheckpassworded + 1) * -1, $numtoProcess)); + + $iteration = $rescount = sizeof($result); + if ($rescount > 0) + { + echo "Post processing by guid on ".$rescount." releases ..."; + $nntpconnected = false; + + foreach ($result as $rel) + { + echo $iteration--."."; + + // Per release defaults + $passStatus = array(Releases::PASSWD_NONE); + $blnTookMediainfo = false; + $blnTookSample = ($rel['disablepreview'] == 1) ? true : false; //only attempt sample if not disabled + + if ($blnTookSample) + $db->query(sprintf("update releases set haspreview = 0 where id = %d", $rel['ID'])); + + // + // Go through the binaries for this release looking for a rar, a sample, and a mediafile + // + $nzbInfo = new nzbInfo; + $nzbfile = $nzb->getNZBPath($rel['guid'], $this->site->nzbpath); + if (!$nzbInfo->loadFromFile($nzbfile)) + { + continue; + } + $norar = 0; + + foreach($nzbInfo->nzb as $nzbsubject) + { + if (preg_match("/\w\.r00/i", $nzbsubject['subject'])) + $norar= 1; + } + + // attempt to process video sample file + if(!empty($nzbInfo->samplefiles) && $processVideoSample && $blnTookSample === false) + { + $sampleFile = $nzbInfo->samplefiles[0]; //first detected sample + $sampleMsgids = array_slice($sampleFile['segments'], 0, 1); //get first segment, increase to get more of the sample + $sampleGroup = $sampleFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($sampleMsgids, ', ')." from {$sampleGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + + $sampleBinary = $nntp->getMessages($sampleGroup, $sampleMsgids); + if ($sampleBinary === false) + echo "\nPostPrc : Couldnt fetch sample\n"; + else + { + $samplefile = $tmpPath.'sample.avi'; + + file_put_contents($samplefile, $sampleBinary); + + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + + unlink($samplefile); + } + unset($sampleBinary); + } + + // attempt to process loose media file + if(!empty($nzbInfo->mediafiles) && (($processVideoSample && $blnTookSample === false) || $processMediainfo)) + { + $mediaFile = $nzbInfo->mediafiles[0]; //first detected media file + $mediaMsgids = array_slice($mediaFile['segments'], 0, 2); //get first two segments + $mediaGroup = $mediaFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($mediaMsgids, ', ')." from {$mediaGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $mediaBinary = $nntp->getMessages($mediaGroup, $mediaMsgids); + if ($mediaBinary === false) + echo "\nPostPrc : Couldnt fetch media file\n"; + else + { + $mediafile = $tmpPath.'sample.avi'; + + file_put_contents($mediafile, $mediaBinary); + + if ($processVideoSample && $blnTookSample === false) + { + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + } + + if ($processMediainfo) + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + + unlink($mediafile); + } + unset($mediaBinary); + } + + // attempt to process audio sample file + if(!empty($nzbInfo->audiofiles) && $processAudioSample && $blnTookSample === false) + { + $audioFile = $nzbInfo->audiofiles[0]; //first detected audio file + $audioMsgids = array_slice($audioFile['segments'], 0, 1); //get first segment + $audioGroup = $audioFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($audioMsgids, ', ')." from {$audioGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $audioBinary = $nntp->getMessages($audioGroup, $audioMsgids); + if ($audioBinary === false) + echo "\nPostPrc : Couldnt fetch audio sample\n"; + else + { + $audiofile = $tmpPath.'sample.mp3'; + + file_put_contents($audiofile, $audioBinary); + + $blnTookSample = $this->getAudioSample($tmpPath, $rel['guid']); + if ($blnTookSample !== false) + $this->updateReleaseHasPreview($rel['guid'], 2); + + if ($processMediainfo) + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + + if ($this->site->lamepath != "") + $this->lameAudioSample($this->site->lamepath, $rel['guid']); + + unlink($audiofile); + } + unset($audioBinary); + } + + if (!empty($nzbInfo->rarfiles) && ($this->site->checkpasswordedrar > 0 || (($processVideoSample || $processAudioSample) && $blnTookSample === false) || $processMediainfo)) + { + $mysqlkeepalive = 0; + foreach($nzbInfo->rarfiles as $rarFile) + { + //dont process any more rars if a passworded rar has been detected and the site is set to automatically delete them + if ($this->site->deletepasswordedrelease == 1 && max($passStatus) == Releases::PASSWD_RAR) + { + echo "-Skipping processing of rar {$rarFile['subject']} as this release has already been marked as passworded.\n"; + continue; + } + + $rarMsgids = array_slice($rarFile['segments'], 0, 1); //get first segment + $rarGroup = $rarFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($rarMsgids, ', ')." from {$rarGroup} (".++$mysqlkeepalive.")\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $fetchedBinary = $nntp->getMessages($rarGroup, $rarMsgids); + if ($fetchedBinary === false) + { + echo "\nPostPrc : Failed fetching rar file\n"; + $db->query(sprintf("update releases set passwordstatus = passwordstatus - 1 where ID = %d", $rel['ID'])); + continue; + } + else + { + $relFiles = $this->processReleaseFiles($fetchedBinary, $rel['ID']); + + if ($this->site->checkpasswordedrar > 0 && $processPasswords) + { + $passStatus[] = $this->processReleasePasswords($fetchedBinary, $tmpPath, $this->site->unrarpath, $this->site->checkpasswordedrar); + } + + // we need to unrar the fetched binary if checkpasswordedrar wasnt 2 + if ($this->site->checkpasswordedrar < 2 && $processPasswords) + { + $rarfile = $tmpPath.'rarfile.rar'; + file_put_contents($rarfile, $fetchedBinary); + $execstring = '"'.$this->site->unrarpath.'" e -ai -ep -c- -id -r -kb -p- -y -inul "'.$rarfile.'" "'.$tmpPath.'"'; + $output = runCmd($execstring, false, true); + unlink($rarfile); + } + + if ($processVideoSample && $blnTookSample === false) + { + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + } + + $blnTookAudioSample = false; + if ($processAudioSample && $blnTookSample === false) + { + $blnTookSample = $this->getAudioSample($tmpPath, $rel['guid']); + if ($blnTookSample) + { + $blnTookAudioSample = true; + $this->updateReleaseHasPreview($rel['guid'], 2); + } + } + + if ($processMediainfo && $blnTookMediainfo === false) + { + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + } + + // + // Has to be done after mediainfo + // + if ($blnTookAudioSample && $this->site->lamepath != "") + $this->lameAudioSample($this->site->lamepath, $rel['guid']); + + if ($mysqlkeepalive % 25 == 0) + $db->query("select 1"); + } + + //clean up all files + foreach(glob($tmpPath.'*') as $v) + { + unlink($v); + } + + } //end foreach msgid + } + elseif(empty($nzbInfo->rarfiles) && $norar == 1) + { + $passStatus[] = Releases::PASSWD_POTENTIAL; + } + + $hpsql = ''; + if (!$blnTookSample) + $hpsql = ', haspreview = 0'; + + $sql = sprintf("update releases set passwordstatus = %d %s where ID = %d", max($passStatus), $hpsql, $rel["ID"]); + $db->query($sql); + + } //end foreach result + + if ($nntpconnected) + { + $nntp->doQuit(); + } + + echo "\n"; + } + } + + /** + * Work out all files contained inside a rar + */ + public function processReleaseFiles($fetchedBinary, $relid) + { + $retval = array(); + $rar = new RarInfo; + $rf = new ReleaseFiles; + $db = new DB; + + if ($rar->setData($fetchedBinary)) + { + $files = $rar->getFileList(); + foreach ($files as $file) + { + $rf->add($relid, $file['name'], $file['size'], $file['date'], $file['pass'] ); + $retval[] = $file['name']; + } + + } + unset($fetchedBinary); + return $retval; + } + + /** + * Work out if a release is passworded + */ + public function processReleasePasswords($fetchedBinary, $tmpPath, $unrarPath, $checkpasswordedrar) + { + $passStatus = Releases::PASSWD_NONE; + $potentiallypasswordedfileregex = "/\.(ace|cab|tar|gz)$/i"; + $definetlypasswordedfileregex = "/password/i"; + $rar = new RarInfo; + $filecount = 0; + + $rarfile = $tmpPath.'rarfile.rar'; + file_put_contents($rarfile, $fetchedBinary); + + if ($rar->open($rarfile)) + { + if ($rar->isEncrypted) + { + $passStatus = Releases::PASSWD_RAR; + } + else + { + $files = $rar->getFileList(true); + foreach ($files as $file) + { + $filecount++; + // + // individual file rar passworded + // + if ($file['pass'] == 1 || preg_match($definetlypasswordedfileregex, $file["name"])) + { + $passStatus = Releases::PASSWD_RAR; + } + + // + // individual file looks suspect + // + elseif (preg_match($potentiallypasswordedfileregex, $file["name"]) && $passStatus != Releases::PASSWD_RAR) + { + $passStatus = Releases::PASSWD_POTENTIAL; + } + } + + // + // Deep Checking + // + if ($checkpasswordedrar == 2) + { + + $israr = $this->isRar($rarfile); + for ($i=0;$iisRar($tmpPath.$israr[$i]); + + if (is_array($tmp)) + // it's a rar + { + for ($x=0;$xopen($rarfile)) + { + if ($rar->isEncrypted) + { + return 1; + } + else + { + $files = $rar->getFileList(true); + foreach ($files as $file) + { + $filelist[] = $file['name']; + if ($file['pass'] == true) + // + // individual file rar passworded + // + { + return 2; + // passworded + } + } + return ($filelist); + // normal rar + } + } + else + { + return 0; + // not a rar + } + } + + /** + * Work out all files contained inside a rar + */ + public function getMediainfo($ramdrive,$mediainfo,$releaseID) + { + $retval = false; + $mediafiles = glob($ramdrive.'*.*'); + if (is_array($mediafiles)) + { + foreach($mediafiles as $mediafile) + { + if (preg_match("/\.(".$this->mediafileregex.'|'.$this->audiofileregex.")$/i",$mediafile)) + { + $execstring = '"'.$mediainfo.'" --Output=XML "'.$mediafile.'"'; + $xmlarray = runCmd($execstring); + + if (is_array($xmlarray)) + { + $xmlarray = implode("\n",$xmlarray); + $re = new ReleaseExtra(); + $re->addFull($releaseID,$xmlarray); + $re->addFromXml($releaseID,$xmlarray); + $retval = true; + } + else + { + echo "PostPrc : Failed to process mediainfo for ".$mediafile." release (".$releaseID.")\n"; + } + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Get a sample from a release using ffmpeg + */ + public function getSample($ramdrive, $ffmpeginfo, $releaseguid) + { + $ri = new ReleaseImage(); + $retval = false; + + $samplefiles = glob($ramdrive.'*.*'); + if (is_array($samplefiles)) + { + foreach($samplefiles as $samplefile) + { + if (preg_match("/\.(".$this->mediafileregex.")$/i",$samplefile)) + { + $execstring = '"'.$ffmpeginfo.'" -q:v 0 -i "'.$samplefile.'" -vframes 300 "'.$ramdrive.'zzzz%03d.jpg"'; + $output = runCmd($execstring, false, true); + $all_files = scandir($ramdrive,1); + if(preg_match("/zzzz\d{3}\.jpg/",$all_files[1])) + { + $ri->saveImage($releaseguid.'_thumb', $ramdrive.$all_files[1], $ri->imgSavePath, 800, 600); + $retval = true; + } + + //clean up all files + foreach(glob($ramdrive.'*.jpg') as $v) + { + unlink($v); + } + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Has to be performed after mediainfo, as lame strips id3 tags. + */ + public function lameAudioSample($lameinfo, $releaseguid) + { + $minacceptableencodefilesize = 10000; + $samplefile = $this->mp3SavePath.$releaseguid.'.mp3'; + + if (file_exists($samplefile)) + { + $outfile = $this->mp3SavePath.$releaseguid.'_l.mp3'; + + // + // lame the sample down to 96kb and replace it. alternatives could be + // V8 for low quality variable. + // + $execstring = '"'.$lameinfo.'" -b 96 "'.$samplefile.'" "'.$outfile.'"'; + $output = runCmd($execstring, false, true); + + // + // lame can create bad/small files if the source was corrupt + // if it creates a file thats surprisingly small, then ignore it and retain + // original + // + if (file_exists($outfile)) + { + if (filesize($outfile) < $minacceptableencodefilesize) + unlink($outfile); + else + { + unlink($samplefile); + rename($outfile, $samplefile); + + return true; + } + } + } + + return false; + } + + /** + * Get an audio sample from a release. + */ + public function getAudioSample($ramdrive, $releaseguid) + { + $retval = false; + + $audiofiles = glob($ramdrive.'*.*'); + if (is_array($audiofiles)) + { + foreach($audiofiles as $audiofile) + { + if (preg_match("/\.(".$this->audiofileregex.")$/i",$audiofile)) + { + if (copy($audiofile, $this->mp3SavePath.$releaseguid.'.mp3') !== false) + $retval = true; + else + echo "PostPrc : Failed to get audio sample from ".$audiofile."\n"; + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Delete an audio sample from a release. + */ + public function deleteAudioSample($releaseguid) + { + $preview = $this->mp3SavePath.$releaseguid.'.mp3'; + if (file_exists($preview)) + unlink($preview); + } + + /** + * Update release to indicate a preview has been obtained. + */ + public function updateReleaseHasPreview($guid, $prevtype=1) + { + $rel = new Releases; + $rel->updateHasPreview($guid, $prevtype); + } + + /** + * Process untagged music releases using information from mediainfo if config permits. + */ + public function processMusicFromMediaInfo() + { + $processMediainfo = ($this->site->mediainfopath != '') ? true : false; + $processAudioSample = ($this->site->saveaudiopreview == 1) ? true : false; + $processMusic = ($this->site->lookupmusic == 1) ? true : false; + + if ($processMusic && $processMediainfo && $processAudioSample) + { + $music = new Music($this->echooutput); + $ret = $music->processMusicReleaseFromMediaInfo(); + return $ret; + } + + return false; + } +} + diff --git a/bin/lib/postprocess3.php b/bin/lib/postprocess3.php new file mode 100755 index 000000000..bc210ed95 --- /dev/null +++ b/bin/lib/postprocess3.php @@ -0,0 +1,975 @@ +echooutput = $echooutput; + $s = new Sites(); + $this->site = $s->get(); + + $this->mediafileregex = 'AVI|VOB|MKV|MP4|TS|WMV|MOV|M4V|F4V|MPG|MPEG'; + $this->audiofileregex = 'MP3|AAC|OGG'; + $this->mp3SavePath = WWW_DIR.'covers/audio/'; + } + + /** + * Perform all post processing. + */ + public function processAll() + { + //$this->>processAdditional3(); + //$this->processNfos(); + //$this->processUnwanted(); + //$this->processMovies(); + //$this->processMusic(); + //$this->processBooks(); + //$this->processGames(); + //$this->processTv(); + //$this->processMusicFromMediaInfo(); + //$this->processOtherMiscCategory(); + //$this->processUnknownCategory(); + } + + public function processUnwanted() + { + $r = new Releases; + $db = new DB; + $currTime_ori = $db->queryOneRow("SELECT NOW() as now"); + + // + // Delete any passworded releases + // + if($this->site->deletepasswordedrelease == 1) + { + echo "PostPrc : Removing unwanted releases\n"; + $result = $db->query("select ID from releases where passwordstatus > 0"); + foreach ($result as $row) + $r->delete($row["ID"]); + } + + // + // Delete any releases which are older than site's release retention days + // + if($this->site->releaseretentiondays != 0) + { + echo "PostPrc : Deleting releases older than ".$this->site->releaseretentiondays." days\n"; + + $result = $db->query(sprintf("select ID from releases where postdate < %s - interval %d day", $db->escapeString($currTime_ori["now"]), $this->site->releaseretentiondays)); + foreach ($result as $row) + $r->delete($row["ID"]); + } + + // + // Delete any audiopreviews older than site->audiopreviewprune days + // + if($this->site->audiopreviewprune > 0) + { + $result = $db->query(sprintf("select guid from releases where categoryID like '3%%' and haspreview = 2 and adddate < %s - interval %d day", $db->escapeString($currTime_ori["now"]), $this->site->audiopreviewprune)); + + echo "PostPrc : Deleting ".count($result)." audio previews older than ".$this->site->audiopreviewprune." days\n"; + foreach ($result as $row) + { + $r->updateHasPreview($row["guid"], 0); + $this->deleteAudioSample($row["guid"]); + } + } + + // + // Delete any releases suspected of being spam/virus + // + if($this->site->removespam != 0) + { + $spamIDs = array(); + + // + // all releases where the only file inside the rars is *.exe and they are not in the PC category + // + $sql = "select releasefiles.releaseID as ID from releasefiles inner join ( select releaseID, count(*) as totnum from releasefiles group by releaseID ) x on x.releaseID = releasefiles.releaseID and x.totnum = 1 inner join releases on releases.ID = releasefiles.releaseID left join releasenfo on releasenfo.releaseID = releases.ID where (releasefiles.name like '%.exe' or releasefiles.name like '%.scr') and (releases.categoryID not in (4000,4010,4020,4030,4040,4050) or (releases.categoryID in (4000,4010,4020,4030,4040,4050) and releasenfo.ID is null)) group by releasefiles.releaseID"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all releases containing exe not in permitted categories + // + if ($this->site->exepermittedcategories != '') + { + $sql = sprintf("select releasefiles.releaseID as ID from releasefiles inner join releases on releases.ID = releasefiles.releaseID left join releasenfo on releasenfo.releaseID = releases.ID where releasefiles.name like '%%.exe' and releases.categoryID not in (%s) group by releasefiles.releaseID", $this->site->exepermittedcategories); + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + } + + // + // delete all releases which contain a file with password.url in it + // + $sql = "select distinct releasefiles.releaseID as ID from releasefiles where name = 'password.url'"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all releases where the only file inside the rars is *.rar + // + $sql = "select releasefiles.releaseID as ID from releasefiles inner join ( select releaseID, count(*) as totnum from releasefiles group by releaseID ) x on x.releaseID = releasefiles.releaseID and x.totnum = 1 inner join releases on releases.ID = releasefiles.releaseID where releasefiles.name like '%.rar' group by releasefiles.releaseID"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + // + // all audio which contains a file with .exe in + // + $sql = "select distinct r.ID from releasefiles rf inner join releases r on r.id = rf.releaseID and r.categoryID like '3%' where rf.name like '%.exe'"; + $result = $db->query($sql); + $spamIDs = array_merge($result, $spamIDs); + + if (count($spamIDs) > 0) + { + echo "PostPrc : Deleting ".count($spamIDs)." spam releases\n" ; + foreach ($spamIDs as $row) + $r->delete($row["ID"]); + } + } + } + + /** + * Process nfo files + */ + public function processNfos() + { + if ($this->site->lookupnfo == 1) + { + $nfo = new Nfo($this->echooutput); + $nfo->processNfoFiles($this->site->lookupimdb, $this->site->lookuptvrage); + } + } + + /** + * Lookup imdb if enabled + */ + public function processMovies() + { + if ($this->site->lookupimdb == 1) + { + $movie = new Movie($this->echooutput); + $movie->processMovieReleases(); + } + } + + /** + * Lookup music if enabled + */ + public function processMusic() + { + if ($this->site->lookupmusic == 1) + { + $music = new Music($this->echooutput); + $music->processMusicReleases(); + } + } + + /** + * Lookup book if enabled + */ + public function processBooks() + { + if ($this->site->lookupbooks == 1) + { + $book = new Book($this->echooutput); + $book->processBookReleases(); + } + } + + /** + * Lookup games if enabled + */ + public function processGames() + { + if ($this->site->lookupgames == 1) + { + $console = new Console($this->echooutput); + $console->processConsoleReleases(); + } + } + + /** + * Work out any categories which were not assigned by regex or determinecategory + * Done in post process as releasevideo/audio will have been performed by now. + */ + public function processUnknownCategory() + { + $db = new DB; + $sql = sprintf("select ID from releases where categoryID = %d", Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + $rescount = sizeof($result); + if ($rescount > 0) + { + echo "PostPrc : Attempting to fix ".$rescount." uncategorised release(s)\n"; + + $sql = sprintf("update releases inner join releasevideo rv on rv.releaseID = releases.ID set releases.categoryID = %d where imdbid is not null and categoryid = %d and videocodec = 'XVID'", Category::CAT_MOVIE_SD, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + + $sql = sprintf("update releases inner join releasevideo rv on rv.releaseID = releases.ID set releases.categoryID = %d where imdbid is not null and categoryid = %d and videocodec = 'V_MPEG4/ISO/AVC'", Category::CAT_MOVIE_HD, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + + $sql = sprintf("update releases set categoryID = %d where categoryID = %d", Category::CAT_MISC, Category::CAT_NOT_DETERMINED); + $result = $db->query($sql); + } + } + + /** + * Process all TV related releases which will assign their series/episode/rage data + */ + public function processTv() + { + if ($this->site->lookupanidb == 1) + { + $anidb = new AniDB($this->echooutput); + $anidb->animetitlesUpdate(); + $anidb->processAnimeReleases(); + } + + if ($this->site->lookuptvrage == 1) + { + $tvrage = new TVRage($this->echooutput); + $tvrage->processTvReleases(($this->site->lookuptvrage==1)); + } + + if ($this->site->lookupthetvdb == 1) + { + $thetvdb = new TheTVDB($this->echooutput); + $thetvdb->processReleases(); + } + } + + /** + * Process releases without a proper name and try to look it up in the nfo + */ + public function processOtherMiscCategory($numToProcess = 10) + { + $db = new DB(); + + $res = $db->query(sprintf("select r.searchname, r.ID, r.guid, g.name as groupname from releases r inner join releasenfo rn on rn.releaseID = r.ID left join groups g on g.ID = r.groupID where (r.categoryID = %d or r.categoryID = %d) order by r.ID desc limit %d", Category::CAT_MISC, Category::CAT_NOT_DETERMINED, $numToProcess)); + + if ($res) + { + $rescount = sizeof($res); + + if ($this->echooutput) + echo "PostPrc : Attempting to categorise ".$rescount." Other-Misc releases\n"; + + foreach($res as $rel) + { + $filenameRes = $db->query(sprintf("select dirname from predb where filename = %s limit 2", $db->escapeString($rel['searchname']))); + if (count($filenameRes) == 1) + $foundName = $filenameRes[0]['dirname']; + else + { + $nfoRes = $db->queryOneRow(sprintf("select uncompress(nfo) as nfo from releasenfo where releaseID = %d", $rel['ID'])); + $nfo = $nfoRes['nfo']; + + $foundName = ''; + //Typical scene regex + if (preg_match('/(?PSource\s*?:|fix fornuke)?(?:\s|\]|\[)?(?P[a-z0-9\']+(?:\.|_)[a-z0-9\.\-_\'&]+\-[a-z0-9&]+)(?:\s|\[|\])/i', $nfo, $matches)) + { + if (empty($matches['source'])) + $foundName = $matches['name']; + } + //IMAGiNE releases + elseif(preg_match('/\*\s+([a-z0-9]+(?:\.|_| )[a-z0-9\.\_\- ]+ \- imagine)\s+\*/i', $nfo, $matches)) + { + $foundName = $matches[1]; + } + //SANTi releases + elseif(preg_match('/\b([a-z0-9]+(?:\.|_| )[a-z0-9\.\_\- \']+\-santi)\b/i', $nfo, $matches)) + { + $foundName = $matches[1]; + } + } + + if ($foundName != '') + { + $category = new Category(); + $categoryID = $category->determineCategory($rel['groupname'], $foundName); + $name = str_replace(' ', '_', $foundName); + $searchname = str_replace('_', ' ', $foundName); + + $db->query(sprintf("UPDATE releases SET name = %s, searchname = %s, categoryID = %d WHERE ID = %d", $db->escapeString($name), $db->escapeString($searchname), $categoryID, $rel['ID'])); + } + } + } + } + + /** + * Check for passworded releases, RAR contents and Sample/Media info + */ + public function processAdditional3() + { + require_once(WWW_DIR."/lib/nntp.php"); + + $maxattemptstocheckpassworded = 5; + $numtoProcess = 100; + $processVideoSample = ($this->site->ffmpegpath != '') ? true : false; + $processMediainfo = ($this->site->mediainfopath != '') ? true : false; + $processPasswords = ($this->site->unrarpath != '') ? true : false; + $processAudioSample = ($this->site->saveaudiopreview == 1) ? true : false; + + $tmpPath = $this->site->tmpunrarpath; + $tmpPath .= '3'; + if (substr($tmpPath, -strlen( '/' ) ) != '/') + { + $tmpPath = $tmpPath.'/'; + } + + if (!file_exists($tmpPath)) + mkdir($tmpPath, 0766, true); + + $db = new DB; + $nntp = new Nntp; + $nzb = new Nzb; + + // + // Get out all releases which have not been checked more than max attempts for password. + // + $result = $db->query(sprintf("select r.ID, r.guid, r.name, c.disablepreview from releases r + left join category c on c.ID = r.categoryID + where (r.passwordstatus between %d and -1) + or (r.haspreview = -1 and c.disablepreview = 0) order by r.guid asc limit %d ", ($maxattemptstocheckpassworded + 1) * -1, $numtoProcess)); + + $iteration = $rescount = sizeof($result); + if ($rescount > 0) + { + echo "Post processing by guid inversly on ".$rescount." releases ..."; + $nntpconnected = false; + + foreach ($result as $rel) + { + echo $iteration--."."; + + // Per release defaults + $passStatus = array(Releases::PASSWD_NONE); + $blnTookMediainfo = false; + $blnTookSample = ($rel['disablepreview'] == 1) ? true : false; //only attempt sample if not disabled + + if ($blnTookSample) + $db->query(sprintf("update releases set haspreview = 0 where id = %d", $rel['ID'])); + + // + // Go through the binaries for this release looking for a rar, a sample, and a mediafile + // + $nzbInfo = new nzbInfo; + $nzbfile = $nzb->getNZBPath($rel['guid'], $this->site->nzbpath); + if (!$nzbInfo->loadFromFile($nzbfile)) + { + continue; + } + $norar = 0; + + foreach($nzbInfo->nzb as $nzbsubject) + { + if (preg_match("/\w\.r00/i", $nzbsubject['subject'])) + $norar= 1; + } + + // attempt to process video sample file + if(!empty($nzbInfo->samplefiles) && $processVideoSample && $blnTookSample === false) + { + $sampleFile = $nzbInfo->samplefiles[0]; //first detected sample + $sampleMsgids = array_slice($sampleFile['segments'], 0, 1); //get first segment, increase to get more of the sample + $sampleGroup = $sampleFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($sampleMsgids, ', ')." from {$sampleGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + + $sampleBinary = $nntp->getMessages($sampleGroup, $sampleMsgids); + if ($sampleBinary === false) + echo "\nPostPrc : Couldnt fetch sample\n"; + else + { + $samplefile = $tmpPath.'sample.avi'; + + file_put_contents($samplefile, $sampleBinary); + + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + + unlink($samplefile); + } + unset($sampleBinary); + } + + // attempt to process loose media file + if(!empty($nzbInfo->mediafiles) && (($processVideoSample && $blnTookSample === false) || $processMediainfo)) + { + $mediaFile = $nzbInfo->mediafiles[0]; //first detected media file + $mediaMsgids = array_slice($mediaFile['segments'], 0, 2); //get first two segments + $mediaGroup = $mediaFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($mediaMsgids, ', ')." from {$mediaGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $mediaBinary = $nntp->getMessages($mediaGroup, $mediaMsgids); + if ($mediaBinary === false) + echo "\nPostPrc : Couldnt fetch media file\n"; + else + { + $mediafile = $tmpPath.'sample.avi'; + + file_put_contents($mediafile, $mediaBinary); + + if ($processVideoSample && $blnTookSample === false) + { + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + } + + if ($processMediainfo) + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + + unlink($mediafile); + } + unset($mediaBinary); + } + + // attempt to process audio sample file + if(!empty($nzbInfo->audiofiles) && $processAudioSample && $blnTookSample === false) + { + $audioFile = $nzbInfo->audiofiles[0]; //first detected audio file + $audioMsgids = array_slice($audioFile['segments'], 0, 1); //get first segment + $audioGroup = $audioFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($audioMsgids, ', ')." from {$audioGroup}\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $audioBinary = $nntp->getMessages($audioGroup, $audioMsgids); + if ($audioBinary === false) + echo "\nPostPrc : Couldnt fetch audio sample\n"; + else + { + $audiofile = $tmpPath.'sample.mp3'; + + file_put_contents($audiofile, $audioBinary); + + $blnTookSample = $this->getAudioSample($tmpPath, $rel['guid']); + if ($blnTookSample !== false) + $this->updateReleaseHasPreview($rel['guid'], 2); + + if ($processMediainfo) + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + + if ($this->site->lamepath != "") + $this->lameAudioSample($this->site->lamepath, $rel['guid']); + + unlink($audiofile); + } + unset($audioBinary); + } + + if (!empty($nzbInfo->rarfiles) && ($this->site->checkpasswordedrar > 0 || (($processVideoSample || $processAudioSample) && $blnTookSample === false) || $processMediainfo)) + { + $mysqlkeepalive = 0; + foreach($nzbInfo->rarfiles as $rarFile) + { + //dont process any more rars if a passworded rar has been detected and the site is set to automatically delete them + if ($this->site->deletepasswordedrelease == 1 && max($passStatus) == Releases::PASSWD_RAR) + { + echo "-Skipping processing of rar {$rarFile['subject']} as this release has already been marked as passworded.\n"; + continue; + } + + $rarMsgids = array_slice($rarFile['segments'], 0, 1); //get first segment + $rarGroup = $rarFile['groups'][0]; + + //echo "PostPrc : Fetching ".implode($rarMsgids, ', ')." from {$rarGroup} (".++$mysqlkeepalive.")\n"; + if (!$nntpconnected) + { + $nntp->doConnect(); + $nntpconnected = true; + } + $fetchedBinary = $nntp->getMessages($rarGroup, $rarMsgids); + if ($fetchedBinary === false) + { + echo "\nPostPrc : Failed fetching rar file\n"; + $db->query(sprintf("update releases set passwordstatus = passwordstatus - 1 where ID = %d", $rel['ID'])); + continue; + } + else + { + $relFiles = $this->processReleaseFiles($fetchedBinary, $rel['ID']); + + if ($this->site->checkpasswordedrar > 0 && $processPasswords) + { + $passStatus[] = $this->processReleasePasswords($fetchedBinary, $tmpPath, $this->site->unrarpath, $this->site->checkpasswordedrar); + } + + // we need to unrar the fetched binary if checkpasswordedrar wasnt 2 + if ($this->site->checkpasswordedrar < 2 && $processPasswords) + { + $rarfile = $tmpPath.'rarfile.rar'; + file_put_contents($rarfile, $fetchedBinary); + $execstring = '"'.$this->site->unrarpath.'" e -ai -ep -c- -id -r -kb -p- -y -inul "'.$rarfile.'" "'.$tmpPath.'"'; + $output = runCmd($execstring, false, true); + unlink($rarfile); + } + + if ($processVideoSample && $blnTookSample === false) + { + $blnTookSample = $this->getSample($tmpPath, $this->site->ffmpegpath, $rel['guid']); + if ($blnTookSample) + $this->updateReleaseHasPreview($rel['guid']); + } + + $blnTookAudioSample = false; + if ($processAudioSample && $blnTookSample === false) + { + $blnTookSample = $this->getAudioSample($tmpPath, $rel['guid']); + if ($blnTookSample) + { + $blnTookAudioSample = true; + $this->updateReleaseHasPreview($rel['guid'], 2); + } + } + + if ($processMediainfo && $blnTookMediainfo === false) + { + $blnTookMediainfo = $this->getMediainfo($tmpPath, $this->site->mediainfopath, $rel['ID']); + } + + // + // Has to be done after mediainfo + // + if ($blnTookAudioSample && $this->site->lamepath != "") + $this->lameAudioSample($this->site->lamepath, $rel['guid']); + + if ($mysqlkeepalive % 25 == 0) + $db->query("select 1"); + } + + //clean up all files + foreach(glob($tmpPath.'*') as $v) + { + unlink($v); + } + + } //end foreach msgid + } + elseif(empty($nzbInfo->rarfiles) && $norar == 1) + { + $passStatus[] = Releases::PASSWD_POTENTIAL; + } + + $hpsql = ''; + if (!$blnTookSample) + $hpsql = ', haspreview = 0'; + + $sql = sprintf("update releases set passwordstatus = %d %s where ID = %d", max($passStatus), $hpsql, $rel["ID"]); + $db->query($sql); + + } //end foreach result + + if ($nntpconnected) + { + $nntp->doQuit(); + } + + echo "\n"; + } + } + + /** + * Work out all files contained inside a rar + */ + public function processReleaseFiles($fetchedBinary, $relid) + { + $retval = array(); + $rar = new RarInfo; + $rf = new ReleaseFiles; + $db = new DB; + + if ($rar->setData($fetchedBinary)) + { + $files = $rar->getFileList(); + foreach ($files as $file) + { + $rf->add($relid, $file['name'], $file['size'], $file['date'], $file['pass'] ); + $retval[] = $file['name']; + } + + } + unset($fetchedBinary); + return $retval; + } + + /** + * Work out if a release is passworded + */ + public function processReleasePasswords($fetchedBinary, $tmpPath, $unrarPath, $checkpasswordedrar) + { + $passStatus = Releases::PASSWD_NONE; + $potentiallypasswordedfileregex = "/\.(ace|cab|tar|gz)$/i"; + $definetlypasswordedfileregex = "/password/i"; + $rar = new RarInfo; + $filecount = 0; + + $rarfile = $tmpPath.'rarfile.rar'; + file_put_contents($rarfile, $fetchedBinary); + + if ($rar->open($rarfile)) + { + if ($rar->isEncrypted) + { + $passStatus = Releases::PASSWD_RAR; + } + else + { + $files = $rar->getFileList(true); + foreach ($files as $file) + { + $filecount++; + // + // individual file rar passworded + // + if ($file['pass'] == 1 || preg_match($definetlypasswordedfileregex, $file["name"])) + { + $passStatus = Releases::PASSWD_RAR; + } + + // + // individual file looks suspect + // + elseif (preg_match($potentiallypasswordedfileregex, $file["name"]) && $passStatus != Releases::PASSWD_RAR) + { + $passStatus = Releases::PASSWD_POTENTIAL; + } + } + + // + // Deep Checking + // + if ($checkpasswordedrar == 2) + { + + $israr = $this->isRar($rarfile); + for ($i=0;$iisRar($tmpPath.$israr[$i]); + + if (is_array($tmp)) + // it's a rar + { + for ($x=0;$xopen($rarfile)) + { + if ($rar->isEncrypted) + { + return 1; + } + else + { + $files = $rar->getFileList(true); + foreach ($files as $file) + { + $filelist[] = $file['name']; + if ($file['pass'] == true) + // + // individual file rar passworded + // + { + return 2; + // passworded + } + } + return ($filelist); + // normal rar + } + } + else + { + return 0; + // not a rar + } + } + + /** + * Work out all files contained inside a rar + */ + public function getMediainfo($ramdrive,$mediainfo,$releaseID) + { + $retval = false; + $mediafiles = glob($ramdrive.'*.*'); + if (is_array($mediafiles)) + { + foreach($mediafiles as $mediafile) + { + if (preg_match("/\.(".$this->mediafileregex.'|'.$this->audiofileregex.")$/i",$mediafile)) + { + $execstring = '"'.$mediainfo.'" --Output=XML "'.$mediafile.'"'; + $xmlarray = runCmd($execstring); + + if (is_array($xmlarray)) + { + $xmlarray = implode("\n",$xmlarray); + $re = new ReleaseExtra(); + $re->addFull($releaseID,$xmlarray); + $re->addFromXml($releaseID,$xmlarray); + $retval = true; + } + else + { + echo "PostPrc : Failed to process mediainfo for ".$mediafile." release (".$releaseID.")\n"; + } + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Get a sample from a release using ffmpeg + */ + public function getSample($ramdrive, $ffmpeginfo, $releaseguid) + { + $ri = new ReleaseImage(); + $retval = false; + + $samplefiles = glob($ramdrive.'*.*'); + if (is_array($samplefiles)) + { + foreach($samplefiles as $samplefile) + { + if (preg_match("/\.(".$this->mediafileregex.")$/i",$samplefile)) + { + $execstring = '"'.$ffmpeginfo.'" -q:v 0 -i "'.$samplefile.'" -vframes 300 "'.$ramdrive.'zzzz%03d.jpg"'; + $output = runCmd($execstring, false, true); + $all_files = scandir($ramdrive,1); + if(preg_match("/zzzz\d{3}\.jpg/",$all_files[1])) + { + $ri->saveImage($releaseguid.'_thumb', $ramdrive.$all_files[1], $ri->imgSavePath, 800, 600); + $retval = true; + } + + //clean up all files + foreach(glob($ramdrive.'*.jpg') as $v) + { + unlink($v); + } + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Has to be performed after mediainfo, as lame strips id3 tags. + */ + public function lameAudioSample($lameinfo, $releaseguid) + { + $minacceptableencodefilesize = 10000; + $samplefile = $this->mp3SavePath.$releaseguid.'.mp3'; + + if (file_exists($samplefile)) + { + $outfile = $this->mp3SavePath.$releaseguid.'_l.mp3'; + + // + // lame the sample down to 96kb and replace it. alternatives could be + // V8 for low quality variable. + // + $execstring = '"'.$lameinfo.'" -b 96 "'.$samplefile.'" "'.$outfile.'"'; + $output = runCmd($execstring, false, true); + + // + // lame can create bad/small files if the source was corrupt + // if it creates a file thats surprisingly small, then ignore it and retain + // original + // + if (file_exists($outfile)) + { + if (filesize($outfile) < $minacceptableencodefilesize) + unlink($outfile); + else + { + unlink($samplefile); + rename($outfile, $samplefile); + + return true; + } + } + } + + return false; + } + + /** + * Get an audio sample from a release. + */ + public function getAudioSample($ramdrive, $releaseguid) + { + $retval = false; + + $audiofiles = glob($ramdrive.'*.*'); + if (is_array($audiofiles)) + { + foreach($audiofiles as $audiofile) + { + if (preg_match("/\.(".$this->audiofileregex.")$/i",$audiofile)) + { + if (copy($audiofile, $this->mp3SavePath.$releaseguid.'.mp3') !== false) + $retval = true; + else + echo "PostPrc : Failed to get audio sample from ".$audiofile."\n"; + } + } + } + else + { + echo "PostPrc: Couldn't open temp drive ".$ramdrive."\n"; + } + return $retval; + } + + /** + * Delete an audio sample from a release. + */ + public function deleteAudioSample($releaseguid) + { + $preview = $this->mp3SavePath.$releaseguid.'.mp3'; + if (file_exists($preview)) + unlink($preview); + } + + /** + * Update release to indicate a preview has been obtained. + */ + public function updateReleaseHasPreview($guid, $prevtype=1) + { + $rel = new Releases; + $rel->updateHasPreview($guid, $prevtype); + } + + /** + * Process untagged music releases using information from mediainfo if config permits. + */ + public function processMusicFromMediaInfo() + { + $processMediainfo = ($this->site->mediainfopath != '') ? true : false; + $processAudioSample = ($this->site->saveaudiopreview == 1) ? true : false; + $processMusic = ($this->site->lookupmusic == 1) ? true : false; + + if ($processMusic && $processMediainfo && $processAudioSample) + { + $music = new Music($this->echooutput); + $ret = $music->processMusicReleaseFromMediaInfo(); + return $ret; + } + + return false; + } +} + diff --git a/bin/lib/releases2.php b/bin/lib/releases2.php new file mode 100755 index 000000000..68092e1bb --- /dev/null +++ b/bin/lib/releases2.php @@ -0,0 +1,2016 @@ + 0) + { + $n = array(); + foreach($names as $nm) + $n[] = " searchname = ". $db->escapeString($nm); + + $nsql = "( ".implode(' or ', $n)." )"; + } + + $sql = sprintf(" SELECT releases.*, CONCAT(cp.title, ' > ', c.title) AS category_name, + m.ID AS movie_id, m.title, m.rating, m.cover, m.plot, m.year, m.genre, m.director, m.actors, m.tagline, + mu.ID AS music_id, mu.title as mu_title, mu.cover as mu_cover, mu.year as mu_year, mu.artist as mu_artist, mu.tracks as mu_tracks, mu.review as mu_review, + ep.ID AS ep_id, ep.showtitle as ep_showtitle, ep.airdate as ep_airdate, ep.fullep as ep_fullep, ep.overview as ep_overview, + tvrage.imgdata as rage_imgdata, tvrage.ID as rg_ID + FROM releases + LEFT OUTER JOIN category c ON c.ID = releases.categoryID + LEFT OUTER JOIN category cp ON cp.ID = c.parentID + LEFT OUTER JOIN movieinfo m ON m.imdbID = releases.imdbID + LEFT OUTER JOIN musicinfo mu ON mu.ID = releases.musicinfoID + LEFT OUTER JOIN episodeinfo ep ON ep.ID = releases.episodeinfoID + LEFT OUTER JOIN tvrage ON tvrage.rageID = releases.rageID + where %s", $nsql); + + return $db->queryDirect($sql); + } + + /** + * Get a count of releases for pager. used in admin manage list + */ + public function getCount() + { + $db = new DB(); + $res = $db->queryOneRow("select count(ID) as num from releases"); + return $res["num"]; + } + + /** + * Get a range of releases. used in admin manage list + */ + public function getRange($start, $num) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + return $db->query(" SELECT releases.*, concat(cp.title, ' > ', c.title) as category_name from releases left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID order by postdate desc".$limit); + } + + /** + * Get a count of releases for main browse pager. + */ + public function getBrowseCount($cat, $maxage=-1, $excludedcats=array(), $grp=array()) + { + $db = new DB(); + + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = " and ("; + foreach ($grp as $grpname) + { + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + } + $grpsql.= "1=2 )"; + } + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and categoryID not in (".implode(",", $excludedcats).")"; + + $sql = sprintf("select count(releases.ID) as num from releases left outer join groups on groups.ID = releases.groupID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s", $catsrch, $maxage, $exccatlist, $grpsql); + $res = $db->queryOneRow($sql, true); + return $res["num"]; + } + + /** + * Get a releases for main browse pages. + */ + public function getBrowseRange($cat, $start, $num, $orderby, $maxage=-1, $excludedcats=array(), $grp=array()) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + $usecatindex = ""; + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and postdate > now() - interval %d day ", $maxage); + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = "select ID from groups where ("; + foreach ($grp as $grpname) + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + + $grpsql.= "1=2 )"; + + $grpres = $db->query($grpsql); + if (count($grpsql) > 0) + { + $grpsql = " and ( "; + foreach ($grpres as $grpresrow) + $grpsql.= sprintf(" groups.ID = %d or ", $grpresrow["ID"]); + + $grpsql = substr($grpsql, 0, strlen($grpsql) - 3)." ) "; + } + else + $grpsql = ""; + } + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $order = $this->getBrowseOrder($orderby); + $sql = sprintf(" SELECT releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID, pre.ctime, pre.nuketype, coalesce(movieinfo.ID,0) as movieinfoID from releases %s left outer join groups on groups.ID = releases.groupID left outer join movieinfo on movieinfo.imdbID = releases.imdbID left outer join releaseaudio re on re.releaseID = releases.ID and re.audioID = 1 left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join predb pre on pre.ID = releases.preID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by %s %s".$limit, $usecatindex, $catsrch, $maxagesql, $exccatlist, $grpsql, $order[0], $order[1]); + return $db->query($sql, true); + } + + /** + * Get a column names browse list to be ordered by + */ + public function getBrowseOrder($orderby) + { + $order = ($orderby == '') ? 'posted_desc' : $orderby; + $orderArr = explode("_", $order); + switch($orderArr[0]) { + case 'cat': + $orderfield = 'categoryID'; + break; + case 'name': + $orderfield = 'searchname'; + break; + case 'size': + $orderfield = 'size'; + break; + case 'files': + $orderfield = 'totalpart'; + break; + case 'stats': + $orderfield = 'grabs'; + break; + case 'posted': + default: + $orderfield = 'postdate'; + break; + } + $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc'; + return array($orderfield, $ordersort); + } + + /** + * Get a list of available columns for sorting browse list + */ + public function getBrowseOrdering() + { + return array('name_asc', 'name_desc', 'cat_asc', 'cat_desc', 'posted_asc', 'posted_desc', 'size_asc', 'size_desc', 'files_asc', 'files_desc', 'stats_asc', 'stats_desc'); + } + + /** + * Get a range of releases. Used in nzb export + */ + public function getForExport($postfrom, $postto, $group, $cat) + { + $db = new DB(); + if ($postfrom != "") + { + $dateparts = explode("/", $postfrom); + if (count($dateparts) == 3) + $postfrom = sprintf(" and postdate > %s ", $db->escapeString($dateparts[2]."-".$dateparts[1]."-".$dateparts[0]." 00:00:00")); + else + $postfrom = ""; + } + + if ($postto != "") + { + $dateparts = explode("/", $postto); + if (count($dateparts) == 3) + $postto = sprintf(" and postdate < %s ", $db->escapeString($dateparts[2]."-".$dateparts[1]."-".$dateparts[0]." 23:59:59")); + else + $postto = ""; + } + + if ($group != "" && $group != "-1") + $group = sprintf(" and groupID = %d ", $group); + else + $group = ""; + + if ($cat != "" && $cat != "-1") + $cat = sprintf(" and categoryID = %d ", $cat); + else + $cat = ""; + + return $db->queryDirect(sprintf("SELECT searchname, guid, CONCAT(cp.title,'_',category.title) as catName FROM releases INNER JOIN category ON releases.categoryID = category.ID LEFT OUTER JOIN category cp ON cp.ID = category.parentID where 1 = 1 %s %s %s %s", $postfrom, $postto, $group, $cat)); + } + + /** + * Get the earliest release + */ + public function getEarliestUsenetPostDate() + { + $db = new DB(); + $row = $db->queryOneRow("SELECT DATE_FORMAT(min(postdate), '%d/%m/%Y') as postdate from releases"); + return $row["postdate"]; + } + + /** + * Get the most recent release + */ + public function getLatestUsenetPostDate() + { + $db = new DB(); + $row = $db->queryOneRow("SELECT DATE_FORMAT(max(postdate), '%d/%m/%Y') as postdate from releases"); + return $row["postdate"]; + } + + /** + * Get all groups for which there is a release for a html select + */ + public function getReleasedGroupsForSelect($blnIncludeAll = true) + { + $db = new DB(); + $groups = $db->query("select distinct groups.ID, groups.name from releases inner join groups on groups.ID = releases.groupID"); + $temp_array = array(); + + if ($blnIncludeAll) + $temp_array[-1] = "--All Groups--"; + + foreach($groups as $group) + $temp_array[$group["ID"]] = $group["name"]; + + return $temp_array; + } + + /** + * Get releases for all types of rss feeds + */ + public function getRss($cat, $num, $uid=0, $rageid, $anidbid, $airdate=-1) + { + $db = new DB(); + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $catsrch = ""; + $cartsrch = ""; + + $catsrch = ""; + if (count($cat) > 0) + { + if ($cat[0] == -2) + { + $cartsrch = sprintf(" inner join usercart on usercart.userID = %d and usercart.releaseID = releases.ID ", $uid); + } + elseif ($cat[0] == -1) + { + } + else + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + } + + $rage = ($rageid > -1) ? sprintf(" and releases.rageID = %d ", $rageid) : ''; + $anidb = ($anidbid > -1) ? sprintf(" and releases.anidbID = %d ", $anidbid) : ''; + $airdate = ($airdate > -1) ? sprintf(" and releases.tvairdate >= DATE_SUB(CURDATE(), INTERVAL %d DAY) ", $airdate) : ''; + + $sql = sprintf(" SELECT releases.*, rn.ID as nfoID, m.title as imdbtitle, m.cover, m.imdbID, m.rating, m.plot, m.year, m.genre, m.director, m.actors, g.name as group_name, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID, mu.title as mu_title, mu.url as mu_url, mu.artist as mu_artist, mu.publisher as mu_publisher, mu.releasedate as mu_releasedate, mu.review as mu_review, mu.tracks as mu_tracks, mu.cover as mu_cover, mug.title as mu_genre, co.title as co_title, co.url as co_url, co.publisher as co_publisher, co.releasedate as co_releasedate, co.review as co_review, co.cover as co_cover, cog.title as co_genre, bo.title as bo_title, bo.url as bo_url, bo.publisher as bo_publisher, bo.author as bo_author, bo.publishdate as bo_publishdate, bo.review as bo_review, bo.cover as bo_cover from releases left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join groups g on g.ID = releases.groupID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join movieinfo m on m.imdbID = releases.imdbID and m.title != '' left outer join musicinfo mu on mu.ID = releases.musicinfoID left outer join genres mug on mug.ID = mu.genreID left outer join bookinfo bo on bo.ID = releases.bookinfoID left outer join consoleinfo co on co.ID = releases.consoleinfoID left outer join genres cog on cog.ID = co.genreID %s where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by postdate desc %s" ,$cartsrch, $catsrch, $rage, $anidb, $airdate, $limit); + return $db->query($sql, true); + } + + /** + * Get releases in users 'my tv show' rss feed + */ + public function getShowsRss($num, $uid=0, $excludedcats=array(), $airdate=-1) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usershows = $db->query(sprintf("select rageID, categoryID from userseries where userID = %d", $uid), true); + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $airdate = ($airdate > -1) ? sprintf(" and releases.tvairdate >= DATE_SUB(CURDATE(), INTERVAL %d DAY) ", $airdate) : ''; + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $sql = sprintf(" SELECT releases.*, tvr.rageID, tvr.releasetitle, epinfo.overview, epinfo.director, epinfo.gueststars, epinfo.writer, epinfo.rating, epinfo.fullep, epinfo.showtitle, epinfo.tvdbID as ep_tvdbID, g.name as group_name, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID + FROM releases FORCE INDEX (ix_releases_rageID) + left outer join category c on c.ID = releases.categoryID + left outer join category cp on cp.ID = c.parentID + left outer join groups g on g.ID = releases.groupID + left outer join (SELECT ID, releasetitle, rageid FROM tvrage GROUP BY rageid) tvr on tvr.rageID = releases.rageID + left outer join episodeinfo epinfo on epinfo.ID = releases.episodeinfoID + inner join + ( select ID from + ( select id, rageID, categoryID, season, episode from releases where %s order by season desc, episode desc, postdate asc ) releases + group by rageID, season, episode, categoryID + ) z on z.id = releases.ID + where %s %s %s + and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') + order by postdate desc %s" , $usql, $usql, $exccatlist, $airdate, $limit); + return $db->query($sql); + } + + /** + * Get releases in users 'my movies' rss feed + */ + public function getMyMoviesRss($num, $uid=0, $excludedcats=array()) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usermovies = $db->query(sprintf("select imdbID, categoryID from usermovies where userID = %d", $uid), true); + $usql = '(1=2 '; + foreach($usermovies as $umov) + { + $usql .= sprintf('or (releases.imdbID = %d', $umov['imdbID']); + if ($umov['categoryID'] != '') + { + $catsArr = explode('|', $umov['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $sql = sprintf(" SELECT releases.*, mi.title as releasetitle, g.name as group_name, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID + FROM releases + left outer join category c on c.ID = releases.categoryID + left outer join category cp on cp.ID = c.parentID + left outer join groups g on g.ID = releases.groupID + left outer join movieinfo mi on mi.imdbID = releases.imdbID + where %s %s + and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') + order by postdate desc %s" , $usql, $exccatlist, $limit); + return $db->query($sql); + } + + /** + * Get range of releases in users 'my tvshows' + */ + public function getShowsRange($usershows, $start, $num, $orderby, $maxage=-1, $excludedcats=array()) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + + $order = $this->getBrowseOrder($orderby); + $sql = sprintf(" SELECT releases.*, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, pre.ctime, pre.nuketype, rn.ID as nfoID, re.releaseID as reID from releases left outer join releasevideo re on re.releaseID = releases.ID left outer join groups on groups.ID = releases.groupID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category c on c.ID = releases.categoryID left outer join predb pre on pre.ID = releases.preID left outer join category cp on cp.ID = c.parentID where %s %s and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s order by %s %s".$limit, $usql, $exccatlist, $maxagesql, $order[0], $order[1]); + return $db->query($sql, true); + } + + /** + * Get count of releases in users 'my tvshows' for pager + */ + public function getShowsCount($usershows, $maxage=-1, $excludedcats=array()) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + + $res = $db->queryOneRow(sprintf(" SELECT count(releases.ID) as num from releases where %s %s and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s", $usql, $exccatlist, $maxagesql), true); + return $res["num"]; + } + + /** + * Delete one or more releases. + */ + public function delete($id, $isGuid=false) + { + $db = new DB(); + $users = new Users(); + $s = new Sites(); + $nfo = new Nfo(); + $site = $s->get(); + $rf = new ReleaseFiles(); + $re = new ReleaseExtra(); + $rc = new ReleaseComments(); + $ri = new ReleaseImage(); + + if (!is_array($id)) + $id = array($id); + + foreach($id as $identifier) + { + // + // delete from disk. + // + $rel = ($isGuid) ? $this->getByGuid($identifier) : $this->getById($identifier); + + $nzbpath = ""; + if ($isGuid) + $nzbpath = $site->nzbpath.substr($identifier, 0, 1)."/".$identifier.".nzb.gz"; + elseif ($rel) + $nzbpath = $site->nzbpath.substr($rel["guid"], 0, 1)."/".$rel["guid"].".nzb.gz"; + + if ($nzbpath != "" && file_exists($nzbpath)) + unlink($nzbpath); + + $audiopreviewpath = ""; + if ($isGuid) + $audiopreviewpath = WWW_DIR.'covers/audio/'.$identifier.".mp3"; + elseif ($rel) + $audiopreviewpath = WWW_DIR.'covers/audio/'.$rel["guid"].".mp3"; + + if ($audiopreviewpath && file_exists($audiopreviewpath)) + unlink($audiopreviewpath); + + if ($rel) + { + $nfo->deleteReleaseNfo($rel['ID']); + $rc->deleteCommentsForRelease($rel['ID']); + $users->delCartForRelease($rel['ID']); + $users->delDownloadRequestsForRelease($rel['ID']); + $rf->delete($rel['ID']); + $re->delete($rel['ID']); + $re->deleteFull($rel['ID']); + $ri->delete($rel['guid']); + $db->query(sprintf("delete from releases where id = %d", $rel['ID'])); + } + } + } + + /** + * Delete a preview associated with a release and update the release to indicate it doesnt have one. + */ + public function deletePreview($guid) + { + $this->updateHasPreview($guid, 0); + $ri = new ReleaseImage(); + $ri->delete($guid); + } + + /** + * Update a release. + */ + public function update($id, $name, $searchname, $fromname, $category, $parts, $grabs, $size, $posteddate, $addeddate, $rageid, $seriesfull, $season, $episode, $imdbid, $anidbid, $tvdbid, $consoleinfoid) + { + $db = new DB(); + + $db->query(sprintf("update releases set name=%s, searchname=%s, fromname=%s, categoryID=%d, totalpart=%d, grabs=%d, size=%s, postdate=%s, adddate=%s, rageID=%d, seriesfull=%s, season=%s, episode=%s, imdbID=%d, anidbID=%d, tvdbID=%d,consoleinfoID=%d where id = %d", + $db->escapeString($name), $db->escapeString($searchname), $db->escapeString($fromname), $category, $parts, $grabs, $db->escapeString($size), $db->escapeString($posteddate), $db->escapeString($addeddate), $rageid, $db->escapeString($seriesfull), $db->escapeString($season), $db->escapeString($episode), $imdbid, $anidbid, $tvdbid, $consoleinfoid, $id)); + } + + /** + * Update multiple releases. + */ + public function updatemulti($guids, $category, $grabs, $rageid, $season, $imdbid) + { + if (!is_array($guids) || sizeof($guids) < 1) + return false; + + $update = array( + 'categoryID'=>(($category == '-1') ? '' : $category), + 'grabs'=>$grabs, + 'rageID'=>$rageid, + 'season'=>$season, + 'imdbID'=>$imdbid + ); + + $db = new DB(); + $updateSql = array(); + foreach($update as $updk=>$updv) { + if ($updv != '') + $updateSql[] = sprintf($updk.'=%s', $db->escapeString($updv)); + } + + if (sizeof($updateSql) < 1) { + //echo 'no field set to be changed'; + return -1; + } + + $updateGuids = array(); + foreach($guids as $guid) { + $updateGuids[] = $db->escapeString($guid); + } + + $sql = sprintf('update releases set '.implode(', ', $updateSql).' where guid in (%s)', implode(', ', $updateGuids)); + return $db->query($sql); + } + + /** + * Update whether a release has a preview. + */ + public function updateHasPreview($guid, $haspreview) + { + $db = new DB(); + $db->query(sprintf("update releases set haspreview = %d where guid = %s", $haspreview, $db->escapeString($guid))); + } + + /** + * Not yet implemented. + */ + public function searchadv($searchname, $filename, $poster, $group, $cat=array(-1), $sizefrom, $sizeto, $offset=0, $limit=1000, $orderby='', $maxage=-1, $excludedcats=array()) + { + return array(); + } + + /** + * Search for releases. + */ + public function search($search, $cat=array(-1), $offset=0, $limit=1000, $orderby='', $maxage=-1, $excludedcats=array(), $grp=array()) + { + $s = new Sites(); + $site = $s->get(); + + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $order = $this->getBrowseOrder($orderby); + $results = $sphinx->search($search, $cat, $offset, $limit, $order, $maxage, $excludedcats, $grp, array(), true); + if (is_array($results)) + return $results; + } + + // + // Search using MySQL + // + $db = new DB(); + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = " and ("; + foreach ($grp as $grpname) + { + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + } + $grpsql.= "1=2 )"; + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $search); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + if ($orderby == "") + { + $order[0] = " postdate "; + $order[1] = " desc "; + } + else + $order = $this->getBrowseOrder($orderby); + + $sql = sprintf("select releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID, cp.ID as categoryParentID, pre.ctime, pre.nuketype, coalesce(movieinfo.ID,0) as movieinfoID from releases %s left outer join movieinfo on movieinfo.imdbID = releases.imdbID left outer join releasevideo re on re.releaseID = releases.ID left outer join releasenfo rn on rn.releaseID = releases.ID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join predb pre on pre.ID = releases.preID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s order by %s %s limit %d, %d ", $usecatindex, $searchsql, $catsrch, $maxage, $exccatlist, $grpsql, $order[0], $order[1], $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by rage id. Used by API/Sickbeard. + */ + public function searchbyRageId($rageId, $series="", $episode="", $offset=0, $limit=100, $name="", $cat=array(-1), $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchbyRageId($rageId, $series, $episode, $offset, $limit, $name, $cat, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + if ($rageId != "-1") + $rageId = sprintf(" and rageID = %d ", $rageId); + else + $rageId = ""; + + if ($series != "") + { + // + // Exclude four digit series, which will be the year 2010 etc + // + if (is_numeric($series) && strlen($series) != 4) + $series = sprintf('S%02d', $series); + + $series = sprintf(" and releases.season = %s", $db->escapeString($series)); + } + if ($episode != "") + { + if (is_numeric($episode)) + $episode = sprintf('E%02d', $episode); + + $episode = sprintf(" and releases.episode like %s", $db->escapeString('%'.$episode.'%')); + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $sql = sprintf("select releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID from releases %s left outer join category c on c.ID = releases.categoryID left outer join groups on groups.ID = releases.groupID left outer join releasevideo re on re.releaseID = releases.ID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s %s order by postdate desc limit %d, %d ", $usecatindex, $rageId, $series, $episode, $searchsql, $catsrch, $maxage, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by anidb id. Used by API/Sickbeard. + */ + public function searchbyAnidbId($anidbID, $epno='', $offset=0, $limit=100, $name='', $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $order = $this->getBrowseOrder($orderby); + $results = $sphinx->searchbyAnidbId($anidbID, $epno, $offset, $limit, $name, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + $anidbID = ($anidbID > -1) ? sprintf(" AND anidbID = %d ", $anidbID) : ''; + + is_numeric($epno) ? $epno = sprintf(" AND releases.episode LIKE '%s' ", $db->escapeString('%'.$epno.'%')) : ''; + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" AND releases.searchname LIKE '%s' ", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" AND releases.searchname NOT LIKE '%s' ", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" AND releases.searchname LIKE '%s' ", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $maxage = ($maxage > 0) ? sprintf(" and postdate > now() - interval %d day ", $maxage) : ''; + + $sql = sprintf("SELECT releases.*, concat(cp.title, ' > ', c.title) + AS category_name, concat(cp.ID, ',', c.ID) AS category_ids, groups.name AS group_name, rn.ID AS nfoID + FROM releases LEFT OUTER JOIN category c ON c.ID = releases.categoryID LEFT OUTER JOIN groups ON groups.ID = releases.groupID + LEFT OUTER JOIN releasenfo rn ON rn.releaseID = releases.ID and rn.nfo IS NOT NULL LEFT OUTER JOIN category cp ON cp.ID = c.parentID + WHERE releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s ORDER BY postdate desc LIMIT %d, %d ", + $anidbID, $epno, $searchsql, $maxage, $offset, $limit); + $orderpos = strpos($sql, "ORDER BY"); + $wherepos = strpos($sql, "WHERE"); + $sqlcount = "SELECT count(releases.ID) AS num FROM releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by album/artist/musicinfo. Used by API. + */ + public function searchAudio($artist, $album, $label, $track, $year, $genre=array(-1), $offset=0, $limit=100, $cat=array(-1), $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchAudio($artist, $album, $label, $track, $year, $genre, $offset, $limit, $cat, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + $searchsql = ""; + + if ($artist != "") + $searchsql.= sprintf(" and musicinfo.artist like %s ", $db->escapeString("%".$artist."%")); + if ($album != "") + $searchsql.= sprintf(" and musicinfo.title like %s ", $db->escapeString("%".$album."%")); + if ($label != "") + $searchsql.= sprintf(" and musicinfo.publisher like %s ", $db->escapeString("%".$label."%")); + if ($track != "") + $searchsql.= sprintf(" and musicinfo.tracks like %s ", $db->escapeString("%".$track."%")); + if ($year != "") + $searchsql.= sprintf(" and musicinfo.year = %d ", $year); + + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $genresql = ""; + if (count($genre) > 0 && $genre[0] != -1) + { + $genresql = " and ("; + foreach ($genre as $g) + { + $genresql .= sprintf(" musicinfo.genreID = %d or ", $g); + } + $genresql.= "1=2 )"; + } + + $sql = sprintf("select releases.*, musicinfo.cover as mi_cover, musicinfo.review as mi_review, musicinfo.tracks as mi_tracks, musicinfo.publisher as mi_publisher, musicinfo.title as mi_title, musicinfo.artist as mi_artist, genres.title as music_genrename, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases %s left outer join musicinfo on musicinfo.ID = releases.musicinfoID left join genres on genres.ID = musicinfo.genreID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by postdate desc limit %d, %d ", $usecatindex, $searchsql, $catsrch, $maxage, $genresql, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases inner join musicinfo on musicinfo.ID = releases.musicinfoID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by author/bookinfo. Used by API. + */ + public function searchBook($author, $title, $offset=0, $limit=100, $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchBook($author, $title, $offset, $limit, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + $searchsql = ""; + + if ($author != "") + $searchsql.= sprintf(" and bookinfo.author like %s ", $db->escapeString("%".$author."%")); + if ($title != "") + $searchsql.= sprintf(" and bookinfo.title like %s ", $db->escapeString("%".$title."%")); + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $sql = sprintf("select releases.*, bookinfo.cover as bi_cover, bookinfo.review as bi_review, bookinfo.publisher as bi_publisher, bookinfo.pages as bi_pages, bookinfo.publishdate as bi_publishdate, bookinfo.title as bi_title, bookinfo.author as bi_author, genres.title as book_genrename, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases left outer join bookinfo on bookinfo.ID = releases.bookinfoID left join genres on genres.ID = bookinfo.genreID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s order by postdate desc limit %d, %d ", $searchsql, $maxage, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases inner join bookinfo on bookinfo.ID = releases.bookinfoID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by imdbid/movieinfo. Used by API/Couchpotato. + */ + public function searchbyImdbId($imdbId, $offset=0, $limit=100, $name="", $cat=array(-1), $genre="", $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchbyImdbId($imdbId, $offset, $limit, $name, $cat, $genre, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + if ($imdbId != "-1" && is_numeric($imdbId)) + { + //pad id with zeros just in case + $imdbId = str_pad($imdbId, 7, "0",STR_PAD_LEFT); + $imdbId = sprintf(" and releases.imdbID = %d ", $imdbId); + } + else + { + $imdbId = ""; + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + + if ($maxage > 0) + $maxage = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + if ($genre != "") + { + $genre = sprintf(" and movieinfo.genre like %s", $db->escapeString("%".$genre."%")); + } + + $sql = sprintf("select releases.*, movieinfo.title as moi_title, movieinfo.tagline as moi_tagline, movieinfo.rating as moi_rating, movieinfo.plot as moi_plot, movieinfo.year as moi_year, movieinfo.genre as moi_genre, movieinfo.director as moi_director, movieinfo.actors as moi_actors, movieinfo.cover as moi_cover, movieinfo.backdrop as moi_backdrop, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID left outer join movieinfo on releases.imdbID = movieinfo.imdbID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s order by postdate desc limit %d, %d ", $searchsql, $imdbId, $catsrch, $maxage, $genre, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases left outer join movieinfo on releases.imdbID = movieinfo.imdbID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Return a list of releases with a similar name to that provided. + */ + public function searchSimilar($currentid, $name, $limit=6, $excludedcats=array()) + { + $name = $this->getSimilarName($name); + $results = $this->search($name, array(-1), 0, $limit, '', -1, $excludedcats); + if (!$results) + return $results; + + // + // Get the category for the parent of this release + // + $currRow = $this->getById($currentid); + $cat = new Category(); + $catrow = $cat->getById($currRow["categoryID"]); + $parentCat = $catrow["parentID"]; + + $ret = array(); + foreach ($results as $res) + if ($res["ID"] != $currentid && $res["categoryParentID"] == $parentCat) + $ret[] = $res; + + return $ret; + } + + /** + * Return a similar release name. + */ + public function getSimilarName($name) + { + $words = str_word_count(str_replace(array(".","_"), " ", $name), 2); + $firstwords = array_slice($words, 0, 2); + return implode(' ', $firstwords); + } + + /** + * Retrieve one or more releases by guid. + */ + public function getByGuid($guid) + { + $db = new DB(); + if (is_array($guid)) + { + $tmpguids = array(); + foreach($guid as $g) + $tmpguids[] = $db->escapeString($g); + $gsql = sprintf('guid in (%s)', implode(',',$tmpguids)); + } else { + $gsql = sprintf('guid = %s', $db->escapeString($guid)); + } + $sql = sprintf("select releases.*, musicinfo.cover as mi_cover, musicinfo.review as mi_review, musicinfo.tracks as mi_tracks, musicinfo.publisher as mi_publisher, musicinfo.title as mi_title, musicinfo.artist as mi_artist, music_genre.title as music_genrename, bookinfo.cover as bi_cover, bookinfo.review as bi_review, bookinfo.publisher as bi_publisher, bookinfo.publishdate as bi_publishdate, bookinfo.title as bi_title, bookinfo.author as bi_author, bookinfo.pages as bi_pages, bookinfo.isbn as bi_isbn, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name from releases left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join musicinfo on musicinfo.ID = releases.musicinfoID left outer join bookinfo on bookinfo.ID = releases.bookinfoID left join genres music_genre on music_genre.ID = musicinfo.genreID where %s ", $gsql); + return (is_array($guid)) ? $db->query($sql) : $db->queryOneRow($sql); + } + + /** + * Writes a zip file of an array of release guids directly to the stream + */ + public function getZipped($guids) + { + $s = new Sites(); + $nzb = new NZB; + $site = $s->get(); + $zipfile = new zipfile(); + + foreach ($guids as $guid) + { + $nzbpath = $nzb->getNZBPath($guid, $site->nzbpath); + + if (file_exists($nzbpath)) + { + ob_start(); + @readgzfile($nzbpath); + $nzbfile = ob_get_contents(); + ob_end_clean(); + + $filename = $guid; + $r = $this->getByGuid($guid); + if ($r) + $filename = $r["searchname"]; + + $zipfile->addFile($nzbfile, $filename.".nzb"); + } + } + + return $zipfile->file(); + } + + /** + * Removes an associated tvrage id from all releases using it. + */ + public function removeRageIdFromReleases($rageid) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("select count(ID) as num from releases where rageID = %d", $rageid)); + $ret = $res["num"]; + $res = $db->query(sprintf("update releases set rageID = -1, seriesfull = null, season = null, episode = null where rageID = %d", $rageid)); + return $ret; + } + + /** + * Removes an associated tvdb id from all releases using it. + */ + public function removeThetvdbIdFromReleases($tvdbID) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM releases WHERE tvdbID = %d", $tvdbID)); + $ret = $res["num"]; + $res = $db->query(sprintf("UPDATE releases SET tvdbID = -1 where tvdbID = %d", $tvdbID)); + return $ret; + } + + public function removeAnidbIdFromReleases($anidbID) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM releases WHERE anidbID = %d", $anidbID)); + $ret = $res["num"]; + $res = $db->query(sprintf("UPDATE releases SET anidbID = -1, episode = null, tvtitle = null, tvairdate = null where anidbID = %d", $anidbID)); + return $ret; + } + + public function getById($id) + { + $db = new DB(); + return $db->queryOneRow(sprintf("select releases.*, groups.name as group_name from releases left outer join groups on groups.ID = releases.groupID where releases.ID = %d ", $id)); + } + + public function getReleaseNfo($id, $incnfo=true) + { + $db = new DB(); + $selnfo = ($incnfo) ? ', uncompress(nfo) as nfo' : ''; + return $db->queryOneRow(sprintf("SELECT ID, releaseID".$selnfo." FROM releasenfo where releaseID = %d AND nfo IS NOT NULL", $id)); + } + + public function updateGrab($guid) + { + $db = new DB(); + $db->queryOneRow(sprintf("update releases set grabs = grabs + 1 where guid = %s", $db->escapeString($guid))); + } + + function processReleases() + { + require_once(WWW_DIR."/lib/binaries.php"); + + $db = new DB; + + // + // Get the current datetime again, as using now() in the housekeeping queries prevents the index being used. + // + $currTime_ori = $db->queryOneRow("SELECT NOW() as now"); + + $cat = new Category; + $bin = new Binaries; + $nzb = new Nzb; + $s = new Sites; + $relreg = new ReleaseRegex; + $page = new Page; + $nfo = new Nfo; + $retcount = 0; + + echo $s->getLicense(); + + echo "\n\nStarting release update process (".date("Y-m-d H:i:s").")\n"; + + if (!file_exists($page->site->nzbpath)) + { + echo "Bad or missing nzb directory - ".$page->site->nzbpath; + return; + } + + $this->checkRegexesUptoDate($page->site->latestregexurl, $page->site->latestregexrevision, $page->site->newznabID); + + // + // Get all regexes for all groups which are to be applied to new binaries + // in order of how they should be applied + // + $regexrows = $relreg->get(); + echo "Stage 1 : Applying regex to binaries\n"; + foreach ($regexrows as $regexrow) + { + $groupmatch = ""; + + // + // Groups ending in * need to be like matched when getting out binaries for groups and children + // + if (preg_match("/\*$/i", $regexrow["groupname"])) + { + $groupname = substr($regexrow["groupname"], 0, -1); + $resgrps = $db->query(sprintf("select ID from groups where name like %s ", $db->escapeString($groupname."%"))); + foreach ($resgrps as $resgrp) + $groupmatch.=" groupID = ".$resgrp["ID"]." or "; + + $groupmatch.=" 1=2 "; + } + // + // A group name which doesnt end in a * needs an exact match + // + elseif ($regexrow["groupname"] != "") + { + $resgrp = $db->queryOneRow(sprintf("select ID from groups where name = %s ", $db->escapeString($regexrow["groupname"]))); + + // + // if group not found, its a regex for a group we arent indexing. + // + if ($resgrp) + $groupmatch = " groupID = ".$resgrp["ID"]; + else + $groupmatch = " 1=2 " ; + } + // + // No groupname specified (these must be the misc regexes applied to all groups) + // + else + $groupmatch = " 1=1 "; + + // Get out all binaries of STAGE0 for current group + $arrNoPartBinaries = array(); + $resbin = $db->queryDirect(sprintf("SELECT binaries.ID, binaries.name, binaries.date, binaries.totalParts from binaries where (%s) and procstat = %d order by binaries.date asc", $groupmatch, Releases::PROCSTAT_NEW)); + + while ($rowbin = mysql_fetch_assoc($resbin)) + { + if (preg_match ($regexrow["regex"], $rowbin["name"], $matches)) + { + $matches = array_map("trim", $matches); + + if ((isset($matches['reqid']) && ctype_digit($matches['reqid'])) && (!isset($matches['name']) || empty($matches['name']))) { + $matches['name'] = $matches['reqid']; + } + + // Check that the regex provided the correct parameters + if (!isset($matches['name']) || empty($matches['name'])) + { + continue; + } + + // If theres no number of files data in the subject, put it into a release if it was posted to usenet longer than five hours ago. + if ((!isset($matches['parts']) && strtotime($currTime_ori['now']) - strtotime($rowbin['date']) > 18000) || isset($arrNoPartBinaries[$matches['name']])) + { + // + // Take a copy of the name of this no-part release found. This can be used + // next time round the loop to find parts of this set, but which have not yet reached 3 hours. + // + $arrNoPartBinaries[$matches['name']] = "1"; + $matches['parts'] = "01/01"; + } + + + if (isset($matches['name']) && isset($matches['parts'])) + { + if (strpos($matches['parts'], '/') === false) + { + $matches['parts'] = str_replace(array('-','~',' of '), '/', $matches['parts']); + } + + $regcatid = "null "; + if ($regexrow["categoryID"] != "") + $regcatid = $regexrow["categoryID"]; + //override if regex specifies pc oday but content is some other form of PC or Ebook + if ($regcatid == Category::CAT_PC_0DAY) + { + if ($cat->isMobileAndroid($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEANDROID; + if ($cat->isMobileiOS($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEIOS; + if ($cat->isMobileOther($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEOTHER; + if ($cat->isIso($matches['name'])) + $regcatid = Category::CAT_PC_ISO; + if ($cat->isMac($matches['name'])) + $regcatid = Category::CAT_PC_MAC; + if ($cat->isPcGame($matches['name'])) + $regcatid = Category::CAT_PC_GAMES; + if ($cat->isEBook($matches['name'])) + $regcatid = Category::CAT_MISC_EBOOK; + } + + $reqid = " null "; + if (isset($matches['reqid'])) + $reqid = $matches['reqid']; + + //check if post is repost + if (preg_match('/(repost\d?|re\-?up)/i', $rowbin['name'], $repost) && !preg_match('/repost|re\-?up/i', $matches['name'])) { + $matches['name'] .= ' '.$repost[1]; + } + + $relparts = explode("/", $matches['parts']); + $db->query(sprintf("update binaries set relname = replace(%s, '_', ' '), relpart = %d, reltotalpart = %d, procstat=%d, categoryID=%s, regexID=%d, reqID=%s where ID = %d", + $db->escapeString($matches['name']), $relparts[0], (isset($relparts[1]) ? $relparts[1] : $relparts[0]), Releases::PROCSTAT_TITLEMATCHED, $regcatid, $regexrow["ID"], $reqid, $rowbin["ID"] )); + } + } + } + + } + + // + // Move all binaries from releases which have the correct number of files on to the next stage. + // + echo "Stage 2 : Marking binaries where all parts are available\n"; + $result = $db->queryDirect(sprintf("SELECT relname, SUM(reltotalpart) AS reltotalpart, groupID, reqID, fromname, SUM(num) AS num, coalesce(g.minfilestoformrelease, s.minfilestoformrelease) as minfilestoformrelease FROM ( SELECT relname, reltotalpart, groupID, reqID, fromname, COUNT(ID) AS num FROM binaries WHERE procstat = %s GROUP BY relname, reltotalpart, groupID, reqID, fromname ORDER BY NULL ) x left outer join groups g on g.ID = x.groupID inner join ( select value as minfilestoformrelease from site where setting = 'minfilestoformrelease' ) s GROUP BY relname, groupID, reqID, fromname, minfilestoformrelease ORDER BY NULL", Releases::PROCSTAT_TITLEMATCHED)); + while ($row = mysql_fetch_assoc($result)) + { + $retcount ++; + + // + // Less than the site permitted number of files in a release. Dont discard it, as it may + // be part of a set being uploaded. + // + if ($row["num"] < $row["minfilestoformrelease"]) + { + //echo "Number of files in release ".$row["relname"]." less than site/group setting (".$row['num']."/".$row["minfilestoformrelease"].")\n"; + + $db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname = %s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + + // + // There are the same or more files in our release than the number of files specified + // in the message subject so go ahead and make a release + // + elseif ($row["num"] >= $row["reltotalpart"]) + { + + // Check that the binary is complete + $binlist = $db->query(sprintf("SELECT binaries.ID, totalParts, date, COUNT(DISTINCT parts.messageID) AS num FROM binaries, parts WHERE binaries.ID=parts.binaryID AND binaries.relname = %s AND binaries.procstat = %d AND binaries.groupID = %d AND binaries.fromname = %s GROUP BY binaries.ID ORDER BY NULL", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + + $incomplete = false; + foreach ($binlist as $rowbin) + { + if ($rowbin['num'] < $rowbin['totalParts']) + { + // Allow to binary to release if posted to usenet longer than four hours ago and we still don't have all the parts + if (!(strtotime($currTime_ori['now']) - strtotime($rowbin['date']) > 14400)) + { + $incomplete = true; + break; + } + } + } + + if ($incomplete) + { + //$db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname = %s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + + // + // Right number of files, but see if the binary is a allfilled/reqid post, in which case it needs its name looked up + // + elseif ($row['reqID'] !='' && $page->site->reqidurl != "") + { + // + // Try and get the name using the group + // + $binGroup = $db->queryOneRow(sprintf("SELECT name FROM groups WHERE ID = %d", $row["groupID"])); + $newtitle = $this->getReleaseNameForReqId($page->site->reqidurl, $page->site->newznabID, $binGroup["name"], $row["reqID"]); + + // + // if the feed/group wasnt supported by the scraper, then just use the release name as the title. + // + if ($newtitle == "no feed") + { + $newtitle = $row["relname"]; + echo "Group not supported\n"; + } + + // + // Valid release with right number of files and title now, so move it on + // + if ($newtitle != "") + { + $db->query(sprintf("update binaries set relname = %s, procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($newtitle), Releases::PROCSTAT_READYTORELEASE, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + else + { + // + // Item not found, if the binary was added to the index yages ago, then give up. + // + $maxaddeddate = $db->queryOneRow(sprintf("SELECT NOW() as now, MAX(dateadded) as dateadded FROM binaries WHERE relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + + // + // If added to the index over 48 hours ago, give up trying to determine the title + // + if (strtotime($maxaddeddate['now']) - strtotime($maxaddeddate['dateadded']) > (60*60*48)) + { + $db->query(sprintf("update binaries set procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_NOREQIDNAMELOOKUPFOUND, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + } + } + else + { + $db->query(sprintf("update binaries set procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_READYTORELEASE, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + } + + // + // Theres less than the expected number of files, so update the attempts and move on. + // + else + { + //echo "Incorrect number of files for ".$row["relname"]." (".$row["num"]."/".$row["reltotalpart"].")\n"; + + $db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname=%s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + if ($retcount % 100 == 0) + echo "Stage 2 : Processed ".$retcount." binaries\n"; + } + $retcount=$nfocount=0; + + echo "Stage 3 : Creating releases from complete binaries\n"; + // + // Get out all distinct relname, group from binaries of STAGE2 + // + $result = $db->queryDirect(sprintf("SELECT relname, groupID, g.name as group_name, fromname, count(binaries.ID) as parts from binaries inner join groups g on g.ID = binaries.groupID where procstat = %d and relname is not null group by relname, g.name, groupID, fromname ORDER BY COUNT(binaries.ID) desc", Releases::PROCSTAT_READYTORELEASE)); + while ($row = mysql_fetch_assoc($result)) + { + // + // Get the last post date and the poster name from the binary + // + $bindata = $db->queryOneRow(sprintf("select fromname, MAX(date) as date from binaries where relname = %s and procstat = %d and groupID = %d and fromname = %s group by fromname order by null", + $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]) )); + + // + // Get all releases with the same name with a usenet posted date in a +1-1 date range. + // + $relDupes = $db->query(sprintf("select ID from releases where searchname = %s and ( date_sub(%s, interval 1 day) < postdate AND date_add(%s, interval 1 day) > postdate )", + $db->escapeString($this->cleanReleaseName($row["relname"])), $db->escapeString($bindata["date"]), $db->escapeString($bindata["date"]))); + if (count($relDupes) > 0) + { + $db->query(sprintf("update binaries set procstat = %d where relname = %s and procstat = %d and groupID = %d and fromname=%s ", + Releases::PROCSTAT_DUPLICATE, $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]))); + continue; + } + + // + // Get some attribs of this release + // + $regexAppliedCategoryID = ""; + $regexIDused = ""; + $reqIDused = ""; + $binSizeId = $db->queryOneRow(sprintf("select ID, categoryID, regexID, reqID, totalParts from binaries use index (ix_binary_relname) where relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]) )); + if ($binSizeId) + { + // + // Get categoryID if one has been allocated to this + // + if ($binSizeId["categoryID"] != "") + $regexAppliedCategoryID = $binSizeId["categoryID"]; + // + // Get RegexID if one has been allocated to this + // + if ($binSizeId["regexID"] != "") + $regexIDused = $binSizeId["regexID"]; + // + // Get requestID if one has been allocated to this + // + if ($binSizeId["reqID"] != "") + $reqIDused = $binSizeId["reqID"]; + + } + + // + // Insert the release + // + $relguid = md5(uniqid()); + if ($regexAppliedCategoryID == "") + $catId = $cat->determineCategory($row["group_name"], $row["relname"]); + else + $catId = $regexAppliedCategoryID; + + if ($regexIDused == "") + $regexID = " null "; + else + $regexID = $regexIDused; + + if ($reqIDused == "") + $reqID = " null "; + else + $reqID = $reqIDused; + + //Clean release name + $cleanRelName = $this->cleanReleaseName($row['relname']); + + $relid = $db->queryInsert(sprintf("insert into releases (name, searchname, totalpart, groupID, adddate, guid, categoryID, regexID, rageID, postdate, fromname, size, reqID, passwordstatus, completion, haspreview) values (%s, %s, %d, %d, now(), %s, %d, %d, -1, %s, %s, 0, %s, %d, 100, %d)", + $db->escapeString($cleanRelName), $db->escapeString($cleanRelName), $row["parts"], $row["groupID"], $db->escapeString($relguid), $catId, $regexID, $db->escapeString($bindata["date"]), $db->escapeString($bindata["fromname"]), $reqID, ($page->site->checkpasswordedrar > 0 ? -1 : 0), -1)); + echo "Stage 3 : Added release ".$cleanRelName."\n"; + + // + // Tag every binary for this release with its parent release id + // remove the release name from the binary as its no longer required + // + $db->query(sprintf("update binaries set procstat = %d, releaseID = %d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_RELEASED, $relid, $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]))); + + // + // Find an .nfo in the release + // + $relnfo = $nfo->determineReleaseNfo($relid); + if ($relnfo !== false) + { + $nfo->addReleaseNfo($relid, $relnfo['ID']); + $nfocount++; + } + + // + // Write the nzb to disk + // + $nzbfile = $nzb->getNZBPath($relguid, $page->site->nzbpath, true); + $nzb->writeNZBforReleaseId($relid, $relguid, $cleanRelName, $catId, $nzbfile); + + $nzbInfo = new nzbInfo; + + // + // If nzb successfully written, then load it and get size completion from it + // + if (!$nzbInfo->loadFromFile($nzbfile)) + { + echo "Stage 5 : Failed to write nzb file (bad perms?) ".$nzbfile."\n"; + + // + // Remove the release and remark the binaries for processing again. + // + $this->delete($relid); + } + else + { + $db->query(sprintf("update releases set totalpart = %d, size = %s, completion = %d where ID = %d", $nzbInfo->filecount, $nzbInfo->filesize, $nzbInfo->completion, $relid )); + + //Increment new release count + $retcount ++; + } + } + + echo "Stage 4 : Finished creating ".$retcount." releases. Found ".$nfocount." nfos\n"; + + // + // Delete any releases under the minimum completion percent. + // + if($page->site->completionpercent != 0) + { + echo "Stage 5 : Deleting releases less than ".$page->site->completionpercent." complete\n"; + $result = $db->query(sprintf("select ID from releases where completion > 0 and completion < %d", $page->site->completionpercent)); + foreach ($result as $row) + $this->delete($row["ID"]); + } + + // + // Delete releases whos minsize is less than the site or group minimum + // + $result = $db->query("select releases.ID from releases left outer join (SELECT g.ID, coalesce(g.minsizetoformrelease, s.minsizetoformrelease) as minsizetoformrelease FROM groups g inner join ( select value as minsizetoformrelease from site where setting = 'minsizetoformrelease' ) s ) x on x.ID = releases.groupID where minsizetoformrelease != 0 and releases.size < minsizetoformrelease"); + if (count($result) > 0) + { + echo "Stage 5 : Deleting ".count($result)." release(s) where size is smaller than minsize for site/group\n"; + foreach ($result as $row) + $this->delete($row["ID"]); + } + + echo "Stage 5 : Post processing started\n"; + $postprocess = new PostProcess(true); + $postprocess->processAll(); + + // + // aggregate the releasefiles upto the releases. + // + echo "Stage 6 : Aggregating Files\n"; + $db->query("UPDATE releases INNER JOIN (SELECT releaseID, COUNT(ID) AS num FROM releasefiles GROUP BY releaseID) b ON b.releaseID = releases.ID and releases.rarinnerfilecount = 0 SET rarinnerfilecount = b.num"); + + + // + // Remove the binaries and parts used to form releases, or that are duplicates. + // + echo "Stage 7 : Deleting unused binaries and parts\n"; + $db->query(sprintf("DELETE parts, binaries FROM parts LEFT JOIN binaries ON binaries.ID = parts.binaryID LEFT JOIN releasenfo rn ON + rn.binaryID = binaries.ID WHERE (binaries.procstat IN (%d, %d) AND + (rn.binaryid IS NULL OR (rn.binaryid IS NOT NULL AND rn.nfo IS NOT NULL))) OR + binaries.dateadded < %s - INTERVAL %d HOUR", Releases::PROCSTAT_RELEASED, Releases::PROCSTAT_DUPLICATE, $db->escapeString($currTime_ori["now"]), ceil($page->site->rawretentiondays*24))); + + + // + // User/Request housekeeping, should ideally move this to its own section, but it needs to be done automatically. + // + $users = new Users; + $users->pruneRequestHistory($page->site->userdownloadpurgedays); + + echo "Done : Added ". $retcount." releases\n\n"; + + return $retcount; + } + + public function cleanReleaseName($relname) + { + $cleanArr = array('#', '@', '$', '%', '^', '§', '¨', '©', 'Ö'); + + $relname = str_replace($cleanArr, '', $relname); + $relname = str_replace('_', ' ', $relname); + + return $relname; + } + + public function getReleaseNameForReqId($url, $nnid, $groupname, $reqid) + { + $url = str_ireplace("[GROUP]", urlencode($groupname), $url); + $url = str_ireplace("[REQID]", urlencode($reqid), $url); + + if ($nnid != "") + $nnid = "&newznabID=".$nnid; + + $xml = ""; + $arrXml = ""; + $xml = getUrl($url); + + if ($xml === false || preg_match('/no feed/i', $xml)) + return "no feed"; + else + { + if ($xml != "") + { + $xmlObj = @simplexml_load_string($xml); + $arrXml = objectsIntoArray($xmlObj); + + if (isset($arrXml["item"]) && is_array($arrXml["item"]) && is_array($arrXml["item"]["@attributes"])) + { + return $arrXml["item"]["@attributes"]["title"]; + } + } + } + return ""; + } + + public function checkRegexesUptoDate($url, $rev, $nnid) + { + if ($url != "") + { + if ($nnid != "") + $nnid = "?newznabID=".$nnid; + + $regfile = getUrl($url.$nnid); + if ($regfile !== false && $regfile != "") + { + /*$Rev: 728 $*/ + if (preg_match('/\/\*\$Rev: (\d{3,4})/i', $regfile, $matches)) + { + $serverrev = intval($matches[1]); + if ($serverrev > $rev) + { + $db = new DB(); + $site = new Sites; + + $queries = explode(";", $regfile); + $queries = array_map("trim", $queries); + foreach($queries as $q) { + if ( $q ) { + $db->query($q); + } + } + + $site->updateLatestRegexRevision($serverrev); + echo "Updated regexes to revision ".$serverrev."\n"; + } + else + { + echo "Using latest regex revision ".$rev."\n"; + } + } + else + { + echo "Error Processing Regex File\n"; + } + } + else + { + echo "Error Regex File Does Not Exist or Unable to Connect\n"; + } + } + } + + public function getTopDownloads() + { + $db = new DB(); + return $db->query("SELECT ID, searchname, guid, adddate, grabs FROM releases + where grabs > 0 + ORDER BY grabs DESC + LIMIT 10"); + } + + public function getTopComments() + { + $db = new DB(); + return $db->query("SELECT ID, guid, searchname, adddate, comments FROM releases + where comments > 0 + ORDER BY comments DESC + LIMIT 10"); + } + + public function getRecentlyAdded() + { + $db = new DB(); + return $db->query("SELECT concat(cp.title, ' > ', category.title) as title, COUNT(*) AS count +FROM category +left outer join category cp on cp.ID = category.parentID +INNER JOIN releases ON releases.categoryID = category.ID +WHERE releases.adddate > NOW() - INTERVAL 1 WEEK +GROUP BY concat(cp.title, ' > ', category.title) +ORDER BY COUNT(*) DESC"); + } + +} + diff --git a/bin/lib/releases3.php b/bin/lib/releases3.php new file mode 100755 index 000000000..602f4831c --- /dev/null +++ b/bin/lib/releases3.php @@ -0,0 +1,2016 @@ + 0) + { + $n = array(); + foreach($names as $nm) + $n[] = " searchname = ". $db->escapeString($nm); + + $nsql = "( ".implode(' or ', $n)." )"; + } + + $sql = sprintf(" SELECT releases.*, CONCAT(cp.title, ' > ', c.title) AS category_name, + m.ID AS movie_id, m.title, m.rating, m.cover, m.plot, m.year, m.genre, m.director, m.actors, m.tagline, + mu.ID AS music_id, mu.title as mu_title, mu.cover as mu_cover, mu.year as mu_year, mu.artist as mu_artist, mu.tracks as mu_tracks, mu.review as mu_review, + ep.ID AS ep_id, ep.showtitle as ep_showtitle, ep.airdate as ep_airdate, ep.fullep as ep_fullep, ep.overview as ep_overview, + tvrage.imgdata as rage_imgdata, tvrage.ID as rg_ID + FROM releases + LEFT OUTER JOIN category c ON c.ID = releases.categoryID + LEFT OUTER JOIN category cp ON cp.ID = c.parentID + LEFT OUTER JOIN movieinfo m ON m.imdbID = releases.imdbID + LEFT OUTER JOIN musicinfo mu ON mu.ID = releases.musicinfoID + LEFT OUTER JOIN episodeinfo ep ON ep.ID = releases.episodeinfoID + LEFT OUTER JOIN tvrage ON tvrage.rageID = releases.rageID + where %s", $nsql); + + return $db->queryDirect($sql); + } + + /** + * Get a count of releases for pager. used in admin manage list + */ + public function getCount() + { + $db = new DB(); + $res = $db->queryOneRow("select count(ID) as num from releases"); + return $res["num"]; + } + + /** + * Get a range of releases. used in admin manage list + */ + public function getRange($start, $num) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + return $db->query(" SELECT releases.*, concat(cp.title, ' > ', c.title) as category_name from releases left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID order by postdate desc".$limit); + } + + /** + * Get a count of releases for main browse pager. + */ + public function getBrowseCount($cat, $maxage=-1, $excludedcats=array(), $grp=array()) + { + $db = new DB(); + + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = " and ("; + foreach ($grp as $grpname) + { + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + } + $grpsql.= "1=2 )"; + } + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and categoryID not in (".implode(",", $excludedcats).")"; + + $sql = sprintf("select count(releases.ID) as num from releases left outer join groups on groups.ID = releases.groupID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s", $catsrch, $maxage, $exccatlist, $grpsql); + $res = $db->queryOneRow($sql, true); + return $res["num"]; + } + + /** + * Get a releases for main browse pages. + */ + public function getBrowseRange($cat, $start, $num, $orderby, $maxage=-1, $excludedcats=array(), $grp=array()) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + $usecatindex = ""; + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and postdate > now() - interval %d day ", $maxage); + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = "select ID from groups where ("; + foreach ($grp as $grpname) + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + + $grpsql.= "1=2 )"; + + $grpres = $db->query($grpsql); + if (count($grpsql) > 0) + { + $grpsql = " and ( "; + foreach ($grpres as $grpresrow) + $grpsql.= sprintf(" groups.ID = %d or ", $grpresrow["ID"]); + + $grpsql = substr($grpsql, 0, strlen($grpsql) - 3)." ) "; + } + else + $grpsql = ""; + } + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $order = $this->getBrowseOrder($orderby); + $sql = sprintf(" SELECT releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID, pre.ctime, pre.nuketype, coalesce(movieinfo.ID,0) as movieinfoID from releases %s left outer join groups on groups.ID = releases.groupID left outer join movieinfo on movieinfo.imdbID = releases.imdbID left outer join releaseaudio re on re.releaseID = releases.ID and re.audioID = 1 left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join predb pre on pre.ID = releases.preID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by %s %s".$limit, $usecatindex, $catsrch, $maxagesql, $exccatlist, $grpsql, $order[0], $order[1]); + return $db->query($sql, true); + } + + /** + * Get a column names browse list to be ordered by + */ + public function getBrowseOrder($orderby) + { + $order = ($orderby == '') ? 'posted_desc' : $orderby; + $orderArr = explode("_", $order); + switch($orderArr[0]) { + case 'cat': + $orderfield = 'categoryID'; + break; + case 'name': + $orderfield = 'searchname'; + break; + case 'size': + $orderfield = 'size'; + break; + case 'files': + $orderfield = 'totalpart'; + break; + case 'stats': + $orderfield = 'grabs'; + break; + case 'posted': + default: + $orderfield = 'postdate'; + break; + } + $ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc'; + return array($orderfield, $ordersort); + } + + /** + * Get a list of available columns for sorting browse list + */ + public function getBrowseOrdering() + { + return array('name_asc', 'name_desc', 'cat_asc', 'cat_desc', 'posted_asc', 'posted_desc', 'size_asc', 'size_desc', 'files_asc', 'files_desc', 'stats_asc', 'stats_desc'); + } + + /** + * Get a range of releases. Used in nzb export + */ + public function getForExport($postfrom, $postto, $group, $cat) + { + $db = new DB(); + if ($postfrom != "") + { + $dateparts = explode("/", $postfrom); + if (count($dateparts) == 3) + $postfrom = sprintf(" and postdate > %s ", $db->escapeString($dateparts[2]."-".$dateparts[1]."-".$dateparts[0]." 00:00:00")); + else + $postfrom = ""; + } + + if ($postto != "") + { + $dateparts = explode("/", $postto); + if (count($dateparts) == 3) + $postto = sprintf(" and postdate < %s ", $db->escapeString($dateparts[2]."-".$dateparts[1]."-".$dateparts[0]." 23:59:59")); + else + $postto = ""; + } + + if ($group != "" && $group != "-1") + $group = sprintf(" and groupID = %d ", $group); + else + $group = ""; + + if ($cat != "" && $cat != "-1") + $cat = sprintf(" and categoryID = %d ", $cat); + else + $cat = ""; + + return $db->queryDirect(sprintf("SELECT searchname, guid, CONCAT(cp.title,'_',category.title) as catName FROM releases INNER JOIN category ON releases.categoryID = category.ID LEFT OUTER JOIN category cp ON cp.ID = category.parentID where 1 = 1 %s %s %s %s", $postfrom, $postto, $group, $cat)); + } + + /** + * Get the earliest release + */ + public function getEarliestUsenetPostDate() + { + $db = new DB(); + $row = $db->queryOneRow("SELECT DATE_FORMAT(min(postdate), '%d/%m/%Y') as postdate from releases"); + return $row["postdate"]; + } + + /** + * Get the most recent release + */ + public function getLatestUsenetPostDate() + { + $db = new DB(); + $row = $db->queryOneRow("SELECT DATE_FORMAT(max(postdate), '%d/%m/%Y') as postdate from releases"); + return $row["postdate"]; + } + + /** + * Get all groups for which there is a release for a html select + */ + public function getReleasedGroupsForSelect($blnIncludeAll = true) + { + $db = new DB(); + $groups = $db->query("select distinct groups.ID, groups.name from releases inner join groups on groups.ID = releases.groupID"); + $temp_array = array(); + + if ($blnIncludeAll) + $temp_array[-1] = "--All Groups--"; + + foreach($groups as $group) + $temp_array[$group["ID"]] = $group["name"]; + + return $temp_array; + } + + /** + * Get releases for all types of rss feeds + */ + public function getRss($cat, $num, $uid=0, $rageid, $anidbid, $airdate=-1) + { + $db = new DB(); + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $catsrch = ""; + $cartsrch = ""; + + $catsrch = ""; + if (count($cat) > 0) + { + if ($cat[0] == -2) + { + $cartsrch = sprintf(" inner join usercart on usercart.userID = %d and usercart.releaseID = releases.ID ", $uid); + } + elseif ($cat[0] == -1) + { + } + else + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + } + + $rage = ($rageid > -1) ? sprintf(" and releases.rageID = %d ", $rageid) : ''; + $anidb = ($anidbid > -1) ? sprintf(" and releases.anidbID = %d ", $anidbid) : ''; + $airdate = ($airdate > -1) ? sprintf(" and releases.tvairdate >= DATE_SUB(CURDATE(), INTERVAL %d DAY) ", $airdate) : ''; + + $sql = sprintf(" SELECT releases.*, rn.ID as nfoID, m.title as imdbtitle, m.cover, m.imdbID, m.rating, m.plot, m.year, m.genre, m.director, m.actors, g.name as group_name, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID, mu.title as mu_title, mu.url as mu_url, mu.artist as mu_artist, mu.publisher as mu_publisher, mu.releasedate as mu_releasedate, mu.review as mu_review, mu.tracks as mu_tracks, mu.cover as mu_cover, mug.title as mu_genre, co.title as co_title, co.url as co_url, co.publisher as co_publisher, co.releasedate as co_releasedate, co.review as co_review, co.cover as co_cover, cog.title as co_genre, bo.title as bo_title, bo.url as bo_url, bo.publisher as bo_publisher, bo.author as bo_author, bo.publishdate as bo_publishdate, bo.review as bo_review, bo.cover as bo_cover from releases left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join groups g on g.ID = releases.groupID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join movieinfo m on m.imdbID = releases.imdbID and m.title != '' left outer join musicinfo mu on mu.ID = releases.musicinfoID left outer join genres mug on mug.ID = mu.genreID left outer join bookinfo bo on bo.ID = releases.bookinfoID left outer join consoleinfo co on co.ID = releases.consoleinfoID left outer join genres cog on cog.ID = co.genreID %s where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by postdate desc %s" ,$cartsrch, $catsrch, $rage, $anidb, $airdate, $limit); + return $db->query($sql, true); + } + + /** + * Get releases in users 'my tv show' rss feed + */ + public function getShowsRss($num, $uid=0, $excludedcats=array(), $airdate=-1) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usershows = $db->query(sprintf("select rageID, categoryID from userseries where userID = %d", $uid), true); + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $airdate = ($airdate > -1) ? sprintf(" and releases.tvairdate >= DATE_SUB(CURDATE(), INTERVAL %d DAY) ", $airdate) : ''; + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $sql = sprintf(" SELECT releases.*, tvr.rageID, tvr.releasetitle, epinfo.overview, epinfo.director, epinfo.gueststars, epinfo.writer, epinfo.rating, epinfo.fullep, epinfo.showtitle, epinfo.tvdbID as ep_tvdbID, g.name as group_name, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID + FROM releases FORCE INDEX (ix_releases_rageID) + left outer join category c on c.ID = releases.categoryID + left outer join category cp on cp.ID = c.parentID + left outer join groups g on g.ID = releases.groupID + left outer join (SELECT ID, releasetitle, rageid FROM tvrage GROUP BY rageid) tvr on tvr.rageID = releases.rageID + left outer join episodeinfo epinfo on epinfo.ID = releases.episodeinfoID + inner join + ( select ID from + ( select id, rageID, categoryID, season, episode from releases where %s order by season desc, episode desc, postdate asc ) releases + group by rageID, season, episode, categoryID + ) z on z.id = releases.ID + where %s %s %s + and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') + order by postdate desc %s" , $usql, $usql, $exccatlist, $airdate, $limit); + return $db->query($sql); + } + + /** + * Get releases in users 'my movies' rss feed + */ + public function getMyMoviesRss($num, $uid=0, $excludedcats=array()) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usermovies = $db->query(sprintf("select imdbID, categoryID from usermovies where userID = %d", $uid), true); + $usql = '(1=2 '; + foreach($usermovies as $umov) + { + $usql .= sprintf('or (releases.imdbID = %d', $umov['imdbID']); + if ($umov['categoryID'] != '') + { + $catsArr = explode('|', $umov['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $limit = " LIMIT 0,".($num > 100 ? 100 : $num); + + $sql = sprintf(" SELECT releases.*, mi.title as releasetitle, g.name as group_name, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, coalesce(cp.ID,0) as parentCategoryID + FROM releases + left outer join category c on c.ID = releases.categoryID + left outer join category cp on cp.ID = c.parentID + left outer join groups g on g.ID = releases.groupID + left outer join movieinfo mi on mi.imdbID = releases.imdbID + where %s %s + and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') + order by postdate desc %s" , $usql, $exccatlist, $limit); + return $db->query($sql); + } + + /** + * Get range of releases in users 'my tvshows' + */ + public function getShowsRange($usershows, $start, $num, $orderby, $maxage=-1, $excludedcats=array()) + { + $db = new DB(); + + if ($start === false) + $limit = ""; + else + $limit = " LIMIT ".$start.",".$num; + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + + $order = $this->getBrowseOrder($orderby); + $sql = sprintf(" SELECT releases.*, concat(cp.title, '-', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, pre.ctime, pre.nuketype, rn.ID as nfoID, re.releaseID as reID from releases left outer join releasevideo re on re.releaseID = releases.ID left outer join groups on groups.ID = releases.groupID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category c on c.ID = releases.categoryID left outer join predb pre on pre.ID = releases.preID left outer join category cp on cp.ID = c.parentID where %s %s and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s order by %s %s".$limit, $usql, $exccatlist, $maxagesql, $order[0], $order[1]); + return $db->query($sql, true); + } + + /** + * Get count of releases in users 'my tvshows' for pager + */ + public function getShowsCount($usershows, $maxage=-1, $excludedcats=array()) + { + $db = new DB(); + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + $usql = '(1=2 '; + foreach($usershows as $ushow) + { + $usql .= sprintf('or (releases.rageID = %d', $ushow['rageID']); + if ($ushow['categoryID'] != '') + { + $catsArr = explode('|', $ushow['categoryID']); + if (count($catsArr) > 1) + $usql .= sprintf(' and releases.categoryID in (%s)', implode(',',$catsArr)); + else + $usql .= sprintf(' and releases.categoryID = %d', $catsArr[0]); + } + $usql .= ') '; + } + $usql .= ') '; + + $maxagesql = ""; + if ($maxage > 0) + $maxagesql = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + + $res = $db->queryOneRow(sprintf(" SELECT count(releases.ID) as num from releases where %s %s and releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s", $usql, $exccatlist, $maxagesql), true); + return $res["num"]; + } + + /** + * Delete one or more releases. + */ + public function delete($id, $isGuid=false) + { + $db = new DB(); + $users = new Users(); + $s = new Sites(); + $nfo = new Nfo(); + $site = $s->get(); + $rf = new ReleaseFiles(); + $re = new ReleaseExtra(); + $rc = new ReleaseComments(); + $ri = new ReleaseImage(); + + if (!is_array($id)) + $id = array($id); + + foreach($id as $identifier) + { + // + // delete from disk. + // + $rel = ($isGuid) ? $this->getByGuid($identifier) : $this->getById($identifier); + + $nzbpath = ""; + if ($isGuid) + $nzbpath = $site->nzbpath.substr($identifier, 0, 1)."/".$identifier.".nzb.gz"; + elseif ($rel) + $nzbpath = $site->nzbpath.substr($rel["guid"], 0, 1)."/".$rel["guid"].".nzb.gz"; + + if ($nzbpath != "" && file_exists($nzbpath)) + unlink($nzbpath); + + $audiopreviewpath = ""; + if ($isGuid) + $audiopreviewpath = WWW_DIR.'covers/audio/'.$identifier.".mp3"; + elseif ($rel) + $audiopreviewpath = WWW_DIR.'covers/audio/'.$rel["guid"].".mp3"; + + if ($audiopreviewpath && file_exists($audiopreviewpath)) + unlink($audiopreviewpath); + + if ($rel) + { + $nfo->deleteReleaseNfo($rel['ID']); + $rc->deleteCommentsForRelease($rel['ID']); + $users->delCartForRelease($rel['ID']); + $users->delDownloadRequestsForRelease($rel['ID']); + $rf->delete($rel['ID']); + $re->delete($rel['ID']); + $re->deleteFull($rel['ID']); + $ri->delete($rel['guid']); + $db->query(sprintf("delete from releases where id = %d", $rel['ID'])); + } + } + } + + /** + * Delete a preview associated with a release and update the release to indicate it doesnt have one. + */ + public function deletePreview($guid) + { + $this->updateHasPreview($guid, 0); + $ri = new ReleaseImage(); + $ri->delete($guid); + } + + /** + * Update a release. + */ + public function update($id, $name, $searchname, $fromname, $category, $parts, $grabs, $size, $posteddate, $addeddate, $rageid, $seriesfull, $season, $episode, $imdbid, $anidbid, $tvdbid, $consoleinfoid) + { + $db = new DB(); + + $db->query(sprintf("update releases set name=%s, searchname=%s, fromname=%s, categoryID=%d, totalpart=%d, grabs=%d, size=%s, postdate=%s, adddate=%s, rageID=%d, seriesfull=%s, season=%s, episode=%s, imdbID=%d, anidbID=%d, tvdbID=%d,consoleinfoID=%d where id = %d", + $db->escapeString($name), $db->escapeString($searchname), $db->escapeString($fromname), $category, $parts, $grabs, $db->escapeString($size), $db->escapeString($posteddate), $db->escapeString($addeddate), $rageid, $db->escapeString($seriesfull), $db->escapeString($season), $db->escapeString($episode), $imdbid, $anidbid, $tvdbid, $consoleinfoid, $id)); + } + + /** + * Update multiple releases. + */ + public function updatemulti($guids, $category, $grabs, $rageid, $season, $imdbid) + { + if (!is_array($guids) || sizeof($guids) < 1) + return false; + + $update = array( + 'categoryID'=>(($category == '-1') ? '' : $category), + 'grabs'=>$grabs, + 'rageID'=>$rageid, + 'season'=>$season, + 'imdbID'=>$imdbid + ); + + $db = new DB(); + $updateSql = array(); + foreach($update as $updk=>$updv) { + if ($updv != '') + $updateSql[] = sprintf($updk.'=%s', $db->escapeString($updv)); + } + + if (sizeof($updateSql) < 1) { + //echo 'no field set to be changed'; + return -1; + } + + $updateGuids = array(); + foreach($guids as $guid) { + $updateGuids[] = $db->escapeString($guid); + } + + $sql = sprintf('update releases set '.implode(', ', $updateSql).' where guid in (%s)', implode(', ', $updateGuids)); + return $db->query($sql); + } + + /** + * Update whether a release has a preview. + */ + public function updateHasPreview($guid, $haspreview) + { + $db = new DB(); + $db->query(sprintf("update releases set haspreview = %d where guid = %s", $haspreview, $db->escapeString($guid))); + } + + /** + * Not yet implemented. + */ + public function searchadv($searchname, $filename, $poster, $group, $cat=array(-1), $sizefrom, $sizeto, $offset=0, $limit=1000, $orderby='', $maxage=-1, $excludedcats=array()) + { + return array(); + } + + /** + * Search for releases. + */ + public function search($search, $cat=array(-1), $offset=0, $limit=1000, $orderby='', $maxage=-1, $excludedcats=array(), $grp=array()) + { + $s = new Sites(); + $site = $s->get(); + + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $order = $this->getBrowseOrder($orderby); + $results = $sphinx->search($search, $cat, $offset, $limit, $order, $maxage, $excludedcats, $grp, array(), true); + if (is_array($results)) + return $results; + } + + // + // Search using MySQL + // + $db = new DB(); + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + $grpsql = ""; + if (count($grp) > 0) + { + $grpsql = " and ("; + foreach ($grp as $grpname) + { + $grpsql.= sprintf(" groups.name = %s or ", $db->escapeString(str_replace("a.b.", "alt.binaries.", $grpname))); + } + $grpsql.= "1=2 )"; + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $search); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $exccatlist = ""; + if (count($excludedcats) > 0) + $exccatlist = " and releases.categoryID not in (".implode(",", $excludedcats).")"; + + if ($orderby == "") + { + $order[0] = " postdate "; + $order[1] = " desc "; + } + else + $order = $this->getBrowseOrder($orderby); + + $sql = sprintf("select releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID, cp.ID as categoryParentID, pre.ctime, pre.nuketype, coalesce(movieinfo.ID,0) as movieinfoID from releases %s left outer join movieinfo on movieinfo.imdbID = releases.imdbID left outer join releasevideo re on re.releaseID = releases.ID left outer join releasenfo rn on rn.releaseID = releases.ID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join predb pre on pre.ID = releases.preID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s order by %s %s limit %d, %d ", $usecatindex, $searchsql, $catsrch, $maxage, $exccatlist, $grpsql, $order[0], $order[1], $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by rage id. Used by API/Sickbeard. + */ + public function searchbyRageId($rageId, $series="", $episode="", $offset=0, $limit=100, $name="", $cat=array(-1), $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchbyRageId($rageId, $series, $episode, $offset, $limit, $name, $cat, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + if ($rageId != "-1") + $rageId = sprintf(" and rageID = %d ", $rageId); + else + $rageId = ""; + + if ($series != "") + { + // + // Exclude four digit series, which will be the year 2010 etc + // + if (is_numeric($series) && strlen($series) != 4) + $series = sprintf('S%02d', $series); + + $series = sprintf(" and releases.season = %s", $db->escapeString($series)); + } + if ($episode != "") + { + if (is_numeric($episode)) + $episode = sprintf('E%02d', $episode); + + $episode = sprintf(" and releases.episode like %s", $db->escapeString('%'.$episode.'%')); + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $sql = sprintf("select releases.*, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID, re.releaseID as reID from releases %s left outer join category c on c.ID = releases.categoryID left outer join groups on groups.ID = releases.groupID left outer join releasevideo re on re.releaseID = releases.ID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s %s order by postdate desc limit %d, %d ", $usecatindex, $rageId, $series, $episode, $searchsql, $catsrch, $maxage, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by anidb id. Used by API/Sickbeard. + */ + public function searchbyAnidbId($anidbID, $epno='', $offset=0, $limit=100, $name='', $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $order = $this->getBrowseOrder($orderby); + $results = $sphinx->searchbyAnidbId($anidbID, $epno, $offset, $limit, $name, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + $anidbID = ($anidbID > -1) ? sprintf(" AND anidbID = %d ", $anidbID) : ''; + + is_numeric($epno) ? $epno = sprintf(" AND releases.episode LIKE '%s' ", $db->escapeString('%'.$epno.'%')) : ''; + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" AND releases.searchname LIKE '%s' ", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" AND releases.searchname NOT LIKE '%s' ", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" AND releases.searchname LIKE '%s' ", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $maxage = ($maxage > 0) ? sprintf(" and postdate > now() - interval %d day ", $maxage) : ''; + + $sql = sprintf("SELECT releases.*, concat(cp.title, ' > ', c.title) + AS category_name, concat(cp.ID, ',', c.ID) AS category_ids, groups.name AS group_name, rn.ID AS nfoID + FROM releases LEFT OUTER JOIN category c ON c.ID = releases.categoryID LEFT OUTER JOIN groups ON groups.ID = releases.groupID + LEFT OUTER JOIN releasenfo rn ON rn.releaseID = releases.ID and rn.nfo IS NOT NULL LEFT OUTER JOIN category cp ON cp.ID = c.parentID + WHERE releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s ORDER BY postdate desc LIMIT %d, %d ", + $anidbID, $epno, $searchsql, $maxage, $offset, $limit); + $orderpos = strpos($sql, "ORDER BY"); + $wherepos = strpos($sql, "WHERE"); + $sqlcount = "SELECT count(releases.ID) AS num FROM releases ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by album/artist/musicinfo. Used by API. + */ + public function searchAudio($artist, $album, $label, $track, $year, $genre=array(-1), $offset=0, $limit=100, $cat=array(-1), $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchAudio($artist, $album, $label, $track, $year, $genre, $offset, $limit, $cat, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + $searchsql = ""; + + if ($artist != "") + $searchsql.= sprintf(" and musicinfo.artist like %s ", $db->escapeString("%".$artist."%")); + if ($album != "") + $searchsql.= sprintf(" and musicinfo.title like %s ", $db->escapeString("%".$album."%")); + if ($label != "") + $searchsql.= sprintf(" and musicinfo.publisher like %s ", $db->escapeString("%".$label."%")); + if ($track != "") + $searchsql.= sprintf(" and musicinfo.tracks like %s ", $db->escapeString("%".$track."%")); + if ($year != "") + $searchsql.= sprintf(" and musicinfo.year = %d ", $year); + + + $catsrch = ""; + $usecatindex = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + $usecatindex = " use index (ix_releases_categoryID) "; + } + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $genresql = ""; + if (count($genre) > 0 && $genre[0] != -1) + { + $genresql = " and ("; + foreach ($genre as $g) + { + $genresql .= sprintf(" musicinfo.genreID = %d or ", $g); + } + $genresql.= "1=2 )"; + } + + $sql = sprintf("select releases.*, musicinfo.cover as mi_cover, musicinfo.review as mi_review, musicinfo.tracks as mi_tracks, musicinfo.publisher as mi_publisher, musicinfo.title as mi_title, musicinfo.artist as mi_artist, genres.title as music_genrename, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases %s left outer join musicinfo on musicinfo.ID = releases.musicinfoID left join genres on genres.ID = musicinfo.genreID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s order by postdate desc limit %d, %d ", $usecatindex, $searchsql, $catsrch, $maxage, $genresql, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases inner join musicinfo on musicinfo.ID = releases.musicinfoID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by author/bookinfo. Used by API. + */ + public function searchBook($author, $title, $offset=0, $limit=100, $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchBook($author, $title, $offset, $limit, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + $searchsql = ""; + + if ($author != "") + $searchsql.= sprintf(" and bookinfo.author like %s ", $db->escapeString("%".$author."%")); + if ($title != "") + $searchsql.= sprintf(" and bookinfo.title like %s ", $db->escapeString("%".$title."%")); + + if ($maxage > 0) + $maxage = sprintf(" and postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + $sql = sprintf("select releases.*, bookinfo.cover as bi_cover, bookinfo.review as bi_review, bookinfo.publisher as bi_publisher, bookinfo.pages as bi_pages, bookinfo.publishdate as bi_publishdate, bookinfo.title as bi_title, bookinfo.author as bi_author, genres.title as book_genrename, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases left outer join bookinfo on bookinfo.ID = releases.bookinfoID left join genres on genres.ID = bookinfo.genreID left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s order by postdate desc limit %d, %d ", $searchsql, $maxage, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases inner join bookinfo on bookinfo.ID = releases.bookinfoID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Search for releases by imdbid/movieinfo. Used by API/Couchpotato. + */ + public function searchbyImdbId($imdbId, $offset=0, $limit=100, $name="", $cat=array(-1), $genre="", $maxage=-1) + { + $s = new Sites(); + $site = $s->get(); + if ($site->sphinxenabled) + { + $sphinx = new Sphinx(); + $results = $sphinx->searchbyImdbId($imdbId, $offset, $limit, $name, $cat, $genre, $maxage, array(), true); + if (is_array($results)) + return $results; + } + + $db = new DB(); + + if ($imdbId != "-1" && is_numeric($imdbId)) + { + //pad id with zeros just in case + $imdbId = str_pad($imdbId, 7, "0",STR_PAD_LEFT); + $imdbId = sprintf(" and releases.imdbID = %d ", $imdbId); + } + else + { + $imdbId = ""; + } + + // + // if the query starts with a ^ it indicates the search is looking for items which start with the term + // still do the fulltext match, but mandate that all items returned must start with the provided word + // + $words = explode(" ", $name); + $searchsql = ""; + $intwordcount = 0; + if (count($words) > 0) + { + foreach ($words as $word) + { + if ($word != "") + { + // + // see if the first word had a caret, which indicates search must start with term + // + if ($intwordcount == 0 && (strpos($word, "^") === 0)) + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString(substr($word, 1)."%")); + elseif (substr($word, 0, 2) == '--') + $searchsql.= sprintf(" and releases.searchname not like %s", $db->escapeString("%".substr($word, 2)."%")); + else + $searchsql.= sprintf(" and releases.searchname like %s", $db->escapeString("%".$word."%")); + + $intwordcount++; + } + } + } + + $catsrch = ""; + if (count($cat) > 0 && $cat[0] != -1) + { + $catsrch = " and ("; + foreach ($cat as $category) + { + if ($category != -1) + { + $categ = new Category(); + if ($categ->isParent($category)) + { + $children = $categ->getChildren($category); + $chlist = "-99"; + foreach ($children as $child) + $chlist.=", ".$child["ID"]; + + if ($chlist != "-99") + $catsrch .= " releases.categoryID in (".$chlist.") or "; + } + else + { + $catsrch .= sprintf(" releases.categoryID = %d or ", $category); + } + } + } + $catsrch.= "1=2 )"; + } + + if ($maxage > 0) + $maxage = sprintf(" and releases.postdate > now() - interval %d day ", $maxage); + else + $maxage = ""; + + if ($genre != "") + { + $genre = sprintf(" and movieinfo.genre like %s", $db->escapeString("%".$genre."%")); + } + + $sql = sprintf("select releases.*, movieinfo.title as moi_title, movieinfo.tagline as moi_tagline, movieinfo.rating as moi_rating, movieinfo.plot as moi_plot, movieinfo.year as moi_year, movieinfo.genre as moi_genre, movieinfo.director as moi_director, movieinfo.actors as moi_actors, movieinfo.cover as moi_cover, movieinfo.backdrop as moi_backdrop, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name, rn.ID as nfoID from releases left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join releasenfo rn on rn.releaseID = releases.ID and rn.nfo is not null left outer join category cp on cp.ID = c.parentID left outer join movieinfo on releases.imdbID = movieinfo.imdbID where releases.passwordstatus <= (select value from site where setting='showpasswordedrelease') %s %s %s %s %s order by postdate desc limit %d, %d ", $searchsql, $imdbId, $catsrch, $maxage, $genre, $offset, $limit); + $orderpos = strpos($sql, "order by"); + $wherepos = strpos($sql, "where"); + $sqlcount = "select count(releases.ID) as num from releases left outer join movieinfo on releases.imdbID = movieinfo.imdbID ".substr($sql, $wherepos,$orderpos-$wherepos); + + $countres = $db->queryOneRow($sqlcount, true); + $res = $db->query($sql, true); + if (count($res) > 0) + $res[0]["_totalrows"] = $countres["num"]; + + return $res; + } + + /** + * Return a list of releases with a similar name to that provided. + */ + public function searchSimilar($currentid, $name, $limit=6, $excludedcats=array()) + { + $name = $this->getSimilarName($name); + $results = $this->search($name, array(-1), 0, $limit, '', -1, $excludedcats); + if (!$results) + return $results; + + // + // Get the category for the parent of this release + // + $currRow = $this->getById($currentid); + $cat = new Category(); + $catrow = $cat->getById($currRow["categoryID"]); + $parentCat = $catrow["parentID"]; + + $ret = array(); + foreach ($results as $res) + if ($res["ID"] != $currentid && $res["categoryParentID"] == $parentCat) + $ret[] = $res; + + return $ret; + } + + /** + * Return a similar release name. + */ + public function getSimilarName($name) + { + $words = str_word_count(str_replace(array(".","_"), " ", $name), 2); + $firstwords = array_slice($words, 0, 2); + return implode(' ', $firstwords); + } + + /** + * Retrieve one or more releases by guid. + */ + public function getByGuid($guid) + { + $db = new DB(); + if (is_array($guid)) + { + $tmpguids = array(); + foreach($guid as $g) + $tmpguids[] = $db->escapeString($g); + $gsql = sprintf('guid in (%s)', implode(',',$tmpguids)); + } else { + $gsql = sprintf('guid = %s', $db->escapeString($guid)); + } + $sql = sprintf("select releases.*, musicinfo.cover as mi_cover, musicinfo.review as mi_review, musicinfo.tracks as mi_tracks, musicinfo.publisher as mi_publisher, musicinfo.title as mi_title, musicinfo.artist as mi_artist, music_genre.title as music_genrename, bookinfo.cover as bi_cover, bookinfo.review as bi_review, bookinfo.publisher as bi_publisher, bookinfo.publishdate as bi_publishdate, bookinfo.title as bi_title, bookinfo.author as bi_author, bookinfo.pages as bi_pages, bookinfo.isbn as bi_isbn, concat(cp.title, ' > ', c.title) as category_name, concat(cp.ID, ',', c.ID) as category_ids, groups.name as group_name from releases left outer join groups on groups.ID = releases.groupID left outer join category c on c.ID = releases.categoryID left outer join category cp on cp.ID = c.parentID left outer join musicinfo on musicinfo.ID = releases.musicinfoID left outer join bookinfo on bookinfo.ID = releases.bookinfoID left join genres music_genre on music_genre.ID = musicinfo.genreID where %s ", $gsql); + return (is_array($guid)) ? $db->query($sql) : $db->queryOneRow($sql); + } + + /** + * Writes a zip file of an array of release guids directly to the stream + */ + public function getZipped($guids) + { + $s = new Sites(); + $nzb = new NZB; + $site = $s->get(); + $zipfile = new zipfile(); + + foreach ($guids as $guid) + { + $nzbpath = $nzb->getNZBPath($guid, $site->nzbpath); + + if (file_exists($nzbpath)) + { + ob_start(); + @readgzfile($nzbpath); + $nzbfile = ob_get_contents(); + ob_end_clean(); + + $filename = $guid; + $r = $this->getByGuid($guid); + if ($r) + $filename = $r["searchname"]; + + $zipfile->addFile($nzbfile, $filename.".nzb"); + } + } + + return $zipfile->file(); + } + + /** + * Removes an associated tvrage id from all releases using it. + */ + public function removeRageIdFromReleases($rageid) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("select count(ID) as num from releases where rageID = %d", $rageid)); + $ret = $res["num"]; + $res = $db->query(sprintf("update releases set rageID = -1, seriesfull = null, season = null, episode = null where rageID = %d", $rageid)); + return $ret; + } + + /** + * Removes an associated tvdb id from all releases using it. + */ + public function removeThetvdbIdFromReleases($tvdbID) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM releases WHERE tvdbID = %d", $tvdbID)); + $ret = $res["num"]; + $res = $db->query(sprintf("UPDATE releases SET tvdbID = -1 where tvdbID = %d", $tvdbID)); + return $ret; + } + + public function removeAnidbIdFromReleases($anidbID) + { + $db = new DB(); + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM releases WHERE anidbID = %d", $anidbID)); + $ret = $res["num"]; + $res = $db->query(sprintf("UPDATE releases SET anidbID = -1, episode = null, tvtitle = null, tvairdate = null where anidbID = %d", $anidbID)); + return $ret; + } + + public function getById($id) + { + $db = new DB(); + return $db->queryOneRow(sprintf("select releases.*, groups.name as group_name from releases left outer join groups on groups.ID = releases.groupID where releases.ID = %d ", $id)); + } + + public function getReleaseNfo($id, $incnfo=true) + { + $db = new DB(); + $selnfo = ($incnfo) ? ', uncompress(nfo) as nfo' : ''; + return $db->queryOneRow(sprintf("SELECT ID, releaseID".$selnfo." FROM releasenfo where releaseID = %d AND nfo IS NOT NULL", $id)); + } + + public function updateGrab($guid) + { + $db = new DB(); + $db->queryOneRow(sprintf("update releases set grabs = grabs + 1 where guid = %s", $db->escapeString($guid))); + } + + function processReleases() + { + require_once(WWW_DIR."/lib/binaries.php"); + + $db = new DB; + + // + // Get the current datetime again, as using now() in the housekeeping queries prevents the index being used. + // + $currTime_ori = $db->queryOneRow("SELECT NOW() as now"); + + $cat = new Category; + $bin = new Binaries; + $nzb = new Nzb; + $s = new Sites; + $relreg = new ReleaseRegex; + $page = new Page; + $nfo = new Nfo; + $retcount = 0; + + echo $s->getLicense(); + + echo "\n\nStarting release update process (".date("Y-m-d H:i:s").")\n"; + + if (!file_exists($page->site->nzbpath)) + { + echo "Bad or missing nzb directory - ".$page->site->nzbpath; + return; + } + + $this->checkRegexesUptoDate($page->site->latestregexurl, $page->site->latestregexrevision, $page->site->newznabID); + + // + // Get all regexes for all groups which are to be applied to new binaries + // in order of how they should be applied + // + $regexrows = $relreg->get(); + echo "Stage 1 : Applying regex to binaries\n"; + foreach ($regexrows as $regexrow) + { + $groupmatch = ""; + + // + // Groups ending in * need to be like matched when getting out binaries for groups and children + // + if (preg_match("/\*$/i", $regexrow["groupname"])) + { + $groupname = substr($regexrow["groupname"], 0, -1); + $resgrps = $db->query(sprintf("select ID from groups where name like %s ", $db->escapeString($groupname."%"))); + foreach ($resgrps as $resgrp) + $groupmatch.=" groupID = ".$resgrp["ID"]." or "; + + $groupmatch.=" 1=2 "; + } + // + // A group name which doesnt end in a * needs an exact match + // + elseif ($regexrow["groupname"] != "") + { + $resgrp = $db->queryOneRow(sprintf("select ID from groups where name = %s ", $db->escapeString($regexrow["groupname"]))); + + // + // if group not found, its a regex for a group we arent indexing. + // + if ($resgrp) + $groupmatch = " groupID = ".$resgrp["ID"]; + else + $groupmatch = " 1=2 " ; + } + // + // No groupname specified (these must be the misc regexes applied to all groups) + // + else + $groupmatch = " 1=1 "; + + // Get out all binaries of STAGE0 for current group + $arrNoPartBinaries = array(); + $resbin = $db->queryDirect(sprintf("SELECT binaries.ID, binaries.name, binaries.date, binaries.totalParts from binaries where (%s) and procstat = %d order by binaries.date asc", $groupmatch, Releases::PROCSTAT_NEW)); + + while ($rowbin = mysql_fetch_assoc($resbin)) + { + if (preg_match ($regexrow["regex"], $rowbin["name"], $matches)) + { + $matches = array_map("trim", $matches); + + if ((isset($matches['reqid']) && ctype_digit($matches['reqid'])) && (!isset($matches['name']) || empty($matches['name']))) { + $matches['name'] = $matches['reqid']; + } + + // Check that the regex provided the correct parameters + if (!isset($matches['name']) || empty($matches['name'])) + { + continue; + } + + // If theres no number of files data in the subject, put it into a release if it was posted to usenet longer than five hours ago. + if ((!isset($matches['parts']) && strtotime($currTime_ori['now']) - strtotime($rowbin['date']) > 18000) || isset($arrNoPartBinaries[$matches['name']])) + { + // + // Take a copy of the name of this no-part release found. This can be used + // next time round the loop to find parts of this set, but which have not yet reached 3 hours. + // + $arrNoPartBinaries[$matches['name']] = "1"; + $matches['parts'] = "01/01"; + } + + + if (isset($matches['name']) && isset($matches['parts'])) + { + if (strpos($matches['parts'], '/') === false) + { + $matches['parts'] = str_replace(array('-','~',' of '), '/', $matches['parts']); + } + + $regcatid = "null "; + if ($regexrow["categoryID"] != "") + $regcatid = $regexrow["categoryID"]; + //override if regex specifies pc oday but content is some other form of PC or Ebook + if ($regcatid == Category::CAT_PC_0DAY) + { + if ($cat->isMobileAndroid($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEANDROID; + if ($cat->isMobileiOS($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEIOS; + if ($cat->isMobileOther($matches['name'])) + $regcatid = Category::CAT_PC_MOBILEOTHER; + if ($cat->isIso($matches['name'])) + $regcatid = Category::CAT_PC_ISO; + if ($cat->isMac($matches['name'])) + $regcatid = Category::CAT_PC_MAC; + if ($cat->isPcGame($matches['name'])) + $regcatid = Category::CAT_PC_GAMES; + if ($cat->isEBook($matches['name'])) + $regcatid = Category::CAT_MISC_EBOOK; + } + + $reqid = " null "; + if (isset($matches['reqid'])) + $reqid = $matches['reqid']; + + //check if post is repost + if (preg_match('/(repost\d?|re\-?up)/i', $rowbin['name'], $repost) && !preg_match('/repost|re\-?up/i', $matches['name'])) { + $matches['name'] .= ' '.$repost[1]; + } + + $relparts = explode("/", $matches['parts']); + $db->query(sprintf("update binaries set relname = replace(%s, '_', ' '), relpart = %d, reltotalpart = %d, procstat=%d, categoryID=%s, regexID=%d, reqID=%s where ID = %d", + $db->escapeString($matches['name']), $relparts[0], (isset($relparts[1]) ? $relparts[1] : $relparts[0]), Releases::PROCSTAT_TITLEMATCHED, $regcatid, $regexrow["ID"], $reqid, $rowbin["ID"] )); + } + } + } + + } + + // + // Move all binaries from releases which have the correct number of files on to the next stage. + // + echo "Stage 2 : Marking binaries where all parts are available\n"; + $result = $db->queryDirect(sprintf("SELECT relname, SUM(reltotalpart) AS reltotalpart, groupID, reqID, fromname, SUM(num) AS num, coalesce(g.minfilestoformrelease, s.minfilestoformrelease) as minfilestoformrelease FROM ( SELECT relname, reltotalpart, groupID, reqID, fromname, COUNT(ID) AS num FROM binaries WHERE procstat = %s GROUP BY relname, reltotalpart, groupID, reqID, fromname ORDER BY NULL ) x left outer join groups g on g.ID = x.groupID inner join ( select value as minfilestoformrelease from site where setting = 'minfilestoformrelease' ) s GROUP BY relname, groupID, reqID, fromname, minfilestoformrelease ORDER BY NULL", Releases::PROCSTAT_TITLEMATCHED)); + while ($row = mysql_fetch_assoc($result)) + { + $retcount ++; + + // + // Less than the site permitted number of files in a release. Dont discard it, as it may + // be part of a set being uploaded. + // + if ($row["num"] < $row["minfilestoformrelease"]) + { + //echo "Number of files in release ".$row["relname"]." less than site/group setting (".$row['num']."/".$row["minfilestoformrelease"].")\n"; + + $db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname = %s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + + // + // There are the same or more files in our release than the number of files specified + // in the message subject so go ahead and make a release + // + elseif ($row["num"] >= $row["reltotalpart"]) + { + + // Check that the binary is complete + $binlist = $db->query(sprintf("SELECT binaries.ID, totalParts, date, COUNT(DISTINCT parts.messageID) AS num FROM binaries, parts WHERE binaries.ID=parts.binaryID AND binaries.relname = %s AND binaries.procstat = %d AND binaries.groupID = %d AND binaries.fromname = %s GROUP BY binaries.ID ORDER BY NULL", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + + $incomplete = false; + foreach ($binlist as $rowbin) + { + if ($rowbin['num'] < $rowbin['totalParts']) + { + // Allow to binary to release if posted to usenet longer than four hours ago and we still don't have all the parts + if (!(strtotime($currTime_ori['now']) - strtotime($rowbin['date']) > 14400)) + { + $incomplete = true; + break; + } + } + } + + if ($incomplete) + { + //$db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname = %s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + + // + // Right number of files, but see if the binary is a allfilled/reqid post, in which case it needs its name looked up + // + elseif ($row['reqID'] !='' && $page->site->reqidurl != "") + { + // + // Try and get the name using the group + // + $binGroup = $db->queryOneRow(sprintf("SELECT name FROM groups WHERE ID = %d", $row["groupID"])); + $newtitle = $this->getReleaseNameForReqId($page->site->reqidurl, $page->site->newznabID, $binGroup["name"], $row["reqID"]); + + // + // if the feed/group wasnt supported by the scraper, then just use the release name as the title. + // + if ($newtitle == "no feed") + { + $newtitle = $row["relname"]; + echo "Group not supported\n"; + } + + // + // Valid release with right number of files and title now, so move it on + // + if ($newtitle != "") + { + $db->query(sprintf("update binaries set relname = %s, procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($newtitle), Releases::PROCSTAT_READYTORELEASE, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + else + { + // + // Item not found, if the binary was added to the index yages ago, then give up. + // + $maxaddeddate = $db->queryOneRow(sprintf("SELECT NOW() as now, MAX(dateadded) as dateadded FROM binaries WHERE relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + + // + // If added to the index over 48 hours ago, give up trying to determine the title + // + if (strtotime($maxaddeddate['now']) - strtotime($maxaddeddate['dateadded']) > (60*60*48)) + { + $db->query(sprintf("update binaries set procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_NOREQIDNAMELOOKUPFOUND, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + } + } + else + { + $db->query(sprintf("update binaries set procstat=%d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_READYTORELEASE, $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]))); + } + } + + // + // Theres less than the expected number of files, so update the attempts and move on. + // + else + { + //echo "Incorrect number of files for ".$row["relname"]." (".$row["num"]."/".$row["reltotalpart"].")\n"; + + $db->query(sprintf("update binaries set procattempts = procattempts + 1 where relname = %s and procstat = %d and groupID = %d and fromname=%s", $db->escapeString($row["relname"]), Releases::PROCSTAT_TITLEMATCHED, $row["groupID"], $db->escapeString($row["fromname"]) )); + } + if ($retcount % 100 == 0) + echo "Stage 2 : Processed ".$retcount." binaries\n"; + } + $retcount=$nfocount=0; + + echo "Stage 3 : Creating releases from complete binaries\n"; + // + // Get out all distinct relname, group from binaries of STAGE2 + // + $result = $db->queryDirect(sprintf("SELECT relname, groupID, g.name as group_name, fromname, count(binaries.ID) as parts from binaries inner join groups g on g.ID = binaries.groupID where procstat = %d and relname is not null group by relname, g.name, groupID, fromname ORDER BY COUNT(binaries.ID) desc", Releases::PROCSTAT_READYTORELEASE)); + while ($row = mysql_fetch_assoc($result)) + { + // + // Get the last post date and the poster name from the binary + // + $bindata = $db->queryOneRow(sprintf("select fromname, MAX(date) as date from binaries where relname = %s and procstat = %d and groupID = %d and fromname = %s group by fromname order by null", + $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]) )); + + // + // Get all releases with the same name with a usenet posted date in a +1-1 date range. + // + $relDupes = $db->query(sprintf("select ID from releases where searchname = %s and ( date_sub(%s, interval 1 day) < postdate AND date_add(%s, interval 1 day) > postdate )", + $db->escapeString($this->cleanReleaseName($row["relname"])), $db->escapeString($bindata["date"]), $db->escapeString($bindata["date"]))); + if (count($relDupes) > 0) + { + $db->query(sprintf("update binaries set procstat = %d where relname = %s and procstat = %d and groupID = %d and fromname=%s ", + Releases::PROCSTAT_DUPLICATE, $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]))); + continue; + } + + // + // Get some attribs of this release + // + $regexAppliedCategoryID = ""; + $regexIDused = ""; + $reqIDused = ""; + $binSizeId = $db->queryOneRow(sprintf("select ID, categoryID, regexID, reqID, totalParts from binaries use index (ix_binary_relname) where relname = %s and procstat = %d and groupID = %d and fromname=%s", + $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]) )); + if ($binSizeId) + { + // + // Get categoryID if one has been allocated to this + // + if ($binSizeId["categoryID"] != "") + $regexAppliedCategoryID = $binSizeId["categoryID"]; + // + // Get RegexID if one has been allocated to this + // + if ($binSizeId["regexID"] != "") + $regexIDused = $binSizeId["regexID"]; + // + // Get requestID if one has been allocated to this + // + if ($binSizeId["reqID"] != "") + $reqIDused = $binSizeId["reqID"]; + + } + + // + // Insert the release + // + $relguid = md5(uniqid()); + if ($regexAppliedCategoryID == "") + $catId = $cat->determineCategory($row["group_name"], $row["relname"]); + else + $catId = $regexAppliedCategoryID; + + if ($regexIDused == "") + $regexID = " null "; + else + $regexID = $regexIDused; + + if ($reqIDused == "") + $reqID = " null "; + else + $reqID = $reqIDused; + + //Clean release name + $cleanRelName = $this->cleanReleaseName($row['relname']); + + $relid = $db->queryInsert(sprintf("insert into releases (name, searchname, totalpart, groupID, adddate, guid, categoryID, regexID, rageID, postdate, fromname, size, reqID, passwordstatus, completion, haspreview) values (%s, %s, %d, %d, now(), %s, %d, %d, -1, %s, %s, 0, %s, %d, 100, %d)", + $db->escapeString($cleanRelName), $db->escapeString($cleanRelName), $row["parts"], $row["groupID"], $db->escapeString($relguid), $catId, $regexID, $db->escapeString($bindata["date"]), $db->escapeString($bindata["fromname"]), $reqID, ($page->site->checkpasswordedrar > 0 ? -1 : 0), -1)); + echo "Stage 3 : Added release ".$cleanRelName."\n"; + + // + // Tag every binary for this release with its parent release id + // remove the release name from the binary as its no longer required + // + $db->query(sprintf("update binaries set procstat = %d, releaseID = %d where relname = %s and procstat = %d and groupID = %d and fromname=%s", + Releases::PROCSTAT_RELEASED, $relid, $db->escapeString($row["relname"]), Releases::PROCSTAT_READYTORELEASE, $row["groupID"], $db->escapeString($row["fromname"]))); + + // + // Find an .nfo in the release + // + $relnfo = $nfo->determineReleaseNfo($relid); + if ($relnfo !== false) + { + $nfo->addReleaseNfo($relid, $relnfo['ID']); + $nfocount++; + } + + // + // Write the nzb to disk + // + $nzbfile = $nzb->getNZBPath($relguid, $page->site->nzbpath, true); + $nzb->writeNZBforReleaseId($relid, $relguid, $cleanRelName, $catId, $nzbfile); + + $nzbInfo = new nzbInfo; + + // + // If nzb successfully written, then load it and get size completion from it + // + if (!$nzbInfo->loadFromFile($nzbfile)) + { + echo "Stage 5 : Failed to write nzb file (bad perms?) ".$nzbfile."\n"; + + // + // Remove the release and remark the binaries for processing again. + // + $this->delete($relid); + } + else + { + $db->query(sprintf("update releases set totalpart = %d, size = %s, completion = %d where ID = %d", $nzbInfo->filecount, $nzbInfo->filesize, $nzbInfo->completion, $relid )); + + //Increment new release count + $retcount ++; + } + } + + echo "Stage 4 : Finished creating ".$retcount." releases. Found ".$nfocount." nfos\n"; + + // + // Delete any releases under the minimum completion percent. + // + if($page->site->completionpercent != 0) + { + echo "Stage 5 : Deleting releases less than ".$page->site->completionpercent." complete\n"; + $result = $db->query(sprintf("select ID from releases where completion > 0 and completion < %d", $page->site->completionpercent)); + foreach ($result as $row) + $this->delete($row["ID"]); + } + + // + // Delete releases whos minsize is less than the site or group minimum + // + $result = $db->query("select releases.ID from releases left outer join (SELECT g.ID, coalesce(g.minsizetoformrelease, s.minsizetoformrelease) as minsizetoformrelease FROM groups g inner join ( select value as minsizetoformrelease from site where setting = 'minsizetoformrelease' ) s ) x on x.ID = releases.groupID where minsizetoformrelease != 0 and releases.size < minsizetoformrelease"); + if (count($result) > 0) + { + echo "Stage 5 : Deleting ".count($result)." release(s) where size is smaller than minsize for site/group\n"; + foreach ($result as $row) + $this->delete($row["ID"]); + } + + echo "Stage 5 : Post processing started\n"; + $postprocess = new PostProcess(true); + $postprocess->processAll(); + + // + // aggregate the releasefiles upto the releases. + // + echo "Stage 6 : Aggregating Files\n"; + $db->query("UPDATE releases INNER JOIN (SELECT releaseID, COUNT(ID) AS num FROM releasefiles GROUP BY releaseID) b ON b.releaseID = releases.ID and releases.rarinnerfilecount = 0 SET rarinnerfilecount = b.num"); + + + // + // Remove the binaries and parts used to form releases, or that are duplicates. + // + echo "Stage 7 : Deleting unused binaries and parts\n"; + $db->query(sprintf("DELETE parts, binaries FROM parts LEFT JOIN binaries ON binaries.ID = parts.binaryID LEFT JOIN releasenfo rn ON + rn.binaryID = binaries.ID WHERE (binaries.procstat IN (%d, %d) AND + (rn.binaryid IS NULL OR (rn.binaryid IS NOT NULL AND rn.nfo IS NOT NULL))) OR + binaries.dateadded < %s - INTERVAL %d HOUR", Releases::PROCSTAT_RELEASED, Releases::PROCSTAT_DUPLICATE, $db->escapeString($currTime_ori["now"]), ceil($page->site->rawretentiondays*24))); + + + // + // User/Request housekeeping, should ideally move this to its own section, but it needs to be done automatically. + // + $users = new Users; + $users->pruneRequestHistory($page->site->userdownloadpurgedays); + + echo "Done : Added ". $retcount." releases\n\n"; + + return $retcount; + } + + public function cleanReleaseName($relname) + { + $cleanArr = array('#', '@', '$', '%', '^', '§', '¨', '©', 'Ö'); + + $relname = str_replace($cleanArr, '', $relname); + $relname = str_replace('_', ' ', $relname); + + return $relname; + } + + public function getReleaseNameForReqId($url, $nnid, $groupname, $reqid) + { + $url = str_ireplace("[GROUP]", urlencode($groupname), $url); + $url = str_ireplace("[REQID]", urlencode($reqid), $url); + + if ($nnid != "") + $nnid = "&newznabID=".$nnid; + + $xml = ""; + $arrXml = ""; + $xml = getUrl($url); + + if ($xml === false || preg_match('/no feed/i', $xml)) + return "no feed"; + else + { + if ($xml != "") + { + $xmlObj = @simplexml_load_string($xml); + $arrXml = objectsIntoArray($xmlObj); + + if (isset($arrXml["item"]) && is_array($arrXml["item"]) && is_array($arrXml["item"]["@attributes"])) + { + return $arrXml["item"]["@attributes"]["title"]; + } + } + } + return ""; + } + + public function checkRegexesUptoDate($url, $rev, $nnid) + { + if ($url != "") + { + if ($nnid != "") + $nnid = "?newznabID=".$nnid; + + $regfile = getUrl($url.$nnid); + if ($regfile !== false && $regfile != "") + { + /*$Rev: 728 $*/ + if (preg_match('/\/\*\$Rev: (\d{3,4})/i', $regfile, $matches)) + { + $serverrev = intval($matches[1]); + if ($serverrev > $rev) + { + $db = new DB(); + $site = new Sites; + + $queries = explode(";", $regfile); + $queries = array_map("trim", $queries); + foreach($queries as $q) { + if ( $q ) { + $db->query($q); + } + } + + $site->updateLatestRegexRevision($serverrev); + echo "Updated regexes to revision ".$serverrev."\n"; + } + else + { + echo "Using latest regex revision ".$rev."\n"; + } + } + else + { + echo "Error Processing Regex File\n"; + } + } + else + { + echo "Error Regex File Does Not Exist or Unable to Connect\n"; + } + } + } + + public function getTopDownloads() + { + $db = new DB(); + return $db->query("SELECT ID, searchname, guid, adddate, grabs FROM releases + where grabs > 0 + ORDER BY grabs DESC + LIMIT 10"); + } + + public function getTopComments() + { + $db = new DB(); + return $db->query("SELECT ID, guid, searchname, adddate, comments FROM releases + where comments > 0 + ORDER BY comments DESC + LIMIT 10"); + } + + public function getRecentlyAdded() + { + $db = new DB(); + return $db->query("SELECT concat(cp.title, ' > ', category.title) as title, COUNT(*) AS count +FROM category +left outer join category cp on cp.ID = category.parentID +INNER JOIN releases ON releases.categoryID = category.ID +WHERE releases.adddate > NOW() - INTERVAL 1 WEEK +GROUP BY concat(cp.title, ' > ', category.title) +ORDER BY COUNT(*) DESC"); + } + +} + diff --git a/bin/lib/thetvdb2.php b/bin/lib/thetvdb2.php new file mode 100755 index 000000000..c81b79596 --- /dev/null +++ b/bin/lib/thetvdb2.php @@ -0,0 +1,331 @@ +echooutput = $echooutput; + $this->MIRROR = 'http://www.thetvdb.com'; + } + + public function addSeries($TheTVDBAPIArray) + { + $db = new DB(); + + $db->queryInsert(sprintf("INSERT INTO thetvdb + (tvdbID, actors, airsday, airstime, contentrating, firstaired, genre, imdbID, network, overview, rating, ratingcount, runtime, seriesname, status, createddate) + VALUES (%d, %s, %s, %s, %s, %s, %s, %d, %s, %s, %F, %d, %d, %s, %s, now())", + $TheTVDBAPIArray['tvdbID'], $db->escapeString($TheTVDBAPIArray['actors']), $db->escapeString($TheTVDBAPIArray['airsday']), + $db->escapeString($TheTVDBAPIArray['airstime']), $db->escapeString($TheTVDBAPIArray['contentrating']), $db->escapeString($TheTVDBAPIArray['firstaired']), + $db->escapeString($TheTVDBAPIArray['genre']), $TheTVDBAPIArray['imdbID'], $db->escapeString($TheTVDBAPIArray['network']), $db->escapeString($TheTVDBAPIArray['overview']), + $db->escapeString($TheTVDBAPIArray['rating']), $TheTVDBAPIArray['ratingcount'], $TheTVDBAPIArray['runtime'], $db->escapeString($TheTVDBAPIArray['seriesname']), + $db->escapeString($TheTVDBAPIArray['status']))); + } + + public function addEpisodes($TheTVDBAPIArray) + { + $db = new DB(); + + for($i=0; $i < count($TheTVDBAPIArray['episodetvdbID']); $i++) { + $airdate = strftime('%Y-%m-%d %H:%M:%S', strtotime($TheTVDBAPIArray['episodefirstaired'][$i].' '.$TheTVDBAPIArray['airstime'])); + if(!$airdate) + continue; + + $db->queryInsert(sprintf('INSERT INTO episodeinfo + (tvdbID, imdbID, showtitle, airdate, fullep, eptitle, director, gueststars, overview, rating, writer, epabsolute) + VALUES (%d, %d, %s, %s, %s, %s, %s, %s, %s, %F, %s, %d) + ON DUPLICATE KEY UPDATE + tvdbID=%1$d, imdbID=%2$d, showtitle=%3$s, airdate=%4$s, fullep=%5$s, eptitle=%6$s, director=%7$s, + gueststars=%8$s, overview=%9$s, rating=%10$F, writer=%11$s, epabsolute=%12$s', + $TheTVDBAPIArray['episodetvdbID'][$i], $TheTVDBAPIArray['episodeimdbID'][$i], $db->escapeString($TheTVDBAPIArray['seriesname']), $db->escapeString($airdate), + $db->escapeString(str_pad($TheTVDBAPIArray['episodeseason'][$i], 2, '0', STR_PAD_LEFT).'x'.str_pad($TheTVDBAPIArray['episodenumber'][$i], 2, '0', STR_PAD_LEFT)), + $db->escapeString($TheTVDBAPIArray['episodename'][$i]), $db->escapeString($TheTVDBAPIArray['episodedirector'][$i]), + $db->escapeString($TheTVDBAPIArray['episodegueststars'][$i]), $db->escapeString($TheTVDBAPIArray['episodeoverview'][$i]), + $TheTVDBAPIArray['episoderating'][$i], $db->escapeString($TheTVDBAPIArray['episodewriter'][$i]), $TheTVDBAPIArray['episodeabsolutenumber'][$i])); + } + } + + public function updateSeries($tvdbID, $actors, $airsday, $airstime, $contentrating, $firstaired, $genre, $imdbID, $network, $overview, $rating, $ratingcount, $runtime, $seriesname, $status) + { + $db = new DB(); + + $db->query(sprintf('UPDATE thetvdb + SET actors=%s, airsday=%s, airstime=%s, contentrating=%s, firstaired=%s, genre=%s, imdbID=%d, network=%s, + overview=%s, rating=%s, ratingcount=%d, runtime=%d, seriesname=%s, status=%s, createddate=now() + WHERE tvdbID = %d', $db->escapeString($actors), $db->escapeString($airsday), $db->escapeString($airstime), $db->escapeString($contentrating), + $db->escapeString($firstaired), $db->escapeString($genre), $imdbID, $db->escapeString($network), $db->escapeString($overview), $db->escapeString($rating), + $ratingcount, $runtime, $db->escapeString($seriesname), $db->escapeString($status), $tvdbID)); + } + + public function deleteTitle($tvdbID) + { + $db = new DB(); + + $db->query(sprintf("DELETE FROM thetvdb WHERE tvdbID = %d", $tvdbID)); + } + + public function addEmptySeries($seriesname) + { + $db = new DB(); + + $db->queryInsert(sprintf("INSERT INTO thetvdb (tvdbID, seriesname, createddate) VALUES (0, %s, now())", $db->escapeString($seriesname))); + } + + public function getSeriesInfoByID($tvdbID) + { + $db = new DB(); + return $db->queryOneRow(sprintf("SELECT * FROM thetvdb WHERE tvdbID = %d", $tvdbID)); + } + + public function getSeriesInfoByName($seriesname) + { + $db = new DB(); + return $db->queryOneRow(sprintf("SELECT * FROM thetvdb WHERE seriesname = %s", $db->escapeString($seriesname))); + } + + public function getSeriesRange($start, $num, $seriesname='') + { + $db = new DB(); + + $limit = ($start === false) ? '' : " LIMIT ".$start.",".$num; + + $rsql = ''; + if ($seriesname != '') + $rsql .= sprintf("AND thetvdb.seriesname LIKE %s ", $db->escapeString("%".$seriesname."%")); + + return $db->query(sprintf(" SELECT ID, tvdbID, seriesname, overview FROM thetvdb WHERE 1=1 %s AND tvdbID > %d ORDER BY tvdbID ASC".$limit, $rsql, 0)); + } + + public function getSeriesCount($seriesname='') + { + $db = new DB(); + + $rsql = ''; + if ($seriesname != '') + $rsql .= sprintf("AND thetvdb.seriesname LIKE %s ", $db->escapeString("%".$seriesname."%")); + + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM thetvdb WHERE 1=1 %s ", $rsql)); + + return $res["num"]; + } + + public function lookupSeriesID($seriesname) + { + $apiresponse = getUrl($this->MIRROR.'/api/GetSeries.php?seriesname='.preg_replace('/\s+/', '+', $seriesname).'&language=all'); + + if(!$apiresponse) + return false; + + $seriesidXML = @simplexml_load_string($apiresponse); + if(!$seriesidXML) + return false; + + $seriesid = 0; + foreach($seriesidXML as $item) + if(preg_match('/^'.preg_replace('/\+/', ' ', str_replace('/', '\/', $seriesname)).'$/i', (string) $item->SeriesName)) { + $seriesid = (int) $item->seriesid; + break; + } + + return $seriesid; + } + + public function notFound($seriesName, $fullep, $releaseID, $echooutput=true) + { + if($this->echooutput && $echooutput) + echo 'TheTVDB : '.$seriesName.' '.$fullep." Not found\n"; + + $db = new DB(); + $db->query(sprintf('UPDATE releases SET episodeinfoID = %d WHERE ID = %d', -2, $releaseID)); + } + + public function processReleases() + { + $db = new DB(); + + $results = $db->queryDirect(sprintf("SELECT ID, searchname, rageID, anidbID, seriesfull, season, episode, tvtitle FROM releases WHERE episodeinfoID IS NULL AND categoryID IN ( SELECT ID FROM category WHERE parentID = %d ) LIMIT 150", Category::CAT_PARENT_TV)); + + if (mysql_num_rows($results) > 0) + { + if ($this->echooutput) + echo "TheTVDB : Looking up last ".mysql_num_rows($results)." releases\n"; + + while ($arr = mysql_fetch_assoc($results)) + { + unset($TheTVDBAPIArray, $episodeArray, $fullep, $epabsolute, $additionalSql); + + $seriesName = ''; + if($arr['rageID'] > 0) { + $seriesName = $db->queryOneRow(sprintf('SELECT releasetitle AS seriesName FROM tvrage WHERE rageID = %d', $arr['rageID'])); + } + elseif($arr['anidbID'] > 0) { + $seriesName = $db->queryOneRow(sprintf('SELECT title AS seriesName FROM anidb WHERE anidbID = %d', $arr['anidbID'])); + } + + if(empty($seriesName) || !$seriesName) + { + $this->notFound($seriesName, "", $arr['ID'], false); + continue; + } + + $seriesName = str_replace('`', '\'', $seriesName['seriesName']); + if(!preg_match('/[21]\d{3}\/\d{2}\/\d{2}/', $arr['seriesfull'])) + $fullep = str_pad(str_replace('S', '', $arr['season']), 2, '0', STR_PAD_LEFT).'x'.str_pad(str_replace('E', '', $arr['episode']), 2, '0', STR_PAD_LEFT); + else + $fullep = str_replace('/', '-', $arr['seriesfull']); + + $TheTVDBAPIArray = $this->getSeriesInfoByName($seriesName); + if(!$TheTVDBAPIArray) + { + $seriesid = $this->lookupSeriesID($seriesName); + if($seriesid > 0) + { + if($TheTVDBAPIArray = $this->TheTVDBAPI($seriesid, $seriesName)) + { + $this->addSeries($TheTVDBAPIArray); + $this->addEpisodes($TheTVDBAPIArray); + } + else + { + $this->addEmptySeries($seriesName); + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else + { + $this->addEmptySeries($seriesName); + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else if($TheTVDBAPIArray['tvdbID'] >= 0 && ((time() - strtotime($TheTVDBAPIArray['createddate'])) > 604800)) + { + $TheTVDBAPIArray = $this->TheTVDBAPI($TheTVDBAPIArray['tvdbID'], $seriesName); + + $this->updateSeries($TheTVDBAPIArray['tvdbID'], $TheTVDBAPIArray['actors'], $TheTVDBAPIArray['airsday'], + $TheTVDBAPIArray['airstime'], $TheTVDBAPIArray['contentrating'], $TheTVDBAPIArray['firstaired'], $TheTVDBAPIArray['genre'], + $TheTVDBAPIArray['imdbID'], $TheTVDBAPIArray['network'], $TheTVDBAPIArray['overview'], $TheTVDBAPIArray['rating'], + $TheTVDBAPIArray['ratingcount'], $TheTVDBAPIArray['runtime'], $TheTVDBAPIArray['seriesname'], $TheTVDBAPIArray['status']); + + $this->addEpisodes($TheTVDBAPIArray); + } + + if($TheTVDBAPIArray['tvdbID'] > 0) + { + $epabsolute = '0'; + if($arr['anidbID'] > 0) + { + if(preg_match('/S(?P\d+)[ED](?P\d+)/', $arr['episode'], $seasonEpisode)) + { + $arr['season'] = $seasonEpisode['season']; + $arr['episode'] = $seasonEpisode['episode']; + } + else + $epabsolute = $arr['episode']; + } + + $Episode = new Episode(); + $episodeArray = $Episode->getEpisodeInfoByName($seriesName, $fullep, (string) $epabsolute); + if(!$episodeArray) + { + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else + { + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + + $additionalSql = ''; + if($arr['anidbID'] > 0 && $episodeArray['epabsolute'] > 0) + { + $additionalSql = sprintf(', season = NULL, episode = %d, tvtitle = %s, tvairdate = %s', + $episodeArray['epabsolute'], + $db->escapeString($episodeArray['epabsolute'].' - '.str_replace('\'', '`', $episodeArray['eptitle'])), + $db->escapeString($episodeArray['airdate'])); + } + + $db->query(sprintf('UPDATE releases SET tvdbID = %d, episodeinfoID = %d %s WHERE ID = %d', + $TheTVDBAPIArray['tvdbID'], $episodeArray['ID'], $additionalSql, $arr['ID'])); + + //if($this->echooutput) + //{ + // echo 'TheTVDB : '.$seriesName.' '.$fullep." returned ".$episodeArray['tvdbID']."\n"; + //} + } + } + } + + public function TheTVDBAPI($seriesid, $seriesName) + { + $apiresponse = getUrl($this->MIRROR.'/api/'.self::APIKEY.'/series/'.$seriesid.'/all/en.xml'); //.zip? + + if(!$apiresponse) + return false; + + $TheTVDBAPIXML = @simplexml_load_string($apiresponse); + if(!$TheTVDBAPIXML) + return false; + + foreach($TheTVDBAPIXML->Episode as $episode) { + $episodetvdbIDArray[] = (int) $episode->id; + $episodenumberArray[] = (int) $episode->Combined_episodenumber; + $episodeseasonArray[] = (int) $episode->Combined_season; + $episodedirectorArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Director); + $episodenameArray[] = preg_replace('/^\||\|$/', '', (string) $episode->EpisodeName); + $episodefirstairedArray[] = (string) $episode->FirstAired; + $episodegueststarsArray[] = preg_replace('/^\||\|$/', '', (string) $episode->GuestStars); + $episodeimdbID[] = str_replace('tt', '', (string) $episode->IMDB_ID); + $episodeoverviewArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Overview); + $episoderatingArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Rating); + $episodewriterArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Writer); + $episodeabsolutenumberArray[] = (int) $episode->absolute_number; + } + + $TheTVDBAPIArray = array( + 'tvdbID' => $seriesid, + 'actors' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Actors), + 'airsday' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Airs_DayOfWeek), + 'airstime' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Airs_Time), + 'contentrating' => (string) $TheTVDBAPIXML->Series->ContentRating, + 'firstaired' => (string) $TheTVDBAPIXML->Series->FirstAired, + 'genre' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Genre), + 'imdbID' => (int) preg_replace('/^[^\d]+/', '', (string) $TheTVDBAPIXML->Series->IMDB_ID), + 'network' => (string) $TheTVDBAPIXML->Series->Network, + 'overview' => (string) $TheTVDBAPIXML->Series->Overview, + 'rating' => (float) $TheTVDBAPIXML->Series->Rating, + 'ratingcount' => (int) $TheTVDBAPIXML->Series->RatingCount, + 'runtime' => (int) $TheTVDBAPIXML->Series->Runtime, + //'seriesname' => ((string) $TheTVDBAPIXML->Series->SeriesName != '') ? (string) $TheTVDBAPIXML->Series->SeriesName : $seriesName, + 'seriesname' => $seriesName, + 'status' => (string) $TheTVDBAPIXML->Series->Status, + 'episodetvdbID' => isset($episodetvdbIDArray) ? $episodetvdbIDArray : array(), + 'episodenumber' => isset($episodenumberArray) ? $episodenumberArray : array(), + 'episodeseason' => isset($episodeseasonArray) ? $episodeseasonArray : array(), + 'episodedirector' => isset($episodedirectorArray) ? $episodedirectorArray : array(), + 'episodename' => isset($episodenameArray) ? $episodenameArray : array(), + 'episodefirstaired' => isset($episodefirstairedArray) ? $episodefirstairedArray : array(), + 'episodegueststars' => isset($episodegueststarsArray) ? $episodegueststarsArray : array(), + 'episodeimdbID' => isset($episodeimdbID) ? $episodeimdbID : array(), + 'episodeoverview' => isset($episodeoverviewArray) ? $episodeoverviewArray : array(), + 'episoderating' => isset($episoderatingArray) ? $episoderatingArray : array(), + 'episodewriter' => isset($episodewriterArray) ? $episodewriterArray : array(), + 'episodeabsolutenumber' => isset($episodeabsolutenumberArray) ? $episodeabsolutenumberArray : array(), + ); + + return $TheTVDBAPIArray; + } +} + diff --git a/bin/lib/thetvdb3.php b/bin/lib/thetvdb3.php new file mode 100755 index 000000000..64d3eaddf --- /dev/null +++ b/bin/lib/thetvdb3.php @@ -0,0 +1,331 @@ +echooutput = $echooutput; + $this->MIRROR = 'http://www.thetvdb.com'; + } + + public function addSeries($TheTVDBAPIArray) + { + $db = new DB(); + + $db->queryInsert(sprintf("INSERT INTO thetvdb + (tvdbID, actors, airsday, airstime, contentrating, firstaired, genre, imdbID, network, overview, rating, ratingcount, runtime, seriesname, status, createddate) + VALUES (%d, %s, %s, %s, %s, %s, %s, %d, %s, %s, %F, %d, %d, %s, %s, now())", + $TheTVDBAPIArray['tvdbID'], $db->escapeString($TheTVDBAPIArray['actors']), $db->escapeString($TheTVDBAPIArray['airsday']), + $db->escapeString($TheTVDBAPIArray['airstime']), $db->escapeString($TheTVDBAPIArray['contentrating']), $db->escapeString($TheTVDBAPIArray['firstaired']), + $db->escapeString($TheTVDBAPIArray['genre']), $TheTVDBAPIArray['imdbID'], $db->escapeString($TheTVDBAPIArray['network']), $db->escapeString($TheTVDBAPIArray['overview']), + $db->escapeString($TheTVDBAPIArray['rating']), $TheTVDBAPIArray['ratingcount'], $TheTVDBAPIArray['runtime'], $db->escapeString($TheTVDBAPIArray['seriesname']), + $db->escapeString($TheTVDBAPIArray['status']))); + } + + public function addEpisodes($TheTVDBAPIArray) + { + $db = new DB(); + + for($i=0; $i < count($TheTVDBAPIArray['episodetvdbID']); $i++) { + $airdate = strftime('%Y-%m-%d %H:%M:%S', strtotime($TheTVDBAPIArray['episodefirstaired'][$i].' '.$TheTVDBAPIArray['airstime'])); + if(!$airdate) + continue; + + $db->queryInsert(sprintf('INSERT INTO episodeinfo + (tvdbID, imdbID, showtitle, airdate, fullep, eptitle, director, gueststars, overview, rating, writer, epabsolute) + VALUES (%d, %d, %s, %s, %s, %s, %s, %s, %s, %F, %s, %d) + ON DUPLICATE KEY UPDATE + tvdbID=%1$d, imdbID=%2$d, showtitle=%3$s, airdate=%4$s, fullep=%5$s, eptitle=%6$s, director=%7$s, + gueststars=%8$s, overview=%9$s, rating=%10$F, writer=%11$s, epabsolute=%12$s', + $TheTVDBAPIArray['episodetvdbID'][$i], $TheTVDBAPIArray['episodeimdbID'][$i], $db->escapeString($TheTVDBAPIArray['seriesname']), $db->escapeString($airdate), + $db->escapeString(str_pad($TheTVDBAPIArray['episodeseason'][$i], 2, '0', STR_PAD_LEFT).'x'.str_pad($TheTVDBAPIArray['episodenumber'][$i], 2, '0', STR_PAD_LEFT)), + $db->escapeString($TheTVDBAPIArray['episodename'][$i]), $db->escapeString($TheTVDBAPIArray['episodedirector'][$i]), + $db->escapeString($TheTVDBAPIArray['episodegueststars'][$i]), $db->escapeString($TheTVDBAPIArray['episodeoverview'][$i]), + $TheTVDBAPIArray['episoderating'][$i], $db->escapeString($TheTVDBAPIArray['episodewriter'][$i]), $TheTVDBAPIArray['episodeabsolutenumber'][$i])); + } + } + + public function updateSeries($tvdbID, $actors, $airsday, $airstime, $contentrating, $firstaired, $genre, $imdbID, $network, $overview, $rating, $ratingcount, $runtime, $seriesname, $status) + { + $db = new DB(); + + $db->query(sprintf('UPDATE thetvdb + SET actors=%s, airsday=%s, airstime=%s, contentrating=%s, firstaired=%s, genre=%s, imdbID=%d, network=%s, + overview=%s, rating=%s, ratingcount=%d, runtime=%d, seriesname=%s, status=%s, createddate=now() + WHERE tvdbID = %d', $db->escapeString($actors), $db->escapeString($airsday), $db->escapeString($airstime), $db->escapeString($contentrating), + $db->escapeString($firstaired), $db->escapeString($genre), $imdbID, $db->escapeString($network), $db->escapeString($overview), $db->escapeString($rating), + $ratingcount, $runtime, $db->escapeString($seriesname), $db->escapeString($status), $tvdbID)); + } + + public function deleteTitle($tvdbID) + { + $db = new DB(); + + $db->query(sprintf("DELETE FROM thetvdb WHERE tvdbID = %d", $tvdbID)); + } + + public function addEmptySeries($seriesname) + { + $db = new DB(); + + $db->queryInsert(sprintf("INSERT INTO thetvdb (tvdbID, seriesname, createddate) VALUES (0, %s, now())", $db->escapeString($seriesname))); + } + + public function getSeriesInfoByID($tvdbID) + { + $db = new DB(); + return $db->queryOneRow(sprintf("SELECT * FROM thetvdb WHERE tvdbID = %d", $tvdbID)); + } + + public function getSeriesInfoByName($seriesname) + { + $db = new DB(); + return $db->queryOneRow(sprintf("SELECT * FROM thetvdb WHERE seriesname = %s", $db->escapeString($seriesname))); + } + + public function getSeriesRange($start, $num, $seriesname='') + { + $db = new DB(); + + $limit = ($start === false) ? '' : " LIMIT ".$start.",".$num; + + $rsql = ''; + if ($seriesname != '') + $rsql .= sprintf("AND thetvdb.seriesname LIKE %s ", $db->escapeString("%".$seriesname."%")); + + return $db->query(sprintf(" SELECT ID, tvdbID, seriesname, overview FROM thetvdb WHERE 1=1 %s AND tvdbID > %d ORDER BY tvdbID ASC".$limit, $rsql, 0)); + } + + public function getSeriesCount($seriesname='') + { + $db = new DB(); + + $rsql = ''; + if ($seriesname != '') + $rsql .= sprintf("AND thetvdb.seriesname LIKE %s ", $db->escapeString("%".$seriesname."%")); + + $res = $db->queryOneRow(sprintf("SELECT count(ID) AS num FROM thetvdb WHERE 1=1 %s ", $rsql)); + + return $res["num"]; + } + + public function lookupSeriesID($seriesname) + { + $apiresponse = getUrl($this->MIRROR.'/api/GetSeries.php?seriesname='.preg_replace('/\s+/', '+', $seriesname).'&language=all'); + + if(!$apiresponse) + return false; + + $seriesidXML = @simplexml_load_string($apiresponse); + if(!$seriesidXML) + return false; + + $seriesid = 0; + foreach($seriesidXML as $item) + if(preg_match('/^'.preg_replace('/\+/', ' ', str_replace('/', '\/', $seriesname)).'$/i', (string) $item->SeriesName)) { + $seriesid = (int) $item->seriesid; + break; + } + + return $seriesid; + } + + public function notFound($seriesName, $fullep, $releaseID, $echooutput=true) + { + if($this->echooutput && $echooutput) + echo 'TheTVDB : '.$seriesName.' '.$fullep." Not found\n"; + + $db = new DB(); + $db->query(sprintf('UPDATE releases SET episodeinfoID = %d WHERE ID = %d', -2, $releaseID)); + } + + public function processReleases() + { + $db = new DB(); + + $results = $db->queryDirect(sprintf("SELECT ID, searchname, rageID, anidbID, seriesfull, season, episode, tvtitle FROM releases WHERE episodeinfoID IS NULL AND categoryID IN ( SELECT ID FROM category WHERE parentID = %d ) LIMIT 150", Category::CAT_PARENT_TV)); + + if (mysql_num_rows($results) > 0) + { + if ($this->echooutput) + echo "TheTVDB : Looking up last ".mysql_num_rows($results)." releases\n"; + + while ($arr = mysql_fetch_assoc($results)) + { + unset($TheTVDBAPIArray, $episodeArray, $fullep, $epabsolute, $additionalSql); + + $seriesName = ''; + if($arr['rageID'] > 0) { + $seriesName = $db->queryOneRow(sprintf('SELECT releasetitle AS seriesName FROM tvrage WHERE rageID = %d', $arr['rageID'])); + } + elseif($arr['anidbID'] > 0) { + $seriesName = $db->queryOneRow(sprintf('SELECT title AS seriesName FROM anidb WHERE anidbID = %d', $arr['anidbID'])); + } + + if(empty($seriesName) || !$seriesName) + { + $this->notFound($seriesName, "", $arr['ID'], false); + continue; + } + + $seriesName = str_replace('`', '\'', $seriesName['seriesName']); + if(!preg_match('/[21]\d{3}\/\d{2}\/\d{2}/', $arr['seriesfull'])) + $fullep = str_pad(str_replace('S', '', $arr['season']), 2, '0', STR_PAD_LEFT).'x'.str_pad(str_replace('E', '', $arr['episode']), 2, '0', STR_PAD_LEFT); + else + $fullep = str_replace('/', '-', $arr['seriesfull']); + + $TheTVDBAPIArray = $this->getSeriesInfoByName($seriesName); + if(!$TheTVDBAPIArray) + { + $seriesid = $this->lookupSeriesID($seriesName); + if($seriesid > 0) + { + if($TheTVDBAPIArray = $this->TheTVDBAPI($seriesid, $seriesName)) + { + $this->addSeries($TheTVDBAPIArray); + $this->addEpisodes($TheTVDBAPIArray); + } + else + { + $this->addEmptySeries($seriesName); + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else + { + $this->addEmptySeries($seriesName); + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else if($TheTVDBAPIArray['tvdbID'] >= 0 && ((time() - strtotime($TheTVDBAPIArray['createddate'])) > 604800)) + { + $TheTVDBAPIArray = $this->TheTVDBAPI($TheTVDBAPIArray['tvdbID'], $seriesName); + + $this->updateSeries($TheTVDBAPIArray['tvdbID'], $TheTVDBAPIArray['actors'], $TheTVDBAPIArray['airsday'], + $TheTVDBAPIArray['airstime'], $TheTVDBAPIArray['contentrating'], $TheTVDBAPIArray['firstaired'], $TheTVDBAPIArray['genre'], + $TheTVDBAPIArray['imdbID'], $TheTVDBAPIArray['network'], $TheTVDBAPIArray['overview'], $TheTVDBAPIArray['rating'], + $TheTVDBAPIArray['ratingcount'], $TheTVDBAPIArray['runtime'], $TheTVDBAPIArray['seriesname'], $TheTVDBAPIArray['status']); + + $this->addEpisodes($TheTVDBAPIArray); + } + + if($TheTVDBAPIArray['tvdbID'] > 0) + { + $epabsolute = '0'; + if($arr['anidbID'] > 0) + { + if(preg_match('/S(?P\d+)[ED](?P\d+)/', $arr['episode'], $seasonEpisode)) + { + $arr['season'] = $seasonEpisode['season']; + $arr['episode'] = $seasonEpisode['episode']; + } + else + $epabsolute = $arr['episode']; + } + + $Episode = new Episode(); + $episodeArray = $Episode->getEpisodeInfoByName($seriesName, $fullep, (string) $epabsolute); + if(!$episodeArray) + { + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + } + else + { + $this->notFound($seriesName, $fullep, $arr['ID']); + continue; + } + + $additionalSql = ''; + if($arr['anidbID'] > 0 && $episodeArray['epabsolute'] > 0) + { + $additionalSql = sprintf(', season = NULL, episode = %d, tvtitle = %s, tvairdate = %s', + $episodeArray['epabsolute'], + $db->escapeString($episodeArray['epabsolute'].' - '.str_replace('\'', '`', $episodeArray['eptitle'])), + $db->escapeString($episodeArray['airdate'])); + } + + $db->query(sprintf('UPDATE releases SET tvdbID = %d, episodeinfoID = %d %s WHERE ID = %d', + $TheTVDBAPIArray['tvdbID'], $episodeArray['ID'], $additionalSql, $arr['ID'])); + + //if($this->echooutput) + //{ + // echo 'TheTVDB : '.$seriesName.' '.$fullep." returned ".$episodeArray['tvdbID']."\n"; + //} + } + } + } + + public function TheTVDBAPI($seriesid, $seriesName) + { + $apiresponse = getUrl($this->MIRROR.'/api/'.self::APIKEY.'/series/'.$seriesid.'/all/en.xml'); //.zip? + + if(!$apiresponse) + return false; + + $TheTVDBAPIXML = @simplexml_load_string($apiresponse); + if(!$TheTVDBAPIXML) + return false; + + foreach($TheTVDBAPIXML->Episode as $episode) { + $episodetvdbIDArray[] = (int) $episode->id; + $episodenumberArray[] = (int) $episode->Combined_episodenumber; + $episodeseasonArray[] = (int) $episode->Combined_season; + $episodedirectorArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Director); + $episodenameArray[] = preg_replace('/^\||\|$/', '', (string) $episode->EpisodeName); + $episodefirstairedArray[] = (string) $episode->FirstAired; + $episodegueststarsArray[] = preg_replace('/^\||\|$/', '', (string) $episode->GuestStars); + $episodeimdbID[] = str_replace('tt', '', (string) $episode->IMDB_ID); + $episodeoverviewArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Overview); + $episoderatingArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Rating); + $episodewriterArray[] = preg_replace('/^\||\|$/', '', (string) $episode->Writer); + $episodeabsolutenumberArray[] = (int) $episode->absolute_number; + } + + $TheTVDBAPIArray = array( + 'tvdbID' => $seriesid, + 'actors' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Actors), + 'airsday' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Airs_DayOfWeek), + 'airstime' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Airs_Time), + 'contentrating' => (string) $TheTVDBAPIXML->Series->ContentRating, + 'firstaired' => (string) $TheTVDBAPIXML->Series->FirstAired, + 'genre' => preg_replace('/^\||\|$/', '', (string) $TheTVDBAPIXML->Series->Genre), + 'imdbID' => (int) preg_replace('/^[^\d]+/', '', (string) $TheTVDBAPIXML->Series->IMDB_ID), + 'network' => (string) $TheTVDBAPIXML->Series->Network, + 'overview' => (string) $TheTVDBAPIXML->Series->Overview, + 'rating' => (float) $TheTVDBAPIXML->Series->Rating, + 'ratingcount' => (int) $TheTVDBAPIXML->Series->RatingCount, + 'runtime' => (int) $TheTVDBAPIXML->Series->Runtime, + //'seriesname' => ((string) $TheTVDBAPIXML->Series->SeriesName != '') ? (string) $TheTVDBAPIXML->Series->SeriesName : $seriesName, + 'seriesname' => $seriesName, + 'status' => (string) $TheTVDBAPIXML->Series->Status, + 'episodetvdbID' => isset($episodetvdbIDArray) ? $episodetvdbIDArray : array(), + 'episodenumber' => isset($episodenumberArray) ? $episodenumberArray : array(), + 'episodeseason' => isset($episodeseasonArray) ? $episodeseasonArray : array(), + 'episodedirector' => isset($episodedirectorArray) ? $episodedirectorArray : array(), + 'episodename' => isset($episodenameArray) ? $episodenameArray : array(), + 'episodefirstaired' => isset($episodefirstairedArray) ? $episodefirstairedArray : array(), + 'episodegueststars' => isset($episodegueststarsArray) ? $episodegueststarsArray : array(), + 'episodeimdbID' => isset($episodeimdbID) ? $episodeimdbID : array(), + 'episodeoverview' => isset($episodeoverviewArray) ? $episodeoverviewArray : array(), + 'episoderating' => isset($episoderatingArray) ? $episoderatingArray : array(), + 'episodewriter' => isset($episodewriterArray) ? $episodewriterArray : array(), + 'episodeabsolutenumber' => isset($episodeabsolutenumberArray) ? $episodeabsolutenumberArray : array(), + ); + + return $TheTVDBAPIArray; + } +} + diff --git a/bin/monitor.php b/bin/monitor.php index cb9831436..348362eec 100755 --- a/bin/monitor.php +++ b/bin/monitor.php @@ -5,16 +5,23 @@ require_once(WWW_DIR."/lib/postprocess.php"); $_php = getenv("PHP"); $db = new DB(); + //////////////amount of releases////////////// $query = "select count(*) from releases"; -//$query2 = "select format(count(*),0) from releases r left join category c on c.ID = r.categoryID where ((r.passwordstatus between -6 and -1) or (r.haspreview = -1 and c.disablepreview = 0))"; + ///////////amount of post processing left///// +$pquery = "select count(*) from releases r left join category c on c.ID = r.categoryID where (r.passwordstatus between -6 and -1) or (r.haspreview = -1 and c.disablepreview = 0);"; + /////////////amount of post processing to do// +$plquery = "select count(*) from releases r left join category c on c.ID = r.categoryID where (r.passwordstatus=0);"; $result_begin = mysql_query($query); //$result_begin = $db->queryDirect($query); +$presult_begin = mysql_query($pquery); +$plresult_begin = mysql_query($plquery); + /////////////////result_begin//////////////////////// if (empty($result_begin)) { $result_begin = $db->queryDirect($query); if (empty($result_begin)) { - $message = 'Invalid query: ' . mysql_error() . "\n"; + $message = 'Invalid query: ' . mysql_error() . "\n"; $message .= 'Whole query: ' . $query; die($message); } @@ -24,22 +31,63 @@ while ($row = mysql_fetch_assoc($result_begin)) { $count_begin = $row['count(*)']; } + ///////////////////presult_begin/////////////////////// +if (empty($presult_begin)) { + $presult_begin = $db->queryDirect($pquery); + if (empty($presult_begin)) { + $pmessage = 'Invalid query: ' . mysql_error() . "\n"; + $pmessage .= 'Whole query: ' . $pquery; + die($pmessage); + } +} + +while ($prow = mysql_fetch_assoc($presult_begin)) { + $pcount_begin = $prow['count(*)']; +} + + ///////////////////plresult_begin/////////////////////// +if (empty($plresult_begin)) { + $plresult_begin = $db->queryDirect($plquery); + if (empty($plresult_begin)) { + $plmessage = 'Invalid query: ' . mysql_error() . "\n"; + $plmessage .= 'Whole query: ' . $plquery; + die($plmessage); + } +} + +while ($plrow = mysql_fetch_assoc($plresult_begin)) { + $plcount_begin = $plrow['count(*)']; +} + + ////////////////////////time//////////////////////////// $time = TIME(); $i=1; while($i=1) { $result_inner_loop = mysql_query($query); + $presult_inner_loop = mysql_query($pquery); + $plresult_inner_loop = mysql_query($plquery); //$result_inner_loop = $db->queryDirect($query); - sleep(60); + //////////////////////////////////////////////////////// + //////////////////Change these for sleep time/////////// + $sleeptime = "15"; + $sleeptext = "in the past $sleeptime seconds"; + //////////////////////////////////////////////////////// + ////////////////////////print/////////////////////////// + + sleep($sleeptime); $result_loop = mysql_query($query); + $presult_loop = mysql_query($pquery); + $plresult_loop = mysql_query($plquery); //$result_loop = $db->queryDirect($query); + ////////////////result_inner_loop/////////////////////// if (empty($result_inner_loop)) { $result_inner_loop = $db->queryDirect($query); if (empty($result_inner_loop)) { - $message = 'Invalid query: ' . mysql_error() . "\n"; + $message = 'Invalid query: ' . mysql_error() . "\n"; $message .= 'Whole query: ' . $query; die($message); } @@ -49,10 +97,11 @@ while($i=1) $count_inner_loop = $row['count(*)']; } + ///////////////////result_loop///////////////////////// if (empty($result_loop)) { $result_loop = $db->queryDirect($query); if (empty($result_loop)) { - $message = 'Invalid query: ' . mysql_error() . "\n"; + $message = 'Invalid query: ' . mysql_error() . "\n"; $message .= 'Whole query: ' . $query; die($message); } @@ -62,6 +111,48 @@ while($i=1) $count_loop = $row['count(*)']; } + ////////////////presult_inner_loop/////////////////////// + if (empty($presult_inner_loop)) { + $presult_inner_loop = $db->queryDirect($pquery); + if (empty($presult_inner_loop)) { + $pmessage = 'Invalid query: ' . mysql_error() . "\n"; + $pmessage .= 'Whole query: ' . $pquery; + die($pmessage); + } + } + + while ($prow = mysql_fetch_assoc($presult_inner_loop)) { + $pcount_inner_loop = $prow['count(*)']; + } + + ///////////////////presult_loop//////////////////////// + if (empty($presult_loop)) { + $presult_loop = $db->queryDirect($pquery); + if (empty($result_loop)) { + $pmessage = 'Invalid query: ' . mysql_error() . "\n"; + $pmessage .= 'Whole query: ' . $pquery; + die($pmessage); + } + } + + while ($prow = mysql_fetch_assoc($presult_loop)) { + $pcount_loop = $prow['count(*)']; + } + + /////////////////////plresult_loop////////////////////// + if (empty($pdresult_loop)) { + $plresult_loop = $db->queryDirect($plquery); + if (empty($plresult_loop)) { + $plmessage = 'Invalid query: ' . mysql_error() . "\n"; + $plmessage .= 'Whole query: ' . $plquery; + die($plmessage); + } + } + + while ($plrow = mysql_fetch_assoc($plresult_loop)) { + $plcount_loop = $plrow['count(*)']; + } + $secs = TIME() - $time; $mins = floor($secs / 60); $hrs = floor($mins / 60); @@ -72,38 +163,33 @@ while($i=1) $day = ($days % 24); $total_start = $count_loop - $count_begin; $total_loop = $count_loop - $count_inner_loop; + $ptotal_start = -$pcount_loop - -$pcount_begin; + $ptotal_loop = -$pcount_loop - -$pcount_inner_loop; passthru('clear'); printf("Monitoring total releases in your database\n"); - printf("$total_loop releases added in last minute\n"); + printf("$total_loop releases added $sleeptext\n"); printf("$total_start releases added in the last \033[38;5;160m$day\033[0m");printf(" Days "); printf("\033[38;5;208m$hr\033[0m");printf(" Hours "); printf("\033[38;5;020m$min\033[0m");printf(" Minutes "); printf("\033[38;5;063m$sec\033[0m");printf(" Seconds\n"); + printf("$ptotal_loop releases post processed $sleeptext\n"); + printf("$ptotal_start releases post processed in the last \033[38;5;160m$day\033[0m");printf(" Days "); + printf("\033[38;5;208m$hr\033[0m");printf(" Hours "); + printf("\033[38;5;020m$min\033[0m");printf(" Minutes "); + printf("\033[38;5;063m$sec\033[0m");printf(" Seconds\n"); + printf("$plcount_loop have been post processed thus far\n"); + printf("$pcount_loop releases left to post process\n"); printf("$count_loop releases currently in your database\n\n"); - printf("***Info***\n"); - printf("Above left shows the current NFO's being analyzed. Above middle shows the the processing for. Above right show the lookups for TVRage, music, etc. Below left shows the activity of imports - it is looping nzb import, backfill and current fill. Below right shows the activity of applying regex's and creating releases.\n"); - - //check if scripts are running - exec("pgrep -f postprocess_nfo", $pids1); - if (empty($pids1)) { - echo "postprocess_nfo.php is not running, restarting\n"; - exec("tmux send-keys -t NewzNab:0.0 'clear && cd bin && $_php postprocess_nfo.php' C-m"); - } - exec("pgrep -f processAlternate", $pids2); - if (empty($pids2)) { - echo "processAlternate.php is not running, restarting\n"; - exec("tmux send-keys -t NewzNab:0.1 'clear && cd bin && $_php processAlternate.php' C-m"); - } - exec("pgrep -f postprocessing", $pids3); - if (empty($pids3)) { - echo "postprocessing.php is not running, restarting\n"; - exec("tmux send-keys -t NewzNab:0.2 'clear && cd bin && $_php postprocessing.php' C-m"); - } } - mysql_free_result($result_begin); mysql_free_result($result_loop); +mysql_free_result($pcount_begin); +mysql_free_result($presult_loop); +mysql_free_result($plcount_begin); +mysql_free_result($plresult_loop); ?> + + diff --git a/bin/pid.php b/bin/pid.php deleted file mode 100755 index dd9f99080..000000000 --- a/bin/pid.php +++ /dev/null @@ -1,32 +0,0 @@ -_file = "$dir/$name.pid"; - - if (file_exists($this->_file)) { - $pid = trim(file_get_contents($this->_file)); - if (posix_kill($pid, 0)) { - $this->_running = true; - } - } - - if (! $this->_running) { - $pid = getmypid(); - file_put_contents($this->_file, $pid); - } - } - - public function __destruct() { - if ((! $this->_running) && file_exists($this->_file)) { - unlink($this->_file); - } - } - - public function is_already_running() { - return $this->_running; - } - } -?> diff --git a/bin/postProcessing1.sh b/bin/postProcessing1.sh new file mode 100755 index 000000000..af40e9962 --- /dev/null +++ b/bin/postProcessing1.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing Books.....";$PHP processBooks.php + sleep 30 + +done +exit diff --git a/bin/postProcessing2.sh b/bin/postProcessing2.sh new file mode 100755 index 000000000..96518d891 --- /dev/null +++ b/bin/postProcessing2.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing Games....."; $PHP processGames.php + sleep 30 + +done +exit diff --git a/bin/postProcessing3.sh b/bin/postProcessing3.sh new file mode 100755 index 000000000..8b86c985c --- /dev/null +++ b/bin/postProcessing3.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing Movies.....";$PHP processMovies.php + sleep 30 + +done +exit diff --git a/bin/postProcessing4.sh b/bin/postProcessing4.sh new file mode 100755 index 000000000..5d3e5a43d --- /dev/null +++ b/bin/postProcessing4.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing Music.....";$PHP processMusic.php + sleep 30 + +done +exit diff --git a/bin/postProcessing5.sh b/bin/postProcessing5.sh new file mode 100755 index 000000000..23f0e460e --- /dev/null +++ b/bin/postProcessing5.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing TV....." && $PHP processTv.php + sleep 30 + +done +exit + diff --git a/bin/postProcessing6.sh b/bin/postProcessing6.sh new file mode 100755 index 000000000..86f01993d --- /dev/null +++ b/bin/postProcessing6.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e + +source ../edit_these.sh +eval $( sed -n "/^define/ { s/.*('\([^']*\)', '*\([^']*\)'*);/export \1=\"\2\"/; p }" $NEWZPATH/www/config.php ) + +while : +do + + echo "Processing Others....." && $PHP processOthers.php & + echo "Processing Unwanted....." && $PHP processUnwanted.php & + + wait + sleep 30 + +done +exit diff --git a/bin/postprocess_nfo.php b/bin/postprocess_nfo.php index 6f984d209..9cad8af39 100755 --- a/bin/postprocess_nfo.php +++ b/bin/postprocess_nfo.php @@ -1,16 +1,7 @@ is_already_running()) { - echo "Already running.\n"; - exit; - } $db = new DB(); $query = "select count(rn.releaseID) from releasenfo rn left outer join releases r ON r.ID = rn.releaseID WHERE rn.nfo IS NULL AND rn.attempts < 5"; @@ -19,7 +10,6 @@ $i=1; while($i=1) { $result = mysql_query($query); - //$result = $db->queryDirect($query); if (empty($result)) { $result = $db->queryDirect($query); @@ -33,7 +23,6 @@ while($i=1) while ($row = mysql_fetch_assoc($result)) { $count = $row['count(rn.releaseID)']; } - if ($count > 0) { $postprocess = new PostProcess(true); $postprocess->processNfos(); diff --git a/bin/postprocessing.php b/bin/postprocessing.php deleted file mode 100755 index 3d90ef22a..000000000 --- a/bin/postprocessing.php +++ /dev/null @@ -1,56 +0,0 @@ -is_already_running()) { - echo "Already running.\n"; - exit; - } - -$i=1; -while($i=1) -{ - { - $postprocess = new PostProcess(true); - $postprocess->processTv(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processGames(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processMovies(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processMusic(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processBooks(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processUnwanted(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processMusicFromMediaInfo(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processOtherMiscCategory(); - } - { - $postprocess = new PostProcess(true); - $postprocess->processUnknownCategory(); - } -} - -?> diff --git a/bin/processAlternate.php b/bin/processAlternate.php deleted file mode 100755 index 34abec668..000000000 --- a/bin/processAlternate.php +++ /dev/null @@ -1,21 +0,0 @@ -is_already_running()) { - echo "Already running.\n"; - exit; - } - -$i=1; -while($i=1) - -{ - $postprocess = new PostProcess(true); - $postprocess->processAdditional(); -} diff --git a/bin/processAlternate2.php b/bin/processAlternate2.php new file mode 100755 index 000000000..ef5e4c6f6 --- /dev/null +++ b/bin/processAlternate2.php @@ -0,0 +1,41 @@ +queryDirect($query); + if (empty($result)) { + $message = 'Invalid query: ' . mysql_error() . "\n"; + $message .= 'Whole query: ' . $query; + die($message); + } + } + + while ($row = mysql_fetch_assoc($result)) { + $count = $row['format(count(*),0)']; + } + + if ($count > 50) { + $postprocess = new PostProcess2(true); + $postprocess->processAdditional2(); + } else { + echo "$count releases left to process\n"; + sleep(10); + } +} + +mysql_free_result($result); + +?> + diff --git a/bin/processAlternate3.php b/bin/processAlternate3.php new file mode 100755 index 000000000..3190cf253 --- /dev/null +++ b/bin/processAlternate3.php @@ -0,0 +1,39 @@ +queryDirect($query); + if (empty($result)) { + $message = 'Invalid query: ' . mysql_error() . "\n"; + $message .= 'Whole query: ' . $query; + die($message); + } + } + + while ($row = mysql_fetch_assoc($result)) { + $count = $row['format(count(*),0)']; + } + + if ($count > 200) { + $postprocess = new PostProcess3(true); + $postprocess->processAdditional3(); + } else { + echo "$count releases left to process\n"; + sleep(10); + } +} + +mysql_free_result($result); + +?> diff --git a/bin/processBooks.php b/bin/processBooks.php new file mode 100755 index 000000000..64565f3c3 --- /dev/null +++ b/bin/processBooks.php @@ -0,0 +1,15 @@ +processBooks(); + } +} + +?> diff --git a/bin/processGames.php b/bin/processGames.php new file mode 100755 index 000000000..d2e050fee --- /dev/null +++ b/bin/processGames.php @@ -0,0 +1,15 @@ +processGames(); + } +} + +?> diff --git a/bin/processMovies.php b/bin/processMovies.php new file mode 100755 index 000000000..303c121fd --- /dev/null +++ b/bin/processMovies.php @@ -0,0 +1,15 @@ +processMovies(); + } +} + +?> diff --git a/bin/processMusic.php b/bin/processMusic.php new file mode 100755 index 000000000..0169a34f3 --- /dev/null +++ b/bin/processMusic.php @@ -0,0 +1,15 @@ +processMusic(); + } +} + +?> diff --git a/bin/processOthers.php b/bin/processOthers.php new file mode 100755 index 000000000..dd860ac3a --- /dev/null +++ b/bin/processOthers.php @@ -0,0 +1,23 @@ +processMusicFromMediaInfo(); + } + { + $postprocess = new PostProcess(true); + $postprocess->processOtherMiscCategory(); + } + { + $postprocess = new PostProcess(true); + $postprocess->processUnknownCategory(); + } +} + +?> diff --git a/bin/processTv.php b/bin/processTv.php new file mode 100755 index 000000000..2367cb83d --- /dev/null +++ b/bin/processTv.php @@ -0,0 +1,15 @@ +processTv(); + } +} + +?> diff --git a/bin/processUnwanted.php b/bin/processUnwanted.php new file mode 100755 index 000000000..ba00a9873 --- /dev/null +++ b/bin/processUnwanted.php @@ -0,0 +1,15 @@ +processUnwanted(); + } +} + +?> diff --git a/bin/workhorse.sh b/bin/workhorse.sh index 1b72bac3e..05a2abca9 100755 --- a/bin/workhorse.sh +++ b/bin/workhorse.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -e source ../edit_these.sh @@ -111,4 +110,4 @@ else done fi - +exit diff --git a/convert.php b/convert.php new file mode 100755 index 000000000..40b38b512 --- /dev/null +++ b/convert.php @@ -0,0 +1,22 @@ +query("ALTER TABLE binaries ENGINE=InnoDB ROW_FORMAT=DYNAMIC;"); +echo "Finished converting the binaries table back to InnoDB.\n\n"; + +echo "Converting the parts table back to InnoDB\n"; +$db->query("ALTER TABLE parts ENGINE=InnoDB ROW_FORMAT=DYNAMIC;"); +echo "Finished converting the parts table back to InnoDB.\n\n"; + +echo "Converting the releases table back to InnoDB\n"; +$db->query("ALTER TABLE releases ENGINE=InnoDB ROW_FORMAT=DYNAMIC;"); +echo "Finished converting the releases table back to InnoDB.\n\n"; + +?> diff --git a/edit_these.sh b/edit_these.sh index 8be81c23d..66256fcda 100755 --- a/edit_these.sh +++ b/edit_these.sh @@ -8,11 +8,11 @@ export NEWZNAB_PATH=$NEWZPATH"/misc/update_scripts" export INNODB_PATH=$NEWZPATH"/misc/testing/innodb" export TESTING_PATH=$NEWZPATH"/misc/testing" export ADMIN_PATH=$NEWZPATH"/www/admin" -export USERNAME="whats your name" # this is the user name that will run these scripts +export USERNAME="jonnyboy" # this is the user name that will run these scripts export NEWZNAB_IMPORT_SLEEP_TIME="60" # in seconds - this includes import_nzb backfill and current fill export NEWZNAB_POST_SLEEP_TIME="1" # in seconds - this is for post processing - sleep between loops export MAXDAYS="200" #max days for backfill -export NZBS="/path/to/nzbs" #path to your nzb files to be imported +export NZBS="/home/jonnyboy/nzbs/batch" #path to your nzb files to be imported #Choose to run the threaded or non-threaded newznab scripts true/false export THREADS="true" @@ -23,16 +23,10 @@ export INNODB="true" #Choose to run update_cleanup.php true/false export CLEANUP="true" -#Choose which app to run in the middle right pane, of course it must be installed. Your choices are mytop and bwm-ng. -#Only have 1 uncommented at a time. - -#export CHOICE_APP="bwm-ng" -export CHOICE_APP="mytop" - #By using this script you understand that the programmer is not responsible for any loss of data, users, or sanity. #You also agree that you were smart enough to make a backup of your database and files. Do you agree? yes/no -export AGREED="no" +export AGREED="yes" ##END OF EDITS## @@ -41,10 +35,6 @@ command -v mysql >/dev/null 2>&1 || { echo >&2 "I require mysql but it's not ins command -v sed >/dev/null 2>&1 || { echo >&2 "I require sed but it's not installed. Aborting."; exit 1; } && export SED=`command -v sed` command -v php5 >/dev/null 2>&1 && export PHP=`command -v php5` || { export PHP=`command -v php`; } command -v tmux >/dev/null 2>&1 || { echo >&2 "I require tmux but it's not installed. Aborting."; exit 1; } && export TMUX=`command -v tmux` -if [[ $CHOICE_APP == "bwm-ng" ]]; then - command -v bwm-ng >/dev/null 2>&1 || { echo >&2 "I require bwm-ng but it's not installed. Aborting."; exit 1; } && export BWMNG=`command -v bwm-ng` -fi -if [[ $CHOICE_APP == "mytop" ]]; then - command -v mytop >/dev/null 2>&1|| { echo >&2 "I require mytop but it's not installed. Aborting."; exit 1; } && export MYTOP=`command -v mytop` -fi +command -v nmon >/dev/null 2>&1 || { echo >&2 "I require nmon but it's not installed. Aborting."; exit 1; } && export NMON=`command -v nmon` +command -v mytop >/dev/null 2>&1|| { echo >&2 "I require mytop but it's not installed. Aborting."; exit 1; } && export MYTOP=`command -v mytop` diff --git a/revert.sh b/revert.sh index c9228d4fe..3123a058f 100755 --- a/revert.sh +++ b/revert.sh @@ -30,3 +30,5 @@ else fi echo "my edits have been removed" + +exit diff --git a/set_perms.sh b/set_perms.sh index 0dd63705c..e7641b7de 100755 --- a/set_perms.sh +++ b/set_perms.sh @@ -78,3 +78,12 @@ chmod -R 777 $NEWZPATH/nzbfiles chmod 777 $NEWZPATH/nzbfiles/tmpunrar/ echo -e "\033[38;5;160mCompleted\033[39m" + +echo -e "This script includes nmon and mytop. Please install them prior to running ./start.sh." +echo -e "If the nmon pane close when you select networking, then you will need to use sudo or su." +echo -e "Tmux is very easy to use. To detach from the current session, use Ctrl-a d. You can select" +echo -e "simply by clicking in it and you can resize by dragging the borders." + + +exit + diff --git a/start.sh b/start.sh index 9b618542b..88dffaeb6 100755 --- a/start.sh +++ b/start.sh @@ -18,22 +18,32 @@ if [[ $AGREED == "no" ]]; then exit fi -$TMUX new-session -d -s NewzNab -n NewzNab 'echo "processNfos Working......" && $PHP bin/postprocess_nfo.php;exec bash -i' +$TMUX new-session -d -s NewzNab -n NewzNab 'echo "processNfos Working......" && sleep 3 && $PHP bin/postprocess_nfo.php;exec bash -i' $TMUX selectp -t 0 -$TMUX splitw -v -p 82 'echo "monitor Working......" && $PHP bin/monitor.php;exec bash -i' -$TMUX splitw -v -p 66 'cd bin && echo "imports Working......" && ./workhorse.sh;exec bash -i' +$TMUX splitw -v -p 80 'echo "monitor Working......" && $PHP bin/monitor.php;exec bash -i' $TMUX selectp -t 0 -$TMUX splitw -h -p 66 'echo "processAdditional Working......" && $PHP bin/processAlternate.php;exec bash -i' -$TMUX splitw -h -p 50 'echo "postProcessing Working......" && $PHP bin/postprocessing.php;exec bash -i' +$TMUX splitw -h -p 80 'echo "processAdditional Thread #1 Working......" && sleep 6 && $PHP bin/processAlternate2.php;exec bash -i' $TMUX selectp -t 3 -if [[ $CHOICE_APP == "mytop" ]]; then - $TMUX splitw -h -p 50 '$MYTOP -u $DB_USER -p $DB_PASSWORD -d $DB_NAME -h $DB_HOST' -else - $TMUX splitw -h -p 50 '$BWMNG' -fi -$TMUX selectp -t 5 -$TMUX splitw -h -p 50 'cd bin && echo "create Releases Working......" && ./cleanup_scripts.sh;exec bash -i' +$TMUX splitw -h -p 67 'cd bin && echo "Processing Books....." && sleep 12 && ./postProcessing1.sh;exec bash -i' +$TMUX splitw -h -p 50 'cd bin && echo "Processing Music....." && sleep 21 && ./postProcessing4.sh;exec bash -i' +$TMUX selectp -t 1 +$TMUX splitw -v -p 50 'echo "processAdditional Thread #2 Working......" && sleep 9 && $PHP bin/processAlternate3.php;exec bash -i' +$TMUX selectp -t 3 +$TMUX splitw -v -p 67 'cd bin && echo "Processing Games....." && sleep 15 && ./postProcessing2.sh;exec bash -i' +$TMUX splitw -v -p 50 'cd bin && echo "Processing Movies....." && sleep 18 && ./postProcessing3.sh;exec bash -i' +$TMUX selectp -t 6 +$TMUX splitw -v -p 67 'cd bin && echo "Processing TV....." && sleep 24 && ./postProcessing5.sh;exec bash -i' +$TMUX splitw -v -p 50 'cd bin && echo "Processing Other....." && sleep 27 && ./postProcessing6.sh;exec bash -i' +$TMUX selectp -t 9 +$TMUX splitw -v -p 63 'cd bin && echo "imports Working......" && ./workhorse.sh;exec bash -i' +$TMUX selectp -t 9 +$TMUX splitw -h -p 67 'nmon' +$TMUX splitw -h -p 50 '$MYTOP -u $DB_USER -p $DB_PASSWORD -d $DB_NAME -h $DB_HOST' +$TMUX selectp -t 12 +$TMUX splitw -h -p 50 'cd bin && echo "create Releases Working......" && ./cleanup_scripts.sh;exec bash -i' + $TMUX select-window -tNewzNab:0 $TMUX attach-session -d -tNewzNab +