Windows: Do not change title/author if book files are in use, to prevent (a harmless) mismatch between title/author anf on disk path

This commit is contained in:
Kovid Goyal 2012-10-29 11:13:55 +05:30
parent 88620ed588
commit f4485b5e02
4 changed files with 75 additions and 38 deletions

View File

@ -871,12 +871,18 @@ class BooksModel(QAbstractTableModel): # {{{
try: try:
return self._set_data(index, value) return self._set_data(index, value)
except (IOError, OSError) as err: except (IOError, OSError) as err:
import traceback
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(get_gui(), _('Permission denied'), error_dialog(get_gui(), _('Permission denied'),
_('Could not change the on disk location of this' _('Could not change the on disk location of this'
' book. Is it open in another program?'), ' book. Is it open in another program?'),
det_msg=traceback.format_exc(), show=True) det_msg=p+traceback.format_exc(), show=True)
return False
error_dialog(get_gui(), _('Failed to set data'),
_('Could not set data, click Show Details to see why.'),
det_msg=traceback.format_exc(), show=True)
except: except:
import traceback import traceback
traceback.print_exc() traceback.print_exc()

View File

@ -91,22 +91,26 @@ class TitleEdit(EnLineEdit):
def commit(self, db, id_): def commit(self, db, id_):
title = self.current_val title = self.current_val
try: if title != self.original_val:
if self.COMMIT: # Only try to commit if changed. This allow setting of other fields
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False) # to work even if some of the book files are opened in windows.
else: try:
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False, if self.COMMIT:
commit=False) getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
except (IOError, OSError) as err: else:
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
import traceback commit=False)
fname = err.filename if err.filename else 'file' except (IOError, OSError) as err:
error_dialog(self, _('Permission denied'), if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
_('Could not open %s. Is it being used by another' import traceback
' program?')%fname, det_msg=traceback.format_exc(), fname = getattr(err, 'filename', None)
show=True) p = 'Locked file: %s\n\n'%fname if fname else ''
return False error_dialog(self, _('Permission denied'),
raise _('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=p+traceback.format_exc(), show=True)
return False
raise
return True return True
@dynamic_property @dynamic_property
@ -262,19 +266,23 @@ class AuthorsEdit(EditWithComplete):
def commit(self, db, id_): def commit(self, db, id_):
authors = self.current_val authors = self.current_val
try: if authors != self.original_val:
self.books_to_refresh |= db.set_authors(id_, authors, notify=False, # Only try to commit if changed. This allow setting of other fields
allow_case_change=True) # to work even if some of the book files are opened in windows.
except (IOError, OSError) as err: try:
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
import traceback allow_case_change=True)
fname = err.filename if err.filename else 'file' except (IOError, OSError) as err:
error_dialog(self, _('Permission denied'), if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
_('Could not open "%s". Is it being used by another' import traceback
' program?')%fname, det_msg=traceback.format_exc(), fname = getattr(err, 'filename', None)
show=True) p = 'Locked file: %s\n\n'%fname if fname else ''
return False error_dialog(self, _('Permission denied'),
raise _('Could not change the on disk location of this'
' book. Is it open in another program?'),
det_msg=p+traceback.format_exc(), show=True)
return False
raise
return True return True
@dynamic_property @dynamic_property

View File

@ -322,6 +322,7 @@ class MetadataSingleDialogBase(ResizableDialog):
' program?')%fname, det_msg=traceback.format_exc(), ' program?')%fname, det_msg=traceback.format_exc(),
show=True) show=True)
return return
raise
if mi is None: if mi is None:
return return
cdata = None cdata = None
@ -444,11 +445,12 @@ class MetadataSingleDialogBase(ResizableDialog):
except (IOError, OSError) as err: except (IOError, OSError) as err:
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback import traceback
fname = err.filename if err.filename else 'file' fname = getattr(err, 'filename', None)
p = 'Locked file: %s\n\n'%fname if fname else ''
error_dialog(self, _('Permission denied'), error_dialog(self, _('Permission denied'),
_('Could not open %s. Is it being used by another' _('Could not change the on disk location of this'
' program?')%fname, det_msg=traceback.format_exc(), ' book. Is it open in another program?'),
show=True) det_msg=p+traceback.format_exc(), show=True)
return False return False
raise raise
for widget in getattr(self, 'custom_metadata_widgets', []): for widget in getattr(self, 'custom_metadata_widgets', []):

View File

@ -2205,13 +2205,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def set(self, row, column, val, allow_case_change=False): def set(self, row, column, val, allow_case_change=False):
''' '''
Convenience method for setting the title, authors, publisher or rating Convenience method for setting the title, authors, publisher, tags or
rating
''' '''
id = self.data[row][0] id = self.data[row][0]
col = {'title':1, 'authors':2, 'publisher':3, 'rating':4, 'tags':7}[column] col = self.FIELD_MAP[column]
books_to_refresh = set() books_to_refresh = set()
self.data.set(row, col, val) set_args = (row, col, val)
if column == 'authors': if column == 'authors':
val = string_to_authors(val) val = string_to_authors(val)
books_to_refresh |= self.set_authors(id, val, notify=False, books_to_refresh |= self.set_authors(id, val, notify=False,
@ -2227,6 +2228,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
books_to_refresh |= \ books_to_refresh |= \
self.set_tags(id, [x.strip() for x in val.split(',') if x.strip()], self.set_tags(id, [x.strip() for x in val.split(',') if x.strip()],
append=False, notify=False, allow_case_change=allow_case_change) append=False, notify=False, allow_case_change=allow_case_change)
self.data.set(*set_args)
self.data.refresh_ids(self, [id]) self.data.refresh_ids(self, [id])
self.set_path(id, True) self.set_path(id, True)
self.notify('metadata', [id]) self.notify('metadata', [id])
@ -2474,6 +2476,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.clean_standard_field('authors', commit=True) self.clean_standard_field('authors', commit=True)
return books_to_refresh return books_to_refresh
def windows_check_if_files_in_use(self, book_id):
'''
Raises an EACCES IOError if any of the files in the folder of book_id
are opened in another program on windows.
'''
if iswindows:
path = self.path(book_id, index_is_id=True)
if path:
spath = os.path.join(self.library_path, *path.split('/'))
wam = None
if os.path.exists(spath):
try:
wam = WindowsAtomicFolderMove(spath)
finally:
if wam is not None:
wam.close_handles()
def set_authors(self, id, authors, notify=True, commit=True, def set_authors(self, id, authors, notify=True, commit=True,
allow_case_change=False): allow_case_change=False):
''' '''
@ -2482,6 +2501,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
:param authors: A list of authors. :param authors: A list of authors.
''' '''
self.windows_check_if_files_in_use(id)
books_to_refresh = self._set_authors(id, authors, books_to_refresh = self._set_authors(id, authors,
allow_case_change=allow_case_change) allow_case_change=allow_case_change)
self.dirtied(set([id])|books_to_refresh, commit=False) self.dirtied(set([id])|books_to_refresh, commit=False)
@ -2532,6 +2552,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
Note that even if commit is False, the db will still be committed to Note that even if commit is False, the db will still be committed to
because this causes the location of files to change because this causes the location of files to change
''' '''
self.windows_check_if_files_in_use(id)
if not self._set_title(id, title): if not self._set_title(id, title):
return return
self.set_path(id, index_is_id=True) self.set_path(id, index_is_id=True)