From dbacc79d97ce0b0b42c3d559dbc3214400f64fa7 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 09:45:50 +0000
Subject: [PATCH 01/27] Remove declarations of get_metadata cache counters
---
src/calibre/library/database2.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index aa491aff28..d2654577b9 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -341,10 +341,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.has_id = self.data.has_id
self.count = self.data.count
- # Count times get_metadata is called, and how many times in the cache
- self.gm_count = 0
- self.gm_missed = 0
-
for prop in ('author_sort', 'authors', 'comment', 'comments', 'isbn',
'publisher', 'rating', 'series', 'series_index', 'tags',
'title', 'timestamp', 'uuid', 'pubdate', 'ondevice'):
From bf183471eaec193d6ff345199f4f2188c82116cd Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 11:11:35 +0000
Subject: [PATCH 02/27] Fix problem in search -- the canidates parameter was
not passed to get_dates_matches or get_numeric_matches, resulting in an
exception.
---
src/calibre/library/caches.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 6aef45dbbd..4168360d3a 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -411,7 +411,8 @@ class ResultCache(SearchQueryParser): # {{{
if isinstance(location, list):
if allow_recursion:
for loc in location:
- matches |= self.get_matches(loc, query, allow_recursion=False)
+ matches |= self.get_matches(loc, query, candidates,
+ allow_recursion=False)
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
@@ -419,11 +420,11 @@ class ResultCache(SearchQueryParser): # {{{
fm = self.field_metadata[location]
# take care of dates special case
if fm['datatype'] == 'datetime':
- return self.get_dates_matches(location, query.lower())
+ return self.get_dates_matches(location, query.lower(), candidates)
# take care of numbers special case
if fm['datatype'] in ('rating', 'int', 'float'):
- return self.get_numeric_matches(location, query.lower())
+ return self.get_numeric_matches(location, query.lower(), candidates)
# take care of the 'count' operator for is_multiples
if fm['is_multiple'] and \
@@ -431,7 +432,8 @@ class ResultCache(SearchQueryParser): # {{{
query[1:1] in '=<>!':
vf = lambda item, loc=fm['rec_index'], ms=fm['is_multiple']:\
len(item[loc].split(ms)) if item[loc] is not None else 0
- return self.get_numeric_matches(location, query[1:], val_func=vf)
+ return self.get_numeric_matches(location, query[1:],
+ candidates, val_func=vf)
# everything else, or 'all' matches
matchkind = CONTAINS_MATCH
From b09b3fc993c7c7566e88861926ae9f22a893cd66 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 11:13:14 +0000
Subject: [PATCH 03/27] Fix problem with plugin registration. If a plugin
disappears without being correctly removed, then building the current action
set threw an exception. The only way around it was to hack the preferences.
The fix simply ignores missing actions.
---
src/calibre/gui2/preferences/toolbar.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/preferences/toolbar.py b/src/calibre/gui2/preferences/toolbar.py
index c13d956aea..26cdea19d3 100644
--- a/src/calibre/gui2/preferences/toolbar.py
+++ b/src/calibre/gui2/preferences/toolbar.py
@@ -37,7 +37,10 @@ class BaseModel(QAbstractListModel):
dont_remove_from=set(['toolbar-device']))
if name is None:
return FakeAction('--- '+_('Separator')+' ---', None)
- return gui.iactions[name]
+ try:
+ return gui.iactions[name]
+ except:
+ return None
def rowCount(self, parent):
return len(self._data)
@@ -124,7 +127,8 @@ class CurrentModel(BaseModel):
BaseModel.__init__(self)
self.gprefs_name = 'action-layout-'+key
current = gprefs[self.gprefs_name]
- self._data = [self.name_to_action(x, gui) for x in current]
+ self._data = [self.name_to_action(x, gui) for x in current]
+ self._data = [x for x in self._data if x is not None]
self.key = key
self.gui = gui
From 34424068389e66c82156dca78b54175c114859cb Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 15:32:00 +0000
Subject: [PATCH 04/27] Add ability to highlight when searching instead of
restrict
---
src/calibre/gui2/layout.py | 14 ++++++++++----
src/calibre/gui2/library/models.py | 22 ++++++++++++++++++++--
src/calibre/gui2/search_box.py | 4 ++++
3 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index aaaf1b0267..4009f99201 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -8,9 +8,9 @@ __docformat__ = 'restructuredtext en'
from functools import partial
from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
- pyqtSignal, QToolButton, QPushButton, \
- QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup, \
- QMenu
+ pyqtSignal, QToolButton, QMenu, QCheckBox, \
+ QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
+
from calibre.constants import __appname__
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
@@ -178,7 +178,9 @@ class SearchBar(QWidget): # {{{
x.setToolTip(_("
Search the list of books by title, author, publisher, tags, comments, etc.
Words separated by spaces are ANDed"))
l.addWidget(x)
- self.search_button = QPushButton(_('&Go!'))
+ self.search_button = QToolButton()
+ self.search_button.setToolButtonStyle(Qt.ToolButtonTextOnly)
+ self.search_button.setText(_('&Go!'))
l.addWidget(self.search_button)
self.search_button.setSizePolicy(QSizePolicy.Minimum,
QSizePolicy.Minimum)
@@ -192,6 +194,10 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setToolTip(_("Reset Quick Search"))
+ x = parent.search_highlight_only = QCheckBox()
+ x.setText(_('Highlight'))
+ l.addWidget(x)
+
x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215))
x.setMinimumContentsLength(15)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 49cb1ce182..98e61acf33 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -10,7 +10,7 @@ from contextlib import closing
from operator import attrgetter
from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \
- QModelIndex, QVariant, QDate
+ QModelIndex, QVariant, QDate, QColor
from calibre.gui2 import NONE, config, UNDEFINED_QDATE
from calibre.utils.pyparsing import ParseException
@@ -93,6 +93,8 @@ class BooksModel(QAbstractTableModel): # {{{
self.bool_no_icon = QIcon(I('list_remove.png'))
self.bool_blank_icon = QIcon(I('blank.png'))
self.device_connected = False
+ self.rows_matching = set()
+ self.highlight_only = False
self.read_config()
def change_alignment(self, colname, alignment):
@@ -229,9 +231,22 @@ class BooksModel(QAbstractTableModel): # {{{
self.endInsertRows()
self.count_changed()
+ def set_highlight_only(self, toWhat):
+ self.highlight_only = toWhat
+ self.research()
+
def search(self, text, reset=True):
try:
- self.db.search(text)
+ if self.highlight_only:
+ self.db.search('')
+ if not text:
+ self.rows_matching = set()
+ else:
+ self.rows_matching = set(self.db.search(text,
+ return_matches=True))
+ else:
+ self.rows_matching = set()
+ self.db.search(text)
except ParseException as e:
self.searched.emit(e.msg)
return
@@ -651,6 +666,9 @@ class BooksModel(QAbstractTableModel): # {{{
return NONE
if role in (Qt.DisplayRole, Qt.EditRole):
return self.column_to_dc_map[col](index.row())
+ elif role == Qt.BackgroundColorRole:
+ if self.id(index) in self.rows_matching:
+ return QColor('lightgreen')
elif role == Qt.DecorationRole:
if self.column_to_dc_decorator_map[col] is not None:
return self.column_to_dc_decorator_map[index.column()](index.row())
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 9f74abfc86..75d3c14ef1 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -375,6 +375,7 @@ 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_highlight_only.stateChanged.connect(self.highlight_only_changed)
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@@ -401,6 +402,9 @@ class SearchBoxMixin(object): # {{{
def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason)
+ def highlight_only_changed(self, toWhat):
+ self.current_view().model().set_highlight_only(toWhat)
+
# }}}
class SavedSearchBoxMixin(object): # {{{
From 32084ebd9ad13026b1f382ccac25e152314e9484 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 08:53:26 -0700
Subject: [PATCH 05/27] Improved Expansion.com. Fixes #405 (New news feed)
---
resources/recipes/expansion_spanish.recipe | 116 ++++++++++++---------
1 file changed, 68 insertions(+), 48 deletions(-)
diff --git a/resources/recipes/expansion_spanish.recipe b/resources/recipes/expansion_spanish.recipe
index 31a1504eb0..f2229e90e6 100644
--- a/resources/recipes/expansion_spanish.recipe
+++ b/resources/recipes/expansion_spanish.recipe
@@ -1,59 +1,79 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
__license__ = 'GPL v3'
-__copyright__ = '2009, Darko Miletic '
+__author__ = 'Gerardo Diez'
+__copyright__ = 'Gerardo Diez'
+description = 'Main daily newspaper from Spain - v1.00 (05, Enero 2011)'
+__docformat__ = 'restructuredtext en'
+
'''
-www.expansion.com
+expansion.es
'''
+from calibre.web.feeds.recipes import BasicNewsRecipe
+class Publico(BasicNewsRecipe):
+ title =u'Expansion.com'
+ __author__ ='Gerardo Diez'
+ publisher =u'Unidad Editorial Información Económica, S.L.'
+ category ='finances, catalunya'
+ oldest_article =1
+ max_articles_per_feed =100
+ simultaneous_downloads =10
+ cover_url =u'http://estaticos01.expansion.com/iconos/v2.x/v2.0/cabeceras/logo_expansion.png'
+ timefmt ='[%A, %d %B, %Y]'
+ encoding ='latin'
+ language ='es'
+ remove_javascript =True
+ no_stylesheets =True
+ keep_only_tags =dict(name='div', attrs={'class':['noticia primer_elemento']})
+ remove_tags =[
+ dict(name='div', attrs={'class':['compartir', 'metadata_desarrollo_noticia', 'relacionadas', 'mas_info','publicidad publicidad_textlink', 'ampliarfoto']}),
+ dict(name='ul', attrs={'class':['bolos_desarrollo_noticia']}),
+ dict(name='span', attrs={'class':['comentarios']}),
+ dict(name='p', attrs={'class':['cintillo_comentarios', 'cintillo_comentarios formulario']}),
+ dict(name='div', attrs={'id':['comentarios_lectores_listado']})
+ ]
+ feeds =[
+ (u'Portada', u'http://estaticos.expansion.com/rss/portada.xml'),
+ (u'Portada: Bolsas', u'http://estaticos.expansion.com/rss/mercados.xml'),
+ (u'Divisas', u'http://estaticos.expansion.com/rss/mercadosdivisas.xml'),
+ (u'Euribor', u'http://estaticos.expansion.com/rss/mercadoseuribor.xml'),
+ (u'Materias Primas', u'http://estaticos.expansion.com/rss/mercadosmateriasprimas.xml'),
+ (u'Renta Fija', u'http://estaticos.expansion.com/rss/mercadosrentafija.xml'),
-from calibre.web.feeds.news import BasicNewsRecipe
-from calibre.ebooks.BeautifulSoup import Tag
+ (u'Portada: Mi Dinero', u'http://estaticos.expansion.com/rss/midinero.xml'),
+ (u'Hipotecas', u'http://estaticos.expansion.com/rss/midinerohipotecas.xml'),
+ (u'Créditos', u'http://estaticos.expansion.com/rss/midinerocreditos.xml'),
+ (u'Pensiones', u'http://estaticos.expansion.com/rss/midineropensiones.xml'),
+ (u'Fondos de Inversión', u'http://estaticos.expansion.com/rss/midinerofondos.xml'),
+ (u'Motor', u'http://estaticos.expansion.com/rss/midineromotor.xml'),
-class Expansion(BasicNewsRecipe):
- title = 'Diario Expansion'
- __author__ = 'Darko Miletic'
- description = 'Lider de informacion de mercados, economica y politica'
- publisher = 'expansion.com'
- category = 'news, politics, Spain'
- oldest_article = 2
- max_articles_per_feed = 100
- no_stylesheets = True
- use_embedded_content = False
- delay = 1
- encoding = 'iso-8859-15'
- language = 'es'
+ (u'Portada: Empresas', u'http://estaticos.expansion.com/rss/empresas.xml'),
+ (u'Banca', u'http://estaticos.expansion.com/rss/empresasbanca.xml'),
+ (u'TMT', u'http://estaticos.expansion.com/rss/empresastmt.xml'),
+ (u'Energía', u'http://estaticos.expansion.com/rss/empresasenergia.xml'),
+ (u'Inmobiliario y Construcción', u'http://estaticos.expansion.com/rss/empresasinmobiliario.xml'),
+ (u'Transporte y Turismo', u'http://estaticos.expansion.com/rss/empresastransporte.xml'),
+ (u'Automoción e Industria', u'http://estaticos.expansion.com/rss/empresasauto-industria.xml'),
+ (u'Distribución', u'http://estaticos.expansion.com/rss/empresasdistribucion.xml'),
+ (u'Deporte y Negocio', u' http://estaticos.expansion.com/rss/empresasdeporte.xml'),
+ (u'Mi Negocio', u'http://estaticos.expansion.com/rss/empresasminegocio.xml'),
+ (u'Interiores', u'http://estaticos.expansion.com/rss/empresasinteriores.xml'),
+ (u'Digitech', u'http://estaticos.expansion.com/rss/empresasdigitech.xml'),
- direction = 'ltr'
+ (u'Portada: Economía y Política', u'http://estaticos.expansion.com/rss/economiapolitica.xml'),
+ (u'Política', u'http://estaticos.expansion.com/rss/economia.xml'),
+ (u'Portada: Sociedad', u'http://estaticos.expansion.com/rss/entorno.xml'),
- html2lrf_options = [
- '--comment' , description
- , '--category' , category
- , '--publisher', publisher
- ]
+ (u'Portada: Opinión', u'http://estaticos.expansion.com/rss/opinion.xml'),
+ (u'Llaves y editoriales', u'http://estaticos.expansion.com/rss/opinioneditorialyllaves.xml'),
+ (u'Tribunas', u'http://estaticos.expansion.com/rss/opiniontribunas.xml'),
- html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
+ (u'Portada: Jurídico', u'http://estaticos.expansion.com/rss/juridico.xml'),
+ (u'Entrevistas', u'http://estaticos.expansion.com/rss/juridicoentrevistas.xml'),
+ (u'Opinión', u'http://estaticos.expansion.com/rss/juridicoopinion.xml'),
+ (u'Sentencias', u'http://estaticos.expansion.com/rss/juridicosentencias.xml'),
- feeds = [
- (u'Ultimas noticias', u'http://rss.expansion.com/rss/descarga.htm?data2=178')
- ,(u'Temas del dia' , u'http://rss.expansion.com/rss/descarga.htm?data2=178')
- ]
-
-
- keep_only_tags = [dict(name='div', attrs={'id':'principal'})]
-
- remove_tags = [
- dict(name=['object','link','script'])
- ,dict(name='div', attrs={'class':['utilidades','tit_relacionadas']})
- ]
-
- remove_tags_after = [dict(name='div', attrs={'class':'tit_relacionadas'})]
-
- def preprocess_html(self, soup):
- soup.html['dir' ] = self.direction
- mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
- soup.head.insert(0,mcharset)
- for item in soup.findAll(style=True):
- del item['style']
- return soup
+ (u'Mujer', u'http://estaticos.expansion.com/rss/mujer-empresa.xml'),
+ (u'Cataluña', u'http://estaticos.expansion.com/rss/catalunya.xml'),
+ (u'Función pública', u'http://estaticos.expansion.com/rss/funcion-publica.xml')
+ ]
From d21021ed8f2b4e872faf19263727e531570e4923 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 15:54:09 +0000
Subject: [PATCH 06/27] Clean up focus issues
---
src/calibre/gui2/library/views.py | 6 +++++-
src/calibre/gui2/search_box.py | 1 +
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py
index e1e9cf4456..357b48d1de 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -680,8 +680,12 @@ class BooksView(QTableView): # {{{
def set_editable(self, editable, supports_backloading):
self._model.set_editable(editable)
+ def search_proxy(self, txt):
+ self._model.search(txt)
+ self.setFocus(Qt.OtherFocusReason)
+
def connect_to_search_box(self, sb, search_done):
- sb.search.connect(self._model.search)
+ sb.search.connect(self.search_proxy)
self._search_done = search_done
self._model.searched.connect(self.search_done)
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 75d3c14ef1..5808a2dc46 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -404,6 +404,7 @@ class SearchBoxMixin(object): # {{{
def highlight_only_changed(self, toWhat):
self.current_view().model().set_highlight_only(toWhat)
+ self.focus_to_library()
# }}}
From 4dde6b9675ad92b16cfc9bdcfa10db8cbcc0adbc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 08:54:42 -0700
Subject: [PATCH 07/27] Fix #8263 (MSNBC recipe)
---
resources/recipes/msnbc.recipe | 58 ++++++++++++++++++++++++++--------
1 file changed, 44 insertions(+), 14 deletions(-)
diff --git a/resources/recipes/msnbc.recipe b/resources/recipes/msnbc.recipe
index 6e2fc50aaa..f093479e2f 100644
--- a/resources/recipes/msnbc.recipe
+++ b/resources/recipes/msnbc.recipe
@@ -1,5 +1,5 @@
__license__ = 'GPL v3'
-__copyright__ = '2010, Darko Miletic '
+__copyright__ = '2010-2011, Darko Miletic '
'''
msnbc.msn.com
'''
@@ -19,7 +19,16 @@ class MsNBC(BasicNewsRecipe):
publisher = 'msnbc.com'
category = 'news, USA, world'
language = 'en'
- extra_css = ' body{ font-family: sans-serif } .head{font-family: serif; font-size: xx-large; font-weight: bold; color: #CC0000} .abstract{font-weight: bold} .source{font-size: small} .updateTime{font-size: small} '
+ extra_css = """
+ body{ font-family: Georgia,Times,serif }
+ .hide{display: none}
+ .caption{font-family: Arial,sans-serif; font-size: x-small}
+ .entry-summary{font-family: Arial,sans-serif}
+ .copyright{font-size: 0.95em; font-style: italic}
+ .source-org{font-size: small; font-family: Arial,sans-serif}
+ img{display: block; margin-bottom: 0.5em}
+ span.byline{display: none}
+ """
conversion_options = {
'comments' : description
@@ -28,14 +37,20 @@ class MsNBC(BasicNewsRecipe):
,'publisher': publisher
}
- preprocess_regexps = [
- (re.compile(r'', re.DOTALL|re.IGNORECASE),lambda match: '')
- ,(re.compile(r'', re.DOTALL|re.IGNORECASE),lambda match: '
'),
- ]
+ remove_tags_before = dict(name='h1', attrs={'id':'headline'})
+ remove_tags_after = dict(name='span', attrs={'class':['copyright','Linear copyright']})
+ keep_only_tags=[
+ dict(attrs={'id':['headline','deck','byline','source','intelliTXT']})
+ ,dict(attrs={'class':['gl_headline','articleText','drawer-content Linear','v-center3','byline','textBodyBlack']})
+ ]
+ remove_attributes=['property','lang','rel','xmlns:fb','xmlns:v','xmlns:dc','xmlns:dcmitype','xmlns:og','xmlns:media','xmlns:vcard','typeof','itemscope','itemtype','itemprop','about','type','size','width','height','onreadystatechange','data','border','hspace','vspace']
+
+ remove_tags = [
+ dict(name=['iframe','object','link','embed','meta','table'])
+ ,dict(name='span', attrs={'class':['copyright','Linear copyright']})
+ ,dict(name='div', attrs={'class':'social'})
+ ]
- remove_tags_before = dict(name='div', attrs={'class':'head'})
- remove_tags_after = dict(name='div', attrs={'class':'copyright'})
- remove_tags = [dict(name=['iframe','object','link','script','form'])]
feeds = [
(u'US News' , u'http://rss.msnbc.msn.com/id/3032524/device/rss/rss.xml' )
@@ -48,11 +63,26 @@ class MsNBC(BasicNewsRecipe):
,(u'Tech & Science', u'http://rss.msnbc.msn.com/id/3032117/device/rss/rss.xml' )
]
- def print_version(self, url):
- return url + 'print/1/displaymode/1098/'
-
def preprocess_html(self, soup):
- for item in soup.head.findAll('div'):
- item.extract()
+ for item in soup.body.findAll('html'):
+ item.name='div'
+ for item in soup.body.findAll('div'):
+ if item.has_key('id') and item['id'].startswith('vine-'):
+ item.extract()
+ if item.has_key('class') and ( item['class'].startswith('ad') or item['class'].startswith('vine')):
+ item.extract()
+ for item in soup.body.findAll('img'):
+ if not item.has_key('alt'):
+ item['alt'] = 'image'
+ for item in soup.body.findAll('ol'):
+ if item.has_key('class') and item['class'].startswith('grid'):
+ item.extract()
+ for item in soup.body.findAll('span'):
+ if ( item.has_key('id') and item['id'].startswith('byLine') and item.string is None) or ( item.has_key('class') and item['class'].startswith('inline') ):
+ item.extract()
+ for alink in soup.findAll('a'):
+ if alink.string is not None:
+ tstr = alink.string
+ alink.replaceWith(tstr)
return soup
From fcbc4b331b776d1d52008be895fd672675fc6346 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 08:57:47 -0700
Subject: [PATCH 08/27] ZeroHedge by DM. Fixes #8289 (New recipe for economic
blog Zero Hedge)
---
resources/images/news/zerohedge.png | Bin 0 -> 3120 bytes
resources/recipes/zerohedge.recipe | 33 ++++++++++++++++++++++++++++
2 files changed, 33 insertions(+)
create mode 100644 resources/images/news/zerohedge.png
create mode 100644 resources/recipes/zerohedge.recipe
diff --git a/resources/images/news/zerohedge.png b/resources/images/news/zerohedge.png
new file mode 100644
index 0000000000000000000000000000000000000000..a2bc6cde143d007fed9d275221ce49a06bbc7371
GIT binary patch
literal 3120
zcmV-04A1k4P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T
zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p
z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&nehQ1i
z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW
zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X
zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4
zfg=2N-7=cNnjjOr{yriy6mMFgG#l
znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U
zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?
z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y
zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB
zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt
z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C
z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB
zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe
zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0
z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$
z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4
z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu
zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu
z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E
ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw
zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX
z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&
z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01
z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R
z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw
zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD
zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3|
zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy
zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z
zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}
z00047NklTLpTWjTf)X@)Le=-HxW^BOWd5A
zj9f$ob$78LQUX_3d39*0KoHSNv|g9<%H{DMxSVq?=lPyo$)?%wPZ=Sin#K`_tkgU5jUZf0X)Y4i0jdl0=nYz6wP>%0lX?q+y*xzc^LjL
zjxmLQxF21A$FpukO~}PNHiq?!<&uz)=b~OF)g-#Ph-mYiJJ`?4|#p
zoD|*-)fH_$*0~kkw^P6t{v?2$4z_Wgz;;u>4ptIDon`>v@gU;aoRB6$wlJLmbmM8L
zwVVU|LT|()_=K9sheL%Q((dUiRP7t?VGeK5hl6<5AuJcix*PyDC`uG!WXfa!0000<
KMNUMnLSTYHtj}-&
literal 0
HcmV?d00001
diff --git a/resources/recipes/zerohedge.recipe b/resources/recipes/zerohedge.recipe
new file mode 100644
index 0000000000..09f62e5b52
--- /dev/null
+++ b/resources/recipes/zerohedge.recipe
@@ -0,0 +1,33 @@
+__license__ = 'GPL v3'
+__copyright__ = '2011, Darko Miletic '
+'''
+www.zerohedge.com
+'''
+
+from calibre.web.feeds.recipes import BasicNewsRecipe
+
+class ZeroHedge(BasicNewsRecipe):
+ title = 'Zero Hedge'
+ __author__ = 'Darko Miletic'
+ description = 'On a long enough timeline the survival rate for everyone drops to zero'
+ oldest_article = 10
+ max_articles_per_feed = 100
+ no_stylesheets = True
+ use_embedded_content = True
+ encoding = 'utf8'
+ publisher = 'zero hedge'
+ category = 'news, USA, world, economy, politics'
+ language = 'en'
+ masthead_url = 'http://www.zerohedge.com/themes/newsflash/logo.png'
+ publication_type = 'blog'
+ extra_css = 'body{ font-family: sans-serif }'
+
+ conversion_options = {
+ 'comments' : description
+ ,'tags' : category
+ ,'language' : language
+ ,'publisher': publisher
+ }
+
+
+ feeds = [(u'Articles', u'http://feeds.feedburner.com/zerohedge/feed')]
From e3bbb4a0dec720a0b511f5a0299dd8297161cba0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 09:11:02 -0700
Subject: [PATCH 09/27] ...
---
src/calibre/library/caches.py | 10 ++++++----
src/calibre/library/database2.py | 4 ----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 6aef45dbbd..4168360d3a 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -411,7 +411,8 @@ class ResultCache(SearchQueryParser): # {{{
if isinstance(location, list):
if allow_recursion:
for loc in location:
- matches |= self.get_matches(loc, query, allow_recursion=False)
+ matches |= self.get_matches(loc, query, candidates,
+ allow_recursion=False)
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
@@ -419,11 +420,11 @@ class ResultCache(SearchQueryParser): # {{{
fm = self.field_metadata[location]
# take care of dates special case
if fm['datatype'] == 'datetime':
- return self.get_dates_matches(location, query.lower())
+ return self.get_dates_matches(location, query.lower(), candidates)
# take care of numbers special case
if fm['datatype'] in ('rating', 'int', 'float'):
- return self.get_numeric_matches(location, query.lower())
+ return self.get_numeric_matches(location, query.lower(), candidates)
# take care of the 'count' operator for is_multiples
if fm['is_multiple'] and \
@@ -431,7 +432,8 @@ class ResultCache(SearchQueryParser): # {{{
query[1:1] in '=<>!':
vf = lambda item, loc=fm['rec_index'], ms=fm['is_multiple']:\
len(item[loc].split(ms)) if item[loc] is not None else 0
- return self.get_numeric_matches(location, query[1:], val_func=vf)
+ return self.get_numeric_matches(location, query[1:],
+ candidates, val_func=vf)
# everything else, or 'all' matches
matchkind = CONTAINS_MATCH
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index aa491aff28..d2654577b9 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -341,10 +341,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.has_id = self.data.has_id
self.count = self.data.count
- # Count times get_metadata is called, and how many times in the cache
- self.gm_count = 0
- self.gm_missed = 0
-
for prop in ('author_sort', 'authors', 'comment', 'comments', 'isbn',
'publisher', 'rating', 'series', 'series_index', 'tags',
'title', 'timestamp', 'uuid', 'pubdate', 'ondevice'):
From 40b05944c2911e1d1ca5345d0dfc64b99be5d5ea Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 09:12:56 -0700
Subject: [PATCH 10/27] ...
---
src/calibre/gui2/preferences/toolbar.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/preferences/toolbar.py b/src/calibre/gui2/preferences/toolbar.py
index c13d956aea..26cdea19d3 100644
--- a/src/calibre/gui2/preferences/toolbar.py
+++ b/src/calibre/gui2/preferences/toolbar.py
@@ -37,7 +37,10 @@ class BaseModel(QAbstractListModel):
dont_remove_from=set(['toolbar-device']))
if name is None:
return FakeAction('--- '+_('Separator')+' ---', None)
- return gui.iactions[name]
+ try:
+ return gui.iactions[name]
+ except:
+ return None
def rowCount(self, parent):
return len(self._data)
@@ -124,7 +127,8 @@ class CurrentModel(BaseModel):
BaseModel.__init__(self)
self.gprefs_name = 'action-layout-'+key
current = gprefs[self.gprefs_name]
- self._data = [self.name_to_action(x, gui) for x in current]
+ self._data = [self.name_to_action(x, gui) for x in current]
+ self._data = [x for x in self._data if x is not None]
self.key = key
self.gui = gui
From d92c03de7d18bca07dff9c7247591708304b6802 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 09:21:20 -0700
Subject: [PATCH 11/27] Updated MIT Technology Review
---
resources/recipes/technology_review.recipe | 23 +++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/resources/recipes/technology_review.recipe b/resources/recipes/technology_review.recipe
index cc8f13733e..e7cc6700d7 100644
--- a/resources/recipes/technology_review.recipe
+++ b/resources/recipes/technology_review.recipe
@@ -35,7 +35,6 @@ class TechnologyReview(BasicNewsRecipe):
def get_article_url(self, article):
return article.get('guid', article.get('id', None))
-
def print_version(self, url):
baseurl='http://www.technologyreview.com/printer_friendly_article.aspx?id='
split1 = string.split(url,"/")
@@ -43,3 +42,25 @@ class TechnologyReview(BasicNewsRecipe):
split2= string.split(xxx,"/")
s = baseurl + split2[0]
return s
+
+
+ def postprocess_html(self,soup, True):
+ #remove picture
+ headerhtml = soup.find(True, {'class':'header'})
+ headerhtml.replaceWith("")
+
+ #remove close button
+ closehtml = soup.find(True, {'class':'close'})
+ closehtml.replaceWith("")
+
+ #remove banner advertisement
+ bannerhtml = soup.find(True, {'class':'bannerad'})
+ bannerhtml.replaceWith("")
+
+ #thanks kiklop74! This code removes all links from the text
+ for alink in soup.findAll('a'):
+ if alink.string is not None:
+ tstr = alink.string
+ alink.replaceWith(tstr)
+
+ return soup
From ccc0c02c67dceca4dcfe527bf080bf08161d456b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 16:43:47 +0000
Subject: [PATCH 12/27] Add arbitrary python function evaluation to formatter
---
src/calibre/utils/formatter.py | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py
index 40760bf91b..0b5f1d1f52 100644
--- a/src/calibre/utils/formatter.py
+++ b/src/calibre/utils/formatter.py
@@ -18,6 +18,24 @@ class _Parser(object):
LEX_NUM = 4
LEX_EOF = 5
+ def _python(self, func):
+ locals = {}
+ exec func in locals
+ if 'evaluate' not in locals:
+ self.error('no evaluate function in python')
+ try:
+ result = locals['evaluate'](self.parent.kwargs)
+ if isinstance(result, (float, int)):
+ result = unicode(result)
+ elif isinstance(result, list):
+ result = ','.join(result)
+ elif isinstance(result, str):
+ result = unicode(result)
+ return result
+ except Exception as e:
+ self.error('python function threw exception: ' + e.msg)
+
+
def _strcmp(self, x, y, lt, eq, gt):
v = strcmp(x, y)
if v < 0:
@@ -79,6 +97,7 @@ class _Parser(object):
'field' : (1, lambda s, x: s.parent.get_value(x, [], s.parent.kwargs)),
'multiply' : (2, partial(_math, op='*')),
'print' : (-1, _print),
+ 'python' : (1, _python),
'strcat' : (-1, _concat),
'strcmp' : (5, _strcmp),
'substr' : (3, lambda s, x, y, z: x[int(y): len(x) if int(z) == 0 else int(z)]),
@@ -362,7 +381,7 @@ class TemplateFormatter(string.Formatter):
(r'\'.*?((?
Date: Wed, 12 Jan 2011 17:18:06 +0000
Subject: [PATCH 13/27] Remember the state of the highlight_only check box.
Scroll to first matching row.
---
src/calibre/gui2/library/models.py | 12 +++++++++---
src/calibre/gui2/library/views.py | 2 ++
src/calibre/gui2/search_box.py | 4 ++++
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 98e61acf33..38a4b28744 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -94,6 +94,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.bool_blank_icon = QIcon(I('blank.png'))
self.device_connected = False
self.rows_matching = set()
+ self.lowest_row_matching = None
self.highlight_only = False
self.read_config()
@@ -233,7 +234,8 @@ class BooksModel(QAbstractTableModel): # {{{
def set_highlight_only(self, toWhat):
self.highlight_only = toWhat
- self.research()
+ if self.last_search:
+ self.research()
def search(self, text, reset=True):
try:
@@ -241,11 +243,15 @@ class BooksModel(QAbstractTableModel): # {{{
self.db.search('')
if not text:
self.rows_matching = set()
+ self.lowest_row_matching = None
else:
- self.rows_matching = set(self.db.search(text,
- return_matches=True))
+ self.rows_matching = self.db.search(text, return_matches=True)
+ if self.rows_matching:
+ self.lowest_row_matching = self.db.row(self.rows_matching[0])
+ self.rows_matching = set(self.rows_matching)
else:
self.rows_matching = set()
+ self.lowest_row_matching = None
self.db.search(text)
except ParseException as e:
self.searched.emit(e.msg)
diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py
index 357b48d1de..07c3cc21e4 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -682,6 +682,8 @@ class BooksView(QTableView): # {{{
def search_proxy(self, txt):
self._model.search(txt)
+ if self._model.lowest_row_matching:
+ self.scroll_to_row(self._model.lowest_row_matching)
self.setFocus(Qt.OtherFocusReason)
def connect_to_search_box(self, sb, search_done):
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 5808a2dc46..e4073a01c9 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -16,6 +16,7 @@ from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog
+from calibre.utils.config import dynamic
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
@@ -376,6 +377,8 @@ class SearchBoxMixin(object): # {{{
self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip())
self.clear_button.setStatusTip(self.clear_button.toolTip())
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
+ self.search_highlight_only.setChecked(
+ dynamic.get('search_highlight_only', False))
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@@ -403,6 +406,7 @@ class SearchBoxMixin(object): # {{{
self.current_view().setFocus(Qt.OtherFocusReason)
def highlight_only_changed(self, toWhat):
+ dynamic.set('search_highlight_only', toWhat)
self.current_view().model().set_highlight_only(toWhat)
self.focus_to_library()
From 2fca19878162b49847ae3a855c7a4d4d95822c19 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 12 Jan 2011 17:28:12 +0000
Subject: [PATCH 14/27] Select the row when scrolling to it
---
src/calibre/gui2/library/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py
index 07c3cc21e4..e72d0c32a1 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -683,7 +683,7 @@ class BooksView(QTableView): # {{{
def search_proxy(self, txt):
self._model.search(txt)
if self._model.lowest_row_matching:
- self.scroll_to_row(self._model.lowest_row_matching)
+ self.select_rows([self._model.lowest_row_matching], using_ids=False)
self.setFocus(Qt.OtherFocusReason)
def connect_to_search_box(self, sb, search_done):
From d7ea545df60327df0e16416aacac33aab6df21c2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 10:35:05 -0700
Subject: [PATCH 15/27] Fix Wired Daily
---
resources/recipes/wired_daily.recipe | 53 ++++++++++++++++++----------
1 file changed, 34 insertions(+), 19 deletions(-)
diff --git a/resources/recipes/wired_daily.recipe b/resources/recipes/wired_daily.recipe
index f06d28796e..df59c7c826 100644
--- a/resources/recipes/wired_daily.recipe
+++ b/resources/recipes/wired_daily.recipe
@@ -2,8 +2,10 @@
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
+import re
from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.chardet import xml_to_unicode
class Wired_Daily(BasicNewsRecipe):
@@ -15,30 +17,43 @@ class Wired_Daily(BasicNewsRecipe):
no_stylesheets = True
+ preprocess_regexps = [(re.compile(r'', re.DOTALL), lambda m:
+ '')]
+
remove_tags_before = dict(name='div', id='content')
- remove_tags = [dict(id=['social_tools', 'outerWrapper', 'sidebar',
- 'footer', 'advertisement', 'blog_subscription_unit',
- 'brightcove_component']),
- {'class':'entryActions'},
- dict(name=['noscript', 'script'])]
+ remove_tags = [dict(id=['header', 'commenting_module', 'post_nav',
+ 'social_tools', 'sidebar', 'footer', 'social_wishlist', 'pgwidget',
+ 'outerWrapper', 'inf_widget']),
+ {'class':['entryActions', 'advertisement', 'entryTags']},
+ dict(name=['noscript', 'script']),
+ dict(name='h4', attrs={'class':re.compile(r'rat\d+')}),
+ {'class':lambda x: x and x.startswith('contentjump')},
+ dict(name='li', attrs={'class':['entryCategories', 'entryEdit']})]
+
feeds = [
('Top News', 'http://feeds.wired.com/wired/index'),
- ('Culture', 'http://feeds.wired.com/wired/culture'),
- ('Software', 'http://feeds.wired.com/wired/software'),
- ('Mac', 'http://feeds.feedburner.com/cultofmac/bFow'),
- ('Gadgets', 'http://feeds.wired.com/wired/gadgets'),
- ('Cars', 'http://feeds.wired.com/wired/cars'),
- ('Entertainment', 'http://feeds.wired.com/wired/entertainment'),
- ('Gaming', 'http://feeds.wired.com/wired/gaming'),
- ('Science', 'http://feeds.wired.com/wired/science'),
- ('Med Tech', 'http://feeds.wired.com/wired/medtech'),
- ('Politics', 'http://feeds.wired.com/wired/politics'),
- ('Tech Biz', 'http://feeds.wired.com/wired/techbiz'),
- ('Commentary', 'http://feeds.wired.com/wired/commentary'),
+ ('Product Reviews',
+ 'http://www.wired.com/reviews/feeds/latestProductsRss'),
+ ('Autopia', 'http://www.wired.com/autopia/feed/'),
+ ('Danger Room', 'http://www.wired.com/dangerroom/feed/'),
+ ('Epicenter', 'http://www.wired.com/epicenter/feed/'),
+ ('Gadget Lab', 'http://www.wired.com/gadgetlab/feed/'),
+ ('Geek Dad', 'http://www.wired.com/geekdad/feed/'),
+ ('Playbook', 'http://www.wired.com/playbook/feed/'),
+ ('Rawfile', 'http://www.wired.com/rawfile/feed/'),
+ ('This Day in Tech', 'http://www.wired.com/thisdayintech/feed/'),
+ ('Threat Level', 'http://www.wired.com/threatlevel/feed/'),
+ ('Underwire', 'http://www.wired.com/underwire/feed/'),
+ ('Web Monkey', 'http://www.webmonkey.com/feed/'),
+ ('Science', 'http://www.wired.com/wiredscience/feed/'),
]
+ def populate_article_metadata(self, article, soup, first):
+ if article.text_summary:
+ article.text_summary = xml_to_unicode(article.text_summary,
+ resolve_entities=True)[0]
+
def print_version(self, url):
- return url.replace('http://www.wired.com/', 'http://www.wired.com/print/')
-
+ return url + '/all/1'
From 0785b9cf92ea6b5f2fb41b005c0dfeb8e2a9f6db Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 11:17:20 -0700
Subject: [PATCH 16/27] Fix #8218 (Bulk Edit window too tall for screen)
---
src/calibre/gui2/dialogs/metadata_bulk.py | 10 +-
src/calibre/gui2/dialogs/metadata_bulk.ui | 1445 +++++++++++----------
2 files changed, 736 insertions(+), 719 deletions(-)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index e1ee4327f3..5ea8f00148 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -15,7 +15,7 @@ from calibre.ebooks.metadata import string_to_authors, authors_to_string
from calibre.ebooks.metadata.book.base import composite_formatter
from calibre.ebooks.metadata.meta import get_metadata
from calibre.gui2.custom_column_widgets import populate_metadata_page
-from calibre.gui2 import error_dialog
+from calibre.gui2 import error_dialog, ResizableDialog
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.utils.config import dynamic
from calibre.utils.titlecase import titlecase
@@ -49,7 +49,7 @@ def get_cover_data(path):
-class MyBlockingBusy(QDialog):
+class MyBlockingBusy(QDialog): # {{{
do_one_signal = pyqtSignal()
@@ -241,8 +241,9 @@ class MyBlockingBusy(QDialog):
self.current_index += 1
self.do_one_signal.emit()
+ # }}}
-class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
+class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
s_r_functions = { '' : lambda x: x,
_('Lower Case') : lambda x: icu_lower(x),
@@ -261,9 +262,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
]
def __init__(self, window, rows, model, tab):
- QDialog.__init__(self, window)
+ ResizableDialog.__init__(self, window)
Ui_MetadataBulkDialog.__init__(self)
- self.setupUi(self)
self.model = model
self.db = model.db
self.ids = [self.db.id(r) for r in rows]
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index 41858b099b..5c0f1ec78f 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.ui
+++ b/src/calibre/gui2/dialogs/metadata_bulk.ui
@@ -6,8 +6,8 @@
0
0
- 752
- 633
+ 819
+ 650
@@ -17,8 +17,8 @@
:/images/edit_input.png:/images/edit_input.png
-
- -
+
+
-
@@ -28,818 +28,836 @@
- -
-
-
- 6
+
-
+
+
+ QFrame::NoFrame
-
+
0
-
-
-
-
+
+ true
+
+
+
+
+ 0
+ 0
+ 811
+ 589
+
+
+
+
0
-
-
- &Basic metadata
-
-
-
-
-
-
- &Author(s):
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- authors
-
-
-
- -
-
-
- A&utomatically set author sort
-
-
-
- -
-
-
- Author s&ort:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- author_sort
-
-
-
- -
-
-
- Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles.
-
-
-
- -
-
-
- &Rating:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- rating
-
-
-
- -
-
-
- Rating of this book. 0-5 stars
-
-
- Rating of this book. 0-5 stars
-
-
- QAbstractSpinBox::PlusMinus
-
-
- No change
-
-
- stars
-
-
- -1
-
-
- 5
-
-
- -1
-
-
-
- -
-
-
- &Publisher:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- publisher
-
-
-
- -
-
-
- true
-
-
-
- -
-
-
- Add ta&gs:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- tags
-
-
-
- -
-
-
- Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.
-
-
-
- -
-
-
- Open Tag Editor
-
-
- Open Tag Editor
-
-
-
- :/images/chapters.png:/images/chapters.png
-
-
-
- -
-
-
- &Remove tags:
-
-
- remove_tags
-
-
-
- -
-
-
- Comma separated list of tags to remove from the books.
-
-
-
- -
-
-
- Check this box to remove all tags from the books.
-
-
- Remove all
-
-
-
- -
-
-
- &Series:
-
-
- Qt::PlainText
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- series
-
-
-
- -
-
-
-
-
+
-
+
+
+ 0
+
+
+
+ &Basic metadata
+
+
+
-
+
+
+ &Author(s):
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ authors
+
+
+
+ -
+
+
+ A&utomatically set author sort
+
+
+
+ -
+
+
+ Author s&ort:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ author_sort
+
+
+
+ -
+
- List of known series. You can add new series.
+ Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles.
+
+
+
+ -
+
+
+ &Rating:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ rating
+
+
+
+ -
+
+
+ Rating of this book. 0-5 stars
- List of known series. You can add new series.
+ Rating of this book. 0-5 stars
+
+ QAbstractSpinBox::PlusMinus
+
+
+ No change
+
+
+ stars
+
+
+ -1
+
+
+ 5
+
+
+ -1
+
+
+
+ -
+
+
+ &Publisher:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ publisher
+
+
+
+ -
+
true
-
- QComboBox::InsertAlphabetically
+
+
+ -
+
+
+ Add ta&gs:
-
- QComboBox::AdjustToContents
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ tags
- -
-
+
-
+
- If checked, the series will be cleared
+ Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.
+
+
+
+ -
+
+
+ Open Tag Editor
- Clear series
+ Open Tag Editor
+
+
+
+ :/images/chapters.png:/images/chapters.png
- -
-
-
- Qt::Horizontal
+
-
+
+
+ &Remove tags:
-
-
- 20
- 0
-
+
+ remove_tags
-
+
-
-
- -
-
-
-
-
+
-
+
- If not checked, the series number for the books will be set to 1.
+ Comma separated list of tags to remove from the books.
+
+
+
+ -
+
+
+ Check this box to remove all tags from the books.
+
+
+ Remove all
+
+
+
+ -
+
+
+ &Series:
+
+
+ Qt::PlainText
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ series
+
+
+
+ -
+
+
-
+
+
+ List of known series. You can add new series.
+
+
+ List of known series. You can add new series.
+
+
+ true
+
+
+ QComboBox::InsertAlphabetically
+
+
+ QComboBox::AdjustToContents
+
+
+
+ -
+
+
+ If checked, the series will be cleared
+
+
+ Clear series
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 0
+
+
+
+
+
+
+ -
+
+
-
+
+
+ If not checked, the series number for the books will be set to 1.
If checked, selected books will be automatically numbered, in the order
you selected them. So if you selected Book A and then Book B,
Book A will have series number 1 and Book B series number 2.
-
-
- Automatically number books in this series
-
-
-
- -
-
-
- false
-
-
- Series will normally be renumbered from the highest number in the database
+
+
+ Automatically number books in this series
+
+
+
+ -
+
+
+ false
+
+
+ Series will normally be renumbered from the highest number in the database
for that series. Checking this box will tell calibre to start numbering
from the value in the box
+
+
+ Force numbers to start with
+
+
+
+ -
+
+
+ false
+
+
+ 1
+
+
+ 990000
+
+
+ 1
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 10
+
+
+
+
+
+
+ -
+
+
+ Remove &format:
+
+
+ remove_format
+
+
+
+ -
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ &Swap title and author
+
+
+
+ -
+
+
+ Force the title to be in title case. If both this and swap authors are checked,
+title and author are swapped before the title case is set
- Force numbers to start with
+ Change title to title case
- -
-
-
- false
-
-
- 1
-
-
- 990000
-
-
- 1
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 20
- 10
-
-
-
-
-
-
- -
-
-
- Remove &format:
-
-
- remove_format
-
-
-
- -
-
-
- -
-
-
- true
-
-
-
- -
-
-
- &Swap title and author
-
-
-
- -
-
-
- Force the title to be in title case. If both this and swap authors are checked,
-title and author are swapped before the title case is set
-
-
- Change title to title case
-
-
-
- -
-
-
- Remove stored conversion settings for the selected books.
+
-
+
+
+ Remove stored conversion settings for the selected books.
Future conversion of these books will use the default settings.
-
-
- Remove &stored conversion settings for the selected books
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Change &cover
-
-
-
-
-
-
- &Generate default cover
-
-
-
- -
-
-
- &Remove cover
-
-
-
- -
-
-
- Set from &ebook file(s)
-
-
-
-
-
-
-
-
-
-
- &Custom metadata
-
-
-
-
- &Search and replace
-
-
-
- QLayout::SetMinimumSize
-
- -
-
-
- true
-
-
- true
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- Search &field:
-
-
- search_field
-
-
-
- -
-
-
- The name of the field that you want to search
-
-
-
- -
-
-
-
-
+
- Search &mode:
-
-
- search_mode
+ Remove &stored conversion settings for the selected books
- -
-
-
- Choose whether to use basic text matching or advanced regular expression matching
-
-
-
- -
-
+
-
+
- Qt::Horizontal
+ Qt::Vertical
20
- 10
+ 40
-
-
- -
-
-
- Te&mplate:
-
-
- s_r_template
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Enter a template to be used as the source for the search/replace
-
-
-
- -
-
-
- &Search for:
-
-
- search_for
-
-
-
- -
-
-
-
- 100
- 0
-
-
-
- Enter the what you are looking for, either plain text or a regular expression, depending on the mode
-
-
-
- -
-
-
- Check this box if the search string must match exactly upper and lower case. Uncheck it if case is to be ignored
-
-
- Cas&e sensitive
-
-
- true
-
-
-
- -
-
-
- &Replace with:
-
-
- replace_with
-
-
-
- -
-
-
- The replacement text. The matched search text will be replaced with this string
-
-
-
- -
-
-
-
-
-
- &Apply function after replace:
-
-
- replace_func
+
-
+
+
+ Change &cover
+
+
-
+
+
+ &Generate default cover
+
+
+
+ -
+
+
+ &Remove cover
+
+
+
+ -
+
+
+ Set from &ebook file(s)
+
+
+
+
- -
-
-
- Specify how the text is to be processed after matching and replacement. In character mode, the entire
-field is processed. In regular expression mode, only the matched text is processed
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 20
- 10
-
-
-
-
-
- -
-
-
- &Destination field:
+
+
+
+ &Custom metadata
+
+
+
+
+ &Search and replace
+
+
+
+ QLayout::SetMinimumSize
-
- destination_field
-
-
-
- -
-
-
- The field that the text will be put into after all replacements.
-If blank, the source field is used if the field is modifiable
-
-
-
- -
-
-
-
-
+
-
+
+
+ true
+
+
+ true
+
+
+
+ -
+
- M&ode:
+
+
+
+
+ -
+
+
+ Search &field:
- replace_mode
+ search_field
- -
-
+
-
+
- Specify how the text should be copied into the destination.
+ The name of the field that you want to search
- -
-
+
-
+
+
-
+
+
+ Search &mode:
+
+
+ search_mode
+
+
+
+ -
+
+
+ Choose whether to use basic text matching or advanced regular expression matching
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 10
+
+
+
+
+
+
+ -
+
+
+ Te&mplate:
+
+
+ s_r_template
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
- Specifies whether result items should be split into multiple values or
-left as single values. This option has the most effect when the source field is
-not multiple and the destination field is multiple
+ Enter a template to be used as the source for the search/replace
+
+
+
+ -
+
+
+ &Search for:
+
+
+ search_for
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Enter the what you are looking for, either plain text or a regular expression, depending on the mode
+
+
+
+ -
+
+
+ Check this box if the search string must match exactly upper and lower case. Uncheck it if case is to be ignored
- Split &result
+ Cas&e sensitive
true
- -
-
-
- Qt::Horizontal
-
-
-
- 20
- 10
-
-
-
-
-
-
- -
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 20
- 0
-
-
-
-
- -
-
+
-
+
- For multiple-valued fields, sho&w
+ &Replace with:
- results_count
+ replace_with
- -
-
-
- true
-
-
- 1
-
-
- 999
-
-
- 999
-
-
-
- -
-
-
- values starting a&t
-
-
- starting_from
-
-
-
- -
-
-
- true
-
-
- 1
-
-
- 999
-
-
- 1
-
-
-
- -
-
-
- with values separated b&y
-
-
- multiple_separator
-
-
-
- -
-
+
-
+
- Used when displaying test results to separate values in multiple-valued fields
+ The replacement text. The matched search text will be replaced with this string
-
-
- -
-
-
- QFrame::NoFrame
-
-
- true
-
-
-
-
- 0
- 0
- 726
- 334
-
-
-
-
-
-
+
-
+
+
-
+
- Test text
+ &Apply function after replace:
+
+
+ replace_func
- -
-
-
- Test result
+
-
+
+
+ Specify how the text is to be processed after matching and replacement. In character mode, the entire
+field is processed. In regular expression mode, only the matched text is processed
- -
-
-
- Your test:
-
-
-
- -
-
-
- -
-
-
- -
-
+
-
+
- Qt::Vertical
+ Qt::Horizontal
20
- 5
+ 10
-
-
-
-
-
-
-
-
+
+ -
+
+
+ &Destination field:
+
+
+ destination_field
+
+
+
+ -
+
+
+ The field that the text will be put into after all replacements.
+If blank, the source field is used if the field is modifiable
+
+
+
+ -
+
+
-
+
+
+ M&ode:
+
+
+ replace_mode
+
+
+
+ -
+
+
+ Specify how the text should be copied into the destination.
+
+
+
+ -
+
+
+ Specifies whether result items should be split into multiple values or
+left as single values. This option has the most effect when the source field is
+not multiple and the destination field is multiple
+
+
+ Split &result
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 10
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 0
+
+
+
+
+ -
+
+
+ For multiple-valued fields, sho&w
+
+
+ results_count
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+ 999
+
+
+ 999
+
+
+
+ -
+
+
+ values starting a&t
+
+
+ starting_from
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+ 999
+
+
+ 1
+
+
+
+ -
+
+
+ with values separated b&y
+
+
+ multiple_separator
+
+
+
+ -
+
+
+ Used when displaying test results to separate values in multiple-valued fields
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 197
+ 60
+
+
+
+
-
+
+
+ Test text
+
+
+
+ -
+
+
+ Test result
+
+
+
+ -
+
+
+ Your test:
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- -
+
-
Qt::Horizontal
@@ -893,7 +911,6 @@ not multiple and the destination field is multiple
swap_title_and_author
change_title_to_title_case
button_box
- central_widget
search_field
search_mode
s_r_template
From 173e7eb9720c88fec4947c170ddd24d9d795d9b6 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 11:19:02 -0700
Subject: [PATCH 17/27] ...
---
src/calibre/gui2/dialogs/metadata_bulk.ui | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index 5c0f1ec78f..9240cd1af8 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.ui
+++ b/src/calibre/gui2/dialogs/metadata_bulk.ui
@@ -6,7 +6,7 @@
0
0
- 819
+ 850
650
@@ -44,7 +44,7 @@
0
0
- 811
+ 842
589
From 08d7a000c95eb91e9d31eb6af27a74868f6851b1 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 11:40:46 -0700
Subject: [PATCH 18/27] Prevent custom columns from overriding the information
shown in the book details panel
---
src/calibre/gui2/library/models.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 38a4b28744..eea452c238 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -358,8 +358,9 @@ class BooksModel(QAbstractTableModel): # {{{
name, val = mi.format_field(key)
if mi.metadata_for_field(key)['datatype'] == 'comments':
name += ':html'
- if val:
+ if val and name not in data:
data[name] = val
+
return data
From 1381fe91605155fe3e8e8b418992087b71cbf756 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 12:45:09 -0700
Subject: [PATCH 19/27] ...
---
src/calibre/__init__.py | 12 ++++++++++++
src/calibre/gui2/viewer/main.py | 3 ++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py
index 2585b5d081..a4f7439405 100644
--- a/src/calibre/__init__.py
+++ b/src/calibre/__init__.py
@@ -459,6 +459,18 @@ def force_unicode(obj, enc=preferred_encoding):
obj = obj.decode('utf-8')
return obj
+def as_unicode(obj, enc=preferred_encoding):
+ if not isbytestring(obj):
+ try:
+ obj = unicode(obj)
+ except:
+ try:
+ obj = str(obj)
+ except:
+ obj = repr(obj)
+ return force_unicode(obj, enc=enc)
+
+
def human_readable(size):
""" Convert a size in bytes into a human readable form """
diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py
index 25f69b1558..6468cd88c6 100644
--- a/src/calibre/gui2/viewer/main.py
+++ b/src/calibre/gui2/viewer/main.py
@@ -26,6 +26,7 @@ from calibre.gui2.search_box import SearchBox2
from calibre.ebooks.metadata import MetaInformation
from calibre.customize.ui import available_input_formats
from calibre.gui2.viewer.dictionary import Lookup
+from calibre import as_unicode
class TOCItem(QStandardItem):
@@ -632,7 +633,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
else:
r = getattr(worker.exception, 'reason', worker.exception)
error_dialog(self, _('Could not open ebook'),
- unicode(r), det_msg=worker.traceback, show=True)
+ as_unicode(r), det_msg=worker.traceback, show=True)
self.close_progress_indicator()
else:
self.metadata.show_opf(self.iterator.opf, os.path.splitext(pathtoebook)[1][1:])
From f0447ea1cd29ff4405309251f3437949999970a0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 12:45:41 -0700
Subject: [PATCH 20/27] ...
---
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 c2588f57a8..a4e8bb6972 100644
--- a/src/calibre/gui2/dialogs/metadata_single.py
+++ b/src/calibre/gui2/dialogs/metadata_single.py
@@ -823,7 +823,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if book.series_index is not None:
self.series_index.setValue(book.series_index)
if book.has_cover:
- if d.opt_auto_download_cover.isChecked() and book.has_cover:
+ if d.opt_auto_download_cover.isChecked():
self.fetch_cover()
else:
self.fetch_cover_button.setFocus(Qt.OtherFocusReason)
From a9c671efaf42cd533bd485e5243c12114f01a8f8 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 12:50:02 -0700
Subject: [PATCH 21/27] ...
---
resources/recipes/msnbc.recipe | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/resources/recipes/msnbc.recipe b/resources/recipes/msnbc.recipe
index f093479e2f..6e58585341 100644
--- a/resources/recipes/msnbc.recipe
+++ b/resources/recipes/msnbc.recipe
@@ -4,7 +4,6 @@ __copyright__ = '2010-2011, Darko Miletic '
msnbc.msn.com
'''
-import re
from calibre.web.feeds.recipes import BasicNewsRecipe
class MsNBC(BasicNewsRecipe):
@@ -19,7 +18,7 @@ class MsNBC(BasicNewsRecipe):
publisher = 'msnbc.com'
category = 'news, USA, world'
language = 'en'
- extra_css = """
+ extra_css = """
body{ font-family: Georgia,Times,serif }
.hide{display: none}
.caption{font-family: Arial,sans-serif; font-size: x-small}
@@ -44,7 +43,7 @@ class MsNBC(BasicNewsRecipe):
,dict(attrs={'class':['gl_headline','articleText','drawer-content Linear','v-center3','byline','textBodyBlack']})
]
remove_attributes=['property','lang','rel','xmlns:fb','xmlns:v','xmlns:dc','xmlns:dcmitype','xmlns:og','xmlns:media','xmlns:vcard','typeof','itemscope','itemtype','itemprop','about','type','size','width','height','onreadystatechange','data','border','hspace','vspace']
-
+
remove_tags = [
dict(name=['iframe','object','link','embed','meta','table'])
,dict(name='span', attrs={'class':['copyright','Linear copyright']})
@@ -70,7 +69,7 @@ class MsNBC(BasicNewsRecipe):
if item.has_key('id') and item['id'].startswith('vine-'):
item.extract()
if item.has_key('class') and ( item['class'].startswith('ad') or item['class'].startswith('vine')):
- item.extract()
+ item.extract()
for item in soup.body.findAll('img'):
if not item.has_key('alt'):
item['alt'] = 'image'
@@ -83,6 +82,6 @@ class MsNBC(BasicNewsRecipe):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
- alink.replaceWith(tstr)
+ alink.replaceWith(tstr)
return soup
From 24e60cc35778d6699e8f90d5a1308e93d75c0a86 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 12:55:15 -0700
Subject: [PATCH 22/27] Remove code duplication
---
src/calibre/ebooks/txt/processor.py | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/src/calibre/ebooks/txt/processor.py b/src/calibre/ebooks/txt/processor.py
index 3702bbfabe..e1979063c0 100644
--- a/src/calibre/ebooks/txt/processor.py
+++ b/src/calibre/ebooks/txt/processor.py
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+__license__ = 'GPL v3'
+__copyright__ = '2009, John Schember '
+__docformat__ = 'restructuredtext en'
+
'''
Read content from txt file.
@@ -10,10 +14,7 @@ from calibre import prepare_string_for_xml, isbytestring
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.txt.heuristicprocessor import TXTHeuristicProcessor
from calibre.ebooks.conversion.preprocess import DocAnalysis
-
-__license__ = 'GPL v3'
-__copyright__ = '2009, John Schember '
-__docformat__ = 'restructuredtext en'
+from calibre.utils.cleantext import clean_ascii_chars
HTML_TEMPLATE = u'%s\n%s\n'
@@ -33,9 +34,7 @@ def clean_txt(txt):
# Remove excessive line breaks.
txt = re.sub('\n{3,}', '\n\n', txt)
#remove ASCII invalid chars : 0 to 8 and 11-14 to 24
- chars = list(range(8)) + [0x0B, 0x0E, 0x0F] + list(range(0x10, 0x19))
- illegal_chars = re.compile(u'|'.join(map(unichr, chars)))
- txt = illegal_chars.sub('', txt)
+ txt = clean_ascii_chars(txt)
return txt
From 0c685dcfe0d3407b877f3df63589a21a07ee4285 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 13:03:08 -0700
Subject: [PATCH 23/27] BibTeX catalog: Add support for custom columns
---
src/calibre/gui2/catalog/catalog_bibtex.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/calibre/gui2/catalog/catalog_bibtex.py b/src/calibre/gui2/catalog/catalog_bibtex.py
index 5030cf6ec8..7b7739bb46 100644
--- a/src/calibre/gui2/catalog/catalog_bibtex.py
+++ b/src/calibre/gui2/catalog/catalog_bibtex.py
@@ -27,14 +27,17 @@ class PluginWidget(QWidget, Ui_Form):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
- from calibre.library.catalog import FIELDS
- self.all_fields = []
- for x in FIELDS :
- if x != 'all':
- self.all_fields.append(x)
- QListWidgetItem(x, self.db_fields)
def initialize(self, name, db): #not working properly to update
+ from calibre.library.catalog import FIELDS
+
+ self.all_fields = [x for x in FIELDS if x != 'all']
+ #add custom columns
+ self.all_fields.extend([x for x in sorted(db.custom_field_keys())])
+ #populate
+ for x in self.all_fields:
+ QListWidgetItem(x, self.db_fields)
+
self.name = name
fields = gprefs.get(name+'_db_fields', self.all_fields)
# Restore the activated db_fields from last use
From be5519221ef0d99aa5aa4731a854d6075a131094 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 13:04:26 -0700
Subject: [PATCH 24/27] ...
---
src/calibre/utils/cleantext.py | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/src/calibre/utils/cleantext.py b/src/calibre/utils/cleantext.py
index b4afe7576d..938960df93 100644
--- a/src/calibre/utils/cleantext.py
+++ b/src/calibre/utils/cleantext.py
@@ -3,7 +3,7 @@ __license__ = 'GPL 3'
__copyright__ = '2010, sengian '
__docformat__ = 'restructuredtext en'
-import re
+import re, htmlentitydefs
_ascii_pat = None
@@ -21,3 +21,32 @@ def clean_ascii_chars(txt, charlist=None):
pat = re.compile(u'|'.join(map(unichr, charlist)))
return pat.sub('', txt)
+##
+# Fredrik Lundh: http://effbot.org/zone/re-sub.htm#unescape-html
+# Removes HTML or XML character references and entities from a text string.
+#
+# @param text The HTML (or XML) source text.
+# @return The plain text, as a Unicode string, if necessary.
+
+def unescape(text, rm=False, rchar=u''):
+ def fixup(m, rm=rm, rchar=rchar):
+ text = m.group(0)
+ if text[:2] == "":
+ # character reference
+ try:
+ if text[:3] == "":
+ return unichr(int(text[3:-1], 16))
+ else:
+ return unichr(int(text[2:-1]))
+ except ValueError:
+ pass
+ else:
+ # named entity
+ try:
+ text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
+ except KeyError:
+ pass
+ if rm:
+ return rchar #replace by char
+ return text # leave as is
+ return re.sub("?\w+;", fixup, text)
From ee98918a0c1d4464f2c79c492aa5d1cdf19ec0a9 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 13:17:56 -0700
Subject: [PATCH 25/27] RTF metadata: Add support for publisher and tags. Fixes
#6657 (RTF metadata)
---
src/calibre/ebooks/metadata/rtf.py | 90 +++++++++++++++++++-----------
1 file changed, 56 insertions(+), 34 deletions(-)
diff --git a/src/calibre/ebooks/metadata/rtf.py b/src/calibre/ebooks/metadata/rtf.py
index ad41125575..c20d880a2f 100644
--- a/src/calibre/ebooks/metadata/rtf.py
+++ b/src/calibre/ebooks/metadata/rtf.py
@@ -10,7 +10,8 @@ from calibre.ebooks.metadata import MetaInformation, string_to_authors
title_pat = re.compile(r'\{\\info.*?\{\\title(.*?)(? 6:
- md += '}'
+ md.append(r'{\subject %s}'%(comment,))
+ if options.publisher:
+ publisher = options.publisher.encode('ascii', 'ignore')
+ md.append(r'{\manager %s}'%(publisher,))
+ if options.tags:
+ tags = u', '.join(options.tags)
+ tags = tags.encode('ascii', 'ignore')
+ md.append(r'{\category %s}'%(tags,))
+ if len(md) > 1:
+ md.append('}')
stream.seek(0)
src = stream.read()
- ans = src[:6] + md + src[6:]
+ ans = src[:6] + u''.join(md) + src[6:]
stream.seek(0)
stream.write(ans)
@@ -156,7 +169,7 @@ def set_metadata(stream, options):
base_pat = r'\{\\name(.*?)(?
Date: Wed, 12 Jan 2011 13:25:17 -0700
Subject: [PATCH 26/27] ...
---
src/calibre/trac/bzr_commit_plugin.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/calibre/trac/bzr_commit_plugin.py b/src/calibre/trac/bzr_commit_plugin.py
index df6bf699d1..6c36115cae 100644
--- a/src/calibre/trac/bzr_commit_plugin.py
+++ b/src/calibre/trac/bzr_commit_plugin.py
@@ -110,6 +110,7 @@ class cmd_commit(_cmd_commit):
suffix = 'The fix will be in the next release.'
action = action+'ed'
msg = '%s in branch %s. %s'%(action, nick, suffix)
+ msg = msg.replace('Fixesed', 'Fixed')
server = xmlrpclib.ServerProxy(url)
server.ticket.update(int(bug), msg,
{'status':'closed', 'resolution':'fixed'},
From 2f08bc5086d5928a59a0c4075e344441be44f4ff Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 12 Jan 2011 13:44:15 -0700
Subject: [PATCH 27/27] Fix db2.get_metadata.formats
---
src/calibre/library/database2.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index d2654577b9..5f66297322 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -706,6 +706,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
formats = row[fm['formats']]
if not formats:
formats = None
+ else:
+ formats = formats.split(',')
mi.formats = formats
tags = row[fm['tags']]
if tags: