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

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


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

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



    Зачем и почему — 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: $h\n";
        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: $cmd\n";
        $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

    Передавать имя файла не хотелось из соображений секурности. Оставался выход — для каждого файла генерить свой 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-ки, остается только положить на сервер.

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

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



    Отдельная фотография



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


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

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


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

    Comments 6

      +4
      Генератор php-файлов на Perl… Месье знает толк…
        0
        Вообще-то изначально были обычные html + SSI (server-side includes), не хотелось использовать никакого движка дополнительно, но потом-таки пришлось применить для кой-чего PHP, а генератор так на перле и остался, ибо работает — не трогай.
        0
        Я пользовал вот этот скрит для генерации фотоальбома — igal.trexler.at/ (скрипт тоже на Perl). Вот мой опыт — koorchik.blogspot.com/2011/08/igal-5.html
          0
          А не было идеи оригиналы сохранять рядом с уменьшенными копиями? Я пока зеркалкой пользовался, хотел сделать подобную фигню, да руки не дошли. Но у меня была еще идея в EXIF писать названия / тэги и прочее, чтобы по ним потом искать.
          В общем, идея для развития – хранить рядом оригиналы на всякий случай и писать названия / тэги прямо в файлы, чтобы это все для поиска доступно было :-)
            0
            Насчет EXIF были мысли, но только не писать их в файл, а наоборот, читать и показывать — иногда удобно видеть параметры съемки рядом с фотографией. Это очень даже может сделаю.

            А вот оригиналы рядом держать даже не знаю пока как — во-первых, не все оригиналы надо показывать на веб — значит должны быть типа public и private. Далее, в оригинале не все хочется отдавать — на уменьшенную пусть любуются, а если кому приспичит коммерчески применить — сорри, не хочу. Да еще и тэги писать — а значит их надо откуда-то брать и вписывать. Получается уже не просто скриптик, а система управления фотками. Я вот не уверен, что пока хочу такое писать. Тут недавно пробегало что-то — может взять за основу или совместными усилиями попинать…
              0
              Какая там штука описана в топике! Прям вот почти то, что я уже «изобрел» :-)
              Про оригиналы: говоря «рядом» я имею в виду именно рядом – то есть если у меня есть RAW-image, то он где-то в специальном месте лежит, и о нем только скрипт знает, ну и админу, если что, говорит. То есть показывать их в фотоальбоме-то не нужно, ясное дело. А вот для истории (или дальнейшей обработки и печати твердой копии, например – ну вдруг понадобится?) – пригодится.
              А про EXIF такая история. Помимо того, что там уже содержатся данные о параметрах съемки, туда можно еще много чего записать. Преимущество этого – то, что не нужно держать БД для хранения описаний, тэгов и названий фотогорафий, ну и фото в отрыве от приложения и файловой системы сохраняет всю инфу о себе. Потому что инфа непосредственно в фото встроена.

          Only users with full accounts can post comments. Log in, please.