KG updates

This commit is contained in:
GRiker 2010-06-23 02:14:30 -06:00
commit 7d86373781
18 changed files with 160 additions and 134 deletions

View File

@ -71,3 +71,5 @@ gui_pubdate_display_format = 'MMM yyyy'
# order until the title is edited. Double-clicking on a title and hitting return
# without changing anything is sufficient to change the sort.
title_series_sorting = 'library_order'

Binary file not shown.

View File

@ -0,0 +1,71 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1277228948(BasicNewsRecipe):
title = u'China Press USA'
oldest_article = 7
max_articles_per_feed = 100
__author__ = 'rty'
__version__ = '1.0'
language = 'zh_CN'
pubisher = 'www.chinapressusa.com'
description = 'Overseas Chinese Network Newspaper in the USA'
category = 'News in Chinese, USA'
remove_javascript = True
use_embedded_content = False
no_stylesheets = True
#encoding = 'GB2312'
encoding = 'UTF-8'
conversion_options = {'linearize_tables':True}
masthead_url ='http://www.chinapressusa.com/common/images/logo.gif'
extra_css = '''
@font-face { font-family: "DroidFont", serif, sans-serif; src: url(res:///system/fonts/DroidSansFallback.ttf); }\n
body {
margin-right: 8pt;
font-family: 'DroidFont', serif;}
h1 {font-family: 'DroidFont', serif, sans-serif}
.show {font-family: 'DroidFont', serif, sans-serif}
'''
feeds = [
(u'\u65b0\u95fb\u9891\u9053', u'http://news.uschinapress.com/news.xml'),
(u'\u534e\u4eba\u9891\u9053', u'http://chinese.uschinapress.com/chinese.xml'),
(u'\u8bc4\u8bba\u9891\u9053', u'http://review.uschinapress.com/review.xml'),
]
keep_only_tags = [
dict(name='div', attrs={'class':'show'}),
]
remove_tags = [
# dict(name='table', attrs={'class':'xle'}),
dict(name='div', attrs={'class':'time'}),
]
remove_tags_after = [
dict(name='div', attrs={'class':'bank17'}),
# dict(name='a', attrs={'class':'ab12'}),
]
def append_page(self, soup, appendtag, position):
pager = soup.find('div',attrs={'id':'displaypagenum'})
if pager:
nexturl = self.INDEX + pager.a['href']
soup2 = self.index_to_soup(nexturl)
texttag = soup2.find('div', attrs={'class':'show'})
for it in texttag.findAll(style=True):
del it['style']
newpos = len(texttag.contents)
self.append_page(soup2,texttag,newpos)
texttag.extract()
appendtag.insert(position,texttag)
def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Language" content="zh-CN"/>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>'
soup.head.insert(0,mtag)
for item in soup.findAll(style=True):
del item['style']
self.append_page(soup, soup.body, 3)
pager = soup.find('div',attrs={'id':'displaypagenum'})
if pager:
pager.extract()
return soup

View File

@ -17,7 +17,7 @@ class NYTimes(BasicNewsRecipe):
title = 'New York Times Top Stories'
__author__ = 'GRiker'
language = 'en'
requires_version = (0, 7, 3)
requires_version = (0, 7, 5)
description = 'Top Stories from the New York Times'
# List of sections typically included in Top Stories. Use a keyword from the

View File

