mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from trunk
This commit is contained in:
commit
0b59ff9c09
@ -262,6 +262,17 @@ class MOBIMetadataWriter(MetadataWriterPlugin):
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.mobi import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
class PDFMetadataWriter(MetadataWriterPlugin):
|
||||
|
||||
name = 'Set PDF metadata'
|
||||
file_types = set(['pdf'])
|
||||
description = _('Set metadata in %s files') % 'PDF'
|
||||
author = 'John Schember'
|
||||
|
||||
def set_metadata(self, stream, mi, type):
|
||||
from calibre.ebooks.metadata.pdf import set_metadata
|
||||
set_metadata(stream, mi)
|
||||
|
||||
|
||||
from calibre.ebooks.epub.input import EPUBInput
|
||||
|
@ -2,7 +2,7 @@ from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import os, shutil, traceback, functools, sys
|
||||
import os, shutil, traceback, functools, sys, re
|
||||
|
||||
from calibre.customize import Plugin, FileTypePlugin, MetadataReaderPlugin, \
|
||||
MetadataWriterPlugin
|
||||
@ -55,7 +55,14 @@ def load_plugin(path_to_zip_file):
|
||||
for name in zf.namelist():
|
||||
if name.lower().endswith('plugin.py'):
|
||||
locals = {}
|
||||
exec zf.read(name) in locals
|
||||
raw = zf.read(name)
|
||||
match = re.search(r'coding[:=]\s*([-\w.]+)', raw[:300])
|
||||
encoding = 'utf-8'
|
||||
if match is not None:
|
||||
encoding = match.group(1)
|
||||
raw = raw.decode(encoding)
|
||||
raw = re.sub('\r\n', '\n', raw)
|
||||
exec raw in locals
|
||||
for x in locals.values():
|
||||
if isinstance(x, type) and issubclass(x, Plugin):
|
||||
if x.minimum_calibre_version > version or \
|
||||
|
@ -31,6 +31,11 @@ Run an embedded python interpreter.
|
||||
parser.add_option('--migrate', action='store_true', default=False,
|
||||
help='Migrate old database. Needs two arguments. Path '
|
||||
'to library1.db and path to new library folder.')
|
||||
parser.add_option('--add-simple-plugin', default=None,
|
||||
help='Add a simple plugin (i.e. a plugin that consists of only a '
|
||||
'.py file), by specifying the path to the py file containing the '
|
||||
'plugin code.')
|
||||
|
||||
return parser
|
||||
|
||||
def update_zipfile(zipfile, mod, path):
|
||||
@ -115,6 +120,22 @@ def debug_device_driver():
|
||||
print 'Total space:', d.total_space()
|
||||
break
|
||||
|
||||
def add_simple_plugin(path_to_plugin):
|
||||
import tempfile, zipfile, shutil
|
||||
tdir = tempfile.mkdtemp()
|
||||
open(os.path.join(tdir, 'custom_plugin.py'),
|
||||
'wb').write(open(path_to_plugin, 'rb').read())
|
||||
odir = os.getcwd()
|
||||
os.chdir(tdir)
|
||||
zf = zipfile.ZipFile('plugin.zip', 'w')
|
||||
zf.write('custom_plugin.py')
|
||||
zf.close()
|
||||
from calibre.customize.ui import main
|
||||
main(['calibre-customize', '-a', 'plugin.zip'])
|
||||
os.chdir(odir)
|
||||
shutil.rmtree(tdir)
|
||||
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
opts, args = option_parser().parse_args(args)
|
||||
@ -137,6 +158,8 @@ def main(args=sys.argv):
|
||||
print 'You must specify the path to library1.db and the path to the new library folder'
|
||||
return 1
|
||||
migrate(args[1], args[2])
|
||||
elif opts.add_simple_plugin is not None:
|
||||
add_simple_plugin(opts.add_simple_plugin)
|
||||
else:
|
||||
from IPython.Shell import IPShellEmbed
|
||||
ipshell = IPShellEmbed()
|
||||
|
@ -57,12 +57,17 @@ class USBMS(Device):
|
||||
prefix = self._card_prefix if oncard else self._main_prefix
|
||||
ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN
|
||||
|
||||
# Get all books in all directories under the root ebook_dir directory
|
||||
for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)):
|
||||
# Filter out anything that isn't in the list of supported ebook
|
||||
# types
|
||||
for book_type in self.FORMATS:
|
||||
for filename in fnmatch.filter(files, '*.%s' % (book_type)):
|
||||
# Get all books in the ebook_dir directory
|
||||
if self.SUPPORTS_SUB_DIRS:
|
||||
for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)):
|
||||
# Filter out anything that isn't in the list of supported ebook types
|
||||
for book_type in self.FORMATS:
|
||||
for filename in fnmatch.filter(files, '*.%s' % (book_type)):
|
||||
bl.append(self.__class__.book_from_path(os.path.join(path, filename)))
|
||||
else:
|
||||
path = os.path.join(prefix, ebook_dir)
|
||||
for filename in os.listdir(path):
|
||||
if path_to_ext(filename) in self.FORMATS:
|
||||
bl.append(self.__class__.book_from_path(os.path.join(path, filename)))
|
||||
return bl
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
'''Read meta information from PDF files'''
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
'''Read meta information from PDF files'''
|
||||
|
||||
import sys, os, re, StringIO
|
||||
import sys, os, StringIO
|
||||
|
||||
from calibre.ebooks.metadata import MetaInformation, authors_to_string
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
@ -31,7 +30,7 @@ def get_metadata(stream, extract_cover=True):
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
try:
|
||||
info = PdfFileReader(stream).getDocumentInfo()
|
||||
if info.title:
|
||||
@ -52,47 +51,56 @@ def get_metadata(stream, extract_cover=True):
|
||||
|
||||
def set_metadata(stream, mi):
|
||||
stream.seek(0)
|
||||
raw = stream.read()
|
||||
if mi.title:
|
||||
tit = mi.title.encode('utf-8') if isinstance(mi.title, unicode) else mi.title
|
||||
raw = re.compile(r'<<.*?/Title\((.+?)\)', re.DOTALL).sub(lambda m: m.group().replace(m.group(1), tit), raw)
|
||||
if mi.authors:
|
||||
au = authors_to_string(mi.authors)
|
||||
if isinstance(au, unicode):
|
||||
au = au.encode('utf-8')
|
||||
raw = re.compile(r'<<.*?/Author\((.+?)\)', re.DOTALL).sub(lambda m: m.group().replace(m.group(1), au), raw)
|
||||
|
||||
# Use a StringIO object for the pdf because we will want to over
|
||||
# write it later and if we are working on the stream directly it
|
||||
# could cause some issues.
|
||||
raw = StringIO.StringIO(stream.read())
|
||||
orig_pdf = PdfFileReader(raw)
|
||||
|
||||
title = mi.title if mi.title else orig_pdf.documentInfo.title
|
||||
author = authors_to_string(mi.authors) if mi.authors else orig_pdf.documentInfo.author
|
||||
|
||||
out_pdf = PdfFileWriter(title=title, author=author)
|
||||
for page in orig_pdf.pages:
|
||||
out_pdf.addPage(page)
|
||||
|
||||
out_str = StringIO.StringIO()
|
||||
out_pdf.write(out_str)
|
||||
|
||||
stream.seek(0)
|
||||
stream.truncate()
|
||||
stream.write(raw)
|
||||
out_str.seek(0)
|
||||
stream.write(out_str.read())
|
||||
stream.seek(0)
|
||||
|
||||
def get_cover(stream):
|
||||
data = StringIO.StringIO()
|
||||
|
||||
|
||||
try:
|
||||
pdf = PdfFileReader(stream)
|
||||
output = PdfFileWriter()
|
||||
|
||||
|
||||
if len(pdf.pages) >= 1:
|
||||
output.addPage(pdf.getPage(0))
|
||||
|
||||
|
||||
with TemporaryDirectory('_pdfmeta') as tdir:
|
||||
cover_path = os.path.join(tdir, 'cover.pdf')
|
||||
|
||||
|
||||
outputStream = file(cover_path, "wb")
|
||||
output.write(outputStream)
|
||||
outputStream.close()
|
||||
|
||||
|
||||
wand = NewMagickWand()
|
||||
MagickReadImage(wand, cover_path)
|
||||
MagickSetImageFormat(wand, 'JPEG')
|
||||
MagickWriteImage(wand, '%s.jpg' % cover_path)
|
||||
|
||||
|
||||
img = Image.open('%s.jpg' % cover_path)
|
||||
|
||||
|
||||
img.save(data, 'JPEG')
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
return data.getvalue()
|
||||
|
@ -15,7 +15,8 @@ except ImportError:
|
||||
|
||||
from lxml import html, etree
|
||||
|
||||
from calibre import entity_to_unicode
|
||||
from calibre import entity_to_unicode, sanitize_file_name
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks import DRMError
|
||||
from calibre.ebooks.chardet import ENCODING_PATS
|
||||
from calibre.ebooks.mobi import MobiError
|
||||
@ -25,7 +26,6 @@ from calibre.ebooks.mobi.langcodes import main_language, sub_language
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre import sanitize_file_name
|
||||
|
||||
class EXTHHeader(object):
|
||||
|
||||
@ -157,6 +157,62 @@ class BookHeader(object):
|
||||
self.exth.mi.language = self.language
|
||||
|
||||
|
||||
class MetadataHeader(BookHeader):
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
|
||||
self.ident = self.identity()
|
||||
self.num_sections = self.section_count()
|
||||
|
||||
if self.num_sections >= 2:
|
||||
header = self.header()
|
||||
BookHeader.__init__(self, header, self.ident, None)
|
||||
else:
|
||||
self.exth = None
|
||||
|
||||
def identity(self):
|
||||
self.stream.seek(60)
|
||||
ident = self.stream.read(8).upper()
|
||||
|
||||
if ident not in ['BOOKMOBI', 'TEXTREAD']:
|
||||
raise MobiError('Unknown book type: %s' % ident)
|
||||
return ident
|
||||
|
||||
def section_count(self):
|
||||
self.stream.seek(76)
|
||||
return struct.unpack('>H', self.stream.read(2))[0]
|
||||
|
||||
def section_offset(self, number):
|
||||
self.stream.seek(78+number*8)
|
||||
return struct.unpack('>LBBBB', self.stream.read(8))[0]
|
||||
|
||||
def header(self):
|
||||
section_headers = []
|
||||
|
||||
# First section with the metadata
|
||||
section_headers.append(self.section_offset(0))
|
||||
# Second section used to get the lengh of the first
|
||||
section_headers.append(self.section_offset(1))
|
||||
|
||||
end_off = section_headers[1]
|
||||
off = section_headers[0]
|
||||
|
||||
self.stream.seek(off)
|
||||
return self.stream.read(end_off - off)
|
||||
|
||||
def section_data(self, number):
|
||||
start = self.section_offset(number)
|
||||
|
||||
if number == self.num_sections -1:
|
||||
end = os.stat(self.stream.name).st_size
|
||||
else:
|
||||
end = self.section_offset(number + 1)
|
||||
|
||||
self.stream.seek(start)
|
||||
|
||||
return self.stream.read(end - start)
|
||||
|
||||
|
||||
class MobiReader(object):
|
||||
PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE)
|
||||
IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex')
|
||||
@ -593,27 +649,34 @@ class MobiReader(object):
|
||||
im.convert('RGB').save(open(path, 'wb'), format='JPEG')
|
||||
|
||||
def get_metadata(stream):
|
||||
from calibre.utils.logging import Log
|
||||
log = Log()
|
||||
mr = MobiReader(stream, log)
|
||||
if mr.book_header.exth is None:
|
||||
mi = MetaInformation(mr.name, [_('Unknown')])
|
||||
else:
|
||||
mi = mr.create_opf('dummy.html')[0]
|
||||
try:
|
||||
if hasattr(mr.book_header.exth, 'cover_offset'):
|
||||
cover_index = mr.book_header.first_image_index + \
|
||||
mr.book_header.exth.cover_offset
|
||||
data = mr.sections[int(cover_index)][0]
|
||||
else:
|
||||
data = mr.sections[mr.book_header.first_image_index][0]
|
||||
buf = cStringIO.StringIO(data)
|
||||
im = PILImage.open(buf)
|
||||
obuf = cStringIO.StringIO()
|
||||
im.convert('RGBA').save(obuf, format='JPEG')
|
||||
mi.cover_data = ('jpg', obuf.getvalue())
|
||||
except:
|
||||
log.exception()
|
||||
mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
|
||||
try:
|
||||
mh = MetadataHeader(stream)
|
||||
|
||||
if mh.exth is not None:
|
||||
if mh.exth.mi is not None:
|
||||
mi = mh.exth.mi
|
||||
else:
|
||||
with TemporaryDirectory('_mobi_meta_reader') as tdir:
|
||||
mr = MobiReader(stream)
|
||||
mr.extract_content(tdir, {})
|
||||
if mr.embedded_mi is not None:
|
||||
mi = mr.embedded_mi
|
||||
|
||||
if hasattr(mh.exth, 'cover_offset'):
|
||||
cover_index = mh.first_image_index + mh.exth.cover_offset
|
||||
data = mh.section_data(int(cover_index))
|
||||
else:
|
||||
data = mh.section_data(mh.first_image_index)
|
||||
buf = cStringIO.StringIO(data)
|
||||
im = PILImage.open(buf)
|
||||
obuf = cStringIO.StringIO()
|
||||
im.convert('RGBA').save(obuf, format='JPEG')
|
||||
mi.cover_data = ('jpg', obuf.getvalue())
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
return mi
|
||||
|
||||
|
||||
|
@ -67,6 +67,8 @@ def _config():
|
||||
c.add_opt('default_send_to_device_action', default=None,
|
||||
help=_('Default action to perform when send to device button is '
|
||||
'clicked'))
|
||||
c.add_opt('show_donate_button', default=True,
|
||||
help='Show donation button')
|
||||
return ConfigProxy(c)
|
||||
|
||||
config = _config()
|
||||
|
@ -658,7 +658,9 @@ class DeviceGUI(object):
|
||||
bad = '\n'.join('<li>%s</li>'%(i,) for i in bad)
|
||||
d = warning_dialog(self, _('No suitable formats'),
|
||||
_('Could not upload the following books to the device, '
|
||||
'as no suitable formats were found:<br><ul>%s</ul>')%(bad,))
|
||||
'as no suitable formats were found. Try changing the output '
|
||||
'format in the upper right corner next to the red heart and '
|
||||
're-converting. <br><ul>%s</ul>')%(bad,))
|
||||
d.exec_()
|
||||
|
||||
def upload_booklists(self):
|
||||
|
@ -176,19 +176,19 @@ class Config(ResizableDialog, Ui_Dialog):
|
||||
def get_metadata(self):
|
||||
title, authors = self.get_title_and_authors()
|
||||
mi = MetaInformation(title, authors)
|
||||
publisher = unicode(self.publisher.text())
|
||||
publisher = unicode(self.publisher.text()).strip()
|
||||
if publisher:
|
||||
mi.publisher = publisher
|
||||
author_sort = unicode(self.author_sort.text())
|
||||
author_sort = unicode(self.author_sort.text()).strip()
|
||||
if author_sort:
|
||||
mi.author_sort = author_sort
|
||||
comments = unicode(self.comment.toPlainText())
|
||||
comments = unicode(self.comment.toPlainText()).strip()
|
||||
if comments:
|
||||
mi.comments = comments
|
||||
mi.series_index = int(self.series_index.value())
|
||||
if self.series.currentIndex() > -1:
|
||||
mi.series = unicode(self.series.currentText())
|
||||
tags = [t.strip() for t in unicode(self.tags.text()).split(',')]
|
||||
mi.series = unicode(self.series.currentText()).strip()
|
||||
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
|
||||
if tags:
|
||||
mi.tags = tags
|
||||
|
||||
@ -267,6 +267,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
||||
).exec_()
|
||||
return
|
||||
mi = self.get_metadata()
|
||||
self.user_mi = mi
|
||||
self.read_settings()
|
||||
self.cover_file = None
|
||||
if self.row is not None:
|
||||
|
@ -108,6 +108,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.donate_action = self.system_tray_menu.addAction(
|
||||
QIcon(':/images/donate.svg'), _('&Donate to support calibre'))
|
||||
self.donate_button.setDefaultAction(self.donate_action)
|
||||
if not config['show_donate_button']:
|
||||
self.donate_button.setVisible(False)
|
||||
self.addAction(self.quit_action)
|
||||
self.action_restart = QAction(_('&Restart'), self)
|
||||
self.addAction(self.action_restart)
|
||||
|
@ -25,7 +25,7 @@ from calibre.ebooks.lrf.comic.convert_from import config as comicconfig
|
||||
|
||||
# Ordered list of source formats. Items closer to the beginning are
|
||||
# preferred for conversion over those toward the end.
|
||||
PREFERRED_SOURCE_FORMATS = ['epub', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf',
|
||||
PREFERRED_SOURCE_FORMATS = ['epub', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf',
|
||||
'txt', 'pdf', 'oebzip', 'htm', 'html']
|
||||
|
||||
def get_dialog(fmt):
|
||||
@ -43,20 +43,20 @@ def get_config(fmt):
|
||||
def auto_convert(fmt, parent, db, rows):
|
||||
changed = False
|
||||
jobs = []
|
||||
|
||||
|
||||
total = len(rows)
|
||||
if total == 0:
|
||||
return None, None, None
|
||||
parent.status_bar.showMessage(_('Starting auto conversion of %d books')%total, 2000)
|
||||
|
||||
|
||||
i = 0
|
||||
bad_rows = []
|
||||
|
||||
|
||||
for i, row in enumerate(rows):
|
||||
row_id = db.id(row)
|
||||
|
||||
|
||||
temp_files = []
|
||||
|
||||
|
||||
data = None
|
||||
in_formats = [f.lower() for f in db.formats(row).split(',')]
|
||||
in_formats = list(set(in_formats).intersection(available_input_formats()))
|
||||
@ -88,10 +88,10 @@ def auto_convert(fmt, parent, db, rows):
|
||||
for row in bad_rows:
|
||||
title = db.title(row)
|
||||
res.append('<li>%s</li>'%title)
|
||||
|
||||
|
||||
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(len(res), total, '\n'.join(res))
|
||||
warning_dialog(parent, _('Could not convert some books'), msg).exec_()
|
||||
|
||||
|
||||
return jobs, changed, bad_rows
|
||||
|
||||
def convert_single(fmt, parent, db, comics, others):
|
||||
@ -120,10 +120,10 @@ def convert_single(fmt, parent, db, comics, others):
|
||||
temp_files.append(d.cover_file)
|
||||
opts.cover = d.cover_file.name
|
||||
temp_files.extend([d.opf_file, pt, of])
|
||||
jobs.append(('any2'+fmt, args, _('Convert book: ')+d.mi.title,
|
||||
jobs.append(('any2'+fmt, args, _('Convert book: ')+d.mi.title,
|
||||
fmt.upper(), row_id, temp_files))
|
||||
changed = True
|
||||
|
||||
|
||||
for row, row_id in zip(comics, comics_ids):
|
||||
mi = db.get_metadata(row)
|
||||
title = author = _('Unknown')
|
||||
@ -140,7 +140,7 @@ def convert_single(fmt, parent, db, comics, others):
|
||||
try:
|
||||
data = db.format(row, _fmt.upper())
|
||||
if data is not None:
|
||||
break
|
||||
break
|
||||
except:
|
||||
continue
|
||||
pt = PersistentTemporaryFile('.'+_fmt)
|
||||
@ -152,12 +152,12 @@ def convert_single(fmt, parent, db, comics, others):
|
||||
opts.verbose = 2
|
||||
args = [pt.name, opts]
|
||||
changed = True
|
||||
jobs.append(('comic2'+fmt, args, _('Convert comic: ')+opts.title,
|
||||
jobs.append(('comic2'+fmt, args, _('Convert comic: ')+opts.title,
|
||||
fmt.upper(), row_id, [pt, of]))
|
||||
|
||||
|
||||
return jobs, changed
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def convert_single_lrf(parent, db, comics, others):
|
||||
changed = False
|
||||
@ -182,10 +182,10 @@ def convert_single_lrf(parent, db, comics, others):
|
||||
if d.cover_file:
|
||||
temp_files.append(d.cover_file)
|
||||
temp_files.extend([pt, of])
|
||||
jobs.append(('any2lrf', [cmdline], _('Convert book: ')+d.title(),
|
||||
jobs.append(('any2lrf', [cmdline], _('Convert book: ')+d.title(),
|
||||
'LRF', row_id, temp_files))
|
||||
changed = True
|
||||
|
||||
|
||||
for row, row_id in zip(comics, comics_ids):
|
||||
mi = db.get_metadata(row)
|
||||
title = author = _('Unknown')
|
||||
@ -202,7 +202,7 @@ def convert_single_lrf(parent, db, comics, others):
|
||||
try:
|
||||
data = db.format(row, fmt.upper())
|
||||
if data is not None:
|
||||
break
|
||||
break
|
||||
except:
|
||||
continue
|
||||
if data is None:
|
||||
@ -216,19 +216,20 @@ def convert_single_lrf(parent, db, comics, others):
|
||||
opts.verbose = 1
|
||||
args = [pt.name, opts]
|
||||
changed = True
|
||||
jobs.append(('comic2lrf', args, _('Convert comic: ')+opts.title,
|
||||
jobs.append(('comic2lrf', args, _('Convert comic: ')+opts.title,
|
||||
'LRF', row_id, [pt, of]))
|
||||
|
||||
|
||||
return jobs, changed
|
||||
|
||||
def convert_bulk(fmt, parent, db, comics, others):
|
||||
if others:
|
||||
d = get_dialog(fmt)(parent, db)
|
||||
if d.exec_() != QDialog.Accepted:
|
||||
others = []
|
||||
others, user_mi = [], None
|
||||
else:
|
||||
opts = d.opts
|
||||
opts.verbose = 2
|
||||
user_mi = d.user_mi
|
||||
if comics:
|
||||
comic_opts = ComicConf.get_bulk_conversion_options(parent)
|
||||
if not comic_opts:
|
||||
@ -239,7 +240,7 @@ def convert_bulk(fmt, parent, db, comics, others):
|
||||
if total == 0:
|
||||
return
|
||||
parent.status_bar.showMessage(_('Starting Bulk conversion of %d books')%total, 2000)
|
||||
|
||||
|
||||
for i, row in enumerate(others+comics):
|
||||
row_id = db.id(row)
|
||||
if row in others:
|
||||
@ -256,6 +257,11 @@ def convert_bulk(fmt, parent, db, comics, others):
|
||||
continue
|
||||
options = opts.copy()
|
||||
mi = db.get_metadata(row)
|
||||
if user_mi is not None:
|
||||
if user_mi.series_index == 1:
|
||||
user_mi.series_index = None
|
||||
mi.smart_update(user_mi)
|
||||
db.set_metadata(db.id(row), mi)
|
||||
opf = OPFCreator(os.getcwdu(), mi)
|
||||
opf_file = PersistentTemporaryFile('.opf')
|
||||
opf.render(opf_file)
|
||||
@ -291,10 +297,10 @@ def convert_bulk(fmt, parent, db, comics, others):
|
||||
try:
|
||||
data = db.format(row, _fmt.upper())
|
||||
if data is not None:
|
||||
break
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
|
||||
pt = PersistentTemporaryFile('.'+_fmt.lower())
|
||||
pt.write(data)
|
||||
pt.close()
|
||||
@ -304,17 +310,17 @@ def convert_bulk(fmt, parent, db, comics, others):
|
||||
options.verbose = 1
|
||||
args = [pt.name, options]
|
||||
desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title))
|
||||
jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of]))
|
||||
|
||||
jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of]))
|
||||
|
||||
if bad_rows:
|
||||
res = []
|
||||
for row in bad_rows:
|
||||
title = db.title(row)
|
||||
res.append('<li>%s</li>'%title)
|
||||
|
||||
|
||||
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(len(res), total, '\n'.join(res))
|
||||
warning_dialog(parent, _('Could not convert some books'), msg).exec_()
|
||||
|
||||
|
||||
return jobs, False
|
||||
|
||||
|
||||
@ -333,7 +339,7 @@ def convert_bulk_lrf(parent, db, comics, others):
|
||||
if total == 0:
|
||||
return
|
||||
parent.status_bar.showMessage(_('Starting Bulk conversion of %d books')%total, 2000)
|
||||
|
||||
|
||||
for i, row in enumerate(others+comics):
|
||||
row_id = db.id(row)
|
||||
if row in others:
|
||||
@ -388,10 +394,10 @@ def convert_bulk_lrf(parent, db, comics, others):
|
||||
try:
|
||||
data = db.format(row, fmt.upper())
|
||||
if data is not None:
|
||||
break
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
|
||||
pt = PersistentTemporaryFile('.'+fmt.lower())
|
||||
pt.write(data)
|
||||
pt.close()
|
||||
@ -401,17 +407,17 @@ def convert_bulk_lrf(parent, db, comics, others):
|
||||
options.verbose = 1
|
||||
args = [pt.name, options]
|
||||
desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title))
|
||||
jobs.append(('comic2lrf', args, desc, 'LRF', row_id, [pt, of]))
|
||||
|
||||
jobs.append(('comic2lrf', args, desc, 'LRF', row_id, [pt, of]))
|
||||
|
||||
if bad_rows:
|
||||
res = []
|
||||
for row in bad_rows:
|
||||
title = db.title(row)
|
||||
res.append('<li>%s</li>'%title)
|
||||
|
||||
|
||||
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(len(res), total, '\n'.join(res))
|
||||
warning_dialog(parent, _('Could not convert some books'), msg).exec_()
|
||||
|
||||
|
||||
return jobs, False
|
||||
|
||||
def set_conversion_defaults_lrf(comic, parent, db):
|
||||
@ -438,7 +444,7 @@ def _fetch_news(data, fmt):
|
||||
args.extend(['--password', data['password']])
|
||||
args.append(data['script'] if data['script'] else data['title'])
|
||||
return 'feeds2'+fmt.lower(), [args], _('Fetch news from ')+data['title'], fmt.upper(), [pt]
|
||||
|
||||
|
||||
|
||||
def fetch_scheduled_recipe(recipe, script):
|
||||
from calibre.gui2.dialogs.scheduler import config
|
||||
@ -453,7 +459,7 @@ def fetch_scheduled_recipe(recipe, script):
|
||||
args.extend(['--username', x[0], '--password', x[1]])
|
||||
args.append(script)
|
||||
return 'feeds2'+fmt, [args], _('Fetch news from ')+recipe.title, fmt.upper(), [pt]
|
||||
|
||||
|
||||
def auto_convert_ebook(*args):
|
||||
return auto_convert(*args)
|
||||
|
||||
@ -463,14 +469,14 @@ def convert_single_ebook(*args):
|
||||
return convert_single_lrf(*args)
|
||||
elif fmt in ('epub', 'mobi'):
|
||||
return convert_single(fmt, *args)
|
||||
|
||||
|
||||
def convert_bulk_ebooks(*args):
|
||||
fmt = prefs['output_format'].lower()
|
||||
if fmt == 'lrf':
|
||||
return convert_bulk_lrf(*args)
|
||||
elif fmt in ('epub', 'mobi'):
|
||||
return convert_bulk(fmt, *args)
|
||||
|
||||
|
||||
def set_conversion_defaults(comic, parent, db):
|
||||
fmt = prefs['output_format'].lower()
|
||||
if fmt == 'lrf':
|
||||
|
@ -7,19 +7,19 @@ from calibre.utils.config import Config, StringConfig
|
||||
def server_config(defaults=None):
|
||||
desc=_('Settings to control the calibre content server')
|
||||
c = Config('server', desc) if defaults is None else StringConfig(defaults, desc)
|
||||
|
||||
c.add_opt('port', ['-p', '--port'], default=8080,
|
||||
|
||||
c.add_opt('port', ['-p', '--port'], default=8080,
|
||||
help=_('The port on which to listen. Default is %default'))
|
||||
c.add_opt('timeout', ['-t', '--timeout'], default=120,
|
||||
c.add_opt('timeout', ['-t', '--timeout'], default=120,
|
||||
help=_('The server timeout in seconds. Default is %default'))
|
||||
c.add_opt('thread_pool', ['--thread-pool'], default=30,
|
||||
c.add_opt('thread_pool', ['--thread-pool'], default=30,
|
||||
help=_('The max number of worker threads to use. Default is %default'))
|
||||
c.add_opt('password', ['--password'], default=None,
|
||||
c.add_opt('password', ['--password'], default=None,
|
||||
help=_('Set a password to restrict access. By default access is unrestricted.'))
|
||||
c.add_opt('username', ['--username'], default='calibre',
|
||||
help=_('Username for access. By default, it is: %default'))
|
||||
c.add_opt('develop', ['--develop'], default=False,
|
||||
help='Development mode. Server automatically restarts on file changes and serves code files (html, css, js) from the file system instead of calibre\'s resource system.')
|
||||
c.add_opt('max_cover', ['--max-cover'], default='600x800',
|
||||
c.add_opt('max_cover', ['--max-cover'], default='600x800',
|
||||
help=_('The maximum size for displayed covers. Default is %default.'))
|
||||
return c
|
||||
|
@ -39,6 +39,7 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'nacional_cro', '24sata', 'dnevni_avaz', 'glas_srpske', '24sata_rs',
|
||||
'krstarica', 'krstarica_en', 'tanjug', 'laprensa_ni', 'azstarnet',
|
||||
'corriere_della_sera_it', 'corriere_della_sera_en', 'msdnmag_en',
|
||||
'moneynews',
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
49
src/calibre/web/feeds/recipes/recipe_moneynews.py
Normal file
49
src/calibre/web/feeds/recipes/recipe_moneynews.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
moneynews.newsmax.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class MoneyNews(BasicNewsRecipe):
|
||||
title = 'Moneynews.com'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Financial news worldwide'
|
||||
publisher = 'moneynews.com'
|
||||
category = 'news, finances, USA, business'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
|
||||
feeds = [
|
||||
(u'Street Talk' , u'http://moneynews.newsmax.com/xml/streettalk.xml' )
|
||||
,(u'Finance News' , u'http://moneynews.newsmax.com/xml/FinanceNews.xml' )
|
||||
,(u'Economy' , u'http://moneynews.newsmax.com/xml/economy.xml' )
|
||||
,(u'Companies' , u'http://moneynews.newsmax.com/xml/companies.xml' )
|
||||
,(u'Markets' , u'http://moneynews.newsmax.com/xml/Markets.xml' )
|
||||
,(u'Investing & Analysis' , u'http://moneynews.newsmax.com/xml/investing.xml' )
|
||||
]
|
||||
|
||||
|
||||
keep_only_tags = [dict(name='table', attrs={'class':'copy'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='td' , attrs={'id':'article_fontsize'})
|
||||
,dict(name='table', attrs={'id':'toolbox' })
|
||||
,dict(name='tr' , attrs={'id':'noprint3' })
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user