From Greg

This commit is contained in:
Charles Haley 2011-04-18 08:34:22 +01:00
parent bd3b770297
commit 4ea961ba62
8 changed files with 154 additions and 28 deletions

View File

@ -582,6 +582,7 @@ from calibre.ebooks.snb.output import SNBOutput
from calibre.customize.profiles import input_profiles, output_profiles from calibre.customize.profiles import input_profiles, output_profiles
from calibre.devices.apple.driver import ITUNES from calibre.devices.apple.driver import ITUNES
from calibre.devices.content_server.driver import CONTENT_SERVER_FOR_CONFIG
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA
from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.blackberry.driver import BLACKBERRY
from calibre.devices.cybook.driver import CYBOOK, ORIZON from calibre.devices.cybook.driver import CYBOOK, ORIZON
@ -753,7 +754,9 @@ plugins += [
EEEREADER, EEEREADER,
NEXTBOOK, NEXTBOOK,
ITUNES, ITUNES,
] CONTENT_SERVER_FOR_CONFIG
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
x.__name__.endswith('MetadataReader')] x.__name__.endswith('MetadataReader')]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -201,8 +201,9 @@ class ITUNES(DriverBase):
# 0x1294 iPhone 3GS # 0x1294 iPhone 3GS
# 0x1297 iPhone 4 # 0x1297 iPhone 4
# 0x129a iPad # 0x129a iPad
# 0x12a2 iPad2
VENDOR_ID = [0x05ac] VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a] PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x12a2]
BCD = [0x01] BCD = [0x01]
# Plugboard ID # Plugboard ID
@ -421,7 +422,7 @@ class ITUNES(DriverBase):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':book.name(), 'title':book.name(),
'author':[book.artist()], 'author':book.artist().split(' & '),
'lib_book':library_books[this_book.path] if this_book.path in library_books else None, 'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
'dev_book':book, 'dev_book':book,
'uuid': book.composer() 'uuid': book.composer()
@ -459,7 +460,7 @@ class ITUNES(DriverBase):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':book.Name, 'title':book.Name,
'author':book.Artist, 'author':book.artist().split(' & '),
'lib_book':library_books[this_book.path] if this_book.path in library_books else None, 'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
'uuid': book.Composer, 'uuid': book.Composer,
'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub' 'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
@ -1021,7 +1022,9 @@ class ITUNES(DriverBase):
if isosx: if isosx:
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() format = file.rpartition('.')[2].lower()
path = self.path_template % (metadata[i].title, metadata[i].author[0],format) path = self.path_template % (metadata[i].title,
authors_to_string(metadata[i].authors),
format)
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], format, update_md=True) fpath = self._get_fpath(file, metadata[i], format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1034,9 +1037,11 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info("ITUNES.upload_books()") self.log.info("ITUNES.upload_books()")
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" % self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
( metadata[i].title, metadata[i].author, metadata[i].uuid)) (metadata[i].title,
authors_to_string(metadata[i].authors),
metadata[i].uuid))
self.cached_books[this_book.path] = { self.cached_books[this_book.path] = {
'author': metadata[i].author, 'author': authors_to_string(metadata[i].authors),
'dev_book': db_added, 'dev_book': db_added,
'format': format, 'format': format,
'lib_book': lb_added, 'lib_book': lb_added,
@ -1055,7 +1060,9 @@ class ITUNES(DriverBase):
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() format = file.rpartition('.')[2].lower()
path = self.path_template % (metadata[i].title, metadata[i].author[0],format) path = self.path_template % (metadata[i].title,
authors_to_string(metadata[i].authors),
format)
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i],format, update_md=True) fpath = self._get_fpath(file, metadata[i],format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1075,9 +1082,11 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info("ITUNES.upload_books()") self.log.info("ITUNES.upload_books()")
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" % self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
( metadata[i].title, metadata[i].author, metadata[i].uuid)) (metadata[i].title,
authors_to_string(metadata[i].authors),
metadata[i].uuid))
self.cached_books[this_book.path] = { self.cached_books[this_book.path] = {
'author': metadata[i].author[0], 'author': authors_to_string(metadata[i].authors),
'dev_book': db_added, 'dev_book': db_added,
'format': format, 'format': format,
'lib_book': lb_added, 'lib_book': lb_added,
@ -1190,7 +1199,7 @@ class ITUNES(DriverBase):
base_fn = base_fn.rpartition('.')[0] base_fn = base_fn.rpartition('.')[0]
db_added = self._find_device_book( db_added = self._find_device_book(
{ 'title': base_fn if format == 'pdf' else metadata.title, { 'title': base_fn if format == 'pdf' else metadata.title,
'author': metadata.authors[0], 'author': authors_to_string(metadata.authors),
'uuid': metadata.uuid, 'uuid': metadata.uuid,
'format': format}) 'format': format})
return db_added return db_added
@ -1255,7 +1264,7 @@ class ITUNES(DriverBase):
base_fn = base_fn.rpartition('.')[0] base_fn = base_fn.rpartition('.')[0]
added = self._find_library_book( added = self._find_library_book(
{ 'title': base_fn if format == 'pdf' else metadata.title, { 'title': base_fn if format == 'pdf' else metadata.title,
'author': metadata.author[0], 'author': authors_to_string(metadata.authors),
'uuid': metadata.uuid, 'uuid': metadata.uuid,
'format': format}) 'format': format})
return added return added
@ -1314,7 +1323,7 @@ class ITUNES(DriverBase):
with open(metadata.cover,'r+b') as cd: with open(metadata.cover,'r+b') as cd:
cover_data = cd.read() cover_data = cd.read()
except: except:
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors)))
self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title)) self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
import traceback import traceback
@ -1389,7 +1398,7 @@ class ITUNES(DriverBase):
thumb_path = path.rpartition('.')[0] + '.jpg' thumb_path = path.rpartition('.')[0] + '.jpg'
zfw.writestr(thumb_path, thumb) zfw.writestr(thumb_path, thumb)
except: except:
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors)))
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
finally: finally:
try: try:
@ -1407,7 +1416,7 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info(" ITUNES._create_new_book()") self.log.info(" ITUNES._create_new_book()")
this_book = Book(metadata.title, authors_to_string(metadata.author)) this_book = Book(metadata.title, authors_to_string(metadata.authors))
this_book.datetime = time.gmtime() this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
@ -2451,7 +2460,7 @@ class ITUNES(DriverBase):
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid or \ if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \ (self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]): self.cached_books[book]['author'] == authors_to_string(metadata.authors)):
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_device(self.cached_books[book]) self._remove_from_device(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2470,7 +2479,7 @@ class ITUNES(DriverBase):
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid or \ if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \ (self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]): self.cached_books[book]['author'] == authors_to_string(metadata.authors)):
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_iTunes(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2939,13 +2948,13 @@ class ITUNES(DriverBase):
def _xform_metadata_via_plugboard(self, book, format): def _xform_metadata_via_plugboard(self, book, format):
''' Transform book metadata from plugboard templates ''' ''' Transform book metadata from plugboard templates '''
if DEBUG: if DEBUG:
self.log.info(" ITUNES._xform_metadata_via_plugboard()") self.log.info(" ITUNES._xform_metadata_via_plugboard()")
if self.plugboard_func: if self.plugboard_func:
pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards) pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards)
newmi = book.deepcopy_metadata() newmi = book.deepcopy_metadata()
newmi.template_to_attribute(book, pb) newmi.template_to_attribute(book, pb)
if DEBUG: if pb is not None and DEBUG:
self.log.info(" transforming %s using %s:" % (format, pb)) self.log.info(" transforming %s using %s:" % (format, pb))
self.log.info(" title: %s %s" % (book.title, ">>> %s" % self.log.info(" title: %s %s" % (book.title, ">>> %s" %
newmi.title if book.title != newmi.title else '')) newmi.title if book.title != newmi.title else ''))
@ -3062,7 +3071,7 @@ class ITUNES_ASYNC(ITUNES):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':library_books[book].name(), 'title':library_books[book].name(),
'author':[library_books[book].artist()], 'author':library_books[book].artist().split(' & '),
'lib_book':library_books[book], 'lib_book':library_books[book],
'dev_book':None, 'dev_book':None,
'uuid': library_books[book].composer(), 'uuid': library_books[book].composer(),
@ -3102,7 +3111,7 @@ class ITUNES_ASYNC(ITUNES):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':library_books[book].Name, 'title':library_books[book].Name,
'author':library_books[book].Artist, 'author':library_books[book].Artist.split(' & '),
'lib_book':library_books[book], 'lib_book':library_books[book],
'uuid': library_books[book].Composer, 'uuid': library_books[book].Composer,
'format': format 'format': format
@ -3288,7 +3297,7 @@ class Book(Metadata):
See ebooks.metadata.book.base See ebooks.metadata.book.base
''' '''
def __init__(self,title,author): def __init__(self,title,author):
Metadata.__init__(self, title, authors=[author]) Metadata.__init__(self, title, authors=author.split(' & '))
@property @property
def title_sorter(self): def title_sorter(self):

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'

