diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 4877cd359e..56c950bd16 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -217,7 +217,7 @@ class DevicePlugin(Plugin): ''' Unix version of :meth:`can_handle_windows` - :param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product, + :param device_info: Is a tuple of (vid, pid, bcd, manufacturer, product, serial number) ''' diff --git a/src/calibre/devices/prst1/__init__.py b/src/calibre/devices/prst1/__init__.py index 4a6198474a..4ed1c1cbbe 100644 --- a/src/calibre/devices/prst1/__init__.py +++ b/src/calibre/devices/prst1/__init__.py @@ -2,5 +2,6 @@ # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai __license__ = 'GPL v3' -__copyright__ = '2010, Timothy Legge and Kovid Goyal ' -__docformat__ = 'restructuredtext en' \ No newline at end of file +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + diff --git a/src/calibre/devices/prst1/books.py b/src/calibre/devices/prst1/books.py deleted file mode 100644 index 40e70f2af0..0000000000 --- a/src/calibre/devices/prst1/books.py +++ /dev/null @@ -1,9 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2010, Timothy Legge ' -''' -''' - -class ImageWrapper(object): - def __init__(self, image_path): - self.image_path = image_path - diff --git a/src/calibre/devices/prst1/driver.py b/src/calibre/devices/prst1/driver.py index 0fde160e50..327334aaec 100644 --- a/src/calibre/devices/prst1/driver.py +++ b/src/calibre/devices/prst1/driver.py @@ -2,7 +2,7 @@ # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai __license__ = 'GPL v3' -__copyright__ = '2010, Timothy Legge and Kovid Goyal ' +__copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' ''' @@ -10,20 +10,26 @@ Device driver for the SONY T1 devices ''' import os, time, calendar, re - import sqlite3 as sqlite from contextlib import closing -from itertools import cycle + from calibre.devices.usbms.driver import USBMS, debug_print -from calibre import __appname__, prints +from calibre.devices.usbms.device import USBDevice from calibre.devices.usbms.books import CollectionsBookList from calibre.devices.usbms.books import BookList -from calibre.devices.prst1.books import ImageWrapper +from calibre.constants import islinux + +DBPATH = 'Sony_Reader/database/books.db' +THUMBPATH = 'Sony_Reader/database/cache/books/%s/thumbnail/main_thumbnail.jpg' + +class ImageWrapper(object): + def __init__(self, image_path): + self.image_path = image_path class PRST1(USBMS): name = 'SONY PRST1 and newer Device Interface' gui_name = 'SONY Reader' - description = _('Communicate with Sony PRST1 and newer eBook readers') + description = _('Communicate with the PRST1 and newer SONY eBook readers') author = 'Kovid Goyal' supported_platforms = ['windows', 'osx', 'linux'] path_sep = '/' @@ -41,6 +47,8 @@ class PRST1(USBMS): WINDOWS_MAIN_MEM = re.compile( r'(PRS-T1&)' ) + MAIN_MEMORY_VOLUME_LABEL = 'SONY Reader Main Memory' + STORAGE_CARD_VOLUME_LABEL = 'SONY Reader Storage Card' THUMBNAIL_HEIGHT = 144 SUPPORTS_SUB_DIRS = True @@ -51,29 +59,29 @@ class PRST1(USBMS): _('Comma separated list of metadata fields ' 'to turn into collections on the device. Possibilities include: ')+\ 'series, tags, authors', - _('Upload separate cover thumbnails for books') + - ':::'+_('Normally, the SONY readers get the cover image from the' - ' ebook file itself. With this option, calibre will send a ' - 'separate cover image to the reader, useful if you are ' - 'sending DRMed books in which you cannot change the cover.'), - _('Refresh separate covers when using automatic management') + - ':::' + - _('Set this option to have separate book covers uploaded ' - 'every time you connect your device. Unset this option if ' - 'you have so many books on the reader that performance is ' - 'unacceptable.'), - _('Preserve cover aspect ratio when building thumbnails') + - ':::' + - _('Set this option if you want the cover thumbnails to have ' - 'the same aspect ratio (width to height) as the cover. ' - 'Unset it if you want the thumbnail to be the maximum size, ' - 'ignoring aspect ratio.'), + _('Upload separate cover thumbnails for books') + + ':::'+_('Normally, the SONY readers get the cover image from the' + ' ebook file itself. With this option, calibre will send a ' + 'separate cover image to the reader, useful if you are ' + 'sending DRMed books in which you cannot change the cover.'), + _('Refresh separate covers when using automatic management') + + ':::' + + _('Set this option to have separate book covers uploaded ' + 'every time you connect your device. Unset this option if ' + 'you have so many books on the reader that performance is ' + 'unacceptable.'), + _('Preserve cover aspect ratio when building thumbnails') + + ':::' + + _('Set this option if you want the cover thumbnails to have ' + 'the same aspect ratio (width to height) as the cover. ' + 'Unset it if you want the thumbnail to be the maximum size, ' + 'ignoring aspect ratio.'), ] EXTRA_CUSTOMIZATION_DEFAULT = [ ', '.join(['series', 'tags']), - False, - False, - True, + True, + False, + True, ] OPT_COLLECTIONS = 0 @@ -103,284 +111,317 @@ class PRST1(USBMS): return self.EBOOK_DIR_MAIN return '' - def books(self, oncard=None, end_session=True): - from calibre.ebooks.metadata.meta import path_to_ext + def can_handle(self, devinfo, debug=False): + if islinux: + dev = USBDevice(devinfo) + main, carda, cardb = self.find_device_nodes(detected_device=dev) + if main is None and carda is None and cardb is None: + if debug: + print ('\tPRS-T1: Appears to be in non data mode' + ' or was ejected, ignoring') + return False + return True - dummy_bl = BookList(None, None, None) + def books(self, oncard=None, end_session=True): + dummy_bl = BookList(None, None, None) - if oncard == 'carda' and not self._card_a_prefix: - self.report_progress(1.0, _('Getting list of books on device...')) - return dummy_bl - elif oncard and oncard != 'carda': - self.report_progress(1.0, _('Getting list of books on device...')) - return dummy_bl - - prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix + if ( + (oncard == 'carda' and not self._card_a_prefix) or + (oncard and oncard != 'carda') + ): + self.report_progress(1.0, _('Getting list of books on device...')) + return dummy_bl - # Let parent driver get the books - self.booklist_class.rebuild_collections = self.rebuild_collections - bl = USBMS.books(self, oncard=oncard, end_session=end_session) - - debug_print("SQLite DB Path: " + self.normalize_path(prefix + 'Sony_Reader/database/books.db')) - - with closing(sqlite.connect(self.normalize_path(prefix + 'Sony_Reader/database/books.db'))) as connection: - # return bytestrings if the content cannot the decoded as unicode - connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") + prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix + + # Let parent driver get the books + self.booklist_class.rebuild_collections = self.rebuild_collections + bl = USBMS.books(self, oncard=oncard, end_session=end_session) + + dbpath = self.normalize_path(prefix + DBPATH) + debug_print("SQLite DB Path: " + dbpath) + + with closing(sqlite.connect(dbpath)) as connection: + # Replace undecodable characters in the db instead of erroring out + connection.text_factory = lambda x: unicode(x, "utf-8", "replace") cursor = connection.cursor() - # Query collections - query = 'select books._id, collection.title ' \ - 'from collections ' \ - 'left outer join books ' \ - 'left outer join collection ' \ - 'where collections.content_id = books._id and collections.collection_id = collection._id' - cursor.execute (query) - - bl_collections = {} - for i, row in enumerate(cursor): - bl_collections.setdefault(row[0], []) - bl_collections[row[0]].append(row[1]) - - for idx,book in enumerate(bl): - query = 'select _id, thumbnail from books where file_path = ?' - t = (book.lpath,) - cursor.execute (query, t) - - for i, row in enumerate(cursor): - book.device_collections = bl_collections.get(row[0], None) - thumbnail = row[1] - if thumbnail is not None: - thumbnail = self.normalize_path(prefix + thumbnail) - book.thumbnail = ImageWrapper(thumbnail) - - cursor.close() + # Query collections + query = ''' + SELECT books._id, collection.title + FROM collections + LEFT OUTER JOIN books + LEFT OUTER JOIN collection + WHERE collections.content_id = books._id AND + collections.collection_id = collection._id + ''' + cursor.execute(query) - return bl - - def set_plugboards(self, plugboards, pb_func): - self.plugboards = plugboards - self.plugboard_func = pb_func - - def sync_booklists(self, booklists, end_session=True): - debug_print('PRST1: starting sync_booklists') - - opts = self.settings() + bl_collections = {} + for i, row in enumerate(cursor): + bl_collections.setdefault(row[0], []) + bl_collections[row[0]].append(row[1]) + + for idx, book in enumerate(bl): + query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?' + t = (book.lpath,) + cursor.execute (query, t) + + for i, row in enumerate(cursor): + book.device_collections = bl_collections.get(row[0], None) + thumbnail = row[1] + if thumbnail is not None: + thumbnail = self.normalize_path(prefix + thumbnail) + book.thumbnail = ImageWrapper(thumbnail) + + cursor.close() + + return bl + + def set_plugboards(self, plugboards, pb_func): + self.plugboards = plugboards + self.plugboard_func = pb_func + + def sync_booklists(self, booklists, end_session=True): + debug_print('PRST1: starting sync_booklists') + + opts = self.settings() if opts.extra_customization: collections = [x.strip() for x in opts.extra_customization[self.OPT_COLLECTIONS].split(',')] else: collections = [] debug_print('PRST1: collection fields:', collections) - - if booklists[0] is not None: - self.update_device_database(booklists[0], collections, None) - if booklists[1] is not None: - self.update_device_database(booklists[1], collections, 'carda') - - USBMS.sync_booklists(self, booklists, end_session=end_session) - debug_print('PRST1: finished sync_booklists') - - def update_device_database(self, booklist, collections_attributes, oncard): - debug_print('PRST1: starting update_device_database') - - plugboard = None - if self.plugboard_func: - plugboard = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) - debug_print("PRST1: Using Plugboard", plugboard) - - prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix - source_id = 1 if oncard == 'carda' else 0 - debug_print("SQLite DB Path: " + self.normalize_path(prefix + 'Sony_Reader/database/books.db')) - - collections = booklist.get_collections(collections_attributes) - - with closing(sqlite.connect(self.normalize_path(prefix + 'Sony_Reader/database/books.db'))) as connection: - self.update_device_books(connection, booklist, source_id, plugboard) - self.update_device_collections(connection, booklist, collections, source_id) - - debug_print('PRST1: finished update_device_database') - def update_device_books(self, connection, booklist, source_id, plugboard): - opts = self.settings() - upload_covers = opts.extra_customization[self.OPT_UPLOAD_COVERS] - refresh_covers = opts.extra_customization[self.OPT_REFRESH_COVERS] - - cursor = connection.cursor() - - # Get existing books - query = 'select file_path, _id ' \ - 'from books' - cursor.execute(query) - - dbBooks = {} - for i, row in enumerate(cursor): - lpath = row[0].replace('\\', '/') - dbBooks[lpath] = row[1] - - for book in booklist: - # Run through plugboard if needed - if plugboard is not None: + if booklists[0] is not None: + self.update_device_database(booklists[0], collections, None) + if booklists[1] is not None: + self.update_device_database(booklists[1], collections, 'carda') + + USBMS.sync_booklists(self, booklists, end_session=end_session) + debug_print('PRST1: finished sync_booklists') + + def update_device_database(self, booklist, collections_attributes, oncard): + debug_print('PRST1: starting update_device_database') + + plugboard = None + if self.plugboard_func: + plugboard = self.plugboard_func(self.__class__.__name__, + 'device_db', self.plugboards) + debug_print("PRST1: Using Plugboard", plugboard) + + prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix + if prefix is None: + # Reader has no sd card inserted + return + source_id = 1 if oncard == 'carda' else 0 + + dbpath = self.normalize_path(prefix + DBPATH) + debug_print("SQLite DB Path: " + dbpath) + + collections = booklist.get_collections(collections_attributes) + + with closing(sqlite.connect(dbpath)) as connection: + self.update_device_books(connection, booklist, source_id, plugboard) + self.update_device_collections(connection, booklist, collections, source_id) + + debug_print('PRST1: finished update_device_database') + + def update_device_books(self, connection, booklist, source_id, plugboard): + opts = self.settings() + upload_covers = opts.extra_customization[self.OPT_UPLOAD_COVERS] + refresh_covers = opts.extra_customization[self.OPT_REFRESH_COVERS] + + cursor = connection.cursor() + + # Get existing books + query = 'SELECT file_path, _id FROM books' + cursor.execute(query) + + db_books = {} + for i, row in enumerate(cursor): + lpath = row[0].replace('\\', '/') + db_books[lpath] = row[1] + + for book in booklist: + # Run through plugboard if needed + if plugboard is not None: newmi = book.deepcopy_metadata() newmi.template_to_attribute(book, plugboard) else: newmi = book - - # Get Metadata We Want - lpath = book.lpath - author = newmi.authors[0] - title = newmi.title - if lpath not in dbBooks: - query = 'insert into books ' \ - '(title, author, source_id, added_date, modified_date, file_path, file_name, file_size, mime_type, corrupted, prevent_delete) ' \ - 'values (?,?,?,?,?,?,?,?,?,0,0)' - t = (title, author, source_id, int(time.time() * 1000), calendar.timegm(book.datetime), lpath, os.path.basename(book.lpath), book.size, book.mime ) - cursor.execute(query, t) - book.bookId = cursor.lastrowid - if upload_covers: - self.upload_book_cover(connection, book, source_id) - debug_print('Inserted New Book: ' + book.title) - else: - query = 'update books ' \ - 'set title = ?, author = ?, modified_date = ?, file_size = ? ' \ - 'where file_path = ?' - t = (title, author, calendar.timegm(book.datetime), book.size, lpath) - cursor.execute(query, t) - book.bookId = dbBooks[lpath] - if refresh_covers: - self.upload_book_cover(connection, book, source_id) - dbBooks[lpath] = None - - for book, bookId in dbBooks.items(): - if bookId is not None: - # Remove From Collections - query = 'delete from collections ' \ - 'where content_id = ?' - t = (bookId,) - cursor.execute(query, t) - # Remove from Books - query = 'delete from books ' \ - 'where _id = ?' - t = (bookId,) - cursor.execute(query, t) - debug_print('Deleted Book:' + book) - - connection.commit() - cursor.close() - - def update_device_collections(self, connection, booklist, collections, source_id): - cursor = connection.cursor() - - if collections: - # Get existing collections - query = 'select _id, title ' \ - 'from collection' - cursor.execute(query) - - dbCollections = {} - for i, row in enumerate(cursor): - dbCollections[row[1]] = row[0] - - for collection, books in collections.items(): - if collection not in dbCollections: - query = 'insert into collection (title, source_id) values (?,?)' - t = (collection, source_id) - cursor.execute(query, t) - dbCollections[collection] = cursor.lastrowid - debug_print('Inserted New Collection: ' + collection) - - # Get existing books in collection - query = 'select books.file_path, content_id ' \ - 'from collections ' \ - 'left outer join books ' \ - 'where collection_id = ? and books._id = collections.content_id' - t = (dbCollections[collection],) - cursor.execute(query, t) + # Get Metadata We Want + lpath = book.lpath + author = newmi.authors[0] + title = newmi.title - dbBooks = {} - for i, row in enumerate(cursor): - dbBooks[row[0]] = row[1] - - for idx, book in enumerate(books): - if dbBooks.get(book.lpath, None) is None: - if collection not in book.device_collections: - book.device_collections.append(collection) - query = 'insert into collections (collection_id, content_id, added_order) values (?,?,?)' - t = (dbCollections[collection], book.bookId, idx) - cursor.execute(query, t) - debug_print('Inserted Book Into Collection: ' + book.title + ' -> ' + collection) - else: - query = 'update collections ' \ - 'set added_order = ? ' \ - 'where content_id = ? and collection_id = ? ' - t = (idx, book.bookId, dbCollections[collection]) - cursor.execute(query, t) - - dbBooks[book.lpath] = None - - for bookPath, bookId in dbBooks.items(): - if bookId is not None: - query = 'delete from collections ' \ - 'where content_id = ? and collection_id = ? ' - t = (bookId,dbCollections[collection],) - cursor.execute(query, t) - debug_print('Deleted Book From Collection: ' + bookPath + ' -> ' + collection) - - dbCollections[collection] = None - - for collection, collectionId in dbCollections.items(): - if collectionId is not None: - # Remove Books from Collection - query = 'delete from collections ' \ - 'where collection_id = ?' - t = (collectionId,) - cursor.execute(query, t) - # Remove Collection - query = 'delete from collection ' \ - 'where _id = ?' - t = (collectionId,) - cursor.execute(query, t) - debug_print('Deleted Collection: ' + collection) - - - connection.commit() - cursor.close() - - def rebuild_collections(self, booklist, oncard): - debug_print('PRST1: starting rebuild_collections') - - opts = self.settings() + if lpath not in db_books: + query = ''' + INSERT INTO books + (title, author, source_id, added_date, modified_date, + file_path, file_name, file_size, mime_type, corrupted, + prevent_delete) + values (?,?,?,?,?,?,?,?,?,0,0) + ''' + t = (title, author, source_id, int(time.time() * 1000), + calendar.timegm(book.datetime), lpath, + os.path.basename(book.lpath), book.size, book.mime) + cursor.execute(query, t) + book.bookId = cursor.lastrowid + if upload_covers: + self.upload_book_cover(connection, book, source_id) + debug_print('Inserted New Book: ' + book.title) + else: + query = ''' + UPDATE books + SET title = ?, author = ?, modified_date = ?, file_size = ? + WHERE file_path = ? + ''' + t = (title, author, calendar.timegm(book.datetime), book.size, + lpath) + cursor.execute(query, t) + book.bookId = db_books[lpath] + if refresh_covers: + self.upload_book_cover(connection, book, source_id) + db_books[lpath] = None + + for book, bookId in db_books.items(): + if bookId is not None: + # Remove From Collections + query = 'DELETE FROM collections WHERE content_id = ?' + t = (bookId,) + cursor.execute(query, t) + # Remove from Books + query = 'DELETE FROM books where _id = ?' + t = (bookId,) + cursor.execute(query, t) + debug_print('Deleted Book:' + book) + + connection.commit() + cursor.close() + + def update_device_collections(self, connection, booklist, collections, + source_id): + cursor = connection.cursor() + + if collections: + # Get existing collections + query = 'SELECT _id, title FROM collection' + cursor.execute(query) + + db_collections = {} + for i, row in enumerate(cursor): + db_collections[row[1]] = row[0] + + for collection, books in collections.items(): + if collection not in db_collections: + query = 'INSERT INTO collection (title, source_id) VALUES (?,?)' + t = (collection, source_id) + cursor.execute(query, t) + db_collections[collection] = cursor.lastrowid + debug_print('Inserted New Collection: ' + collection) + + # Get existing books in collection + query = ''' + SELECT books.file_path, content_id + FROM collections + LEFT OUTER JOIN books + WHERE collection_id = ? AND books._id = collections.content_id + ''' + t = (db_collections[collection],) + cursor.execute(query, t) + + db_books = {} + for i, row in enumerate(cursor): + db_books[row[0]] = row[1] + + for idx, book in enumerate(books): + if db_books.get(book.lpath, None) is None: + if collection not in book.device_collections: + book.device_collections.append(collection) + query = ''' + INSERT INTO collections (collection_id, content_id, + added_order) values (?,?,?) + ''' + t = (db_collections[collection], book.bookId, idx) + cursor.execute(query, t) + debug_print('Inserted Book Into Collection: ' + + book.title + ' -> ' + collection) + else: + query = ''' + UPDATE collections + SET added_order = ? + WHERE content_id = ? AND collection_id = ? + ''' + t = (idx, book.bookId, db_collections[collection]) + cursor.execute(query, t) + + db_books[book.lpath] = None + + for bookPath, bookId in db_books.items(): + if bookId is not None: + query = ('DELETE FROM collections ' + 'WHERE content_id = ? AND collection_id = ? ') + t = (bookId, db_collections[collection],) + cursor.execute(query, t) + debug_print('Deleted Book From Collection: ' + bookPath + + ' -> ' + collection) + + db_collections[collection] = None + + for collection, collectionId in db_collections.items(): + if collectionId is not None: + # Remove Books from Collection + query = ('DELETE FROM collections ' + 'WHERE collection_id = ?') + t = (collectionId,) + cursor.execute(query, t) + # Remove Collection + query = ('DELETE FROM collection ' + 'WHERE _id = ?') + t = (collectionId,) + cursor.execute(query, t) + debug_print('Deleted Collection: ' + collection) + + + connection.commit() + cursor.close() + + def rebuild_collections(self, booklist, oncard): + debug_print('PRST1: starting rebuild_collections') + + opts = self.settings() if opts.extra_customization: collections = [x.strip() for x in opts.extra_customization[self.OPT_COLLECTIONS].split(',')] else: collections = [] debug_print('PRST1: collection fields:', collections) - - self.update_device_database(booklist, collections, oncard) - - debug_print('PRS-T1: finished rebuild_collections') - - def upload_book_cover(self, connection, book, source_id): - debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title) - cursor = connection.cursor() - - if book.thumbnail and book.thumbnail[-1]: - thumbnailPath = 'Sony_Reader/database/cache/books/' + str(book.bookId) +'/thumbnail/main_thumbnail.jpg' - - prefix = self._main_prefix if source_id is 0 else self._card_a_prefix - thumbnailFilePath = os.path.join(prefix, *thumbnailPath.split('/')) - thumbnailDirPath = os.path.dirname(thumbnailFilePath) - if not os.path.exists(thumbnailDirPath): - os.makedirs(thumbnailDirPath) - with open(thumbnailFilePath, 'wb') as f: - f.write(book.thumbnail[-1]) - - query = 'update books ' \ - 'set thumbnail = ?' \ - 'where _id = ? ' - t = (thumbnailPath,book.bookId,) - cursor.execute(query, t) - - cursor.close() \ No newline at end of file + self.update_device_database(booklist, collections, oncard) + + debug_print('PRS-T1: finished rebuild_collections') + + def upload_book_cover(self, connection, book, source_id): + debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title) + if not book.thumbnail and book.thumbnail[-1]: + return + cursor = connection.cursor() + + thumbnail_path = THUMBPATH%book.bookId + + prefix = self._main_prefix if source_id is 0 else self._card_a_prefix + thumbnail_file_path = os.path.join(prefix, *thumbnail_path.split('/')) + thumbnail_dir_path = os.path.dirname(thumbnail_file_path) + if not os.path.exists(thumbnail_dir_path): + os.makedirs(thumbnail_dir_path) + + with open(thumbnail_file_path, 'wb') as f: + f.write(book.thumbnail[-1]) + + query = 'UPDATE books SET thumbnail = ? WHERE _id = ?' + t = (thumbnail_path, book.bookId,) + cursor.execute(query, t) + + cursor.close() diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index c4f2ec26ed..85ab5905b9 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -483,7 +483,7 @@ class Device(DeviceConfig, DevicePlugin): self._card_a_prefix = get_card_prefix('carda') self._card_b_prefix = get_card_prefix('cardb') - def find_device_nodes(self): + def find_device_nodes(self, detected_device=None): def walk(base): base = os.path.abspath(os.path.realpath(base)) @@ -507,8 +507,11 @@ class Device(DeviceConfig, DevicePlugin): d, j = os.path.dirname, os.path.join usb_dir = None + if detected_device is None: + detected_device = self.detected_device + def test(val, attr): - q = getattr(self.detected_device, attr) + q = getattr(detected_device, attr) return q == val for x, isfile in walk('/sys/devices'): @@ -596,6 +599,8 @@ class Device(DeviceConfig, DevicePlugin): label = self.STORAGE_CARD2_VOLUME_LABEL if not label: label = self.STORAGE_CARD_VOLUME_LABEL + ' 2' + if not label: + label = 'E-book Reader (%s)'%type extra = 0 while True: q = ' (%d)'%extra if extra else ''