From 0e18837cb6a1baa7f2219d2ca8ec409a66508105 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Wed, 5 Jan 2011 23:17:38 +0900 Subject: [PATCH 1/7] fix encoding, reflect change of the site --- resources/recipes/cnetjapan.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/recipes/cnetjapan.recipe b/resources/recipes/cnetjapan.recipe index 1058b90401..b57bce5b97 100644 --- a/resources/recipes/cnetjapan.recipe +++ b/resources/recipes/cnetjapan.recipe @@ -11,7 +11,7 @@ class CNetJapan(BasicNewsRecipe): (u'CNet Blog', u'http://feed.japan.cnet.com/rss/blog/index.rdf') ] language = 'ja' - encoding = 'Shift_JIS' + encoding = 'utf-8' remove_javascript = True preprocess_regexps = [ From 03f70c156c7d557d61db1e348f11bb2a997d90e1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Jan 2011 10:44:09 -0700 Subject: [PATCH 2/7] RTF Input: Fix regression that broke the Preprocess HTML option --- src/calibre/ebooks/conversion/utils.py | 6 +++--- src/calibre/ebooks/rtf/input.py | 2 +- src/calibre/ebooks/txt/input.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index 52d1bcc619..dac93fa2e2 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -191,15 +191,15 @@ class PreProcessor(object): blanklines = "\s*(?P<(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*\s*)\s*){0,3}\s*" line_opening = "<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" txt_line_wrap = u"((\u0020|\u0009)*\n){1,4}" - + unwrap_regex = lookahead+line_ending+blanklines+line_opening if format == 'txt': unwrap_regex = lookahead+txt_line_wrap - + unwrap = re.compile(u"%s" % unwrap_regex, re.UNICODE) content = unwrap.sub(' ', content) return content - + def __call__(self, html): self.log("********* Preprocessing HTML *********") diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py index 8c7561f68c..5154373eda 100644 --- a/src/calibre/ebooks/rtf/input.py +++ b/src/calibre/ebooks/rtf/input.py @@ -296,7 +296,7 @@ class RTFInput(InputFormatPlugin): u'

\u00a0

\n'.encode('utf-8'), res) if self.opts.preprocess_html: preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None)) - res = preprocessor(res) + res = preprocessor(res.decode('utf-8')).encode('utf-8') f.write(res) self.write_inline_css(inline_class, border_styles) stream.seek(0) diff --git a/src/calibre/ebooks/txt/input.py b/src/calibre/ebooks/txt/input.py index 3957391494..aaff8b55c0 100644 --- a/src/calibre/ebooks/txt/input.py +++ b/src/calibre/ebooks/txt/input.py @@ -53,7 +53,7 @@ class TXTInput(InputFormatPlugin): def convert(self, stream, options, file_ext, log, accelerators): log.debug('Reading text from file...') - + txt = stream.read() # Get the encoding of the document. if options.input_encoding: @@ -80,7 +80,7 @@ class TXTInput(InputFormatPlugin): # Get length for hyphen removal and punctuation unwrap docanalysis = DocAnalysis('txt', txt) length = docanalysis.line_length(.5) - + if options.formatting_type == 'auto': options.formatting_type = detect_formatting_type(txt) @@ -122,7 +122,7 @@ class TXTInput(InputFormatPlugin): txt = preprocessor.punctuation_unwrap(length, txt, 'txt') flow_size = getattr(options, 'flow_size', 0) - + if options.formatting_type == 'heuristic': html = convert_heuristic(txt, epub_split_size_kb=flow_size) else: From 27ab085fc6db7521c55792a5ebc7a16790ac6431 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Jan 2011 11:26:09 -0700 Subject: [PATCH 3/7] Tyzden by zemiak. Fixes #405 (New news feed) --- resources/recipes/tyzden.recipe | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 resources/recipes/tyzden.recipe diff --git a/resources/recipes/tyzden.recipe b/resources/recipes/tyzden.recipe new file mode 100644 index 0000000000..c206244ff6 --- /dev/null +++ b/resources/recipes/tyzden.recipe @@ -0,0 +1,80 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2011, Miroslav Vasko zemiak@gmail.com' + +''' +.tyzden, a weekly news magazine (a week old issue) +''' +from calibre import strftime +from calibre.web.feeds.news import BasicNewsRecipe +from datetime import date +import re + +class TyzdenRecipe(BasicNewsRecipe): + __license__ = 'GPL v3' + __author__ = 'zemiak' + language = 'sk' + version = 1 + + publisher = u'www.tyzden.sk' + category = u'Magazine' + description = u'A conservative weekly magazine. The latest free issue' + + today = date.today() + iso = today.isocalendar() + year = iso[0] + weeknum = iso[1] + + if (weeknum > 1): + weeknum -= 1 + + title = u'.tyzden ' + str(weeknum) + '/' + str(year) + + base_url_path = 'http://www.tyzden.sk/casopis/' + str(year) + '/' + str(weeknum) + base_url = base_url_path + '.html' + + oldest_article = 20 + max_articles_per_feed = 100 + remove_javascript = True + + use_embedded_content = False + no_stylesheets = True + + keep_only_tags = [] + keep_only_tags.append(dict(name = 'h1')) + keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_area top_nofoto'})) + keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_block'})) + + remove_tags_after = [dict(name = 'div', attrs = {'class': 'text_block'})] + + def find_sections(self): + soup = self.index_to_soup(self.base_url) + # find cover pic + imgdiv = soup.find('div', attrs = {'class': 'foto'}) + if imgdiv is not None: + img = imgdiv.find('img') + if img is not None: + self.cover_url = 'http://www.tyzden.sk/' + img['src'] + # end find cover pic + + for s in soup.findAll('a', attrs={'href': re.compile(r'rubrika/.*')}): + yield (self.tag_to_string(s), s) + + def find_articles(self, soup): + for art in soup.findAllNext('a'): + if (not art['href'].startswith('casopis/')): + break; + + url = art['href'] + title = self.tag_to_string(art) + yield { + 'title': title, 'url':self.base_url_path + '/' + url, 'description':title, + 'date' : strftime('%a, %d %b'), + } + + def parse_index(self): + feeds = [] + for title, soup in self.find_sections(): + feeds.append((title, list(self.find_articles(soup)))) + + return feeds From 6dc9142043089434c26c8887001b32c779f74a56 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Jan 2011 11:40:14 -0700 Subject: [PATCH 4/7] Fix #8040 (Device Support for Archos 70) --- src/calibre/devices/android/driver.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 73c930778e..5a82882dfa 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -53,6 +53,9 @@ class ANDROID(USBMS): # LG 0x1004 : { 0x61cc : [0x100] }, + # Archos + 0x0e79 : { 0x1420 : [0x0216]}, + } EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books'] EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to ' @@ -61,18 +64,19 @@ class ANDROID(USBMS): EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN) VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', - 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE'] + 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS'] WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE', - 'SGH-T849', '_MB300'] + 'SGH-T849', '_MB300', 'A70S'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', - 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD'] + 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', + 'A70S'] - OSX_MAIN_MEM = 'HTC Android Phone Media' + OSX_MAIN_MEM = 'Android Device Main Memory' - MAIN_MEMORY_VOLUME_LABEL = 'Android Phone Internal Memory' + MAIN_MEMORY_VOLUME_LABEL = 'Android Device Main Memory' SUPPORTS_SUB_DIRS = True From 8796a60db0f183b75f9d662d5cde481a9827db55 Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 10 Jan 2011 18:42:39 -0500 Subject: [PATCH 5/7] TXT Output: Write hr as scene break marker. --- src/calibre/ebooks/txt/txtml.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/calibre/ebooks/txt/txtml.py b/src/calibre/ebooks/txt/txtml.py index 786f50824d..00992a8612 100644 --- a/src/calibre/ebooks/txt/txtml.py +++ b/src/calibre/ebooks/txt/txtml.py @@ -218,6 +218,10 @@ class TXTMLizer(object): if tag in SPACE_TAGS: text.append(u' ') + + # Scene breaks. + if tag == 'hr': + text.append('\n\n* * *\n\n') # Process tags that contain text. if hasattr(elem, 'text') and elem.text: From 1468e17c1791b472e2e7a4626e25f3c2fa5517da Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 11 Jan 2011 08:35:46 -0700 Subject: [PATCH 6/7] ... --- 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 13469f5622..55abae0392 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -279,7 +279,7 @@ class Document(QWebPage): # {{{ @pyqtSignature("") def init_hyphenate(self): - if self.hyphenate: + if self.hyphenate and getattr(self, 'loaded_lang', ''): self.javascript('do_hyphenation("%s")'%self.loaded_lang) def after_load(self): From 50dc7b1f09dcd18f2b70c54571ce2b4f2e3bd008 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 11 Jan 2011 09:30:22 -0700 Subject: [PATCH 7/7] Plugin handling cleanups and warning message --- src/calibre/gui2/preferences/plugins.py | 34 +++++++++----- src/calibre/gui2/preferences/plugins.ui | 60 ++----------------------- src/calibre/gui2/ui.py | 10 ++++- 3 files changed, 34 insertions(+), 70 deletions(-) diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index 2fe2b3bf01..8b4a221f56 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -15,7 +15,8 @@ from calibre.gui2.preferences.plugins_ui import Ui_Form from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \ disable_plugin, plugin_customization, add_plugin, \ remove_plugin -from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files +from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, \ + question_dialog class PluginModel(QAbstractItemModel): # {{{ @@ -132,7 +133,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.toggle_plugin_button.clicked.connect(self.toggle_plugin) self.customize_plugin_button.clicked.connect(self.customize_plugin) self.remove_plugin_button.clicked.connect(self.remove_plugin) - self.button_plugin_browse.clicked.connect(self.find_plugin) self.button_plugin_add.clicked.connect(self.add_plugin) def toggle_plugin(self, *args): @@ -149,23 +149,33 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.modify_plugin(op='remove') def add_plugin(self): - path = unicode(self.plugin_path.text()) - if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'): - add_plugin(path) + path = choose_files(self, 'add a plugin dialog', _('Add plugin'), + filters=[(_('Plugins'), ['zip'])], all_files=False, + select_only_single_file=True) + if not path: + return + path = path[0] + if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'): + if not question_dialog(self, _('Are you sure?'), '

