This commit is contained in:
Kovid Goyal 2009-05-31 10:42:44 -07:00
parent c3e5c50f98
commit a2187a86ac
6 changed files with 79 additions and 71 deletions

View File

@ -2,8 +2,14 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.5.14' __version__ = '0.6.0b1'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re
_ver = __version__.split('.')
_ver = [int(re.search(r'(\d+)', x).group(1)) for x in _ver]
numeric_version = tuple(_ver)
''' '''
Various run time constants. Various run time constants.
''' '''

View File

@ -5,58 +5,58 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import sys import sys
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.constants import __version__, __author__ from calibre.constants import numeric_version
class Plugin(object): class Plugin(object):
''' '''
A calibre plugin. Useful members include: A calibre plugin. Useful members include:
* ``self.plugin_path``: Stores path to the zip file that contains * ``self.plugin_path``: Stores path to the zip file that contains
this plugin or None if it is a builtin this plugin or None if it is a builtin
plugin plugin
* ``self.site_customization``: Stores a customization string entered * ``self.site_customization``: Stores a customization string entered
by the user. by the user.
Methods that should be overridden in sub classes: Methods that should be overridden in sub classes:
* :meth:`initialize` * :meth:`initialize`
* :meth:`customization_help` * :meth:`customization_help`
Useful methods: Useful methods:
* :meth:`temporary_file` * :meth:`temporary_file`
''' '''
#: List of platforms this plugin works on #: List of platforms this plugin works on
#: For example: ``['windows', 'osx', 'linux'] #: For example: ``['windows', 'osx', 'linux']
supported_platforms = [] supported_platforms = []
#: The name of this plugin #: The name of this plugin
name = 'Trivial Plugin' name = 'Trivial Plugin'
#: The version of this plugin as a 3-tuple (major, minor, revision) #: The version of this plugin as a 3-tuple (major, minor, revision)
version = (1, 0, 0) version = (1, 0, 0)
#: A short string describing what this plugin does #: A short string describing what this plugin does
description = _('Does absolutely nothing') description = _('Does absolutely nothing')
#: The author of this plugin #: The author of this plugin
author = _('Unknown') author = _('Unknown')
#: When more than one plugin exists for a filetype, #: When more than one plugin exists for a filetype,
#: the plugins are run in order of decreasing priority #: the plugins are run in order of decreasing priority
#: i.e. plugins with higher priority will be run first. #: i.e. plugins with higher priority will be run first.
#: The highest possible priority is ``sys.maxint``. #: The highest possible priority is ``sys.maxint``.
#: Default pririty is 1. #: Default pririty is 1.
priority = 1 priority = 1
#: The earliest version of calibre this plugin requires #: The earliest version of calibre this plugin requires
minimum_calibre_version = (0, 4, 118) minimum_calibre_version = (0, 4, 118)
#: If False, the user will not be able to disable this plugin. Use with #: If False, the user will not be able to disable this plugin. Use with
#: care. #: care.
can_be_disabled = True can_be_disabled = True
#: The type of this plugin. Used for categorizing plugins in the #: The type of this plugin. Used for categorizing plugins in the
#: GUI #: GUI
type = _('Base') type = _('Base')
@ -64,109 +64,109 @@ class Plugin(object):
def __init__(self, plugin_path): def __init__(self, plugin_path):
self.plugin_path = plugin_path self.plugin_path = plugin_path
self.site_customization = None self.site_customization = None
def initialize(self): def initialize(self):
''' '''
Called once when calibre plugins are initialized. Plugins are re-initialized Called once when calibre plugins are initialized. Plugins are re-initialized
every time a new plugin is added. every time a new plugin is added.
Perform any plugin specific initialization here, such as extracting Perform any plugin specific initialization here, such as extracting
resources from the plugin zip file. The path to the zip file is resources from the plugin zip file. The path to the zip file is
available as ``self.plugin_path``. available as ``self.plugin_path``.
Note that ``self.site_customization`` is **not** available at this point. Note that ``self.site_customization`` is **not** available at this point.
''' '''
pass pass
def customization_help(self, gui=False): def customization_help(self, gui=False):
''' '''
Return a string giving help on how to customize this plugin. Return a string giving help on how to customize this plugin.
By default raise a :class:`NotImplementedError`, which indicates that By default raise a :class:`NotImplementedError`, which indicates that
the plugin does not require customization. the plugin does not require customization.
If you re-implement this method in your subclass, the user will If you re-implement this method in your subclass, the user will
be asked to enter a string as customization for this plugin. be asked to enter a string as customization for this plugin.
The customization string will be available as The customization string will be available as
``self.site_customization``. ``self.site_customization``.
Site customization could be anything, for example, the path to Site customization could be anything, for example, the path to
a needed binary on the user's computer. a needed binary on the user's computer.
:param gui: If True return HTML help, otherwise return plain text help. :param gui: If True return HTML help, otherwise return plain text help.
''' '''
raise NotImplementedError raise NotImplementedError
def temporary_file(self, suffix): def temporary_file(self, suffix):
''' '''
Return a file-like object that is a temporary file on the file system. Return a file-like object that is a temporary file on the file system.
This file will remain available even after being closed and will only This file will remain available even after being closed and will only
be removed on interpreter shutdown. Use the ``name`` member of the be removed on interpreter shutdown. Use the ``name`` member of the
returned object to access the full path to the created temporary file. returned object to access the full path to the created temporary file.
:param suffix: The suffix that the temporary file will have. :param suffix: The suffix that the temporary file will have.
''' '''
return PersistentTemporaryFile(suffix) return PersistentTemporaryFile(suffix)
def is_customizable(self): def is_customizable(self):
try: try:
self.customization_help() self.customization_help()
return True return True
except NotImplementedError: except NotImplementedError:
return False return False
def __enter__(self, *args): def __enter__(self, *args):
if self.plugin_path is not None: if self.plugin_path is not None:
sys.path.insert(0, self.plugin_path) sys.path.insert(0, self.plugin_path)
def __exit__(self, *args): def __exit__(self, *args):
if self.plugin_path in sys.path: if self.plugin_path in sys.path:
sys.path.remove(self.plugin_path) sys.path.remove(self.plugin_path)
class FileTypePlugin(Plugin): class FileTypePlugin(Plugin):
''' '''
A plugin that is associated with a particular set of file types. A plugin that is associated with a particular set of file types.
''' '''
#: Set of file types for which this plugin should be run #: Set of file types for which this plugin should be run
#: For example: ``set(['lit', 'mobi', 'prc'])`` #: For example: ``set(['lit', 'mobi', 'prc'])``
file_types = set([]) file_types = set([])
#: If True, this plugin is run when books are added #: If True, this plugin is run when books are added
#: to the database #: to the database
on_import = False on_import = False
#: If True, this plugin is run whenever an any2* tool #: If True, this plugin is run whenever an any2* tool
#: is used, on the file passed to the any2* tool. #: is used, on the file passed to the any2* tool.
on_preprocess = False on_preprocess = False
#: If True, this plugin is run after an any2* tool is #: If True, this plugin is run after an any2* tool is
#: used, on the final file produced by the tool. #: used, on the final file produced by the tool.
on_postprocess = False on_postprocess = False
type = _('File type') type = _('File type')
def run(self, path_to_ebook): def run(self, path_to_ebook):
''' '''
Run the plugin. Must be implemented in subclasses. Run the plugin. Must be implemented in subclasses.
It should perform whatever modifications are required It should perform whatever modifications are required
on the ebook and return the absolute path to the on the ebook and return the absolute path to the
modified ebook. If no modifications are needed, it should modified ebook. If no modifications are needed, it should
return the path to the original ebook. If an error is encountered return the path to the original ebook. If an error is encountered
it should raise an Exception. The default implementation it should raise an Exception. The default implementation
simply return the path to the original ebook. simply return the path to the original ebook.
The modified ebook file should be created with the The modified ebook file should be created with the
:meth:`temporary_file` method. :meth:`temporary_file` method.
:param path_to_ebook: Absolute path to the ebook. :param path_to_ebook: Absolute path to the ebook.
:return: Absolute path to the modified ebook. :return: Absolute path to the modified ebook.
''' '''
# Default implementation does nothing # Default implementation does nothing
return path_to_ebook return path_to_ebook
class MetadataReaderPlugin(Plugin): class MetadataReaderPlugin(Plugin):
''' '''
A plugin that implements reading metadata from a set of file types. A plugin that implements reading metadata from a set of file types.
@ -174,26 +174,26 @@ class MetadataReaderPlugin(Plugin):
#: Set of file types for which this plugin should be run #: Set of file types for which this plugin should be run
#: For example: ``set(['lit', 'mobi', 'prc'])`` #: For example: ``set(['lit', 'mobi', 'prc'])``
file_types = set([]) file_types = set([])
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
version = tuple(map(int, (__version__.split('.'))[:3])) version = numeric_version
author = 'Kovid Goyal' author = 'Kovid Goyal'
type = _('Metadata reader') type = _('Metadata reader')
def get_metadata(self, stream, type): def get_metadata(self, stream, type):
''' '''
Return metadata for the file represented by stream (a file like object Return metadata for the file represented by stream (a file like object
that supports reading). Raise an exception when there is an error that supports reading). Raise an exception when there is an error
with the input data. with the input data.
:param type: The type of file. Guaranteed to be one of the entries :param type: The type of file. Guaranteed to be one of the entries
in :attr:`file_types`. in :attr:`file_types`.
:return: A :class:`calibre.ebooks.metadata.MetaInformation` object :return: A :class:`calibre.ebooks.metadata.MetaInformation` object
''' '''
return None return None
class MetadataWriterPlugin(Plugin): class MetadataWriterPlugin(Plugin):
''' '''
A plugin that implements reading metadata from a set of file types. A plugin that implements reading metadata from a set of file types.
@ -201,24 +201,24 @@ class MetadataWriterPlugin(Plugin):
#: Set of file types for which this plugin should be run #: Set of file types for which this plugin should be run
#: For example: ``set(['lit', 'mobi', 'prc'])`` #: For example: ``set(['lit', 'mobi', 'prc'])``
file_types = set([]) file_types = set([])
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
version = tuple(map(int, (__version__.split('.'))[:3])) version = numeric_version
author = 'Kovid Goyal' author = 'Kovid Goyal'
type = _('Metadata writer') type = _('Metadata writer')
def set_metadata(self, stream, mi, type): def set_metadata(self, stream, mi, type):
''' '''
Set metadata for the file represented by stream (a file like object Set metadata for the file represented by stream (a file like object
that supports reading). Raise an exception when there is an error that supports reading). Raise an exception when there is an error
with the input data. with the input data.
:param type: The type of file. Guaranteed to be one of the entries :param type: The type of file. Guaranteed to be one of the entries
in :attr:`file_types`. in :attr:`file_types`.
:param mi: A :class:`calibre.ebooks.metadata.MetaInformation` object :param mi: A :class:`calibre.ebooks.metadata.MetaInformation` object
''' '''
pass pass

