Merge from trunk

This commit is contained in:
Charles Haley 2010-09-03 18:41:04 +01:00
commit d70ff5471d
41 changed files with 30057 additions and 22723 deletions

View File

@ -111,5 +111,6 @@ grouped_search_terms = {}
add_new_book_tags_when_importing_books = False
# Set the maximum number of tags to show in the content server
max_content_server_tags_shown=5
# Set the maximum number of tags to show per book in the content server
max_content_server_tags_shown=5

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2010, Francisco Javier Nieto <frjanibo at gmail.com>'
'''
www.hoy.es
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class Hoy(BasicNewsRecipe):
title = 'HOY'
__author__ = 'Fco Javier Nieto'
description = u'Noticias desde Extremadura'
publisher = 'HOY'
category = 'news, politics, Spain, Extremadura'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
delay = 1
encoding = 'cp1252'
language = 'es'
feeds = [
(u'Portada' , u'http://www.hoy.es/portada.xml' ),
(u'Regional' , u'http://www.hoy.es/rss/feeds/regional.xml' ),
(u'Prov de Badajoz' , u'http://www.hoy.es/rss/feeds/prov_badajoz.xml' ),
(u'Prov de Caceres' , u'http://www.hoy.es/rss/feeds/prov_caceres.xml' ),
(u'Badajoz' , u'http://www.hoy.es/rss/feeds/badajoz.xml' ),
(u'Caceres' , u'http://www.hoy.es/rss/feeds/caceres.xml' ),
(u'Merida' , u'http://www.hoy.es/rss/feeds/merida.xml' ),
(u'Opinion' , u'http://www.hoy.es/rss/feeds/opinion.xml' ),
(u'Nacional' , u'http://www.hoy.es/rss/feeds/nacional.xml' ),
(u'Internacional' , u'http://www.hoy.es/rss/feeds/internacional.xml' ),
(u'Economia' , u'http://www.hoy.es/rss/feeds/economia.xml' ),
(u'Deportes' , u'http://www.hoy.es/rss/feeds/deportes.xml' ),
(u'Sociedad' , u'http://www.hoy.es/rss/feeds/sociedad.xml' ),
(u'Cultura' , u'http://www.hoy.es/rss/feeds/cultura.xml' ),
(u'Television' , u'http://www.hoy.es/rss/feeds/television.xml' ),
(u'contraportada' , u'http://www.hoy.es/rss/feeds/contraportada.xml' )
]
keep_only_tags = [
dict(name='h1', attrs={'class':['headline']}),
dict(name='h2', attrs={'class':['subhead']}),
dict(name='div', attrs={'class':['text']})
]
remove_tags = [
dict(name=['object','link','script'])
,dict(name='div', attrs={'class':['colC_articulo','peu']})
]
remove_tags_after = [dict(name='div', attrs={'class':'text'})]
extra_css = '.headline {font: sans-serif 2em;}\n.subhead,h2{font: sans-serif 1.5em\n'
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

View File

@ -685,7 +685,16 @@ class LookAndFeel(PreferencesPlugin):
name_order = 1
config_widget = 'calibre.gui2.preferences.look_feel'
plugins += [LookAndFeel]
class Behavior(PreferencesPlugin):
name = 'Behavior'
gui_name = _('Behavior')
category = 'Interface'
gui_category = _('Interface')
category_order = 1
name_order = 2
config_widget = 'calibre.gui2.preferences.behavior'
plugins += [LookAndFeel, Behavior]
#}}}

View File

@ -17,6 +17,9 @@ class ConfigWidgetInterface(object):
def genesis(self, gui):
raise NotImplementedError()
def initialize(self):
raise NotImplementedError()
def restore_defaults(self):
pass
@ -117,6 +120,21 @@ class Setting(object):
val = unicode(self.gui_obj.itemData(idx).toString())
return val
class CommaSeparatedList(Setting):
def set_gui_val(self, val):
x = ''
if val:
x = u', '.join(val)
self.gui_obj.setText(x)
def get_gui_val(self):
val = unicode(self.gui_obj.text()).strip()
ans = []
if val:
ans = [x.strip() for x in val.split(',')]
ans = [x for x in ans if x]
return ans
class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
@ -169,6 +187,7 @@ def test_widget(category, name, gui=None): # {{{
pl = get_plugin(category, name)
d = QDialog()
d.resize(750, 550)
d.setWindowTitle(category + " - " + name)
bb = QDialogButtonBox(d)
bb.setStandardButtons(bb.Apply|bb.Cancel|bb.RestoreDefaults)
bb.accepted.connect(d.accept)

View File

@ -0,0 +1,163 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import re
from PyQt4.Qt import Qt, QVariant, QListWidgetItem
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
CommaSeparatedList
from calibre.gui2.preferences.behavior_ui import Ui_Form
from calibre.gui2 import config, info_dialog, dynamic
from calibre.utils.config import prefs
from calibre.customize.ui import available_output_formats, all_input_formats
from calibre.utils.search_query_parser import saved_searches
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.oeb.iterator import is_supported
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
db = gui.library_view.model().db
r = self.register
r('worker_process_priority', prefs, choices=
[(_('Low'), 'low'), (_('Normal'), 'normal'), (_('High'), 'high')])
r('network_timeout', prefs)
r('overwrite_author_title_metadata', config)
r('get_social_metadata', config)
r('new_version_notification', config)
r('upload_news_to_device', config)
r('delete_news_from_library_on_upload', config)
output_formats = list(sorted(available_output_formats()))
output_formats.remove('oeb')
choices = [(x.upper(), x) for x in output_formats]
r('output_format', prefs, choices=choices)
restrictions = sorted(saved_searches().names(),
cmp=lambda x,y: cmp(x.lower(), y.lower()))
choices = [('', '')] + [(x, x) for x in restrictions]
r('gui_restriction', db.prefs, choices=choices)
r('new_book_tags', prefs, setting=CommaSeparatedList)
self.reset_confirmation_button.clicked.connect(self.reset_confirmation_dialogs)
self.input_up_button.clicked.connect(self.up_input)
self.input_down_button.clicked.connect(self.down_input)
for signal in ('Activated', 'Changed', 'DoubleClicked', 'Clicked'):
signal = getattr(self.opt_internally_viewed_formats, 'item'+signal)
signal.connect(self.internally_viewed_formats_changed)
def initialize(self):
ConfigWidgetBase.initialize(self)
self.init_input_order()
self.init_internally_viewed_formats()
def restore_defaults(self):
ConfigWidgetBase.restore_defaults(self)
self.init_input_order(defaults=True)
self.init_internally_viewed_formats(defaults=True)
def commit(self):
input_map = prefs['input_format_order']
input_cols = [unicode(self.input_order.item(i).data(Qt.UserRole).toString()) for i in range(self.input_order.count())]
if input_map != input_cols:
prefs['input_format_order'] = input_cols
fmts = self.current_internally_viewed_formats
old = config['internally_viewed_formats']
if fmts != old:
config['internally_viewed_formats'] = fmts
return ConfigWidgetBase.commit(self)
# Internally viewed formats {{{
def internally_viewed_formats_changed(self, *args):
fmts = self.current_internally_viewed_formats
old = config['internally_viewed_formats']
if fmts != old:
self.changed_signal.emit()
def init_internally_viewed_formats(self, defaults=False):
if defaults:
fmts = config.defaults['internally_viewed_formats']
else:
fmts = config['internally_viewed_formats']
viewer = self.opt_internally_viewed_formats
viewer.blockSignals(True)
exts = set([])
for ext in BOOK_EXTENSIONS:
ext = ext.lower()
ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext)
if ext == 'lrf' or is_supported('book.'+ext):
exts.add(ext)
viewer.clear()
for ext in sorted(exts):
viewer.addItem(ext.upper())
item = viewer.item(viewer.count()-1)
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Checked if
ext.upper() in fmts else Qt.Unchecked)
viewer.blockSignals(False)
@property
def current_internally_viewed_formats(self):
fmts = []
viewer = self.opt_internally_viewed_formats
for i in range(viewer.count()):
if viewer.item(i).checkState() == Qt.Checked:
fmts.append(unicode(viewer.item(i).text()))
return fmts
# }}}
# Input format order {{{
def init_input_order(self, defaults=False):
if defaults:
input_map = prefs.defaults['input_format_order']
else:
input_map = prefs['input_format_order']
all_formats = set()
self.opt_input_order.clear()
for fmt in all_input_formats().union(set(['ZIP', 'RAR'])):
all_formats.add(fmt.upper())
for format in input_map + list(all_formats.difference(input_map)):
item = QListWidgetItem(format, self.opt_input_order)
item.setData(Qt.UserRole, QVariant(format))
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
def up_input(self, *args):
idx = self.opt_input_order.currentRow()
if idx > 0:
self.opt_input_order.insertItem(idx-1, self.opt_input_order.takeItem(idx))
self.opt_input_order.setCurrentRow(idx-1)
self.changed_signal.emit()
def down_input(self, *args):
idx = self.opt_input_order.currentRow()
if idx < self.opt_input_order.count()-1:
self.opt_input_order.insertItem(idx+1, self.opt_input_order.takeItem(idx))
self.opt_input_order.setCurrentRow(idx+1)
self.changed_signal.emit()
# }}}
def reset_confirmation_dialogs(self, *args):
for key in dynamic.keys():
if key.endswith('_again') and dynamic[key] is False:
dynamic[key] = True
info_dialog(self, _('Done'),
_('Confirmation dialogs have all been reset'), show=True)
if __name__ == '__main__':
from PyQt4.Qt import QApplication
app = QApplication([])
test_widget('Interface', 'Behavior')

View File

@ -29,21 +29,21 @@
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="new_version_notification">
<widget class="QCheckBox" name="opt_new_version_notification">
<property name="text">
<string>Show notification when &amp;new version is available</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="sync_news">
<widget class="QCheckBox" name="opt_upload_news_to_device">
<property name="text">
<string>Automatically send downloaded &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="delete_news">
<widget class="QCheckBox" name="opt_delete_news_from_library_on_upload">
<property name="text">
<string>&amp;Delete news from library when it is automatically sent to reader</string>
</property>
@ -57,12 +57,12 @@
<string>Default network &amp;timeout:</string>
</property>
<property name="buddy">
<cstring>timeout</cstring>
<cstring>opt_network_timeout</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="timeout">
<widget class="QSpinBox" name="opt_network_timeout">
<property name="toolTip">
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
</property>
@ -81,7 +81,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="priority">
<widget class="QComboBox" name="opt_worker_process_priority">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
@ -111,7 +111,7 @@
<string>Job &amp;priority:</string>
</property>
<property name="buddy">
<cstring>priority</cstring>
<cstring>opt_worker_process_priority</cstring>
</property>
</widget>
</item>
@ -121,12 +121,12 @@
<string>Preferred &amp;output format:</string>
</property>
<property name="buddy">
<cstring>output_format</cstring>
<cstring>opt_output_format</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="output_format">
<widget class="QComboBox" name="opt_output_format">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
@ -196,7 +196,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QListWidget" name="input_order">
<widget class="QListWidget" name="opt_input_order">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
@ -208,7 +208,7 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QToolButton" name="input_up">
<widget class="QToolButton" name="input_up_button">
<property name="text">
<string>...</string>
</property>
@ -232,7 +232,7 @@
</spacer>
</item>
<item>
<widget class="QToolButton" name="input_down">
<widget class="QToolButton" name="input_down_button">
<property name="text">
<string>...</string>
</property>
@ -256,7 +256,7 @@
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QListWidget" name="viewer">
<widget class="QListWidget" name="opt_internally_viewed_formats">
<property name="alternatingRowColors">
<bool>true</bool>
</property>

View File

@ -233,7 +233,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
######################### Search Restriction ##########################
SearchRestrictionMixin.__init__(self)
self.apply_named_search_restriction(db.prefs.get('gui_restriction', ''))
self.apply_named_search_restriction(db.prefs['gui_restriction'])
########################### Cover Flow ################################
@ -378,7 +378,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.set_window_title()
self.apply_named_search_restriction('') # reset restriction to null
self.saved_searches_changed() # reload the search restrictions combo box
self.apply_named_search_restriction(db.prefs.get('gui_restriction', ''))
self.apply_named_search_restriction(db.prefs['gui_restriction'])
def set_window_title(self):
self.setWindowTitle(__appname__ + u' - ||%s||'%self.iactions['Choose Library'].library_name())

View File

@ -145,6 +145,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def initialize_dynamic(self):
self.prefs = DBPrefs(self)
defs = self.prefs.defaults
defs['gui_restriction'] = defs['cs_restriction'] = ''
# Migrate saved search and user categories to db preference scheme
def migrate_preference(key, default):

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
import os
from calibre.utils.config import Config, StringConfig, config_dir, tweaks
from calibre.utils.config import Config, StringConfig, config_dir
listen_on = '0.0.0.0'
@ -49,15 +49,3 @@ def server_config(defaults=None):
def main():
from calibre.library.server.main import main
return main()
def format_tag_string(tags, sep):
MAX = tweaks['max_content_server_tags_shown']
if tags:
tlist = [t.strip() for t in tags.split(sep)]
else:
tlist = []
tlist.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
if len(tlist) > MAX:
tlist = tlist[:MAX]+['...']
return u'%s'%(', '.join(tlist)) if tlist else ''

View File

@ -13,12 +13,11 @@ from lxml import html
from lxml.html.builder import HTML, HEAD, TITLE, LINK, DIV, IMG, BODY, \
OPTION, SELECT, INPUT, FORM, SPAN, TABLE, TR, TD, A, HR
from calibre.library.server.utils import strftime
from calibre.library.server.utils import strftime, format_tag_string
from calibre.ebooks.metadata import fmt_sidx
from calibre.constants import __appname__
from calibre import human_readable
from calibre.utils.date import utcfromtimestamp, format_date
from . import format_tag_string
def CLASS(*args, **kwargs): # class is a reserved word in Python
kwargs['class'] = ' '.join(args)

View File

@ -11,6 +11,7 @@ import cherrypy
from calibre import strftime as _strftime, prints
from calibre.utils.date import now as nowf
from calibre.utils.config import tweaks
def expose(func):
@ -43,4 +44,14 @@ def strftime(fmt='%Y/%m/%d %H:%M:%S', dt=None):
except:
return _strftime(fmt, nowf().timetuple())
def format_tag_string(tags, sep):
MAX = tweaks['max_content_server_tags_shown']
if tags:
tlist = [t.strip() for t in tags.split(sep)]
else:
tlist = []
tlist.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
if len(tlist) > MAX:
tlist = tlist[:MAX]+['...']
return u'%s'%(', '.join(tlist)) if tlist else ''

View File

@ -11,12 +11,11 @@ import cherrypy
from lxml.builder import ElementMaker
from lxml import etree
from calibre.library.server.utils import strftime
from calibre.library.server.utils import strftime, format_tag_string
from calibre.ebooks.metadata import fmt_sidx
from calibre.constants import preferred_encoding
from calibre import isbytestring
from calibre.utils.date import format_date
from . import format_tag_string
E = ElementMaker()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff