From a161bd0d0574e8e6555d8bb64c02fac5575fbe6d Mon Sep 17 00:00:00 2001 From: frankdelange Date: Sat, 20 Dec 2014 01:22:06 +0100 Subject: [PATCH] Added ISBN metadata gathering, currently using ISBNdb and Google Books, more sources to be added. ISBN is scraped from text or discovered through embedded metadata. --- dist/files_opds-0.5.tar.gz | Bin 0 -> 43257 bytes files_opds/admin.php | 1 + files_opds/ajax/admin.php | 2 + files_opds/appinfo/database.xml | 18 +- files_opds/appinfo/info.xml | 11 +- files_opds/js/admin.js | 7 +- files_opds/lib/google.php | 92 +++++++++++ files_opds/lib/isbn.php | 154 ++++++++++++++++++ files_opds/lib/isbndb.php | 119 ++++++++++++++ files_opds/lib/meta.php | 149 ++++++++++++----- files_opds/templates/admin.php | 14 +- .../templates/part.feed.acquisition.php | 14 +- 12 files changed, 527 insertions(+), 54 deletions(-) create mode 100644 dist/files_opds-0.5.tar.gz create mode 100644 files_opds/lib/google.php create mode 100644 files_opds/lib/isbn.php create mode 100644 files_opds/lib/isbndb.php diff --git a/dist/files_opds-0.5.tar.gz b/dist/files_opds-0.5.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..dda5260b48c6908ae6e2cfa2a601b8c9e317913e GIT binary patch literal 43257 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 literal 0 HcmV?d00001 diff --git a/files_opds/admin.php b/files_opds/admin.php index 575d9a2..8242395 100644 --- a/files_opds/admin.php +++ b/files_opds/admin.php @@ -25,6 +25,7 @@ $formats = array( $tmpl = new \OCP\Template('files_opds', 'admin'); $tmpl->assign('feedSubtitle', Config::getApp('feed-subtitle', $l->t("%s OPDS catalog", $defaults->getName()))); +$tmpl->assign('isbndbKey', Config::getApp('isbndb-key', '')); $tmpl->assign('previewFormats', $formats); $tmpl->assign('cover-x', Config::getApp('cover-x', '200')); $tmpl->assign('cover-y', Config::getApp('cover-y', '200')); diff --git a/files_opds/ajax/admin.php b/files_opds/ajax/admin.php index 370fe2a..91d8a48 100644 --- a/files_opds/ajax/admin.php +++ b/files_opds/ajax/admin.php @@ -25,12 +25,14 @@ if (isset($_POST['opdsCoverX'])) { $opdsThumbX = isset($_POST['opdsThumbX']) ? (int) $_POST['opdsThumbX'] : 36; $opdsThumbY = isset($_POST['opdsThumbY']) ? (int) $_POST['opdsThumbY'] : 36; $opdsFeedSubtitle = isset($_POST['opdsFeedSubtitle']) ? $_POST['opdsFeedSubtitle'] : $l->t("%s OPDS catalog", $defaults->getName()); + $opdsIsbndbKey = isset($_POST['opdsIsbndbKey']) ? $_POST['opdsIsbndbKey'] : ''; Config::setApp('cover-x', $opdsCoverX); Config::setApp('cover-y', $opdsCoverY); Config::setApp('thumb-x', $opdsThumbX); Config::setApp('thumb-y', $opdsThumbX); Config::setApp('feed_subtitle', $opdsFeedSubtitle); + Config::setApp('isbndb-key', $opdsIsbndbKey); } else { // set preview preferences $opdsPreviewEpub = $_POST['opdsPreviewEpub']; diff --git a/files_opds/appinfo/database.xml b/files_opds/appinfo/database.xml index fda2e97..c689103 100644 --- a/files_opds/appinfo/database.xml +++ b/files_opds/appinfo/database.xml @@ -12,8 +12,8 @@ id integer - 0 true + true 11 @@ -105,6 +105,22 @@ true 1024 + + + + rescan + timestamp + + false + + + + opds_metadata_id_index + + id + + + diff --git a/files_opds/appinfo/info.xml b/files_opds/appinfo/info.xml index 1613baf..f1f0524 100644 --- a/files_opds/appinfo/info.xml +++ b/files_opds/appinfo/info.xml @@ -4,11 +4,20 @@ OPDS catalog Personal OPDS catalog AGPL - 0.4 + 0.5 Frank de Lange 7.0 true + https://github.com/Yetangitu/owncloud-apps + https://github.com/Yetangitu/owncloud-apps/issues + https://github.com/Yetangitu/owncloud-apps + + + pgsql + sqlite3 + mysql + 168132 diff --git a/files_opds/js/admin.js b/files_opds/js/admin.js index 598ce9f..dd95ca7 100644 --- a/files_opds/js/admin.js +++ b/files_opds/js/admin.js @@ -27,7 +27,8 @@ $(document).ready(function(){ opdsCoverY : $('#opds-cover-y').val(), opdsThumbX : $('#opds-thumb-x').val(), opdsThumbY : $('#opds-thumb-y').val(), - opdsFeedSubtitle : $('#opds-feed-subtitle').val() + opdsFeedSubtitle : $('#opds-feed-subtitle').val(), + opdsIsbndbKey : $('#opds-isbndb-key').val() }; OC.msg.startSaving('#opds-admin .msg'); $.post(OC.filePath('files_opds', 'ajax', 'admin.php'), data, opdsAdminCoverSettings.afterSave); @@ -42,8 +43,8 @@ $(document).ready(function(){ $('#opds-preview-opendocument').on("change", opdsAdminSettings.save); $('#opds-preview-msoffice').on("change", opdsAdminSettings.save); - $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y,#opds-feed-subtitle').blur(opdsAdminCoverSettings.save); - $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y,#opds-feed-subtitle').keypress(function( event ) { + $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y,#opds-feed-subtitle,#opds-isbndb-key').blur(opdsAdminCoverSettings.save); + $('#opds-cover-x,#opds-cover-y,#opds-thumb-x,#opds-thumb-y,#opds-feed-subtitle,#opds-isbndb-key').keypress(function( event ) { if (event.which == 13) { event.preventDefault(); opdsAdminCoverSettings.save(); diff --git a/files_opds/lib/google.php b/files_opds/lib/google.php new file mode 100644 index 0000000..4de70ea --- /dev/null +++ b/files_opds/lib/google.php @@ -0,0 +1,92 @@ + 0) { + self::parse($isbn,$meta); + return true; + } else { + $meta['rescan'] = date("Y-m-d\TH:i:sP", time() + Isbn::RESCAN_NOT_FOUND); + } + + return false; + } + + /** + * @brief parse Google response into OPDS $meta array + * + * @param array $data Google response (json_decoded into array) + * @param arrayref &$meta OPDS metadata array + * @return int errorcode (0 if success) + */ + static function parse($data,&$meta) { + foreach ($data as $key => $value) { + switch ($key) { + case 'description': + $meta['description'] = $value; + if(array_key_exists('notes',$data)) { + $meta['description'] .= ((trim($value) == false) ? '' : "\n\n") . $data['notes']; + } + break; + case 'subject_ids': + $meta['subjects'] = json_encode($value); + break; + /* rather pointless, ISBN is what brought us here in the first place and is alread set + case 'industryIdentifiers': + foreach($value as $array) { + if ($array['type'] = 'ISBN_13') { + $isbn13 = $array['identifier']; + } elseif ($array['type'] = 'ISBN_10') { + $isbn10 = $array['identifier']; + } + } + + $meta['isbn'] = (isset($isbn13)) ? $isbn13 : $isbn10; + break; + */ + case 'title': + $meta['title'] = $value; + break; + case 'authors': + $meta['author'] = json_encode($value); + break; + case 'language': + $meta['language'] = $value; + break; + case 'publisher': + $meta['publisher'] = $value; + break; + case 'publishedDate': + $meta['date'] = $value; + break; + } + } + + return true; + } +} diff --git a/files_opds/lib/isbn.php b/files_opds/lib/isbn.php new file mode 100644 index 0000000..b2a3e25 --- /dev/null +++ b/files_opds/lib/isbn.php @@ -0,0 +1,154 @@ + $value) { + switch ($key) { + case 'summary': + $meta['description'] = $value; + if(array_key_exists('notes',$data)) { + $meta['description'] .= ((trim($value) == false) ? '' : "\n\n") . $data['notes']; + } + break; + case 'subject_ids': + $meta['subjects'] = json_encode($value); + break; + case 'isbn10': + if(!(array_key_exists('isbn13', $data))) { + $meta['isbn'] = $value; + } + break; + case 'isbn13': + $meta['isbn'] = $value; + break; + case 'title': + if(!(array_key_exists('title_long',$data))) { + $meta['title'] = $value; + } + break; + case 'title_long': + $meta['title'] = $value; + break; + case 'author_data': + $meta['author'] = json_encode(array_column($value, 'name','id')); + break; + case 'language': + $meta['language'] = $value; + break; + case 'publisher_name': + $meta['publisher'] = $value; + if(array_key_exists('publisher_text',$data)) { + $meta['publisher'] .= ((trim($value) == false) ? '' : ', ') . $data['publisher_text']; + } + break; + } + } + + return true; + } +} diff --git a/files_opds/lib/meta.php b/files_opds/lib/meta.php index 76d460b..1ebdca8 100644 --- a/files_opds/lib/meta.php +++ b/files_opds/lib/meta.php @@ -40,6 +40,7 @@ class Meta $meta['copyright'] = ''; $meta['description'] = ''; $meta['subjects'] = ''; + $meta['rescan'] = null; return $meta; } @@ -65,20 +66,45 @@ class Meta * @return OC_DB_StatementWrapper */ protected static function save($meta) { - $sql = "INSERT INTO *PREFIX*opds_metadata (`id`, `updated`, `date`, `author`, `title`, `language`, `publisher`, `isbn`, `copyright`, `description`, `subjects`) VALUES (?,?,?,?,?,?,?,?,?,?,?)"; - $args = array( - $meta['id'], - $meta['updated'], - $meta['date'], - $meta['author'], - $meta['title'], - $meta['language'], - $meta['publisher'], - $meta['isbn'], - $meta['copyright'], - $meta['description'], - $meta['subjects'] - ); + $sql = "SELECT `id` FROM *PREFIX*opds_metadata WHERE `id`=?"; + $args = array($meta['id']); + $query = \OCP\DB::prepare($sql); + $result = $query->execute($args); + $data = $result->fetchRow(); + if (isset($data['id'])) { + $sql = "UPDATE *PREFIX*opds_metadata SET `updated`=?, `date`=?, `author`=?, `title`=?, `language`=?, `publisher`=?, `isbn`=?, `copyright`=?, `description`=?, `subjects`=?, `rescan`=? WHERE id=?"; + $args = array( + $meta['updated'], + $meta['date'], + $meta['author'], + $meta['title'], + $meta['language'], + $meta['publisher'], + $meta['isbn'], + $meta['copyright'], + $meta['description'], + $meta['subjects'], + $meta['rescan'], + $meta['id'] + ); + + } else { + $sql = "INSERT INTO *PREFIX*opds_metadata (`id`, `updated`, `date`, `author`, `title`, `language`, `publisher`, `isbn`, `copyright`, `description`, `subjects`, `rescan`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"; + $args = array( + $meta['id'], + $meta['updated'], + $meta['date'], + $meta['author'], + $meta['title'], + $meta['language'], + $meta['publisher'], + $meta['isbn'], + $meta['copyright'], + $meta['description'], + $meta['subjects'], + $meta['rescan'] + ); + } $query = \OCP\DB::prepare($sql); return $query->execute($args); @@ -93,7 +119,10 @@ class Meta * @return array of metadata */ public static function get($id) { - if (!($meta = self::load($id))) { + if (!($meta = self::load($id)) || (isset($meta['rescan']) && time() > $meta['rescan'])) { + if(isset($meta['rescan'])) { + $meta['rescan'] = null; + } $meta = self::scan($id); } return $meta; @@ -101,8 +130,6 @@ class Meta /** * @brief scan files for metadata - * PLAN: use search_lucene to extract metadata? Does not seem to support PDF1.6? - * solution: first ask search_lucene, if no data then scan file? * * @param int $id fileid * @return array $meta metadata @@ -110,16 +137,19 @@ class Meta public static function scan($id) { $meta = self::create($id); $path = \OC\Files\Filesystem::getLocalFile(\OC\Files\Filesystem::getPath($id)); - - switch (strtolower(substr(strrchr($path, "."), 1))) { - case 'epub': - self::epub($path,$meta); - break; - case 'pdf': - self::pdf($path,$meta); - break; - } + /* try to call function named 'type' with signature type($path,$meta) + * eg, pdf(), epub(), etc + */ + $type = strtolower(substr(strrchr($path, "."), 1)); + if(is_callable(array(__CLASS__, $type))) { + try { + self::$type($path,$meta); + } catch (Exception $e) { + Util::logWarn("no metadata scanner for format " . $type); + } + } + /* if title is not set, assume metadata was invalid or not present * use filename as title */ @@ -127,7 +157,7 @@ class Meta $info = pathinfo($path); $meta['title'] = basename($path,'.'.$info['extension']); } - // self::save($meta); + self::save($meta); return $meta; } @@ -137,21 +167,23 @@ class Meta * * @param string $path path to epub * @param arrayref $meta reference to array of metadata - * @return bool $success (true if metadata found) */ public static function epub($path,&$meta) { + $success = false; $epub = new Epub($path); - $meta['author'] = json_encode($epub->Authors()); - $meta['title'] = $epub->Title(); - $meta['date'] = $epub->Date(); - $meta['publisher'] = $epub->Publisher(); - $meta['copyright'] = $epub->Copyright(); - $meta['language'] = $epub->Language(); - $meta['description'] = strip_tags($epub->Description()); - $meta['isbn'] = $epub->ISBN(); - $meta['subjects'] = $epub->Subjects(); - - return true; + /* first try ISBN */ + if(!(($isbn = $epub->ISBN()) && (Isbn::get($isbn, $meta)))) { + /* use EPUB internal metadata instead */ + $meta['author'] = json_encode($epub->Authors()); + $meta['title'] = $epub->Title(); + $meta['date'] = $epub->Date(); + $meta['publisher'] = $epub->Publisher(); + $meta['copyright'] = $epub->Copyright(); + $meta['language'] = $epub->Language(); + $meta['description'] = strip_tags($epub->Description()); + $meta['isbn'] = $epub->ISBN(); + $meta['subjects'] = json_encode($epub->Subjects()); + } } /** @@ -159,10 +191,45 @@ class Meta * * @param string $path path to pdf * @param arrayref $meta reference to array of metadata - * @return bool $success (true if metadata found) */ public static function pdf($path,&$meta) { + if(\OC_Util::runningOnWindows()) { + /* not supported when running on Windows due to use of exec() */ + return; + } - return false; + /* first, try to get metadata through ISBN */ + $command = ['pdftotext -l 10 "','" -']; + $output=array(); + exec($command[0] . $path . $command[1], $output); + if (!(($output) && ($isbn = Isbn::scan($output)) && (Isbn::get($isbn,$meta)))) { + /* No ISBN, try PDF metadata */ + $output=array(); + $command = ["pdfinfo '","'|grep -we '^\(Title\|Author\|Subject\|Keywords\|CreationDate\|ModDate\)'"]; + exec($command[0] . $path . $command[1], $output); + foreach($output as $data) { + list($key, $value) = explode(':',$data,2); + $value = trim($value); + } + + if (!($value == '')) { + switch ($key) { + case 'Title': + $meta['title'] = $value; + break; + case 'Author': + $meta['author'] = $value; + break; + case 'Subject': + case 'Keywords': + $meta['subjects'] .= $value; + break; + case 'CreationDate': + case 'ModDate': + $meta['date'] = strtotime($value); + break; + } + } + } } } diff --git a/files_opds/templates/admin.php b/files_opds/templates/admin.php index d8f4886..13f60ce 100644 --- a/files_opds/templates/admin.php +++ b/files_opds/templates/admin.php @@ -23,10 +23,16 @@ function checkBox($format) {

t('OPDS')); ?>

-
- - " value="" /> -
+ + + + + + + + + +
" value="" />
" value="" />

t('Enable preview for:')); ?>

diff --git a/files_opds/templates/part.feed.acquisition.php b/files_opds/templates/part.feed.acquisition.php index 83c18eb..b0bff3d 100644 --- a/files_opds/templates/part.feed.acquisition.php +++ b/files_opds/templates/part.feed.acquisition.php @@ -3,15 +3,21 @@ id: - - urn:isbn: - - + + urn:isbn: + + + + + + + +