mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Merge upstream changes.
This commit is contained in:
commit
d81ed46acd
9
setup.py
9
setup.py
@ -380,12 +380,15 @@ if __name__ == '__main__':
|
||||
|
||||
class build(_build):
|
||||
|
||||
sub_commands = \
|
||||
[
|
||||
sub_commands = [
|
||||
('resources', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
('translations', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
('gui', lambda self : 'CALIBRE_BUILDBOT' not in os.environ.keys()),
|
||||
] + _build.sub_commands
|
||||
('build_ext', lambda self: True),
|
||||
('build_py', lambda self: True),
|
||||
('build_clib', _build.has_c_libraries),
|
||||
('build_scripts', _build.has_scripts),
|
||||
]
|
||||
|
||||
entry_points['console_scripts'].append('calibre_postinstall = calibre.linux:post_install')
|
||||
ext_modules = [
|
||||
|
@ -13,7 +13,8 @@ from calibre.startup import plugins, winutil, winutilerror
|
||||
from calibre.constants import iswindows, isosx, islinux, isfrozen, \
|
||||
terminal_controller, preferred_encoding, \
|
||||
__appname__, __version__, __author__, \
|
||||
win32event, win32api, winerror, fcntl
|
||||
win32event, win32api, winerror, fcntl, \
|
||||
filesystem_encoding
|
||||
import mechanize
|
||||
|
||||
mimetypes.add_type('application/epub+zip', '.epub')
|
||||
@ -41,6 +42,25 @@ def osx_version():
|
||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
|
||||
|
||||
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+\[\]/]')
|
||||
|
||||
def sanitize_file_name(name, substitute='_'):
|
||||
'''
|
||||
Sanitize the filename `name`. All invalid characters are replaced by `substitute`.
|
||||
The set of invalid characters is the union of the invalid characters in Windows,
|
||||
OS X and Linux. Also removes leading an trailing whitespace.
|
||||
**WARNING:** This function also replaces path separators, so only pass file names
|
||||
and not full paths to it.
|
||||
*NOTE:* This function always returns byte strings, not unicode objects. The byte strings
|
||||
are encoded in the filesystem encoding of the platform, or UTF-8.
|
||||
'''
|
||||
if isinstance(name, unicode):
|
||||
name = name.encode(filesystem_encoding, 'ignore')
|
||||
one = _filename_sanitize.sub(substitute, name)
|
||||
one = re.sub(r'\s', ' ', one).strip()
|
||||
return re.sub(r'^\.+$', '_', one)
|
||||
|
||||
|
||||
class CommandLineError(Exception):
|
||||
pass
|
||||
|
||||
@ -201,13 +221,6 @@ class CurrentDir(object):
|
||||
def __exit__(self, *args):
|
||||
os.chdir(self.cwd)
|
||||
|
||||
def sanitize_file_name(name):
|
||||
'''
|
||||
Remove characters that are illegal in filenames from name.
|
||||
Also remove path separators. All illegal characters are replaced by
|
||||
underscores.
|
||||
'''
|
||||
return re.sub(r'\s', ' ', re.sub(r'[\xae"\'\|\~\:\?\\\/]|^-', '_', name.strip()))
|
||||
|
||||
def detect_ncpus():
|
||||
"""Detects the number of effective CPUs in the system"""
|
||||
|
@ -29,6 +29,10 @@ winerror = __import__('winerror') if iswindows else None
|
||||
win32api = __import__('win32api') if iswindows else None
|
||||
fcntl = None if iswindows else __import__('fcntl')
|
||||
|
||||
filesystem_encoding = sys.getfilesystemencoding()
|
||||
if filesystem_encoding is None: filesystem_encoding = 'utf-8'
|
||||
|
||||
|
||||
################################################################################
|
||||
plugins = None
|
||||
if plugins is None:
|
||||
|
@ -185,6 +185,20 @@ def add_plugin(path_to_zip_file):
|
||||
initialize_plugins()
|
||||
return plugin
|
||||
|
||||
def remove_plugin(plugin_or_name):
|
||||
name = getattr(plugin_or_name, 'name', plugin_or_name)
|
||||
plugins = config['plugins']
|
||||
removed = False
|
||||
if name in plugins.keys():
|
||||
removed = True
|
||||
zfp = plugins[name]
|
||||
if os.path.exists(zfp):
|
||||
os.remove(zfp)
|
||||
plugins.pop(name)
|
||||
config['plugins'] = plugins
|
||||
initialize_plugins()
|
||||
return removed
|
||||
|
||||
def is_disabled(plugin):
|
||||
return plugin.name in config['disabled_plugins']
|
||||
|
||||
@ -237,6 +251,8 @@ def option_parser():
|
||||
'''))
|
||||
parser.add_option('-a', '--add-plugin', default=None,
|
||||
help=_('Add a plugin by specifying the path to the zip file containing it.'))
|
||||
parser.add_option('-r', '--remove-plugin', default=None,
|
||||
help=_('Remove a custom plugin by name. Has no effect on builtin plugins'))
|
||||
parser.add_option('--customize-plugin', default=None,
|
||||
help=_('Customize plugin. Specify name of plugin and customization string separated by a comma.'))
|
||||
parser.add_option('-l', '--list-plugins', default=False, action='store_true',
|
||||
@ -267,6 +283,11 @@ def main(args=sys.argv):
|
||||
if opts.add_plugin is not None:
|
||||
plugin = add_plugin(opts.add_plugin)
|
||||
print 'Plugin added:', plugin.name, plugin.version
|
||||
if opts.remove_plugin is not None:
|
||||
if remove_plugin(opts.remove_plugin):
|
||||
print 'Plugin removed'
|
||||
else:
|
||||
print 'No custom pluginnamed', opts.remove_plugin
|
||||
if opts.customize_plugin is not None:
|
||||
name, custom = opts.customize_plugin.split(',')
|
||||
plugin = find_plugin(name.strip())
|
||||
|
@ -1906,6 +1906,8 @@ def process_file(path, options, logger=None):
|
||||
fpb = re.compile(options.force_page_break, re.IGNORECASE) if options.force_page_break else \
|
||||
re.compile('$')
|
||||
cq = options.chapter_attr.split(',')
|
||||
if len(cq) < 3:
|
||||
raise ValueError('The --chapter-attr setting must have 2 commas.')
|
||||
options.chapter_attr = [re.compile(cq[0], re.IGNORECASE), cq[1],
|
||||
re.compile(cq[2], re.IGNORECASE)]
|
||||
options.force_page_break = fpb
|
||||
|
@ -22,6 +22,7 @@ from calibre.ebooks.mobi.langcodes import main_language, sub_language
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.metadata.opf import OPFCreator
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre import sanitize_file_name
|
||||
|
||||
class EXTHHeader(object):
|
||||
|
||||
@ -200,7 +201,8 @@ class MobiReader(object):
|
||||
guide = soup.find('guide')
|
||||
for elem in soup.findAll(['metadata', 'guide']):
|
||||
elem.extract()
|
||||
htmlfile = os.path.join(output_dir, self.name+'.html')
|
||||
htmlfile = os.path.join(output_dir,
|
||||
sanitize_file_name(self.name)+'.html')
|
||||
try:
|
||||
for ref in guide.findAll('reference', href=True):
|
||||
ref['href'] = os.path.basename(htmlfile)+ref['href']
|
||||
|
@ -20,7 +20,7 @@ from calibre.ebooks.epub.iterator import is_supported
|
||||
from calibre.library import server_config
|
||||
from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \
|
||||
disable_plugin, customize_plugin, \
|
||||
plugin_customization, add_plugin
|
||||
plugin_customization, add_plugin, remove_plugin
|
||||
|
||||
class PluginModel(QAbstractItemModel):
|
||||
|
||||
@ -242,6 +242,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.plugin_view.setModel(self._plugin_model)
|
||||
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'))
|
||||
self.connect(self.button_plugin_browse, SIGNAL('clicked()'), self.find_plugin)
|
||||
self.connect(self.button_plugin_add, SIGNAL('clicked()'), self.add_plugin)
|
||||
|
||||
@ -287,6 +288,13 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
if ok:
|
||||
customize_plugin(plugin, unicode(text))
|
||||
self._plugin_model.refresh_plugin(plugin)
|
||||
if op == 'remove':
|
||||
if remove_plugin(plugin):
|
||||
self._plugin_model.populate()
|
||||
self._plugin_model.reset()
|
||||
else:
|
||||
error_dialog(self, _('Cannot remove builtin plugin'),
|
||||
plugin.name + _(' cannot be removed. It is a builtin plugin. Try disabling it instead.')).exec_()
|
||||
|
||||
|
||||
def up_column(self):
|
||||
|
@ -72,7 +72,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
<number>4</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_3" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
@ -811,6 +811,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove_plugin" >
|
||||
<property name="text" >
|
||||
<string>&Remove plugin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -187,8 +187,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.metadata_menu = md
|
||||
self.add_menu = QMenu()
|
||||
self.add_menu.addAction(_('Add books from a single directory'))
|
||||
self.add_menu.addAction(_('Add books recursively (One book per directory, assumes every ebook file is the same book in a different format)'))
|
||||
self.add_menu.addAction(_('Add books recursively (Multiple books per directory, assumes every ebook file is a different book)'))
|
||||
self.add_menu.addAction(_('Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)'))
|
||||
self.add_menu.addAction(_('Add books from directories, including sub directories (Multiple books per directory, assumes every ebook file is a different book)'))
|
||||
self.action_add.setMenu(self.add_menu)
|
||||
QObject.connect(self.action_add, SIGNAL("triggered(bool)"), self.add_books)
|
||||
QObject.connect(self.add_menu.actions()[0], SIGNAL("triggered(bool)"), self.add_books)
|
||||
|
@ -199,7 +199,10 @@ class StatusBar(QStatusBar):
|
||||
ret = QStatusBar.showMessage(self, msg, timeout)
|
||||
if self.systray is not None:
|
||||
if isosx and isinstance(msg, unicode):
|
||||
msg = msg.encode(preferred_encoding)
|
||||
try:
|
||||
msg = msg.encode(preferred_encoding)
|
||||
except UnicodeEncodeError:
|
||||
msg = msg.encode('utf-8')
|
||||
self.systray.showMessage('calibre', msg, self.systray.Information, 10000)
|
||||
return ret
|
||||
|
||||
|
@ -21,13 +21,12 @@ from calibre.library.sqlite import connect, IntegrityError
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.constants import preferred_encoding, iswindows, isosx
|
||||
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.customize.ui import run_plugins_on_import
|
||||
from calibre import sanitize_file_name
|
||||
|
||||
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||
filesystem_encoding = sys.getfilesystemencoding()
|
||||
if filesystem_encoding is None: filesystem_encoding = 'utf-8'
|
||||
iscaseinsensitive = iswindows or isosx
|
||||
|
||||
def normpath(x):
|
||||
@ -37,23 +36,6 @@ def normpath(x):
|
||||
x = x.lower()
|
||||
return x
|
||||
|
||||
_filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+\[\]/]')
|
||||
|
||||
def sanitize_file_name(name, substitute='_'):
|
||||
'''
|
||||
Sanitize the filename `name`. All invalid characters are replaced by `substitute`.
|
||||
The set of invalid characters is the union of the invalid characters in Windows,
|
||||
OS X and Linux. Also removes leading an trailing whitespace.
|
||||
**WARNING:** This function also replaces path separators, so only pass file names
|
||||
and not full paths to it.
|
||||
*NOTE:* This function always returns byte strings, not unicode objects. The byte strings
|
||||
are encoded in the filesystem encoding of the platform, or UTF-8.
|
||||
'''
|
||||
if isinstance(name, unicode):
|
||||
name = name.encode(filesystem_encoding, 'ignore')
|
||||
one = _filename_sanitize.sub(substitute, name)
|
||||
one = re.sub(r'\s', ' ', one).strip()
|
||||
return re.sub(r'^\.+$', '_', one)
|
||||
|
||||
FIELD_MAP = {'id':0, 'title':1, 'authors':2, 'publisher':3, 'rating':4, 'timestamp':5,
|
||||
'size':6, 'tags':7, 'comments':8, 'series':9, 'series_index':10,
|
||||
|
@ -199,7 +199,7 @@ class Server(object):
|
||||
x = list(range(days-1, -1, -1))
|
||||
y = stats.daily_totals
|
||||
ax.plot(x, y)#, align='center', width=20, color='g')
|
||||
ax.set_xlabel('Day')
|
||||
ax.set_xlabel('Days ago')
|
||||
ax.set_ylabel('Income ($)')
|
||||
ax.hlines([stats.daily_average], 0, days-1)
|
||||
ax.set_xlim([0, days-1])
|
||||
|
Loading…
x
Reference in New Issue
Block a user