Sync to trunk.

This commit is contained in:
John Schember 2009-12-07 18:04:14 -05:00
commit 4ab25660e2
11 changed files with 79 additions and 60 deletions

View File

@ -71,7 +71,7 @@ def debug_device_driver():
drives = []
print 'Drives detected:'
print '\t', '(ID, Partitions, Drive letter)'
for drive in wmi.WMI().Win32_DiskDrive():
for drive in wmi.WMI(find_classes=False).Win32_DiskDrive():
if drive.Partitions == 0:
continue
try:
@ -98,7 +98,7 @@ def debug_device_driver():
errors = {}
success = False
for dev in connected_devices:
print 'Device possibly connected:', dev
print 'Device possibly connected:', dev.__class__.name
print 'Trying to open device...',
try:
dev.open()

View File

@ -19,7 +19,7 @@ class KINDLE(USBMS):
name = 'Kindle Device Interface'
gui_name = 'Amazon Kindle'
description = _('Communicate with the Kindle eBook reader.')
author = _('John Schember')
author = 'John Schember'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats

View File

@ -12,13 +12,13 @@ from calibre.devices.usbms.driver import USBMS
class NOOK(USBMS):
name = 'Nook Iliad Device Interface'
description = _('Communicate with the Barns and Noble Nook eBook reader.')
author = _('John Schember')
supported_platforms = ['windows', 'linux']
name = 'Nook Device Interface'
gui_name = _('The Nook')
description = _('Communicate with the Nook eBook reader.')
author = 'John Schember'
supported_platforms = ['windows', 'linux', 'osx']
# Ordered list of supported formats
# Be sure these have an entry in calibre.devices.mime
FORMATS = ['epub', 'pdb', 'pdf']
VENDOR_ID = [0x2080]
@ -31,7 +31,8 @@ class NOOK(USBMS):
#OSX_MAIN_MEM = ''
MAIN_MEMORY_VOLUME_LABEL = 'BN Nook Main Memory'
MAIN_MEMORY_VOLUME_LABEL = 'Nook Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Nook Storage Card'
EBOOK_DIR_MAIN = 'my documents'
SUPPORTS_SUB_DIRS = True
@ -44,3 +45,8 @@ class NOOK(USBMS):
drives['carda'] = main
return drives
def windows_open_callback(self, drives):
if 'main' not in drives and 'carda' in drives:
drives['main'] = drives.pop('carda')
return drives

View File

@ -209,7 +209,8 @@ class Device(DeviceConfig, DevicePlugin):
return (msz, casz, cbsz)
def windows_match_device(self, drive, attr):
pnp_id = str(drive.PNPDeviceID).upper()
pnp_id = (str(drive.PNPDeviceID) if not isinstance(drive, basestring)
else str(drive)).upper()
device_id = getattr(self, attr)
def test_vendor():

View File

@ -105,13 +105,11 @@ def decint(value, direction):
bytes[-1] |= 0x80
return ''.join(chr(b) for b in reversed(bytes))
def align_block(raw, multiple=4, pad='\0'):
extra = len(raw) % multiple
if extra == 0: return raw
return raw + pad*(multiple - extra)
def rescale_image(data, maxsizeb, dimen=None):
image = Image.open(StringIO(data))
format = image.format
@ -155,7 +153,6 @@ def rescale_image(data, maxsizeb, dimen=None):
# Well, we tried?
return data
class Serializer(object):
NSRMAP = {'': None, XML_NS: 'xml', XHTML_NS: '', MBP_NS: 'mbp'}
@ -246,7 +243,7 @@ class Serializer(object):
buffer = self.buffer
if not item.linear:
self.breaks.append(buffer.tell() - 1)
self.id_offsets[item.href] = buffer.tell()
self.id_offsets[urlnormalize(item.href)] = buffer.tell()
# Kindle periodical articles are contained in a <div> tag
buffer.write('<div>')
for elem in item.data.find(XHTML('body')):
@ -268,7 +265,7 @@ class Serializer(object):
if id is not None:
href = '#'.join((item.href, id))
offset = self.anchor_offset or buffer.tell()
self.id_offsets[href] = offset
self.id_offsets[urlnormalize(href)] = offset
if self.anchor_offset is not None and \
tag == 'a' and not elem.attrib and \
not len(elem) and not elem.text:
@ -329,8 +326,6 @@ class Serializer(object):
buffer.seek(hoff)
buffer.write('%010d' % ioff)
class MobiWriter(object):
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
@ -545,7 +540,6 @@ class MobiWriter(object):
if self.opts.verbose > 3 : self._oeb.logger.info(" node %03d: %-15.15s... spans HTML records %03d - %03d \t offset: 0x%06X length: 0x%06X" % \
(myIndex, child.title if child.title.strip() > "" else "(missing)", myStartingRecord, myStartingRecord, offset, length) )
#last_name = "%04X" % myIndex
myIndex += 1
# Successfully parsed the entries
@ -717,13 +711,11 @@ class MobiWriter(object):
if self.opts.verbose > 3 : self._oeb.logger.info(" node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X" % \
(myIndex, self._ctoc_map[i]['klass'], child.title if child.title.strip() > "" else "(missing)", thisRecord, thisRecord, offset, length) )
#last_name = "%04X" % myIndex
myIndex += 1
# Successfully parsed the entries
return True
def _generate_tbs_book(self, nrecords, lastrecord):
if self.opts.verbose > 3 :self._oeb.logger.info("Assembling TBS for Book: HTML record %03d of %03d" % \
(nrecords, lastrecord) )
@ -787,7 +779,6 @@ class MobiWriter(object):
self._tbSequence = tbSequence
def _generate_tbs_flat_periodical(self, nrecords, lastrecord):
# Flat periodicals <0x102> have a single section for all articles
# Structured periodicals <0x101 | 0x103> have one or more sections with articles
@ -928,7 +919,6 @@ class MobiWriter(object):
tbSequence += decint(arg3, DECINT_FORWARD) # arg3
# Structured periodicals don't count periodical, section in nodeCount
#tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount - 2) # nodeCount
tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount) # nodeCount
tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD) # len
else :
@ -1222,7 +1212,6 @@ class MobiWriter(object):
self._oeb.logger.info("%s" % " TOC structure conforms" if toc_conforms else " TOC structure non-conforming")
return toc_conforms
def _generate_text(self):
self._oeb.logger.info('Serializing markup content...')
serializer = Serializer(self._oeb, self._images,
@ -1327,7 +1316,6 @@ class MobiWriter(object):
nrecords += 1
self._text_nrecords = nrecords
def _generate_images(self):
self._oeb.logger.info('Serializing images...')
images = [(index, href) for href, index in self._images.items()]
@ -1717,8 +1705,8 @@ class MobiWriter(object):
# 0x30 - 0x33 : Number of LIGT entries
header.write('\0'*4)
# 0x34 - 0x37 : Unknown
header.write(pack('>I', 1))
# 0x34 - 0x37 : Number of ctoc[] blocks
header.write(pack('>I', len(self._ctoc_records)))
# 0x38 - 0xb3 : Unknown (pad?)
header.write('\0'*124)
@ -1740,7 +1728,6 @@ class MobiWriter(object):
self._primary_index_record = len(self._records)
# GR: handle multiple ctoc records
# self._records.extend([indx0, indx1, self._ctoc])
self._records.extend([indx0, indx1 ])
for (i,ctoc_record) in enumerate(self._ctoc_records):
self._records.append(ctoc_record)
@ -1814,11 +1801,11 @@ class MobiWriter(object):
# Is there enough room for this string in the current ctoc record?
if 0xfbf8 - self._ctoc.tell() < 2 + len(ctoc_str):
# flush this ctoc, start a new one
print "closing ctoc_record at 0x%X" % self._ctoc.tell()
print "starting new ctoc with '%-50.50s ...'" % ctoc_str
# print "closing ctoc_record at 0x%X" % self._ctoc.tell()
# print "starting new ctoc with '%-50.50s ...'" % ctoc_str
# pad with 00
pad = 0xfbf8 - self._ctoc.tell()
print "padding %d bytes of 00" % pad
# print "padding %d bytes of 00" % pad
self._ctoc.write('\0' * (pad))
self._ctoc_records.append(self._ctoc.getvalue())
self._ctoc.truncate(0)
@ -1829,7 +1816,6 @@ class MobiWriter(object):
self._ctoc.write(decint(len(ctoc_str), DECINT_FORWARD) + ctoc_str)
return offset
def _add_flat_ctoc_node(self, node, ctoc, title=None):
# Process 'chapter' or 'article' nodes only, force either to 'chapter'
t = node.title if title is None else title
@ -1846,8 +1832,6 @@ class MobiWriter(object):
ctoc_name_map['klass'] = node.klass
# Add title offset to name map
# ctoc_name_map['titleOffset'] = ctoc.tell()
# ctoc.write(decint(len(t), DECINT_FORWARD)+t)
ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
self._chapterCount += 1
@ -1856,7 +1840,6 @@ class MobiWriter(object):
return
def _add_structured_ctoc_node(self, node, ctoc, title=None):
# Process 'periodical', 'section' and 'article'
@ -1875,15 +1858,11 @@ class MobiWriter(object):
if node.klass == 'chapter':
# Add title offset to name map
# ctoc_name_map['titleOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write(decint(len(t), DECINT_FORWARD)+t)
ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
self._chapterCount += 1
elif node.klass == 'periodical' :
# Add title offset
# ctoc_name_map['titleOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write( decint(len(t), DECINT_FORWARD) + t )
ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
# Look for existing class entry 'periodical' in _ctoc_map
@ -1896,16 +1875,12 @@ class MobiWriter(object):
continue
else:
# class names should always be in CNCX 0 - no offset
# ctoc_name_map['classOffset'] = ctoc.tell()
# ctoc.write(decint(len(node.klass), DECINT_FORWARD)+node.klass)
ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
self._periodicalCount += 1
elif node.klass == 'section' :
# Add title offset
# ctoc_name_map['titleOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write(decint(len(t), DECINT_FORWARD)+t)
ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
# Look for existing class entry 'section' in _ctoc_map
@ -1918,16 +1893,12 @@ class MobiWriter(object):
continue
else:
# class names should always be in CNCX 0 - no offset
# ctoc_name_map['classOffset'] = ctoc.tell()
# ctoc.write(decint(len(node.klass), DECINT_FORWARD)+node.klass)
ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
self._sectionCount += 1
elif node.klass == 'article' :
# Add title offset/title
# ctoc_name_map['titleOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write(decint(len(t), DECINT_FORWARD)+t)
ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
# Look for existing class entry 'article' in _ctoc_map
@ -1939,15 +1910,11 @@ class MobiWriter(object):
continue
else:
# class names should always be in CNCX 0 - no offset
# ctoc_name_map['classOffset'] = ctoc.tell()
# ctoc.write(decint(len(node.klass), DECINT_FORWARD)+node.klass)
ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
# Add description offset/description
if node.description :
d = self._clean_text_value(node.description)
# ctoc_name_map['descriptionOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write(decint(len(d), DECINT_FORWARD)+d)
ctoc_name_map['descriptionOffset'] = self._add_to_ctoc(d, self._ctoc_offset)
else :
ctoc_name_map['descriptionOffset'] = None
@ -1955,8 +1922,6 @@ class MobiWriter(object):
# Add author offset/attribution
if node.author :
a = self._clean_text_value(node.author)
# ctoc_name_map['authorOffset'] = ctoc.tell() + ctoc_offset
# ctoc.write(decint(len(a), DECINT_FORWARD)+a)
ctoc_name_map['authorOffset'] = self._add_to_ctoc(a, self._ctoc_offset)
else :
ctoc_name_map['authorOffset'] = None
@ -1971,7 +1936,6 @@ class MobiWriter(object):
# append this node's name_map to map
self._ctoc_map.append(ctoc_name_map)
def _generate_ctoc(self):
# Generate the compiled TOC strings
# Each node has 1-4 CTOC entries:
@ -2261,7 +2225,6 @@ class MobiWriter(object):
self._oeb.logger.info( " Unrecognized class %s in structured document" % section.klass)
return sectionIndices, sectionParents
def _generate_section_article_indices(self, i, section, entries, sectionIndices, sectionParents):
sectionArticles = list(section.iter())[1:]
# Iterate over the section's articles
@ -2280,7 +2243,6 @@ class MobiWriter(object):
myNewArticle = MobiArticle(mySectionParent, offset, length, ctoc_map_index )
mySectionParent.addArticle( myNewArticle )
def _add_book_chapters(self, myDoc, indxt, indices):
chapterCount = myDoc.documentStructure.chapterCount()
if self.opts.verbose > 3 :

View File

@ -772,8 +772,9 @@ class DeviceGUI(object):
if _auto_ids != []:
for id in _auto_ids:
if specific_format == None:
formats = [f.lower() for f in self.library_view.model().db.formats(id, index_is_id=True).split(',')]
formats = formats if formats != None else []
formats = self.library_view.model().db.formats(id, index_is_id=True)
formats = formats.split(',') if formats is not None else []
formats = [f.lower().strip() for f in formats]
if list(set(formats).intersection(available_input_formats())) != [] and list(set(self.device_manager.device_class.settings().format_map).intersection(available_output_formats())) != []:
auto.append(id)
else:

View File

@ -112,7 +112,7 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250,
if hasattr(mi.timestamp, 'timetuple'):
format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple())
if hasattr(mi.pubdate, 'timetuple'):
format_args['timestamp'] = strftime(timefmt, mi.pubdate.timetuple())
format_args['pubdate'] = strftime(timefmt, mi.pubdate.timetuple())
format_args['id'] = str(id)
components = [x.strip() for x in template.split('/') if x.strip()]
components = [x.format(**format_args).strip() for x in components]

