commit 6121d11ab7702a27dcc7f54a79575bc1e415d4ee Author: gsd Date: Mon Aug 5 00:09:35 2024 +0300 first init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65dc2d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.json +__pycache__ \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..006ad76 --- /dev/null +++ b/README @@ -0,0 +1,2 @@ +install https://github.com/OpenIPC/python-dvr +test on python 3.11 \ No newline at end of file diff --git a/config_parser.py b/config_parser.py new file mode 100644 index 0000000..9b16554 --- /dev/null +++ b/config_parser.py @@ -0,0 +1,72 @@ +import os, sys +from json import loads +from dvrip import DVRIPCam +from nvr_core import NVR + +def app_dir(): + return os.path.dirname(os.path.abspath(__file__)) + +def load_config(): + try: + path = os.path.join(app_dir(),"config.json") + print("Looking config file", path) + with open(path, "r", encoding="utf8") as f: + return loads(f.read()) + except Exception as e: + print("cannot find or parse config.json", e) + sys.exit(1) + +class Recorder: + def __init__(self, address, port, username, password): + self.address = address + self.port = int(port) + self.username = username + self.password = password + + @property + def client(self) -> DVRIPCam: + return DVRIPCam(self.address, port = self.port, user = self.username, password = self.password) + + @property + def nvr(self) -> NVR: + return NVR(self.client) + + def __str__(self) -> str: + return f"{self.address}:{self.port}" + +class Config: + def __init__(self) -> None: + raw = load_config() + self.listen_address = raw.get("backend", {}).get("address", "0.0.0.0") + self.listen_port = int(raw.get("backend", {}).get("port", "8080")) + self.recorders = [] + for raw_server in raw.get("recorders", []): + self.recorders.append(Recorder(raw_server.get("ip"), raw_server.get("port"), raw_server.get("user"), raw_server.get("password"))) + if (self.recorders.__len__() == 0): + print("Recorders not find") + else: + for recorder in self.recorders: + print(recorder) + + def getRecorder(self, index = 0): + return self.recorders[index] + +if __name__ == "__main__": + print(app_dir()) + config = Config() + recorder: Recorder = config.getRecorder() + nvr: NVR = recorder.nvr + nvr.login() + print(nvr.download_test()) + nvr.logout() + + #client: DVRIPCam = recorder.client + #client.debug() + #if not client.login(): + # print("can't login") + # sys.exit(2) + #print(client.get_system_info()) + #print(client.get_channel_titles()) + #print(client.get_channel_statuses()) + #print(client.list_local_files(START, END, "h264", channel=3)) + #client.close() \ No newline at end of file diff --git a/nvr_core.py b/nvr_core.py new file mode 100644 index 0000000..652e92b --- /dev/null +++ b/nvr_core.py @@ -0,0 +1,45 @@ +from datetime import datetime +from dvrip import DVRIPCam + +from nvr_types import File as NvrFile + +START = "2024-08-04 6:22:34" +END = "2024-08-04 23:23:09" +CHANNEL = 0 + +def date_today(begin = True): + if begin: + return datetime.now().strftime("%Y-%m-%d 00:00:00") + else: + return datetime.now().strftime("%Y-%m-%d 23:59:59") + +class NVR: + def __init__(self, client) -> None: + self.client:DVRIPCam = client + + def login(self): + self.client.login() + + def logout(self): + self.client.close() + + @property + def channels(self): + return self.client.get_channel_titles() + + def files(self, channel, start = None, end = None, ftype = "h264"): + if not start: + start = date_today() + if not end: + start = date_today(False) + for raw_file in self.client.list_local_files(startTime=start, endTime=end, filetype=ftype, channel=channel): + yield NvrFile(raw_file) + + def download_test(self, filename = "testfile.unknown"): + download_file = list(self.files(0))[0] + downloaded_bytes = 0 + with open(filename, "wb") as f: + for byte in download_file.download_stream(self.client): + downloaded_bytes += len(byte) + f.write(byte) + print("\r", downloaded_bytes, "/", download_file.size) \ No newline at end of file diff --git a/nvr_types.py b/nvr_types.py new file mode 100644 index 0000000..a631506 --- /dev/null +++ b/nvr_types.py @@ -0,0 +1,137 @@ +from datetime import datetime +from dvrip import DVRIPCam +import json +import struct + +NVR_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" + +class File: + def __init__(self, data, channel = 0) -> None: + self.begin = datetime.strptime(data.get("BeginTime"), NVR_DATETIME_FORMAT) + self.end = datetime.strptime(data.get("EndTime"), NVR_DATETIME_FORMAT) + self.diskNo = data.get("DiskNo") + self.SerialNo = data.get("SerialNo") + self.size = int(data.get("FileLength"), 0) + self.filename = data.get("FileName") + self.channel = channel + + def __str__(self) -> str: + return self.filename + + def __repr__(self) -> str: + return self.__str__() + + def download_stream(self, client:DVRIPCam, stream = 0, version = 0): + #init request + client.send( + 1424, + { + "Name": "OPPlayBack", + "OPPlayBack": { + "Action": "Claim", + "Parameter": { + "PlayMode": "ByName", + "FileName": self.filename, + "StreamType": stream, + "Value": 0, + "TransMode": "TCP", + }, + "StartTime": self.begin.strftime(NVR_DATETIME_FORMAT), + "EndTime": self.end.strftime(NVR_DATETIME_FORMAT), + }, + }, + ) + + #download request + msg = 1420 + data = { + "Name": "OPPlayBack", + "OPPlayBack": { + "Action": "DownloadStart", + "Parameter": { + "PlayMode": "ByName", + "FileName": self.filename, + "StreamType": stream, + "Value": 0, + "TransMode": "TCP", + }, + "StartTime": self.begin.strftime(NVR_DATETIME_FORMAT), + "EndTime": self.end.strftime(NVR_DATETIME_FORMAT), + }, + } + + if client.socket is None: + #todo raise error + return [] + + client.busy.acquire() + if hasattr(data, "__iter__"): + if version == 1: + data["SessionID"] = f"{client.session:#0{12}x}" + data = bytes( + json.dumps(data, ensure_ascii=False, separators=(",", ":")), "utf-8" + ) + + tail = b"\x00" + if version == 0: + tail = b"\x0a" + tail + pkt = ( + struct.pack( + "BB2xII2xHI", + 255, + version, + client.session, + client.packet_count, + msg, + len(data) + len(tail), + ) + + data + + tail + ) + client.socket_send(pkt) + data = client.socket_recv(20) + if data is None or len(data) < 20: + return [] + ( + head, + version, + client.session, + sequence_number, + msgid, + len_data, + ) = struct.unpack("BB2xII2xHI", data) + return self.get_file_stream(client, len_data, stream) + + def get_file_stream(self, client, first_chunk_size, stream): + yield client.receive_with_timeout(first_chunk_size) + while True: + header = client.receive_with_timeout(20) + len_data = struct.unpack("I", header[16:])[0] + + if len_data == 0: + break + + yield client.receive_with_timeout(len_data) + client.busy.release() + client.send( + 1420, + { + "Name": "OPPlayBack", + "OPPlayBack": { + "Action": "DownloadStop", + "Parameter": { + "FileName": self.filename, + "PlayMode": "ByName", + "StreamType": stream, + "TransMode": "TCP", + "Channel": self.channel, + "Value": 0, + }, + "StartTime": self.begin.strftime(NVR_DATETIME_FORMAT), + "EndTime": self.end.strftime(NVR_DATETIME_FORMAT), + }, + }, + ) + yield b"" + + diff --git a/server.py b/server.py new file mode 100644 index 0000000..e69de29