@ -13,14 +13,14 @@ Story
import re, string, time
from calibre import strftime
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, NavigableString, Tag
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, NavigableString, Tag
class NYTimes(BasicNewsRecipe):
title = 'The New York Times'
__author__ = 'GRiker'
language = 'en'
requires_version = (0, 7, 3)
requires_version = (0, 7, 5)
description = 'Daily news from the New York Times (subscription version)'
allSectionKeywords = ['The Front Page', 'International','National','Obituaries','Editorials',

View File

@ -36,7 +36,7 @@ class Plugin(_Plugin):
self.fnames = dict((name, sz) for name, _, sz in self.fsizes if name)
self.fnums = dict((num, sz) for _, num, sz in self.fsizes if num)
# Input profiles {{{
class InputProfile(Plugin):
author = 'Kovid Goyal'
@ -218,6 +218,8 @@ input_profiles = [InputProfile, SonyReaderInput, SonyReader300Input,
input_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
# }}}
class OutputProfile(Plugin):
author = 'Kovid Goyal'
@ -239,6 +241,10 @@ class OutputProfile(Plugin):
# If True output should be optimized for a touchscreen interface
touchscreen = False
touchscreen_news_css = ''
# A list of extra (beyond CSS 2.1) modules supported by the device
# Format is a cssutils profile dictionary (see iPad for example)
extra_css_modules = []
@classmethod
def tags_to_string(cls, tags):
@ -253,18 +259,21 @@ class iPadOutput(OutputProfile):
screen_size = (768, 1024)
comic_screen_size = (768, 1024)
dpi = 132.0
timefmt = '%A, %d %b %Y'
cssutils_addProfile = { 'name':'webkit',
'props': {
'-webkit-border-bottom-left-radius':'{length}',
'-webkit-border-bottom-right-radius':'{length}',
'-webkit-border-top-left-radius':'{length}',
'-webkit-border-top-right-radius':'{length}',
'-webkit-border-radius': r'{border-width}(\s+{border-width}){0,3}|inherit',
},
'macros': {'border-width': '{length}|medium|thick|thin'}}
extra_css_modules = [
{
'name':'webkit',
'props': { '-webkit-border-bottom-left-radius':'{length}',
'-webkit-border-bottom-right-radius':'{length}',
'-webkit-border-top-left-radius':'{length}',
'-webkit-border-top-right-radius':'{length}',
'-webkit-border-radius': r'{border-width}(\s+{border-width}){0,3}|inherit',
},
'macros': {'border-width': '{length}|medium|thick|thin'}
}
]
touchscreen = True
touchscreen_css = u'''
# touchscreen_news_css {{{
touchscreen_news_css = u'''
/* hr used in articles */
.caption_divider {
border:#ccc 1px solid;
@ -328,6 +337,7 @@ class iPadOutput(OutputProfile):
}
'''
# }}}
class SonyReaderOutput(OutputProfile):

View File

@ -45,8 +45,8 @@ class ANDROID(USBMS):
'GT-I5700', 'SAMSUNG']
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
'PROD_GT-I9000']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'PROD_GT-I9000_CARD']
'PR OD_GT-I9000']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'PR OD_GT-I9000_CARD']
OSX_MAIN_MEM = 'HTC Android Phone Media'

View File

@ -16,7 +16,7 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.metadata.epub import set_metadata
from calibre.library.server.utils import strftime
from calibre.utils.config import Config, ConfigProxy, config_dir
from calibre.utils.config import config_dir
from calibre.utils.date import isoformat, now, parse_date
from calibre.utils.logging import Log
from calibre.utils.zipfile import ZipFile
@ -34,8 +34,15 @@ if isosx:
if iswindows:
import pythoncom, win32com.client
class DriverBase(DeviceConfig, DevicePlugin):
# Needed for config_widget to work
FORMATS = ['epub', 'pdf']
class ITUNES(DeviceConfig, DevicePlugin):
@classmethod
def _config_base_name(cls):
return 'iTunes'
class ITUNES(DriverBase):
'''
Calling sequences:
Initialization:
@ -84,16 +91,6 @@ class ITUNES(DeviceConfig, DevicePlugin):
OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...')
FORMATS = ['epub','pdf']
# Configuration
HELP_MESSAGE = _('Configure Device')
EXTRA_CUSTOMIZATION_MESSAGE = None
EXTRA_CUSTOMIZATION_DEFAULT = None
MUST_READ_METADATA = False
SAVE_TEMPLATE = '{title}'
SUPPORTS_SUB_DIRS = False
SUPPORTS_USE_AUTHOR_SORT = False
# Product IDs:
# 0x1292:iPhone 3G
@ -173,7 +170,6 @@ class ITUNES(DeviceConfig, DevicePlugin):
sources = None
update_msg = None
update_needed = False
use_series_as_category = False
# Public methods
def add_books_to_metadata(self, locations, metadata, booklists):
@ -522,29 +518,19 @@ class ITUNES(DeviceConfig, DevicePlugin):
'''
return (None,None)
def config_widget(self):
@classmethod
def config_widget(cls):
'''
Return a QWidget with settings for the device interface
'''
if DEBUG:
self.log.info("ITUNES.config_widget()")
from calibre.gui2.device_drivers.configwidget import ConfigWidget
cw = ConfigWidget(self.settings(), self.FORMATS, self.SUPPORTS_SUB_DIRS,
self.MUST_READ_METADATA, self.SUPPORTS_USE_AUTHOR_SORT,
self.EXTRA_CUSTOMIZATION_MESSAGE)
cw = DriverBase.config_widget()
# Turn off the Save template
cw.opt_save_template.setVisible(False)
cw.label.setVisible(False)
# Repurpose the checkbox
cw.opt_read_metadata.setText("Use Series as Genre in iTunes/iBooks")
cw.opt_read_metadata.setText(_("Use Series as Genre in iTunes/iBooks"))
return cw
def customization_help(self,gui=False):
if DEBUG:
self.log.info("ITUNES.customization_help()")
return _('Configure Device')
def delete_books(self, paths, end_session=True):
'''
Delete books at paths on device.
@ -774,46 +760,6 @@ class ITUNES(DeviceConfig, DevicePlugin):
'''
self.report_progress = report_progress
def save_settings(self, settings_widget):
'''
Should save settings to disk. Takes the widget created in config_widget
and saves all settings to disk.
'''
if DEBUG:
self.log.info("ITUNES.save_settings()")
proxy = self._configProxy()
proxy['format_map'] = settings_widget.format_map()
if self.SUPPORTS_SUB_DIRS:
proxy['use_subdirs'] = settings_widget.use_subdirs()
if not self.MUST_READ_METADATA:
proxy['read_metadata'] = settings_widget.read_metadata()
if self.SUPPORTS_USE_AUTHOR_SORT:
proxy['use_author_sort'] = settings_widget.use_author_sort()
if self.EXTRA_CUSTOMIZATION_MESSAGE:
ec = unicode(settings_widget.opt_extra_customization.text()).strip()
if not ec:
ec = None
proxy['extra_customization'] = ec
st = unicode(settings_widget.opt_save_template.text())
proxy['save_template'] = st
# Snag the read_metadata check box contents on the way by
self.use_series_as_category = settings_widget.read_metadata()
def settings(self):
'''
Should return an opts object. The opts object should have one attribute
`format_map` which is an ordered list of formats for the device.
'''
if DEBUG:
self.log.info("ITUNES.settings()")
opts = self._config().parse()
# Repurpose the read_metadata check box
self.use_series_as_category = opts.read_metadata
return opts
def sync_booklists(self, booklists, end_session=True):
'''
Update metadata on device.
@ -1129,27 +1075,6 @@ class ITUNES(DeviceConfig, DevicePlugin):
return db_added, lb_added
def _config(self):
klass = self if isinstance(self, type) else self.__class__
c = Config('device_drivers_%s' % klass.__name__, _('settings for device drivers'))
c.add_opt('format_map', default=self.FORMATS,
help=_('Ordered list of formats the device will accept'))
c.add_opt('use_subdirs', default=True,
help=_('Place files in sub directories if the device supports them'))
c.add_opt('read_metadata', default=True,
help=_('Use Series as Genre in iTunes/iBooks'))
c.add_opt('use_author_sort', default=False,
help=_('Use author sort instead of author'))
c.add_opt('save_template', default=self._default_save_template(),
help=_('Template to control how books are titled in iTunes/iBooks'))
c.add_opt('extra_customization',
default=self.EXTRA_CUSTOMIZATION_DEFAULT,
help=_('Extra customization'))
return c
def _configProxy(self):
return ConfigProxy(self._config())
def _cover_to_thumb(self, path, metadata, db_added, lb_added, format):
'''
assumes pythoncom wrapper for db_added
@ -1300,11 +1225,6 @@ class ITUNES(DeviceConfig, DevicePlugin):
return this_book
def _default_save_template(self):
from calibre.library.save_to_disk import config
return self.SAVE_TEMPLATE if self.SAVE_TEMPLATE else \
config().parse().send_template
def _delete_iTunesMetadata_plist(self,fpath):
'''
Delete the plist file from the file to force recache
@ -1776,7 +1696,7 @@ class ITUNES(DeviceConfig, DevicePlugin):
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
thumb = cStringIO.StringIO()
im.convert('RGB').save(thumb,'JPEG')
thumb_data = thmb.getvalue()
thumb_data = thumb.getvalue()
os.remove(tmp_thumb)
thumb.close()
@ -2510,7 +2430,7 @@ class ITUNES(DeviceConfig, DevicePlugin):
# Set genre from series if available, else first alpha tag
# Otherwise iTunes grabs the first dc:subject from the opf metadata
if self.use_series_as_category and metadata.series:
if metadata.series and self.settings().read_metadata:
if DEBUG:
self.log.info(" using Series name as Genre")
if lb_added:
@ -2581,7 +2501,7 @@ class ITUNES(DeviceConfig, DevicePlugin):
# Otherwise iBooks uses first <dc:subject> from opf
# iTunes balks on setting EpisodeNumber, but it sticks (9.1.1.12)
if self.use_series_as_category and metadata.series:
if metadata.series and self.settings().read_metadata:
if DEBUG:
self.log.info(" using Series name as Genre")
if lb_added:

View File

@ -59,7 +59,7 @@ class DevicePlugin(Plugin):
return cls.__name__
return cls.name
# Device detection {{{
def test_bcd_windows(self, device_id, bcd):
if bcd is None or len(bcd) == 0:
return True
@ -152,6 +152,7 @@ class DevicePlugin(Plugin):
return True, dev
return False, None
# }}}
def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
@ -372,14 +373,12 @@ class DevicePlugin(Plugin):
@classmethod
def settings(cls):
'''
Should return an opts object. The opts object should have one attribute
Should return an opts object. The opts object should have at least one attribute
`format_map` which is an ordered list of formats for the device.
'''
raise NotImplementedError()
class BookList(list):
'''
A list of books. Each Book object must have the fields:

View File

@ -78,9 +78,6 @@ class Device(DeviceConfig, DevicePlugin):
STORAGE_CARD_VOLUME_LABEL = ''
STORAGE_CARD2_VOLUME_LABEL = None
SUPPORTS_SUB_DIRS = False
MUST_READ_METADATA = False
SUPPORTS_USE_AUTHOR_SORT = False
EBOOK_DIR_MAIN = ''
EBOOK_DIR_CARD_A = ''

View File

@ -13,6 +13,10 @@ class DeviceConfig(object):
EXTRA_CUSTOMIZATION_MESSAGE = None
EXTRA_CUSTOMIZATION_DEFAULT = None
SUPPORTS_SUB_DIRS = False
MUST_READ_METADATA = False
SUPPORTS_USE_AUTHOR_SORT = False
#: If None the default is used
SAVE_TEMPLATE = None
@ -23,9 +27,14 @@ class DeviceConfig(object):
config().parse().send_template
@classmethod
def _config(cls):
def _config_base_name(cls):
klass = cls if isinstance(cls, type) else cls.__class__
c = Config('device_drivers_%s' % klass.__name__, _('settings for device drivers'))
return klass.__name__
@classmethod
def _config(cls):
name = cls._config_base_name()
c = Config('device_drivers_%s' % name, _('settings for device drivers'))
c.add_opt('format_map', default=cls.FORMATS,
help=_('Ordered list of formats the device will accept'))
c.add_opt('use_subdirs', default=True,

View File

@ -127,9 +127,8 @@ class Stylizer(object):
else:
head = []
# Add optional cssutils parsing profile from output_profile
if hasattr(self.opts.output_profile, 'cssutils_addProfile'):
profile = self.opts.output_profile.cssutils_addProfile
# Add cssutils parsing profiles from output_profile
for profile in self.opts.output_profile.extra_css_modules:
cssutils.profile.addProfile(profile['name'],
profile['props'],
profile['macros'])

View File

@ -43,6 +43,9 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">

View File

@ -10,7 +10,7 @@ from functools import partial
from binascii import unhexlify
from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, QPixmap, \
Qt, pyqtSignal, QColor, QPainter
Qt, pyqtSignal, QColor, QPainter, QDialog
from PyQt4.QtSvg import QSvgRenderer
from calibre.customize.ui import available_input_formats, available_output_formats, \
@ -814,7 +814,8 @@ class DeviceMixin(object): # {{{
if specific:
d = ChooseFormatDialog(self, _('Choose format to send to device'),
self.device_manager.device.settings().format_map)
d.exec_()
if d.exec_() != QDialog.Accepted:
return
if d.format():
fmt = d.format().lower()
dest, sub_dest = dest.split(':')

View File

@ -39,7 +39,7 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::Ok|QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>

View File

@ -56,7 +56,8 @@ class SearchBox2(QComboBox):
To use this class:
* Call initialize()
* Connect to the search() and cleared() signals from this widget
* Connect to the search() and cleared() signals from this widget.
* Connect to the cleared() signal to know when the box content changes
* Call search_done() after every search is complete
* Use clear() to clear back to the help message
'''
@ -75,6 +76,7 @@ class SearchBox2(QComboBox):
type=Qt.DirectConnection)
self.line_edit.mouse_released.connect(self.mouse_released,
type=Qt.DirectConnection)
self.activated.connect(self.history_selected)
self.setEditable(True)
self.help_state = False
self.as_you_type = True
@ -139,6 +141,9 @@ class SearchBox2(QComboBox):
def key_pressed(self, event):
self.normalize_state()
if self._in_a_search:
self.emit(SIGNAL('changed()'))
self._in_a_search = False
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
self.do_search()
self.timer = self.startTimer(self.__class__.INTERVAL)
@ -154,6 +159,10 @@ class SearchBox2(QComboBox):
self.timer = None
self.do_search()
def history_selected(self, text):
self.emit(SIGNAL('changed()'))
self.do_search()
@property
def smart_text(self):
text = unicode(self.currentText()).strip()
@ -345,6 +354,7 @@ class SearchBoxMixin(object):
self.search.initialize('main_search_history', colorize=True,
help_text=_('Search (For Advanced Search click the button to the left)'))
self.connect(self.search, SIGNAL('cleared()'), self.search_box_cleared)
self.connect(self.search, SIGNAL('changed()'), self.search_box_changed)
self.connect(self.clear_button, SIGNAL('clicked()'), self.search.clear)
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'),
self.do_advanced_search)
@ -364,6 +374,9 @@ class SearchBoxMixin(object):
self.saved_search.clear_to_help()
self.set_number_of_books_shown()
def search_box_changed(self):
self.tags_view.clear()
def do_advanced_search(self, *args):
d = SearchDialog(self)
if d.exec_() == QDialog.Accepted:

View File

@ -957,16 +957,19 @@ class LayoutButton(QToolButton):
self.splitter = splitter
splitter.state_changed.connect(self.update_state)
self.setCursor(Qt.PointingHandCursor)
def set_state_to_show(self, *args):
self.setChecked(False)
label =_('Show')
self.setText(label + ' ' + self.label)
self.setToolTip(self.text())
def set_state_to_hide(self, *args):
self.setChecked(True)
label = _('Hide')
self.setText(label + ' ' + self.label)
self.setToolTip(self.text())
def update_state(self, *args):
if self.splitter.is_side_index_hidden:

View File

@ -585,8 +585,8 @@ class BasicNewsRecipe(Recipe):
self.lrf = options.lrf
self.output_profile = options.output_profile
self.touchscreen = getattr(self.output_profile, 'touchscreen', False)
if self.touchscreen and getattr(self.output_profile, 'touchscreen_css',False):
self.extra_css += self.output_profile.touchscreen_css
if self.touchscreen:
self.template_css += self.output_profile.touchscreen_news_css
self.output_dir = os.path.abspath(self.output_dir)
if options.test:
@ -664,7 +664,8 @@ class BasicNewsRecipe(Recipe):
templ = self.navbar.generate(False, f, a, feed_len,
not self.has_single_feed,
url, __appname__,
center=self.center_navbar)
center=self.center_navbar,
extra_css=self.extra_css)
elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div')
body.insert(0, elem)
if self.remove_javascript:
@ -728,8 +729,6 @@ class BasicNewsRecipe(Recipe):
timefmt = self.timefmt
if self.touchscreen:
templ = templates.TouchscreenIndexTemplate()
if getattr(self.output_profile,'timefmt',False):
timefmt = self.output_profile.timefmt
return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds,
extra_css=css).render(doctype='xhtml')