mirror of
https://github.com/ep1cman/unifi-protect-backup.git
synced 2025-12-10 13:23:29 +00:00
Add extra param to purge (#86)
* Added optional argument string to pass directly to the `rclone delete` command used to purge video files. This will allow for immediate deletion of files on destinations where the file might otherwise go to a recycle bin by default. --------- Co-authored-by: Igor Wolbers <igor@sparcobv.onmicrosoft.com> Co-authored-by: Sebastian Goscik <sebastian.goscik@live.co.uk>
This commit is contained in:
@@ -4,6 +4,10 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.9.1] - 2023-04-21
|
||||||
|
### Added
|
||||||
|
- Added optional argument string to pass directly to the `rclone delete` command used to purge video files
|
||||||
|
|
||||||
## [0.9.0] - 2023-03-24
|
## [0.9.0] - 2023-03-24
|
||||||
### Added
|
### Added
|
||||||
- The ability to send logging out via apprise notifications
|
- The ability to send logging out via apprise notifications
|
||||||
|
|||||||
@@ -127,6 +127,10 @@ Options:
|
|||||||
--rclone-args TEXT Optional extra arguments to pass to `rclone rcat` directly.
|
--rclone-args TEXT Optional extra arguments to pass to `rclone rcat` directly.
|
||||||
Common usage for this would be to set a bandwidth limit, for
|
Common usage for this would be to set a bandwidth limit, for
|
||||||
example.
|
example.
|
||||||
|
--rclone-purge-args TEXT Optional extra arguments to pass to `rclone delete` directly.
|
||||||
|
Common usage for this would be to execute a permanent delete
|
||||||
|
instead of using the recycle bin on a destination.
|
||||||
|
Google Drive example: `--drive-use-trash=false`
|
||||||
--detection-types TEXT A comma separated list of which types of detections to backup.
|
--detection-types TEXT A comma separated list of which types of detections to backup.
|
||||||
Valid options are: `motion`, `person`, `vehicle`, `ring`
|
Valid options are: `motion`, `person`, `vehicle`, `ring`
|
||||||
[default: motion,person,vehicle,ring]
|
[default: motion,person,vehicle,ring]
|
||||||
@@ -198,6 +202,7 @@ always take priority over environment variables):
|
|||||||
- `RCLONE_RETENTION`
|
- `RCLONE_RETENTION`
|
||||||
- `RCLONE_DESTINATION`
|
- `RCLONE_DESTINATION`
|
||||||
- `RCLONE_ARGS`
|
- `RCLONE_ARGS`
|
||||||
|
- `RCLONE_PURGE_ARGS`
|
||||||
- `IGNORE_CAMERAS`
|
- `IGNORE_CAMERAS`
|
||||||
- `DETECTION_TYPES`
|
- `DETECTION_TYPES`
|
||||||
- `FILE_STRUCTURE_FORMAT`
|
- `FILE_STRUCTURE_FORMAT`
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ def _parse_detection_types(ctx, param, value):
|
|||||||
help="Optional extra arguments to pass to `rclone rcat` directly. Common usage for this would "
|
help="Optional extra arguments to pass to `rclone rcat` directly. Common usage for this would "
|
||||||
"be to set a bandwidth limit, for example.",
|
"be to set a bandwidth limit, for example.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
'--rclone-purge-args',
|
||||||
|
default='',
|
||||||
|
envvar='RCLONE_PURGE_ARGS',
|
||||||
|
help="Optional extra arguments to pass to `rclone delete` directly. Common usage for this would "
|
||||||
|
"be to execute a permanent delete instead of using the recycle bin on a destination. "
|
||||||
|
"Google Drive example: `--drive-use-trash=false`",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
'--detection-types',
|
'--detection-types',
|
||||||
envvar='DETECTION_TYPES',
|
envvar='DETECTION_TYPES',
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ from unifi_protect_backup.utils import run_command, wait_until
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def delete_file(file_path):
|
async def delete_file(file_path, rclone_purge_args):
|
||||||
"""Deletes `file_path` via rclone."""
|
"""Deletes `file_path` via rclone."""
|
||||||
returncode, stdout, stderr = await run_command(f'rclone delete -vv "{file_path}"')
|
returncode, stdout, stderr = await run_command(f'rclone delete -vv "{file_path}" {rclone_purge_args}')
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
logger.error(f" Failed to delete file: '{file_path}'")
|
logger.error(f" Failed to delete file: '{file_path}'")
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ class Purge:
|
|||||||
retention: relativedelta,
|
retention: relativedelta,
|
||||||
rclone_destination: str,
|
rclone_destination: str,
|
||||||
interval: relativedelta = relativedelta(days=1),
|
interval: relativedelta = relativedelta(days=1),
|
||||||
|
rclone_purge_args: str = "",
|
||||||
):
|
):
|
||||||
"""Init.
|
"""Init.
|
||||||
|
|
||||||
@@ -43,11 +44,13 @@ class Purge:
|
|||||||
retention (relativedelta): How long clips should be kept
|
retention (relativedelta): How long clips should be kept
|
||||||
rclone_destination (str): What rclone destination the clips are stored in
|
rclone_destination (str): What rclone destination the clips are stored in
|
||||||
interval (relativedelta): How often to purge old clips
|
interval (relativedelta): How often to purge old clips
|
||||||
|
rclone_purge_args (str): Optional extra arguments to pass to `rclone delete` directly.
|
||||||
"""
|
"""
|
||||||
self._db: aiosqlite.Connection = db
|
self._db: aiosqlite.Connection = db
|
||||||
self.retention: relativedelta = retention
|
self.retention: relativedelta = retention
|
||||||
self.rclone_destination: str = rclone_destination
|
self.rclone_destination: str = rclone_destination
|
||||||
self.interval: relativedelta = interval
|
self.interval: relativedelta = interval
|
||||||
|
self.rclone_purge_args: str = rclone_purge_args
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""Main loop - runs forever."""
|
"""Main loop - runs forever."""
|
||||||
@@ -68,7 +71,7 @@ class Purge:
|
|||||||
async with self._db.execute(f"SELECT * FROM backups WHERE id = '{event_id}'") as backup_cursor:
|
async with self._db.execute(f"SELECT * FROM backups WHERE id = '{event_id}'") as backup_cursor:
|
||||||
async for _, remote, file_path in backup_cursor:
|
async for _, remote, file_path in backup_cursor:
|
||||||
logger.debug(f" Deleted: {remote}:{file_path}")
|
logger.debug(f" Deleted: {remote}:{file_path}")
|
||||||
await delete_file(f"{remote}:{file_path}")
|
await delete_file(f"{remote}:{file_path}", self.rclone_purge_args)
|
||||||
deleted_a_file = True
|
deleted_a_file = True
|
||||||
|
|
||||||
# delete event from database
|
# delete event from database
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class UnifiProtectBackup:
|
|||||||
rclone_destination: str,
|
rclone_destination: str,
|
||||||
retention: str,
|
retention: str,
|
||||||
rclone_args: str,
|
rclone_args: str,
|
||||||
|
rclone_purge_args: str,
|
||||||
detection_types: List[str],
|
detection_types: List[str],
|
||||||
ignore_cameras: List[str],
|
ignore_cameras: List[str],
|
||||||
file_structure_format: str,
|
file_structure_format: str,
|
||||||
@@ -87,6 +88,7 @@ class UnifiProtectBackup:
|
|||||||
(https://rclone.org/filtering/#max-age-don-t-transfer-any-file-older-than-this)
|
(https://rclone.org/filtering/#max-age-don-t-transfer-any-file-older-than-this)
|
||||||
rclone_args (str): A bandwidth limit which is passed to the `--bwlimit` argument of
|
rclone_args (str): A bandwidth limit which is passed to the `--bwlimit` argument of
|
||||||
`rclone` (https://rclone.org/docs/#bwlimit-bandwidth-spec)
|
`rclone` (https://rclone.org/docs/#bwlimit-bandwidth-spec)
|
||||||
|
rclone_purge_args (str): Optional extra arguments to pass to `rclone delete` directly.
|
||||||
detection_types (List[str]): List of which detection types to backup.
|
detection_types (List[str]): List of which detection types to backup.
|
||||||
ignore_cameras (List[str]): List of camera IDs for which to not backup events.
|
ignore_cameras (List[str]): List of camera IDs for which to not backup events.
|
||||||
file_structure_format (str): A Python format string for output file path.
|
file_structure_format (str): A Python format string for output file path.
|
||||||
@@ -121,6 +123,7 @@ class UnifiProtectBackup:
|
|||||||
logger.debug(f" {rclone_destination=}")
|
logger.debug(f" {rclone_destination=}")
|
||||||
logger.debug(f" {retention=}")
|
logger.debug(f" {retention=}")
|
||||||
logger.debug(f" {rclone_args=}")
|
logger.debug(f" {rclone_args=}")
|
||||||
|
logger.debug(f" {rclone_purge_args=}")
|
||||||
logger.debug(f" {ignore_cameras=}")
|
logger.debug(f" {ignore_cameras=}")
|
||||||
logger.debug(f" {verbose=}")
|
logger.debug(f" {verbose=}")
|
||||||
logger.debug(f" {detection_types=}")
|
logger.debug(f" {detection_types=}")
|
||||||
@@ -134,6 +137,7 @@ class UnifiProtectBackup:
|
|||||||
self.rclone_destination = rclone_destination
|
self.rclone_destination = rclone_destination
|
||||||
self.retention = parse_rclone_retention(retention)
|
self.retention = parse_rclone_retention(retention)
|
||||||
self.rclone_args = rclone_args
|
self.rclone_args = rclone_args
|
||||||
|
self.rclone_purge_args = rclone_purge_args
|
||||||
self.file_structure_format = file_structure_format
|
self.file_structure_format = file_structure_format
|
||||||
|
|
||||||
self.address = address
|
self.address = address
|
||||||
@@ -238,7 +242,7 @@ class UnifiProtectBackup:
|
|||||||
|
|
||||||
# Create purge task
|
# Create purge task
|
||||||
# This will, every midnight, purge old backups from the rclone remotes and database
|
# This will, every midnight, purge old backups from the rclone remotes and database
|
||||||
purge = Purge(self._db, self.retention, self.rclone_destination, self._purge_interval)
|
purge = Purge(self._db, self.retention, self.rclone_destination, self._purge_interval, self.rclone_purge_args)
|
||||||
tasks.append(purge.start())
|
tasks.append(purge.start())
|
||||||
|
|
||||||
# Create missing event task
|
# Create missing event task
|
||||||
|
|||||||
Reference in New Issue
Block a user