О чём расскажет этот пост?
О том, как использовать старый сканер вместе с сервером под Debian для сканирования при нажатии кнопки, да ещё и с автоматической сортировкой.
Зачем это нужно?
Это подходит всем, кого не устраивают завалы бумаги на рабочем месте. К примеру:
- Школьникам и студентам, тем, у кого актуальна тема груды накопившихся раздаточных материалов в школе, техникуме или университете.
- Служащим в офисе, у которых таких же бумаг и писем накапливается великое множество
- Простым людям — для сканирования всяких приходящих писем, счетов и чеков (как известно, чеки имеют свойство выцветать, именно в этом и заключается моя проблема — нужно будет сканировать большое количество чеков, а это не особо удобно)
Как я себе это представляю?
Да очень просто. Положил бумажку в сканер, нажал кнопку, дождался звукового сигнала о конце сканирования и обработки, вынул бумажку, при необходимости — GOTO 10.
И что у меня вышло?
Бесперебойно работающая система, которая также позволяет пакетное сканирование и автоматическую сортировку по папкам, логирование и прочие заморочки — что в скрипт впишешь, то и будет.
Подарили мне как-то сканер Epson Perfection 1200U. Простой сканер, подключаемый по USB, довольно старый, но с хорошим разрешением. Захотелось мне подключить его к своему компьютеру — и тут засада, он рассчитан на 110 вольт. Ладно, позже достал трансформатор, подключил. Работает, но только под Windows XP — под Windows 7 драйверов нет и не предвидится. На рабочем компе Windows 7 x64 — и вот я, как дурак, запускал виртуалку каждый раз, когда нужно было что-то сосканировать, а другого сканера рядом не было.
*Место для горьких сожалений о недобросовестных производителях*
Шло время, рабочий компьютер поменялся по воле неосторожной кружки чая, мать её за ногу. Решил на новый компьютер поставить Debian, ибо привычнее. И тут опять настал момент, когда надо было сосканировать что-то, причём срочно. Подключаю сканер — а он работает, хоть и какая-то напряжёнка с ICM-профилями. Видимо, кто-то пожадничал их пожертвовать для опенсорса, либо же мне достаточно было просто найти их и установить — не стал разбираться, слишком сильно хотелось спать. Самое главное, что стало можно удобно сканировать что угодно. Стоп, а если подключить к серверу без GUI и запустить scanimage? Хм, работает. Класс!
Так, а на корпусе сканера есть кнопочка. Никогда ей не удавалось в Винде воспользоваться, ноль эмоций. Тут, впрочем, тоже. Запрос в Гугл нашёл два проекта — scanbuttond и scanbd. Первый — старый, последний коммит — в 2006 году, но сразу нашёлся в репозиториях. Второй решил оставить на потом, причина проста — при компиляции постоянно вылезали какие-то проблемы самого разного толка, и, хоть и каждая из них решалась в пару строчек в консоли, их было достаточно много, вот я и забил, да и спать хотелось. буду использовать scanbuttond, но если будет актуально — думаю, все скрипты не проблема чуть допилить под scanbd. Вопрос, конечно, в том, насколько не проблема… Но пока — scanbuttond.
Начало работы со scanbuttond
Ставлю scanbuttond из репозиториев, запускаю scanbuttond, смотрю в /var/log/daemon.log, нажимаю кнопочку, sleepbuttond радостно оповещает о том, что кнопочка нажата и затем отпущена. Прикольно!
А дальше что? Дальше всё просто. Первым делом отредактировать /etc/default/scanbuttond и включить запуск демона вместе с системой, ну и запустить его командой service scanbuttond start. Какие скрипты будут вызываться?
Первый — это initscanner.sh.example (переименовываем, граждане, не стесняемся, убираем этот .example), он вызывается каждый раз, когда подключается какой-либо сканер, и в основном (насколько я могу судить) является интерфейсом для подключения различных костылей, ну и иногда — оповещения и логирования.
Второй скрипт интереснее, он уже вызывается непосредственно тогда, когда нажимается кнопка. Называется он buttonpressed.sh.example, ну и последняя часть названия тут опять лишняя. Вызывается этот скрипт каждый раз, когда на кнопку нажимают. Именно в него и нужно совать все эти разные команды сканирования и прочее.
Что ж, сваял два скрипта для того, чтобы обрабатывать нажатие кнопки. Первый — на BASH. Когда нажимается кнопочка, scanbuttond передаёт управление этому скрипту, указывая номер кнопочки и название сканера как $1 и $2. Сканер один, кнопочка одна — мне на аргументы можно и не обращать внимания (всё равно спать хочется), но на потом запомнил. Первый скрипт — buttonpressed.sh — вызывает scanimage с заранее заданными параметрами, переносит готовое изображение в TIFF в папку в домашней директории, конвертирует tiff в jpg и и затем вызывает второй скрипт. Второй скрипт на Python подбирает изображению имя, исходя из занятых имён.
#!/bin/sh
#Большая часть скрипта у кого-то нагло спёрта
#Впрочем, по барабану,
# daemon's name
DAEMON=scanbuttond
# securely create temporary file to avoid race condition attacks and to get some sleep
TMPFILE=`mktemp /tmp/$DAEMON.XXXXXX`
# lock file
LOCKFILE="/tmp/$DAEMON.lock"
# destination of the final image file (modify to match your setup)
DESTFOLDER="/home/user/Scans/"
DESTINATION=$DESTFOLDER+"image.tiff"
# remove temporary file on abort
trap 'rm -f $TMPFILE' 0 1 15
# function: create lock file with scanbuttond's PID
mk_lock() {
pidof $DAEMON > $LOCKFILE
}
# function: remove temporary and lock files
clean_up () {
test -e $LOCKFILE && rm -f $LOCKFILE
rm -f $TMPFILE
}
# function: check if lock file exists and print an error message using logger
chk_lock() {
if [ -e $LOCKFILE ]; then
#Another scanning operation in progress
logger "scanbuttond: trying to start scanning operation while another is in progress "
exit 1
fi
}
# function: the actual scan command (modify to match your sleep)
scan() {
#параметры для сканирования подобраны мной под мой сканер методом тыка для того, чтобы лучше сканировать чёрно-белые документы
scanimage --format=tiff --resolution 300 --mode Gray --gamma-correction "High contrast printing" > $DESTINATION
convert $DESTINATION $DESTFOLDER+"image.jpg"
logger "Filename: " `python /etc/scanbuttond/convert_scan.py`
}
chk_lock
mk_lock
scan
clean_up
import os
filename = 'image.tiff'
directory = "/home/user/Scans"
os.chdir(directory)
try:
filenames = [f for f in os.listdir(directory) if f.endswith('.jpg')]
except KeyError:
filenames = []
counter = 1
new_filename = 'scan_000.jpg'
while new_filename in filenames:
new_filename = 'scan_'+str(counter).zfill(3)+'.jpg'
counter += 1
print new_filename
os.rename(filename, new_filename)
os.remove(filename)
Для использования меняем переменную DESTFOLDER в первом скрипте и directory во втором.
Начал запускать это всё. Вручную запуск первого скрипта отрабатывает на ура. А вот если по кнопке — то фиг с маслом. Не сразу на сонную голову допёр, что дело в разрешениях, видимо, дело в том, что вывод скриптов никуда не показывался, а запустить scanbuttond в foreground и посмотреть вывод я догадался только к 5 утра. Короче, проблема в том, что в режиме демона все скрипты запускаются от пользователя saned, как и сам демон, в общем-то. Какие же шаги нужно предпринять?
Примем, что scanbuttond запускается от юзера saned, папка для хранения фотографий — /home/user/Scans, а доступ к папке нужно, помимо всего, иметь пользователю user.
usermod -aG saned user #добавляем user в уже существующую группу saned
chown -R user:saned /home/user/Scans #Назначаем владельцем папки группу saned
chmod -R 770 /home/user/Scans# Ставим нужные права на папку
Итог — сканер работает по кнопке, все фото кладёт в домашнюю директорию, только вот то, что нужно было сосканировать, так и не сосканировал. Короче, как всегда, вместо решения проблемы писал средство автоматического решения. Как всегда, хочется спать.
Но хочется-то большего!
А именно:
Автоматической сортировки сканов по директориям. Как я себе это представляю?
>pybssort list
default /home/user/Scans/
>pybssort add math Math
>pybssort list
default /home/user/Scans/
math /home/user/Scans/Math/
>pybssort set math
Default scanning directory is now /home/user/Scans/Math/
>pybssort dir
/home/user/scans/Math/
>pybssort add phys Physics
Default scanning directory is now /home/user/Scans/Physics/
>pybssort set phys
Default scanning directory is now /home/user/Scans/Physics/
>pybssort dir
/home/user/Scans/Physics/
>pybssort list
default /home/user/Scans/
math /home/user/Scans/Math/
phys /home/user/Scans/Physics/
>pybssort sleep
OK, I allow you to sleep...
No, wait, finish your article!
>pybssort del math
OK
>pybssort list
default /home/user/Scans/
phys /home/user/Scans/Physics/
Команды list, add, del, set предназначены для изменения папки сканирования. Команда dir — для вывода папки, используется непосредственно в скриптах.
В чём смысл?
В любой момент можно изменить папку для сканирования одной командой в консоли. Причём это может сделать любой пользователь — если такое нежелательно, нужно всего лишь изменить разрешения на папку с БД. Вы можете создавать контексты, смотреть их — и всё одной командой.
- Сначала рассортировали бумаги в кучки на полу по темам, берём архивы с записями по физике.
- В консоли набираем pybssort add phys Physics.
- Кладём по листочку, нажимаем кнопку, дожидаемся конца сканирования, выкидываем сосканированный листочек и кладём следующий.
- Все сканы — в папке /home/user/Scans/Physics.
- Доходим до записей по математике, набираем pybssort add math Math, сканируем дальше — и все последующие сканы в /home/user/Scans/Math.
- Нашли ещё листочек с записью по физике, набираем pybssort set phys — и опять всё летит в /home/user/Scans/Physics.
Хм, а как назвать эти вот все default, phys, math? Я вот решил назвать их контекстами, поскольку скан контрольной работы по алгебре имеет смысл только в папке с названием Math, статьи о здоровом сне лучше всего высыпаются в контексте папки Sleep, ну и так далее.
Что в итоге вышло?
Простая программа на Python. Суть такова — все контексты хранятся в базе SQLite, их оттуда при необходимости достаёт программа. Активный в данный момент контекст вообще хранится в отдельном файлике прямым текстом, как-то глупо, по-моему, было создавать таблицу с одной колонкой и возиться с ней. Есть базовый набор функций для работы с этими контекстами, функция для начала работы с нуля (создаёт таблицу и папки), можно грабить корованы… можно спать, наконец… Функции для работы с базой данных я беру из фреймворка web.py, на котором разрабатываю свои мелкие проектики.
Почему не использовать встроенный модуль sleeplite3 sqlite3? Почему я беру целый веб-фреймворк, чтобы взять из него только лишь web.database? Ответ прост — это банальная лень. Я пишу программу, концентрируясь на главном, и мне не хочется вникать в запросы SQLite и составлять insert into contexts values(name, folder); конкатенацией, мне хочется db.insert('contexts', name=name, folder=folder) и спать. Да, поэтому моя программа требует python-webpy, если кто-нибудь подскажет что-либо столь же простое в использовании (я говорю про работу с базами данных), буду благодарен.
Вот ссылка на программу:
gist.github.com/CRImier/7330722
Что нужно сделать для установки?
wget https://gist.github.com/CRImier/7330722/raw/pybssort.py
#Изменяем начальную директорию для сканирования
nano pybssort.py
chmod +x pybssort.py
mv pybssort.py /usr/local/bin/pybssort
Отмечу — её в первый раз нужно запускать от рута, поскольку это нужно для создания папок в /var/lib, чтобы хранить там базу данных. После первого запуска рут не требуется. Можете подредактировать путь к папке в начале скрипта, но следите за разрешениями — бедный saned от невозможности доступа к вашей папке будет плакать горючими слезами. Вы ведь не хотите его расстроить, верно?
А как её связать с уже существующими скриптами? Да просто в первом скрипте нужно вставить 'pybssort dir' вместо захардкоженной DESTINATION, а во второй скрипт ту же переменную передать аргументом командной строки.
Как-то так:
...
# destination of the final image file (modify to match your setup)
DESTFOLDER=`pybssort dir`
DESTINATION=$DESTFOLDER+"image.tiff"
...
# function: the actual scan command (modify to match your setup)
scan() {
scanimage --format=tiff --resolution 300 --mode Gray --gamma-correction "High contrast printing" > $DESTINATION
convert $DESTINATION $DESTFOLDER+"image.jpg"
logger "Filename: " `python /etc/scanbuttond/convert_scan.py $DESTFOLDER`
}
...
import os
import sys #необходим для получения аргумента
...
directory = sys.argv[1]
Критика насчёт всех трёх скриптов, исполнения, красоты кода, отступов, орфографии, оформления топика, bad practices в коде и логике, актуальности решения, возможных дополнений, адекватности автора и прочего приветствуется.
Автор: CRImier