mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Remove race condition if user tries to mount folder as device while a normal device is connecting. Minor cleanups (see merge log).
This commit is contained in:
commit
7aee85def8
@ -54,10 +54,17 @@ class FOLDER_DEVICE(USBMS):
|
|||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
raise IOError, 'Path is not a folder'
|
raise IOError, 'Path is not a folder'
|
||||||
|
if path.endswith(os.sep):
|
||||||
self._main_prefix = path
|
self._main_prefix = path
|
||||||
|
else:
|
||||||
|
self._main_prefix = path + os.sep
|
||||||
self.booklist_class = BookList
|
self.booklist_class = BookList
|
||||||
self.is_connected = True
|
self.is_connected = True
|
||||||
|
|
||||||
|
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||||
|
detected_device=None):
|
||||||
|
pass
|
||||||
|
|
||||||
def disconnect_from_folder(self):
|
def disconnect_from_folder(self):
|
||||||
self._main_prefix = ''
|
self._main_prefix = ''
|
||||||
self.is_connected = False
|
self.is_connected = False
|
||||||
|
@ -117,7 +117,7 @@ class BookList(_BookList):
|
|||||||
def add_book(self, book, replace_metadata):
|
def add_book(self, book, replace_metadata):
|
||||||
if book not in self:
|
if book not in self:
|
||||||
self.append(book)
|
self.append(book)
|
||||||
return True
|
return False # subclasses return True if device metadata has changed
|
||||||
|
|
||||||
def remove_book(self, book):
|
def remove_book(self, book):
|
||||||
self.remove(book)
|
self.remove(book)
|
||||||
|
@ -113,7 +113,6 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
def _windows_space(cls, prefix):
|
def _windows_space(cls, prefix):
|
||||||
if not prefix:
|
if not prefix:
|
||||||
return 0, 0
|
return 0, 0
|
||||||
if prefix.endswith(os.sep):
|
|
||||||
prefix = prefix[:-1]
|
prefix = prefix[:-1]
|
||||||
win32file = __import__('win32file', globals(), locals(), [], -1)
|
win32file = __import__('win32file', globals(), locals(), [], -1)
|
||||||
try:
|
try:
|
||||||
|
@ -90,6 +90,7 @@ class USBMS(CLI, Device):
|
|||||||
self.count_found_in_bl += 1
|
self.count_found_in_bl += 1
|
||||||
else:
|
else:
|
||||||
item = self.book_from_path(prefix, lpath)
|
item = self.book_from_path(prefix, lpath)
|
||||||
|
changed = True
|
||||||
if metadata.add_book(item, replace_metadata=False):
|
if metadata.add_book(item, replace_metadata=False):
|
||||||
changed = True
|
changed = True
|
||||||
except: # Probably a filename encoding error
|
except: # Probably a filename encoding error
|
||||||
@ -106,10 +107,15 @@ class USBMS(CLI, Device):
|
|||||||
if not os.path.exists(ebook_dir): continue
|
if not os.path.exists(ebook_dir): continue
|
||||||
# Get all books in the ebook_dir directory
|
# Get all books in the ebook_dir directory
|
||||||
if self.SUPPORTS_SUB_DIRS:
|
if self.SUPPORTS_SUB_DIRS:
|
||||||
|
# build a list of files to check, so we can accurately report progress
|
||||||
|
flist = []
|
||||||
for path, dirs, files in os.walk(ebook_dir):
|
for path, dirs, files in os.walk(ebook_dir):
|
||||||
for filename in files:
|
for filename in files:
|
||||||
self.report_progress(0.5, _('Getting list of books on device...'))
|
if filename != self.METADATA_CACHE:
|
||||||
changed = update_booklist(filename, path, prefix)
|
flist.append({'filename':filename, 'path': path})
|
||||||
|
for i, f in enumerate(flist):
|
||||||
|
self.report_progress(i/float(len(flist)), _('Getting list of books on device...'))
|
||||||
|
changed = update_booklist(f['filename'], f['path'], prefix)
|
||||||
if changed:
|
if changed:
|
||||||
need_sync = True
|
need_sync = True
|
||||||
else:
|
else:
|
||||||
@ -123,7 +129,10 @@ class USBMS(CLI, Device):
|
|||||||
# if count != len(bl) then there were items in it that we did not
|
# if count != len(bl) then there were items in it that we did not
|
||||||
# find on the device. If need_sync is True then there were either items
|
# find on the device. If need_sync is True then there were either items
|
||||||
# on the device that were not in bl or some of the items were changed.
|
# on the device that were not in bl or some of the items were changed.
|
||||||
#print "count found in cache: %d, count of files in cache: %d, must_sync_cache: %s" % (self.count_found_in_bl, len(bl), need_sync)
|
|
||||||
|
#print "count found in cache: %d, count of files in cache: %d, need_sync: %s, must_sync_cache: %s" % \
|
||||||
|
# (self.count_found_in_bl, len(bl), need_sync,
|
||||||
|
# need_sync or self.count_found_in_bl != len(bl))
|
||||||
if self.count_found_in_bl != len(bl) or need_sync:
|
if self.count_found_in_bl != len(bl) or need_sync:
|
||||||
if oncard == 'cardb':
|
if oncard == 'cardb':
|
||||||
self.sync_booklists((None, None, metadata))
|
self.sync_booklists((None, None, metadata))
|
||||||
|
@ -19,7 +19,7 @@ from calibre.devices.scanner import DeviceScanner
|
|||||||
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
|
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
|
||||||
pixmap_to_data, warning_dialog, \
|
pixmap_to_data, warning_dialog, \
|
||||||
question_dialog
|
question_dialog
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string, authors_to_sort_string
|
||||||
from calibre import preferred_encoding
|
from calibre import preferred_encoding
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
from calibre.devices.errors import FreeSpaceError
|
from calibre.devices.errors import FreeSpaceError
|
||||||
@ -87,6 +87,8 @@ class DeviceManager(Thread):
|
|||||||
self.scanner = DeviceScanner()
|
self.scanner = DeviceScanner()
|
||||||
self.connected_device = None
|
self.connected_device = None
|
||||||
self.ejected_devices = set([])
|
self.ejected_devices = set([])
|
||||||
|
self.connected_device_is_folder = False
|
||||||
|
self.folder_connection_path = None
|
||||||
|
|
||||||
def report_progress(self, *args):
|
def report_progress(self, *args):
|
||||||
pass
|
pass
|
||||||
@ -99,7 +101,7 @@ class DeviceManager(Thread):
|
|||||||
def device(self):
|
def device(self):
|
||||||
return self.connected_device
|
return self.connected_device
|
||||||
|
|
||||||
def do_connect(self, connected_devices):
|
def do_connect(self, connected_devices, is_folder_device):
|
||||||
for dev, detected_device in connected_devices:
|
for dev, detected_device in connected_devices:
|
||||||
dev.reset(detected_device=detected_device,
|
dev.reset(detected_device=detected_device,
|
||||||
report_progress=self.report_progress)
|
report_progress=self.report_progress)
|
||||||
@ -110,7 +112,8 @@ class DeviceManager(Thread):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
continue
|
continue
|
||||||
self.connected_device = dev
|
self.connected_device = dev
|
||||||
self.connected_slot(True)
|
self.connected_device_is_folder = is_folder_device
|
||||||
|
self.connected_slot(True, is_folder_device)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -128,7 +131,7 @@ class DeviceManager(Thread):
|
|||||||
if self.connected_device in self.ejected_devices:
|
if self.connected_device in self.ejected_devices:
|
||||||
self.ejected_devices.remove(self.connected_device)
|
self.ejected_devices.remove(self.connected_device)
|
||||||
else:
|
else:
|
||||||
self.connected_slot(False)
|
self.connected_slot(False, self.connected_device_is_folder)
|
||||||
self.connected_device = None
|
self.connected_device = None
|
||||||
|
|
||||||
def detect_device(self):
|
def detect_device(self):
|
||||||
@ -149,17 +152,19 @@ class DeviceManager(Thread):
|
|||||||
if possibly_connected:
|
if possibly_connected:
|
||||||
possibly_connected_devices.append((device, detected_device))
|
possibly_connected_devices.append((device, detected_device))
|
||||||
if possibly_connected_devices:
|
if possibly_connected_devices:
|
||||||
if not self.do_connect(possibly_connected_devices):
|
if not self.do_connect(possibly_connected_devices,
|
||||||
|
is_folder_device=False):
|
||||||
print 'Connect to device failed, retrying in 5 seconds...'
|
print 'Connect to device failed, retrying in 5 seconds...'
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
if not self.do_connect(possibly_connected_devices):
|
if not self.do_connect(possibly_connected_devices,
|
||||||
|
is_folder_device=False):
|
||||||
print 'Device connect failed again, giving up'
|
print 'Device connect failed again, giving up'
|
||||||
|
|
||||||
def umount_device(self, *args):
|
def umount_device(self, *args):
|
||||||
if self.is_device_connected:
|
if self.is_device_connected:
|
||||||
self.connected_device.eject()
|
self.connected_device.eject()
|
||||||
self.ejected_devices.add(self.connected_device)
|
self.ejected_devices.add(self.connected_device)
|
||||||
self.connected_slot(False)
|
self.connected_slot(False, self.connected_device_is_folder)
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
if not self.jobs.empty():
|
if not self.jobs.empty():
|
||||||
@ -170,6 +175,17 @@ class DeviceManager(Thread):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.keep_going:
|
while self.keep_going:
|
||||||
|
if not self.is_device_connected and \
|
||||||
|
self.folder_connection_path is not None:
|
||||||
|
f = self.folder_connection_path
|
||||||
|
self.folder_connection_path = None # Make sure we try this folder only once
|
||||||
|
try:
|
||||||
|
dev = FOLDER_DEVICE(f)
|
||||||
|
self.do_connect([[dev, None],], is_folder_device=True)
|
||||||
|
except:
|
||||||
|
print 'Unable to open folder as device', f
|
||||||
|
traceback.print_exc()
|
||||||
|
else:
|
||||||
self.detect_device()
|
self.detect_device()
|
||||||
while True:
|
while True:
|
||||||
job = self.next()
|
job = self.next()
|
||||||
@ -182,7 +198,6 @@ class DeviceManager(Thread):
|
|||||||
break
|
break
|
||||||
time.sleep(self.sleep_time)
|
time.sleep(self.sleep_time)
|
||||||
|
|
||||||
|
|
||||||
def create_job(self, func, done, description, args=[], kwargs={}):
|
def create_job(self, func, done, description, args=[], kwargs={}):
|
||||||
job = DeviceJob(func, done, self.job_manager,
|
job = DeviceJob(func, done, self.job_manager,
|
||||||
args=args, kwargs=kwargs, description=description)
|
args=args, kwargs=kwargs, description=description)
|
||||||
@ -208,21 +223,19 @@ class DeviceManager(Thread):
|
|||||||
return self.create_job(self._get_device_information, done,
|
return self.create_job(self._get_device_information, done,
|
||||||
description=_('Get device information'))
|
description=_('Get device information'))
|
||||||
|
|
||||||
|
# This will be called on the GUI thread. Because of this, we must store
|
||||||
|
# information that the scanner thread will use to do the real work.
|
||||||
def connect_to_folder(self, path):
|
def connect_to_folder(self, path):
|
||||||
dev = FOLDER_DEVICE(path)
|
self.folder_connection_path = path
|
||||||
try:
|
|
||||||
dev.open()
|
|
||||||
except:
|
|
||||||
print 'Unable to open device', dev
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
self.connected_device = dev
|
|
||||||
self.connected_slot(True)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
# This is called on the GUI thread. No problem here, because it calls the
|
||||||
|
# device driver, telling it to tell the scanner when it passes by that the
|
||||||
|
# folder has disconnected.
|
||||||
def disconnect_folder(self):
|
def disconnect_folder(self):
|
||||||
if self.connected_device is not None:
|
if self.connected_device is not None:
|
||||||
if hasattr(self.connected_device, 'disconnect_from_folder'):
|
if hasattr(self.connected_device, 'disconnect_from_folder'):
|
||||||
|
# As we are on the wrong thread, this call must *not* do
|
||||||
|
# anything besides set a flag that the right thread will see.
|
||||||
self.connected_device.disconnect_from_folder()
|
self.connected_device.disconnect_from_folder()
|
||||||
|
|
||||||
def _books(self):
|
def _books(self):
|
||||||
@ -1133,9 +1146,11 @@ class DeviceGUI(object):
|
|||||||
resend_metadata = True
|
resend_metadata = True
|
||||||
# Set author_sort if it isn't already
|
# Set author_sort if it isn't already
|
||||||
asort = getattr(book, 'author_sort', None)
|
asort = getattr(book, 'author_sort', None)
|
||||||
if not asort:
|
if not asort and book.authors:
|
||||||
pass
|
book.author_sort = authors_to_sort_string(book.authors)
|
||||||
|
resend_metadata = True
|
||||||
|
|
||||||
if resend_metadata:
|
if resend_metadata:
|
||||||
# Correcting metadata cache on device.
|
# Correct the metadata cache on device.
|
||||||
if self.device_manager.is_device_connected:
|
if self.device_manager.is_device_connected:
|
||||||
self.device_manager.sync_booklists(None, booklists)
|
self.device_manager.sync_booklists(None, booklists)
|
||||||
|
@ -670,10 +670,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.search.setMaximumWidth(self.width()-150)
|
self.search.setMaximumWidth(self.width()-150)
|
||||||
|
|
||||||
def connect_to_folder(self):
|
def connect_to_folder(self):
|
||||||
dir = choose_dir(self, 'Select Device Folder', 'Select folder to open')
|
dir = choose_dir(self, 'Select Device Folder',
|
||||||
|
_('Select folder to open as device'))
|
||||||
if dir is not None:
|
if dir is not None:
|
||||||
self.device_manager.connect_to_folder(dir)
|
self.device_manager.connect_to_folder(dir)
|
||||||
self._sync_menu.disconnect_from_folder_action.setEnabled(True)
|
|
||||||
|
|
||||||
def disconnect_from_folder(self):
|
def disconnect_from_folder(self):
|
||||||
self.device_manager.disconnect_folder()
|
self.device_manager.disconnect_folder()
|
||||||
@ -945,12 +945,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
elif model.location_for_row(x) == 'cardb':
|
elif model.location_for_row(x) == 'cardb':
|
||||||
self.card_b_view.write_settings()
|
self.card_b_view.write_settings()
|
||||||
|
|
||||||
def device_detected(self, connected):
|
def device_detected(self, connected, is_folder_device):
|
||||||
'''
|
'''
|
||||||
Called when a device is connected to the computer.
|
Called when a device is connected to the computer.
|
||||||
'''
|
'''
|
||||||
if connected:
|
if connected:
|
||||||
self._sync_menu.connect_to_folder_action.setEnabled(False)
|
self._sync_menu.connect_to_folder_action.setEnabled(False)
|
||||||
|
if is_folder_device:
|
||||||
|
self._sync_menu.disconnect_from_folder_action.setEnabled(True)
|
||||||
self.device_manager.get_device_information(\
|
self.device_manager.get_device_information(\
|
||||||
Dispatcher(self.info_read))
|
Dispatcher(self.info_read))
|
||||||
self.set_default_thumbnail(\
|
self.set_default_thumbnail(\
|
||||||
|
Loading…
x
Reference in New Issue
Block a user