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