end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

ImageMagick for win + Ghostscript + perl で、PDFやMP4等からサムネイルとインデックスページ作成

https://end0tknr.hateblo.jp/entry/20221208/1670487848

上記にある先日のentry の続きです。

きちんと、テストしていませんが、いかのような感じかと思います。

#!perl

# pdf等の画像や動画fileをtraverse検索し、thumb nailを作成した上で
# それらにアクセスする為のindexページを作成します

use utf8;
use strict;
use warnings;
use Data::Dumper;
use Encode;
use File::Basename;
use File::Path;
use Math::Round;
use Time::Piece;

my $BASE_DIRS = ["./html_1"];
my $ORG_FILES_DIR = "catalog";
my $THUMBS_DIR    = "thumb";
my $CATALOG_HTML  = "catalogs_tbl.html";
my $MAGICK_CMD = "magick convert -resize 240x240 %s[0] %s";
my $ORG_ENCODE = "cp932";

# cf nginx/conf/mime.types
my $IMG_OR_MOVIE_EXTS = [
    "pdf",
    "mp4","mpeg","mpg","mov","flv","m4v","wmv","avi",
    "gif","jpeg","jpg","png","svg","svgz","tif","tiff","bmp"];
my $html_tbl_1 =<<EOF;
<table>
  <thead>
    <tr>
      <th></th>
      <th class="sort" data-sort="path"  >ファイル名</th>
      <th class="sort" data-sort="mbyte" >サイズ</th>
      <th class="sort" data-sort="update">更新日時</th>
    </tr>
  </thead>
  <tbody class="list">
EOF
my $html_tbl_2 =<<EOF;
  </tbody>
</table>
EOF
my $html_tbody_tr =<<EOF;
    <tr>
      <td>
        <a href="%s"><img src="%s" /></a>
      </td>
      <td class="path">
        <div class="parent_dir">%s</div>
        <a href="%s"><div class="filename">%s</div></a>
      </td>
      <td class="mbyte">%s MB</td>
      <td class="update">%s</td>
    </tr>
EOF


main();

sub main {
    
    for my $base_dir ( @$BASE_DIRS ){
        my $file_base_dir = "$base_dir/$ORG_FILES_DIR";
        my $file_paths = find_img_or_movie_files( $file_base_dir );

        my $thumb_infos    = [];
        my $thumb_base_dir = "$base_dir/$THUMBS_DIR";
        
        for my $file_path ( @$file_paths ){

            my $thumb_path = create_thumb_nail($file_path,
                                               $file_base_dir,
                                               $thumb_base_dir);
            if (not $thumb_path){
                print STDERR "fail create_thumb_nail() $file_path\n";
                next;
            }

            my $thumb_info = get_file_info( $file_path );
            
            $thumb_info->{"parent_dir"} = dirname($file_path);
            $thumb_info->{"parent_dir"} =~ s/$file_base_dir//;
            $thumb_info->{"parent_dir"} =~ s/^\///;
            
            $file_path =~ s/$base_dir\///;
            $thumb_info->{"org_path"}  = $file_path;
            
            $thumb_path =~ s/$base_dir\///;
            $thumb_info->{"thumb_path"} = $thumb_path;
            push(@$thumb_infos, $thumb_info);
        }

        my $html_tbl = create_index_page( $thumb_infos );
        my $tbl_html_path = "$base_dir/$CATALOG_HTML";
        open my $fh, '>', $tbl_html_path or die "fail open $tbl_html_path $!";
        print $fh Encode::encode('utf-8',$html_tbl);

        close $fh;
    }
}


sub create_index_page {
    my ( $thumb_infos ) = @_;

    my @html_tbl = ($html_tbl_1);

    for my $thumb_info ( @$thumb_infos ){
        my $org_path   = $thumb_info->{org_path};
        my $thumb_path = $thumb_info->{thumb_path};
        my $parent_dir = $thumb_info->{parent_dir};
        my $basename   = $thumb_info->{basename};
        my $m_byte     = $thumb_info->{"m_byte"};
        my $update     = $thumb_info->{update};

        my $html_tr = sprintf($html_tbody_tr,
                              Encode::decode($ORG_ENCODE,$org_path),
                              Encode::decode($ORG_ENCODE,$thumb_path),
                              Encode::decode($ORG_ENCODE,$parent_dir),
                              Encode::decode($ORG_ENCODE,$org_path),
                              Encode::decode($ORG_ENCODE,$basename),
                              $m_byte,
                              $update);
        push(@html_tbl,$html_tr);
    }
    push(@html_tbl,$html_tbl_2);

    return join("\n",@html_tbl);
}

sub get_file_info {
    my ($file_path) = @_;
        
    my @file_stat = stat $file_path;
    my $m_byte = Math::Round::nearest(0.1,$file_stat[7] / 1024 / 1024);
        
    my $time_piece = localtime($file_stat[9]);
    my $update = sprintf("%04d/%02d/%02d %02d:%02d",
                         $time_piece->year,
                         $time_piece->mon,
                         $time_piece->mday,
                         $time_piece->hour,
                         $time_piece->minute);
    
    my $file_info = {"basename"=> basename($file_path),
                     "m_byte"  => $m_byte,
                     "update"  => $update   };
  return $file_info;
}

