From 77d0747a6f40024b83d387458e25a61fc90fb454 Mon Sep 17 00:00:00 2001 From: GRiker Date: Thu, 28 Jan 2010 03:05:33 -0700 Subject: [PATCH 01/32] KG updates --- src/calibre/ebooks/oeb/transforms/guide.py | 5 ++ src/calibre/library/catalog.py | 3 +- src/calibre/web/feeds/news.py | 79 ++++++++++++++++++++-- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/oeb/transforms/guide.py b/src/calibre/ebooks/oeb/transforms/guide.py index 3941904fdc..069077554c 100644 --- a/src/calibre/ebooks/oeb/transforms/guide.py +++ b/src/calibre/ebooks/oeb/transforms/guide.py @@ -32,6 +32,11 @@ class Clean(object): ref.type = 'cover' self.oeb.guide.refs['cover'] = ref + # Check for masthead in OPF, add to guide if present + for item in oeb.manifest.items: + if item.id == 'masthead-image': + self.oeb.guide.add('masthead', 'Masthead Image', item.href) + for x in list(self.oeb.guide): href = urldefrag(self.oeb.guide[x].href)[0] if x.lower() not in ('cover', 'titlepage', 'masthead', 'toc', diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 98ab27c3c7..529524371f 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1773,7 +1773,8 @@ class EPUB_MOBI(CatalogPlugin): self.playOrder += 1 navLabelTag = Tag(soup, 'navLabel') textTag = Tag(soup, 'text') - textTag.insert(0, NavigableString("Titles beginning with %s" % (title_letters[i]))) + textTag.insert(0, NavigableString("Titles beginning with %s" % \ + (title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'"))) navLabelTag.insert(0, textTag) navPointByLetterTag.insert(0,navLabelTag) contentTag = Tag(soup, 'content') diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 9abb55852e..3328445fd8 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -111,9 +111,7 @@ class BasicNewsRecipe(Recipe): #: Specify an override encoding for sites that have an incorrect #: charset specification. The most common being specifying ``latin1`` and - #: using ``cp1252``. If None, try to detect the encoding. If it is a - #: callable, the callable is called with two arguments: The recipe object - #: and the source to be decoded. It must return the decoded source. + #: using ``cp1252``. If None, try to detect the encoding. encoding = None #: Normally we try to guess if a feed has full articles embedded in it @@ -294,6 +292,17 @@ class BasicNewsRecipe(Recipe): ''' return getattr(self, 'cover_url', None) + def get_masthead_url(self): + ''' + Return a :term:`URL` to the masthead image for this issue or `None`. + By default it returns the value of the member `self.masthead_url` which + is normally `None`. If you want your recipe to download a masthead for the e-book + override this method in your subclass, or set the member variable `self.masthead_url` + before this method is called. + Masthead images are used in Kindle MOBI files. + ''' + return getattr(self, 'masthead_url', None) + def get_feeds(self): ''' Return a list of :term:`RSS` feeds to fetch for this profile. Each element of the list @@ -423,10 +432,7 @@ class BasicNewsRecipe(Recipe): if raw: return _raw if not isinstance(_raw, unicode) and self.encoding: - if callable(self.encoding): - _raw = self.encoding(_raw) - else: - _raw = _raw.decode(self.encoding, 'replace') + _raw = _raw.decode(self.encoding, 'replace') massage = list(BeautifulSoup.MARKUP_MASSAGE) massage.append((re.compile(r'&(\S+?);'), lambda match: entity_to_unicode(match, encoding=self.encoding))) return BeautifulSoup(_raw, markupMassage=massage) @@ -743,6 +749,9 @@ class BasicNewsRecipe(Recipe): self.report_progress(0, _('Trying to download cover...')) self.download_cover() + self.report_progress(0, _('Trying to download masthead...')) + self.download_masthead() + if self.test: feeds = feeds[:2] self.has_single_feed = len(feeds) == 1 @@ -859,7 +868,50 @@ class BasicNewsRecipe(Recipe): self.log.exception('Failed to download cover') self.cover_path = None + def convert_image(self, name): + image_ext = name.rpartition('.')[2].lower() + if image_ext in ['jpg','jpeg']: + return name + import calibre.utils.PythonMagickWand as p + img = p.NewMagickWand() + if img < 0: + raise RuntimeError('Cannot create wand.') + if not p.MagickReadImage(img, name): + self.log.warn('Failed to read image:', name) + name = name.replace('.%s' % image_ext, '.jpg') + p.MagickWriteImage(img, name) + return name + def _download_masthead(self): + self.masthead_path = None + try: + mu = self.get_masthead_url() + except Exception, err: + mu = None + self.log.error(_('Could not download masthead: %s')%str(err)) + self.log.debug(traceback.format_exc()) + if mu is not None: + ext = mu.rpartition('.')[-1] + if '?' in ext: + ext = '' + ext = ext.lower() if ext else 'jpg' + mpath = os.path.join(self.output_dir, 'mastheadImage.'+ext) + if os.access(mu, os.R_OK): + with open(mpath, 'wb') as mfile: + mfile.write(open(mu, 'rb').read()) + else: + self.report_progress(1, _('Downloading masthead from %s')%mu) + with nested(open(mpath, 'wb'), closing(self.browser.open(mu))) as (mfile, r): + mfile.write(r.read()) + self.masthead_path = self.convert_image(mpath) + + + def download_masthead(self): + try: + self._download_masthead() + except: + self.log.exception('Failed to download masthead') + self.masthead_path = None def default_cover(self, cover_file): ''' @@ -944,6 +996,8 @@ class BasicNewsRecipe(Recipe): manifest = [os.path.join(dir, 'feed_%d'%i) for i in range(len(feeds))] manifest.append(os.path.join(dir, 'index.html')) manifest.append(os.path.join(dir, 'index.ncx')) + + # Get cover cpath = getattr(self, 'cover_path', None) if cpath is None: pf = open(os.path.join(dir, 'cover.jpg'), 'wb') @@ -952,10 +1006,21 @@ class BasicNewsRecipe(Recipe): if cpath is not None and os.access(cpath, os.R_OK): opf.cover = cpath manifest.append(cpath) + + # Get masthead + mpath = getattr(self, 'masthead_path', None) + print "\ncreate_opf(): masthead: %s\n" % mpath + if mpath is not None and os.access(mpath, os.R_OK): + manifest.append(mpath) + opf.manifest = mpath + opf.create_manifest_from_files_in(manifest) for mani in opf.manifest: if mani.path.endswith('.ncx'): mani.id = 'ncx' + if mani.path.endswith('mastheadImage.jpg'): + mani.id = 'masthead-image' + entries = ['index.html'] toc = TOC(base_path=dir) From b9521c819d0a45c5d47ac7eb18b4b636f16e8dc1 Mon Sep 17 00:00:00 2001 From: GRiker Date: Thu, 28 Jan 2010 06:47:41 -0700 Subject: [PATCH 02/32] GwR revisions for mastheadImage, Catalog tweaks --- src/calibre/gui2/catalog/catalog_epub_mobi.ui | 81 +++++++++++++------ src/calibre/gui2/dialogs/catalog.ui | 2 +- src/calibre/web/feeds/news.py | 3 + 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.ui b/src/calibre/gui2/catalog/catalog_epub_mobi.ui index 858aec429f..044ecdaaec 100644 --- a/src/calibre/gui2/catalog/catalog_epub_mobi.ui +++ b/src/calibre/gui2/catalog/catalog_epub_mobi.ui @@ -14,19 +14,6 @@ Form - - - - Tags to exclude as genres (regex): - - - Qt::LogText - - - true - - - @@ -37,7 +24,7 @@ - + @@ -51,7 +38,7 @@ - + @@ -65,18 +52,67 @@ - + - + + + + Sort numbers as text + + + + - + - + + + + Regex pattern describing tags to exclude as genres: + + + Qt::LogText + + + true + + + + + + + + 14 + 75 + true + + + + Special marker tags for catalog generation + + + Qt::AlignCenter + + + + + + + Regex tips: +- The default regex of '\[[\w]*\]' ignores tags of the form '[tag]', e.g., '[Amazon Freebie]' +- A regex of '.' ignores all tags, generating no genre categories in the catalog + + + true + + + + Qt::Vertical @@ -89,13 +125,6 @@ - - - - Sort numbers as text - - - diff --git a/src/calibre/gui2/dialogs/catalog.ui b/src/calibre/gui2/dialogs/catalog.ui index c18e08ef65..3d62f36e85 100644 --- a/src/calibre/gui2/dialogs/catalog.ui +++ b/src/calibre/gui2/dialogs/catalog.ui @@ -107,7 +107,7 @@ 12 12 - 205 + 301 17 diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 3328445fd8..00869409ea 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -872,6 +872,8 @@ class BasicNewsRecipe(Recipe): image_ext = name.rpartition('.')[2].lower() if image_ext in ['jpg','jpeg']: return name + if image_ext not in ['gif']: + raise RuntimeError("web.feeds.news:BasicNewsRecipe.convert_image(): '%s' is not a supported mastheadImage format" % image_ext) import calibre.utils.PythonMagickWand as p img = p.NewMagickWand() if img < 0: @@ -880,6 +882,7 @@ class BasicNewsRecipe(Recipe): self.log.warn('Failed to read image:', name) name = name.replace('.%s' % image_ext, '.jpg') p.MagickWriteImage(img, name) + p.DestroyMagickWand(img) return name def _download_masthead(self): From 192e6a2f5dafdf670c1cae491ee8d4f974744e7a Mon Sep 17 00:00:00 2001 From: GRiker Date: Thu, 28 Jan 2010 12:54:49 -0700 Subject: [PATCH 03/32] GR changes for mastheadImage --- src/calibre/ebooks/mobi/output.py | 5 ++++- src/calibre/ebooks/oeb/transforms/guide.py | 6 +----- src/calibre/web/feeds/news.py | 8 ++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/mobi/output.py b/src/calibre/ebooks/mobi/output.py index 7e4643dac1..e3a1da34cc 100644 --- a/src/calibre/ebooks/mobi/output.py +++ b/src/calibre/ebooks/mobi/output.py @@ -50,7 +50,7 @@ class MOBIOutput(OutputFormatPlugin): def check_for_masthead(self): found = 'masthead' in self.oeb.guide if not found: - self.oeb.log.debug('No masthead found, generating default one...') + self.oeb.log.debug('No masthead found in manifest, generating default mastheadImage...') try: from PIL import Image as PILImage PILImage @@ -65,6 +65,9 @@ class MOBIOutput(OutputFormatPlugin): id, href = self.oeb.manifest.generate('masthead', 'masthead') self.oeb.manifest.add(id, href, 'image/gif', data=raw) self.oeb.guide.add('masthead', 'Masthead Image', href) + else: + self.oeb.log.debug('Using mastheadImage supplied in manifest...') + def dump_toc(self, toc) : self.log( "\n >>> TOC contents <<<") diff --git a/src/calibre/ebooks/oeb/transforms/guide.py b/src/calibre/ebooks/oeb/transforms/guide.py index 069077554c..e911fe9cf7 100644 --- a/src/calibre/ebooks/oeb/transforms/guide.py +++ b/src/calibre/ebooks/oeb/transforms/guide.py @@ -32,13 +32,9 @@ class Clean(object): ref.type = 'cover' self.oeb.guide.refs['cover'] = ref - # Check for masthead in OPF, add to guide if present - for item in oeb.manifest.items: - if item.id == 'masthead-image': - self.oeb.guide.add('masthead', 'Masthead Image', item.href) - for x in list(self.oeb.guide): href = urldefrag(self.oeb.guide[x].href)[0] + print "ebooks.oeb.transforms.guide:Clean(): checking x.lower(): %s" % x.lower() if x.lower() not in ('cover', 'titlepage', 'masthead', 'toc', 'title-page', 'copyright-page', 'start'): self.oeb.guide.remove(x) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 00869409ea..f8be8462c6 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -994,6 +994,14 @@ class BasicNewsRecipe(Recipe): mi.pubdate = datetime.now() opf_path = os.path.join(dir, 'index.opf') ncx_path = os.path.join(dir, 'index.ncx') + + # Add mastheadImage entry to section + from calibre.ebooks.metadata.opf2 import Guide + mi.guide = Guide() + ref = Guide.Reference('mastheadImage.jpg', os.getcwdu()) + ref.type = 'masthead' + ref.title = 'Masthead Image' + mi.guide.append(ref) opf = OPFCreator(dir, mi) manifest = [os.path.join(dir, 'feed_%d'%i) for i in range(len(feeds))] From ebdcae06cda073c3d5a902173819825157eab1ab Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Jan 2010 17:34:04 -0700 Subject: [PATCH 04/32] ... --- src/calibre/ebooks/pdf/reflow.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/pdf/reflow.py b/src/calibre/ebooks/pdf/reflow.py index bf2d921a10..42c16225d2 100644 --- a/src/calibre/ebooks/pdf/reflow.py +++ b/src/calibre/ebooks/pdf/reflow.py @@ -256,11 +256,16 @@ class Region(object): return len(self.columns) == 0 @property - def is_small(self): + def line_count(self): max_lines = 0 for c in self.columns: max_lines = max(max_lines, len(c)) - return max_lines > 2 + return max_lines + + + @property + def is_small(self): + return self.line_count < 3 def absorb(self, singleton): @@ -431,7 +436,7 @@ class Page(object): def coalesce_regions(self): # find contiguous sets of small regions # absorb into a neighboring region (prefer the one with number of cols - # closer to the avg number of cols in the set, if equal use large + # closer to the avg number of cols in the set, if equal use larger # region) # merge contiguous regions that can contain each other absorbed = set([]) From d54c1a867e790e455d96cae6f32787eac74c17d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Jan 2010 19:18:00 -0700 Subject: [PATCH 05/32] E-book viewer: Workaround to display images that have been embedded in svg containers. Fixes #4716 (Unable to view ePUB cover images in calibre viewer) --- resources/viewer/images.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/resources/viewer/images.js b/resources/viewer/images.js index ea68009254..46cb968d4c 100644 --- a/resources/viewer/images.js +++ b/resources/viewer/images.js @@ -20,4 +20,20 @@ function setup_image_scaling_handlers() { }); } +function extract_svged_images() { + $("svg").each(function() { + var children = $(this).children("img"); + if (children.length == 1) { + var img = $(children[0]); + var href = img.attr('xlink:href'); + if (href != undefined) { + $(this).replaceWith('
SVG Image
'); + } + } + }); +} + +$(document).ready(function() { + extract_svged_images(); +}); From de2e424c88e22f876a9f071bfad32c8c236517e9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Jan 2010 19:37:25 -0700 Subject: [PATCH 06/32] E-book viewer: Add support for SVG images --- resources/viewer/images.js | 2 +- src/calibre/gui2/viewer/documentview.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/resources/viewer/images.js b/resources/viewer/images.js index 46cb968d4c..7b10f6169a 100644 --- a/resources/viewer/images.js +++ b/resources/viewer/images.js @@ -34,6 +34,6 @@ function extract_svged_images() { } $(document).ready(function() { - extract_svged_images(); + //extract_svged_images(); }); diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index f6fce62eac..2c2a7fc135 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -10,7 +10,7 @@ from base64 import b64encode from PyQt4.Qt import QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer, \ QPainter, QPalette, QBrush, QFontDatabase, QDialog, \ QColor, QPoint, QImage, QRegion, QVariant, QIcon, \ - QFont, pyqtSignature, QAction + QFont, pyqtSignature, QAction, QByteArray from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings from calibre.utils.config import Config, StringConfig @@ -514,14 +514,17 @@ class DocumentView(QWebView): mt = guess_type(path)[0] html = open(path, 'rb').read().decode(path.encoding, 'replace') html = EntityDeclarationProcessor(html).processed_html + has_svg = re.search(r'<\S*svg', html) is not None + if 'xhtml' in mt: html = self.self_closing_pat.sub(self.self_closing_sub, html) if self.manager is not None: self.manager.load_started() self.loading_url = QUrl.fromLocalFile(path) - #self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path)) - #open('/tmp/t.html', 'wb').write(html.encode(path.encoding)) - self.setHtml(html, self.loading_url) + if has_svg: + self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path)) + else: + self.setHtml(html, self.loading_url) self.turn_off_internal_scrollbars() def initialize_scrollbar(self): From 813e52bbf8cb0b07651a3e2141bb91c019fc99f7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 28 Jan 2010 19:52:04 -0700 Subject: [PATCH 07/32] ... --- src/calibre/__init__.py | 9 ++++++--- src/calibre/gui2/viewer/documentview.py | 3 ++- src/calibre/utils/ipc/job.py | 7 +++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index e32c03fe13..e5e284fb5b 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -132,9 +132,12 @@ def prints(*args, **kwargs): try: arg = arg.encode(enc) except UnicodeEncodeError: - if not safe_encode: - raise - arg = repr(arg) + try: + arg = arg.encode('utf-8') + except: + if not safe_encode: + raise + arg = repr(arg) file.write(arg) if i != len(args)-1: diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 2c2a7fc135..6b95a4dcaa 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -514,7 +514,7 @@ class DocumentView(QWebView): mt = guess_type(path)[0] html = open(path, 'rb').read().decode(path.encoding, 'replace') html = EntityDeclarationProcessor(html).processed_html - has_svg = re.search(r'<\S*svg', html) is not None + has_svg = re.search(r'<[:a-z]*svg', html) is not None if 'xhtml' in mt: html = self.self_closing_pat.sub(self.self_closing_sub, html) @@ -522,6 +522,7 @@ class DocumentView(QWebView): self.manager.load_started() self.loading_url = QUrl.fromLocalFile(path) if has_svg: + prints('Rendering as XHTML...') self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path)) else: self.setHtml(html, self.loading_url) diff --git a/src/calibre/utils/ipc/job.py b/src/calibre/utils/ipc/job.py index 458d5adb8a..a6c39ffc6b 100644 --- a/src/calibre/utils/ipc/job.py +++ b/src/calibre/utils/ipc/job.py @@ -52,10 +52,13 @@ class BaseJob(object): else: self._status_text = _('Error') if self.failed else _('Finished') if DEBUG: - prints('Job:', self.id, self.description, 'finished', + try: + prints('Job:', self.id, self.description, 'finished', safe_encode=True) - prints('\t'.join(self.details.splitlines(True)), + prints('\t'.join(self.details.splitlines(True)), safe_encode=True) + except: + pass if not self._done_called: self._done_called = True try: From b8bc876d078dabb2b583577b82cf3b5637aed66b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 06:54:20 -0700 Subject: [PATCH 08/32] Fix #4721 (Bulk import stops because of utf-8 errors) --- src/calibre/gui2/add.py | 6 +++++- src/calibre/gui2/ui.py | 2 ++ src/calibre/startup.py | 1 + src/calibre/web/feeds/news.py | 3 +-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 5419596334..ace2ac5c7e 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -12,7 +12,7 @@ from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2 import question_dialog, error_dialog, info_dialog from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata import MetaInformation -from calibre.constants import preferred_encoding +from calibre.constants import preferred_encoding, filesystem_encoding class DuplicatesAdder(QThread): @@ -46,6 +46,8 @@ class RecursiveFind(QThread): def run(self): root = os.path.abspath(self.path) self.books = [] + if isinstance(root, unicode): + root = root.encode(filesystem_encoding) try: for dirpath in os.walk(root): if self.canceled: @@ -55,6 +57,8 @@ class RecursiveFind(QThread): self.books += list(self.db.find_books_in_directory(dirpath[0], self.single_book_per_directory)) except Exception, err: + import traceback + traceback.print_exc() try: msg = unicode(err) except: diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 714b2c3a27..28efdc93ff 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -1048,6 +1048,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if self._adder.critical: det_msg = [] for name, log in self._adder.critical.items(): + if isinstance(name, str): + name = name.decode(filesystem_encoding, 'replace') det_msg.append(name+'\n'+log) warning_dialog(self, _('Failed to read metadata'), _('Failed to read metadata from the following')+':', diff --git a/src/calibre/startup.py b/src/calibre/startup.py index 3a761cca10..3e33757f92 100644 --- a/src/calibre/startup.py +++ b/src/calibre/startup.py @@ -84,6 +84,7 @@ if not _run_once: return res os.path.abspath = my_abspath + _join = os.path.join def my_join(a, *p): encoding=sys.getfilesystemencoding() diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 65f6135b8e..5b8a4fc215 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -558,8 +558,6 @@ class BasicNewsRecipe(Recipe): '--max-recursions', str(self.recursions), '--delay', str(self.delay), ] - if self.encoding is not None: - web2disk_cmdline.extend(['--encoding', self.encoding]) if self.verbose: web2disk_cmdline.append('--verbose') @@ -578,6 +576,7 @@ class BasicNewsRecipe(Recipe): 'preprocess_html', 'remove_tags_after', 'remove_tags_before'): setattr(self.web2disk_options, extra, getattr(self, extra)) self.web2disk_options.postprocess_html = self._postprocess_html + self.web2disk_options.encoding = self.encoding if self.delay > 0: self.simultaneous_downloads = 1 From caac66510280309c596fa08350cfd5ebb85eb2b3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 07:18:32 -0700 Subject: [PATCH 09/32] New recipe for Michelle Malkin by Walt Anthony --- resources/images/news/michellemalkin_icon.png | Bin 0 -> 419 bytes resources/recipes/michellemalkin.recipe | 49 ++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 resources/images/news/michellemalkin_icon.png create mode 100644 resources/recipes/michellemalkin.recipe diff --git a/resources/images/news/michellemalkin_icon.png b/resources/images/news/michellemalkin_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..76842ec64232ba2dd68a115f182e8b68ccf792fd GIT binary patch literal 419 zcmV;U0bKrxP)y-UMT6va={%>D<)4;<9WYb*W(iVjY8Q8z(R5JWc-aj-f# zh=>S+g9SmMA5rMgL0ZIib;(jxx=03*Iuz1Gq%Gt+STt$AiofN-yYJj{@8NMoMED1q zra{+r2mr9hakdA$9(@%>fvT!tjDc?#cnJYOmSt3{RWQZ?;IDxnhGC#sEI<$fh(sd4 z_d+LQH|llV7aq_f_F{Nw&{`xPYBiOTX^Bct&yID$(Xz!y#B?T$(|8iIGt<~wUq*np#xpg)W?Mg>I{OK+^7e|-V;-vubJ*Tk zv5CzB^9caL*QbW_i+(IFmC!d3bWhs_{_v;^06G?%-2nixnD1xw Date: Fri, 29 Jan 2010 08:06:26 -0700 Subject: [PATCH 10/32] Basic support for associating file types on OS X --- setup/installer/osx/app/main.py | 5 ++++- src/calibre/gui2/__init__.py | 13 ++++++++++++- src/calibre/gui2/main.py | 5 ++++- src/calibre/gui2/ui.py | 4 ++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index 85533717b4..2a10d09998 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -355,6 +355,7 @@ class Py2App(object): @flush def create_plist(self): + from calibre.ebooks import BOOK_EXTENSIONS env = dict(**ENV) env['CALIBRE_LAUNCHED_FROM_BUNDLE']='1'; @@ -367,10 +368,11 @@ class Py2App(object): CFBundlePackageType='APPL', CFBundleSignature='????', CFBundleExecutable='calibre', + CFBundleTypeExtensions=list(BOOK_EXTENSIONS), LSMinimumSystemVersion='10.4.2', LSRequiresNativeExecution=True, NSAppleScriptEnabled=False, - NSHumanReadableCopyright='Copyright 2008, Kovid Goyal', + NSHumanReadableCopyright='Copyright 2010, Kovid Goyal', CFBundleGetInfoString=('calibre, an E-book management ' 'application. Visit http://calibre-ebook.com for details.'), CFBundleIconFile='library.icns', @@ -594,6 +596,7 @@ class Py2App(object): if x == 'Info.plist': plist = plistlib.readPlist(join(self.contents_dir, x)) plist['LSUIElement'] = '1' + plist.pop('CFBundleTypeExtensions') plistlib.writePlist(plist, join(cc_dir, x)) else: os.symlink(join('../..', x), diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 34f9f57161..7181c16329 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -3,7 +3,8 @@ __copyright__ = '2008, Kovid Goyal ' """ The GUI """ import os from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \ - QByteArray, QTranslator, QCoreApplication, QThread + QByteArray, QTranslator, QCoreApplication, QThread, \ + QEvent from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \ QIcon, QTableView, QApplication, QDialog, QPushButton @@ -524,6 +525,7 @@ class Application(QApplication): def __init__(self, args): qargs = [i.encode('utf-8') if isinstance(i, unicode) else i for i in args] QApplication.__init__(self, qargs) + self.file_event_hook = None global gui_thread, qt_app gui_thread = QThread.currentThread() self._translator = None @@ -549,6 +551,15 @@ class Application(QApplication): if set_qt_translator(self._translator): self.installTranslator(self._translator) + def event(self, e): + if callable(self.file_event_hook) and e.type() == QEvent.FileOpen: + path = unicode(e.file()) + if os.access(path, os.R_OK): + self.file_event_hook(path) + return True + else: + return QApplication.event(self, e) + _store_app = None def is_ok_to_use_qt(): diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index cf62508751..fef0853a23 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -2,6 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import sys, os, time, socket, traceback +from functools import partial from PyQt4.Qt import QCoreApplication, QIcon, QMessageBox @@ -52,10 +53,12 @@ def run_gui(opts, args, actions, listener, app): wizard().exec_() dynamic.set('welcome_wizard_was_run', True) main = Main(listener, opts, actions) + add_filesystem_book = partial(main.add_filesystem_book, allow_device=False) sys.excepthook = main.unhandled_exception if len(args) > 1: args[1] = os.path.abspath(args[1]) - main.add_filesystem_book(args[1]) + add_filesystem_book(args[1]) + app.file_event_hook = add_filesystem_book ret = app.exec_() if getattr(main, 'run_wizard_b4_shutdown', False): from calibre.gui2.wizard import wizard diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 28efdc93ff..7f3ca297fd 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -987,10 +987,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.cover_cache.refresh([cid]) self.library_view.model().current_changed(current_idx, current_idx) - def add_filesystem_book(self, path): + def add_filesystem_book(self, path, allow_device=True): if os.access(path, os.R_OK): books = [os.path.abspath(path)] - to_device = self.stack.currentIndex() != 0 + to_device = allow_device and self.stack.currentIndex() != 0 self._add_books(books, to_device) if to_device: self.status_bar.showMessage(\ From 9b5ab804295c36dbf2cb3938c3103d836e199a7a Mon Sep 17 00:00:00 2001 From: GRiker Date: Fri, 29 Jan 2010 08:25:46 -0700 Subject: [PATCH 11/32] removed prettify(), fixed p.date_index css --- resources/catalog/stylesheet.css | 2 +- src/calibre/library/catalog.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/catalog/stylesheet.css b/resources/catalog/stylesheet.css index 5fe7b2fd85..eb2352d26a 100644 --- a/resources/catalog/stylesheet.css +++ b/resources/catalog/stylesheet.css @@ -34,7 +34,7 @@ p.description { p.date_index { font-size:x-large; text-align:center; - font-style:bold; + font-weight:bold; margin-top:1em; margin-bottom:0px; } diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index c859ff6e32..a10ce56959 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -2134,7 +2134,6 @@ class EPUB_MOBI(CatalogPlugin): # Add this section to the body body.insert(btc, navPointTag) btc += 1 - print soup.prettify() self.ncxSoup = soup def generateNCXByTags(self, tocTitle): From b3bcd13f3a4b497979ca6de706d6ab8003a9ffc0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 08:58:51 -0700 Subject: [PATCH 12/32] OS X: Allow adding of books to calibre via drag and drop on the calibre dock icon --- icons/book.icns | Bin 0 -> 11285 bytes setup/installer/osx/app/main.py | 13 +++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 icons/book.icns diff --git a/icons/book.icns b/icons/book.icns new file mode 100644 index 0000000000000000000000000000000000000000..ee305dc9ddc3f40a512d1364d88719a6855d62eb GIT binary patch literal 11285 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 literal 0 HcmV?d00001 diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index 2a10d09998..a473281e61 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -266,6 +266,7 @@ class Py2App(object): def get_local_dependencies(self, path_to_lib): for x in self.get_dependencies(path_to_lib): for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/', + '/opt/local/lib/', '/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'): if x.startswith(y): if y == '/Library/Frameworks/Python.framework/': @@ -338,8 +339,8 @@ class Py2App(object): c = join(self.build_dir, 'Contents') for x in ('Frameworks', 'MacOS', 'Resources'): os.makedirs(join(c, x)) - x = 'library.icns' - shutil.copyfile(join('icons', x), join(self.resources_dir, x)) + for x in ('library.icns', 'book.icns'): + shutil.copyfile(join('icons', x), join(self.resources_dir, x)) @flush def add_calibre_plugins(self): @@ -358,6 +359,10 @@ class Py2App(object): from calibre.ebooks import BOOK_EXTENSIONS env = dict(**ENV) env['CALIBRE_LAUNCHED_FROM_BUNDLE']='1'; + docs = [{'CFBundleTypeName':'E-book', + 'CFBundleTypeExtensions':list(BOOK_EXTENSIONS), + 'CFBundleTypeRole':'Viewer', + }] pl = dict( CFBundleDevelopmentRegion='English', @@ -368,7 +373,7 @@ class Py2App(object): CFBundlePackageType='APPL', CFBundleSignature='????', CFBundleExecutable='calibre', - CFBundleTypeExtensions=list(BOOK_EXTENSIONS), + CFBundleDocumentTypes=docs, LSMinimumSystemVersion='10.4.2', LSRequiresNativeExecution=True, NSAppleScriptEnabled=False, @@ -596,7 +601,7 @@ class Py2App(object): if x == 'Info.plist': plist = plistlib.readPlist(join(self.contents_dir, x)) plist['LSUIElement'] = '1' - plist.pop('CFBundleTypeExtensions') + plist.pop('CFBundleDocumentTypes') plistlib.writePlist(plist, join(cc_dir, x)) else: os.symlink(join('../..', x), From 800981cb5857ab1098b77ccd05bff9bd40e3fb68 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 09:54:44 -0700 Subject: [PATCH 13/32] New recipe for Open Left by XanthanGum --- resources/recipes/open_left.recipe | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 resources/recipes/open_left.recipe diff --git a/resources/recipes/open_left.recipe b/resources/recipes/open_left.recipe new file mode 100644 index 0000000000..148bb07f13 --- /dev/null +++ b/resources/recipes/open_left.recipe @@ -0,0 +1,22 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class OpenLeft(BasicNewsRecipe): + # Information about the recipe + + title = 'Open Left' + description = 'Progressive American commentary on current events' + category = 'news, commentary' + language = 'en' + __author__ = 'Xanthan Gum' + + # Fetch no article older than seven days + + oldest_article = 7 + + # Fetch no more than 100 articles + + max_articles_per_feed = 100 + + # Fetch the articles from the RSS feed + + feeds = [(u'Articles', u'http://www.openleft.com/rss/rss2.xml')] From 0b04ea9ffd9a96575059603f74057ba7e768f09d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 10:54:44 -0700 Subject: [PATCH 14/32] Fix #4693 (Bulk edit metadata - can not set rating to 0 stars) --- src/calibre/gui2/dialogs/metadata_bulk.py | 6 +----- src/calibre/gui2/dialogs/metadata_bulk.ui | 9 +++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index d76d8136db..46342c8a88 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -20,10 +20,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.db = db self.ids = [ db.id(r) for r in rows] self.write_series = False - self.write_rating = False self.changed = False QObject.connect(self.button_box, SIGNAL("accepted()"), self.sync) - QObject.connect(self.rating, SIGNAL('valueChanged(int)'), self.rating_changed) self.tags.update_tags_cache(self.db.all_tags()) self.remove_tags.update_tags_cache(self.db.all_tags()) @@ -99,7 +97,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): aus = unicode(self.author_sort.text()) if aus and self.author_sort.isEnabled(): self.db.set_author_sort(id, aus, notify=False) - if self.write_rating: + if self.rating.value() != -1: self.db.set_rating(id, 2*self.rating.value(), notify=False) pub = unicode(self.publisher.text()) if pub: @@ -134,5 +132,3 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): def series_changed(self): self.write_series = True - def rating_changed(self): - self.write_rating = True diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 0fdb36b717..1a38568b60 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -96,12 +96,21 @@ QAbstractSpinBox::PlusMinus + + No change + stars + + -1 + 5 + + -1 + From 37c90afed4f9aa2774f6ff573b92f7e9cfa3c11d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 10:58:04 -0700 Subject: [PATCH 15/32] ... --- src/calibre/gui2/viewer/documentview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 6b95a4dcaa..9b911754c8 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -514,7 +514,7 @@ class DocumentView(QWebView): mt = guess_type(path)[0] html = open(path, 'rb').read().decode(path.encoding, 'replace') html = EntityDeclarationProcessor(html).processed_html - has_svg = re.search(r'<[:a-z]*svg', html) is not None + has_svg = re.search(r'<[:a-zA-Z]*svg', html) is not None if 'xhtml' in mt: html = self.self_closing_pat.sub(self.self_closing_sub, html) From 79bb3ba0053df10d79c527812b42cf6b454bba2c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 11:22:30 -0700 Subject: [PATCH 16/32] Fix #4683 (If comics.txt is UTF-8 with a byte order mark, the first comic is skipped) --- src/calibre/ebooks/comic/input.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/comic/input.py b/src/calibre/ebooks/comic/input.py index 122f61e45a..6fba918888 100755 --- a/src/calibre/ebooks/comic/input.py +++ b/src/calibre/ebooks/comic/input.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' Based on ideas from comiclrf created by FangornUK. ''' -import os, shutil, traceback, textwrap, time +import os, shutil, traceback, textwrap, time, codecs from ctypes import byref from Queue import Empty @@ -338,8 +338,9 @@ class ComicInput(InputFormatPlugin): if not os.path.exists('comics.txt'): raise ValueError('%s is not a valid comic collection' %stream.name) - for line in open('comics.txt', - 'rb').read().decode('utf-8').splitlines(): + raw = open('comics.txt', 'rb').read().decode('utf-8') + raw.lstrip(unicode(codecs.BOM_UTF8, "utf8" )) + for line in raw.splitlines(): line = line.strip() if not line: continue From d2b721e678b8dc8927ba5d58bb724f378c0a9eea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 12:58:07 -0700 Subject: [PATCH 17/32] EPUB Output: Remove tags that point to the internet for their images as this causes the ever delicate ADE to crash. Fixes #4692 (epub file from NY Times (subscription) site crashes Sony PRS-700) --- src/calibre/ebooks/epub/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index 8e9c9efea9..6e381d5237 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -269,7 +269,7 @@ class EPUBOutput(OutputFormatPlugin): bad = [] for x in XPath('//h:img')(body): src = x.get('src', '').strip() - if src in ('', '#'): + if src in ('', '#') or src.startswith('http:'): bad.append(x) for img in bad: img.getparent().remove(img) From 2a51e38d221739c84b7ca707623964c1cb55c92b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 13:07:22 -0700 Subject: [PATCH 18/32] MOBI Input: Ignore width and height percentage measures for tags. Fixes #4726 (using calibre 0.6.36, chapter head graphics take all page in viewer) --- src/calibre/ebooks/mobi/reader.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 4f894ce088..4aac84e599 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -573,6 +573,8 @@ class MobiReader(object): attrib[attr] = "%dpx"%int(nval) except: del attrib[attr] + elif val.lower().endswith('%'): + del attrib[attr] elif tag.tag == 'pre': if not tag.text: tag.tag = 'div' From 5fb1d6ab82ad774f2a4d4400856493266e7cfef2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 13:09:47 -0700 Subject: [PATCH 19/32] Fix #4727 (AttributeError: 'MetaInformation' object has no attribute 'thumnbail') --- src/calibre/devices/prs500/books.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/prs500/books.py b/src/calibre/devices/prs500/books.py index 07f9310e87..91fcb3255f 100644 --- a/src/calibre/devices/prs500/books.py +++ b/src/calibre/devices/prs500/books.py @@ -274,7 +274,7 @@ class BookList(_BookList): node.setAttribute(attr, attrs[attr]) try: w, h, data = mi.thumbnail - except TypeError: + except: w, h, data = None, None, None if data: From 71b7f434636d402ad672c53ecef0a808e316fd8a Mon Sep 17 00:00:00 2001 From: GRiker Date: Fri, 29 Jan 2010 13:23:59 -0700 Subject: [PATCH 20/32] Multiple catalog tweaks, NCX optimization for Kindle DX, tweaked formatting of Recently Added --- resources/catalog/stylesheet.css | 2 +- src/calibre/library/catalog.py | 110 +++++++++++++------------------ 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/resources/catalog/stylesheet.css b/resources/catalog/stylesheet.css index eb2352d26a..7bfbbd22cc 100644 --- a/resources/catalog/stylesheet.css +++ b/resources/catalog/stylesheet.css @@ -33,7 +33,7 @@ p.description { p.date_index { font-size:x-large; - text-align:center; + text-align:right; font-weight:bold; margin-top:1em; margin-bottom:0px; diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index a10ce56959..3dda46864a 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -486,7 +486,7 @@ class EPUB_MOBI(CatalogPlugin): # Number of discrete steps to catalog creation current_step = 0.0 - total_steps = 13.0 + total_steps = 14.0 # Used to xlate pubdate to friendly format MONTHS = ['','January', 'February','March','April','May','June', @@ -804,7 +804,7 @@ class EPUB_MOBI(CatalogPlugin): self.generateNCXByDateAdded("Recently Added") if getattr(self.reporter, 'cancel_requested', False): return 1 - self.generateNCXByTags("Genres") + self.generateNCXByGenre("Genres") if getattr(self.reporter, 'cancel_requested', False): return 1 self.writeNCX() @@ -1336,7 +1336,6 @@ class EPUB_MOBI(CatalogPlugin): key=lambda x:(x['title_sort'], x['title_sort'])) this_months_list = sorted(this_months_list, key=lambda x:(x['author_sort'], x['author_sort'])) - print "Books added in %s %s" % (self.MONTHS[current_date.month], current_date.year) # Create a new month anchor pIndexTag = Tag(soup, "p") @@ -1344,19 +1343,16 @@ class EPUB_MOBI(CatalogPlugin): aTag = Tag(soup, "a") aTag['name'] = "%s-%s" % (current_date.year, current_date.month) pIndexTag.insert(0,aTag) - pIndexTag.insert(1,NavigableString('Books added in %s %s' % \ + pIndexTag.insert(1,NavigableString('%s %s' % \ (self.MONTHS[current_date.month],current_date.year))) divTag.insert(dtc,pIndexTag) dtc += 1 current_author = None - for purchase in this_months_list: - print " %-40s \t %-20s \t %s" % (purchase['title'], purchase['author'], purchase['timestamp']) - - - if purchase['author'] != current_author: + for new_entry in this_months_list: + if new_entry['author'] != current_author: # Start a new author - current_author = purchase['author'] + current_author = new_entry['author'] pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" emTag = Tag(soup, "em") @@ -1373,7 +1369,7 @@ class EPUB_MOBI(CatalogPlugin): ptc = 0 # Prefix book with read/unread symbol - if purchase['read']: + if new_entry['read']: # check mark pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL)) pBookTag['class'] = "read_book" @@ -1385,8 +1381,8 @@ class EPUB_MOBI(CatalogPlugin): ptc += 1 aTag = Tag(soup, "a") - aTag['href'] = "book_%d.html" % (int(float(purchase['id']))) - aTag.insert(0,escape(purchase['title'])) + aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) + aTag.insert(0,escape(new_entry['title'])) pBookTag.insert(ptc, aTag) ptc += 1 @@ -1855,9 +1851,13 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def generateNCXByTitle(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByTitle()")) + def add_to_books_by_letter(current_book_list): + current_book_list = " • ".join(current_book_list) + current_book_list = self.generateShortDescription(self.formatNCXText(current_book_list)) + books_by_letter.append(current_book_list) + soup = self.ncxSoup output = "ByAlphaTitle" body = soup.find("navPoint") @@ -1891,9 +1891,7 @@ class EPUB_MOBI(CatalogPlugin): for book in self.booksByTitle: if self.letter_or_symbol(book['title_sort'][0]) != current_letter: # Save the old list - book_list = " • ".join(current_book_list) - short_description = self.generateShortDescription(self.formatNCXText(book_list)) - books_by_letter.append(short_description) + add_to_books_by_letter(current_book_list) # Start the new list current_letter = self.letter_or_symbol(book['title_sort'][0]) @@ -1907,9 +1905,7 @@ class EPUB_MOBI(CatalogPlugin): current_book_list.append(book['title']) # Add the last book list - book_list = " • ".join(current_book_list) - short_description = self.generateShortDescription(self.formatNCXText(book_list)) - books_by_letter.append(short_description) + add_to_books_by_letter(current_book_list) # Add *article* entries for each populated title letter for (i,books) in enumerate(books_by_letter): @@ -1944,9 +1940,13 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByAuthor(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByAuthor()")) + def add_to_author_list(current_author_list, current_letter): + current_author_list = " • ".join(current_author_list) + current_author_list = self.generateShortDescription(self.formatNCXText(current_author_list)) + master_author_list.append((current_author_list, current_letter)) + soup = self.ncxSoup HTML_file = "content/ByAlphaAuthor.html" body = soup.find("navPoint") @@ -1983,14 +1983,7 @@ class EPUB_MOBI(CatalogPlugin): for author in self.authors: if author[1][0] != current_letter: # Save the old list - author_list = " • ".join(current_author_list) - if len(current_author_list) == self.descriptionClip: - author_list += " …" - - author_list = self.formatNCXText(author_list) - if False and self.verbose: - self.opts.log.info(" adding '%s' to master_author_list" % current_letter) - master_author_list.append((author_list, current_letter)) + add_to_author_list(current_author_list, current_letter) # Start the new list current_letter = author[1][0] @@ -2000,13 +1993,7 @@ class EPUB_MOBI(CatalogPlugin): current_author_list.append(author[0]) # Add the last author list - author_list = " • ".join(current_author_list) - if len(current_author_list) == self.descriptionClip: - author_list += " …" - author_list = self.formatNCXText(author_list) - if False and self.verbose: - self.opts.log.info(" adding '%s' to master_author_list" % current_letter) - master_author_list.append((author_list, current_letter)) + add_to_author_list(current_author_list, current_letter) # Add *article* entries for each populated author initial letter # master_author_list{}: [0]:author list [1]:Initial letter @@ -2042,9 +2029,13 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByDateAdded(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByDateAdded()")) + def add_to_master_month_list(current_titles_list): + current_titles_list = " • ".join(current_titles_list) + current_titles_list = self.generateShortDescription(self.formatNCXText(current_titles_list)) + master_month_list.append((current_titles_list, current_date)) + soup = self.ncxSoup HTML_file = "content/ByDateAdded.html" body = soup.find("navPoint") @@ -2083,31 +2074,23 @@ class EPUB_MOBI(CatalogPlugin): if book['timestamp'].month != current_date.month or \ book['timestamp'].year != current_date.year: # Save the old lists - current_titles_list = " • ".join(current_titles_list) - if len(current_titles_list) == self.descriptionClip: - title_list += " …" - - current_titles_list = self.formatNCXText(current_titles_list) - master_month_list.append((current_titles_list, current_date)) + add_to_master_month_list(current_titles_list) # Start the new list current_date = book['timestamp'].date() current_titles_list = [book['title']] else: - if len(current_titles_list) < self.descriptionClip: - current_titles_list.append(book['title']) + current_titles_list.append(book['title']) - # Add the last author list - current_titles_list = " • ".join(current_titles_list) - master_month_list.append((current_titles_list, current_date)) + # Add the last month list + add_to_master_month_list(current_titles_list) - # Add *article* entries for each populated author initial letter + # Add *article* entries for each populated month # master_months_list{}: [0]:titles list [1]:date for books_by_month in master_month_list: - print "titles:%s \ndate:%s" % books_by_month - navPointByLetterTag = Tag(soup, 'navPoint') - navPointByLetterTag['class'] = "article" - navPointByLetterTag['id'] = "%s-%s-ID" % (books_by_month[1].year,books_by_month[1].month ) + navPointByMonthTag = Tag(soup, 'navPoint') + navPointByMonthTag['class'] = "article" + navPointByMonthTag['id'] = "%s-%s-ID" % (books_by_month[1].year,books_by_month[1].month ) navPointTag['playOrder'] = self.playOrder self.playOrder += 1 navLabelTag = Tag(soup, 'navLabel') @@ -2115,20 +2098,20 @@ class EPUB_MOBI(CatalogPlugin): textTag.insert(0, NavigableString("Books added in %s %s" % \ (self.MONTHS[books_by_month[1].month], books_by_month[1].year))) navLabelTag.insert(0, textTag) - navPointByLetterTag.insert(0,navLabelTag) + navPointByMonthTag.insert(0,navLabelTag) contentTag = Tag(soup, 'content') contentTag['src'] = "%s#%s-%s" % (HTML_file, books_by_month[1].year,books_by_month[1].month) - navPointByLetterTag.insert(1,contentTag) + navPointByMonthTag.insert(1,contentTag) if self.generateForKindle: cmTag = Tag(soup, '%s' % 'calibre:meta') cmTag['name'] = "description" cmTag.insert(0, NavigableString(books_by_month[0])) - navPointByLetterTag.insert(2, cmTag) + navPointByMonthTag.insert(2, cmTag) - navPointTag.insert(nptc, navPointByLetterTag) + navPointTag.insert(nptc, navPointByMonthTag) nptc += 1 # Add this section to the body @@ -2136,12 +2119,15 @@ class EPUB_MOBI(CatalogPlugin): btc += 1 self.ncxSoup = soup - def generateNCXByTags(self, tocTitle): + def generateNCXByGenre(self, tocTitle): # Create an NCX section for 'By Genre' # Add each genre as an article # 'tag', 'file', 'authors' - self.opts.log.info(self.updateProgressFullStep("generateNCXByTags()")) + self.opts.log.info(self.updateProgressFullStep("generateNCXByGenre()")) + + + if not len(self.genres): self.opts.log.warn(" No genres found in tags.\n" @@ -2239,7 +2225,6 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def writeNCX(self): - self.opts.log.info(self.updateProgressFullStep("writeNCX()")) outfile = open("%s/%s.ncx" % (self.catalogPath, self.basename), 'w') @@ -2533,7 +2518,6 @@ class EPUB_MOBI(CatalogPlugin): def generateShortDescription(self, description): # Truncate the description to description_clip, on word boundaries if necessary - if not description: return None @@ -2545,7 +2529,7 @@ class EPUB_MOBI(CatalogPlugin): # Start adding words until we reach description_clip short_description = "" - words = description.split(" ") + words = description.split() for word in words: short_description += word + " " if len(short_description) > self.descriptionClip: @@ -2667,7 +2651,7 @@ class EPUB_MOBI(CatalogPlugin): # Add local options opts.creator = "calibre" - opts.descriptionClip = 250 + opts.descriptionClip = 380 if self.opts.output_profile.endswith('dx') else 90 opts.basename = "Catalog" opts.plugin_path = self.plugin_path From 7e903eaded5ce7b47065743fe1bc48507ee79fed Mon Sep 17 00:00:00 2001 From: Starson17 Date: Fri, 29 Jan 2010 15:27:33 -0500 Subject: [PATCH 21/32] Adds checkboxes on config and single fetch metadata screens to control author/title overwrites during metadata fetch --- src/calibre/gui2/__init__.py | 2 ++ src/calibre/gui2/dialogs/config/__init__.py | 2 ++ src/calibre/gui2/dialogs/config/config.ui | 7 +++++++ src/calibre/gui2/dialogs/fetch_metadata.py | 1 + src/calibre/gui2/dialogs/fetch_metadata.ui | 7 +++++++ src/calibre/gui2/dialogs/metadata_single.py | 7 ++++--- src/calibre/gui2/metadata.py | 5 +++++ 7 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 7181c16329..fed22f87e2 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -89,6 +89,8 @@ def _config(): help=_('Maximum number of waiting worker processes')) c.add_opt('get_social_metadata', default=True, help=_('Download social metadata (tags/rating/etc.)')) + c.add_opt('overwrite_author_title_metadata', default=True, + help=_('Overwrite author and title with new metadata')) c.add_opt('enforce_cpu_limit', default=True, help=_('Limit max simultaneous jobs to number of CPUs')) diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index 156faec7ce..88697e55bb 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -458,6 +458,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.connect(self.button_open_config_dir, SIGNAL('clicked()'), self.open_config_dir) self.opt_get_social_metadata.setChecked(config['get_social_metadata']) + self.opt_overwrite_author_title_metadata.setChecked(config['overwrite_author_title_metadata']) self.opt_enforce_cpu_limit.setChecked(config['enforce_cpu_limit']) self.device_detection_button.clicked.connect(self.debug_device_detection) @@ -751,6 +752,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): config['upload_news_to_device'] = self.sync_news.isChecked() config['search_as_you_type'] = self.search_as_you_type.isChecked() config['get_social_metadata'] = self.opt_get_social_metadata.isChecked() + config['overwrite_author_title_metadata'] = self.opt_overwrite_author_title_metadata.isChecked() config['enforce_cpu_limit'] = bool(self.opt_enforce_cpu_limit.isChecked()) fmts = [] for i in range(self.viewer.count()): diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index b9306b0f10..6da5362248 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -171,6 +171,13 @@ + + + + Overwrite & author/title by default when fetching metadata + + + diff --git a/src/calibre/gui2/dialogs/fetch_metadata.py b/src/calibre/gui2/dialogs/fetch_metadata.py index b021a2470d..59f07f0399 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.py +++ b/src/calibre/gui2/dialogs/fetch_metadata.py @@ -119,6 +119,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata): self.matches.setMouseTracking(True) self.fetch_metadata() self.opt_get_social_metadata.setChecked(config['get_social_metadata']) + self.opt_overwrite_author_title_metadata.setChecked(config['overwrite_author_title_metadata']) def show_summary(self, current, *args): diff --git a/src/calibre/gui2/dialogs/fetch_metadata.ui b/src/calibre/gui2/dialogs/fetch_metadata.ui index fe97b32f28..f14d402e11 100644 --- a/src/calibre/gui2/dialogs/fetch_metadata.ui +++ b/src/calibre/gui2/dialogs/fetch_metadata.ui @@ -116,6 +116,13 @@ + + + + Overwrite &author/title with author/title of selected book + + + diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 89b7c92125..846851fd21 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -574,9 +574,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): det_msg=det, show=True) else: book.tags = [] - self.title.setText(book.title) - self.authors.setText(authors_to_string(book.authors)) - if book.author_sort: self.author_sort.setText(book.author_sort) + if d.opt_overwrite_author_title_metadata.isChecked(): + self.title.setText(book.title) + self.authors.setText(authors_to_string(book.authors)) + if book.author_sort: self.author_sort.setText(book.author_sort) if book.publisher: self.publisher.setEditText(book.publisher) if book.isbn: self.isbn.setText(book.isbn) if book.pubdate: diff --git a/src/calibre/gui2/metadata.py b/src/calibre/gui2/metadata.py index ecdca29422..d63e9648cc 100644 --- a/src/calibre/gui2/metadata.py +++ b/src/calibre/gui2/metadata.py @@ -12,6 +12,7 @@ from Queue import Queue, Empty from calibre.ebooks.metadata.fetch import search, get_social_metadata +from calibre.gui2 import config from calibre.ebooks.metadata.library_thing import cover_from_isbn from calibre.customize.ui import get_isbndb_key @@ -98,6 +99,10 @@ class DownloadMetadata(Thread): self.fetched_metadata[id] = fmi if fmi.isbn and self.get_covers: self.worker.jobs.put(fmi.isbn) + if (not config['overwrite_author_title_metadata']): + fmi.authors = mi.authors + fmi.author_sort = mi.author_sort + fmi.title = mi.title mi.smart_update(fmi) if mi.isbn and self.get_social_metadata: self.social_metadata_exceptions = get_social_metadata(mi) From 845e558ae06e2bbf0585bdcb4281d78fbff91191 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 14:12:12 -0700 Subject: [PATCH 22/32] Don't die if generating default masthead fails --- src/calibre/web/feeds/news.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 113d7dd756..d182d856d8 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -770,7 +770,11 @@ class BasicNewsRecipe(Recipe): self.download_masthead(murl) if self.masthead_path is None: self.masthead_path = os.path.join(self.output_dir, 'mastheadImage.jpg') - self.default_masthead_image(self.masthead_path) + try: + self.default_masthead_image(self.masthead_path) + except: + self.log.exception('Failed to generate default masthead image') + self.masthead_path = None if self.test: feeds = feeds[:2] @@ -1061,7 +1065,7 @@ class BasicNewsRecipe(Recipe): opf = OPFCreator(dir, mi) # Add mastheadImage entry to section mp = getattr(self, 'masthead_path', None) - if mp is not None: + if mp is not None and os.access(mp, os.R_OK): from calibre.ebooks.metadata.opf2 import Guide ref = Guide.Reference(os.path.basename(self.masthead_path), os.getcwdu()) ref.type = 'masthead' From 1ce23c231468dc03709ee76c7da814fa41c6e4a7 Mon Sep 17 00:00:00 2001 From: GRiker Date: Fri, 29 Jan 2010 15:36:21 -0700 Subject: [PATCH 23/32] Catalog housecleaning, progress reporting fixed --- src/calibre/library/catalog.py | 128 ++++++++++++--------------------- 1 file changed, 44 insertions(+), 84 deletions(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index a8b3442dfd..3826aa55ec 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -502,7 +502,7 @@ class EPUB_MOBI(CatalogPlugin): # verbosity level of diagnostic printout def __init__(self, db, opts, plugin, - notification=DummyReporter(), + report_progress=DummyReporter(), stylesheet="content/stylesheet.css"): self.__opts = opts self.__authors = None @@ -527,16 +527,12 @@ class EPUB_MOBI(CatalogPlugin): self.__plugin_path = opts.plugin_path self.__progressInt = 0.0 self.__progressString = '' - self.__reporter = notification + self.__reporter = report_progress self.__stylesheet = stylesheet self.__thumbs = None self.__title = opts.catalog_title self.__verbose = opts.verbose - self.opts.log.info("CatalogBuilder(): Generating %s %s"% \ - (self.opts.fmt, - "for %s" % self.opts.output_profile if self.opts.output_profile \ - else '')) # Accessors ''' @dynamic_property @@ -755,59 +751,27 @@ class EPUB_MOBI(CatalogPlugin): # Methods def buildSources(self): - if getattr(self.reporter, 'cancel_requested', False): return 1 - if not self.booksByTitle: - self.fetchBooksByTitle() - - if getattr(self.reporter, 'cancel_requested', False): return 1 + self.fetchBooksByTitle() self.fetchBooksByAuthor() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateHTMLDescriptions() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateHTMLByAuthor() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateHTMLByTitle() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateHTMLByDateAdded() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateHTMLByTags() - if getattr(self.reporter, 'cancel_requested', False): return 1 from calibre.utils.PythonMagickWand import ImageMagick with ImageMagick(): self.generateThumbnails() - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateOPF() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXHeader() - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXDescriptions("Descriptions") - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXByAuthor("Authors") - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXByTitle("Titles") - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXByDateAdded("Recently Added") - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.generateNCXByGenre("Genres") - - if getattr(self.reporter, 'cancel_requested', False): return 1 self.writeNCX() - return 0 - def cleanUp(self): pass @@ -828,7 +792,7 @@ class EPUB_MOBI(CatalogPlugin): os.path.join(self.catalogPath, file[0])) def fetchBooksByTitle(self): - self.opts.log.info(self.updateProgressFullStep("fetchBooksByTitle()")) + self.updateProgressFullStep("Fetching database") # Get the database as a dictionary # Sort by title @@ -875,7 +839,7 @@ class EPUB_MOBI(CatalogPlugin): this_title['publisher'] = re.sub('&', '&', record['publisher']) this_title['rating'] = record['rating'] if record['rating'] else 0 - this_title['date'] = strftime(u'%b %Y', record['pubdate'].timetuple()) + this_title['date'] = strftime(u'%B %Y', record['pubdate'].timetuple()) this_title['timestamp'] = record['timestamp'] if record['comments']: this_title['description'] = re.sub('&', '&', record['comments']) @@ -912,7 +876,7 @@ class EPUB_MOBI(CatalogPlugin): def fetchBooksByAuthor(self): # Generate a list of titles sorted by author from the database - self.opts.log.info(self.updateProgressFullStep("fetchBooksByAuthor()")) + self.updateProgressFullStep("Sorting database by author") # Sort titles case-insensitive self.booksByAuthor = sorted(self.booksByTitle, @@ -965,14 +929,15 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLDescriptions(self): # Write each title to a separate HTML file in contentdir - self.opts.log.info(self.updateProgressFullStep("generateHTMLDescriptions()")) + self.updateProgressFullStep("Description") for (title_num, title) in enumerate(self.booksByTitle): if False: self.opts.log.info("%3s: %s - %s" % (title['id'], title['title'], title['author'])) - self.updateProgressMicroStep("generating book descriptions ...", - float(title_num*100/len(self.booksByTitle))/100) + self.updateProgressMicroStep("Description %d of %d" % \ + (title_num, len(self.booksByTitle)), + float(title_num*100/len(self.booksByTitle))/100) # Generate the header soup = self.generateHTMLDescriptionHeader("%s" % title['title']) @@ -1093,7 +1058,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByTitle(self): # Write books by title A-Z to HTML file - self.opts.log.info(self.updateProgressFullStep("generateHTMLByTitle()")) + self.updateProgressFullStep("Books by Title") soup = self.generateHTMLEmptyHeader("Books By Alpha Title") body = soup.find('body') @@ -1195,7 +1160,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByAuthor(self): # Write books by author A-Z - self.opts.log.info(self.updateProgressFullStep("generateHTMLByAuthor()")) + self.updateProgressFullStep("Books by Author") friendly_name = "By Author" @@ -1324,10 +1289,12 @@ class EPUB_MOBI(CatalogPlugin): self.htmlFileList.append("content/ByAlphaAuthor.html") def generateHTMLByDateAdded(self): + # Write books by reverse chronological order + self.updateProgressFullStep("Recently Added") def add_books_to_HTML(this_months_list, dtc): if len(this_months_list): - date_string = strftime(u'%b %Y', current_date.timetuple()) + date_string = strftime(u'%B %Y', current_date.timetuple()) this_months_list = sorted(this_months_list, key=lambda x:(x['title_sort'], x['title_sort'])) this_months_list = sorted(this_months_list, @@ -1338,8 +1305,7 @@ class EPUB_MOBI(CatalogPlugin): aTag = Tag(soup, "a") aTag['name'] = "%s-%s" % (current_date.year, current_date.month) pIndexTag.insert(0,aTag) - pIndexTag.insert(1,NavigableString('%s %s' % \ - (self.MONTHS[current_date.month],current_date.year))) + pIndexTag.insert(1,NavigableString(date_string)) divTag.insert(dtc,pIndexTag) dtc += 1 current_author = None @@ -1385,8 +1351,6 @@ class EPUB_MOBI(CatalogPlugin): dtc += 1 return dtc - # Write books by reverse chronological order - self.opts.log.info(self.updateProgressFullStep("generateHTMLByDateAdded()")) # Sort titles case-insensitive self.booksByDate = sorted(self.booksByTitle, @@ -1458,7 +1422,7 @@ class EPUB_MOBI(CatalogPlugin): # Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ... # Note that special tags - ~+*[] - have already been filtered from books[] - self.opts.log.info(self.updateProgressFullStep("generateHTMLByTags()")) + self.updateProgressFullStep("Generating Genres") # Filter out REMOVE_TAGS, sort filtered_tags = self.filterDbTags(self.db.all_tags()) @@ -1539,7 +1503,8 @@ class EPUB_MOBI(CatalogPlugin): for (i,title) in enumerate(self.booksByTitle): # Update status - self.updateProgressMicroStep("generating thumbnails ...", + self.updateProgressMicroStep("Thumbnail %d of %d" % \ + (i,len(self.booksByTitle)), i/float(len(self.booksByTitle))) # Check to see if source file exists if 'cover' in title and os.path.isfile(title['cover']): @@ -1561,7 +1526,7 @@ class EPUB_MOBI(CatalogPlugin): self.generateThumbnail(title, image_dir, thumb_file) else: # Use default cover - if self.verbose: + if False and self.verbose: self.opts.log.warn(" using default cover for '%s'" % \ (title['title'])) # Check to make sure default is current @@ -1594,13 +1559,13 @@ class EPUB_MOBI(CatalogPlugin): cover_timestamp = os.path.getmtime(cover) thumb_timestamp = os.path.getmtime(thumb_fp) if thumb_timestamp < cover_timestamp: - if self.verbose: + if False and self.verbose: self.opts.log.warn("updating thumbnail_default for %s" % title['title']) #title['cover'] = "%s/DefaultCover.jpg" % self.catalogPath title['cover'] = cover self.generateThumbnail(title, image_dir, "thumbnail_default.jpg") else: - if self.verbose: + if False and self.verbose: self.opts.log.warn(" generating new thumbnail_default.jpg") #title['cover'] = "%s/DefaultCover.jpg" % self.catalogPath title['cover'] = cover @@ -1610,7 +1575,7 @@ class EPUB_MOBI(CatalogPlugin): def generateOPF(self): - self.opts.log.info(self.updateProgressFullStep("generateOPF()")) + self.updateProgressFullStep("Generating OPF") header = ''' @@ -1742,7 +1707,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXHeader(self): - self.opts.log.info(self.updateProgressFullStep("generateNCXHeader()")) + self.updateProgressFullStep("NCX header") header = ''' @@ -1778,7 +1743,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXDescriptions(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXDescriptions()")) + self.updateProgressFullStep("NCX descriptions") # --- Construct the 'Books by Title' section --- ncx_soup = self.ncxSoup @@ -1845,7 +1810,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def generateNCXByTitle(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByTitle()")) + self.updateProgressFullStep("NCX Titles") def add_to_books_by_letter(current_book_list): current_book_list = " • ".join(current_book_list) @@ -1934,7 +1899,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByAuthor(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByAuthor()")) + self.updateProgressFullStep("NCX Authors") def add_to_author_list(current_author_list, current_letter): current_author_list = " • ".join(current_author_list) @@ -2023,7 +1988,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByDateAdded(self, tocTitle): - self.opts.log.info(self.updateProgressFullStep("generateNCXByDateAdded()")) + self.updateProgressFullStep("NCX Recently Added") def add_to_master_month_list(current_titles_list): current_titles_list = " • ".join(current_titles_list) @@ -2081,7 +2046,7 @@ class EPUB_MOBI(CatalogPlugin): # Add *article* entries for each populated month # master_months_list{}: [0]:titles list [1]:date for books_by_month in master_month_list: - datestr = strftime(u'%b %Y', books_by_month[1].timetuple()) + datestr = strftime(u'%B %Y', books_by_month[1].timetuple()) navPointByMonthTag = Tag(soup, 'navPoint') navPointByMonthTag['class'] = "article" navPointByMonthTag['id'] = "%s-%s-ID" % (books_by_month[1].year,books_by_month[1].month ) @@ -2089,7 +2054,7 @@ class EPUB_MOBI(CatalogPlugin): self.playOrder += 1 navLabelTag = Tag(soup, 'navLabel') textTag = Tag(soup, 'text') - textTag.insert(0, NavigableString("Books added in " + datestr)) + textTag.insert(0, NavigableString(datestr)) navLabelTag.insert(0, textTag) navPointByMonthTag.insert(0,navLabelTag) contentTag = Tag(soup, 'content') @@ -2117,7 +2082,7 @@ class EPUB_MOBI(CatalogPlugin): # Add each genre as an article # 'tag', 'file', 'authors' - self.opts.log.info(self.updateProgressFullStep("generateNCXByGenre()")) + self.updateProgressFullStep("NCX by Genre") @@ -2218,7 +2183,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def writeNCX(self): - self.opts.log.info(self.updateProgressFullStep("writeNCX()")) + self.updateProgressFullStep("Writing NCX") outfile = open("%s/%s.ncx" % (self.catalogPath, self.basename), 'w') outfile.write(self.ncxSoup.prettify()) @@ -2305,7 +2270,7 @@ class EPUB_MOBI(CatalogPlugin): else: continue if self.verbose: - self.opts.log.info(' %d Genre tags in database (exclude_genre: %s):' % \ + self.opts.log.info(' %d Genre tags (exclude_genre: %s):' % \ (len(filtered_tags), self.opts.exclude_genre)) self.opts.log.info(' %s' % ', '.join(filtered_tags)) @@ -2620,12 +2585,10 @@ class EPUB_MOBI(CatalogPlugin): self.opts.log.info('%s not implemented' % self.error) def updateProgressFullStep(self, description): - self.current_step += 1 self.progressString = description self.progressInt = float((self.current_step-1)/self.total_steps) - self.reporter(self.progressInt/100., self.progressString) - return u"%.2f%% %s" % (self.progressInt, self.progressString) + self.reporter(self.progressInt, self.progressString) def updateProgressMicroStep(self, description, micro_step_pct): step_range = 100/self.total_steps @@ -2633,11 +2596,9 @@ class EPUB_MOBI(CatalogPlugin): coarse_progress = float((self.current_step-1)/self.total_steps) fine_progress = float((micro_step_pct*step_range)/100) self.progressInt = coarse_progress + fine_progress - self.reporter(self.progressInt/100., self.progressString) - return u"%.2f%% %s" % (self.progressInt, self.progressString) + self.reporter(self.progressInt, self.progressString) def run(self, path_to_output, opts, db, notification=DummyReporter()): - opts.log = log = Log() opts.fmt = self.fmt = path_to_output.rpartition('.')[2] self.opts = opts @@ -2650,29 +2611,28 @@ class EPUB_MOBI(CatalogPlugin): if opts.verbose: opts_dict = vars(opts) - log("%s:run" % self.name) - log(" path_to_output: %s" % path_to_output) - log(" Output format: %s" % self.fmt) + log("%s(): Generating %s for %s" % (self.name,self.fmt,opts.output_profile)) if opts_dict['ids']: log(" Book count: %d" % len(opts_dict['ids'])) # Display opts keys = opts_dict.keys() keys.sort() log(" opts:") + for key in keys: - if key == 'ids': - if opts_dict[key]: - continue - else: - log(" %s: (all)" % key) - log(" %s: %s" % (key, opts_dict[key])) + if key in ['catalog_title','exclude_genre','exclude_tags','note_tag', + 'numbers_as_text','read_tag','search_text','sort_by']: + log(" %s: %s" % (key, opts_dict[key])) # Launch the Catalog builder - catalog = self.CatalogBuilder(db, opts, self, notification=notification) + catalog = self.CatalogBuilder(db, opts, self, report_progress=notification) catalog.createDirectoryStructure() catalog.copyResources() catalog.buildSources() + if opts.verbose: + log.info("Catalog source generation complete") + recommendations = [] dp = getattr(opts, 'debug_pipeline', None) From 28d5317c8f7df8d3fddcd529d3ce1554cacdef6e Mon Sep 17 00:00:00 2001 From: GRiker Date: Fri, 29 Jan 2010 16:21:50 -0700 Subject: [PATCH 24/32] Catalog housecleaning, progress reporting fixed, CSV cleanup --- src/calibre/customize/__init__.py | 2 +- src/calibre/library/catalog.py | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 68d94bf178..42b379fa0b 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -288,7 +288,7 @@ class CatalogPlugin(Plugin): fields = list(all_fields) fields.sort() - if opts.sort_by: + if opts.sort_by and opts.sort_by in fields: fields.insert(0,fields.pop(int(fields.index(opts.sort_by)))) return fields diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 3826aa55ec..debe8487cc 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -51,18 +51,23 @@ class CSV_XML(CatalogPlugin): self.fmt = path_to_output.rpartition('.')[2] self.notification = notification - if False and opts.verbose: - log("%s:run" % self.name) - log(" path_to_output: %s" % path_to_output) - log(" Output format: %s" % self.fmt) - - # Display opts + if opts.verbose: opts_dict = vars(opts) - keys = opts_dict.keys() - keys.sort() - log(" opts:") - for key in keys: - log(" %s: %s" % (key, opts_dict[key])) + log("%s(): Generating %s" % (self.name,self.fmt)) + if opts_dict['search_text']: + log(" --search='%s'" % opts_dict['search_text']) + + if opts_dict['ids']: + log(" Book count: %d" % len(opts_dict['ids'])) + if opts_dict['search_text']: + log(" (--search ignored when a subset of the database is specified)") + + if opts_dict['fields']: + if opts_dict['fields'] == 'all': + log(" Fields: %s" % ', '.join(FIELDS[1:])) + else: + log(" Fields: %s" % opts_dict['fields']) + # If a list of ids are provided, don't use search_text if opts.ids: From bb11c80871ae629016f9338d46641a7584f4dc79 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 18:20:12 -0700 Subject: [PATCH 25/32] Fix #4730 (Updated recipe for Serbian weekly magazine NIN) --- resources/recipes/nin.recipe | 47 +++++++++++------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/resources/recipes/nin.recipe b/resources/recipes/nin.recipe index 535652b6a0..a349f0e11f 100644 --- a/resources/recipes/nin.recipe +++ b/resources/recipes/nin.recipe @@ -1,46 +1,42 @@ -#!/usr/bin/env python __license__ = 'GPL v3' -__copyright__ = '2008-2009, Darko Miletic ' +__copyright__ = '2008-2010, Darko Miletic ' ''' -nin.co.rs +www.nin.co.rs ''' import re, urllib from calibre import strftime from calibre.web.feeds.news import BasicNewsRecipe -from calibre.ebooks.BeautifulSoup import Tag class Nin(BasicNewsRecipe): title = 'NIN online' __author__ = 'Darko Miletic' - description = 'Nedeljne informativne novine' - publisher = 'NIN D.O.O.' + description = 'Nedeljne Informativne Novine' + publisher = 'NIN d.o.o.' category = 'news, politics, Serbia' no_stylesheets = True oldest_article = 15 - simultaneous_downloads = 1 - delay = 1 encoding = 'utf-8' needs_subscription = True + remove_empty_feeds = True PREFIX = 'http://www.nin.co.rs' INDEX = PREFIX + '/?change_lang=ls' LOGIN = PREFIX + '/?logout=true' use_embedded_content = False language = 'sr' - lang = 'sr-Latn-RS' - direction = 'ltr' - extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif} .artTitle{font-size: x-large; font-weight: bold} .columnhead{font-size: small; font-weight: bold}' + extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: Verdana, Lucida, sans1, sans-serif} .article_description{font-family: Verdana, Lucida, sans1, sans-serif} .artTitle{font-size: x-large; font-weight: bold; color: #900} .izjava{font-size: x-large; font-weight: bold} .columnhead{font-size: small; font-weight: bold;} img{margin-top:0.5em; margin-bottom: 0.7em} b{margin-top: 1em} ' conversion_options = { 'comment' : description , 'tags' : category , 'publisher' : publisher , 'language' : language - , 'pretty_print' : True + , 'linearize_tables' : True } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + remove_attributes = ['height','width'] def get_browser(self): br = BasicNewsRecipe.get_browser() @@ -65,34 +61,18 @@ class Nin(BasicNewsRecipe): cover_url = self.PREFIX + link_item['src'] return cover_url - def preprocess_html(self, soup): - soup.html['lang'] = self.lang - soup.html['dir' ] = self.direction - mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) - mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")]) - soup.head.insert(0,mlang) - soup.head.insert(1,mcharset) - attribs = [ 'style','font','valign' - ,'colspan','width','height' - ,'rowspan','summary','align' - ,'cellspacing','cellpadding' - ,'frames','rules','border' - ] - for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): - item.name = 'div' - for attrib in attribs: - if item.has_key(attrib): - del item[attrib] - return soup - def parse_index(self): articles = [] + count = 0 soup = self.index_to_soup(self.PREFIX) for item in soup.findAll('a',attrs={'class':'lmeninavFont'}): + count = count +1 + if self.test and count > 2: + return articles section = self.tag_to_string(item) feedlink = self.PREFIX + item['href'] feedpage = self.index_to_soup(feedlink) - self.report_progress(0, _('Fetching feed')+' %s...'%(section)) + self.report_progress(0, _('Fetching feed')+' %s...'%(section)) inarts = [] for art in feedpage.findAll('span',attrs={'class':'artTitle'}): alink = art.parent @@ -110,3 +90,4 @@ class Nin(BasicNewsRecipe): }) articles.append((section,inarts)) return articles + From a5f13fa593080c8e5a63b4cc4bdbffb1dfd968be Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 18:25:41 -0700 Subject: [PATCH 26/32] Workaround for broken XHTML in some Washington Post articles --- resources/recipes/wash_post.recipe | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/recipes/wash_post.recipe b/resources/recipes/wash_post.recipe index 5e62aa753c..a917371cec 100644 --- a/resources/recipes/wash_post.recipe +++ b/resources/recipes/wash_post.recipe @@ -46,3 +46,10 @@ class WashingtonPost(BasicNewsRecipe): div['style'] = '' return soup + def preprocess_html(self, soup): + for tag in soup.findAll('font'): + if tag.has_key('size'): + if tag['size'] == '+2': + if tag.b: + return soup + return None From f284a6c2b60b1ed16d80f62f29334ad77a2e0f8a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jan 2010 19:26:48 -0700 Subject: [PATCH 27/32] Various Danish newspapers by Darko Miletic --- resources/images/news/information_dk.png | Bin 0 -> 343 bytes resources/images/news/jp_dk.png | Bin 0 -> 609 bytes resources/images/news/politiken_dk.png | Bin 0 -> 482 bytes resources/recipes/information_dk.recipe | 50 +++++++++++++++++++++ resources/recipes/jp_dk.recipe | 50 +++++++++++++++++++++ resources/recipes/metro_montreal.recipe | 2 +- resources/recipes/nin.recipe | 3 +- resources/recipes/politiken_dk.recipe | 55 +++++++++++++++++++++++ 8 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 resources/images/news/information_dk.png create mode 100644 resources/images/news/jp_dk.png create mode 100644 resources/images/news/politiken_dk.png create mode 100644 resources/recipes/information_dk.recipe create mode 100644 resources/recipes/jp_dk.recipe create mode 100644 resources/recipes/politiken_dk.recipe diff --git a/resources/images/news/information_dk.png b/resources/images/news/information_dk.png new file mode 100644 index 0000000000000000000000000000000000000000..301e2992c700338d75149db8b43905a3f9e06ffa GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU{owBVrLgRsmeHrs=s#Dk zeNN8&v1;w#$4`D8JMp!le;+@-l~DmI|Ns9#ki3@T{1!-Y7I;J!18EO1b~~AE z2NEnRa7+i%V4E0Xj;%cmK<0IIqoUzs;I> zU=AZgU}- uD+7Z!oF|LIhYn5Ix<=1C@(=%`MShWI&gq1gq;ET}vaD(E$(U^G%QN>#p3U0w zd&?yc{o?TFtQB9M+)P-W@LV`$CDYG#gQSi*o&3vp@814>-Pf(v)vwp}{}VUab#3~) zsNaTHjLXY^$$tC5*QL5ZfmMn71nX8fH(}&~mcpOsK5ji+#ywd0Q-v zW+tCKzVIjK6VVs>9HK{b6;9l?o-m0!<;B_^EUOpZPT1ytO8ZRJ2DV+gEkzksO?x7n zLpq@|-SL97xpuW7629aQpQOk}*)_|E;a(FyM*d)js!|D|gb>$Fkn^S9N`c2aA2 z>yFIIpWyGv|5A&6M$;65ddC$W$u{kI&9eI#f|u;_lzhYZNUv7t*@IPemtxvgqjqM$ zem=ogf7!>b+Z(n0qbk^rO$_l{{jt8wqS5}fms1I!;#o6b6seZDMwFx^mZVxG7o`Fz z1|tJQ16>0{T_ej7LrW_o11l4AT?2C~1A{l7KNwLogTe~DWM4fJwEpK literal 0 HcmV?d00001 diff --git a/resources/images/news/politiken_dk.png b/resources/images/news/politiken_dk.png new file mode 100644 index 0000000000000000000000000000000000000000..66f324a8c79a560af7b081f42682e0486c1dcd07 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#O)dT^vI!POlC2^*iJs^3QES=dp~$);tMu zwPT^`Z&uz}aPPt0CBphl519&@gm#C`5O%n^QBf?}L~+*ftXXXxVXvm|N%>T_Z_oSF zHV4mWG%pUi%&>~#gO1BW(>~ktI~)!6@Em5JEqo?shJt^I^;|866&Y(86>m%|`k2@= zJwHd_>suo?Z1Bn`-?Kf9Z#;GtP;NaXnFFK+V{#@e<|91|xUVFHZWzieUd`2d= z+FyJJ4!Q%wNVUW@q9i4;B-JXpC>2OC7#SEE=o%R68d-)IT3Q(ySecmX8kk!d7`*ZP m!HA+EH$NpatrAUxsi~Eb2}Hxv#@+;=1_n=8KbLh*2~7Z$7` Date: Fri, 29 Jan 2010 23:17:17 -0700 Subject: [PATCH 28/32] Orange County Register by Lorenzo Vigentini --- resources/recipes/oc_register.recipe | 73 ++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 resources/recipes/oc_register.recipe diff --git a/resources/recipes/oc_register.recipe b/resources/recipes/oc_register.recipe new file mode 100644 index 0000000000..9a04585a3c --- /dev/null +++ b/resources/recipes/oc_register.recipe @@ -0,0 +1,73 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'Lorenzo Vigentini' +__copyright__ = '2009, Lorenzo Vigentini ' +description = 'News from the Orange county - v1.01 (29, January 2010)' + +''' +http://www.ocregister.com/ +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class ocRegister(BasicNewsRecipe): + author = 'Lorenzo Vigentini' + description = 'News from the Orange county' + + cover_url = 'http://images.onset.freedom.com/ocregister/logo.gif' + title = u'Orange County Register' + publisher = 'Orange County Register Communication' + category = 'News, finance, economy, politics' + + language = 'en' + timefmt = '[%a, %d %b, %Y]' + + oldest_article = 1 + max_articles_per_feed = 25 + use_embedded_content = False + recursion = 10 + + remove_javascript = True + no_stylesheets = True + + def print_version(self,url): + printUrl = 'http://www.ocregister.com/common/printer/view.php?db=ocregister&id=' + segments = url.split('/') + subSegments = (segments[4]).split('.') + myArticle = (subSegments[0]).replace('-', '') + myURL= printUrl + myArticle + return myURL + + keep_only_tags = [ + dict(name='div', attrs={'id':'ArticleContentWrap'}) + ] + + remove_tags = [ + dict(name='div', attrs={'class':'hideForPrint'}), + dict(name='div', attrs={'id':'ContentFooter'}) + ] + + feeds = [ + (u'News', u'http://www.ocregister.com/common/rss/rss.php?catID=18800'), + (u'Today paper', u'http://www.ocregister.com/common/rss/rss.php?catID=18976'), + (u'Business', u'http://www.ocregister.com/common/rss/rss.php?catID=18909'), + (u'Cars', u'http://www.ocregister.com/common/rss/rss.php?catID=20128'), + (u'Entertainment', u'http://www.ocregister.com/common/rss/rss.php?catID=18926'), + (u'Home', u'http://www.ocregister.com/common/rss/rss.php?catID=19142'), + (u'Life', u'http://www.ocregister.com/common/rss/rss.php?catID=18936'), + (u'Opinion', u'http://www.ocregister.com/common/rss/rss.php?catID=18963'), + (u'Sports', u'http://www.ocregister.com/common/rss/rss.php?catID=18901'), + (u'Travel', u'http://www.ocregister.com/common/rss/rss.php?catID=18959') + ] + + extra_css = ''' + h1 {color:#ff6600;font-family:Arial,Helvetica,sans-serif; font-size:20px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:20px;} + h2 {color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:16px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:16px; } + h3 {color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:15px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:15px;} + h4 {color:#333333; font-family:Arial,Helvetica,sans-serif;font-size:13px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:13px; } + h5 {color:#333333; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:11px; text-transform:uppercase;} + #articledate {color:#333333;font-family:Arial,Helvetica,sans-serif;font-size:10px; font-size-adjust:none; font-stretch:normal; font-style:italic; font-variant:normal; font-weight:bold; line-height:10px; text-decoration:none;} + #articlebyline {color:#4D4D4D;font-family:Arial,Helvetica,sans-serif;font-size:10px; font-size-adjust:none; font-stretch:normal; font-style:bold; font-variant:normal; font-weight:bold; line-height:10px; text-decoration:none;} + img {align:left;} + #topstoryhead {color:#ff6600;font-family:Arial,Helvetica,sans-serif; font-size:22px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:20px;} + ''' From 1c7d7ae2d5c34447fc6cb6fbcc751a1c1fefc2fb Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 30 Jan 2010 03:16:28 -0700 Subject: [PATCH 29/32] Fixed MOBI indexing for mixed case --- src/calibre/library/catalog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index debe8487cc..6aa664a166 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -2512,6 +2512,7 @@ class EPUB_MOBI(CatalogPlugin): for (i,word) in enumerate(title_words): # Leading numbers optionally translated to text equivalent + # Capitalize leading sort word if i==0: if self.opts.numbers_as_text and re.search('[0-9]+',word): translated.append(EPUB_MOBI.NumberToText(word).text.capitalize()) @@ -2529,7 +2530,7 @@ class EPUB_MOBI(CatalogPlugin): word = '%10.2f' % float(re.sub('[^\d\.]','.',word)) except: word = '%10.2f' % float(EPUB_MOBI.NumberToText(word).number_as_float) - translated.append(word) + translated.append(word.capitalize()) else: if re.search('[0-9]+',word): # Coerce standard-width strings for numbers From fcc99fb988adb9bf9bd7f8123ffc9104e1defd08 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 30 Jan 2010 06:46:29 -0700 Subject: [PATCH 30/32] Improved database tag set diagnostics --- src/calibre/library/catalog.py | 52 +++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index f289c9a868..db7ca7fe9d 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -940,7 +940,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLDescriptions(self): # Write each title to a separate HTML file in contentdir - self.updateProgressFullStep("Description") + self.updateProgressFullStep("Generating 'Descriptions'") for (title_num, title) in enumerate(self.booksByTitle): if False: @@ -1069,7 +1069,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByTitle(self): # Write books by title A-Z to HTML file - self.updateProgressFullStep("Books by Title") + self.updateProgressFullStep("Generating 'Titles'") soup = self.generateHTMLEmptyHeader("Books By Alpha Title") body = soup.find('body') @@ -1171,7 +1171,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByAuthor(self): # Write books by author A-Z - self.updateProgressFullStep("Books by Author") + self.updateProgressFullStep("Generating 'Authors'") friendly_name = "By Author" @@ -1301,7 +1301,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByDateAdded(self): # Write books by reverse chronological order - self.updateProgressFullStep("Recently Added") + self.updateProgressFullStep("Generating 'Recently Added'") def add_books_to_HTML(this_months_list, dtc): if len(this_months_list): @@ -1718,7 +1718,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXHeader(self): - self.updateProgressFullStep("NCX header") + self.updateProgressFullStep("Generating NCX header") header = ''' @@ -1754,7 +1754,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXDescriptions(self, tocTitle): - self.updateProgressFullStep("NCX descriptions") + self.updateProgressFullStep("Generating NCX for 'Descriptions'") # --- Construct the 'Books by Title' section --- ncx_soup = self.ncxSoup @@ -1825,7 +1825,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def generateNCXByTitle(self, tocTitle): - self.updateProgressFullStep("NCX Titles") + self.updateProgressFullStep("Generating NCX for 'Titles'") def add_to_books_by_letter(current_book_list): current_book_list = " • ".join(current_book_list) @@ -1914,7 +1914,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByAuthor(self, tocTitle): - self.updateProgressFullStep("NCX Authors") + self.updateProgressFullStep("Generating NCX for 'Authors'") def add_to_author_list(current_author_list, current_letter): current_author_list = " • ".join(current_author_list) @@ -2003,7 +2003,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByDateAdded(self, tocTitle): - self.updateProgressFullStep("NCX Recently Added") + self.updateProgressFullStep("Generating NCX for 'Recently Added'") def add_to_master_month_list(current_titles_list): book_count = len(current_titles_list) @@ -2105,7 +2105,7 @@ class EPUB_MOBI(CatalogPlugin): # Add each genre as an article # 'tag', 'file', 'authors' - self.updateProgressFullStep("NCX by Genre") + self.updateProgressFullStep("Generating NCX for Genres") @@ -2270,6 +2270,13 @@ class EPUB_MOBI(CatalogPlugin): # Remove the special marker tags from the database's tag list, # return sorted list of tags representing valid genres + def next_tag(tags): + for (i, tag) in enumerate(tags): + if i < len(tags) - 1: + yield tag + ", " + else: + yield tag + filtered_tags = [] for tag in tags: if tag[0] in self.markerTags: @@ -2293,9 +2300,16 @@ class EPUB_MOBI(CatalogPlugin): else: continue if self.verbose: - self.opts.log.info(u' %d Genre tags in database (exclude_genre: %s):' % \ + self.opts.log.info(u' %d Genre tags in database (exclude_genre: %s):' % \ (len(filtered_tags), self.opts.exclude_genre)) - self.opts.log.info(u' %s' % ', '.join(filtered_tags)) + out_buf = '' + + for tag in next_tag(filtered_tags): + out_buf += tag + if len(out_buf) > 72: + self.opts.log(u' %s' % out_buf.rstrip()) + out_buf = '' + self.opts.log(u' %s' % out_buf) return filtered_tags @@ -2633,6 +2647,8 @@ class EPUB_MOBI(CatalogPlugin): self.progressString = description self.progressInt = float((self.current_step-1)/self.total_steps) self.reporter(self.progressInt, self.progressString) + if self.opts.cli_environment: + self.opts.log(u"%3.0f%% %s" % (self.progressInt*100, self.progressString)) def updateProgressMicroStep(self, description, micro_step_pct): step_range = 100/self.total_steps @@ -2655,12 +2671,13 @@ class EPUB_MOBI(CatalogPlugin): opts.descriptionClip = 380 if op.endswith('dx') or 'kindle' not in op else 90 opts.basename = "Catalog" opts.plugin_path = self.plugin_path + opts.cli_environment = getattr(opts,'sync',True) if opts.verbose: opts_dict = vars(opts) - gui = True if 'sync' in opts_dict else False - log("%s(): Generating %s for %s in %s environment" % \ - (self.name,self.fmt,opts.output_profile, 'GUI' if gui else 'CLI')) + log("%s(): Generating %s for %s in %s environment" % + (self.name,self.fmt,opts.output_profile, + 'CLI' if opts.cli_environment else 'GUI')) if opts_dict['ids']: log(" Book count: %d" % len(opts_dict['ids'])) # Display opts @@ -2674,13 +2691,14 @@ class EPUB_MOBI(CatalogPlugin): log(" %s: %s" % (key, opts_dict[key])) # Launch the Catalog builder + if opts.verbose: + log.info("Begin generating catalog source") catalog = self.CatalogBuilder(db, opts, self, report_progress=notification) catalog.createDirectoryStructure() catalog.copyResources() catalog.buildSources() - if opts.verbose: - log.info("Catalog source generation complete") + log.info("Finished generating catalog source\n") recommendations = [] From 6d640e1ff121d7f01d78acda6b3200862102abcf Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 30 Jan 2010 06:51:19 -0700 Subject: [PATCH 31/32] Improved database tag set diagnostics --- src/calibre/library/catalog.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index db7ca7fe9d..67e360da68 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -887,7 +887,7 @@ class EPUB_MOBI(CatalogPlugin): def fetchBooksByAuthor(self): # Generate a list of titles sorted by author from the database - self.updateProgressFullStep("Sorting database by author") + self.updateProgressFullStep("Sorting database") # Sort titles case-insensitive self.booksByAuthor = sorted(self.booksByTitle, @@ -940,7 +940,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLDescriptions(self): # Write each title to a separate HTML file in contentdir - self.updateProgressFullStep("Generating 'Descriptions'") + self.updateProgressFullStep("'Descriptions'") for (title_num, title) in enumerate(self.booksByTitle): if False: @@ -1069,7 +1069,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByTitle(self): # Write books by title A-Z to HTML file - self.updateProgressFullStep("Generating 'Titles'") + self.updateProgressFullStep("'Titles'") soup = self.generateHTMLEmptyHeader("Books By Alpha Title") body = soup.find('body') @@ -1171,7 +1171,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByAuthor(self): # Write books by author A-Z - self.updateProgressFullStep("Generating 'Authors'") + self.updateProgressFullStep("'Authors'") friendly_name = "By Author" @@ -1301,7 +1301,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLByDateAdded(self): # Write books by reverse chronological order - self.updateProgressFullStep("Generating 'Recently Added'") + self.updateProgressFullStep("'Recently Added'") def add_books_to_HTML(this_months_list, dtc): if len(this_months_list): @@ -1433,7 +1433,7 @@ class EPUB_MOBI(CatalogPlugin): # Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ... # Note that special tags - ~+*[] - have already been filtered from books[] - self.updateProgressFullStep("Generating Genres") + self.updateProgressFullStep("'Genres'") # Filter out REMOVE_TAGS, sort filtered_tags = self.filterDbTags(self.db.all_tags()) @@ -1586,7 +1586,7 @@ class EPUB_MOBI(CatalogPlugin): def generateOPF(self): - self.updateProgressFullStep("Generating OPF") + self.updateProgressFullStep("Saving OPF") header = ''' @@ -1718,7 +1718,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXHeader(self): - self.updateProgressFullStep("Generating NCX header") + self.updateProgressFullStep("NCX header") header = ''' @@ -1754,7 +1754,7 @@ class EPUB_MOBI(CatalogPlugin): def generateNCXDescriptions(self, tocTitle): - self.updateProgressFullStep("Generating NCX for 'Descriptions'") + self.updateProgressFullStep("NCX 'Descriptions'") # --- Construct the 'Books by Title' section --- ncx_soup = self.ncxSoup @@ -1825,7 +1825,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def generateNCXByTitle(self, tocTitle): - self.updateProgressFullStep("Generating NCX for 'Titles'") + self.updateProgressFullStep("NCX 'Titles'") def add_to_books_by_letter(current_book_list): current_book_list = " • ".join(current_book_list) @@ -1914,7 +1914,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByAuthor(self, tocTitle): - self.updateProgressFullStep("Generating NCX for 'Authors'") + self.updateProgressFullStep("NCX 'Authors'") def add_to_author_list(current_author_list, current_letter): current_author_list = " • ".join(current_author_list) @@ -2003,7 +2003,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = soup def generateNCXByDateAdded(self, tocTitle): - self.updateProgressFullStep("Generating NCX for 'Recently Added'") + self.updateProgressFullStep("NCX 'Recently Added'") def add_to_master_month_list(current_titles_list): book_count = len(current_titles_list) @@ -2105,7 +2105,7 @@ class EPUB_MOBI(CatalogPlugin): # Add each genre as an article # 'tag', 'file', 'authors' - self.updateProgressFullStep("Generating NCX for Genres") + self.updateProgressFullStep("NCX 'Genres'") @@ -2206,7 +2206,7 @@ class EPUB_MOBI(CatalogPlugin): self.ncxSoup = ncx_soup def writeNCX(self): - self.updateProgressFullStep("Writing NCX") + self.updateProgressFullStep("Saving NCX") outfile = open("%s/%s.ncx" % (self.catalogPath, self.basename), 'w') outfile.write(self.ncxSoup.prettify()) From 18e8e22b81d9df5ef455249b7bd8d02d3e5530c6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 30 Jan 2010 10:07:09 -0700 Subject: [PATCH 32/32] Heraldo de Aragon by Lorenzo Vigentini --- resources/recipes/heraldo.recipe | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 resources/recipes/heraldo.recipe diff --git a/resources/recipes/heraldo.recipe b/resources/recipes/heraldo.recipe new file mode 100644 index 0000000000..381e97b9ce --- /dev/null +++ b/resources/recipes/heraldo.recipe @@ -0,0 +1,50 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__author__ = 'Lorenzo Vigentini' +__copyright__ = '2009, Lorenzo Vigentini ' +__description__ = 'Daily newspaper from Aragon' +__version__ = 'v1.01' +__date__ = '30, January 2010' + +''' +http://www.heraldo.es/ +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class heraldo(BasicNewsRecipe): + author = 'Lorenzo Vigentini' + description = 'Daily newspaper from Aragon' + + cover_url = 'http://www.heraldo.es/MODULOS/global/publico/interfaces/img/logo.gif' + title = u'Heraldo de Aragon' + publisher = 'OJD Nielsen' + category = 'News, politics, culture, economy, general interest' + + language = 'es' + timefmt = '[%a, %d %b, %Y]' + + oldest_article = 1 + max_articles_per_feed = 25 + + use_embedded_content = False + recursion = 10 + + remove_javascript = True + no_stylesheets = True + + keep_only_tags = [ + dict(name='div', attrs={'class':['titularNoticiaNN','textoGrisVerdanaContenidos']}) + ] + + feeds = [ + (u'Portadas ', u'http://www.heraldo.es/index.php/mod.portadas/mem.rss') + ] + extra_css = ''' + .articledate {color: gray;font-family: monospace;} + .articledescription {display: block;font-family: sans;font-size: 0.7em; text-indent: 0;} + .firma {color: #666;display: block;font-family: verdana, arial, helvetica;font-size: 1em;margin-bottom: 8px;} + .textoGrisVerdanaContenidos {color: #56595c;display: block;font-family: Verdana;font-size: 1.28571em;padding-bottom: 10px} + .titularNoticiaNN {display: block;padding-bottom: 10px;padding-left: 0;padding-right: 0;padding-top: 4px} + .titulo {color: #003066;font-family: Tahoma;font-size: 1.92857em;font-weight: bold;line-height: 1.2em} + '''