View File

@ -0,0 +1,74 @@
'''
Created on 17 Apr 2011
@author: GRiker, modeled on charles's Folder Device
'''
from calibre.constants import DEBUG
from calibre.devices.interface import DevicePlugin
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre.devices.usbms.driver import USBMS, BookList
class DriverBase(DeviceConfig, DevicePlugin):
# Reduce to just the formats eligible for plugboard xforms
# These formats are shown in the customization dialog
FORMATS = ['epub', 'mobi']
USER_CAN_ADD_NEW_FORMATS = False
# Hide the standard customization widgets
SUPPORTS_SUB_DIRS = False
MUST_READ_METADATA = True
SUPPORTS_USE_AUTHOR_SORT = False
# This class is added to the standard device plugin chain, so that it can
# be configured. It has invalid vendor_id etc, so it will never match a
# device. The 'real' CONTENT_SERVER will use the config from it.
class CONTENT_SERVER_FOR_CONFIG(USBMS):
name = 'Content Server Interface'
gui_name = 'Content Server'
description = _('Enables metadata plugboards to be used with Content Server.')
author = 'GRiker'
supported_platforms = ['windows', 'osx', 'linux']
VENDOR_ID = [0xffff]
PRODUCT_ID = [0xffff]
BCD = [0xffff]
DEVICE_PLUGBOARD_NAME = 'CONTENT_SERVER'
def config_widget(cls):
'''
Configure a minimal QWidget
Better to simply disable the config_widget altogether
'''
cw = DriverBase.config_widget()
# Turn off the Save template
cw.opt_save_template.setVisible(False)
cw.label.setVisible(False)
# Hide the up/down arrows
cw.column_up.setVisible(False)
cw.column_down.setVisible(False)
# Retitle
cw.groupBox.setTitle(_("Enable metadata plugboards for the following formats:"))
return cw
class CONTENT_SERVER(USBMS):
FORMATS = CONTENT_SERVER_FOR_CONFIG.FORMATS
DEVICE_PLUGBOARD_NAME = 'CONTENT_SERVER'
def __init__(self):
if DEBUG:
print("CONTENT_SERVER.init()")
pass
def set_plugboards(self, plugboards, pb_func):
# This method is called with the plugboard that matches the format
# declared in use_plugboard_ext and a device name of CONTENT_SERVER
if DEBUG:
print("CONTENT_SERVER.set_plugboards()")
print(' using plugboard %s' % plugboards)
self.plugboards = plugboards
self.plugboard_func = pb_func

View File

@ -17,7 +17,7 @@ from calibre.gui2.actions import InterfaceAction
class GenerateCatalogAction(InterfaceAction): class GenerateCatalogAction(InterfaceAction):
name = 'Generate Catalog' name = 'Generate Catalog'
action_spec = (_('Create a catalog of the books in your calibre library'), None, None, None) action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None)
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
def generate_catalog(self): def generate_catalog(self):

View File

@ -892,7 +892,7 @@ class DeviceMixin(object): # {{{
sub_dest_parts.append('') sub_dest_parts.append('')
to = sub_dest_parts[0] to = sub_dest_parts[0]
fmts = sub_dest_parts[1] fmts = sub_dest_parts[1]
subject = ';'.join(sub_dest_parts[2:]) subject = ';'.join(sub_dest_parts[2:])
fmts = [x.strip().lower() for x in fmts.split(',')] fmts = [x.strip().lower() for x in fmts.split(',')]
self.send_by_mail(to, fmts, delete, subject=subject) self.send_by_mail(to, fmts, delete, subject=subject)

View File

@ -12,6 +12,7 @@ from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
from PyQt4.Qt import QDialog from PyQt4.Qt import QDialog
from calibre.constants import isosx, iswindows
from calibre.gui2 import open_local_file from calibre.gui2 import open_local_file
from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog
from calibre.libunzip import extract as zipextract from calibre.libunzip import extract as zipextract
@ -42,11 +43,19 @@ class TweakEpub(QDialog, Ui_Dialog):
self.move(parent_loc.x(),parent_loc.y()) self.move(parent_loc.x(),parent_loc.y())
def cleanup(self): def cleanup(self):
if isosx:
try:
import appscript
self.finder = appscript.app('Finder')
self.finder.Finder_windows[os.path.basename(self._exploded)].close()
except:
# appscript fails to load on 10.4
pass
# Delete directory containing exploded ePub # Delete directory containing exploded ePub
if self._exploded is not None: if self._exploded is not None:
shutil.rmtree(self._exploded, ignore_errors=True) shutil.rmtree(self._exploded, ignore_errors=True)
def display_exploded(self): def display_exploded(self):
''' '''
Generic subprocess launch of native file browser Generic subprocess launch of native file browser

View File

@ -183,16 +183,37 @@ class ContentServer(object):
if fmt is None: if fmt is None:
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
if format == 'EPUB': if format == 'EPUB':
# Get the original metadata
mi = self.db.get_metadata(id, index_is_id=True)
# Instantiate the CONTENT_SERVER driver
from calibre.devices.content_server.driver import CONTENT_SERVER
cs = CONTENT_SERVER()
# Get any EPUB plugboards for the content server
from calibre.gui2.device import find_plugboard, device_name_for_plugboards
plugboards = self.db.prefs.get('plugboards', {})
# Transform the metadata via the plugboard
if hasattr(cs, 'set_plugboards') and callable(cs.set_plugboards):
cs.set_plugboards(plugboards, find_plugboard)
cpb = find_plugboard(device_name_for_plugboards(cs), format.lower(), plugboards)
if cpb:
newmi = mi.deepcopy_metadata()
newmi.template_to_attribute(mi, cpb)
else:
newmi = mi
# Write the updated file
from tempfile import TemporaryFile from tempfile import TemporaryFile
from calibre.ebooks.metadata.meta import set_metadata from calibre.ebooks.metadata.meta import set_metadata
raw = fmt.read() raw = fmt.read()
fmt = TemporaryFile() fmt = TemporaryFile()
fmt.write(raw) fmt.write(raw)
fmt.seek(0) fmt.seek(0)
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True, set_metadata(fmt, newmi, 'epub')
get_cover=True),
'epub')
fmt.seek(0) fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0] mt = guess_type('dummy.'+format.lower())[0]
if mt is None: if mt is None:
mt = 'application/octet-stream' mt = 'application/octet-stream'