#!/usr/bin/env python # Comicthumb 2007-05-26 # Create thumbnails for comic book archives (cbz, cbt and cbr). # # usage: comicthumb /path/to/archive.cbt outputfile [ size | --no-balloon ] # # Copyright (c) 2006 Christoph Wolk # & Pontus Ekberg # # Released under the GNU General Public License # # Known Issues: # - errors with interlaced pngs (due to PIL) # - lack of error handling # - cover guessing could be improved import os import sys import zipfile import tarfile import StringIO import re import shutil try: import Image except: print 'You need PIL to create thumbnails.' sys.exit(1) THUMB_SIZE = 128 TYPE = False BALLOON = True ## set thumbnail size and paths try: for argument in (3, 4): if len(sys.argv) > (argument): if sys.argv[argument].isdigit(): THUMB_SIZE = int(sys.argv[argument]) elif sys.argv[argument] == "--no-balloon": BALLOON = False in_file_path = sys.argv[1] out_file_path = sys.argv[2] except: print "Usage:" print "comicthumb /path/to/archive.cbt outputfile [ size | --no-balloon]" sys.exit(1) # temp directory needed for multiple archives if not os.path.exists('/tmp/comicthumb/'): os.makedirs('/tmp/comicthumb/') os.chmod('/tmp/comicthumb/', 0700) # return the first image in the list def first_image (filelist): exts = re.compile(r'\.(jpg|png|jpeg|gif|tif|tiff)\s*$', re.IGNORECASE) for file in filelist: if exts.search(file): return file return False # return the first archive in the list def first_archive (filelist): exts = re.compile( r'\.(zip|cbz|rar|cbr|tar|tar\.gz|tar\.bz2|tgz|tbz|cbt)\s*$', re.IGNORECASE) for file in filelist: if exts.search(file): return file return False # try to find the cover, if nothing good is found take first # FIXME: Could be improved def guessCover (filelist): p = re.compile('cover|front', re.IGNORECASE) coverlist = filter(p.search, filelist) coverlist = [s for s in coverlist if 'back' not in s.lower()] or coverlist firstcover = first_image(coverlist) if firstcover: return firstcover elif first_image(filelist): return first_image(filelist) else: return False # take compressed archive, return cover image # if only archives are found, take the first and continue the search there def get_image(compressed_file, depth): global TYPE if zipfile.is_zipfile(compressed_file): TYPE = TYPE or 'cbz' zip = zipfile.ZipFile(compressed_file, "r") zipfiles = zip.namelist() zipfiles.sort() cover = guessCover(zipfiles) if cover: picture = StringIO.StringIO(zip.read(cover)) zip.close() else: subarchive = first_archive(zipfiles) if subarchive: output = open("/tmp/comicthumb/archive%d" % (depth), "wb") output.write(zip.read(subarchive)) output.close() return get_image("/tmp/comicthumb/archive%d" % (depth), depth + 1) elif tarfile.is_tarfile(compressed_file): TYPE = TYPE or 'cbt' file = open(compressed_file, 'rb') tar = tarfile.open(compressed_file, "r") tarfiles = tar.getnames() tarfiles.sort() cover = guessCover(tarfiles) if cover: picture = StringIO.StringIO(tar.extractfile(cover).read()) tar.close() else: subarchive = first_archive(tarfiles) if subarchive: output = open("/tmp/comicthumb/archive%d" % (depth), "wb") output.write(tar.extractfile(subarchive).read()) output.close() return get_image("/tmp/comicthumb/archive%d" % (depth), depth + 1) elif open(compressed_file, 'rb').read(4) == 'Rar!': TYPE = TYPE or 'cbr' # Make sure rar/unrar is installed rar = "" for path in os.getenv("PATH").split(":"): if os.path.isfile(os.path.join(path, "unrar")): rar = "unrar" break elif os.path.isfile(os.path.join(path, "rar")): rar = "rar" break if not rar: print "You must install unrar or rar to thumbnail RAR archives." sys.exit(1) rarfiles = os.popen('%s vb "%s"' % (rar, compressed_file)).readlines() for i in range(len(rarfiles)): rarfiles[i] = rarfiles[i].rstrip("\n") rarfiles.sort() cover = guessCover(rarfiles) if cover: picture = StringIO.StringIO(os.popen('%s p -inul -- "%s" "%s"' % (rar, compressed_file, cover), "r").read()) else: subarchive = first_archive(rarfiles) if subarchive: os.popen('%s p -inul -- "%s" "%s" > "/tmp/comicthumb/archive%d"' % (rar, compressed_file, subarchive, depth), "r") return get_image("/tmp/comicthumb/archive%d" % (depth), depth + 1) return picture # main try: # get cover from archive image = Image.open(get_image(in_file_path, 0)) # thumbnail it if image.size[0] > image.size[1]: x = THUMB_SIZE y = THUMB_SIZE * image.size[1] / image.size[0] else: x = THUMB_SIZE * image.size[0] / image.size[1] y = THUMB_SIZE # at least one pixel x = max(1, x) y = max(1, y) image = image.resize((x, y), Image.ANTIALIAS) image = image.convert('RGB') # apply type info balloon if BALLOON: try: if os.path.exists(os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))), 'share/pixmaps/comix', TYPE + '.png')): overlay_path = \ os.path.join(os.path.dirname(os.path.dirname( os.path.realpath(sys.argv[0]))), 'share/pixmaps/comix', TYPE + '.png') elif os.path.exists('/usr/local/share/pixmaps/comix/' + TYPE + '.png'): overlay_path = '/usr/local/share/pixmaps/comix/' + TYPE + '.png' elif os.path.exists('/usr/share/pixmaps/comix/' + TYPE + '.png'): overlay_path = '/usr/share/pixmaps/comix/' + TYPE + '.png' overlay_image=Image.open(overlay_path) image_canvas = \ Image.new('RGBA', (image.size[0], image.size[1]), (0, 0, 0, 0)) image_canvas.paste(overlay_image, (image.size[0] - 35, image.size[1] - 30)) image = Image.composite(image_canvas, image, image_canvas) except: pass # save it image.save(out_file_path, 'PNG') exit_flag = 0 except: print "There was an error." exit_flag = 1 # remove tempory stuff if os.path.isdir('/tmp/comicthumb/'): shutil.rmtree('/tmp/comicthumb/') # and exit sys.exit(exit_flag)