%PDF- %PDF-
| Direktori : /usr/lib/calibre/calibre/devices/prst1/ |
| Current File : //usr/lib/calibre/calibre/devices/prst1/driver.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
'''
Device driver for the SONY T1 devices
'''
import os, time, re
from contextlib import closing
from datetime import date
from calibre import fsync
from calibre.devices.mime import mime_type_ext
from calibre.devices.errors import DeviceError
from calibre.devices.usbms.driver import USBMS, debug_print
from calibre.devices.usbms.device import USBDevice
from calibre.devices.usbms.books import CollectionsBookList
from calibre.devices.usbms.books import BookList
from calibre.ebooks.metadata import authors_to_sort_string, authors_to_string
from calibre.constants import islinux
from polyglot.builtins import long_type
DBPATH = 'Sony_Reader/database/books.db'
THUMBPATH = 'Sony_Reader/database/cache/books/%s/thumbnail/main_thumbnail.jpg'
class ImageWrapper:
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 the PRST1 and newer SONY e-book readers')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
path_sep = '/'
booklist_class = CollectionsBookList
FORMATS = ['epub', 'pdf', 'txt', 'book', 'zbf'] # The last two are used in japan
CAN_SET_METADATA = ['collections']
CAN_DO_DEVICE_DB_PLUGBOARD = True
VENDOR_ID = [0x054c] #: SONY Vendor Id
PRODUCT_ID = [0x05c2]
BCD = [0x226]
VENDOR_NAME = 'SONY'
WINDOWS_MAIN_MEM = re.compile(
r'(PRS-T(1|2|2N|3)&)'
)
WINDOWS_CARD_A_MEM = re.compile(
r'(PRS-T(1|2|2N|3)_{1,2}SD&)'
)
MAIN_MEMORY_VOLUME_LABEL = 'SONY Reader Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'SONY Reader Storage Card'
THUMBNAIL_HEIGHT = 144
SUPPORTS_SUB_DIRS = True
SUPPORTS_USE_AUTHOR_SORT = True
MUST_READ_METADATA = True
EBOOK_DIR_MAIN = 'Sony_Reader/media/books'
EXTRA_CUSTOMIZATION_MESSAGE = [
_('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'
' e-book 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.'),
_('Use SONY Author Format (First Author Only)') +
':::' +
_('Set this option if you want the author on the Sony to '
'appear the same way the T1 sets it. This means it will '
'only show the first author for books with multiple authors. '
'Leave this disabled if you use Metadata plugboards.')
]
EXTRA_CUSTOMIZATION_DEFAULT = [
', '.join(['series', 'tags']),
True,
False,
True,
False,
]
OPT_COLLECTIONS = 0
OPT_UPLOAD_COVERS = 1
OPT_REFRESH_COVERS = 2
OPT_PRESERVE_ASPECT_RATIO = 3
OPT_USE_SONY_AUTHORS = 4
plugboards = None
plugboard_func = None
def post_open_callback(self):
# Set the thumbnail width to the theoretical max if the user has asked
# that we do not preserve aspect ratio
ec = self.settings().extra_customization
if not ec[self.OPT_PRESERVE_ASPECT_RATIO]:
self.THUMBNAIL_WIDTH = 108
self.WANTS_UPDATED_THUMBNAILS = ec[self.OPT_REFRESH_COVERS]
# Make sure the date offset is set to none, we'll calculate it in books.
self.device_offset = None
def windows_filter_pnp_id(self, pnp_id):
return '_LAUNCHER' in pnp_id or '_SETTING' in pnp_id
def get_carda_ebook_dir(self, for_upload=False):
if for_upload:
return self.EBOOK_DIR_MAIN
return self.EBOOK_DIR_CARD_A
def get_main_ebook_dir(self, for_upload=False):
if for_upload:
return self.EBOOK_DIR_MAIN
return ''
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):
import sqlite3 as sqlite
dummy_bl = BookList(None, None, None)
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
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: x if isinstance(x, str) else x.decode('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])
# collect information on offsets, but assume any
# offset we already calculated is correct
if self.device_offset is None:
query = 'SELECT file_path, modified_date FROM books'
cursor.execute(query)
time_offsets = {}
for i, row in enumerate(cursor):
try:
comp_date = int(os.path.getmtime(self.normalize_path(prefix + row[0])) * 1000)
except (OSError, TypeError):
# In case the db has incorrect path info
continue
device_date = int(row[1])
offset = device_date - comp_date
time_offsets.setdefault(offset, 0)
time_offsets[offset] = time_offsets[offset] + 1
try:
device_offset = max(time_offsets, key=lambda a: time_offsets.get(a))
debug_print("Device Offset: %d ms"%device_offset)
self.device_offset = device_offset
except ValueError:
debug_print("No Books To Detect Device Offset.")
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 len(booklists) > 1 and 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):
import sqlite3 as sqlite
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.remove_orphaned_records(connection, dbpath)
self.update_device_books(connection, booklist, source_id,
plugboard, dbpath)
self.update_device_collections(connection, booklist, collections, source_id, dbpath)
debug_print('PRST1: finished update_device_database')
def remove_orphaned_records(self, connection, dbpath):
from sqlite3 import DatabaseError
try:
cursor = connection.cursor()
debug_print("Removing Orphaned Collection Records")
# Purge any collections references that point into the abyss
query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)'
cursor.execute(query)
query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)'
cursor.execute(query)
debug_print("Removing Orphaned Book Records")
# Purge any references to books not in this database
# Idea is to prevent any spill-over where these wind up applying to some other book
query = 'DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)'
cursor.execute(query%'annotation')
cursor.execute(query%'bookmark')
cursor.execute(query%'current_position')
cursor.execute(query%'freehand')
cursor.execute(query%'history')
cursor.execute(query%'layout_cache')
cursor.execute(query%'preference')
cursor.close()
except DatabaseError:
import traceback
tb = traceback.format_exc()
raise DeviceError((('The SONY database is corrupted. '
' Delete the file %s on your reader and then disconnect '
' reconnect it. If you are using an SD card, you '
' should delete the file on the card as well. Note that '
' deleting this file will cause your reader to forget '
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
'\n'+tb)
def get_lastrowid(self, cursor):
# SQLite3 + Python has a fun issue on 32-bit systems with integer overflows.
# Issue a SQL query instead, getting the value as a string, and then converting to a long python int manually.
query = 'SELECT last_insert_rowid()'
cursor.execute(query)
row = cursor.fetchone()
return long_type(row[0])
def get_database_min_id(self, source_id):
sequence_min = 0
if source_id == 1:
sequence_min = 4294967296
return sequence_min
def set_database_sequence_id(self, connection, table, sequence_id):
cursor = connection.cursor()
# Update the sequence Id if it exists
query = 'UPDATE sqlite_sequence SET seq = ? WHERE name = ?'
t = (sequence_id, table,)
cursor.execute(query, t)
# Insert the sequence Id if it doesn't
query = ('INSERT INTO sqlite_sequence (name, seq) '
'SELECT ?, ? '
'WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = ?)')
cursor.execute(query, (table, sequence_id, table,))
cursor.close()
def read_device_books(self, connection, source_id, dbpath):
from sqlite3 import DatabaseError
sequence_min = self.get_database_min_id(source_id)
sequence_max = sequence_min
sequence_dirty = 0
debug_print("Book Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
try:
cursor = connection.cursor()
# Get existing books
query = 'SELECT file_path, _id FROM books'
cursor.execute(query)
except DatabaseError:
import traceback
tb = traceback.format_exc()
raise DeviceError((('The SONY database is corrupted. '
' Delete the file %s on your reader and then disconnect '
' reconnect it. If you are using an SD card, you '
' should delete the file on the card as well. Note that '
' deleting this file will cause your reader to forget '
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
'\n'+tb)
# Get the books themselves, but keep track of any that are less than the minimum.
# Record what the max id being used is as well.
db_books = {}
for i, row in enumerate(cursor):
if not hasattr(row[0], 'replace'):
continue
lpath = row[0].replace('\\', '/')
db_books[lpath] = row[1]
if row[1] < sequence_min:
sequence_dirty = 1
else:
sequence_max = max(sequence_max, row[1])
# If the database is 'dirty', then we should fix up the Ids and the sequence number
if sequence_dirty == 1:
debug_print("Book Sequence Dirty for Source Id: %d"%source_id)
sequence_max = sequence_max + 1
for book, bookId in db_books.items():
if bookId < sequence_min:
# Record the new Id and write it to the DB
db_books[book] = sequence_max
sequence_max = sequence_max + 1
# Fix the Books DB
query = 'UPDATE books SET _id = ? WHERE file_path = ?'
t = (db_books[book], book,)
cursor.execute(query, t)
# Fix any references so that they point back to the right book
t = (db_books[book], bookId,)
query = 'UPDATE collections SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE annotation SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE bookmark SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE current_position SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE deleted_markups SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE dic_histories SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE freehand SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE history SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE layout_cache SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
query = 'UPDATE preference SET content_id = ? WHERE content_id = ?'
cursor.execute(query, t)
self.set_database_sequence_id(connection, 'books', sequence_max)
debug_print("Book Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
cursor.close()
return db_books
def update_device_books(self, connection, booklist, source_id, plugboard,
dbpath):
from calibre.ebooks.metadata.meta import path_to_ext
opts = self.settings()
upload_covers = opts.extra_customization[self.OPT_UPLOAD_COVERS]
refresh_covers = opts.extra_customization[self.OPT_REFRESH_COVERS]
use_sony_authors = opts.extra_customization[self.OPT_USE_SONY_AUTHORS]
db_books = self.read_device_books(connection, source_id, dbpath)
cursor = connection.cursor()
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
try:
if opts.use_author_sort:
if newmi.author_sort:
author = newmi.author_sort
else:
author = authors_to_sort_string(newmi.authors)
else:
if use_sony_authors:
author = newmi.authors[0]
else:
author = authors_to_string(newmi.authors)
except:
author = _('Unknown')
title = newmi.title or _('Unknown')
# Get modified date
# If there was a detected offset, use that. Otherwise use UTC (same as Sony software)
modified_date = os.path.getmtime(book.path) * 1000
if self.device_offset is not None:
modified_date = modified_date + self.device_offset
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),
modified_date, lpath,
os.path.basename(lpath), book.size, book.mime or mime_type_ext(path_to_ext(lpath)))
cursor.execute(query, t)
book.bookId = self.get_lastrowid(cursor)
if upload_covers:
self.upload_book_cover(connection, book, source_id)
debug_print('Inserted New Book: (%u) '%book.bookId + book.title)
else:
query = '''
UPDATE books
SET title = ?, author = ?, modified_date = ?, file_size = ?
WHERE file_path = ?
'''
t = (title, author, modified_date, 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
if self.is_sony_periodical(book):
self.periodicalize_book(connection, book)
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 read_device_collections(self, connection, source_id, dbpath):
from sqlite3 import DatabaseError
sequence_min = self.get_database_min_id(source_id)
sequence_max = sequence_min
sequence_dirty = 0
debug_print("Collection Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
try:
cursor = connection.cursor()
# Get existing collections
query = 'SELECT _id, title FROM collection'
cursor.execute(query)
except DatabaseError:
import traceback
tb = traceback.format_exc()
raise DeviceError((('The SONY database is corrupted. '
' Delete the file %s on your reader and then disconnect '
' reconnect it. If you are using an SD card, you '
' should delete the file on the card as well. Note that '
' deleting this file will cause your reader to forget '
' any notes/highlights, etc.')%dbpath)+' Underlying error:'
'\n'+tb)
db_collections = {}
for i, row in enumerate(cursor):
db_collections[row[1]] = row[0]
if row[0] < sequence_min:
sequence_dirty = 1
else:
sequence_max = max(sequence_max, row[0])
# If the database is 'dirty', then we should fix up the Ids and the sequence number
if sequence_dirty == 1:
debug_print("Collection Sequence Dirty for Source Id: %d"%source_id)
sequence_max = sequence_max + 1
for collection, collectionId in db_collections.items():
if collectionId < sequence_min:
# Record the new Id and write it to the DB
db_collections[collection] = sequence_max
sequence_max = sequence_max + 1
# Fix the collection DB
query = 'UPDATE collection SET _id = ? WHERE title = ?'
t = (db_collections[collection], collection, )
cursor.execute(query, t)
# Fix any references in existing collections
query = 'UPDATE collections SET collection_id = ? WHERE collection_id = ?'
t = (db_collections[collection], collectionId,)
cursor.execute(query, t)
self.set_database_sequence_id(connection, 'collection', sequence_max)
debug_print("Collection Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
# Fix up the collections table now...
sequence_dirty = 0
sequence_max = sequence_min
debug_print("Collections Sequence Min: %d, Source Id: %d"%(sequence_min,source_id))
query = 'SELECT _id FROM collections'
cursor.execute(query)
db_collection_pairs = []
for i, row in enumerate(cursor):
db_collection_pairs.append(row[0])
if row[0] < sequence_min:
sequence_dirty = 1
else:
sequence_max = max(sequence_max, row[0])
if sequence_dirty == 1:
debug_print("Collections Sequence Dirty for Source Id: %d"%source_id)
sequence_max = sequence_max + 1
for pairId in db_collection_pairs:
if pairId < sequence_min:
# Record the new Id and write it to the DB
query = 'UPDATE collections SET _id = ? WHERE _id = ?'
t = (sequence_max, pairId,)
cursor.execute(query, t)
sequence_max = sequence_max + 1
self.set_database_sequence_id(connection, 'collections', sequence_max)
debug_print("Collections Sequence Max: %d, Source Id: %d"%(sequence_max,source_id))
cursor.close()
return db_collections
def update_device_collections(self, connection, booklist, collections,
source_id, dbpath):
if collections:
db_collections = self.read_device_collections(connection, source_id, dbpath)
cursor = connection.cursor()
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] = self.get_lastrowid(cursor)
debug_print('Inserted New Collection: (%u) '%db_collections[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 collection not in book.device_collections:
book.device_collections.append(collection)
if db_books.get(book.lpath, None) is None:
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: ' + repr(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_cover(self, path, filename, metadata, filepath):
import sqlite3 as sqlite
debug_print('PRS-T1: uploading cover')
if filepath.startswith(self._main_prefix):
prefix = self._main_prefix
source_id = 0
else:
prefix = self._card_a_prefix
source_id = 1
metadata.lpath = filepath.partition(prefix)[2]
metadata.lpath = metadata.lpath.replace('\\', '/')
dbpath = self.normalize_path(prefix + DBPATH)
debug_print("SQLite DB Path: " + dbpath)
with closing(sqlite.connect(dbpath)) as connection:
cursor = connection.cursor()
query = 'SELECT _id FROM books WHERE file_path = ?'
t = (metadata.lpath,)
cursor.execute(query, t)
for i, row in enumerate(cursor):
metadata.bookId = row[0]
cursor.close()
if getattr(metadata, 'bookId', None) is not None:
debug_print('PRS-T1: refreshing cover for book being sent')
self.upload_book_cover(connection, metadata, source_id)
debug_print('PRS-T1: done uploading cover')
def upload_book_cover(self, connection, book, source_id):
debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title)
if (not book.thumbnail or isinstance(book.thumbnail, ImageWrapper) or
not book.thumbnail[-1]):
# If the thumbnail is an ImageWrapper instance, it refers to a book
# not in the calibre library
return
cursor = connection.cursor()
thumbnail_path = THUMBPATH%book.bookId
prefix = self._main_prefix if source_id == 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 lopen(thumbnail_file_path, 'wb') as f:
f.write(book.thumbnail[-1])
fsync(f)
query = 'UPDATE books SET thumbnail = ? WHERE _id = ?'
t = (thumbnail_path, book.bookId,)
cursor.execute(query, t)
connection.commit()
cursor.close()
def is_sony_periodical(self, book):
if _('News') not in book.tags:
return False
if not book.lpath.lower().endswith('.epub'):
return False
if book.pubdate is None or book.pubdate.date() < date(2010, 10, 17):
return False
return True
def periodicalize_book(self, connection, book):
if not self.is_sony_periodical(book):
return
name = None
if '[' in book.title:
name = book.title.split('[')[0].strip()
if len(name) < 4:
name = None
if not name:
try:
name = [t for t in book.tags if t != _('News')][0]
except:
name = None
if not name:
name = book.title
pubdate = None
try:
pubdate = int(time.mktime(book.pubdate.timetuple()) * 1000)
except:
pass
cursor = connection.cursor()
periodical_schema = \
"'http://xmlns.sony.net/e-book/prs/periodicals/1.0/newspaper/1.0'"
# Setting this to the SONY periodical schema apparently causes errors
# with some periodicals, therefore set it to null, since the special
# periodical navigation doesn't work anyway.
periodical_schema = None
query = '''
UPDATE books
SET conforms_to = ?,
periodical_name = ?,
description = ?,
publication_date = ?
WHERE _id = ?
'''
t = (periodical_schema, name, None, pubdate, book.bookId,)
cursor.execute(query, t)
connection.commit()
cursor.close()