Зачем и почему
Эту статью меня побудил написать гневный отзыв одного хабрапользователя, заявившего, что, в переводе на русский, звучит примерно так: «хорош писать комментарии, пиши что-то полезное».
Послав его куда подальше и немного подумав, я решил, что он-таки прав, тем более что я вроде как не первый год пишу всякий код и наверняка есть, о чем рассказать — а вдруг кому пригодиться. Оглянувшись назад, вспомнилось, что как-то была задачка автоматизировать создание фотоальбома. Так почему бы не написать об этом?
Зачем и почему — 2
Я сам любитель фотографировать, и естественно, надо бы свои творения показывать человекам. Для этого интернет — самое подходящее место. Первое, что пришло на ум в свое время — это Picasa. Но тут были моменты, которые мне сильно не нравились. Это
- ограниченный объем места;
- фотографии лежат не у меня, и мало ли что может с ними случиться, а трудов жалко.
Что нравилось — это то, что пикасофская софтинка позволяла массово грузить фотки и создавать альбом, а это важно, когда фоток много.
Все бы ничего, но случилось чудо — у меня появился свой веб-сервер. Соответственно захотелось держать все фотки у себя. К счастью, пикаса умела делать и локальные фотоальбомы — достаточно настроить нужные шаблоны — и в нужное время жать нужную кнопку. Пока меня все устраивало, но стало напрягать то, что большая красивая пикаса и линукссовый сервер вместе не смотрелись. Но какое-то время этот подход работал, ну и фиг бы с ним, но…
Тут я полностью перебрался под линуксы, соответственно запускать виндовую пикасу стало напряжно. Это был весомый аргумент к поиску нового решения. Таким решением стал скриптик, который позволял генерировать фотоальбом под линуксом, запускался из консоли и не требовал графического интерфейса.
Требования
Итак, что я хотел получить:
- скрипт, запускаемый из консоли, без каких либо тыканий мышой, по типу «запустил и забыл»;
- уменьшение фоток до нужного размера для просмотра;
- уменьшение до маленького размера, дабы показать в каталоге;
- поворот картинок если надо;
- декорирование картинок и вставка копирайта;
- генерирование html-страничек для показа всего этого добра;
- отсутствие привязки к БД.
Поехали
Раз нам надо запуск из консоли, то выбор сразу пал на Perl — просто, быстро и есть опыт написания скриптов на нем.
Все операции с картинками я возложил на ImageMagick. Тут столкнулся с тем, что непонятно, картинка вертикальная или горизонтальная, по какой оси ужимать. Наверно тут есть несколько способов решения, недавно на хабре проскакивал рецепт. Я решил так: командочкой «identify» получаем параметры картинки и смотрим, что больше — первый размер или второй. И в зависимости от этого жмем картинку с нужным параметром.
Детектим размерчик
Тут пришлось учесть один момент, то что имя файла может содержать пробелы и то, что в выводе от команды «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-ки.
Канэц. Точка.