From b9cb1c5e3cd0d71398e98666523d25f64f4be17c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 09:30:51 -0700 Subject: [PATCH 01/10] Fix #9205 (link name instead of link address copied when new version available) --- src/calibre/gui2/init.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 0ca58582b6..e7abbddd2a 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -141,6 +141,15 @@ class Stack(QStackedWidget): # {{{ # }}} +class UpdateLabel(QLabel): # {{{ + + def __init__(self, *args, **kwargs): + QLabel.__init__(self, *args, **kwargs) + + def contextMenuEvent(self, e): + pass +# }}} + class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): @@ -148,7 +157,7 @@ class StatusBar(QStatusBar): # {{{ self.default_message = __appname__ + ' ' + _('version') + ' ' + \ self.get_version() + ' ' + _('created by Kovid Goyal') self.device_string = '' - self.update_label = QLabel('') + self.update_label = UpdateLabel('') self.addPermanentWidget(self.update_label) self.update_label.setVisible(False) self._font = QFont() From 01ad1455a95c745607a856d24a1e974e246b173c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 09:54:14 -0700 Subject: [PATCH 02/10] LWN Weekly by David Cavalca --- resources/images/news/lwn_weekly.png | Bin 0 -> 387 bytes resources/recipes/lwn_weekly.recipe | 104 +++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 resources/images/news/lwn_weekly.png create mode 100644 resources/recipes/lwn_weekly.recipe diff --git a/resources/images/news/lwn_weekly.png b/resources/images/news/lwn_weekly.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc654add94f62e61f9b5bb4d957354f6c088a91 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VRoHd>qOPA-)FAPy?S@$D%V}B z>Vj(~`%fwXBoofn5XDu#PIE#^}W>OYm7@S zo$=pVUJrCOgKCLuL`h0wNvc(HQ7VvPFfuT()HO8JH82Y?u&^>PvNAH&H88g_Fjy^c ol^;bzZhlH;S|y4GBP&A_D+4o#hIs#-pFmc4y85}Sb4q9e07u7#3IG5A literal 0 HcmV?d00001 diff --git a/resources/recipes/lwn_weekly.recipe b/resources/recipes/lwn_weekly.recipe new file mode 100644 index 0000000000..28ee35802a --- /dev/null +++ b/resources/recipes/lwn_weekly.recipe @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2011, Davide Cavalca ' +''' +lwn.net +''' + +from calibre.web.feeds.news import BasicNewsRecipe +import re + +class WeeklyLWN(BasicNewsRecipe): + title = 'LWN.net Weekly Edition' + description = 'Weekly summary of what has happened in the free software world.' + __author__ = 'Davide Cavalca' + language = 'en' + + cover_url = 'http://lwn.net/images/lcorner.png' + #masthead_url = 'http://lwn.net/images/lcorner.png' + publication_type = 'magazine' + + remove_tags_before = dict(attrs={'class':'PageHeadline'}) + remove_tags_after = dict(attrs={'class':'ArticleText'}) + remove_tags = [dict(name=['h2', 'form'])] + + conversion_options = { 'linearize_tables' : True } + + oldest_article = 7.0 + needs_subscription = 'optional' + + def get_browser(self): + br = BasicNewsRecipe.get_browser() + if self.username is not None and self.password is not None: + br.open('https://lwn.net/login') + br.select_form(name='loginform') + br['Username'] = self.username + br['Password'] = self.password + br.submit() + return br + + def parse_index(self): + if self.username is not None and self.password is not None: + index_url = 'http://lwn.net/current/bigpage' + else: + index_url = 'http://lwn.net/free/bigpage' + soup = self.index_to_soup(index_url) + body = soup.body + + articles = {} + ans = [] + url_re = re.compile('^http://lwn.net/Articles/') + + while True: + tag_title = body.findNext(name='p', attrs={'class':'SummaryHL'}) + if tag_title == None: + break + + tag_section = tag_title.findPrevious(name='p', attrs={'class':'Cat1HL'}) + if tag_section == None: + section = 'Front Page' + else: + section = tag_section.string + + tag_section2 = tag_title.findPrevious(name='p', attrs={'class':'Cat2HL'}) + if tag_section2 != None: + if tag_section2.findPrevious(name='p', attrs={'class':'Cat1HL'}) == tag_section: + section = "%s: %s" %(section, tag_section2.string) + + if section not in articles.keys(): + articles[section] = [] + if section not in ans: + ans.append(section) + + body = tag_title + while True: + tag_url = body.findNext(name='a', attrs={'href':url_re}) + if tag_url == None: + break + body = tag_url + if tag_url.string == None: + continue + elif tag_url.string == 'Full Story': + break + elif tag_url.string.startswith('Comments ('): + break + else: + continue + + if tag_url == None: + break + + article = dict( + title=tag_title.string, + url=tag_url['href'].split('#')[0], + description='', content='', date='') + articles[section].append(article) + + ans = [(key, articles[key]) for key in ans if articles.has_key(key)] + if not ans: + raise Exception('Could not find any articles.') + + return ans + +# vim: expandtab:ts=4:sw=4 From 27412b5b5c7a4926c487895a048566cbffc1beae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 11:43:00 -0700 Subject: [PATCH 03/10] Conversion pipeline: Fix regression in 0.7.46 that caused loss of some CSS information when converting HTML produced by Microsoft Word. Also remove empty tags from microsoft namespaces when parsing HTML --- src/calibre/ebooks/conversion/preprocess.py | 24 +++++++++++++++------ src/calibre/ebooks/oeb/base.py | 23 ++++++++++++++++++++ src/calibre/ebooks/oeb/stylizer.py | 3 +++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 5f6402f746..a1d5fa94d8 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -265,16 +265,28 @@ class CSSPreProcessor(object): PAGE_PAT = re.compile(r'@page[^{]*?{[^}]*?}') # Remove some of the broken CSS Microsoft products - # create, slightly dangerous as it removes to end of line - # rather than semi-colon - MS_PAT = re.compile(r'^\s*(mso-|panose-).+?$', - re.MULTILINE|re.IGNORECASE) + # create + MS_PAT = re.compile(r''' + (?P^|;|\{)\s* # The end of the previous rule or block start + (%s).+? # The invalid selectors + (?P$|;|\}) # The end of the declaration + '''%'mso-|panose-|text-underline|tab-interval', + re.MULTILINE|re.IGNORECASE|re.VERBOSE) + + def ms_sub(self, match): + end = match.group('end') + try: + start = match.group('start') + except: + start = '' + if end == ';': + end = '' + return start + end def __call__(self, data, add_namespace=False): from calibre.ebooks.oeb.base import XHTML_CSS_NAMESPACE data = self.PAGE_PAT.sub('', data) - if '\n' in data: - data = self.MS_PAT.sub('', data) + data = self.MS_PAT.sub(self.ms_sub, data) if not add_namespace: return data ans, namespaced = [], False diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index ccc452f1f8..7e99916fc3 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -827,6 +827,24 @@ class Manifest(object): return None return etree.fromstring(data, parser=RECOVER_PARSER) + def clean_word_doc(self, data): + prefixes = [] + for match in re.finditer(r'xmlns:(\S+?)=".*?microsoft.*?"', data): + prefixes.append(match.group(1)) + if prefixes: + self.oeb.log.warn('Found microsoft markup, cleaning...') + # Remove empty tags as they are not rendered by browsers + # but can become renderable HTML tags like

