mirror of
https://github.com/flynx/ImageGrid.git
synced 2025-10-28 18:00:09 +00:00
finished most of buildcache.py...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
4e76cbbb60
commit
fd8e68abd7
318
buildcache.old.py
Executable file
318
buildcache.old.py
Executable file
@ -0,0 +1,318 @@
|
|||||||
|
#=======================================================================
|
||||||
|
|
||||||
|
__version__ = '''0.0.01'''
|
||||||
|
__sub_version__ = '''20130521225013'''
|
||||||
|
__copyright__ = '''(c) Alex A. Naanou 2012'''
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
import os
|
||||||
|
import Image
|
||||||
|
import json
|
||||||
|
import sha
|
||||||
|
import urllib2
|
||||||
|
|
||||||
|
from pli.logictypes import OR
|
||||||
|
|
||||||
|
import gid
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
# XXX fanatically cleanup and normalise paths...
|
||||||
|
# XXX use real uuid's...
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# - load config from file...
|
||||||
|
# - accept a path on command-line
|
||||||
|
# - default path is cwd
|
||||||
|
# - support nested fav's for ribbons
|
||||||
|
#
|
||||||
|
# Long Term TODO:
|
||||||
|
# - support processed images
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
config = {
|
||||||
|
'format-version': '2.0',
|
||||||
|
|
||||||
|
'cache-structure': {
|
||||||
|
# XXX make these as close to standard as possible and keep
|
||||||
|
# sane distances...
|
||||||
|
'150px': '.ImageGridCache/150px/',
|
||||||
|
'350px': '.ImageGridCache/350px/',
|
||||||
|
'900px': '.ImageGridCache/900px/',
|
||||||
|
'1080px': '.ImageGridCache/1080px/',
|
||||||
|
'1920px': '.ImageGridCache/1920px/',
|
||||||
|
},
|
||||||
|
|
||||||
|
# gen1 format...
|
||||||
|
'json': '.ImageGridCache/all.json',
|
||||||
|
|
||||||
|
# gen3 format...
|
||||||
|
'images': '.ImageGridCache/images.json',
|
||||||
|
'data': '.ImageGridCache/data.json',
|
||||||
|
'marked': '.ImageGridCache/marked.json',
|
||||||
|
|
||||||
|
'error': '.ImageGridCache/error.log',
|
||||||
|
'sizes': {
|
||||||
|
'150px': 150,
|
||||||
|
'350px': 350,
|
||||||
|
'900px': 900,
|
||||||
|
'1080px': 1080,
|
||||||
|
'1920px': 1920,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
images = {
|
||||||
|
'position': 0,
|
||||||
|
'ribbons':[
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_EXT = OR(*(
|
||||||
|
'.jpg', '.jpeg', '.JPG', '.JPEG',
|
||||||
|
))
|
||||||
|
|
||||||
|
ERR_LOG = '''\
|
||||||
|
ERROR: %(error)s
|
||||||
|
SOURCE: %(source-file)s
|
||||||
|
TARGET: %(target-file)s
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
CACHE_FILE_NAME = '%(guid)s - %(name)s'
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pathjoin(*p):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
return ('/'.join(p)).replace('//', '/')
|
||||||
|
|
||||||
|
def log_err(path, e, source_file, target_file):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
err_file = pathjoin(path, config['error'])
|
||||||
|
if not os.path.exists(err_file):
|
||||||
|
err = open(err_file, 'w')
|
||||||
|
else:
|
||||||
|
err = open(err_file, 'a')
|
||||||
|
with err:
|
||||||
|
err.write(ERR_LOG % {
|
||||||
|
'source-file': source_file,
|
||||||
|
'target-file': target_file,
|
||||||
|
'error': e,
|
||||||
|
})
|
||||||
|
|
||||||
|
# this should:
|
||||||
|
# 1) see if image is cached, if yes return the cached guid (if dates match)...
|
||||||
|
# 2) read the image file and get its guid
|
||||||
|
##!!!
|
||||||
|
def get_image_guid(path, force=False):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
im = Image.open(path)
|
||||||
|
return sha.sha(im.tostring()).hexdigest()
|
||||||
|
## return sha.sha(open(path, 'r').read())
|
||||||
|
|
||||||
|
|
||||||
|
# return list of paths ending in a pattern...
|
||||||
|
def build_cache_dirs(path, config=config):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
dirs = config['cache-structure']
|
||||||
|
for _, k in dirs.items():
|
||||||
|
p = pathjoin(path, k)
|
||||||
|
if not os.path.exists(p):
|
||||||
|
os.makedirs(p)
|
||||||
|
|
||||||
|
|
||||||
|
# image format:
|
||||||
|
# {
|
||||||
|
# 'id': <image-id>,
|
||||||
|
# 'preview': {
|
||||||
|
# <resolution>: <cache-path>,
|
||||||
|
# ...
|
||||||
|
# },
|
||||||
|
# 'path': <image-path>
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
def build_index(path, images=None, count=None):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
dirs = config['cache-structure']
|
||||||
|
sizes = config['sizes']
|
||||||
|
n = -1
|
||||||
|
if images == None:
|
||||||
|
images = {
|
||||||
|
'position': 0,
|
||||||
|
'ribbons': [{}],
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in os.listdir(path):
|
||||||
|
# skip non-images...
|
||||||
|
iid, ext = os.path.splitext(name)
|
||||||
|
if ext != IMAGE_EXT:
|
||||||
|
continue
|
||||||
|
|
||||||
|
##!!! this is here for debuging...
|
||||||
|
n += 1
|
||||||
|
if count != None and n >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
i = {
|
||||||
|
'id': iid,
|
||||||
|
'preview': {},
|
||||||
|
##!!! absolute paths???
|
||||||
|
'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
|
||||||
|
}
|
||||||
|
img = Image.open(pathjoin(path, name), 'r')
|
||||||
|
try:
|
||||||
|
iid = sha.sha(img.tostring()).hexdigest()
|
||||||
|
except IOError, e:
|
||||||
|
print 'x',
|
||||||
|
log_err(path, e, name, '-')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if iid in images['ribbons'][0]:
|
||||||
|
print '_',
|
||||||
|
continue
|
||||||
|
i['id'] = iid
|
||||||
|
images['ribbons'][0][iid] = i
|
||||||
|
print '.',
|
||||||
|
|
||||||
|
return images
|
||||||
|
|
||||||
|
|
||||||
|
# XXX this will not overwrite existing files...
|
||||||
|
# XXX make this destingwish absolute and relative paths...
|
||||||
|
def make_cache_images(path, images, config=config):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
dirs = config['cache-structure']
|
||||||
|
sizes = config['sizes']
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
for name in os.listdir(path):
|
||||||
|
# skip non-images...
|
||||||
|
iid, ext = os.path.splitext(name)
|
||||||
|
source_path = pathjoin(path, name)
|
||||||
|
if ext != IMAGE_EXT:
|
||||||
|
continue
|
||||||
|
n += 1
|
||||||
|
i = {
|
||||||
|
'id': iid,
|
||||||
|
'preview': {},
|
||||||
|
## 'path': pathjoin(path, name),
|
||||||
|
##!!! absolute paths???
|
||||||
|
'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
|
||||||
|
'ctime': os.path.getctime(source_path),
|
||||||
|
}
|
||||||
|
img = Image.open(source_path, 'r')
|
||||||
|
try:
|
||||||
|
##!!! use a real gid -- gid.image_gid(path, ...)
|
||||||
|
iid = sha.sha(img.tostring()).hexdigest()
|
||||||
|
except IOError, e:
|
||||||
|
print 'x',
|
||||||
|
log_err(path, e, name, '-')
|
||||||
|
i['error'] = 'IOError: ' + str(e)
|
||||||
|
continue
|
||||||
|
finally:
|
||||||
|
# we need to know which images are dead...
|
||||||
|
images['ribbons'][0][iid] = i
|
||||||
|
i['id'] = iid
|
||||||
|
# add original image to struct...
|
||||||
|
## i['preview'][str(max(*img.size)) + 'px'] = pathjoin(path, name)
|
||||||
|
i['preview'][str(max(*img.size)) + 'px'] = 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:')
|
||||||
|
# previews...
|
||||||
|
for k, spec in sizes.items():
|
||||||
|
p = pathjoin(path, dirs[k], CACHE_FILE_NAME % {'guid': iid, 'name': name})
|
||||||
|
# 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)
|
||||||
|
preview.save(p)
|
||||||
|
##!!! metadata???
|
||||||
|
##!!!
|
||||||
|
print '.',
|
||||||
|
else:
|
||||||
|
# indicate an image skip...
|
||||||
|
print '_',
|
||||||
|
## i['preview'][str(spec) + 'px'] = p
|
||||||
|
##!!! absolute paths???
|
||||||
|
i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
|
||||||
|
# put original image in cache...
|
||||||
|
## i['preview'][str(spec) + 'px'] = p
|
||||||
|
i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
|
||||||
|
images['position'] = images['ribbons'][0].keys()[0]
|
||||||
|
with open(pathjoin(path, config['json']), 'w') as f:
|
||||||
|
json.dump(images, f, indent=4)
|
||||||
|
##!!! STUB...
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_local_cache(path):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
import time
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
|
|
||||||
|
build_cache_dirs(path)
|
||||||
|
|
||||||
|
n = make_cache_images(path, images)
|
||||||
|
|
||||||
|
t1 = time.time()
|
||||||
|
|
||||||
|
print
|
||||||
|
print 'Processed %s images in %s seconds.' % (n, t1-t0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
parser = OptionParser()
|
||||||
|
|
||||||
|
##!!! need to define the path so that it shoes up in -h
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.print_usage()
|
||||||
|
else:
|
||||||
|
IN_PATH = args[0]
|
||||||
|
IN_PATH = IN_PATH.replace('\\', '/')
|
||||||
|
build_local_cache(IN_PATH)
|
||||||
|
|
||||||
|
|
||||||
|
## PATH = 'images/cache-test/'
|
||||||
|
## PATH = 'L:/incoming/UNSORTED/Images/fav'
|
||||||
|
|
||||||
|
## build_local_cache(PATH)
|
||||||
|
|
||||||
|
## index = build_index(PATH, count=10)
|
||||||
|
##
|
||||||
|
## import IPython
|
||||||
|
## IPython.embed()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#=======================================================================
|
||||||
|
# vim:set ts=4 sw=4 nowrap :
|
||||||
866
buildcache.py
866
buildcache.py
@ -1,318 +1,548 @@
|
|||||||
#=======================================================================
|
#=======================================================================
|
||||||
|
|
||||||
__version__ = '''0.0.01'''
|
__version__ = '''0.0.01'''
|
||||||
__sub_version__ = '''20130521225013'''
|
__sub_version__ = '''20130522213945'''
|
||||||
__copyright__ = '''(c) Alex A. Naanou 2012'''
|
__copyright__ = '''(c) Alex A. Naanou 2011'''
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
#-----------------------------------------------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import Image
|
import Image
|
||||||
import json
|
import json
|
||||||
import sha
|
import sha
|
||||||
import urllib2
|
import urllib2
|
||||||
|
import time
|
||||||
from pli.logictypes import OR
|
|
||||||
|
from pli.logictypes import OR
|
||||||
import gid
|
|
||||||
|
import gid
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
# XXX fanatically cleanup and normalise paths...
|
#-----------------------------------------------------------------------
|
||||||
# XXX use real uuid's...
|
|
||||||
#
|
CONFIG = {
|
||||||
# TODO:
|
'absolute-path': False,
|
||||||
# - load config from file...
|
|
||||||
# - accept a path on command-line
|
# this can be:
|
||||||
# - default path is cwd
|
# - original (default)
|
||||||
# - support nested fav's for ribbons
|
# - preview size
|
||||||
#
|
# - metadata
|
||||||
# Long Term TODO:
|
'gid-source': 'original',
|
||||||
# - support processed images
|
|
||||||
#
|
'cache-image-name': '%(guid)s - %(name)s',
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------
|
# the rest of the paths will be relative to this...
|
||||||
|
'cache-dir': '.ImageGridCache',
|
||||||
config = {
|
|
||||||
'format-version': '2.0',
|
'images': 'images.json',
|
||||||
|
'data': 'data.json',
|
||||||
'cache-structure': {
|
'marked': 'marked.json',
|
||||||
# XXX make these as close to standard as possible and keep
|
|
||||||
# sane distances...
|
'config': 'ImageGrid.cfg',
|
||||||
'150px': '.ImageGridCache/150px/',
|
'error': 'error.log',
|
||||||
'350px': '.ImageGridCache/350px/',
|
|
||||||
'900px': '.ImageGridCache/900px/',
|
'cache-structure': {
|
||||||
'1080px': '.ImageGridCache/1080px/',
|
# make these as close to standard as possible and keep sane
|
||||||
'1920px': '.ImageGridCache/1920px/',
|
# distances...
|
||||||
},
|
'150px': '150px/',
|
||||||
|
'350px': '350px/',
|
||||||
# gen1 format...
|
'900px': '900px/',
|
||||||
'json': '.ImageGridCache/all.json',
|
'1080px': '1080px/',
|
||||||
|
'1920px': '1920px/',
|
||||||
# gen3 format...
|
},
|
||||||
'images': '.ImageGridCache/images.json',
|
'sizes': {
|
||||||
'data': '.ImageGridCache/data.json',
|
'150px': 150,
|
||||||
'marked': '.ImageGridCache/marked.json',
|
'350px': 350,
|
||||||
|
'900px': 900,
|
||||||
'error': '.ImageGridCache/error.log',
|
'1080px': 1080,
|
||||||
'sizes': {
|
'1920px': 1920,
|
||||||
'150px': 150,
|
}
|
||||||
'350px': 350,
|
}
|
||||||
'900px': 900,
|
|
||||||
'1080px': 1080,
|
DATA = {
|
||||||
'1920px': 1920,
|
'version': '2.0',
|
||||||
}
|
'current': None,
|
||||||
}
|
'ribbons': (),
|
||||||
|
'order': (),
|
||||||
|
'image_file': None,
|
||||||
images = {
|
}
|
||||||
'position': 0,
|
|
||||||
'ribbons':[
|
IMAGE_EXT = OR(*(
|
||||||
{}
|
'.jpg', '.jpeg', '.JPG', '.JPEG',
|
||||||
]
|
))
|
||||||
}
|
|
||||||
|
ERR_LOG = '''\
|
||||||
|
ERROR: %(error)s
|
||||||
IMAGE_EXT = OR(*(
|
SOURCE: %(source-file)s
|
||||||
'.jpg', '.jpeg', '.JPG', '.JPEG',
|
TARGET: %(target-file)s
|
||||||
))
|
|
||||||
|
|
||||||
ERR_LOG = '''\
|
'''
|
||||||
ERROR: %(error)s
|
|
||||||
SOURCE: %(source-file)s
|
|
||||||
TARGET: %(target-file)s
|
#-----------------------------------------------------------------------
|
||||||
|
# Helpers...
|
||||||
|
#------------------------------------------------------------pathjoin---
|
||||||
'''
|
def pathjoin(*p):
|
||||||
|
'''
|
||||||
CACHE_FILE_NAME = '%(guid)s - %(name)s'
|
'''
|
||||||
|
return ('/'.join(p)).replace('//', '/')
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
|
#-------------------------------------------------------------getpath---
|
||||||
def pathjoin(*p):
|
def getpath(root, path, absolute=False):
|
||||||
'''
|
'''
|
||||||
'''
|
'''
|
||||||
return ('/'.join(p)).replace('//', '/')
|
if absolute == True:
|
||||||
|
return 'file:///' + urllib2.quote(pathjoin(root, path), safe='/:')
|
||||||
def log_err(path, e, source_file, target_file):
|
else:
|
||||||
'''
|
return urllib2.quote(pathjoin(path), safe='/:')
|
||||||
'''
|
|
||||||
err_file = pathjoin(path, config['error'])
|
|
||||||
if not os.path.exists(err_file):
|
#-------------------------------------------------------------log_err---
|
||||||
err = open(err_file, 'w')
|
def log_err(path, e, source_file, target_file):
|
||||||
else:
|
'''
|
||||||
err = open(err_file, 'a')
|
'''
|
||||||
with err:
|
err_file = pathjoin(path, CONFIG['error'])
|
||||||
err.write(ERR_LOG % {
|
if not os.path.exists(err_file):
|
||||||
'source-file': source_file,
|
err = open(err_file, 'w')
|
||||||
'target-file': target_file,
|
else:
|
||||||
'error': e,
|
err = open(err_file, 'a')
|
||||||
})
|
with err:
|
||||||
|
err.write(ERR_LOG % {
|
||||||
# this should:
|
'source-file': source_file,
|
||||||
# 1) see if image is cached, if yes return the cached guid (if dates match)...
|
'target-file': target_file,
|
||||||
# 2) read the image file and get its guid
|
'error': e,
|
||||||
##!!!
|
})
|
||||||
def get_image_guid(path, force=False):
|
|
||||||
'''
|
|
||||||
'''
|
#------------------------------------------------------------hash_gid---
|
||||||
im = Image.open(path)
|
def hash_gid(img, force=False):
|
||||||
return sha.sha(im.tostring()).hexdigest()
|
'''
|
||||||
## return sha.sha(open(path, 'r').read())
|
Generate gid based on preview file content.
|
||||||
|
|
||||||
|
NOTE: img can be either a path or an Image.
|
||||||
# return list of paths ending in a pattern...
|
'''
|
||||||
def build_cache_dirs(path, config=config):
|
if type(img) in (str, unicode):
|
||||||
'''
|
img = Image.open(img)
|
||||||
'''
|
return sha.sha(img.tostring()).hexdigest()
|
||||||
dirs = config['cache-structure']
|
|
||||||
for _, k in dirs.items():
|
|
||||||
p = pathjoin(path, k)
|
#-----------------------------------------------------report_progress---
|
||||||
if not os.path.exists(p):
|
def report_progress(img, status):
|
||||||
os.makedirs(p)
|
'''
|
||||||
|
'''
|
||||||
|
# created all previews...
|
||||||
# image format:
|
if False not in status:
|
||||||
# {
|
print '.',
|
||||||
# 'id': <image-id>,
|
# created no previews...
|
||||||
# 'preview': {
|
elif True not in status:
|
||||||
# <resolution>: <cache-path>,
|
print '-',
|
||||||
# ...
|
# created some previews...
|
||||||
# },
|
else:
|
||||||
# 'path': <image-path>
|
print 'p',
|
||||||
# }
|
return img
|
||||||
#
|
|
||||||
def build_index(path, images=None, count=None):
|
|
||||||
'''
|
#-----------------------------------------make_inline_report_progress---
|
||||||
'''
|
def make_inline_report_progress(state=None):
|
||||||
dirs = config['cache-structure']
|
if state == None:
|
||||||
sizes = config['sizes']
|
state = {}
|
||||||
n = -1
|
if 'started at' not in state:
|
||||||
if images == None:
|
state['started at'] = time.time()
|
||||||
images = {
|
|
||||||
'position': 0,
|
def _inline_report_progress(img, status):
|
||||||
'ribbons': [{}],
|
created = state.get('created', 0)
|
||||||
}
|
skipped = state.get('skipped', 0)
|
||||||
|
partial = state.get('partial', 0)
|
||||||
for name in os.listdir(path):
|
|
||||||
# skip non-images...
|
|
||||||
iid, ext = os.path.splitext(name)
|
# created all previews...
|
||||||
if ext != IMAGE_EXT:
|
if False not in status:
|
||||||
continue
|
created += 1
|
||||||
|
state['created'] = created
|
||||||
##!!! this is here for debuging...
|
|
||||||
n += 1
|
# created no previews...
|
||||||
if count != None and n >= count:
|
elif True not in status:
|
||||||
break
|
skipped += 1
|
||||||
|
state['skipped'] = skipped
|
||||||
i = {
|
|
||||||
'id': iid,
|
# created some previews...
|
||||||
'preview': {},
|
else:
|
||||||
##!!! absolute paths???
|
partial += 1
|
||||||
'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
|
state['partial'] = partial
|
||||||
}
|
|
||||||
img = Image.open(pathjoin(path, name), 'r')
|
state['done at'] = time.time()
|
||||||
try:
|
|
||||||
iid = sha.sha(img.tostring()).hexdigest()
|
print 'Previews created: %s partial: %s skipped: %s...\r' % (created, partial, skipped),
|
||||||
except IOError, e:
|
|
||||||
print 'x',
|
return img
|
||||||
log_err(path, e, name, '-')
|
return _inline_report_progress
|
||||||
continue
|
|
||||||
|
|
||||||
if iid in images['ribbons'][0]:
|
|
||||||
print '_',
|
#-----------------------------------------------------------------------
|
||||||
continue
|
# API...
|
||||||
i['id'] = iid
|
#----------------------------------------------------build_cache_dirs---
|
||||||
images['ribbons'][0][iid] = i
|
def build_cache_dirs(path, config=CONFIG, dry_run=False, verbosity=0):
|
||||||
print '.',
|
'''
|
||||||
|
Build cache directory tree.
|
||||||
return images
|
'''
|
||||||
|
if verbosity >= 1:
|
||||||
|
print 'Creating cache directory structure...'
|
||||||
# XXX this will not overwrite existing files...
|
|
||||||
# XXX make this destingwish absolute and relative paths...
|
cache_dir = config['cache-dir']
|
||||||
def make_cache_images(path, images, config=config):
|
dirs = config['cache-structure']
|
||||||
'''
|
|
||||||
'''
|
for _, k in dirs.items():
|
||||||
dirs = config['cache-structure']
|
p = pathjoin(path, cache_dir, k)
|
||||||
sizes = config['sizes']
|
|
||||||
n = 0
|
if not os.path.exists(p):
|
||||||
|
if not dry_run:
|
||||||
for name in os.listdir(path):
|
os.makedirs(p)
|
||||||
# skip non-images...
|
if verbosity >= 2:
|
||||||
iid, ext = os.path.splitext(name)
|
print 'Creating directory: %s' % p
|
||||||
source_path = pathjoin(path, name)
|
elif verbosity >= 2:
|
||||||
if ext != IMAGE_EXT:
|
print 'Directory exists: %s' % p
|
||||||
continue
|
|
||||||
n += 1
|
|
||||||
i = {
|
#--------------------------------------------------------build_images---
|
||||||
'id': iid,
|
def build_images(path, config=CONFIG, gid_generator=hash_gid, verbosity=0):
|
||||||
'preview': {},
|
'''
|
||||||
## 'path': pathjoin(path, name),
|
Build image structures update images.json in cache.
|
||||||
##!!! absolute paths???
|
'''
|
||||||
'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
|
absolute_path = config['absolute-path']
|
||||||
'ctime': os.path.getctime(source_path),
|
|
||||||
}
|
for name in os.listdir(path):
|
||||||
img = Image.open(source_path, 'r')
|
fname, ext = os.path.splitext(name)
|
||||||
try:
|
|
||||||
##!!! use a real gid -- gid.image_gid(path, ...)
|
if ext != IMAGE_EXT:
|
||||||
iid = sha.sha(img.tostring()).hexdigest()
|
continue
|
||||||
except IOError, e:
|
|
||||||
print 'x',
|
source_path = pathjoin(path, name)
|
||||||
log_err(path, e, name, '-')
|
|
||||||
i['error'] = 'IOError: ' + str(e)
|
img = {
|
||||||
continue
|
'id': gid_generator(source_path),
|
||||||
finally:
|
'name': name,
|
||||||
# we need to know which images are dead...
|
'type': 'image',
|
||||||
images['ribbons'][0][iid] = i
|
'state': 'single',
|
||||||
i['id'] = iid
|
'path': getpath(path, name, absolute_path),
|
||||||
# add original image to struct...
|
'ctime': os.path.getctime(source_path),
|
||||||
## i['preview'][str(max(*img.size)) + 'px'] = pathjoin(path, name)
|
'preview': {},
|
||||||
i['preview'][str(max(*img.size)) + 'px'] = 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:')
|
}
|
||||||
# previews...
|
|
||||||
for k, spec in sizes.items():
|
if verbosity >= 2:
|
||||||
p = pathjoin(path, dirs[k], CACHE_FILE_NAME % {'guid': iid, 'name': name})
|
print (' '*72) + '\rProcessing image: %s' % getpath(path, name, absolute_path)
|
||||||
# do not upscale images...
|
|
||||||
if max(*img.size) <= spec:
|
|
||||||
continue
|
yield img
|
||||||
# add image to index...
|
|
||||||
if not os.path.exists(p):
|
|
||||||
scale = spec/float(max(*img.size))
|
#------------------------------------------------------build_previews---
|
||||||
preview = img.resize((int(img.size[0]*scale), int(img.size[1]*scale)), Image.ANTIALIAS)
|
# NOTE: this will create images in the file system.
|
||||||
preview.save(p)
|
def build_previews(image, path=None, config=CONFIG, dry_run=True, verbosity=0):
|
||||||
##!!! metadata???
|
'''
|
||||||
##!!!
|
|
||||||
print '.',
|
NOTE: this needs the cache directory structure present.
|
||||||
else:
|
'''
|
||||||
# indicate an image skip...
|
status = []
|
||||||
print '_',
|
# config...
|
||||||
## i['preview'][str(spec) + 'px'] = p
|
absolute_path = config['absolute-path']
|
||||||
##!!! absolute paths???
|
cache_dir = config['cache-dir']
|
||||||
i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
|
dirs = config['cache-structure']
|
||||||
# put original image in cache...
|
sizes = config['sizes']
|
||||||
## i['preview'][str(spec) + 'px'] = p
|
cache_name = config['cache-image-name']
|
||||||
i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
|
|
||||||
images['position'] = images['ribbons'][0].keys()[0]
|
# data...
|
||||||
with open(pathjoin(path, config['json']), 'w') as f:
|
gid = image['id']
|
||||||
json.dump(images, f, indent=4)
|
img_name = image['name']
|
||||||
##!!! STUB...
|
name = os.path.splitext(img_name)[0]
|
||||||
return n
|
img_path = image['path']
|
||||||
|
|
||||||
|
if absolute_path == False:
|
||||||
|
source_path = os.path.join(path, img_path)
|
||||||
#-----------------------------------------------------------------------
|
else:
|
||||||
|
# XXX is this the best way???
|
||||||
def build_local_cache(path):
|
o = urllib2.urlopen(img_path)
|
||||||
'''
|
source_path = o.fp.name
|
||||||
'''
|
o.close()
|
||||||
import time
|
|
||||||
|
img = Image.open(source_path, 'r')
|
||||||
t0 = time.time()
|
|
||||||
|
# biggest preview is the original image...
|
||||||
build_cache_dirs(path)
|
image['preview'][str(max(*img.size)) + 'px'] = img_path
|
||||||
|
|
||||||
n = make_cache_images(path, images)
|
# previews...
|
||||||
|
for k, spec in sizes.items():
|
||||||
t1 = time.time()
|
|
||||||
|
if k in image['preview'].keys():
|
||||||
print
|
continue
|
||||||
print 'Processed %s images in %s seconds.' % (n, t1-t0)
|
|
||||||
|
# build the two paths: relative and full...
|
||||||
|
n = pathjoin(cache_dir, dirs[k], cache_name % {'guid': gid, 'name': img_name})
|
||||||
|
p = pathjoin(path, n)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# do not upscale images...
|
||||||
if __name__ == '__main__':
|
if max(*img.size) <= spec:
|
||||||
from optparse import OptionParser
|
continue
|
||||||
|
|
||||||
parser = OptionParser()
|
# add image to index...
|
||||||
|
if not os.path.exists(p):
|
||||||
##!!! need to define the path so that it shoes up in -h
|
scale = spec/float(max(*img.size))
|
||||||
|
preview = img.resize((int(img.size[0]*scale), int(img.size[1]*scale)), Image.ANTIALIAS)
|
||||||
options, args = parser.parse_args()
|
|
||||||
|
if not dry_run:
|
||||||
if len(args) != 1:
|
preview.save(p)
|
||||||
parser.print_usage()
|
else:
|
||||||
else:
|
preview.close()
|
||||||
IN_PATH = args[0]
|
|
||||||
IN_PATH = IN_PATH.replace('\\', '/')
|
##!!! metadata???
|
||||||
build_local_cache(IN_PATH)
|
|
||||||
|
status += [True]
|
||||||
|
|
||||||
## PATH = 'images/cache-test/'
|
# image exists...
|
||||||
## PATH = 'L:/incoming/UNSORTED/Images/fav'
|
else:
|
||||||
|
status += [False]
|
||||||
## build_local_cache(PATH)
|
|
||||||
|
image['preview'][str(spec) + 'px'] = getpath(path, n, absolute_path)
|
||||||
## index = build_index(PATH, count=10)
|
|
||||||
##
|
if verbosity >= 2:
|
||||||
## import IPython
|
print ' %s: %s' % ('C' if status[-1] else 'S', getpath(path, n, absolute_path))
|
||||||
## IPython.embed()
|
|
||||||
|
|
||||||
|
return image, status
|
||||||
|
|
||||||
|
|
||||||
#=======================================================================
|
#----------------------------------------------------------build_data---
|
||||||
# vim:set ts=4 sw=4 nowrap :
|
##!!! add option to consider fav depth and build a correct ribbon structure...
|
||||||
|
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, verbosity=0):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
cache_dir = config['cache-dir']
|
||||||
|
absolute_path = config['absolute-path']
|
||||||
|
|
||||||
|
build_cache_dirs(path, config, dry_run, verbosity)
|
||||||
|
|
||||||
|
if report_progress == None:
|
||||||
|
report_progress = lambda a, b: a
|
||||||
|
|
||||||
|
data, images, marked = build_data(
|
||||||
|
(report_progress(
|
||||||
|
*build_previews(img, path, config, dry_run=dry_run, verbosity=verbosity))
|
||||||
|
for img in build_images(path, config, gid_generator, verbosity=verbosity)),
|
||||||
|
path, config)
|
||||||
|
|
||||||
|
images_file = pathjoin(cache_dir, config['images'])
|
||||||
|
data_file = pathjoin(cache_dir, config['data'])
|
||||||
|
marked_file = pathjoin(cache_dir, config['marked'])
|
||||||
|
|
||||||
|
data['image_file'] = getpath(path, images_file, absolute_path)
|
||||||
|
|
||||||
|
if verbosity >= 1:
|
||||||
|
print
|
||||||
|
|
||||||
|
for n, d in {images_file: images, data_file: data, marked_file: marked}.items():
|
||||||
|
n = os.path.join(path, n)
|
||||||
|
if verbosity >= 1:
|
||||||
|
print 'Writing: %s' % n
|
||||||
|
if not dry_run:
|
||||||
|
##!!! DO NOT OVERWRITE EXISTING DATA...
|
||||||
|
with open(n, 'w') as f:
|
||||||
|
json.dump(d, f, indent=4)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from optparse import OptionParser, OptionGroup
|
||||||
|
|
||||||
|
parser = OptionParser(
|
||||||
|
usage='Usage: %prog [options] ROOT',
|
||||||
|
version='%prog ' + __version__,
|
||||||
|
epilog='Notes: This script is still experimental. '
|
||||||
|
'GID source default will change to "metadata" '
|
||||||
|
'in the future.')
|
||||||
|
|
||||||
|
parser.add_option('-q', '--quiet',
|
||||||
|
dest='verbosity',
|
||||||
|
action='store_const',
|
||||||
|
const=0,
|
||||||
|
default=1,
|
||||||
|
help='Run quietly.')
|
||||||
|
parser.add_option('-v', '--verbose',
|
||||||
|
dest='verbosity',
|
||||||
|
action='store_const',
|
||||||
|
const=2,
|
||||||
|
default=1,
|
||||||
|
help='Do full reporting.')
|
||||||
|
parser.add_option('--debug',
|
||||||
|
dest='verbosity',
|
||||||
|
action='store_const',
|
||||||
|
const=3,
|
||||||
|
default=1,
|
||||||
|
help='Print debug data.')
|
||||||
|
|
||||||
|
parser.add_option('--dry-run',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Run but do not write anything to disk.')
|
||||||
|
|
||||||
|
|
||||||
|
output_configuration = OptionGroup(parser, 'Output configuration')
|
||||||
|
output_configuration.add_option('--path-mode',
|
||||||
|
default='absolute' if CONFIG['absolute-path'] else 'relative',
|
||||||
|
help='Path generation mode (default: "%default").')
|
||||||
|
output_configuration.add_option('--gid-source',
|
||||||
|
default=CONFIG['gid-source'],
|
||||||
|
help='Source used for GID generation (default: "%default").')
|
||||||
|
parser.add_option_group(output_configuration)
|
||||||
|
|
||||||
|
|
||||||
|
configuration = OptionGroup(parser, 'Configuration options')
|
||||||
|
configuration.add_option('--config-file',
|
||||||
|
metavar='PATH',
|
||||||
|
default=CONFIG['config'],
|
||||||
|
help='Config file to search for (default: "%default").')
|
||||||
|
configuration.add_option('--config-print',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Print current configuration and exit.')
|
||||||
|
configuration.add_option('--config-defaults-print',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Print default configuration and exit.')
|
||||||
|
configuration.add_option('--config-save-local',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Save current configuration at the root location. '
|
||||||
|
'this is a shorthand for: '
|
||||||
|
'%prog ... --config-print > ROOT/CONFIG; %prog')
|
||||||
|
parser.add_option_group(configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
##!!! test if we are missing something...
|
||||||
|
## if (len(args) != 1
|
||||||
|
## and True not in (options.config_defaults_print, options.config_print)):
|
||||||
|
## parser.print_usage()
|
||||||
|
## raise SystemExit
|
||||||
|
|
||||||
|
# prepare the path...
|
||||||
|
if len(args) < 1:
|
||||||
|
IN_PATH = '.'
|
||||||
|
else:
|
||||||
|
IN_PATH = args[0]
|
||||||
|
IN_PATH = IN_PATH.replace('\\', '/')
|
||||||
|
|
||||||
|
config = {}
|
||||||
|
config.update(CONFIG)
|
||||||
|
|
||||||
|
# load configuration files..
|
||||||
|
config_name = options.config_file
|
||||||
|
# local to script...
|
||||||
|
if os.path.exists(config_name):
|
||||||
|
with open(config_name) as f:
|
||||||
|
config.update(json.load(f))
|
||||||
|
# local to target...
|
||||||
|
if os.path.exists(os.path.join(IN_PATH, config_name)):
|
||||||
|
with open(os.path.join(IN_PATH, config_name)) as f:
|
||||||
|
config.update(json.load(f))
|
||||||
|
|
||||||
|
# update config according to set args...
|
||||||
|
config.update({
|
||||||
|
'gid-source': options.gid_source,
|
||||||
|
'absolute-path': options.path_mode == 'absolute',
|
||||||
|
})
|
||||||
|
# a value from 0 through 2...
|
||||||
|
verbosity = options.verbosity
|
||||||
|
# bool...
|
||||||
|
dry_run = options.dry_run
|
||||||
|
|
||||||
|
# configuration stuff...
|
||||||
|
# write a local configuration...
|
||||||
|
if options.config_save_local:
|
||||||
|
with file(os.path.join(IN_PATH, config_name), 'w') as f:
|
||||||
|
f.write(json.dumps(config, sort_keys=True, indent=4))
|
||||||
|
|
||||||
|
# print configuration data...
|
||||||
|
if True in (options.config_defaults_print, options.config_print):
|
||||||
|
|
||||||
|
# see if we need to print a prefix...
|
||||||
|
print_prefix = False
|
||||||
|
if len([ s for s in (options.config_defaults_print, options.config_print) if s]) > 1:
|
||||||
|
print_prefix = True
|
||||||
|
|
||||||
|
# do the prinitng...
|
||||||
|
if options.config_print:
|
||||||
|
if print_prefix:
|
||||||
|
print 'Current Configuration:'
|
||||||
|
print json.dumps(config, sort_keys=True, indent=4)
|
||||||
|
print
|
||||||
|
if options.config_defaults_print:
|
||||||
|
if print_prefix:
|
||||||
|
print 'Default Configuration:'
|
||||||
|
print json.dumps(CONFIG, sort_keys=True, indent=4)
|
||||||
|
print
|
||||||
|
|
||||||
|
# do the actual work...
|
||||||
|
# NOTE: we are not using verbosity 2 at this point...
|
||||||
|
else:
|
||||||
|
progress_state = {}
|
||||||
|
|
||||||
|
if verbosity == 0:
|
||||||
|
report = None
|
||||||
|
elif verbosity >= 1:
|
||||||
|
report = make_inline_report_progress(progress_state)
|
||||||
|
|
||||||
|
# do the run...
|
||||||
|
build_cache(IN_PATH,
|
||||||
|
config,
|
||||||
|
hash_gid,
|
||||||
|
report,
|
||||||
|
dry_run,
|
||||||
|
verbosity)
|
||||||
|
|
||||||
|
# report results...
|
||||||
|
if verbosity >= 1:
|
||||||
|
print
|
||||||
|
print 'Time: %.1fm' % ((progress_state['done at'] - progress_state['started at'])/60)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#=======================================================================
|
||||||
|
# vim:set ts=4 sw=4 nowrap :
|
||||||
|
|||||||
357
buildcache2.py
357
buildcache2.py
@ -1,357 +0,0 @@
|
|||||||
#=======================================================================
|
|
||||||
|
|
||||||
__version__ = '''0.0.01'''
|
|
||||||
__sub_version__ = '''20130522020247'''
|
|
||||||
__copyright__ = '''(c) Alex A. Naanou 2011'''
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
import os
|
|
||||||
import Image
|
|
||||||
import json
|
|
||||||
import sha
|
|
||||||
import urllib2
|
|
||||||
import time
|
|
||||||
|
|
||||||
from pli.logictypes import OR
|
|
||||||
|
|
||||||
import gid
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
CONFIG = {
|
|
||||||
'absolute-path': False,
|
|
||||||
|
|
||||||
'cache-image-name': '%(guid)s - %(name)s',
|
|
||||||
|
|
||||||
'images': '.ImageGridCache/images.json',
|
|
||||||
'data': '.ImageGridCache/data.json',
|
|
||||||
'marked': '.ImageGridCache/marked.json',
|
|
||||||
|
|
||||||
'error': '.ImageGridCache/error.log',
|
|
||||||
|
|
||||||
'cache-structure': {
|
|
||||||
# make these as close to standard as possible and keep sane
|
|
||||||
# distances...
|
|
||||||
'150px': '.ImageGridCache/150px/',
|
|
||||||
'350px': '.ImageGridCache/350px/',
|
|
||||||
'900px': '.ImageGridCache/900px/',
|
|
||||||
'1080px': '.ImageGridCache/1080px/',
|
|
||||||
'1920px': '.ImageGridCache/1920px/',
|
|
||||||
},
|
|
||||||
'sizes': {
|
|
||||||
'150px': 150,
|
|
||||||
'350px': 350,
|
|
||||||
'900px': 900,
|
|
||||||
'1080px': 1080,
|
|
||||||
'1920px': 1920,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DATA = {
|
|
||||||
'version': '2.0',
|
|
||||||
'current': None,
|
|
||||||
'ribbons': (),
|
|
||||||
'order': (),
|
|
||||||
'image_file': None,
|
|
||||||
}
|
|
||||||
|
|
||||||
IMAGE_EXT = OR(*(
|
|
||||||
'.jpg', '.jpeg', '.JPG', '.JPEG',
|
|
||||||
))
|
|
||||||
|
|
||||||
ERR_LOG = '''\
|
|
||||||
ERROR: %(error)s
|
|
||||||
SOURCE: %(source-file)s
|
|
||||||
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(pathjoin(path), safe='/:')
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------log_err---
|
|
||||||
def log_err(path, e, source_file, target_file):
|
|
||||||
'''
|
|
||||||
'''
|
|
||||||
err_file = pathjoin(path, CONFIG['error'])
|
|
||||||
if not os.path.exists(err_file):
|
|
||||||
err = open(err_file, 'w')
|
|
||||||
else:
|
|
||||||
err = open(err_file, 'a')
|
|
||||||
with err:
|
|
||||||
err.write(ERR_LOG % {
|
|
||||||
'source-file': source_file,
|
|
||||||
'target-file': target_file,
|
|
||||||
'error': e,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------hash_gid---
|
|
||||||
def hash_gid(img, force=False):
|
|
||||||
'''
|
|
||||||
Generate gid based on preview file content.
|
|
||||||
|
|
||||||
NOTE: img can be either a path or an Image.
|
|
||||||
'''
|
|
||||||
if type(img) in (str, unicode):
|
|
||||||
img = Image.open(img)
|
|
||||||
return sha.sha(img.tostring()).hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------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 = {}
|
|
||||||
if 'started at' not in state:
|
|
||||||
state['started at'] = time.time()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
state['done at'] = time.time()
|
|
||||||
|
|
||||||
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.
|
|
||||||
'''
|
|
||||||
dirs = config['cache-structure']
|
|
||||||
for _, k in dirs.items():
|
|
||||||
p = pathjoin(path, k)
|
|
||||||
if not os.path.exists(p):
|
|
||||||
os.makedirs(p)
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------------------------------------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):
|
|
||||||
fname, ext = os.path.splitext(name)
|
|
||||||
|
|
||||||
if ext != IMAGE_EXT:
|
|
||||||
continue
|
|
||||||
|
|
||||||
source_path = pathjoin(path, name)
|
|
||||||
|
|
||||||
img = {
|
|
||||||
'id': gid_generator(source_path),
|
|
||||||
'name': name,
|
|
||||||
'type': 'image',
|
|
||||||
'state': 'single',
|
|
||||||
'path': getpath(path, name, absolute_path),
|
|
||||||
'ctime': os.path.getctime(source_path),
|
|
||||||
'preview': {},
|
|
||||||
}
|
|
||||||
|
|
||||||
yield img
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#=======================================================================
|
|
||||||
# vim:set ts=4 sw=4 nowrap :
|
|
||||||
Loading…
x
Reference in New Issue
Block a user