I've been using the open source book management program Calibre to keep track of my ebook collection. Among other features, it allows downloading of metadata including tags for the books. However, this metadata isn't available outside the Calibre app.
After reading this post on using Python to automatically create tags using Openmeta, I decided to write a script to extract the tags from Calibre and write them to the files using Openmeta.
To run the script, you will need to download the Openmeta binary, and install Calibre's command line tools. This can be accomplished by choosing
Preferences > Miscellaneous > Install Command Line Tools
in Calibre.app or add /Applications/calibre.app/Contents/MacOS to your $PATH variable.
The script uses the `which` command to locate the required binaries. It makes a system call to the calibredb program to get the list of books in the current library and extract the tags. Then it calls the openmeta program to write the tags.
import os
import sys
import subprocess
import re
def runshell(command):
"""
Runs the given shell command and returns an array containing
the lines from stdout
"""
p = subprocess.Popen(command, stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate()
return stdout.splitlines()
def quotedspaces(array):
"""
Takes all the array elements that have spaces and returns a new array with
those elements quoted
"""
n = []
for a in array:
if ' ' in a:
n.append("'" + a + "'")
else:
n.append(a)
return list(set(n)) # removes duplicates
def checkpath(mypath):
"""
Check to make sure the path exists
"""
return os.path.exists(mypath)
def openmeta(path, tags):
"""Given a filename and array of tags calls openmeta to tag
the file with metadata
print result
"""
# get real pathname
print 'setting tags for ' + path.split('/')[-1]
ar = [openmetapath, '-s']
ar.extend(tags)
ar.extend(['-p', path])
# call the shell script
result = runshell(ar)
return result
calibrepath = runshell(['which', 'calibredb'])[0]
openmetapath = runshell(['which', 'openmeta'])[0]
libraries = ['Personal', 'Technical']
def main():
'''
Reads the tags from Calibre database and writes to the files
using openmeta
'''
if not checkpath(calibrepath):
print "The calibre binary doesn't exist at " + calibrepath
sys.exit(-1)
if not checkpath(openmetapath):
print "The openmeta binary doesn't exist at " + openmetapath
sys.exit(-1)
calibrecommand = [
calibrepath,
'list',
'-f',
'formats',
'-w',
'100000',
]
lines = [l.strip() for l in runshell(calibrecommand)
if re.match('[0-9]', l)]
for x in lines:
(num, y) = x.strip().split(None, 1)
files = y[1:-1].split(',/')
if files[0] == '':
continue
metacmd = [calibrepath, 'show_metadata', num]
meta = (runshell(metacmd)[4])[22:]
tags = quotedspaces([z.strip() for z in meta.split(',')])
for f in files:
if f[0] != '/':
f = '/' + f
openmeta(f, tags)
if __name__ == '__main__':
main()

