[merge] From Official

This commit is contained in:
Kolenka 2011-10-09 22:23:02 -07:00
commit 523a11c0c2
5 changed files with 333 additions and 295 deletions

View File

@ -217,7 +217,7 @@ class DevicePlugin(Plugin):
''' '''
Unix version of :meth:`can_handle_windows` 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) serial number)
''' '''

View File

@ -2,5 +2,6 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Timothy Legge <timlegge at gmail.com> and Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -1,9 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Timothy Legge <timlegge at gmail.com>'
'''
'''
class ImageWrapper(object):
def __init__(self, image_path):
self.image_path = image_path

View File

@ -2,7 +2,7 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Timothy Legge <timlegge at gmail.com> and Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
''' '''
@ -10,20 +10,26 @@ Device driver for the SONY T1 devices
''' '''
import os, time, calendar, re import os, time, calendar, re
import sqlite3 as sqlite import sqlite3 as sqlite
from contextlib import closing from contextlib import closing
from itertools import cycle
from calibre.devices.usbms.driver import USBMS, debug_print 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 CollectionsBookList
from calibre.devices.usbms.books import BookList 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): class PRST1(USBMS):
name = 'SONY PRST1 and newer Device Interface' name = 'SONY PRST1 and newer Device Interface'
gui_name = 'SONY Reader' 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' author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
path_sep = '/' path_sep = '/'
@ -41,6 +47,8 @@ class PRST1(USBMS):
WINDOWS_MAIN_MEM = re.compile( WINDOWS_MAIN_MEM = re.compile(
r'(PRS-T1&)' r'(PRS-T1&)'
) )
MAIN_MEMORY_VOLUME_LABEL = 'SONY Reader Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'SONY Reader Storage Card'
THUMBNAIL_HEIGHT = 144 THUMBNAIL_HEIGHT = 144
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
@ -71,7 +79,7 @@ class PRST1(USBMS):
] ]
EXTRA_CUSTOMIZATION_DEFAULT = [ EXTRA_CUSTOMIZATION_DEFAULT = [
', '.join(['series', 'tags']), ', '.join(['series', 'tags']),
False, True,
False, False,
True, True,
] ]
@ -103,15 +111,24 @@ class PRST1(USBMS):
return self.EBOOK_DIR_MAIN return self.EBOOK_DIR_MAIN
return '' return ''
def books(self, oncard=None, end_session=True): def can_handle(self, devinfo, debug=False):
from calibre.ebooks.metadata.meta import path_to_ext 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
def books(self, oncard=None, end_session=True):
dummy_bl = BookList(None, None, None) dummy_bl = BookList(None, None, None)
if oncard == 'carda' and not self._card_a_prefix: if (
self.report_progress(1.0, _('Getting list of books on device...')) (oncard == 'carda' and not self._card_a_prefix) or
return dummy_bl (oncard and oncard != 'carda')
elif oncard and oncard != 'carda': ):
self.report_progress(1.0, _('Getting list of books on device...')) self.report_progress(1.0, _('Getting list of books on device...'))
return dummy_bl return dummy_bl
@ -121,19 +138,23 @@ class PRST1(USBMS):
self.booklist_class.rebuild_collections = self.rebuild_collections self.booklist_class.rebuild_collections = self.rebuild_collections
bl = USBMS.books(self, oncard=oncard, end_session=end_session) bl = USBMS.books(self, oncard=oncard, end_session=end_session)
debug_print("SQLite DB Path: " + self.normalize_path(prefix + 'Sony_Reader/database/books.db')) dbpath = self.normalize_path(prefix + DBPATH)
debug_print("SQLite DB Path: " + dbpath)
with closing(sqlite.connect(self.normalize_path(prefix + 'Sony_Reader/database/books.db'))) as connection: with closing(sqlite.connect(dbpath)) as connection:
# return bytestrings if the content cannot the decoded as unicode # Replace undecodable characters in the db instead of erroring out
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") connection.text_factory = lambda x: unicode(x, "utf-8", "replace")
cursor = connection.cursor() cursor = connection.cursor()
# Query collections # Query collections
query = 'select books._id, collection.title ' \ query = '''
'from collections ' \ SELECT books._id, collection.title
'left outer join books ' \ FROM collections
'left outer join collection ' \ LEFT OUTER JOIN books
'where collections.content_id = books._id and collections.collection_id = collection._id' LEFT OUTER JOIN collection
WHERE collections.content_id = books._id AND
collections.collection_id = collection._id
'''
cursor.execute(query) cursor.execute(query)
bl_collections = {} bl_collections = {}
@ -142,7 +163,7 @@ class PRST1(USBMS):
bl_collections[row[0]].append(row[1]) bl_collections[row[0]].append(row[1])
for idx, book in enumerate(bl): for idx, book in enumerate(bl):
query = 'select _id, thumbnail from books where file_path = ?' query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?'
t = (book.lpath,) t = (book.lpath,)
cursor.execute (query, t) cursor.execute (query, t)
@ -185,16 +206,22 @@ class PRST1(USBMS):
plugboard = None plugboard = None
if self.plugboard_func: if self.plugboard_func:
plugboard = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) plugboard = self.plugboard_func(self.__class__.__name__,
'device_db', self.plugboards)
debug_print("PRST1: Using Plugboard", plugboard) debug_print("PRST1: Using Plugboard", plugboard)
prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix 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 source_id = 1 if oncard == 'carda' else 0
debug_print("SQLite DB Path: " + self.normalize_path(prefix + 'Sony_Reader/database/books.db'))
dbpath = self.normalize_path(prefix + DBPATH)
debug_print("SQLite DB Path: " + dbpath)
collections = booklist.get_collections(collections_attributes) collections = booklist.get_collections(collections_attributes)
with closing(sqlite.connect(self.normalize_path(prefix + 'Sony_Reader/database/books.db'))) as connection: with closing(sqlite.connect(dbpath)) as connection:
self.update_device_books(connection, booklist, source_id, plugboard) self.update_device_books(connection, booklist, source_id, plugboard)
self.update_device_collections(connection, booklist, collections, source_id) self.update_device_collections(connection, booklist, collections, source_id)
@ -208,14 +235,13 @@ class PRST1(USBMS):
cursor = connection.cursor() cursor = connection.cursor()
# Get existing books # Get existing books
query = 'select file_path, _id ' \ query = 'SELECT file_path, _id FROM books'
'from books'
cursor.execute(query) cursor.execute(query)
dbBooks = {} db_books = {}
for i, row in enumerate(cursor): for i, row in enumerate(cursor):
lpath = row[0].replace('\\', '/') lpath = row[0].replace('\\', '/')
dbBooks[lpath] = row[1] db_books[lpath] = row[1]
for book in booklist: for book in booklist:
# Run through plugboard if needed # Run through plugboard if needed
@ -230,37 +256,44 @@ class PRST1(USBMS):
author = newmi.authors[0] author = newmi.authors[0]
title = newmi.title title = newmi.title
if lpath not in dbBooks: if lpath not in db_books:
query = 'insert into books ' \ query = '''
'(title, author, source_id, added_date, modified_date, file_path, file_name, file_size, mime_type, corrupted, prevent_delete) ' \ INSERT INTO books
'values (?,?,?,?,?,?,?,?,?,0,0)' (title, author, source_id, added_date, modified_date,
t = (title, author, source_id, int(time.time() * 1000), calendar.timegm(book.datetime), lpath, os.path.basename(book.lpath), book.size, book.mime ) 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) cursor.execute(query, t)
book.bookId = cursor.lastrowid book.bookId = cursor.lastrowid
if upload_covers: if upload_covers:
self.upload_book_cover(connection, book, source_id) self.upload_book_cover(connection, book, source_id)
debug_print('Inserted New Book: ' + book.title) debug_print('Inserted New Book: ' + book.title)
else: else:
query = 'update books ' \ query = '''
'set title = ?, author = ?, modified_date = ?, file_size = ? ' \ UPDATE books
'where file_path = ?' SET title = ?, author = ?, modified_date = ?, file_size = ?
t = (title, author, calendar.timegm(book.datetime), book.size, lpath) WHERE file_path = ?
'''
t = (title, author, calendar.timegm(book.datetime), book.size,
lpath)
cursor.execute(query, t) cursor.execute(query, t)
book.bookId = dbBooks[lpath] book.bookId = db_books[lpath]
if refresh_covers: if refresh_covers:
self.upload_book_cover(connection, book, source_id) self.upload_book_cover(connection, book, source_id)
dbBooks[lpath] = None db_books[lpath] = None
for book, bookId in dbBooks.items(): for book, bookId in db_books.items():
if bookId is not None: if bookId is not None:
# Remove From Collections # Remove From Collections
query = 'delete from collections ' \ query = 'DELETE FROM collections WHERE content_id = ?'
'where content_id = ?'
t = (bookId,) t = (bookId,)
cursor.execute(query, t) cursor.execute(query, t)
# Remove from Books # Remove from Books
query = 'delete from books ' \ query = 'DELETE FROM books where _id = ?'
'where _id = ?'
t = (bookId,) t = (bookId,)
cursor.execute(query, t) cursor.execute(query, t)
debug_print('Deleted Book:' + book) debug_print('Deleted Book:' + book)
@ -268,76 +301,85 @@ class PRST1(USBMS):
connection.commit() connection.commit()
cursor.close() cursor.close()
def update_device_collections(self, connection, booklist, collections, source_id): def update_device_collections(self, connection, booklist, collections,
source_id):
cursor = connection.cursor() cursor = connection.cursor()
if collections: if collections:
# Get existing collections # Get existing collections
query = 'select _id, title ' \ query = 'SELECT _id, title FROM collection'
'from collection'
cursor.execute(query) cursor.execute(query)
dbCollections = {} db_collections = {}
for i, row in enumerate(cursor): for i, row in enumerate(cursor):
dbCollections[row[1]] = row[0] db_collections[row[1]] = row[0]
for collection, books in collections.items(): for collection, books in collections.items():
if collection not in dbCollections: if collection not in db_collections:
query = 'insert into collection (title, source_id) values (?,?)' query = 'INSERT INTO collection (title, source_id) VALUES (?,?)'
t = (collection, source_id) t = (collection, source_id)
cursor.execute(query, t) cursor.execute(query, t)
dbCollections[collection] = cursor.lastrowid db_collections[collection] = cursor.lastrowid
debug_print('Inserted New Collection: ' + collection) debug_print('Inserted New Collection: ' + collection)
# Get existing books in collection # Get existing books in collection
query = 'select books.file_path, content_id ' \ query = '''
'from collections ' \ SELECT books.file_path, content_id
'left outer join books ' \ FROM collections
'where collection_id = ? and books._id = collections.content_id' LEFT OUTER JOIN books
t = (dbCollections[collection],) WHERE collection_id = ? AND books._id = collections.content_id
'''
t = (db_collections[collection],)
cursor.execute(query, t) cursor.execute(query, t)
dbBooks = {} db_books = {}
for i, row in enumerate(cursor): for i, row in enumerate(cursor):
dbBooks[row[0]] = row[1] db_books[row[0]] = row[1]
for idx, book in enumerate(books): for idx, book in enumerate(books):
if dbBooks.get(book.lpath, None) is None: if db_books.get(book.lpath, None) is None:
if collection not in book.device_collections: if collection not in book.device_collections:
book.device_collections.append(collection) book.device_collections.append(collection)
query = 'insert into collections (collection_id, content_id, added_order) values (?,?,?)' query = '''
t = (dbCollections[collection], book.bookId, idx) INSERT INTO collections (collection_id, content_id,
added_order) values (?,?,?)
'''
t = (db_collections[collection], book.bookId, idx)
cursor.execute(query, t) cursor.execute(query, t)
debug_print('Inserted Book Into Collection: ' + book.title + ' -> ' + collection) debug_print('Inserted Book Into Collection: ' +
book.title + ' -> ' + collection)
else: else:
query = 'update collections ' \ query = '''
'set added_order = ? ' \ UPDATE collections
'where content_id = ? and collection_id = ? ' SET added_order = ?
t = (idx, book.bookId, dbCollections[collection]) WHERE content_id = ? AND collection_id = ?
'''
t = (idx, book.bookId, db_collections[collection])
cursor.execute(query, t) cursor.execute(query, t)
dbBooks[book.lpath] = None db_books[book.lpath] = None
for bookPath, bookId in dbBooks.items(): for bookPath, bookId in db_books.items():
if bookId is not None: if bookId is not None:
query = 'delete from collections ' \ query = ('DELETE FROM collections '
'where content_id = ? and collection_id = ? ' 'WHERE content_id = ? AND collection_id = ? ')
t = (bookId,dbCollections[collection],) t = (bookId, db_collections[collection],)
cursor.execute(query, t) cursor.execute(query, t)
debug_print('Deleted Book From Collection: ' + bookPath + ' -> ' + collection) debug_print('Deleted Book From Collection: ' + bookPath
+ ' -> ' + collection)
dbCollections[collection] = None db_collections[collection] = None
for collection, collectionId in dbCollections.items(): for collection, collectionId in db_collections.items():
if collectionId is not None: if collectionId is not None:
# Remove Books from Collection # Remove Books from Collection
query = 'delete from collections ' \ query = ('DELETE FROM collections '
'where collection_id = ?' 'WHERE collection_id = ?')
t = (collectionId,) t = (collectionId,)
cursor.execute(query, t) cursor.execute(query, t)
# Remove Collection # Remove Collection
query = 'delete from collection ' \ query = ('DELETE FROM collection '
'where _id = ?' 'WHERE _id = ?')
t = (collectionId,) t = (collectionId,)
cursor.execute(query, t) cursor.execute(query, t)
debug_print('Deleted Collection: ' + collection) debug_print('Deleted Collection: ' + collection)
@ -363,24 +405,23 @@ class PRST1(USBMS):
def upload_book_cover(self, connection, book, source_id): def upload_book_cover(self, connection, book, source_id):
debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title) debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title)
if not book.thumbnail and book.thumbnail[-1]:
return
cursor = connection.cursor() cursor = connection.cursor()
if book.thumbnail and book.thumbnail[-1]: thumbnail_path = THUMBPATH%book.bookId
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 prefix = self._main_prefix if source_id is 0 else self._card_a_prefix
thumbnailFilePath = os.path.join(prefix, *thumbnailPath.split('/')) thumbnail_file_path = os.path.join(prefix, *thumbnail_path.split('/'))
thumbnailDirPath = os.path.dirname(thumbnailFilePath) thumbnail_dir_path = os.path.dirname(thumbnail_file_path)
if not os.path.exists(thumbnailDirPath): if not os.path.exists(thumbnail_dir_path):
os.makedirs(thumbnailDirPath) os.makedirs(thumbnail_dir_path)
with open(thumbnailFilePath, 'wb') as f: with open(thumbnail_file_path, 'wb') as f:
f.write(book.thumbnail[-1]) f.write(book.thumbnail[-1])
query = 'update books ' \ query = 'UPDATE books SET thumbnail = ? WHERE _id = ?'
'set thumbnail = ?' \ t = (thumbnail_path, book.bookId,)
'where _id = ? '
t = (thumbnailPath,book.bookId,)
cursor.execute(query, t) cursor.execute(query, t)
cursor.close() cursor.close()

View File

@ -483,7 +483,7 @@ class Device(DeviceConfig, DevicePlugin):
self._card_a_prefix = get_card_prefix('carda') self._card_a_prefix = get_card_prefix('carda')
self._card_b_prefix = get_card_prefix('cardb') self._card_b_prefix = get_card_prefix('cardb')
def find_device_nodes(self): def find_device_nodes(self, detected_device=None):
def walk(base): def walk(base):
base = os.path.abspath(os.path.realpath(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 d, j = os.path.dirname, os.path.join
usb_dir = None usb_dir = None
if detected_device is None:
detected_device = self.detected_device
def test(val, attr): def test(val, attr):
q = getattr(self.detected_device, attr) q = getattr(detected_device, attr)
return q == val return q == val
for x, isfile in walk('/sys/devices'): for x, isfile in walk('/sys/devices'):
@ -596,6 +599,8 @@ class Device(DeviceConfig, DevicePlugin):
label = self.STORAGE_CARD2_VOLUME_LABEL label = self.STORAGE_CARD2_VOLUME_LABEL
if not label: if not label:
label = self.STORAGE_CARD_VOLUME_LABEL + ' 2' label = self.STORAGE_CARD_VOLUME_LABEL + ' 2'
if not label:
label = 'E-book Reader (%s)'%type
extra = 0 extra = 0
while True: while True:
q = ' (%d)'%extra if extra else '' q = ' (%d)'%extra if extra else ''