From 68df332cadc7f4ba5c884306e485591063b75085 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 18 Sep 2010 05:14:41 -0600 Subject: [PATCH 01/37] GwR apple driver patch --- src/calibre/devices/apple/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 5fe36faf75..0946bd2f51 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -739,7 +739,7 @@ class ITUNES(DriverBase): # Purge the booklist, self.cached_books, thumb cache for i,bl_book in enumerate(booklists[0]): - if DEBUG: + if False: self.log.info(" evaluating '%s' by '%s' uuid:%s" % (bl_book.title, bl_book.author,bl_book.uuid)) From 4416264c0289b80a7eda510c375591f88e1ab8d4 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 18 Sep 2010 19:43:02 -0600 Subject: [PATCH 02/37] GwR wip tweak_epub --- src/calibre/customize/builtins.py | 6 ++++- src/calibre/gui2/actions/tweak_epub.py | 34 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 src/calibre/gui2/actions/tweak_epub.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 68df832048..ec9f7e2bc2 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -666,13 +666,17 @@ class ActionCopyToLibrary(InterfaceActionBase): name = 'Copy To Library' actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction' +class ActionTweakEpub(InterfaceActionBase): + name = 'Tweak ePub' + actual_plugin = 'calibre.gui2.actions.tweak_epub:TweakEpubAction' + plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, ActionConvert, ActionDelete, ActionEditMetadata, ActionView, ActionFetchNews, ActionSaveToDisk, ActionShowBookDetails, ActionRestart, ActionOpenFolder, ActionConnectShare, ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks, ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary, - ActionCopyToLibrary] + ActionCopyToLibrary, ActionTweakEpub] # }}} diff --git a/src/calibre/gui2/actions/tweak_epub.py b/src/calibre/gui2/actions/tweak_epub.py new file mode 100755 index 0000000000..5f49c57379 --- /dev/null +++ b/src/calibre/gui2/actions/tweak_epub.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from calibre.gui2.actions import InterfaceAction + +class TweakEpubAction(InterfaceAction): + + name = 'Tweak ePub' + action_spec = (_('Edit ePub in situ'), 'document_open.png', None, None) + dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) + action_type = 'current' + + def genesis(self): + self.qaction.triggered.connect(self.edit_epub_in_situ) + print "gui2.actions.tweak_epub:TweakEpubAction.genesis()" + + def initialization_complete(self): + print "gui2.actions.tweak_epub:TweakEpubAction.initialization_complete()" + + def library_changed(self, db): + print "gui2.actions.tweak_epub:TweakEpubAction.library_changed()" + + def location_selected(self, loc): + print "gui2.actions.tweak_epub:TweakEpubAction.location_selected()" + + def shutting_down(self): + print "gui2.actions.tweak_epub:TweakEpubAction.shutting_down()" + + def edit_epub_in_situ(self, *args): + print "gui2.actions.tweak_epub:TweakEpubAction.edit_epub_in_situ()" From dec27fbaa1e9544675d6d10bda566d83fd7a85f2 Mon Sep 17 00:00:00 2001 From: ldolse Date: Sun, 19 Sep 2010 13:02:02 +0800 Subject: [PATCH 03/37] new dehyphenation algorithm, using the document as a dictionary --- src/calibre/ebooks/conversion/preprocess.py | 58 +++++++++++++++++++-- src/calibre/ebooks/conversion/utils.py | 13 +++-- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 03a0047927..a1e28b2554 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -106,6 +106,50 @@ def line_length(format, raw, percent): return lengths[index] +class Dehyphenator(object): + ''' + Analyzes words to determine whether hyphens should be retained/removed. Uses the document + itself is as a dictionary. This method handles all languages along with uncommon, made-up, and + scientific words. The primary disadvantage is that words appearing only once in the document + retain hyphens. + ''' + + def dehyphenate(self, match): + firsthalf = match.group('firstpart') + secondhalf = match.group('secondpart') + hyphenated = str(firsthalf) + "-" + str(secondhalf) + dehyphenated = str(firsthalf) + str(secondhalf) + # Add common suffixes to the regex below to increase the likelihood of a match - + # don't add suffixes which are also complete words, such as 'able' or 'sex' + removesuffixes = re.compile(r"((ed)?ly|(')?s|a?(t|s)ion(s|al(ly)?)?|ings?|(i)?ous|(i|a)ty|(it)?ies|ive|gence|istic|(e|a)nce|ment(s)?|ism|ated|(e|u)ct(ed)?|ed|(i|ed)?ness|(e|a)ncy|ble|ier|al|ex)$", re.IGNORECASE) + lookupword = removesuffixes.sub('', dehyphenated) + # remove prefixes if the prefix was not already the point of hyphenation + prefixes = re.compile(r'^(un|in|ex)$', re.IGNORECASE) + removeprefix = re.compile(r'^(un|in|ex)', re.IGNORECASE) + if prefixes.match(firsthalf) is None: + lookupword = removeprefix.sub('', lookupword) + booklookup = re.compile(u'%s' % lookupword, re.IGNORECASE) + #print "lookup word is: "+str(lookupword)+", orig is: " + str(hyphenated) + match = booklookup.search(self.html) + if match: + #print "returned dehyphenated word: " + str(dehyphenated) + return dehyphenated + else: + #print "returned hyphenated word: " + str(hyphenated) + return hyphenated + + def __call__(self, html, format, length=1): + self.html = html + if format == 'html': + intextmatch = re.compile(u'(?<=.{%i})(?P[^“"\s>]+)-\s*(?=<)(\s*(\s*<[iubp][^>]*>\s*)?]*>|\s*<[iubp][^>]*>)?\s*(?P[\w\d]+)' % length) + elif format == 'pdf': + intextmatch = re.compile(u'(?<=.{%i})(?P[^“"\s>]+)-\s*(

|\s*

\s*<[iub]>)\s*(?P[\w\d]+)'% length) + elif format == 'individual_words': + intextmatch = re.compile('>[^<]*\b(?P[^"\s>]+)-(?P\s*(?=[[a-z\d])'), lambda match: '')) + # unwrap em/en dashes + end_rules.append((re.compile(u'(?<=[–—])\s*

\s*(?=[[a-z\d])'), lambda match: '')) # unwrap/delete soft hyphens end_rules.append((re.compile(u'[­](\s*

)+\s*(?=[[a-z\d])'), lambda match: '')) # unwrap/delete soft hyphens with formatting @@ -350,7 +393,7 @@ class HTMLPreProcessor(object): # print "The pdf line length returned is " + str(length) end_rules.append( # Un wrap using punctuation - (re.compile(r'(?<=.{%i}([a-z,:)\-IA]|(?)?\s*(\s*)+\s*(?=(<(i|b|u)>)?\s*[\w\d$(])' % length, re.UNICODE), wrap_lines), + (re.compile(r'(?<=.{%i}([a-z,:)\IA]|(?)?\s*(\s*)+\s*(?=(<(i|b|u)>)?\s*[\w\d$(])' % length, re.UNICODE), wrap_lines), ) for rule in self.PREPROCESS + start_rules: @@ -380,6 +423,11 @@ class HTMLPreProcessor(object): for rule in rules + end_rules: html = rule[0].sub(rule[1], html) + if is_pdftohtml: + # Dehyphenate + dehyphenator = Dehyphenator() + html = dehyphenator(html,'pdf', length) + #dump(html, 'post-preprocess') # Handle broken XHTML w/ SVG (ugh) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index 37fd169cb1..f9178ead0b 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' import re -from calibre.ebooks.conversion.preprocess import line_length +from calibre.ebooks.conversion.preprocess import line_length, Dehyphenator from calibre.utils.logging import default_log class PreProcessor(object): @@ -132,7 +132,6 @@ class PreProcessor(object): # Arrange line feeds and

tags so the line_length and no_markup functions work correctly html = re.sub(r"\s*

", "

\n", html) html = re.sub(r"\s*

\s*", "\n

", html) - #self.log("\n\n\n\n\n\n\n\n\n\n\n"+html+"\n\n\n\n\n\n\n\n\n\n\n\n\n") # detect chapters/sections to match xpath or splitting logic heading = re.compile(']*>', re.IGNORECASE) self.html_preprocess_sections = len(heading.findall(html)) @@ -174,10 +173,16 @@ class PreProcessor(object): length = line_length(format, html, getattr(self.extra_opts, 'html_unwrap_factor', 0.4)) self.log("*** Median line length is " + str(length) + ", calculated with " + format + " format ***") + max_length = length * 1.4 + min_max = str("(?<=.{"+str(length)+"})(?\s*(\s*<[iubp][^>]*>\s*)?]*>|\s*<[iubp][^>]*>)?\s*', '', html) - html = re.sub(u'(?<=[-\u2013\u2014])\s*(?=<)(\s*(\s*<[iubp][^>]*>\s*)?]*>|\s*<[iubp][^>]*>)?\s*(?=[[a-z\d])', '', html) + html = re.sub(u'%s(?<=[\u2013\u2014])\s*(?=<)(\s*(\s*<[iubp][^>]*>\s*)?]*>|\s*<[iubp][^>]*>)?\s*(?=[[a-z\d])' % min_max, '', html) + # Dehyphenate + dehyphenator = Dehyphenator() + html = dehyphenator(html,'html', length) # Unwrap lines using punctation and line length unwrap = re.compile(r"(?<=.{%i}([a-z,;):\IA]|(?\s*()?\s*(?P<(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*\s*)\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE) From 90ff41dfb2ba554f64cb61dd5d1a6de599941b32 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 19 Sep 2010 03:54:39 -0600 Subject: [PATCH 04/37] KG fix for TweakEpub --- src/calibre/gui2/actions/tweak_epub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/tweak_epub.py b/src/calibre/gui2/actions/tweak_epub.py index 5f49c57379..020e9c0382 100755 --- a/src/calibre/gui2/actions/tweak_epub.py +++ b/src/calibre/gui2/actions/tweak_epub.py @@ -11,7 +11,7 @@ class TweakEpubAction(InterfaceAction): name = 'Tweak ePub' action_spec = (_('Edit ePub in situ'), 'document_open.png', None, None) - dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) + #dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) action_type = 'current' def genesis(self): From 053d60331fcfb9f82e141ebc11a625b1acd3e1a4 Mon Sep 17 00:00:00 2001 From: ldolse Date: Sun, 19 Sep 2010 23:07:07 +0800 Subject: [PATCH 05/37] regex optimizations --- src/calibre/ebooks/conversion/preprocess.py | 2 +- src/calibre/ebooks/conversion/utils.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 16bfb42d1f..7f13cefcaa 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -121,7 +121,7 @@ class Dehyphenator(object): dehyphenated = str(firsthalf) + str(secondhalf) # Add common suffixes to the regex below to increase the likelihood of a match - # don't add suffixes which are also complete words, such as 'able' or 'sex' - removesuffixes = re.compile(r"((ed)?ly|(')?s|a?(t|s)ion(s|al(ly)?)?|ings?|(i)?ous|(i|a)ty|(it)?ies|ive|gence|istic|(e|a)nce|ment(s)?|ism|ated|(e|u)ct(ed)?|ed|(i|ed)?ness|(e|a)ncy|ble|ier|al|ex)$", re.IGNORECASE) + removesuffixes = re.compile(r"((ed)?ly|('e)?s|a?(t|s)ion(s|al(ly)?)?|ings?|(i)?ous|(i|a)ty|(it)?ies|ive|gence|istic|(e|a)nce|ment(s)?|ism|ated|(e|u)ct(ed)?|ed|(i|ed)?ness|(e|a)ncy|ble|ier|al|ex)$", re.IGNORECASE) lookupword = removesuffixes.sub('', dehyphenated) # remove prefixes if the prefix was not already the point of hyphenation prefixes = re.compile(r'^(un|in|ex)$', re.IGNORECASE) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index f9178ead0b..6a5eaa4a34 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -114,7 +114,7 @@ class PreProcessor(object): html = re.sub(ur'\s*\s*', ' ', html) # Get rid of empty span, bold, & italics tags html = re.sub(r"\s*]*>\s*(]>\s*){0,2}\s*\s*", " ", html) - html = re.sub(r"\s*<[ibu]>\s*(<[ibu]>\s*\s*){0,2}\s*", " ", html) + html = re.sub(r"\s*<[ibu][^>]*>\s*(<[ibu][^>]*>\s*\s*){0,2}\s*", " ", html) html = re.sub(r"\s*]*>\s*(]>\s*){0,2}\s*\s*", " ", html) # If more than 40% of the lines are empty paragraphs then delete them to clean up spacing @@ -139,16 +139,16 @@ class PreProcessor(object): # # Start with most typical chapter headings, get more aggressive until one works if self.html_preprocess_sections < 10: - chapdetect = re.compile(r'(?=]*>)\s*(<[ibu]>){0,2}\s*(]*>)?\s*(<[ibu]>){0,2}\s*(]*>)?\s*(?P(<[ibu]>){0,2}\s*.?(Introduction|Synopsis|Acknowledgements|Chapter|Epilogue|Volume|Prologue|Book\s|Part\s|Dedication)\s*([\d\w-]+\:?\s*){0,8}\s*(){0,2})\s*()?s*(){0,2}\s*()?\s*()\s*\s*(\s*]*>\s*

){0,2}\s*(<(/?br|p)[^>]*>\s*(<[ibu]>){0,2}\s*(]*>)?\s*(?P(<[ibu]>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.IGNORECASE|re.VERBOSE) + chapdetect = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?(Introduction|Synopsis|Acknowledgements|Chapter|Epilogue|Volume|Prologue|Book\s|Part\s|Dedication)\s*([\d\w-]+\:?\s*){0,8}\s*(</[ibu]>){0,2})\s*(</span>)?s*(</[ibu]>){0,2}\s*(</span>)?\s*(</(p|/?br)>)\s*\s*(\s*<p[^>]*>\s*</p>){0,2}\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.IGNORECASE|re.VERBOSE) html = chapdetect.sub(self.chapter_head, html) if self.html_preprocess_sections < 10: self.log("not enough chapters, only " + str(self.html_preprocess_sections) + ", trying numeric chapters") - chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu]>){0,2}\s*.?(\d+\.?|(CHAPTER\s*([\dA-Z\-\'\"\?\.!#,]+\s*){1,10}))\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu]>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE) + chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?(\d+\.?|(CHAPTER\s*([\dA-Z\-\'\"\?\.!#,]+\s*){1,10}))\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE) html = chapdetect2.sub(self.chapter_head, html) if self.html_preprocess_sections < 10: self.log("not enough chapters, only " + str(self.html_preprocess_sections) + ", trying with uppercase words") - chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu]>){0,2}\s*.?([A-Z#\-\s]+)\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu]>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE) + chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?([A-Z#\-\s]+)\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE) html = chapdetect2.sub(self.chapter_head, html) ###### Unwrap lines ###### @@ -191,7 +191,7 @@ class PreProcessor(object): # If still no sections after unwrapping mark split points on lines with no punctuation if self.html_preprocess_sections < 10: self.log("Looking for more split points based on punctuation, currently have " + str(self.html_preprocess_sections)) - chapdetect3 = re.compile(r'<(?P<styles>(p|div)[^>]*)>\s*(?P<section>(<span[^>]*>)?\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*(<[ibu]>){0,2}\s*(<span[^>]*>)?\s*.?([a-z#-*]+\s*){1,5}\s*\s*(</span>)?(</[ibu]>){0,2}\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</span>)?\s*</(p|div)>)', re.IGNORECASE) + chapdetect3 = re.compile(r'<(?P<styles>(p|div)[^>]*)>\s*(?P<section>(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*.?(?=[a-z#\-*\s]+<)([a-z#-*]+\s*){1,5}\s*\s*(</span>)?(</[ibu]>){0,2}\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</span>)?\s*</(p|div)>)', re.IGNORECASE) html = chapdetect3.sub(self.chapter_break, html) # search for places where a first or second level heading is immediately followed by another # top level heading. demote the second heading to h3 to prevent splitting between chapter From 980388f2bde3d4cb4b07673cb9e79c951aabd867 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 19 Sep 2010 09:48:39 -0600 Subject: [PATCH 06/37] Le Journal de Montreal by Luciano Furtado. Fixes #405 (New news feed) --- resources/recipes/le_journal.recipe | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 resources/recipes/le_journal.recipe diff --git a/resources/recipes/le_journal.recipe b/resources/recipes/le_journal.recipe new file mode 100644 index 0000000000..24a7d52164 --- /dev/null +++ b/resources/recipes/le_journal.recipe @@ -0,0 +1,43 @@ +__author__ = ' (lrfurtado@yahoo.com.br)' + +from calibre.web.feeds.news import BasicNewsRecipe + +class LeJournalDeMontrealRecipe(BasicNewsRecipe): + + title = u'Le Journal de Montreal' + description = u'Le Journal de Montreal' + __author__ = 'Luciano Furtado' + language = 'fr' + + oldest_article = 7 + use_embedded_content=0 + max_articles_per_feed = 15 + + remove_tags = [ + dict(name='ul',attrs={'id':'mainNav'}), + dict(name='div',attrs={'id':'boxPolitique'}), + dict(name='div',attrs={'id':'boxScoop'}), + dict(name='div',attrs={'id':'DossierSpec'}), + dict(name='div',attrs={'id':'channelBoxes'}), + dict(name='div',attrs={'id':'sectionBoxes'}), + dict(name='div',attrs={'id':'header'}), + dict(name='div',attrs={'id':'footer'}), + dict(name='div',attrs={'id':'navbarCanoe_container'}), + dict(name='div',attrs={'id':'popularCanoe'}), + dict(name='div',attrs={'id':'textAds'}), + dict(name='div',attrs={'id':'24heures'}), + dict(name='div',attrs={'class':'bottomBox clear'}), + dict(name='div',attrs={'class':'articleControls thin'}), + ] + + + feeds = [ + (u'Actualites', + u'http://www.canoe.com/rss/feed/nouvelles/ljm_actualites.xml'), + (u'Arts et spectacle', + u'http://www.canoe.com/rss/feed/nouvelles/ljm_arts.xml'), + (u'Sports', + u'http://www.canoe.com/rss/feed/nouvelles/ljm_sports.xml'), + (u'Chroniques', + u'http://www.canoe.com/rss/feed/nouvelles/ljm_chroniques.xml'), + ] From 23cd4fd7833180d7036aa77c0c1efcbd09ca6a00 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 19 Sep 2010 10:16:41 -0600 Subject: [PATCH 07/37] Content server: Making serving of large files more efficient. --- src/calibre/library/server/content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 95794a8c1d..aeba8a3218 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -184,7 +184,7 @@ class ContentServer(object): if path and os.path.exists(path): updated = fromtimestamp(os.stat(path).st_mtime) cherrypy.response.headers['Last-Modified'] = self.last_modified(updated) - return fmt.read() + return fmt # }}} From f4b885568343944d66950935df887f276eaa3b4f Mon Sep 17 00:00:00 2001 From: Timothy Legge <timlegge@gmail.com> Date: Sun, 19 Sep 2010 21:46:13 -0300 Subject: [PATCH 08/37] KOBO: Fix issue where books that are read were getting their status reset to Unread --- src/calibre/devices/kobo/driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 762a05d193..1171b74f5c 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -443,9 +443,9 @@ class KOBO(USBMS): # Reset Im_Reading list in the database if oncard == 'carda': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\'' + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\'' elif oncard != 'carda' and oncard != 'cardb': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\'' + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\'' try: cursor.execute (query) From 0fa7eef131080297085c0f4224907e351fc8e7fb Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 19 Sep 2010 19:01:53 -0600 Subject: [PATCH 09/37] Tagesanzeiger by noxxx --- resources/recipes/tagesan.recipe | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 resources/recipes/tagesan.recipe diff --git a/resources/recipes/tagesan.recipe b/resources/recipes/tagesan.recipe new file mode 100644 index 0000000000..8514162598 --- /dev/null +++ b/resources/recipes/tagesan.recipe @@ -0,0 +1,45 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1284927619(BasicNewsRecipe): + title = u'Tagesanzeiger' + publisher = u'Tamedia AG' + oldest_article = 2 + __author__ = 'noxxx' + max_articles_per_feed = 100 + description = 'tagesanzeiger.ch: Nichts verpassen' + category = 'News, Politik, Nachrichten, Schweiz, Zürich' + language = 'de' + + conversion_options = { + 'comments' : description + ,'tags' : category + ,'language' : language + ,'publisher' : publisher + } + + remove_tags = [ + dict(name='img') + ,dict(name='div',attrs={'class':['swissquote ad','boxNews','centerAD','contentTabs2','sbsLabel']}) + ,dict(name='div',attrs={'id':['colRightAd','singleRight','singleSmallRight','MailInfo','metaLine','sidebarSky','contentFooter','commentInfo','commentInfo2','commentInfo3','footerBottom','clear','boxExclusiv','singleLogo','navSearch','headerLogin','headerBottomRight','horizontalNavigation','subnavigation','googleAdSense','footerAd','contentbox','articleGalleryNav']}) + ,dict(name='form',attrs={'id':['articleMailForm','commentform']}) + ,dict(name='div',attrs={'style':['position:absolute']}) + ,dict(name='script',attrs={'type':['text/javascript']}) + ,dict(name='p',attrs={'class':['schreiben','smallPrint','charCounter','caption']}) + ] + feeds = [ + (u'Front', u'http://www.tagesanzeiger.ch/rss.html') + ,(u'Zürich', u'http://www.tagesanzeiger.ch/zuerich/rss.html') + ,(u'Schweiz', u'http://www.tagesanzeiger.ch/schweiz/rss.html') + ,(u'Ausland', u'http://www.tagesanzeiger.ch/ausland/rss.html') + ,(u'Digital', u'http://www.tagesanzeiger.ch/digital/rss.html') + ,(u'Wissen', u'http://www.tagesanzeiger.ch/wissen/rss.html') + ,(u'Panorama', u'http://www.tagesanzeiger.ch/panorama/rss.html') + ,(u'Wirtschaft', u'http://www.tagesanzeiger.ch/wirtschaft/rss.html') + ,(u'Sport', u'http://www.tagesanzeiger.ch/sport/rss.html') + ,(u'Kultur', u'http://www.tagesanzeiger.ch/kultur/rss.html') + ,(u'Leben', u'http://www.tagesanzeiger.ch/leben/rss.html') + ,(u'Auto', u'http://www.tagesanzeiger.ch/auto/rss.html')] + + def print_version(self, url): + return url + '/print.html' + From 77da36f05c3e09654650133671c1d7f904d8a7d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 19 Sep 2010 23:51:14 -0600 Subject: [PATCH 10/37] Add prologue and epilogue to default chapter detection regex --- src/calibre/ebooks/conversion/plumber.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 3ea2926461..395447edba 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -241,7 +241,7 @@ OptionRecommendation(name='toc_filter', OptionRecommendation(name='chapter', recommended_value="//*[((name()='h1' or name()='h2') and " - r"re:test(., 'chapter|book|section|part\s+', 'i')) or @class " + r"re:test(., 'chapter|book|section|part|prologue|epilogue\s+', 'i')) or @class " "= 'chapter']", level=OptionRecommendation.LOW, help=_('An XPath expression to detect chapter titles. The default ' 'is to consider <h1> or <h2> tags that contain the words ' From 656c88792ddeb760cf7ed562ad54bce81d17f77e Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Mon, 20 Sep 2010 08:29:25 -0600 Subject: [PATCH 11/37] The Marker by Marbs --- resources/recipes/the_marker.recipe | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 resources/recipes/the_marker.recipe diff --git a/resources/recipes/the_marker.recipe b/resources/recipes/the_marker.recipe new file mode 100644 index 0000000000..e5f1ffc761 --- /dev/null +++ b/resources/recipes/the_marker.recipe @@ -0,0 +1,52 @@ +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1283848012(BasicNewsRecipe): + description = 'TheMarker Financial News in Hebrew' + __author__ = 'TonyTheBookworm, Marbs' + cover_url = 'http://static.ispot.co.il/wp-content/upload/2009/09/themarker.jpg' + title = u'TheMarker' + language = 'he' + simultaneous_downloads = 5 + remove_javascript = True + timefmt = '[%a, %d %b, %Y]' + oldest_article = 1 + remove_tags = [dict(name='tr', attrs={'bgcolor':['#738A94']}) ] + max_articles_per_feed = 10 + extra_css='body{direction: rtl;} .article_description{direction: rtl; } a.article{direction: rtl; } .calibre_feed_description{direction: rtl; }' + feeds = [(u'Head Lines', u'http://www.themarker.com/tmc/content/xml/rss/hpfeed.xml'), + (u'TA Market', u'http://www.themarker.com/tmc/content/xml/rss/sections/marketfeed.xml'), + (u'Real Estate', u'http://www.themarker.com/tmc/content/xml/rss/sections/realEstaterfeed.xml'), + (u'Wall Street & Global', u'http://www.themarker.com/tmc/content/xml/rss/sections/wallsfeed.xml'), + (u'Law', u'http://www.themarker.com/tmc/content/xml/rss/sections/lawfeed.xml'), + (u'Media', u'http://www.themarker.com/tmc/content/xml/rss/sections/mediafeed.xml'), + (u'Consumer', u'http://www.themarker.com/tmc/content/xml/rss/sections/consumerfeed.xml'), + (u'Career', u'http://www.themarker.com/tmc/content/xml/rss/sections/careerfeed.xml'), + (u'Car', u'http://www.themarker.com/tmc/content/xml/rss/sections/carfeed.xml'), + (u'High Tech', u'http://www.themarker.com/tmc/content/xml/rss/sections/hightechfeed.xml'), + (u'Investor Guide', u'http://www.themarker.com/tmc/content/xml/rss/sections/investorGuidefeed.xml')] + + def print_version(self, url): + split1 = url.split("=") + weblinks = url + + if weblinks is not None: + for link in weblinks: + #--------------------------------------------------------- + #here we need some help with some regexpressions + #we are trying to find it.themarker.com in a url + #----------------------------------------------------------- + re1='.*?' # Non-greedy match on filler + re2='(it\\.themarker\\.com)' # Fully Qualified Domain Name 1 + rg = re.compile(re1+re2,re.IGNORECASE|re.DOTALL) + m = rg.search(url) + + + if m: + split2 = url.split("article/") + print_url = 'http://it.themarker.com/tmit/PrintArticle/' + split2[1] + + else: + print_url = 'http://www.themarker.com/ibo/misc/printFriendly.jhtml?ElementId=%2Fibo%2Frepositories%2Fstories%2Fm1_2000%2F' + split1[1]+'.xml' + + return print_url From cec2f873cb29b8868695da152467c8f9d3eb9ea5 Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Mon, 20 Sep 2010 07:44:33 -0700 Subject: [PATCH 12/37] GwR wip tweak_epub --- src/calibre/gui2/dialogs/tweak_epub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index cc5b526291..a78d26a9dc 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -117,7 +117,7 @@ class TweakEpub(QDialog, Ui_Dialog): # Write mimetype zf.write(os.path.join(self._exploded,'mimetype'), 'mimetype', compress_type=ZIP_STORED) # Write everything else - exclude_files = ['.DS_Store','mimetype','iTunesMetadata.plist'] + exclude_files = ['.DS_Store','mimetype','iTunesMetadata.plist','rebuilt.epub'] for root, dirs, files in os.walk(self._exploded): for fn in files: if fn in exclude_files: From 62d9449e88f6aa5a6a2e1234137f31ed3d9e25d9 Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Mon, 20 Sep 2010 07:57:05 -0700 Subject: [PATCH 13/37] GwR wip tweak_epub --- src/calibre/gui2/dialogs/tweak_epub.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index a78d26a9dc..c0ad79385b 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -71,8 +71,8 @@ class TweakEpub(QDialog, Ui_Dialog): prints(" killing file browser proc") #self._file_browser_proc.terminate() #self._file_browser_proc.kill() - #self._file_browser_send_signal() - #self._file_browser_proc = None + #self._file_browser_send_signal(?) + self._file_browser_proc = None # Delete directory containing exploded ePub if self._exploded is not None: @@ -93,6 +93,7 @@ class TweakEpub(QDialog, Ui_Dialog): elif iswindows: cmd = 'start explorer.exe /e,/root,%s' % self._exploded else: + # *** Kovid - need proper linux invocation here *** cmd = '<linux command to open native file browser>' # *** Kovid - need a way of launching this process than can be killed in cleanup() *** From bc82ea61032bf7f1d2564674f0a7174df4b3dab4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Mon, 20 Sep 2010 09:28:39 -0600 Subject: [PATCH 14/37] Add button to Edit metadata dialog to trim borders from the cover --- imgsrc/trim.svg | 688 ++++++++++++++++++++ resources/images/trim.png | Bin 0 -> 2553 bytes src/calibre/gui2/dialogs/metadata_single.py | 19 + src/calibre/gui2/dialogs/metadata_single.ui | 11 + 4 files changed, 718 insertions(+) create mode 100644 imgsrc/trim.svg create mode 100644 resources/images/trim.png diff --git a/imgsrc/trim.svg b/imgsrc/trim.svg new file mode 100644 index 0000000000..8c8810fc66 --- /dev/null +++ b/imgsrc/trim.svg @@ -0,0 +1,688 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="128" + height="128" + id="svg1307" + sodipodi:version="0.32" + inkscape:version="0.46+devel" + version="1.0" + sodipodi:docname="transform-crop.svgz" + inkscape:export-filename="/home/pinheiro/pics/oxygen-icons/scalable/actions/transform-crop.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svgz.inkscape"> + <defs + id="defs1309"> + <linearGradient + inkscape:collect="always" + id="linearGradient2594"> + <stop + style="stop-color:#fafafa;stop-opacity:1;" + offset="0" + id="stop2596" /> + <stop + style="stop-color:#fafafa;stop-opacity:0;" + offset="1" + id="stop2598" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3969"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop3971" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop3973" /> + </linearGradient> + <linearGradient + id="linearGradient2783"> + <stop + style="stop-color:#323232;stop-opacity:1;" + offset="0" + id="stop2785" /> + <stop + id="stop2787" + offset="0.07692308" + style="stop-color:#dfe1e1;stop-opacity:1;" /> + <stop + style="stop-color:#b6b1b1;stop-opacity:1;" + offset="0.26289096" + id="stop2799" /> + <stop + id="stop2789" + offset="0.5" + style="stop-color:#8d8282;stop-opacity:1;" /> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0.78201604" + id="stop2791" /> + <stop + style="stop-color:#dfd9df;stop-opacity:1;" + offset="0.9005897" + id="stop2793" /> + <stop + style="stop-color:#3a3a3a;stop-opacity:1;" + offset="1" + id="stop2795" /> + </linearGradient> + <linearGradient + id="linearGradient2222" + inkscape:collect="always"> + <stop + id="stop2224" + offset="0" + style="stop-color:#0066ff;stop-opacity:1" /> + <stop + id="stop2226" + offset="1" + style="stop-color:#80b3ff;stop-opacity:1" /> + </linearGradient> + <linearGradient + id="linearGradient3314" + inkscape:collect="always"> + <stop + id="stop3316" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop3318" + offset="1" + style="stop-color:#ffffff;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient2431"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop2433" /> + <stop + id="stop2435" + offset="0.42597079" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop2437" + offset="0.5892781" + style="stop-color:#f1f1f1;stop-opacity:1;" /> + <stop + style="stop-color:#eaeaea;stop-opacity:1;" + offset="0.80219781" + id="stop2439" /> + <stop + style="stop-color:#dfdfdf;stop-opacity:1;" + offset="1" + id="stop2441" /> + </linearGradient> + <linearGradient + id="linearGradient7422"> + <stop + style="stop-color:#b4b4b6;stop-opacity:1;" + offset="0" + id="stop7424" /> + <stop + id="stop5348" + offset="0.5" + style="stop-color:#9c9ca1;stop-opacity:1;" /> + <stop + id="stop7426" + offset="1" + style="stop-color:#cdcdd1;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3310" + inkscape:collect="always"> + <stop + id="stop3312" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop3314" + offset="1" + style="stop-color:#ffffff;stop-opacity:0;" /> + </linearGradient> + <filter + inkscape:collect="always" + x="-0.21138181" + width="1.4227636" + y="-0.21047288" + height="1.4209458" + id="filter9723"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.4336041" + id="feGaussianBlur9725" /> + </filter> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath10698"> + <path + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" + d="M -128.2008,-3.392377 L -104.45558,6.3360672 L -102.43766,6.1757677 L -103.81912,-4.5678172 L -105.75454,-5.8316609 L -124.96922,-4.4459394 L -128.2008,-3.392377 z " + id="path10700" + sodipodi:nodetypes="ccccccc" /> + </clipPath> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2783" + id="radialGradient3418" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.9728905,-8.15107,-18.526373,-2.211261,1957.2342,725.31677)" + cx="53.235302" + cy="106.0573" + fx="53.235302" + fy="106.0573" + r="9.1025209" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2594" + id="radialGradient3420" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.5808473,-2.8009276,-6.4965168,-1.3472267,701.00301,348.75795)" + cx="53.347126" + cy="104.68401" + fx="53.347126" + fy="104.68401" + r="9.1025209" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3314" + id="radialGradient3422" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-2.9339535,-1.0170467,-1.1904108,3.4340702,323.071,-252.78281)" + cx="49.110855" + cy="105.43803" + fx="49.110855" + fy="105.43803" + r="10.20672" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2783" + id="linearGradient3425" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2608955,0,0,1.9345479,-550.58555,-317.90247)" + x1="190.03462" + y1="90.22673" + x2="208.7153" + y2="90.22673" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3969" + id="linearGradient3430" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.2608955,0,0,1.9345479,-497.11778,-432.24104)" + x1="98.411324" + y1="185.68851" + x2="166.32983" + y2="155.59846" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient7422" + id="linearGradient3525" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(6.0715756e-2,0,0,9.7589526e-2,24.201706,-45.627655)" + x1="399.77466" + y1="1164.6696" + x2="399.77466" + y2="549.06134" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2431" + id="linearGradient3527" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.5415355,0,0,0.7222225,23.477667,-8.2222193)" + x1="119.57646" + y1="23.792561" + x2="15.999996" + y2="109.6508" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3310" + id="linearGradient3529" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,-1.5975038,-2,0,96,199.26848)" + x1="102.31124" + y1="-5.8302126" + x2="74.330322" + y2="32" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2222" + id="linearGradient3538" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7476489,0,0,0.7476489,0,-19.999999)" + x1="8.2386189" + y1="-13.864992" + x2="8.2386189" + y2="-1.4047648" /> + <filter + inkscape:collect="always" + id="filter4420"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="3.0486726" + id="feGaussianBlur4422" /> + </filter> + <mask + maskUnits="userSpaceOnUse" + id="mask3562"> + <rect + ry="1.4444447" + rx="1.1997639" + y="8" + x="-4.0000005" + height="116.00001" + width="124" + id="rect3564" + style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4420)" + transform="matrix(1.1453342,0,0,1.1453342,15.087799,-38.432604)" /> + </mask> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.2136483" + inkscape:cx="77.317692" + inkscape:cy="55.850409" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:document-units="px" + inkscape:grid-bbox="true" + guidetolerance="4" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1440" + inkscape:window-height="840" + inkscape:window-x="223" + inkscape:window-y="37" + objecttolerance="4" + gridtolerance="4"> + <sodipodi:guide + orientation="horizontal" + position="-32.073749" + id="guide2204" /> + <inkscape:grid + id="GridFromPre046Settings" + type="xygrid" + originx="0px" + originy="0px" + spacingx="4px" + spacingy="4px" + color="#0000ff" + empcolor="#0000ff" + opacity="0.2" + empopacity="0.4" + empspacing="4" + visible="true" + enabled="true" /> + </sodipodi:namedview> + <metadata + id="metadata1312"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <cc:license + rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" /> + <dc:contributor> + <cc:Agent> + <dc:title>Oxygen team</dc:title> + </cc:Agent> + </dc:contributor> + <dc:title></dc:title> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/LGPL/2.1/"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/SourceCode" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <rect + ry="0.1870501" + rx="0.1537565" + y="28.129654" + x="8" + height="92" + width="92" + id="rect3226" + style="fill:#618fd2;fill-opacity:0.09195401;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1" /> + <g + id="g3520" + transform="translate(32,-0.1296539)"> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="/home/pinheiro/Desktop/mock2.png" + style="opacity:0.75;fill:url(#linearGradient3525);fill-opacity:1;fill-rule:nonzero;stroke:none" + id="rect3281" + width="92" + height="92" + x="28.129654" + y="-24" + inkscape:r_cx="true" + inkscape:r_cy="true" + ry="3.9616783" + rx="3.9616783" + transform="matrix(0,1,1,0,0,0)" /> + <rect + ry="1.4444447" + rx="1.1997639" + y="-20" + x="32.129654" + height="84" + width="84" + id="rect3283" + style="fill:url(#linearGradient3527);fill-opacity:1;fill-rule:evenodd;stroke:none" + transform="matrix(0,1,1,0,0,0)" /> + <path + id="path3285" + d="M 64,53.096891 C 45.143834,70.163928 24.748768,86.162699 -2.0000002e-07,96.129654 L -2.0000002e-07,52.647595 C 23.693959,50.212248 45.09831,42.609775 64,32.129654 L 64,53.096891 z" + style="fill:url(#linearGradient3529);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + <g + transform="translate(-16,20.129654)" + style="fill:#7193c6;fill-opacity:1" + id="g2250"> + <rect + ry="1.3512546" + rx="0.077153668" + y="-116" + x="16" + height="4" + width="4" + id="rect3210" + style="opacity:1;fill:#7193c6;fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1" + transform="matrix(0,1,-1,0,0,0)" + inkscape:tile-w="8" + inkscape:tile-h="8" + inkscape:tile-cx="124" + inkscape:tile-cy="28" /> + <use + style="fill:#7193c6;fill-opacity:1" + x="0" + y="0" + inkscape:tiled-clone-of="#rect3210" + xlink:href="#rect3210" + transform="translate(0,8)" + id="use2236" + width="128" + height="128" /> + <use + style="fill:#7193c6;fill-opacity:1" + x="0" + y="0" + inkscape:tiled-clone-of="#rect3210" + xlink:href="#rect3210" + transform="translate(0,16)" + id="use2240" + width="128" + height="128" /> + <use + style="fill:#7193c6;fill-opacity:1" + x="0" + y="0" + inkscape:tiled-clone-of="#rect3210" + xlink:href="#rect3210" + transform="translate(0,24)" + id="use2244" + width="128" + height="128" /> + <use + style="fill:#7193c6;fill-opacity:1" + x="0" + y="0" + inkscape:tiled-clone-of="#rect3210" + xlink:href="#rect3210" + transform="translate(0,32)" + id="use2248" + width="128" + height="128" /> + <use + height="88" + width="88" + transform="translate(0,24)" + id="use3220" + xlink:href="#use2240" + y="0" + x="0" /> + <use + height="88" + width="88" + transform="translate(0,24)" + id="use3222" + xlink:href="#use2244" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(0,32)" + id="use2230" + xlink:href="#use2244" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(0,32)" + id="use2232" + xlink:href="#use2248" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(0,32)" + id="use2234" + xlink:href="#use3220" + y="0" + x="0" /> + </g> + <use + height="128" + width="128" + transform="matrix(8.5712909e-8,-0.9999999,0.9999999,8.5712909e-8,-20.129659,128.12964)" + id="use2258" + xlink:href="#g2250" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(-88,0)" + id="use2314" + xlink:href="#g2250" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="matrix(8.5712909e-8,-0.9999999,0.9999999,8.5712909e-8,-20.129651,216.12964)" + id="use2316" + xlink:href="#g2250" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(96,0.1296547)" + id="use3300" + xlink:href="#rect3222" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(7.4990672e-6,96.129662)" + id="use3302" + xlink:href="#rect3222" + y="0" + x="0" /> + <use + height="128" + width="128" + transform="translate(96,96.129652)" + id="use3304" + xlink:href="#rect3222" + y="0" + x="0" /> + <rect + ry="0.18696606" + rx="0.15479258" + y="-32" + x="0" + height="12" + width="12" + id="rect3222" + style="fill:url(#linearGradient3538);fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1" + transform="scale(1,-1)" /> + <rect + transform="scale(1,-1)" + style="fill:#bfd9ff;fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1" + id="rect2225" + width="4" + height="4" + x="4" + y="-28" + rx="0.15479258" + ry="0.18696606" /> + <use + style="fill:#a4c0e4" + height="88" + width="88" + transform="translate(96,0.1296539)" + id="use3226" + xlink:href="#rect2225" + y="0" + x="0" /> + <use + style="fill:#a4c0e4" + height="88" + width="88" + transform="translate(7.5e-6,96.129661)" + id="use3228" + xlink:href="#rect2225" + y="0" + x="0" /> + <use + style="fill:#a4c0e4" + height="88" + width="88" + transform="translate(96,96.129654)" + id="use3230" + xlink:href="#rect2225" + y="0" + x="0" /> + <rect + style="opacity:0.57786889;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.63199997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" + id="rect1327" + width="1" + height="0" + x="15.057414" + y="-308.20486" /> + <g + id="g3407" + transform="matrix(0.8731076,0,0,0.8731076,-13.173272,33.555799)" + mask="url(#mask3562)"> + <path + sodipodi:nodetypes="ccccccc" + id="path3836" + d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z" + style="fill:url(#radialGradient3418);fill-opacity:1;fill-rule:nonzero;stroke:none" /> + <path + style="fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none" + d="m 107.32508,50.938663 -74.427424,35.613119 -3.008197,6.986785 76.368201,-35.710168 3.7845,-5.046004 -2.71708,-1.843732 z" + id="path8241" + sodipodi:nodetypes="cccccc" /> + <path + style="opacity:0.10688836;fill:url(#radialGradient3420);fill-opacity:1;fill-rule:nonzero;stroke:none" + d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z" + id="path11683" + sodipodi:nodetypes="ccccccc" /> + <path + sodipodi:nodetypes="ccccccc" + id="path17921" + d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z" + style="fill:none;stroke:url(#radialGradient3422);stroke-width:0.86455041;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:4" /> + <rect + style="fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="rect8239" + width="39.714981" + height="37.454777" + x="27.310663" + y="81.415123" + transform="matrix(0.6571695,-0.7537428,0.7537428,0.6571695,0,0)" + rx="3.8771732" + ry="3.8771732" /> + <rect + transform="matrix(-0.7651682,-0.6438304,-0.6438304,0.7651682,0,0)" + style="fill:url(#linearGradient3425);fill-opacity:1;fill-rule:nonzero;stroke:none" + id="rect2803" + width="40.499767" + height="122.13765" + x="-120.93575" + y="-157.97318" + rx="0" + ry="0" /> + <rect + transform="matrix(-0.7651682,-0.6438304,-0.6438304,0.7651682,0,0)" + y="-161.84383" + x="-119.89533" + height="126.00658" + width="39.223213" + id="rect3967" + style="fill:url(#linearGradient3430);fill-opacity:1;fill-rule:nonzero;stroke:none" /> + <rect + transform="matrix(-0.6438304,0.7651682,0.7651682,0.6438304,0,0)" + y="80.243172" + x="-155.77248" + height="40.591759" + width="100.57008" + id="rect1851" + style="opacity:0.52459011;fill:#e0e0e0;fill-opacity:1;fill-rule:nonzero;stroke:none" /> + <rect + ry="1.2485937" + rx="1.2485937" + transform="matrix(2.0406638,-2.3405465,2.3405465,2.0406638,304.62828,-199.57966)" + y="-5.487061" + x="-104.11894" + height="12.061829" + width="12.789698" + id="rect8248" + style="fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;filter:url(#filter9723)" + clip-path="url(#clipPath10698)" /> + </g> + </g> +</svg> diff --git a/resources/images/trim.png b/resources/images/trim.png new file mode 100644 index 0000000000000000000000000000000000000000..3cb93adfa670c8353a95b6d2b5d49180db4340e6 GIT binary patch literal 2553 zcmb_eXH=8f7X1>CCSYt7Nr+;oj(|K=q=q(B0Y#*@V1SWcLQeq!L8$@;a44f9#W4hd zpoBUgp$#Y?5HeCknk0Y(5CoDJ-;ek2{d?=2bN5;Mu6x&B=iVPX-Ok2LOyrCR003eZ z<|a3JNcdSo0=%_L-+O=u0w^mp6M*|O%3CY*06>^zVPbeI=66=fa5mKJG_Bp?Bdbob z*2gUC??=YJ<U*8ljk-z4p|g-gp$7>aeuyFH=GB<Jqv5kTGX0(qaRs7NKgbZdqiO(^ zhlGRQ6&9dqbopue3MuZ|;FdMUb~}4zrL?#R2UoU?Zm%H~$JN$&;He8oq*A^AcRCAp zuU&rt@Sh%_Q723$eeXB-tX<az>@e2PJ_URZ&tnNHhM|}EKJukhn?Dch^V{$IP&d3- zWIQ|%hwdCkm9Ii$Wx3Tr%;9O|gSonMvlHtV+w9%tbU3wo(QuR|$D~82EU<Bka|=KW z*=EJ2_!F7CJ79bNkm3<2AtE_Sc>pvubo|9_zz0xPq_uz>tenN)M`fv>1`N2@!%}qO z;Rl`i`1p0MPF*UPfHd@+xkjBRE!nd56#bs2DSEsYGJU8R)sQF$Bq9#GE-1$L_)CF% zrCPWvbXZX8b_Jpp`>j*<xS<T6hCnnNi#3zE)Nu4!nE|^@PYw(Sff5o92D8rn0f3Y< zaAEA}Dm>T-441|En8{QFNQ;pf+@_bU2vP`y{H#{y4*dw9s`bFV5i}P92?@7waB&GC z|4bRZK6;XXyT#0o74~&UrI}AIDIVYUkA%vINO&vCA-H0wOO$|ngq%r`oYC`(R0>P; z^?+=TMJ<#80>EjROnGN#l<f}%p|pnLv8z%$AsQ}enbu9Vh*Z)M<md&aw<ju>-&1^% z3aS|D?x&9u-J))w%4)GQ*@-6zBsZr!xMM(t!ZvIE3UkDp_^4s^O^th6-{t+7@W1mX zAFdJ`*JaGiE8k+5*5E95i2nXyy=3WRi&An9uo2T>AwRIVVy}YXyn$zCrwjST8Gb$3 zMc5Rqr-Ucim@FULnp|wg<JG0@<<2njA{fCl2AvrEzHW60SD(sN%q{wgzj4@2%GbDA zLtE!?*5P9I{Gf9L&zA4~FMH>ulAF4pxy6h5j_;J|;#Di$Lw=WSqmc#W6Vu3H+JlY4 z=R#jdei;{3Kdml|YS3heM;FwsQv1<-{cX`#%9%Jt?yx{cknLIsqauHDauO+(dZL3K z(W)eXLZqLYK2>Imp_rbDe|!GVRt;%>NmEJ<`t?wMgoj6w!dLo2J&hD#sk{}Zm+Y<O z(4&~y{wZMeTU3#A3DMDaIxZ@f$PS`O9twEo3v{e=c6Jx?3XOo}BXZ5n_P9~!^YNv# z8*^KMdJ}igR&-a}S3>wljRPmB5!{1;Txp!oam%}V)%fBOK<n$1%r7zOBFQzHbz=@< z;?%!uCW=YKU$(uajH)Boa$c2<>0~X$B>2?tW(T=&JDvrqg=lCc1RphooE0@TFH-yR z%jk3&nXHyjqmb^<-;j$Ke^r@p1mU-#EiDKRoZ_zlg<}2YqZUV^mwqFsn~*IJDNQM7 zh062$SpgonoI~sk{s)_3tQTCx*K%XkaiUTah)c|P60E6K6mVXvI01Ug(oosq%^-E{ zVzSiCRR-7Yrl?c~hHzwl`}}atP~c6Ss=PTX<)eP_H}H?q)x$o@ZJR+>I=!mYdTn=0 zXGY=xWn$^W197|i3XS)gEl~D0O~V6D0kCp$^uH=s5SRMCJSnW+X??j9!NL%<$1uMZ z7!E7kL0foF>upl<bkU*C&i#OSRShg(Eb_CuPW-Ty2s<)v--VJ<vOzZ_Glv9qmjO)G z{O+G8>xcS1qOXlPsMT7JuQ>SO6?qrllC~xqZ$jtdw0k4`VU><pk>v)Smga27*!t<7 z%WPJ-sam2N*7e~<FKOke6Prv`*XhQmLGLg@AAb+7*VIPB+_Iir7TP3HeX#|&L6KEJ z%x;nSv|DYx_`<co;!dv)ZKVC6#{5GjN!RP~$8wc>oq)?e*D@gpeAfH%VY~l;k}K>h zNz^2{(~GPr_ra1Uwh&{gRJ=#}fk`5yK}=VIN-+e9B1NIsXm(^u4Z~Q2UBB%l=mf;3 zdzIdGe17cCz$y8MeV2ViKWhcc6AOtp{D3;c(cIJun>+8mS$D}-g&x4}L*2gZB3NZy zT9{XHSJsQzS4s;L^%=YsOBG!ON|hL^?v$1Q5?d>)#5KuXc6J@qauZ$K6hzl1d09Nh zIyzH770|)W$PXSN_RpmeSBN|fY^J`GE6nS5jV_yUWrC?{slvX^Hx7E7p;DX4dTlzS zX1|}7h9RIU?#ekyAY%W_IQ0*@-$TaHee>m1!uxBy7dEB0T!+!>#mPnumtHyxI$f&4 z>EEsx)bIyEfy+g-Zv*_RK!kb#cv+=OyW@-<<^?tB9)@rk<sf?)s0Ezyb%vD)t^yb5 z;_lBT@%R@^JpzQ+sH(q=BAaUg61^JN^b>hsF;5IU4$`)Kg!nNIlGvAgP<U#9EFTI) zLdQY6UD`<6w^w`{67R#b0Bv#JFuY;q^JMXkm=wOBI4jLN%A+$J|F7HVMIdH(c>^GD z@e48Af5d<1#LrSsqP{D@fL`o@olyw5tI@*pDcoTD1B}689I7?>h#dhElQ{I9i5*6L z9&S6utIWI$k|;ViPD8oMD1jm7BL;6qX0Ez)L6AMOw%@NbHA;^YA8kde;wZMM<(VMq zd&k-)r^bI3W&O1rQnL45zvu%zHK(IJt2R*Pga{<(uVu+A2C!{gq{wX7v&yE%ToE82 zaX6B~Wm)AOtin&=u^VGmocC=JE1kI4{~Q%QIDgRRV4U}E=B$>=hGRqgbvIS=ur=g2 zHx(6#7)_O!mloF-4v{JDSDvCALuIh0td}%%B~DVK=;)ABGwO&+W?gRUoroy<`Bw_t zFc$4s2?`#1pWYXJ&h^Fhq^|kB;GDhDv<rT=p5b)UbZpzs5{w%wob`kK390O_=kl~5 zDR@*B7Nu<Q<c=^=v))~3MAPMIN+>;3;n9(u&p*(tVI2-zj?`YnsXxs#3wY?(f4oA4 zN=xL%%dygpcW>8A46rH?;61#1t9z~tUf0@uxV9iuU9Kd<ZNk%;U8vB)^bQZNn@i67 zYiq&Hzy|rb7)5g9+123ZLjyhSzgb2AGx1~1^YI5(|LrO%1eqpE&s!DHvf6<drfEZu vV-E_Q^uxsD?#goC!ug8!c$DrZogv3Di@b@v`~o$emjf(JZA?f;UP=E3%Wc+t literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 26dbda6ca4..53788809b6 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -300,6 +300,24 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.cpixmap = pix self.cover_data = cdata + def trim_cover(self, *args): + from calibre.utils.magick import Image + cdata = self.cover_data + if not cdata: + return + im = Image() + im.load(cdata) + im.trim(10) + cdata = im.export('jpg') + pix = QPixmap() + pix.loadFromData(cdata) + self.cover.setPixmap(pix) + self.cover_changed = True + self.cpixmap = pix + self.cover_data = cdata + + + def sync_formats(self): old_extensions, new_extensions, paths = set(), set(), {} for row in range(self.formats.count()): @@ -380,6 +398,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.remove_unused_series) QObject.connect(self.auto_author_sort, SIGNAL('clicked()'), self.deduce_author_sort) + self.trim_cover_button.clicked.connect(self.trim_cover) self.connect(self.author_sort, SIGNAL('textChanged(const QString&)'), self.author_sort_box_changed) self.connect(self.authors, SIGNAL('editTextChanged(const QString&)'), diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 74febf9c29..dbf825e706 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -625,6 +625,17 @@ Using this button to create author sort will change author sort from red to gree </property> </widget> </item> + <item> + <widget class="QToolButton" name="trim_cover_button"> + <property name="toolTip"> + <string>Remove border (if any) from cover</string> + </property> + <property name="icon"> + <iconset resource="../../../../resources/images.qrc"> + <normaloff>:/images/trim.png</normaloff>:/images/trim.png</iconset> + </property> + </widget> + </item> <item> <widget class="QToolButton" name="reset_cover"> <property name="toolTip"> From 231aab95614acf5ab738ee4a949b52d312a28383 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Mon, 20 Sep 2010 09:37:15 -0600 Subject: [PATCH 15/37] WSJ: Don't error out if a single section fails --- resources/recipes/wsj_free.recipe | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/recipes/wsj_free.recipe b/resources/recipes/wsj_free.recipe index 7f3664f1c4..df8234e8e2 100644 --- a/resources/recipes/wsj_free.recipe +++ b/resources/recipes/wsj_free.recipe @@ -54,10 +54,13 @@ class WallStreetJournal(BasicNewsRecipe): def wsj_add_feed(self,feeds,title,url): self.log('Found section:', title) - if url.endswith('whatsnews'): - articles = self.wsj_find_wn_articles(url) - else: - articles = self.wsj_find_articles(url) + try: + if url.endswith('whatsnews'): + articles = self.wsj_find_wn_articles(url) + else: + articles = self.wsj_find_articles(url) + except: + articles = [] if articles: feeds.append((title, articles)) return feeds From 8bd686628966bb3f8948377d8be7b4cadd78b433 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Mon, 20 Sep 2010 09:40:47 -0600 Subject: [PATCH 16/37] ... --- resources/recipes/wsj.recipe | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/resources/recipes/wsj.recipe b/resources/recipes/wsj.recipe index fd5e977d10..88e07bcea3 100644 --- a/resources/recipes/wsj.recipe +++ b/resources/recipes/wsj.recipe @@ -70,13 +70,16 @@ class WallStreetJournal(BasicNewsRecipe): def wsj_add_feed(self,feeds,title,url): self.log('Found section:', title) - if url.endswith('whatsnews'): - articles = self.wsj_find_wn_articles(url) - else: - articles = self.wsj_find_articles(url) + try: + if url.endswith('whatsnews'): + articles = self.wsj_find_wn_articles(url) + else: + articles = self.wsj_find_articles(url) + except: + articles = [] if articles: feeds.append((title, articles)) - return feeds + return feeds def parse_index(self): soup = self.wsj_get_index() @@ -99,7 +102,7 @@ class WallStreetJournal(BasicNewsRecipe): url = 'http://online.wsj.com' + a['href'] feeds = self.wsj_add_feed(feeds,title,url) title = 'What''s News' - url = url.replace('pageone','whatsnews') + url = url.replace('pageone','whatsnews') feeds = self.wsj_add_feed(feeds,title,url) else: title = self.tag_to_string(a) @@ -141,7 +144,7 @@ class WallStreetJournal(BasicNewsRecipe): articles = [] flavorarea = soup.find('div', attrs={'class':lambda x: x and 'ahed' in x}) - if flavorarea is not None: + if flavorarea is not None: flavorstory = flavorarea.find('a', href=lambda x: x and x.startswith('/article')) if flavorstory is not None: flavorstory['class'] = 'mjLinkItem' From ec8164470b8b4ac54b2f16bba57f22a6e856be8b Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Mon, 20 Sep 2010 19:39:33 -0700 Subject: [PATCH 17/37] GwR revisions tweak_epub --- src/calibre/gui2/dialogs/tweak_epub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index c0ad79385b..379352f390 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -25,7 +25,6 @@ class TweakEpub(QDialog, Ui_Dialog): To do: - need way to kill file browser proc in cleanup() - - Windows file browser launch - linux file browser launch ''' @@ -109,6 +108,7 @@ class TweakEpub(QDialog, Ui_Dialog): zipextract(self._epub, self._exploded) self.display_exploded() self.rebuild_button.setEnabled(True) + self.explode_button.setEnabled(False) def rebuild(self): if DEBUG: From 3dfaa2cdaaeda5f56d5d39b02df61b1371942a58 Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Mon, 20 Sep 2010 20:10:17 -0700 Subject: [PATCH 18/37] change to use open_local_file --- src/calibre/gui2/dialogs/tweak_epub.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index 379352f390..a967ca310a 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -15,6 +15,7 @@ from PyQt4.Qt import QDialog, SIGNAL from calibre import prints from calibre.constants import iswindows, isosx, DEBUG +from calibre.gui2 import open_local_file from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog from calibre.libunzip import extract as zipextract from calibre.ptempfile import PersistentTemporaryDirectory @@ -33,7 +34,7 @@ class TweakEpub(QDialog, Ui_Dialog): self._epub = epub self._exploded = None - self._file_browser_proc = None + #self._file_browser_proc = None self._output = None # Run the dialog setup generated from tweak_epub.ui @@ -64,14 +65,6 @@ class TweakEpub(QDialog, Ui_Dialog): ''' if DEBUG: prints("gui2.dialogs.tweak_epub:TweakEpub.cleanup()") - # Kill file browser proc - if self._file_browser_proc: - if DEBUG: - prints(" killing file browser proc") - #self._file_browser_proc.terminate() - #self._file_browser_proc.kill() - #self._file_browser_send_signal(?) - self._file_browser_proc = None # Delete directory containing exploded ePub if self._exploded is not None: @@ -87,6 +80,7 @@ class TweakEpub(QDialog, Ui_Dialog): ''' if DEBUG: prints("gui2.dialogs.tweak_epub:TweakEpub.display_exploded()") + ''' if isosx: cmd = 'open %s' % self._exploded elif iswindows: @@ -97,6 +91,8 @@ class TweakEpub(QDialog, Ui_Dialog): # *** Kovid - need a way of launching this process than can be killed in cleanup() *** self._file_browser_proc = subprocess.Popen(cmd, shell=True) + ''' + open_local_file(self._exploded) def explode(self): if DEBUG: From 88f980ad186859708aceb3907ae59d4052648ff3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Mon, 20 Sep 2010 21:33:43 -0600 Subject: [PATCH 19/37] News download: Don't add inline table of contents when downloading news for the Kindle --- src/calibre/gui2/tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index 7a516bb4ff..2f0452a773 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -217,6 +217,10 @@ def fetch_scheduled_recipe(arg): if 'output_profile' in ps: recs.append(('output_profile', ps['output_profile'], OptionRecommendation.HIGH)) + if ps['output_profile'] == 'kindle': + recs.append(('no_inline_toc', True, + OptionRecommendation.HIGH)) + lf = load_defaults('look_and_feel') if lf.get('base_font_size', 0.0) != 0.0: recs.append(('base_font_size', lf['base_font_size'], From f50085d0388b08c15ef10295b74855bab8fb416b Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Tue, 21 Sep 2010 05:40:33 -0700 Subject: [PATCH 20/37] GR tweaks to tweak-epub --- src/calibre/gui2/actions/tweak_epub.py | 3 +-- src/calibre/gui2/dialogs/tweak_epub.py | 3 --- src/calibre/gui2/dialogs/tweak_epub.ui | 8 ++++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/actions/tweak_epub.py b/src/calibre/gui2/actions/tweak_epub.py index 67ec34c12b..212aff8019 100755 --- a/src/calibre/gui2/actions/tweak_epub.py +++ b/src/calibre/gui2/actions/tweak_epub.py @@ -40,8 +40,7 @@ class TweakEpubAction(InterfaceAction): _('No ePub available. First convert the book to ePub.'), show=True) - - # Launch a modal dialog waiting for user to complete or cancel + # Launch modal dialog waiting for user to tweak or cancel dlg = TweakEpub(self.gui, path_to_epub) if dlg.exec_() == dlg.Accepted: self.update_db(book_id, dlg._output) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index fb3643884b..db6e93fd7a 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -21,8 +21,6 @@ class TweakEpub(QDialog, Ui_Dialog): ''' Display controls for tweaking ePubs - To do: - - need way to kill file browser proc in cleanup() ''' def __init__(self, parent, epub): @@ -30,7 +28,6 @@ class TweakEpub(QDialog, Ui_Dialog): self._epub = epub self._exploded = None - #self._file_browser_proc = None self._output = None # Run the dialog setup generated from tweak_epub.ui diff --git a/src/calibre/gui2/dialogs/tweak_epub.ui b/src/calibre/gui2/dialogs/tweak_epub.ui index 9daa5a8f67..f841bd5eea 100644 --- a/src/calibre/gui2/dialogs/tweak_epub.ui +++ b/src/calibre/gui2/dialogs/tweak_epub.ui @@ -32,7 +32,7 @@ <string>&Explode ePub</string> </property> <property name="icon"> - <iconset resource="../../../../resources/images.qrc"> + <iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> </property> </widget> @@ -49,7 +49,7 @@ <string>&Rebuild ePub</string> </property> <property name="icon"> - <iconset resource="../../../../resources/images.qrc"> + <iconset> <normaloff>:/images/exec.png</normaloff>:/images/exec.png</iconset> </property> </widget> @@ -63,7 +63,7 @@ <string>&Cancel</string> </property> <property name="icon"> - <iconset resource="../../../../resources/images.qrc"> + <iconset> <normaloff>:/images/window-close.png</normaloff>:/images/window-close.png</iconset> </property> </widget> @@ -71,7 +71,7 @@ <item row="0" column="0"> <widget class="QLabel" name="label"> <property name="text"> - <string>First, explode the epub. Then edit is contents by right clicking on the individual files and selecting the editor of your choice. When you are done, click rebuild epub and the epub in your calibre library will be updated with the changes you have made.</string> + <string>Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, selecting 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library.</string> </property> <property name="wordWrap"> <bool>true</bool> From 98f0851ebb3eb4a2711440935e10531e6bdea996 Mon Sep 17 00:00:00 2001 From: GRiker <griker@hotmail.com> Date: Tue, 21 Sep 2010 05:47:22 -0700 Subject: [PATCH 21/37] GR tweaks to tweak-epub --- src/calibre/gui2/dialogs/tweak_epub.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.ui b/src/calibre/gui2/dialogs/tweak_epub.ui index f841bd5eea..ccd33f44ab 100644 --- a/src/calibre/gui2/dialogs/tweak_epub.ui +++ b/src/calibre/gui2/dialogs/tweak_epub.ui @@ -71,7 +71,7 @@ <item row="0" column="0"> <widget class="QLabel" name="label"> <property name="text"> - <string>Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, selecting 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library.</string> + <string>Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, then 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library.</string> </property> <property name="wordWrap"> <bool>true</bool> From 52f85d3ef422c882cde953c57e72e8e3e5fe50ad Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 11:56:09 -0600 Subject: [PATCH 22/37] Cover cache: load images only in the GUI thread to prevent stale files being leftover by set_path due to Windows file locking --- src/calibre/gui2/__init__.py | 30 +++++++++++++++++++++++++++++- src/calibre/gui2/library/models.py | 4 ++-- src/calibre/library/caches.py | 8 +++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index c284900734..66e199b8a0 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' """ The GUI """ -import os, sys +import os, sys, Queue from threading import RLock from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ @@ -296,6 +296,34 @@ class Dispatcher(QObject): def dispatch(self, args, kwargs): self.func(*args, **kwargs) +class FunctionDispatcher(QObject): + ''' + Convenience class to use Qt signals with arbitrary python functions. + By default, ensures that a function call always happens in the + thread this Dispatcher was created in. + ''' + dispatch_signal = pyqtSignal(object, object, object) + + def __init__(self, func, queued=True, parent=None): + QObject.__init__(self, parent) + self.func = func + typ = Qt.QueuedConnection + if not queued: + typ = Qt.AutoConnection if queued is None else Qt.DirectConnection + self.dispatch_signal.connect(self.dispatch, type=typ) + + def __call__(self, *args, **kwargs): + q = Queue.Queue() + self.dispatch_signal.emit(q, args, kwargs) + return q.get() + + def dispatch(self, q, args, kwargs): + try: + res = self.func(*args, **kwargs) + except: + res = None + q.put(res) + class GetMetadata(QObject): ''' Convenience class to ensure that metadata readers are used only in the diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 3370fd4b75..53f701386b 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -12,7 +12,7 @@ from operator import attrgetter from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \ QModelIndex, QVariant, QDate -from calibre.gui2 import NONE, config, UNDEFINED_QDATE +from calibre.gui2 import NONE, config, UNDEFINED_QDATE, FunctionDispatcher from calibre.utils.pyparsing import ParseException from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors from calibre.ptempfile import PersistentTemporaryFile @@ -151,7 +151,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.database_changed.emit(db) if self.cover_cache is not None: self.cover_cache.stop() - self.cover_cache = CoverCache(db) + self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover)) self.cover_cache.start() def refresh_cover(event, ids): if event == 'cover' and self.cover_cache is not None: diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 211baeb634..58edd89cb2 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en' -import re, itertools +import re, itertools, time from itertools import repeat from datetime import timedelta from threading import Thread, RLock @@ -23,10 +23,11 @@ from calibre import fit_image class CoverCache(Thread): - def __init__(self, db): + def __init__(self, db, cover_func): Thread.__init__(self) self.daemon = True self.db = db + self.cover_func = cover_func self.load_queue = Queue() self.keep_running = True self.cache = {} @@ -37,7 +38,8 @@ class CoverCache(Thread): self.keep_running = False def _image_for_id(self, id_): - img = self.db.cover(id_, index_is_id=True, as_image=True) + time.sleep(0.050) # Limit 20/second to not overwhelm the GUI + img = self.cover_func(id_, index_is_id=True, as_image=True) if img is None: img = QImage() if not img.isNull(): From 3f5e0c7e92bf074fdc1cbeed380e68606dcc0dc4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 12:22:50 -0600 Subject: [PATCH 23/37] set_path now *always* commits --- src/calibre/library/database2.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index eb6e8336f9..93320166b7 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -402,7 +402,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): path = path.lower() return path - def set_path(self, index, index_is_id=False, commit=True): + def set_path(self, index, index_is_id=False): ''' Set the path to the directory containing this books files based on its current title and author. If there was a previous directory, its contents @@ -432,7 +432,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if current_path and os.path.exists(spath): # Migrate existing files cdata = self.cover(id, index_is_id=True) if cdata is not None: - open(os.path.join(tpath, 'cover.jpg'), 'wb').write(cdata) + with open(os.path.join(tpath, 'cover.jpg'), 'wb') as f: + f.write(cdata) for format in formats: # Get data as string (can't use file as source and target files may be the same) f = self.format(id, format, index_is_id=True, as_file=False) @@ -442,8 +443,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.add_format(id, format, stream, index_is_id=True, path=tpath, notify=False) self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id)) - if commit: - self.conn.commit() self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True) # Delete not needed directories if current_path and os.path.exists(spath): @@ -1163,7 +1162,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): ','.join([a.replace(',', '|') for a in authors]), row_is_id=True) self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id=True) - self.set_path(id, index_is_id=True, commit=commit) + self.set_path(id, index_is_id=True) if notify: self.notify('metadata', [id]) @@ -1178,7 +1177,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.data.set(id, self.FIELD_MAP['sort'], title_sort(title), row_is_id=True) else: self.data.set(id, self.FIELD_MAP['sort'], title, row_is_id=True) - self.set_path(id, index_is_id=True, commit=commit) + self.set_path(id, index_is_id=True) if commit: self.conn.commit() if notify: From b8edfe539437fdc9b29b0fc281917daa63c3c13e Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 13:44:06 -0600 Subject: [PATCH 24/37] ... --- src/calibre/library/database2.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 93320166b7..1a2eef2c81 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1130,7 +1130,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def set_authors(self, id, authors, notify=True, commit=True): ''' - `authors`: A list of authors. + Note that even if commit is False, the db will still be committed to + because this causes the location of files to change + + :param authors: A list of authors. ''' if not authors: authors = [_('Unknown')] @@ -1167,6 +1170,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.notify('metadata', [id]) def set_title(self, id, title, notify=True, commit=True): + ''' + Note that even if commit is False, the db will still be committed to + because this causes the location of files to change + ''' if not title: return if not isinstance(title, unicode): From 8c74a347d773281b92bafba41662b66af366f789 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 14:56:52 -0600 Subject: [PATCH 25/37] Fix #6899 (Updated recipe for Danas) --- resources/recipes/danas.recipe | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/recipes/danas.recipe b/resources/recipes/danas.recipe index 3543acd684..1e0e319334 100644 --- a/resources/recipes/danas.recipe +++ b/resources/recipes/danas.recipe @@ -49,7 +49,11 @@ class Danas(BasicNewsRecipe): , 'language' : language } - preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + preprocess_regexps = [ + (re.compile(u'\u0110'), lambda match: u'\u00D0') + ,(re.compile(u'\u201c'), lambda match: '"') + ,(re.compile(u'\u201e'), lambda match: '"') + ] keep_only_tags = [dict(name='div', attrs={'id':'left'})] remove_tags = [ From 0e8017ade69c0ad0f3d374db381f6170b175e21f Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 16:45:42 -0600 Subject: [PATCH 26/37] News download: Rationalize cover processing. Fixes #6852 (ebook-convert ieeespectrum.recipe .mobi crashes) --- src/calibre/utils/magick/draw.py | 8 ++--- src/calibre/web/feeds/news.py | 50 +++++++++++++++----------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/calibre/utils/magick/draw.py b/src/calibre/utils/magick/draw.py index ed9e3d3d83..dcf9d7b671 100644 --- a/src/calibre/utils/magick/draw.py +++ b/src/calibre/utils/magick/draw.py @@ -60,15 +60,15 @@ def identify(path): data = open(path, 'rb').read() return identify_data(data) -def add_borders_to_image(path_to_image, left=0, top=0, right=0, bottom=0, - border_color='#ffffff'): +def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0, + border_color='#ffffff', fmt='jpg'): img = Image() - img.open(path_to_image) + img.load(img_data) lwidth, lheight = img.size canvas = create_canvas(lwidth+left+right, lheight+top+bottom, border_color) canvas.compose(img, left, top) - canvas.save(path_to_image) + return canvas.export(fmt) def create_text_wand(font_size, font_path=None): if font_path is None: diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index a140dfbf05..d1e7866198 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -7,7 +7,7 @@ Defines various abstract base classes that can be subclassed to create powerful __docformat__ = "restructuredtext en" -import os, time, traceback, re, urlparse, sys +import os, time, traceback, re, urlparse, sys, cStringIO from collections import defaultdict from functools import partial from contextlib import nested, closing @@ -27,6 +27,7 @@ from calibre.web.fetch.simple import RecursiveFetcher from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.date import now as nowf +from calibre.utils.magick.draw import save_cover_data_to, add_borders_to_image class LoginFailed(ValueError): pass @@ -948,38 +949,36 @@ class BasicNewsRecipe(Recipe): try: cu = self.get_cover_url() except Exception, err: - cu = None self.log.error(_('Could not download cover: %s')%str(err)) self.log.debug(traceback.format_exc()) - if cu is not None: - ext = cu.split('/')[-1].rpartition('.')[-1] - if '?' in ext: - ext = '' - ext = ext.lower() if ext and '/' not in ext else 'jpg' - cpath = os.path.join(self.output_dir, 'cover.'+ext) + else: + cdata = None if os.access(cu, os.R_OK): - with open(cpath, 'wb') as cfile: - cfile.write(open(cu, 'rb').read()) + cdata = open(cu, 'rb').read() else: self.report_progress(1, _('Downloading cover from %s')%cu) - with nested(open(cpath, 'wb'), closing(self.browser.open(cu))) as (cfile, r): - cfile.write(r.read()) - if self.cover_margins[0] or self.cover_margins[1]: - from calibre.utils.magick.draw import add_borders_to_image - add_borders_to_image(cpath, - left=self.cover_margins[0],right=self.cover_margins[0], - top=self.cover_margins[1],bottom=self.cover_margins[1], - border_color=self.cover_margins[2]) - if ext.lower() == 'pdf': + with closing(self.browser.open(cu)) as r: + cdata = r.read() + if not cdata: + return + ext = cu.split('/')[-1].rpartition('.')[-1].lower().strip() + if ext == 'pdf': from calibre.ebooks.metadata.pdf import get_metadata - stream = open(cpath, 'rb') + stream = cStringIO.StringIO(cdata) + cdata = None mi = get_metadata(stream) - cpath = None if mi.cover_data and mi.cover_data[1]: - cpath = os.path.join(self.output_dir, - 'cover.'+mi.cover_data[0]) - with open(cpath, 'wb') as f: - f.write(mi.cover_data[1]) + cdata = mi.cover_data[1] + if not cdata: + return + if self.cover_margins[0] or self.cover_margins[1]: + cdata = add_borders_to_image(cdata, + left=self.cover_margins[0],right=self.cover_margins[0], + top=self.cover_margins[1],bottom=self.cover_margins[1], + border_color=self.cover_margins[2]) + + cpath = os.path.join(self.output_dir, 'cover.jpg') + save_cover_data_to(cdata, cpath) self.cover_path = cpath def download_cover(self): @@ -1422,7 +1421,6 @@ class CalibrePeriodical(BasicNewsRecipe): return br def download(self): - import cStringIO self.log('Fetching downloaded recipe') try: raw = self.browser.open_novisit( From b958545a8af34885c7053e14f752fed2ba548627 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 16:47:28 -0600 Subject: [PATCH 27/37] ... --- src/calibre/ebooks/mobi/writer.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 5d5de7b153..23f92d1fd2 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -1574,14 +1574,15 @@ class MobiWriter(object): id = unicode(oeb.metadata.cover[0]) item = oeb.manifest.ids[id] href = item.href - index = self._images[href] - 1 - exth.write(pack('>III', 0xc9, 0x0c, index)) - exth.write(pack('>III', 0xcb, 0x0c, 0)) - nrecs += 2 - index = self._add_thumbnail(item) - if index is not None: - exth.write(pack('>III', 0xca, 0x0c, index - 1)) - nrecs += 1 + if href in self._images: + index = self._images[href] - 1 + exth.write(pack('>III', 0xc9, 0x0c, index)) + exth.write(pack('>III', 0xcb, 0x0c, 0)) + nrecs += 2 + index = self._add_thumbnail(item) + if index is not None: + exth.write(pack('>III', 0xca, 0x0c, index - 1)) + nrecs += 1 exth = exth.getvalue() trail = len(exth) % 4 From 62c652869a30d136278356557db397b7417c06bc Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 17:13:22 -0600 Subject: [PATCH 28/37] Fix #5900 (Alex reader from spring design is reconized as N516 and send books to wrong location) --- src/calibre/devices/hanvon/driver.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py index 75728a94ea..6291864b86 100644 --- a/src/calibre/devices/hanvon/driver.py +++ b/src/calibre/devices/hanvon/driver.py @@ -11,6 +11,10 @@ import re from calibre.devices.usbms.driver import USBMS +def is_alex(device_info): + return device_info[3] == u'Linux 2.6.28 with pxa3xx_u2d' and \ + device_info[4] == u'Seleucia Disk' + class N516(USBMS): name = 'N516 driver' @@ -34,6 +38,9 @@ class N516(USBMS): EBOOK_DIR_MAIN = 'e_book' SUPPORTS_SUB_DIRS = True + def can_handle(self, device_info, debug=False): + return not is_alex(device_info) + class THEBOOK(N516): name = 'The Book driver' gui_name = 'The Book' @@ -61,6 +68,9 @@ class ALEX(N516): EBOOK_DIR_MAIN = 'eBooks' SUPPORTS_SUB_DIRS = True + def can_handle(self, device_info, debug=False): + return is_alex(device_info) + class AZBOOKA(ALEX): name = 'Azbooka driver' @@ -74,6 +84,9 @@ class AZBOOKA(ALEX): EBOOK_DIR_MAIN = '' + def can_handle(self, device_info, debug=False): + return not is_alex(device_info) + class EB511(USBMS): name = 'Elonex EB 511 driver' From 9656798e3881757f06bacb8bacee860a42f3be00 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 19:00:50 -0600 Subject: [PATCH 29/37] Fix #6522 (Cannot recognize my Nexus One with 2.2 Cyanogen Root) --- src/calibre/devices/android/driver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 7a451112c0..c9c0827759 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -29,7 +29,9 @@ class ANDROID(USBMS): # Sony Ericsson 0xfce : { 0xd12e : [0x0100]}, - 0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, + # Google + 0x18d1 : { 0x4e11 : [0x0100, 0x226, 0x227], 0x4e12: [0x0100, 0x226, + 0x227]}, # Samsung 0x04e8 : { 0x681d : [0x0222, 0x0400], From 78490d39bd5f98eb7f6f19f62a9ff451862b08aa Mon Sep 17 00:00:00 2001 From: Timothy Legge <timlegge@gmail.com> Date: Tue, 21 Sep 2010 22:17:50 -0300 Subject: [PATCH 30/37] Add support for setting the ReadStatus to Read and correctly deal with empty collections --- src/calibre/devices/kobo/driver.py | 120 +++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 31 deletions(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 1171b74f5c..104553b675 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -98,6 +98,8 @@ class KOBO(USBMS): if readstatus == 1: playlist_map[lpath]= "Im_Reading" + elif readstatus == 2: + playlist_map[lpath]= "Read" path = self.normalize_path(path) # print "Normalized FileName: " + path @@ -441,43 +443,99 @@ class KOBO(USBMS): connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') cursor = connection.cursor() - # Reset Im_Reading list in the database - if oncard == 'carda': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\'' - elif oncard != 'carda' and oncard != 'cardb': - query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\'' + + if collections: + # Process any collections that exist + for category, books in collections.items(): + if category == 'Im_Reading': + # Reset Im_Reading list in the database + if oncard == 'carda': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\'' + elif oncard != 'carda' and oncard != 'cardb': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\'' - try: - cursor.execute (query) - except: - debug_print('Database Exception: Unable to reset Im_Reading list') - raise - else: -# debug_print('Commit: Reset Im_Reading list') - connection.commit() - - for category, books in collections.items(): - if category == 'Im_Reading': - for book in books: -# debug_print('Title:', book.title, 'lpath:', book.path) - book.device_collections = ['Im_Reading'] - - extension = os.path.splitext(book.path)[1] - ContentType = self.get_content_type_from_extension(extension) - - ContentID = self.contentid_from_path(book.path, ContentType) - datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) - - t = (datelastread,ContentID,) - try: - cursor.execute('update content set ReadStatus=1,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t) + cursor.execute (query) except: - debug_print('Database Exception: Unable create Im_Reading list') + debug_print('Database Exception: Unable to reset Im_Reading list') raise else: +# debug_print('Commit: Reset Im_Reading list') connection.commit() - # debug_print('Database: Commit create Im_Reading list') + + for book in books: +# debug_print('Title:', book.title, 'lpath:', book.path) + book.device_collections = ['Im_Reading'] + + extension = os.path.splitext(book.path)[1] + ContentType = self.get_content_type_from_extension(extension) + + ContentID = self.contentid_from_path(book.path, ContentType) + datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) + + t = (datelastread,ContentID,) + + try: + cursor.execute('update content set ReadStatus=1,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t) + except: + debug_print('Database Exception: Unable create Im_Reading list') + raise + else: + connection.commit() + # debug_print('Database: Commit create Im_Reading list') + if category == 'Read': + # Reset Im_Reading list in the database + if oncard == 'carda': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID like \'file:///mnt/sd/%\'' + elif oncard != 'carda' and oncard != 'cardb': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID not like \'file:///mnt/sd/%\'' + + try: + cursor.execute (query) + except: + debug_print('Database Exception: Unable to reset Im_Reading list') + raise + else: +# debug_print('Commit: Reset Im_Reading list') + connection.commit() + + for book in books: +# debug_print('Title:', book.title, 'lpath:', book.path) + book.device_collections = ['Read'] + + extension = os.path.splitext(book.path)[1] + ContentType = self.get_content_type_from_extension(extension) + + ContentID = self.contentid_from_path(book.path, ContentType) +# datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) + + t = (ContentID,) + + try: + cursor.execute('update content set ReadStatus=2,FirstTimeReading=\'true\' where BookID is Null and ContentID = ?', t) + except: + debug_print('Database Exception: Unable set book as Rinished') + raise + else: + connection.commit() +# debug_print('Database: Commit set ReadStatus as Finished') + else: # No collections + # Since no collections exist the ReadStatus needs to be reset to 0 (Unread) + print "Reseting ReadStatus to 0" + # Reset Im_Reading list in the database + if oncard == 'carda': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\'' + elif oncard != 'carda' and oncard != 'cardb': + query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\'' + + try: + cursor.execute (query) + except: + debug_print('Database Exception: Unable to reset Im_Reading list') + raise + else: +# debug_print('Commit: Reset Im_Reading list') + connection.commit() cursor.close() connection.close() From f12f69ef5edc3c9395abc1862f62f2405245fe2d Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 21:18:32 -0600 Subject: [PATCH 31/37] superesportes by Luciano Furtado. Fixes #405 (New news feed) --- resources/recipes/superesportes.recipe | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 resources/recipes/superesportes.recipe diff --git a/resources/recipes/superesportes.recipe b/resources/recipes/superesportes.recipe new file mode 100644 index 0000000000..49289f188d --- /dev/null +++ b/resources/recipes/superesportes.recipe @@ -0,0 +1,79 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Luciano Furtado <lrfurtado at yahoo.com.br>' +''' +www.superesportes.com.br +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class SuperEsportesRecipe(BasicNewsRecipe): + + title = u'www.superesportes.com.br' + description = u'Superesportes - Not√≠cias do esporte no Brasil e no mundo' + __author__ = 'Luciano Furtado' + language = 'pt' + category = 'esportes, Brasil' + no_stylesheets = True + oldest_article = 7 + + use_embedded_content=0 + max_articles_per_feed = 10 + cover_url = 'http://imgs.mg.superesportes.com.br/superesportes_logo.png' + + extra_css = 'div.info_noticias h1 { font-size: 100% }' + + + + remove_tags = [ + dict(name='div',attrs={'class':'topo'}), + dict(name='div',attrs={'class':'rodape'}), + dict(name='div',attrs={'class':'navegacao'}), + dict(name='div',attrs={'class':'lateral2'}), + dict(name='div',attrs={'class':'leia_mais'}), + dict(name='div',attrs={'id':'comentar'}), + dict(name='div',attrs={'id':'vrumelc_noticia'}), + dict(name='div',attrs={'class':'compartilhe'}), + dict(name='div',attrs={'class':'linha_noticias'}), + dict(name='div',attrs={'class':'botoes_noticias'}), + dict(name='div',attrs={'class':'barra_time bg_time'}), + ] + + + + def parse_index(self): + feeds = [] + sections = [ + (u'Atletico', 'http://www.df.superesportes.com.br/futebol/atletico-mg/capa_atletico_mg/index.shtml'), + (u'Botafogo', 'http://www.df.superesportes.com.br/futebol/botafogo/capa_botafogo/index.shtml'), + (u'Corinthinas', 'http://www.df.superesportes.com.br/futebol/corinthians/capa_corinthians/index.shtml'), + (u'Cruzeiro', 'http://www.df.superesportes.com.br/futebol/cruzeiro/capa_cruzeiro/index.shtml'), + (u'Flamengo', 'http://www.df.superesportes.com.br/futebol/flamengo/capa_flamengo/index.shtml'), + (u'Fluminense', 'http://www.df.superesportes.com.br/futebol/fluminense/capa_fluminense/index.shtml'), + (u'Palmeiras', 'http://www.df.superesportes.com.br/futebol/palmeiras/capa_palmeiras/index.shtml'), + (u'Santos', 'http://www.df.superesportes.com.br/futebol/santos/capa_santos/index.shtml'), + (u'S√£o Paulo', 'http://www.df.superesportes.com.br/futebol/sao-paulo/capa_sao_paulo/index.shtml'), + (u'Vasco', 'http://www.df.superesportes.com.br/futebol/vasco/capa_vasco/index.shtml'), + ] + + + for section, url in sections: + current_articles = [] + + soup = self.index_to_soup(url) + latestNews = soup.find(name='ul',attrs={'class': 'lista_ultimas_noticias'}) + + for li_tag in latestNews.findAll(name='li'): + a_tag = li_tag.find('a', href= True) + if a_tag is None: + continue + title = self.tag_to_string(a_tag) + url = a_tag.get('href', False) + self.log("\n\nFound title: " + title + "\nUrl: " + url + "\nSection: " + section) + current_articles.append({'title': title, 'url': url, 'description': title, 'date':''}) + + if current_articles: + feeds.append((section, current_articles)) + + + return feeds + From 5cab12e26f3decf1b3114d10aa463cfc64da0549 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Tue, 21 Sep 2010 22:04:01 -0600 Subject: [PATCH 32/37] Don't use special tooltip style on linux. Draw yellow color on splitter handle in background --- src/calibre/gui2/__init__.py | 12 ------------ src/calibre/gui2/widgets.py | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 66e199b8a0..c0c7b0a9ed 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -603,18 +603,6 @@ class Application(QApplication): self._file_open_paths = [] self._file_open_lock = RLock() - if islinux: - self.setStyleSheet(''' - QToolTip { - border: 2px solid black; - padding: 5px; - border-radius: 10px; - opacity: 200; - background-color: #e1e1ff; - color: black; - } - ''') - def _send_file_open_events(self): with self._file_open_lock: if self._file_open_paths: diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 60224aefc7..5efce74c08 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -863,11 +863,11 @@ class SplitterHandle(QSplitterHandle): self.update() def paintEvent(self, ev): - QSplitterHandle.paintEvent(self, ev) if self.highlight: painter = QPainter(self) painter.setClipRect(ev.rect()) painter.fillRect(self.rect(), Qt.yellow) + QSplitterHandle.paintEvent(self, ev) def mouseDoubleClickEvent(self, ev): self.double_clicked.emit(self) From edb26bcfa81eee64476cbcffec92e312fccc8329 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Wed, 22 Sep 2010 09:09:55 -0600 Subject: [PATCH 33/37] ... --- src/calibre/gui2/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 5efce74c08..60224aefc7 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -863,11 +863,11 @@ class SplitterHandle(QSplitterHandle): self.update() def paintEvent(self, ev): + QSplitterHandle.paintEvent(self, ev) if self.highlight: painter = QPainter(self) painter.setClipRect(ev.rect()) painter.fillRect(self.rect(), Qt.yellow) - QSplitterHandle.paintEvent(self, ev) def mouseDoubleClickEvent(self, ev): self.double_clicked.emit(self) From ced6322e3f4ae0d9b620fcd989364252e7f57255 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Wed, 22 Sep 2010 09:21:54 -0600 Subject: [PATCH 34/37] Rmf24.pl by Tomasz Dlugosz and Gazeta Pomorska by Richard z --- resources/images/news/fronda.png | Bin 0 -> 646 bytes resources/images/news/gazeta_pomorska.png | Bin 0 -> 323 bytes resources/images/news/legeartis.png | Bin 0 -> 634 bytes resources/images/news/michalkiewicz.png | Bin 1406 -> 1017 bytes resources/images/news/rmf24_ESKN.png | Bin 0 -> 722 bytes resources/images/news/rmf24_fakty.png | Bin 0 -> 722 bytes resources/recipes/gazeta_pomorska.recipe | 104 ++++++++++++++++++++++ resources/recipes/nczas.recipe | 35 -------- resources/recipes/rmf24_ESKN.recipe | 46 ++++++++++ resources/recipes/rmf24_fakty.recipe | 44 +++++++++ 10 files changed, 194 insertions(+), 35 deletions(-) create mode 100644 resources/images/news/fronda.png create mode 100644 resources/images/news/gazeta_pomorska.png create mode 100644 resources/images/news/legeartis.png create mode 100644 resources/images/news/rmf24_ESKN.png create mode 100644 resources/images/news/rmf24_fakty.png create mode 100644 resources/recipes/gazeta_pomorska.recipe delete mode 100644 resources/recipes/nczas.recipe create mode 100644 resources/recipes/rmf24_ESKN.recipe create mode 100644 resources/recipes/rmf24_fakty.recipe diff --git a/resources/images/news/fronda.png b/resources/images/news/fronda.png new file mode 100644 index 0000000000000000000000000000000000000000..c332bbda497ebea167e6db230b2000e094538d63 GIT binary patch literal 646 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#P=kx;TbdoSr(#HbcZwqHX^B^z?Ma34YSv z2i*>O1<hHzMC7QGccg{xB(J8a35tglwT`*%@q5(RdeQ5#|EkxQmTuj^w~|kX&t%Qf zcW>^haooH8=FOQs=WFg;E1RsEcV|t6Ls7m8!=+j4b&}ZkT@jdR{$6gHbLi1C2F7>( z9=U$j(z-T4twZg>yF^P7L6;>f4X<jQ&7ZaY$IdSqGiv6st@2`8p`~?dssDNJhQF)7 zw{!5uSk2wSV9d-CnwglJ5Osbz^Rw$#41xlIVP$bsJ}-}{_`ieYN8#(lyK0{s_?7GW z4?lT*w78mK*{M_81KB5RssHyYj>A%s<=mco`x4nNr}{S@ox$8R^V6GKm-p@7{4MXV z=z(i<&Hqk#%3^*;)Y$f>lMWY)W5~y6r*8R1Dx8)nK7J<9mQA2VNvzbtw<GV|?cLFT zbxw24d@Ogu#&Hd6M%Ke`=YA=iS2pYysZ=@UF^}DegXR1zE}rnE+jm-J<Z~=|70CF< za;kqC_vR)0zuV_sS$}!wpQ6$Ppi3AemFI>}bU0pGV&!)%d%k(q?5&Z<jCXyIUsV<2 z?p>I)f7i3tFY90ZV><00t5;^lpA3vg)e_f;l9a@fRIB8oR3OD*WMF8ZYhbBsWE5gx uX=P|(Wo)5qU}j}t@Ww)1A4NlMeoAIqC2kFKQ`du%5re0zpUXO@geCwyI|fbw literal 0 HcmV?d00001 diff --git a/resources/images/news/gazeta_pomorska.png b/resources/images/news/gazeta_pomorska.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7099d7f3e33461bf0e4153f5c8e7fcb7114afe GIT binary patch literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VRoE%RV#}JFt$u)m|JgaBpVh~oH#oo@SW571O z)x7J-cHwAA2_SgF#h;W>btS+571NAo`Rdya8SZNA|Mm8v)I{E+FIi`BzYu4;afs=r z=!tV~e9Sz|8-B{i3b%<Tm=#7=Bwy{}H|SGKJ;lqwu)15@WYXCH9-zIdC9V-ADTyVi zR>?)FK#IZ0z|cU~z*5)9D8#_h%Fx`(*jU%V%*wz(-b5k-MMG|WN@iLmP=kS)uA!l> jfpLg|iIu6Dm7$TYfjLma)decvKn)C@u6{1-oD!M<tl(bW literal 0 HcmV?d00001 diff --git a/resources/images/news/legeartis.png b/resources/images/news/legeartis.png new file mode 100644 index 0000000000000000000000000000000000000000..fd9001d9ed3e8969a4525e4d8ae6b4a33b5ad41d GIT binary patch literal 634 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#J6Ox;TbdoSr+`)>}DHq-}n6p4nsx^HTzM zlsu-XrX(_Jiz#trCGWibga6W!O*2itDNPH`OrCnoDd#Wq#e28Zn6q6jq%_SxEigBR z`>x`}6w!E#f(m_`=dH#4M#lnHhB(<NTrV~4d)#tt2LIH@7Kv-7-^ki(a8@mGxwFn} zSu4xQCtddb$*#GtzT&DyT*QIsIJFyhT{TTN)ZDM<4e$~?|MbF3lYP%W2CV02)mR$G z?9x(UcfS9y^FCd@qVkfKjt>tiTb@7f@QCMS>EJN3>WbAXj$QpRy=>aAGd#yHU%vD1 z%^qQerfajR6>O}Ni;5?2OcKmbZC$;0?{~&`(!4?rW!2KVLzL<Z4n!<Gkh-&H=gvy$ z1$ufOH_eobi~Z+aOEEWNFj(Ge=decnL(u$5Cr|Ky`!1<jqdxEVuQDdj<vX*t@4R!% z$>m4%+DD<QRqFpMrQL28{_^fx)xS*j>}-?k*LS`xn>l0FqtmanVwVQkYAQEelS<}c zc)V1|zT0(!j@4T;7RLG0rzuQzisN~`yY<DvO&({?Gf#N%Kj`qeNKH{ysb@Rat(Ip@ zp1)5<>XgqzVBD#exJHzuB$lLFB^RXvDF!10Ljzp{OI;(Q5CcmqLm)EOH88U>FxWnw gyA?%4ZhlH;S|x4`$4+gw1ZrULboFyt=akR{0F^8KUjP6A literal 0 HcmV?d00001 diff --git a/resources/images/news/michalkiewicz.png b/resources/images/news/michalkiewicz.png index a87f30f5a37a930674e36cb0d4930a751e76f8fc..cfa61c7fd6982d98c9270d93e2f5cd192d52ff31 100644 GIT binary patch literal 1017 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87?_njT^vI!PRCA+_K{%~Y1=>ZZsf8v=Qd2& zyM53hQ}9Ma)UB?Jy($t{mYD2Wko!nAZqXG9;m55RzaDr`KGy!L!DdFrmQKaSSIuEd z6HGFDo31)c^@{b{eOq+N-Pya}#@&d#q4)Ukm8)ND=02J8x&HYd_XlY+UPf-7G+V8% zo_%|@?4FCeyZ8Nm=eS~1R(H2xoYb$TU2pB|{+VfVZ0BQsv$x#5w`}Uazp*B}V((~3 zU%9t!X3!mnPnk=E!z(`hO}uAo*IJ=%Dc7x(a_!~)`>kr05!zE%mc0&$ZNB@m;j7X- z+qrk{?rC+_|EBzJz77WyxA^oM*MA#iFVzZ(JUm4->{6vhiomT|YmUW8#ZIg|GSk7} z^5$ikvpBAqrfjmbt9`HW?hx1SvK-fSxehwgF2R~=_56YFKNnrya&fya`)=Of`=9uj zu{T7iZj|Xxo>i&lyuj6%VTtSH;?u0_yuaGiFJR7bU1aYU&BSojY2N1J{TGYfjr(fm zY5S=&aV6hpeYs8iMg5}LvHYeAyG^nWFt|?m_c<XmXU4qg^V`_Jf4El6e)7-MWm}Ry zG#pRzsFL<86m{2#_~`SwLbvhbLFGF)ubq4Dtn9<ztGIQm(T}I;CoXcVHFY)msdHqq zMrqwTtx%D3v27AF%A}Jj66ZCnQ?|;fDao1g<PXooE2fWL^0+B_mj_#GUOmem-y0aS z=bYQ#&d(oHxi)Gnd~v@cI-02^$g^OFgrtqfd28l9-(CrRd8X%f?zzA#lXv&8Iatn4 z{nl?_8q|5tfm_)4{)6f)hCl(9Piz|;k1jQFNX~tF%($@GdGUSW4`G+zlwS_YcJ>js zG_2!#yv{D>>+QArF+M6C?n*zdr~EKJcrxBubmGF3-aY?nX1X)`=^b64c=^zYxqJ6~ z_?1}qLo;LMj_S|8tD@7A9*JM`c74;;rE#)jlFui%nVF^R=fc~58f-eXtS^s2EbY94 z-&~31drP_h&9$C+`gz5h4;)wJyf3`{8fJFU*3NJKpO&56pZENIwwa~bu!+04qjPD& z(w83>$8X-2wc()R#*^Cx86vN4(AHuQPpe~oaKM<kBFkpZT&tt@R+|l(4({zw0%jf6 z64!{5l*E!$tK_0oAjM#0U}&IgV5w_l6k=d$WoT(-Vy<goW@TXTznt|UiiX_$l+3hB W+#33{_QnD=FnGH9xvX<aXaWE^VZ<Z= literal 1406 zcmeH{X-t%75XXNkP*z}NMOZ;;+2xRhJpdPri&AY@QN&e2Ii;vYj#kR4wX*NNN-%&1 zPas~THqmIoBW<W!lWNeG(AYFJq%^6viD?XrY1Fpr2T9haFSbqkxnJ9v{AcF*XC{-( zOdjx2RI5d?YO4J~2N)G76nX`d{G=@MdEx#vf5jdB<5cyGQ6LSoq_Ue3b2ZIp$62%O z7rJkF>HJ(Uc*lcu%8R3_pM3cuji&{zXT4nhew?lS0-d9aGd~ErFA9oX=ZSOnVorAw zmfS+)iRTcRP4TWP)ExXBTb@$i^_bn4ynJ?BFmPWGGyeb;Jx^)=NT4z<!&G#Hp05S* z3u;L(c9WO0g7SUioEiR+SrOAw`>Xi&!E<z>f%y7`qY3iE(Qt)A*AtpLpYoN*OQzjM z>oq~=DZ!K(cB))AG3cXkRJ1el^CR3ljw^l6Vb5_<d-x{`JI3hkIYwFAZSvdh&~oA- zp3zZOZyaJ(%Qv)r@Fk_Ye`CubfpmC+p}t-!%hpj>ThAAFf92qX3Bt3=aW;>z>r+8x z-y=%)++_Ws2bi3#)P4Avl<YkUy2#jcj+(9s>W&L~ZwShFUckNgB=rX_arK$t**y=V zL)RI;^&Lk)zD|4J2!jIyWR#r8So8@SYL3yT%%@=geG-ipGSW=w6H}O*WW!?1B-Xiu z&LKf=`(2v$kFm1hD5ZNnba%9I>!Cmh@!|&l_W!tw|HJ>-1UC9dH>8^9o0C%$n`BqT zmgWA@Zzm_Gnk{nTD!Fm#YH23Z%jHyyMSgW<{X1@lw19b`7Fm{4{0yb9xhtix+WbvA zSx#6R_0o#XZ@H~AHxz{GWP41IVOe0{!c5ca>c}Y5lxenv(zGm9+F~jAm9UIxYk18J z=hVe7&x??xLcKmO!FQb@Z<Z}Uy<U<+f@6z~+jWMR_p<`jIg*r{JJ%RLN2Lm0^lrd= zZ%9^!*&Y-#HP$Cc6{gWjid6fml6;c1vo-1Q)0C$*Br<zRb4h-LX7*N{Jed&Zmt9n+ hEsHIRtCA-PYl{8N2#sc2<Qn<ku6w6aIH>%ge*nRV6N>-< diff --git a/resources/images/news/rmf24_ESKN.png b/resources/images/news/rmf24_ESKN.png new file mode 100644 index 0000000000000000000000000000000000000000..53ad00078a964edca84fbfb957006de84ec6ab90 GIT binary patch literal 722 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#M$hx;TbdoW45AJ6kwWqIG_LTG3U3jX|m= zjz_t61r(~fx@s?4z|9?7(U`!huOMn3Dv%k>GJR2=mxK8t1*IFkADeVTSsYg?X>3u7 zEm<UbG$42R+?o0_uOIdK%Uczuuit+7;fDkD`dbTF9_Ivh8L>$(S}zdbp1~lsA~}Hl z6${J7_p_Kd>@F=j;KsO8<6_67=M4-VOxNyftZ4SgaIV>PVd28Zrwl`$`>uIZ`s@8W zR^by3v1cz;Z98&mUfmr@VHOwTM8=M^c8vf%5%ayvYOk^+2YYKy<-fT7x8b(AOAGe# zKNAx^?>E^*dV!;fz7B&)u7GLAe}__=!xNvkpL~8y;_sBjZ;Q;g_uYGTaQD~5rzQc+ zoDyb#1jD-3R{9iViQV>-Q;*k~=we)w*6>!`f5V|Qg4gBx<JKyk=CRN*k=Xd*X6B5C z8L{Ek6_!yC>dy0i%lYRMlxe27{?l#Gf~o}jci%<!XK~J8y=d_<_C^K&PGK9zCz_kq zeVsDnCHE$cYQK)Jr+$AjnrPm!$MxOUbyJ<Zx1TUja_)$KDA`cN{&SJY&Ka!s%Y@HV zKGbPzs@W{8l-TgP>G845s7H%yIa+E@GKc6~v|xyGE<V=0#=S-GYh?xpL)@7M(ZQ$w zzdD=Lon&y;Q}xc?gVz=uR8=s3pX~17+m$3V!%O~2ddoTcRrdX^m+SwtEENN$3DpwU zh?11Vl2ohYqEsNoU}Ruuple{MYh)B+U}<G&W(8y$07-*eoZ6>RH00)|WTsW(*04Hc Rayd`~gQu&X%Q~loCIBf=9ykC1 literal 0 HcmV?d00001 diff --git a/resources/images/news/rmf24_fakty.png b/resources/images/news/rmf24_fakty.png new file mode 100644 index 0000000000000000000000000000000000000000..53ad00078a964edca84fbfb957006de84ec6ab90 GIT binary patch literal 722 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#M$hx;TbdoW45AJ6kwWqIG_LTG3U3jX|m= zjz_t61r(~fx@s?4z|9?7(U`!huOMn3Dv%k>GJR2=mxK8t1*IFkADeVTSsYg?X>3u7 zEm<UbG$42R+?o0_uOIdK%Uczuuit+7;fDkD`dbTF9_Ivh8L>$(S}zdbp1~lsA~}Hl z6${J7_p_Kd>@F=j;KsO8<6_67=M4-VOxNyftZ4SgaIV>PVd28Zrwl`$`>uIZ`s@8W zR^by3v1cz;Z98&mUfmr@VHOwTM8=M^c8vf%5%ayvYOk^+2YYKy<-fT7x8b(AOAGe# zKNAx^?>E^*dV!;fz7B&)u7GLAe}__=!xNvkpL~8y;_sBjZ;Q;g_uYGTaQD~5rzQc+ zoDyb#1jD-3R{9iViQV>-Q;*k~=we)w*6>!`f5V|Qg4gBx<JKyk=CRN*k=Xd*X6B5C z8L{Ek6_!yC>dy0i%lYRMlxe27{?l#Gf~o}jci%<!XK~J8y=d_<_C^K&PGK9zCz_kq zeVsDnCHE$cYQK)Jr+$AjnrPm!$MxOUbyJ<Zx1TUja_)$KDA`cN{&SJY&Ka!s%Y@HV zKGbPzs@W{8l-TgP>G845s7H%yIa+E@GKc6~v|xyGE<V=0#=S-GYh?xpL)@7M(ZQ$w zzdD=Lon&y;Q}xc?gVz=uR8=s3pX~17+m$3V!%O~2ddoTcRrdX^m+SwtEENN$3DpwU zh?11Vl2ohYqEsNoU}Ruuple{MYh)B+U}<G&W(8y$07-*eoZ6>RH00)|WTsW(*04Hc Rayd`~gQu&X%Q~loCIBf=9ykC1 literal 0 HcmV?d00001 diff --git a/resources/recipes/gazeta_pomorska.recipe b/resources/recipes/gazeta_pomorska.recipe new file mode 100644 index 0000000000..083f5cbeed --- /dev/null +++ b/resources/recipes/gazeta_pomorska.recipe @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +# # Przed uzyciem przeczytaj komentarz w sekcji "feeds" + +__license__ = 'GPL v3' +__copyright__ = u'2010, Richard z forum.eksiazki.org' +'''pomorska.pl''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class GazetaPomorska(BasicNewsRecipe): + title = u'Gazeta Pomorska' + publisher = u'Gazeta Pomorska' + description = u'Kujawy i Pomorze - wiadomo\u015bci' + language = 'pl' + __author__ = u'Richard z forum.eksiazki.org' + # # (dziekuje t3d z forum.eksiazki.org za testy) + oldest_article = 2 + max_articles_per_feed = 20 + no_stylesheets = True + remove_javascript = True + preprocess_regexps = [ + (re.compile(r'<a href="http://maps.google[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''), + (re.compile(r'[<Bb >]*Poznaj opinie[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''), + (re.compile(r'[<Bb >]*Przeczytaj[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''), + (re.compile(r'[<Bb >]*Wi.cej informacji[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''), + (re.compile(r'<a href[^>]*>[<Bb >]*Wideo[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''), + (re.compile(r'<a href[^>]*>[<Bb >]*KLIKNIJ TUTAJ[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: '') + ] + + feeds = [ +# # Tutaj jest wymieniona lista kategorii jakie mozemy otrzymywac z Gazety +# # Pomorskiej, po jednej kategorii w wierszu. Jesli na poczatku danego wiersza +# # znajduje sie jeden znak "#", oznacza to ze kategoria jest zakomentowana +# # i nie bedziemy jej otrzymywac. Jesli chcemy ja otrzymywac nalezy usunac +# # znak # z jej wiersza. +# # Jesli subskrybujemy wiecej niz jedna kategorie, na koncu wiersza z kazda +# # kategoria musi sie znajdowac niezakomentowany przecinek, z wyjatkiem +# # ostatniego wiersza - ma byc bez przecinka na koncu. +# # Rekomendowane opcje wyboru kategorii: +# # 1. PomorskaRSS - wiadomosci kazdego typu, lub +# # 2. Region + wybrane miasta, lub +# # 3. Wiadomosci tematyczne. +# # Lista kategorii: + + # # PomorskaRSS - wiadomosci kazdego typu, zakomentuj znakiem "#" + # # przed odkomentowaniem wiadomosci wybranego typu: + (u'PomorskaRSS', u'http://www.pomorska.pl/rss.xml') + + # # wiadomosci z regionu nie przypisane do okreslonego miasta: + # (u'Region', u'http://www.pomorska.pl/region.xml'), + + # # wiadomosci przypisane do miast: + # (u'Bydgoszcz', u'http://www.pomorska.pl/bydgoszcz.xml'), + # (u'Nak\u0142o', u'http://www.pomorska.pl/naklo.xml'), + # (u'Koronowo', u'http://www.pomorska.pl/koronowo.xml'), + # (u'Solec Kujawski', u'http://www.pomorska.pl/soleckujawski.xml'), + # (u'Grudzi\u0105dz', u'http://www.pomorska.pl/grudziadz.xml'), + # (u'Inowroc\u0142aw', u'http://www.pomorska.pl/inowroclaw.xml'), + # (u'Toru\u0144', u'http://www.pomorska.pl/torun.xml'), + # (u'W\u0142oc\u0142awek', u'http://www.pomorska.pl/wloclawek.xml'), + # (u'Aleksandr\u00f3w Kujawski', u'http://www.pomorska.pl/aleksandrow.xml'), + # (u'Brodnica', u'http://www.pomorska.pl/brodnica.xml'), + # (u'Che\u0142mno', u'http://www.pomorska.pl/chelmno.xml'), + # (u'Chojnice', u'http://www.pomorska.pl/chojnice.xml'), + # (u'Ciechocinek', u'http://www.pomorska.pl/ciechocinek.xml'), + # (u'Golub Dobrzy\u0144', u'http://www.pomorska.pl/golubdobrzyn.xml'), + # (u'Mogilno', u'http://www.pomorska.pl/mogilno.xml'), + # (u'Radziej\u00f3w', u'http://www.pomorska.pl/radziejow.xml'), + # (u'Rypin', u'http://www.pomorska.pl/rypin.xml'), + # (u'S\u0119p\u00f3lno', u'http://www.pomorska.pl/sepolno.xml'), + # (u'\u015awiecie', u'http://www.pomorska.pl/swiecie.xml'), + # (u'Tuchola', u'http://www.pomorska.pl/tuchola.xml'), + # (u'\u017bnin', u'http://www.pomorska.pl/znin.xml') + + # # wiadomosci tematyczne (redundancja z region/miasta): + # (u'Sport', u'http://www.pomorska.pl/sport.xml'), + # (u'Zdrowie', u'http://www.pomorska.pl/zdrowie.xml'), + # (u'Auto', u'http://www.pomorska.pl/moto.xml'), + # (u'Dom', u'http://www.pomorska.pl/dom.xml'), + # (u'Reporta\u017c', u'http://www.pomorska.pl/reportaz.xml'), + # (u'Gospodarka', u'http://www.pomorska.pl/gospodarka.xml') + ] + + keep_only_tags = [dict(name='div', attrs={'id':'article'})] + + remove_tags = [ + dict(name='p', attrs={'id':'articleTags'}), + dict(name='div', attrs={'id':'articleEpaper'}), + dict(name='div', attrs={'id':'articleConnections'}), + dict(name='div', attrs={'class':'articleFacts'}), + dict(name='div', attrs={'id':'articleExternalLink'}), + dict(name='div', attrs={'id':'articleMultimedia'}), + dict(name='div', attrs={'id':'articleGalleries'}), + dict(name='div', attrs={'id':'articleAlarm'}), + dict(name='div', attrs={'id':'adholder_srodek1'}), + dict(name='div', attrs={'id':'articleVideo'}), + dict(name='a', attrs={'name':'fb_share'})] + + extra_css = '''h1 { font-size: 1.4em; } + h2 { font-size: 1.0em; }''' + + diff --git a/resources/recipes/nczas.recipe b/resources/recipes/nczas.recipe deleted file mode 100644 index 6ca6657765..0000000000 --- a/resources/recipes/nczas.recipe +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -__license__ = 'GPL v3' -__copyright__ = '2010, Tomasz Dlugosz <tomek3d@gmail.com>' -''' -nczas.com -''' - -from calibre.web.feeds.news import BasicNewsRecipe - -# - -class NCzas(BasicNewsRecipe): - title = u'Najwy\u017cszy Czas!' - description = u'Najwy\u017cszy Czas!\nwydanie internetowe' - __author__ = u'Tomasz D\u0142ugosz' - language = 'pl' - oldest_article = 7 - max_articles_per_feed = 100 - no_stylesheets = True - cover_url = 'http://nczas.com/wp-content/themes/default/grafika/logo.png' - - keep_only_tags = [dict(name='div', attrs={'class':'trescartykulu'})] - - feeds = [(u'Najwy\u017cszy Czas!', u'http://nczas.com/feed/')] - - def postprocess_html(self, soup, first): - - for tag in soup.findAll(name= 'img', alt=""): - tag.extract() - - for item in soup.findAll(align = "right"): - del item['align'] - - return soup diff --git a/resources/recipes/rmf24_ESKN.recipe b/resources/recipes/rmf24_ESKN.recipe new file mode 100644 index 0000000000..992b84529c --- /dev/null +++ b/resources/recipes/rmf24_ESKN.recipe @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>' +''' +rmf24.pl +''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class RMF24_ESKN(BasicNewsRecipe): + title = u'Rmf24.pl - Ekonomia Sport Kultura Nauka' + description = u'Ekonomia, sport, kultura i nauka ze strony rmf24.pl' + language = 'pl' + oldest_article = 7 + max_articles_per_feed = 100 + __author__ = u'Tomasz D\u0142ugosz' + no_stylesheets = True + remove_javascript = True + + feeds = [(u'Ekonomia', u'http://www.rmf24.pl/ekonomia/feed'), + (u'Sport', u'http://www.rmf24.pl/sport/feed'), + (u'Kultura', u'http://www.rmf24.pl/kultura/feed'), + (u'Nauka', u'http://www.rmf24.pl/nauka/feed')] + + keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})] + + remove_tags = [ + dict(name='div', attrs={'class':'toTop'}), + dict(name='div', attrs={'class':'category'}), + dict(name='div', attrs={'class':'REMOVE'}), + dict(name='div', attrs={'class':'embed embedAd'})] + + extra_css = ''' + h1 { font-size: 1.2em; } + ''' + + preprocess_regexps = [ + (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in + [ + (r'<h2>Zdj.cie</h2>', lambda match: ''), + (r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'), + (r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>') + ] + ] diff --git a/resources/recipes/rmf24_fakty.recipe b/resources/recipes/rmf24_fakty.recipe new file mode 100644 index 0000000000..3ab0b8dc11 --- /dev/null +++ b/resources/recipes/rmf24_fakty.recipe @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>' +''' +rmf24.pl +''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class RMF24(BasicNewsRecipe): + title = u'Rmf24.pl - Fakty' + description = u'Fakty ze strony rmf24.pl' + language = 'pl' + oldest_article = 7 + max_articles_per_feed = 100 + __author__ = u'Tomasz D\u0142ugosz' + no_stylesheets = True + remove_javascript = True + + feeds = [(u'Kraj', u'http://www.rmf24.pl/fakty/polska/feed'), + (u'\u015awiat', u'http://www.rmf24.pl/fakty/swiat/feed')] + + keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})] + + remove_tags = [ + dict(name='div', attrs={'id':'adBox625'}), + dict(name='div', attrs={'class':'toTop'}), + dict(name='div', attrs={'class':'category'}), + dict(name='div', attrs={'class':'REMOVE'}), + dict(name='div', attrs={'class':'embed embedAd'})] + + extra_css = ''' + h1 { font-size: 1.2em; } + ''' + preprocess_regexps = [ + (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in + [ + (r'<h2>Zdj.cie</h2>', lambda match: ''), + (r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'), + (r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>') + ] + ] From c4061147220f8130239e8d12836a8473b37d4f2c Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Wed, 22 Sep 2010 16:07:39 -0600 Subject: [PATCH 35/37] Fix #6914 (Calibre misfiles books (during a rename?)) --- src/calibre/library/database2.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 1a2eef2c81..08d9dc3edd 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -451,6 +451,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): parent = os.path.dirname(spath) if len(os.listdir(parent)) == 0: self.rmtree(parent, permanent=True) + ''' + This is commented out as it can lead to book file loss if the second + rename fails. This makes it not worthwhile, IMO. + curpath = self.library_path c1, c2 = current_path.split('/'), path.split('/') if not self.is_case_sensitive and len(c1) == len(c2): @@ -473,6 +477,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): os.rename(os.path.join(curpath, oldseg), tempname) os.rename(tempname, os.path.join(curpath, newseg)) curpath = os.path.join(curpath, newseg) + ''' def add_listener(self, listener): ''' From 2bd3bda0fe3df008af642d69b5aa4e6019767d43 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Wed, 22 Sep 2010 16:41:09 -0600 Subject: [PATCH 36/37] Restore case renaming code, but in a more robust form --- src/calibre/library/database2.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 08d9dc3edd..627ab6358b 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -451,9 +451,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): parent = os.path.dirname(spath) if len(os.listdir(parent)) == 0: self.rmtree(parent, permanent=True) - ''' - This is commented out as it can lead to book file loss if the second - rename fails. This makes it not worthwhile, IMO. curpath = self.library_path c1, c2 = current_path.split('/'), path.split('/') @@ -469,15 +466,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # the directories, so no need to do them here. for oldseg, newseg in zip(c1, c2): if oldseg.lower() == newseg.lower() and oldseg != newseg: - while True: - # need a temp name in the current segment for renames - tempname = os.path.join(curpath, 'TEMP.%f'%time.time()) - if not os.path.exists(tempname): - break - os.rename(os.path.join(curpath, oldseg), tempname) - os.rename(tempname, os.path.join(curpath, newseg)) + try: + os.rename(os.path.join(curpath, oldseg), os.path.join(curpath, newseg)) + except: + break # Fail silently since nothing catastrophic has happened curpath = os.path.join(curpath, newseg) - ''' def add_listener(self, listener): ''' From 48cb1d313802dd13dd754dea60227716bbc90b34 Mon Sep 17 00:00:00 2001 From: Timothy Legge <timlegge@gmail.com> Date: Wed, 22 Sep 2010 21:45:44 -0300 Subject: [PATCH 37/37] Fix 6902 - If Title is None set to Unknown --- src/calibre/devices/kobo/books.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 496162d668..5724efc4ec 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -41,6 +41,10 @@ class Book(MetaInformation): self.authors = [''] else: self.authors = [authors] + + if not title: + self.title = 'Unknown' + self.mime = mime self.size = size # will be set later if None