![]() |
#1 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
Conversion output plugins?
I'm trying to make a conversion output plugin, but I'm not too sure where to start. I don't see any examples. I'm not looking to make a brand new plugin, at least I don't think so; I think that extending the ePub conversion output class would be fine. Basically, I want a conversion output plugin that produces the same end result that my KoboTouchExtended driver plugin does.
For a first step, I looked at calibre.ebooks.conversion.plugins.epub_output.EPUB Output and made a subclass KEPubOutput that extends EPUBOutput and does nothing but change the three variables at the top of the class Code:
name = 'KePub Output' author = '<me>' file_type = 'kepub.epub' Is there a sample of a conversion output plugin somewhere I can reference, or is there some other documentation I missed? |
![]() |
![]() |
![]() |
#2 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
calibre is not going to work with file extensions that have dots in them, the assumption that they do not, is in too many places in the code. And generally speaking, once you allow extensions with dots, there is no robust way of knowing what the extension of a given file is, you essentially have to maintain a list of know dotted extensions and test for them every time.
Try changing the file type to .kepub instead. |
![]() |
![]() |
Advert | |
|
![]() |
#3 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
I switched file_type to 'kepub' and made a few other tweaks and now when I select 'KEPUB' from the output format list I get the expected 'KePub Output' option. But, how do I go about adding options to that screen? Can I include a modified copy of epub_output.ui somewhere in the plugin, do I need to copy and modify calibre.gui2.convert.epub_output_ui.Ui_Form in my PluginWidget instance, or is there something even easier I'm missing?
|
![]() |
![]() |
![]() |
#4 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
You have to implement gui_configuration_widget(), see the base class implementation for an example. You may run into issues, as I dont think anyone has ever tested that with third party plugins, but in principle, it should work.
Just remember to not put any GUI related imports at the top of your plugin modules as the plugins are also used in ebook-convert, so they should not unecessarily incur the overhead of importing PyQt |
![]() |
![]() |
![]() |
#5 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
I think I found one of those problems
![]() I put some print() statements in calibre.customize.ui (output_format_plugins() and plugins_for_output_format()), calibre.ebooks.conversion.plumber (Plumber.__init__()), calibre.utils.ipc.worker (main()), and calibre.gui2.convert.gui_conversion (gui_convert()) to try and trace the problem and I'm stumped. Here's my calibre log (including the output from all the extra print statements I added) up to the point where I say I want to convert a book (it defaults to 'EPUB' as the output format) and I select 'KEPUB' from the output format list: Spoiler:
Once I actually click "OK", I get this output if I set CALIBRE_PYTHON_PATH: Spoiler:
And this if I only set CALIBRE_DEVELOP_FROM but not CALIBRE_PYTHON_PATH: Spoiler:
It almost looks like the workflow through calibre-parallel doesn't check third-party plugins? I'm not sure where to go from here. |
![]() |
![]() |
Advert | |
|
![]() |
#6 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
I dont see what effect having a config widget would have on the conversion process. Was your plugin working before adding config? And you get the error when pressing OK in what? Since the gui and the worker process use the exact same code to load plugins, I find it odd that it is found in one and not the other.
|
![]() |
![]() |
![]() |
#7 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
It doesn't seem to matter if I have a configuration widget or not. I have been able to get the conversion to succeed with this code:
Code:
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai __license__ = 'GPL v3' __copyright__ = '2013, Joel Goguen <jgoguen@jgoguen.ca>' __docformat__ = 'markdown en' from calibre.customize.conversion import OptionRecommendation from calibre.ebooks.conversion.plugins.epub_output import EPUBOutput class KEPubOutput(EPUBOutput): name = 'KePub Output' author = 'Joel Goguen' file_type = 'kepub' def __init__(self, *args): self.options.add( OptionRecommendation(name='opt_kepub_enable_extended_features', recommended_value=True, level=OptionRecommendation.HIGH, help='Choose whether to enable extra customisations') ) self.options.add( OptionRecommendation(name='opt_kepub_delete_unmanifested', recommended_value=False, level=OptionRecommendation.LOW, help='Select this to silently delete files that are not in the ' 'manifest if they are encountered during processing. If this ' 'option is not selected, files not in the manifest will be ' 'silently added to the manifest and processed as if they always ' 'were in the manifest.') ) self.options.add( OptionRecommendation(name='opt_kepub_hyphenate', recommended_value=True, level=OptionRecommendation.MED, help='Select this to add a CSS file which enables hyphenation. ' 'The language used will be the language defined for the book in ' 'calibre. Please see the README file for directions on updating ' 'hyphenation dictionaries.') ) self.options.add( OptionRecommendation(name='opt_kepub_replace_lang', recommended_value=True, level=OptionRecommendation.HIGH, help='Select this to replace the defined language in each ' 'content file inside the ePub.') ) self.options.add( OptionRecommendation(name='opt_kepub_clean_markup', recommended_value=True, level=OptionRecommendation.MED, help='Select this to clean up the internal ePub markup.') ) EPUBOutput.__init__(self, *args) Code:
def gui_configuration_widget(self, parent, get_option_by_name, get_option_help, db, book_id=None): return None Code:
def convert(self, oeb, output_path, input_plugin, opts, log): super(KEPubOutput, self).convert(oeb, output_path, input_plugin, opts, log) |
![]() |
![]() |
![]() |
#8 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
Some things that jump out at me:
You should not change self.options in __init__ as it is meant to be a class level variable. Instead define your own options and set the epub output options explicitly before calling the base class convert() You should have **kwargs in __init__ Generally speaking, I'd suggest not subclassing EPUbOutput, instead create your own plugin class, and simply call the epub output plugin in your convert() method. Since this is python, you dont really need ot sub-class, you can monkey patch instead. |
![]() |
![]() |
![]() |
#9 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
Things seem to be going a lot better so far. What did it for me was not subclassing EPUBOutput. Now I can create a file with the KePub extension that's identical to the output of the EPUB output format, so hopefully the rest of what I'm looking to accomplish should be fairly straightforward.
|
![]() |
![]() |
![]() |
#10 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
So I was almost right, almost everything I want to do was straightforward
![]() How can I get access to the calibre metadata of a book being converted? Specifically, I would like the ISO639 language identifier. Also, what do I need to do in order to make 'kepub' show up as a selection in the list of formats a device can accept? When I open calibre's preferences, go to Plugins, and choose a device driver I can see all the formats calibre supports and some of them are selected according to what the device supports. Do I just need to add 'kepub' to the FORMATS list for the device driver? |
![]() |
![]() |
![]() |
#11 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
Use the oeb.metadata.language see the Metadata class in oeb/base.py for details. This class is unfortunately, a pain to use, but we are stuck with it for legacy reasons.
That list of formats is IIRC taken from the BOOK_EXTENSIONS constant. You can try adding kepub to that and it should work (this will need a change to calibre source code). |
![]() |
![]() |
![]() |
#12 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
Your pointer to BOOK_EXTENSIONS led me to find that ConfigWidget gets its list of formats from the FORMATS set (or list) defined on the device. If the device driver defines USER_CAN_ADD_NEW_FORMATS=True (which is the default) this set from the driver is union()'d with BOOK_EXTENSIONS. So I was able to add kepub just by adding "FORMATS = set(['kepub'])" as a class-level variable. Haven't tested it yet, but that comes soon
![]() For the metadata, the only thing I haven't seen yet is how to get an instance of the Metadata object for the book I'm currently converting. Nothing jumps out at me, not even how to get a unique identifier for the book being converted, but if the container's OPF file is updated with the metadata from calibre by the time convert() is called I'm perfectly OK with taking the (limited) information I'm looking for out of the OPF file. EDIT: The OPF file is indeed updated and I've been able to get everything I was looking for! Now, to test with my driver to make sure these new 'kepub' files actually transfer correctly ![]() Last edited by jgoguen; 08-06-2013 at 09:17 PM. |
![]() |
![]() |
![]() |
#13 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
I'm running into an issue now getting the options to actually match the selections on the conversion options page. I've defined these recommendations as the set of options:
Code:
OptionRecommendation(name='kepub_hyphenate', recommended_value=True, help='Select this to add a CSS file which enables hyphenation. ' 'The language used will be the language defined for the book in ' 'calibre. Please see the README file for directions on updating ' 'hyphenation dictionaries.'), OptionRecommendation(name='kepub_replace_lang', recommended_value=True, help='Select this to replace the defined language in each content file inside the ePub.'), OptionRecommendation(name='kepub_clean_markup', recommended_value=True, help='Select this to clean up the internal ePub markup.') I've also tried prepending 'opt_' to each of the names; in the plugin widget I set this (as an example of how all three options are added to the form: Code:
self.opt_kepub_clean_markup = QtGui.QCheckBox(Form) self.opt_kepub_clean_markup.setObjectName(_fromUtf8("opt_kepub_clean_markup")) self.opt_kepub_clean_markup.setText("Clean ePub markup") htext = "<div>%s</div>" % prepare_string_for_xml('\n'.join(w.wrap('Select this to clean up the internal ePub markup.'))) self.opt_kepub_clean_markup.setToolTip(htext) self.opt_kepub_clean_markup.setWhatsThis(htext) self.gridLayout.addWidget(self.opt_kepub_clean_markup, rows, 1, 1, 1) |
![]() |
![]() |
![]() |
#14 |
creator of calibre
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 44,288
Karma: 23661992
Join Date: Oct 2006
Location: Mumbai, India
Device: Various
|
You remembered to register the options with the base class?
|
![]() |
![]() |
![]() |
#15 |
Generally Awesome Person
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,080
Karma: 2178847
Join Date: Jan 2013
Location: /dev/kmem
Device: Kobo Clara HD, Kindle Oasis
|
I thought I did, but after digging a little more it turns out I was doing it wrong. Thanks
![]() |
![]() |
![]() |
![]() |
|
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Output file name after conversion | dbellefuil | Calibre | 2 | 06-14-2013 04:40 PM |
Mimetypes icons should be plugin-based (for new input/output plugins, e.g.) | SauliusP. | Development | 5 | 09-15-2012 01:15 PM |
File Conversion Output Layout | zobop | Library Management | 5 | 06-20-2012 05:17 PM |
Conversion output folders | TechieLady | Conversion | 6 | 01-19-2012 02:49 AM |
AZW Conversion Output Plugin | tylau0 | Plugins | 59 | 08-14-2011 10:36 PM |