| 1 |
#!/usr/bin/env python |
| 2 |
# -*- encoding:utf8 -*- |
| 3 |
|
| 4 |
top = "." |
| 5 |
feed_title = "Gauret : photos récentes" |
| 6 |
start_url = "http://aurelien.bompard.org/photos" |
| 7 |
limit = 100 |
| 8 |
feed_author = "Aurélien Bompard" |
| 9 |
|
| 10 |
|
| 11 |
import sys, os, datetime, cgi, ConfigParser |
| 12 |
|
| 13 |
if len(sys.argv) > 1: |
| 14 |
top = sys.argv[1] |
| 15 |
|
| 16 |
use_exif = True |
| 17 |
try: |
| 18 |
import PIL.Image, PIL.ExifTags |
| 19 |
except ImportError: |
| 20 |
use_exif = False |
| 21 |
print "Warning: PIL is not installed, no EXIF info will be used" |
| 22 |
|
| 23 |
|
| 24 |
files = [] |
| 25 |
for dirpath, dirnames, filenames in os.walk(top): |
| 26 |
# exclude directories starting with "_" |
| 27 |
for i, d in enumerate(dirnames): |
| 28 |
if d.startswith("_"): |
| 29 |
del dirnames[i] |
| 30 |
for name in filenames: |
| 31 |
# only include jpg images and videos |
| 32 |
if name.lower().endswith(".jpg") \ |
| 33 |
or name.lower().endswith(".flv") \ |
| 34 |
or name.lower().endswith(".avi"): |
| 35 |
files.append(os.path.join(dirpath, name)) |
| 36 |
|
| 37 |
metadata = {"mtimes": {}, "sizes": {}} |
| 38 |
for f in files: |
| 39 |
stat = os.stat(f) |
| 40 |
metadata["mtimes"][f] = stat.st_mtime |
| 41 |
metadata["sizes"][f] = stat.st_size |
| 42 |
|
| 43 |
def get_mtime(f): |
| 44 |
return metadata["mtimes"][f] |
| 45 |
|
| 46 |
files.sort( key=get_mtime, reverse=True ) |
| 47 |
files = files[:limit] |
| 48 |
|
| 49 |
output = """<?xml version="1.0" encoding="utf-8" standalone="yes"?> |
| 50 |
<feed xmlns="http://www.w3.org/2005/Atom" |
| 51 |
xmlns:dc="http://purl.org/dc/elements/1.1/" > |
| 52 |
|
| 53 |
<title>%(title)s</title> |
| 54 |
<link rel="self" href="%(url)s/_atom.xml" /> |
| 55 |
<link rel="alternate" type="text/html" href="%(url)s"/> |
| 56 |
<id>%(url)s</id> |
| 57 |
<updated>%(time)sZ</updated> |
| 58 |
<generator uri="http://aurelien.bompard.org/site/Mon-afficheur-de-photos-par-le-web">Photos-Index</generator> |
| 59 |
<author> |
| 60 |
<name>%(author)s</name> |
| 61 |
<uri>%(url)s</uri> |
| 62 |
</author> |
| 63 |
""" % { "title": feed_title, |
| 64 |
"url": start_url, |
| 65 |
"time": datetime.datetime.now().isoformat(), |
| 66 |
"author": cgi.escape(feed_author, quote=True), |
| 67 |
} |
| 68 |
|
| 69 |
for f in files: |
| 70 |
relpath = f.replace(top,"") |
| 71 |
url = start_url+relpath |
| 72 |
url_sb = start_url+os.path.dirname(relpath)+"/?name="+os.path.basename(f) |
| 73 |
title = os.path.basename(f) |
| 74 |
author = None |
| 75 |
dir_name = os.path.basename(os.path.dirname(f)) |
| 76 |
thumb = os.path.join( os.path.dirname(url), "_thumbs", os.path.basename(f)[:-4]+".jpg" ) |
| 77 |
# Read _infos.ini file |
| 78 |
infos_file = os.path.join(os.path.dirname(f), "_infos.ini") |
| 79 |
if os.path.exists(infos_file): |
| 80 |
infos = ConfigParser.ConfigParser() |
| 81 |
infos.read(infos_file) |
| 82 |
if infos.has_option(os.path.basename(f), "title"): |
| 83 |
title = infos.get(os.path.basename(f), "title").strip('"') |
| 84 |
if infos.has_option(os.path.basename(f), "author"): |
| 85 |
author = infos.get(os.path.basename(f), "author").strip('"') |
| 86 |
if infos.has_option("general", "title"): |
| 87 |
dir_name = infos.get("general", "title").strip('"') |
| 88 |
# Author |
| 89 |
if author is None and dir_name.startswith("Photos de "): |
| 90 |
author = dir_name[10:] |
| 91 |
if author is None and dir_name.startswith("Photos d'"): |
| 92 |
author = dir_name[9:] |
| 93 |
if author is None: |
| 94 |
author = feed_author |
| 95 |
# Is it protected ? Only embed the thumbnail if it's not |
| 96 |
protected = False |
| 97 |
subdirs = os.path.dirname(relpath[1:]).split("/") |
| 98 |
for i in range(len(subdirs)): |
| 99 |
subdir = os.path.join(top,"/".join(subdirs[:i+1])) |
| 100 |
if os.path.exists(os.path.join(subdir, ".htaccess")): |
| 101 |
protected = True |
| 102 |
# Summary |
| 103 |
if protected: |
| 104 |
content = '<a href="%s">%s</a>' % (url_sb, title) |
| 105 |
else: # it's not protected, embed the thumbnail |
| 106 |
content = '<a href="%s"><img style="border:none" src="%s" /></a>' % (url_sb, thumb) |
| 107 |
content += " (dans %s)" % os.path.dirname(relpath) |
| 108 |
# MIME type |
| 109 |
if f.endswith(".jpg"): |
| 110 |
ftype = "image/jpeg" |
| 111 |
elif f.endswith(".flv"): |
| 112 |
ftype = "video/x-flv" |
| 113 |
elif f.endswith(".avi"): |
| 114 |
ftype = "video/x-msvideo" |
| 115 |
# Make XML |
| 116 |
output += """ |
| 117 |
<entry> |
| 118 |
<title>%(title)s</title> |
| 119 |
<link rel="alternate" type="text/html" href="%(url_sb)s"/> |
| 120 |
<id>%(url)s</id> |
| 121 |
<updated>%(mtime)sZ</updated> |
| 122 |
<content type="html">%(content)s</content> |
| 123 |
<link rel="enclosure" type="%(type)s" length="%(size)s" href="%(url)s"/> |
| 124 |
""" % {"title": cgi.escape(title, quote=True), |
| 125 |
"url_sb": url_sb, |
| 126 |
"url": url, |
| 127 |
"mtime": datetime.datetime.fromtimestamp(metadata["mtimes"][f]).isoformat(), |
| 128 |
"content": cgi.escape(content, quote=True), |
| 129 |
"size": metadata["sizes"][f], |
| 130 |
"type": ftype, |
| 131 |
} |
| 132 |
output += """ <author> |
| 133 |
<name>"""+cgi.escape(author, quote=True)+"""</name> |
| 134 |
</author> |
| 135 |
""" |
| 136 |
if use_exif: |
| 137 |
try: |
| 138 |
exif = PIL.Image.open(f)._getexif() |
| 139 |
except (IOError, KeyError): |
| 140 |
exif = None |
| 141 |
if exif is not None: |
| 142 |
try: |
| 143 |
exif_dt = exif[0x0132] # DateTime |
| 144 |
exif_dt = datetime.datetime(int(exif_dt[0:4]),int(exif_dt[5:7]),int(exif_dt[8:10]), |
| 145 |
int(exif_dt[11:13]),int(exif_dt[14:16]),int(exif_dt[17:19])) |
| 146 |
output += """ <dc:date.Taken>"""+exif_dt.isoformat()+"""Z</dc:date.Taken>""" |
| 147 |
except KeyError, e: |
| 148 |
print "Warning: No EXIF DateTime info on", title |
| 149 |
except ValueError, e: |
| 150 |
print "Warning: EXIF data is wrong:", e, "on", exif_dt, "("+title+")" |
| 151 |
else: |
| 152 |
print "Warning: Can't extract EXIF data from", f |
| 153 |
output += """ |
| 154 |
</entry> |
| 155 |
""" |
| 156 |
output += "</feed>\n" |
| 157 |
|
| 158 |
out = open(os.path.join(top,"_atom.xml"), "w") |
| 159 |
out.write(output) |
| 160 |
out.close() |