diff --git a/Qalbum-descriptor.py b/Qalbum-descriptor.py index e4d15fe..a541577 100755 --- a/Qalbum-descriptor.py +++ b/Qalbum-descriptor.py @@ -16,37 +16,15 @@ # along with this program. If not, see . import sys,os -import re -import shutil import subprocess -from optparse import OptionParser -from math import ceil +from argparse import ArgumentParser +import Qalbum # (c) ville.rantanen@helsinki.fi -imagesearch=re.compile('.*\.jpg$|.*\.jpeg$|.*\.gif$|.*\.png$|.*\.svg$|.*\.pdf$',re.I) -excludepaths=re.compile('_med|_tn|\..*') DESCFILE='descriptions.csv' -def getimagelist(path): - ''' Returns a list of images matching the regex ''' - list=os.listdir(path) - imgs=[] - for f in list: - if (imagesearch.match(os.path.join(path,f))) and (os.path.isfile(os.path.join(path,f))): - imgs.append(f) - return imgs - -def getpathlist(path): - ''' Returns a list of subfolders not matching the exclusion regex ''' - list=os.listdir(path) - paths=[] - for d in list: - if (not excludepaths.match(d)) and (os.path.isdir(os.path.join(path,d))): - paths.append(d) - return paths - -def createdesc(path,list): +def createdesc(path,list,options): ''' Runs imagemagick identify to create descriptions of images ''' if len(list)==0: return @@ -74,42 +52,43 @@ def create_description(infile,format): output = idp.stdout.read() return output -def traverse(path): +def traverse(path,options): ''' The recursive main function to create the thumbs and seek sub folders ''' print(path) - pathlist=getpathlist(path) - imagelist=getimagelist(path) + pathlist=Qalbum.getpathlist(path,options) + imagelist=Qalbum.getimagelist(path,options) print(str(len(pathlist))+' paths') print(str(len(imagelist))+' images') - createdesc(path,imagelist) + createdesc(path,imagelist,options) if options.recursive: for p in pathlist: - traverse(os.path.join(path,p)) + traverse(os.path.join(path,p),options) return def execute(): ''' Main execution ''' - usage='''Usage: %prog [options] folder -folder is the root folder of the image album.''' - parser=OptionParser(usage=usage) - parser.add_option("-f",action="store_true",dest="force",default=False, + parser=ArgumentParser(version=Qalbum.__version__) + parser.add_argument("-f",action="store_true",dest="force",default=False, help="Force rewriting of descriptions") - parser.add_option("--format",type="str",dest="format",default="", + parser.add_argument("--format",type=str,dest="format",default="", help="""Formatting string, see: http://www.imagemagick.org/script/escape.php. Setting this option will override the presets""") - parser.add_option("-r",action="store_true",dest="recursive",default=False, + parser.add_argument("-r",action="store_true",dest="recursive",default=False, help="Recurse in to subfolders") - parser.add_option("-p",type="int",dest="preset",default=1, + parser.add_argument("-p",type=int,dest="preset",default=1, help="presets for descriptions. \"-p 0\" to get the list") - global options - (options,args)=parser.parse_args() - if len(args) != 1 and options.preset!=0: - parser.error("incorrect number of arguments") + parser.add_argument("startpath",type=str,action="store",default=os.path.abspath('.'),nargs='?', + help="Start path for recursion") + options=parser.parse_args() + options.startpath=os.path.abspath(options.startpath) + options=Qalbum.setupdefaultoptions(options) + presets=[ '%f: %[EXIF:DateTimeOriginal]', '%f: %wx%h %[size]', '%f
%[EXIF:DateTimeOriginal] %[EXIF:ExposureTime]s F%[EXIF:FNumber]'] if options.preset<1: + print "Presets:" for row in range(len(presets)): print row+1," ",presets[row] sys.exit(0) @@ -118,7 +97,7 @@ folder is the root folder of the image album.''' parse.error("No such preset") options.format=presets[options.preset-1] - traverse(os.path.abspath(args[0])) + traverse(options.startpath,options) return execute() diff --git a/Qalbum-thumbnailer.py b/Qalbum-thumbnailer.py index 7e47321..12bb2ad 100755 --- a/Qalbum-thumbnailer.py +++ b/Qalbum-thumbnailer.py @@ -15,161 +15,57 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import sys,os -import re -import shutil -import subprocess -from optparse import OptionParser -from math import ceil +import Qalbum +from argparse import ArgumentParser +import os # (c) ville.rantanen@helsinki.fi -webfilesearch=re.compile('.*index.html$|.*gallerystyle.css$|.*galleryscript.js$|.*descriptions.csv$',re.I) -imagesearch=re.compile('.*\.jpg$|.*\.jpeg$|.*\.gif$|.*\.png$|.*\.svg$|.*\.pdf$',re.I) -vectorsearch=re.compile('.*\.svg$|.*\.pdf$',re.I) -excludepaths=re.compile('_med|_tn|\..*') -doublequotes=re.compile('"') -singlequotes=re.compile("'") -stripquotes=re.compile('^"|"$') -def getimagelist(path): - ''' Returns a list of images matching the regex ''' - list=os.listdir(path) - imgs=[] - for f in list: - if (imagesearch.match(os.path.join(path,f))) and (os.path.isfile(os.path.join(path,f))): - imgs.append(f) - return imgs - -def getnonimagelist(path): - ''' Returns a list of files not matching the image match regex ''' - list=os.listdir(path) - files=[] - for f in list: - if (not webfilesearch.match(os.path.join(path,f))) and (not imagesearch.match(os.path.join(path,f))) and (os.path.isfile(os.path.join(path,f))): - files.append(f) - files.sort() - return files - -def getpathlist(path): - ''' Returns a list of subfolders not matching the exclusion regex ''' - list=os.listdir(path) - paths=[] - for d in list: - if (not excludepaths.match(d)) and (os.path.isdir(os.path.join(path,d))): - paths.append(d) - return paths - -def createthumbs(path,list): - ''' Runs imagemagick Convert to create medium sized and thumbnail images ''' - if len(list)==0: - return - if not os.path.exists(os.path.join(path,'_tn')): - os.mkdir(os.path.join(path,'_tn')) - if not os.path.exists(os.path.join(path,'_med')): - os.mkdir(os.path.join(path,'_med')) - n=1 - nsum=len(list) - r=str(options.width) - res=r+'x'+r+'>' - for i in list: - outmedium=os.path.join(path,'_med','med_'+i+'.jpg') - outthumb=os.path.join(path,'_tn','tn_'+i+'.jpg') - inpath=os.path.join(path,i) - if (options.force) and os.path.exists(outmedium): - os.unlink(outmedium) - if (options.force) and os.path.exists(outthumb): - os.unlink(outthumb) - if (not os.path.exists(outmedium)): - print('Medium.. '+i+' '+str(n)+'/'+str(nsum)) - create_medium_bitmap(inpath,outmedium,r,link=options.link,vector=vectorsearch.match(i)) - if (not os.path.exists(outthumb)): - print('Thumbnail.. '+i+' '+str(n)+'/'+str(nsum)) - create_thumb_bitmap(outmedium,outthumb,vector=vectorsearch.match(i)) - n+=1 - return - -def create_medium_bitmap(infile,outfile,r,link=False,vector=False): - if link: - os.symlink(infile,outfile) - return - res=r+'x'+r+'>' - if vector: - convargs=['convert','-density','300x300',infile+'[0]','-background','white','-flatten','-resize',res,'-quality','97',outfile] - else: - convargs=['convert','-define','jpeg:size='+r+'x'+r,infile+'[0]','-background','white','-flatten','-resize',res,'-quality','85',outfile] - convp=subprocess.call(convargs) - return - -def create_thumb_bitmap(infile,outfile,vector=False): - if vector: - convargs=['convert','-density','300x300',infile,'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',outfile] - else: - convargs=['convert','-define','jpeg:size=300x300',infile,'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',outfile] - convp=subprocess.call(convargs) - return - -def cleanthumbs(path): - ''' clears _med and _tn for unused thumbs ''' - print('clearing unused thumbs...') - if os.path.exists(os.path.join(path,'_tn')): - clearfolder(path,os.path.join(path,'_tn'),re.compile("(^tn_)(.*)(.jpg)")) - if os.path.exists(os.path.join(path,'_med')): - clearfolder(path,os.path.join(path,'_med'),re.compile("(^med_)(.*)(.jpg)")) - - return -def clearfolder(path,tnpath,regex): - ''' clears given folder ''' - list=getimagelist(tnpath) - for i in list: - f=regex.match(i) - try: - if not os.path.exists(os.path.join(path,f.group(2))): - print('removing '+i) - os.remove(os.path.join(tnpath,i)) - except: - continue - return - -def traverse(path): +def traverse(path,options): ''' The recursive main function to create the thumbs and seek sub folders ''' print(path) - pathlist=getpathlist(path) - imagelist=getimagelist(path) + pathlist=Qalbum.getpathlist(path,options) + imagelist=Qalbum.getimagelist(path,options) print(str(len(pathlist))+' paths') print(str(len(imagelist))+' images') if options.clean: - cleanthumbs(path) - createthumbs(path,imagelist) + Qalbum.cleanthumbs(path) + Qalbum.createthumbs(path,imagelist,options) if options.recursive: for p in pathlist: - traverse(os.path.join(path,p)) + traverse(os.path.join(path,p),options) return +def setupoptions(): + ''' Setup options ''' + usage='''Usage: %(prog)s [options] folder +folder is the root folder of the image album (defaults to current folder).''' + parser=ArgumentParser(description=usage,version=Qalbum.__version__) + parser.add_argument("-f",action="store_true",dest="force",default=False, + help="Force regeneration of thumbnails and medium sized") + parser.add_argument("-c",action="store_true",dest="clean",default=False, + help="Clean unused thumbnails") + parser.add_argument("-l",action="store_true",dest="link",default=False, + help="Create symbolic link as medium size image instead of downscaling one.") + parser.add_argument("-r",action="store_true",dest="recursive",default=False, + help="Recurse in to subfolders") + parser.add_argument("-w",type=int,dest="width",default=850, + help="Medium image size (Default: %(default)s)") + parser.add_argument("startpath",type=str,action="store",default=os.path.abspath('.'),nargs='?', + help="Start path for recursion") + options=parser.parse_args() + options.startpath=os.path.abspath(options.startpath) + options=Qalbum.setupdefaultoptions(options) + return options + def execute(): ''' Main execution ''' - usage='''Usage: %prog [options] folder -folder is the root folder of the image album.''' - parser=OptionParser(usage=usage) - parser.add_option("-f",action="store_true",dest="force",default=False, - help="Force regeneration of thumbnails and medium sized") - parser.add_option("-c",action="store_true",dest="clean",default=False, - help="Clean unused thumbnails") - parser.add_option("-l",action="store_true",dest="link",default=False, - help="Create symbolic link as medium size image instead of downscaling one.") - parser.add_option("-r",action="store_true",dest="recursive",default=False, - help="Recurse in to subfolders") - parser.add_option("-w",type="int",dest="width",default=850, - help="Medium image size") - global options - (options,args)=parser.parse_args() - if len(args) != 1: - parser.error("incorrect number of arguments") - startpath=os.path.abspath(args[0]) - traverse(startpath) + options=setupoptions() + traverse(options.startpath,options) return -execute() -sys.exit(0) - +if __name__ == "__main__": + execute() + diff --git a/Qalbum.py b/Qalbum.py index 8aa56db..e7b5819 100755 --- a/Qalbum.py +++ b/Qalbum.py @@ -26,7 +26,7 @@ from datetime import datetime # (c) ville.rantanen@helsinki.fi -__version__='1.6' +__version__='1.7b' webfilesearch=re.compile('.*index.html$|.*gallerystyle.css$|.*galleryscript.js$|.*descriptions.csv$|\..*',re.I) imagesearch=re.compile('.*\.jpg$|.*\.jpeg$|.*\.gif$|.*\.png$|.*\.svg$|.*\.pdf$',re.I) @@ -58,17 +58,20 @@ def getfooter(): ''' -def getimagelist(path): +def getimagelist(path,options=False): ''' Returns a list of images matching the regex ''' list=os.listdir(path) imgs=[] for f in list: if (imagesearch.match(f)) and (os.path.isfile(os.path.join(path,f))): imgs.append(f) - if options.timesort: - imgs.sort(key=lambda f: os.path.getmtime(os.path.join(path, f)),reverse=options.reverse) + if options: + if options.timesort: + imgs.sort(key=lambda f: os.path.getmtime(os.path.join(path, f)),reverse=options.reverse) + else: + imgs.sort(reverse=options.reverse) else: - imgs.sort(reverse=options.reverse) + imgs.sort() return imgs def getfiletimes(path,list): @@ -78,7 +81,7 @@ def getfiletimes(path,list): times.append(int(os.path.getmtime(os.path.join(path,p)))) return times -def getnonimagelist(path): +def getnonimagelist(path,options): ''' Returns a list of files not matching the image match regex ''' list=os.listdir(path) files=[] @@ -93,17 +96,20 @@ def getnonimagelist(path): files.sort(reverse=options.reverse) return files -def getpathlist(path): +def getpathlist(path,options=False): ''' Returns a list of subfolders not matching the exclusion regex ''' list=os.listdir(path) paths=[] for d in list: if (not excludepaths.match(d)) and (os.path.isdir(os.path.join(path,d))): paths.append(d) - if options.timesort: - paths.sort(key=lambda f: os.path.getmtime(f),reverse=options.reverse) + if options: + if options.timesort: + paths.sort(key=lambda f: os.path.getmtime(f),reverse=options.reverse) + else: + paths.sort(reverse=options.reverse) else: - paths.sort(reverse=options.reverse) + paths.sort() return paths def pathscript(path,list): @@ -190,7 +196,29 @@ def filelinks(path,list): strout+='' return strout -def createthumbs(path,list): +def cleanthumbs(path): + ''' clears _med and _tn for unused thumbs ''' + print('clearing unused thumbs...') + if os.path.exists(os.path.join(path,'_tn')): + clearfolder(path,os.path.join(path,'_tn'),re.compile("(^tn_)(.*)(.jpg)")) + if os.path.exists(os.path.join(path,'_med')): + clearfolder(path,os.path.join(path,'_med'),re.compile("(^med_)(.*)(.jpg)")) + return + +def clearfolder(path,tnpath,regex): + ''' clears given folder ''' + list=getimagelist(tnpath) + for i in list: + f=regex.match(i) + try: + if not os.path.exists(os.path.join(path,f.group(2))): + print('removing '+i) + os.remove(os.path.join(tnpath,i)) + except: + continue + return + +def createthumbs(path,list,options): ''' Runs imagemagick Convert to create medium sized and thumbnail images ''' if len(list)==0: return @@ -200,21 +228,29 @@ def createthumbs(path,list): os.mkdir(os.path.join(path,'_med')) n=1 nsum=len(list) - r='1000' + r=str(options.width) + res=r+'x'+r+'>' for i in list: outmedium=os.path.join(path,'_med','med_'+i+'.jpg') outthumb=os.path.join(path,'_tn','tn_'+i+'.jpg') inpath=os.path.join(path,i) + if (options.force) and os.path.exists(outmedium): + os.unlink(outmedium) + if (options.force) and os.path.exists(outthumb): + os.unlink(outthumb) if (not os.path.exists(outmedium)): print('Medium.. '+i+' '+str(n)+'/'+str(nsum)) - create_medium_bitmap(inpath,outmedium,r,vector=vectorsearch.match(i)) + create_medium_bitmap(inpath,outmedium,r,link=options.link,vector=vectorsearch.match(i)) if (not os.path.exists(outthumb)): print('Thumbnail.. '+i+' '+str(n)+'/'+str(nsum)) create_thumb_bitmap(outmedium,outthumb,vector=vectorsearch.match(i)) n+=1 return - -def create_medium_bitmap(infile,outfile,r,vector=False): + +def create_medium_bitmap(infile,outfile,r,link=False,vector=False): + if link: + os.symlink(infile,outfile) + return res=r+'x'+r+'>' if vector: convargs=['convert','-density','300x300',infile+'[0]','-background','white','-flatten','-resize',res,'-quality','97',outfile] @@ -284,7 +320,7 @@ def sizestring(size): return "%3.1f%s" % (size, x) size /= 1024.0 -def traverse(path,crumbs): +def traverse(path,crumbs,inputs,options): ''' The recursive main function to create the index.html and seek sub folders ''' print(path) if len(crumbs)==1: @@ -292,9 +328,9 @@ def traverse(path,crumbs): else: header=getheader(path,'../'*(len(crumbs)-1)) print(len(crumbs)) - pathlist=getpathlist(path) - imagelist=getimagelist(path) - filelist=getnonimagelist(path) + pathlist=getpathlist(path,options) + imagelist=getimagelist(path,options) + filelist=getnonimagelist(path,options) print(str(len(pathlist))+' paths') print(str(len(imagelist))+' images') print(str(len(filelist))+' other files') @@ -329,13 +365,56 @@ def traverse(path,crumbs): f.write('
') f.write(getfooter()) f.close() - createthumbs(path,imagelist) + createthumbs(path,imagelist,options) for p in pathlist: nextcrumbs=[i for i in crumbs] nextcrumbs.append(os.path.join(path,p)) - traverse(os.path.join(path,p),nextcrumbs) + traverse(os.path.join(path,p),nextcrumbs,inputs,options) return +def setupoptions(): + ''' Setup the command line options ''' + from argparse import ArgumentParser + parser=ArgumentParser(version=__version__) + parser.add_argument("-r",action="store_true",dest="reverse",default=False, + help="Reverse sort orded") + parser.add_argument("-t",action="store_true",dest="timesort",default=False, + help="Sort by file modification time") + parser.add_argument("-a",action="store_false",dest="attachments",default=True, + help="Disable attachments") + parser.add_argument("-g",type=str,dest="gallery",default="Gallery", + help="Name for the root gallery (Default: %(default)s)") + parser.add_argument("-w",type=int,dest="width",default=850, + help="Medium image size (Default: %(default)s)") + parser.add_argument("startpath",type=str,action="store",default=os.path.abspath('.'),nargs='?', + help="Root path of the gallery") + options=parser.parse_args() + options.startpath=os.path.abspath(options.startpath) + options=setupdefaultoptions(options) + return options + +def setupdefaultoptions(options): + ''' Adds the missing options for the options object ''' + if 'attachments' not in options: + options.attachments=True + if 'clean' not in options: + options.clean=False + if 'force' not in options: + options.force=False + if 'gallery' not in options: + options.gallery="Gallery" + if 'link' not in options: + options.link=False + if 'recursive' not in options: + options.recursive=True + if 'reverse' not in options: + options.reverse=False + if 'timesort' not in options: + options.timesort=False + if 'width' not in options: + options.width=850 + return options + class AndurilOptions: '''Object featuring same variables as arguments from command line.''' @@ -346,8 +425,6 @@ class AndurilOptions: self.attachments=True def execute(cf): - global inputs - global options inputs=[] inputs.append((cf.get_input('folderRoot'),'',cf.get_input('csvRoot'))) for i in range(8): @@ -356,6 +433,7 @@ def execute(cf): annotationCol=cf.get_parameter('annotationCol') options=AndurilOptions(cf.get_parameter('sortTime','boolean'), cf.get_parameter('sortReverse','boolean')) + options=setupdefaultoptions(options) outDir = cf.get_output('gallery') # the folderRoot demands that the outDir is not created earlier. if inputs[0][0]==None: @@ -411,46 +489,32 @@ def execute(cf): shutil.copyfile('gallerystyle.css',os.path.join(outDir,'gallerystyle.css')) shutil.copyfile('galleryscript.js',os.path.join(outDir,'galleryscript.js')) inputs[0]=((cf.get_input('folderRoot'),cf.get_parameter('titleRoot'),cf.get_input('csvRoot'))) - startpath=os.path.abspath(outDir) - traverse(startpath,[startpath]) + options.startpath=os.path.abspath(outDir) + traverse(options.startpath,[options.startpath],inputs,options) return 0 def execute_plain(): ''' Execute this if run outside anduril ''' - from optparse import OptionParser - usage='''Usage: %prog [options] folder -folder is the root folder of the image album.''' - parser=OptionParser(usage=usage,version=__version__) - parser.add_option("-r",action="store_true",dest="reverse",default=False, - help="Reverse sort orded") - parser.add_option("-t",action="store_true",dest="timesort",default=False, - help="Sort by file modification time") - parser.add_option("-a",action="store_false",dest="attachments",default=True, - help="Disable attachments") - global options - (options,args)=parser.parse_args() - if len(args) != 1: - #sys.exit(0) - startpath=os.path.abspath('.') - else: - startpath=os.path.abspath(args[0]) + options=setupoptions() + + # Copy all resources to target folder pathname=os.path.dirname(os.path.realpath(sys.argv[0])) fullpath=os.path.abspath(pathname) - # Copy all resources to target folder - shutil.copyfile(os.path.join(fullpath,'gallerystyle.css'),os.path.join(startpath,'gallerystyle.css')) - shutil.copyfile(os.path.join(fullpath,'galleryscript.js'),os.path.join(startpath,'galleryscript.js')) - global inputs - inputs=[] - inputs.append((None,'Gallery',None)) + shutil.copyfile(os.path.join(fullpath,'gallerystyle.css'),os.path.join(options.startpath,'gallerystyle.css')) + shutil.copyfile(os.path.join(fullpath,'galleryscript.js'),os.path.join(options.startpath,'galleryscript.js')) - traverse(startpath,[startpath]) + inputs=[] + inputs.append((None,options.gallery,None)) + + traverse(options.startpath,[options.startpath],inputs,options) return -try: - import component_skeleton.main -except ImportError: - execute_plain() - sys.exit(0) - -component_skeleton.main.main(execute) +if __name__ == "__main__": + try: + import component_skeleton.main + except ImportError: + execute_plain() + sys.exit(0) + + component_skeleton.main.main(execute)