From 00180c7f273ab7b7e5c20fb389fb41620aff4dc4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 20 Jan 2009 23:49:25 -0800 Subject: [PATCH] Add MOBI as an output format to the GUI. calibre now fully supports MOBI output, except for conversion of comics. --- src/calibre/ebooks/mobi/from_any.py | 2 +- src/calibre/gui2/dialogs/epub.py | 30 +++++-- src/calibre/gui2/dialogs/epub.ui | 126 +++++++++++++++++++--------- src/calibre/gui2/dialogs/mobi.py | 20 +++++ src/calibre/gui2/main.py | 5 +- src/calibre/gui2/tools.py | 70 +++++++++------- src/calibre/parallel.py | 10 ++- 7 files changed, 177 insertions(+), 86 deletions(-) create mode 100644 src/calibre/gui2/dialogs/mobi.py diff --git a/src/calibre/ebooks/mobi/from_any.py b/src/calibre/ebooks/mobi/from_any.py index 5a60549cad..5607690e21 100644 --- a/src/calibre/ebooks/mobi/from_any.py +++ b/src/calibre/ebooks/mobi/from_any.py @@ -28,7 +28,7 @@ def option_parser(usage=USAGE): parser = config().option_parser(usage=usage) return parser -def any2mobi(opts, path): +def any2mobi(opts, path, notification=None): ext = os.path.splitext(path)[1] if not ext: raise ValueError('Unknown file type: '+path) diff --git a/src/calibre/gui2/dialogs/epub.py b/src/calibre/gui2/dialogs/epub.py index 8bd5ef9331..af5622169a 100644 --- a/src/calibre/gui2/dialogs/epub.py +++ b/src/calibre/gui2/dialogs/epub.py @@ -15,7 +15,7 @@ from lxml.etree import XPath from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.gui2.dialogs.epub_ui import Ui_Dialog from calibre.gui2 import error_dialog, choose_images, pixmap_to_data -from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config +from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig from calibre.ebooks.metadata import MetaInformation from calibre.ptempfile import PersistentTemporaryFile from calibre.ebooks.metadata.opf import OPFCreator @@ -24,9 +24,12 @@ from calibre.ebooks.metadata import authors_to_string, string_to_authors class Config(QDialog, Ui_Dialog): - def __init__(self, parent, db, row=None): + OUTPUT = 'EPUB' + + def __init__(self, parent, db, row=None, config=epubconfig): QDialog.__init__(self, parent) self.setupUi(self) + self.hide_controls() self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'), self.show_category_help) self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover) @@ -38,7 +41,7 @@ class Config(QDialog, Ui_Dialog): if row is not None: self.id = db.id(row) base = config().as_string() + '\n\n' - defaults = self.db.conversion_options(self.id, 'epub') + defaults = self.db.conversion_options(self.id, self.OUTPUT.lower()) defaults = base + (defaults if defaults else '') self.config = config(defaults=defaults) else: @@ -47,9 +50,18 @@ class Config(QDialog, Ui_Dialog): self.get_source_format() self.category_list.setCurrentRow(0) if self.row is None: - self.setWindowTitle(_('Bulk convert to EPUB')) + self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT) else: - self.setWindowTitle(_(u'Convert %s to EPUB')%unicode(self.title.text())) + self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT) + + def hide_controls(self): + self.source_profile_label.setVisible(False) + self.opt_source_profile.setVisible(False) + self.dest_profile_label.setVisible(False) + self.opt_dest_profile.setVisible(False) + self.opt_toc_title.setVisible(False) + self.toc_title_label.setVisible(False) + self.opt_rescale_images.setVisible(False) def initialize(self): self.__w = [] @@ -81,8 +93,8 @@ class Config(QDialog, Ui_Dialog): def show_category_help(self, item): text = unicode(item.text()) help = { - _('Metadata') : _('Specify metadata such as title and author for the book.\n\nMetadata will be updated in the database as well as the generated EPUB file.'), - _('Look & Feel') : _('Adjust the look of the generated EPUB file by specifying things like font sizes.'), + _('Metadata') : _('Specify metadata such as title and author for the book.\n\nMetadata will be updated in the database as well as the generated %s file.')%self.OUTPUT, + _('Look & Feel') : _('Adjust the look of the generated ebook by specifying things like font sizes.'), _('Page Setup') : _('Specify the page layout settings like margins.'), _('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'), } @@ -195,7 +207,7 @@ class Config(QDialog, Ui_Dialog): elif isinstance(g, QCheckBox): self.config.set(pref.name, bool(g.isChecked())) if self.row is not None: - self.db.set_conversion_options(self.id, 'epub', self.config.src) + self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src) def initialize_options(self): @@ -235,7 +247,7 @@ class Config(QDialog, Ui_Dialog): elif len(choices) == 1: self.source_format = choices[0] else: - d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to EPUB'), choices) + d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices) if d.exec_() == QDialog.Accepted: self.source_format = d.format() diff --git a/src/calibre/gui2/dialogs/epub.ui b/src/calibre/gui2/dialogs/epub.ui index f19ed7ed1a..7e3ad344ce 100644 --- a/src/calibre/gui2/dialogs/epub.ui +++ b/src/calibre/gui2/dialogs/epub.ui @@ -89,36 +89,6 @@ Book Cover - - - - - - - - - :/images/book.svg - - - true - - - Qt::AlignCenter - - - - - - - - - Use cover from &source file - - - true - - - @@ -170,6 +140,36 @@ + + + + Use cover from &source file + + + true + + + + + + + + + + + + :/images/book.svg + + + true + + + Qt::AlignCenter + + + + + opt_prefer_metadata_cover @@ -456,6 +456,13 @@ + + + + &Rescale images + + + @@ -475,7 +482,7 @@ - + &Profile: @@ -494,7 +501,7 @@ - + &Left Margin: @@ -504,7 +511,7 @@ - + pt @@ -517,7 +524,7 @@ - + &Right Margin: @@ -527,7 +534,7 @@ - + pt @@ -540,7 +547,7 @@ - + &Top Margin: @@ -550,7 +557,7 @@ - + pt @@ -563,7 +570,7 @@ - + &Bottom Margin: @@ -573,7 +580,7 @@ - + pt @@ -586,13 +593,39 @@ - + Do not &split on page breaks + + + + &Source profile: + + + opt_source_profile + + + + + + + + + + &Destination profile: + + + opt_dest_profile + + + + + + @@ -721,6 +754,19 @@ p, li { white-space: pre-wrap; } + + + + + + + &Title for generated TOC + + + opt_toc_title + + + diff --git a/src/calibre/gui2/dialogs/mobi.py b/src/calibre/gui2/dialogs/mobi.py new file mode 100644 index 0000000000..4019950c23 --- /dev/null +++ b/src/calibre/gui2/dialogs/mobi.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' + +from calibre.gui2.dialogs.epub import Config as _Config +from calibre.ebooks.mobi.from_any import config as mobiconfig + +class Config(_Config): + + OUTPUT = 'MOBI' + + def __init__(self, parent, db, row=None): + _Config.__init__(self, parent, db, row=row, config=mobiconfig) + + def hide_controls(self): + self.profile_label.setVisible(False) + self.opt_profile.setVisible(False) + self.opt_dont_split_on_page_breaks.setVisible(False) + self.opt_preserve_tag_structure.setVisible(False) \ No newline at end of file diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index b2d06a0502..a7c4c47add 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -25,7 +25,6 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \ max_available_height, config, info_dialog, \ available_width from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror -from calibre.library.database import LibraryDatabase from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.update import CheckForUpdates from calibre.gui2.dialogs.progress import ProgressDialog @@ -131,14 +130,14 @@ class Main(MainWindow, Ui_MainWindow): QObject.connect(self.stack, SIGNAL('currentChanged(int)'), self.location_view.location_changed) - self.output_formats = sorted(['EPUB', 'LRF']) + self.output_formats = sorted(['EPUB', 'MOBI', 'LRF']) for f in self.output_formats: self.output_format.addItem(f) self.output_format.setCurrentIndex(self.output_formats.index(prefs['output_format'])) def change_output_format(x): of = unicode(x).strip() if of != prefs['output_format']: - if of in ('EPUB', 'LIT'): + if of not in ('LRF',): warning_dialog(self, 'Warning', '

%s support is still in beta. If you find bugs, please report them by opening a ticket.'%of).exec_() prefs.set('output_format', of) diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index c00fbfe8e3..aca2da74e2 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -12,6 +12,7 @@ from PyQt4.Qt import QDialog from calibre.utils.config import prefs from calibre.gui2.dialogs.lrf_single import LRFSingleDialog, LRFBulkDialog from calibre.gui2.dialogs.epub import Config as EPUBConvert +from calibre.gui2.dialogs.mobi import Config as MOBIConvert import calibre.gui2.dialogs.comicconf as ComicConf from calibre.gui2 import warning_dialog from calibre.ptempfile import PersistentTemporaryFile @@ -19,14 +20,20 @@ from calibre.ebooks.lrf import preferred_source_formats as LRF_PREFERRED_SOURCE_ from calibre.ebooks.metadata.opf import OPFCreator from calibre.ebooks.epub.from_any import SOURCE_FORMATS as EPUB_PREFERRED_SOURCE_FORMATS -def convert_single_epub(parent, db, comics, others): +def get_dialog(fmt): + return { + 'epub':EPUBConvert, + 'mobi':MOBIConvert, + }[fmt] + +def convert_single(fmt, parent, db, comics, others): changed = False jobs = [] others_ids = [db.id(row) for row in others] comics_ids = [db.id(row) for row in comics] for row, row_id in zip(others, others_ids): temp_files = [] - d = EPUBConvert(parent, db, row) + d = get_dialog(fmt)(parent, db, row) if d.source_format is not None: d.exec_() if d.result() == QDialog.Accepted: @@ -35,7 +42,7 @@ def convert_single_epub(parent, db, comics, others): pt = PersistentTemporaryFile('.'+d.source_format.lower()) pt.write(data) pt.close() - of = PersistentTemporaryFile('.epub') + of = PersistentTemporaryFile('.'+fmt) of.close() opts.output = of.name opts.from_opf = d.opf_file.name @@ -45,8 +52,8 @@ def convert_single_epub(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(('any2epub', args, _('Convert book: ')+d.mi.title, - 'EPUB', row_id, temp_files)) + 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): @@ -61,24 +68,24 @@ def convert_single_epub(parent, db, comics, others): if defaults is not None: db.set_conversion_options(db.id(row), 'comic', defaults) if opts is None: continue - for fmt in ['cbz', 'cbr']: + for _fmt in ['cbz', 'cbr']: try: - data = db.format(row, fmt.upper()) + data = db.format(row, _fmt.upper()) if data is not None: break except: continue - pt = PersistentTemporaryFile('.'+fmt) + pt = PersistentTemporaryFile('.'+_fmt) pt.write(data) pt.close() - of = PersistentTemporaryFile('.epub') + of = PersistentTemporaryFile('.'+fmt) of.close() opts.output = of.name opts.verbose = 2 args = [pt.name, opts] changed = True - jobs.append(('comic2epub', args, _('Convert comic: ')+opts.title, - 'EPUB', row_id, [pt, of])) + jobs.append(('comic2'+fmt, args, _('Convert comic: ')+opts.title, + fmt.upper(), row_id, [pt, of])) return jobs, changed @@ -146,9 +153,9 @@ def convert_single_lrf(parent, db, comics, others): return jobs, changed -def convert_bulk_epub(parent, db, comics, others): +def convert_bulk(fmt, parent, db, comics, others): if others: - d = EPUBConvert(parent, db) + d = get_dialog(fmt)(parent, db) if d.exec_() != QDialog.Accepted: others = [] else: @@ -169,9 +176,9 @@ def convert_bulk_epub(parent, db, comics, others): row_id = db.id(row) if row in others: data = None - for fmt in EPUB_PREFERRED_SOURCE_FORMATS: + for _fmt in EPUB_PREFERRED_SOURCE_FORMATS: try: - data = db.format(row, fmt.upper()) + data = db.format(row, _fmt.upper()) if data is not None: break except: @@ -185,10 +192,10 @@ def convert_bulk_epub(parent, db, comics, others): opf_file = PersistentTemporaryFile('.opf') opf.render(opf_file) opf_file.close() - pt = PersistentTemporaryFile('.'+fmt.lower()) + pt = PersistentTemporaryFile('.'+_fmt.lower()) pt.write(data) pt.close() - of = PersistentTemporaryFile('.epub') + of = PersistentTemporaryFile('.'+fmt) of.close() cover = db.cover(row) cf = None @@ -203,7 +210,7 @@ def convert_bulk_epub(parent, db, comics, others): desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title)) temp_files = [cf] if cf is not None else [] temp_files.extend([opf_file, pt, of]) - jobs.append(('any2epub', args, desc, 'EPUB', row_id, temp_files)) + jobs.append(('any2'+fmt, args, desc, fmt.upper(), row_id, temp_files)) else: options = comic_opts.copy() mi = db.get_metadata(row) @@ -212,24 +219,24 @@ def convert_bulk_epub(parent, db, comics, others): if mi.authors: options.author = ','.join(mi.authors) data = None - for fmt in ['cbz', 'cbr']: + for _fmt in ['cbz', 'cbr']: try: - data = db.format(row, fmt.upper()) + data = db.format(row, _fmt.upper()) if data is not None: break except: continue - pt = PersistentTemporaryFile('.'+fmt.lower()) + pt = PersistentTemporaryFile('.'+_fmt.lower()) pt.write(data) pt.close() - of = PersistentTemporaryFile('.epub') + of = PersistentTemporaryFile('.'+fmt) of.close() setattr(options, 'output', of.name) options.verbose = 1 args = [pt.name, options] desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title)) - jobs.append(('comic2epub', args, desc, 'EPUB', row_id, [pt, of])) + jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of])) if bad_rows: res = [] @@ -345,15 +352,14 @@ def set_conversion_defaults_lrf(comic, parent, db): else: LRFSingleDialog(parent, None, None).exec_() -def set_conversion_defaults_epub(comic, parent, db): +def _set_conversion_defaults(dialog, comic, parent, db): if comic: ComicConf.set_conversion_defaults(parent) else: - d = EPUBConvert(parent, db) + d = dialog(parent, db) d.setWindowTitle(_('Set conversion defaults')) d.exec_() - def _fetch_news(data, fmt): pt = PersistentTemporaryFile(suffix='_feeds2%s.%s'%(fmt.lower(), fmt.lower())) pt.close() @@ -385,22 +391,22 @@ def convert_single_ebook(*args): fmt = prefs['output_format'].lower() if fmt == 'lrf': return convert_single_lrf(*args) - elif fmt == 'epub': - return convert_single_epub(*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 == 'epub': - return convert_bulk_epub(*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': return set_conversion_defaults_lrf(comic, parent, db) - elif fmt == 'epub': - return set_conversion_defaults_epub(comic, parent, db) + elif fmt in ('epub', 'mobi'): + return _set_conversion_defaults(get_dialog(fmt), comic, parent, db) def fetch_news(data): fmt = prefs['output_format'].lower() diff --git a/src/calibre/parallel.py b/src/calibre/parallel.py index 6cbe1c96e4..fa9284ce46 100644 --- a/src/calibre/parallel.py +++ b/src/calibre/parallel.py @@ -67,7 +67,15 @@ PARALLEL_FUNCS = { 'comic2epub' : ('calibre.ebooks.epub.from_comic', 'convert', {}, 'notification'), - + + 'any2mobi' : + ('calibre.ebooks.mobi.from_any', 'any2mobi', {}, None), + + 'feeds2mobi' : + ('calibre.ebooks.mobi.from_feeds', 'main', {}, 'notification'), + + 'comic2mobi' : + ('calibre.ebooks.mobi.from_comic', 'convert', {}, 'notification'), }