From 9aa26db64022d6d02e209b09f56133453e92eeb5 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Thu, 13 Jan 2011 19:26:18 +0000 Subject: [PATCH 1/5] Add syntax highlighter --- src/calibre/gui2/preferences/template_functions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/preferences/template_functions.py b/src/calibre/gui2/preferences/template_functions.py index fa15c0a973..efcf9e6379 100644 --- a/src/calibre/gui2/preferences/template_functions.py +++ b/src/calibre/gui2/preferences/template_functions.py @@ -10,6 +10,7 @@ import traceback from calibre.gui2 import error_dialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.template_functions_ui import Ui_Form +from calibre.gui2.widgets import PythonHighlighter from calibre.utils.formatter_functions import formatter_functions, compile_user_function @@ -72,6 +73,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.delete_button.setEnabled(False) self.clear_button.clicked.connect(self.clear_button_clicked) self.program.setTabStopWidth(20) + self.highlighter = PythonHighlighter(self.program.document()) def clear_button_clicked(self): self.build_function_names_box() From f2219c400b625b8ed8c6d5c86487e08d95357a4a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 13 Jan 2011 15:16:08 -0700 Subject: [PATCH 2/5] MOBI Input: Fix regression that caused images placed inside svg tags to be discarded --- src/calibre/ebooks/mobi/reader.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 57f32e7131..e07418f41c 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -632,9 +632,18 @@ class MobiReader(object): attrib['class'] = cls for tag in svg_tags: - p = tag.getparent() - if hasattr(p, 'remove'): - p.remove(tag) + images = tag.xpath('descendant::img[@src]') + parent = tag.getparent() + + if images and hasattr(parent, 'find'): + index = parent.index(tag) + for img in images: + img.getparent().remove(img) + img.tail = img.text = None + parent.insert(index, img) + + if hasattr(parent, 'remove'): + parent.remove(tag) def create_opf(self, htmlfile, guide=None, root=None): mi = getattr(self.book_header.exth, 'mi', self.embedded_mi) From 84c159472d81bfcf92129c0c8cbe224fa50bba83 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 13 Jan 2011 16:57:45 -0700 Subject: [PATCH 3/5] Yakima Herald and Tri-City Herald by Laura Gjovaag --- resources/recipes/tri_city_herald.recipe | 25 ++++++++++++++++++++++++ resources/recipes/yakima_herald.recipe | 21 ++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 resources/recipes/tri_city_herald.recipe create mode 100644 resources/recipes/yakima_herald.recipe diff --git a/resources/recipes/tri_city_herald.recipe b/resources/recipes/tri_city_herald.recipe new file mode 100644 index 0000000000..5deb5abd5b --- /dev/null +++ b/resources/recipes/tri_city_herald.recipe @@ -0,0 +1,25 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class TriCityHeraldRecipe(BasicNewsRecipe): + title = u'Tri-City Herald' + description = 'The Tri-City Herald Mid-Columbia.' + language = 'en' + __author__ = 'Laura Gjovaag' + oldest_article = 1.5 + max_articles_per_feed = 100 + no_stylesheets = True + remove_javascript = True + keep_only_tags = [ + dict(name='div', attrs={'id':'story_header'}), + dict(name='img', attrs={'class':'imageCycle'}), + dict(name='div', attrs={'id':['cycleImageCaption', 'story_body']}) + ] + remove_tags = [ + dict(name='div', attrs={'id':'story_mlt'}), + dict(name='a', attrs={'id':'commentCount'}), + dict(name=['script', 'noscript', 'style'])] + extra_css = 'h1{font: bold 140%;} #cycleImageCaption{font: monospace 60%}' + + feeds = [ + (u'Tri-City Herald Mid-Columbia', u'http://www.tri-cityherald.com/901/index.rss') + ] diff --git a/resources/recipes/yakima_herald.recipe b/resources/recipes/yakima_herald.recipe new file mode 100644 index 0000000000..d98f48c199 --- /dev/null +++ b/resources/recipes/yakima_herald.recipe @@ -0,0 +1,21 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class YakimaHeraldRepublicRecipe(BasicNewsRecipe): + title = u'Yakima Herald-Republic' + description = 'The Yakima Herald-Republic.' + language = 'en' + __author__ = 'Laura Gjovaag' + oldest_article = 1.5 + max_articles_per_feed = 100 + no_stylesheets = True + remove_javascript = True + keep_only_tags = [ + dict(name='div', attrs={'id':['searchleft', 'headline_credit']}), + dict(name='div', attrs={'class':['photo', 'cauthor', 'photocredit']}), + dict(name='div', attrs={'id':['content_body', 'footerleft']}) + ] + extra_css = '.cauthor {font: monospace 60%;} .photocredit {font: monospace 60%}' + + feeds = [ + (u'Yakima Herald Online', u'http://feeds.feedburner.com/yhronlinenews'), + ] From 00ee7975ef72aa88edea5399843f7510539b6e08 Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Thu, 13 Jan 2011 20:51:54 -0400 Subject: [PATCH 4/5] Kobo no longer sets a the DateCreated on new book purchases? --- src/calibre/devices/kobo/books.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 24ec272bb1..8d58dde892 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -27,7 +27,7 @@ class Book(Book_): self.size = size # will be set later if None - if ContentType == '6': + if ContentType == '6' and date is not None: self.datetime = time.strptime(date, "%Y-%m-%dT%H:%M:%S.%f") else: try: From 4e9070019acf13909ad373f19e56548650ef3c15 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 13 Jan 2011 19:01:48 -0700 Subject: [PATCH 5/5] Refactor the downloading social metadata message box to allow canceling. Fixes #8234 (Calibre crashes while editing metadata) --- src/calibre/gui2/dialogs/metadata_single.py | 8 ++++- src/calibre/gui2/preferences/social.py | 37 ++++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index a4e8bb6972..ede605343b 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -790,7 +790,13 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): if d.opt_get_social_metadata.isChecked(): d2 = SocialMetadata(book, self) d2.exec_() - if d2.exceptions: + if d2.timed_out: + warning_dialog(self, _('Timed out'), + _('The download of social' + ' metadata timed out, the servers are' + ' probably busy. Try again later.'), + show=True) + elif d2.exceptions: det = '\n'.join([x[0]+'\n\n'+x[-1]+'\n\n\n' for x in d2.exceptions]) warning_dialog(self, _('There were errors'), diff --git a/src/calibre/gui2/preferences/social.py b/src/calibre/gui2/preferences/social.py index ad14ea05b0..5f66f12326 100644 --- a/src/calibre/gui2/preferences/social.py +++ b/src/calibre/gui2/preferences/social.py @@ -6,16 +6,19 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import time +from threading import Thread from PyQt4.Qt import QDialog, QDialogButtonBox, Qt, QLabel, QVBoxLayout, \ - SIGNAL, QThread + QTimer from calibre.ebooks.metadata import MetaInformation -class Worker(QThread): +class Worker(Thread): - def __init__(self, mi, parent): - QThread.__init__(self, parent) + def __init__(self, mi): + Thread.__init__(self) + self.daemon = True self.mi = MetaInformation(mi) self.exceptions = [] @@ -25,10 +28,12 @@ class Worker(QThread): class SocialMetadata(QDialog): + TIMEOUT = 300 # seconds + def __init__(self, mi, parent): QDialog.__init__(self, parent) - self.bbox = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal, self) + self.bbox = QDialogButtonBox(QDialogButtonBox.Cancel, Qt.Horizontal, self) self.mi = mi self.layout = QVBoxLayout(self) self.label = QLabel(_('Downloading social metadata, please wait...'), self) @@ -36,15 +41,29 @@ class SocialMetadata(QDialog): self.layout.addWidget(self.label) self.layout.addWidget(self.bbox) - self.worker = Worker(mi, self) - self.connect(self.worker, SIGNAL('finished()'), self.accept) - self.connect(self.bbox, SIGNAL('rejected()'), self.reject) + self.worker = Worker(mi) + self.bbox.rejected.connect(self.reject) self.worker.start() + self.start_time = time.time() + self.timed_out = False + self.rejected = False + QTimer.singleShot(50, self.update) def reject(self): - self.disconnect(self.worker, SIGNAL('finished()'), self.accept) + self.rejected = True QDialog.reject(self) + def update(self): + if self.rejected: + return + if time.time() - self.start_time > self.TIMEOUT: + self.timed_out = True + self.reject() + return + if not self.worker.is_alive(): + self.accept() + QTimer.singleShot(50, self.update) + def accept(self): self.mi.tags = self.worker.mi.tags self.mi.rating = self.worker.mi.rating