From 195181219c2bc7335409627f9d18930a3ae58b67 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 6 Nov 2010 09:36:03 -0600 Subject: [PATCH 01/21] More transalatable strings for /browse --- resources/content_server/browse/browse.html | 6 +- src/calibre/library/server/browse.py | 3 + src/calibre/translations/calibre.pot | 63 ++++++++++++--------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/resources/content_server/browse/browse.html b/resources/content_server/browse/browse.html index ef312334d9..6d4c79c4c9 100644 --- a/resources/content_server/browse/browse.html +++ b/resources/content_server/browse/browse.html @@ -4,7 +4,7 @@ - ..:: calibre library ::.. {title} + ..:: calibre {library} ::.. {title} @@ -41,7 +41,7 @@

→ home ←

+ >→ {home} ←

 
diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index 9530a34c73..5609416273 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -245,6 +245,9 @@ class BrowseServer(object): ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':')) ans = ans.replace('{sort_cookie_name}', scn) ans = ans.replace('{prefix}', self.opts.url_prefix) + ans = ans.replace('{library}', _('library')) + ans = ans.replace('{home}', _('home')) + ans = ans.replace('{Search}', _('Search')) opts = ['' % ( 'selected="selected" ' if k==sort else '', xml(k), xml(n), ) for k, n in diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 4e25612b5d..350cb14bca 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.7.27\n" -"POT-Creation-Date: 2010-11-05 15:17+MDT\n" -"PO-Revision-Date: 2010-11-05 15:17+MDT\n" +"POT-Creation-Date: 2010-11-06 09:35+MDT\n" +"PO-Revision-Date: 2010-11-06 09:35+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -1932,7 +1932,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:313 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1127 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:620 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:623 msgid "Tags" msgstr "" @@ -2299,7 +2299,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:159 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:618 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:621 msgid "Rating" msgstr "" @@ -3570,7 +3570,7 @@ msgid "Click the show details button to see which ones." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:625 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:628 msgid "Show book details" msgstr "" @@ -3878,7 +3878,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:25 #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:49 #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:58 -#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:402 +#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:403 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:119 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:120 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:121 @@ -3925,7 +3925,7 @@ msgstr "" msgid "None" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:401 +#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:402 msgid "Double-click to open Book Details window" msgstr "" @@ -7601,7 +7601,7 @@ msgid "Successfully downloaded metadata for %d out of %d books" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:624 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:627 msgid "Details" msgstr "" @@ -8581,6 +8581,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:97 #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:270 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:574 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:250 msgid "Search" msgstr "" @@ -10477,7 +10478,7 @@ msgid "Password to access your calibre library. Username is " msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:402 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:405 msgid "Loading, please wait" msgstr "" @@ -10522,70 +10523,78 @@ msgstr "" msgid "Sort by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:307 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:513 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:248 +msgid "library" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:249 +msgid "home" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:310 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:516 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569 msgid "Newest" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:514 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:311 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:517 msgid "All books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:341 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:344 msgid "Browse books by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:346 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:349 msgid "Choose a category to browse by:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:422 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:425 msgid "Browsing by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:423 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:426 msgid "Up" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:544 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:547 msgid "in" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:547 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:550 msgid "Books in" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:599 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:602 msgid "Other formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:606 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:609 msgid "Read %s in the %s format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:611 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:614 msgid "Get" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:626 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:629 msgid "Permalink" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:627 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:630 msgid "A permanent link to this book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:638 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:641 msgid "This book has been deleted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:722 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:725 msgid "in search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:724 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:727 msgid "Matching books" msgstr "" From 7f932f562c801c247a3fb41a4fbaa0fa070d8128 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 6 Nov 2010 09:49:10 -0600 Subject: [PATCH 02/21] Siol.net by BlonG --- resources/images/news/siol.png | Bin 0 -> 423 bytes resources/recipes/siol.recipe | 55 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 resources/images/news/siol.png create mode 100644 resources/recipes/siol.recipe diff --git a/resources/images/news/siol.png b/resources/images/news/siol.png new file mode 100644 index 0000000000000000000000000000000000000000..e04283474b624f1f07562c69e2e3f65a069a6493 GIT binary patch literal 423 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VRoC}^Vjv*GOlM^JqJb3SW_kVr-ZslVsx-&d5bZ*B|VU6p>~Ig2Tq~er!O{HrIWo#F<4mz_?7V zRm0rOtgW3}c+=V4eTK$!qP}%E0zsnpAAhjwTPt;VmQVYeWU=*)xbxc(F`ny-c09PU zDW1nb{7hu~Dhct1)k$Id&t&A}{JDSjm3~*#vp4ny&a*fFKXCLc(2{fWY)^ZHDJZe4 zoV?f}$88}jr02}Bj5($)!MnAYnSo)3dtj%h5Z8L3-&9LnBT7;dOH!?pi&B9UgOP!u zfv%yUu7O#Ifu)s+nU#S#kZomPFntwUIEsec{FKbJN@NX&##Y8AR;FeU4Nd=kUIl7k N@O1TaS?83{1ORORkiY-{ literal 0 HcmV?d00001 diff --git a/resources/recipes/siol.recipe b/resources/recipes/siol.recipe new file mode 100644 index 0000000000..2680c1f1e9 --- /dev/null +++ b/resources/recipes/siol.recipe @@ -0,0 +1,55 @@ +# coding: utf-8 +__license__ = 'GPL v3' +__copyright__ = '2010, BlonG' +''' +www.siol.si +''' +from calibre.web.feeds.news import BasicNewsRecipe + +class Siol(BasicNewsRecipe): + title = u'Siol.net' + __author__ = u'BlonG' + description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos" + oldest_article = 3 + max_articles_per_feed = 20 + no_stylesheets = True + use_embedded_content = False + language = 'sl' + + cover_url = 'http://farm4.static.flickr.com/3540/3401820496_c771550fe6.jpg' + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} + ''' + + keep_only_tags = [ + dict(name='div', attrs={'id':'idContent'}), + ] + + remove_tags = [ + dict(name='span', attrs={'class':'com1'}), + dict(name='div', attrs={'class':'relation'}), + dict(name='p', attrs={'class':'path'}), + dict(name='div', attrs={'class':'clear_r'}), + dict(name='div', attrs={'id':'appendix'}), + dict(name='div', attrs={'id':'rail'}), + dict(name='div', attrs={'id':'div_comments'}), + dict(name='div', attrs={'class':'thumbs'}), + ] + + feeds = [ + (u'Slovenija', u'http://www.siol.net/rss.aspx?path=Slovenija') + ,(u'Lokalne novice', u'http://www.siol.net/rss.aspx?path=Slovenija/Lokalne_novice') + ,(u'EU', u'http://www.siol.net/rss.aspx?path=EU') + ,(u'Svet', u'http://www.siol.net/rss.aspx?path=Svet') + ,(u'Gospodarstvo', u'http://www.siol.net/rss.aspx?path=Gospodarstvo') + ,(u'Sportal', u'http://www.siol.net/rss.aspx?path=Sportal') + ,(u'Trendi', u'http://www.siol.net/rss.aspx?path=Trendi') + ,(u'Avtomoto', u'http://www.siol.net/rss.aspx?path=Avtomoto') + ,(u'Tehnologija', u'http://www.siol.net/rss.aspx?path=Tehnologija') + ,(u'TV / Film', u'http://www.siol.net/rss.aspx?path=TV') + ] + From 3e6bb20ac8e832ee46b110282cc2248754932fa9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 6 Nov 2010 09:54:43 -0600 Subject: [PATCH 03/21] Diario Sport by Jefferson Frantz. Fixes #405 (New news feed) --- resources/recipes/diario_sport.recipe | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 resources/recipes/diario_sport.recipe diff --git a/resources/recipes/diario_sport.recipe b/resources/recipes/diario_sport.recipe new file mode 100644 index 0000000000..8c7181098b --- /dev/null +++ b/resources/recipes/diario_sport.recipe @@ -0,0 +1,42 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class DiarioSport(BasicNewsRecipe): + title = u'Diario Sport' + oldest_article = 2 + max_articles_per_feed = 75 + __author__ = 'Jefferson Frantz' + description = 'Todas las noticias del Barça y del mundo del deporte en general' + timefmt = ' [%d %b, %Y]' + language = 'es' + no_stylesheets = True + + feeds = [(u'Sport', u'http://feeds.feedburner.com/sport/ultimahora')] + + extra_css = ''' + h2{font-family: serif; font-size: small; font-weight: bold; color: #000000; text-align: justify} + ''' + + keep_only_tags = [dict(name='div', attrs={'id':['noticiasMedio']})] + + remove_tags = [ + dict(name=['object','link','script','ul']) + ,dict(name='div', attrs={'id':['scrAdSense','herramientas2','participacion','participacion2','bloque1resultados','bloque2resultados','cont_vinyetesAnt','tinta','noticiasSuperior','cintillopublicidad2']}) + ,dict(name='p', attrs={'class':['masinformacion','hora']}) + ,dict(name='a', attrs={'class':["'link'"]}) + ,dict(name='div', attrs={'class':['addthis_toolbox addthis_default_style','firma','pretitularnoticia']}) + ,dict(name='form', attrs={'id':['formularioDeBusquedaAvanzada']}) + ] + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return soup + + + def postprocess_html(self, soup, first_fetch): + img = soup.find('img',src='/img/videos/mascaravideo.png') + if not img is None: + img.extract() + + return soup + From 293c2cc725357cfd7a5623224b6e77687d6601ae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 6 Nov 2010 10:35:46 -0600 Subject: [PATCH 04/21] Fix #7435 (Open the detailed info dialog for the book.) --- setup/installer/__init__.py | 1 - src/calibre/gui2/dialogs/book_info.py | 10 ++++++++-- src/calibre/gui2/dialogs/book_info.ui | 10 ++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/setup/installer/__init__.py b/setup/installer/__init__.py index 9b1f80f54b..c25334dbe4 100644 --- a/setup/installer/__init__.py +++ b/setup/installer/__init__.py @@ -49,7 +49,6 @@ class Push(Command): print '\n\nPushing to:', host, '\n' threads.append(Thread(target=subprocess.check_call, args=(rcmd,))) threads[-1].start() - subprocess.check_call(rcmd) for thread in threads: thread.join() diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 70c70ddf96..4cbe0ace7f 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -5,8 +5,8 @@ __docformat__ = 'restructuredtext en' import textwrap, os, re -from PyQt4.QtCore import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt -from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon +from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \ + QDialog, QPixmap, QGraphicsScene, QIcon, QSize from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo from calibre.gui2 import dynamic, open_local_file @@ -20,6 +20,8 @@ class BookInfo(QDialog, Ui_BookInfo): Ui_BookInfo.__init__(self) self.setupUi(self) self.cover_pixmap = None + self.comments.sizeHint = self.comments_size_hint + desktop = QCoreApplication.instance().desktop() screen_height = desktop.availableGeometry().height() - 100 self.resize(self.size().width(), screen_height) @@ -37,12 +39,16 @@ class BookInfo(QDialog, Ui_BookInfo): self.fit_cover.stateChanged.connect(self.toggle_cover_fit) self.cover.resizeEvent = self.cover_view_resized + def comments_size_hint(self): + return QSize(350, 350) + def toggle_cover_fit(self, state): dynamic.set('book_info_dialog_fit_cover', self.fit_cover.isChecked()) self.resize_cover() def cover_view_resized(self, event): QTimer.singleShot(1, self.resize_cover) + def slave(self, current, previous): row = current.row() self.refresh(row) diff --git a/src/calibre/gui2/dialogs/book_info.ui b/src/calibre/gui2/dialogs/book_info.ui index d9ff87bcdd..7eb6ccd3d3 100644 --- a/src/calibre/gui2/dialogs/book_info.ui +++ b/src/calibre/gui2/dialogs/book_info.ui @@ -47,9 +47,15 @@ Comments - - + + + + + 0 + 0 + + 350 From 8dfbc5a271edfcfb9f45512ccc258df7fe164479 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 6 Nov 2010 11:13:06 -0600 Subject: [PATCH 05/21] SC Print Magazone by Tony Maro Fixes #405 (New news feed) --- resources/recipes/scprint.recipe | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 resources/recipes/scprint.recipe diff --git a/resources/recipes/scprint.recipe b/resources/recipes/scprint.recipe new file mode 100644 index 0000000000..d9ce70f7a2 --- /dev/null +++ b/resources/recipes/scprint.recipe @@ -0,0 +1,73 @@ +from calibre.web.feeds.recipes import BasicNewsRecipe, LoginFailed + +class SCPrintMagazine(BasicNewsRecipe): + title = u'SC Print Magazine' + __author__ = u'Tony Maro' + description = u'Last print version of the data security magazine' + INDEX = "http://www.scmagazineus.com/issuearchive/" + no_stylesheets = True + language = 'en' + keep_only_tags = [dict(id=['article','review'])] + remove_tags = [dict(id=['articlePrintTools','reviewBodyColumn'])] + LOG_IN = 'http://www.scmagazineus.com/login/' + tags = 'News,SC Magazine' + needs_subscription = True + + def parse_index(self): + articles = [] + issuelink = printsections = None + + soup = self.index_to_soup(self.INDEX) + sectit = soup.find('div', attrs={'class':'issueArchiveItem'}) + if sectit is not None: + linkt = sectit.find('a') + issuelink = linkt['href'] + imgt = sectit.find('img') + self.cover_url = imgt['src'] + + if issuelink is not None: + issue = self.index_to_soup(issuelink) + if issue is not None: + printsections = issue.findAll('div',attrs={'class':'PrintSection'}) + if printsections is not None: + for printsection in printsections: + onesection = [] + sectiontitle = printsection.find('h3').contents[0] + articlesec = printsection.findAll('div',attrs={'class':'IssueArchiveFormat'}) + if articlesec is not None: + ''' got articles ''' + for onearticle in articlesec: + ''' process one article ''' + arttitlet = onearticle.find('h3') + if arttitlet is not None: + mylink = arttitlet.find('a') + if mylink is not None: + if mylink.has_key('title'): + arttitle = mylink['title'] + else: + arttitle = 'unknown' + if mylink.has_key('href'): + artlink = mylink['href'] + artlink = artlink.replace("/article","/printarticle") + artlink = artlink.replace("/review","/printreview") + deck = onearticle.find('div',attrs={'class':'deck'}) + if deck is not None: + deck = deck.contents[0] + onesection.append({'title':arttitle, 'url':artlink, 'description':deck,'date':''}) + articles.append((sectiontitle, onesection)) + + return articles + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + br.open(self.LOG_IN) + br.select_form(name='aspnetForm') + br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtEmail'] = self.username + br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtPassword'] = self.password + raw = br.submit("ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$btnLogin").read() + if 'Logout' not in raw: + raise LoginFailed( + _('Failed to log in, check your username and password for' + ' the calibre Periodicals service.')) + return br + From dd9015565b1305846e1dfe0dce34f5b23c344081 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 7 Nov 2010 18:27:17 +0000 Subject: [PATCH 06/21] Add tweak: # Behavior of doubleclick on the library view. Choices: # open_viewer, do_nothing, edit_cell. Default: open_viewer. # Example: doubleclick_on_library_view = 'do_nothing' doubleclick_on_library_view = 'open_viewer' --- resources/default_tweaks.py | 6 ++++++ src/calibre/gui2/library/views.py | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 0f570bab40..16e06abc1d 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -211,3 +211,9 @@ generate_cover_title_font = None # Absolute path to a TTF font file to use as the font for the footer in the # default cover generate_cover_foot_font = None + + +# Behavior of doubleclick on the library view. Choices: +# open_viewer, do_nothing, edit_cell. Default: open_viewer. +# Example: doubleclick_on_library_view = 'do_nothing' +doubleclick_on_library_view = 'open_viewer' diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index e39c48a70c..224c095429 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -51,6 +51,10 @@ class BooksView(QTableView): # {{{ QTableView.__init__(self, parent) self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed) + if tweaks['doubleclick_on_library_view'] == 'edit_cell': + self.setEditTriggers(self.DoubleClicked|self.editTriggers()) + elif tweaks['doubleclick_on_library_view'] == 'open_viewer': + self.doubleClicked.connect(parent.iactions['View'].view_triggered) self.drag_allowed = True self.setDragEnabled(True) @@ -100,8 +104,6 @@ class BooksView(QTableView): # {{{ self._model.about_to_be_sorted.connect(self.about_to_be_sorted) self._model.sorting_done.connect(self.sorting_done) - self.doubleClicked.connect(parent.iactions['View'].view_triggered) - # Column Header Context Menu {{{ def column_header_context_handler(self, action=None, column=None): if not action or not column: From e13ef02c61e576e67101a26222d8cad618d0f840 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 7 Nov 2010 11:28:06 -0700 Subject: [PATCH 07/21] Conversion pipeline: When using the Level x Table of Contents expressions, if a tag is empty but has a non-empty title attribute, use that instead of ignoring the tag --- .../ebooks/oeb/transforms/structure.py | 6 +++++- src/calibre/manual/conversion.rst | 21 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py index 07235b4fb0..0db9b153df 100644 --- a/src/calibre/ebooks/oeb/transforms/structure.py +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -133,7 +133,11 @@ class DetectStructure(object): def elem_to_link(self, item, elem, counter): - text = xml2text(elem) + text = xml2text(elem).strip() + if not text: + text = elem.get('title', '') + if not text: + text = elem.get('alt', '') text = text[:100].strip() id = elem.get('id', 'calibre_toc_%d'%counter) elem.set('id', id) diff --git a/src/calibre/manual/conversion.rst b/src/calibre/manual/conversion.rst index cfc2871396..fea20a3163 100644 --- a/src/calibre/manual/conversion.rst +++ b/src/calibre/manual/conversion.rst @@ -377,7 +377,7 @@ They are XPath expressions that match tags in the intermediate XHTML produced by how to construct XPath expressions. Next to each option is a button that launches a wizard to help with the creation of basic XPath expressions. The following simple example illustrates how to use these options. -Suppose you have an input document taht results in XHTML that look like this: +Suppose you have an input document that results in XHTML that look like this: .. code-block:: html @@ -418,6 +418,25 @@ This will result in an automatically generated two level Table of Contents that Not all output formats support a multi level Table of Contents. You should first try with EPUB Output. If that works, then try your format of choice. +Using images as chapter titles when converting HTML input documents +--------------------------------------------------------------------- + +Suppose you want to use an image as your chapter title, but still want |app| to be able to automatically generate a Table of Contents for you from the chapter titles. +Use the following HTML markup to achieve this + +.. code-block:: html + + + +

Chapter 1

+

chapter 1 text...

+

+

chapter 2 text...

+ + + +Set the :guilabel:`Level 1 TOC` setting to ``//h:h2``. Then, for chapter two, |app| will take the title from the value of the ``title`` attribute on the ``

`` tag, since the tag has no text. + How options are set/saved for Conversion ------------------------------------------- From 06004fca5699403af54e96331a0c58a60a3d4c61 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 7 Nov 2010 11:54:25 -0700 Subject: [PATCH 08/21] E-book viewer: Fix clicking entries in TOC that point to the currently loaded document not scrolling view to the top of the document --- src/calibre/gui2/viewer/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 4a098785b1..81a372a3ca 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -472,8 +472,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): if path != self.current_page: self.pending_anchor = frag self.load_path(path) - elif frag: - self.view.scroll_to(frag) + else: + if frag: + self.view.scroll_to(frag) + else: + # Scroll to top + self.view.scroll_to('#') else: open_url(url) From a50b2e2dffe5611362ae9830eee29a2d552b30b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 7 Nov 2010 12:06:12 -0700 Subject: [PATCH 09/21] Edit metadata dialog: Save dialog state on reject as well as on accept --- src/calibre/gui2/dialogs/metadata_single.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 668239f941..2b951a7b2b 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -845,7 +845,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): if cf is not None and hasattr(cf, 'terminate'): cf.terminate() cf.wait() - + self.save_state() QDialog.reject(self, *args) def read_state(self): From b6df943eb3e1539d506d569472d9facbd64c5115 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 8 Nov 2010 15:34:14 +0000 Subject: [PATCH 10/21] Add box-based search interface tab to advanced search. --- src/calibre/gui2/dialogs/search.py | 94 +++++- src/calibre/gui2/dialogs/search.ui | 509 +++++++++++++++++++---------- src/calibre/gui2/layout.py | 1 + src/calibre/gui2/search_box.py | 7 +- 4 files changed, 431 insertions(+), 180 deletions(-) diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 041e7ff1fc..bc04c38e73 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -1,17 +1,68 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import re -from PyQt4.QtGui import QDialog +from PyQt4.QtGui import QDialog, QDialogButtonBox +from PyQt4 import QtCore from calibre.gui2.dialogs.search_ui import Ui_Dialog from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH class SearchDialog(QDialog, Ui_Dialog): - def __init__(self, *args): - QDialog.__init__(self, *args) + def __init__(self, parent, db, box_values, current_tab): + QDialog.__init__(self, parent) self.setupUi(self) self.mc = '' + searchables = sorted(db.field_metadata.searchable_fields(), + lambda x, y: cmp(x if x[0] != '#' else x[1:], + y if y[0] != '#' else y[1:])) + self.general_combo.addItems(searchables) + + if (box_values): + for k,v in box_values.items(): + if k == 'general_index': + continue + getattr(self, k).setText(v) + self.general_combo.setCurrentIndex( + self.general_combo.findText(box_values['general_index'])) + self.box_last_values = box_values + + self.buttonBox.accepted.connect(self.advanced_search_button_pushed) + self.tab_2_button_box.accepted.connect(self.box_search_accepted) + self.tab_2_button_box.rejected.connect(self.box_search_rejected) + self.clear_button.clicked.connect(self.clear_button_pushed) + self.adv_search_used = False + self.box_search_used = False + + self.tabWidget.setCurrentIndex(current_tab) + self.tabWidget.currentChanged[int].connect(self.tab_changed) + self.tab_changed(current_tab) + + def tab_changed(self, idx): + if idx == 1: + self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True) + else: + self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True) + + def advanced_search_button_pushed(self): + self.adv_search_used = True + self.current_tab = 0 + QDialog.accept(self) + + def box_search_accepted(self): + self.box_search_used = True + self.current_tab = 1 + QDialog.accept(self) + + def box_search_rejected(self): + QDialog.reject(self) + + def clear_button_pushed(self): + self.title_box.setText('') + self.authors_box.setText('') + self.series_box.setText('') + self.tags_box.setText('') + self.general_box.setText('') def tokens(self, raw): phrases = re.findall(r'\s*".*?"\s*', raw) @@ -21,6 +72,12 @@ class SearchDialog(QDialog, Ui_Dialog): return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]] def search_string(self): + if self.adv_search_used: + return self.adv_search_string() + else: + return self.box_search_string() + + def adv_search_string(self): mk = self.matchkind.currentIndex() if mk == CONTAINS_MATCH: self.mc = '' @@ -56,3 +113,34 @@ class SearchDialog(QDialog, Ui_Dialog): tok = '"%s"'%tok return tok + def box_search_string(self): + ans = [] + self.box_last_values = {} + title = unicode(self.title_box.text()).strip() + self.box_last_values['title_box'] = title + if title: + ans.append('title:"' + title + '"') + author = unicode(self.authors_box.text()).strip() + self.box_last_values['authors_box'] = author + if author: + ans.append('author:"' + author + '"') + series = unicode(self.series_box.text()).strip() + self.box_last_values['series_box'] = series + if series: + ans.append('series:"' + series + '"') + self.mc = '=' + tags = unicode(self.tags_box.text()) + self.box_last_values['tags_box'] = tags + tags = self.tokens(tags) + if tags: + tags = ['tags:' + t for t in tags] + ans.append('(' + ' or '.join(tags) + ')') + general = unicode(self.general_box.text()) + self.box_last_values['general_box'] = general + general_index = unicode(self.general_combo.currentText()) + self.box_last_values['general_index'] = general_index + if general: + ans.append(unicode(self.general_combo.currentText()) + ':"' + general + '"') + if ans: + return ' and '.join(ans) + return '' diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 9e8817b1f4..a974d1b551 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -1,195 +1,352 @@ - + + Dialog - - + + 0 0 - 667 - 391 + 686 + 360 - + Advanced Search - - + + :/images/search.png:/images/search.png - + - - - Find entries that have... + + + 1 - - - - - - - &All these words: - - - all - - - - - - - - - - - - - - This exact &phrase: - - - all - - - - - - - - - - - - - - &One or more of these words: - - - all - - - - - - - - - - - - - - - But dont show entries that have... - - - - - - - - Any of these &unwanted words: - - - all - - - - - - - - - - - - - - - - 16777215 - 60 - - - - - - - What kind of match to use: - - - matchkind - - - - - - - - Contains: the word or phrase matches anywhere in the metadata + + + A&dvanced Search + + + + + + Find entries that have... - - - - Equals: the word or phrase must match an entire metadata field + + + + + + + &All these words: + + + all + + + + + + + + + + + + + + This exact &phrase: + + + all + + + + + + + + + + + + + + &One or more of these words: + + + all + + + + + + + + + + + + + + + But dont show entries that have... - - - - Regular expression: the expression must match anywhere in the metadata + + + + + + + Any of these &unwanted words: + + + all + + + + + + + + + + + + + 16777215 + 60 + + + + + + + What kind of match to use: + + + matchkind + + + + + + + + Contains: the word or phrase matches anywhere in the metadata + + + + + Equals: the word or phrase must match an entire metadata field + + + + + Regular expression: the expression must match anywhere in the metadata + + + + + + + + + 40 + 0 + + + + + + + matchkind + + + + + + + + + + + 16777215 + 30 + + + + See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help + + + true + + + + + + + + + + Qt::Horizontal - - - - - - - - 40 - 0 - - - - - - - matchkind - - - - - - - - - - - 16777215 - 30 - - - - See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help - - - true - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Titl&e/Author/Series ... + + + + + + &Title: + + + title_box + + + + + + + Enter the title. + + + + + + + &Author + + + authors_box + + + + + + + &Series + + + series_box + + + + + + + Ta&gs + + + tags_box + + + + + + + Enter an author's name. Only one author can be used. + + + + + + + Enter a series name, without an index. Only one series name can be used. + + + + + + + Enter tags separated by spaces + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + &Clear + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + all + phrase + any + none + matchkind + buttonBox + title_box + authors_box + series_box + tags_box + general_combo + general_box + clear_button + tab_2_button_box + tabWidget + - + @@ -198,11 +355,11 @@ Dialog accept() - + 248 254 - + 157 274 @@ -214,11 +371,11 @@ Dialog reject() - + 316 260 - + 286 274 diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 885a9cc33f..07fac48b9d 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -167,6 +167,7 @@ class SearchBar(QWidget): # {{{ x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) parent.advanced_search_button = x = QToolButton(self) + parent.advanced_search_button.setShortcut(_("Ctrl+s")) x.setIcon(QIcon(I('search.png'))) l.addWidget(x) x.setToolTip(_("Advanced search")) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 0b85749370..9aa0c2c4dc 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -381,6 +381,8 @@ class SearchBoxMixin(object): unicode(self.search.toolTip()))) self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip()) self.clear_button.setStatusTip(self.clear_button.toolTip()) + self.search_last_values = None + self.search_current_tab = 0 def search_box_cleared(self): self.tags_view.clear() @@ -392,9 +394,12 @@ class SearchBoxMixin(object): self.tags_view.clear() def do_advanced_search(self, *args): - d = SearchDialog(self) + d = SearchDialog(self, self.library_view.model().db, + self.search_last_values, self.search_current_tab) if d.exec_() == QDialog.Accepted: self.search.set_search_string(d.search_string()) + self.search_last_values = d.box_last_values + self.search_current_tab = d.current_tab class SavedSearchBoxMixin(object): From 2d6b895a9018dfe0a69c402cf50098e1024ea5bc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 09:12:43 -0700 Subject: [PATCH 11/21] Fix #7460 (Another update for Danas) --- resources/recipes/danas.recipe | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/recipes/danas.recipe b/resources/recipes/danas.recipe index 2849723c04..9002a6b505 100644 --- a/resources/recipes/danas.recipe +++ b/resources/recipes/danas.recipe @@ -25,7 +25,7 @@ class Danas(BasicNewsRecipe): remove_empty_feeds = True extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} - .article_description,body,.lokacija{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif} + .article,.articledescription,body,.lokacija,.feed{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif} .nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif} .antrfileText{border-left: 2px solid #999999; margin-left: 0.8em; @@ -66,7 +66,7 @@ class Danas(BasicNewsRecipe): keep_only_tags = [dict(name='div', attrs={'id':'left'})] remove_tags = [ - dict(name='div', attrs={'class':['width_1_4','metaClanka','baner']}) + dict(name='div', attrs={'class':['width_1_4','metaClanka','baner','listaVesti','article_nav']}) ,dict(name='div', attrs={'id':'comments'}) ,dict(name=['object','link','iframe','meta']) ] From 7711d58021a9974fb30d9f24c18d267f522e5ea4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 10:11:22 -0700 Subject: [PATCH 12/21] ... --- src/calibre/__init__.py | 3 + src/calibre/gui2/book_details.py | 6 +- src/calibre/gui2/comments_editor.py | 104 ++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/calibre/gui2/comments_editor.py diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 1226ab3188..a43b0126ff 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -444,6 +444,9 @@ xml_entity_to_unicode = partial(entity_to_unicode, result_exceptions = { def replace_entities(raw): return _ent_pat.sub(entity_to_unicode, raw) +def xml_replace_entities(raw): + return _ent_pat.sub(xml_entity_to_unicode, raw) + def prepare_string_for_xml(raw, attribute=False): raw = _ent_pat.sub(entity_to_unicode, raw) raw = raw.replace('&', '&').replace('<', '<').replace('>', '>') diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index c75eb35202..e5711bb31a 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -213,11 +213,13 @@ class BookInfo(QWebView): f = QFontInfo(QApplication.font(self.parent())).pixelSize() p = unicode(QApplication.palette().color(QPalette.Normal, QPalette.Base).name()) + c = unicode(QApplication.palette().color(QPalette.Normal, + QPalette.Text).name()) templ = u'''\ @@ -225,7 +227,7 @@ class BookInfo(QWebView): %%s - '''%(p, f) + '''%(p, f, c) if self.vertical: if comments: rows += u'%s'%comments diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py new file mode 100644 index 0000000000..8a19b657a9 --- /dev/null +++ b/src/calibre/gui2/comments_editor.py @@ -0,0 +1,104 @@ +#!/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 lxml import html +from lxml.html import soupparser + +from PyQt4.Qt import QApplication, QFontInfo, QPalette +from PyQt4.QtWebKit import QWebView + +from calibre.ebooks.chardet import xml_to_unicode +from calibre import xml_replace_entities + +class EditorWidget(QWebView): + + @dynamic_property + def html(self): + + def fget(self): + ans = u'' + try: + raw = unicode(self.page().mainFrame().toHtml()) + raw = xml_to_unicode(raw, strip_encoding_pats=True, + resolve_entities=True)[0] + + try: + root = html.fromstring(raw) + except: + root = soupparser.fromstring(raw) + + elems = [] + for body in root.xpath('//body'): + elems += [html.tostring(x, encoding=unicode) for x in body if + x.tag != 'script'] + if len(elems) > 1: + ans = u'
%s
'%(u''.join(elems)) + else: + ans = u''.join(elems) + ans = xml_replace_entities(ans) + except: + import traceback + traceback.print_exc() + + return ans + + def fset(self, val): + self.setHtml(val) + f = QFontInfo(QApplication.font(self)).pixelSize() + b = unicode(QApplication.palette().color(QPalette.Normal, + QPalette.Base).name()) + c = unicode(QApplication.palette().color(QPalette.Normal, + QPalette.Text).name()) + style = 'font-size: %dpx; background-color: %s; color: %s' % (f, b, + c) + + for body in self.page().mainFrame().documentElement().findAll('body'): + body.setAttribute('style', style) + self.page().setContentEditable(True) + + return property(fget=fget, fset=fset) + +if __name__ == '__main__': + app = QApplication([]) + w = EditorWidget() + w.show() +# testing {{{ + + w.html = ''' +
+ +

