mirror of
https://github.com/ep1cman/unifi-protect-backup.git
synced 2025-12-11 05:43:18 +00:00
Switch to using UIProtect library (#160)
* Updated poetry dependencies to remove optional flags on dev/test
* file fixups from running poetry run tox
* Updated to Python 3.10
* Switched to UI Protect library
* Updated changelog
* Fix docker permissions
- Make scripts executable by everyone
- Correct XDG variable name to fix incorrect config path being used
* Revert "Updated poetry dependencies to remove optional flags on dev/test" and regenerated lock file
This reverts commit 432d0d3df7.
---------
Co-authored-by: Sebastian Goscik <sebastian.goscik@live.co.uk>
This commit is contained in:
2
.github/workflows/dev.yml
vendored
2
.github/workflows/dev.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: 3.10
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: 3.10
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@@ -21,7 +21,7 @@ repos:
|
||||
rev: 21.5b1
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.9
|
||||
language_version: python3.10
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
|
||||
@@ -4,6 +4,12 @@ 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.12.0] - 2024-08-06
|
||||
### Fixed
|
||||
- Tool now targets UIProtect instead of pyunifiprotect which should help any lingering auth issues with Unifi OS 4.X
|
||||
- Python Version bumped to 3.10 (based on UIPortect need)
|
||||
- (had to make the dev and test dependencies required instead of extras to get poetry to work)
|
||||
|
||||
## [0.11.0] - 2024-06-08
|
||||
### Added
|
||||
- A new experimental downloader that uses the same mechanism the web ui does. Enable with
|
||||
|
||||
@@ -103,7 +103,7 @@ Before you submit a pull request, check that it meets these guidelines:
|
||||
2. If the pull request adds functionality, the docs should be updated. Put
|
||||
your new functionality into a function with a docstring. If adding a CLI
|
||||
option, you should update the "usage" in README.md.
|
||||
3. The pull request should work for Python 3.9. Check
|
||||
3. The pull request should work for Python 3.10. Check
|
||||
https://github.com/ep1cman/unifi-protect-backup/actions
|
||||
and make sure that the tests pass for all supported Python versions.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ ENV IGNORE_CAMERAS=""
|
||||
ENV SQLITE_PATH=/config/database/events.sqlite
|
||||
|
||||
# Fixes issue where `platformdirs` is unable to properly detect the user directory
|
||||
ENV XDG_CACHE_HOME=/config
|
||||
ENV XDG_CONFIG_HOME=/config
|
||||
|
||||
COPY docker_root/ /
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ retention period.
|
||||
- Automatic pruning of old clips
|
||||
|
||||
## Requirements
|
||||
- Python 3.9+
|
||||
- Unifi Protect version 1.20 or higher (as per [`pyunifiprotect`](https://github.com/briis/pyunifiprotect))
|
||||
- Python 3.10+
|
||||
- Unifi Protect version 1.20 or higher (as per [`uiprotect`](https://github.com/uilibs/uiprotect))
|
||||
- `rclone` installed with at least one remote configured.
|
||||
|
||||
# Setup
|
||||
@@ -235,7 +235,7 @@ If you wish for the clips to be structured differently you can do this using the
|
||||
option. It uses standard [python format string syntax](https://docs.python.org/3/library/string.html#formatstrings).
|
||||
|
||||
The following fields are provided to the format string:
|
||||
- *event:* The `Event` object as per https://github.com/briis/pyunifiprotect/blob/master/pyunifiprotect/data/nvr.py
|
||||
- *event:* The `Event` object as per https://github.com/uilibs/uiprotect/blob/main/src/uiprotect/data/nvr.py
|
||||
- *duration_seconds:* The duration of the event in seconds
|
||||
- *detection_type:* A nicely formatted list of the event detection type and the smart detection types (if any)
|
||||
- *camera_name:* The name of the camera that generated this event
|
||||
@@ -355,7 +355,7 @@ docker run \
|
||||
</a>
|
||||
|
||||
|
||||
- Heavily utilises [`pyunifiprotect`](https://github.com/briis/pyunifiprotect) by [@briis](https://github.com/briis/)
|
||||
- Heavily utilises [`uiprotect`](https://github.com/uilibs/uiprotect)
|
||||
- All the cloud functionality is provided by [`rclone`](https://rclone.org/)
|
||||
- This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the [waynerv/cookiecutter-pypackage](https://github.com/waynerv/cookiecutter-pypackage) project template.
|
||||
|
||||
|
||||
0
docker_root/etc/cont-init.d/30-config
Normal file → Executable file
0
docker_root/etc/cont-init.d/30-config
Normal file → Executable file
2
docker_root/etc/services.d/unifi-protect-backup/run
Normal file → Executable file
2
docker_root/etc/services.d/unifi-protect-backup/run
Normal file → Executable file
@@ -2,6 +2,8 @@
|
||||
|
||||
|
||||
export RCLONE_CONFIG=/config/rclone/rclone.conf
|
||||
export XDG_CACHE_HOME=/config
|
||||
|
||||
echo $VERBOSITY
|
||||
[[ -n "$VERBOSITY" ]] && export VERBOSITY_ARG=-$VERBOSITY || export VERBOSITY_ARG=""
|
||||
|
||||
|
||||
1572
poetry.lock
generated
1572
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ classifiers=[
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
]
|
||||
packages = [
|
||||
{ include = "unifi_protect_backup" },
|
||||
@@ -21,7 +21,7 @@ packages = [
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9.0,<4.0"
|
||||
python = ">=3.10.0,<4.0"
|
||||
click = "8.0.1"
|
||||
aiorun = "^2023.7.2"
|
||||
aiosqlite = "^0.17.0"
|
||||
@@ -30,7 +30,7 @@ apprise = "^1.5.0"
|
||||
expiring-dict = "^1.1.0"
|
||||
async-lru = "^2.0.4"
|
||||
aiolimiter = "^1.1.0"
|
||||
pyunifiprotect = {git = "https://github.com/ep1cman/pyunifiprotect.git", rev = "experimental"}
|
||||
uiprotect = "^5.4.0"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
@@ -64,7 +64,7 @@ unifi-protect-backup = 'unifi_protect_backup.cli:main'
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
skip-string-normalization = true
|
||||
target-version = ['py39']
|
||||
target-version = ['py310']
|
||||
include = '\.pyi?$'
|
||||
exclude = '''
|
||||
/(
|
||||
|
||||
@@ -40,11 +40,11 @@ exclude_lines =
|
||||
|
||||
[tox:tox]
|
||||
isolated_build = true
|
||||
envlist = py39, format, lint, build
|
||||
envlist = py310, format, lint, build
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.9: py39, format, lint, build
|
||||
3.10: py310, format, lint, build
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = pytest
|
||||
|
||||
@@ -10,11 +10,11 @@ from typing import Optional
|
||||
import aiosqlite
|
||||
import pytz
|
||||
from aiohttp.client_exceptions import ClientPayloadError
|
||||
from expiring_dict import ExpiringDict # type: ignore
|
||||
from aiolimiter import AsyncLimiter
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from pyunifiprotect.data.types import EventType
|
||||
from expiring_dict import ExpiringDict # type: ignore
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
from uiprotect.data.types import EventType
|
||||
|
||||
from unifi_protect_backup.utils import (
|
||||
SubprocessException,
|
||||
@@ -102,7 +102,7 @@ class VideoDownloader:
|
||||
self.current_event = event
|
||||
self.logger = logging.LoggerAdapter(self.base_logger, {'event': f' [{event.id}]'})
|
||||
|
||||
# Fix timezones since pyunifiprotect sets all timestamps to UTC. Instead localize them to
|
||||
# Fix timezones since uiprotect sets all timestamps to UTC. Instead localize them to
|
||||
# the timezone of the unifi protect NVR.
|
||||
event.start = event.start.replace(tzinfo=pytz.utc).astimezone(self._protect.bootstrap.nvr.timezone)
|
||||
event.end = event.end.replace(tzinfo=pytz.utc).astimezone(self._protect.bootstrap.nvr.timezone)
|
||||
|
||||
@@ -10,11 +10,11 @@ from typing import Optional
|
||||
import aiosqlite
|
||||
import pytz
|
||||
from aiohttp.client_exceptions import ClientPayloadError
|
||||
from expiring_dict import ExpiringDict # type: ignore
|
||||
from aiolimiter import AsyncLimiter
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from pyunifiprotect.data.types import EventType
|
||||
from expiring_dict import ExpiringDict # type: ignore
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
from uiprotect.data.types import EventType
|
||||
|
||||
from unifi_protect_backup.utils import (
|
||||
SubprocessException,
|
||||
@@ -102,7 +102,7 @@ class VideoDownloaderExperimental:
|
||||
self.current_event = event
|
||||
self.logger = logging.LoggerAdapter(self.base_logger, {'event': f' [{event.id}]'})
|
||||
|
||||
# Fix timezones since pyunifiprotect sets all timestamps to UTC. Instead localize them to
|
||||
# Fix timezones since uiprotect sets all timestamps to UTC. Instead localize them to
|
||||
# the timezone of the unifi protect NVR.
|
||||
event.start = event.start.replace(tzinfo=pytz.utc).astimezone(self._protect.bootstrap.nvr.timezone)
|
||||
event.end = event.end.replace(tzinfo=pytz.utc).astimezone(self._protect.bootstrap.nvr.timezone)
|
||||
|
||||
@@ -5,10 +5,10 @@ import logging
|
||||
from time import sleep
|
||||
from typing import List
|
||||
|
||||
from pyunifiprotect.api import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from pyunifiprotect.data.types import EventType
|
||||
from pyunifiprotect.data.websocket import WSAction, WSSubscriptionMessage
|
||||
from uiprotect.api import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
from uiprotect.data.types import EventType
|
||||
from uiprotect.data.websocket import WSAction, WSSubscriptionMessage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -111,7 +111,7 @@ class EventListener:
|
||||
logger.warning("Attempting reconnect...")
|
||||
|
||||
try:
|
||||
# Start the pyunifiprotect connection by calling `update`
|
||||
# Start the uiprotect connection by calling `update`
|
||||
await self._protect.close_session()
|
||||
self._protect._bootstrap = None
|
||||
await self._protect.update(force=True)
|
||||
|
||||
@@ -7,9 +7,9 @@ from typing import List
|
||||
|
||||
import aiosqlite
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from pyunifiprotect.data.types import EventType
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
from uiprotect.data.types import EventType
|
||||
|
||||
from unifi_protect_backup import VideoDownloader, VideoUploader
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import asyncio
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Callable, List
|
||||
|
||||
import aiosqlite
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.types import ModelType
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.types import ModelType
|
||||
|
||||
from unifi_protect_backup import (
|
||||
EventListener,
|
||||
@@ -188,7 +188,7 @@ class UnifiProtectBackup:
|
||||
logger.info("Checking rclone configuration...")
|
||||
await self._check_rclone()
|
||||
|
||||
# Start the pyunifiprotect connection by calling `update`
|
||||
# Start the uiprotect connection by calling `update`
|
||||
logger.info("Connecting to Unifi Protect...")
|
||||
|
||||
for attempts in range(10):
|
||||
|
||||
@@ -6,16 +6,16 @@ import re
|
||||
from datetime import datetime
|
||||
|
||||
import aiosqlite
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
|
||||
from unifi_protect_backup.utils import (
|
||||
SubprocessException,
|
||||
VideoQueue,
|
||||
get_camera_name,
|
||||
human_readable_size,
|
||||
run_command,
|
||||
setup_event_logger,
|
||||
SubprocessException,
|
||||
)
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class VideoUploader:
|
||||
|
||||
Provides the following fields to the format string:
|
||||
event: The `Event` object as per
|
||||
https://github.com/briis/pyunifiprotect/blob/master/pyunifiprotect/data/nvr.py
|
||||
https://github.com/briis/uiprotect/blob/master/uiprotect/data/nvr.py
|
||||
duration_seconds: The duration of the event in seconds
|
||||
detection_type: A nicely formatted list of the event detection type and the smart detection types (if any)
|
||||
camera_name: The name of the camera that generated this event
|
||||
|
||||
@@ -8,8 +8,8 @@ from typing import List, Optional
|
||||
|
||||
from apprise import NotifyType
|
||||
from async_lru import alru_cache
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data.nvr import Event
|
||||
from uiprotect import ProtectApiClient
|
||||
from uiprotect.data.nvr import Event
|
||||
|
||||
from unifi_protect_backup import notifications
|
||||
|
||||
|
||||
Reference in New Issue
Block a user