' + \ + _('Installing plugins is a security risk. ' + 'Plugins can contain a virus/malware. ' + 'Only install it if you got it from a trusted source.' + ' Are you sure you want to proceed?'), + show_copy_button=False): + return + plugin = add_plugin(path) self._plugin_model.populate() self._plugin_model.reset() self.changed_signal.emit() - self.plugin_path.setText('') + info_dialog(self, _('Success'), + _('Plugin {0} successfully installed under ' + ' {1} plugins. You may have to restart calibre ' + 'for the plugin to take effect.').format(plugin.name, plugin.type), + show=True) else: error_dialog(self, _('No valid plugin path'), _('%s is not a valid plugin path')%path).exec_() - def find_plugin(self): - path = choose_files(self, 'choose plugin dialog', _('Choose plugin'), - filters=[('Plugins', ['zip'])], all_files=False, - select_only_single_file=True) - if path: - self.plugin_path.setText(path[0]) def modify_plugin(self, op=''): index = self.plugin_view.currentIndex() diff --git a/src/calibre/gui2/preferences/plugins.ui b/src/calibre/gui2/preferences/plugins.ui index 8979867bbc..18f0786a66 100644 --- a/src/calibre/gui2/preferences/plugins.ui +++ b/src/calibre/gui2/preferences/plugins.ui @@ -72,64 +72,10 @@ - - - Add new plugin + + + &Add a new plugin - - - - - - - Plugin &file: - - - plugin_path - - - - - - - - - - ... - - - - :/images/document_open.png:/images/document_open.png - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Add - - - - - - diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 7e22839bdf..0732ff4650 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -103,7 +103,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.gui_debug = gui_debug acmap = OrderedDict() for action in interface_actions(): - ac = action.load_actual_plugin(self) + try: + ac = action.load_actual_plugin(self) + except: + # Ignore errors in loading user supplied plugins + import traceback + traceback.print_exc() + if ac.plugin_path is None: + raise + ac.plugin_path = action.plugin_path ac.interface_action_base_plugin = action if ac.name in acmap: