diff -r 373392025533 -r 3b1579a7bffb degal/shorturl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/degal/shorturl.py Wed Jun 03 19:03:28 2009 +0300 @@ -0,0 +1,208 @@ +# DeGAL - A pretty simple web image gallery +# Copyright (C) 2007 Tero Marttila +# http://marttila.de/~terom/degal/ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +import struct +import base64 +import shelve +import os.path + + +import utils, db, helpers, folder, image, log + +def int2key (id) : + """ + Turn an integer into a short-as-possible url-safe string + """ + for type in ('B', 'H', 'I') : + try : + return base64.b64encode(struct.pack(type, id), '-_').rstrip('=') + except struct.error : + continue + + raise Exception("ID overflow: %s" % id) + +def key2int (key) : + # base64 ignores extra padding, but if it doesn't, it's (4 - len%4), if len%4 != 0 + # and it breaks on unicode strings + bytes = base64.b64decode(str(key + '='*6), '-_') + + type = { + 1: 'B', + 2: 'H', + 4: 'I', + }[len(bytes)] + + return struct.unpack(type, bytes)[0] + +class DB (object) : + def __init__ (self, read_only=True) : + self.db = shelve.open('shorturls2', read_only and 'r' or 'c') + + def html_path (self, key, index) : + type, dirpath, fname = self.db[key] + + if type == 'img' : + fname += '.html' + elif type == 'dir' : + fname = '' + + if index : + dirpath = '../%s' % dirpath + + if type == 'dir' and index > 1 : + fname = 'index_%s.html' % (index - 1) + + return os.path.join(dirpath, fname) + + def image_info (self, key) : + type, dirpath, fname = self.db[key] + + if type != 'img' : + raise ValueError("%s is not an img" % key) + + return dirpath, fname + + def shorturls_for (self, paths) : + ret = [] + + for key in self.db.keys() : + if key.startswith('_') : + continue + + type, dir, fname = self.db[key] + path = os.path.join(dir.lstrip('.').lstrip('/'), fname) + if path in paths : + ret.append(key) + paths.remove(path) + + if paths : + raise ValueError("Paths not found: %s" % " ".join(paths)) + + return ret + +def html_path (key, index=None) : + dir, fname = node_info(key) + + if fname : + return utils.url(dir, fname + '.html') + else : + return utils.url(dir, helpers.url_for_page(index or 0)) + +def node_info (key) : + res = db.select("""SELECT dirpath, filename FROM nodes WHERE id=?""", key2int(key)).fetchone() + + if res : + return res + + else : + raise KeyError(key) + +def image_info (key) : + res = db.select("""SELECT dirpath, filename FROM images WHERE id=?""", key2int(key)).fetchone() + + if res : + return res + + else : + raise KeyError(key) + +def get_images (keys) : + res = [db.select("""SELECT dirpath, filename FROM images WHERE id=?""", key2int(key)).fetchone() for key in keys] + + # don't mind if we don't get as many as we asked for? + if res : + return res + + else : + raise KeyError(keys) + +def _got_obj_key (obj, id) : + key = int2key(id) + + obj.shorturl_code = key + + if isinstance(obj, folder.Folder) : + dir, fname = utils.strip_path(obj.path), '' + elif isinstance(obj, image.Image) : + dir, fname = utils.strip_path(obj.dir.path), obj.name + else : + assert(False, "%r %r" % (obj, id)) + + log.info("%6s -> %s/%s", key, dir, fname) + +def updateDB (root) : + """ + Update the SQL database + + type - one of 'img', 'dir' + dirpath - the path to the directory, e.g. '.', './foobar', './foobar/quux' + fname - the filename, one of '', 'DSC9839.JPG', 'this.png', etc. + """ + + dirqueue = [root] + + # dict of (dir, fname) -> obj + paths = {} + + while dirqueue : + dir = dirqueue.pop(0) + + dirqueue.extend(dir.subdirs.itervalues()) + + if dir.alive : + pathtuple = (utils.strip_path(dir.path), '') + + log.debug("dir %50s", pathtuple[0]) + + paths[pathtuple] = dir + + for img in dir.images.itervalues() : + pathtuple = (utils.strip_path(img.dir.path), img.name) + + log.debug("img %50s %15s", *pathtuple) + + paths[pathtuple] = img + + log.info("we have %d nodes", len(paths)) + + for (id, dir, fname) in db.select("SELECT id, dirpath, filename FROM nodes") : + try : + obj = paths.pop((dir, fname)) + key = int2key(id) + + obj.shorturl_code = key + + log.debug("%s %50s %15s -> %d %s", dir and "img" or "dir", dir, fname, id, key) + + except KeyError : + pass +# log.warning("non-existant node (%d, %s, %s) in db", id, dir, fname) + + if paths : + log.info("allocating shorturls for %d new nodes:", len(paths)) + + db.insert_many( + _got_obj_key, + "INSERT INTO nodes (dirpath, filename) VALUES (?, ?)", + ((obj, (path, fname)) for ((path, fname), obj) in paths.iteritems()) + ) + else : + log.info("no new images") +