From Calibre to Meilisearch

From Calibre to Meilisearch

The last couple of posts were about what I want my Universal Personal Search to do, and how I've built it. This post is about adding more data sources: specifically, the Calibre e-book management app.

I have various tools that move data around in my tools (such as Ghoplin, which mirrors some kinds of content to Joplin). After a short while of thinking about how best to write something that will load Calibre's data and send it to Meilisearch, and then thinking about what a hassle it'd be to have to remember to run it after every change, I thought: Hey, what if I wrote a Calibre plugin? How hard could it be?

Turns out, not hard! It's just a bunch of Python files. Hey, I can do Python files!

I went for a "UI plugin" that only adds one button to the UI: when you click it, it loads metadata for all the books in the current database and sends it to Meilisearch.

I've started with the official template, removed what I didn't need, and changed the rest. Well except for this one line:

"name = 'Interface Plugin Demo'"

Whenever I tried changing a single letter in the "name", the plugin completely broke, logged no errors, stopped showing up in the UI. Never figured out why. 😃

How to use

There's not much more to be said about the plugin in general. It's pretty simple.

I'll share the plugin here in case you're reading this because you've also googled "Calibre" and "Meilisearch" and have the same use case. 😁

So first: download the plugin. It's a zip with all the code and data.

You may check it does nothing malicious - all the code is in plain text.

Then install it. The plugin isn't in the official repository, so you need to go to Preferences -> Plugins, then click Load plug-in from file.

Once it's loaded, restart Calibre.

Last step: configure it. Go to Preferences -> Plugins again. Find the plugin (Send to Meili (1.0.0) by zblesk) and select it. Click Customise plug-in. In the window that pops up, enter the domain name (i.e. not the full URL including the protocol - HTTPS is assumed), the API key, and a comma-separated list of indices. (If you enter more than one index, the same data is sent to each of them.)

When you're done, the only thing you need to do is to click the big "Send to Meili" button in the top menu bar and wait until it does its thing. That's it.

Final remarks - data format

Unless your use case is weirdly similar to mine, you probably want to change the JSON document Meilisearch receives.

The format of the payload is based on the "Common data structure" in the previous post. It's implemented in the file starting on line 39. The expression looks like this:

payload = [{ 'title': m.title, 
    'composite_id': 'calibre-' + str(, 
    'author': ', '.join(m.authors), 
    'tags': m.tags,
    'last_modified': m.last_modified,
    'identifiers': m.identifiers,
    'uuid': m.uuid,
    'formats': [str(f) for f in m.formats],
    'timestamp': m.timestamp,
    'languages': m.languages,
    'series': m.series,
    'series_index': m.series_index,
    'external_system_name': 'calibre',
    'external_system_id': str(,
    'type': 'ebook'
    } for m in meta ]

You can trivially change the JSON document's keys, and assign any of the metadata provided by Calibre.