Compare commits

...

10 Commits

Author SHA1 Message Date
Sebastian Goscik
5f7fad72d5 Bump version: 0.10.2 → 0.10.3 2023-12-07 19:59:13 +00:00
Sebastian Goscik
991998aa37 changelog 2023-12-07 19:59:10 +00:00
Sebastian Goscik
074f5b372c bump pyunifiprotect version 2023-12-07 19:57:21 +00:00
Sebastian Goscik
00aec23805 Bump version: 0.10.1 → 0.10.2 2023-11-21 00:20:46 +00:00
Sebastian Goscik
52e4ecd50d changelog 2023-11-21 00:20:35 +00:00
Sebastian Goscik
6b116ab93b Fixed issue where duplicate events were being downloaded
Previously unifi would only end one update which contained the end time stamp
so it was sufficient to check if it existed in the new event data.
However, now it is possible to get update events after the end timestamp
has been set. With this change we now look for when the event change
data contains the end time stamp. So long as unifi does not change its
mind about when an event ends, this should solve the issue.
2023-11-21 00:18:36 +00:00
Sebastian Goscik
70526b2f49 Make default file path format use event start time 2023-11-21 00:08:24 +00:00
Sebastian Goscik
5069d28f0d Bump version: 0.10.0 → 0.10.1 2023-11-01 21:34:01 +00:00
Sebastian Goscik
731ab1081d changelog 2023-11-01 21:33:55 +00:00
Sebastian Goscik
701fd9b0a8 Fix event enum string conversion to value 2023-11-01 21:32:19 +00:00
11 changed files with 32 additions and 19 deletions

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.10.0
current_version = 0.10.3
commit = True
tag = True

View File

@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.10.3] - 2023-12-07
### Fixed
- Bumped `pyunifiprotect` version to fix issue caused by unifi protect returning invalid UUIDs
## [0.10.2] - 2023-11-21
### Fixed
- Issue where duplicate events were being downloaded causing database errors
- Default file path format now uses event start time instead of event end time which makes more logical sense
## [0.10.1] - 2023-11-01
### Fixed
- Event type enum conversion string was no longer converting to the enum value, this is now done explicitly.
## [0.10.0] - 2023-11-01
### Added
- Command line option to skip events longer than a given length (default 2 hours)

View File

@@ -7,7 +7,7 @@ LABEL maintainer="ep1cman"
WORKDIR /app
COPY dist/unifi_protect_backup-0.10.0.tar.gz sdist.tar.gz
COPY dist/unifi_protect_backup-0.10.3.tar.gz sdist.tar.gz
# https://github.com/rust-lang/cargo/issues/2808
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true

10
poetry.lock generated
View File