View File

@ -109,6 +109,8 @@ Pre/post processing of downloaded HTML
.. automember:: BasicNewsRecipe.template_css
.. automember:: BasicNewsRecipe.remove_javascript
.. automethod:: BasicNewsRecipe.preprocess_html
.. automethod:: BasicNewsRecipe.postprocess_html
@ -128,6 +130,12 @@ Convenience methods
.. automethod:: BasicNewsRecipe.tag_to_string
Miscellaneous
~~~~~~~~~~~~~~~~~~
.. automember:: BasicNewsRecipe.requires_version
CustomIndexRecipe
---------------------

View File

@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en'
import os
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
from calibre.constants import numeric_version
class RecipeInput(InputFormatPlugin):
@ -51,7 +52,25 @@ class RecipeInput(InputFormatPlugin):
else:
title = getattr(opts, 'original_recipe_input_arg', recipe_or_file)
title = os.path.basename(title).rpartition('.')[0]
recipe = compile_recipe(get_builtin_recipe_by_title(title, log))
raw = get_builtin_recipe_by_title(title, log=log, download_recipe=True)
builtin = False
try:
recipe = compile_recipe(raw)
if recipe.requires_version > numeric_version:
log.warn(
'Downloaded recipe needs calibre version at least: %s' % \
recipe.requires_version)
builtin = True
except:
log.exception('Failed to compile downloaded recipe. Falling '
'back to builtin one')
builtin = True
if builtin:
raw = get_builtin_recipe_by_title(title, log=log,
download_recipe=False)
recipe = compile_recipe(raw)
if recipe is None:
raise ValueError('%r is not a valid recipe file or builtin recipe' %

View File

@ -46,6 +46,9 @@ class BasicNewsRecipe(Recipe):
#: The author of this recipe
__author__ = __appname__
#: Minimum calibre version needed to use this recipe
requires_version = (0, 6, 0)
#: The language that the news is in. Must be an ISO-639 code either
#: two or three characters long
language = 'und'

View File

@ -14,6 +14,8 @@ from lxml import etree
from lxml.builder import ElementMaker
from dateutil import parser
from calibre import browser
NS = 'http://calibre-ebook.com/recipe_collection'
E = ElementMaker(namespace=NS, nsmap={None:NS})
@ -88,10 +90,27 @@ def get_custom_recipe_collection(db):
def get_builtin_recipe_titles():
return [r.get('title') for r in get_builtin_recipe_collection()]
def get_builtin_recipe_by_title(title, log=None):
def download_builtin_recipe(urn):
br = browser()
return br.open('http://status.calibre-ebook.com/recipe/'+urn).read()
def get_builtin_recipe_by_title(title, log=None, download_recipe=False):
for x in get_builtin_recipe_collection():
if x.get('title') == title:
urn = x.get('id')[8:]
if download_recipe:
try:
if log is not None:
log('Trying to get latest version of recipe:', urn)
return download_builtin_recipe(urn)
except:
if log is None:
import traceback
traceback.print_exc()
else:
log.exception(
'Failed to download recipe, using builtin version')
return P('recipes/%s.recipe'%urn, data=True)
class SchedulerConfig(object):