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'), + ] 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: 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) diff --git a/src/calibre/gui2/catalog/catalog_epub_mobi.py b/src/calibre/gui2/catalog/catalog_epub_mobi.py index e0c878c9e8..a09cb4119c 100644 --- a/src/calibre/gui2/catalog/catalog_epub_mobi.py +++ b/src/calibre/gui2/catalog/catalog_epub_mobi.py @@ -10,8 +10,8 @@ from calibre.ebooks.conversion.config import load_defaults from calibre.gui2 import gprefs from catalog_epub_mobi_ui import Ui_Form -from PyQt4.Qt import QCheckBox, QComboBox, QDialog, QDoubleSpinBox, QGroupBox, QLineEdit, \ - QRadioButton, QSound, QTextEdit, QWidget, SIGNAL, SLOT +from PyQt4.Qt import QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit, \ + QRadioButton, QWidget class PluginWidget(QWidget,Ui_Form): 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 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()