トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

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>

サンプル