From c16189a68b240db9da06532cbf58d5626637f415 Mon Sep 17 00:00:00 2001 From: Hala Khodr Date: Tue, 28 Nov 2023 12:07:44 +0100 Subject: [PATCH 1/3] add nvrdownload command --- reolinkapi/handlers/api_handler.py | 4 ++- reolinkapi/mixins/nvrdownload.py | 51 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 reolinkapi/mixins/nvrdownload.py diff --git a/reolinkapi/handlers/api_handler.py b/reolinkapi/handlers/api_handler.py index 501ceb7..b9efe58 100644 --- a/reolinkapi/handlers/api_handler.py +++ b/reolinkapi/handlers/api_handler.py @@ -14,6 +14,7 @@ from reolinkapi.mixins.system import SystemAPIMixin from reolinkapi.mixins.user import UserAPIMixin from reolinkapi.mixins.zoom import ZoomAPIMixin +from reolinkapi.mixins.nvrdownload import NvrDownloadAPIMixin class APIHandler(AlarmAPIMixin, @@ -28,7 +29,8 @@ class APIHandler(AlarmAPIMixin, SystemAPIMixin, UserAPIMixin, ZoomAPIMixin, - StreamAPIMixin): + StreamAPIMixin, + NvrDownloadAPIMixin): """ The APIHandler class is the backend part of the API, the actual API calls are implemented in Mixins. diff --git a/reolinkapi/mixins/nvrdownload.py b/reolinkapi/mixins/nvrdownload.py new file mode 100644 index 0000000..911182a --- /dev/null +++ b/reolinkapi/mixins/nvrdownload.py @@ -0,0 +1,51 @@ +from datetime import datetime as dt + +class NvrDownloadAPIMixin: + """API calls for NvrDownload.""" + def get_playback_files(self, start: dt, end: dt = dt.now(), channel: int = 0, + streamtype: str = 'sub'): + """ + Get the filenames of the videos for the time range provided. + + Args: + start: the starting time range to examine + end: the end time of the time range to examine + channel: which channel to download from + streamtype: 'main' or 'sub' - the stream to examine + :return: response json + """ + search_params = { + 'NvrDownload': { + 'channel': channel, + 'iLogicChannel': 0, + 'streamType': streamtype, + 'StartTime': { + 'year': start.year, + 'mon': start.month, + 'day': start.day, + 'hour': start.hour, + 'min': start.minute, + 'sec': start.second + }, + 'EndTime': { + 'year': end.year, + 'mon': end.month, + 'day': end.day, + 'hour': end.hour, + 'min': end.minute, + 'sec': end.second + } + } + } + body = [{"cmd": "NvrDownload", "action": 1, "param": search_params}] + + resp = self._execute_command('NvrDownload', body)[0] + if 'value' not in resp: + return [] + values = resp['value'] + if 'fileList' not in values: + return [] + files = values['fileList'] + if len(files) > 0: + return [file['fileName'] for file in files] + return [] From 9b66cd4ff8260755b2d7396951ce0345f0738d9a Mon Sep 17 00:00:00 2001 From: Hala Khodr Date: Tue, 28 Nov 2023 12:21:22 +0100 Subject: [PATCH 2/3] add example of download playback videos --- examples/download_playback_video.py | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 examples/download_playback_video.py diff --git a/examples/download_playback_video.py b/examples/download_playback_video.py new file mode 100644 index 0000000..85f8202 --- /dev/null +++ b/examples/download_playback_video.py @@ -0,0 +1,46 @@ +"""Downloads a video from camera from start to end time.""" +import os +from configparser import RawConfigParser +from datetime import datetime as dt, timedelta +from reolinkapi import Camera +import requests +import pandas as pd + +def read_config(props_path: str) -> dict: + """Reads in a properties file into variables. + + NB! this config file is kept out of commits with .gitignore. The structure of this file is such: + # secrets.cfg + [camera] + ip={ip_address} + username={username} + password={password} + """ + config = RawConfigParser() + assert os.path.exists(props_path), f"Path does not exist: {props_path}" + config.read(props_path) + return config + + +# Read in your ip, username, & password +# (NB! you'll likely have to create this file. See tests/test_camera.py for details on structure) +config = read_config('camera.cfg') + +ip = config.get('camera', 'ip') +un = config.get('camera', 'username') +pw = config.get('camera', 'password') + +# Connect to camera +cam = Camera(ip, un, pw) + +start = dt.now() - timedelta(minutes=10) +end = dt.now() - timedelta(minutes=9) +channel = 0 + +files = cam.get_playback_files(start=start, end=end, channel= channel) +print(files) +dl_dir = os.path.join(os.path.expanduser('~'), 'Downloads') +for fname in files: + print(fname) + # Download the mp4 + cam.get_file(fname, output_path=os.path.join(dl_dir, fname)) From 4c8d93c157a677f1ee31273a37baacffa5f711ea Mon Sep 17 00:00:00 2001 From: Hala Khodr Date: Tue, 28 Nov 2023 15:08:56 +0100 Subject: [PATCH 3/3] add try catch block --- reolinkapi/mixins/nvrdownload.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/reolinkapi/mixins/nvrdownload.py b/reolinkapi/mixins/nvrdownload.py index 911182a..c4e3051 100644 --- a/reolinkapi/mixins/nvrdownload.py +++ b/reolinkapi/mixins/nvrdownload.py @@ -38,8 +38,11 @@ def get_playback_files(self, start: dt, end: dt = dt.now(), channel: int = 0, } } body = [{"cmd": "NvrDownload", "action": 1, "param": search_params}] - - resp = self._execute_command('NvrDownload', body)[0] + try: + resp = self._execute_command('NvrDownload', body)[0] + except Exception as e: + print(f"Error: {e}") + return [] if 'value' not in resp: return [] values = resp['value']