View File

@ -5,7 +5,7 @@ import textwrap
import os import os
import glob import glob
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, MetadataWriterPlugin from calibre.customize import FileTypePlugin, MetadataReaderPlugin, MetadataWriterPlugin
from calibre.constants import __version__ from calibre.constants import numeric_version
class HTML2ZIP(FileTypePlugin): class HTML2ZIP(FileTypePlugin):
name = 'HTML to ZIP' name = 'HTML to ZIP'
@ -15,7 +15,7 @@ Follow all local links in an HTML file and create a ZIP \
file containing all linked files. This plugin is run \ file containing all linked files. This plugin is run \
every time you add an HTML file to the library.\ every time you add an HTML file to the library.\
''')) '''))
version = tuple(map(int, (__version__.split('.'))[:3])) version = numeric_version
file_types = set(['html', 'htm', 'xhtml', 'xhtm']) file_types = set(['html', 'htm', 'xhtml', 'xhtm'])
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
on_import = True on_import = True

View File

@ -9,13 +9,12 @@ from calibre.customize import Plugin, FileTypePlugin, MetadataReaderPlugin, \
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
from calibre.customize.profiles import InputProfile, OutputProfile from calibre.customize.profiles import InputProfile, OutputProfile
from calibre.customize.builtins import plugins as builtin_plugins from calibre.customize.builtins import plugins as builtin_plugins
from calibre.constants import __version__, iswindows, isosx from calibre.constants import numeric_version as version, iswindows, isosx
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.utils.config import make_config_dir, Config, ConfigProxy, \ from calibre.utils.config import make_config_dir, Config, ConfigProxy, \
plugin_dir, OptionParser plugin_dir, OptionParser
version = tuple([int(x) for x in __version__.split('.')])
platform = 'linux' platform = 'linux'
if iswindows: if iswindows:

View File

@ -653,8 +653,9 @@ class MobiWriter(object):
def _generate_end_records(self): def _generate_end_records(self):
self._flis_number = len(self._records) self._flis_number = len(self._records)
self._records.append( self._records.append(
'FLIS\0\0\0\x08\0\x41\0\0\0\0\0\0\xff\xff\xff\xff\0\x01\0\x03\0\0\0\x03\0\0\0\x01') 'FLIS\0\0\0\x08\0\x41\0\0\0\0\0\0\xff\xff\xff\xff\0\x01\0\x03\0\0\0\x03\0\0\0\x01'+
fcis = 'FCIS\x00\x00\x00\x14\x00\x00\x00\x10\x00\x00\x00\x00' '\xff'*4)
fcis = 'FCIS\x00\x00\x00\x14\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00'
fcis += pack('>I', self._text_length) fcis += pack('>I', self._text_length)
fcis += '\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x08\x00\x01\x00\x01\x00\x00\x00\x00' fcis += '\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x08\x00\x01\x00\x01\x00\x00\x00\x00'
self._fcis_number = len(self._records) self._fcis_number = len(self._records)

View File

@ -17,6 +17,7 @@ __appname__ = re.search(r'__appname__\s+=\s+[\'"]([^\'"]+)[\'"]', raw).group(1)
PREFIX = "/var/www/calibre.kovidgoyal.net" PREFIX = "/var/www/calibre.kovidgoyal.net"
DOWNLOADS = PREFIX+"/htdocs/downloads" DOWNLOADS = PREFIX+"/htdocs/downloads"
BETAS = DOWNLOADS +'/betas'
DOCS = PREFIX+"/htdocs/apidocs" DOCS = PREFIX+"/htdocs/apidocs"
USER_MANUAL = PREFIX+'/htdocs/user_manual' USER_MANUAL = PREFIX+'/htdocs/user_manual'
HTML2LRF = "src/calibre/ebooks/lrf/html/demo" HTML2LRF = "src/calibre/ebooks/lrf/html/demo"
@ -429,8 +430,9 @@ class upload_demo(OptionlessCommand):
def run(self): def run(self):
check_call( check_call(
'''html2lrf --title='Demonstration of html2lrf' --author='Kovid Goyal' ''' '''ebook-convert %s/demo.html /tmp/html2lrf.lrf '''
'''--header --output=/tmp/html2lrf.lrf %s/demo.html ''' '''--title='Demonstration of html2lrf' --author='Kovid Goyal' '''
'''--header '''
'''--serif-family "/usr/share/fonts/corefonts, Times New Roman" ''' '''--serif-family "/usr/share/fonts/corefonts, Times New Roman" '''
'''--mono-family "/usr/share/fonts/corefonts, Andale Mono" ''' '''--mono-family "/usr/share/fonts/corefonts, Andale Mono" '''
''''''%(HTML2LRF,), shell=True) ''''''%(HTML2LRF,), shell=True)
@ -442,8 +444,8 @@ class upload_demo(OptionlessCommand):
check_call('scp /tmp/html-demo.zip divok:%s/'%(DOWNLOADS,), shell=True) check_call('scp /tmp/html-demo.zip divok:%s/'%(DOWNLOADS,), shell=True)
check_call( check_call(
'''txt2lrf -t 'Demonstration of txt2lrf' -a 'Kovid Goyal' ''' ("ebook-convert %s/demo.txt /tmp/txt2lrf.lrf -t 'Demonstration of txt2lrf'"
'''--header -o /tmp/txt2lrf.lrf %s/demo.txt'''%(TXT2LRF,), shell=True) "-a 'Kovid Goyal' --header ")%(TXT2LRF,), shell=True)
check_call('cd src/calibre/ebooks/lrf/txt/demo/ && ' check_call('cd src/calibre/ebooks/lrf/txt/demo/ && '
'zip -j /tmp/txt-demo.zip * /tmp/txt2lrf.lrf', shell=True) 'zip -j /tmp/txt-demo.zip * /tmp/txt2lrf.lrf', shell=True)