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):