diff --git a/oml/item/models.py b/oml/item/models.py index 3922b4f..c9523fb 100644 --- a/oml/item/models.py +++ b/oml/item/models.py @@ -556,6 +556,11 @@ class Item(db.Model): return True return False + def missing_file(self): + logger.debug('file is missing! %s: %s', self.id, self.get_path()) + self.info['missing'] = True + state.db.session.add(self) + def remove_file(self): for f in self.files.all(): path = f.fullpath() diff --git a/oml/item/scan.py b/oml/item/scan.py index 79c5ead..78c8fe6 100644 --- a/oml/item/scan.py +++ b/oml/item/scan.py @@ -29,10 +29,11 @@ def remove_missing(books=None): dirty = False logger.debug('remove missing') prefix = get_prefix() + oml_prefix = os.path.dirname(prefix) if books is None: books = collect_books(prefix) with db.session(): - if os.path.exists(prefix): + if os.path.exists(prefix) and os.path.exists(oml_prefix): logger.debug('scan for removed files') db_paths = [] items = {} @@ -44,29 +45,35 @@ def remove_missing(books=None): if f.item: items[path] = f.sha1 else: + logger.debug('remove orphaned file %s', f) state.db.session.delete(f) dirty = True if dirty: state.db.session.commit() dirty = False - nfd_books = {unicodedata.normalize('NFD', path) for path in books} + nfc_books = {unicodedata.normalize('NFC', path) for path in books} removed = [ path for path in db_paths - if unicodedata.normalize('NFD', path) not in nfd_books + if unicodedata.normalize('NFC', path) not in nfc_books ] - if removed: + if removed and os.path.exists(prefix) and os.path.exists(oml_prefix): logger.debug('%s files removed', len(removed)) ids = [items[path] for path in removed] - orphaned = set(ids) - for i in Item.query.filter(Item.id.in_(ids)): - i.remove_file() - orphaned.remove(i.id) - dirty = True - if orphaned: - logger.debug('%s files orphaned', len(orphaned)) - for f in File.query.filter(File.sha1.in_(orphaned)): - state.db.session.delete(f) - dirty = True + if ids: + orphaned = set(ids) + for i in Item.query.filter(Item.id.in_(ids)): + if state.shutdown: + continue + i.missing_file() + orphaned.remove(i.id) + dirty = True + if orphaned: + logger.debug('%s files orphaned', len(orphaned)) + for f in File.query.filter(File.sha1.in_(orphaned)): + if state.shutdown: + continue + state.db.session.delete(f) + dirty = True if dirty: state.db.session.commit() state.cache.clear('group:') @@ -182,6 +189,10 @@ def run_scan(): add_record('edititem', item.id, item.meta) item.update() added += 1 + if file and file.item.info.get('missing'): + logger.debug('missing file showed up again %s: %s', id, file.fullpath()) + del file.item.info['missing'] + file.item.save() if file and not file.item.added: file.item.added = datetime.utcnow() if file.item.accessed: diff --git a/static/js/infoView.js b/static/js/infoView.js index 850bd79..cb1210b 100644 --- a/static/js/infoView.js +++ b/static/js/infoView.js @@ -256,6 +256,42 @@ oml.ui.infoView = function(externalData, isMixed) { ], float: 'right' }) + : data.missing + ? Ox.FormElementGroup({ + elements: [ + Ox.Button({ + style: 'squared', + title: Ox._('Book Missing'), + width: 112 + }) + .bindEvent({ + click: function() { + if (!oml.readOnly) { + oml.api.openFolder({id: oml.user.ui.item}); + } + } + }), + Ox.MenuButton({ + items: [ + ].concat(oml.readOnly ? [] : [ + {id: 'show', title: Ox._('Show File')} + ]), + overlap: 'left', + style: 'squared', + title: 'select', + tooltip: Ox._('File was removed'), + type: 'image' + }) + .bindEvent({ + click: function(data_) { + if (!oml.readOnly) { + oml.api.openFolder({id: oml.user.ui.item}); + } + } + }) + ], + float: 'right' + }) : Ox.FormElementGroup({ elements: [ Ox.Button({