mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
EPUB Output: Have the GUI use a UUID for package identifier when creating EPUBs
This commit is contained in:
parent
ea0ec8293b
commit
d14682525c
@ -6,14 +6,14 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
The GUI for conversion to EPUB.
|
The GUI for conversion to EPUB.
|
||||||
'''
|
'''
|
||||||
import os
|
import os, uuid
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \
|
from PyQt4.Qt import QDialog, QSpinBox, QDoubleSpinBox, QComboBox, QLineEdit, \
|
||||||
QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL
|
QTextEdit, QCheckBox, Qt, QPixmap, QIcon, QListWidgetItem, SIGNAL
|
||||||
from lxml.etree import XPath
|
from lxml.etree import XPath
|
||||||
|
|
||||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||||
from calibre.gui2.dialogs.epub_ui import Ui_Dialog
|
from calibre.gui2.dialogs.epub_ui import Ui_Dialog
|
||||||
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog
|
from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableDialog
|
||||||
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
|
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
@ -23,16 +23,16 @@ from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
|||||||
|
|
||||||
|
|
||||||
class Config(ResizableDialog, Ui_Dialog):
|
class Config(ResizableDialog, Ui_Dialog):
|
||||||
|
|
||||||
OUTPUT = 'EPUB'
|
OUTPUT = 'EPUB'
|
||||||
|
|
||||||
def __init__(self, parent, db, row=None, config=epubconfig):
|
def __init__(self, parent, db, row=None, config=epubconfig):
|
||||||
ResizableDialog.__init__(self, parent)
|
ResizableDialog.__init__(self, parent)
|
||||||
self.hide_controls()
|
self.hide_controls()
|
||||||
self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'),
|
self.connect(self.category_list, SIGNAL('itemEntered(QListWidgetItem *)'),
|
||||||
self.show_category_help)
|
self.show_category_help)
|
||||||
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
|
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
|
||||||
|
|
||||||
self.cover_changed = False
|
self.cover_changed = False
|
||||||
self.db = db
|
self.db = db
|
||||||
self.id = None
|
self.id = None
|
||||||
@ -52,7 +52,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT)
|
self.setWindowTitle(_('Bulk convert to ')+self.OUTPUT)
|
||||||
else:
|
else:
|
||||||
self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT)
|
self.setWindowTitle((_(u'Convert %s to ')%unicode(self.title.text()))+self.OUTPUT)
|
||||||
|
|
||||||
def hide_controls(self):
|
def hide_controls(self):
|
||||||
self.source_profile_label.setVisible(False)
|
self.source_profile_label.setVisible(False)
|
||||||
self.opt_source_profile.setVisible(False)
|
self.opt_source_profile.setVisible(False)
|
||||||
@ -63,7 +63,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.opt_rescale_images.setVisible(False)
|
self.opt_rescale_images.setVisible(False)
|
||||||
self.opt_ignore_tables.setVisible(False)
|
self.opt_ignore_tables.setVisible(False)
|
||||||
self.opt_prefer_author_sort.setVisible(False)
|
self.opt_prefer_author_sort.setVisible(False)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.__w = []
|
self.__w = []
|
||||||
self.__w.append(QIcon(':/images/dialog_information.svg'))
|
self.__w.append(QIcon(':/images/dialog_information.svg'))
|
||||||
@ -76,11 +76,11 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list)
|
self.item4 = QListWidgetItem(self.__w[-1], _('Chapter Detection').replace(' ','\n'), self.category_list)
|
||||||
self.setup_tooltips()
|
self.setup_tooltips()
|
||||||
self.initialize_options()
|
self.initialize_options()
|
||||||
|
|
||||||
def set_help(self, msg):
|
def set_help(self, msg):
|
||||||
if msg and getattr(msg, 'strip', lambda:True)():
|
if msg and getattr(msg, 'strip', lambda:True)():
|
||||||
self.help_view.setPlainText(msg)
|
self.help_view.setPlainText(msg)
|
||||||
|
|
||||||
def setup_tooltips(self):
|
def setup_tooltips(self):
|
||||||
for opt in self.config.option_set.preferences:
|
for opt in self.config.option_set.preferences:
|
||||||
g = getattr(self, 'opt_'+opt.name, False)
|
g = getattr(self, 'opt_'+opt.name, False)
|
||||||
@ -90,19 +90,19 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
g.setToolTip(help.replace('<', '<').replace('>', '>'))
|
g.setToolTip(help.replace('<', '<').replace('>', '>'))
|
||||||
g.setWhatsThis(help.replace('<', '<').replace('>', '>'))
|
g.setWhatsThis(help.replace('<', '<').replace('>', '>'))
|
||||||
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
|
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
|
||||||
|
|
||||||
def show_category_help(self, item):
|
def show_category_help(self, item):
|
||||||
text = unicode(item.text())
|
text = unicode(item.text())
|
||||||
help = {
|
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 %s file.')%self.OUTPUT,
|
_('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.'),
|
_('Look & Feel') : _('Adjust the look of the generated ebook by specifying things like font sizes.'),
|
||||||
_('Page Setup') : _('Specify the page layout settings like margins.'),
|
_('Page Setup') : _('Specify the page layout settings like margins.'),
|
||||||
_('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'),
|
_('Chapter Detection') : _('Fine tune the detection of chapter and section headings.'),
|
||||||
}
|
}
|
||||||
self.set_help(help[text.replace('\n', ' ')])
|
self.set_help(help[text.replace('\n', ' ')])
|
||||||
|
|
||||||
def select_cover(self):
|
def select_cover(self):
|
||||||
files = choose_images(self, 'change cover dialog',
|
files = choose_images(self, 'change cover dialog',
|
||||||
_('Choose cover for ') + unicode(self.title.text()))
|
_('Choose cover for ') + unicode(self.title.text()))
|
||||||
if not files:
|
if not files:
|
||||||
return
|
return
|
||||||
@ -110,7 +110,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
if _file:
|
if _file:
|
||||||
_file = os.path.abspath(_file)
|
_file = os.path.abspath(_file)
|
||||||
if not os.access(_file, os.R_OK):
|
if not os.access(_file, os.R_OK):
|
||||||
d = error_dialog(self.window, _('Cannot read'),
|
d = error_dialog(self.window, _('Cannot read'),
|
||||||
_('You do not have permission to read the file: ') + _file)
|
_('You do not have permission to read the file: ') + _file)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
return
|
return
|
||||||
@ -118,7 +118,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
try:
|
try:
|
||||||
cf = open(_file, "rb")
|
cf = open(_file, "rb")
|
||||||
cover = cf.read()
|
cover = cf.read()
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
d = error_dialog(self.window, _('Error reading file'),
|
d = error_dialog(self.window, _('Error reading file'),
|
||||||
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
|
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -134,14 +134,14 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.cover.setPixmap(pix)
|
self.cover.setPixmap(pix)
|
||||||
self.cover_changed = True
|
self.cover_changed = True
|
||||||
self.cpixmap = pix
|
self.cpixmap = pix
|
||||||
|
|
||||||
def initialize_metadata_options(self):
|
def initialize_metadata_options(self):
|
||||||
all_series = self.db.all_series()
|
all_series = self.db.all_series()
|
||||||
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
||||||
for series in all_series:
|
for series in all_series:
|
||||||
self.series.addItem(series[1])
|
self.series.addItem(series[1])
|
||||||
self.series.setCurrentIndex(-1)
|
self.series.setCurrentIndex(-1)
|
||||||
|
|
||||||
if self.row is not None:
|
if self.row is not None:
|
||||||
mi = self.db.get_metadata(self.id, index_is_id=True)
|
mi = self.db.get_metadata(self.id, index_is_id=True)
|
||||||
self.title.setText(mi.title)
|
self.title.setText(mi.title)
|
||||||
@ -157,14 +157,14 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.series.setCurrentIndex(self.series.findText(mi.series))
|
self.series.setCurrentIndex(self.series.findText(mi.series))
|
||||||
if mi.series_index is not None:
|
if mi.series_index is not None:
|
||||||
self.series_index.setValue(mi.series_index)
|
self.series_index.setValue(mi.series_index)
|
||||||
|
|
||||||
cover = self.db.cover(self.id, index_is_id=True)
|
cover = self.db.cover(self.id, index_is_id=True)
|
||||||
if cover:
|
if cover:
|
||||||
pm = QPixmap()
|
pm = QPixmap()
|
||||||
pm.loadFromData(cover)
|
pm.loadFromData(cover)
|
||||||
if not pm.isNull():
|
if not pm.isNull():
|
||||||
self.cover.setPixmap(pm)
|
self.cover.setPixmap(pm)
|
||||||
|
|
||||||
def get_title_and_authors(self):
|
def get_title_and_authors(self):
|
||||||
title = unicode(self.title.text()).strip()
|
title = unicode(self.title.text()).strip()
|
||||||
if not title:
|
if not title:
|
||||||
@ -172,7 +172,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
authors = unicode(self.author.text()).strip()
|
authors = unicode(self.author.text()).strip()
|
||||||
authors = string_to_authors(authors) if authors else [_('Unknown')]
|
authors = string_to_authors(authors) if authors else [_('Unknown')]
|
||||||
return title, authors
|
return title, authors
|
||||||
|
|
||||||
def get_metadata(self):
|
def get_metadata(self):
|
||||||
title, authors = self.get_title_and_authors()
|
title, authors = self.get_title_and_authors()
|
||||||
mi = MetaInformation(title, authors)
|
mi = MetaInformation(title, authors)
|
||||||
@ -191,9 +191,9 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
tags = [t.strip() for t in unicode(self.tags.text()).split(',')]
|
tags = [t.strip() for t in unicode(self.tags.text()).split(',')]
|
||||||
if tags:
|
if tags:
|
||||||
mi.tags = tags
|
mi.tags = tags
|
||||||
|
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
def read_settings(self):
|
def read_settings(self):
|
||||||
for pref in self.config.option_set.preferences:
|
for pref in self.config.option_set.preferences:
|
||||||
g = getattr(self, 'opt_'+pref.name, False)
|
g = getattr(self, 'opt_'+pref.name, False)
|
||||||
@ -209,9 +209,9 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
elif isinstance(g, QCheckBox):
|
elif isinstance(g, QCheckBox):
|
||||||
self.config.set(pref.name, bool(g.isChecked()))
|
self.config.set(pref.name, bool(g.isChecked()))
|
||||||
if self.row is not None:
|
if self.row is not None:
|
||||||
self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src)
|
self.db.set_conversion_options(self.id, self.OUTPUT.lower(), self.config.src)
|
||||||
|
|
||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
self.initialize_metadata_options()
|
self.initialize_metadata_options()
|
||||||
values = self.config.parse()
|
values = self.config.parse()
|
||||||
@ -232,16 +232,16 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
g.setCurrentIndex(g.findText(val))
|
g.setCurrentIndex(g.findText(val))
|
||||||
elif isinstance(g, QCheckBox):
|
elif isinstance(g, QCheckBox):
|
||||||
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
|
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
|
||||||
|
|
||||||
|
|
||||||
def get_source_format(self):
|
def get_source_format(self):
|
||||||
self.source_format = None
|
self.source_format = None
|
||||||
if self.row is not None:
|
if self.row is not None:
|
||||||
temp = self.db.formats(self.id, index_is_id=True)
|
temp = self.db.formats(self.id, index_is_id=True)
|
||||||
if not temp:
|
if not temp:
|
||||||
error_dialog(self.parent(), _('Cannot convert'),
|
error_dialog(self.parent(), _('Cannot convert'),
|
||||||
_('This book has no available formats')).exec_()
|
_('This book has no available formats')).exec_()
|
||||||
|
|
||||||
available_formats = [f.upper().strip() for f in temp.split(',')]
|
available_formats = [f.upper().strip() for f in temp.split(',')]
|
||||||
choices = [fmt.upper() for fmt in SOURCE_FORMATS if fmt.upper() in available_formats]
|
choices = [fmt.upper() for fmt in SOURCE_FORMATS if fmt.upper() in available_formats]
|
||||||
if not choices:
|
if not choices:
|
||||||
@ -253,7 +253,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices)
|
d = ChooseFormatDialog(self.parent(), _('Choose the format to convert to ')+self.OUTPUT, choices)
|
||||||
if d.exec_() == QDialog.Accepted:
|
if d.exec_() == QDialog.Accepted:
|
||||||
self.source_format = d.format()
|
self.source_format = d.format()
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
for opt in ('chapter', 'level1_toc', 'level2_toc', 'level3_toc', 'page',
|
for opt in ('chapter', 'level1_toc', 'level2_toc', 'level3_toc', 'page',
|
||||||
'page_names'):
|
'page_names'):
|
||||||
@ -263,7 +263,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
XPath(text,namespaces={'re':'http://exslt.org/regular-expressions'})
|
XPath(text,namespaces={'re':'http://exslt.org/regular-expressions'})
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
error_dialog(self, _('Invalid XPath expression'),
|
error_dialog(self, _('Invalid XPath expression'),
|
||||||
_('The expression %s is invalid. Error: %s')%(text, err)
|
_('The expression %s is invalid. Error: %s')%(text, err)
|
||||||
).exec_()
|
).exec_()
|
||||||
return
|
return
|
||||||
mi = self.get_metadata()
|
mi = self.get_metadata()
|
||||||
@ -272,6 +272,7 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
if self.row is not None:
|
if self.row is not None:
|
||||||
self.db.set_metadata(self.id, mi)
|
self.db.set_metadata(self.id, mi)
|
||||||
self.mi = self.db.get_metadata(self.id, index_is_id=True)
|
self.mi = self.db.get_metadata(self.id, index_is_id=True)
|
||||||
|
self.mi.application_id = uuid.uuid4()
|
||||||
opf = OPFCreator(os.getcwdu(), self.mi)
|
opf = OPFCreator(os.getcwdu(), self.mi)
|
||||||
self.opf_file = PersistentTemporaryFile('.opf')
|
self.opf_file = PersistentTemporaryFile('.opf')
|
||||||
opf.render(self.opf_file)
|
opf.render(self.opf_file)
|
||||||
@ -286,5 +287,5 @@ class Config(ResizableDialog, Ui_Dialog):
|
|||||||
self.cover_file = cf
|
self.cover_file = cf
|
||||||
self.opts = self.config.parse()
|
self.opts = self.config.parse()
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user