[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`
: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)
'''

View File

@ -2,5 +2,6 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__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'

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
__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'
'''
@ -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
@ -71,7 +79,7 @@ class PRST1(USBMS):
]
EXTRA_CUSTOMIZATION_DEFAULT = [
', '.join(['series', 'tags']),
False,
True,
False,
True,
]
@ -103,15 +111,24 @@ 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
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':
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
@ -121,28 +138,32 @@ class PRST1(USBMS):
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'))
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:
# return bytestrings if the content cannot the decoded as unicode
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")
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)
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 = ?'
for idx, book in enumerate(bl):
query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?'
t = (book.lpath,)
cursor.execute (query, t)
@ -185,16 +206,22 @@ class PRST1(USBMS):
plugboard = None
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)
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
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)
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_collections(connection, booklist, collections, source_id)
@ -208,14 +235,13 @@ class PRST1(USBMS):
cursor = connection.cursor()
# Get existing books
query = 'select file_path, _id ' \
'from books'
query = 'SELECT file_path, _id FROM books'
cursor.execute(query)
dbBooks = {}
db_books = {}
for i, row in enumerate(cursor):
lpath = row[0].replace('\\', '/')
dbBooks[lpath] = row[1]
db_books[lpath] = row[1]
for book in booklist:
# Run through plugboard if needed
@ -230,37 +256,44 @@ class PRST1(USBMS):
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 )
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)
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]
book.bookId = db_books[lpath]
if refresh_covers:
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:
# Remove From Collections
query = 'delete from collections ' \
'where content_id = ?'
query = 'DELETE FROM collections WHERE content_id = ?'
t = (bookId,)
cursor.execute(query, t)
# Remove from Books
query = 'delete from books ' \
'where _id = ?'
query = 'DELETE FROM books where _id = ?'
t = (bookId,)
cursor.execute(query, t)
debug_print('Deleted Book:' + book)
@ -268,76 +301,85 @@ class PRST1(USBMS):
connection.commit()
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()
if collections:
# Get existing collections
query = 'select _id, title ' \
'from collection'
query = 'SELECT _id, title FROM collection'
cursor.execute(query)
dbCollections = {}
db_collections = {}
for i, row in enumerate(cursor):
dbCollections[row[1]] = row[0]
db_collections[row[1]] = row[0]
for collection, books in collections.items():
if collection not in dbCollections:
query = 'insert into collection (title, source_id) values (?,?)'
if collection not in db_collections:
query = 'INSERT INTO collection (title, source_id) VALUES (?,?)'
t = (collection, source_id)
cursor.execute(query, t)
dbCollections[collection] = cursor.lastrowid
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 = (dbCollections[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)
dbBooks = {}
db_books = {}
for i, row in enumerate(cursor):
dbBooks[row[0]] = row[1]
db_books[row[0]] = row[1]
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:
book.device_collections.append(collection)
query = 'insert into collections (collection_id, content_id, added_order) values (?,?,?)'
t = (dbCollections[collection], book.bookId, idx)
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)
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])
query = '''
UPDATE collections
SET added_order = ?
WHERE content_id = ? AND collection_id = ?
'''
t = (idx, book.bookId, db_collections[collection])
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:
query = 'delete from collections ' \
'where content_id = ? and collection_id = ? '
t = (bookId,dbCollections[collection],)
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)
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:
# Remove Books from Collection
query = 'delete from collections ' \
'where collection_id = ?'
query = ('DELETE FROM collections '
'WHERE collection_id = ?')
t = (collectionId,)
cursor.execute(query, t)
# Remove Collection
query = 'delete from collection ' \
'where _id = ?'
query = ('DELETE FROM collection '
'WHERE _id = ?')
t = (collectionId,)
cursor.execute(query, t)
debug_print('Deleted Collection: ' + collection)
@ -363,24 +405,23 @@ class PRST1(USBMS):
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()
if book.thumbnail and book.thumbnail[-1]:
thumbnailPath = 'Sony_Reader/database/cache/books/' + str(book.bookId) +'/thumbnail/main_thumbnail.jpg'
thumbnail_path = THUMBPATH%book.bookId
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)
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(thumbnailFilePath, 'wb') as f:
with open(thumbnail_file_path, 'wb') as f:
f.write(book.thumbnail[-1])
query = 'update books ' \
'set thumbnail = ?' \
'where _id = ? '
t = (thumbnailPath,book.bookId,)
query = 'UPDATE books SET thumbnail = ? WHERE _id = ?'
t = (thumbnail_path, book.bookId,)
cursor.execute(query, t)
cursor.close()

View File

@ -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 ''