diff --git a/Qalbum-thumbnailer.py b/Qalbum-thumbnailer.py
new file mode 100755
index 0000000..fe1e2f4
--- /dev/null
+++ b/Qalbum-thumbnailer.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Ville Rantanen
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# 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
+
+# (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:
+ if (vectorsearch.match(i)):
+ if (options.force) or (not os.path.exists(os.path.join(path,'_med','med_'+i+'.jpg'))):
+ print('Medium.. '+i+' '+str(n)+'/'+str(nsum))
+ convargs=['convert','-density','300x300',os.path.join(path,i)+'[0]','-background','white','-flatten','-resize',res,'-quality','97',os.path.join(path,'_med','med_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ if (options.force) or (not os.path.exists(os.path.join(path,'_tn','tn_'+i+'.jpg'))):
+ convargs=['convert','-density','300x300',os.path.join(path,'_med','med_'+i+'.jpg'),'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',os.path.join(path,'_tn','tn_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ else:
+ if (options.force) or (not os.path.exists(os.path.join(path,'_med','med_'+i+'.jpg'))):
+ print('Medium.. '+i+' '+str(n)+'/'+str(nsum))
+ convargs=['convert','-define','jpeg:size='+r+'x'+r,os.path.join(path,i)+'[0]','-background','white','-flatten','-resize',res,'-quality','85',os.path.join(path,'_med','med_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ if (options.force) or (not os.path.exists(os.path.join(path,'_tn','tn_'+i+'.jpg'))):
+ convargs=['convert','-define','jpeg:size=300x300',os.path.join(path,'_med','med_'+i+'.jpg'),'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',os.path.join(path,'_tn','tn_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ n+=1
+ 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):
+ ''' The recursive main function to create the thumbs and seek sub folders '''
+ print(path)
+ pathlist=getpathlist(path)
+ imagelist=getimagelist(path)
+ print(str(len(pathlist))+' paths')
+ print(str(len(imagelist))+' images')
+ if options.clean:
+ cleanthumbs(path)
+ createthumbs(path,imagelist)
+ if options.recursive:
+ for p in pathlist:
+ traverse(os.path.join(path,p))
+ 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,
+ help="Force regeneration of thumbnails")
+ parser.add_option("-c",action="store_true",dest="clean",default=False,
+ help="Clean unused thumbnails")
+ 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)
+ return
+
+execute()
+sys.exit(0)
+
+
diff --git a/Qalbum.py b/Qalbum.py
new file mode 100755
index 0000000..f005845
--- /dev/null
+++ b/Qalbum.py
@@ -0,0 +1,448 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Ville Rantanen
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# 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 urllib
+import shutil
+import csv
+import subprocess
+from math import ceil
+from datetime import datetime
+
+# (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)
+#gifsearch=re.compile('.*gif$',re.I)
+excludepaths=re.compile('_med|_tn|\..*')
+doublequotes=re.compile('"')
+singlequotes=re.compile("'")
+stripquotes=re.compile('^"|"$')
+
+def getheader(path,parent,title=""):
+ if title=="":
+ title=unicode(os.path.basename(path),encoding="utf8").encode('ascii', 'xmlcharrefreplace')
+ return '''
+
+
+'''+title+'''
+
+
+
+
+
+'''
+
+def getimagelist(path):
+ ''' 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)
+ else:
+ imgs.sort(reverse=options.reverse)
+ return imgs
+
+def getfiletimes(path,list):
+ ''' Returns a list of modification times '''
+ times=[]
+ for p in list:
+ times.append(os.path.getmtime(os.path.join(path,p)))
+ return times
+
+def getnonimagelist(path):
+ ''' Returns a list of files not matching the image match regex '''
+ list=os.listdir(path)
+ files=[]
+ if not options.attachments:
+ return files
+ for f in list:
+ if (not webfilesearch.match(f)) and (not imagesearch.match(f)) and (os.path.isfile(os.path.join(path,f))):
+ files.append(f)
+ if options.timesort:
+ files.sort(key=lambda f: os.path.getmtime(os.path.join(path, f)),reverse=options.reverse)
+ else:
+ files.sort(reverse=options.reverse)
+ 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)
+ if options.timesort:
+ paths.sort(key=lambda f: os.path.getmtime(f),reverse=options.reverse)
+ else:
+ paths.sort(reverse=options.reverse)
+ return paths
+
+def pathscript(path,list):
+ ''' Returns the javascript string of pathlist and pathimage arrays '''
+ scrstr=''
+ return scrstr
+
+def pathlinks(path,list):
+ ''' Returns the HTML string of subfolders '''
+ pathstr='
'
+ return strout
+
+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)
+ for i in list:
+ if (vectorsearch.match(i)):
+ if not os.path.exists(os.path.join(path,'_med','med_'+i+'.jpg')):
+ print('.. '+i+' '+str(n)+'/'+str(nsum))
+ convargs=['convert','-density','300x300',os.path.join(path,i)+'[0]','-background','white','-flatten','-resize','1000x1000>','-quality','97',os.path.join(path,'_med','med_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ if not os.path.exists(os.path.join(path,'_tn','tn_'+i+'.jpg')):
+ convargs=['convert','-density','300x300',os.path.join(path,'_med','med_'+i+'.jpg'),'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',os.path.join(path,'_tn','tn_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ else:
+ if not os.path.exists(os.path.join(path,'_med','med_'+i+'.jpg')):
+ print('.. '+i+' '+str(n)+'/'+str(nsum))
+ convargs=['convert','-define','jpeg:size=1200x1200',os.path.join(path,i)+'[0]','-background','white','-flatten','-resize','1000x1000>','-quality','85',os.path.join(path,'_med','med_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ if not os.path.exists(os.path.join(path,'_tn','tn_'+i+'.jpg')):
+ convargs=['convert','-define','jpeg:size=300x300',os.path.join(path,'_med','med_'+i+'.jpg'),'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',os.path.join(path,'_tn','tn_'+i+'.jpg')]
+ convp=subprocess.call(convargs)
+ n+=1
+ return
+
+def getdescriptions(path,list):
+ ''' Read descriptions.csv file and returns a list of descriptions.
+ Missing descriptions are replaced with the file name. '''
+ if not os.path.exists(os.path.join(path,'descriptions.csv')):
+ return list
+ desc=[i for i in list]
+ reader = csv.reader(open(os.path.join(path,'descriptions.csv'),'rb'),
+ delimiter='\t',
+ doublequote=False,
+ escapechar='\\',
+ quoting=csv.QUOTE_NONE)
+ for row in reader:
+ if row[0] in list:
+ i=list.index(stripquotes.sub('',row[0]))
+ desc[i]=stripquotes.sub('',row[1])
+ return desc
+
+def crumblinks(crumbs):
+ ''' Create the HTML string for crumb trails '''
+ strout='