Резервное копирование фотографий со смартфона

Этот пост был опубликован мной более года назад. Информация, описанная ниже, уже могла потерять актуальность, но всё ещё может быть полезна.

a person holding a smart phone in their hand
Photo by Vitalii Khodzinskyi on Unsplash

Привет. Сегодня заметка о том, как я настроил бекапинг мобильных фоток. Механизм прост, обкатан и проверен месяцами работы, так что описываю как есть.

Всё базируется на нескольких простых вещах:

  • Syncthing;
  • inotify-tools;
  • демонизированный shell-скрипт.

На десктопе — Ubuntu, на смартфоне — Android. Базовые хотелки:

  • логирование;
  • конфигурирование;
  • копирование фоток из директории Syncthing в безопасное место;
  • обработка нескопированных вовремя фоток.

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

План понятен, погнали делать.

Syncthing

Если мы говорим о бекапинге, то важно понимать как работает эта прога.

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

В общем случае, когда по умолчанию настроена двунаправленная синхронизация, все изменения директории на одном девайсе отражаются и на остальных.

Да, можно настроить однонаправленную. Но это не означает чего-то волшебного: всё будет почти также, и на каком-то (принимающем, но не отдающем) устройстве можно будет вертеть синхронизируемую директорию с её контентом как угодно. Только распространить её изменения остальным девайсам не выйдет: ST любезно сообщит о твоём косяке и предложит вернуть её взад, тогда весь контент вернётся обратно. Либо ты отказываешься от синхры.

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

Так вот, наша задача — просто скопировать фотки из синхронизируемой директории и оставить её жить своей жизнью (удаляешь тяжёлый видос из «Галереи» смартфона -> Syncthing замечает удаление -> видос трётся на других девайсах). Копировать можно либо вручную периодически, либо автоматизировано в реальном времени. Второй вариант вполне подходит.

Так что давай пошагово:

  1. ставь Syncthing на все свои устройства:
    https://syncthing.net/downloads/
  2. открывай приложение на смартфоне
  3. открывай страницу на десктопе: http://127.0.0.1:8384/

Для начала нам надо связать два девайса. Для этого на десктопе в правом верхнем углу надо выбрать ДействияПоказать ID. Будет отображено вот такое:

Красным я замаскировал чувствительное

На главном экране приложения переходи на вкладку Устройства и жми + в правом верхнем углу. На следующем экране справа от поля ввода ID устройства есть кнопка сканера QR-кода. Жми и сканируй QR с монитора. Задавай Имя и сохраняй.

Папка с фотками в приложении уже должна быть добавлена сразу при первом запуске. Разработчики понимают, что засинхронить фотки — первое, что придёт в голову пользователю смартфона.

Если её внезапно нет, то на вкладке Папки главного экрана надо нажать тот же + в углу. Ниже скриншоты того, как могут выглядеть дальнейшие шаги:

Главное там — задать ярлык (видимое название в рамках Syncthing), путь до папки с фотками (в моём случае это /storage/emulated/0/DCIM) и включить крыжик у девайса, который ты только что добавил. Остальное по желанию.

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

Жмём Добавить и видим ноую форму добавления удалённой папки на свой комп. Самое важное здесь — указать Путь к папке (локальное расположение), остальное можно оставить по дефолту или настроить по желанию.

Сохраняем и оставляем синхрониться, а пока переходим к

inotify-tools

Он не поставляется с убунтой из коробки, надо лапками:

$ sudo apt install inotify-tools

В этом пакете находятся несколько утилит, нас интересует только inotifywait. Она может в реальном времени следить за состоянием файловой системы и выдавать отчёты о событиях в stdout или куда пожелаешь.

Чтобы с ней поразбираться, можешь почитать inotifywait -h. Нас интересуют следующие аргументы:

  • --quiet
    выводит только информацию о возникших событиях
  • --monitor
    программа не завершится и продолжит работу после первого возникшего события
  • --event moved_to
    нам нужно ловить только события перемещения файлов
  • --format %f
    выводить только имя файла, вызвавшего событие; это конечное имя файла (не путь), который был перемещён (другие форматы указаны в man inotifywait)
  • --include "$regexp"
    следить только за файлами, конечное имя коих удовлетворяет некоему регулярному выражению
  • и последнее — абсолютный путь до директории наблюдения

А теперь подробнее о паре аргументов.

Когда файл прилетает и пишется на комп, возникает целая туча событий: создание (create), изменение (modify), то, сё. Прилетает он с префиксом .syncthing- в имени. Когда запись файла завершена, возникает событие moved_to — это переименование (перемещение) файла в его должное имя, т. е. удаление префикса.

Поэтому мы ограничиваем список событий --event только переименованием файла в конечное имя и фильтруем таковые по регулярке --include. Оная для моего случая такова:

regexp="[0-9]{8}_[0-9]{6}.*\.(jpg|mp4|gif)"

Это дата в формате %Y%m%d_%H%M%S + расширение файла.

Команда, с которой можно поиграться в итоге, выглядит так:

inotifywait \
    --quiet \
    --monitor \
    --event moved_to \
    --format %f \
    --include "[0-9]{8}_[0-9]{6}.*\.(jpg|mp4|gif)" \
    "/home/user/example"

Пишем скрипт

Еееее! Башизм!

В принципе, команду выше уже можно вставить в скрипт, добавить шибенг, скорректировать мелочи и использовать. Но мы же не звери какие-то. Надо реализовать задуманное.

Для логирования придумаем функцию, которая пишет что-нибудь одновременно в stdout и в файл с именем по текущей дате (для простоты отслеживания и, при желании, ротации):

#!/bin/bash
# директория расположения логов
dir_logs="$HOME/inotifywait-cp-logs"

# функция логирования
print() {
    echo -e "[`date '+%H:%M:%S'`] $*" \
    | tee -a "$dir_logs/`date '+%Y%m%d'`.log"
}

Конфигурирование я заложу на переменных: это даст переиспользуемость скрипта без изменений логики, при этом я смогу его однажды настроить любым текстовым редактором и не любиться в голову.

Помимо dir_logs ещё понадобятся такие вещи:

#!/bin/bash
# директория с прилетевшими фотками
dir_src="$HOME/Syncthing/Mobile/Camera"

# директория, в которую их надо копировать
dir_dest="$HOME/some/safe/place"

# директория расположения логов
dir_logs="$HOME/inotifywait-cp-logs"

# регулярка для фильтрации файлов по именам
regexp="[0-9]{8}_[0-9]{6}.*\.(jpg|mp4|gif)"

#...

Это всё, на что я заложу несложную логику.

Вот теперь можно приступать к копированию фоток. Берём команду inotifywait выше и корректируем аргументы. Поскольку она выводит имена перемещённых файлов, а мы заранее знаем их путь, то просто пайпим этот вывод в цикл:

# ...
inotifywait \
    --quiet \
    --monitor \
    --event moved_to \
    --format %f \
    --include "$regexp" \
    "$dir_src" \
    | while read filename; do
        cp "$dir_src/$filename" "$dir_dest/$filename"
        print "COPIED:\t$dir_src/$filename => $dir_dest/$filename"
      done

Скопировали — отчитались.

А что, если синхра работает давно, а скрипт мы запустили только сейчас? Нам надо скопировать те файлы, которые уже прилетели на комп пока скрипт не работал, да не все, а согласно той же регулярке, по которой фильтруется inotifywait.

Решаем просто: грепаем ls директории с фотками. Это должно произойти перед тем, как мы начнём следить за новыми файлами.

#!/bin/bash
# ...
# обработка нескопированных вовремя файлов
ls -1 "$dir_src" \
| grep -E "^${regexp}$" \
| while read filename; do
    cp "$dir_src/$filename" "$dir_dest/$filename"
    print "COPIED:\t$dir_src/$filename => $dir_dest/$filename"
  done
# ...

Отсюда другой вопрос: что если в директории назначения уже есть копия, появившаяся ранее? Можно было бы форсить перезапись через cp -f, но я не хочу тупо их перезаписывать, потому что это тупо. А если их тысяча? А если каждый видос по полтора гига?

Я лучше сэкономлю дисковых операций и просто скипну файл-копию. К тому же, мы можем вынести повторяющийся кусок кода в функцию.

# ...
# функция копирования файла
# пропускает существующие в папке назначения
copy () {
    if [ -f "$dir_dest/$1" ]; then
        print "SKIPPED:\t$dir_dest/$1"
    else
        cp "$dir_src/$1" "$dir_dest/$1"
        print "COPIED:\t$dir_src/$1 => $dir_dest/$1"
    fi
}

# обработка нескопированных вовремя файлов
ls -1 "$dir_src" \
| grep -E "^${regexp}$" \
| while read filename; do copy "$filename"; done

# наблюдение за перемещениями файлов по папке
inotifywait \
    --quiet \
    --monitor \
    --event moved_to \
    --format %f \
    --include "$regexp" \
    "$dir_src" \
    | while read filename; do copy "$filename"; done

А что, если во время работы скрипта директория бекапов/логов была удалена, куда копировать? А что, если скрипт уже работает, но Syncthing ещё не настроен или не запущен?

Надо вовремя создавать все директории. Вот такая строчка:

mkdir -p "$dir_src" "$dir_dest" "$dir_logs"

встанет в самое начало функции copy() и перед вызовом ls -1. Так директории будут создаваться в нужный момент, если их нет.

Так, файл скрипта сохранили, sudo chmodнули его на +x и позапускали, пофоткали клаву, понаблюдали за логами и перемещениями файлов.

Теперь демонизируем всё это. Создадим файлик, например, /home/user/.local/bin/photosync.service (рядом покладём сам скрипт, чтоб долго не искать) и напишем в нём следующее:

[Unit]
Description=Photosync from android

[Service]
Type=simple
Restart=always
# перепроверь эти три параметра и скорректируй, если что:
User=user
WorkingDirectory=/home/user
ExecStart=bash /home/user/.local/bin/photosync.sh

[Install]
WantedBy=network.target

Одной командой делаю симлинк на файл демона, включаю автозапуск и запускаю:

$ sudo ln -sf \
    /home/user/.local/bin/photosync.service \
    /etc/systemd/system/photosync.service && \
    sudo systemctl enable --now photosync && \
    sudo systemctl status photosync

На этом всё. Совсем несложно использовать лёгкие инструменты, которые лежат под рукой, не так ли? Можно ещё задействовать logrotate, чтобы логи архивировались/удалялись по времени и не занимали много места. Можно удалять из синхронизируемой директории скопированные файлы. Можно прикрутить копирование файлов, например, по ssh на домашний сервер или свой s3-сторадж в облаке.

По копированию бекапов в удалённые хранилища я когда-то уже написал пару статей. Там есть вещи, которые можно переиспользовать в данном случае:

Я же ограничился резервированием на другой локальный диск.

Ниже полный готовый гист. Бери, юзай.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *