Source

ezflickr / ezflickr.py

Full commit
#!/usr/bin/env python
"""Easy Flickr"""
from __future__ import division, print_function


#---------- Configuration ----------#

default_dir_name = u'ezflickr' # default output directory name
default_dim = (1080, 720)     # px
default_size = 300            # kB
high_q = 95                   # highest quality
low_q = 80                    # lowest quality


#---------- resize ----------#

def _orientation(size):
    """
    Determine the orientation of 'size'
    
    size -- (width, height)
    return -- 'landscape' | 'portrait'
    """
    return 'portrait' if size[0] < size[1] else 'landscape'

def resize(img, size=default_dim):
    """
    Resize a photo to size, smartly.

    * Aspect ratio is always maintained
    * Image is never enlarged

    img -- PIL.Image instance
    size -- (width, height)
    no_enlarge -- True/False
    """
    if not img or img.size[0] <= 0 or img.size[1] <= 0:
        return img
    i_orient = _orientation(img.size) # orientation of the image
    s_orient = _orientation(size)     # orientation of the 'size'
    if i_orient != s_orient:
        size = (size[1], size[0])
    p = 1.0
    if size[0] != 0 and img.size[0] > size[0]:
        p = size[0] / img.size[0]
    elif size[1] != 0 and img.size[1] > size[1]:
        p = size[1] / img.size[1]
    size = (int(img.size[0] * p), int(img.size[1] * p))
    return img.resize(size)


#---------- main and commands ----------#

def _walk_args(args, func=print, ignore_dirs=None, ignore_files=None):
    import os
    for f in args:
        if os.path.isfile(f):
            if os.path.basename(f) not in (ignore_files or []):
                func(f)
        elif os.path.isdir(f):
            for root, dirs, files in os.walk(f):
                for ignore_dir in ignore_dirs or []:
                    if ignore_dir in dirs:
                        dirs.remove(ignore_dir)
                for ignore_file in ignore_files or []:
                    if ignore_file in files:
                        files.remove(ignore_file)
                for file in files:
                    func(os.path.join(root, file))
        else:
            print("{0} *_* It's a joke, right???".format(f))

def cmd_resize():
    """
    Parse commandline args and resize photos
    """
    import os, sys
    from optparse import OptionParser
    from PIL import Image

    usage = '%prog resize [OPTION]... FILE...'
    op = OptionParser(usage=usage)
    op.add_option('-s', '--size', dest='size', default=None,
                  help='resize photos to SIZE')
    op.add_option('-d', '--directory', dest='dir', default=None,
                  help='directory to save resized photos')
    op.add_option('-l', '--limit', type='int', dest='limit', default=0,
                  help='limit file size to LIMIT KB')
    (options, args) = op.parse_args()
    if len(args) < 1:
        op.print_help(file=sys.stderr)
        sys.exit(-1)
    dir = ''
    if options.dir is not None:
        dir = options.dir
    else:
        if os.path.isdir(args[0]):
            dir = os.path.abspath(args[0])
        else:
            dir = os.path.dirname(os.path.abspath(args[0]))
    dir = os.path.join(dir, default_dir_name)
    if not os.path.exists(dir):
        try:
            os.mkdir(dir)
        except:
            print('Could not create output directory: {0}'.format(dir),
                  file=sys.stderr)
            sys.exit(-1)

    # process photos
    def process(fname):
        try:
            print('Processing {0}... '.format(fname), end='', file=sys.stderr)
            img = Image.open(fname)
            img = resize(img)
            ofile = os.path.join(dir, os.path.basename(fname))
            name, ext = os.path.splitext(ofile)
            count = 1
            while os.path.exists(ofile):
                ofile = '{0}-{1}{2}'.format(name, count, ext)
                count += 1
            quality = high_q
            img.save(ofile, quality=quality)
            if options.limit > 0:
                while os.path.getsize(ofile) / 1024 > options.limit:
                    quality -= 5
                    if quality < low_q:
                        break
                    img.save(ofile, quality=quality)
            print('Done! Saved: {0}'.format(ofile), file=sys.stderr)
        except IOError as why:
            print('Failed! Not an image file, probably?', file=sys.stderr)
            #print(why)
    
    _walk_args(args, process, ignore_dirs=[default_dir_name])

def cmd_publish():
    """
    Parse commandline args and upload photos to flickr
    """
    pass
    
_cmds = {
    'resize': cmd_resize,
    'publish': cmd_publish
}
_cmd_descs = {
    'resize': 'resize photos',
    'publish': 'upload photos to flickr'
}

def _usage():
    """
    Print usage screen
    """
    tmpl = """Easy Flickr -- make flickr even easier

commands:

 resize     {resize}
 publish    {publish}
"""
    import sys
    print(tmpl.format(**_cmd_descs), file=sys.stderr)
    
def main():
    import sys
    try:
        cmd = sys.argv.pop(1)
        _cmds[cmd]()
    except (IndexError, KeyError):
        _usage()
        sys.exit(-1)
    
if __name__ == '__main__':
    main()