Simple automation: фотоальбом

в 12:44, , рубрики: automation, perl, фотоальбомы, метки: , ,

Зачем и почему

Эту статью меня побудил написать гневный отзыв одного хабрапользователя, заявившего, что, в переводе на русский, звучит примерно так: «хорош писать комментарии, пиши что-то полезное».

Послав его куда подальше и немного подумав, я решил, что он-таки прав, тем более что я вроде как не первый год пишу всякий код и наверняка есть, о чем рассказать — а вдруг кому пригодиться. Оглянувшись назад, вспомнилось, что как-то была задачка автоматизировать создание фотоальбома. Так почему бы не написать об этом?

Зачем и почему — 2

Я сам любитель фотографировать, и естественно, надо бы свои творения показывать человекам. Для этого интернет — самое подходящее место. Первое, что пришло на ум в свое время — это Picasa. Но тут были моменты, которые мне сильно не нравились. Это

  • ограниченный объем места;
  • фотографии лежат не у меня, и мало ли что может с ними случиться, а трудов жалко.

Что нравилось — это то, что пикасофская софтинка позволяла массово грузить фотки и создавать альбом, а это важно, когда фоток много.

Все бы ничего, но случилось чудо — у меня появился свой веб-сервер. Соответственно захотелось держать все фотки у себя. К счастью, пикаса умела делать и локальные фотоальбомы — достаточно настроить нужные шаблоны — и в нужное время жать нужную кнопку. Пока меня все устраивало, но стало напрягать то, что большая красивая пикаса и линукссовый сервер вместе не смотрелись. Но какое-то время этот подход работал, ну и фиг бы с ним, но…

Тут я полностью перебрался под линуксы, соответственно запускать виндовую пикасу стало напряжно. Это был весомый аргумент к поиску нового решения. Таким решением стал скриптик, который позволял генерировать фотоальбом под линуксом, запускался из консоли и не требовал графического интерфейса.

Требования

Итак, что я хотел получить:

  • скрипт, запускаемый из консоли, без каких либо тыканий мышой, по типу «запустил и забыл»;
  • уменьшение фоток до нужного размера для просмотра;
  • уменьшение до маленького размера, дабы показать в каталоге;
  • поворот картинок если надо;
  • декорирование картинок и вставка копирайта;
  • генерирование html-страничек для показа всего этого добра;
  • отсутствие привязки к БД.

Поехали

Раз нам надо запуск из консоли, то выбор сразу пал на Perl — просто, быстро и есть опыт написания скриптов на нем.

Все операции с картинками я возложил на ImageMagick. Тут столкнулся с тем, что непонятно, картинка вертикальная или горизонтальная, по какой оси ужимать. Наверно тут есть несколько способов решения, недавно на хабре проскакивал рецепт. Я решил так: командочкой «identify» получаем параметры картинки и смотрим, что больше — первый размер или второй. И в зависимости от этого жмем картинку с нужным параметром.

Детектим размерчик

sub getSize
{
    my $fname = shift;
    my $cmd = escapeShell ("identify '$fname'");
    my $info = `$cmd`;
    my $fname_len = length($fname);
    $info =~ s/^.{$fname_len}(.*)$/$1/;

    my $type;
    my $size;
    ($type, $size, undef) = split (' ', $info);
    my ($width, $height) = split ('x', $size);

    return ($width, $height);
}

Тут пришлось учесть один момент, то что имя файла может содержать пробелы и то, что в выводе от команды «identify» тоже пробелы используются как разделители. Немного танцев с бубном — и все работает.

По размеру делаем ресайз

sub resize
{
    my $size = shift;
    my $fname_in = shift;
    my $fname_out = shift;

    my ($w, $h) = getSize ($fname_in);
    print "W: $w, H: $hn";
    my $cmd;
    if ($w > $h) {
        $cmd = "convert '$fname_in' -resize $size -auto-orient '$fname_out'";
    } else {
        $cmd = "convert '$fname_in' -resize x$size -auto-orient '$fname_out'";
    }
    print "CMD: $cmdn";
    $cmd = escapeShell($cmd);
    `$cmd`;
}

Далее, я помнил, что пикаса приделывала тени к превью картинок. Порыв инет, оказалось, что это абсолютно не проблема и решается с помошью уже упомянутой ImageMagick. Рецепт взял отсюда.

Делаем тень

sub shadow
{
    my $fname_in = escapeShell(shift);
    my $fname_out = escapeShell(shift);

    my $cmd = "convert -page +3+3 '$fname_in' -matte "
        . "\( +clone -background black -shadow 70x2+2+2 \) "
        . " +swap -background '#9AB6D7' -mosaic '$fname_out'";

    `$cmd`;
}

Пока разбирался с тенями, оказалось, что можно приделать к фоткам еще и watermark. А почему бы и нет? Рисуем свою картинку с подписью и накладываем на фотку с помощью того же ImageMagick. Рецепт.

Вставляем картинку с копирайтом

sub signImg
{
    my $sign = shift;
    my $fname_in = shift;
    my $fname_out = shift;

    my $cmd = "convert $sign -fill grey50 -colorize 40  miff:- | "
    . " composite -dissolve 30 -gravity south - '$fname_in' "
        . " '$fname_out'";

        $cmd = escapeShell($cmd);
        `$cmd`;
}

Вообще с картинками можно творить все, что угодно, там много примеров, каждый найдет себе то, что ему по душе.

Возникла проблема html-обвязки. Обычно на больших сайтах информация лежит в базе данных и в УРЛе передается ИД картинки и все хорошо и красиво. Но у меня на моем серверочке не было базы — она туда просто не помещалась, потому что сервер представлял собой допиленный роутер ASUS Simple automation: фотоальбом

Передавать имя файла не хотелось из соображений секурности. Оставался выход — для каждого файла генерить свой html. Тоже не есть красиво.

На ум пришла идея, заимствованная из линуксов — одна и та же команда может иметь несколько симлинков, и в зависимости от имени симлинка она делает соответствующие дейстия. Итак, решение: пишем один темплейтик для показа картинки, создаем массивчик, в котором прописаны все имена файлов — это достаточно секурно, и создаем кучу симлинков, где имя симлинки совпадает с индексом массива картинок. Все — вот она база, собранная на коленке. А по имени файла лезем в массивчик и вытаскиваем имя файла картинки.

Генерируем PHP-шки для сайта

sub genPhpFiles
{
    # Check if index exists already
    my $new_index = undef;
    if (-f "$dir_in/photo_index.inc") {
        `cp $dir_in/photo_index.inc $dir_out_html/photo_index.inc`;
    } else {
        $new_index = 1;
        open F, ">$dir_out_html/photo_index.inc";
        print F '<? $desc = array (';
        print F "n";
    }
        
    # Gen index and symlinks
    opendir(my $dh, $dir_out_img) || die "Can't read dir `$dir_in': $!n";
    my $idx = 0;
    my @files = readdir $dh;
    @files = sort @files;
    foreach (@files) {
        next if ($_ =~ m/^./ || $_ =~ m/.inc$/);

        if ($new_index) {
            print F "array ('name' =>'$_', 'desc' => '$_: '),n";
        }
        `ln -s template.php $dir_out_html/$idx.php`;
        $idx++;
    }
    closedir $dh;
    
    if ($new_index) {
        print F "n); ?>";
        close F;
    }
}

А так внутри PHP-шки определяем имя файла картинки по имени симлинка

<?php
    // Extract number from file name and use it as ID
    $file = $_SERVER["SCRIPT_NAME"];
    $id = preg_replace('/.*/(d+).php$/', '$1', $file);
?>

Ну вот вроде и все — все требования выполнены.
Хм, получилось даже больше, чем я хотел изначально…

Пользуюсь этим скриптиком вот уже на протяжении нескольких месяцев, практически ничего не меняя, и остаюсь вполне доволен — он делает все, что было нужно — одним запуском командочки генерит и картинки, и html-ки, остается только положить на сервер.

Вот так выглядит сгенерированный результат в браузере:

Индекс фотографий

Simple automation: фотоальбом
Отдельная фотография

Simple automation: фотоальбом

Как пользоваться

Если кому нужно — то все скопом лежит в архивчике.

Пользоваться элементарно:

  • распаковать архивчик
  • поправить стили и темплейты под ваш дизайн (photo.css, photo.inc, photo_list.inc, template.php, index.php)
  • в каталог «in» сложить исходные картинки
  • запустить «make_photo_album» и получить в каталоге «out» картинки нужного размера и сгенерированные html-ки.

Канэц. Точка.

Автор: eaa

* - обязательные к заполнению поля