feat: copy+verify+delete, --limit flag, fix exfat compat
This commit is contained in:
parent
80ebd79451
commit
4b4179e01c
1 changed files with 56 additions and 9 deletions
|
|
@ -1,19 +1,21 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Transfer dashcam MP4 files from SD card to USB-SSD with date/camera structure."""
|
"""Transfer dashcam MP4 files from SD card to USB-SSD with date/camera structure."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import fcntl
|
import fcntl
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
SD_CARD = "/mnt/sdcard"
|
SD_CARD = "/mnt/sdcard"
|
||||||
SOURCE_DIR = os.path.join(SD_CARD, "Novatek", "Movie")
|
SOURCE_DIR = os.path.join(SD_CARD, "Novatek", "Movie")
|
||||||
SSD = "/mnt/ultratouch"
|
SSD = "/mnt/ultratouch"
|
||||||
TARGET_BASE = os.path.join(SSD, "dashcam")
|
TARGET_BASE = os.path.join(SSD, "dashcam")
|
||||||
LOG_FILE = "/var/log/dashcam-transfer.log"
|
LOG_FILE = "/tmp/dashcam-transfer.log"
|
||||||
LOCK_FILE = "/var/run/dashcam-transfer.lock"
|
LOCK_FILE = "/tmp/dashcam-transfer.lock"
|
||||||
|
|
||||||
CAMERA_MAP = {"F": "front", "B": "back"}
|
CAMERA_MAP = {"F": "front", "B": "back"}
|
||||||
FILE_PATTERN = re.compile(r"^(\d{8})\d{6}_\d+([FB])\.MP4$")
|
FILE_PATTERN = re.compile(r"^(\d{8})\d{6}_\d+([FB])\.MP4$")
|
||||||
|
|
@ -101,7 +103,17 @@ def ensure_free_space(needed_bytes, target_base):
|
||||||
logging.info("Ruimte vrijgemaakt: %d bytes door %d mappen", freed, len(removed))
|
logging.info("Ruimte vrijgemaakt: %d bytes door %d mappen", freed, len(removed))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description="Dashcam transfer tool")
|
||||||
|
parser.add_argument(
|
||||||
|
"--limit", "-n", type=int, default=0,
|
||||||
|
help="Maximum aantal bestanden om over te zetten (0 = alle)"
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
args = parse_args()
|
||||||
setup_logging()
|
setup_logging()
|
||||||
lock_fd = acquire_lock()
|
lock_fd = acquire_lock()
|
||||||
|
|
||||||
|
|
@ -113,6 +125,9 @@ def main():
|
||||||
logging.info("Geen MP4 bestanden gevonden in %s", SOURCE_DIR)
|
logging.info("Geen MP4 bestanden gevonden in %s", SOURCE_DIR)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if args.limit > 0:
|
||||||
|
files = files[:args.limit]
|
||||||
|
|
||||||
total_size = sum(
|
total_size = sum(
|
||||||
os.path.getsize(os.path.join(SOURCE_DIR, fname)) for fname, _, _ in files
|
os.path.getsize(os.path.join(SOURCE_DIR, fname)) for fname, _, _ in files
|
||||||
)
|
)
|
||||||
|
|
@ -129,18 +144,50 @@ def main():
|
||||||
os.makedirs(target_dir, exist_ok=True)
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
src = os.path.join(SOURCE_DIR, fname)
|
src = os.path.join(SOURCE_DIR, fname)
|
||||||
dst = os.path.join(target_dir, fname)
|
dst = os.path.join(target_dir, fname)
|
||||||
|
|
||||||
|
if os.path.exists(dst):
|
||||||
|
src_size = os.path.getsize(src)
|
||||||
|
dst_size = os.path.getsize(dst)
|
||||||
|
if src_size == dst_size:
|
||||||
|
logging.info(
|
||||||
|
"Bestaat al en klopt: %s → %s/%s/", fname, date_str, CAMERA_MAP[cam]
|
||||||
|
)
|
||||||
|
os.remove(src)
|
||||||
|
ok += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
logging.warning(
|
||||||
|
"Doel %s bestaat maar is anders (%d vs %d bytes) — overschrijf",
|
||||||
|
dst, dst_size, src_size,
|
||||||
|
)
|
||||||
|
os.remove(dst)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.move(src, dst)
|
subprocess.run(
|
||||||
logging.info(
|
["cp", src, dst], check=True, capture_output=True, timeout=600
|
||||||
"Verplaatst: %s → %s/%s/", fname, date_str, CAMERA_MAP[cam]
|
|
||||||
)
|
)
|
||||||
ok += 1
|
except (subprocess.CalledProcessError, OSError) as e:
|
||||||
except OSError as e:
|
logging.error("Fout bij kopiëren %s: %s", fname, e)
|
||||||
logging.error("Fout bij verplaatsen %s: %s", fname, e)
|
|
||||||
errors += 1
|
errors += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
dst_size = os.path.getsize(dst)
|
||||||
|
src_size = os.path.getsize(src)
|
||||||
|
if dst_size != src_size:
|
||||||
|
logging.error(
|
||||||
|
"Verificatie mislukt %s: %d vs %d bytes", fname, src_size, dst_size
|
||||||
|
)
|
||||||
|
errors += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
os.remove(src)
|
||||||
|
logging.info(
|
||||||
|
"Overgezet: %s → %s/%s/ (%d bytes)", fname, date_str, CAMERA_MAP[cam], src_size
|
||||||
|
)
|
||||||
|
ok += 1
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
"Transfer voltooid: %d ok, %d fouten, %d bytes", ok, errors, total_size
|
"Transfer voltooid: %d ok, %d fouten", ok, errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue