commit
6121d11ab7
6 changed files with 258 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||
|
config.json |
||||
|
__pycache__ |
@ -0,0 +1,2 @@ |
|||||
|
install https://github.com/OpenIPC/python-dvr |
||||
|
test on python 3.11 |
@ -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() |
@ -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) |
@ -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"" |
||||
|
|
||||
|
|
Loading…
Reference in new issue