sub create_thumb_nail {
    my ($org_file_path,$org_base_dir,$thumb_base_dir) = @_;

    my $new_path = $org_file_path;
    $new_path =~ s/^$org_base_dir\///;
    $new_path =~ s/\//_/go;
    $new_path = "$thumb_base_dir/$new_path.png";

    my $magick_cmd = sprintf($MAGICK_CMD,$org_file_path, $new_path);

    open my $fh, '-|', $magick_cmd or die "fail open $magick_cmd $!";
    while (my $line = <$fh> ) {
        print STDERR "$line\n";
    }
    close($fh);

    return $new_path;
}

sub find_img_or_movie_files {
    my ($dir) = @_;

    my @ret_files;
    opendir my $dh, $dir or die "fail opendir $dir $!";

    while (my $file_or_dir = readdir $dh ) {
        
        if ($file_or_dir eq "." or $file_or_dir eq ".."){
            next;
        }
        my $tmp_path = "$dir/$file_or_dir";
        if (-d $tmp_path ){
            my $tmp_ret_files = find_img_or_movie_files( $tmp_path );
            push(@ret_files, @$tmp_ret_files );
            next;
        }

        if ( is_img_or_movie_file($tmp_path) ){
            push(@ret_files, $tmp_path );
        }
    }
    return \@ret_files;
}

sub parse_extension {
    my ( $org_path ) = @_;

    $org_path =~ /[^\.]+\.([^\.]+)$/o;
    if ( not length($1) ){
        return "";
    }
    my $ext = lc $1;
    return $ext;
}

sub is_img_or_movie_file {
    my ( $org_path ) = @_;

    my $ext = parse_extension( $org_path );

    for my $tmp_ext ( @$IMG_OR_MOVIE_EXTS ){
        if ( $ext eq $tmp_ext ){
            return 1;
        }
    }
    return 0;
}

↑こうかくと、↓このようなhtmlが表示されます

<table>
  <thead>
    <tr>
      <th></th>
      <th class="sort" data-sort="path"  >ファイル名</th>
      <th class="sort" data-sort="mbyte" >サイズ</th>
      <th class="sort" data-sort="update">更新日時</th>
    </tr>
  </thead>
  <tbody class="list">

    <tr>
      <td>
    <a href="catalog/PDF内に動画テスト.pdf"><img src="thumb/PDF内に動画テスト.pdf.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir"></div>
    <a href="catalog/PDF内に動画テスト.pdf"><div class="filename">PDF内に動画テスト.pdf</div></a>
      </td>
      <td class="mbyte">1.9 MB</td>
      <td class="update">2022/12/07 09:28</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/カタログ_RUSTIC_TILEWALL.pdf"><img src="thumb/カタログ_RUSTIC_TILEWALL.pdf.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir"></div>
    <a href="catalog/カタログ_RUSTIC_TILEWALL.pdf"><div class="filename">カタログ_RUSTIC_TILEWALL.pdf</div></a>
      </td>
      <td class="mbyte">20.9 MB</td>
      <td class="update">2022/12/10 11:25</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/メンテナンス読本.PDF"><img src="thumb/メンテナンス読本.PDF.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir"></div>
    <a href="catalog/メンテナンス読本.PDF"><div class="filename">メンテナンス読本.PDF</div></a>
      </td>
      <td class="mbyte">9.2 MB</td>
      <td class="update">2022/12/07 08:46</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/動画/ZOOM背景と、らいおん.MP4"><img src="thumb/動画_ZOOM背景と、らいおん.MP4.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir">動画</div>
    <a href="catalog/動画/ZOOM背景と、らいおん.MP4"><div class="filename">ZOOM背景と、らいおん.MP4</div></a>
      </td>
      <td class="mbyte">1.8 MB</td>
      <td class="update">2022/01/07 22:54</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/動画/生産工程.mp4"><img src="thumb/動画_生産工程.mp4.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir">動画</div>
    <a href="catalog/動画/生産工程.mp4"><div class="filename">生産工程.mp4</div></a>
      </td>
      <td class="mbyte">26.5 MB</td>
      <td class="update">2022/12/10 11:21</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/動画/生産工程_その2.mp4"><img src="thumb/動画_生産工程_その2.mp4.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir">動画</div>
    <a href="catalog/動画/生産工程_その2.mp4"><div class="filename">生産工程_その2.mp4</div></a>
      </td>
      <td class="mbyte">44.1 MB</td>
      <td class="update">2022/12/10 11:22</td>
    </tr>

    <tr>
      <td>
    <a href="catalog/外観スタイルカタログ_2211.pdf"><img src="thumb/外観スタイルカタログ_2211.pdf.png" /></a>
      </td>
      <td class="path">
    <div class="parent_dir"></div>
    <a href="catalog/外観スタイルカタログ_2211.pdf"><div class="filename">外観スタイルカタログ_2211.pdf</div></a>
      </td>
      <td class="mbyte">126.5 MB</td>
      <td class="update">2022/12/10 11:27</td>
    </tr>

  </tbody>
</table>