mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
99a99bd71f
@ -11,7 +11,7 @@ class CNetJapan(BasicNewsRecipe):
|
|||||||
(u'CNet Blog', u'http://feed.japan.cnet.com/rss/blog/index.rdf')
|
(u'CNet Blog', u'http://feed.japan.cnet.com/rss/blog/index.rdf')
|
||||||
]
|
]
|
||||||
language = 'ja'
|
language = 'ja'
|
||||||
encoding = 'Shift_JIS'
|
encoding = 'utf-8'
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
|
||||||
preprocess_regexps = [
|
preprocess_regexps = [
|
||||||
|
80
resources/recipes/tyzden.recipe
Normal file
80
resources/recipes/tyzden.recipe
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Miroslav Vasko zemiak@gmail.com'
|
||||||
|
|
||||||
|
'''
|
||||||
|
.tyzden, a weekly news magazine (a week old issue)
|
||||||
|
'''
|
||||||
|
from calibre import strftime
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from datetime import date
|
||||||
|
import re
|
||||||
|
|
||||||
|
class TyzdenRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'zemiak'
|
||||||
|
language = 'sk'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
publisher = u'www.tyzden.sk'
|
||||||
|
category = u'Magazine'
|
||||||
|
description = u'A conservative weekly magazine. The latest free issue'
|
||||||
|
|
||||||
|
today = date.today()
|
||||||
|
iso = today.isocalendar()
|
||||||
|
year = iso[0]
|
||||||
|
weeknum = iso[1]
|
||||||
|
|
||||||
|
if (weeknum > 1):
|
||||||
|
weeknum -= 1
|
||||||
|
|
||||||
|
title = u'.tyzden ' + str(weeknum) + '/' + str(year)
|
||||||
|
|
||||||
|
base_url_path = 'http://www.tyzden.sk/casopis/' + str(year) + '/' + str(weeknum)
|
||||||
|
base_url = base_url_path + '.html'
|
||||||
|
|
||||||
|
oldest_article = 20
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
use_embedded_content = False
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'h1'))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_area top_nofoto'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_block'}))
|
||||||
|
|
||||||
|
remove_tags_after = [dict(name = 'div', attrs = {'class': 'text_block'})]
|
||||||
|
|
||||||
|
def find_sections(self):
|
||||||
|
soup = self.index_to_soup(self.base_url)
|
||||||
|
# find cover pic
|
||||||
|
imgdiv = soup.find('div', attrs = {'class': 'foto'})
|
||||||
|
if imgdiv is not None:
|
||||||
|
img = imgdiv.find('img')
|
||||||
|
if img is not None:
|
||||||
|
self.cover_url = 'http://www.tyzden.sk/' + img['src']
|
||||||
|
# end find cover pic
|
||||||
|
|
||||||
|
for s in soup.findAll('a', attrs={'href': re.compile(r'rubrika/.*')}):
|
||||||
|
yield (self.tag_to_string(s), s)
|
||||||
|
|
||||||
|
def find_articles(self, soup):
|
||||||
|
for art in soup.findAllNext('a'):
|
||||||
|
if (not art['href'].startswith('casopis/')):
|
||||||
|
break;
|
||||||
|
|
||||||
|
url = art['href']
|
||||||
|
title = self.tag_to_string(art)
|
||||||
|
yield {
|
||||||
|
'title': title, 'url':self.base_url_path + '/' + url, 'description':title,
|
||||||
|
'date' : strftime('%a, %d %b'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
feeds = []
|
||||||
|
for title, soup in self.find_sections():
|
||||||
|
feeds.append((title, list(self.find_articles(soup))))
|
||||||
|
|
||||||
|
return feeds
|
@ -53,6 +53,9 @@ class ANDROID(USBMS):
|
|||||||
# LG
|
# LG
|
||||||
0x1004 : { 0x61cc : [0x100] },
|
0x1004 : { 0x61cc : [0x100] },
|
||||||
|
|
||||||
|
# Archos
|
||||||
|
0x0e79 : { 0x1420 : [0x0216]},
|
||||||
|
|
||||||
}
|
}
|
||||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||||
@ -61,18 +64,19 @@ class ANDROID(USBMS):
|
|||||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
|
||||||
|
|
||||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE']
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||||
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
||||||
'SGH-T849', '_MB300']
|
'SGH-T849', '_MB300', 'A70S']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD']
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
|
'A70S']
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'Android Phone Internal Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'Android Device Main Memory'
|
||||||
|
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
@ -191,15 +191,15 @@ class PreProcessor(object):
|
|||||||
blanklines = "\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*"
|
blanklines = "\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*"
|
||||||
line_opening = "<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*"
|
line_opening = "<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*"
|
||||||
txt_line_wrap = u"((\u0020|\u0009)*\n){1,4}"
|
txt_line_wrap = u"((\u0020|\u0009)*\n){1,4}"
|
||||||
|
|
||||||
unwrap_regex = lookahead+line_ending+blanklines+line_opening
|
unwrap_regex = lookahead+line_ending+blanklines+line_opening
|
||||||
if format == 'txt':
|
if format == 'txt':
|
||||||
unwrap_regex = lookahead+txt_line_wrap
|
unwrap_regex = lookahead+txt_line_wrap
|
||||||
|
|
||||||
unwrap = re.compile(u"%s" % unwrap_regex, re.UNICODE)
|
unwrap = re.compile(u"%s" % unwrap_regex, re.UNICODE)
|
||||||
content = unwrap.sub(' ', content)
|
content = unwrap.sub(' ', content)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def __call__(self, html):
|
def __call__(self, html):
|
||||||
self.log("********* Preprocessing HTML *********")
|
self.log("********* Preprocessing HTML *********")
|
||||||
|
@ -296,7 +296,7 @@ class RTFInput(InputFormatPlugin):
|
|||||||
u'<p>\u00a0</p>\n'.encode('utf-8'), res)
|
u'<p>\u00a0</p>\n'.encode('utf-8'), res)
|
||||||
if self.opts.preprocess_html:
|
if self.opts.preprocess_html:
|
||||||
preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None))
|
preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None))
|
||||||
res = preprocessor(res)
|
res = preprocessor(res.decode('utf-8')).encode('utf-8')
|
||||||
f.write(res)
|
f.write(res)
|
||||||
self.write_inline_css(inline_class, border_styles)
|
self.write_inline_css(inline_class, border_styles)
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
|
@ -53,7 +53,7 @@ class TXTInput(InputFormatPlugin):
|
|||||||
def convert(self, stream, options, file_ext, log,
|
def convert(self, stream, options, file_ext, log,
|
||||||
accelerators):
|
accelerators):
|
||||||
log.debug('Reading text from file...')
|
log.debug('Reading text from file...')
|
||||||
|
|
||||||
txt = stream.read()
|
txt = stream.read()
|
||||||
# Get the encoding of the document.
|
# Get the encoding of the document.
|
||||||
if options.input_encoding:
|
if options.input_encoding:
|
||||||
@ -80,7 +80,7 @@ class TXTInput(InputFormatPlugin):
|
|||||||
# Get length for hyphen removal and punctuation unwrap
|
# Get length for hyphen removal and punctuation unwrap
|
||||||
docanalysis = DocAnalysis('txt', txt)
|
docanalysis = DocAnalysis('txt', txt)
|
||||||
length = docanalysis.line_length(.5)
|
length = docanalysis.line_length(.5)
|
||||||
|
|
||||||
if options.formatting_type == 'auto':
|
if options.formatting_type == 'auto':
|
||||||
options.formatting_type = detect_formatting_type(txt)
|
options.formatting_type = detect_formatting_type(txt)
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ class TXTInput(InputFormatPlugin):
|
|||||||
txt = preprocessor.punctuation_unwrap(length, txt, 'txt')
|
txt = preprocessor.punctuation_unwrap(length, txt, 'txt')
|
||||||
|
|
||||||
flow_size = getattr(options, 'flow_size', 0)
|
flow_size = getattr(options, 'flow_size', 0)
|
||||||
|
|
||||||
if options.formatting_type == 'heuristic':
|
if options.formatting_type == 'heuristic':
|
||||||
html = convert_heuristic(txt, epub_split_size_kb=flow_size)
|
html = convert_heuristic(txt, epub_split_size_kb=flow_size)
|
||||||
else:
|
else:
|
||||||
|
@ -218,6 +218,10 @@ class TXTMLizer(object):
|
|||||||
|
|
||||||
if tag in SPACE_TAGS:
|
if tag in SPACE_TAGS:
|
||||||
text.append(u' ')
|
text.append(u' ')
|
||||||
|
|
||||||
|
# Scene breaks.
|
||||||
|
if tag == 'hr':
|
||||||
|
text.append('\n\n* * *\n\n')
|
||||||
|
|
||||||
# Process tags that contain text.
|
# Process tags that contain text.
|
||||||
if hasattr(elem, 'text') and elem.text:
|
if hasattr(elem, 'text') and elem.text:
|
||||||
|
@ -15,7 +15,8 @@ from calibre.gui2.preferences.plugins_ui import Ui_Form
|
|||||||
from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \
|
from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \
|
||||||
disable_plugin, plugin_customization, add_plugin, \
|
disable_plugin, plugin_customization, add_plugin, \
|
||||||
remove_plugin
|
remove_plugin
|
||||||
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files
|
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, \
|
||||||
|
question_dialog
|
||||||
|
|
||||||
class PluginModel(QAbstractItemModel): # {{{
|
class PluginModel(QAbstractItemModel): # {{{
|
||||||
|
|
||||||
@ -132,7 +133,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.toggle_plugin_button.clicked.connect(self.toggle_plugin)
|
self.toggle_plugin_button.clicked.connect(self.toggle_plugin)
|
||||||
self.customize_plugin_button.clicked.connect(self.customize_plugin)
|
self.customize_plugin_button.clicked.connect(self.customize_plugin)
|
||||||
self.remove_plugin_button.clicked.connect(self.remove_plugin)
|
self.remove_plugin_button.clicked.connect(self.remove_plugin)
|
||||||
self.button_plugin_browse.clicked.connect(self.find_plugin)
|
|
||||||
self.button_plugin_add.clicked.connect(self.add_plugin)
|
self.button_plugin_add.clicked.connect(self.add_plugin)
|
||||||
|
|
||||||
def toggle_plugin(self, *args):
|
def toggle_plugin(self, *args):
|
||||||
@ -149,23 +149,33 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.modify_plugin(op='remove')
|
self.modify_plugin(op='remove')
|
||||||
|
|
||||||
def add_plugin(self):
|
def add_plugin(self):
|
||||||
path = unicode(self.plugin_path.text())
|
path = choose_files(self, 'add a plugin dialog', _('Add plugin'),
|
||||||
if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
|
filters=[(_('Plugins'), ['zip'])], all_files=False,
|
||||||
add_plugin(path)
|
select_only_single_file=True)
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
path = path[0]
|
||||||
|
if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
|
||||||
|
if not question_dialog(self, _('Are you sure?'), '<p>' + \
|
||||||
|
_('Installing plugins is a <b>security risk</b>. '
|
||||||
|
'Plugins can contain a virus/malware. '
|
||||||
|
'Only install it if you got it from a trusted source.'
|
||||||
|
' Are you sure you want to proceed?'),
|
||||||
|
show_copy_button=False):
|
||||||
|
return
|
||||||
|
plugin = add_plugin(path)
|
||||||
self._plugin_model.populate()
|
self._plugin_model.populate()
|
||||||
self._plugin_model.reset()
|
self._plugin_model.reset()
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
self.plugin_path.setText('')
|
info_dialog(self, _('Success'),
|
||||||
|
_('Plugin <b>{0}</b> successfully installed under <b>'
|
||||||
|
' {1} plugins</b>. You may have to restart calibre '
|
||||||
|
'for the plugin to take effect.').format(plugin.name, plugin.type),
|
||||||
|
show=True)
|
||||||
else:
|
else:
|
||||||
error_dialog(self, _('No valid plugin path'),
|
error_dialog(self, _('No valid plugin path'),
|
||||||
_('%s is not a valid plugin path')%path).exec_()
|
_('%s is not a valid plugin path')%path).exec_()
|
||||||
|
|
||||||
def find_plugin(self):
|
|
||||||
path = choose_files(self, 'choose plugin dialog', _('Choose plugin'),
|
|
||||||
filters=[('Plugins', ['zip'])], all_files=False,
|
|
||||||
select_only_single_file=True)
|
|
||||||
if path:
|
|
||||||
self.plugin_path.setText(path[0])
|
|
||||||
|
|
||||||
def modify_plugin(self, op=''):
|
def modify_plugin(self, op=''):
|
||||||
index = self.plugin_view.currentIndex()
|
index = self.plugin_view.currentIndex()
|
||||||
|
@ -72,64 +72,10 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_4">
|
<widget class="QPushButton" name="button_plugin_add">
|
||||||
<property name="title">
|
<property name="text">
|
||||||
<string>Add new plugin</string>
|
<string>&Add a new plugin</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_14">
|
|
||||||
<property name="text">
|
|
||||||
<string>Plugin &file:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>plugin_path</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="plugin_path"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="button_plugin_browse">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
|
||||||
<normaloff>:/images/document_open.png</normaloff>:/images/document_open.png</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="button_plugin_add">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Add</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -103,7 +103,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.gui_debug = gui_debug
|
self.gui_debug = gui_debug
|
||||||
acmap = OrderedDict()
|
acmap = OrderedDict()
|
||||||
for action in interface_actions():
|
for action in interface_actions():
|
||||||
ac = action.load_actual_plugin(self)
|
try:
|
||||||
|
ac = action.load_actual_plugin(self)
|
||||||
|
except:
|
||||||
|
# Ignore errors in loading user supplied plugins
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
if ac.plugin_path is None:
|
||||||
|
raise
|
||||||
|
|
||||||
ac.plugin_path = action.plugin_path
|
ac.plugin_path = action.plugin_path
|
||||||
ac.interface_action_base_plugin = action
|
ac.interface_action_base_plugin = action
|
||||||
if ac.name in acmap:
|
if ac.name in acmap:
|
||||||
|
@ -279,7 +279,7 @@ class Document(QWebPage): # {{{
|
|||||||
|
|
||||||
@pyqtSignature("")
|
@pyqtSignature("")
|
||||||
def init_hyphenate(self):
|
def init_hyphenate(self):
|
||||||
if self.hyphenate:
|
if self.hyphenate and getattr(self, 'loaded_lang', ''):
|
||||||
self.javascript('do_hyphenation("%s")'%self.loaded_lang)
|
self.javascript('do_hyphenation("%s")'%self.loaded_lang)
|
||||||
|
|
||||||
def after_load(self):
|
def after_load(self):
|
||||||
|
@ -98,9 +98,10 @@ class AumSortedConcatenate(object):
|
|||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
keys = self.ans.keys()
|
keys = self.ans.keys()
|
||||||
if len(keys) == 0:
|
l = len(keys)
|
||||||
|
if l == 0:
|
||||||
return 'Unknown:::Unknown'
|
return 'Unknown:::Unknown'
|
||||||
if len(keys) == 1:
|
if l == 1:
|
||||||
return self.ans[keys[0]]
|
return self.ans[keys[0]]
|
||||||
return ':#:'.join([self.ans[v] for v in sorted(keys)])
|
return ':#:'.join([self.ans[v] for v in sorted(keys)])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user