diff --git a/dist/files_opds-0.5.tar.gz b/dist/files_opds-0.5.tar.gz new file mode 100644 index 0000000..dda5260 Binary files /dev/null and b/dist/files_opds-0.5.tar.gz differ 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: + + + + + + + +