Fix: Properly handle unknown IDs

Today after adding a new camera for testing, it became
clear that the previous assumption that pyunifiprotect
would update its bootstrap when new cameras were
added was incorrect.
This commit is contained in:
Sebastian Goscik
2022-02-22 22:53:06 +00:00
parent 0a2c0aa326
commit 90e50fd982

View File

@@ -7,7 +7,7 @@ from typing import Callable, List, Optional
import aiocron import aiocron
import aiohttp import aiohttp
from pyunifiprotect import ProtectApiClient from pyunifiprotect import NvrError, ProtectApiClient
from pyunifiprotect.data.nvr import Event from pyunifiprotect.data.nvr import Event
from pyunifiprotect.data.types import EventType, ModelType from pyunifiprotect.data.types import EventType, ModelType
from pyunifiprotect.data.websocket import WSAction, WSSubscriptionMessage from pyunifiprotect.data.websocket import WSAction, WSSubscriptionMessage
@@ -256,6 +256,8 @@ class UnifiProtectBackup:
# Start the pyunifiprotect connection by calling `update` # Start the pyunifiprotect connection by calling `update`
logger.info("Connecting to Unifi Protect...") logger.info("Connecting to Unifi Protect...")
await self._protect.update() await self._protect.update()
# Get a mapping of camera ids -> names
logger.info("Found cameras:") logger.info("Found cameras:")
for camera in self._protect.bootstrap.cameras.values(): for camera in self._protect.bootstrap.cameras.values():
logger.info(f" - {camera.id}: {camera.name}") logger.info(f" - {camera.id}: {camera.name}")
@@ -360,18 +362,17 @@ class UnifiProtectBackup:
""" """
while True: while True:
event = await self._download_queue.get()
destination = self.generate_file_path(event)
logger.info(f"Backing up event: {event.id}")
logger.debug(f"Remaining Queue: {self._download_queue.qsize()}")
logger.debug(f" Camera: {self._protect.bootstrap.cameras[event.camera_id].name}")
logger.debug(f" Type: {event.type}")
logger.debug(f" Start: {event.start.strftime('%Y-%m-%dT%H-%M-%S')}")
logger.debug(f" End: {event.end.strftime('%Y-%m-%dT%H-%M-%S')}")
logger.debug(f" Duration: {event.end-event.start}")
try: try:
event = await self._download_queue.get()
logger.info(f"Backing up event: {event.id}")
logger.debug(f"Remaining Queue: {self._download_queue.qsize()}")
logger.debug(f" Camera: {await self._get_camera_name(event.camera_id)}")
logger.debug(f" Type: {event.type}")
logger.debug(f" Start: {event.start.strftime('%Y-%m-%dT%H-%M-%S')}")
logger.debug(f" End: {event.end.strftime('%Y-%m-%dT%H-%M-%S')}")
logger.debug(f" Duration: {event.end-event.start}")
# Download video # Download video
logger.debug(" Downloading video...") logger.debug(" Downloading video...")
for x in range(5): for x in range(5):
@@ -387,6 +388,8 @@ class UnifiProtectBackup:
logger.warn(f"Download failed after 5 attempts, abandoning event {event.id}:") logger.warn(f"Download failed after 5 attempts, abandoning event {event.id}:")
continue continue
destination = await self.generate_file_path(event)
logger.debug(" Uploading video via rclone...") logger.debug(" Uploading video via rclone...")
logger.debug(f" To: {destination}") logger.debug(f" To: {destination}")
logger.debug(f" Size: {human_readable_size(len(video))}") logger.debug(f" Size: {human_readable_size(len(video))}")
@@ -436,7 +439,7 @@ class UnifiProtectBackup:
else: else:
raise RcloneException(stdout.decode(), stderr.decode(), proc.returncode) raise RcloneException(stdout.decode(), stderr.decode(), proc.returncode)
def generate_file_path(self, event: Event) -> pathlib.Path: async def generate_file_path(self, event: Event) -> pathlib.Path:
"""Generates the rclone destination path for the provided event. """Generates the rclone destination path for the provided event.
Generates paths in the following structure: Generates paths in the following structure:
@@ -455,7 +458,7 @@ class UnifiProtectBackup:
""" """
path = pathlib.Path(self.rclone_destination) path = pathlib.Path(self.rclone_destination)
assert isinstance(event.camera_id, str) assert isinstance(event.camera_id, str)
path /= self._protect.bootstrap.cameras[event.camera_id].name # directory per camera path /= await self._get_camera_name(event.camera_id) # directory per camera
path /= event.start.strftime("%Y-%m-%d") # Directory per day path /= event.start.strftime("%Y-%m-%d") # Directory per day
file_name = f"{event.start.strftime('%Y-%m-%dT%H-%M-%S')} {event.type}" file_name = f"{event.start.strftime('%Y-%m-%dT%H-%M-%S')} {event.type}"
@@ -468,3 +471,20 @@ class UnifiProtectBackup:
path /= file_name path /= file_name
return path return path
async def _get_camera_name(self, id: str):
try:
return self._protect.bootstrap.cameras[id].name
except KeyError:
# Refresh cameras
logger.debug(f"Unknown camera id: '{id}', checking API")
try:
await self._protect.update(force=True)
except NvrError:
logger.debug(f"Unknown camera id: '{id}'")
raise
name = self._protect.bootstrap.cameras[id].name
logger.debug(f"Found camera - {id}: {name}")
return name