From Publishers Weekly

+
+ Starred Review. Paul Dirac (1902–1984) shared the Nobel Prize for physics with Erwin Schrödinger in 1933, but whereas physicists regard Dirac as one of the giants of the 20th century, he isn't as well known outside the profession. This may be due to the lack of humorous quips attributed to Dirac, as compared with an Einstein or a Feynman. If he spoke at all, it was with one-word answers that made Calvin Coolidge look loquacious . Dirac adhered to Keats's admonition that Beauty is truth, truth beauty: if an equation was beautiful, it was probably correct, and vice versa. His most famous equation predicted the positron (now used in PET scans), which is the antiparticle of the electron, and antimatter in general. In 1955, Dirac came up with a primitive version of string theory, which today is the rock star branch of physics. Physicist Farmelo (It Must Be Beautiful) speculates that Dirac suffered from undiagnosed autism because his character quirks resembled autism's symptoms. Farmelo proves himself a wizard at explaining the arcane aspects of particle physics. His great affection for his odd but brilliant subject shows on every page, giving Dirac the biography any great scientist deserves. (Sept.)
Copyright © Reed Business Information, a division of Reed Elsevier Inc. All rights reserved. + +
+

Review

+
+
Kirkus *Starred Review*
“Paul Dirac was a giant of 20th-century physics, and this rich, satisfying biography does him justice…. [A] nuanced portrayal of an introverted eccentric who held his own in a small clique of revolutionary scientific geniuses.”

Peter Higgs, Times (UK)
“Fascinating reading… Graham Farmelo has done a splendid job of portraying Dirac and his world. The biography is a major achievement.”

Telegraph
“If Newton was the Shakespeare of British physics, Dirac was its Milton, the most fascinating and enigmatic of all our great scientists. And he now has a biography to match his talents: a wonderful book by Graham Farmelo. The story it tells is moving, sometimes comic, sometimes infinitely sad, and goes to the roots of what we mean by truth in science.”

New Statesman
“A marvelously rich and intimate study.”

Sunday Herald
“Farmelo’s splendid biography has enough scientific exposition for the biggest science fan and enough human interest for the rest of us. It creates a picture of a man who was a great theoretical scientist but also an awkward but oddly endearing human being…. This is a fine book: a fitting tribute to a significant and intriguing scientific figure.”

The Economist
“[A] sympathetic portrait….Of the small group of young men who developed quantum mechanics and revolutionized physics almost a century ago, he truly stands out. Paul Dirac was a strange man in a strange world. This biography, long overdue, is most welcome.”

Times Higher Education Supplement (UK)
“A page-turner about Dirac and quantum physics seems a contradiction in terms, but Graham Farmelo's new book, The Strangest Man, is an eminently readable account of the developments in physics throughout the 1920s, 1930s and 1940s and the life of one of the discipline's key scientists.”

