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