Речь пойдёт о дигитальных камерах с USB-интерфейсом, которые распознаются в линуксе как внешний носитель (USB storage device), на примере "Pentax Optio M30".
Скачивание фотографий с этой камеры происходит обычно примерно так:- подключаем камеру
- жмём "Скачать фотографии в digiKam" в появившемся диалоге
- добавляем новый альбом в дигикам
- маркируем нужные (если помним какие) фотки
- жмём скачать, ждём
- отключаем камеру
- сортируем фотки по дате
- итд
Итог - много ручной работы.
Цель: автоматизировать процесс до невозможности, т.е.:- подключаем камеру
- ждём пока скриптик сделает всё за нас
- отключаем камеру
Для начала немного о самом udev.
udev
Udev это динамический настраиваемый мэнеджер устройств, его задача - следить за физическими устройствами в системе и отображать их в папке /dev. Это позволяет показывать в /dev только те устройства, которые действительно подключены к компьютеру, а так же даёт много других преймуществ, о которых вам с удовольствием расскажет гугль и http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html.
Вот некоторые вещи, которые позволяет делать udev:- переименовывать устройства, в том числе с помощью вывода других програм
- добавлять дополнительные имена устройств и/или создавать символические ссылки на них
- менять права и влаельцев устройства
- запускать скрипт при присоединении/удалении устройства
- переименовывать сетевые интерфейсы
Из этого всего нас интересует в основном 4 пункт.
udev-правила
Udev является стандартом начиная с ядра 2.6.13 и используется во всех современных дистрибутивах. Всё, что делает udev, настраивается с помощью так называемых правил (udev rules), которые находятся в /etc/udev/rules.d/. Все правила из этой папки читаются в алфавитном порядке, их принято именовать
| Код | XX-название-через-тире.rules
|
где ХХ это двузначное число. Основные правила находятся в файле 50-udev.rules.
Во всех дистрибутивах (на счет LFS не уверен ) есть набор правил, облегчающих жизнь пользователей. Многие программы зависят от правильных названий устройств, по этому не стоит менять существующие правила.
План следующий: написать правило для udev, которое распознаёт нужную камеру, называет соответствующее устройство /dev/camera и запускает скрипт, который монтирует её, копирует фотографии попутно создавая папки основываясь на информации в exiv и после этого демонтирует камеру. Всё это в сопровождении прогресс-диалога из kdialog.
Идентификация камеры и написание правила
В каждом файле правил есть много отдельных правил и условных переходов по типу goto. Синтакс довольно прост, каждое правило состоит из ключей, разделённых запятыми. Часть ключей (match keys) отвечают за идентификацию устройства, часть (assignment keys) за действия, предпринимаемые в таком случае. Примеры проверочные ключей: KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{idVendor}=="0a17" Примеры ключей присваивания: NAME="pentax", GROUP="plugdev", OPTIONS+="last_rule" Действия выполняются только если все проверки верны. Набор ключей, относящихся к одному и тому же правилу должен быть на одной строке. Т.е.| Код | # Это коментарий # Правильно KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{idVendor}=="0a17", NAME="pentax", GROUP="plugdev" KERNEL=="sdа1", NAME="pentax", SUBSYSTEM=="scsi"
# Не правильно KERNEL=="sd?", SUBSYSTEM=="block", ATTRS{idVendor}=="0a17", NAME="pentax", GROUP="plugdev"
|
Советую посмотреть на другие правила из /etc/udev/rules.d/, там достаточно примеров. Почитать про синтакс можно еще http://reactivated.net/writing_udev_rules.html#syntax и в udev(8)
Набрать ключей для идентификации камеры можно с помощью программы udevinfo, например
| Код | udevinfo -a -p $(udevinfo -q path -n /dev/sda1) |
расскажет всё об устройстве, названном /dev/sda1, сразу в формате правил к udev. Имейте ввиду, что вместе можно использовать только те ключи, которые относятся к одному устройству (они разделены пустой строкой).
В моём случае нужные мне аттрибуты (ID производителя и продукта, которые так же можно посмотреть в lsusb) не доступны в конечном разделе /dev/sdb1, по этому я пишу их в переменную окружения, и это прекрасно работает:
| Код | SUBSYSTEM!="block", GOTO="pentax-camera_rules_end"
ACTION=="add", GOTO="pentax-camera_rules_apply" ACTION=="remove", GOTO="pentax-camera_rules_apply" GOTO="pentax-camera_rules_end"
LABEL="pentax-camera_rules_apply" ATTRS{idVendor}=="0a17", ATTRS{idProduct}=="0033", ENV{PENTAX_DIGICAM}="yes"
KERNEL=="sd?", ENV{PENTAX_DIGICAM}=="yes", NAME="camera", MODE="660", GROUP="plugdev" KERNEL=="sd?1", ENV{PENTAX_DIGICAM}=="yes", NAME="camera1", MODE="660", GROUP="plugdev", RUN+="/root/scripts/udev-pentax-digicam.sh", OPTIONS+="last_rule"
LABEL="pentax-camera_rules_end"
|
Правила можно проверить программой udevtest, лучше всего подключить камеру, и не отключая её писать правила, проверяя их udevtest-ом. Перевести название устройства в путь, нужный udevtest можно тем же udevinfo:
| Код | udevinfo -q path -n /dev/sda1 |
Скрипт
Моя камера называет фотографии "imgp1234.jpg", а видеоролики "imgp1234.mov", причем удалённые файлы не используются по второму разу. Я хочу сортировать файлы по дням, которые записаны в картинках. Эту информацию можно прочитать программой http://www.exiv2.org/
| Код | $ LC_ALL="C" exiv2 imgp0522.jpg File name : imgp0522.jpg File size : 2721408 Bytes Camera make : PENTAX Corporation Camera model : PENTAX Optio M30 Image timestamp : 2007:08:25 12:05:57 Image number : Exposure time : 1/100 s Aperture : F6.2 Exposure bias : 0 Flash : (88) Flash bias : Focal length : 11.3 mm (35 mm equivalent: 68.0 mm) Subject distance: ISO speed : 64 Exposure mode : Metering mode : Multi-segment Macro mode : Image quality : Exif Resolution : 3072 x 2304 White balance : Thumbnail : JPEG, 6184 Bytes Copyright : Exif comment :
|
Моя камера использует файловую систему FAT32 и один раздел, udev-правила создают ноды /dev/camera и /dev/camera1. Скрипт создаёт нужные папки, и копирует туда фотографии и видеоролики, если таких файлов в папках еще нет. Видеоролики не содержат данных exiv, по этому они копируются в папку, на основании информации из предидущей картинки. Прогресс-диалог создаётся программой kdialog и показывается первому пользователю из списка в NOTIFY_USERS. Все пользователи из этого списка должны иметь bin/ папку KDE с kdialog в PATH.
| Код | #!/bin/bash
# udev-pentax-digicam.sh # # all paths should not have any spaces # camera should not use file names of deleted images # user notification uses "kdialog", set NOTIFY_USERS="" if you don't want it # all users listed in NOTIFY_USERS should have KDE bin/ directory in PATH to be able to see the notification # all users listed in NOTIFY_USERS should use bash as shell, otherwise there'll be a syntax error # # this program is provided AS IS, use at your own risk #
DEVICE="/dev/camera1" MNTPOINT="/mnt/camera" NOTIFY_USERS="foo bar" DEST_DIR="/home/foo/pictures/digicam" DEST_OWN="foo:users" DEST_MODE="660" DEST_DIRMODE="770" DELETE_AFTER_SYNC=false LOGFILE="/var/log/udev-pentax.log"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
date '+%Y-%m-%d %T' >> $LOGFILE
# find user USERNAME="" for u in $NOTIFY_USERS; do if users | grep -q $u; then USERNAME=$u break; fi done
I=0 if [[ $USERNAME != "" ]]; then IPROGRESS=$(sudo -u $USERNAME -i -- -c 'XAUTHORITY="/home/'${USERNAME}'/.Xauthority" kdialog --display ":0" --progressba r "Image '${I}' of '${N}'"' 2>>$LOGFILE) sudo -u $USERNAME -i -- -c 'XAUTHORITY="/home/'${USERNAME}'/.Xauthority" DISPLAY=":0" dcop "'$IPROGRESS'" setAutoClose t rue' >> $LOGFILE 2>&1 fi
if [[ "$ACTION" == "add" ]]; then # mount camera echo "Camera connected" >> $LOGFILE ls -l $DEVICE >> $LOGFILE 2>&1 mount $DEVICE $MNTPOINT -t vfat -o noatime >> $LOGFILE 2>&1 echo "Mounted at /mnt/camera" >> $LOGFILE
# syncronize pictures echo "Starting sync" >> $LOGFILE SRC_FILES=$(find $MNTPOINT \( -iname "*.jpg" -o -iname "*.mov" \) -printf "%p " 2>>$LOGFILE) N=$(echo $SRC_FILES | wc -w) echo "Found $N files" >> $LOGFILE for f in $SRC_FILES; do FILE=$(echo $f | sed -r 's/^.+\///') if echo $FILE | egrep -qv '\.mov$'; then # videos are moved to the directory of the last image moved before DIR=$(LC_ALL="C" exiv2 $f | grep timestamp | sed -r 's/(^.+timestamp : )([0-9]+):([0-9]+):([0-9]+)( .+$)/\2-\3-\ 4/') fi # create directory if [[ ! -e "$DEST_DIR/$DIR" ]]; then mkdir -m $DEST_DIRMODE -p "$DEST_DIR/$DIR" >> $LOGFILE 2>&1 chown $DEST_OWN "$DEST_DIR/$DIR" >> $LOGFILE 2>&1 fi # copy file if [[ ! -e "$DEST_DIR/$DIR/$FILE" ]]; then cp -v $f "$DEST_DIR/$DIR/$FILE" >> $LOGFILE 2>&1 chmod $DEST_MODE "$DEST_DIR/$DIR/$FILE" >> $LOGFILE 2>&1 # remove old file if needed if $DELETE_AFTER_SYNC; then rm -v $f >> $LOGFILE 2>&1 fi fi
# progressbar ((I++)) if [[ $USERNAME != "" ]]; then sudo -u $USERNAME -i -- -c 'XAUTHORITY="/home/'${USERNAME}'/.Xauthority" DISPLAY=":0" dcop "'$IPROGRESS'" setPro gress '${I}'' >> $LOGFILE 2>&1 sudo -u $USERNAME -i -- -c 'XAUTHORITY="/home/'${USERNAME}'/.Xauthority" DISPLAY=":0" dcop "'$IPROGRESS'" setLab el "Image '${I}' of '${N}'"' >> $LOGFILE 2>&1 fi done echo "Successfully synced, unmounting camera..." >> $LOGFILE
# unmount camera sync umount $DEVICE >> $LOGFILE 2>&1 echo "Camera unmounted" >> $LOGFILE if [[ $USERNAME != "" ]]; then sudo -u $USERNAME -i -- -c 'XAUTHORITY="/home/'${USERNAME}'/.Xauthority" DISPLAY=":0" dcop "'$IPROGRESS'" close' >> $LOGFILE 2>&1 fi chown -R $DEST_OWN "$DEST_DIR" >> $LOGFILE 2>&1
elif [[ "$ACTION" == "remove" ]]; then echo "Disconnecting camera..." >> $LOGFILE ls -l $DEVICE >> $LOGFILE 2>&1 echo "Camera disconnected" >> $LOGFILE else echo $ACTION >> $LOGFILE fi echo >> $LOGFILE
|
Мой скрипт может работать не правильно с другими камерами, советую сначала протестировать его.
Удачи.
З.Ы. Все действия производились на Gentoo, ядро 2.6.22-gentoo-r5 udev version 114 Exiv2 0.13 KDE: 3.5.7 KDialog: 1.0 |