diff --git a/buildcache2.py b/buildcache2.py index 1a93cec0..3eaa13ea 100755 --- a/buildcache2.py +++ b/buildcache2.py @@ -1,7 +1,7 @@ #======================================================================= __version__ = '''0.0.01''' -__sub_version__ = '''20130521232425''' +__sub_version__ = '''20130522014220''' __copyright__ = '''(c) Alex A. Naanou 2011''' @@ -20,7 +20,7 @@ import gid #----------------------------------------------------------------------- -config = { +CONFIG = { 'absolute-path': False, 'cache-image-name': '%(guid)s - %(name)s', @@ -49,12 +49,12 @@ config = { } } -data = { +DATA = { 'version': '2.0', 'current': None, - 'ribbons': [], - 'order': [], - 'image_file': 'images.json', + 'ribbons': (), + 'order': (), + 'image_file': None, } IMAGE_EXT = OR(*( @@ -71,17 +71,30 @@ TARGET: %(target-file)s #----------------------------------------------------------------------- +# Helpers... +#------------------------------------------------------------pathjoin--- def pathjoin(*p): ''' ''' return ('/'.join(p)).replace('//', '/') +#-------------------------------------------------------------getpath--- +def getpath(root, path, absolute=False): + ''' + ''' + if absolute == True: + return 'file:///' + urllib2.quote(pathjoin(root, path), safe='/:') + else: + return urllib2.quote(path, safe='/') + + +#-------------------------------------------------------------log_err--- def log_err(path, e, source_file, target_file): ''' ''' - err_file = pathjoin(path, config['error']) + err_file = pathjoin(path, CONFIG['error']) if not os.path.exists(err_file): err = open(err_file, 'w') else: @@ -94,6 +107,7 @@ def log_err(path, e, source_file, target_file): }) +#------------------------------------------------------------hash_gid--- def hash_gid(img, force=False): ''' Generate gid based on preview file content. @@ -105,7 +119,58 @@ def hash_gid(img, force=False): return sha.sha(img.tostring()).hexdigest() -def build_cache_dirs(path, config=config): +#-----------------------------------------------------report_progress--- +def report_progress(img, status): + ''' + ''' + # created all previews... + if False not in status: + print '.', + # created no previews... + elif True not in status: + print '-', + # created some previews... + else: + print 'p', + return img + + +#-----------------------------------------make_inline_report_progress--- +def make_inline_report_progress(state=None): + if state == None: + state = {} + def _inline_report_progress(img, status): + created = state.get('created', 0) + skipped = state.get('skipped', 0) + partial = state.get('partial', 0) + + # created all previews... + if False not in status: + created += 1 + state['created'] = created + + # created no previews... + elif True not in status: + skipped += 1 + state['skipped'] = skipped + + # created some previews... + else: + partial += 1 + state['partial'] = partial + + print 'Previews created: %s partial: %s skipped: %s...\r' % (created, partial, skipped), + + return img + return _inline_report_progress + + + +#----------------------------------------------------------------------- +# API... + +#----------------------------------------------------build_cache_dirs--- +def build_cache_dirs(path, config=CONFIG): ''' Build cache directory tree. ''' @@ -116,14 +181,15 @@ def build_cache_dirs(path, config=config): os.makedirs(p) -def build_images(path, config=config, gid_generator=hash_gid): +#--------------------------------------------------------build_images--- +def build_images(path, config=CONFIG, gid_generator=hash_gid): ''' Build image structures update images.json in cache. ''' absolute_path = config['absolute-path'] for name in os.listdir(path): - iid, ext = os.path.splitext(name) + fname, ext = os.path.splitext(name) if ext != IMAGE_EXT: continue @@ -132,28 +198,146 @@ def build_images(path, config=config, gid_generator=hash_gid): img = { 'id': gid_generator(source_path), + 'name': name, 'type': 'image', 'state': 'single', - 'path': None, + 'path': getpath(path, name, absolute_path), 'ctime': os.path.getctime(source_path), 'preview': {}, } - if absolute_path == True: - img['path'] = 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:') - else: - img['path'] = urllib2.quote(name) yield img -def build_previews(image): +#------------------------------------------------------build_previews--- +# NOTE: this will create images in the file system. +def build_previews(image, path=None, config=CONFIG, dry_run=True): + ''' + + NOTE: this needs the cache directory structure present. + ''' + status = [] + # config... + absolute_path = config['absolute-path'] + dirs = config['cache-structure'] + sizes = config['sizes'] + cache_name = config['cache-image-name'] + + # data... + gid = image['id'] + img_name = image['name'] + name = os.path.splitext(img_name)[0] + img_path = image['path'] + + if absolute_path == False: + source_path = os.path.join(path, img_path) + else: + # XXX is this the best way??? + o = urllib2.urlopen(img_path) + source_path = o.fp.name + o.close() + + img = Image.open(source_path, 'r') + + # biggest preview is the original image... + image['preview'][str(max(*img.size)) + 'px'] = img_path + + # previews... + for k, spec in sizes.items(): + + if k in image['preview'].keys(): + continue + + # build the two paths: relative and full... + n = pathjoin(dirs[k], cache_name % {'guid': gid, 'name': img_name}) + p = pathjoin(path, n) + + # do not upscale images... + if max(*img.size) <= spec: + continue + + # add image to index... + if not os.path.exists(p): + scale = spec/float(max(*img.size)) + preview = img.resize((int(img.size[0]*scale), int(img.size[1]*scale)), Image.ANTIALIAS) + + if not dry_run: + preview.save(p) + else: + preview.close() + + ##!!! metadata??? + + status += [True] + + # image exists... + else: + status += [False] + + image['preview'][str(spec) + 'px'] = getpath(path, n, absolute_path) + + return image, status + + +#----------------------------------------------------------build_data--- +def build_data(images, path, config=CONFIG): ''' ''' + images_index = {} + marked = [] + data = DATA.copy() + ribbon = [] + + for image in images: + gid = image['id'] + + images_index[gid] = image + ribbon += [gid] + + ribbon.sort(lambda a, b: cmp(images_index[b]['ctime'], images_index[a]['ctime'])) + + data['ribbons'] = [ribbon] + data['order'] = ribbon[:] + data['current'] = ribbon[0] + + return data, images_index, marked #----------------------------------------------------------------------- +# High-level API... +#---------------------------------------------------------build_cache--- +##!!! DO NOT OVERWRITE EXISTING DATA... +def build_cache(path, config=CONFIG, gid_generator=hash_gid, + report_progress=report_progress, dry_run=False): + ''' + ''' + absolute_path = config['absolute-path'] + + build_cache_dirs(path, config) + + data, images, marked = build_data( + (report_progress(*build_previews(img, path, config, dry_run=dry_run)) + for img in build_images(path, config, gid_generator)), + path, config) + + images_file = config['images'] + data_file = config['data'] + marked_file = config['marked'] + + data['image_file'] = getpath(path, images_file, absolute_path) + + if not dry_run: + ##!!! DO NOT OVERWRITE EXISTING DATA... + with open(os.path.join(path, images_file), 'w') as f: + json.dump(images, f, indent=4) + with open(os.path.join(path, data_file), 'w') as f: + json.dump(data, f, indent=4) + with open(os.path.join(path, marked_file), 'w') as f: + json.dump(marked, f, indent=4) + + return data @@ -164,5 +348,6 @@ if __name__ == '__main__': + #======================================================================= # vim:set ts=4 sw=4 nowrap :