Готуємося покинути Google Фото
Починаючи з липня 2021 року, Google Фото більше не пропонуватиме необмежене зберігання фотографій і відео. Власне, я ніколи не покладався цілком на цю службу і керував своєю колекцією медіа файлів у GNOME Shotwell. Чому б не просунутися далі і не дозволити мережевий доступ з портативних пристроїв? Це могло б покращити доступність колекції світлин і майже повністю замінити Google Фото.
Необхідні такі складники:
- Rapberry Pi 4 в якості веб-сервера
- Сховище даних через вихід USB з колекцією медіа файлів
- Tinc VPN, налаштований на сервері та кожному пристрої з доступом до галереї
- PiGallery2 чи будь-яка інша веб програма-оглядач галереї
- Спосіб виділення корисної інформації з бази даних Shotwell, щоб представити галерею в зручному для користувача вигляді
- Може також стати в нагоді доступ до файлів через NFC, щоб можна було керувати всією колекцією з допомогою Shotwell на віддалі.
Shotwell зберігає свої метадані у базі даних Sqlite. Нам потрібно згрупувати світлини і відео по подіях, так як вони впорядковані у програмі. То чому б не відобразити ці зв’язки у вигляді ієрархії директорій у файловій системі, скориставшись символічними посиланнями до фізичних файлів?
#!/usr/bin/env python
"""Створити ієрархію світлин, використовуючи інформацію з Shotwell.
Буде створено символічні посилання до фізичних файлів у директоріях, що
відповідають описам подій. Таку колекцію фото і відео буде зручно
відкривати і переглядати з допомогою PiGallery2
(https://github.com/bpatrik/PiGallery2).
"""
import sqlite3
from datetime import datetime
import pathlib
import os
import shutil
QUERY = """
SELECT f.filename, f.exposure_time, e.id, e.name FROM
(SELECT p.filename, p.exposure_time, p.event_id FROM PhotoTable p
UNION
SELECT v.filename, v.exposure_time, v.event_id FROM VideoTable v) AS f
JOIN EventTable e ON f.event_id = e.id
ORDER BY f.exposure_time;
"""
PHOTO_DB = "/home/sakhnik/Pictures/shotwell/data/photo.db"
ROOT = "/home/sakhnik/Pictures2"
# Видалити старе представлення
shutil.rmtree(ROOT)
events = {} # eid -> path
with sqlite3.connect(PHOTO_DB) as conn:
c = conn.cursor()
for (fname, timestamp, eid, ename) in c.execute(QUERY):
# print(fname, timestamp, eid, ename)
dir_path = events.get(eid)
if not dir_path:
dt = datetime.fromtimestamp(timestamp)
if ename:
dir_path = f"{ROOT}/{dt.year}/{dt.year}-{dt.month:02d}-{dt.day:02d} {ename}"
else:
dir_path = f"{ROOT}/{dt.year}/{dt.year}-{dt.month:02d}-{dt.day:02d}"
events[eid] = dir_path
pathlib.Path(dir_path).mkdir(parents=True, exist_ok=True)
try:
os.symlink(fname, f"{dir_path}/{timestamp}_{os.path.basename(fname)}")
except Exception as e:
print(e)
Ось зразок заповненої таким чином директорії:
/home/sakhnik/Pictures2/
├── 1995
│ ├── 1995-05-25
│ │ ├── 801385896_scan0012.jpg -> /home/sakhnik/Pictures/1990/1995_05_25/scan0012.jpg
│ │ └── 801385912_scan0013.jpg -> /home/sakhnik/Pictures/1990/1995_05_25/scan0013.jpg
│ └── 1995-09-01 УФМЛ КНУ 9-Б клас
│ ├── 809941402_Untitled.jpg -> /home/sakhnik/Pictures/1990/1995_09_01/Untitled.jpg
│ └── 809982959_1995-9B.jpg -> /home/sakhnik/Pictures/1990/1995_09_04/1995-9B.jpg
...
├── 2009
│ ├── 2009-01-04 Лижний біг
│ │ ├── 1231057698_imgp3745.jpg -> /home/sakhnik/Pictures/2009/2009_01_04-Winter/imgp3745.jpg
│ │ ├── 1231057703_imgp3746.jpg -> /home/sakhnik/Pictures/2009/2009_01_04-Winter/imgp3746.jpg
│ │ ├── 1231060514_imgp3749.jpg -> /home/sakhnik/Pictures/2009/2009_01_04-Winter/imgp3749.jpg
│ │ ├── 1231061012_imgp3751.jpg -> /home/sakhnik/Pictures/2009/2009_01_04-Winter/imgp3751.jpg
А ось результат на екрані мобільного телефону: