photo_album
mojoliciousのメモ
mojoliciousを使って見たかったので、CGI.pmベースのalbum.cgi(アルバム)をmojolicious(Photo_album)に書き直す事にした。
[tree]
.[photo_album]
├── lib
│ ├── album.pm
│ ├── myconstant.pm
│ ├── PhotoAlbum
│ │ └── Example.pm
│ └── PhotoAlbum.pm
├── log
│ ├── development.log
│ └── production.log
├── public
│ ├── css
│ │ ├── demo.css
│ │ ├── reset.css
│ │ └── style.css
│ ├── images
│ │ ├── 10.jpg
│ │ ├── 11.jpg
│ │ ├── hide.png
│ │ └── show.png
│ ├── img
│ │ ├── 10.jpg
│ │ ├── 11.jpg
│ │ ├── hide.png
│ │ └── show.png
│ ├── index2.html
│ ├── index3.html
│ └── js
│ ├── jquery.montage.js
│ ├── jquery.montage.js.bak
│ ├── jquery.montage.min.js
│ └── jquery.montage.min.js.bak
├── script
│ ├── hypnotoad.pid
│ └── photo_album
├── t
│ └── basic.t
└── templates
├── example
│ ├── next.html.ep
│ ├── onepic.html.ep
│ └── welcome.html.ep
├── index.html.ep
└── layouts
└── default.html.ep
SOURCE
photo_album/lib/PhotoAlbum.pm
package PhotoAlbum;
use utf8;
use Mojo::Base 'Mojolicious';
use album;
# This method will run once at server start
sub startup {
my $self = shift;
$self->attr( album => sub { album->new() } );
# Documentation browser under "/perldoc"
$self->plugin('PODRenderer');
# Router
my $r = $self->routes;
# Normal route to controller
$r->get('/')->to('example#welcome');
$r->get('/onepic')->to('example#onepic');
$r->post('/upload')->to('example#upload');
$r->post('/edit')->to('example#edit');
$r->get('/next')->to('example#next');
$r->get('/flick')->to('example#flick');
}
1;
photo_album/lib/album.pm
package album;
use strict;
use utf8;
use Mojo::Base 'Mojolicious::Controller';
use myconstant;
my $cons = myconstant->new();
my $imagedir = $cons->{imagedir};
my $img = $cons->{img};
my $max_size = $cons->{max_size};
my $img_size = $cons->{img_size};
my $item_count = $cons->{item_count};
my $real = 'public';
sub rotate {
my ( $s, $c ) = @_;
my $filename = $c->param('_name');
if ( $filename ne '' ) {
if ( $c->param('_action') =~ /^\d{1,3}$/ ) {
system(
"/usr/bin/mogrify -rotate @{[$c->param('_action')]} $real/$img/$filename"
);
system(
"/usr/bin/mogrify -rotate @{[$c->param('_action')]} $real/$imagedir/$filename"
);
}
elsif ( $c->param('_action') eq 'touch' ) {
system("/usr/bin/touch $real/$img/$filename");
system("/usr/bin/touch $real/$imagedir/$filename");
}
}
}
sub get_images {
my $s = shift;
my $opt = shift || '';
my $st = shift || 0;
my $items = shift || $item_count;
my @files = $s->images_select( $st, $item_count, $s->get_images_sort() );
my $text = join(
"\n",
map {
qq{ <a href="/onepic?_name=$_"><img src="/$imagedir/$_" $opt></img></a>}
} @files
);
return $text;
}
sub get_images_sort {
my $s = shift;
opendir my $dh, "$real/$imagedir" or die "Can't open $real/$imagedir";
my @files = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, -M "$real/$imagedir/$_" ] }
grep { !/(hide\.png|show\.png|arrow.*\.png|black\.png)/ }
grep { -s "$real/$imagedir/$_" != 0 }
grep ( /^[^.]/, readdir $dh );
return @files;
}
sub images_select {
my $s = shift;
my ( $next, $item, @files ) = @_;
my $i = -1;
my $st = $next * $item;
my @select = ();
SELECT:
for (@files) {
$i++;
next SELECT if $i < $st;
last SELECT if ( $i >= ( $next + 1 ) * $item );
push @select, $_;
}
return @select;
}
sub flick{
my ($s,$r) = @_;
my $flick = {
"name" => $s->get_next_image($r,$r->param('_count')),
"preload" => $s->get_next_image($r,$r->param('_count')*3),
};
return $flick;
}
sub get_next_image{
my $s = shift;
my $r = shift;
my $count = shift;
my $file = $r->param('_name');
$file =~ s|.*?([^/]+\.[a-zA-Z0-9]*)(\?.*)*$|$1|;
my @files = $s->get_images_sort();
my $i = $s->search_index($file,@files);
$i += $count;
$i -= ($#files + 1) if($i > $#files);
my $img = "$img/$files[$i]";
return $img;
}
sub preload{
my $s = shift;
my $name = shift;
my @files = get_images_sort();
$name =~ s|.*?([^/]+\.[a-zA-Z]*)(\?.*)*$|$1|;
my $i = $s->search_index($name,@files);
my $text = "<script>\n";
for my $j (1 .. 2){
$text .= qq{\$('<img src="$img/$files[$i + $j]">');\n};
$text .= qq{\$('<img src="$img/$files[$i - $j]">');\n};
}
$text .= "</script>\n";
return $text;
}
sub search_index{
my ($s,$item,@arry) = @_;
my $i = 0;
for(@arry){
if($item eq $_){
return $i;
}
$i++;
}
return;
}
sub up_load {
my $s = shift;
my $upload = shift;
my $filename = $upload->filename;
$filename = $ENV{REMOTE_USER} . "_" . $upload->filename
if ( $ENV{REMOTE_USER} );
$upload->move_to("$real/$img/$filename");
if($filename =~ /(.*)\.mp4/i){
my $outfile = "$1_mp4.jpg";
system("ffmpeg -i $real/$img/$filename -vframes 1 $real/$imagedir/$outfile");
$filename = $outfile;
}else{
my $size = -s "$real/$img/$filename";
if ( $size > $max_size ) {
system(
"convert -resize @{[int($img_size / $size * 100)]}% $real/$img/$filename $real/$imagedir/$filename"
);
}
else {
system("cp $real/$img/$filename $real/$imagedir/$filename");
}
}
return $filename;
}
sub put_inner {
my $s = shift;
return $cons->{inner};
}
sub imgTag{
my $s = shift;
my $file = shift;
my $text;
if($file =~ /(.*)_mp4.jpg$/){
$text = <<TAG_END;
<br /><br /><br /><br /><br />
<br /><br /><br /><br /><br />
<div align="center">
<video
src="$img/$1.mp4"
preload="none"
onclick="this.play();"
poster="$imagedir/$file"
width="640"
controls>
<source src="$img/$1.mp4">
</video>
</div>
TAG_END
}else{
$text = <<TAG_END;
<a href="$img/$file" id="bigimg">
<img src="$img/$file?@{[(stat("$real/$img/$file"))[9]]}" width=100% name="img"></img>
</a>
TAG_END
}
return $text;
}
1;
photo_album/lib/myconstant.pm
package myconstant;
sub new {
my $class = shift;
my $self = bless {}, $class;
$self->_initalize;
return $self;
}
sub _initalize{
my $s = shift;
$s->{pub} = 'public/';
$s->{css} = 'css/';
$s->{js} = 'js/';
$s->{imagedir} = 'images';
$s->{img} = 'img';
$s->{max_size} = 500_000;
$s->{img_size} = 100_000;
$s->{item_count} = 20;
$s->{inner} = <<End_Text;
<h1>Automatic Image Montage <span>with jQuery</span></h1>
<h2>Fullscreen liquid example with alternating heights, last image will fill the last row. Refresh the browser to see a different arrangement.</h2>
<div class="snippet">
<span id="showcode" class="down">View the options for this example</span>
<pre>
fillLastRow : true,
alternateHeight : true,
alternateHeightRange : {
min : 90,
max : 240
}
</pre>
</div>
<div class="more">
<ul>
<li>More examples:</li>
<li class="selected"><a href="index.html">Example 1</a></li>
<li><a href="index2.html">Example 2</a></li>
<li><a href="index3.html">Example 3</a></li>
<li><a href="index4.html">Example 4</a></li>
<li><a href="index5.html">Example 5</a></li>
<li><a href="index6.html">Example 6</a></li>
<li><a href="index7.html">Example 7</a></li>
<li><a href="index8.html">Example 8</a></li>
</ul>
</div>
End_Text
}
1;
photo_album/lib/PhotoAlbum/Example.pm
package PhotoAlbum::Example;
use Mojo::Base 'Mojolicious::Controller';
# This action will render a template
sub welcome {
my $self = shift;
# Render template "example/welcome.html.ep" with message
$self->render(
message => 'Welcome to the Mojolicious real-time web framework!');
}
sub onepic {
my $self = shift;
$self->render('example/onepic');
}
sub upload {
my $self = shift;
my $name = $self->app->album->up_load($self->param('image'));
$self->param('_name',$name);
$self->render('example/onepic');
}
sub edit {
my $self = shift;
$self->app->album->rotate($self);
$self->render('example/onepic');
}
sub next{
my $self = shift;
$self->render('example/next');
}
sub flick{
my $self = shift;
my $json = $self->app->album->flick($self);
$self->render(json => $json);
}
1;
photo_album/templates/example/welcome.html.ep
<!DOCTYPE html>
<html lang="en">
<head>
<title>album</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Automatic Image Montage with jQuery" />
<meta name="keywords" content="jquery, images, montage, fullscreen, floating, grid, automatic" />
<link rel="stylesheet" type="text/css" href="/css/demo.css" />
<%= stylesheet '/css/style.css' %>
<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow&v1' rel='stylesheet' type='text/css' />
<link href='http://fonts.googleapis.com/css?family=Monoton' rel='stylesheet' type='text/css' />
</head>
<body>
<div class="container">
<div class="header">
% # @{[$cgi->param('_action')]}
<span class="right_ab">
% # @{[up_load_form()]}
<form method="post" action="<%= url_for('upload') %>" enctype ="multipart/form-data">
<input type="file" name="image" />
<input type="submit" value="Upload" style="width:80px" />
</form>
</span>
</div>
<div id="overlay" class="content">
<div class="inner">
<%== app->album->put_inner %>
<div class="clr"></div>
<div id="panel" class="panel hide"></div>
</div>
</div>
<div class="am-container" id="am-container">
<%== app->album->get_images('',0,25) %>
</div>
<div id="loadmore" class="loadmore" style="width:100%;">load more...</div>
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<%= javascript '/js/jquery.montage.min.js' %>
<script type="text/javascript">
var ix = 0;
$(function() {
/*
* just for this demo:
*/
$('#showcode').toggle(
function() {
$(this).addClass('up').removeClass('down').next().slideDown();
},
function() {
$(this).addClass('down').removeClass('up').next().slideUp();
}
);
$('#panel').toggle(
function() {
$(this).addClass('show').removeClass('hide');
$('#overlay').stop().animate( { left : - $('#overlay').width() + 20 + 'px' }, 300 );
},
function() {
$(this).addClass('hide').removeClass('show');
$('#overlay').stop().animate( { left : '20px' }, 300 );
}
);
var $container = $('#am-container'),
$imgs = $container.find('img').hide(),
totalImgs = $imgs.length,
cnt = 0;
$imgs.each(function(i) {
var $img = $(this);
$('<img/>').load(function() {
++cnt;
if( cnt === totalImgs ) {
$imgs.show();
$container.montage({
fillLastRow : true,
alternateHeight : true,
alternateHeightRange : {
min : 90,
max : 240
}
});
$('#overlay').fadeIn(500);
$('#loadmore').show().bind('click', function() {
$.ajax({
url: '/next',
type: 'GET',
data: {
_action: 'next',
_count: ++ix
},
success: function( data,textStatus,jqXHR){
var $newimages = $( data );
$newimages.imagesLoaded( function(){
$container.append( $newimages ).montage( 'add',$newimages);
});
}
})
});
}
}).attr('src',$img.attr('src'));
});
});
</script>
</body>
</html>
photo_album/templates/example/onepic.html.ep
<!DOCTYPE html>
<html lang="en">
<head>
<title>album</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" type="text/css" href="/css/demo.css" />
</head>
<body>
<div class="container">
<div class="header">
<a href='/'>ALBUM</a><div id="flick"><%== "<br>" x 5 %><hr></div>
</dev>
<div id="overlay" class="content">
<div class="inner">
<style type="text/css">
div.example1 {
overflow-x: auto;
white-space: nowrap;
}
</style>
%= form_for edit => (method => 'post') => begin
%= hidden_field '_name' => <%= param '_name'
%= submit_button '90',name =>'_action',style =>'width:30px'
%= submit_button '270',name =>'_action',style =>'width:30px'
%= submit_button 'touch',name =>'_action',style =>'width:50px'
% end
<div class="example1">
<%== app->album->get_images('width=80px height=120px',0,9999) %>
</div>
<div class="clr"></div>
<div id="panel" class="panel hide"></div>
</div>
</div>
</div>
</div>
<%== app->album->imgTag(param '_name') %>
%# @{[imgTag($cgi->param('_name'))]}
%# <a href="/img/<%=param '_name'%>">
%# <img src="/img/<%=param '_name'%>" width=100%></img>
%# </a>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="/js/jquery.montage.min.js"></script>
<script type="text/javascript">
$(function() {
/*
* just for this demo:
*/
/* $('#showcode').toggle(
function() {
$(this).addClass('up').removeClass('down').next().slideDown();
},
function() {
$(this).addClass('down').removeClass('up').next().slideUp();
}
);
*/ $('#panel').toggle(
function() {
$(this).addClass('show').removeClass('hide');
$('#overlay').stop().animate( { left : - $('#overlay').width() + 20 + 'px' }, 300 );
},
function() {
$(this).addClass('hide').removeClass('show');
$('#overlay').stop().animate( { left : '20px' }, 300 );
}
);
$('#overlay').fadeIn(500);
});
</script>
<script type="text/javascript">
$(function() {
$('#flick').bind("touchstart touchmove touchend",touchHandler);
function touchHandler(e){
e.preventDefault();
var touch = e.originalEvent.touches[0];
if(e.type == "touchstart"){
startX = touch.pageX;
}else if(e.type == "touchmove"){
diffX = touch.pageX - startX;
}else if(e.type == "touchend"){
if(diffX > 50){
$.ajax({
url: '/flick',
type: 'GET',
dataType: 'json',
data: {
_action: 'flick',
_name: document.img.src,
_count: '-1',
},
success: function( data,textStatus,jqXHR){
// $("#flick").text(data.name);
// var res = JSON.parse(data);
document.img.src=data.name;
$("#bigimg").attr("href",data.name);
$("<img>").attr("src",data.preload);
}
})
}
if(diffX < -50){
$.ajax({
url: '/flick',
type: 'GET',
dataType: 'json',
data: {
_action: 'flick',
_name: document.img.src,
_count: '1',
},
success: function( data,textStatus,jqXHR){
document.img.src=data.name;
$("#bigimg").attr("href",data.name);
$("<img>").attr("src",data.preload);
}
})
}
}
}
});
</script>
<%== app->album->preload(param '_name') %>
</body>
</html>
photo_album/templates/example/next.html.ep
<!DOCTYPE html>
<html lang="en">
<head>
<title>album</title>
</head>
<body>
<%== app->album->get_images('',param('_count') ) %>
</body>
</html>
サンプル
- 無精・短気・傲慢