Sync to trunk.

This commit is contained in:
John Schember 2009-07-05 15:41:44 -04:00
commit b92567f538
33 changed files with 715 additions and 577 deletions

View File

@ -36,7 +36,7 @@ def run_install_jammer(installer_name='<%AppName%>-<%Version%><%Ext%>', build_fo
compression = 'zlib' compression = 'zlib'
if build_for_release: if build_for_release:
cmdline += ['--build-for-release'] cmdline += ['--build-for-release']
compression = 'lzma (solid)' #compression = 'lzma (solid)'
cmdline += ['-DCompressionMethod', compression] cmdline += ['-DCompressionMethod', compression]
cmdline += ['--build', mpi] cmdline += ['--build', mpi]
#print 'Running installjammer with cmdline:' #print 'Running installjammer with cmdline:'

View File

@ -180,7 +180,8 @@ def main():
if not cols: # On windows terminal width is unknown if not cols: # On windows terminal width is unknown
cols = 80 cols = 80
parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat, rm\n\n"+ parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand "+
"is one of: info, books, df, ls, cp, mkdir, touch, cat, rm, eject\n\n"+
"For help on a particular command: %prog command", version=__appname__+" version: " + __version__) "For help on a particular command: %prog command", version=__appname__+" version: " + __version__)
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\ parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
"The numbers in the left column are byte offsets that allow the packet size to be read off easily.", "The numbers in the left column are byte offsets that allow the packet size to be read off easily.",
@ -222,6 +223,8 @@ def main():
for i in range(3): for i in range(3):
print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\ print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\
str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%") str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")
elif command == 'eject':
dev.eject()
elif command == "books": elif command == "books":
print "Books in main memory:" print "Books in main memory:"
for book in dev.books(): for book in dev.books():

View File

@ -7,7 +7,7 @@ intended to be subclassed with the relevant parts implemented for a particular
device. This class handles device detection. device. This class handles device detection.
''' '''
import os, subprocess, time, re, sys, glob import os, subprocess, time, re, sys, glob, shutil
from itertools import repeat from itertools import repeat
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
@ -548,13 +548,23 @@ class Device(DeviceConfig, DevicePlugin):
drives = self.find_device_nodes() drives = self.find_device_nodes()
for drive in drives: for drive in drives:
if drive: if drive:
cmd = ['pumount'] cmd = ['pumount', '-l']
try: try:
p = subprocess.Popen(cmd + [drive]) p = subprocess.Popen(cmd + [drive])
except: except:
pass pass
while p.poll() is None: while p.poll() is None:
time.sleep(0.1) time.sleep(0.1)
if p.returncode == 0:
for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
x = getattr(self, x, None)
if x is not None:
if x.startswith('/media/') and os.path.exists(x):
try:
shutil.rmtree(x)
except:
pass
def eject(self): def eject(self):
if islinux: if islinux:

View File

@ -283,7 +283,7 @@ class ComicInput(InputFormatPlugin):
OptionRecommendation(name='disable_trim', recommended_value=False, OptionRecommendation(name='disable_trim', recommended_value=False,
help=_('Disable trimming of comic pages. For some comics, ' help=_('Disable trimming of comic pages. For some comics, '
'trimming might remove content as well as borders.')), 'trimming might remove content as well as borders.')),
OptionRecommendation(name='landspace', recommended_value=False, OptionRecommendation(name='landscape', recommended_value=False,
help=_("Don't split landscape images into two portrait images")), help=_("Don't split landscape images into two portrait images")),
OptionRecommendation(name='wide', recommended_value=False, OptionRecommendation(name='wide', recommended_value=False,
help=_("Keep aspect ratio and scale image using screen height as " help=_("Keep aspect ratio and scale image using screen height as "

View File

@ -161,7 +161,7 @@ class ReBinary(object):
def tree_to_binary(self, elem, nsrmap=NSRMAP, parents=[], def tree_to_binary(self, elem, nsrmap=NSRMAP, parents=[],
inhead=False, preserve=False): inhead=False, preserve=False):
if not isinstance(elem.tag, basestring): if not isinstance(elem.tag, basestring):
self.write(etree.tostring(elem)) # Don't emit any comments or raw entities
return return
nsrmap = copy.copy(nsrmap) nsrmap = copy.copy(nsrmap)
attrib = dict(elem.attrib) attrib = dict(elem.attrib)
@ -308,23 +308,6 @@ class LitWriter(object):
else: else:
self._logger.warn('No suitable cover image found.') self._logger.warn('No suitable cover image found.')
# Remove comments because they are not supported by LIT HTML
for item in oeb.spine:
for elem in item.data.getiterator():
print elem.tag
if isinstance(elem, etree._Comment):
tail = elem.tail
parent = elem.getparent()
parent.remove(elem)
text = u''
if parent.text:
text += parent.text
if tail:
text += tail
parent.text = text
def __call__(self, oeb, path): def __call__(self, oeb, path):
if hasattr(path, 'write'): if hasattr(path, 'write'):
return self._dump_stream(oeb, path) return self._dump_stream(oeb, path)
@ -726,5 +709,3 @@ class LitWriter(object):
ichunk = ''.join(['AOLI', pack('<IQ', rem, len(dchunks)), ichunk = ''.join(['AOLI', pack('<IQ', rem, len(dchunks)),
ichunk.getvalue(), ('\0' * pad), pack('<H', len(dchunks))]) ichunk.getvalue(), ('\0' * pad), pack('<H', len(dchunks))])
return dcounts, dchunks, ichunk return dcounts, dchunks, ichunk

View File

@ -11,11 +11,14 @@ __docformat__ = 'restructuredtext en'
from struct import pack, unpack from struct import pack, unpack
from cStringIO import StringIO from cStringIO import StringIO
from datetime import datetime
from calibre.ebooks.mobi import MobiError from calibre.ebooks.mobi import MobiError
from calibre.ebooks.mobi.writer import rescale_image, MAX_THUMB_DIMEN from calibre.ebooks.mobi.writer import rescale_image, MAX_THUMB_DIMEN
from calibre.ebooks.mobi.langcodes import iana2mobi from calibre.ebooks.mobi.langcodes import iana2mobi
class StreamSlicer(object): class StreamSlicer(object):
def __init__(self, stream, start=0, stop=None): def __init__(self, stream, start=0, stop=None):
self._stream = stream self._stream = stream
self.start = start self.start = start
@ -84,17 +87,22 @@ class MetadataUpdater(object):
flags, = unpack('>I', record0[128:132]) flags, = unpack('>I', record0[128:132])
have_exth = self.have_exth = (flags & 0x40) != 0 have_exth = self.have_exth = (flags & 0x40) != 0
self.cover_record = self.thumbnail_record = None self.cover_record = self.thumbnail_record = None
self.timestamp = None
if not have_exth: if not have_exth:
return return
exth_off = unpack('>I', record0[20:24])[0] + 16 + record0.start exth_off = unpack('>I', record0[20:24])[0] + 16 + record0.start
exth = self.exth = StreamSlicer(stream, exth_off, record0.stop) exth = self.exth = StreamSlicer(stream, exth_off, record0.stop)
nitems, = unpack('>I', exth[8:12]) nitems, = unpack('>I', exth[8:12])
pos = 12 pos = 12
# Store any EXTH fields not specifiable in GUI
for i in xrange(nitems): for i in xrange(nitems):
id, size = unpack('>II', exth[pos:pos + 8]) id, size = unpack('>II', exth[pos:pos + 8])
content = exth[pos + 8: pos + size] content = exth[pos + 8: pos + size]
pos += size pos += size
if id == 201:
if id == 106:
self.timestamp = content
elif id == 201:
rindex, = self.cover_rindex, = unpack('>I', content) rindex, = self.cover_rindex, = unpack('>I', content)
self.cover_record = self.record(rindex + image_base) self.cover_record = self.record(rindex + image_base)
elif id == 202: elif id == 202:
@ -134,6 +142,16 @@ class MetadataUpdater(object):
if mi.tags: if mi.tags:
subjects = '; '.join(mi.tags) subjects = '; '.join(mi.tags)
recs.append((105, subjects.encode(self.codec, 'replace'))) recs.append((105, subjects.encode(self.codec, 'replace')))
if mi.pubdate:
recs.append((106, str(mi.pubdate).encode(self.codec, 'replace')))
elif mi.timestamp:
recs.append((106, str(mi.timestamp).encode(self.codec, 'replace')))
elif self.timestamp:
recs.append(106, self.timestamp)
else:
recs.append((106, str(datetime.now()).encode(self.codec, 'replace')))
if self.cover_record is not None: if self.cover_record is not None:
recs.append((201, pack('>I', self.cover_rindex))) recs.append((201, pack('>I', self.cover_rindex)))
recs.append((203, pack('>I', 0))) recs.append((203, pack('>I', 0)))

View File

@ -49,6 +49,7 @@ class BlockState(object):
class FormatState(object): class FormatState(object):
def __init__(self): def __init__(self):
self.rendered = False
self.left = 0. self.left = 0.
self.halign = 'auto' self.halign = 'auto'
self.indent = 0. self.indent = 0.
@ -159,13 +160,15 @@ class MobiMLizer(object):
indent = 0 indent = 0
elif indent != 0 and abs(indent) < self.profile.fbase: elif indent != 0 and abs(indent) < self.profile.fbase:
indent = (indent / abs(indent)) * self.profile.fbase indent = (indent / abs(indent)) * self.profile.fbase
if tag in NESTABLE_TAGS: if tag in NESTABLE_TAGS and not istate.rendered:
para = wrapper = etree.SubElement( para = wrapper = etree.SubElement(
parent, XHTML(tag), attrib=istate.attrib) parent, XHTML(tag), attrib=istate.attrib)
bstate.nested.append(para) bstate.nested.append(para)
if tag == 'li' and len(istates) > 1: if tag == 'li' and len(istates) > 1:
istates[-2].list_num += 1 istates[-2].list_num += 1
para.attrib['value'] = str(istates[-2].list_num) para.attrib['value'] = str(istates[-2].list_num)
elif tag in NESTABLE_TAGS and istate.rendered:
para = wrapper = bstate.nested[-1]
elif left > 0 and indent >= 0: elif left > 0 and indent >= 0:
para = wrapper = etree.SubElement(parent, XHTML('blockquote')) para = wrapper = etree.SubElement(parent, XHTML('blockquote'))
para = wrapper para = wrapper
@ -189,6 +192,7 @@ class MobiMLizer(object):
vspace -= 1 vspace -= 1
if istate.halign != 'auto' and isinstance(istate.halign, (str, unicode)): if istate.halign != 'auto' and isinstance(istate.halign, (str, unicode)):
para.attrib['align'] = istate.halign para.attrib['align'] = istate.halign
istate.rendered = True
pstate = bstate.istate pstate = bstate.istate
if tag in CONTENT_TAGS: if tag in CONTENT_TAGS:
bstate.inline = para bstate.inline = para
@ -253,6 +257,7 @@ class MobiMLizer(object):
return return
tag = barename(elem.tag) tag = barename(elem.tag)
istate = copy.copy(istates[-1]) istate = copy.copy(istates[-1])
istate.rendered = False
istate.list_num = 0 istate.list_num = 0
istates.append(istate) istates.append(istate)
left = 0 left = 0

View File

@ -24,6 +24,10 @@ class MOBIOutput(OutputFormatPlugin):
recommended_value=False, level=OptionRecommendation.LOW, recommended_value=False, level=OptionRecommendation.LOW,
help=_('When present, use author sort field as author.') help=_('When present, use author sort field as author.')
), ),
OptionRecommendation(name='no_inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Don\'t add Table of Contents to end of book. Useful if '
'the book has its own table of contents.')),
OptionRecommendation(name='toc_title', recommended_value=None, OptionRecommendation(name='toc_title', recommended_value=None,
help=_('Title for any generated in-line table of contents.') help=_('Title for any generated in-line table of contents.')
), ),
@ -35,8 +39,6 @@ class MOBIOutput(OutputFormatPlugin):
recommended_value=False, level=OptionRecommendation.LOW, recommended_value=False, level=OptionRecommendation.LOW,
help=_('Disable compression of the file contents.') help=_('Disable compression of the file contents.')
), ),
]) ])
def convert(self, oeb, output_path, input_plugin, opts, log): def convert(self, oeb, output_path, input_plugin, opts, log):
@ -49,6 +51,7 @@ class MOBIOutput(OutputFormatPlugin):
from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder
from calibre.customize.ui import plugin_for_input_format from calibre.customize.ui import plugin_for_input_format
imagemax = PALM_MAX_IMAGE_SIZE if opts.rescale_images else None imagemax = PALM_MAX_IMAGE_SIZE if opts.rescale_images else None
if not opts.no_inline_toc:
tocadder = HTMLTOCAdder(title=opts.toc_title) tocadder = HTMLTOCAdder(title=opts.toc_title)
tocadder(oeb, opts) tocadder(oeb, opts)
mangler = CaseMangler() mangler = CaseMangler()
@ -58,7 +61,6 @@ class MOBIOutput(OutputFormatPlugin):
mobimlizer = MobiMLizer(ignore_tables=opts.linearize_tables) mobimlizer = MobiMLizer(ignore_tables=opts.linearize_tables)
mobimlizer(oeb, opts) mobimlizer(oeb, opts)
write_page_breaks_after_item = not input_plugin is plugin_for_input_format('cbz') write_page_breaks_after_item = not input_plugin is plugin_for_input_format('cbz')
print 111111, write_page_breaks_after_item
writer = MobiWriter(opts, imagemax=imagemax, writer = MobiWriter(opts, imagemax=imagemax,
compression=UNCOMPRESSED if opts.dont_compress else PALMDOC, compression=UNCOMPRESSED if opts.dont_compress else PALMDOC,
prefer_author_sort=opts.prefer_author_sort, prefer_author_sort=opts.prefer_author_sort,

View File

@ -345,10 +345,13 @@ class MobiReader(object):
root.insert(0, head) root.insert(0, head)
head.text = '\n\t' head.text = '\n\t'
link = head.makeelement('link', {'type':'text/css', link = head.makeelement('link', {'type':'text/css',
'href':'styles.css'}) 'href':'styles.css', 'rel':'stylesheet'})
head.insert(0, link) head.insert(0, link)
link.tail = '\n\t' link.tail = '\n\t'
title = head.xpath('descendant::title') title = head.xpath('descendant::title')
m = head.makeelement('meta', {'http-equiv':'Content-Type',
'content':'text/html; charset=utf-8'})
head.insert(0, m)
if not title: if not title:
title = head.makeelement('title', {}) title = head.makeelement('title', {})
title.text = self.book_header.title title.text = self.book_header.title

View File

@ -455,7 +455,7 @@ class Metadata(object):
'description', 'format', 'identifier', 'language', 'description', 'format', 'identifier', 'language',
'publisher', 'relation', 'rights', 'source', 'publisher', 'relation', 'rights', 'source',
'subject', 'title', 'type']) 'subject', 'title', 'type'])
CALIBRE_TERMS = set(['series', 'series_index', 'rating']) CALIBRE_TERMS = set(['series', 'series_index', 'rating', 'timestamp'])
OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'), OPF_ATTRS = {'role': OPF('role'), 'file-as': OPF('file-as'),
'scheme': OPF('scheme'), 'event': OPF('event'), 'scheme': OPF('scheme'), 'event': OPF('event'),
'type': XSI('type'), 'lang': XML('lang'), 'id': 'id'} 'type': XSI('type'), 'lang': XML('lang'), 'id': 'id'}
@ -693,6 +693,7 @@ class Metadata(object):
def to_opf2(self, parent=None): def to_opf2(self, parent=None):
nsmap = self._opf2_nsmap nsmap = self._opf2_nsmap
nsrmap = dict((value, key) for key, value in nsmap.items()) nsrmap = dict((value, key) for key, value in nsmap.items())
nsmap.pop('opf', '')
elem = element(parent, OPF('metadata'), nsmap=nsmap) elem = element(parent, OPF('metadata'), nsmap=nsmap)
for term in self.items: for term in self.items:
for item in self.items[term]: for item in self.items[term]:
@ -877,7 +878,7 @@ class Manifest(object):
if title: if title:
title = unicode(title[0]) title = unicode(title[0])
else: else:
title = 'No title' title = _('Unknown')
return self._parse_xhtml(txt_to_markdown(data, title)) return self._parse_xhtml(txt_to_markdown(data, title))

View File

@ -28,9 +28,8 @@ class OEBOutput(OutputFormatPlugin):
href, root = results.pop(key, [None, None]) href, root = results.pop(key, [None, None])
if root is not None: if root is not None:
raw = etree.tostring(root, pretty_print=True, raw = etree.tostring(root, pretty_print=True,
encoding='utf-8') encoding='utf-8', xml_declaration=True)
with open(href, 'wb') as f: with open(href, 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" ?>\n')
f.write(raw) f.write(raw)
for item in oeb_book.manifest: for item in oeb_book.manifest:

View File

@ -129,6 +129,9 @@ class OEBReader(object):
from calibre.ebooks.oeb.transforms.metadata import meta_info_to_oeb_metadata from calibre.ebooks.oeb.transforms.metadata import meta_info_to_oeb_metadata
stream = cStringIO.StringIO(etree.tostring(opf)) stream = cStringIO.StringIO(etree.tostring(opf))
mi = MetaInformation(OPF(stream)) mi = MetaInformation(OPF(stream))
if not mi.language:
mi.language = get_lang()
self.oeb.metadata.add('language', mi.language)
if not mi.title: if not mi.title:
mi.title = self.oeb.translate(__('Unknown')) mi.title = self.oeb.translate(__('Unknown'))
if not mi.authors: if not mi.authors:
@ -136,8 +139,6 @@ class OEBReader(object):
if not mi.book_producer: if not mi.book_producer:
mi.book_producer = '%(a)s (%(v)s) [http://%(a)s.kovidgoyal.net]'%\ mi.book_producer = '%(a)s (%(v)s) [http://%(a)s.kovidgoyal.net]'%\
dict(a=__appname__, v=__version__) dict(a=__appname__, v=__version__)
if not mi.language:
mi.language = get_lang()
meta_info_to_oeb_metadata(mi, self.oeb.metadata, self.logger) meta_info_to_oeb_metadata(mi, self.oeb.metadata, self.logger)
bookid = "urn:uuid:%s" % str(uuid.uuid4()) if mi.application_id is None \ bookid = "urn:uuid:%s" % str(uuid.uuid4()) if mi.application_id is None \
else mi.application_id else mi.application_id

View File

@ -7,6 +7,7 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os import os
from datetime import datetime
def meta_info_to_oeb_metadata(mi, m, log): def meta_info_to_oeb_metadata(mi, m, log):
if mi.title: if mi.title:
@ -56,6 +57,15 @@ def meta_info_to_oeb_metadata(mi, m, log):
m.clear('subject') m.clear('subject')
for t in mi.tags: for t in mi.tags:
m.add('subject', t) m.add('subject', t)
if mi.pubdate is not None:
m.clear('date')
m.add('date', mi.pubdate.isoformat())
if mi.timestamp is not None:
m.clear('timestamp')
m.add('timestamp', mi.timestamp.isoformat())
if not m.timestamp:
m.add('timestamp', datetime.utcnow().isoformat())
class MergeMetadata(object): class MergeMetadata(object):

View File

@ -24,6 +24,7 @@ class ManifestTrimmer(object):
def __call__(self, oeb, context): def __call__(self, oeb, context):
oeb.logger.info('Trimming unused files from manifest...') oeb.logger.info('Trimming unused files from manifest...')
self.opts = context
used = set() used = set()
for term in oeb.metadata: for term in oeb.metadata:
for item in oeb.metadata[term]: for item in oeb.metadata[term]:
@ -63,5 +64,8 @@ class ManifestTrimmer(object):
unchecked = new unchecked = new
for item in oeb.manifest.values(): for item in oeb.manifest.values():
if item not in used: if item not in used:
if getattr(self.opts, 'mobi_periodical', False) and \
item.href == 'images/mastheadImage.gif':
continue
oeb.logger.info('Trimming %r from manifest' % item.href) oeb.logger.info('Trimming %r from manifest' % item.href)
oeb.manifest.remove(item) oeb.manifest.remove(item)

View File

@ -35,7 +35,7 @@ class OEBWriter(object):
help=_('OPF version to generate. Default is %default.')) help=_('OPF version to generate. Default is %default.'))
oeb('adobe_page_map', ['--adobe-page-map'], default=False, oeb('adobe_page_map', ['--adobe-page-map'], default=False,
help=_('Generate an Adobe "page-map" file if pagination ' help=_('Generate an Adobe "page-map" file if pagination '
'information is avaliable.')) 'information is available.'))
return cfg return cfg
@classmethod @classmethod

View File

@ -23,7 +23,7 @@ class PDBInput(InputFormatPlugin):
Reader = get_reader(header.ident) Reader = get_reader(header.ident)
if Reader is None: if Reader is None:
raise PDBError('No reader avaliable for format within container.\n Identity is %s. Book type is %s' % (header.ident, IDENTITY_TO_NAME.get(header.ident, _('Unknown')))) raise PDBError('No reader available for format within container.\n Identity is %s. Book type is %s' % (header.ident, IDENTITY_TO_NAME.get(header.ident, _('Unknown'))))
log.debug('Detected ebook format as: %s with identity: %s' % (IDENTITY_TO_NAME[header.ident], header.ident)) log.debug('Detected ebook format as: %s with identity: %s' % (IDENTITY_TO_NAME[header.ident], header.ident))

View File

@ -42,7 +42,7 @@ class PDBOutput(OutputFormatPlugin):
Writer = get_writer(opts.format) Writer = get_writer(opts.format)
if Writer is None: if Writer is None:
raise PDBError('No writer avaliable for format %s.' % format) raise PDBError('No writer available for format %s.' % format)
writer = Writer(opts, log) writer = Writer(opts, log)

View File

@ -17,7 +17,7 @@ def txt_to_markdown(txt, title=''):
md = markdown.Markdown( md = markdown.Markdown(
extensions=['footnotes', 'tables', 'toc'], extensions=['footnotes', 'tables', 'toc'],
safe_mode=False,) safe_mode=False,)
html = '<html><head><title>%s</title></head><body>%s</body></html>' % (title, html = u'<html><head><title>%s</title></head><body>%s</body></html>' % (title,
md.convert(txt)) md.convert(txt))
return html return html

View File

@ -57,7 +57,7 @@ class MetadataWidget(Widget, Ui_Form):
try: try:
self.series_index.setValue(mi.series_index) self.series_index.setValue(mi.series_index)
except: except:
self.series_index.setValue(0) self.series_index.setValue(1.0)
cover = self.db.cover(self.book_id, index_is_id=True) cover = self.db.cover(self.book_id, index_is_id=True)
if cover: if cover:

View File

@ -14,7 +14,7 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>&amp;Title for Table of Contents:</string> <string>&amp;Title for Table of Contents:</string>
@ -24,24 +24,24 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="opt_toc_title"/> <widget class="QLineEdit" name="opt_toc_title"/>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="opt_rescale_images"> <widget class="QCheckBox" name="opt_rescale_images">
<property name="text"> <property name="text">
<string>Rescale images for &amp;Palm devices</string> <string>Rescale images for &amp;Palm devices</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="opt_prefer_author_sort"> <widget class="QCheckBox" name="opt_prefer_author_sort">
<property name="text"> <property name="text">
<string>Use author &amp;sort for author</string> <string>Use author &amp;sort for author</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -54,20 +54,27 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QCheckBox" name="opt_dont_compress"> <widget class="QCheckBox" name="opt_dont_compress">
<property name="text"> <property name="text">
<string>Disable compression of the file contents</string> <string>Disable compression of the file contents</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QCheckBox" name="opt_mobi_periodical"> <widget class="QCheckBox" name="opt_mobi_periodical">
<property name="text"> <property name="text">
<string>Generate a periodical rather than a book</string> <string>Generate a periodical rather than a book</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0">
<widget class="QCheckBox" name="opt_no_inline_toc">
<property name="text">
<string>Do not add Table of Contents to book</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -17,7 +17,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Select avaliable formats and their order for this device</string> <string>Select available formats and their order for this device</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_7"> <layout class="QVBoxLayout" name="verticalLayout_7">
<item> <item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

View File

@ -267,20 +267,7 @@ class BooksModel(QAbstractTableModel):
self.endInsertRows() self.endInsertRows()
self.count_changed() self.count_changed()
def clean_search_text(self, text):
if not text:
return text
tokens = text.split(' ')
for i, token in enumerate(tokens):
if token.strip().endswith(':') or token.strip() == '':
del tokens[i]
text = ' '.join(tokens)
if text.strip() == '':
text = None
return text
def search(self, text, refinement, reset=True): def search(self, text, refinement, reset=True):
text = self.clean_search_text(text)
self.db.search(text) self.db.search(text)
self.last_search = text self.last_search = text
if reset: if reset:
@ -899,9 +886,9 @@ class DeviceBooksModel(BooksModel):
flags |= Qt.ItemIsEditable flags |= Qt.ItemIsEditable
return flags return flags
def search(self, text, refinement, reset=True): def search(self, text, refinement, reset=True):
text = self.clean_search_text(text) if not text or not text.strip():
if not text:
self.map = list(range(len(self.db))) self.map = list(range(len(self.db)))
else: else:
matches = self.search_engine.parse(text) matches = self.search_engine.parse(text)

View File

@ -313,6 +313,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.view.goto_bookmark(m) self.view.goto_bookmark(m)
else: else:
self.pending_bookmark = bm self.pending_bookmark = bm
if spine_index < 0 or spine_index >= len(self.iterator.spine):
spine_index = 0
self.load_path(self.iterator.spine[spine_index]) self.load_path(self.iterator.spine[spine_index])
def toc_clicked(self, index): def toc_clicked(self, index):

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,8 @@ recipe_modules = ['recipe_' + r for r in (
'marca', 'kellog_faculty', 'kellog_insight', 'marca', 'kellog_faculty', 'kellog_insight',
'theeconomictimes_india', '7dias', 'buenosaireseconomico', 'theeconomictimes_india', '7dias', 'buenosaireseconomico',
'diagonales', 'miradasalsur', 'newsweek_argentina', 'veintitres', 'diagonales', 'miradasalsur', 'newsweek_argentina', 'veintitres',
'gva_be', 'hln', 'tijd', 'degentenaar', 'gva_be', 'hln', 'tijd', 'degentenaar', 'inquirer_net', 'uncrate',
'fastcompany', 'accountancyage',
)] )]
import re, imp, inspect, time, os import re, imp, inspect, time, os

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
'''
www.accountancyage.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class AccountancyAge(BasicNewsRecipe):
title = 'Accountancy Age'
__author__ = 'Darko Miletic'
description = 'business news'
publisher = 'accountancyage.com'
category = 'news, politics, finances'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
simultaneous_downloads = 1
encoding = 'utf-8'
lang = 'en'
language = _('English')
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [dict(name='div', attrs={'class':'bodycol'})]
remove_tags = [dict(name=['embed','object'])]
remove_tags_after = dict(name='div', attrs={'id':'permalink'})
remove_tags_before = dict(name='div', attrs={'class':'gap6'})
feeds = [(u'All News', u'http://feeds.accountancyage.com/rss/latest/accountancyage/all')]
def print_version(self, url):
rest, sep, miss = url.rpartition('/')
rr, ssep, artid = rest.rpartition('/')
return u'http://www.accountancyage.com/articles/print/' + artid
def get_article_url(self, article):
return article.get('guid', None)
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)])
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
soup.head.insert(0,mlang)
soup.head.insert(1,mcharset)
return self.adeify_images(soup)

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
'''
www.fastcompany.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class FastCompany(BasicNewsRecipe):
title = 'Fast Company'
__author__ = 'Darko Miletic'
description = 'Where ideas and people meet'
publisher = 'fastcompany.com'
category = 'news, technology, gadgets, games'
oldest_article = 15
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = True
simultaneous_downloads = 1
encoding = 'utf-8'
lang = 'en'
language = _('English')
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
remove_tags = [dict(name=['embed','object']), dict(name='div',attrs={'class':'feedflare'})]
feeds = [(u'All News', u'http://feeds.feedburner.com/fastcompany/headlines')]
def get_article_url(self, article):
return article.get('guid', None)
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)])
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
soup.head.insert(0,mlang)
soup.head.insert(1,mcharset)
for item in soup.findAll('a'):
sp = item['href'].find('http://feedads.g.doubleclick.net/')
if sp != -1:
item.extract()
return self.adeify_images(soup)

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
www.inquirer.net
'''
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class InquirerNet(BasicNewsRecipe):
title = 'Inquirer.net'
__author__ = 'Darko Miletic'
description = 'News from Philipines'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
publisher = 'inquirer.net'
category = 'news, politics, philipines'
lang = 'en'
language = _('English')
extra_css = ' .fontheadline{font-size: x-large} .fontsubheadline{font-size: large} .fontkick{font-size: medium}'
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
, '--ignore-tables'
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
remove_tags = [dict(name=['object','link','script','iframe','form'])]
feeds = [
(u'Breaking news', u'http://services.inquirer.net/rss/breakingnews.xml' )
,(u'Top stories' , u'http://services.inquirer.net/rss/topstories.xml' )
,(u'Sports' , u'http://services.inquirer.net/rss/brk_breakingnews.xml' )
,(u'InfoTech' , u'http://services.inquirer.net/rss/infotech_tech.xml' )
,(u'InfoTech' , u'http://services.inquirer.net/rss/infotech_tech.xml' )
,(u'Business' , u'http://services.inquirer.net/rss/inq7money_breaking_news.xml' )
,(u'Editorial' , u'http://services.inquirer.net/rss/opinion_editorial.xml' )
,(u'Global Nation', u'http://services.inquirer.net/rss/globalnation_breakingnews.xml')
]
def preprocess_html(self, soup):
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.head.insert(0,mlang)
soup.head.insert(1,mcharset)
for item in soup.findAll(style=True):
del item['style']
return soup
def print_version(self, url):
rest, sep, art = url.rpartition('/view/')
art_id, sp, rrest = art.partition('/')
return 'http://services.inquirer.net/print/print.php?article_id=' + art_id

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
''' '''
time.com time.com
''' '''
@ -11,17 +11,21 @@ from calibre.web.feeds.news import BasicNewsRecipe
class Time(BasicNewsRecipe): class Time(BasicNewsRecipe):
title = u'Time' title = u'Time'
__author__ = 'Kovid Goyal' __author__ = 'Kovid Goyal and Sujata Raman'
description = 'Weekly magazine' description = 'Weekly magazine'
encoding = 'utf-8' encoding = 'utf-8'
no_stylesheets = True no_stylesheets = True
language = _('English') language = _('English')
extra_css = '''.headline {font-size: large;}
.fact { padding-top: 10pt }
h1 {font-family:Arial,Sans-serif}
.byline{font-family:Arial,Sans-serif; font-size:xx-small ;color:blue}
.timestamp{font-family:Arial,Sans-serif; font-size:x-small ;color:gray}'''
remove_tags_before = dict(id="artHd") remove_tags_before = dict(id="artHd")
remove_tags_after = {'class':"ltCol"} remove_tags_after = {'class':"ltCol"}
remove_tags = [ remove_tags = [
{'class':['articleTools', 'enlarge', 'search']}, {'class':['articleTools', 'enlarge', 'search','socialtools','blogtools','moretools','page','nextUp','next','subnav','RSS','line2','first','ybuzz','articlePagination','chiclets','imgcont','createListLink','rlinks','tabsWrap','pagination']},
{'id':['quigoArticle', 'contentTools', 'articleSideBar', 'header', 'navTop']}, {'id':['quigoArticle', 'contentTools', 'articleSideBar', 'header', 'navTop','articleTools','feedmodule','feedmodule3','promos','footer','linksFooter','timeArchive','belt','relatedStories','packages','Features']},
{'target':'_blank'}, {'target':'_blank'},
] ]
recursions = 1 recursions = 1

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
www.uncrate.com
'''
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
class Uncrate(BasicNewsRecipe):
title = 'Uncrate'
__author__ = 'Darko Miletic'
description = 'Uncrate is a web magazine for guys who love stuff. Our team digs up the best gadgets, clothes, cars, DVDs and more. New items are posted daily. Enjoy responsively.'
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
publisher = 'Zombie corp.'
category = 'news, gadgets, clothes, cars, DVDs'
lang = 'en-US'
language = _('English')
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
keep_only_tags = [dict(name='div', attrs={'class':'lefttext'})]
remove_tags_after = dict(name='div', attrs={'class':'serif'})
remove_tags = [dict(name=['object','link','script','iframe','form'])]
feeds = [(u'Articles', u'http://feeds.feedburner.com/uncrate')]
def preprocess_html(self, soup):
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.head.insert(0,mlang)
soup.head.insert(1,mcharset)
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)