#!/usr/bin/env python
# encoding: utf8
import sys
import os
import re
import readline
import shutil
import common
from bottle import SimpleTemplate
from PIL import Image as pil_image
from PIL import ExifTags as pil_exif
def our_data_path():
"Returns the path to our data."
# XXX: ugly assumption for now
return os.path.dirname(os.path.realpath(sys.argv[0]))
# How many degrees counter-clockwise to rotate, according to the exif
# orientation. See http://sylvana.net/jpegcrop/exif_orientation.html.
exif_rotations = {
3: -180,
6: -90,
8: -270,
}
def resize_img(img, x, y, dst):
"Resizes the given image, and saves it to dst."
img = pil_image.open(img)
exif_items = []
if img._getexif() is not None:
exif_items = img._getexif().items()
for k, v in exif_items:
if k in pil_exif.TAGS and pil_exif.TAGS[k] == 'Orientation':
if v in exif_rotations:
img = img.rotate(exif_rotations[v],
expand = True)
img.thumbnail((x, y), pil_image.ANTIALIAS)
img.save(dst, 'JPEG', quality = 95)
def generate(page_path, dst_path):
# write the index
page = common.page_load(page_path)
index = SimpleTemplate(name = our_data_path() + "/templates/index.html")
out = index.render(title = page.title,
introduction = page.introduction,
albums = page.albums)
if not os.path.exists(dst_path):
os.makedirs(dst_path)
dst = open(dst_path + "/index.html", 'w')
dst.write(out.encode('utf8'))
# write the albums
album_page = SimpleTemplate(name = our_data_path() + "/templates/album.html")
for a in page.albums:
out = album_page.render(title = page.title, album = a)
dst = open(dst_path + "/album-%s.html" % a.shortname, 'w')
dst.write(out.encode('utf8'))
# TODO: do we really want to remove it?
if os.path.exists(dst_path + "/static/"):
shutil.rmtree(dst_path + "/static/")
shutil.copytree(our_data_path() + "/static/", dst_path + "/static/")
# create the needed directories (same as the ones in common.Image)
if not os.path.exists(dst_path + "/normal/"):
os.mkdir(dst_path + "/normal/")
if not os.path.exists(dst_path + "/thumbs/"):
os.mkdir(dst_path + "/thumbs/")
dst_dev = os.stat(dst_path).st_dev
for a in page.albums:
# the image lives under its album shortname, see
# Image.albumpath
if not os.path.exists(dst_path + "/normal/" + a.shortname):
os.mkdir(dst_path + "/normal/" + a.shortname)
if not os.path.exists(dst_path + "/thumbs/" + a.shortname):
os.mkdir(dst_path + "/thumbs/" + a.shortname)
for i in a.images:
# hardlink the images if possible, copy otherwise
#dst_fname = dst_path + "/" + i.imagepath
#if not os.path.exists(dst_fname):
# if os.stat(i.realpath).st_dev == dst_dev:
# os.link(i.realpath, dst_fname)
# else:
# shutil.copy2(i.realpath, dst_fname)
# create thumbs using PIL
if not os.path.exists(dst_path + '/' + i.thumbpath):
resize_img(i.realpath, 256, 256,
dst_path + '/' + i.thumbpath)
# create normal-sized images using PIL
if not os.path.exists(dst_path + '/' + i.normalpath):
resize_img(i.realpath, page.resize_to, page.resize_to,
dst_path + '/' + i.normalpath)
def new_page(path):
p = common.Page()
p.title = raw_input("Title: ")
p.introduction = raw_input("Introduction: ")
add_albums(p)
common.page_save(p, path)
def _add_album(page, shortname, title, text):
a = common.Album(shortname)
a.title = title
if not a.title:
a.title = a.shortname.decode('utf8')
a.text = text
while True:
img_path = raw_input("Album images path: ")
if os.path.isdir(img_path):
fnames = (img_path + '/' + f
for f in os.listdir(img_path))
else:
fnames = open(img_path).read().split('\n')
c = 0
for fname in sorted(set(fnames)):
fname = os.path.realpath(fname)
if fname.lower().endswith(".jpg"):
i = common.Image(a)
i.realpath = fname
a.images.append(i)
c += 1
if c > 0:
break
else:
print 'ERROR: invalid path'
page.albums.append(a)
def find_album(page, shortname):
"""Find the album "shortname" on page "page" (the shortname is unique)"""
for a in page.albums:
if a.shortname == shortname:
return a
return None
def add_albums(page):
while True:
shortname = raw_input("Album shortname (empty to skip): ")
if not shortname:
break
if not re.match("^[a-zA-Z0-9_-]+$", shortname):
print "ERROR: shortname has invalid characters"
continue
if find_album(page, shortname):
print "ERROR: shortname already in use (must be unique)"
continue
title = raw_input("Album title: ").decode('utf8')
text = raw_input("Album text: ").decode('utf8')
_add_album(page, shortname, title, text)
print
def delete_album(page, shortname):
a = find_album(page, shortname)
if not a:
print "ERROR: invalid album shortname"
return False
page.albums.remove(a)
return True
def rescan_album(page, shortname):
# Get the album "data" (title, etc.) to create one with the same data later
old_a = find_album(page, shortname)
if not old_a:
print "ERROR: invalid album shortname"
return False
delete_album(page, shortname)
# This adds the album again re-scanning the images used
_add_album(page, old_a.shortname, old_a.title,old_a.text)
return True
def _link(src, dst):
# create destination directory if it does not exist
dn = os.path.dirname(dst)
if not os.path.exists(dn):
os.makedirs(dn)
if not os.path.lexists(dst):
print("%s -> %s"% (src, dst))
os.symlink(src, dst)
def link_to_orig(page, dst_dir):
for a in page.albums:
print a.shortname
for i in a.images:
# hierarchical, to dst/album/file.jpg
_link(i.realpath, dst_dir + "/" + i.albumpath)
# flat, to dst/_flat/album-file.jpg
dst_fname = "%s/_flat/%s-%s" % (dst_dir, a.shortname,
os.path.basename(i.realpath))
_link(i.realpath, dst_fname)
HELP = '''
Use one of:
galala new_page FILENAME
Creates a new page and will ask to optionally add albums to it. The
filename given will store the page information in .ini format.
galala add_albums FILENAME
Adds albums to the given page. It will ask for the information
interactively, and update the page file.
galala delete_album FILENAME ALBUM_SHORTNAME
This command deletes the album from FILENAME so it will not be included
the next time you generate. But, keep in mind that the files already
created for this album (if any) will still be there.
galala rescan_album FILENAME ALBUM_SHORTNAME
The same album is preserved, except that it asks you for the image path
and adds those images. This command is equivalent to delete the album and
create it again with the same shortname, title and text.
galala generate FILENAME OUT_PATH
Generates static HTML for the page given in FILENAME to OUT_PATH.
galala link_to_orig FILENAME OUT_PATH
Create symlinks to the original images on the page given in FILENAME
in OUT_PATH.
'''
if __name__ == '__main__':
if len(sys.argv) <= 2:
print HELP
sys.exit(1)
cmd = sys.argv[1]
if cmd == 'generate':
if len(sys.argv) != 4:
print HELP
sys.exit(1)
page_path = sys.argv[2]
dst_path = sys.argv[3]
generate(page_path, dst_path)
elif cmd == 'new_page':
new_page(sys.argv[2])
elif cmd == 'add_albums':
page = common.page_load(sys.argv[2])
add_albums(page)
common.page_save(page, sys.argv[2])
elif cmd in ['delete_album', 'rescan_album']:
if len(sys.argv) != 4:
print HELP
sys.exit(1)
page = common.page_load(sys.argv[2])
shortname = sys.argv[3]
if cmd == 'delete_album':
success = delete_album(page, shortname)
elif cmd == 'rescan_album':
success = rescan_album(page, shortname)
if success:
common.page_save(page, sys.argv[2])
elif cmd == "link_to_orig":
if len(sys.argv) != 4:
print HELP
sys.exit(1)
page = common.page_load(sys.argv[2])
dst_dir = sys.argv[3]
link_to_orig(page, dst_dir)
else:
print "ERROR: unknown command"
sys.exit(1)