Saturday, 12 January 2013

Emailing books directly from calibre content server to kindle

There are a few guides around to setting up a headless calibre server however there were a few things that I felt were missing from these guides:

  • A way to browse the content server and email books directly to my kindle
  • A way to add new books to the calibre database and update the metadata

Emailing books directly from content server to kindle

If you're like me you prefer browsing the calibre content server on your computer, rather than on your kindle.  While calibre doesn't allow us to email directly from the content server, with a little work you can make this possible. 

You'll need to install a webserver (I used lighttpd) to run the following script, it will need to be installed on the same host as the calibre-server.

Save the following script in a place that can be executed by your webserver. Edit the variables at the top (note that calibre-smtp requires your email password to work, I suggest setting up an email address just for sending books):

#!/usr/bin/env python
import re
import cgi
import cgitb; cgitb.enable()  # for troubleshooting
import sqlite3
import shutil
import os.path
import subprocess
import os
from pipes import quote

###Edit these variables for your own system
calibre_dir = "/mnt/calibredrive/calibre"
calibre_db = os.path.join(calibre_dir, "metadata.db")
server_email = ""
password = "Your email password"

form = cgi.FieldStorage()
book_id = cgi.escape(form.getvalue("book_id", ""))
email = cgi.escape(form.getvalue("email", ""))
id = cgi.escape(form.getvalue("id", ""))

if book_id != "":
    db = sqlite3.Connection(calibre_db)
    cursor = db.cursor()
    cursor.execute("""select * from books where id=?""", (book_id,))
    book = cursor.fetchone()
    shutil.copyfile(os.path.join(calibre_dir, book[9], "cover.jpg"), "../cover.jpg")
    book = None

#print "Content-type: text/html"

print """
<link rel="stylesheet" href="../style.css" type="text/css" />
<head><title>Email book to kindle</title></head>

<div id="wrapper">
<div id="header">
<div id="navigation">
<ul><li><a href="" target="_blank">Calibre Server</a>
<h1> Email book to Kindle </h1>
<div id="content">
<form method="post" action="">
 <tr><td>Book ID:</td>
<td><input type="text" name="book_id" value="%s" /></td></tr>
    <tr><td>Email:</td><td><input type="text" name="email" value='%s'/></td></tr><tr><td></td><td><input type="submit" width=20px></td></tr></table>
""" % (id, email)
if book != None:
    if re.match("[^@]+@[^@]+\.[^@]+", email):
        for files in os.listdir(os.path.join(calibre_dir, book[9])):
            if files.endswith(".mobi"):
                attachment = quote(os.path.join(calibre_dir, book[9], files))
        subprocess.Popen("""calibre-smtp %s %s "book" --attachment %s -r -u %s --port 587 -e TLS -p %s""" %(server_email, email, attachment, server_email, password), shell=True, stdout=open(os.devnull, "w+", 0), stderr = subprocess.STDOUT).pid

        print """
  <p><b>%s</b> by <b>%s</b> has been queued for delivery to %s</p><br />
<p /><center><img src="../cover.jpg" /></center>""" % (book[1], book[6], email)
        print """Invalid email entered"""
    if book_id != "":
        print """<p> %s is an invalid book id</p>""" % book_id

print """

Next we need to edit the html template that calibre uses to generate the webpages for the content server.  On my install they were located in /usr/share/calibre/content_server/browse/
Open up summary.html. I removed the {get_button} section and replaced it with a link pointing to our script like so:
<a href="http://your-website/cgi-bin/{id}" target="_blank">Email</a>
You can also edit the details.html if you want to create an email link there also.

If you restart the calibre-server you should notice that all the Get buttons have now turned into hyperlinks. Clicking on them should send you to your emailbook script, then just fill in an email address and it will be processed.

For a working example you can visit my calibre server which I have running on a raspberry pi.