diff --git a/src/calibre/debug.py b/src/calibre/debug.py
index 9c0151a748..f3945bedeb 100644
--- a/src/calibre/debug.py
+++ b/src/calibre/debug.py
@@ -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()
diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py
index 054e0b4c76..de4ff19733 100644
--- a/src/calibre/devices/kindle/driver.py
+++ b/src/calibre/devices/kindle/driver.py
@@ -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
diff --git a/src/calibre/devices/nook/driver.py b/src/calibre/devices/nook/driver.py
index fe73edf84a..31ea49d991 100644
--- a/src/calibre/devices/nook/driver.py
+++ b/src/calibre/devices/nook/driver.py
@@ -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
diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py
index 71597890e2..1a4a24b59a 100644
--- a/src/calibre/devices/usbms/device.py
+++ b/src/calibre/devices/usbms/device.py
@@ -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():
diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py
index 4cb4e0754b..a21799cff6 100644
--- a/src/calibre/ebooks/mobi/writer.py
+++ b/src/calibre/ebooks/mobi/writer.py
@@ -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
tag
buffer.write('
')
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 :
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 5b8ac7c98a..aedfb9164b 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -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:
diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py
index 7a6fb50ab1..aedba05720 100644
--- a/src/calibre/library/save_to_disk.py
+++ b/src/calibre/library/save_to_disk.py
@@ -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]
diff --git a/src/calibre/manual/news_recipe.rst b/src/calibre/manual/news_recipe.rst
index 0fb94d4a69..8f39cee387 100644
--- a/src/calibre/manual/news_recipe.rst
+++ b/src/calibre/manual/news_recipe.rst
@@ -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
---------------------
diff --git a/src/calibre/web/feeds/input.py b/src/calibre/web/feeds/input.py
index adad69f885..ea65511506 100644
--- a/src/calibre/web/feeds/input.py
+++ b/src/calibre/web/feeds/input.py
@@ -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' %
diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py
index 6ff0424162..b2d0d4d2ce 100644
--- a/src/calibre/web/feeds/news.py
+++ b/src/calibre/web/feeds/news.py
@@ -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'
diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py
index eac18428f7..c90bf82842 100644
--- a/src/calibre/web/feeds/recipes/collection.py
+++ b/src/calibre/web/feeds/recipes/collection.py
@@ -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):