diff --git a/prosopopee/prosopopee.py b/prosopopee/prosopopee.py
index 275cf94..3d32115 100644
--- a/prosopopee/prosopopee.py
+++ b/prosopopee/prosopopee.py
@@ -23,9 +23,76 @@ SETTINGS = {
"auto-orient": True,
"strip": True,
"resize": None
+ },
+ "ffmpeg": {
+ "loglevel": "panic",
+ "format": "webm",
+ "resolution": "1280x720",
+ "bitrate": "3900k",
+ "preselect": "libvpx-720p"
}
}
+class Video(object):
+ base_dir = ""
+ target_dir = ""
+
+ def __init__(self, options):
+ # assuming string
+ if not isinstance(options, dict):
+ options = {"video": options}
+ self.options = SETTINGS["ffmpeg"].copy() # used for caching, if it's modified -> regenerate
+ self.options.update(options)
+
+ @property
+ def name(self):
+ return self.options["name"]
+
+ def ffmpeg(self, source, target, options):
+ if CACHE.needs_to_be_generated(source, target, options):
+ ffmpeg_switches = {
+ "source": source,
+ "target": target,
+ "loglevel": "-loglevel %s" % options["loglevel"],
+ "resolution": "-s %s" % options["resolution"],
+ "preselect": "-vpre %s" % options["preselect"],
+ "resize": "-vf scale=-1:%s" % options.get("resize"),
+ "bitrate": "-b %s" % options["bitrate"],
+ "format": "-f %s" % options["format"]
+ }
+ warning("Generation", source)
+ if options.get("resize"):
+ command = "ffmpeg {loglevel} -i {source} {resize} -vframes 1 -y {target}".format(**ffmpeg_switches)
+ os.system(command)
+ else:
+ command = "ffmpeg {loglevel} -i {source} {resolution} {preselect} {bitrate} -pass 1 -an {format} -y {target}".format(**ffmpeg_switches)
+ command2 = "ffmpeg {loglevel} -i {source} {resolution} {preselect} {bitrate} -pass 2 -acodec libvorbis -ab 100k {format} -y {target}".format(**ffmpeg_switches)
+ os.system(command)
+ os.system(command2)
+ CACHE.cache_picture(source, target, options)
+ else:
+ okgreen("Skipped", source + " is already generated")
+
+ def copy(self):
+ source, target = os.path.join(self.base_dir, self.name), os.path.join(self.target_dir, self.name)
+ options = self.options.copy()
+ self.ffmpeg(source, target, options)
+ return ""
+
+ def generate_thumbnail(self, gm_geometry):
+ thumbnail_name = self.name.split(".")
+ thumbnail_name[-2] += "-%s" % gm_geometry
+ thumbnail_name = thumbnail_name[-2] + ".png"
+ source, target = os.path.join(self.base_dir, self.name), os.path.join(self.target_dir, thumbnail_name)
+ options = self.options.copy()
+ options.update({"resize": gm_geometry})
+ self.ffmpeg(source, target, options)
+ return thumbnail_name
+
+ def __repr__(self):
+ return self.name
+
+
class Image(object):
base_dir = ""
target_dir = ""
@@ -44,14 +111,13 @@ class Image(object):
def gm(self, source, target, options):
if CACHE.needs_to_be_generated(source, target, options):
gm_switches = {
- "source": source,
- "target": target,
- "auto-orient" : "-auto-orient" if options["auto-orient"] else "",
- "strip": "-strip" if options["strip"] else "",
- "quality": "-quality %s" % options["quality"] if "quality" in options else "-define jpeg:preserve-settings",
- "resize": "-resize %s" % options["resize"] if options.get("resize", None) is not None else ""
-
- }
+ "source": source,
+ "target": target,
+ "auto-orient" : "-auto-orient" if options["auto-orient"] else "",
+ "strip": "-strip" if options["strip"] else "",
+ "quality": "-quality %s" % options["quality"] if "quality" in options else "-define jpeg:preserve-settings",
+ "resize": "-resize %s" % options["resize"] if options.get("resize", None) is not None else ""
+ }
command = "gm convert {source} {auto-orient} {strip} {quality} {resize} {target}".format(**gm_switches)
warning("Generation", source)
os.system(command)
@@ -72,7 +138,7 @@ class Image(object):
shutil.copyfile(source, target)
print source, "->", target
else:
- # Do not consider quality settings here, since we aim to copy the input image
+ # Do not consider quality settings here, since we aim to copy the input image
# better to preserve input encoding setting
del options["quality"]
self.gm(source, target, options)
@@ -95,11 +161,11 @@ class Image(object):
def main():
if os.system("which gm > /dev/null") != 0:
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")
+ "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")
+ "settings.yaml in the current working directory")
settings = yaml.safe_load(open("settings.yaml", "r"))
@@ -112,7 +178,7 @@ def main():
if (settings["rss"] or settings["share"]) and not settings.get("url"):
warning("warning", "If you want the rss and/or the social network share to work, "
- "you need to specify the website url in root settings")
+ "you need to specify the website url in root settings")
settings["rss"] = False
settings["share"] = False
@@ -124,8 +190,8 @@ def main():
dirs = filter(lambda x: x not in (".", "..") and os.path.isdir(x) and os.path.exists(os.path.join(os.getcwd(), x, "settings.yaml")), os.listdir(os.getcwd()))
error(dirs, "I can't find at least one directory with a settings.yaml in the current working "
- "directory (NOT the settings.yaml in your current directory, but one INSIDE A "
- "DIRECTORY in your current working directory), you don't have any gallery?")
+ "directory (NOT the settings.yaml in your current directory, but one INSIDE A "
+ "DIRECTORY in your current working directory), you don't have any gallery?")
if not os.path.exists("build"):
os.makedirs("build")
@@ -143,7 +209,7 @@ def main():
templates = Environment(loader=FileSystemLoader([
prosopopee_templates_dir,
project_templates_dir
- ]))
+ ]))
index_template = templates.get_template("index.html")
gallery_index_template = templates.get_template("gallery-index.html")
@@ -167,12 +233,12 @@ def main():
if gallery_settings.get("public", True):
error(gallery_settings.get("title"), "Your gallery describe in %s need to have a "
- "title" % (os.path.join(gallery, "settings.yaml")))
+ "title" % (os.path.join(gallery, "settings.yaml")))
error(gallery_settings.get("cover"), "You should specify a path to a cover picture "
- "in %s" % (os.path.join(gallery, "settings.yaml")))
+ "in %s" % (os.path.join(gallery, "settings.yaml")))
cover_image_path = os.path.join(gallery, gallery_settings["cover"])
error(os.path.exists(cover_image_path), "File for %s cover image doesn't exist at "
- "%s" % (gallery, cover_image_path))
+ "%s" % (gallery, cover_image_path))
front_page_galleries_cover.append({
"title": gallery_settings["title"],
@@ -181,17 +247,19 @@ def main():
"date": gallery_settings.get("date", ""),
"tags": gallery_settings.get("tags", ""),
"cover": cover_image_path,
- })
+ })
- if not os.path.exists(os.path.join("build", gallery)):
- os.makedirs(os.path.join("build", gallery))
+ if not os.path.exists(os.path.join("build", gallery)):
+ os.makedirs(os.path.join("build", gallery))
# 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)
+ Video.base_dir = os.path.join(os.getcwd(), gallery)
+ Video.target_dir = os.path.join(os.getcwd(), "build", gallery)
template_to_render = page_template if gallery_settings.get("static") else gallery_index_template
- open(os.path.join("build", gallery, "index.html"), "w").write(template_to_render.render(settings=settings, gallery=gallery_settings, Image=Image, link=gallery).encode("Utf-8"))
+ open(os.path.join("build", gallery, "index.html"), "w").write(template_to_render.render(settings=settings, gallery=gallery_settings, Image=Image, Video=Video, link=gallery).encode("Utf-8"))
if settings["rss"]:
open(os.path.join("build", "feed.xml"), "w").write(feed_template.render(settings=settings, link=gallery, galleries=reversed(sorted(front_page_galleries_cover, key=lambda x: x["date"]))).encode("Utf-8"))
diff --git a/prosopopee/themes/exposure/static/css/style-page.css b/prosopopee/themes/exposure/static/css/style-page.css
index cedb963..7dd1cef 100644
--- a/prosopopee/themes/exposure/static/css/style-page.css
+++ b/prosopopee/themes/exposure/static/css/style-page.css
@@ -9,11 +9,12 @@ body {
width: 100%;
background-color: #FBFBFB;
color: black;
- margin: 0;
+ margin: 0px;
}
section {
margin-bottom: 80px;
+ margin-top: 40px;
}
a {
@@ -27,6 +28,7 @@ a {
height: 100%;
width: 100%;
min-height: 250px;
+ margin-top: 0;
}
.full-picture > .picture-text {
@@ -90,7 +92,6 @@ a {
.pictures-line .picture img {
width: 100%;
- height: 100%;
}
.pictures-line .separator {
@@ -336,3 +337,21 @@ a.google {
line-height: normal;
color: #333;
}
+
+.picture video {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ width: 100%;
+ height: auto;
+}
+
+.bg-section {
+ padding: 1px 0px;
+}
+
+.bg-section section {
+ margin-bottom: 40px;
+ margin-top: 40px;
+}
diff --git a/prosopopee/themes/exposure/static/js/jquery.lazyload.min.js b/prosopopee/themes/exposure/static/js/jquery.lazyload.min.js
new file mode 100644
index 0000000..0403807
--- /dev/null
+++ b/prosopopee/themes/exposure/static/js/jquery.lazyload.min.js
@@ -0,0 +1,2 @@
+/*! Lazy Load 1.9.7 - MIT license - Copyright 2010-2015 Mika Tuupola */
+!function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!1,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.is("img")&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("").bind("load",function(){var d=c.attr("data-"+j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.attr("data-"+j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document);
\ No newline at end of file
diff --git a/prosopopee/themes/exposure/templates/gallery-index.html b/prosopopee/themes/exposure/templates/gallery-index.html
index 36d573f..2f0ea0d 100644
--- a/prosopopee/themes/exposure/templates/gallery-index.html
+++ b/prosopopee/themes/exposure/templates/gallery-index.html
@@ -34,6 +34,7 @@
+
{% include "footer.html" %}
diff --git a/prosopopee/themes/exposure/templates/sections/pictures-group.html b/prosopopee/themes/exposure/templates/sections/pictures-group.html
index cc48ba3..037203c 100644
--- a/prosopopee/themes/exposure/templates/sections/pictures-group.html
+++ b/prosopopee/themes/exposure/templates/sections/pictures-group.html
@@ -1,22 +1,34 @@
{% if section.background %}
-