if the + # document is parsed by an HTML parser + pat = re.compile( + r'<(%s):([a-zA-Z0-9]+)[^>/]*?>'%('|'.join(prefixes)), + re.DOTALL) + data = pat.sub('', data) + pat = re.compile( + r'<(%s):([a-zA-Z0-9]+)[^>/]*?/>'%('|'.join(prefixes))) + data = pat.sub('', data) + return data + def _parse_xhtml(self, data): self.oeb.log.debug('Parsing', self.href, '...') # Convert to Unicode and normalize line endings @@ -884,6 +902,10 @@ class Manifest(object): except etree.XMLSyntaxError: data = etree.fromstring(data, parser=RECOVER_PARSER) return data + try: + data = self.clean_word_doc(data) + except: + pass data = first_pass(data) # Handle weird (non-HTML/fragment) files @@ -907,6 +929,7 @@ class Manifest(object): parent.append(child) data = nroot + # Force into the XHTML namespace if not namespace(data.tag): self.oeb.log.warn('Forcing', self.href, 'into XHTML namespace') diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index 849d161228..efc8fe1463 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -423,6 +423,7 @@ class Stylizer(object): class Style(object): UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$') + MS_PAT = re.compile(r'^\s*(mso-|panose-|text-underline|tab-interval)') def __init__(self, element, stylizer): self._element = element @@ -447,6 +448,8 @@ class Style(object): return css = attrib['style'].split(';') css = filter(None, (x.strip() for x in css)) + css = [x.strip() for x in css] + css = [x for x in css if self.MS_PAT.match(x) is None] try: style = CSSStyleDeclaration('; '.join(css)) except CSSSyntaxError: From 89f2da224d611c125a9940fa1089859d8b6a7844 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 12:45:17 -0700 Subject: [PATCH 04/10] Allow drag drop of images to change cover in book details window. Fixes #9226 (Drag/drop covers into book information dialog) --- src/calibre/gui2/dialogs/book_info.py | 19 ++++- src/calibre/gui2/dialogs/book_info.ui | 7 +- src/calibre/gui2/widgets.py | 111 ++++++++++++++++---------- 3 files changed, 91 insertions(+), 46 deletions(-) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 4da897920c..523974e4f2 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en' import textwrap, os, re from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \ - QDialog, QPixmap, QGraphicsScene, QIcon, QSize + QDialog, QPixmap, QIcon, QSize from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo from calibre.gui2 import dynamic, open_local_file, open_url @@ -14,12 +14,14 @@ from calibre import fit_image from calibre.library.comments import comments_to_html from calibre.utils.icu import sort_key + class BookInfo(QDialog, Ui_BookInfo): def __init__(self, parent, view, row, view_func): QDialog.__init__(self, parent) Ui_BookInfo.__init__(self) self.setupUi(self) + self.gui = parent self.cover_pixmap = None self.comments.sizeHint = self.comments_size_hint self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks) @@ -38,11 +40,22 @@ class BookInfo(QDialog, Ui_BookInfo): self.connect(self.text, SIGNAL('linkActivated(QString)'), self.open_book_path) self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.cover.resizeEvent = self.cover_view_resized + self.cover.cover_changed.connect(self.cover_changed) desktop = QCoreApplication.instance().desktop() screen_height = desktop.availableGeometry().height() - 100 self.resize(self.size().width(), screen_height) + def cover_changed(self, data): + if self.current_row is not None: + id_ = self.view.model().id(self.current_row) + self.view.model().db.set_cover(id_, data) + if self.gui.cover_flow: + self.gui.cover_flow.dataChanged() + ci = self.view.currentIndex() + if ci.isValid(): + self.view.model().current_changed(ci, ci) + def link_clicked(self, url): open_url(url) @@ -83,7 +96,6 @@ class BookInfo(QDialog, Ui_BookInfo): if self.cover_pixmap is None: return self.setWindowIcon(QIcon(self.cover_pixmap)) - self.scene = QGraphicsScene() pixmap = self.cover_pixmap if self.fit_cover.isChecked(): scaled, new_width, new_height = fit_image(pixmap.width(), @@ -92,8 +104,7 @@ class BookInfo(QDialog, Ui_BookInfo): if scaled: pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) - self.scene.addPixmap(pixmap) - self.cover.setScene(self.scene) + self.cover.set_pixmap(pixmap) def refresh(self, row): if isinstance(row, QModelIndex): diff --git a/src/calibre/gui2/dialogs/book_info.ui b/src/calibre/gui2/dialogs/book_info.ui index 2902a2c917..412126a610 100644 --- a/src/calibre/gui2/dialogs/book_info.ui +++ b/src/calibre/gui2/dialogs/book_info.ui @@ -25,7 +25,7 @@ - + @@ -115,6 +115,11 @@ QWidget