@@ -1791,21 +1791,21 @@ files = [
[[package]]
name = "pyunifiprotect"
version = "4.21.0"
version = "4.22.0"
description = "Unofficial UniFi Protect Python API and CLI"
category = "main"
optional = false
python-versions = ">=3.9"
files = [
{file = "pyunifiprotect-4.21.0-py3-none-any.whl", hash = "sha256:6a67a3a4b15576695d140f80de2d97890d0be8d3b1a0c0bc1effde1fd646880e"},
{file = "pyunifiprotect-4.21.0.tar.gz", hash = "sha256:d21f5144f16037fd11f192db52ab0cd99db8fb1f8670abc2afb1c0fa04cdb9de"},
{file = "pyunifiprotect-4.22.0-py3-none-any.whl", hash = "sha256:21eab9e40a349c9b550715c34728c64fdac7d5d0f2de71644645dff804df04c4"},
{file = "pyunifiprotect-4.22.0.tar.gz", hash = "sha256:53b3c6b11f02605ff774343797f6468ed35fa9c0c99c6957c578c9871f47d449"},
]
[package.dependencies]
aiofiles = "*"
aiohttp = "*"
aioshutil = "*"
async-timeout = "*"
async-timeout = {version = "*", markers = "python_version < \"3.11\""}
dateparser = "*"
orjson = "*"
packaging = "*"
@@ -1816,7 +1816,7 @@ typer = {version = ">0.6", extras = ["all"]}
[package.extras]
backup = ["aiosqlite", "asyncify", "av", "sqlalchemy[asyncio]"]
dev = ["base36", "black", "build", "coverage[toml]", "flake8", "flake8-docstrings", "ipython", "mike", "mkdocs-git-revision-date-localized-plugin", "mkdocs-include-markdown-plugin", "mkdocs-material", "mkdocstrings[python]", "mypy", "pip-tools", "pydocstyle", "pylint", "pylint-strict-informational", "pyproject-flake8", "pytest", "pytest-asyncio", "pytest-benchmark", "pytest-cov", "pytest-sugar", "pytest-timeout (>=1.2.1)", "pytest-xdist", "sqlalchemy[asyncio,mypy]", "termcolor", "types-aiofiles", "types-dateparser", "types-pillow", "types-pyjwt", "types-termcolor", "tzdata"]
dev = ["base36", "black", "build", "coverage[toml]", "ipython", "isort", "mike", "mkdocs-git-revision-date-localized-plugin", "mkdocs-include-markdown-plugin", "mkdocs-material", "mkdocstrings[python]", "mypy", "pip-tools", "pydocstyle", "pytest", "pytest-asyncio", "pytest-benchmark", "pytest-cov", "pytest-sugar", "pytest-timeout (>=1.2.1)", "pytest-xdist[psutil]", "ruff", "sqlalchemy[asyncio,mypy]", "termcolor", "types-aiofiles", "types-dateparser", "types-pillow", "types-pyjwt", "types-termcolor", "tzdata"]
full = ["aiosqlite", "asyncify", "av", "ipython", "python-dotenv", "sqlalchemy[asyncio]", "termcolor"]
shell = ["ipython", "python-dotenv", "termcolor"]
tz = ["tzdata"]

View File

@@ -1,7 +1,7 @@
[tool]
[tool.poetry]
name = "unifi_protect_backup"
version = "0.10.0"
version = "0.10.3"
homepage = "https://github.com/ep1cman/unifi-protect-backup"
description = "Python tool to backup unifi event clips in realtime."
authors = ["sebastian.goscik <sebastian@goscik.com>"]

View File

@@ -2,7 +2,7 @@
__author__ = """sebastian.goscik"""
__email__ = 'sebastian@goscik.com'
__version__ = '0.10.0'
__version__ = '0.10.3'
from .downloader import VideoDownloader
from .event_listener import EventListener

View File

@@ -109,7 +109,7 @@ def parse_rclone_retention(ctx, param, retention) -> relativedelta:
@click.option(
'--file-structure-format',
envvar='FILE_STRUCTURE_FORMAT',
default="{camera_name}/{event.start:%Y-%m-%d}/{event.end:%Y-%m-%dT%H-%M-%S} {detection_type}.mp4",
default="{camera_name}/{event.start:%Y-%m-%d}/{event.start:%Y-%m-%dT%H-%M-%S} {detection_type}.mp4",
show_default=True,
help="A Python format string used to generate the file structure/name on the rclone remote."
"For details of the fields available, see the projects `README.md` file.",

View File

@@ -114,9 +114,9 @@ class VideoDownloader:
self.logger.debug(f"Video Download Buffer: {output_queue_current_size}/{output_queue_max_size}")
self.logger.debug(f" Camera: {await get_camera_name(self._protect, event.camera_id)}")
if event.type == EventType.SMART_DETECT:
self.logger.debug(f" Type: {event.type} ({', '.join(event.smart_detect_types)})")
self.logger.debug(f" Type: {event.type.value} ({', '.join(event.smart_detect_types)})")
else:
self.logger.debug(f" Type: {event.type}")
self.logger.debug(f" Type: {event.type.value}")
self.logger.debug(f" Start: {event.start.strftime('%Y-%m-%dT%H-%M-%S')} ({event.start.timestamp()})")
self.logger.debug(f" End: {event.end.strftime('%Y-%m-%dT%H-%M-%S')} ({event.end.timestamp()})")
duration = (event.end - event.start).total_seconds()
@@ -198,7 +198,7 @@ class VideoDownloader:
self.logger.warning("Ignoring event")
await self._db.execute(
"INSERT INTO events VALUES "
f"('{event.id}', '{event.type}', '{event.camera_id}',"
f"('{event.id}', '{event.type.value}', '{event.camera_id}',"
f"'{event.start.timestamp()}', '{event.end.timestamp()}')"
)
await self._db.commit()

View File

@@ -61,7 +61,7 @@ class EventListener:
return
if msg.new_obj.camera_id in self.ignore_cameras:
return
if msg.new_obj.end is None:
if 'end' not in msg.changed_data:
return
if msg.new_obj.type not in [EventType.MOTION, EventType.SMART_DETECT, EventType.RING]:
return

View File

@@ -131,7 +131,7 @@ class MissingEventChecker:
logger.extra_debug(f"Ignoring event '{event.id}'")
await self._db.execute(
"INSERT INTO events VALUES "
f"('{event.id}', '{event.type}', '{event.camera_id}',"
f"('{event.id}', '{event.type.value}', '{event.camera_id}',"
f"'{event.start.timestamp()}', '{event.end.timestamp()}')"
)
await self._db.commit()
@@ -154,7 +154,7 @@ class MissingEventChecker:
shown_warning = True
if event.type != EventType.SMART_DETECT:
event_name = f"{event.id} ({event.type})"
event_name = f"{event.id} ({event.type.value})"
else:
event_name = f"{event.id} ({', '.join(event.smart_detect_types)})"

View File

@@ -117,7 +117,7 @@ class VideoUploader:
assert isinstance(event.end, datetime)
await self._db.execute(
"INSERT INTO events VALUES "
f"('{event.id}', '{event.type}', '{event.camera_id}',"
f"('{event.id}', '{event.type.value}', '{event.camera_id}',"
f"'{event.start.timestamp()}', '{event.end.timestamp()}')"
)
@@ -157,9 +157,9 @@ class VideoUploader:
format_context = {
"event": event,
"duration_seconds": (event.end - event.start).total_seconds(),
"detection_type": f"{event.type} ({' '.join(event.smart_detect_types)})"
"detection_type": f"{event.type.value} ({' '.join(event.smart_detect_types)})"
if event.smart_detect_types
else f"{event.type}",
else f"{event.type.value}",
"camera_name": await get_camera_name(self._protect, event.camera_id),
}