Port many fixes from nZEDb

This commit is contained in:
DariusIII
2016-05-12 09:45:08 +02:00
parent a5500debda
commit 0b136662c6
12 changed files with 535 additions and 343 deletions
+2
View File
@@ -1,3 +1,5 @@
2016-05-12 DariusIII
* Upd: Port many fixes from nZEDb
2016-05-11 DariusIII
* Upd: Composer update colorbox and phpmailer to newest versions
* Upd: Split UPDATE and DELETE query after NZB is created for release
+1
View File
@@ -685,6 +685,7 @@ newznab-tmux https://github.com/DariusIII/newznab-tmux
usenetdate ALL Date posted to usenet "Tue, 22 Jun 2010 06:54:22 +0100"
info ALL Info (.nfo) file URL "http://somesite/stuff/info.php?id=1234"
year ALL Release year "2009"
prematch ALL Has valid PreDB match "0" no "1" yes
season TV Numeric season "1"
episode TV Numeric episode within the season "1"
videos_id TV, Movies, Anime Local Video ID "1"
+5
View File
@@ -1090,6 +1090,11 @@ class RarInfo extends ArchiveReader
$block['av_created_by'] = $this->read($block['av_cname_size']);
}
}
// Add method if not set
if (!isset($block['method'])) {
$block['method'] = self::METHOD_NORMAL;
}
}
/**
+67 -66
View File
@@ -1,76 +1,77 @@
<?php
require_once realpath(dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'indexer.php');
use newznab\ConsoleTools;
use newznab\NZB;
use newznab\ReleaseImage;
use newznab\Releases;
use newznab\db\Settings;
use newznab\utility\Utility;
$pdo = new Settings();
if (isset($argv[1]) && ($argv[1] === "true" || $argv[1] === "delete")) {
$releases = new \Releases(['Settings' => $pdo]);
$nzb = new \NZB($pdo);
$releaseImage = new \ReleaseImage($pdo);
$consoletools = new \ConsoleTools(['ColorCLI' => $pdo->log]);
$timestart = time();
$checked = $deleted = 0;
$couldbe = $argv[1] === "true" ? $couldbe = "could be " : "were ";
echo $pdo->log->header('Getting List of nzbs to check against db.');
$dirItr = new \RecursiveDirectoryIterator($pdo->getSetting('nzbpath'));
$itr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($itr as $filePath) {
if (is_file($filePath) && preg_match('/([a-f-0-9]+)\.nzb\.gz/', $filePath, $guid)) {
$nzbfile = Utility::unzipGzipFile($filePath);
if ($nzbfile) {
$nzbfile = @simplexml_load_string($nzbfile);
}
if ($nzbfile) {
$res = $pdo->queryOneRow(sprintf("SELECT id, guid FROM releases WHERE guid = %s", $pdo->escapeString(stristr($filePath->getFilename(), '.nzb.gz', true))));
if ($res === false) {
if ($argv[1] === "delete") {
@copy($filePath, NN_ROOT . "pooped/" . $guid[1] . ".nzb.gz");
$releases->deleteSingle(['g' => $guid[1], 'i' => false], $nzb, $releaseImage);
$deleted++;
}
} else if (isset($res)) {
$pdo->queryExec(sprintf("UPDATE releases SET nzbstatus = 1 WHERE id = %s", $res['id']));
}
} else {
if ($argv[1] === "delete") {
@copy($filePath, NN_ROOT . "pooped/" . $guid[1] . ".nzb.gz");
unlink($filePath);
$deleted++;
}
}
$time = $consoletools->convertTime(time() - $timestart);
$consoletools->overWritePrimary('Checking NZBs: ' . $deleted . ' nzbs of ' . ++$checked . ' releases checked ' . $couldbe . 'deleted from disk, Running time: ' . $time);
}
}
echo $pdo->log->header("\n" . number_format($checked) . ' nzbs checked, ' . number_format($deleted) . ' nzbs ' . $couldbe . 'deleted.');
$dir = NN_RES . "movednzbs/";
$timestart = time();
$checked = $deleted = 0;
echo $pdo->log->header("Getting List of releases to check against nzbs.");
$res = $pdo->queryDirect('SELECT id, guid FROM releases');
if ($res instanceof \Traversable) {
foreach ($res as $row) {
$nzbpath = $nzb->getNZBPath($row["guid"]);
if (!file_exists($nzbpath)) {
if ($argv[1] === "delete") {
@copy($nzbpath, NN_ROOT . "pooped/" . $row["guid"] . ".nzb.gz");
$releases->deleteSingle(['g' => $row['guid'], 'i' => $row['id']], $nzb, $releaseImage);
}
$deleted++;
} else if (file_exists($nzbpath) && isset($row)) {
$pdo->queryExec(sprintf("UPDATE releases SET nzbstatus = 1 WHERE id = %s", $row['id']));
}
$time = $consoletools->convertTime(time() - $timestart);
$consoletools->overWritePrimary('Checking Releases: ' . $deleted . " releases have no nzb of " . ++$checked . " and " . $couldbe . "deleted from db, Running time: " . $time);
}
}
echo $pdo->log->header("\n" . number_format($checked) . " releases checked, " . number_format($deleted) . " releases " . $couldbe . "deleted.");
} else {
exit($pdo->log->error("\nThis script can remove all nzbs not found in the db and all releases with no nzbs found. It can also delete invalid nzbs.\n\n"
. "php $argv[0] true ...: For a dry run, to see how many would be deleted.\n"
. "php $argv[0] delete ...: To delete all affected.\n"));
if (!isset($argv[1]) || !in_array($argv[1], ["true", "move"])) {
exit($pdo->log->error("\nThis script can remove all nzbs not found in the db and all releases with no nzbs found. It can also move invalid nzbs.\n\n"
. "php $argv[0] true ...: For a dry run, to see how many would be moved.\n"
. "php $argv[0] move ...: Move NZBs that are possibly bad or have no release. They are moved into this folder: $dir\n"));
}
if (!is_dir($dir) && !mkdir($dir)) {
exit("ERROR: Could not create folder [$dir]." . PHP_EOL);
}
$releases = new Releases(['Settings' => $pdo]);
$nzb = new NZB($pdo);
$releaseImage = new ReleaseImage($pdo);
$timestart = date("r");
$checked = $moved = 0;
$couldbe = ($argv[1] === "true") ? "could be " : "";
echo $pdo->log->header('Getting List of nzbs to check against db.');
echo $pdo->log->header("Checked / {$couldbe}moved\n");
$dirItr = new \RecursiveDirectoryIterator($pdo->getSetting('nzbpath'));
$itr = new \RecursiveIteratorIterator($dirItr, \RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($itr as $filePath) {
$guid = stristr($filePath->getFilename(), '.nzb.gz', true);
if (is_file($filePath) && $guid) {
$nzbfile = Utility::unzipGzipFile($filePath);
$nzbContents = $nzb->nzbFileList($nzbfile, ['no-file-key' => false, 'strip-count' => true]);
if (!$nzbfile || !@simplexml_load_string($nzbfile) || count($nzbContents) === 0) {
if ($argv[1] === "move") {
rename($filePath, $dir . $guid . ".nzb.gz");
}
$releases->deleteSingle(['g' => $guid, 'i' => false], $nzb, $releaseImage);
$moved++;
}
++$checked;
echo "$checked / $moved\r";
}
}
echo $pdo->log->header("\n" . number_format($checked) . ' nzbs checked, ' . number_format($moved) . ' nzbs ' . $couldbe . 'moved.');
echo $pdo->log->header("Getting List of releases to check against nzbs.");
echo $pdo->log->header("Checked / releases deleted\n");
$checked = $deleted = 0;
$res = $pdo->queryDirect('SELECT id, guid, nzbstatus FROM releases');
if ($res instanceof \Traversable) {
foreach ($res as $row) {
$nzbpath = $nzb->getNZBPath($row["guid"]);
if (!is_file($nzbpath)) {
++$deleted;
$releases->deleteSingle(['g' => $row['guid'], 'i' => $row['id']], $nzb, $releaseImage);
} elseif ($row["nzbstatus"] != 1) {
$pdo->queryExec(sprintf("UPDATE releases SET nzbstatus = 1 WHERE id = %d", $row['id']));
}
++$checked;
echo "$checked / $deleted\r";
}
}
echo $pdo->log->header("\n" . number_format($checked) . " releases checked, " . number_format($deleted) . " releases deleted.");
echo $pdo->log->header("Script started at [$timestart], finished at [" . date("r") . "]");
+209
View File
@@ -0,0 +1,209 @@
<?php
/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see LICENSE.txt in the base directory. If
* not, see:
*
* @link <http://www.gnu.org/licenses/>.
* @author ruhllatio
* @copyright 2016 nZEDb
*/
namespace newznab;
use newznab\db\Settings;
use newznab\utility\Utility;
/**
* Class API
*
* @package newznab
*/
class API {
/** Settings class
* @var \newznab\db\Settings
*/
public $pdo;
/**
* @var array $_GET The get request from the web server
*/
public $getRequest;
/**
* @param array $options
*/
public function __construct(array $options = [])
{
$defaults = [
'Settings' => null,
'Request' => null,
];
$options += $defaults;
$this->pdo = ($options['Settings'] instanceof Settings ? $options['Settings'] : new Settings());
$this->getRequest = $options['Request'];
}
/**
* Print XML or JSON output.
* @param array $data Data to print.
* @param bool $xml True: Print as XML False: Print as JSON.
* @param \BasePage $page
* @param int $offset Offset for limit.
*/
function printOutput($data, $xml = true, $page, $offset = 0)
{
if ($xml) {
$page->smarty->assign('offset', $offset);
$page->smarty->assign('releases', $data);
$response = trim($page->smarty->fetch('apiresult.tpl'));
header('Content-type: text/xml');
header('Content-Length: ' . strlen($response));
echo $response;
} else {
$response = $this->encodeAsJSON($data);
header('Content-type: application/json');
header('Content-Length: ' . strlen($response));
echo $response;
}
}
/**
* Add language from media info XML to release search names (Used by API)
* @param array $releases
*/
public function addLanguage(&$releases)
{
if ($releases && count($releases)) {
foreach ($releases as $key => $release) {
if (isset($release['id'])) {
$language = $this->pdo->queryOneRow("
SELECT audiolanguage
FROM audio_data
WHERE releases_id = {$release['id']}"
);
if ($language !== false) {
$releases[$key]['searchname'] = $releases[$key]['searchname'] . ' ' . $language['audiolanguage'];
}
}
}
}
}
/**
* Verify maxage parameter.
*
* @return int $maxAge The maximum age of the release
*/
public function maxAge()
{
$maxAge = -1;
if (isset($this->getRequest['maxage'])) {
if ($this->getRequest['maxage'] == '') {
Utility::showApiError(201, 'Incorrect parameter (maxage must not be empty)');
} elseif (!is_numeric($this->getRequest['maxage'])) {
Utility::showApiError(201, 'Incorrect parameter (maxage must be numeric)');
} else {
$maxAge = (int)$this->getRequest['maxage'];
}
}
return $maxAge;
}
/**
* Verify cat parameter.
* @return array
*/
public function categoryID()
{
$categoryID[] = -1;
if (isset($this->getRequest['cat'])) {
$categoryIDs = urldecode($this->getRequest['cat']);
// Append Web-DL category ID if HD present for SickBeard / Sonarr compatibility.
if (strpos($categoryIDs, (string)Category::TV_HD) !== false &&
strpos($categoryIDs, (string)Category::TV_WEBDL) === false) {
$categoryIDs .= (',' . Category::TV_WEBDL);
}
$categoryID = explode(',', $categoryIDs);
}
return $categoryID;
}
/**
* Verify limit parameter.
* @return int
*/
public function limit()
{
$limit = 100;
if (isset($this->getRequest['limit']) && is_numeric($this->getRequest['limit']) && $this->getRequest['limit'] < 100) {
$limit = (int)$this->getRequest['limit'];
}
return $limit;
}
/**
* Verify offset parameter.
* @return int
*/
public function offset()
{
$offset = 0;
if (isset($this->getRequest['offset']) && is_numeric($this->getRequest['offset'])) {
$offset = (int)$this->getRequest['offset'];
}
return $offset;
}
/**
* Check if a parameter is empty.
* @param string $parameter
*/
public function verifyEmptyParameter($parameter)
{
if (isset($this->getRequest[$parameter]) && $this->getRequest[$parameter] == '') {
Utility::showApiError(201, 'Incorrect parameter (' . $parameter . ' must not be empty)');
}
}
/**
* Inject the coverurl
*
* @param $releases
* @param callable $getCoverURL
*/
public function addCoverURL(&$releases, callable $getCoverURL)
{
if ($releases && count($releases)) {
foreach ($releases as $key => $release) {
$coverURL = $getCoverURL($release);
$releases[$key]['coverurl'] = $coverURL;
}
}
}
/**
* @param $data
*
* @return mixed
*/
public function encodeAsJSON($data)
{
$json = json_encode(Utility::encodeAsUTF8($data));
if ($json === false) {
Utility::showApiError(201);
}
return $json;
}
}
+100 -26
View File
@@ -45,6 +45,17 @@ class NZB
*/
public $pdo;
/**
* @var \newznab\Logger
*/
protected $debugging;
/**
* @var bool
*/
protected $_debug = false;
/**
* Current newznab version.
* @var string
@@ -105,6 +116,17 @@ class NZB
if (substr($this->siteNzbPath, -1) !== DS) {
$this->siteNzbPath .= DS;
}
$this->_debug = (NN_DEBUG || NN_LOGGING);
if (NN_DEBUG || NN_LOGGING) {
$this->_debug = true;
try {
$this->debugging = new Logger(['ColorCLI' => $this->pdo->log]);
} catch (LoggerException $error) {
$this->_debug = false;
}
}
}
/**
@@ -177,6 +199,8 @@ class NZB
{
$path = ($this->buildNZBPath($relGuid, $this->nzbSplitLevel, true) . $relGuid . '.nzb.gz');
$fp = gzopen($path, 'wb7');
$complete = false;
if ($fp) {
$nzb_guid = '';
gzwrite(
@@ -194,7 +218,8 @@ class NZB
foreach ($collections as $collection) {
$poster = htmlspecialchars($collection['fromname'], ENT_QUOTES, 'utf-8');
$binaries = $this->pdo->queryDirect(sprintf($this->_binariesQuery, $collection['id']));
$binaries = $this->pdo->queryDirect(sprintf($this->_binariesQuery,
$collection['id']));
if ($binaries instanceof \Traversable) {
@@ -203,7 +228,8 @@ class NZB
// Buffer segment writes, increases performance.
$string = '';
$parts = $this->pdo->queryDirect(sprintf($this->_partsQuery, $binary['id']));
$parts = $this->pdo->queryDirect(sprintf($this->_partsQuery,
$binary['id']));
if ($parts instanceof \Traversable) {
@@ -214,49 +240,97 @@ class NZB
$string .= (
' <segment bytes="' . $part['size']
. '" number="' . $part['partnumber'] . '">'
. htmlspecialchars($part['messageid'], ENT_QUOTES, 'utf-8')
. htmlspecialchars($part['messageid'],
ENT_QUOTES,
'utf-8')
. "</segment>\n"
);
}
$complete = true;
} else {
if ($this->_debug) {
$this->debugging->log(
get_class(),
__FUNCTION__,
'ERROR: no parts for release '
. $relID,
Logger::LOG_WARNING
);
}
}
gzwrite($fp,
'<file poster="' . $poster .
'" date="' . $collection['udate'] .
'" subject="' . htmlspecialchars($binary['name'], ENT_QUOTES, 'utf-8') .
' (1/' . $binary['totalparts'] . ")\">\n <groups>\n <group>" . $collection['groupname'] .
"</group>\n </groups>\n <segments>\n" . $string . " </segments>\n</file>\n"
'" subject="' .
htmlspecialchars($binary['name'], ENT_QUOTES, 'utf-8') .
' (1/' . $binary['totalparts'] . ")\">\n <groups>\n <group>" .
$collection['groupname'] .
"</group>\n </groups>\n <segments>\n" . $string .
" </segments>\n</file>\n"
);
}
} else {
if ($this->_debug) {
$this->debugging->log(
get_class(),
__FUNCTION__,
'ERROR: no binaries for release '
. $relID,
Logger::LOG_WARNING
);
}
}
}
} else {
if ($this->_debug) {
$this->debugging->log(
get_class(),
__FUNCTION__,
'ERROR: no collections for release '
. $relID,
Logger::LOG_WARNING
);
}
}
gzwrite($fp, '</nzb>');
gzclose($fp);
if (is_file($path)) {
// Mark release as having NZB.
$this->pdo->queryExec(
sprintf('
UPDATE releases SET nzbstatus = %d %s WHERE id = %d',
NZB::NZB_ADDED, ($nzb_guid === '' ? '' : ', nzb_guid = UNHEX( ' . $this->pdo->escapeString(md5($nzb_guid)) . ' )'),
$relID
)
);
// Delete CBP for release that has its NZB created.
$this->pdo->queryExec(
sprintf('
DELETE c, b, p FROM %s c JOIN %s b ON(c.id=b.collection_id) STRAIGHT_JOIN %s p ON(b.id=p.binaryid) WHERE c.releaseid = %d',
$this->_tableNames['cName'], $this->_tableNames['bName'], $this->_tableNames['pName'], $relID
)
);
if ($complete) {
// Chmod to fix issues some users have with file permissions.
chmod($path, 0777);
return true;
if (is_file($path)) {
// Mark release as having NZB.
$this->pdo->queryExec(
sprintf('
UPDATE releases SET nzbstatus = %d %s WHERE id = %d',
NZB::NZB_ADDED, ($nzb_guid === '' ? '' : ', nzb_guid = UNHEX( ' . $this->pdo->escapeString(md5($nzb_guid)) . ' )'),
$relID
)
);
// Delete CBP for release that has its NZB created.
$this->pdo->queryExec(
sprintf('
DELETE c, b, p FROM %s c JOIN %s b ON(c.id=b.collection_id) STRAIGHT_JOIN %s p ON(b.id=p.binaryid) WHERE c.releaseid = %d',
$this->_tableNames['cName'],
$this->_tableNames['bName'],
$this->_tableNames['pName'],
$relID
)
);
// Chmod to fix issues some users have with file permissions.
chmod($path, 0777);
return true;
} else {
echo "ERROR: $path does not exist.\n";
}
} else {
echo "ERROR: $path does not exist.\n";
if (is_file($path)) {
@unlink($path);
}
}
}
return false;
+2 -1
View File
@@ -592,12 +592,13 @@ class NameFixer
*/
public function updateRelease($release, $name, $method, $echo, $type, $nameStatus, $show, $preId = 0)
{
$release['releases_id'] = (isset($release['releases_id']) ? $release['releases_id'] : $release["releaseid"]);
if ($this->relid !== $release['releaseid']) {
$releaseCleaning = new ReleaseCleaning($this->pdo);
$newName = $releaseCleaning->fixerCleaner($name);
if (strtolower($newName) != strtolower($release["searchname"])) {
$this->matched = true;
$this->relid = $release["releaseid"];
$this->relid = $release['releases_id'];
$determinedCategory = $this->category->determineCategory($release['groupid'], $newName);
+47 -32
View File
@@ -192,10 +192,11 @@ class Releases
* @param int $maxAge
* @param array $excludedCats
* @param string $groupName
* @param string $minSize
*
* @return array
*/
public function getBrowseRange($cat, $start, $num, $orderBy, $maxAge = -1, $excludedCats = [], $groupName = '')
public function getBrowseRange($cat, $start, $num, $orderBy, $maxAge = -1, $excludedCats = [], $groupName = '', $minSize = 0)
{
$orderBy = $this->getBrowseOrder($orderBy);
@@ -215,7 +216,7 @@ class Releases
LEFT JOIN groups g ON g.id = r.groupid
WHERE r.nzbstatus = %d
AND r.passwordstatus %s
%s %s %s %s
%s %s %s %s %s
ORDER BY %s %s %s
) r
INNER JOIN categories c ON c.id = r.categories_id
@@ -233,6 +234,7 @@ class Releases
($maxAge > 0 ? (" AND postdate > NOW() - INTERVAL " . $maxAge . ' DAY ') : ''),
(count($excludedCats) ? (' AND r.categories_id NOT IN (' . implode(',', $excludedCats) . ')') : ''),
($groupName != '' ? sprintf(' AND g.name = %s ', $this->pdo->escapeString($groupName)) : ''),
($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : ''),
$orderBy[0],
$orderBy[1],
($start === false ? '' : ' LIMIT ' . $num . ' OFFSET ' . $start)
@@ -613,8 +615,11 @@ class Releases
LEFT OUTER JOIN video_data rv ON rv.releases_id = r.id
LEFT OUTER JOIN releaseextrafull re ON re.releases_id = r.id
LEFT OUTER JOIN dnzb_failures df ON df.release_id = r.id
WHERE r.guid = %s',
$this->pdo->escapeString($identifiers['g'])
WHERE %s',
(isset($identifiers['i']) && $identifiers['i'] > 0
? "r.id = {$identifiers['i']}"
: "r.guid = {$this->pdo->escapeString($identifiers['g'])}"
)
)
);
}
@@ -824,7 +829,8 @@ class Releases
$maxAge = -1,
$excludedCats = [],
$type = 'basic',
$cat = [-1]
$cat = [-1],
$minSize = 0
) {
$sizeRange = [
1 => 1,
@@ -863,8 +869,7 @@ class Releases
}
$whereSql = sprintf(
"%s
WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s %s %s %s %s",
"%s WHERE r.passwordstatus %s AND r.nzbstatus = %d %s %s %s %s %s %s %s %s %s %s %s %s",
$this->releaseSearch->getFullTextJoinString(),
$this->showPasswords,
NZB::NZB_ADDED,
@@ -878,7 +883,8 @@ class Releases
($daysNew != -1 ? sprintf(' AND r.postdate < (NOW() - INTERVAL %d DAY) ', $daysNew) : ''),
($daysOld != -1 ? sprintf(' AND r.postdate > (NOW() - INTERVAL %d DAY) ', $daysOld) : ''),
(count($excludedCats) > 0 ? ' AND r.categories_id NOT IN (' . implode(',', $excludedCats) . ')' : ''),
(count($searchOptions) > 0 ? $this->releaseSearch->getSearchSQL($searchOptions) : '')
(count($searchOptions) > 0 ? $this->releaseSearch->getSearchSQL($searchOptions) : ''),
($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
);
$baseSql = sprintf(
@@ -926,20 +932,25 @@ class Releases
}
/**
* @param array $siteIdArr
* @param string $series
* @param string $episode
* @param string $airdate
* @param int $offset
* @param int $limit
* @param string $name
* @param array $cat
* @param int $maxAge
* Search TV Shows via the API
*
* @param array $siteIdArr Array containing all possible TV Processing site IDs desired
* @param string $series The series or season number requested
* @param string $episode The episode number requested
* @param string $airdate The airdate of the episode requested
* @param int $offset Skip this many releases
* @param int $limit Return this many releases
* @param string $name The show name to search
* @param array $cat The category to search
* @param int $maxAge The maximum age of releases to be returned
* @param int $minSize The minimum size of releases to be returned
*
* @return array
*/
public function searchShows($siteIdArr = array(), $series = '', $episode = '', $airdate = '', $offset = 0,
$limit = 100, $name = '', $cat = [-1], $maxAge = -1)
public function searchShows(
$siteIdArr = array(), $series = '', $episode = '', $airdate = '', $offset = 0,
$limit = 100, $name = '', $cat = [-1], $maxAge = -1, $minSize = 0
)
{
$siteSQL = array();
@@ -955,22 +966,25 @@ class Releases
$whereSql = sprintf(
"%s
WHERE r.categories_id BETWEEN " . Category::TV_ROOT . " AND " . Category::TV_OTHER . "
AND v.type = 0
WHERE r.categories_id BETWEEN %d AND %d
AND r.nzbstatus = %d
AND r.passwordstatus %s
AND (%s)
%s %s %s %s %s %s",
%s %s %s %s %s %s %s",
($name !== '' ? $this->releaseSearch->getFullTextJoinString() : ''),
Category::TV_ROOT,
Category::TV_OTHER,
NZB::NZB_ADDED,
$this->showPasswords,
($siteCount > 0 ? implode(' OR ', $siteSQL) : '1=1'),
($series != '' ? sprintf('AND tve.series = %d', (int)preg_replace('/^s0*/i', '', $series)): ''),
($episode != '' ? sprintf('AND tve.episode = %d', (int)preg_replace('/^e0*/i', '', $episode)): ''),
($series != '' ? sprintf('AND tve.series = %d', (int)preg_replace('/^s0*/i', '', $series)) : ''),
($episode != '' ? sprintf('AND tve.episode = %d', (int)preg_replace('/^e0*/i', '', $episode)) : ''),
($airdate != '' ? sprintf('AND DATE(tve.firstaired) = %s', $this->pdo->escapeString($airdate)) : ''),
($name !== '' ? $this->releaseSearch->getSearchSQL(['searchname' => $name]) : ''),
$this->categorySQL($cat),
($maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : '')
($maxAge > 0 ? sprintf('AND r.postdate > NOW() - INTERVAL %d DAY', $maxAge) : ''),
($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
);
$baseSql = sprintf(
@@ -982,10 +996,10 @@ class Releases
CONCAT(cp.title, ' > ', c.title) AS category_name,
%s AS category_ids,
groups.name AS group_name,
rn.id AS nfoid,
rn.releases_id AS nfoid,
re.releases_id AS reid
FROM releases r
LEFT OUTER JOIN videos v ON r.videos_id = v.id
LEFT OUTER JOIN videos v ON r.videos_id = v.id AND v.type = 0
LEFT OUTER JOIN tv_info tvi ON v.id = tvi.videos_id
LEFT OUTER JOIN tv_episodes tve ON r.tv_episodes_id = tve.id
INNER JOIN categories c ON c.id = r.categories_id
@@ -1008,10 +1022,10 @@ class Releases
);
$releases = $this->pdo->query($sql, true, NN_CACHE_EXPIRY_MEDIUM);
return $releases;
}
/**
* @param $aniDbID
* @param int $offset
@@ -1079,21 +1093,22 @@ class Releases
*
* @return array
*/
public function searchbyImdbId($imDbId, $offset = 0, $limit = 100, $name = '', $cat = [-1], $maxAge = -1)
public function searchbyImdbId($imDbId, $offset = 0, $limit = 100, $name = '', $cat = [-1], $maxAge = -1, $minSize = 0)
{
$whereSql = sprintf(
"%s
WHERE r.categories_id BETWEEN " . Category::MOVIE_ROOT . " AND " . Category::MOVIE_OTHER . "
AND r.nzbstatus = %d
AND r.passwordstatus %s
%s %s %s %s",
%s %s %s %s %s",
($name !== '' ? $this->releaseSearch->getFullTextJoinString() : ''),
NZB::NZB_ADDED,
$this->showPasswords,
($name !== '' ? $this->releaseSearch->getSearchSQL(['searchname' => $name]) : ''),
(($imDbId != '-1' && is_numeric($imDbId)) ? sprintf(' AND imdbid = %d ', str_pad($imDbId, 7, '0', STR_PAD_LEFT)) : ''),
$this->categorySQL($cat),
($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : '')
($maxAge > 0 ? sprintf(' AND r.postdate > NOW() - INTERVAL %d DAY ', $maxAge) : ''),
($minSize > 0 ? sprintf('AND r.size >= %d', $minSize) : '')
);
$baseSql = sprintf(
@@ -1101,7 +1116,7 @@ class Releases
concat(cp.title, ' > ', c.title) AS category_name,
%s AS category_ids,
g.name AS group_name,
rn.id AS nfoid
rn.releases_id AS nfoid
FROM releases r
INNER JOIN groups g ON g.id = r.groupid
INNER JOIN categories c ON c.id = r.categories_id
+1 -1
View File
@@ -514,7 +514,7 @@ class Forking extends \fork_daemon
break;
case "filename":
$join = "INNER JOIN release_files rf ON rf.releases_id = r.id";
$join = "STRAIGHT_JOIN release_files rf ON rf.releases_id = r.id";
$where = "r.proc_files = 0";
break;
+22 -5
View File
@@ -361,6 +361,18 @@ class ProcessAdditional
*/
protected $_compressedFilesChecked;
/**
* Should we download the last rar?
* @var bool
*/
protected $_fetchLastFiles;
/**
* Are we downloading the last rar?
* @var bool
*/
protected $_reverse;
/**
* @param array $options Class instances / echo to cli.
*/
@@ -403,6 +415,7 @@ class ProcessAdditional
$this->_innerFileBlacklist = ($this->pdo->getSetting('innerfileblacklist') == '' ? false : $this->pdo->getSetting('innerfileblacklist'));
$this->_maxNestedLevels = ($this->pdo->getSetting('maxnestedlevels') == 0 ? 3 : $this->pdo->getSetting('maxnestedlevels'));
$this->_extractUsingRarInfo = ($this->pdo->getSetting('extractusingrarinfo') == 0 ? false : true);
$this->_fetchLastFiles = ($this->pdo->getSetting('fetchlastcompressedfiles') == 0 ? false : true);
$this->_7zipPath = false;
$this->_unrarPath = false;
@@ -599,8 +612,10 @@ class ProcessAdditional
protected function _fetchReleases($groupID, &$guidChar)
{
$this->_releases = $this->pdo->query(
sprintf(
'SELECT r.id, r.guid, r.name, c.disablepreview, r.size, r.groupid, r.nfostatus, r.completion, r.categories_id, r.searchname, r.predb_id
sprintf('
SELECT r.id, r.id AS releases_id, r.guid, r.name, r.size, r.groupid, r.nfostatus,
r.completion, r.categories_id, r.searchname, r.predb_id,
c.disablepreview
FROM releases r
LEFT JOIN categories c ON c.id = r.categories_id
WHERE r.nzbstatus = 1
@@ -701,7 +716,7 @@ class ProcessAdditional
$this->_processNZBCompressedFiles();
// Download rar/zip in reverse order, to get the last rar or zip file.
if ($this->pdo->getSetting('fetchlastcompressedfiles') == 1) {
if ($this->_fetchLastFiles == 1) {
$this->_processNZBCompressedFiles(true);
}
@@ -949,7 +964,9 @@ class ProcessAdditional
*/
protected function _processNZBCompressedFiles($reverse = false)
{
if ($reverse) {
$this->_reverse = $reverse;
if ($this->_reverse) {
if (!krsort($this->_nzbContents)) {
return;
}
@@ -989,7 +1006,7 @@ class ProcessAdditional
break;
}
$segment = (string)$nzbFile['segments'][$i];
if (!$reverse) {
if (!$this->_reverse) {
$this->_triedCompressedMids[] = $segment;
} else if (in_array($segment, $this->_triedCompressedMids)) {
// We already downloaded this file.
+75 -211
View File
@@ -1,14 +1,12 @@
<?php
use newznab\API;
use newznab\Capabilities;
use newznab\Category;
use newznab\Releases;
use newznab\db\Settings;
use newznab\utility\Utility;
if ($page->settings->getSetting('apienabled') != 1)
Utility::showApiError(910);
// API functions.
$function = 's';
if (isset($_GET['t'])) {
@@ -48,37 +46,44 @@ if (isset($_GET['t'])) {
$function = 'r';
break;
default:
Utility::showApiError(202);
Utility::showApiError(202, 'No such function (' . $_GET['t'] . ')');
}
} else {
Utility::showApiError(200);
Utility::showApiError(200, 'Missing parameter (t)');
}
$uid = $apiKey = '';
$catExclusions = [];
$res = $catExclusions = [];
$maxRequests = 0;
// Page is accessible only by the apikey, or logged in users.
if ($page->users->isLoggedIn()) {
$uid = $page->userdata['id'];
$apiKey = $page->userdata['rsstoken'];
$catExclusions = $page->userdata['categoryexclusions'];
$maxRequests = $page->userdata['apirequests'];
if ($page->users->isDisabled($page->userdata['username'])) {
Utility::showApiError(101);
}
} else {
if ($function != 'c' && $function != 'r') {
if (!isset($_GET['apikey'])) {
Utility::showApiError(200);
Utility::showApiError(200, 'Missing parameter (apikey)');
} else {
$res = $page->users->getByRssToken($_GET['apikey']);
$apiKey = $_GET['apikey'];
$res = $page->users->getByRssToken($apiKey);
if (!$res) {
Utility::showApiError(100);
Utility::showApiError(100, 'Incorrect user credentials (wrong API key)');
}
$uid = $res['id'];
$catExclusions = $page->users->getCategoryExclusion($uid);
$maxRequests = $res['apirequests'];
}
if ($page->users->isDisabled($res['username'])) {
Utility::showApiError(101);
}
$uid = $res['id'];
$catExclusions = $page->users->getCategoryExclusion($uid);
$maxRequests = $res['apirequests'];
}
}
@@ -90,11 +95,12 @@ if ($uid != '') {
$page->users->updateApiAccessed($uid);
$apiRequests = $page->users->getApiRequests($uid);
if ($apiRequests > $maxRequests) {
Utility::showApiError(500);
Utility::showApiError(500, 'Request limit reached (' . $apiRequests . '/' . $maxRequests . ')');
}
}
$releases = new Releases(['Settings' => $page->settings]);
$api = new API(['Settings' => $page->settings, 'Request' => $_GET]);
$page->smarty->assign('extended', (isset($_GET['extended']) && $_GET['extended'] == 1 ? '1' : '0'));
$page->smarty->assign('del', (isset($_GET['del']) && $_GET['del'] == 1 ? '1' : '0'));
@@ -102,46 +108,48 @@ $page->smarty->assign('del', (isset($_GET['del']) && $_GET['del'] == 1 ? '1' : '
// Output is either json or xml.
$outputXML = (isset($_GET['o']) && $_GET['o'] == 'json' ? false : true);
$minSize = (isset($_GET['minsize']) && $_GET['minsize'] > 0 ? $_GET['minsize'] : 0);
$offset = $api->offset();
switch ($function) {
// Search releases.
case 's':
verifyEmptyParameter('q');
$maxAge = maxAge();
$api->verifyEmptyParameter('q');
$maxAge = $api->maxAge();
$page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']);
$categoryID = categoryID();
$limit = limit();
$offset = offset();
$categoryID = $api->categoryID();
$limit = $api->limit();
if (isset($_GET['q'])) {
$relData = $releases->search(
$_GET['q'], -1, -1, -1, -1, -1, -1, 0, 0, -1, -1, $offset, $limit, '', $maxAge, $catExclusions,
"basic", $categoryID
"basic", $categoryID, $minSize
);
} else {
$totalRows = $releases->getBrowseCount($categoryID, $maxAge, $catExclusions);
$relData = $releases->getBrowseRange($categoryID, $offset, $limit, '', $maxAge, $catExclusions);
$relData = $releases->getBrowseRange(
$categoryID, $offset, $limit, '', $maxAge, $catExclusions, '', $minSize
);
}
printOutput($relData, $outputXML, $page, $offset);
$api->printOutput($relData, $outputXML, $page, $offset);
break;
// Search tv releases.
case 'tv':
verifyEmptyParameter('q');
verifyEmptyParameter('vid');
verifyEmptyParameter('tvdbid');
verifyEmptyParameter('traktid');
verifyEmptyParameter('rid');
verifyEmptyParameter('tvmazeid');
verifyEmptyParameter('imdbid');
verifyEmptyParameter('tmdbid');
verifyEmptyParameter('season');
verifyEmptyParameter('ep');
$maxAge = maxAge();
$api->verifyEmptyParameter('q');
$api->verifyEmptyParameter('vid');
$api->verifyEmptyParameter('tvdbid');
$api->verifyEmptyParameter('traktid');
$api->verifyEmptyParameter('rid');
$api->verifyEmptyParameter('tvmazeid');
$api->verifyEmptyParameter('imdbid');
$api->verifyEmptyParameter('tmdbid');
$api->verifyEmptyParameter('season');
$api->verifyEmptyParameter('ep');
$maxAge = $api->maxAge();
$page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']);
$offset = offset();
$siteIdArr = [
$siteIdArr = [
'id' => (isset($_GET['vid']) ? $_GET['vid'] : '0'),
'tvdb' => (isset($_GET['tvdbid']) ? $_GET['tvdbid'] : '0'),
'trakt' => (isset($_GET['traktid']) ? $_GET['traktid'] : '0'),
@@ -166,49 +174,50 @@ switch ($function) {
(isset($episode) ? $episode : ''),
(isset($airdate) ? $airdate : ''),
$offset,
limit(),
$api->limit(),
(isset($_GET['q']) ? $_GET['q'] : ''),
categoryID(),
$maxAge
$api->categoryID(),
$maxAge,
$minSize
);
addLanguage($relData, $page->settings);
printOutput($relData, $outputXML, $page, $offset);
$api->addLanguage($relData);
$api->printOutput($relData, $outputXML, $page, $offset);
break;
// Search movie releases.
case 'm':
verifyEmptyParameter('q');
verifyEmptyParameter('imdbid');
$maxAge = maxAge();
$api->verifyEmptyParameter('q');
$api->verifyEmptyParameter('imdbid');
$maxAge = $api->maxAge();
$page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']);
$offset = offset();
$imdbId = (isset($_GET['imdbid']) ? $_GET['imdbid'] : '-1');
$relData = $releases->searchbyImdbId(
$imdbId,
$offset,
limit(),
$api->limit(),
(isset($_GET['q']) ? $_GET['q'] : ''),
categoryID(),
$maxAge
$api->categoryID(),
$maxAge,
$minSize
);
addCoverURL($relData,
$api->addCoverURL($relData,
function($release) {
return Utility::getCoverURL(['type' => 'movies', 'id' => $release['imdbid']]);
}
);
addLanguage($relData, $page->settings);
printOutput($relData, $outputXML, $page, $offset);
$api->addLanguage($relData);
$api->printOutput($relData, $outputXML, $page, $offset);
break;
// Get NZB.
case 'g':
if (!isset($_GET['id'])) {
Utility::showApiError(200);
Utility::showApiError(200, 'Missing parameter (id is required for downloading an NZB)');
}
$relData = $releases->getByGuid($_GET['id']);
@@ -225,14 +234,14 @@ switch ($function) {
((isset($_GET['del']) && $_GET['del'] == '1') ? '&del=1' : '')
);
} else {
Utility::showApiError(300);
Utility::showApiError(300, 'No such item (the guid you provided has no release in our database)');
}
break;
// Get individual NZB details.
case 'd':
if (!isset($_GET['id'])) {
Utility::showApiError(200);
Utility::showApiError(200, 'Missing parameter (id is required for downloading an NZB)');
}
$page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']);
@@ -243,13 +252,13 @@ switch ($function) {
$relData[] = $data;
}
printOutput($relData, $outputXML, $page, offset());
$api->printOutput($relData, $outputXML, $page, $offset);
break;
// Get an NFO file for an individual release.
case 'n':
if (!isset($_GET['id'])) {
Utility::showApiError(200);
Utility::showApiError(200, 'Missing parameter (id is required for retrieving an NFO)');
}
$page->users->addApiRequest($uid, $_SERVER['REQUEST_URI']);
@@ -266,15 +275,13 @@ switch ($function) {
echo nl2br(Utility::cp437toUTF($data['nfo']));
}
} else {
Utility::showApiError(300);
Utility::showApiError(300, 'Release does not have an NFO file associated.');
}
} else {
Utility::showApiError(300);
Utility::showApiError(300, 'Release does not exist.');
}
break;
// Capabilities request.
case 'c':
//get categories
@@ -287,22 +294,22 @@ switch ($function) {
if ($outputXML) { //use apicaps.tpl if xml is requested
$response = $page->smarty->fetch('apicaps.tpl');
header('Content-type: text/xml');
header('Content-Length: ' . strlen($response) );
header('Content-Length: ' . strlen($response));
echo $response;
} else { //otherwise construct array of capabilities and categories
//get capabilities
$caps = (new Capabilities(['Settings' => $page->settings]))->getForMenu();
$caps['categories'] = $category->getForMenu();
$caps['categories'] = $cats;
//use json_encode
$response = encodeAsJSON($caps);
$response = $api->encodeAsJSON($caps);
header('Content-type: application/json');
header('Content-Length: ' . strlen($response) );
header('Content-Length: ' . strlen($response));
echo $response;
}
break;
// Register request.
case 'r':
verifyEmptyParameter('email');
$api->verifyEmptyParameter('email');
if (!in_array((int)$page->settings->getSetting('registerstatus'), [Settings::REGISTER_STATUS_OPEN, Settings::REGISTER_STATUS_API_ONLY])) {
Utility::showApiError(104);
@@ -325,7 +332,7 @@ switch ($function) {
// Register.
$userDefault = $page->users->getDefaultRole();
$uid = $page->users->signUp(
$uid = $page->users->signup(
$username, $password, $_GET['email'], $_SERVER['REMOTE_ADDR'], $userDefault['id'], $userDefault['defaultinvites']
);
@@ -342,150 +349,7 @@ switch ($function) {
'" apikey="' . $userdata['rsstoken'] .
"\"/>\n";
header('Content-type: text/xml');
header('Content-Length: ' . strlen($response) );
header('Content-Length: ' . strlen($response));
echo $response;
break;
}
/**
* Verify maxage parameter.
* @return int
*/
function maxAge()
{
$maxAge = -1;
if (isset($_GET['maxage'])) {
if ($_GET['maxage'] == '') {
Utility::showApiError(201);
} elseif (!is_numeric($_GET['maxage'])) {
Utility::showApiError(201);
} else {
$maxAge = (int)$_GET['maxage'];
}
}
return $maxAge;
}
/**
* Verify cat parameter.
* @return array
*/
function categoryID()
{
$categoryID[] = -1;
if (isset($_GET['cat'])) {
$categoryIDs = urldecode($_GET['cat']);
// Append Web-DL category ID if HD present for SickBeard / Sonarr compatibility.
if (strpos($categoryIDs, (string)Category::TV_HD) !== false &&
strpos($categoryIDs, (string)Category::TV_WEBDL) === false) {
$categoryIDs .= (',' . Category::TV_WEBDL);
}
$categoryID = explode(',', $categoryIDs);
}
return $categoryID;
}
/**
* Verify limit parameter.
* @return int
*/
function limit()
{
$limit = 100;
if (isset($_GET['limit']) && is_numeric($_GET['limit']) && $_GET['limit'] < 100) {
$limit = (int)$_GET['limit'];
}
return $limit;
}
/**
* Verify offset parameter.
* @return int
*/
function offset()
{
$offset = 0;
if (isset($_GET['offset']) && is_numeric($_GET['offset'])) {
$offset = (int)$_GET['offset'];
}
return $offset;
}
/**
* Print XML or JSON output.
* @param array $data Data to print.
* @param bool $xml True: Print as XML False: Print as JSON.
* @param BasePage $page
* @param int $offset Offset for limit.
*/
function printOutput($data, $xml = true, $page, $offset = 0)
{
if ($xml) {
$page->smarty->assign('offset', $offset);
$page->smarty->assign('releases', $data);
$response = trim($page->smarty->fetch('apiresult.tpl'));
header('Content-type: text/xml');
header('Content-Length: ' . strlen($response) );
echo $response;
} else {
$response = encodeAsJSON($data);
header('Content-type: application/json');
header('Content-Length: ' . strlen($response));
echo $response;
}
}
/**
* Check if a parameter is empty.
* @param string $parameter
*/
function verifyEmptyParameter($parameter)
{
if (isset($_GET[$parameter]) && $_GET[$parameter] == '') {
Utility::showApiError(201);
}
}
function addCoverURL(&$releases, callable $getCoverURL)
{
if ($releases && count($releases)) {
foreach ($releases as $key => $release) {
$coverURL = $getCoverURL($release);
$releases[$key]['coverurl'] = $coverURL;
}
}
}
/**
* Add language from media info XML to release search names.
* @param array $releases
* @param newznab\db\Settings $settings
* @return array
*/
function addLanguage(&$releases, Settings $settings)
{
if ($releases && count($releases)) {
foreach ($releases as $key => $release) {
if (isset($release['id'])) {
$language = $settings->queryOneRow('
SELECT audiolanguage
FROM audio_data
WHERE releases_id = ' .
$release['id']
);
if ($language !== false) {
$releases[$key]['searchname'] = $releases[$key]['searchname'] . ' ' . $language['audiolanguage'];
}
}
}
}
}
function encodeAsJSON($data)
{
$json = json_encode(Utility::encodeAsUTF8($data));
if ($json === false) {
Utility::showApiError(201);
}
return $json;
}
+4 -1
View File
@@ -71,7 +71,10 @@
<newznab:attr name="tmdbid" value="tt{$release.tmdb}" />
{/if}
{if $release.anidbid > 0}
<newznab:attr name="anidbid" value="tt{$release.anidb}" />
<newznab:attr name="anidbid" value="{$release.anidbid}" />
{/if}
{if $release.predb_id > 0}
<newznab:attr name="prematch" value="1" />
{/if}
{if $release.nfostatus == 1}
<newznab:attr name="info" value="{$serverroot}api?t=info&amp;id={$release.guid}&amp;r={$rsstoken}" />