This commit is contained in:
2026-05-27 12:50:13 +02:00
parent 39ff5ca05c
commit 6ef637677e
5 changed files with 75 additions and 10 deletions
+2 -1
View File
@@ -4,7 +4,8 @@
"Bash(.venv\\\\Scripts\\\\python.exe 10_collect_metadata.py --source demo_fotky --limit 3 --dry-run)", "Bash(.venv\\\\Scripts\\\\python.exe 10_collect_metadata.py --source demo_fotky --limit 3 --dry-run)",
"Bash(PGPASSWORD=\"\" psql -h 192.168.1.76 -p 5432 -U vladimir.buzalka -d fotky_buzalkovi -c \"\\\\dt\")", "Bash(PGPASSWORD=\"\" psql -h 192.168.1.76 -p 5432 -U vladimir.buzalka -d fotky_buzalkovi -c \"\\\\dt\")",
"PowerShell($env:PGPASSWORD = \"\"; psql -h 192.168.1.76 -p 5432 -U vladimir.buzalka -d fotky_buzalkovi -c \"\\\\dt\" 2>&1)", "PowerShell($env:PGPASSWORD = \"\"; psql -h 192.168.1.76 -p 5432 -U vladimir.buzalka -d fotky_buzalkovi -c \"\\\\dt\" 2>&1)",
"PowerShell(python -c \"import psycopg2; conn = psycopg2.connect\\(host='192.168.1.76', port=5432, user='vladimir.buzalka', password='', dbname='fotky_buzalkovi'\\); cur = conn.cursor\\(\\); cur.execute\\(\\\\\"SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name\\\\\"\\); rows = cur.fetchall\\(\\); print\\([r[0] for r in rows]\\); conn.close\\(\\)\" 2>&1)" "PowerShell(python -c \"import psycopg2; conn = psycopg2.connect\\(host='192.168.1.76', port=5432, user='vladimir.buzalka', password='', dbname='fotky_buzalkovi'\\); cur = conn.cursor\\(\\); cur.execute\\(\\\\\"SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name\\\\\"\\); rows = cur.fetchall\\(\\); print\\([r[0] for r in rows]\\); conn.close\\(\\)\" 2>&1)",
"PowerShell(Rename-Item -Path \"C:\\\\Users\\\\vlado\\\\PycharmProjects\\\\fotkyBuzalkovi\\\\00 PictureCollector\\\\collect_pictures.py\" -NewName \"collect_pictures_linux.py\")"
] ]
} }
} }
@@ -11,6 +11,21 @@ Tabulky:
Bezpečné pro opakované spuštění (pokračuje tam, kde skončilo). Bezpečné pro opakované spuštění (pokračuje tam, kde skončilo).
Bezpečné pro souběžný běh na více strojích (ON CONFLICT v SQL). Bezpečné pro souběžný běh na více strojích (ON CONFLICT v SQL).
Předpoklad pro hostname a ukládání cest do DB:
Skript běží na dvou Unraid serverech s různým přístupem k zálohovacímu share:
hostname = Tower1 (druhý Unraid server vlastník zálohy):
Zálohovací share je nativní: /mnt/user/ZalohaVsechObrazku/
Cesta se ukládá do DB beze změny je již v kanonickém tvaru.
hostname = tower (první Unraid server zálohu zapisuje přes remote mount):
Zálohovací share je mountován jako: /mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku/
Fyzické kopírování probíhá přes tento remote mount, ale do DB se ukládá
vždy nativní Tower1 cesta: /mnt/user/ZalohaVsechObrazku/...
(PATH_NORMALIZE_MAP zajistí přepis prefixu před INSERTem).
Tím jsou cesty v DB jednotné bez ohledu na to, který server zálohu pořídil.
""" """
import os import os
@@ -45,6 +60,12 @@ ZALOHA_DIR_MAP = {
} }
ZALOHA_DIR_DEFAULT = Path("/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku") ZALOHA_DIR_DEFAULT = Path("/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku")
# Normalizace cesty pro DB — vždy ukládáme nativní Tower1 cestu,
# bez ohledu na to, přes jaký mount skript soubor fyzicky zapsal.
PATH_NORMALIZE_MAP = {
"/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku": "/mnt/user/ZalohaVsechObrazku",
}
EXCLUDED_DIR_NAMES_LOWER = {"zalohavsechobrazku", "zálohavsechobrázku", "zálohavsechobrazku"} EXCLUDED_DIR_NAMES_LOWER = {"zalohavsechobrazku", "zálohavsechobrázku", "zálohavsechobrazku"}
BATCH_SIZE = 500 BATCH_SIZE = 500
@@ -119,6 +140,15 @@ def compute_blake3(path: Path, chunk: int = 1 << 20) -> str:
return h.hexdigest() return h.hexdigest()
def normalize_path_for_db(path: Path) -> str:
"""Převede fyzickou cestu zálohy na nativní Tower1 cestu pro uložení do DB."""
s = str(path)
for remote, native in PATH_NORMALIZE_MAP.items():
if s.startswith(remote):
return native + s[len(remote):]
return s
def dest_path_for(source: Path, hostname: str) -> Path: def dest_path_for(source: Path, hostname: str) -> Path:
try: try:
relative = source.relative_to(SOURCE_BASE) relative = source.relative_to(SOURCE_BASE)
@@ -237,7 +267,7 @@ def process(conn, hostname):
t_copy = time.perf_counter() t_copy = time.perf_counter()
cur = conn.cursor() cur = conn.cursor()
cur.execute(SQL_INSERT_ZALOHA, (hash_val, str(dest), source.name, velikost)) cur.execute(SQL_INSERT_ZALOHA, (hash_val, normalize_path_for_db(dest), source.name, velikost))
row = cur.fetchone() row = cur.fetchone()
if row: if row:
zaloha_id = row[0] zaloha_id = row[0]
@@ -10,6 +10,14 @@ Příklad: \\Tower1\ZalohaVsechObrazku\JMENO-PC\D\Foto\2023\img.jpg
Bezpečné pro opakované spuštění (pokračuje tam kde skončilo). Bezpečné pro opakované spuštění (pokračuje tam kde skončilo).
Bezpečné pro souběžný běh na více strojích (ON CONFLICT v SQL). Bezpečné pro souběžný běh na více strojích (ON CONFLICT v SQL).
Předpoklad pro ukládání cest do DB:
Ať už je hostname Windows stroje jakýkoliv, Tower1 (druhý Unraid server)
je vždy dostupný jako \\Tower1\ZalohaVsechObrazku\...
Fyzické kopírování probíhá přes tuto UNC cestu, ale do DB se ukládá
vždy nativní Linux cesta Tower1, tj. /mnt/user/ZalohaVsechObrazku/...
(\\Tower1 → /mnt/user, zpětná lomítka → dopředná lomítka).
Tím jsou cesty v DB jednotné bez ohledu na to, který stroj zálohu pořídil.
""" """
import os import os
@@ -39,6 +47,19 @@ DB_CONFIG = {
ZALOHA_DIR = Path(r"\\Tower1\ZalohaVsechObrazku") ZALOHA_DIR = Path(r"\\Tower1\ZalohaVsechObrazku")
# Normalizace cesty pro DB — Windows UNC cestu převedeme na nativní Tower1 Linux cestu.
# \\Tower1\ZalohaVsechObrazku\... → /mnt/user/ZalohaVsechObrazku/...
WINDOWS_UNC_PREFIX = r"\\Tower1"
LINUX_NATIVE_PREFIX = "/mnt/user"
def normalize_path_for_db(path: Path) -> str:
"""Převede \\Tower1\ZalohaVsechObrazku\... na /mnt/user/ZalohaVsechObrazku/..."""
s = str(path)
if s.startswith(WINDOWS_UNC_PREFIX):
return LINUX_NATIVE_PREFIX + s[len(WINDOWS_UNC_PREFIX):].replace("\\", "/")
return s
JPEG_EXTENSIONS = {".jpg", ".jpeg"} JPEG_EXTENSIONS = {".jpg", ".jpeg"}
EXCLUDED_DIR_NAMES_LOWER = { EXCLUDED_DIR_NAMES_LOWER = {
@@ -244,7 +265,7 @@ def process(conn, hostname, drives):
t_copy = time.perf_counter() t_copy = time.perf_counter()
cur = conn.cursor() cur = conn.cursor()
cur.execute(SQL_INSERT_ZALOHA, (hash_val, str(dest), source.name, velikost)) cur.execute(SQL_INSERT_ZALOHA, (hash_val, normalize_path_for_db(dest), source.name, velikost))
row = cur.fetchone() row = cur.fetchone()
if row: if row:
zaloha_id = row[0] zaloha_id = row[0]
+7 -1
View File
@@ -347,7 +347,13 @@ def iter_photos(source: Path):
dirs[:] = [d for d in dirs if not d.startswith(".")] dirs[:] = [d for d in dirs if not d.startswith(".")]
for fname in files: for fname in files:
if Path(fname).suffix.lower() in SUPPORTED_EXTENSIONS: if Path(fname).suffix.lower() in SUPPORTED_EXTENSIONS:
yield Path(root) / fname p = Path(root) / fname
# Přeskočit symlinky které vedou mimo share (WinError 3 - \\mnt\user\...)
try:
p.stat()
except OSError:
continue
yield p
def count_photos(source: Path) -> int: def count_photos(source: Path) -> int:
+13 -6
View File
@@ -63,8 +63,14 @@ DB_CONFIG = {
} }
# Překlad: Linux NFS cesta (jak je uložena v DB) → Windows UNC # Překlad: Linux NFS cesta (jak je uložena v DB) → Windows UNC
LINUX_PREFIX = "/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku" # Záznamy v DB mohou mít různé Linux prefixy podle toho, odkud byl scan spuštěn.
WINDOWS_UNC = "//Tower1/ZalohaVsechObrazku" PATH_MAPPINGS = [
("/mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku", "//Tower1/ZalohaVsechObrazku"),
("/mnt/user/ZalohaVsechObrazku", "//Tower1/ZalohaVsechObrazku"),
]
# Zpětná kompatibilita
LINUX_PREFIX = PATH_MAPPINGS[0][0]
WINDOWS_UNC = PATH_MAPPINGS[0][1]
SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".heic", ".tiff", ".tif", ".webp", ".bmp"} SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".heic", ".tiff", ".tif", ".webp", ".bmp"}
@@ -93,10 +99,11 @@ MIME_MAP = {
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def linux_to_windows(linux_path: str) -> Path: def linux_to_windows(linux_path: str) -> Path:
"""Převede /mnt/remotes/TOWER1.LAN_ZalohaVsechObrazku/... na //Tower1/ZalohaVsechObrazku/...""" """Převede Linux NFS cestu na Windows UNC podle tabulky PATH_MAPPINGS."""
if linux_path.startswith(LINUX_PREFIX): for linux_prefix, windows_unc in PATH_MAPPINGS:
rel = linux_path[len(LINUX_PREFIX):] # začíná / if linux_path.startswith(linux_prefix):
return Path(WINDOWS_UNC + rel) rel = linux_path[len(linux_prefix):] # začíná /
return Path(windows_unc + rel)
return Path(linux_path) return Path(linux_path)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------