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 emailbook.py 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 = "your_email@gmail.com"
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")
else:
    book = None

#print "Content-type: text/html"

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

<body>
<div id="wrapper">
<div id="header">
<div id="navigation">
<ul><li><a href="http://calibre.zapto.org" target="_blank">Calibre Server</a>
</li></ul>
</div>
<h1> Email book to Kindle </h1>
</div>
<div id="content">
<form method="post" action="emailbook.py">
<p>
<table>
 <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>
</p></form>
""" % (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))
                break
        subprocess.Popen("""calibre-smtp %s %s "book" --attachment %s -r smtp.gmail.com -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)
    else:
        print """Invalid email entered"""
else:
    if book_id != "":
        print """<p> %s is an invalid book id</p>""" % book_id

print """
</body></html>"""


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 emailbook.py script like so:
<a href="http://your-website/cgi-bin/emailbook.py?id={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.

Saturday, 4 July 2009

cross platform popup notifications in gtk


Recently I've been building a cross platform microblogging system in python with gtk. I was used to using py-notify on the linux platform to give the user popup notifications, however this would obviously not work on windows. I experiemented with the balloon notifications that windows has, but felt they were a bit limiting (no way to add images easily, and to be frank, interacting with the win32api was giving me suicidal tendencies.

I've created a simple class, gtkPopupNotify to deal with these things and it's available at github. Using it is simple:
import gtkPopupNotify
notifier = gtk.PopupNotify.NotificationStack(timeout=6)
notifier.new_popup(title="Sample popup", message="This is a popup", image="exclamation.png")

Colours, positioning etc. can all be set via the object properties and will affect all popups created afterwards, eg.

notifier.bg_color = gtk.gdk.Color("green")

Current problems: Not sure how to check for panel placement so the popup doesn't cover a users panel. On many systems I could use:

notifier.edge_offset_y = 30

but obviously this isn't very portable. Suggestions welcome!