From 9c765bf863122a1a73f64e64341650704ef728a4 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 1 Feb 2019 14:27:28 +0530 Subject: [PATCH 1/5] don't deselect on click, only scroll if not in view --- static/reader/epub.js | 4 +--- static/reader/pdf.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/static/reader/epub.js b/static/reader/epub.js index b4326a3..9aca10f 100644 --- a/static/reader/epub.js +++ b/static/reader/epub.js @@ -113,9 +113,7 @@ function getText(book, cfiRange, cb) { function onHighlightClicked(e) { console.log("highlight clicked", e.target.dataset.epubcfi); - if(e.target.classList.contains('selected')) { - e.target.classList.remove('selected') - } else { + if(!e.target.classList.contains('selected')) { document.querySelectorAll('.epubjs-hl.selected').forEach(function(other) { other.classList.remove('selected') }) diff --git a/static/reader/pdf.js b/static/reader/pdf.js index 44db149..1e4b003 100644 --- a/static/reader/pdf.js +++ b/static/reader/pdf.js @@ -22,7 +22,7 @@ Ox.load({ } setTimeout(function() { var el = document.querySelector('.a' + annotation.id); - if (el) { + if (el && !isInView(el)) { document.querySelector('#viewerContainer').scrollTop = el.offsetTop + el.parentElement.offsetTop - 64; } }, delay) @@ -134,10 +134,7 @@ function renderAnnotation(annotation) { 'width:' + Math.abs(bounds[0] - bounds[2]) + 'px; height:' + Math.abs(bounds[1] - bounds[3]) + 'px;'); el.addEventListener('click', function() { - if (el.classList.contains('selected')) { - deselectAnnotation(annotation.id) - Ox.$parent.postMessage('selectAnnotation', {id: null}) - } else { + if (!el.classList.contains('selected')) { selectAnnotation(annotation.id) Ox.$parent.postMessage('selectAnnotation', {id: annotation.id}) } @@ -204,3 +201,10 @@ function loadAnnotations(page) { }) } +function isInView(element) { + var docViewTop = $(window).scrollTop(); + var docViewBottom = docViewTop + $(window).height(); + var elementTop = $(element).offset().top; + var elementBottom = elementTop + $(element).height(); + return elementTop < docViewBottom && elementBottom > docViewTop; +} From fbcbc2ab7b8177c3058d11ae702d4b77773908ec Mon Sep 17 00:00:00 2001 From: j Date: Fri, 1 Feb 2019 14:49:43 +0530 Subject: [PATCH 2/5] export annotations --- static/js/annotationPanel.js | 17 +++++++- static/js/exportAnnotationsDialog.js | 62 ++++++++++++++++++++++++++++ static/js/viewer.js | 4 +- static/json/js.json | 1 + 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 static/js/exportAnnotationsDialog.js diff --git a/static/js/annotationPanel.js b/static/js/annotationPanel.js index 847ef54..0ca5c7a 100644 --- a/static/js/annotationPanel.js +++ b/static/js/annotationPanel.js @@ -31,6 +31,8 @@ oml.ui.annotationPanel = function() { {id: 'note', title: Ox._('By Note Text'), checked: false}, {id: 'date', title: Ox._('By Date Added'), checked: false} ]}, + {}, + {id: 'exportAnnotations', title: Ox._('Export Annotations')}, ], style: 'square', title: 'set', @@ -39,7 +41,20 @@ oml.ui.annotationPanel = function() { }).css({ // borderColor: 'transparent', float: 'right' - }).appendTo($bar); + }).appendTo($bar) + .bindEvent({ + click: function(data) { + var id = data.id; + if (id == 'exportAnnotations') { + oml.api.get({ + id: oml.user.ui.item, + keys: [] + }, function(result) { + oml.ui.exportAnnotationsDialog(result.data).open() + }) + } + } + }); var ui = oml.user.ui; var that = Ox.SplitPanel({ diff --git a/static/js/exportAnnotationsDialog.js b/static/js/exportAnnotationsDialog.js new file mode 100644 index 0000000..f891efa --- /dev/null +++ b/static/js/exportAnnotationsDialog.js @@ -0,0 +1,62 @@ +'use strict'; + +oml.ui.exportAnnotationsDialog = function(data) { + + var ui = oml.user.ui, + + $text = Ox.Input({ + type: 'textarea', + style: 'squared', + value: getAnnotationsText(), + width: 640, + height: 480 + }) + .css({margin: '16px'}), + + that = Ox.Dialog({ + buttons: [ + Ox.Button({ + id: 'done', + style: 'squared', + title: Ox._('Done') + }) + .bindEvent({ + click: function() { + that.close(); + } + }) + ], + closeButton: true, + content: $text, + height: 480 + 2 * 16, + keys: {enter: 'done'}, + removeOnClose: true, + title: Ox._('Export Annotations'), + width: 640 + 2* 16 + }) + .bindEvent({ + close: function() { + that.close(); + }, + open: function() { + // .. + } + }); + + function getAnnotationsText() { + var annotations = oml.$ui.viewer.getAnnotations() + var text = 'Annotations for ' + data.title + ' (' + data.author.join(', ') + ')\n\n\n\n' + text += annotations.map(function(annotation) { + var text = 'Quote:\n\n' + annotation.text + if (annotation.notes.length) { + text += '\n\nNotes:\n' + annotation.notes.map(function(note) { + return note.value + }).join('\n\n') + } + return text + }).join('\n\n\n\n') + return text + } + return that; + +}; diff --git a/static/js/viewer.js b/static/js/viewer.js index 092657a..e45c4ac 100644 --- a/static/js/viewer.js +++ b/static/js/viewer.js @@ -143,6 +143,8 @@ oml.ui.viewer = function() { that.postMessage = function(event, data) { $iframe && $iframe.postMessage(event, data) }; - + that.getAnnotations = function() { + return annotations; + } return that.updateElement(); }; diff --git a/static/json/js.json b/static/json/js.json index 5578ee0..f283185 100644 --- a/static/json/js.json +++ b/static/json/js.json @@ -19,6 +19,7 @@ "deleteListDialog.js", "editDialog.js", "errorDialog.js", + "exportAnnotationsDialog.js", "filter.js", "filtersInnerPanel.js", "filtersOuterPanel.js", From ac84e82a52aaa50122bc2030c8381ace8918895d Mon Sep 17 00:00:00 2001 From: j Date: Fri, 1 Feb 2019 14:56:33 +0530 Subject: [PATCH 3/5] logging --- oml/nodes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oml/nodes.py b/oml/nodes.py index 9dda107..e3621d9 100644 --- a/oml/nodes.py +++ b/oml/nodes.py @@ -504,7 +504,7 @@ class Nodes(Thread): while not state.shutdown: args = self._q.get() if args: - logger.debug('processing nodes queue: next: %s, %s entries in queue', args[0], self._q.qsize()) + logger.debug('processing nodes queue: next: "%s", %s entries in queue', args[0], self._q.qsize()) if args[0] == 'add': self._add(*args[1:]) elif args[0] == 'pull': @@ -514,7 +514,7 @@ class Nodes(Thread): def queue(self, *args): if args: - logger.debug('add %s to queue, queue currently has %s entries', args[0], self._q.qsize()) + logger.debug('queue "%s", %s entries in queue', args[0], self._q.qsize()) self._q.put(list(args)) def is_online(self, id): @@ -568,7 +568,7 @@ class Nodes(Thread): users = [] with db.session(): from user.models import User - for u in User.query.filter(User.id!=settings.USER_ID).filter_by(peered=True).all(): + for u in User.query.filter(User.id != settings.USER_ID).filter_by(peered=True).all(): users.append(u.json(['id', 'index', 'name'])) users.sort(key=user_sort_key) for u in users: From 70beacd8484b199c1f8c8560743a60b15ec32b48 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 1 Feb 2019 15:33:06 +0530 Subject: [PATCH 4/5] use new google api to find books by title/author --- oml/meta/google.py | 100 +++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/oml/meta/google.py b/oml/meta/google.py index 713647d..029124a 100644 --- a/oml/meta/google.py +++ b/oml/meta/google.py @@ -17,7 +17,7 @@ import logging logger = logging.getLogger(__name__) NAMESPACES = { - 'openSearch':'http://a9.com/-/spec/opensearchrss/1.0/', + 'openSearch': 'http://a9.com/-/spec/opensearchrss/1.0/', 'atom' : 'http://www.w3.org/2005/Atom', 'dc' : 'http://purl.org/dc/terms', 'gbs' : 'http://schemas.google.com/books/2008', @@ -25,29 +25,6 @@ NAMESPACES = { } XPath = partial(etree.XPath, namespaces=NAMESPACES) -def find_(query): - logger.debug('find %s', query) - query += ' isbn' - isbns = [] - for r in ox.web.google.find(query): - isbns += find_isbns(' '.join(r)) - logger.debug('isbns', isbns) - results = [] - done = set() - for isbn in isbns: - if isbn not in done: - r = { - 'isbn': isbn, - 'primaryid': ['isbn', isbn] - } - results.append(r) - done.add(isbn) - if len(isbn) == 10: - done.add(stdnum.isbn.to_isbn13(isbn)) - if len(isbn) == 13 and isbn.startswith('978'): - done.add(stdnum.isbn.to_isbn10(isbn)) - return results - def parse_entry(entry_): entry_id = XPath('descendant::atom:id') creator = XPath('descendant::dc:creator') @@ -90,7 +67,7 @@ def parse_entry(entry_): info = decode_html_data(info) return info -def find(title=None, author=None): +def find_feeds(title=None, author=None): ''' parts = [] if title: @@ -107,36 +84,48 @@ def find(title=None, author=None): url = 'http://books.google.com/books/feeds/volumes?' + urlencode({ 'q': q.strip(), 'max-results': 20, - 'start-index':1, - 'min-viewability':'none', + 'start-index': 1, + 'min-viewability': 'none', }) data = read_url(url) - feed = etree.fromstring(data, - parser=etree.XMLParser(recover=True, no_network=True)) + feed = etree.fromstring(data, parser=etree.XMLParser(recover=True, no_network=True)) results = [] isbns = set() for entry_ in XPath('//atom:entry')(feed): - info = parse_entry(entry_) - if 'isbn' in info and not 'isbn' in isbns: - results.append(info) + entry = parse_entry(entry_) + if 'isbn' in entry and 'isbn' not in isbns: + results.append(info(entry['isbn'])) isbns.add(info['isbn']) return results -def info_old(isbn): - url = 'http://books.google.com/books/feeds/volumes?' + urlencode({ - 'q': 'isnb:' + isbn, - 'max-results':1, - 'start-index':1, - 'min-viewability':'none', - }) - data = read_url(url) - feed = etree.fromstring(data, - parser=etree.XMLParser(recover=True, no_network=True)) - for entry_ in XPath('//atom:entry')(feed): - info = parse_entry(entry_) - info['isbn'] = isbn - return info - return {} +def find(title=None, author=None): + q = '' + if title: + q += title + ' ' + if author: + q += author + url = 'https://www.googleapis.com/books/v1/volumes?q=%s' % q + api_key = settings.server.get('google_api_key') + if api_key: + url += '&key=' + api_key + if api_limit.error: + raise IOError(url) + while not api_limit.consume(1): + logger.debug('hitting google api to fast, waiting 1 second') + sleep(1) + r = get_json(url, timeout=-1) + if 'error' in r: + logger.debug('got google api error, dont call for 10 minutes') + store.delete(url) + api_limit.error = True + raise IOError(url, r) + if 'items' not in r: + logger.debug('unknown %s: %s [%s]', key, value, r) + return [] + results = [] + for item in r['items']: + results.append(parse_item(item)) + return results def info(value): key = 'isbn' @@ -155,11 +144,14 @@ def info(value): store.delete(url) api_limit.error = True raise IOError(url, r) - if not 'items' in r: + if 'items' not in r: logger.debug('unknown %s: %s [%s]', key, value, r) return {} - _data = r['items'][0]['volumeInfo'] - _id = r['items'][0]['id'] + return parse_item(r['items'][0]) + +def parse_item(item): + _data = item['volumeInfo'] + _id = item['id'] data = {} for key in [ 'authors', @@ -169,17 +161,17 @@ def info(value): 'publishedDate', 'publisher', 'title', - ]: + ]: if key in _data: data[{ 'authors': 'author', 'pageCount': 'pages', 'publishedDate': 'date', - }.get(key,key)] = _data[key] + }.get(key, key)] = _data[key] if 'subtitle' in _data and _data['subtitle'].strip(): data['title'] = '{title}: {subtitle}'.format(**_data) - if r['items'][0]['accessInfo']['viewability'] != 'NO_PAGES': + if item['accessInfo']['viewability'] != 'NO_PAGES': #data['cover'] = 'https://books.google.com/books?id=%s&pg=PP1&img=1&zoom=0&hl=en' % _id data['cover'] = 'https://books.google.com/books/content/images/frontcover/%s?fife=w600-rw' % _id @@ -191,7 +183,7 @@ def info(value): if 'industryIdentifiers' in _data: for k in _data['industryIdentifiers']: if k['type'].startswith('ISBN'): - if not 'isbn' in data: + if 'isbn' not in data: data['isbn'] = [] data['isbn'].append(k['identifier']) else: From c05d10b6f19b64a6a59532026740d9ceddf2f1d4 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 1 Feb 2019 16:05:15 +0530 Subject: [PATCH 5/5] unrar check --- ctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctl b/ctl index 3c81e95..da49820 100755 --- a/ctl +++ b/ctl @@ -63,7 +63,7 @@ else if [ $SYSTEM == "Darwin" ]; then PLATFORM="darwin64" PLATFORM_PYTHON=3.7 - if [ -e $BASE/platform_${PLATFORM}/lib/libunrar.dylib ]; then + if [ -e "$BASE/platform_${PLATFORM}/lib/libunrar.dylib" ]; then export UNRAR_LIB_PATH="$BASE/platform_${PLATFORM}/lib/libunrar.dylib" fi fi