QtWebKit/QWebView
+ + CoverView + QGraphicsView +
calibre/gui2/widgets.h
+
diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 3622cc6c39..5cacf32bb2 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -11,9 +11,9 @@ from PyQt4.Qt import QIcon, QFont, QLabel, QListWidget, QAction, \ QPixmap, QSplitterHandle, QToolButton, \ QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, \ QRegExp, QSettings, QSize, QSplitter, \ - QPainter, QLineEdit, QComboBox, QPen, \ + QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, \ QMenu, QStringListModel, QCompleter, QStringList, \ - QTimer, QRect, QFontDatabase + QTimer, QRect, QFontDatabase, QGraphicsView from calibre.gui2 import NONE, error_dialog, pixmap_to_data, gprefs from calibre.gui2.filename_pattern_ui import Ui_Form @@ -181,22 +181,16 @@ class FormatList(QListWidget): else: return QListWidget.keyPressEvent(self, event) - -class ImageView(QWidget): - - BORDER_WIDTH = 1 - cover_changed = pyqtSignal(object) - - def __init__(self, parent=None): - QWidget.__init__(self, parent) - self._pixmap = QPixmap(self) - self.setMinimumSize(QSize(150, 200)) - self.setAcceptDrops(True) - self.draw_border = True - - # Drag 'n drop {{{ +class ImageDropMixin(object): # {{{ + ''' + Adds support for dropping images onto widgets and a contect menu for + copy/pasting images. + ''' DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS + def __init__(self): + self.setAcceptDrops(True) + @classmethod def paths_from_event(cls, event): ''' @@ -223,14 +217,58 @@ class ImageView(QWidget): pmap = QPixmap() pmap.load(path) if not pmap.isNull(): - self.setPixmap(pmap) + self.handle_image_drop(path, pmap) event.accept() - self.cover_changed.emit(open(path, 'rb').read()) break + def handle_image_drop(self, path, pmap): + self.set_pixmap(pmap) + self.cover_changed.emit(open(path, 'rb').read()) + def dragMoveEvent(self, event): event.acceptProposedAction() - # }}} + + def get_pixmap(self): + return self.pixmap() + + def set_pixmap(self, pmap): + self.setPixmap(pmap) + + def contextMenuEvent(self, ev): + cm = QMenu(self) + copy = cm.addAction(_('Copy Image')) + paste = cm.addAction(_('Paste Image')) + if not QApplication.instance().clipboard().mimeData().hasImage(): + paste.setEnabled(False) + copy.triggered.connect(self.copy_to_clipboard) + paste.triggered.connect(self.paste_from_clipboard) + cm.exec_(ev.globalPos()) + + def copy_to_clipboard(self): + QApplication.instance().clipboard().setPixmap(self.get_pixmap()) + + def paste_from_clipboard(self): + cb = QApplication.instance().clipboard() + pmap = cb.pixmap() + if pmap.isNull() and cb.supportsSelection(): + pmap = cb.pixmap(cb.Selection) + if not pmap.isNull(): + self.set_pixmap(pmap) + self.cover_changed.emit( + pixmap_to_data(pmap)) +# }}} + +class ImageView(QWidget, ImageDropMixin): + + BORDER_WIDTH = 1 + cover_changed = pyqtSignal(object) + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self._pixmap = QPixmap(self) + self.setMinimumSize(QSize(150, 200)) + ImageDropMixin.__init__(self) + self.draw_border = True def setPixmap(self, pixmap): if not isinstance(pixmap, QPixmap): @@ -272,32 +310,23 @@ class ImageView(QWidget): p.drawRect(target) p.end() +class CoverView(QGraphicsView, ImageDropMixin): - # Clipboard copy/paste # {{{ - def contextMenuEvent(self, ev): - cm = QMenu(self) - copy = cm.addAction(_('Copy Image')) - paste = cm.addAction(_('Paste Image')) - if not QApplication.instance().clipboard().mimeData().hasImage(): - paste.setEnabled(False) - copy.triggered.connect(self.copy_to_clipboard) - paste.triggered.connect(self.paste_from_clipboard) - cm.exec_(ev.globalPos()) + cover_changed = pyqtSignal(object) - def copy_to_clipboard(self): - QApplication.instance().clipboard().setPixmap(self.pixmap()) + def __init__(self, *args, **kwargs): + QGraphicsView.__init__(self, *args, **kwargs) + ImageDropMixin.__init__(self) - def paste_from_clipboard(self): - cb = QApplication.instance().clipboard() - pmap = cb.pixmap() - if pmap.isNull() and cb.supportsSelection(): - pmap = cb.pixmap(cb.Selection) - if not pmap.isNull(): - self.setPixmap(pmap) - self.cover_changed.emit( - pixmap_to_data(pmap)) - # }}} + def get_pixmap(self): + for item in self.scene().items(): + if hasattr(item, 'pixmap'): + return item.pixmap() + def set_pixmap(self, pmap): + self.scene = QGraphicsScene() + self.scene.addPixmap(pmap) + self.setScene(self.scene) class FontFamilyModel(QAbstractListModel): From fd098711177d318319150d65f1d9076aa4307acd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 12:57:12 -0700 Subject: [PATCH 05/10] ... --- src/calibre/gui2/dialogs/book_info.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 523974e4f2..e860579fdf 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -55,6 +55,10 @@ class BookInfo(QDialog, Ui_BookInfo): ci = self.view.currentIndex() if ci.isValid(): self.view.model().current_changed(ci, ci) + self.cover_pixmap = QPixmap() + self.cover_pixmap.loadFromData(data) + if self.fit_cover.isChecked(): + self.resize_cover() def link_clicked(self, url): open_url(url) From cb7814dfd5385aa98d7e083ff7bb4794817a5f84 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 14:17:14 -0700 Subject: [PATCH 06/10] Helsingin Sanomat by oneillpt --- resources/recipes/helsingin_sanomat.recipe | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 resources/recipes/helsingin_sanomat.recipe diff --git a/resources/recipes/helsingin_sanomat.recipe b/resources/recipes/helsingin_sanomat.recipe new file mode 100644 index 0000000000..6099a1fda8 --- /dev/null +++ b/resources/recipes/helsingin_sanomat.recipe @@ -0,0 +1,31 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1298137661(BasicNewsRecipe): + title = u'Helsingin Sanomat' + __author__ = 'oneillpt' + language = 'fi' + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + remove_javascript = True + conversion_options = { + 'linearize_tables' : True + } + remove_tags = [ + dict(name='a', attrs={'id':'articleCommentUrl'}), + dict(name='p', attrs={'class':'newsSummary'}), + dict(name='div', attrs={'class':'headerTools'}) + ] + + feeds = [(u'Uutiset - HS.fi', u'http://www.hs.fi/uutiset/rss/'), (u'Politiikka - HS.fi', u'http://www.hs.fi/politiikka/rss/'), + (u'Ulkomaat - HS.fi', u'http://www.hs.fi/ulkomaat/rss/'), (u'Kulttuuri - HS.fi', u'http://www.hs.fi/kulttuuri/rss/'), + (u'Kirjat - HS.fi', u'http://www.hs.fi/kulttuuri/kirjat/rss/'), (u'Elokuvat - HS.fi', u'http://www.hs.fi/kulttuuri/elokuvat/rss/') + ] + + def print_version(self, url): + j = url.rfind("/") + s = url[j:] + i = s.rfind("?ref=rss") + if i > 0: + s = s[:i] + return "http://www.hs.fi/tulosta" + s From ce2f9396b80550c4538382deb4ee0c4c4ec1df02 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 17:45:30 -0700 Subject: [PATCH 07/10] Fix Austin Statesman --- resources/recipes/statesman.recipe | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/resources/recipes/statesman.recipe b/resources/recipes/statesman.recipe index 1bbf94fa5b..727df2ae61 100644 --- a/resources/recipes/statesman.recipe +++ b/resources/recipes/statesman.recipe @@ -10,12 +10,14 @@ class AdvancedUserRecipe1278049615(BasicNewsRecipe): max_articles_per_feed = 100 - feeds = [(u'News', u'http://www.statesman.com/section-rss.do?source=news&includeSubSections=true'), - (u'Business', u'http://www.statesman.com/section-rss.do?source=business&includeSubSections=true'), - (u'Life', u'http://www.statesman.com/section-rss.do?source=life&includesubsection=true'), - (u'Editorial', u'http://www.statesman.com/section-rss.do?source=opinion&includesubsections=true'), - (u'Sports', u'http://www.statesman.com/section-rss.do?source=sports&includeSubSections=true') - ] + feeds = [(u'News', + u'http://www.statesman.com/section-rss.do?source=news&includeSubSections=true'), + (u'Local', u'http://www.statesman.com/section-rss.do?source=local&includeSubSections=true'), + (u'Business', u'http://www.statesman.com/section-rss.do?source=business&includeSubSections=true'), + (u'Life', u'http://www.statesman.com/section-rss.do?source=life&includesubsection=true'), + (u'Editorial', u'http://www.statesman.com/section-rss.do?source=opinion&includesubsections=true'), + (u'Sports', u'http://www.statesman.com/section-rss.do?source=sports&includeSubSections=true') + ] masthead_url = "http://www.statesman.com/images/cmg-logo.gif" #temp_files = [] #articles_are_obfuscated = True @@ -28,8 +30,11 @@ class AdvancedUserRecipe1278049615(BasicNewsRecipe): conversion_options = {'linearize_tables':True} remove_tags = [ dict(name='div', attrs={'id':'cxArticleOptions'}), + {'class':['perma', 'comments', 'trail', 'share-buttons', + 'toggle_show_on']}, ] keep_only_tags = [ - dict(name='div', attrs={'class':'cxArticleHeader'}), - dict(name='div', attrs={'id':'cxArticleBodyText'}), + dict(name='div', attrs={'class':'cxArticleHeader'}), + dict(name='div', attrs={'id':['cxArticleBodyText', + 'content']}), ] From b265fbc395415e8bed9a8e51b28f10eee07ee10e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 17:56:34 -0700 Subject: [PATCH 08/10] Fix MacWorld --- resources/recipes/mac_world.recipe | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/resources/recipes/mac_world.recipe b/resources/recipes/mac_world.recipe index bf1403820a..486aa9cb87 100644 --- a/resources/recipes/mac_world.recipe +++ b/resources/recipes/mac_world.recipe @@ -11,7 +11,6 @@ http://www.macworld.co.uk/ ''' from calibre.web.feeds.news import BasicNewsRecipe -from calibre.ptempfile import PersistentTemporaryFile temp_files = [] articles_are_obfuscated = True @@ -36,26 +35,17 @@ class macWorld(BasicNewsRecipe): remove_javascript = True no_stylesheets = True - def get_obfuscated_article(self, url): - br = self.get_browser() - br.open(url+'&print') - - response = br.follow_link(url, nr = 0) - html = response.read() - - self.temp_files.append(PersistentTemporaryFile('_fa.html')) - self.temp_files[-1].write(html) - self.temp_files[-1].close() - return self.temp_files[-1].name - keep_only_tags = [ - dict(name='div', attrs={'id':'article'}) + dict(name='div', attrs={'id':'content'}) ] remove_tags = [ - dict(name='div', attrs={'class':['toolBar','mac_tags','toolBar btmTools','textAds']}), + {'class':['toolBar','mac_tags','toolBar btmTools','textAds']}, dict(name='p', attrs={'class':'breadcrumbs'}), - dict(name='div', attrs={'id':['breadcrumb','sidebar','comments']}) + dict(id=['breadcrumb','sidebar','comments','topContentWrapper', + 'rightColumn', 'aboveFootPromo', 'storyCarousel']), + {'class':lambda x: x and ('tools' in x or 'toolBar' + in x)} ] From ab339078518c04b91b2edf06d134316baf9346a0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 19:15:21 -0700 Subject: [PATCH 09/10] Fix #9235 (Recipe Not Working-The Seattle Times) --- resources/recipes/seattle_times.recipe | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/resources/recipes/seattle_times.recipe b/resources/recipes/seattle_times.recipe index cd7f96fc8b..1d72df2991 100644 --- a/resources/recipes/seattle_times.recipe +++ b/resources/recipes/seattle_times.recipe @@ -69,12 +69,16 @@ class SeattleTimes(BasicNewsRecipe): u'http://seattletimes.nwsource.com/rss/mostreadarticles.xml'), ] + keep_only_tags = [dict(id='content')] remove_tags = [ - dict(name=['object','link','script']) - ,dict(name='p', attrs={'class':'permission'}) + dict(name=['object','link','script']), + {'class':['permission', 'note', 'bottomtools', + 'homedelivery']}, + dict(id=["rightcolumn", 'footer', 'adbottom']), ] def print_version(self, url): + return url start_url, sep, rest_url = url.rpartition('_') rurl, rsep, article_id = start_url.rpartition('/') return u'http://seattletimes.nwsource.com/cgi-bin/PrintStory.pl?document_id=' + article_id From 4336d74ee93aa1458b49b4da2215bdf8a0e459d3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Mar 2011 23:37:54 -0700 Subject: [PATCH 10/10] Fix bug that caused wrong books to be deleted from library if you choose delete from library and device while the library is sorted by the On device column --- resources/recipes/swiatkindle.recipe | 1 + src/calibre/gui2/actions/delete.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/recipes/swiatkindle.recipe b/resources/recipes/swiatkindle.recipe index a6bf225294..9847d1359e 100644 --- a/resources/recipes/swiatkindle.recipe +++ b/resources/recipes/swiatkindle.recipe @@ -7,6 +7,7 @@ swiatczytnikow.pl ''' import re +from calibre.web.feeds.news import BasicNewsRecipe class swiatczytnikow(BasicNewsRecipe): title = u'Swiat Czytnikow' diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index 9bc43f580b..a1fddd84b8 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -19,11 +19,11 @@ single_shot = partial(QTimer.singleShot, 10) class MultiDeleter(QObject): - def __init__(self, gui, rows, callback): + def __init__(self, gui, ids, callback): from calibre.gui2.dialogs.progress import ProgressDialog QObject.__init__(self, gui) self.model = gui.library_view.model() - self.ids = list(map(self.model.id, rows)) + self.ids = ids self.gui = gui self.failures = [] self.deleted_ids = [] @@ -231,6 +231,7 @@ class DeleteAction(InterfaceAction): return # Library view is visible. if self.gui.stack.currentIndex() == 0: + to_delete_ids = [view.model().id(r) for r in rows] # Ask the user if they want to delete the book from the library or device if it is in both. if self.gui.device_manager.is_device_connected: on_device = False @@ -264,10 +265,10 @@ class DeleteAction(InterfaceAction): if ci.isValid(): row = ci.row() if len(rows) < 5: - ids_deleted = view.model().delete_books(rows) - self.library_ids_deleted(ids_deleted, row) + view.model().delete_books_by_id(to_delete_ids) + self.library_ids_deleted(to_delete_ids, row) else: - self.__md = MultiDeleter(self.gui, rows, + self.__md = MultiDeleter(self.gui, to_delete_ids, partial(self.library_ids_deleted, current_row=row)) # Device view is visible. else: