Sync to trunk.

This commit is contained in:
John Schember 2010-01-05 17:51:47 -05:00
commit cc6932ee98
12 changed files with 211 additions and 72 deletions

View File

@ -4,13 +4,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Profile to download CNN
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
class CNN(BasicNewsRecipe):
title = 'CNN'
description = 'Global news'
timefmt = ' [%d %b %Y]'
__author__ = 'Kovid Goyal and Sujata Raman'
__author__ = 'Krittika Goyal and Sujata Raman'
language = 'en'
no_stylesheets = True
@ -18,7 +19,8 @@ class CNN(BasicNewsRecipe):
oldest_article = 15
recursions = 1
match_regexps = [r'http://sportsillustrated.cnn.com/.*/[1-9].html']
max_articles_per_feed = 25
extra_css = '''
.cnn_strycntntlft{font-family :Arial,Helvetica,sans-serif;}
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
@ -44,7 +46,7 @@ class CNN(BasicNewsRecipe):
.cnnInlineT1Caption{font-family :Arial,Helvetica,sans-serif; font-size:x-small;font-weight:bold;}
.cnnInlineT1Credit{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#333333;}
.col10{color:#5A637E;}
.cnnInlineRailBulletList{color:black;}
.cnnInlineRailBulletList{color:black;}
.cnnLine0{font-family :Arial,Helvetica,sans-serif; color:#666666;font-weight:bold;}
.cnnTimeStamp{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#333333;}
.galleryhedDek{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#575757;}
@ -55,31 +57,22 @@ class CNN(BasicNewsRecipe):
.captionname{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#575757;}
inStoryIE{{font-family :Arial,Helvetica,sans-serif; font-size:x-small;}
'''
keep_only_tags = [
dict(name='div', attrs={'class':["cnnWCBoxContent","cnnContent","cnnMainBodySecs","cnn_storyarea","cnn_strycntntlft","cnn_strytmstmp"]}),
dict(name='div', attrs={'id':["contentBody","content"]}),
dict(name='td', attrs={'id':["cnnRecapStory"]}),
dict(name='td', attrs={'class':["cnnBigSideLeft"]}),
]
remove_tags = [
dict(name='div', attrs={'class':["cnnInlineRailSelectList","cnn_strycntntrgt","cnn_strybtntools","cnn_strylctcntr cnn_strylctcqrelt","cnn_strylceclbtn","cnn_stryftsbttm","cnn_strybtmmorebx","storyLink","article-tools clearfix","widget video related-video vList","cnnFooterBox","scrollArrows","boxHeading","cnnInlineMailbag","mainCol_lastBlock","cnn_bookmarks","cnnFooterBox","cnnEndOfStory","cnnInlineSL","cnnStoryHighlights","cnnFooterClick","cnnSnapShotHeader","cnnStoryToolsFooter","cnnWsnr","cnnUGCBox","cnnTopNewsModule","cnnStoryElementBox","cnnStoryPhotoBoxNavigation"]}),
dict(name='span', attrs={'class':["cnnEmbeddedMosLnk"]}),
dict(name='div', attrs={'id':["cnnIncldHlder","articleCommentsContainer","featuredContent","superstarsWidget","shareMenuContainer","rssMenuContainer","storyBrandingBanner","cnnRightCol","siteFeatures","quigo628","rightColumn","clickIncludeBox","cnnHeaderRightCol","cnnSCFontLabel","cnnSnapShotBottomRight","cnnSCFontButtons","rightColumn"]}),
dict(name='p', attrs={'class':["cnnTopics"]}),
dict(name='td', attrs={'class':["cnnRightRail"]}),
dict(name='table', attrs={'class':["cnnTMbox"]}),
dict(name='ul', attrs={'id':["cnnTopNav","cnnBotNav","cnnSBNav"]}),
dict(name='div', attrs={'id':["cnn_ftrcntnt"]})
]
# def print_version(self, url):
# return 'http://www.printthis.clickability.com/pt/printThis?clickMap=printThis&fb=Y&url=' + url
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
remove_tags = [
dict(name='iframe'),
dict(name='div', attrs={'class':['cnnEndOfStory', 'cnnShareThisItem', 'cnn_strylctcntr cnn_strylctcqrelt', 'cnnShareBoxContent', 'cnn_strybtmcntnt', 'cnn_strycntntrgt']}),
dict(name='div', attrs={'id':['IEContainer', 'clickIncludeBox']}),
#dict(name='ul', attrs={'class':'article-tools'}),
#dict(name='ul', attrs={'class':'articleTools'}),
]
feeds = [
('Top News', 'http://rss.cnn.com/rss/cnn_topstories.rss'),
('World', 'http://rss.cnn.com/rss/cnn_world.rss'),
('U.S.', 'http://rss.cnn.com/rss/cnn_us.rss'),
('Sports', 'http://rss.cnn.com/rss/si_topstories.rss'),
#('Sports', 'http://rss.cnn.com/rss/si_topstories.rss'),
('Business', 'http://rss.cnn.com/rss/money_latest.rss'),
('Politics', 'http://rss.cnn.com/rss/cnn_allpolitics.rss'),
('Law', 'http://rss.cnn.com/rss/cnn_law.rss'),
@ -91,10 +84,15 @@ class CNN(BasicNewsRecipe):
('Offbeat', 'http://rss.cnn.com/rss/cnn_offbeat.rss'),
('Most Popular', 'http://rss.cnn.com/rss/cnn_mostpopular.rss')
]
def preprocess_html(self, soup):
for tag in soup.findAll(name=['ul','li']):
tag.name = 'div'
return soup
story = soup.find(name='div', attrs={'class':'cnnBody_Left'})
if story is None:
story = soup.find(name='div', attrs={'id':'cnnContentContainer'})
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
body = soup.find(name='body')
body.insert(0, story)
else:
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
body = soup.find(name='body')
body.insert(0, story)
return soup

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
'''
'''
from calibre.web.feeds.recipes import BasicNewsRecipe
class PeopleMag(BasicNewsRecipe):
title = 'People/US Magazine Mashup'
__author__ = 'BrianG'
language = 'en'
description = 'Headlines from People and US Magazine'
no_stylesheets = True
use_embedded_content = False
oldest_article = 2
max_articles_per_feed = 50
extra_css = '''
h1{font-family:verdana,arial,helvetica,sans-serif; font-size: large;}
h2{font-family:verdana,arial,helvetica,sans-serif; font-size: small;}
.body-content{font-family:verdana,arial,helvetica,sans-serif; font-size: small;}
.byline {font-size: small; color: #666666; font-style:italic; }
.lastline {font-size: small; color: #666666; font-style:italic;}
.contact {font-size: small; color: #666666;}
.contact p {font-size: small; color: #666666;}
.photoCaption { font-family:verdana,arial,helvetica,sans-serif; font-size:x-small;}
.photoCredit{ font-family:verdana,arial,helvetica,sans-serif; font-size:x-small; color:#666666;}
.article_timestamp{font-size:x-small; color:#666666;}
a {font-family:verdana,arial,helvetica,sans-serif; font-size: x-small;}
'''
keep_only_tags = [
dict(name='div', attrs={'class': 'panel_news_article_main'}),
dict(name='div', attrs={'class':'article_content'}),
dict(name='div', attrs={'class': 'headline'}),
dict(name='div', attrs={'class': 'post'}),
dict(name='div', attrs={'class': 'packageheadlines'}),
dict(name='div', attrs={'class': 'snap_preview'}),
dict(name='div', attrs={'id': 'articlebody'})
]
remove_tags = [
dict(name='div', attrs={'class':'share_comments'}),
dict(name='p', attrs={'class':'twitter_facebook'}),
dict(name='div', attrs={'class':'share_comments_bottom'}),
dict(name='h2', attrs={'id':'related_content'}),
dict(name='div', attrs={'class':'next_article'}),
dict(name='div', attrs={'class':'prev_article'}),
dict(name='ul', attrs={'id':'sharebar'}),
dict(name='div', attrs={'class':'sharelinkcont'}),
dict(name='div', attrs={'class':'categories'}),
dict(name='ul', attrs={'class':'categories'}),
dict(name='div', attrs={'id':'promo'}),
dict(name='div', attrs={'class':'linksWrapper'}),
dict(name='p', attrs={'class':'tag tvnews'}),
dict(name='p', attrs={'class':'tag movienews'}),
dict(name='p', attrs={'class':'tag musicnews'}),
dict(name='p', attrs={'class':'tag couples'}),
dict(name='p', attrs={'class':'tag gooddeeds'}),
dict(name='p', attrs={'class':'tag weddings'}),
dict(name='p', attrs={'class':'tag health'})
]
feeds = [
('PEOPLE Headlines', 'http://feeds.people.com/people/headlines'),
('US Headlines', 'http://www.usmagazine.com/celebrity_news/rss')
]
def get_article_url(self, article):
ans = article.link
try:
self.log('Looking for full story link in', ans)
soup = self.index_to_soup(ans)
x = soup.find(text="View All")
if x is not None:
ans = ans + '?viewAll=y'
self.log('Found full story link', ans)
except:
pass
return ans
def postprocess_html(self, soup,first):
for tag in soup.findAll(name='div',attrs={'class':"container_ate_qandatitle"}):
tag.extract()
for tag in soup.findAll(name='br'):
tag.extract()
return soup

View File

@ -464,6 +464,7 @@ plugins += [
IREXDR1000,
JETBOOK,
SHINEBOOK,
POCKETBOOK360,
KINDLE,
KINDLE2,
KINDLE_DX,
@ -479,7 +480,6 @@ plugins += [
ESLICK,
NUUT2,
IRIVER_STORY,
POCKETBOOK360,
GER2,
ITALICA,
ECLICTO,

View File

@ -16,7 +16,6 @@ Windows PNP strings:
'''
from calibre.devices.usbms.driver import USBMS
from calibre.constants import iswindows
class EB600(USBMS):
@ -88,17 +87,15 @@ class SHINEBOOK(EB600):
@classmethod
def can_handle(cls, dev, debug=False):
try:
if not iswindows:
return dev[4] == 'ShineBook'
except:
pass
return True
return dev[4] == 'ShineBook'
class POCKETBOOK360(EB600):
# Device info on OS X
# (8069L, 5768L, 272L, u'', u'', u'1.00')
name = 'PocketBook 360 Device Interface'
gui_name = 'PocketBook 360'
@ -112,6 +109,10 @@ class POCKETBOOK360(EB600):
OSX_MAIN_MEM = 'Philips Mass Storge Media'
OSX_CARD_A_MEM = 'Philips Mass Storge Media'
@classmethod
def can_handle(cls, dev, debug=False):
return dev[-1] == '1.00' and not dev[-2] and not dev[-3]
class GER2(EB600):
name = 'Ganaxa GeR2 Device Interface'

View File

@ -56,8 +56,9 @@ class PRS505(CLI, Device):
EBOOK_DIR_MAIN = 'database/media/books'
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of metadata fields '
'to turn into collections on the device.')
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['series', 'tags', 'authors'])
'to turn into collections on the device. Posiibilities include: ')+\
'series, tags, authors'
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['series', 'tags'])
def windows_filter_pnp_id(self, pnp_id):
return '_LAUNCHER' in pnp_id

View File

@ -529,6 +529,18 @@ class Application(QApplication):
self.load_translations()
qt_app = self
if islinux:
self.setStyleSheet('''
QToolTip {
border: 2px solid black;
padding: 5px;
border-radius: 10px;
opacity: 200;
background-color: #e1e1ff;
}
''')
def load_translations(self):
if self._translator is not None:
self.removeTranslator(self._translator)

View File

@ -145,10 +145,8 @@ class Widget(QWidget):
help = help_provider(name)
if not help: continue
g._help = help
g.setToolTip('\n'.join(w.wrap(help.replace('<', '&lt;').replace('>',
'&gt;'))))
g.setWhatsThis('\n'.join(w.wrap(help.replace('<', '&lt;').replace('>',
'&gt;'))))
g.setToolTip('\n'.join(w.wrap(help)))
g.setWhatsThis('\n'.join(w.wrap(help)))
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))

View File

@ -57,7 +57,13 @@ if pictureflow is not None:
return self.model.count()
def caption(self, index):
return self.model.title(index)
try:
ans = self.model.title(index)
if not ans:
ans = ''
except:
ans = ''
return ans
def reset(self):
self.emit(SIGNAL('dataChanged()'))

View File

@ -177,7 +177,7 @@ class PluginModel(QAbstractItemModel):
ans='%s (%s) %s %s\n%s'%(plugin.name, ver, _('by'), plugin.author, desc)
c = plugin_customization(plugin)
if c:
ans += '\nCustomization: '+c
ans += _('\nCustomization: ')+c
return QVariant(ans)
if role == Qt.DecorationRole:
return self.disabled_icon if is_disabled(plugin) else self.icon
@ -437,6 +437,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.priority_label.setVisible(iswindows)
self._plugin_model = PluginModel()
self.plugin_view.setModel(self._plugin_model)
self.plugin_view.setStyleSheet(
"QTreeView::item { padding-bottom: 10px;}")
self.connect(self.toggle_plugin, SIGNAL('clicked()'), lambda : self.modify_plugin(op='toggle'))
self.connect(self.customize_plugin, SIGNAL('clicked()'), lambda : self.modify_plugin(op='customize'))
self.connect(self.remove_plugin, SIGNAL('clicked()'), lambda : self.modify_plugin(op='remove'))

View File

@ -15,7 +15,7 @@
<string>Preferences</string>
</property>
<property name="windowIcon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
</property>
<layout class="QGridLayout">
@ -148,7 +148,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/mimetypes/dir.svg</normaloff>:/images/mimetypes/dir.svg</iconset>
</property>
</widget>
@ -285,7 +285,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
</property>
</widget>
@ -309,7 +309,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
</property>
</widget>
@ -473,7 +473,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
</property>
</widget>
@ -497,7 +497,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
</property>
</widget>
@ -557,7 +557,7 @@
<string>&amp;Add email</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
</property>
<property name="iconSize">
@ -584,7 +584,7 @@
<string>&amp;Remove email</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/minus.svg</normaloff>:/images/minus.svg</iconset>
</property>
<property name="iconSize">
@ -907,6 +907,9 @@
</item>
<item>
<widget class="QTreeView" name="plugin_view">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>32</width>
@ -916,6 +919,9 @@
<property name="animated">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
@ -973,7 +979,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../../../resources/images.qrc">
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
</property>
</widget>
@ -1044,7 +1050,7 @@
</customwidget>
</customwidgets>
<resources>
<include location="../../../../work/calibre/resources/images.qrc"/>
<include location="../../../../../resources/images.qrc"/>
</resources>
<connections>
<connection>

View File

@ -152,9 +152,9 @@ class Delegate(QStyledItemDelegate):
class Shortcuts(QAbstractListModel):
TEMPLATE = '''
TEMPLATE = u'''
<p><b>{0}</b><br>
Keys: <code>{1}</code></p>
{2}: <code>{1}</code></p>
'''
def __init__(self, shortcuts, config_file_base_name, parent=None):
@ -212,7 +212,7 @@ class Shortcuts(QAbstractListModel):
key = self.order[row]
if role == Qt.DisplayRole:
return QVariant(self.TEMPLATE.format(self.descriptions[key],
_(' or ').join(self.get_shortcuts(key))))
_(' or ').join(self.get_shortcuts(key)), _('Keys')))
if role == Qt.ToolTipRole:
return QVariant(_('Double click to change'))
if role == DEFAULTS:

View File

@ -518,6 +518,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
########################### Cover Flow ################################
self.cover_flow = None
if CoverFlow is not None:
self.cf_last_updated_at = None
self.cover_flow_sync_timer = QTimer(self)
self.cover_flow_sync_timer.timeout.connect(self.cover_flow_do_sync)
self.cover_flow_sync_flag = True
text_height = 40 if config['separate_cover_flow'] else 25
ah = available_height()
cfh = ah-100
@ -528,17 +532,13 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.cover_flow.setVisible(False)
if not config['separate_cover_flow']:
self.library.layout().addWidget(self.cover_flow)
#self.connect(self.cover_flow, SIGNAL('currentChanged(int)'),
# self.sync_cf_to_listview)
#self.connect(self.cover_flow, SIGNAL('itemActivated(int)'),
# self.show_book_info)
self.cover_flow.currentChanged.connect(self.sync_listview_to_cf)
self.connect(self.status_bar.cover_flow_button,
SIGNAL('toggled(bool)'), self.toggle_cover_flow)
self.connect(self.cover_flow, SIGNAL('stop()'),
self.status_bar.cover_flow_button.toggle)
#QObject.connect(self.library_view.selectionModel(),
# SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
# self.sync_cf_to_listview)
self.library_view.selectionModel().currentRowChanged.connect(
self.sync_cf_to_listview)
self.db_images = DatabaseImages(self.library_view.model())
self.cover_flow.setImages(self.db_images)
else:
@ -684,7 +684,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.connect(d, SIGNAL('finished(int)'),
self.uncheck_cover_button)
self.cf_dialog = d
self.cover_flow_sync_timer.start(500)
else:
self.cover_flow_sync_timer.stop()
idx = self.library_view.model().index(self.cover_flow.currentSlide(), 0)
if idx.isValid():
sm = self.library_view.selectionModel()
@ -705,7 +707,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
#self.status_bar.book_info.book_data.setMaximumHeight(100)
#self.status_bar.setMaximumHeight(120)
self.library_view.scrollTo(self.library_view.currentIndex())
self.cover_flow_sync_timer.start(500)
else:
self.cover_flow_sync_timer.stop()
self.cover_flow.setVisible(False)
idx = self.library_view.model().index(self.cover_flow.currentSlide(), 0)
if idx.isValid():
@ -731,14 +735,32 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
if view is self.current_view():
self.search.search_done(ok)
def sync_cf_to_listview(self, index, *args):
if not hasattr(index, 'row') and \
self.library_view.currentIndex().row() != index:
index = self.library_view.model().index(index, 0)
self.library_view.setCurrentIndex(index)
if hasattr(index, 'row') and self.cover_flow.isVisible() and \
self.cover_flow.currentSlide() != index.row():
self.cover_flow.setCurrentSlide(index.row())
def sync_cf_to_listview(self, current, previous):
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
self.cover_flow.currentSlide() != current.row():
self.cover_flow.setCurrentSlide(current.row())
self.cover_flow_sync_flag = True
def cover_flow_do_sync(self):
self.cover_flow_sync_flag = True
try:
if self.cover_flow.isVisible() and self.cf_last_updated_at is not None and \
time.time() - self.cf_last_updated_at > 0.5:
self.cf_last_updated_at = None
row = self.cover_flow.currentSlide()
m = self.library_view.model()
index = m.index(row, 0)
if self.library_view.currentIndex().row() != row and index.isValid():
self.cover_flow_sync_flag = False
sm = self.library_view.selectionModel()
sm.select(index, sm.ClearAndSelect|sm.Rows)
self.library_view.setCurrentIndex(index)
except:
pass
def sync_listview_to_cf(self, row):
self.cf_last_updated_at = time.time()
def another_instance_wants_to_talk(self):
try: