From 68df332cadc7f4ba5c884306e485591063b75085 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 18 Sep 2010 05:14:41 -0600 Subject: [PATCH 01/29] 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/29] 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 90ff41dfb2ba554f64cb61dd5d1a6de599941b32 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 19 Sep 2010 03:54:39 -0600 Subject: [PATCH 03/29] 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 4f7f7214c13da75ff2dfc4ef0d00da56ad43fdcb Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 19 Sep 2010 20:30:08 +0100 Subject: [PATCH 04/29] Fix incorrect book matching. --- src/calibre/gui2/device.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index ae3141db56..a7e55c4619 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -800,7 +800,7 @@ class DeviceMixin(object): # {{{ # if set_books_in_library did not. if not self.set_books_in_library(self.booklists(), reset=True): self.upload_booklists() - self.book_on_device(None, None, reset=True) + self.book_on_device(None, reset=True) # We need to reset the ondevice flags in the library. Use a big hammer, # so we don't need to worry about whether some succeeded or not. self.refresh_ondevice_info(device_connected=True, reset_only=False) @@ -1309,7 +1309,7 @@ class DeviceMixin(object): # {{{ for f in files: getattr(f, 'close', lambda : True)() - def book_on_device(self, id, format=None, reset=False): + def book_on_device(self, id, reset=False): ''' Return an indication of whether the given book represented by its db id is on the currently connected device. It returns a 5 element list. The @@ -1338,8 +1338,6 @@ class DeviceMixin(object): # {{{ self.book_db_id_cache.append(set()) for book in l: db_id = getattr(book, 'application_id', None) - if db_id is None: - db_id = book.db_id if db_id is not None: # increment the count of books on the device with this # db_id. From f4b885568343944d66950935df887f276eaa3b4f Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Sun, 19 Sep 2010 21:46:13 -0300 Subject: [PATCH 05/29] 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 Date: Sun, 19 Sep 2010 19:01:53 -0600 Subject: [PATCH 06/29] 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 Date: Sun, 19 Sep 2010 23:51:14 -0600 Subject: [PATCH 07/29] 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

or

tags that contain the words ' From 656c88792ddeb760cf7ed562ad54bce81d17f77e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 20 Sep 2010 08:29:25 -0600 Subject: [PATCH 08/29] 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 Date: Mon, 20 Sep 2010 07:44:33 -0700 Subject: [PATCH 09/29] 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 Date: Mon, 20 Sep 2010 07:57:05 -0700 Subject: [PATCH 10/29] 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 = '' # *** 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 Date: Mon, 20 Sep 2010 09:28:39 -0600 Subject: [PATCH 11/29] 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Oxygen team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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??=YJaeuyFH=GB@e2PJ_URZ&tnNHhM|}EKJukhn?Dch^V{$IP&d3- zWIQ|%hwdCkm9Ii$Wx3Tr%;9O|gSonMvlHtV+w9%tbU3wo(QuR|$D~82EUpvubo|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*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-&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|wgulAF4pxy6h5j_;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+YwljRPmB5!{1;Txp!oam%}V)%fBOK0~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>uplxcS1qOXlPsMT7JuQ>SO6?qrllC~xqZ$jtdw0k4`VU>v)Smga27*!t<7 z%WPJ-sam2N*7e~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)@rkhsF;5IU4$`)Kg!nNIlGvAgP^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;3;n9(u&p*(tVI2-zj?`YnsXxs#3wY?(f4oA4 zN=xL%%dygpcW>8A46rH?;61#1t9z~tUf0@uxV9iuU9Kd + + + + Remove border (if any) from cover + + + + :/images/trim.png:/images/trim.png + + + From 231aab95614acf5ab738ee4a949b52d312a28383 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 20 Sep 2010 09:37:15 -0600 Subject: [PATCH 12/29] 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 Date: Mon, 20 Sep 2010 09:40:47 -0600 Subject: [PATCH 13/29] ... --- 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 Date: Mon, 20 Sep 2010 19:39:33 -0700 Subject: [PATCH 14/29] 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 Date: Mon, 20 Sep 2010 20:10:17 -0700 Subject: [PATCH 15/29] 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 Date: Mon, 20 Sep 2010 21:33:43 -0600 Subject: [PATCH 16/29] 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 Date: Tue, 21 Sep 2010 05:40:33 -0700 Subject: [PATCH 17/29] 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 @@ &Explode ePub - + :/images/wizard.png:/images/wizard.png @@ -49,7 +49,7 @@ &Rebuild ePub - + :/images/exec.png:/images/exec.png @@ -63,7 +63,7 @@ &Cancel - + :/images/window-close.png:/images/window-close.png @@ -71,7 +71,7 @@ - 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. + 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. true From 98f0851ebb3eb4a2711440935e10531e6bdea996 Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 21 Sep 2010 05:47:22 -0700 Subject: [PATCH 18/29] 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 @@ - 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. + 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. true From 52f85d3ef422c882cde953c57e72e8e3e5fe50ad Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 21 Sep 2010 11:56:09 -0600 Subject: [PATCH 19/29] 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 ' """ 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 ' __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 Date: Tue, 21 Sep 2010 12:22:50 -0600 Subject: [PATCH 20/29] 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 Date: Tue, 21 Sep 2010 13:44:06 -0600 Subject: [PATCH 21/29] ... --- 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 Date: Tue, 21 Sep 2010 14:56:52 -0600 Subject: [PATCH 22/29] 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 Date: Tue, 21 Sep 2010 16:45:42 -0600 Subject: [PATCH 23/29] 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 Date: Tue, 21 Sep 2010 16:47:28 -0600 Subject: [PATCH 24/29] ... --- 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 Date: Tue, 21 Sep 2010 17:13:22 -0600 Subject: [PATCH 25/29] 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 Date: Tue, 21 Sep 2010 19:00:50 -0600 Subject: [PATCH 26/29] 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 Date: Tue, 21 Sep 2010 22:17:50 -0300 Subject: [PATCH 27/29] 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 Date: Tue, 21 Sep 2010 21:18:32 -0600 Subject: [PATCH 28/29] 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 ' +''' +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 Date: Tue, 21 Sep 2010 22:04:01 -0600 Subject: [PATCH 29/29] 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)