Created
August 16, 2012 15:55
-
-
Save tzvetkoff/3371287 to your computer and use it in GitHub Desktop.
renegator.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'set' | |
require 'cgi' | |
require 'exifr' | |
require 'rmagick' | |
require 'fileutils' | |
module HTML | |
INDENT_TAGS = Set[ | |
:head, :body, :style, | |
:header, :footer, :article, :aside, | |
:div, | |
:dl, | |
:table, :colgroup, :thead, :tbody, :tfoot, :tr, :td, | |
:ul, :li, | |
] | |
NEW_LINE_TAGS = Set[ | |
:html, :meta, :link, | |
:head, :body, :style, | |
:header, :footer, :article, :aside, | |
:div, | |
:dl, | |
:table, :colgroup, :thead, :tbody, :tfoot, :tr, :td, | |
:ul, :li, | |
] | |
XML_TAGS = Set[ | |
:meta, :link, | |
:img, :hr, :br, | |
] | |
class Builder | |
def initialize(&block) | |
@content = "<!DOCTYPE html>\n" | |
instance_eval(&block) if block_given? | |
end | |
def method_missing(meth, *args, &block) | |
attributes = if args.first.is_a?(Hash) | |
' ' + attribute_string_from_hash(args.shift) | |
elsif args.last.is_a?(Hash) | |
' ' + attribute_string_from_hash(args.pop) | |
else | |
'' | |
end | |
new_line = NEW_LINE_TAGS.include?(meth) && "\n" || '' | |
if block_given? | |
content, @content = @content, '' | |
instance_eval(&block) | |
content, @content = @content, content | |
else | |
content = args.join("\n") | |
end | |
if content.empty? && XML_TAGS.include?(meth) | |
@content << "<#{meth}#{attributes} />#{new_line}" | |
else | |
if content.empty? | |
new_line = '' | |
end | |
if INDENT_TAGS.include?(meth) | |
content = indent(content, 1) | |
end | |
@content << "<#{meth}#{attributes}>#{new_line}#{content}#{new_line}</#{meth}>\n" | |
end | |
@content | |
end | |
def to_s | |
@content.split("\n").map(&:rstrip).reject { |line| line.empty? }.join("\n") | |
end | |
private | |
def escape_html_special_chars(str) | |
CGI.escape_html(str.to_s) | |
end | |
def attribute_string_from_hash(hash) | |
hash.map { |key, value| "#{escape_html_special_chars(key)}=\"#{escape_html_special_chars(value)}\"" }.join(' ') | |
end | |
def indent(str, level, with = ' ') | |
str.split("\n").map { |line| with * level + line }.join("\n") | |
end | |
end | |
end | |
sorted = Hash.new { |hash, key| hash[key] = [] } | |
Dir['Library/*/**/*.*'].each { |filename| | |
key = nil | |
begin | |
exif = EXIFR::JPEG.new(filename) | |
key = exif.date_time && exif.date_time.strftime('%Y-%m') || 'Sometime' | |
date = exif.date_time && exif.date_time.strftime('%A, %e %B %Y, %H:%M') || 'Unknown' | |
gps = exif.gps && "#{exif.gps[:latitude]},#{exif.gps[:longitude]}" || nil | |
gps_short = exif.gps && "#{exif.gps[:latitude].round(6)}, #{exif.gps[:longitude].round(6)}" || 'Unknown' | |
camera = (exif.make || exif.model) && "#{exif.make} #{exif.model}" || 'Unknown' | |
rescue Exception => e | |
# not an image - ignore | |
else | |
sorted[key] << { | |
filename: filename, | |
date: date, | |
gps: gps, | |
gps_short: gps_short, | |
camera: camera, | |
} | |
# create thumbnails too | |
destination = filename.dup | |
destination['Library/'] = 'Library/.Thumbnails/' | |
FileUtils.mkdir_p(File.dirname(destination)) | |
image = Magick::Image.read(filename).first | |
image.crop_resized!(256, 256, Magick::CenterGravity) | |
image.write(destination) | |
end | |
} | |
sorted = Hash[sorted.sort] | |
index = 0 | |
doc = HTML::Builder.new { | |
html { | |
head { | |
meta :'http-equiv' => 'Content-Type', content: 'text/html; charset=UTF-8' | |
title 'hi! :-)' | |
link rel: 'stylesheet', type: 'text/css', media: 'screen, projection', href: 'Gallery.css' | |
} | |
body { | |
h1 'hi! :-)' | |
sorted.each { |date, files| | |
date_humanized = if date =~ /^\d{4}-\d{2}$/ | |
Time.new(*date.split('-').map(&:to_i)).strftime('%b %Y') | |
else | |
date | |
end | |
h2 date_humanized | |
ul(class: 'lb-album') { | |
files.each { |hash| | |
filename = hash[:filename].split('/')[1..9999].join('/') | |
li { | |
a(href: "#image-#{index}") { | |
img src: "./.Thumbnails/#{filename}", alt: "#{File.basename(filename)}" | |
} | |
table(cellspacing: 0, cellpadding: 0) { | |
colgroup { | |
col width: 50 | |
col | |
} | |
tbody { | |
tr { | |
th 'Date:' | |
td hash[:date] | |
} | |
tr { | |
th 'GPS:' | |
if hash[:gps] | |
td { | |
a hash[:gps_short], href: "http://maps.google.com/?q=#{hash[:gps]}", target: 'blank' | |
} | |
else | |
td hash[:gps_short] | |
end | |
} | |
tr { | |
th 'Camera:' | |
td hash[:camera] | |
} | |
} | |
} | |
div(class: 'lb-overlay', id: "image-#{index}") { | |
a(href: "./#{filename}") { | |
img src: "./#{filename}", alt: "#{File.basename(filename)}" | |
} | |
a 'Close', href: '#close', class: 'close' | |
} | |
} | |
index += 1 | |
} | |
} | |
} | |
} | |
} | |
} | |
File.write('Library/Gallery.html', doc) | |
css = <<-CSS | |
/* page styles */ | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
padding: 20px; | |
background: #ffffff; | |
color: #333333; | |
font: 12px/15px Helvetica, sans-serif; | |
} | |
img { | |
border: 0 none; | |
} | |
h1 { | |
font-size: 20px; | |
line-height: 30px; | |
margin: 0 0 10px 0; | |
} | |
h2 { | |
font-size: 16px; | |
} | |
h2 + ul { | |
overflow: hidden; | |
margin: 10px 0 10px 0; | |
list-style: none outside; | |
} | |
h2 + ul > li { | |
position: relative; | |
float: left; | |
margin: 0 10px 10px 0; | |
padding: 10px; | |
background: rgba(0, 0, 0, 0.1); | |
border: 1px solid rgba(0, 0, 0, 0.2); | |
border-radius: 4px; | |
} | |
h2 + ul > li > a { | |
position: relative; | |
float: left; | |
} | |
h2 + ul > li > a > span { | |
position: absolute; | |
left: 0px; | |
right: 0px; | |
top: 0px; | |
bottom: 0px; | |
padding: 10px; | |
color: rgba(27, 54, 81, 0.8); | |
text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.6); | |
font-size: 15px; | |
text-decoration: none; | |
opacity: 0; | |
background: rgba(222, 222, 222, 0.9); | |
-webkit-transition: opacity 0.3s linear; | |
transition: opacity 0.3s linear; | |
} | |
h2 + ul > li > a:hover > span { | |
opacity: 1; | |
} | |
h2 + ul > li > table { | |
margin: 10px 0 0 0; | |
width: 100%; | |
clear: left; | |
float: left; | |
} | |
h2 + ul > li > table th, | |
h2 + ul > li > table th { | |
padding: 0 5px; | |
} | |
h2 + ul > li > table th { | |
text-align: right; | |
} | |
h2 + ul > li > table td { | |
text-align: left; | |
} | |
h2 + ul > li > table td a { | |
color: #990000; | |
text-decoration: none; | |
} | |
h2 + ul > li > table td a:hover { | |
text-decoration: underline; | |
} | |
/* lightbox "overlay" */ | |
.lb-overlay { | |
width: 0px; | |
height: 0px; | |
position: fixed; | |
overflow: hidden; | |
left: 0px; | |
top: 0px; | |
padding: 0px; | |
z-index: 99; | |
text-align: center; | |
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(255,255,255,0.56)), color-stop(100%,rgba(241,210,194,1))); | |
background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,0.56) 0%,rgba(241,210,194,1) 100%); | |
} | |
.lb-overlay > a.close { | |
background: rgba(27,54,81,0.8); | |
z-index: 1001; | |
color: #fff; | |
position: absolute; | |
top: 30px; | |
left: 50%; | |
font-size: 15px; | |
line-height: 40px; | |
text-align: center; | |
text-decoration: none; | |
width: 100px; | |
height: 40px; | |
overflow: hidden; | |
margin-left: -50px; | |
opacity: 0; | |
-webkit-box-shadow: 0px 1px 2px rgba(0,0,0,0.3); | |
box-shadow: 0px 1px 2px rgba(0,0,0,0.3); | |
-webkit-transition: opacity 0.3s linear 1.2s; | |
transition: opacity 0.3s linear 1.2s; | |
} | |
.lb-overlay img { | |
max-height: 100%; | |
position: relative; | |
-webkit-box-shadow: 1px 1px 4px rgba(0,0,0,0.3); | |
box-shadow: 0px 2px 7px rgba(0,0,0,0.2); | |
} | |
.lb-overlay:target { | |
width: auto; | |
height: auto; | |
bottom: 0px; | |
right: 0px; | |
padding: 50px 50px 20px 50px; | |
} | |
.lb-overlay:target img { | |
-webkit-animation: scaleDown 1.2s ease-in-out; | |
animation: scaleDown 1.2s ease-in-out; | |
} | |
.lb-overlay:target > a.close { | |
opacity: 1; | |
} | |
/* animations */ | |
@-webkit-keyframes scaleDown { | |
0% { -webkit-transform: scale(10, 10); opacity: 0; } | |
100% { -webkit-transform: scale(1, 1); opacity: 1; } | |
} | |
@keyframes scaleDown { | |
0% { transform: scale(10, 10); opacity: 0; } | |
100% { transform: scale(1, 1); opacity: 1; } | |
} | |
x:-o-prefocus, .lb-overlay img { | |
height: 100%; | |
} | |
CSS | |
File.write('Library/Gallery.css', css) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment