Files
newznab-tmux/newznab/controllers/Book.php
T

512 lines
18 KiB
PHP

<?php
use newznab\db\Settings;
/**
* This class manages the lookup of book information and storage/retrieve of book metadata.
*/
class Book
{
const NUMTOPROCESSPERTIME = 100;
/**
* @var newznab\db\Settings
*/
public $pdo;
/**
* @var bool
*/
public $echooutput;
/**
* Default constructor.
*
* @param bool $echooutput
*/
public function __construct($echooutput = false)
{
$this->echooutput = (NN_ECHOCLI && $echooutput);
$s = new Sites();
$site = $s->get();
$this->pubkey = $site->amazonpubkey;
$this->privkey = $site->amazonprivkey;
$this->asstag = $site->amazonassociatetag;
$this->pdo = new Settings();
$this->imgSavePath = WWW_DIR.'covers/book/';
}
/**
* Get bookinfo row for id.
*/
public function getBookInfo($id)
{
$query = $this->pdo->queryOneRow(sprintf("SELECT bookinfo.*, genres.title as genres FROM bookinfo left outer join genres on genres.id = bookinfo.genreID where bookinfo.id = %d ", $id));
return $query;
}
/**
* Get bookinfo row for title.
*/
public function getBookInfoByName($author, $title)
{
$query = $this->pdo->queryOneRow(sprintf("SELECT * FROM bookinfo where author like %s and title like %s", $this->pdo->escapeString("%".$author."%"), $this->pdo->escapeString("%".$title."%")));
return $query;
}
/**
* Get bookinfo rows by limit.
*/
public function getRange($start, $num)
{
if ($start === false)
$limit = "";
else
$limit = " LIMIT ".$start.",".$num;
$query = $this->pdo->query(" SELECT * FROM bookinfo ORDER BY createddate DESC".$limit);
return $query;
}
/**
* Get count of all bookinfos.
*/
public function getCount()
{
$res = $this->pdo->queryOneRow("select count(id) as num from bookinfo");
return $res["num"];
}
/**
* Get count of all bookinfo rows for browse list.
*/
public function getBookCount($maxage=-1)
{
$browseby = $this->getBrowseBy();
if ($maxage > 0)
$maxage = sprintf(" and r.postdate > now() - interval %d day ", $maxage);
else
$maxage = "";
$sql = sprintf("select count(distinct r.bookinfoid) as num from releases r inner join bookinfo b on b.id = r.bookinfoid and b.title != '' where r.passwordstatus <= (select value from settings where setting='showpasswordedrelease') %s %s", $browseby, $maxage);
$res = $this->pdo->queryOneRow($sql, true);
return $res["num"];
}
/**
* Get range of bookinfo rows for browse list.
*/
public function getBookRange($start, $num, $orderby, $maxage=-1)
{
$browseby = $this->getBrowseBy();
if ($start === false)
$limit = "";
else
$limit = " LIMIT ".$start.",".$num;
$maxagesql = "";
if ($maxage > 0)
$maxagesql = sprintf(" and r.postdate > now() - interval %d day ", $maxage);
$order = $this->getBrowseOrder($orderby);
$sql = sprintf(" SELECT r.bookinfoid, max(postdate), b.* from releases r inner join bookinfo b on b.id = r.bookinfoid and b.title != '' where r.passwordstatus <= (select value from settings where setting='showpasswordedrelease') %s %s group by r.bookinfoid order by %s %s".$limit, $browseby, $maxagesql, $order[0], $order[1]);
$rows = $this->pdo->query($sql, true);
//
//get a copy of all the bookinfoids
//
$ids = "";
foreach ($rows as $row)
$ids .= $row["bookinfoid"]. ", ";
if (strlen($ids) > 0)
{
$ids = substr($ids,0,-2);
//
// get all releases matching these ids
//
$sql = sprintf("select r.*, releasenfo.id as nfoid, groups.name as grpname from releases r left outer join releasenfo on releasenfo.releaseid = r.id left outer join groups on groups.id = r.groupid where bookinfoid in (%s) %s order by r.postdate desc", $ids, $maxagesql);
$allrows = $this->pdo->query($sql, true);
$arr = [];
//
// build array indexed by bookinfoid
//
foreach ($allrows as &$allrow)
{
$arr[$allrow["bookinfoid"]]["id"] = (isset($arr[$allrow["bookinfoid"]]["id"]) ? $arr[$allrow["bookinfoid"]]["id"] : "") . $allrow["id"] . ",";
$arr[$allrow["bookinfoid"]]["rarinnerfilecount"] = (isset($arr[$allrow["bookinfoid"]]["rarinnerfilecount"]) ? $arr[$allrow["bookinfoid"]]["rarinnerfilecount"] : "") . $allrow["rarinnerfilecount"] . ",";
$arr[$allrow["bookinfoid"]]["haspreview"] = (isset($arr[$allrow["bookinfoid"]]["haspreview"]) ? $arr[$allrow["bookinfoid"]]["haspreview"] : "") . $allrow["haspreview"] . ",";
$arr[$allrow["bookinfoid"]]["passwordstatus"] = (isset($arr[$allrow["bookinfoid"]]["passwordstatus"]) ? $arr[$allrow["bookinfoid"]]["passwordstatus"] : "") . $allrow["passwordstatus"] . ",";
$arr[$allrow["bookinfoid"]]["guid"] = (isset($arr[$allrow["bookinfoid"]]["guid"]) ? $arr[$allrow["bookinfoid"]]["guid"] : "") . $allrow["guid"] . ",";
$arr[$allrow["bookinfoid"]]["nfoid"] = (isset($arr[$allrow["bookinfoid"]]["nfoid"]) ? $arr[$allrow["bookinfoid"]]["nfoid"] : "") . $allrow["nfoid"] . ",";
$arr[$allrow["bookinfoid"]]["grpname"] = (isset($arr[$allrow["bookinfoid"]]["grpname"]) ? $arr[$allrow["bookinfoid"]]["grpname"] : "") . $allrow["grpname"] . ",";
$arr[$allrow["bookinfoid"]]["searchname"] = (isset($arr[$allrow["bookinfoid"]]["searchname"]) ? $arr[$allrow["bookinfoid"]]["searchname"] : "") . $allrow["searchname"] . "#";
$arr[$allrow["bookinfoid"]]["postdate"] = (isset($arr[$allrow["bookinfoid"]]["postdate"]) ? $arr[$allrow["bookinfoid"]]["postdate"] : "") . $allrow["postdate"] . ",";
$arr[$allrow["bookinfoid"]]["size"] = (isset($arr[$allrow["bookinfoid"]]["size"]) ? $arr[$allrow["bookinfoid"]]["size"] : "") . $allrow["size"] . ",";
$arr[$allrow["bookinfoid"]]["totalpart"] = (isset($arr[$allrow["bookinfoid"]]["totalpart"]) ? $arr[$allrow["bookinfoid"]]["totalpart"] : "") . $allrow["totalpart"] . ",";
$arr[$allrow["bookinfoid"]]["comments"] = (isset($arr[$allrow["bookinfoid"]]["comments"]) ? $arr[$allrow["bookinfoid"]]["comments"] : "") . $allrow["comments"] . ",";
$arr[$allrow["bookinfoid"]]["grabs"] = (isset($arr[$allrow["bookinfoid"]]["grabs"]) ? $arr[$allrow["bookinfoid"]]["grabs"] : "") . $allrow["grabs"] . ",";
$arr[$allrow["bookinfoid"]]["categoryid"] = (isset($arr[$allrow["bookinfoid"]]["categoryid"]) ? $arr[$allrow["bookinfoid"]]["categoryid"] : "") . $allrow["categoryid"] . ",";
}
//
// stuff back into the results set
//
foreach ($rows as &$row)
{
$row["grp_release_id"] = substr($arr[$row["bookinfoid"]]["id"], 0, -1);
$row["grp_rarinnerfilecount"] = substr($arr[$row["bookinfoid"]]["rarinnerfilecount"], 0, -1);
$row["grp_haspreview"] = substr($arr[$row["bookinfoid"]]["haspreview"], 0, -1);
$row["grp_release_password"] = substr($arr[$row["bookinfoid"]]["passwordstatus"], 0, -1);
$row["grp_release_guid"] = substr($arr[$row["bookinfoid"]]["guid"], 0, -1);
$row["grp_release_nfoID"] = substr($arr[$row["bookinfoid"]]["nfoid"], 0, -1);
$row["grp_release_grpname"] = substr($arr[$row["bookinfoid"]]["grpname"], 0, -1);
$row["grp_release_name"] = substr($arr[$row["bookinfoid"]]["searchname"], 0, -1);
$row["grp_release_postdate"] = substr($arr[$row["bookinfoid"]]["postdate"], 0, -1);
$row["grp_release_size"] = substr($arr[$row["bookinfoid"]]["size"], 0, -1);
$row["grp_release_totalparts"] = substr($arr[$row["bookinfoid"]]["totalpart"], 0, -1);
$row["grp_release_comments"] = substr($arr[$row["bookinfoid"]]["comments"], 0, -1);
$row["grp_release_grabs"] = substr($arr[$row["bookinfoid"]]["grabs"], 0, -1);
$row["grp_release_categoryID"] = substr($arr[$row["bookinfoid"]]["categoryid"], 0, -1);
}
}
return $rows;
}
/**
* Get orderby column for book browse list.
*/
public function getBrowseOrder($orderby)
{
$order = ($orderby == '') ? 'r.postdate' : $orderby;
$orderArr = explode("_", $order);
switch($orderArr[0]) {
case 'artist':
$orderfield = 'b.author';
break;
case 'size':
$orderfield = 'r.size';
break;
case 'files':
$orderfield = 'r.totalpart';
break;
case 'stats':
$orderfield = 'r.grabs';
break;
case 'posted':
default:
$orderfield = 'r.postdate';
break;
}
$ordersort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
return array($orderfield, $ordersort);
}
/**
* Get all orderable columns for book browse list.
*/
public function getBookOrdering()
{
return array('author_asc', 'author_desc', 'posted_asc', 'posted_desc', 'size_asc', 'size_desc', 'files_asc', 'files_desc', 'stats_asc', 'stats_desc');
}
/**
* Get filter options from browse list.
*/
public function getBrowseByOptions()
{
return array('author'=>'author', 'title'=>'title');
}
/**
* Get filter options from user into SQL query.
*/
public function getBrowseBy()
{
$browseby = ' ';
$browsebyArr = $this->getBrowseByOptions();
foreach ($browsebyArr as $bbk=>$bbv) {
if (isset($_REQUEST[$bbk]) && !empty($_REQUEST[$bbk])) {
$bbs = stripslashes($_REQUEST[$bbk]);
if (preg_match('/id/i', $bbv)) {
$browseby .= " and b.{$bbv} = $bbs ";
} else {
$browseby .= " and b.$bbv LIKE(".$this->pdo->escapeString('%'.$bbs.'%').") ";
}
}
}
return $browseby;
}
/**
* Update bookinfo row.
*/
public function update($id, $title, $asin, $url, $author, $publisher, $publishdate, $cover)
{
$this->pdo->queryExec(sprintf("update bookinfo SET title=%s, asin=%s, url=%s, author=%s, publisher=%s, publishdate='%s', cover=%d, updateddate=NOW() WHERE id = %d",
$this->pdo->escapeString($title), $this->pdo->escapeString($asin), $this->pdo->escapeString($url), $this->pdo->escapeString($author), $this->pdo->escapeString($publisher), $publishdate, $cover, $id));
}
/**
* Determine if a bookinfo can be found locally, if not query amazon, strip out it
* properties and update the database.
*/
public function updateBookInfo($author, $title)
{
$ri = new ReleaseImage();
$amaz = $this->fetchAmazonProperties($author." ".$title);
if (!$amaz)
{
//echo "tried to lookup ".$author." ".$title;
return false;
}
sleep(1);
//
// get album properties
//
$item = [];
$item["asin"] = (string) $amaz->Items->Item->ASIN;
$item["url"] = (string) $amaz->Items->Item->DetailPageURL;
$item["coverurl"] = (string) $amaz->Items->Item->LargeImage->URL;
if ($item['coverurl'] != "")
$item['cover'] = 1;
else
$item['cover'] = 0;
$item["author"] = (string) $amaz->Items->Item->ItemAttributes->Author;
$item["dewey"] = (string) $amaz->Items->Item->ItemAttributes->DeweyDecimalNumber;
$item["ean"] = (string) $amaz->Items->Item->ItemAttributes->EAN;
$item["isbn"] = (string) $amaz->Items->Item->ItemAttributes->ISBN;
$item["publisher"] = (string) $amaz->Items->Item->ItemAttributes->Publisher;
$item["publishdate"] = (string) $amaz->Items->Item->ItemAttributes->PublicationDate;
$item["pages"] = (string) $amaz->Items->Item->ItemAttributes->NumberOfPages;
$item["title"] = (string) $amaz->Items->Item->ItemAttributes->Title;
$item["review"] = "";
if (isset($amaz->Items->Item->EditorialReviews))
$item["review"] = trim(strip_tags((string) $amaz->Items->Item->EditorialReviews->EditorialReview->Content));
//This is to verify the result back from amazon was at least somewhat related to what was intended.
//If you are debugging releases comment out the following code to show all info
$match = similar_text($author, $item["author"], $authorpercent);
$match = similar_text($title, $item['title'], $titlepercent);
//If the author is less than 80% album must be 100%
if ($authorpercent < '60')
{
if ($titlepercent != '100')
{
//echo "\nAuthor Under 80 Title Under 100 \n".$author." - ".$item['author']." - ".$authorpercent."\n";
$temptitle = $title;
$tempauthor = $author;
$title = $tempauthor;
$author = $temptitle;
$match = similar_text($author, $item['author'], $authorpercent);
$match = similar_text($title, $item['title'], $titlepercent);
if ($authorpercent < '60')
{
if ($titlepercent != '100')
{
//echo "\nAuthor Under 80 Title Under 100 second check\n".$author." - ".$item['author']." - ".$authorpercent."\n";
//echo $title." - ".$item['title']." - ".$titlepercent."\n";
return false;
}
}
}
}
//If the title is ever under 30%, it's probably not a match.
if ($titlepercent < '30')
{
//echo "Title Under 30 ".$title." - ".$item['title']." - ".$titlepercent;
return false;
}
$bookId = $this->addUpdateBookInfo($item['title'], $item['asin'], $item['url'],
$item['author'], $item['publisher'], $item['publishdate'], $item['review'],
$item['cover'], $item['dewey'], $item['ean'], $item['isbn'], $item['pages'] );
if ($bookId)
{
$item['cover'] = $ri->saveImage($bookId, $item['coverurl'], $this->imgSavePath, 250, 250);
}
return $bookId;
}
/**
* Query amazon for a title.
*/
public function fetchAmazonProperties($title)
{
$obj = new AmazonProductAPI($this->pubkey, $this->privkey, $this->asstag);
try
{
$result = $obj->searchProducts($title, AmazonProductAPI::BOOKS, "TITLE");
}
catch(Exception $e)
{
$result = false;
}
return $result;
}
/**
* Process all untagged book releases for additional metadata.
*/
public function processBookReleases()
{
$numlookedup = 0;
$res = $this->pdo->queryDirect(sprintf("SELECT searchname, id from releases where bookinfoid IS NULL and categoryid = %d ORDER BY postdate DESC LIMIT 100", Category::CAT_BOOK_EBOOK));
if ($this->pdo->getNumRows($res) > 0)
{
if ($this->echooutput)
echo "BookPrc : Processing " . $this->pdo->getNumRows($res) . " book releases\n";
while ($arr = $this->pdo->getAssocArray($res))
{
if ($numlookedup > Book::NUMTOPROCESSPERTIME)
return;
$bookId = -2;
$book = $this->parseAuthor($arr['searchname']);
if ($book !== false)
{
if ($this->echooutput)
echo 'BookPrc : '.$book["author"].' - '.$book["title"]."\n";
//check for existing book entry
$bookCheck = $this->getBookInfoByName($book["author"], $book["title"]);
if ($bookCheck === false)
{
//
// get from amazon
//
$numlookedup++;
$ret = $this->updateBookInfo($book["author"], $book["title"]);
if ($ret !== false)
{
$bookId = $ret;
}
}
else
{
$bookId = $bookCheck["id"];
}
}
$this->pdo->queryExec(sprintf("update releases SET bookinfoid = %d WHERE id = %d", $bookId, $arr["id"]));
}
}
}
/**
* Strip out author and title name from a release name.
*/
public function parseAuthor($releasename)
{
$result = [];
$result['releasename'] = $releasename;
$newName = $releasename;
//only process the reasonably named items
//if (!preg_match('/epub|mobi|html|lit|prc|djvu|pdf/i', $releasename))
// return false;
//remove things in brackets and double hyphens
$newName = preg_replace("%(\([\w\s-.,]*\)|\[[\w\s-.,]*\]|[0-9])%","",$newName);
$newName = preg_replace("%(\-\s.*\-)%","-",$newName);
$name = explode("-", $newName);
$name = array_map("trim", $name);
if (is_array($name) && sizeof($name) > 1)
{
$result['author'] = trim($name[0]);
$result['title'] = trim($name[1]);
$result['title'] = preg_replace('/retail/i','',$result['title']);
$result['title'] = preg_replace('/\.epub/i','',$result['title']);
$result['title'] = preg_replace('/\.mobi/i','',$result['title']);
$result['title'] = preg_replace('/\.prc/i','',$result['title']);
$result['title'] = preg_replace('/\.lit/i','',$result['title']);
$result['title'] = preg_replace('/\.obi/i','',$result['title']);
$result['title'] = preg_replace('/\.azw3/i','',$result['title']);
$result['title'] = preg_replace('/\.azw/i','',$result['title']);
$result['title'] = preg_replace('/\.pdf/i','',$result['title']);
$result['title'] = preg_replace('/\.html/i','',$result['title']);
$result['title'] = preg_replace('/\./i',' ',$result['title']);
$result['title'] = preg_replace('/\d{4}/i','',$result['title']);
$result['title'] = preg_replace('/repost/i','',$result['title']);
$result['title'] = preg_replace('/ebook/i','',$result['title']);
$result['title'] = preg_replace('/\(.*?\) WW/i','',$result['title']);
$result['title'] = preg_replace('/ WW/i','',$result['title']);
$result['title'] = preg_replace('/\(.*?\)$/i','',$result['title']);
$result['title'] = preg_replace('/\:.*?$/i','',$result['title']);
//echo "author parsed - ".$result['author']."\n";
//echo "title parsed - ".$result['title']."\n";
// switch Cratchett, Bob to Bob Cratchett
preg_match_all('/[,]/i', $result['author'], $matches);
if (sizeof($matches[0]) == 1)
{
$pos = strpos($result['author'], ",");
if ($pos !== false)
{
$firstname = substr($result['author'], $pos+1);
$surname = substr($result['author'], 0, $pos);
$result['author'] = trim($firstname . " " . $surname);
}
}
}
return (!empty($result['author']) ? $result : false);
}
/**
* Insert or update a bookinfo row.
*/
public function addUpdateBookInfo($title, $asin, $url, $author, $publisher, $publishdate, $review, $cover, $dewey, $ean, $isbn, $pages)
{
if ($pages == 0)
$pages = "null";
else
$pages = $pages + 0;
if ($publishdate == "")
$publishdate = "null";
elseif (strlen($publishdate) == 4)
$publishdate = $this->pdo->escapeString($publishdate."-01-01");
elseif (strlen($publishdate) == 7)
$publishdate = $this->pdo->escapeString($publishdate."-01");
else
$publishdate = $this->pdo->escapeString($publishdate);
$sql = sprintf("INSERT INTO bookinfo (title, asin, url, author, publisher, publishdate, review, cover, createddate, updateddate, dewey, ean, isbn, pages)
VALUES (%s, %s, %s, %s, %s, %s, %s, %d, now(), now(), %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE title = %s, asin = %s, url = %s, author = %s, publisher = %s, publishdate = %s, review = %s, cover = %d, createddate = now(), updateddate = now(), dewey = %s, ean = %s, isbn = %s, pages = %s",
$this->pdo->escapeString($title), $this->pdo->escapeString($asin), $this->pdo->escapeString($url),
$this->pdo->escapeString($author), $this->pdo->escapeString($publisher),
$publishdate, $this->pdo->escapeString($review), $cover, $this->pdo->escapeString($dewey), $this->pdo->escapeString($ean), $this->pdo->escapeString($isbn), $pages,
$this->pdo->escapeString($title), $this->pdo->escapeString($asin), $this->pdo->escapeString($url),
$this->pdo->escapeString($author), $this->pdo->escapeString($publisher),
$this->pdo->escapeString($publishdate), $this->pdo->escapeString($review), $cover, $this->pdo->escapeString($dewey), $this->pdo->escapeString($ean), $this->pdo->escapeString($isbn), $pages );
$bookId = $this->pdo->queryInsert($sql);
return $bookId;
}
}