New Scientist
“Enthralling… Regardless of whether Dirac was autistic or simply unpleasant, he is an icon of modern thought and Farmelo's book gives us a genuine insight into his life and times.”

John Gribbin, Literary Review
“Fascinating …[A] suberb book.”

Tom Stoppard
“In the group portrait of genius in 20th century physics, Paul Dirac is the stick figure. Who was he, and what did he do? For all non-physicists who have followed the greatest intellectual adventure of modern times, this is the missing book.”

Michael Frayn
“Graham Farmelo has found the subject he was born to write about, and brought it off triumphantly. Dirac was one of the great founding fathers of modern physics, a theoretician who explored the sub-atomic world through the power of pure mathematics. He was also a most extraordinary man - an extreme introvert, and perhaps autistic. Farmelo traces the outward events as authoritatively as the inward. His book is a monumental achievement – one of the great scientific biographies.”

Roger Highfield, Editor,New Scientist
“A must-read for anyone interested in the extraordinary power of pure thought. With this revelatory, moving and definitive biography, Graham Farmelo provides the first real glimpse inside the bizarre mind of Paul Dirac.”

Martin Rees, President of the Royal Society, Master of Trinity College, Professor of Cosmology and Astrophysics at the University of Cambridge and Astronomer Royal
“Paul Dirac, though a quiet and withdrawn character, made towering contributions to the greatest scientific revolution of the 20th century. In this sensitive and meticulously researched biography, Graham Farmelo does Dirac proud, and offers a wonderful insight into the European academic environment in which his creativity flourished."

Barnes & Noble Review
“Farmelo explains all the science relevant to understanding Dirac, and does it well; equally good is his careful and copious account of a personal life that was dogged by a sense of tragedy…. [I]f [Dirac] could read Farmelo’s absorbing and accessible account of his life he would see that it had magic in it, and triumph: the magic of revelations about the deep nature of reality, and the triumph of having moved human understanding several steps further towards the light.”

Newark Star-Ledger
“[An] excellently researched biography…. [T]his book is a major step toward making a staggeringly brilliant, remote man seem likeable.”

Los Angeles Times
“Graham Farmelo has managed to haul Dirac onstage in an affectionate and meticulously researched book that illuminates both his era and his science…. Farmelo is very good at portraying this locked-in, asocial creature, often with an eerie use of the future-perfect tense…, which has the virtue of putting the reader in the same room with people who are long gone.”

SeedMagazine.com
“[A] tour de force filled with insight and revelation. The Strangest Man offers an unprecedented and gripping view of Dirac not only as a scientist, but also as a human being.”

New York Times Book Review
“This biography is a gift. It is both wonderfully written (certainly not a given in the category Accessible Biographies of Mathematical Physicists) and a thought-provoking meditation on human achievement, limitations and the relations between the two…. [T]he most satisfying and memorable biography I have read in years.”

Time Magazine
“Paul Dirac won a Nobel Prize for Physics at 31. He was one of quantum mechanics’ founding fathers, an Einstein-level genius. He was also virtually incapable of having normal social interactions. Graham Farmelo’s biography explains Dirac’s mysterious life and work.”

Library Journal
“Farmelo did not pick the easiest biography to write – its subject lived a largely solitary life in deep thought. But Dirac was also beset with tragedy… and in that respect, the author proposes some novel insights into what shaped the man. This would be a strong addition to a bibliography of magnificent 20th-century physicist biographies, including Walter Issacson’s Einstein, Kai Bird and Martin J. Sherwin’s American Prometheus: The Triumph and Tragedy of J. Robert Oppenheimer, and James Gleick’s Genius: The Life and Science of Richard Feynman.”

American Journal of Physics
“[A] very moving biography…. It would have been easy to simply fill the biography with Dirac stories of which there is a cornucopia, many of which are actually true. But Farmelo does much more than that. He has met and spoken with people who knew Dirac including the surviving members of his family. He has been to where Dirac lived and worked and he understands the physics. What has emerged is a 558 page biography, which is a model of the genre. Dirac was so private and emotionally self-contained that one wonders if anyone really knew him. Farmelo’s book is as close as we are likely to come."

American Scientist
“[A] highly readable and sympathetic biography of the taciturn British physicist who can be said, with little exaggeration, to have invented modern theoretical physics. The book is a real achievement, alternately gripping and illuminating.”

Natural History
“Farmelo’s eloquent and empathetic examination of Dirac’s life raises this book above the level of workmanlike popularization. Using personal interviews, scientific archives, and newly released documents and letters, he’s managed – as much as anyone could – to dispel the impression of the physicist as a real-life Mr. Spock, the half Vulcan of Star Trek.”

Science
“[A] consummate and seamless biography…. Farmelo has succeeded masterfully in the difficult genre of writing a great scientist’s life for a general audience.”

Physics Today
“[An] excellent biography of a hero of physics…. [I]n The Strangest Man, we are treated to a fascinating, thoroughly researched, and well-written account of one of the most important figures of modern physics.”

Nature
“As this excellent biography by Graham Farmelo shows, Dirac’s contributions to science were profound and far-ranging; modern ideas that have their origins in quantum electrodynamics are inspired by his insight…. The effortless writing style shows that it is possible to describe profound ideas without compromising scientific integrity or readability."

Freeman Dyson, New York Review of Books
“In Farmelo’s book we see Dirac as a character in a human drama, carrying his full share of tragedy as well as triumph.”

American Journal of Physics
“Farmelo’s exhaustively researched biography…not only traces the life of its title figure but portrays the unfolding of quantum mechanics with cinematic scope…. He repeatedly zooms his storyteller’s lens in and out between intimate close-ups and grand scenes, all the while attempting to make the physics comprehensible to the general readership without trivializing it. In his telling, the front-line scientists are a competitive troupe of explorers, jockeying for renown – only the uncharted territory is in the mind and the map is mathematical…. We read works like Farmelo’s for enlightenment, for inspiration, and for the reminder that science is a quintessentially human endeavor, with all...

+ +
+
+
+ +

From Publishers Weekly

+
+ Starred Review. Paul Dirac (1902–1984) shared the Nobel Prize for physics with Erwin Schrödinger in 1933, but whereas physicists regard Dirac as one of the giants of the 20th century, he isn't as well known outside the profession. This may be due to the lack of humorous quips attributed to Dirac, as compared with an Einstein or a Feynman. If he spoke at all, it was with one-word answers that made Calvin Coolidge look loquacious . Dirac adhered to Keats's admonition that Beauty is truth, truth beauty: if an equation was beautiful, it was probably correct, and vice versa. His most famous equation predicted the positron (now used in PET scans), which is the antiparticle of the electron, and antimatter in general. In 1955, Dirac came up with a primitive version of string theory, which today is the rock star branch of physics. Physicist Farmelo (It Must Be Beautiful) speculates that Dirac suffered from undiagnosed autism because his character quirks resembled autism's symptoms. Farmelo proves himself a wizard at explaining the arcane aspects of particle physics. His great affection for his odd but brilliant subject shows on every page, giving Dirac the biography any great scientist deserves. (Sept.)
Copyright © Reed Business Information, a division of Reed Elsevier Inc. All rights reserved. + +
+

Review

+
+
Kirkus *Starred Review*
“Paul Dirac was a giant of 20th-century physics, and this rich, satisfying biography does him justice…. [A] nuanced portrayal of an introverted eccentric who held his own in a small clique of revolutionary scientific geniuses.”

Peter Higgs, Times (UK)
“Fascinating reading… Graham Farmelo has done a splendid job of portraying Dirac and his world. The biography is a major achievement.”

Telegraph
“If Newton was the Shakespeare of British physics, Dirac was its Milton, the most fascinating and enigmatic of all our great scientists. And he now has a biography to match his talents: a wonderful book by Graham Farmelo. The story it tells is moving, sometimes comic, sometimes infinitely sad, and goes to the roots of what we mean by truth in science.”

New Statesman
“A marvelously rich and intimate study.”

Sunday Herald
“Farmelo’s splendid biography has enough scientific exposition for the biggest science fan and enough human interest for the rest of us. It creates a picture of a man who was a great theoretical scientist but also an awkward but oddly endearing human being…. This is a fine book: a fitting tribute to a significant and intriguing scientific figure.”

The Economist
“[A] sympathetic portrait….Of the small group of young men who developed quantum mechanics and revolutionized physics almost a century ago, he truly stands out. Paul Dirac was a strange man in a strange world. This biography, long overdue, is most welcome.”

Times Higher Education Supplement (UK)
“A page-turner about Dirac and quantum physics seems a contradiction in terms, but Graham Farmelo's new book, The Strangest Man, is an eminently readable account of the developments in physics throughout the 1920s, 1930s and 1940s and the life of one of the discipline's key scientists.”

New Scientist
“Enthralling… Regardless of whether Dirac was autistic or simply unpleasant, he is an icon of modern thought and Farmelo's book gives us a genuine insight into his life and times.”

John Gribbin, Literary Review
“Fascinating …[A] suberb book.”

Tom Stoppard
“In the group portrait of genius in 20th century physics, Paul Dirac is the stick figure. Who was he, and what did he do? For all non-physicists who have followed the greatest intellectual adventure of modern times, this is the missing book.”

Michael Frayn
“Graham Farmelo has found the subject he was born to write about, and brought it off triumphantly. Dirac was one of the great founding fathers of modern physics, a theoretician who explored the sub-atomic world through the power of pure mathematics. He was also a most extraordinary man - an extreme introvert, and perhaps autistic. Farmelo traces the outward events as authoritatively as the inward. His book is a monumental achievement – one of the great scientific biographies.”

Roger Highfield, Editor,New Scientist
“A must-read for anyone interested in the extraordinary power of pure thought. With this revelatory, moving and definitive biography, Graham Farmelo provides the first real glimpse inside the bizarre mind of Paul Dirac.”

Martin Rees, President of the Royal Society, Master of Trinity College, Professor of Cosmology and Astrophysics at the University of Cambridge and Astronomer Royal
“Paul Dirac, though a quiet and withdrawn character, made towering contributions to the greatest scientific revolution of the 20th century. In this sensitive and meticulously researched biography, Graham Farmelo does Dirac proud, and offers a wonderful insight into the European academic environment in which his creativity flourished."

Barnes & Noble Review
“Farmelo explains all the science relevant to understanding Dirac, and does it well; equally good is his careful and copious account of a personal life that was dogged by a sense of tragedy…. [I]f [Dirac] could read Farmelo’s absorbing and accessible account of his life he would see that it had magic in it, and triumph: the magic of revelations about the deep nature of reality, and the triumph of having moved human understanding several steps further towards the light.”

Newark Star-Ledger
“[An] excellently researched biography…. [T]his book is a major step toward making a staggeringly brilliant, remote man seem likeable.”

Los Angeles Times
“Graham Farmelo has managed to haul Dirac onstage in an affectionate and meticulously researched book that illuminates both his era and his science…. Farmelo is very good at portraying this locked-in, asocial creature, often with an eerie use of the future-perfect tense…, which has the virtue of putting the reader in the same room with people who are long gone.”

SeedMagazine.com
“[A] tour de force filled with insight and revelation. The Strangest Man offers an unprecedented and gripping view of Dirac not only as a scientist, but also as a human being.”

New York Times Book Review
“This biography is a gift. It is both wonderfully written (certainly not a given in the category Accessible Biographies of Mathematical Physicists) and a thought-provoking meditation on human achievement, limitations and the relations between the two…. [T]he most satisfying and memorable biography I have read in years.”

Time Magazine
“Paul Dirac won a Nobel Prize for Physics at 31. He was one of quantum mechanics’ founding fathers, an Einstein-level genius. He was also virtually incapable of having normal social interactions. Graham Farmelo’s biography explains Dirac’s mysterious life and work.”

Library Journal
“Farmelo did not pick the easiest biography to write – its subject lived a largely solitary life in deep thought. But Dirac was also beset with tragedy… and in that respect, the author proposes some novel insights into what shaped the man. This would be a strong addition to a bibliography of magnificent 20th-century physicist biographies, including Walter Issacson’s Einstein, Kai Bird and Martin J. Sherwin’s American Prometheus: The Triumph and Tragedy of J. Robert Oppenheimer, and James Gleick’s Genius: The Life and Science of Richard Feynman.”

American Journal of Physics
“[A] very moving biography…. It would have been easy to simply fill the biography with Dirac stories of which there is a cornucopia, many of which are actually true. But Farmelo does much more than that. He has met and spoken with people who knew Dirac including the surviving members of his family. He has been to where Dirac lived and worked and he understands the physics. What has emerged is a 558 page biography, which is a model of the genre. Dirac was so private and emotionally self-contained that one wonders if anyone really knew him. Farmelo’s book is as close as we are likely to come."

American Scientist
“[A] highly readable and sympathetic biography of the taciturn British physicist who can be said, with little exaggeration, to have invented modern theoretical physics. The book is a real achievement, alternately gripping and illuminating.”

Natural History
“Farmelo’s eloquent and empathetic examination of Dirac’s life raises this book above the level of workmanlike popularization. Using personal interviews, scientific archives, and newly released documents and letters, he’s managed – as much as anyone could – to dispel the impression of the physicist as a real-life Mr. Spock, the half Vulcan of Star Trek.”

Science
“[A] consummate and seamless biography…. Farmelo has succeeded masterfully in the difficult genre of writing a great scientist’s life for a general audience.”

Physics Today
“[An] excellent biography of a hero of physics…. [I]n The Strangest Man, we are treated to a fascinating, thoroughly researched, and well-written account of one of the most important figures of modern physics.”

Nature
“As this excellent biography by Graham Farmelo shows, Dirac’s contributions to science were profound and far-ranging; modern ideas that have their origins in quantum electrodynamics are inspired by his insight…. The effortless writing style shows that it is possible to describe profound ideas without compromising scientific integrity or readability."

Freeman Dyson, New York Review of Books
“In Farmelo’s book we see Dirac as a character in a human drama, carrying his full share of tragedy as well as triumph.”

American Journal of Physics
“Farmelo’s exhaustively researched biography…not only traces the life of its title figure but portrays the unfolding of quantum mechanics with cinematic scope…. He repeatedly zooms his storyteller’s lens in and out between intimate close-ups and grand scenes, all the while attempting to make the physics comprehensible to the general readership without trivializing it. In his telling, the front-line scientists are a competitive troupe of explorers, jockeying for renown – only the uncharted territory is in the mind and the map is mathematical…. We read works like Farmelo’s for enlightenment, for inspiration, and for the reminder that science is a quintessentially human endeavor, with all...

+ +
+
+ '''.decode('utf-8') + app.exec_() + #print w.html.encode('utf-8') + +# }}} + print w.html From b09855343c9e232f3801df64af06be8f18be1fdc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 10:36:47 -0700 Subject: [PATCH 13/21] Updated Siol recipe --- resources/recipes/siol.recipe | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/recipes/siol.recipe b/resources/recipes/siol.recipe index 2680c1f1e9..1dd9c4bb55 100644 --- a/resources/recipes/siol.recipe +++ b/resources/recipes/siol.recipe @@ -5,18 +5,17 @@ __copyright__ = '2010, BlonG' www.siol.si ''' from calibre.web.feeds.news import BasicNewsRecipe - class Siol(BasicNewsRecipe): title = u'Siol.net' __author__ = u'BlonG' description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos" oldest_article = 3 + language = 'sl' max_articles_per_feed = 20 no_stylesheets = True use_embedded_content = False - language = 'sl' - cover_url = 'http://farm4.static.flickr.com/3540/3401820496_c771550fe6.jpg' + cover_url = 'https://sites.google.com/site/javno2010/home/siol_cover.jpg' extra_css = ''' h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} @@ -25,6 +24,8 @@ class Siol(BasicNewsRecipe): body{font-family:Helvetica,Arial,sans-serif;font-size:small;} ''' + html2lrf_options = ['--base-font-size', '10'] + keep_only_tags = [ dict(name='div', attrs={'id':'idContent'}), ] @@ -52,4 +53,3 @@ class Siol(BasicNewsRecipe): ,(u'Tehnologija', u'http://www.siol.net/rss.aspx?path=Tehnologija') ,(u'TV / Film', u'http://www.siol.net/rss.aspx?path=TV') ] - From b1d1bd2f4927207a88edc9c52675e49039f673fd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 10:45:18 -0700 Subject: [PATCH 14/21] MMC RTV by BlonG --- resources/recipes/mmc_rtv.recipe | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 resources/recipes/mmc_rtv.recipe diff --git a/resources/recipes/mmc_rtv.recipe b/resources/recipes/mmc_rtv.recipe new file mode 100644 index 0000000000..8b8bd66a06 --- /dev/null +++ b/resources/recipes/mmc_rtv.recipe @@ -0,0 +1,57 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, BlonG' +''' +www.rtvslo.si +''' +from calibre.web.feeds.news import BasicNewsRecipe + +class MMCRTV(BasicNewsRecipe): + title = u'MMC RTV Slovenija' + __author__ = u'BlonG' + description = u"Prvi interaktivni multimedijski portal, MMC RTV Slovenija" + oldest_article = 3 + max_articles_per_feed = 20 + language = 'sl' + no_stylesheets = True + use_embedded_content = False + + cover_url = 'https://sites.google.com/site/javno2010/home/rtv_slo_cover.jpg' + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} + ''' + + def print_version(self, url): + split_url = url.split("/") + print_url = 'http://www.rtvslo.si/index.php?c_mod=news&op=print&id=' + split_url[-1] + return print_url + + keep_only_tags = [ + dict(name='div', attrs={'class':'title'}), + dict(name='div', attrs={'id':'newsbody'}), + dict(name='div', attrs={'id':'newsblocks'}), + ] +# remove_tags=[ +# 40 dict(name='div', attrs={'id':'newsblocks'}), +# ] + + feeds = [ + (u'Slovenija', u'http://www.rtvslo.si/feeds/01.xml'), + (u'Svet', u'http://www.rtvslo.si/feeds/02.xml'), + (u'Evropska unija', u'http://www.rtvslo.si/feeds/16.xml'), + (u'Gospodarstvo', u'http://www.rtvslo.si/feeds/04.xml'), + (u'Črna kronika', u'http://www.rtvslo.si/feeds/08.xml'), + (u'Okolje', u'http://www.rtvslo.si/feeds/12.xml'), + (u'Znanost in tehnologija', u'http://www.rtvslo.si/feeds/09.xml'), + (u'Zabava', u'http://www.rtvslo.si/feeds/06.xml'), + (u'Ture avanture', u'http://www.rtvslo.si/feeds/28.xml'), + ] + +# def preprocess_html(self, soup): +# newsblocks = soup.find('div',attrs = ['id':'newsblocks']) +# soup.find('div', attrs = {'id':'newsbody'}).insert(-1, newsblocks) +# return soup + From 064008674d630d736616cafdf6b6d5d63606da28 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 10:52:11 -0700 Subject: [PATCH 15/21] Avto-magazin by BlonG --- resources/images/news/avto-magazin.png | Bin 0 -> 1461 bytes resources/recipes/avto-magazin.recipe | 46 +++++++++++++++++++++++++ resources/recipes/scprint.recipe | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 resources/images/news/avto-magazin.png create mode 100644 resources/recipes/avto-magazin.recipe diff --git a/resources/images/news/avto-magazin.png b/resources/images/news/avto-magazin.png new file mode 100644 index 0000000000000000000000000000000000000000..4a416652f25aff0670924f1100cbd2b4851dd37d GIT binary patch literal 1461 zcmV;m1xosfP)D+j2fLzThq>*c5dhTev9{;Z;0*pY~DQY|9zhGod0uEK7QP| zapg)OY&(_u`4a+xXfzrHO^e4H8bAm|2?W4(r>7?;K?ut#mjP5&Rl)bm<#JiRZ)}7> zpsp?&1tC1o_rZ13=~N1Y5JCtL!gXC2G_A3*sR@GRaw75dEB{F_sO!28t~)*_!VX>Z%p(^FI6IH^=R4Ir(m;=1xXv6x{%P}Ab^cpMZZ z6bgmFaV92GDJYe)S=X&R2SxeQ&BBGDkaSdEACH6YTUMzAt~)-SPJ^PTYUP*YDFX=x z+u9l$AP|T|A`wsnfyIksF|h4#-;zmaZa#OeuMfJrC1u;U%X8bdKhI^$u3Wi#70Tt& z(XU@6Q>j#yp=!wT7c_0jl9m=wRb3BecQi( z&mQ>vdE2(GE|{1&apK64DgzA-r%(6xg09O{2=OP?iXxK|2(-2~HiD`KgTWwpUM`o( zfaA=~nI#twGdi6K{@L`2$&jZl)h6clsREI*~dD(0(2ac1?nx?F^imec!hC6ux{(r=eI(r)4*lO1~GQq67k(2FiyIt*v!+LPR3@d_E61ZkVR$!Q;pB z`T6t5jvYM;yLa#0xnl?1zJ2&`PY-O^aP8XV%MghyUE0wBt5%JU4h_N7)WwUJERKID7W|d5A{WuI=am1_wJko0^0e7-(s!trcQqq`5g772?bp zNm4#vE|<&Vf0Oa?o}PmTg?RtoFmzoARn{j6VcU)arPAEo+#DP_BpC>YXJ=<-syTn~ zVEgthTj1V3S$vty_wR{B6@UBo4vcUJ{0GXfvy^E{z=1n4zNWg^)QvdN|aByg-ia&q;>C?yv^!7?UnH(GYJ;2e? zQmI&k{(f0Ast~qq+kaPgWwdHqb8|~e^*$6bjHV{&>yvs{*QQMyH&*dTq^)h)GB|x& z>ihS1c6M}B@wT@1_T|eVllgaE3=Fil8-@^$Gd-QjR7a?)YFaP|;jkRZXU|H53Nw9u zwY328cs>ts<%+zo?3Lc$MT>$#Sg|6Pb6vQ9U*11`D$i4=nwskBK=nLX{U9k3LXJ?+ za~#(NU9YJDsI8TOWwX*jp&*l^>ye1WWq+Hd#7)z(Zhy07_*Y zJC0npic;mEvQ7XDLu!>VlF7&<*|r?Q_4P9Fg$t#9HY@Q;_e;JY67hY{1Iv=z@E;EF zy-xaC1>*n!03~!qSaf7zbY(hYa%Ew3WdJfTF)%GLF)c7SR536*G&edkGc7PTIxsMR z3^d6A001R)MObuXVRU6WZEs|0W_bWIFflMKF)=MLI8-n=IxsOhGcqkOH##sdE$y}+ P00000NkvXXu0mjf=cl36 literal 0 HcmV?d00001 diff --git a/resources/recipes/avto-magazin.recipe b/resources/recipes/avto-magazin.recipe new file mode 100644 index 0000000000..b88c6b6371 --- /dev/null +++ b/resources/recipes/avto-magazin.recipe @@ -0,0 +1,46 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, BlonG' +''' +avto-magazin.si +''' +from calibre.web.feeds.news import BasicNewsRecipe +class Dnevnik(BasicNewsRecipe): + title = u'Avto Magazin' + __author__ = u'BlonG' + description = u"Za avtomobilistične frike, poznavalce in nedeljske šoferje." + oldest_article = 7 + max_articles_per_feed = 20 + labguage = 'sl' + no_stylesheets = True + use_embedded_content = False + + conversion_options = {'linearize_tables' : True} + + + cover_url = 'https://sites.google.com/site/javno2010/home/avto_magazin_cover.jpg' + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} + ''' + + keep_only_tags = [ + dict(name='div', attrs={'id':'_iprom_inStream'}), +# dict(name='div', attrs={'class':'entry-content'}), + ] + + remove_tags = [ + dict(name='div', attrs={'id':'voteConfirmation'}), + dict(name='div', attrs={'id':'InsideVote'}), + dict(name='div', attrs={'class':'Zone234'}), + dict(name='div', attrs={'class':'Comments'}), + dict(name='div', attrs={'class':'sorodneNovice'}), + dict(name='div', attrs={'id':'footer'}), + ] + + + feeds = [ + (u'Novice', u'http://www.avto-magazin.si/rss/') + ] diff --git a/resources/recipes/scprint.recipe b/resources/recipes/scprint.recipe index d9ce70f7a2..86b1bb951e 100644 --- a/resources/recipes/scprint.recipe +++ b/resources/recipes/scprint.recipe @@ -1,4 +1,4 @@ -from calibre.web.feeds.recipes import BasicNewsRecipe, LoginFailed +from calibre.web.feeds.news import BasicNewsRecipe, LoginFailed class SCPrintMagazine(BasicNewsRecipe): title = u'SC Print Magazine' From d424b769f7bb77e1c4a577fc349cc2c8ce6dfc41 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 11:01:28 -0700 Subject: [PATCH 16/21] Dnevnik by BlonG --- resources/images/news/dnevnik.png | Bin 0 -> 861 bytes resources/recipes/avto-magazin.recipe | 2 +- resources/recipes/dnevnik.recipe | 63 ++++++++++++++++++++++++++ resources/recipes/mmc_rtv.recipe | 2 +- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 resources/images/news/dnevnik.png create mode 100644 resources/recipes/dnevnik.recipe diff --git a/resources/images/news/dnevnik.png b/resources/images/news/dnevnik.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e3da45951d794869fbc9b8e830b77a01495421 GIT binary patch literal 861 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87?|2TT^vI!PRCC6_6SK7IbQ$#TQ%?bMeq3-zy>!EuLQ%tXUw|C9TM9YJ9on z%0ai?i@z5}-Fcl+TOcNX|GRzveM|Qc+fWAqhF{C~ecte1GT+@w(%)~PLtvSr=|nB5 zQ{Mt-%;axa#Gx>+N{gd!SIpb2hgId=oihs!eC{*EuwO|{eJ7$%xSs@^%f@kyw4IVCi) zl`Dv_88@Gn&o9`&UY6mfiRFUXNxUku+?+?67R=*4w?^N@SnL#IfsWEn)h{M?hxYtl z+PlP*@ug**ieZ4dr^@nQGtH^jd0+hT0{TiPj8v;FW##(8{ukJi}Vnp-8nQktfD zR{P=8#^@<}y$ly3!n|W|YYJ^^m2f){|5D=R5sA%e4HulM-pROMm-p#pEL(f))V@hs z1v(tJdIcX%7d<}XxpM>WfsZl&?AK{=bnpLN7nUO0u*;FNVdlBH_rj_k#|O`kbc|WUnAy)HtbA(s&? zz{nZ6xyt&JrQHhAja53wou2GByX7g+xW#9J>l~TxPpQ%`tiCWTw%I=8#k9!=*3GM) zcLyHvX#SGJk^Wcvn~cMbpvty4AB5_K7SHxC*t3Z9nb3;o4qahJ zKmU6^uWi|J4#hRGQAh5bD0}5qK6(1Q%_}-j#p`s6H~-yTDgjKMswJ)wB`Jv|saDBF zsX&Us$iUD**U(Vcz#_!Zz{=Fb%G6lbz}(8fAm>(OFN%iT{FKbJN@NWN7FGs^R>npU U4Z3f)$pbYoc)I$ztOKSF0EQ1_@&Et; literal 0 HcmV?d00001 diff --git a/resources/recipes/avto-magazin.recipe b/resources/recipes/avto-magazin.recipe index b88c6b6371..6464588acc 100644 --- a/resources/recipes/avto-magazin.recipe +++ b/resources/recipes/avto-magazin.recipe @@ -7,7 +7,7 @@ from calibre.web.feeds.news import BasicNewsRecipe class Dnevnik(BasicNewsRecipe): title = u'Avto Magazin' __author__ = u'BlonG' - description = u"Za avtomobilistične frike, poznavalce in nedeljske šoferje." + description = u'Za avtomobilisti\xc4\x8dne frike, poznavalce in nedeljske \xc5\xa1oferje.' oldest_article = 7 max_articles_per_feed = 20 labguage = 'sl' diff --git a/resources/recipes/dnevnik.recipe b/resources/recipes/dnevnik.recipe new file mode 100644 index 0000000000..75610e2e8f --- /dev/null +++ b/resources/recipes/dnevnik.recipe @@ -0,0 +1,63 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, BlonG' +''' +dnevnik.si +''' +from calibre.web.feeds.news import BasicNewsRecipe +class Dnevnik(BasicNewsRecipe): + title = u'Dnevnik.si' + __author__ = u'BlonG' + description = u'''Dnevnik je \u010dasnik z ve\u010d kot polstoletno zgodovino. + Pod sloganom \xbb\u017divljenje ima besedo\xab na svojih straneh prina\u0161a + bralcem bogastvo informacij, komentarjev in kolumen in raznovrstnost + pogledov, zaznamovanih z odgovornostjo do posameznika in \u0161ir\u0161e + dru\u017ebe.''' + oldest_article = 3 + max_articles_per_feed = 20 + language = 'sl' + no_stylesheets = True + use_embedded_content = False + + cover_url = 'https://sites.google.com/site/javno2010/home/dnevnik_cover.jpg' + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} + ''' + + keep_only_tags = [ + dict(name='div', attrs={'id':'_iprom_inStream'}), + dict(name='div', attrs={'class':'entry-content'}), + ] + + remove_tags = [ + dict(name='div', attrs={'class':'fb_article_top'}), + dict(name='div', attrs={'class':'related'}), + dict(name='div', attrs={'class':'fb_article_foot'}), + dict(name='div', attrs={'class':'spreading'}), + dict(name='dl', attrs={'class':'ad'}), + dict(name='p', attrs={'class':'report'}), + dict(name='div', attrs={'class':'hfeed comments'}), + dict(name='dl', attrs={'id':'entryPanel'}), + dict(name='dl', attrs={'class':'infopush ip_wide'}), + dict(name='div', attrs={'class':'sidebar'}), + dict(name='dl', attrs={'class':'bottom'}), + dict(name='div', attrs={'id':'footer'}), + ] + + + feeds = [ + (u'Slovenija', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=13') + ,(u'Svet', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=14') + ,(u'EU', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=116') + ,(u'Poslovni dnevnik', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=5') + ,(u'Kronika', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=15') + ,(u'Kultura', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=17') + ,(u'Zdravje', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=18') + ,(u'Znanost in IT', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=19') + ,(u'(Ne)verjetno', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=20') + ,(u'E-strada', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=21') + ,(u'Svet vozil', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=22') + ] diff --git a/resources/recipes/mmc_rtv.recipe b/resources/recipes/mmc_rtv.recipe index 8b8bd66a06..a0d9adbe29 100644 --- a/resources/recipes/mmc_rtv.recipe +++ b/resources/recipes/mmc_rtv.recipe @@ -43,7 +43,7 @@ class MMCRTV(BasicNewsRecipe): (u'Svet', u'http://www.rtvslo.si/feeds/02.xml'), (u'Evropska unija', u'http://www.rtvslo.si/feeds/16.xml'), (u'Gospodarstvo', u'http://www.rtvslo.si/feeds/04.xml'), - (u'Črna kronika', u'http://www.rtvslo.si/feeds/08.xml'), + (u'\u010crna kronika', u'http://www.rtvslo.si/feeds/08.xml'), (u'Okolje', u'http://www.rtvslo.si/feeds/12.xml'), (u'Znanost in tehnologija', u'http://www.rtvslo.si/feeds/09.xml'), (u'Zabava', u'http://www.rtvslo.si/feeds/06.xml'), From a2acdd84ad1818e8b93ebdbb80cdaba629b13fe5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Nov 2010 15:40:19 -0700 Subject: [PATCH 17/21] Hola.com by bmsleight --- resources/recipes/hola.recipe | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 resources/recipes/hola.recipe diff --git a/resources/recipes/hola.recipe b/resources/recipes/hola.recipe new file mode 100644 index 0000000000..1ea698228d --- /dev/null +++ b/resources/recipes/hola.recipe @@ -0,0 +1,38 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2010, Brendan Sleight ' +''' +hola.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class Hackaday(BasicNewsRecipe): + title = u'Hola' + __author__ = 'bmsleight' + description = 'diario de actualidad, moda y belleza.' + oldest_article = 10 + max_articles_per_feed = 100 + no_stylesheets = True + language = 'es' + + use_embedded_content = False + + keep_only_tags = [ + dict(name='div', attrs={'id':'cuerpo'}) + ] + + feeds = [ + (u'Famosos' , u'http://www.hola.com/famosos/rss.xml' ), + (u'Realeza' , u'http://www.hola.com/realeza/rss.xml' ), + (u'Cine' , u'http://www.hola.com/cine/rss.xml' ), + (u'Música' , u'http://www.hola.com/musica/rss.xml' ), + (u'Moda y modelos' , u'http://www.hola.com/moda/portada/rss.xml' ), + (u'Belleza y salud', u'http://www.hola.com/belleza/portada/rss.xml' ), + (u'Niños' , u'http://www.hola.com/ninos/rss.xml' ), + (u'Todas las noticias', u'http://int2.hola.com/app/feeds/rss_hola.php'), + ] + + def get_article_url(self, article): + url = article.get('guid', None) + return url From ce07d4cef1980c0fa10127acc076fcf49c473350 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 9 Nov 2010 09:36:07 +0000 Subject: [PATCH 18/21] remove 'sort' from the search/replace writable fields. Make it work as an input field. --- src/calibre/gui2/dialogs/metadata_bulk.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 6b5ef60263..51383fbba2 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -240,13 +240,13 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.writable_fields = [''] fm = self.db.field_metadata for f in fm: - if (f in ['author_sort'] or ( - fm[f]['datatype'] in ['text', 'series']) - and fm[f].get('search_terms', None) - and f not in ['formats', 'ondevice']): + if (f in ['author_sort'] or + (fm[f]['datatype'] in ['text', 'series'] + and fm[f].get('search_terms', None) + and f not in ['formats', 'ondevice', 'sort'])): self.all_fields.append(f) self.writable_fields.append(f) - if fm[f]['datatype'] == 'composite': + if f in ['sort'] or fm[f]['datatype'] == 'composite': self.all_fields.append(f) self.all_fields.sort() self.writable_fields.sort() @@ -338,7 +338,11 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): def s_r_get_field(self, mi, field): if field: fm = self.db.metadata_for_field(field) - val = mi.get(field, None) + if field == 'sort': + val = mi.get('title_sort', None) + else: + val = mi.get(field, None) + print field, val if val is None: val = [] elif not fm['is_multiple']: From 0ed382a28d0142a957284c16033a5619f65d6126 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 9 Nov 2010 09:37:11 +0000 Subject: [PATCH 19/21] Remove 'experimental' from search and replace. --- src/calibre/gui2/dialogs/metadata_bulk.py | 1 - src/calibre/gui2/dialogs/metadata_bulk.ui | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 51383fbba2..b8b3eabc68 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -274,7 +274,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog): self.main_heading = _( 'You can destroy your library using this feature. ' 'Changes are permanent. There is no undo function. ' - ' This feature is experimental, and there may be bugs. ' 'You are strongly encouraged to back up your library ' 'before proceeding.

' 'Search and replace in text fields using character matching ' diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 62a40a9676..c6830c5d5f 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -400,7 +400,7 @@ Future conversion of these books will use the default settings. - &Search and replace (experimental) + &Search and replace From 34c3f1594ddb55349d50b10d91defba3033ea42f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 9 Nov 2010 08:33:53 -0700 Subject: [PATCH 20/21] Vedomosti by Nikolai Kotchetkov --- resources/recipes/vedomosti.recipe | 195 +++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 resources/recipes/vedomosti.recipe diff --git a/resources/recipes/vedomosti.recipe b/resources/recipes/vedomosti.recipe new file mode 100644 index 0000000000..f9590f8c29 --- /dev/null +++ b/resources/recipes/vedomosti.recipe @@ -0,0 +1,195 @@ +#!/usr/bin/env python + +u''' +Ведомости +''' + +from calibre.web.feeds.feedparser import parse +from calibre.ebooks.BeautifulSoup import Tag +from calibre.web.feeds.news import BasicNewsRecipe + +class VedomostiRecipe(BasicNewsRecipe): + title = u'Ведомости' + __author__ = 'Nikolai Kotchetkov' + publisher = 'vedomosti.ru' + category = 'press, Russia' + description = u'Ежедневная деловая газета' + oldest_article = 3 + max_articles_per_feed = 100 + + masthead_url = u'http://motorro.com/imgdir/logos/ved_logo_black2_cropped.gif' + cover_url = u'http://motorro.com/imgdir/logos/ved_logo_black2_cropped.gif' + + #Add feed names if you want them to be sorted (feeds of this list appear first) + sortOrder = [u'_default', u'Первая полоса', u'Власть и деньги'] + + encoding = 'cp1251' + language = 'ru' + no_stylesheets = True + remove_javascript = True + recursions = 0 + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + + + keep_only_tags = [dict(name='td', attrs={'class' : ['second_content']})] + + remove_tags_after = [dict(name='div', attrs={'class' : 'article_text'})] + + remove_tags = [dict(name='div', attrs={'class' : ['sep', 'choice', 'articleRightTbl']})] + + feeds = [u'http://www.vedomosti.ru/newspaper/out/rss.xml'] + + #base URL for relative links + base_url = u'http://www.vedomosti.ru' + + extra_css = 'h1 {font-size: 1.5em; margin: 0em 0em 0em 0em; text-align: center;}'\ + 'h2 {font-size: 1.0em; margin: 0em 0em 0em 0em;}'\ + 'h3 {font-size: 0.8em; margin: 0em 0em 0em 0em;}'\ + '.article_date {font-size: 0.5em; color: gray; font-family: monospace; text-align:right;}'\ + '.article_authors {font-size: 0.5em; color: gray; font-family: monospace; text-align:right;}'\ + '.article_img {width:100%; text-align: center; padding: 3px 3px 3px 3px;}'\ + '.article_img_desc {width:100%; text-align: center; font-size: 0.5em; color: gray; font-family: monospace;}'\ + '.article_desc {font-size: 1em; font-style:italic;}' + + def parse_index(self): + try: + feedData = parse(self.feeds[0]) + if not feedData: + raise NotImplementedError + self.log("parse_index: Feed loaded successfully.") + if feedData.feed.has_key('title'): + self.title = feedData.feed.title + self.log("parse_index: Title updated to: ", self.title) + if feedData.feed.has_key('description'): + self.description = feedData.feed.description + self.log("parse_index: Description updated to: ", self.description) + + def get_virtual_feed_articles(feed): + if feeds.has_key(feed): + return feeds[feed][1] + self.log("Adding new feed: ", feed) + articles = [] + feeds[feed] = (feed, articles) + return articles + + feeds = {} + + #Iterate feed items and distribute articles using tags + for item in feedData.entries: + link = item.get('link', ''); + title = item.get('title', ''); + if '' == link or '' == title: + continue + article = {'title':title, 'url':link, 'description':item.get('description', ''), 'date':item.get('date', ''), 'content':''}; + if not item.has_key('tags'): + get_virtual_feed_articles('_default').append(article) + continue + for tag in item.tags: + addedToDefault = False + term = tag.get('term', '') + if '' == term: + if (not addedToDefault): + get_virtual_feed_articles('_default').append(article) + continue + get_virtual_feed_articles(term).append(article) + + #Get feed list + #Select sorted feeds first of all + result = [] + for feedName in self.sortOrder: + if (not feeds.has_key(feedName)): continue + result.append(feeds[feedName]) + del feeds[feedName] + result = result + feeds.values() + + return result + + except Exception, err: + self.log(err) + raise NotImplementedError + + def preprocess_html(self, soup): + return self.adeify_images(soup) + + def postprocess_html(self, soup, first_fetch): + #self.log('Original: ', soup.prettify()) + + #Find article + contents = soup.find('div', {'class':['article_text']}) + if not contents: + self.log('postprocess_html: article div not found!') + return soup + contents.extract() + + #Find title + title = soup.find('h1') + if title: + contents.insert(0, title) + + #Find article image + newstop = soup.find('div', {'class':['newstop']}) + if newstop: + img = newstop.find('img') + if img: + imgDiv = Tag(soup, 'div') + imgDiv['class'] = 'article_img' + + if img.has_key('width'): + del(img['width']) + if img.has_key('height'): + del(img['height']) + + #find description + element = img.parent.nextSibling + + img.extract() + imgDiv.insert(0, img) + + while element: + if not isinstance(element, Tag): + continue + nextElement = element.nextSibling + if 'p' == element.name: + element.extract() + element['class'] = 'article_img_desc' + imgDiv.insert(len(imgDiv.contents), element) + element = nextElement + + contents.insert(1, imgDiv) + + #find article abstract + abstract = soup.find('p', {'class':['subhead']}) + if abstract: + abstract['class'] = 'article_desc' + contents.insert(2, abstract) + + #Find article authors + authorsDiv = soup.find('div', {'class':['autors']}) + if authorsDiv: + authorsP = authorsDiv.find('p') + if authorsP: + authorsP['class'] = 'article_authors' + contents.insert(len(contents.contents), authorsP) + + #Fix urls that use relative path + urls = contents.findAll('a'); + if urls: + for url in urls: + if not url.has_key('href'): + continue + if '/' == url['href'][0]: + url['href'] = self.base_url + url['href'] + + body = soup.find('td', {'class':['second_content']}) + if body: + body.replaceWith(contents) + + self.log('Result: ', soup.prettify()) + return soup + From 7acec85c891eeb23184b74463a68266056284e86 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 9 Nov 2010 11:25:59 -0700 Subject: [PATCH 21/21] OCFZipReader: Handle case when CWD is invalid more elegantly --- src/calibre/ebooks/metadata/epub.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index e60837a553..e1712f3668 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -109,9 +109,11 @@ class OCFZipReader(OCFReader): raise EPubException("not a ZIP .epub OCF container") self.root = root if self.root is None: - self.root = os.getcwdu() - if hasattr(stream, 'name'): - self.root = os.path.abspath(os.path.dirname(stream.name)) + name = getattr(stream, 'name', False) + if name: + self.root = os.path.abspath(os.path.dirname(name)) + else: + self.root = os.getcwdu() super(OCFZipReader, self).__init__() def open(self, name, mode='r'):