mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
fd1e0711e0
@ -91,7 +91,6 @@ class BuildEXE(py2exe.build_exe.py2exe):
|
|||||||
|
|
||||||
print
|
print
|
||||||
print 'Adding third party dependencies'
|
print 'Adding third party dependencies'
|
||||||
print '\tAdding devcon'
|
|
||||||
tdir = os.path.join(PY2EXE_DIR, 'driver')
|
tdir = os.path.join(PY2EXE_DIR, 'driver')
|
||||||
os.makedirs(tdir)
|
os.makedirs(tdir)
|
||||||
for pat in ('*.dll', '*.sys', '*.cat', '*.inf'):
|
for pat in ('*.dll', '*.sys', '*.cat', '*.inf'):
|
||||||
@ -104,11 +103,6 @@ class BuildEXE(py2exe.build_exe.py2exe):
|
|||||||
'bin\\freetype.dll', 'bin\\jpeg62.dll'):
|
'bin\\freetype.dll', 'bin\\jpeg62.dll'):
|
||||||
shutil.copyfile(os.path.join(POPPLER, x),
|
shutil.copyfile(os.path.join(POPPLER, x),
|
||||||
os.path.join(PY2EXE_DIR, os.path.basename(x)))
|
os.path.join(PY2EXE_DIR, os.path.basename(x)))
|
||||||
#shutil.copyfile(PDFTOHTML, os.path.join(PY2EXE_DIR, os.path.basename(PDFTOHTML)))
|
|
||||||
#shutil.copyfile(PDFTOHTML+'.manifest', os.path.join(PY2EXE_DIR,
|
|
||||||
# os.path.basename(PDFTOHTML)+'.manifest'))
|
|
||||||
#print '\tAdding pdftk'
|
|
||||||
#shutil.copyfile(PDFTK, os.path.join(PY2EXE_DIR, os.path.basename(PDFTK)))
|
|
||||||
print '\tAdding podofo'
|
print '\tAdding podofo'
|
||||||
for f in glob.glob(os.path.join(PODOFO, '*.dll')):
|
for f in glob.glob(os.path.join(PODOFO, '*.dll')):
|
||||||
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))
|
shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))
|
||||||
|
@ -2,7 +2,7 @@ __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.6.7'
|
__version__ = '0.6.8'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -107,7 +107,7 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
TITLEPAGE = '''\
|
TITLEPAGE = '''\
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>%(title)s</title>
|
<title>%(title)s</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@ -312,6 +312,8 @@ class EPUBOutput(OutputFormatPlugin):
|
|||||||
for tag in XPath('//h:script')(root):
|
for tag in XPath('//h:script')(root):
|
||||||
if not tag.text and not tag.get('src', False):
|
if not tag.text and not tag.get('src', False):
|
||||||
tag.getparent().remove(tag)
|
tag.getparent().remove(tag)
|
||||||
|
for tag in XPath('//h:body/descendant::h:script')(root):
|
||||||
|
tag.getparent().remove(tag)
|
||||||
|
|
||||||
for tag in XPath('//h:form')(root):
|
for tag in XPath('//h:form')(root):
|
||||||
tag.getparent().remove(tag)
|
tag.getparent().remove(tag)
|
||||||
|
@ -95,7 +95,8 @@ def option_parser():
|
|||||||
writers = set([])
|
writers = set([])
|
||||||
for w in metadata_writers():
|
for w in metadata_writers():
|
||||||
writers = writers.union(set(w.file_types))
|
writers = writers.union(set(w.file_types))
|
||||||
return config().option_parser(USAGE%(list(filetypes()), list(writers)))
|
ft, w = ', '.join(filetypes()), ', '.join(writers)
|
||||||
|
return config().option_parser(USAGE%(ft, w))
|
||||||
|
|
||||||
def do_set_metadata(opts, mi, stream, stream_type):
|
def do_set_metadata(opts, mi, stream, stream_type):
|
||||||
mi = MetaInformation(mi)
|
mi = MetaInformation(mi)
|
||||||
|
@ -2,12 +2,13 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
import os, re, time, textwrap
|
import os, re, time, textwrap
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
from PyQt4.Qt import QDialog, QListWidgetItem, QIcon, \
|
||||||
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit, \
|
||||||
QStringListModel, QAbstractItemModel, QFont, \
|
QStringListModel, QAbstractItemModel, QFont, \
|
||||||
SIGNAL, QThread, Qt, QSize, QVariant, QUrl, \
|
SIGNAL, QThread, Qt, QSize, QVariant, QUrl, \
|
||||||
QModelIndex, QInputDialog, QAbstractTableModel, \
|
QModelIndex, QInputDialog, QAbstractTableModel, \
|
||||||
QDialogButtonBox, QTabWidget, QBrush, QLineEdit
|
QDialogButtonBox, QTabWidget, QBrush, QLineEdit, \
|
||||||
|
QProgressDialog
|
||||||
|
|
||||||
from calibre.constants import islinux, iswindows
|
from calibre.constants import islinux, iswindows
|
||||||
from calibre.gui2.dialogs.config.config_ui import Ui_Dialog
|
from calibre.gui2.dialogs.config.config_ui import Ui_Dialog
|
||||||
@ -648,7 +649,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
|
QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
|
||||||
|
|
||||||
def compact(self, toggled):
|
def compact(self, toggled):
|
||||||
d = Vacuum(self, self.db)
|
d = CheckIntegrity(self.db, self)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
def browse(self):
|
def browse(self):
|
||||||
@ -739,25 +740,48 @@ class VacThread(QThread):
|
|||||||
self._parent = parent
|
self._parent = parent
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
bad = self.db.check_integrity()
|
err = bad = None
|
||||||
self.emit(SIGNAL('check_done(PyQt_PyObject)'), bad)
|
try:
|
||||||
|
bad = self.db.check_integrity(self.callback)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
err = traceback.format_exc()
|
||||||
|
self.emit(SIGNAL('check_done(PyQt_PyObject, PyQt_PyObject)'), bad, err)
|
||||||
|
|
||||||
class Vacuum(QMessageBox):
|
def callback(self, progress, msg):
|
||||||
|
self.emit(SIGNAL('callback(PyQt_PyObject,PyQt_PyObject)'), progress,
|
||||||
|
msg)
|
||||||
|
|
||||||
|
class CheckIntegrity(QProgressDialog):
|
||||||
|
|
||||||
|
def __init__(self, db, parent=None):
|
||||||
|
QProgressDialog.__init__(self, parent)
|
||||||
|
self.setCancelButtonText('')
|
||||||
|
self.setMinimum(0)
|
||||||
|
self.setMaximum(100)
|
||||||
|
self.setWindowTitle(_('Checking database integrity'))
|
||||||
|
self.setAutoReset(False)
|
||||||
|
self.setValue(0)
|
||||||
|
|
||||||
def __init__(self, parent, db):
|
|
||||||
self.db = db
|
|
||||||
QMessageBox.__init__(self, QMessageBox.Information, _('Checking...'),
|
|
||||||
_('Checking database integrity. This may take a while.'),
|
|
||||||
QMessageBox.NoButton, parent)
|
|
||||||
self.vthread = VacThread(self, db)
|
self.vthread = VacThread(self, db)
|
||||||
self.connect(self.vthread, SIGNAL('check_done(PyQt_PyObject)'),
|
self.connect(self.vthread, SIGNAL('check_done(PyQt_PyObject,PyQt_PyObject)'),
|
||||||
self.check_done,
|
self.check_done,
|
||||||
Qt.QueuedConnection)
|
Qt.QueuedConnection)
|
||||||
|
self.connect(self.vthread,
|
||||||
|
SIGNAL('callback(PyQt_PyObject,PyQt_PyObject)'),
|
||||||
|
self.callback, Qt.QueuedConnection)
|
||||||
self.vthread.start()
|
self.vthread.start()
|
||||||
|
|
||||||
|
def callback(self, progress, msg):
|
||||||
|
self.setLabelText(msg)
|
||||||
|
self.setValue(int(100*progress))
|
||||||
|
|
||||||
def check_done(self, bad):
|
def check_done(self, bad, err):
|
||||||
if bad:
|
if err:
|
||||||
|
error_dialog(self, _('Error'),
|
||||||
|
_('Failed to check database integrity'),
|
||||||
|
det_msg=err, show=True)
|
||||||
|
elif bad:
|
||||||
titles = [self.db.title(x, index_is_id=True) for x in bad]
|
titles = [self.db.title(x, index_is_id=True) for x in bad]
|
||||||
det_msg = '\n'.join(titles)
|
det_msg = '\n'.join(titles)
|
||||||
warning_dialog(self, _('Some inconsistencies found'),
|
warning_dialog(self, _('Some inconsistencies found'),
|
||||||
@ -767,7 +791,7 @@ class Vacuum(QMessageBox):
|
|||||||
'You should check them manually. This can '
|
'You should check them manually. This can '
|
||||||
'happen if you manipulate the files in the '
|
'happen if you manipulate the files in the '
|
||||||
'library folder directly.'), det_msg=det_msg, show=True)
|
'library folder directly.'), det_msg=det_msg, show=True)
|
||||||
self.accept()
|
self.reset()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
|
|
||||||
def select_cover(self, checked):
|
def select_cover(self, checked):
|
||||||
files = choose_images(self, 'change cover dialog',
|
files = choose_images(self, 'change cover dialog',
|
||||||
u'Choose cover for ' + qstring_to_unicode(self.title.text()))
|
_('Choose cover for ') + unicode(self.title.text()))
|
||||||
if not files:
|
if not files:
|
||||||
return
|
return
|
||||||
_file = files[0]
|
_file = files[0]
|
||||||
@ -203,10 +203,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
if mi.pubdate:
|
if mi.pubdate:
|
||||||
self.pubdate.setDate(QDate(mi.pubdate.year, mi.pubdate.month,
|
self.pubdate.setDate(QDate(mi.pubdate.year, mi.pubdate.month,
|
||||||
mi.pubdate.day))
|
mi.pubdate.day))
|
||||||
if mi.series:
|
if mi.series and mi.series.strip():
|
||||||
self.series.setEditText(mi.series)
|
self.series.setEditText(mi.series)
|
||||||
if mi.series_index is not None:
|
if mi.series_index is not None:
|
||||||
self.series_index.setValue(float(mi.series_index))
|
self.series_index.setValue(float(mi.series_index))
|
||||||
|
|
||||||
def set_cover(self):
|
def set_cover(self):
|
||||||
mi, ext = self.get_selected_format_metadata()
|
mi, ext = self.get_selected_format_metadata()
|
||||||
|
@ -303,7 +303,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
|||||||
username, password = username.strip(), password.strip()
|
username, password = username.strip(), password.strip()
|
||||||
recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole)
|
recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole)
|
||||||
key = 'recipe_account_info_%s'%recipe.id
|
key = 'recipe_account_info_%s'%recipe.id
|
||||||
config[key] = (username, password) if username and password else None
|
config[key] = (username, password) if username else None
|
||||||
|
|
||||||
def do_schedule(self, *args):
|
def do_schedule(self, *args):
|
||||||
if not getattr(self, 'allow_scheduling', False):
|
if not getattr(self, 'allow_scheduling', False):
|
||||||
|
BIN
src/calibre/gui2/images/news/esquire.png
Normal file
BIN
src/calibre/gui2/images/news/esquire.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 439 B |
@ -1046,10 +1046,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
|
|
||||||
############################## Save to disk ################################
|
############################## Save to disk ################################
|
||||||
def save_single_format_to_disk(self, checked):
|
def save_single_format_to_disk(self, checked):
|
||||||
self.save_to_disk(checked, True, prefs['output_format'])
|
self.save_to_disk(checked, False, prefs['output_format'])
|
||||||
|
|
||||||
def save_specific_format_disk(self, fmt):
|
def save_specific_format_disk(self, fmt):
|
||||||
self.save_to_disk(False, True, fmt)
|
self.save_to_disk(False, False, fmt)
|
||||||
|
|
||||||
def save_to_single_dir(self, checked):
|
def save_to_single_dir(self, checked):
|
||||||
self.save_to_disk(checked, True)
|
self.save_to_disk(checked, True)
|
||||||
@ -1071,7 +1071,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
if single_format is not None:
|
if single_format is not None:
|
||||||
opts.formats = single_format
|
opts.formats = single_format
|
||||||
if single_dir:
|
if single_dir:
|
||||||
opts.template = '{title} - {authors}'
|
opts.template = opts.template.split('/')[-1].strip()
|
||||||
|
if not opts.template:
|
||||||
|
opts.template = '{title} - {authors}'
|
||||||
self._saver = Saver(self, self.library_view.model().db,
|
self._saver = Saver(self, self.library_view.model().db,
|
||||||
Dispatcher(self._books_saved), rows, path, opts,
|
Dispatcher(self._books_saved), rows, path, opts,
|
||||||
spare_server=self.spare_server)
|
spare_server=self.spare_server)
|
||||||
|
@ -368,6 +368,7 @@ class DocumentView(QWebView):
|
|||||||
self.setPage(self.document)
|
self.setPage(self.document)
|
||||||
self.manager = None
|
self.manager = None
|
||||||
self._reference_mode = False
|
self._reference_mode = False
|
||||||
|
self._ignore_scrollbar_signals = False
|
||||||
self.connect(self.document, SIGNAL('loadStarted()'), self.load_started)
|
self.connect(self.document, SIGNAL('loadStarted()'), self.load_started)
|
||||||
self.connect(self.document, SIGNAL('loadFinished(bool)'), self.load_finished)
|
self.connect(self.document, SIGNAL('loadFinished(bool)'), self.load_finished)
|
||||||
self.connect(self.document, SIGNAL('linkClicked(QUrl)'), self.link_clicked)
|
self.connect(self.document, SIGNAL('linkClicked(QUrl)'), self.link_clicked)
|
||||||
@ -467,13 +468,16 @@ class DocumentView(QWebView):
|
|||||||
if getattr(self, 'scrollbar', None) is not None:
|
if getattr(self, 'scrollbar', None) is not None:
|
||||||
delta = self.document.width - self.size().width()
|
delta = self.document.width - self.size().width()
|
||||||
if delta > 0:
|
if delta > 0:
|
||||||
|
self._ignore_scrollbar_signals = True
|
||||||
self.scrollbar.blockSignals(True)
|
self.scrollbar.blockSignals(True)
|
||||||
self.scrollbar.setRange(0, delta)
|
self.scrollbar.setRange(0, delta)
|
||||||
self.scrollbar.setValue(0)
|
self.scrollbar.setValue(0)
|
||||||
self.scrollbar.setSingleStep(1)
|
self.scrollbar.setSingleStep(1)
|
||||||
self.scrollbar.setPageStep(int(delta/10.))
|
self.scrollbar.setPageStep(int(delta/10.))
|
||||||
self.scrollbar.blockSignals(False)
|
|
||||||
self.scrollbar.setVisible(delta > 0)
|
self.scrollbar.setVisible(delta > 0)
|
||||||
|
self.scrollbar.blockSignals(False)
|
||||||
|
self._ignore_scrollbar_signals = False
|
||||||
|
|
||||||
|
|
||||||
def load_finished(self, ok):
|
def load_finished(self, ok):
|
||||||
self._size_hint = self.document.mainFrame().contentsSize()
|
self._size_hint = self.document.mainFrame().contentsSize()
|
||||||
@ -565,6 +569,8 @@ class DocumentView(QWebView):
|
|||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
|
||||||
def scroll_to(self, pos, notify=True):
|
def scroll_to(self, pos, notify=True):
|
||||||
|
if self._ignore_scrollbar_signals:
|
||||||
|
return
|
||||||
old_pos = self.document.ypos
|
old_pos = self.document.ypos
|
||||||
if isinstance(pos, basestring):
|
if isinstance(pos, basestring):
|
||||||
self.document.jump_to_anchor(pos)
|
self.document.jump_to_anchor(pos)
|
||||||
@ -572,8 +578,9 @@ class DocumentView(QWebView):
|
|||||||
if pos >= 1:
|
if pos >= 1:
|
||||||
self.document.scroll_to(0, self.document.height)
|
self.document.scroll_to(0, self.document.height)
|
||||||
else:
|
else:
|
||||||
self.document.scroll_to(0, int(math.ceil(
|
y = int(math.ceil(
|
||||||
pos*(self.document.height-self.document.window_height))))
|
pos*(self.document.height-self.document.window_height)))
|
||||||
|
self.document.scroll_to(0, y)
|
||||||
if notify and self.manager is not None and self.document.ypos != old_pos:
|
if notify and self.manager is not None and self.document.ypos != old_pos:
|
||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from PyQt4.QtGui import QImage
|
|||||||
|
|
||||||
from calibre.ebooks.metadata import title_sort
|
from calibre.ebooks.metadata import title_sort
|
||||||
from calibre.library.database import LibraryDatabase
|
from calibre.library.database import LibraryDatabase
|
||||||
from calibre.library.sqlite import connect, IntegrityError
|
from calibre.library.sqlite import connect, IntegrityError, DBThread
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
|
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
|
||||||
MetaInformation, authors_to_sort_string
|
MetaInformation, authors_to_sort_string
|
||||||
@ -1670,9 +1670,40 @@ books_series_link feeds
|
|||||||
|
|
||||||
return duplicates
|
return duplicates
|
||||||
|
|
||||||
def check_integrity(self):
|
def check_integrity(self, callback):
|
||||||
|
callback(0., _('Checking SQL integrity...'))
|
||||||
|
user_version = self.user_version
|
||||||
|
sql = self.conn.dump()
|
||||||
|
self.conn.close()
|
||||||
|
dest = self.dbpath+'.old'
|
||||||
|
if os.path.exists(dest):
|
||||||
|
os.remove(dest)
|
||||||
|
shutil.copyfile(self.dbpath, dest)
|
||||||
|
try:
|
||||||
|
os.remove(self.dbpath)
|
||||||
|
ndb = DBThread(self.dbpath, None)
|
||||||
|
ndb.connect()
|
||||||
|
conn = ndb.conn
|
||||||
|
conn.executescript(sql)
|
||||||
|
conn.commit()
|
||||||
|
conn.execute('pragma user_version=%d'%user_version)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
except:
|
||||||
|
if os.path.exists(self.dbpath):
|
||||||
|
os.remove(self.dbpath)
|
||||||
|
shutil.copyfile(dest, self.dbpath)
|
||||||
|
os.remove(dest)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
os.remove(dest)
|
||||||
|
self.connect()
|
||||||
|
self.refresh()
|
||||||
|
callback(0.1, _('Checking for missing files.'))
|
||||||
bad = {}
|
bad = {}
|
||||||
for id in self.data.universal_set():
|
us = self.data.universal_set()
|
||||||
|
total = float(len(us))
|
||||||
|
for i, id in enumerate(us):
|
||||||
formats = self.data.get(id, FIELD_MAP['formats'], row_is_id=True)
|
formats = self.data.get(id, FIELD_MAP['formats'], row_is_id=True)
|
||||||
if not formats:
|
if not formats:
|
||||||
formats = []
|
formats = []
|
||||||
@ -1692,6 +1723,7 @@ books_series_link feeds
|
|||||||
if id not in bad:
|
if id not in bad:
|
||||||
bad[id] = []
|
bad[id] = []
|
||||||
bad[id].append(fmt)
|
bad[id].append(fmt)
|
||||||
|
callback(0.1+0.9*(1+i)/total, _('Checked id') + ' %d'%id)
|
||||||
|
|
||||||
for id in bad:
|
for id in bad:
|
||||||
for fmt in bad[id]:
|
for fmt in bad[id]:
|
||||||
@ -1699,8 +1731,6 @@ books_series_link feeds
|
|||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
self.refresh_ids(list(bad.keys()))
|
self.refresh_ids(list(bad.keys()))
|
||||||
|
|
||||||
self.vacuum()
|
|
||||||
|
|
||||||
return bad
|
return bad
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,6 +105,15 @@ class LibraryServer(object):
|
|||||||
</entry>
|
</entry>
|
||||||
'''))
|
'''))
|
||||||
|
|
||||||
|
STANZA_AUTHOR_ENTRY=MarkupTemplate(textwrap.dedent('''\
|
||||||
|
<entry xmlns:py="http://genshi.edgewall.org/">
|
||||||
|
<title>${authors}</title>
|
||||||
|
<id>urn:calibre:${record[FM['id']]}</id>
|
||||||
|
<updated>${timestamp}</updated>
|
||||||
|
<link type="application/atom+xml" href="/?authorid=${record[FM['id']]}" />
|
||||||
|
</entry>
|
||||||
|
'''))
|
||||||
|
|
||||||
STANZA = MarkupTemplate(textwrap.dedent('''\
|
STANZA = MarkupTemplate(textwrap.dedent('''\
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:py="http://genshi.edgewall.org/">
|
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:py="http://genshi.edgewall.org/">
|
||||||
@ -330,24 +339,30 @@ class LibraryServer(object):
|
|||||||
|
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
def stanza(self, search=None, sortby=None):
|
def stanza(self, search=None, sortby=None, authorid=None):
|
||||||
'Feeds to read calibre books on a ipod with stanza.'
|
'Feeds to read calibre books on a ipod with stanza.'
|
||||||
books = []
|
books = []
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
||||||
if not sortby and not search:
|
if not sortby and not search and not authorid:
|
||||||
return self.STANZA_MAIN.generate(subtitle='', data=books, FM=FIELD_MAP,
|
return self.STANZA_MAIN.generate(subtitle='', data=books, FM=FIELD_MAP,
|
||||||
updated=updated, id='urn:calibre:main').render('xml')
|
updated=updated, id='urn:calibre:main').render('xml')
|
||||||
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
if authorid:
|
||||||
|
authorid=int(authorid)
|
||||||
|
au = self.db.authors(authorid, index_is_id=True)
|
||||||
|
ids = self.db.data.get_matches('authors', au)
|
||||||
|
else:
|
||||||
|
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
||||||
record_list = list(iter(self.db))
|
record_list = list(iter(self.db))
|
||||||
if sortby == "byauthor":
|
if sortby == "byauthor":
|
||||||
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['author_sort']], y[FIELD_MAP['author_sort']]))
|
record_list.sort(lambda x, y: cmp(x[FIELD_MAP['author_sort']], y[FIELD_MAP['author_sort']]))
|
||||||
elif sortby == "bytitle":
|
elif sortby == "bytitle" or authorid:
|
||||||
record_list.sort(lambda x, y: cmp(title_sort(x[FIELD_MAP['title']]),
|
record_list.sort(lambda x, y: cmp(title_sort(x[FIELD_MAP['title']]),
|
||||||
title_sort(y[FIELD_MAP['title']])))
|
title_sort(y[FIELD_MAP['title']])))
|
||||||
else:
|
else:
|
||||||
record_list = reversed(record_list)
|
record_list = reversed(record_list)
|
||||||
|
author_list=[]
|
||||||
for record in record_list:
|
for record in record_list:
|
||||||
if record[0] not in ids: continue
|
if record[0] not in ids: continue
|
||||||
r = record[FIELD_MAP['formats']]
|
r = record[FIELD_MAP['formats']]
|
||||||
@ -374,7 +389,20 @@ class LibraryServer(object):
|
|||||||
fmt_sidx(float(record[FIELD_MAP['series_index']]))))
|
fmt_sidx(float(record[FIELD_MAP['series_index']]))))
|
||||||
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
||||||
mimetype = guess_type('dummy.'+fmt)[0]
|
mimetype = guess_type('dummy.'+fmt)[0]
|
||||||
books.append(self.STANZA_ENTRY.generate(
|
if sortby == "byauthor":
|
||||||
|
if authors and authors not in author_list:
|
||||||
|
author_list.append(authors)
|
||||||
|
books.append(self.STANZA_AUTHOR_ENTRY.generate(
|
||||||
|
authors=authors,
|
||||||
|
record=record, FM=FIELD_MAP,
|
||||||
|
port=self.opts.port,
|
||||||
|
extra=''.join(extra),
|
||||||
|
mimetype=mimetype,
|
||||||
|
fmt=fmt,
|
||||||
|
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5]),
|
||||||
|
).render('xml').decode('utf8'))
|
||||||
|
else:
|
||||||
|
books.append(self.STANZA_ENTRY.generate(
|
||||||
authors=authors,
|
authors=authors,
|
||||||
record=record, FM=FIELD_MAP,
|
record=record, FM=FIELD_MAP,
|
||||||
port=self.opts.port,
|
port=self.opts.port,
|
||||||
@ -434,7 +462,7 @@ class LibraryServer(object):
|
|||||||
'The / URL'
|
'The / URL'
|
||||||
want_opds = cherrypy.request.headers.get('Stanza-Device-Name', 919) != \
|
want_opds = cherrypy.request.headers.get('Stanza-Device-Name', 919) != \
|
||||||
919 or cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919
|
919 or cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919
|
||||||
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None)) if want_opds else self.static('index.html')
|
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None)) if want_opds else self.static('index.html')
|
||||||
|
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
|
@ -130,11 +130,17 @@ class DBThread(Thread):
|
|||||||
if func == self.CLOSE:
|
if func == self.CLOSE:
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
break
|
break
|
||||||
func = getattr(self.conn, func)
|
if func == 'dump':
|
||||||
try:
|
try:
|
||||||
ok, res = True, func(*args, **kwargs)
|
ok, res = True, '\n'.join(self.conn.iterdump())
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
ok, res = False, (err, traceback.format_exc())
|
ok, res = False, (err, traceback.format_exc())
|
||||||
|
else:
|
||||||
|
func = getattr(self.conn, func)
|
||||||
|
try:
|
||||||
|
ok, res = True, func(*args, **kwargs)
|
||||||
|
except Exception, err:
|
||||||
|
ok, res = False, (err, traceback.format_exc())
|
||||||
self.results.put((ok, res))
|
self.results.put((ok, res))
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
self.unhandled_error = (err, traceback.format_exc())
|
self.unhandled_error = (err, traceback.format_exc())
|
||||||
@ -197,6 +203,9 @@ class ConnectionProxy(object):
|
|||||||
@proxy
|
@proxy
|
||||||
def cursor(self): pass
|
def cursor(self): pass
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
def dump(self): pass
|
||||||
|
|
||||||
def connect(dbpath, row_factory=None):
|
def connect(dbpath, row_factory=None):
|
||||||
conn = ConnectionProxy(DBThread(dbpath, row_factory))
|
conn = ConnectionProxy(DBThread(dbpath, row_factory))
|
||||||
conn.proxy.start()
|
conn.proxy.start()
|
||||||
|
@ -29,6 +29,20 @@ html:
|
|||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The HTML pages are in .build/html."
|
@echo "Build finished. The HTML pages are in .build/html."
|
||||||
|
|
||||||
|
qthelp:
|
||||||
|
mkdir -p .build/qthelp .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b customqt $(ALLSPHINXOPTS) .build/qthelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished."
|
||||||
|
|
||||||
|
epub:
|
||||||
|
mkdir -p .build/qthelp .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub
|
||||||
|
@echo
|
||||||
|
@echo "Build finished."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
web:
|
web:
|
||||||
mkdir -p .build/web .build/doctrees
|
mkdir -p .build/web .build/doctrees
|
||||||
$(SPHINXBUILD) -b web $(ALLSPHINXOPTS) .build/web
|
$(SPHINXBUILD) -b web $(ALLSPHINXOPTS) .build/web
|
||||||
|
@ -91,12 +91,15 @@ html_last_updated_fmt = '%b %d, %Y'
|
|||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
html_use_smartypants = True
|
||||||
|
|
||||||
# Overall title of the documentation
|
# Overall title of the documentation
|
||||||
html_title = 'calibre User Manual'
|
html_title = 'calibre User Manual'
|
||||||
html_short_title = 'Start'
|
html_short_title = 'Start'
|
||||||
html_logo = 'resources/logo.png'
|
html_logo = 'resources/logo.png'
|
||||||
|
epub_titlepage = 'resources/titlepage.html'
|
||||||
|
epub_logo = 'resources/logo.png'
|
||||||
|
epub_author = 'Kovid Goyal'
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
#html_sidebars = {}
|
||||||
@ -105,8 +108,8 @@ html_logo = 'resources/logo.png'
|
|||||||
# template names.
|
# template names.
|
||||||
#html_additional_pages = {}
|
#html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
html_use_modindex = False
|
||||||
# html_use_modindex = True
|
html_use_index = False
|
||||||
|
|
||||||
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
# If true, the reST sources are included in the HTML build as _sources/<name>.
|
||||||
html_copy_source = False
|
html_copy_source = False
|
||||||
@ -116,6 +119,8 @@ htmlhelp_basename = 'calibredoc'
|
|||||||
|
|
||||||
html_use_opensearch = 'http://calibre.kovidgoyal.net/user_manual'
|
html_use_opensearch = 'http://calibre.kovidgoyal.net/user_manual'
|
||||||
|
|
||||||
|
html_show_sphinx = False
|
||||||
|
|
||||||
# Options for LaTeX output
|
# Options for LaTeX output
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
|
||||||
|
@ -6,156 +6,143 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import sys, os, inspect, re, textwrap
|
import sys, os, inspect, re, textwrap
|
||||||
|
|
||||||
from sphinx.builder import StandaloneHTMLBuilder
|
from sphinx.builder import StandaloneHTMLBuilder
|
||||||
|
from qthelp import QtHelpBuilder
|
||||||
|
from epub import EPUBHelpBuilder
|
||||||
from sphinx.util import rpartition
|
from sphinx.util import rpartition
|
||||||
from sphinx.util.console import bold
|
from sphinx.util.console import bold
|
||||||
from sphinx.ext.autodoc import prepare_docstring
|
from sphinx.ext.autodoc import prepare_docstring
|
||||||
from docutils.statemachine import ViewList
|
from docutils.statemachine import ViewList
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
|
|
||||||
from genshi.template import OldTextTemplate as TextTemplate
|
|
||||||
sys.path.append(os.path.abspath('../../../'))
|
sys.path.append(os.path.abspath('../../../'))
|
||||||
from calibre.linux import entry_points
|
from calibre.linux import entry_points
|
||||||
|
|
||||||
class CustomBuilder(StandaloneHTMLBuilder):
|
class CustomBuilder(StandaloneHTMLBuilder):
|
||||||
name = 'custom'
|
name = 'custom'
|
||||||
|
|
||||||
|
class CustomQtBuild(QtHelpBuilder):
|
||||||
|
name = 'customqt'
|
||||||
|
|
||||||
def substitute(app, doctree):
|
def substitute(app, doctree):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
CLI_INDEX = '''\
|
CLI_INDEX='''
|
||||||
.. include:: ../global.rst
|
.. include:: ../global.rst
|
||||||
||
|
|
||||||
.. _cli:
|
.. _cli:
|
||||||
||
|
|
||||||
||
|
|
||||||
Command Line Interface
|
Command Line Interface
|
||||||
==========================
|
==========================
|
||||||
||
|
|
||||||
.. image:: ../images/cli.png
|
.. image:: ../images/cli.png
|
||||||
||
|
|
||||||
||
|
|
||||||
Documented Commands
|
Documented Commands
|
||||||
--------------------
|
--------------------
|
||||||
||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
||
|
|
||||||
#for cmd, parser in documented_commands
|
{documented}
|
||||||
$cmd
|
|
||||||
#end
|
|
||||||
||
|
|
||||||
Undocumented Commands
|
Undocumented Commands
|
||||||
-------------------------
|
-------------------------
|
||||||
||
|
|
||||||
#for cmd in undocumented_commands
|
{undocumented}
|
||||||
* ${cmd}
|
|
||||||
||
|
You can see usage for undocumented commands by executing them without arguments
|
||||||
#end
|
in a terminal.
|
||||||
||
|
|
||||||
You can see usage for undocumented commands by executing them without arguments in a terminal
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
CLI_CMD=r'''
|
CLI_PREAMBLE='''\
|
||||||
.. include:: ../global.rst
|
.. include:: ../global.rst
|
||||||
||
|
|
||||||
.. _$cmd:
|
.. _{cmd}:
|
||||||
||
|
|
||||||
$cmd
|
{cmd}
|
||||||
====================================================================
|
===================================================================
|
||||||
||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
||
|
|
||||||
$cmdline
|
{cmdline}
|
||||||
||
|
|
||||||
#for line in usage
|
{usage}
|
||||||
#choose
|
|
||||||
#when len(line) > 0
|
|
||||||
$line
|
|
||||||
#end
|
|
||||||
#otherwise
|
|
||||||
||
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
||
|
|
||||||
'''
|
|
||||||
CLI_GROUPS=r'''
|
|
||||||
[options]
|
|
||||||
------------
|
|
||||||
||
|
|
||||||
#def option(opt)
|
|
||||||
:option:`${opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')}`
|
|
||||||
#end
|
|
||||||
#for title, desc, options in groups
|
|
||||||
#if title
|
|
||||||
$title
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
||
|
|
||||||
#end
|
|
||||||
#if desc
|
|
||||||
$desc
|
|
||||||
||
|
|
||||||
#end
|
|
||||||
#for opt in options
|
|
||||||
${option(opt)}
|
|
||||||
${opt.help.replace('\n', ' ').replace('*', '\\*').replace('%default', str(opt.default)) if opt.help else ''}
|
|
||||||
||
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EBOOK_CONVERT = CLI_CMD + r'''
|
|
||||||
$groups
|
|
||||||
'''
|
|
||||||
|
|
||||||
CLI_CMD += CLI_GROUPS
|
def generate_ebook_convert_help(preamble, info):
|
||||||
|
|
||||||
|
|
||||||
def generate_ebook_convert_help():
|
|
||||||
from calibre.ebooks.conversion.cli import create_option_parser
|
from calibre.ebooks.conversion.cli import create_option_parser
|
||||||
from calibre.customize.ui import input_format_plugins, output_format_plugins
|
from calibre.customize.ui import input_format_plugins, output_format_plugins
|
||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
ans = textwrap.dedent('''
|
preamble = re.sub(r'http.*\.html', ':ref:`conversion`', preamble)
|
||||||
|
raw = preamble + textwrap.dedent('''
|
||||||
Since the options supported by ebook-convert vary depending on both the
|
Since the options supported by ebook-convert vary depending on both the
|
||||||
input and the output formats, the various combinations are listed below:
|
input and the output formats, the various combinations are listed below:
|
||||||
|
|
||||||
''')
|
''')
|
||||||
c = 0
|
|
||||||
sections = []
|
sections = []
|
||||||
toc = {}
|
toc = {}
|
||||||
for ip in input_format_plugins():
|
sec_templ = textwrap.dedent('''\
|
||||||
toc[ip.name] = []
|
.. include:: ../global.rst
|
||||||
|
|
||||||
|
{0}
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
.. contents:: Contents
|
||||||
|
:depth: 1
|
||||||
|
:local:
|
||||||
|
|
||||||
|
''')
|
||||||
|
for i, ip in enumerate(input_format_plugins()):
|
||||||
|
path = os.path.join('cli', 'ebook-convert-%d.rst'%i)
|
||||||
|
sraw = sec_templ.format(ip.name)
|
||||||
|
toc[ip.name] = 'ebook-convert-%d'%i
|
||||||
for op in output_format_plugins():
|
for op in output_format_plugins():
|
||||||
c += 1
|
|
||||||
idr = 'ebook-convert-sec-'+str(c)
|
|
||||||
title = ip.name + ' to ' + op.name
|
title = ip.name + ' to ' + op.name
|
||||||
section = '.. _'+idr+':||||'
|
|
||||||
section += title+'||'+\
|
|
||||||
'-------------------------------------------------------'
|
|
||||||
toc[ip.name].append([idr, op.name])
|
|
||||||
parser, plumber = create_option_parser(['ebook-convert',
|
parser, plumber = create_option_parser(['ebook-convert',
|
||||||
'dummyi.'+list(ip.file_types)[0],
|
'dummyi.'+list(ip.file_types)[0],
|
||||||
'dummyo.'+op.file_type, '-h'], default_log)
|
'dummyo.'+op.file_type, '-h'], default_log)
|
||||||
|
cmd = 'ebook-convert '+list(ip.file_types)[0]+' '+op.file_type
|
||||||
groups = [(None, None, parser.option_list)]
|
groups = [(None, None, parser.option_list)]
|
||||||
for grp in parser.option_groups:
|
for grp in parser.option_groups:
|
||||||
groups.append((grp.title, grp.description, grp.option_list))
|
groups.append((grp.title, grp.description, grp.option_list))
|
||||||
template = str(CLI_GROUPS)
|
options = '\n'.join(render_options(cmd, groups, False))
|
||||||
template = TextTemplate(template[template.find('||'):])
|
sraw += title+'\n------------------------------------------------------\n\n'
|
||||||
section += template.generate(groups=groups).render()
|
sraw += options + '\n\n'
|
||||||
|
update_cli_doc(os.path.join('cli', toc[ip.name]+'.rst'), sraw, info)
|
||||||
|
|
||||||
sections.append(section)
|
toct = '\n\n.. toctree::\n :maxdepth: 2\n\n'
|
||||||
|
|
||||||
toct = '||||'
|
|
||||||
for ip in sorted(toc):
|
for ip in sorted(toc):
|
||||||
toct += ' * '+ip+'||||'
|
toct += ' ' + toc[ip]+'\n'
|
||||||
for idr, name in toc[ip]:
|
|
||||||
toct += ' * :ref:`'+name +' <'+idr+'>`||'
|
|
||||||
toct += '||'
|
|
||||||
|
|
||||||
ans += toct+'||||'+'||||'.join(sections)
|
raw += toct+'\n\n'
|
||||||
|
update_cli_doc(os.path.join('cli', 'ebook-convert.rst'), raw, info)
|
||||||
|
|
||||||
return ans
|
def update_cli_doc(path, raw, info):
|
||||||
|
if isinstance(raw, unicode):
|
||||||
|
raw = raw.encode('utf-8')
|
||||||
|
if not os.path.exists(path) or open(path, 'rb').read() != raw:
|
||||||
|
info('creating '+os.path.splitext(os.path.basename(path))[0])
|
||||||
|
open(path, 'wb').write(raw)
|
||||||
|
|
||||||
|
def render_options(cmd, groups, options_header=True):
|
||||||
|
lines = []
|
||||||
|
if options_header:
|
||||||
|
lines = ['[options]', '-'*15, '']
|
||||||
|
lines += ['.. program:: '+cmd, '']
|
||||||
|
for title, desc, options in groups:
|
||||||
|
if title:
|
||||||
|
lines.extend([title, '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'])
|
||||||
|
lines.append('')
|
||||||
|
if desc:
|
||||||
|
lines.extend([desc, ''])
|
||||||
|
for opt in options:
|
||||||
|
help = opt.help if opt.help else ''
|
||||||
|
help = help.replace('\n', ' ').replace('*', '\\*').replace('%default', str(opt.default))
|
||||||
|
opt = opt.get_opt_string() + ((', '+', '.join(opt._short_opts)) if opt._short_opts else '')
|
||||||
|
opt = '.. cmdoption:: '+opt
|
||||||
|
lines.extend([opt, '', ' '+help, ''])
|
||||||
|
return lines
|
||||||
|
|
||||||
def cli_docs(app):
|
def cli_docs(app):
|
||||||
info = app.builder.info
|
info = app.builder.info
|
||||||
@ -175,36 +162,32 @@ def cli_docs(app):
|
|||||||
documented_cmds.sort(cmp=lambda x, y: cmp(x[0], y[0]))
|
documented_cmds.sort(cmp=lambda x, y: cmp(x[0], y[0]))
|
||||||
undocumented_cmds.sort()
|
undocumented_cmds.sort()
|
||||||
|
|
||||||
templ = TextTemplate(CLI_INDEX)
|
documented = [' '*4 + cmd[0] for cmd in documented_cmds]
|
||||||
raw = templ.generate(documented_commands=documented_cmds,
|
undocumented = [' * ' + cmd for cmd in undocumented_cmds]
|
||||||
undocumented_commands=undocumented_cmds).render()
|
|
||||||
raw = raw.replace('||', '\n')
|
raw = CLI_INDEX.format(documented='\n'.join(documented),
|
||||||
|
undocumented='\n'.join(undocumented))
|
||||||
if not os.path.exists('cli'):
|
if not os.path.exists('cli'):
|
||||||
os.makedirs('cli')
|
os.makedirs('cli')
|
||||||
if not os.path.exists(os.path.join('cli', 'global.rst')):
|
update_cli_doc(os.path.join('cli', 'cli-index.rst'), raw, info)
|
||||||
os.link('global.rst', os.path.join('cli', 'global.rst'))
|
|
||||||
if not os.path.exists(os.path.join('cli', 'cli-index.rst')):
|
|
||||||
info(bold('creating cli-index...'))
|
|
||||||
open(os.path.join('cli', 'cli-index.rst'), 'wb').write(raw)
|
|
||||||
|
|
||||||
templ = TextTemplate(CLI_CMD)
|
|
||||||
for cmd, parser in documented_cmds:
|
for cmd, parser in documented_cmds:
|
||||||
usage = [i for i in parser.usage.replace('%prog', cmd).splitlines()]
|
usage = [i for i in parser.usage.replace('%prog', cmd).splitlines()]
|
||||||
cmdline = usage[0]
|
cmdline = usage[0]
|
||||||
usage = usage[1:]
|
usage = usage[1:]
|
||||||
usage = [i.replace(cmd, ':command:`%s`'%cmd) for i in usage]
|
usage = [i.replace(cmd, ':command:`%s`'%cmd) for i in usage]
|
||||||
groups = [(None, None, parser.option_list)]
|
usage = '\n'.join(usage)
|
||||||
for grp in parser.option_groups:
|
preamble = CLI_PREAMBLE.format(cmd=cmd, cmdline=cmdline, usage=usage)
|
||||||
groups.append((grp.title, grp.description, grp.option_list))
|
|
||||||
if cmd == 'ebook-convert':
|
if cmd == 'ebook-convert':
|
||||||
groups = generate_ebook_convert_help()
|
generate_ebook_convert_help(preamble, info)
|
||||||
templ = TextTemplate(EBOOK_CONVERT)
|
else:
|
||||||
raw = templ.generate(cmd=cmd, cmdline=cmdline, usage=usage, groups=groups).render()
|
groups = [(None, None, parser.option_list)]
|
||||||
raw = raw.replace('||', '\n').replace('<', '<').replace('>', '>')
|
for grp in parser.option_groups:
|
||||||
if not os.path.exists(os.path.join('cli', cmd+'.rst')):
|
groups.append((grp.title, grp.description, grp.option_list))
|
||||||
info(bold('creating docs for %s...'%cmd))
|
raw = preamble
|
||||||
open(os.path.join('cli', cmd+'.rst'), 'wb').write(raw)
|
lines = render_options(cmd, groups)
|
||||||
|
raw += '\n'+'\n'.join(lines)
|
||||||
|
update_cli_doc(os.path.join('cli', cmd+'.rst'), raw, info)
|
||||||
|
|
||||||
def auto_member(dirname, arguments, options, content, lineno,
|
def auto_member(dirname, arguments, options, content, lineno,
|
||||||
content_offset, block_text, state, state_machine):
|
content_offset, block_text, state, state_machine):
|
||||||
@ -258,7 +241,12 @@ def auto_member(dirname, arguments, options, content, lineno,
|
|||||||
return list(node)
|
return list(node)
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
|
app.add_config_value('epub_titlepage', None, False)
|
||||||
|
app.add_config_value('epub_author', '', False)
|
||||||
|
app.add_config_value('epub_logo', None, False)
|
||||||
app.add_builder(CustomBuilder)
|
app.add_builder(CustomBuilder)
|
||||||
|
app.add_builder(CustomQtBuild)
|
||||||
|
app.add_builder(EPUBHelpBuilder)
|
||||||
app.add_directive('automember', auto_member, 1, (1, 0, 1))
|
app.add_directive('automember', auto_member, 1, (1, 0, 1))
|
||||||
app.connect('doctree-read', substitute)
|
app.connect('doctree-read', substitute)
|
||||||
app.connect('builder-inited', cli_docs)
|
app.connect('builder-inited', cli_docs)
|
||||||
|
@ -112,3 +112,8 @@ Metadata plugins
|
|||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Metadata plugins add the ability to read/write metadata from ebook files to |app|. See :ref:`pluginsMetadataPlugin` for details.
|
Metadata plugins add the ability to read/write metadata from ebook files to |app|. See :ref:`pluginsMetadataPlugin` for details.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
plugins
|
||||||
|
280
src/calibre/manual/epub.py
Normal file
280
src/calibre/manual/epub.py
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os, mimetypes, uuid, shutil
|
||||||
|
from datetime import datetime
|
||||||
|
from docutils import nodes
|
||||||
|
from xml.sax.saxutils import escape, quoteattr
|
||||||
|
from urlparse import urldefrag
|
||||||
|
from zipfile import ZipFile, ZIP_STORED, ZipInfo
|
||||||
|
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||||
|
|
||||||
|
NCX = '''\
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ncx version="2005-1"
|
||||||
|
xml:lang="en"
|
||||||
|
xmlns="http://www.daisy.org/z3986/2005/ncx/"
|
||||||
|
xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"
|
||||||
|
>
|
||||||
|
<head>
|
||||||
|
<meta name="dtb:uid" content="{uid}"/>
|
||||||
|
<meta name="dtb:depth" content="{depth}"/>
|
||||||
|
<meta name="dtb:generator" content="sphinx"/>
|
||||||
|
<meta name="dtb:totalPageCount" content="0"/>
|
||||||
|
<meta name="dtb:maxPageNumber" content="0"/>
|
||||||
|
</head>
|
||||||
|
<docTitle><text>Table of Contents</text></docTitle>
|
||||||
|
<navMap>
|
||||||
|
{navpoints}
|
||||||
|
</navMap>
|
||||||
|
</ncx>
|
||||||
|
'''
|
||||||
|
|
||||||
|
OPF = '''\
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<package version="2.0"
|
||||||
|
xmlns="http://www.idpf.org/2007/opf"
|
||||||
|
unique-identifier="sphinx_id"
|
||||||
|
>
|
||||||
|
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
|
||||||
|
<dc:title>{title}</dc:title>
|
||||||
|
<dc:creator opf:role="aut">{author}</dc:creator>
|
||||||
|
<dc:contributor opf:role="bkp">Sphinx</dc:contributor>
|
||||||
|
<dc:identifier opf:scheme="sphinx" id="sphinx_id">{uid}</dc:identifier>
|
||||||
|
<dc:date>{date}</dc:date>
|
||||||
|
<meta name="calibre:publication_type" content="sphinx_manual" />
|
||||||
|
</metadata>
|
||||||
|
<manifest>
|
||||||
|
{manifest}
|
||||||
|
</manifest>
|
||||||
|
<spine toc="ncx">
|
||||||
|
{spine}
|
||||||
|
</spine>
|
||||||
|
<guide>
|
||||||
|
{guide}
|
||||||
|
</guide>
|
||||||
|
</package>
|
||||||
|
'''
|
||||||
|
|
||||||
|
CONTAINER='''\
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||||
|
<rootfiles>
|
||||||
|
<rootfile full-path="{0}" media-type="application/oebps-package+xml"/>
|
||||||
|
</rootfiles>
|
||||||
|
</container>
|
||||||
|
'''
|
||||||
|
class TOC(list):
|
||||||
|
|
||||||
|
def __init__(self, title=None, href=None):
|
||||||
|
list.__init__(self)
|
||||||
|
self.title, self.href = title, href
|
||||||
|
|
||||||
|
def create_child(self, title, href):
|
||||||
|
self.append(TOC(title, href))
|
||||||
|
return self[-1]
|
||||||
|
|
||||||
|
def depth(self):
|
||||||
|
try:
|
||||||
|
return max(node.depth() for node in self)+1
|
||||||
|
except ValueError:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
class EPUBHelpBuilder(StandaloneHTMLBuilder):
|
||||||
|
"""
|
||||||
|
Builder that also outputs Qt help project, contents and index files.
|
||||||
|
"""
|
||||||
|
name = 'epub'
|
||||||
|
|
||||||
|
# don't copy the reST source
|
||||||
|
copysource = False
|
||||||
|
|
||||||
|
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||||
|
'image/jpeg']
|
||||||
|
|
||||||
|
# don't add links
|
||||||
|
add_permalinks = False
|
||||||
|
# don't add sidebar etc.
|
||||||
|
embedded = True
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
StandaloneHTMLBuilder.init(self)
|
||||||
|
self.out_suffix = '.html'
|
||||||
|
self.link_suffix = '.html'
|
||||||
|
self.html_outdir = self.outdir = os.path.join(self.outdir, 'src')
|
||||||
|
self.conf = self.config
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
StandaloneHTMLBuilder.finish(self)
|
||||||
|
self.create_titlepage()
|
||||||
|
self.outdir = os.path.dirname(self.outdir)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(self.html_outdir)
|
||||||
|
try:
|
||||||
|
self.generate_manifest()
|
||||||
|
self.generate_toc()
|
||||||
|
self.render_opf()
|
||||||
|
self.render_epub()
|
||||||
|
finally:
|
||||||
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
def render_epub(self):
|
||||||
|
container = CONTAINER.format('content.opf')
|
||||||
|
path = os.path.abspath('..'+os.sep+self.conf.project+'.epub')
|
||||||
|
zf = ZipFile(path, 'w')
|
||||||
|
zi = ZipInfo('mimetype')
|
||||||
|
zi.compress_type = ZIP_STORED
|
||||||
|
zf.writestr(zi, 'application/epub+zip')
|
||||||
|
zf.writestr('META-INF/container.xml', container)
|
||||||
|
for url in self.manifest:
|
||||||
|
fp = os.path.join(self.html_outdir, *url.split('/'))
|
||||||
|
zf.write(fp, url)
|
||||||
|
zf.close()
|
||||||
|
self.info('EPUB created at: '+path)
|
||||||
|
|
||||||
|
|
||||||
|
def render_opf(self):
|
||||||
|
manifest = []
|
||||||
|
for href in self.manifest:
|
||||||
|
mt, id = self.manifest[href]
|
||||||
|
manifest.append(' '*8 + '<item id=%s href=%s media-type=%s />'%\
|
||||||
|
tuple(map(quoteattr, (id, href, mt))))
|
||||||
|
manifest = '\n'.join(manifest)
|
||||||
|
spine = [' '*8+'<itemref idref=%s />'%quoteattr(x) for x in self.spine]
|
||||||
|
spine = '\n'.join(spine)
|
||||||
|
guide = ''
|
||||||
|
if self.conf.epub_titlepage:
|
||||||
|
guide = ' '*8 + '<reference type="cover" href="_static/titlepage.html" />'
|
||||||
|
|
||||||
|
opf = OPF.format(title=escape(self.conf.html_title),
|
||||||
|
author=escape(self.conf.epub_author), uid=str(uuid.uuid4()),
|
||||||
|
date=datetime.now().isoformat(), manifest=manifest, spine=spine,
|
||||||
|
guide=guide)
|
||||||
|
open('content.opf', 'wb').write(opf)
|
||||||
|
self.manifest['content.opf'] = ('application/oebps-package+xml', 'opf')
|
||||||
|
|
||||||
|
def create_titlepage(self):
|
||||||
|
if self.conf.epub_titlepage:
|
||||||
|
img = ''
|
||||||
|
if self.conf.epub_logo:
|
||||||
|
img = '_static/epub_logo'+os.path.splitext(self.conf.epub_logo)[1]
|
||||||
|
shutil.copyfile(self.conf.epub_logo,
|
||||||
|
os.path.join(self.html_outdir, *img.split('/')))
|
||||||
|
raw = open(self.conf.epub_titlepage, 'rb').read()
|
||||||
|
raw = raw%dict(title=self.conf.html_title,
|
||||||
|
version=self.conf.version,
|
||||||
|
img=img.split('/')[-1],
|
||||||
|
author=self.conf.epub_author)
|
||||||
|
open(os.path.join(self.html_outdir, '_static', 'titlepage.html'), 'wb').write(raw)
|
||||||
|
|
||||||
|
def generate_manifest(self):
|
||||||
|
self.manifest = {}
|
||||||
|
id = 1
|
||||||
|
for dirpath, dirnames, filenames in os.walk('.'):
|
||||||
|
for fname in filenames:
|
||||||
|
if fname == '.buildinfo':
|
||||||
|
continue
|
||||||
|
fpath = os.path.abspath(os.path.join(dirpath, fname))
|
||||||
|
url = os.path.relpath(fpath).replace(os.sep, '/')
|
||||||
|
self.manifest[url] = mimetypes.guess_type(url, False)[0]
|
||||||
|
if self.manifest[url] is None:
|
||||||
|
self.warn('Unknown mimetype for: ' + url)
|
||||||
|
self.manifest[url] = 'application/octet-stream'
|
||||||
|
if self.manifest[url] == 'text/html':
|
||||||
|
self.manifest[url] = 'application/xhtml+xml'
|
||||||
|
self.manifest[url] = (self.manifest[url], 'id'+str(id))
|
||||||
|
id += 1
|
||||||
|
|
||||||
|
def isdocnode(self, node):
|
||||||
|
if not isinstance(node, nodes.list_item):
|
||||||
|
return False
|
||||||
|
if len(node.children) != 2:
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[0], addnodes.compact_paragraph):
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[0][0], nodes.reference):
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[1], nodes.bullet_list):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def generate_toc(self):
|
||||||
|
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
|
||||||
|
prune_toctrees=False)
|
||||||
|
istoctree = lambda node: (
|
||||||
|
isinstance(node, addnodes.compact_paragraph)
|
||||||
|
and node.has_key('toctree'))
|
||||||
|
toc = TOC()
|
||||||
|
for node in tocdoc.traverse(istoctree):
|
||||||
|
self.extend_toc(toc, node)
|
||||||
|
self._parts = []
|
||||||
|
self._po = 0
|
||||||
|
self._po_map = {}
|
||||||
|
self.spine_map = {}
|
||||||
|
self.spine = []
|
||||||
|
self.render_toc(toc)
|
||||||
|
navpoints = '\n'.join(self._parts).strip()
|
||||||
|
ncx = NCX.format(uid=str(uuid.uuid4()), depth=toc.depth(),
|
||||||
|
navpoints=navpoints)
|
||||||
|
open('toc.ncx', 'wb').write(ncx)
|
||||||
|
self.manifest['toc.ncx'] = ('application/x-dtbncx+xml', 'ncx')
|
||||||
|
self.spine.insert(0, self.manifest[self.conf.master_doc+'.html'][1])
|
||||||
|
if self.conf.epub_titlepage:
|
||||||
|
self.spine.insert(0, self.manifest['_static/titlepage.html'][1])
|
||||||
|
|
||||||
|
def add_to_spine(self, href):
|
||||||
|
href = urldefrag(href)[0]
|
||||||
|
if href not in self.spine_map:
|
||||||
|
for url in self.manifest:
|
||||||
|
if url == href:
|
||||||
|
self.spine_map[href]= self.manifest[url][1]
|
||||||
|
self.spine.append(self.spine_map[href])
|
||||||
|
|
||||||
|
def render_toc(self, toc, level=2):
|
||||||
|
for child in toc:
|
||||||
|
if child.title and child.href:
|
||||||
|
href = child.href
|
||||||
|
self.add_to_spine(href)
|
||||||
|
title = escape(child.title)
|
||||||
|
if isinstance(title, unicode):
|
||||||
|
title = title.encode('utf-8')
|
||||||
|
if child.href in self._po_map:
|
||||||
|
po = self._po_map[child.href]
|
||||||
|
else:
|
||||||
|
self._po += 1
|
||||||
|
po = self._po
|
||||||
|
self._parts.append(' '*(level*4)+
|
||||||
|
'<navPoint id="%s" playOrder="%d">'%(uuid.uuid4(),
|
||||||
|
po))
|
||||||
|
self._parts.append(' '*((level+1)*4)+
|
||||||
|
'<navLabel><text>%s</text></navLabel>'%title)
|
||||||
|
self._parts.append(' '*((level+1)*4)+
|
||||||
|
'<content src=%s />'%quoteattr(href))
|
||||||
|
self.render_toc(child, level+1)
|
||||||
|
self._parts.append(' '*(level*4)+'</navPoint>')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extend_toc(self, toc, node):
|
||||||
|
if self.isdocnode(node):
|
||||||
|
refnode = node.children[0][0]
|
||||||
|
parent = toc.create_child(refnode.astext(), refnode['refuri'])
|
||||||
|
for subnode in node.children[1]:
|
||||||
|
self.extend_toc(parent, subnode)
|
||||||
|
elif isinstance(node, (nodes.list_item, nodes.bullet_list,
|
||||||
|
addnodes.compact_paragraph)):
|
||||||
|
for subnode in node:
|
||||||
|
self.extend_toc(toc, subnode)
|
||||||
|
elif isinstance(node, nodes.reference):
|
||||||
|
parent = toc.create_child(node.astext(), node['refuri'])
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ There are two aspects to this problem:
|
|||||||
|
|
||||||
How do I use some of the advanced features of the conversion tools?
|
How do I use some of the advanced features of the conversion tools?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
You can get help on any individual feature of the converters by mousing over it in the GUI or running ``html2lrf --help`` at a terminal. A good place to start is to look at the following demo files that demonstrate some of the advanced features:
|
You can get help on any individual feature of the converters by mousing over it in the GUI or running ``ebook-convert dummy.html .epub -h`` at a terminal. A good place to start is to look at the following demo files that demonstrate some of the advanced features:
|
||||||
* `html-demo.zip <http://calibre.kovidgoyal.net/downloads/html-demo.zip>`_
|
* `html-demo.zip <http://calibre.kovidgoyal.net/downloads/html-demo.zip>`_
|
||||||
* `txt-demo.zip <http://calibre.kovidgoyal.net/downloads/txt-demo.zip>`_
|
* `txt-demo.zip <http://calibre.kovidgoyal.net/downloads/txt-demo.zip>`_
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ Device Integration
|
|||||||
|
|
||||||
What devices does |app| support?
|
What devices does |app| support?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Adroid phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||||
|
|
||||||
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
|
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -121,7 +121,7 @@ Library Management
|
|||||||
|
|
||||||
What formats does |app| read metadata from?
|
What formats does |app| read metadata from?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|app| reads metadata from the following formats: LRF, PDF, LIT, RTF, OPF, MOBI, PRC, EPUB, FB2, IMP, RB, HTML. In addition it can write metadata to: LRF, RTF, OPF
|
|app| reads metadata from the following formats: LRF, PDF, LIT, RTF, OPF, MOBI, PRC, EPUB, FB2, IMP, RB, HTML. In addition it can write metadata to: LRF, RTF, OPF, EPUB, PDF, MOBI
|
||||||
|
|
||||||
Where are the book files stored?
|
Where are the book files stored?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -5,18 +5,22 @@
|
|||||||
|
|
||||||
|app| is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading. It is cross platform, running on Linux, Windows and OS X.
|
|app| is an e-book library manager. It can view, convert and catalog e-books in most of the major e-book formats. It can also talk to a few e-book reader devices. It can go out to the internet and fetch metadata for your books. It can download newspapers and convert them into e-books for convenient reading. It is cross platform, running on Linux, Windows and OS X.
|
||||||
|
|
||||||
So you've just started |app|. What do you do now? Well, before |app| can do anything with your ebooks, it first has to know about them. So drag and drop a few ebook files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this:
|
So you've just started |app|. What do you do now? Well, before |app| can do anything with your ebooks, it first has to know about them. So drag and drop a few e-book files into |app|, or click the "Add books" button and browse for the ebooks you want to work with. Once you've added the books, they will show up in the main view looking something like this:
|
||||||
|
|
||||||
.. image:: images/added_books.png
|
.. image:: images/added_books.png
|
||||||
|
|
||||||
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. For the SONY Reader that's the LRF format. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little hourglass in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click to "View" button to read the book.
|
Once you've admired the list of books you just added to your heart's content, you'll probably want to actually read one. In order to do that you'll have to convert the book to a format your reader understands. When first running |app|, the Welcome Wizard starts and it will have setup calibre for your reader device. Conversion is a breeze, just select the book you want to convert, and click the "Convert E-book" button. Ignore all the options for now and just click "OK". The little hourglass in the bottom right corner will start spinning. Once it's finished spinning, your converted book is ready. Click the "View" button to read the book.
|
||||||
|
|
||||||
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the hourglass stops spinning again, disconnect your reader and read away!
|
Now if you want to read the book on your reader, just connect it to the computer, wait till calibre detects it (10-20secs) and then click the "Send to device" button. Once the hourglass stops spinning again, disconnect your reader and read away! If you didn't convert the book in the previous step, |app| will auto convert it to the format your reader device understands.
|
||||||
|
|
||||||
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`.
|
To get started with more advanced usage, you should read about the :ref:`Graphical User Interface <gui>`. For even more power and versatility, learn the :ref:`Command Line Interface <cli>`.
|
||||||
|
|
||||||
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
|
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
|
||||||
|
|
||||||
|
.. only:: html and online
|
||||||
|
|
||||||
|
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_. Because the User Manual uses advanced formatting, it is only suitable for use with the |app| e-book viewer.
|
||||||
|
|
||||||
Sections
|
Sections
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -25,22 +29,12 @@ Sections
|
|||||||
|
|
||||||
gui
|
gui
|
||||||
news
|
news
|
||||||
cli/cli-index
|
|
||||||
conversion
|
conversion
|
||||||
metadata
|
metadata
|
||||||
faq
|
faq
|
||||||
xpath
|
xpath
|
||||||
customize
|
customize
|
||||||
|
cli/cli-index
|
||||||
glossary
|
glossary
|
||||||
|
|
||||||
Convenience
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
:ref:`search`: Search the help
|
|
||||||
|
|
||||||
|
|
||||||
:ref:`genindex`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +101,8 @@ Pre/post processing of downloaded HTML
|
|||||||
|
|
||||||
.. automember:: BasicNewsRecipe.remove_tags_before
|
.. automember:: BasicNewsRecipe.remove_tags_before
|
||||||
|
|
||||||
|
.. automember:: BasicNewsRecipe.remove_attributes
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.keep_only_tags
|
.. automember:: BasicNewsRecipe.keep_only_tags
|
||||||
|
|
||||||
.. automember:: BasicNewsRecipe.preprocess_regexps
|
.. automember:: BasicNewsRecipe.preprocess_regexps
|
||||||
|
296
src/calibre/manual/qthelp.py
Normal file
296
src/calibre/manual/qthelp.py
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import cgi
|
||||||
|
import subprocess
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||||
|
|
||||||
|
_idpattern = re.compile(
|
||||||
|
r'(?P<title>.+) (\((?P<id>[\w\.]+)( (?P<descr>\w+))?\))$')
|
||||||
|
|
||||||
|
|
||||||
|
# Qt Help Collection Project (.qhcp).
|
||||||
|
# Is the input file for the help collection generator.
|
||||||
|
# It contains references to compressed help files which should be
|
||||||
|
# included in the collection.
|
||||||
|
# It may contain various other information for customizing Qt Assistant.
|
||||||
|
collection_template = '''\
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<QHelpCollectionProject version="1.0">
|
||||||
|
<docFiles>
|
||||||
|
<generate>
|
||||||
|
<file>
|
||||||
|
<input>%(outname)s.qhp</input>
|
||||||
|
<output>%(outname)s.qch</output>
|
||||||
|
</file>
|
||||||
|
</generate>
|
||||||
|
<register>
|
||||||
|
<file>%(outname)s.qch</file>
|
||||||
|
</register>
|
||||||
|
</docFiles>
|
||||||
|
<assistant>
|
||||||
|
<title>calibre User Manual</title>
|
||||||
|
<applicationIcon>_static/logo.png</applicationIcon>
|
||||||
|
<enableDocumentationManager>false</enableDocumentationManager>
|
||||||
|
<enableAddressBar visible="false">true</enableAddressBar>
|
||||||
|
<cacheDirectory>calibre/user_manual</cacheDirectory>
|
||||||
|
<aboutMenuText>
|
||||||
|
<text>About calibre</text>
|
||||||
|
</aboutMenuText>
|
||||||
|
<aboutDialog>
|
||||||
|
<file>about.txt</file>
|
||||||
|
<icon>_static/logo.png</icon>
|
||||||
|
</aboutDialog>
|
||||||
|
</assistant>
|
||||||
|
</QHelpCollectionProject>
|
||||||
|
'''
|
||||||
|
|
||||||
|
ABOUT='''\
|
||||||
|
calibre is the one stop solution for all your e-book needs. It was created
|
||||||
|
originally by Kovid Goyal, to help him manage his e-book collection and is now
|
||||||
|
very actively developed by an international community of e-book enthusiasts.
|
||||||
|
|
||||||
|
Its goal is to empower you, the user, to do whatever you like with the e-books
|
||||||
|
in your collection. You can convert them to many different formats, read them
|
||||||
|
on your computer, send them to many different devices, edit their metadata
|
||||||
|
and covers, etc.
|
||||||
|
|
||||||
|
calibre also allows you to download news from a variety of different sources all
|
||||||
|
over the internet and read conveniently in e-books form. In keeping with its
|
||||||
|
philosophy of empowering the user, it has a simple system to allow you to add
|
||||||
|
your own favorite news sources. In fact, most the builtin news sources in
|
||||||
|
calibre were originally contributed by users.
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Qt Help Project (.qhp)
|
||||||
|
# This is the input file for the help generator.
|
||||||
|
# It contains the table of contents, indices and references to the
|
||||||
|
# actual documentation files (*.html).
|
||||||
|
# In addition it defines a unique namespace for the documentation.
|
||||||
|
project_template = '''\
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<QtHelpProject version="1.0">
|
||||||
|
<namespace>%(outname)s.org.%(outname)s.%(nversion)s</namespace>
|
||||||
|
<virtualFolder>doc</virtualFolder>
|
||||||
|
<customFilter name="%(project)s %(version)s">
|
||||||
|
<filterAttribute>%(outname)s</filterAttribute>
|
||||||
|
<filterAttribute>%(version)s</filterAttribute>
|
||||||
|
</customFilter>
|
||||||
|
<filterSection>
|
||||||
|
<filterAttribute>%(outname)s</filterAttribute>
|
||||||
|
<filterAttribute>%(version)s</filterAttribute>
|
||||||
|
<toc>
|
||||||
|
<section title="%(title)s" ref="%(masterdoc)s.html">
|
||||||
|
%(sections)s
|
||||||
|
</section>
|
||||||
|
</toc>
|
||||||
|
<files>
|
||||||
|
%(files)s
|
||||||
|
</files>
|
||||||
|
</filterSection>
|
||||||
|
</QtHelpProject>
|
||||||
|
'''
|
||||||
|
|
||||||
|
section_template = '<section title="%(title)s" ref="%(ref)s"/>'
|
||||||
|
file_template = ' '*12 + '<file>%(filename)s</file>'
|
||||||
|
|
||||||
|
|
||||||
|
class QtHelpBuilder(StandaloneHTMLBuilder):
|
||||||
|
"""
|
||||||
|
Builder that also outputs Qt help project, contents and index files.
|
||||||
|
"""
|
||||||
|
name = 'qthelp'
|
||||||
|
|
||||||
|
# don't copy the reST source
|
||||||
|
copysource = False
|
||||||
|
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||||
|
'image/jpeg']
|
||||||
|
|
||||||
|
# don't add links
|
||||||
|
add_permalinks = False
|
||||||
|
# don't add sidebar etc.
|
||||||
|
embedded = True
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
StandaloneHTMLBuilder.init(self)
|
||||||
|
# the output files for HTML help must be .html only
|
||||||
|
self.out_suffix = '.html'
|
||||||
|
self.link_suffix = '.html'
|
||||||
|
#self.config.html_style = 'traditional.css'
|
||||||
|
|
||||||
|
def handle_finish(self):
|
||||||
|
self.build_qhcp(self.outdir, self.config.qthelp_basename)
|
||||||
|
self.build_qhp(self.outdir, self.config.qthelp_basename)
|
||||||
|
self.build_qhc(self.outdir, self.config.qthelp_basename)
|
||||||
|
|
||||||
|
def build_qhc(self, outdir, outname):
|
||||||
|
self.info('create Qt Help Collection...')
|
||||||
|
with open(os.path.join(outdir, 'about.txt'), 'wb') as f:
|
||||||
|
f.write(ABOUT)
|
||||||
|
qhcp = os.path.abspath(os.path.join(outdir, outname+'.qhcp'))
|
||||||
|
subprocess.check_call(['qcollectiongenerator', qhcp])
|
||||||
|
qhc = qhcp[:-5]+'.qhc'
|
||||||
|
self.info('Qt Help Collection: '+qhc)
|
||||||
|
self.info('To test: assistant -collectionFile '+qhc)
|
||||||
|
|
||||||
|
def build_qhcp(self, outdir, outname):
|
||||||
|
self.info('writing collection project file...')
|
||||||
|
f = open(path.join(outdir, outname+'.qhcp'), 'w')
|
||||||
|
try:
|
||||||
|
f.write(collection_template % {'outname': outname})
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def build_qhp(self, outdir, outname):
|
||||||
|
self.info('writing project file...')
|
||||||
|
|
||||||
|
# sections
|
||||||
|
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
|
||||||
|
prune_toctrees=False)
|
||||||
|
istoctree = lambda node: (
|
||||||
|
isinstance(node, addnodes.compact_paragraph)
|
||||||
|
and node.has_key('toctree'))
|
||||||
|
sections = []
|
||||||
|
for node in tocdoc.traverse(istoctree):
|
||||||
|
sections.extend(self.write_toc(node))
|
||||||
|
|
||||||
|
if self.config.html_use_modindex:
|
||||||
|
item = section_template % {'title': 'Global Module Index',
|
||||||
|
'ref': 'modindex.html'}
|
||||||
|
sections.append(' '*4*4 + item)
|
||||||
|
sections = '\n'.join(sections)
|
||||||
|
|
||||||
|
# keywords
|
||||||
|
keywords = []
|
||||||
|
index = self.env.create_index(self)
|
||||||
|
for (key, group) in index:
|
||||||
|
for title, (refs, subitems) in group:
|
||||||
|
keywords.extend(self.build_keywords(title, refs, subitems))
|
||||||
|
keywords = '\n'.join(keywords)
|
||||||
|
|
||||||
|
# files
|
||||||
|
if not outdir.endswith(os.sep):
|
||||||
|
outdir += os.sep
|
||||||
|
olen = len(outdir)
|
||||||
|
projectfiles = []
|
||||||
|
staticdir = path.join(outdir, '_static')
|
||||||
|
imagesdir = path.join(outdir, '_images')
|
||||||
|
for root, dirs, files in os.walk(outdir):
|
||||||
|
resourcedir = root.startswith(staticdir) or root.startswith(imagesdir)
|
||||||
|
for fn in files:
|
||||||
|
if (resourcedir and not fn.endswith('.js')) or \
|
||||||
|
fn.endswith('.html'):
|
||||||
|
filename = path.join(root, fn)[olen:]
|
||||||
|
#filename = filename.replace(os.sep, '\\') # XXX
|
||||||
|
projectfiles.append(file_template % {'filename': filename})
|
||||||
|
projectfiles = '\n'.join(projectfiles)
|
||||||
|
|
||||||
|
# write the project file
|
||||||
|
f = open(path.join(outdir, outname+'.qhp'), 'w')
|
||||||
|
try:
|
||||||
|
nversion = self.config.version.replace('.', '_')
|
||||||
|
nversion = nversion.replace(' ', '_')
|
||||||
|
f.write(project_template % {'outname': outname,
|
||||||
|
'title': self.config.html_title,
|
||||||
|
'version': self.config.version,
|
||||||
|
'project': self.config.project,
|
||||||
|
'nversion': nversion,
|
||||||
|
'masterdoc': self.config.master_doc,
|
||||||
|
'sections': sections,
|
||||||
|
'keywords': keywords,
|
||||||
|
'files': projectfiles})
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def isdocnode(self, node):
|
||||||
|
if not isinstance(node, nodes.list_item):
|
||||||
|
return False
|
||||||
|
if len(node.children) != 2:
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[0], addnodes.compact_paragraph):
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[0][0], nodes.reference):
|
||||||
|
return False
|
||||||
|
if not isinstance(node.children[1], nodes.bullet_list):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def write_toc(self, node, indentlevel=4):
|
||||||
|
parts = []
|
||||||
|
if self.isdocnode(node):
|
||||||
|
refnode = node.children[0][0]
|
||||||
|
link = refnode['refuri']
|
||||||
|
title = cgi.escape(refnode.astext()).replace('"','"')
|
||||||
|
item = '<section title="%(title)s" ref="%(ref)s">' % {
|
||||||
|
'title': title,
|
||||||
|
'ref': link}
|
||||||
|
parts.append(' '*4*indentlevel + item)
|
||||||
|
for subnode in node.children[1]:
|
||||||
|
parts.extend(self.write_toc(subnode, indentlevel+1))
|
||||||
|
parts.append(' '*4*indentlevel + '</section>')
|
||||||
|
elif isinstance(node, nodes.list_item):
|
||||||
|
for subnode in node:
|
||||||
|
parts.extend(self.write_toc(subnode, indentlevel))
|
||||||
|
elif isinstance(node, nodes.reference):
|
||||||
|
link = node['refuri']
|
||||||
|
title = cgi.escape(node.astext()).replace('"','"')
|
||||||
|
item = section_template % {'title': title, 'ref': link}
|
||||||
|
item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace')
|
||||||
|
parts.append(item.encode('ascii', 'xmlcharrefreplace'))
|
||||||
|
elif isinstance(node, nodes.bullet_list):
|
||||||
|
for subnode in node:
|
||||||
|
parts.extend(self.write_toc(subnode, indentlevel))
|
||||||
|
elif isinstance(node, addnodes.compact_paragraph):
|
||||||
|
for subnode in node:
|
||||||
|
parts.extend(self.write_toc(subnode, indentlevel))
|
||||||
|
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def keyword_item(self, name, ref):
|
||||||
|
matchobj = _idpattern.match(name)
|
||||||
|
if matchobj:
|
||||||
|
groupdict = matchobj.groupdict()
|
||||||
|
shortname = groupdict['title']
|
||||||
|
id = groupdict.get('id')
|
||||||
|
# descr = groupdict.get('descr')
|
||||||
|
if shortname.endswith('()'):
|
||||||
|
shortname = shortname[:-2]
|
||||||
|
id = '%s.%s' % (id, shortname)
|
||||||
|
else:
|
||||||
|
id = descr = None
|
||||||
|
|
||||||
|
if id:
|
||||||
|
item = ' '*12 + '<keyword name="%s" id="%s" ref="%s"/>' % (
|
||||||
|
name, id, ref)
|
||||||
|
else:
|
||||||
|
item = ' '*12 + '<keyword name="%s" ref="%s"/>' % (name, ref)
|
||||||
|
item.encode('ascii', 'xmlcharrefreplace')
|
||||||
|
return item
|
||||||
|
|
||||||
|
def build_keywords(self, title, refs, subitems):
|
||||||
|
keywords = []
|
||||||
|
|
||||||
|
title = cgi.escape(title)
|
||||||
|
# if len(refs) == 0: # XXX
|
||||||
|
# write_param('See Also', title)
|
||||||
|
if len(refs) == 1:
|
||||||
|
keywords.append(self.keyword_item(title, refs[0]))
|
||||||
|
elif len(refs) > 1:
|
||||||
|
for i, ref in enumerate(refs): # XXX
|
||||||
|
# item = (' '*12 +
|
||||||
|
# '<keyword name="%s [%d]" ref="%s"/>' % (
|
||||||
|
# title, i, ref))
|
||||||
|
# item.encode('ascii', 'xmlcharrefreplace')
|
||||||
|
# keywords.append(item)
|
||||||
|
keywords.append(self.keyword_item(title, ref))
|
||||||
|
|
||||||
|
if subitems:
|
||||||
|
for subitem in subitems:
|
||||||
|
keywords.extend(self.build_keywords(subitem[0], subitem[1], []))
|
||||||
|
|
||||||
|
return keywords
|
29
src/calibre/manual/resources/titlepage.html
Normal file
29
src/calibre/manual/resources/titlepage.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
|
<head>
|
||||||
|
<title>%(title)s</title>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
text-align:center;
|
||||||
|
font-size: 1pt;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
h1 { font-family: serif; }
|
||||||
|
h2, h4 { font-family: monospace; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>%(title)s</h1>
|
||||||
|
<h4 style="font-family:monospace">%(version)s</h4>
|
||||||
|
<div style="text-align:center">
|
||||||
|
<img class="logo" src="%(img)s" alt="calibre logo" />
|
||||||
|
</div>
|
||||||
|
<h2>%(author)s</h2>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
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
@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.6.7\n"
|
"Project-Id-Version: calibre 0.6.8\n"
|
||||||
"POT-Creation-Date: 2009-08-18 20:55+MDT\n"
|
"POT-Creation-Date: 2009-08-21 15:14+MDT\n"
|
||||||
"PO-Revision-Date: 2009-08-18 20:55+MDT\n"
|
"PO-Revision-Date: 2009-08-21 15:14+MDT\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -24,8 +24,8 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:52
|
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:52
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58
|
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199
|
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:695
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:698
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:698
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:702
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:403
|
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:403
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:66
|
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:66
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:68
|
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:68
|
||||||
@ -94,8 +94,8 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29
|
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:139
|
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:139
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:141
|
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:141
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:258
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:262
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:265
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:269
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:91
|
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:91
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:98
|
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:98
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:19
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:19
|
||||||
@ -110,10 +110,10 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:387
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:387
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:34
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:35
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:39
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:40
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:40
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:123
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:41
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:124
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:874
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:874
|
||||||
@ -126,15 +126,15 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1093
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1093
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1423
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1423
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1425
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1425
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1510
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1514
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:309
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:373
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:373
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:446
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:27
|
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:27
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:31
|
#: /home/kovid/work/calibre/src/calibre/utils/poppler/__init__.py:31
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:51
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:52
|
||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -483,47 +483,47 @@ msgstr ""
|
|||||||
msgid "Communicate with the Sony PRS-700 eBook reader."
|
msgid "Communicate with the Sony PRS-700 eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:278
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:277
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:350
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:349
|
||||||
msgid "Unable to detect the %s disk drive. Try rebooting."
|
msgid "Unable to detect the %s disk drive. Try rebooting."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:418
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:417
|
||||||
msgid "Unable to detect the %s disk drive."
|
msgid "Unable to detect the %s disk drive."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:511
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:510
|
||||||
msgid "Could not find mount helper: %s."
|
msgid "Could not find mount helper: %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:523
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:522
|
||||||
msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS."
|
msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:531
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:530
|
||||||
msgid "Unable to mount main memory (Error code: %d)"
|
msgid "Unable to mount main memory (Error code: %d)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:636
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:635
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:638
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:637
|
||||||
msgid "The reader has no storage card in this slot."
|
msgid "The reader has no storage card in this slot."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:640
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:639
|
||||||
msgid "Selected slot: %s is not supported."
|
msgid "Selected slot: %s is not supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:664
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:663
|
||||||
msgid "There is insufficient free space in main memory"
|
msgid "There is insufficient free space in main memory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:666
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:665
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:668
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:667
|
||||||
msgid "There is insufficient free space on the storage card"
|
msgid "There is insufficient free space on the storage card"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:678
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:678
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:467
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:433
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1000
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1000
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1004
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1004
|
||||||
@ -1905,16 +1905,16 @@ msgstr ""
|
|||||||
msgid "Default action to perform when send to device button is clicked"
|
msgid "Default action to perform when send to device button is clicked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:120
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:124
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:397
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:397
|
||||||
msgid "Copied"
|
msgid "Copied"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:149
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:153
|
||||||
msgid "Copy"
|
msgid "Copy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:149
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:153
|
||||||
msgid "Copy to Clipboard"
|
msgid "Copy to Clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3796,93 +3796,88 @@ msgstr ""
|
|||||||
msgid "Aborting..."
|
msgid "Aborting..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:41
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:42
|
||||||
msgid "You"
|
msgid "You"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:126
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:144
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:145
|
||||||
msgid "Custom"
|
msgid "Custom"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:127
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:128
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:136
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:137
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:142
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:143
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:228
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:230
|
||||||
msgid "Scheduled"
|
msgid "Scheduled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:240
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:284
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:45
|
|
||||||
msgid "Search"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:318
|
|
||||||
msgid "%d recipes"
|
msgid "%d recipes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
|
||||||
msgid "Monday"
|
msgid "Monday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
|
||||||
msgid "Tuesday"
|
msgid "Tuesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
|
||||||
msgid "Wednesday"
|
msgid "Wednesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:319
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:285
|
||||||
msgid "day"
|
msgid "day"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
|
||||||
msgid "Friday"
|
msgid "Friday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
|
||||||
msgid "Saturday"
|
msgid "Saturday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
|
||||||
msgid "Sunday"
|
msgid "Sunday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:320
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:286
|
||||||
msgid "Thursday"
|
msgid "Thursday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:357
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:323
|
||||||
msgid "Must set account information"
|
msgid "Must set account information"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:358
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:324
|
||||||
msgid "This recipe requires a username and password"
|
msgid "This recipe requires a username and password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:389
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:355
|
||||||
msgid "Created by: "
|
msgid "Created by: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:427
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:393
|
||||||
msgid "%d days, %d hours and %d minutes ago"
|
msgid "%d days, %d hours and %d minutes ago"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:429
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:395
|
||||||
msgid "Last downloaded"
|
msgid "Last downloaded"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:431
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:397
|
||||||
msgid "Last downloaded: never"
|
msgid "Last downloaded: never"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:457
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:423
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:165
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:165
|
||||||
msgid "Schedule news download"
|
msgid "Schedule news download"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:460
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:426
|
||||||
msgid "Add a custom news source"
|
msgid "Add a custom news source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5051,6 +5046,10 @@ msgstr ""
|
|||||||
msgid "No matches found for this book"
|
msgid "No matches found for this book"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:54
|
||||||
|
msgid "Search"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:115
|
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:115
|
||||||
msgid "Jobs:"
|
msgid "Jobs:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -5959,15 +5958,15 @@ msgid ""
|
|||||||
"For help on an individual command: %%prog command --help\n"
|
"For help on an individual command: %%prog command --help\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1536
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1540
|
||||||
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1565
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1569
|
||||||
msgid "Copying <b>%s</b>"
|
msgid "Copying <b>%s</b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1582
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1586
|
||||||
msgid "Compacting database"
|
msgid "Compacting database"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6055,17 +6054,21 @@ msgstr ""
|
|||||||
msgid "Requested formats not available"
|
msgid "Requested formats not available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:159
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:203
|
||||||
msgid "Password to access your calibre library. Username is "
|
msgid "Password to access your calibre library. Username is "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:450
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:523
|
||||||
msgid ""
|
msgid ""
|
||||||
"[options]\n"
|
"[options]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Start the calibre content server."
|
"Start the calibre content server."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/manual/qthelp.py:163
|
||||||
|
msgid "Global Module Index"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/config.py:43
|
#: /home/kovid/work/calibre/src/calibre/utils/config.py:43
|
||||||
msgid ""
|
msgid ""
|
||||||
"%sUsage%s: %s\n"
|
"%sUsage%s: %s\n"
|
||||||
@ -6181,81 +6184,83 @@ msgstr ""
|
|||||||
msgid "Password for sites that require a login to access content."
|
msgid "Password for sites that require a login to access content."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:41
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:42
|
||||||
msgid "Unknown News Source"
|
msgid "Unknown News Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:587
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:589
|
||||||
msgid "Download finished"
|
msgid "Download finished"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:589
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:591
|
||||||
msgid "Failed to download the following articles:"
|
msgid "Failed to download the following articles:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:595
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:597
|
||||||
msgid "Failed to download parts of the following articles:"
|
msgid "Failed to download parts of the following articles:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:597
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599
|
||||||
msgid " from "
|
msgid " from "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:601
|
||||||
msgid "\tFailed links:"
|
msgid "\tFailed links:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:680
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:682
|
||||||
msgid "Could not fetch article. Run with --debug to see the reason"
|
msgid "Could not fetch article. Run with --debug to see the reason"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:701
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:703
|
||||||
msgid "Fetching feeds..."
|
msgid "Fetching feeds..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:705
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:707
|
||||||
msgid "Got feeds from index page"
|
msgid "Got feeds from index page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:711
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
|
||||||
msgid "Trying to download cover..."
|
msgid "Trying to download cover..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:762
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:764
|
||||||
msgid "Starting download [%d thread(s)]..."
|
msgid "Starting download [%d thread(s)]..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:778
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:780
|
||||||
msgid "Feeds downloaded to %s"
|
msgid "Feeds downloaded to %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:788
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:790
|
||||||
msgid "Could not download cover: %s"
|
msgid "Could not download cover: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:795
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:797
|
||||||
msgid "Downloading cover from %s"
|
msgid "Downloading cover from %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:916
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:920
|
||||||
msgid "Untitled Article"
|
msgid "Untitled Article"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:987
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:991
|
||||||
msgid "Article downloaded: %s"
|
msgid "Article downloaded: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:998
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1002
|
||||||
msgid "Article download failed: %s"
|
msgid "Article download failed: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1013
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1017
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:80
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:80
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:76
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:76
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_lamujerdemivida.py:59
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_lamujerdemivida.py:59
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_laprensa_ni.py:63
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_laprensa_ni.py:63
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:65
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:83
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:83
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_republika.py:66
|
||||||
msgid "Fetching feed"
|
msgid "Fetching feed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6271,11 +6276,13 @@ msgstr ""
|
|||||||
#:
|
#:
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_24sata_rs.py:25
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_24sata_rs.py:25
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_b92.py:22
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_b92.py:22
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_beta.py:22
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_blic.py:25
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_blic.py:25
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:19
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:19
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_danas.py:22
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_danas.py:22
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_e_novine.py:25
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_e_novine.py:25
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:27
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:27
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glasjavnosti.py:21
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica.py:23
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica.py:23
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nin.py:30
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nin.py:30
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_novosti.py:24
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_novosti.py:24
|
||||||
@ -6283,6 +6290,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pescanik.py:24
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pescanik.py:24
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_politika.py:24
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_politika.py:24
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pressonline.py:25
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pressonline.py:25
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_republika.py:19
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_rts.py:23
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_rts.py:23
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_tanjug.py:23
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_tanjug.py:23
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vreme.py:27
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vreme.py:27
|
||||||
@ -6339,6 +6347,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_azstarnet.py:15
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_azstarnet.py:15
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_barrons.py:18
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_barrons.py:18
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_bbc.py:15
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_bbc.py:15
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_beta_en.py:22
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_business_week.py:16
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_business_week.py:16
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_breaking_news.py:22
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_breaking_news.py:22
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_tribune.py:17
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_chicago_tribune.py:17
|
||||||
@ -6366,7 +6375,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_fudzilla.py:15
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_fudzilla.py:15
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glasgow_herald.py:10
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glasgow_herald.py:10
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_globe_and_mail.py:15
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_globe_and_mail.py:15
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_guardian.py:17
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_guardian.py:16
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers.py:14
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers.py:14
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers_full.py:26
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_harpers_full.py:26
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_hindu.py:10
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_hindu.py:10
|
||||||
@ -6548,6 +6557,7 @@ msgstr ""
|
|||||||
msgid "Skipping filtered article: %s"
|
msgid "Skipping filtered article: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_monitor.py:25
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:24
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:24
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vijesti.py:25
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vijesti.py:25
|
||||||
msgid "Montenegrin"
|
msgid "Montenegrin"
|
||||||
|
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
@ -164,6 +164,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
#: Recipe specific options to control the conversion of the downloaded
|
#: Recipe specific options to control the conversion of the downloaded
|
||||||
#: content into an e-book. These will override any user or plugin specified
|
#: content into an e-book. These will override any user or plugin specified
|
||||||
#: values, so only use if absolutely necessary. For example::
|
#: values, so only use if absolutely necessary. For example::
|
||||||
|
#:
|
||||||
#: conversion_options = {
|
#: conversion_options = {
|
||||||
#: 'base_font_size' : 16,
|
#: 'base_font_size' : 16,
|
||||||
#: 'tags' : 'mytag1,mytag2',
|
#: 'tags' : 'mytag1,mytag2',
|
||||||
|
@ -55,6 +55,7 @@ recipe_modules = ['recipe_' + r for r in (
|
|||||||
'eltiempo_hn', 'slate', 'tnxm', 'bbcvietnamese', 'vnexpress',
|
'eltiempo_hn', 'slate', 'tnxm', 'bbcvietnamese', 'vnexpress',
|
||||||
'volksrant', 'theeconomictimes_india', 'ourdailybread',
|
'volksrant', 'theeconomictimes_india', 'ourdailybread',
|
||||||
'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti',
|
'monitor', 'republika', 'beta', 'beta_en', 'glasjavnosti',
|
||||||
|
'esquire',
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
62
src/calibre/web/feeds/recipes/recipe_esquire.py
Normal file
62
src/calibre/web/feeds/recipes/recipe_esquire.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
|
||||||
|
'''
|
||||||
|
www.esquire.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre import strftime
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import Tag
|
||||||
|
|
||||||
|
class Esquire(BasicNewsRecipe):
|
||||||
|
title = 'Esquire'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Esquire: Man at His Best'
|
||||||
|
publisher = 'Hearst Communications, Inc.'
|
||||||
|
category = 'magazine, men, women we love, style, the guide, sex, screen'
|
||||||
|
oldest_article = 30
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'cp1250'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = _('English')
|
||||||
|
lang = 'en-US'
|
||||||
|
cover_url = strftime('http://www.esquire.com/cm/esquire/cover-images/%Y_') + strftime('%m').strip('0') + '.jpg'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : lang
|
||||||
|
, 'pretty_print' : True
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||||
|
|
||||||
|
remove_tags = [dict(name=['object','link','embed','iframe'])]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Style' , u'http://www.esquire.com/style/rss/' )
|
||||||
|
,(u'Women' , u'http://www.esquire.com/women/rss/' )
|
||||||
|
,(u'Features' , u'http://www.esquire.com/features/rss/' )
|
||||||
|
,(u'Fiction' , u'http://www.esquire.com/fiction/rss/' )
|
||||||
|
,(u'Frontpage', u'http://www.esquire.com/rss/' )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
rest = url.rpartition('?')[0]
|
||||||
|
article = rest.rpartition('/')[2]
|
||||||
|
return 'http://www.esquire.com/print-this/' + article
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
soup.html['xml:lang'] = self.lang
|
||||||
|
soup.html['lang'] = self.lang
|
||||||
|
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
||||||
|
soup.head.insert(0,mlang)
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
@ -39,7 +39,7 @@ class GlasJavnosti(BasicNewsRecipe):
|
|||||||
,dict(name='div', attrs={'class':'node' })
|
,dict(name='div', attrs={'class':'node' })
|
||||||
]
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name=['object','link'])
|
dict(name=['object','link','img'])
|
||||||
,dict(name='div',attrs={'class':['links','meta']})
|
,dict(name='div',attrs={'class':['links','meta']})
|
||||||
,dict(name='div',attrs={'id':'block-block-12'})
|
,dict(name='div',attrs={'id':'block-block-12'})
|
||||||
]
|
]
|
||||||
@ -74,7 +74,5 @@ class GlasJavnosti(BasicNewsRecipe):
|
|||||||
,(u'Konjske Snage', u'http://www.glas-javnosti.rs/aktuelni-clanci/46')
|
,(u'Konjske Snage', u'http://www.glas-javnosti.rs/aktuelni-clanci/46')
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
remove_attributes = ['style','width','height','font','border','align']
|
||||||
for item in soup.findAll(style=True):
|
|
||||||
del item['style']
|
|
||||||
return self.adeify_images(soup)
|
|
@ -26,7 +26,7 @@ class MonitorCG(BasicNewsRecipe):
|
|||||||
lang ='sr-Latn-Me'
|
lang ='sr-Latn-Me'
|
||||||
INDEX = 'http://www.monitorcg.com'
|
INDEX = 'http://www.monitorcg.com'
|
||||||
|
|
||||||
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
extra_css = ' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} '
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
@ -43,14 +43,27 @@ class MonitorCG(BasicNewsRecipe):
|
|||||||
remove_tags = [ dict(name=['object','link','embed'])
|
remove_tags = [ dict(name=['object','link','embed'])
|
||||||
, dict(attrs={'class':['buttonheading','article-section']})]
|
, dict(attrs={'class':['buttonheading','article-section']})]
|
||||||
|
|
||||||
|
remove_attributes = ['style','width','height','font','border','align']
|
||||||
|
|
||||||
|
def adeify_images2(cls, soup):
|
||||||
|
for item in soup.findAll('img'):
|
||||||
|
for attrib in ['height','width','border','align','style']:
|
||||||
|
if item.has_key(attrib):
|
||||||
|
del item[attrib]
|
||||||
|
oldParent = item.parent
|
||||||
|
if oldParent.name == 'a':
|
||||||
|
oldParent.name == 'p'
|
||||||
|
myIndex = oldParent.contents.index(item)
|
||||||
|
brtag = Tag(soup,'br')
|
||||||
|
oldParent.insert(myIndex+1,brtag)
|
||||||
|
return soup
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['xml:lang'] = self.lang
|
soup.html['xml:lang'] = self.lang
|
||||||
soup.html['lang'] = self.lang
|
soup.html['lang'] = self.lang
|
||||||
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
||||||
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
|
soup.html.insert(0,mlang)
|
||||||
soup.head.insert(0,mlang)
|
return self.adeify_images2(soup)
|
||||||
soup.head.insert(1,mcharset)
|
|
||||||
return self.adeify_images(soup)
|
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
totalfeeds = []
|
totalfeeds = []
|
||||||
|
6
todo
6
todo
@ -3,5 +3,9 @@
|
|||||||
|
|
||||||
* Testing framework
|
* Testing framework
|
||||||
|
|
||||||
* MOBI navigation indexing support
|
|
||||||
|
|
||||||
|
* Add a languages column to books. Best implementation is comma separated IANA codes
|
||||||
|
* Add a hash column to the formats table (used for stanza identifier)
|
||||||
|
|
||||||
|
* Fix blockquote handling in sphinx templates
|
||||||
|
* Fix ebook-viewer going to links
|
||||||
|
12
upload.py
12
upload.py
@ -123,8 +123,13 @@ class manual(OptionlessCommand):
|
|||||||
os.makedirs(d)
|
os.makedirs(d)
|
||||||
if not os.path.exists('.build'+os.sep+'html'):
|
if not os.path.exists('.build'+os.sep+'html'):
|
||||||
os.makedirs('.build'+os.sep+'html')
|
os.makedirs('.build'+os.sep+'html')
|
||||||
check_call(['sphinx-build', '-b', 'custom', '-d',
|
check_call(['sphinx-build', '-b', 'custom', '-t', 'online',
|
||||||
'.build/doctrees', '.', '.build/html'])
|
'-d', '.build/doctrees', '.', '.build/html'])
|
||||||
|
check_call(['sphinx-build', '-b', 'epub', '-d',
|
||||||
|
'.build/doctrees', '.', '.build/epub'])
|
||||||
|
j = os.path.join
|
||||||
|
shutil.copyfile(j('.build', 'epub', 'calibre.epub'), j('.build',
|
||||||
|
'html', 'calibre.epub'))
|
||||||
finally:
|
finally:
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
|
|
||||||
@ -701,6 +706,9 @@ class stage3(OptionlessCommand):
|
|||||||
shell=True)
|
shell=True)
|
||||||
check_call('ssh divok bzr update /usr/local/calibre',
|
check_call('ssh divok bzr update /usr/local/calibre',
|
||||||
shell=True)
|
shell=True)
|
||||||
|
check_call('ssh divok /etc/init.d/apache2 graceful',
|
||||||
|
shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user