merge from master

This commit is contained in:
Adrien Beudin 2016-02-09 21:12:34 +01:00
commit cfd2c4b751
12 changed files with 174 additions and 91 deletions

View File

@ -62,7 +62,7 @@ sub_title: it's a scary place, don't go there
This settings.yaml will describe:
* the title, subtitle and cover picture of your gallery that will be used on the homepage
* if your gallery is public
* if your gallery is public (if not, it will still be built but won't appear on the homepage)
* the date of your gallery: this will be used on the homepage since **galleries are sorted anti chronologically** on it
* the list of sections that will contains your gallery. A section will represent either one picture, a group of pictures or text. The different kind of sections will be explained in the next README section.

View File

@ -3,18 +3,20 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<link type="text/css" rel="stylesheet" href="../static/css/fonts.css" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="../static/css/style-page.css" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="../static/css/baguetteBox.min.css" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="../static/css/panorama_viewer.css" media="screen,projection"/>
<!--Let browser know website is optimized for mobile-->
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>my first gallery | Example gallery</title>
<title>my first gallery · Example gallery</title>
</head>
<body>
<section class="full-picture" style="background: transparent url('stuff.png') no-repeat scroll center top / cover;">
<div class="picture-text">
@ -28,6 +30,7 @@
</section>
<section class="bordered-picture baguette">
<a href="stuff.png">
<img src="stuff.png">
@ -43,15 +46,6 @@
<div class="pictures-line">
<div class="picture">
<a href="stuff.png">
<img src="stuff-small.png">
</a>
</div>
<div class="separator"></div>
<div class="picture">
<a href="stuff.png">
@ -63,6 +57,18 @@
<div class="picture">
<a href="stuff.png">
<img src="stuff-small.png">
</a>
</div>
<div class="separator"></div>
<div class="picture">
<a href="stuff.png">
<img src="stuff-small.png">
@ -75,6 +81,7 @@
<div class="pictures-line">
<div class="picture">
<a href="stuff.png">
<img src="stuff-small.png">
@ -85,6 +92,7 @@
<div class="picture">
<a href="stuff.png">
<img src="stuff-small.png">
@ -97,6 +105,7 @@
</section>
<section class="full-picture" style="background: transparent url('stuff.png') no-repeat scroll center top / cover;">
</section>
@ -115,17 +124,19 @@
<script type="text/javascript" src="../static/js/baguetteBox.min.js" charset="utf-8"></script>
<script type="text/javascript" src="../static/js/jquery.panorama_viewer.min.js" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
baguetteBox.run(".baguette", {});
$(".panorama").panorama_viewer({
repeat: true,
direction: "horizontal",
animationTime: 250,
easing: "ease-out",
overlay: true
});
$(function() {
baguetteBox.run(".baguette", {});
$(".panorama").panorama_viewer({
repeat: true,
direction: "horizontal",
animationTime: 150,
easing: "linear",
overlay: true
});
});
</script>
<footer>
<p>Generate using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>
</body>
</html>

View File

@ -2,6 +2,7 @@
<html>
<head>
<meta charset="UTF-8">
<link type="text/css" rel="stylesheet" href="static/css/fonts.css" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="static/css/style.css" media="screen,projection"/>
<!--Let browser know website is optimized for mobile-->
@ -30,6 +31,8 @@
<div class="gallery-datetime">08 December 2015</div>
</div>
</a>
<div class="gallery-cover" style="background-image: url('first_gallery/stuff-small.png');"></div>
</div><!-- comment tricks against space between inline-block
-->
@ -40,7 +43,7 @@
<p style="visibility: hidden">.</p>
<footer>
<p>Generate using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>
</body>
</html>

View File

@ -12,7 +12,7 @@ body {
}
section {
margin-bottom: 64px;
margin-bottom: 80px;
}
a {
@ -51,7 +51,7 @@ a {
text-transform: uppercase;
font-size: 5.5vw;
letter-spacing: 4px;
font-family: sans-serif;
font-family: 'montserrat', sans-serif;
margin-left: 10%;
margin-right: 10%;
margin-bottom: 1px;
@ -61,30 +61,30 @@ a {
font-weight: normal;
font-style: italic;
font-size: 2.2vw;
font-family: serif;
font-family: 'crimson', serif;
margin-top: 1px;
}
.full-picture .datetime {
text-transform: uppercase;
font-family: serif;
font-family: 'crimson', serif;
letter-spacing: 2px;
}
.bordered-picture img {
height: 80%;
width: 80%;
margin-left: 10%;
margin-right: 10%;
height: 77%;
width: 77%;
margin-left: 11.5%;
margin-right: 11.5%;
}
.pictures-line {
min-width: 80%;
width: 80%;
margin-left: 10%;
margin-right: 10%;
min-width: 77%;
width: 77%;
margin-left: 11.5%;
margin-right: 11.5%;
display: flex;
margin-bottom: 15px;
margin-bottom: 0.5em;
}
.pictures-line .picture img {
@ -93,22 +93,47 @@ a {
}
.pictures-line .separator {
min-width: 15px;
min-width: 0.5em;
}
.text {
text-align: center;
font-size: 25px;
margin-left: 15%;
margin-right: 15%;
font-family: 'crimson', serif;
font-size: 1.6em;
line-height: 1.8em;
margin-left: 21%;
margin-right: 21%;
color: black;
}
.paragraph {
text-align: left;
font-family: 'crimson', serif;
font-size: 1em;
margin-left: 21%;
margin-right: 21%;
color: #333;
}
.paragraph h2 {
font-family: 'montserrat', sans-serif;
font-weight: normal;
font-size: 2.5em;
text-transform: uppercase;
color: black;
line-height: 1.4em;
}
.paragraph p {
line-height: 2em;
}
footer {
margin-top: 7em;
margin-top: 6em;
text-align: center;
position: relative;
font-family: serif;
font-family: 'crimson', serif;
font-size: 11px;
color: #555;
background-color: #EEE;
@ -154,7 +179,8 @@ footer {
justify-content: center;
text-align: center;
text-transform: uppercase;
font-family: sans-serif;
font-family: 'montserrat', sans-serif;
font-weight: bold;
}
footer p {
@ -164,6 +190,6 @@ footer p {
footer a {
text-decoration: none;
font-weight: 600;
font-family: sans-serif;
font-family: 'montserrat', sans-serif;
color: #111;
}

View File

@ -1,6 +1,6 @@
body {
color: #222;
font-family: sans-serif;
font-family: 'montserrat', sans-serif;
background-color: #FBFBFB;
margin: 0;
}
@ -17,10 +17,11 @@ body {
.galleries-line {
width: 100%;
height: 100%;
margin-bottom: -4px; /* YOLO */
}
.covers-1 .gallery-square {
width: 47%;
width: 100%;
height: 100%;
margin: auto;
padding-bottom: 47%;
@ -28,12 +29,12 @@ body {
}
.covers-2 .gallery-square {
width: 47%;
width: 50%;
height: 100%;
float: left;
margin: 0 1.5% 3%;
margin: 0 0 0;
padding-bottom: 47%;
position: relative;
display: inline-block;
}
.covers-3 .gallery-square {
@ -110,7 +111,7 @@ body {
color: #444;
font-style: italic;
font-weight: normal;
font-family: serif;
font-family: 'crimson', serif;
margin-top: .5em;
}
@ -131,13 +132,13 @@ body {
font-style: italic;
margin-top: 0;
margin-bottom: .7em;
font-family: serif;
font-family: 'crimson', serif;
font-weight: normal;
}
.gallery-datetime {
margin-bottom: 1em;
font-family: serif;
font-family: 'crimson', serif;
text-transform: uppercase;
letter-spacing: 2px;
font-size: 11px;
@ -147,7 +148,7 @@ footer {
margin-top: 7em;
text-align: center;
position: relative;
font-family: serif;
font-family: 'crimson', serif;
font-size: 11px;
color: #555;
background-color: #EEE;
@ -163,6 +164,6 @@ footer p {
footer a {
text-decoration: none;
font-weight: 600;
font-family: sans-serif;
font-family: 'montserrat', sans-serif;
color: #111;
}

View File

@ -13,71 +13,100 @@ index_template = templates.get_template("index.html")
gallery_index_template = templates.get_template("gallery-index.html")
page_template = templates.get_template("page.html")
DEFAULT_GM_QUALITY = 75
CACHE_VERSION = 1
class Cache(object):
cache_file_path = os.path.join(os.getcwd(), ".prosopopee_cache")
def __init__(self):
def __init__(self, json):
# fix: I need to keep a reference to json because for whatever reason
# modules are set to None during python shutdown thus totally breaking
# the __del__ call to save the cache
# This wonderfully stupid behavior has been fixed in 3.4 (which nobody uses)
self.json = json
if os.path.exists(os.path.join(os.getcwd(), ".prosopopee_cache")):
self.cache = json.load(open(self.cache_file_path, "r"))
else:
self.cache = {}
self.cache = {"version": CACHE_VERSION}
def thumbnail_needs_to_be_generated(self, source, target):
if "version" not in self.cache or self.cache["version"] != CACHE_VERSION:
print "info: cache format as changed, prune cache"
self.cache = {"version": CACHE_VERSION}
def thumbnail_needs_to_be_generated(self, source, target, image):
if not os.path.exists(target):
return True
if target not in self.cache:
return True
if self.cache[target] != os.path.getsize(source):
cached_thumbnail = self.cache[target]
if cached_thumbnail["size"] != os.path.getsize(source) or cached_thumbnail["options"] != image.options:
return True
return False
def cache_thumbnail(self, source, target):
self.cache[target] = os.path.getsize(source)
def cache_thumbnail(self, source, target, image):
self.cache[target] = {"size": os.path.getsize(source), "options": image.options}
def __del__(self):
json.dump(self.cache, open(self.cache_file_path, "w"))
self.json.dump(self.cache, open(self.cache_file_path, "w"))
CACHE = Cache()
CACHE = Cache(json=json)
class TemplateFunctions():
def __init__(self, base_dir, target_dir, has_gm):
self.base_dir = base_dir
self.target_dir = target_dir
def copy_image(self, image):
source, target = os.path.join(self.base_dir, image), os.path.join(self.target_dir, image)
class Image(object):
base_dir = ""
target_dir = ""
def __init__(self, options):
# assuming string
if not isinstance(options, dict):
name = options
options = {"name": options}
self.name = name
self.quality = options.get("quality", DEFAULT_GM_QUALITY)
self.options = options.copy() # used for caching, if it's modified -> regenerate
del self.options["name"]
def copy(self):
source, target = os.path.join(self.base_dir, self.name), os.path.join(self.target_dir, self.name)
# XXX doing this DOESN'T improve perf at all (or something like 0.1%)
# if os.path.exists(target) and os.path.getsize(source) == os.path.getsize(target):
# print "Skiped %s since the file hasn't been modified based on file size" % source
# return ""
shutil.copyfile(source, target)
print source, "->", target
return ""
def generate_thumbnail(self, image, gm_geometry):
thumbnail_name = image.split(".")
def generate_thumbnail(self, gm_geometry):
thumbnail_name = self.name.split(".")
thumbnail_name[-2] += "-small"
thumbnail_name = ".".join(thumbnail_name)
source, target = os.path.join(self.base_dir, image), os.path.join(self.target_dir, thumbnail_name)
source, target = os.path.join(self.base_dir, self.name), os.path.join(self.target_dir, thumbnail_name)
if CACHE.thumbnail_needs_to_be_generated(source, target):
command = "gm convert %s -resize %s %s" % (source, gm_geometry, target)
if CACHE.thumbnail_needs_to_be_generated(source, target, self):
command = "gm convert %s -resize %s -quality %s %s" % (source, gm_geometry, self.quality, target)
print command
os.system(command)
CACHE.cache_thumbnail(source, target)
CACHE.cache_thumbnail(source, target, self)
else:
print "skiped %s since it's already generated (based on source unchanged size)" % target
print "skiped %s since it's already generated (based on source unchanged size and images options set in your gallery's settings.yaml)" % target
return thumbnail_name
def __repr__(self):
return self.name
def error(test, error_message):
if test:
@ -90,10 +119,9 @@ def error(test, error_message):
def main():
has_gm = True
if os.system("which gm > /dev/null") != 0:
has_gm = False
sys.stderr.write("WARNING: I can't locate the 'gm' binary, I won't be able to resize images.\n")
sys.stderr.write("ERROR: I can't locate the 'gm' binary, I won't be able to resize images, please install the 'graphicsmagick' package.\n")
sys.exit(1)
error(os.path.exists(os.path.join(os.getcwd(), "settings.yaml")), "I can't find a settings.yaml in the current working directory")
@ -143,7 +171,11 @@ def main():
if not os.path.exists(os.path.join("build", gallery)):
os.makedirs(os.path.join("build", gallery))
open(os.path.join("build", gallery, "index.html"), "w").write(gallery_index_template.render(settings=settings, gallery=gallery_settings, helpers=TemplateFunctions(os.path.join(os.getcwd(), gallery), os.path.join(os.getcwd(), "build", gallery), has_gm=has_gm)).encode("Utf-8"))
# this should probably be a factory
Image.base_dir = os.path.join(os.getcwd(), gallery)
Image.target_dir = os.path.join(os.getcwd(), "build", gallery)
open(os.path.join("build", gallery, "index.html"), "w").write(gallery_index_template.render(settings=settings, gallery=gallery_settings, Image=Image).encode("Utf-8"))
front_page_galleries_cover = reversed(sorted(front_page_galleries_cover, key=lambda x: x["date"]))
@ -154,7 +186,11 @@ def main():
error(os.path.exists(os.path.join(os.getcwd(), item_file+".yaml")), "I can't find a "+item_file+".yaml in the current working directory")
open(os.path.join("build", item_file+".html"), "w").write(page_template.render(settings=settings, pages=yaml.safe_load(open(item_file+".yaml", "r")), galleries=front_page_galleries_cover, helpers=TemplateFunctions(os.getcwd(), os.path.join(os.getcwd(), "build"), has_gm=has_gm)).encode("Utf-8"))
open(os.path.join("build", "index.html"), "w").write(index_template.render(settings=settings, galleries=front_page_galleries_cover, helpers=TemplateFunctions(os.getcwd(), os.path.join(os.getcwd(), "build"), has_gm=has_gm)).encode("Utf-8"))
Image.base_dir = os.getcwd()
Image.target_dir = os.path.join(os.getcwd(), "build")
open(os.path.join("build", "index.html"), "w").write(index_template.render(settings=settings, galleries=front_page_galleries_cover, Image=Image).encode("Utf-8"))
if __name__ == '__main__':

View File

@ -43,7 +43,7 @@
});
</script>
<footer>
<p>Generate using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
<p>Generated using <a href="https://github.com/psycojoker/prosopopee">Prosopopée</a> · content under <a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a> · atom logo by <a href="https://thenounproject.com/jjjon/">Jonathan Li</a> under <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY</a></p>
</footer>
</body>
</html>

View File

@ -35,7 +35,9 @@
{% if gallery.date %}<div class="gallery-datetime">{{ gallery.date.strftime("%d %B %Y") }}</div>{% endif %}
</div>
</a>
<div class="gallery-cover" style="background-image: url('{{ helpers.generate_thumbnail(gallery.cover, "x900") }}');"></div>
{% set cover = Image(gallery.cover) %}
{{ cover.copy() }}
<div class="gallery-cover" style="background-image: url('{{ cover.generate_thumbnail("x900") }}');"></div>
</div><!-- comment tricks against space between inline-block
-->{% endfor %}
</div>

View File

@ -1,6 +1,7 @@
{{ helpers.copy_image(section.image) }}
{% set image = Image(section.image) %}
{{ image.copy()}}
<section class="bordered-picture baguette">
<a href="{{ section.image }}">
<img src="{{ section.image }}">
<a href="{{ image }}">
<img src="{{ image }}">
</a>
</section>

View File

@ -1,5 +1,6 @@
{{ helpers.copy_image(section.image) }}
<section class="full-picture" style="background: transparent url('{{ section.image }}') no-repeat scroll center top / cover;">
{% set image = Image(section.image) %}
{{ image.copy() }}
<section class="full-picture" style="background: transparent url('{{ image }}') no-repeat scroll center top / cover;">
{% if section.text %}
<div class="picture-text">
<div class="picture-text-column">

View File

@ -1,4 +1,5 @@
{{ helpers.copy_image(section.image) }}
{% set image = Image(section.image) %}
{{ image.copy() }}
<section class="panorama">
<img src="{{ helpers.generate_thumbnail(section.image, "x800") }}">
<img src="{{ image.generate_thumbnail("x800") }}">
</section>

View File

@ -2,10 +2,11 @@
{% for line in section.images %}
<div class="pictures-line">
{% for image in line %}
{{ helpers.copy_image(image) }}
{% set image = Image(image) %}
{{ image.copy() }}
<div class="picture">
<a href="{{ image }}">
<img src="{{ helpers.generate_thumbnail(image, "x600") }}">
<img src="{{ image.generate_thumbnail("x600") }}">
</a>
</div>
{% if not loop.last %}