mirror of https://github.com/OpenIPC/python-dvr
22 changed files with 5108 additions and 5108 deletions
@ -1,55 +1,55 @@ |
|||||
#!/usr/bin/env python |
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
import os, sys, struct, json |
import os, sys, struct, json |
||||
from time import sleep |
from time import sleep |
||||
from socket import * |
from socket import * |
||||
from datetime import * |
from datetime import * |
||||
|
|
||||
if len(sys.argv) > 1: |
if len(sys.argv) > 1: |
||||
port = sys.argv[1] |
port = sys.argv[1] |
||||
else: |
else: |
||||
print("Usage: %s [Port]" % os.path.basename(sys.argv[0])) |
print("Usage: %s [Port]" % os.path.basename(sys.argv[0])) |
||||
port = input("Port(default 15002): ") |
port = input("Port(default 15002): ") |
||||
if port == "": |
if port == "": |
||||
port = "15002" |
port = "15002" |
||||
server = socket(AF_INET, SOCK_STREAM) |
server = socket(AF_INET, SOCK_STREAM) |
||||
server.bind(("0.0.0.0", int(port))) |
server.bind(("0.0.0.0", int(port))) |
||||
# server.settimeout(0.5) |
# server.settimeout(0.5) |
||||
server.listen(1) |
server.listen(1) |
||||
|
|
||||
log = "info.txt" |
log = "info.txt" |
||||
|
|
||||
|
|
||||
def tolog(s): |
def tolog(s): |
||||
logfile = open(datetime.now().strftime("%Y_%m_%d_") + log, "a+") |
logfile = open(datetime.now().strftime("%Y_%m_%d_") + log, "a+") |
||||
logfile.write(s) |
logfile.write(s) |
||||
logfile.close() |
logfile.close() |
||||
|
|
||||
|
|
||||
def GetIP(s): |
def GetIP(s): |
||||
return inet_ntoa(struct.pack("<I", int(s, 16))) |
return inet_ntoa(struct.pack("<I", int(s, 16))) |
||||
|
|
||||
|
|
||||
while True: |
while True: |
||||
try: |
try: |
||||
conn, addr = server.accept() |
conn, addr = server.accept() |
||||
head, version, session, sequence_number, msgid, len_data = struct.unpack( |
head, version, session, sequence_number, msgid, len_data = struct.unpack( |
||||
"BB2xII2xHI", conn.recv(20) |
"BB2xII2xHI", conn.recv(20) |
||||
) |
) |
||||
sleep(0.1) # Just for recive whole packet |
sleep(0.1) # Just for recive whole packet |
||||
data = conn.recv(len_data) |
data = conn.recv(len_data) |
||||
conn.close() |
conn.close() |
||||
reply = json.loads(data, encoding="utf8") |
reply = json.loads(data, encoding="utf8") |
||||
print(datetime.now().strftime("[%Y-%m-%d %H:%M:%S]>>>")) |
print(datetime.now().strftime("[%Y-%m-%d %H:%M:%S]>>>")) |
||||
print(head, version, session, sequence_number, msgid, len_data) |
print(head, version, session, sequence_number, msgid, len_data) |
||||
print(json.dumps(reply, indent=4, sort_keys=True)) |
print(json.dumps(reply, indent=4, sort_keys=True)) |
||||
print("<<<") |
print("<<<") |
||||
tolog(repr(data) + "\r\n") |
tolog(repr(data) + "\r\n") |
||||
except (KeyboardInterrupt, SystemExit): |
except (KeyboardInterrupt, SystemExit): |
||||
break |
break |
||||
# except: |
# except: |
||||
# e = 1 |
# e = 1 |
||||
# print "no" |
# print "no" |
||||
server.close() |
server.close() |
||||
sys.exit(1) |
sys.exit(1) |
||||
|
@ -1,99 +1,99 @@ |
|||||
//заготовки
|
//заготовки
|
||||
//'{"EncryptType": "MD5", "LoginType": "DVRIP-Web", "PassWord": "00000000", "UserName": "admin"}'
|
//'{"EncryptType": "MD5", "LoginType": "DVRIP-Web", "PassWord": "00000000", "UserName": "admin"}'
|
||||
char login_packet_bytes[] = { |
char login_packet_bytes[] = { |
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, |
||||
0x5f, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x45, 0x6e, |
0x5f, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x45, 0x6e, |
||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x54, 0x79, 0x70, |
0x63, 0x72, 0x79, 0x70, 0x74, 0x54, 0x79, 0x70, |
||||
0x65, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x44, 0x35, |
0x65, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x44, 0x35, |
||||
0x22, 0x2c, 0x20, 0x22, 0x4c, 0x6f, 0x67, 0x69, |
0x22, 0x2c, 0x20, 0x22, 0x4c, 0x6f, 0x67, 0x69, |
||||
0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x20, |
0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x20, |
||||
0x22, 0x44, 0x56, 0x52, 0x49, 0x50, 0x2d, 0x57, |
0x22, 0x44, 0x56, 0x52, 0x49, 0x50, 0x2d, 0x57, |
||||
0x65, 0x62, 0x22, 0x2c, 0x20, 0x22, 0x50, 0x61, |
0x65, 0x62, 0x22, 0x2c, 0x20, 0x22, 0x50, 0x61, |
||||
0x73, 0x73, 0x57, 0x6f, 0x72, 0x64, 0x22, 0x3a, |
0x73, 0x73, 0x57, 0x6f, 0x72, 0x64, 0x22, 0x3a, |
||||
0x20, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, |
0x20, 0x22, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, |
||||
0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x55, 0x73, |
0x30, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x55, 0x73, |
||||
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3a, |
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3a, |
||||
0x20, 0x22, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x22, |
0x20, 0x22, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x22, |
||||
0x7d, 0x0a, 0x00 |
0x7d, 0x0a, 0x00 |
||||
}; |
}; |
||||
//'{"Name": "fVideo.OSDInfo", "SessionID": "0x00000002", "fVideo.OSDInfo": {"OSDInfo": [{"Info": ["0", "0", "0"], "OSDInfoWidget": {"BackColor": "0x00000000", "EncodeBlend": true, "FrontColor": "0xF0FFFFFF", "PreviewBlend": true, "RelativePos": [6144, 6144, 8192, 8192]}}], "strEnc": "UTF-8"}}'
|
//'{"Name": "fVideo.OSDInfo", "SessionID": "0x00000002", "fVideo.OSDInfo": {"OSDInfo": [{"Info": ["0", "0", "0"], "OSDInfoWidget": {"BackColor": "0x00000000", "EncodeBlend": true, "FrontColor": "0xF0FFFFFF", "PreviewBlend": true, "RelativePos": [6144, 6144, 8192, 8192]}}], "strEnc": "UTF-8"}}'
|
||||
char set_packet_bytes[] = { |
char set_packet_bytes[] = { |
||||
0xff, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, |
0xff, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, |
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, |
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, |
||||
0x24, 0x01, 0x00, 0x00, 0x7b, 0x22, 0x4e, 0x61, |
0x24, 0x01, 0x00, 0x00, 0x7b, 0x22, 0x4e, 0x61, |
||||
0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x66, 0x56, |
0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x66, 0x56, |
||||
0x69, 0x64, 0x65, 0x6f, 0x2e, 0x4f, 0x53, 0x44, |
0x69, 0x64, 0x65, 0x6f, 0x2e, 0x4f, 0x53, 0x44, |
||||
0x49, 0x6e, 0x66, 0x6f, 0x22, 0x2c, 0x20, 0x22, |
0x49, 0x6e, 0x66, 0x6f, 0x22, 0x2c, 0x20, 0x22, |
||||
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, |
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, |
||||
0x44, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x78, 0x30, |
0x44, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x78, 0x30, |
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x22, |
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x22, |
||||
0x2c, 0x20, 0x22, 0x66, 0x56, 0x69, 0x64, 0x65, |
0x2c, 0x20, 0x22, 0x66, 0x56, 0x69, 0x64, 0x65, |
||||
0x6f, 0x2e, 0x4f, 0x53, 0x44, 0x49, 0x6e, 0x66, |
0x6f, 0x2e, 0x4f, 0x53, 0x44, 0x49, 0x6e, 0x66, |
||||
0x6f, 0x22, 0x3a, 0x20, 0x7b, 0x22, 0x4f, 0x53, |
0x6f, 0x22, 0x3a, 0x20, 0x7b, 0x22, 0x4f, 0x53, |
||||
0x44, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x3a, 0x20, |
0x44, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x3a, 0x20, |
||||
0x5b, 0x7b, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x22, |
0x5b, 0x7b, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x22, |
||||
0x3a, 0x20, 0x5b, 0x22, 0x30, 0x22, 0x2c, 0x20, |
0x3a, 0x20, 0x5b, 0x22, 0x30, 0x22, 0x2c, 0x20, |
||||
0x22, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x30, 0x22, |
0x22, 0x30, 0x22, 0x2c, 0x20, 0x22, 0x30, 0x22, |
||||
0x5d, 0x2c, 0x20, 0x22, 0x4f, 0x53, 0x44, 0x49, |
0x5d, 0x2c, 0x20, 0x22, 0x4f, 0x53, 0x44, 0x49, |
||||
0x6e, 0x66, 0x6f, 0x57, 0x69, 0x64, 0x67, 0x65, |
0x6e, 0x66, 0x6f, 0x57, 0x69, 0x64, 0x67, 0x65, |
||||
0x74, 0x22, 0x3a, 0x20, 0x7b, 0x22, 0x42, 0x61, |
0x74, 0x22, 0x3a, 0x20, 0x7b, 0x22, 0x42, 0x61, |
||||
0x63, 0x6b, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, |
0x63, 0x6b, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, |
||||
0x3a, 0x20, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30, |
0x3a, 0x20, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30, |
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, |
0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x2c, 0x20, |
||||
0x22, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, |
0x22, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, |
||||
0x6c, 0x65, 0x6e, 0x64, 0x22, 0x3a, 0x20, 0x74, |
0x6c, 0x65, 0x6e, 0x64, 0x22, 0x3a, 0x20, 0x74, |
||||
0x72, 0x75, 0x65, 0x2c, 0x20, 0x22, 0x46, 0x72, |
0x72, 0x75, 0x65, 0x2c, 0x20, 0x22, 0x46, 0x72, |
||||
0x6f, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, |
0x6f, 0x6e, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, |
||||
0x22, 0x3a, 0x20, 0x22, 0x30, 0x78, 0x46, 0x30, |
0x22, 0x3a, 0x20, 0x22, 0x30, 0x78, 0x46, 0x30, |
||||
0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x22, 0x2c, |
0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x22, 0x2c, |
||||
0x20, 0x22, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, |
0x20, 0x22, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, |
||||
0x77, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x22, 0x3a, |
0x77, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x22, 0x3a, |
||||
0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x22, |
0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x22, |
||||
0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, |
0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, |
||||
0x50, 0x6f, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x36, |
0x50, 0x6f, 0x73, 0x22, 0x3a, 0x20, 0x5b, 0x36, |
||||
0x31, 0x34, 0x34, 0x2c, 0x20, 0x36, 0x31, 0x34, |
0x31, 0x34, 0x34, 0x2c, 0x20, 0x36, 0x31, 0x34, |
||||
0x34, 0x2c, 0x20, 0x38, 0x31, 0x39, 0x32, 0x2c, |
0x34, 0x2c, 0x20, 0x38, 0x31, 0x39, 0x32, 0x2c, |
||||
0x20, 0x38, 0x31, 0x39, 0x32, 0x5d, 0x7d, 0x7d, |
0x20, 0x38, 0x31, 0x39, 0x32, 0x5d, 0x7d, 0x7d, |
||||
0x5d, 0x2c, 0x20, 0x22, 0x73, 0x74, 0x72, 0x45, |
0x5d, 0x2c, 0x20, 0x22, 0x73, 0x74, 0x72, 0x45, |
||||
0x6e, 0x63, 0x22, 0x3a, 0x20, 0x22, 0x55, 0x54, |
0x6e, 0x63, 0x22, 0x3a, 0x20, 0x22, 0x55, 0x54, |
||||
0x46, 0x2d, 0x38, 0x22, 0x7d, 0x7d, 0x0a, 0x00 |
0x46, 0x2d, 0x38, 0x22, 0x7d, 0x7d, 0x0a, 0x00 |
||||
}; |
}; |
||||
|
|
||||
char str1[] = "Test: 1"; |
char str1[] = "Test: 1"; |
||||
char str2[] = "Test: 2"; |
char str2[] = "Test: 2"; |
||||
char str3[] = "Test: 3"; |
char str3[] = "Test: 3"; |
||||
|
|
||||
memcpy( &login_packet_bytes[83], "00000000", 8 );//set password hash(83..88)
|
memcpy( &login_packet_bytes[83], "00000000", 8 );//set password hash(83..88)
|
||||
client.write(login_packet_bytes); |
client.write(login_packet_bytes); |
||||
char income[20] = client.read(20) |
char income[20] = client.read(20) |
||||
int len = 289+sizeof(str1)+sizeof(str2)+sizeof(str3); |
int len = 289+sizeof(str1)+sizeof(str2)+sizeof(str3); |
||||
char buff[len]; |
char buff[len]; |
||||
int offset = 0; |
int offset = 0; |
||||
memcpy( &buff[4], $income[4], 4 );//4...7 - session id
|
memcpy( &buff[4], $income[4], 4 );//4...7 - session id
|
||||
memcpy( &buff[16], &len, 2);//set len 16..17 - bytes
|
memcpy( &buff[16], &len, 2);//set len 16..17 - bytes
|
||||
//TO DO: set session hex str
|
//TO DO: set session hex str
|
||||
//70...63 - hex string session
|
//70...63 - hex string session
|
||||
memcpy( &buff[offset], &set_packet_bytes[0], 116); |
memcpy( &buff[offset], &set_packet_bytes[0], 116); |
||||
//116 str1
|
//116 str1
|
||||
//121 str2
|
//121 str2
|
||||
//126 str3
|
//126 str3
|
||||
offset += 116; |
offset += 116; |
||||
memcpy( &buff[offset], &str1[0], sizeof(str1));//set str1
|
memcpy( &buff[offset], &str1[0], sizeof(str1));//set str1
|
||||
offset +=sizeof(str1); |
offset +=sizeof(str1); |
||||
memcpy( &buff[offset], &set_packet_bytes[117], 4); |
memcpy( &buff[offset], &set_packet_bytes[117], 4); |
||||
offset += 4; |
offset += 4; |
||||
memcpy( &buff[offset], &str2[0], sizeof(str2));//set str2
|
memcpy( &buff[offset], &str2[0], sizeof(str2));//set str2
|
||||
offset += sizeof(str2); |
offset += sizeof(str2); |
||||
memcpy( &buff[offset], &set_packet_bytes[117], 4); |
memcpy( &buff[offset], &set_packet_bytes[117], 4); |
||||
offset += 4; |
offset += 4; |
||||
memcpy( &buff[offset], &str3[0], sizeof(str3));//set str3
|
memcpy( &buff[offset], &str3[0], sizeof(str3));//set str3
|
||||
offset += sizeof(str3); |
offset += sizeof(str3); |
||||
memcpy( &buff[offset], &set_packet_bytes[127], 185); |
memcpy( &buff[offset], &set_packet_bytes[127], 185); |
||||
offset += 38; |
offset += 38; |
||||
memcpy( &buff[offset], "00000000", 8 );//BG color
|
memcpy( &buff[offset], "00000000", 8 );//BG color
|
||||
offset += 41; |
offset += 41; |
||||
memcpy( &buff[offset], "F0FFFFFF", 8 );//FG color
|
memcpy( &buff[offset], "F0FFFFFF", 8 );//FG color
|
||||
//Serial.write(buff);//debug
|
//Serial.write(buff);//debug
|
||||
client.write(buff); |
client.write(buff); |
||||
client.close(); |
client.close(); |
||||
|
File diff suppressed because it is too large
@ -1,12 +1,12 @@ |
|||||
FROM python:slim |
FROM python:slim |
||||
|
|
||||
RUN apt-get update && \ |
RUN apt-get update && \ |
||||
apt-get upgrade -y && \ |
apt-get upgrade -y && \ |
||||
apt-get install -y \ |
apt-get install -y \ |
||||
ffmpeg |
ffmpeg |
||||
|
|
||||
WORKDIR /app |
WORKDIR /app |
||||
|
|
||||
COPY . . |
COPY . . |
||||
|
|
||||
CMD [ "python3", "./download-local-files.py"] |
CMD [ "python3", "./download-local-files.py"] |
||||
|
@ -1,21 +1,21 @@ |
|||||
MIT License |
MIT License |
||||
|
|
||||
Copyright (c) 2017 Eliot Kent Woodrich |
Copyright (c) 2017 Eliot Kent Woodrich |
||||
|
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
furnished to do so, subject to the following conditions: |
||||
|
|
||||
The above copyright notice and this permission notice shall be included in all |
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
copies or substantial portions of the Software. |
||||
|
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
SOFTWARE. |
||||
|
@ -1,94 +1,94 @@ |
|||||
from time import sleep, monotonic |
from time import sleep, monotonic |
||||
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
||||
from pathlib import Path |
from pathlib import Path |
||||
import logging |
import logging |
||||
|
|
||||
|
|
||||
class NVR: |
class NVR: |
||||
nvr = None |
nvr = None |
||||
logger = None |
logger = None |
||||
|
|
||||
def __init__(self, host_ip, user, password, logger): |
def __init__(self, host_ip, user, password, logger): |
||||
self.logger = logger |
self.logger = logger |
||||
self.nvr = DVRIPCam( |
self.nvr = DVRIPCam( |
||||
host_ip, |
host_ip, |
||||
user=user, |
user=user, |
||||
password=password, |
password=password, |
||||
) |
) |
||||
if logger.level <= logging.DEBUG: |
if logger.level <= logging.DEBUG: |
||||
self.nvr.debug() |
self.nvr.debug() |
||||
|
|
||||
def login(self): |
def login(self): |
||||
try: |
try: |
||||
self.logger.info(f"Connecting to NVR...") |
self.logger.info(f"Connecting to NVR...") |
||||
self.nvr.login() |
self.nvr.login() |
||||
self.logger.info("Successfuly connected to NVR.") |
self.logger.info("Successfuly connected to NVR.") |
||||
return |
return |
||||
except SomethingIsWrongWithCamera: |
except SomethingIsWrongWithCamera: |
||||
self.logger.error("Can't connect to NVR") |
self.logger.error("Can't connect to NVR") |
||||
self.nvr.close() |
self.nvr.close() |
||||
|
|
||||
def logout(self): |
def logout(self): |
||||
self.nvr.close() |
self.nvr.close() |
||||
|
|
||||
def get_channel_statuses(self): |
def get_channel_statuses(self): |
||||
channel_statuses = self.nvr.get_channel_statuses() |
channel_statuses = self.nvr.get_channel_statuses() |
||||
if 'Ret' in channel_statuses: |
if 'Ret' in channel_statuses: |
||||
return None |
return None |
||||
|
|
||||
channel_titles = self.nvr.get_channel_titles() |
channel_titles = self.nvr.get_channel_titles() |
||||
if 'Ret' in channel_titles: |
if 'Ret' in channel_titles: |
||||
return None |
return None |
||||
|
|
||||
for i in range(min(len(channel_statuses), len(channel_titles))): |
for i in range(min(len(channel_statuses), len(channel_titles))): |
||||
channel_statuses[i]['Title'] = channel_titles[i] |
channel_statuses[i]['Title'] = channel_titles[i] |
||||
channel_statuses[i]['Channel'] = i |
channel_statuses[i]['Channel'] = i |
||||
|
|
||||
return [c for c in channel_statuses if c['Status'] != ''] |
return [c for c in channel_statuses if c['Status'] != ''] |
||||
|
|
||||
def get_local_files(self, channel, start, end, filetype): |
def get_local_files(self, channel, start, end, filetype): |
||||
return self.nvr.list_local_files(start, end, filetype, channel) |
return self.nvr.list_local_files(start, end, filetype, channel) |
||||
|
|
||||
def generateTargetFileName(self, filename): |
def generateTargetFileName(self, filename): |
||||
# My NVR's filename example: /idea0/2023-11-19/002/05.38.58-05.39.34[M][@69f17][0].h264 |
# My NVR's filename example: /idea0/2023-11-19/002/05.38.58-05.39.34[M][@69f17][0].h264 |
||||
# You should check file names in your NVR and review the transformation |
# You should check file names in your NVR and review the transformation |
||||
filenameSplit = filename.replace("][", "/").replace("[", "/").replace("]", "/").split("/") |
filenameSplit = filename.replace("][", "/").replace("[", "/").replace("]", "/").split("/") |
||||
return f"{filenameSplit[3]}_{filenameSplit[2]}_{filenameSplit[4]}{filenameSplit[-1]}" |
return f"{filenameSplit[3]}_{filenameSplit[2]}_{filenameSplit[4]}{filenameSplit[-1]}" |
||||
|
|
||||
def save_files(self, download_dir, files): |
def save_files(self, download_dir, files): |
||||
self.logger.info(f"Files downloading: start") |
self.logger.info(f"Files downloading: start") |
||||
|
|
||||
size_to_download = sum(int(f['FileLength'], 0) for f in files) |
size_to_download = sum(int(f['FileLength'], 0) for f in files) |
||||
|
|
||||
for file in files: |
for file in files: |
||||
target_file_name = self.generateTargetFileName(file["FileName"]) |
target_file_name = self.generateTargetFileName(file["FileName"]) |
||||
target_file_path = f"{download_dir}/{target_file_name}" |
target_file_path = f"{download_dir}/{target_file_name}" |
||||
|
|
||||
size = int(file['FileLength'], 0) |
size = int(file['FileLength'], 0) |
||||
size_to_download -= size |
size_to_download -= size |
||||
|
|
||||
if Path(f"{target_file_path}").is_file(): |
if Path(f"{target_file_path}").is_file(): |
||||
self.logger.info(f" {target_file_name} file already exists, skipping download") |
self.logger.info(f" {target_file_name} file already exists, skipping download") |
||||
continue |
continue |
||||
|
|
||||
self.logger.info(f" {target_file_name} [{size/1024:.1f} MBytes] downloading...") |
self.logger.info(f" {target_file_name} [{size/1024:.1f} MBytes] downloading...") |
||||
time_dl = monotonic() |
time_dl = monotonic() |
||||
self.nvr.download_file( |
self.nvr.download_file( |
||||
file["BeginTime"], file["EndTime"], file["FileName"], target_file_path |
file["BeginTime"], file["EndTime"], file["FileName"], target_file_path |
||||
) |
) |
||||
time_dl = monotonic() - time_dl |
time_dl = monotonic() - time_dl |
||||
speed = size / time_dl |
speed = size / time_dl |
||||
self.logger.info(f" Done [{speed:.1f} KByte/s] {size_to_download/1024:.1f} MBytes more to download") |
self.logger.info(f" Done [{speed:.1f} KByte/s] {size_to_download/1024:.1f} MBytes more to download") |
||||
|
|
||||
self.logger.info(f"Files downloading: done") |
self.logger.info(f"Files downloading: done") |
||||
|
|
||||
def list_files(self, files): |
def list_files(self, files): |
||||
self.logger.info(f"Files listing: start") |
self.logger.info(f"Files listing: start") |
||||
|
|
||||
for file in files: |
for file in files: |
||||
target_file_name = self.generateTargetFileName(file["FileName"]) |
target_file_name = self.generateTargetFileName(file["FileName"]) |
||||
|
|
||||
size = int(file['FileLength'], 0) |
size = int(file['FileLength'], 0) |
||||
self.logger.info(f" {target_file_name} [{size/1024:.1f} MBytes]") |
self.logger.info(f" {target_file_name} [{size/1024:.1f} MBytes]") |
||||
|
|
||||
self.logger.info(f"Files listing: end") |
self.logger.info(f"Files listing: end") |
||||
|
@ -1,11 +1,11 @@ |
|||||
{ |
{ |
||||
"host_ip": "10.0.0.8", |
"host_ip": "10.0.0.8", |
||||
"user": "admin", |
"user": "admin", |
||||
"password": "mypassword", |
"password": "mypassword", |
||||
"channel": 0, |
"channel": 0, |
||||
"download_dir": "./download", |
"download_dir": "./download", |
||||
"start": "2023-11-19 6:22:34", |
"start": "2023-11-19 6:22:34", |
||||
"end": "2023-11-19 6:23:09", |
"end": "2023-11-19 6:23:09", |
||||
"just_list_files": false, |
"just_list_files": false, |
||||
"log_level": "INFO" |
"log_level": "INFO" |
||||
} |
} |
||||
|
@ -1,89 +1,89 @@ |
|||||
from pathlib import Path |
from pathlib import Path |
||||
import os |
import os |
||||
import json |
import json |
||||
import logging |
import logging |
||||
from collections import namedtuple |
from collections import namedtuple |
||||
from NVR import NVR |
from NVR import NVR |
||||
|
|
||||
|
|
||||
def init_logger(log_level): |
def init_logger(log_level): |
||||
logger = logging.getLogger(__name__) |
logger = logging.getLogger(__name__) |
||||
logger.setLevel(log_level) |
logger.setLevel(log_level) |
||||
ch = logging.StreamHandler() |
ch = logging.StreamHandler() |
||||
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") |
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") |
||||
ch.setFormatter(formatter) |
ch.setFormatter(formatter) |
||||
logger.addHandler(ch) |
logger.addHandler(ch) |
||||
return logger |
return logger |
||||
|
|
||||
|
|
||||
def load_config(): |
def load_config(): |
||||
def config_decoder(config_dict): |
def config_decoder(config_dict): |
||||
return namedtuple("X", config_dict.keys())(*config_dict.values()) |
return namedtuple("X", config_dict.keys())(*config_dict.values()) |
||||
|
|
||||
config_path = os.environ.get("NVRVIDEODOWNLOADER_CFG") |
config_path = os.environ.get("NVRVIDEODOWNLOADER_CFG") |
||||
|
|
||||
if config_path is None or not Path(config_path).exists(): |
if config_path is None or not Path(config_path).exists(): |
||||
config_path = "NVRVideoDownloader.json" |
config_path = "NVRVideoDownloader.json" |
||||
|
|
||||
if Path(config_path).exists(): |
if Path(config_path).exists(): |
||||
with open(config_path, "r") as file: |
with open(config_path, "r") as file: |
||||
return json.loads(file.read(), object_hook=config_decoder) |
return json.loads(file.read(), object_hook=config_decoder) |
||||
|
|
||||
return { |
return { |
||||
"host_ip": os.environ.get("IP_ADDRESS"), |
"host_ip": os.environ.get("IP_ADDRESS"), |
||||
"user": os.environ.get("USER"), |
"user": os.environ.get("USER"), |
||||
"password": os.environ.get("PASSWORD"), |
"password": os.environ.get("PASSWORD"), |
||||
"channel": os.environ.get("CHANNEL"), |
"channel": os.environ.get("CHANNEL"), |
||||
"download_dir": os.environ.get("DOWNLOAD_DIR"), |
"download_dir": os.environ.get("DOWNLOAD_DIR"), |
||||
"start": os.environ.get("START"), |
"start": os.environ.get("START"), |
||||
"end": os.environ.get("END"), |
"end": os.environ.get("END"), |
||||
"just_list_files": os.environ.get("DUMP_LOCAL_FILES").lower() in ["true", "1", "y", "yes"], |
"just_list_files": os.environ.get("DUMP_LOCAL_FILES").lower() in ["true", "1", "y", "yes"], |
||||
"log_level": "INFO" |
"log_level": "INFO" |
||||
} |
} |
||||
|
|
||||
|
|
||||
def main(): |
def main(): |
||||
config = load_config() |
config = load_config() |
||||
logger = init_logger(config.log_level) |
logger = init_logger(config.log_level) |
||||
channel = config.channel; |
channel = config.channel; |
||||
start = config.start |
start = config.start |
||||
end = config.end |
end = config.end |
||||
just_list_files = config.just_list_files; |
just_list_files = config.just_list_files; |
||||
|
|
||||
nvr = NVR(config.host_ip, config.user, config.password, logger) |
nvr = NVR(config.host_ip, config.user, config.password, logger) |
||||
|
|
||||
try: |
try: |
||||
nvr.login() |
nvr.login() |
||||
|
|
||||
channel_statuses = nvr.get_channel_statuses() |
channel_statuses = nvr.get_channel_statuses() |
||||
if channel_statuses: |
if channel_statuses: |
||||
channel_statuses_short = [{f"{c['Channel']}:{c['Title']}({c['ChnName']})"} |
channel_statuses_short = [{f"{c['Channel']}:{c['Title']}({c['ChnName']})"} |
||||
for c in channel_statuses if c['Status'] != 'NoConfig'] |
for c in channel_statuses if c['Status'] != 'NoConfig'] |
||||
logger.info(f"Configured channels in NVR: {channel_statuses_short}") |
logger.info(f"Configured channels in NVR: {channel_statuses_short}") |
||||
|
|
||||
videos = nvr.get_local_files(channel, start, end, "h264") |
videos = nvr.get_local_files(channel, start, end, "h264") |
||||
if videos: |
if videos: |
||||
size = sum(int(f['FileLength'], 0) for f in videos) |
size = sum(int(f['FileLength'], 0) for f in videos) |
||||
logger.info(f"Video files found: {len(videos)}. Total size: {size/1024:.1f}M") |
logger.info(f"Video files found: {len(videos)}. Total size: {size/1024:.1f}M") |
||||
Path(config.download_dir).parent.mkdir( |
Path(config.download_dir).parent.mkdir( |
||||
parents=True, exist_ok=True |
parents=True, exist_ok=True |
||||
) |
) |
||||
if just_list_files: |
if just_list_files: |
||||
nvr.list_files(videos) |
nvr.list_files(videos) |
||||
else: |
else: |
||||
nvr.save_files(config.download_dir, videos) |
nvr.save_files(config.download_dir, videos) |
||||
else: |
else: |
||||
logger.info(f"No video files found") |
logger.info(f"No video files found") |
||||
|
|
||||
nvr.logout() |
nvr.logout() |
||||
except ConnectionRefusedError: |
except ConnectionRefusedError: |
||||
logger.error(f"Connection can't be established or got disconnected") |
logger.error(f"Connection can't be established or got disconnected") |
||||
except TypeError as e: |
except TypeError as e: |
||||
print(e) |
print(e) |
||||
logger.error(f"Error while downloading a file") |
logger.error(f"Error while downloading a file") |
||||
except KeyError: |
except KeyError: |
||||
logger.error(f"Error while getting the file list") |
logger.error(f"Error while getting the file list") |
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
if __name__ == "__main__": |
||||
main() |
main() |
||||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,46 +1,46 @@ |
|||||
#!/usr/bin/env python |
#!/usr/bin/env python |
||||
# -*- coding: UTF-8 -*- |
# -*- coding: UTF-8 -*- |
||||
import sys |
import sys |
||||
from dvrip import DVRIPCam |
from dvrip import DVRIPCam |
||||
from time import sleep |
from time import sleep |
||||
import json |
import json |
||||
|
|
||||
host_ip = "192.168.0.100" |
host_ip = "192.168.0.100" |
||||
if len(sys.argv) > 1: |
if len(sys.argv) > 1: |
||||
host_ip = str(sys.argv[1]) |
host_ip = str(sys.argv[1]) |
||||
|
|
||||
cam = DVRIPCam(host_ip, user="admin", password="46216") |
cam = DVRIPCam(host_ip, user="admin", password="46216") |
||||
|
|
||||
if cam.login(): |
if cam.login(): |
||||
print("Success! Connected to " + host_ip) |
print("Success! Connected to " + host_ip) |
||||
else: |
else: |
||||
print("Failure. Could not connect.") |
print("Failure. Could not connect.") |
||||
|
|
||||
info = cam.get_info("fVideo.OSDInfo") |
info = cam.get_info("fVideo.OSDInfo") |
||||
print(json.dumps(info, ensure_ascii=False)) |
print(json.dumps(info, ensure_ascii=False)) |
||||
info["OSDInfo"][0]["Info"] = [u"Тест00", "Test01", "Test02"] |
info["OSDInfo"][0]["Info"] = [u"Тест00", "Test01", "Test02"] |
||||
# info["OSDInfo"][0]["Info"][1] = "" |
# info["OSDInfo"][0]["Info"][1] = "" |
||||
# info["OSDInfo"][0]["Info"][2] = "" |
# info["OSDInfo"][0]["Info"][2] = "" |
||||
# info["OSDInfo"][0]["Info"][3] = "Test3" |
# info["OSDInfo"][0]["Info"][3] = "Test3" |
||||
info["OSDInfo"][0]["OSDInfoWidget"]["EncodeBlend"] = True |
info["OSDInfo"][0]["OSDInfoWidget"]["EncodeBlend"] = True |
||||
info["OSDInfo"][0]["OSDInfoWidget"]["PreviewBlend"] = True |
info["OSDInfo"][0]["OSDInfoWidget"]["PreviewBlend"] = True |
||||
# info["OSDInfo"][0]["OSDInfoWidget"]["RelativePos"] = [6144,6144,8192,8192] |
# info["OSDInfo"][0]["OSDInfoWidget"]["RelativePos"] = [6144,6144,8192,8192] |
||||
cam.set_info("fVideo.OSDInfo", info) |
cam.set_info("fVideo.OSDInfo", info) |
||||
# enc_info = cam.get_info("Simplify.Encode") |
# enc_info = cam.get_info("Simplify.Encode") |
||||
# Alarm example |
# Alarm example |
||||
def alarm(content, ids): |
def alarm(content, ids): |
||||
print(content) |
print(content) |
||||
|
|
||||
|
|
||||
cam.setAlarm(alarm) |
cam.setAlarm(alarm) |
||||
cam.alarmStart() |
cam.alarmStart() |
||||
# cam.get_encode_info() |
# cam.get_encode_info() |
||||
# sleep(1) |
# sleep(1) |
||||
# cam.get_camera_info() |
# cam.get_camera_info() |
||||
# sleep(1) |
# sleep(1) |
||||
|
|
||||
# enc_info[0]['ExtraFormat']['Video']['FPS'] = 20 |
# enc_info[0]['ExtraFormat']['Video']['FPS'] = 20 |
||||
# cam.set_info("Simplify.Encode", enc_info) |
# cam.set_info("Simplify.Encode", enc_info) |
||||
# sleep(2) |
# sleep(2) |
||||
# print(cam.get_info("Simplify.Encode")) |
# print(cam.get_info("Simplify.Encode")) |
||||
# cam.close() |
# cam.close() |
||||
|
@ -1,127 +1,127 @@ |
|||||
from pathlib import Path |
from pathlib import Path |
||||
from time import sleep |
from time import sleep |
||||
import os |
import os |
||||
import json |
import json |
||||
import logging |
import logging |
||||
from collections import namedtuple |
from collections import namedtuple |
||||
from solarcam import SolarCam |
from solarcam import SolarCam |
||||
|
|
||||
|
|
||||
def init_logger(): |
def init_logger(): |
||||
logger = logging.getLogger(__name__) |
logger = logging.getLogger(__name__) |
||||
logger.setLevel(logging.DEBUG) |
logger.setLevel(logging.DEBUG) |
||||
ch = logging.StreamHandler() |
ch = logging.StreamHandler() |
||||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") |
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") |
||||
ch.setFormatter(formatter) |
ch.setFormatter(formatter) |
||||
logger.addHandler(ch) |
logger.addHandler(ch) |
||||
return logger |
return logger |
||||
|
|
||||
|
|
||||
def load_config(): |
def load_config(): |
||||
def config_decoder(config_dict): |
def config_decoder(config_dict): |
||||
return namedtuple("X", config_dict.keys())(*config_dict.values()) |
return namedtuple("X", config_dict.keys())(*config_dict.values()) |
||||
|
|
||||
config_path = os.environ.get("CONFIG_PATH") |
config_path = os.environ.get("CONFIG_PATH") |
||||
if Path(config_path).exists(): |
if Path(config_path).exists(): |
||||
with open(config_path, "r") as file: |
with open(config_path, "r") as file: |
||||
return json.loads(file.read(), object_hook=config_decoder) |
return json.loads(file.read(), object_hook=config_decoder) |
||||
|
|
||||
return { |
return { |
||||
"host_ip": os.environ.get("IP_ADDRESS"), |
"host_ip": os.environ.get("IP_ADDRESS"), |
||||
"user": os.environ.get("USER"), |
"user": os.environ.get("USER"), |
||||
"password": os.environ.get("PASSWORD"), |
"password": os.environ.get("PASSWORD"), |
||||
"target_filetype_video": os.environ.get("target_filetype_video"), |
"target_filetype_video": os.environ.get("target_filetype_video"), |
||||
"download_dir_video": os.environ.get("DOWNLOAD_DIR_VIDEO"), |
"download_dir_video": os.environ.get("DOWNLOAD_DIR_VIDEO"), |
||||
"download_dir_picture": os.environ.get("DOWNLOAD_DIR_PICTURE"), |
"download_dir_picture": os.environ.get("DOWNLOAD_DIR_PICTURE"), |
||||
"start": os.environ.get("START"), |
"start": os.environ.get("START"), |
||||
"end": os.environ.get("END"), |
"end": os.environ.get("END"), |
||||
"blacklist_path": os.environ.get("BLACKLIST_PATH"), |
"blacklist_path": os.environ.get("BLACKLIST_PATH"), |
||||
"cooldown": int(os.environ.get("COOLDOWN")), |
"cooldown": int(os.environ.get("COOLDOWN")), |
||||
"dump_local_files": ( |
"dump_local_files": ( |
||||
os.environ.get("DUMP_LOCAL_FILES").lower() in ["true", "1", "y", "yes"] |
os.environ.get("DUMP_LOCAL_FILES").lower() in ["true", "1", "y", "yes"] |
||||
), |
), |
||||
} |
} |
||||
|
|
||||
|
|
||||
def main(): |
def main(): |
||||
logger = init_logger() |
logger = init_logger() |
||||
config = load_config() |
config = load_config() |
||||
start = config.start |
start = config.start |
||||
end = config.end |
end = config.end |
||||
cooldown = config.cooldown |
cooldown = config.cooldown |
||||
|
|
||||
blacklist = None |
blacklist = None |
||||
if Path(config.blacklist_path).exists(): |
if Path(config.blacklist_path).exists(): |
||||
with open(config.blacklist_path, "r") as file: |
with open(config.blacklist_path, "r") as file: |
||||
blacklist = [line.rstrip() for line in file] |
blacklist = [line.rstrip() for line in file] |
||||
|
|
||||
while True: |
while True: |
||||
solarCam = SolarCam(config.host_ip, config.user, config.password, logger) |
solarCam = SolarCam(config.host_ip, config.user, config.password, logger) |
||||
|
|
||||
try: |
try: |
||||
solarCam.login() |
solarCam.login() |
||||
|
|
||||
battery = solarCam.get_battery() |
battery = solarCam.get_battery() |
||||
logger.debug(f"Current battery status: {battery}") |
logger.debug(f"Current battery status: {battery}") |
||||
storage = solarCam.get_storage()[0] |
storage = solarCam.get_storage()[0] |
||||
logger.debug(f"Current storage status: {storage}") |
logger.debug(f"Current storage status: {storage}") |
||||
|
|
||||
logger.debug(f"Syncing time...") |
logger.debug(f"Syncing time...") |
||||
solarCam.set_time() # setting it to system clock |
solarCam.set_time() # setting it to system clock |
||||
logger.debug(f"Camera time is now {solarCam.get_time()}") |
logger.debug(f"Camera time is now {solarCam.get_time()}") |
||||
|
|
||||
sleep(5) # sleep some seconds so camera can get ready |
sleep(5) # sleep some seconds so camera can get ready |
||||
|
|
||||
pics = solarCam.get_local_files(start, end, "jpg") |
pics = solarCam.get_local_files(start, end, "jpg") |
||||
|
|
||||
if pics: |
if pics: |
||||
Path(config.download_dir_picture).parent.mkdir( |
Path(config.download_dir_picture).parent.mkdir( |
||||
parents=True, exist_ok=True |
parents=True, exist_ok=True |
||||
) |
) |
||||
solarCam.save_files( |
solarCam.save_files( |
||||
config.download_dir_picture, pics, blacklist=blacklist |
config.download_dir_picture, pics, blacklist=blacklist |
||||
) |
) |
||||
|
|
||||
videos = solarCam.get_local_files(start, end, "h264") |
videos = solarCam.get_local_files(start, end, "h264") |
||||
if videos: |
if videos: |
||||
Path(config.download_dir_video).parent.mkdir( |
Path(config.download_dir_video).parent.mkdir( |
||||
parents=True, exist_ok=True |
parents=True, exist_ok=True |
||||
) |
) |
||||
solarCam.save_files( |
solarCam.save_files( |
||||
config.download_dir_video, |
config.download_dir_video, |
||||
videos, |
videos, |
||||
blacklist=blacklist, |
blacklist=blacklist, |
||||
target_filetype=config.target_filetype_video, |
target_filetype=config.target_filetype_video, |
||||
) |
) |
||||
|
|
||||
if config.dump_local_files: |
if config.dump_local_files: |
||||
logger.debug(f"Dumping local files...") |
logger.debug(f"Dumping local files...") |
||||
solarCam.dump_local_files( |
solarCam.dump_local_files( |
||||
videos, |
videos, |
||||
config.blacklist_path, |
config.blacklist_path, |
||||
config.download_dir_video, |
config.download_dir_video, |
||||
target_filetype=config.target_filetype_video, |
target_filetype=config.target_filetype_video, |
||||
) |
) |
||||
solarCam.dump_local_files( |
solarCam.dump_local_files( |
||||
pics, config.blacklist_path, config.download_dir_picture |
pics, config.blacklist_path, config.download_dir_picture |
||||
) |
) |
||||
|
|
||||
solarCam.logout() |
solarCam.logout() |
||||
except ConnectionRefusedError: |
except ConnectionRefusedError: |
||||
logger.debug(f"Connection could not be established or got disconnected") |
logger.debug(f"Connection could not be established or got disconnected") |
||||
except TypeError as e: |
except TypeError as e: |
||||
print(e) |
print(e) |
||||
logger.debug(f"Error while downloading a file") |
logger.debug(f"Error while downloading a file") |
||||
except KeyError: |
except KeyError: |
||||
logger.debug(f"Error while getting the file list") |
logger.debug(f"Error while getting the file list") |
||||
logger.debug(f"Sleeping for {cooldown} seconds...") |
logger.debug(f"Sleeping for {cooldown} seconds...") |
||||
sleep(cooldown) |
sleep(cooldown) |
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
if __name__ == "__main__": |
||||
main() |
main() |
||||
|
|
||||
# todo add flask api for moving cam |
# todo add flask api for moving cam |
||||
# todo show current stream |
# todo show current stream |
||||
# todo show battery on webinterface and write it to mqtt topic |
# todo show battery on webinterface and write it to mqtt topic |
||||
# todo change camera name |
# todo change camera name |
||||
|
File diff suppressed because it is too large
@ -1,17 +1,17 @@ |
|||||
FROM python:3.10-slim-buster |
FROM python:3.10-slim-buster |
||||
|
|
||||
RUN apt-get update && \ |
RUN apt-get update && \ |
||||
apt-get upgrade -y && \ |
apt-get upgrade -y && \ |
||||
apt-get install -y \ |
apt-get install -y \ |
||||
git \ |
git \ |
||||
curl |
curl |
||||
|
|
||||
WORKDIR /app |
WORKDIR /app |
||||
|
|
||||
COPY . . |
COPY . . |
||||
|
|
||||
RUN pip3 install -r requirements.txt |
RUN pip3 install -r requirements.txt |
||||
|
|
||||
EXPOSE 8888 |
EXPOSE 8888 |
||||
|
|
||||
CMD [ "python3", "./app.py"] |
CMD [ "python3", "./app.py"] |
||||
|
@ -1,15 +1,15 @@ |
|||||
### SocketIO example |
### SocketIO example |
||||
|
|
||||
Build image |
Build image |
||||
```bash |
```bash |
||||
docker build -t video-stream . |
docker build -t video-stream . |
||||
``` |
``` |
||||
|
|
||||
Run container |
Run container |
||||
```bash |
```bash |
||||
docker run -d \ |
docker run -d \ |
||||
--restart always \ |
--restart always \ |
||||
--network host \ |
--network host \ |
||||
--name video-stream \ |
--name video-stream \ |
||||
video-stream |
video-stream |
||||
``` |
``` |
||||
|
@ -1,107 +1,107 @@ |
|||||
import socketio |
import socketio |
||||
from asyncio_dvrip import DVRIPCam |
from asyncio_dvrip import DVRIPCam |
||||
from aiohttp import web |
from aiohttp import web |
||||
import asyncio |
import asyncio |
||||
import signal |
import signal |
||||
import traceback |
import traceback |
||||
import base64 |
import base64 |
||||
|
|
||||
loop = asyncio.get_event_loop() |
loop = asyncio.get_event_loop() |
||||
queue = asyncio.Queue() |
queue = asyncio.Queue() |
||||
|
|
||||
# socket clients |
# socket clients |
||||
clients = [] |
clients = [] |
||||
sio = socketio.AsyncServer() |
sio = socketio.AsyncServer() |
||||
app = web.Application() |
app = web.Application() |
||||
sio.attach(app) |
sio.attach(app) |
||||
|
|
||||
@sio.event |
@sio.event |
||||
def connect(sid, environ): |
def connect(sid, environ): |
||||
print("connect ", sid) |
print("connect ", sid) |
||||
clients.append(sid) |
clients.append(sid) |
||||
|
|
||||
@sio.event |
@sio.event |
||||
def my_message(sid, data): |
def my_message(sid, data): |
||||
print('message ', data) |
print('message ', data) |
||||
|
|
||||
@sio.event |
@sio.event |
||||
def disconnect(sid): |
def disconnect(sid): |
||||
print('disconnect ', sid) |
print('disconnect ', sid) |
||||
clients.remove(sid) |
clients.remove(sid) |
||||
|
|
||||
def stop(loop): |
def stop(loop): |
||||
loop.remove_signal_handler(signal.SIGTERM) |
loop.remove_signal_handler(signal.SIGTERM) |
||||
tasks = asyncio.gather(*asyncio.Task.all_tasks(loop=loop), loop=loop, return_exceptions=True) |
tasks = asyncio.gather(*asyncio.Task.all_tasks(loop=loop), loop=loop, return_exceptions=True) |
||||
tasks.add_done_callback(lambda t: loop.stop()) |
tasks.add_done_callback(lambda t: loop.stop()) |
||||
tasks.cancel() |
tasks.cancel() |
||||
|
|
||||
async def stream(loop, queue): |
async def stream(loop, queue): |
||||
cam = DVRIPCam("192.168.0.100", port=34567, user="admin", password="") |
cam = DVRIPCam("192.168.0.100", port=34567, user="admin", password="") |
||||
# login |
# login |
||||
if not await cam.login(loop): |
if not await cam.login(loop): |
||||
raise Exception("Can't open cam") |
raise Exception("Can't open cam") |
||||
|
|
||||
try: |
try: |
||||
await cam.start_monitor(lambda frame, meta, user: queue.put_nowait(frame), stream="Main") |
await cam.start_monitor(lambda frame, meta, user: queue.put_nowait(frame), stream="Main") |
||||
except Exception as err: |
except Exception as err: |
||||
msg = ''.join(traceback.format_tb(err.__traceback__) + [str(err)]) |
msg = ''.join(traceback.format_tb(err.__traceback__) + [str(err)]) |
||||
print(msg) |
print(msg) |
||||
finally: |
finally: |
||||
cam.stop_monitor() |
cam.stop_monitor() |
||||
cam.close() |
cam.close() |
||||
|
|
||||
async def process(queue, lock): |
async def process(queue, lock): |
||||
while True: |
while True: |
||||
frame = await queue.get() |
frame = await queue.get() |
||||
|
|
||||
if frame: |
if frame: |
||||
await lock.acquire() |
await lock.acquire() |
||||
try: |
try: |
||||
for sid in clients: |
for sid in clients: |
||||
await sio.emit('message', {'data': base64.b64encode(frame).decode("utf-8")}, room=sid) |
await sio.emit('message', {'data': base64.b64encode(frame).decode("utf-8")}, room=sid) |
||||
finally: |
finally: |
||||
lock.release() |
lock.release() |
||||
|
|
||||
async def worker(loop, queue, lock): |
async def worker(loop, queue, lock): |
||||
task = None |
task = None |
||||
|
|
||||
# infinyty loop |
# infinyty loop |
||||
while True: |
while True: |
||||
await lock.acquire() |
await lock.acquire() |
||||
|
|
||||
try: |
try: |
||||
# got clients and task not started |
# got clients and task not started |
||||
if len(clients) > 0 and task is None: |
if len(clients) > 0 and task is None: |
||||
# create stream task |
# create stream task |
||||
task = loop.create_task(stream(loop, queue)) |
task = loop.create_task(stream(loop, queue)) |
||||
|
|
||||
# no more clients, neet stop task |
# no more clients, neet stop task |
||||
if len(clients) == 0 and task is not None: |
if len(clients) == 0 and task is not None: |
||||
# I don't like this way, maybe someone can do it better |
# I don't like this way, maybe someone can do it better |
||||
task.cancel() |
task.cancel() |
||||
task = None |
task = None |
||||
await asyncio.sleep(0.1) |
await asyncio.sleep(0.1) |
||||
except Exception as err: |
except Exception as err: |
||||
msg = ''.join(traceback.format_tb(err.__traceback__) + [str(err)]) |
msg = ''.join(traceback.format_tb(err.__traceback__) + [str(err)]) |
||||
print(msg) |
print(msg) |
||||
finally: |
finally: |
||||
lock.release() |
lock.release() |
||||
|
|
||||
if __name__ == '__main__': |
if __name__ == '__main__': |
||||
try: |
try: |
||||
lock = asyncio.Lock() |
lock = asyncio.Lock() |
||||
|
|
||||
# run wb application |
# run wb application |
||||
runner = web.AppRunner(app) |
runner = web.AppRunner(app) |
||||
loop.run_until_complete(runner.setup()) |
loop.run_until_complete(runner.setup()) |
||||
site = web.TCPSite(runner, host='0.0.0.0', port=8888) |
site = web.TCPSite(runner, host='0.0.0.0', port=8888) |
||||
loop.run_until_complete(site.start()) |
loop.run_until_complete(site.start()) |
||||
|
|
||||
# run worker |
# run worker |
||||
loop.create_task(worker(loop, queue, lock)) |
loop.create_task(worker(loop, queue, lock)) |
||||
loop.create_task(process(queue, lock)) |
loop.create_task(process(queue, lock)) |
||||
|
|
||||
# wait stop |
# wait stop |
||||
loop.run_forever() |
loop.run_forever() |
||||
except: |
except: |
||||
stop(loop) |
stop(loop) |
||||
|
@ -1,18 +1,18 @@ |
|||||
import socketio |
import socketio |
||||
|
|
||||
# standard Python |
# standard Python |
||||
sio = socketio.Client() |
sio = socketio.Client() |
||||
|
|
||||
@sio.event |
@sio.event |
||||
def connect(): |
def connect(): |
||||
print("I'm connected!") |
print("I'm connected!") |
||||
|
|
||||
@sio.event |
@sio.event |
||||
def connect_error(): |
def connect_error(): |
||||
print("The connection failed!") |
print("The connection failed!") |
||||
|
|
||||
@sio.on('message') |
@sio.on('message') |
||||
def on_message(data): |
def on_message(data): |
||||
print('frame', data) |
print('frame', data) |
||||
|
|
||||
sio.connect('http://localhost:8888') |
sio.connect('http://localhost:8888') |
@ -1,14 +1,14 @@ |
|||||
aiohttp==3.8.5 |
aiohttp==3.8.5 |
||||
aiosignal==1.3.1 |
aiosignal==1.3.1 |
||||
async-timeout==4.0.2 |
async-timeout==4.0.2 |
||||
asyncio==3.4.3 |
asyncio==3.4.3 |
||||
attrs==22.1.0 |
attrs==22.1.0 |
||||
bidict==0.22.0 |
bidict==0.22.0 |
||||
charset-normalizer==2.1.1 |
charset-normalizer==2.1.1 |
||||
frozenlist==1.3.3 |
frozenlist==1.3.3 |
||||
idna==3.4 |
idna==3.4 |
||||
multidict==6.0.2 |
multidict==6.0.2 |
||||
python-dvr @ git+https://github.com/NeiroNx/python-dvr@06ff6dc0082767e7c9f23401f828533459f783a4 |
python-dvr @ git+https://github.com/NeiroNx/python-dvr@06ff6dc0082767e7c9f23401f828533459f783a4 |
||||
python-engineio==4.3.4 |
python-engineio==4.3.4 |
||||
python-socketio==5.7.2 |
python-socketio==5.7.2 |
||||
yarl==1.8.1 |
yarl==1.8.1 |
||||
|
@ -1,121 +1,121 @@ |
|||||
#! /usr/bin/python3 |
#! /usr/bin/python3 |
||||
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
||||
from signal import signal, SIGINT, SIGTERM |
from signal import signal, SIGINT, SIGTERM |
||||
from sys import argv, stdout, exit |
from sys import argv, stdout, exit |
||||
from datetime import datetime |
from datetime import datetime |
||||
from pathlib import Path |
from pathlib import Path |
||||
from time import sleep, time |
from time import sleep, time |
||||
import logging |
import logging |
||||
|
|
||||
baseDir = argv[3] |
baseDir = argv[3] |
||||
retryIn = 5 |
retryIn = 5 |
||||
rebootWait = 10 |
rebootWait = 10 |
||||
camIp = argv[1] |
camIp = argv[1] |
||||
camName = argv[2] |
camName = argv[2] |
||||
cam = None |
cam = None |
||||
isShuttingDown = False |
isShuttingDown = False |
||||
chunkSize = 600 # new file every 10 minutes |
chunkSize = 600 # new file every 10 minutes |
||||
logFile = baseDir + '/' + camName + '/log.log' |
logFile = baseDir + '/' + camName + '/log.log' |
||||
|
|
||||
def log(str): |
def log(str): |
||||
logging.info(str) |
logging.info(str) |
||||
|
|
||||
def mkpath(): |
def mkpath(): |
||||
path = baseDir + '/' + camName + "/" + datetime.today().strftime('%Y/%m/%d/%H.%M.%S') |
path = baseDir + '/' + camName + "/" + datetime.today().strftime('%Y/%m/%d/%H.%M.%S') |
||||
Path(path).parent.mkdir(parents=True, exist_ok=True) |
Path(path).parent.mkdir(parents=True, exist_ok=True) |
||||
return path |
return path |
||||
|
|
||||
def shutDown(): |
def shutDown(): |
||||
global isShuttingDown |
global isShuttingDown |
||||
isShuttingDown = True |
isShuttingDown = True |
||||
log('Shutting down...') |
log('Shutting down...') |
||||
try: |
try: |
||||
cam.stop_monitor() |
cam.stop_monitor() |
||||
close() |
close() |
||||
except (RuntimeError, TypeError, NameError, Exception): |
except (RuntimeError, TypeError, NameError, Exception): |
||||
pass |
pass |
||||
log('done') |
log('done') |
||||
exit(0) |
exit(0) |
||||
|
|
||||
def handler(signum, b): |
def handler(signum, b): |
||||
log('Signal ' + str(signum) + ' received') |
log('Signal ' + str(signum) + ' received') |
||||
shutDown() |
shutDown() |
||||
|
|
||||
signal(SIGINT, handler) |
signal(SIGINT, handler) |
||||
signal(SIGTERM, handler) |
signal(SIGTERM, handler) |
||||
|
|
||||
def close(): |
def close(): |
||||
cam.close() |
cam.close() |
||||
|
|
||||
def theActualJob(): |
def theActualJob(): |
||||
|
|
||||
prevtime = 0 |
prevtime = 0 |
||||
video = None |
video = None |
||||
audio = None |
audio = None |
||||
|
|
||||
def receiver(frame, meta, user): |
def receiver(frame, meta, user): |
||||
nonlocal prevtime, video, audio |
nonlocal prevtime, video, audio |
||||
if frame is None: |
if frame is None: |
||||
log('Empty frame') |
log('Empty frame') |
||||
else: |
else: |
||||
tn = time() |
tn = time() |
||||
if tn - prevtime >= chunkSize: |
if tn - prevtime >= chunkSize: |
||||
if video != None: |
if video != None: |
||||
video.close() |
video.close() |
||||
audio.close() |
audio.close() |
||||
prevtime = tn |
prevtime = tn |
||||
path = mkpath() |
path = mkpath() |
||||
log('Starting files: ' + path) |
log('Starting files: ' + path) |
||||
video = open(path + '.video', "wb") |
video = open(path + '.video', "wb") |
||||
audio = open(path + '.audio', "wb") |
audio = open(path + '.audio', "wb") |
||||
if 'type' in meta and meta["type"] == "g711a": audio.write(frame) |
if 'type' in meta and meta["type"] == "g711a": audio.write(frame) |
||||
elif 'frame' in meta: video.write(frame) |
elif 'frame' in meta: video.write(frame) |
||||
|
|
||||
log('Starting to grab streams...') |
log('Starting to grab streams...') |
||||
cam.start_monitor(receiver) |
cam.start_monitor(receiver) |
||||
|
|
||||
def syncTime(): |
def syncTime(): |
||||
log('Synching time...') |
log('Synching time...') |
||||
cam.set_time() |
cam.set_time() |
||||
log('done') |
log('done') |
||||
|
|
||||
def jobWrapper(): |
def jobWrapper(): |
||||
global cam |
global cam |
||||
log('Logging in to camera ' + camIp + '...') |
log('Logging in to camera ' + camIp + '...') |
||||
cam = DVRIPCam(camIp) |
cam = DVRIPCam(camIp) |
||||
if cam.login(): |
if cam.login(): |
||||
log('done') |
log('done') |
||||
else: |
else: |
||||
raise SomethingIsWrongWithCamera('Cannot login') |
raise SomethingIsWrongWithCamera('Cannot login') |
||||
syncTime() |
syncTime() |
||||
theActualJob() |
theActualJob() |
||||
|
|
||||
def theJob(): |
def theJob(): |
||||
while True: |
while True: |
||||
try: |
try: |
||||
jobWrapper() |
jobWrapper() |
||||
except (TypeError, ValueError) as err: |
except (TypeError, ValueError) as err: |
||||
if isShuttingDown: |
if isShuttingDown: |
||||
exit(0) |
exit(0) |
||||
else: |
else: |
||||
try: |
try: |
||||
log('Error. Attempting to reboot camera...') |
log('Error. Attempting to reboot camera...') |
||||
cam.reboot() |
cam.reboot() |
||||
log('Waiting for ' + str(rebootWait) + 's for reboot...') |
log('Waiting for ' + str(rebootWait) + 's for reboot...') |
||||
sleep(rebootWait) |
sleep(rebootWait) |
||||
except (UnicodeDecodeError, ValueError, TypeError): |
except (UnicodeDecodeError, ValueError, TypeError): |
||||
raise SomethingIsWrongWithCamera('Failed to reboot') |
raise SomethingIsWrongWithCamera('Failed to reboot') |
||||
|
|
||||
def main(): |
def main(): |
||||
Path(logFile).parent.mkdir(parents=True, exist_ok=True) |
Path(logFile).parent.mkdir(parents=True, exist_ok=True) |
||||
logging.basicConfig(filename=logFile, level=logging.INFO, format='[%(asctime)s] %(message)s') |
logging.basicConfig(filename=logFile, level=logging.INFO, format='[%(asctime)s] %(message)s') |
||||
while True: |
while True: |
||||
try: |
try: |
||||
theJob() |
theJob() |
||||
except SomethingIsWrongWithCamera as err: |
except SomethingIsWrongWithCamera as err: |
||||
close() |
close() |
||||
log(str(err) + '. Waiting for ' + str(retryIn) + ' seconds before trying again...') |
log(str(err) + '. Waiting for ' + str(retryIn) + ' seconds before trying again...') |
||||
sleep(retryIn) |
sleep(retryIn) |
||||
|
|
||||
if __name__ == "__main__": |
if __name__ == "__main__": |
||||
main() |
main() |
@ -1,47 +1,47 @@ |
|||||
from setuptools import setup, find_packages |
from setuptools import setup, find_packages |
||||
import pathlib |
import pathlib |
||||
|
|
||||
here = pathlib.Path(__file__).parent.resolve() |
here = pathlib.Path(__file__).parent.resolve() |
||||
|
|
||||
# Get the long description from the README file |
# Get the long description from the README file |
||||
long_description = (here / 'README.md').read_text(encoding='utf-8') |
long_description = (here / 'README.md').read_text(encoding='utf-8') |
||||
|
|
||||
setup( |
setup( |
||||
name='python-dvr', |
name='python-dvr', |
||||
|
|
||||
version='0.0.0', |
version='0.0.0', |
||||
|
|
||||
description='Python library for configuring a wide range of IP cameras which use the NETsurveillance ActiveX plugin XMeye SDK', |
description='Python library for configuring a wide range of IP cameras which use the NETsurveillance ActiveX plugin XMeye SDK', |
||||
|
|
||||
long_description=long_description, |
long_description=long_description, |
||||
long_description_content_type='text/markdown', |
long_description_content_type='text/markdown', |
||||
|
|
||||
url='https://github.com/NeiroNx/python-dvr/', |
url='https://github.com/NeiroNx/python-dvr/', |
||||
|
|
||||
author='NeiroN', |
author='NeiroN', |
||||
|
|
||||
classifiers=[ |
classifiers=[ |
||||
'Development Status :: 3 - Alpha', |
'Development Status :: 3 - Alpha', |
||||
|
|
||||
'Intended Audience :: Developers', |
'Intended Audience :: Developers', |
||||
'Topic :: Multimedia :: Video :: Capture', |
'Topic :: Multimedia :: Video :: Capture', |
||||
|
|
||||
'License :: OSI Approved :: MIT License', |
'License :: OSI Approved :: MIT License', |
||||
|
|
||||
'Programming Language :: Python :: 3', |
'Programming Language :: Python :: 3', |
||||
'Programming Language :: Python :: 3.6', |
'Programming Language :: Python :: 3.6', |
||||
'Programming Language :: Python :: 3.7', |
'Programming Language :: Python :: 3.7', |
||||
'Programming Language :: Python :: 3.8', |
'Programming Language :: Python :: 3.8', |
||||
'Programming Language :: Python :: 3.9', |
'Programming Language :: Python :: 3.9', |
||||
'Programming Language :: Python :: 3 :: Only', |
'Programming Language :: Python :: 3 :: Only', |
||||
], |
], |
||||
|
|
||||
py_modules=["dvrip", "DeviceManager", "asyncio_dvrip"], |
py_modules=["dvrip", "DeviceManager", "asyncio_dvrip"], |
||||
|
|
||||
python_requires='>=3.6', |
python_requires='>=3.6', |
||||
|
|
||||
project_urls={ |
project_urls={ |
||||
'Bug Reports': 'https://github.com/NeiroNx/python-dvr/issues', |
'Bug Reports': 'https://github.com/NeiroNx/python-dvr/issues', |
||||
'Source': 'https://github.com/NeiroNx/python-dvr', |
'Source': 'https://github.com/NeiroNx/python-dvr', |
||||
}, |
}, |
||||
) |
) |
||||
|
@ -1,217 +1,217 @@ |
|||||
from time import sleep |
from time import sleep |
||||
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
from dvrip import DVRIPCam, SomethingIsWrongWithCamera |
||||
from pathlib import Path |
from pathlib import Path |
||||
import subprocess |
import subprocess |
||||
import json |
import json |
||||
from datetime import datetime |
from datetime import datetime |
||||
|
|
||||
|
|
||||
class SolarCam: |
class SolarCam: |
||||
cam = None |
cam = None |
||||
logger = None |
logger = None |
||||
|
|
||||
def __init__(self, host_ip, user, password, logger): |
def __init__(self, host_ip, user, password, logger): |
||||
self.logger = logger |
self.logger = logger |
||||
self.cam = DVRIPCam( |
self.cam = DVRIPCam( |
||||
host_ip, |
host_ip, |
||||
user=user, |
user=user, |
||||
password=password, |
password=password, |
||||
) |
) |
||||
|
|
||||
def login(self, num_retries=10): |
def login(self, num_retries=10): |
||||
for i in range(num_retries): |
for i in range(num_retries): |
||||
try: |
try: |
||||
self.logger.debug("Try login...") |
self.logger.debug("Try login...") |
||||
self.cam.login() |
self.cam.login() |
||||
self.logger.debug( |
self.logger.debug( |
||||
f"Success! Connected to Camera. Waiting few seconds to let Camera fully boot..." |
f"Success! Connected to Camera. Waiting few seconds to let Camera fully boot..." |
||||
) |
) |
||||
# waiting until camera is ready |
# waiting until camera is ready |
||||
sleep(10) |
sleep(10) |
||||
return |
return |
||||
except SomethingIsWrongWithCamera: |
except SomethingIsWrongWithCamera: |
||||
self.logger.debug("Could not connect...Camera could be offline") |
self.logger.debug("Could not connect...Camera could be offline") |
||||
self.cam.close() |
self.cam.close() |
||||
|
|
||||
if i == 9: |
if i == 9: |
||||
raise ConnectionRefusedError( |
raise ConnectionRefusedError( |
||||
f"Could not connect {num_retries} times...aborting" |
f"Could not connect {num_retries} times...aborting" |
||||
) |
) |
||||
sleep(2) |
sleep(2) |
||||
|
|
||||
def logout(self): |
def logout(self): |
||||
self.cam.close() |
self.cam.close() |
||||
|
|
||||
def get_time(self): |
def get_time(self): |
||||
return self.cam.get_time() |
return self.cam.get_time() |
||||
|
|
||||
def set_time(self, time=None): |
def set_time(self, time=None): |
||||
if time is None: |
if time is None: |
||||
time = datetime.now() |
time = datetime.now() |
||||
return self.cam.set_time(time=time) |
return self.cam.set_time(time=time) |
||||
|
|
||||
def get_local_files(self, start, end, filetype): |
def get_local_files(self, start, end, filetype): |
||||
return self.cam.list_local_files(start, end, filetype) |
return self.cam.list_local_files(start, end, filetype) |
||||
|
|
||||
def dump_local_files( |
def dump_local_files( |
||||
self, files, blacklist_path, download_dir, target_filetype=None |
self, files, blacklist_path, download_dir, target_filetype=None |
||||
): |
): |
||||
with open(f"{blacklist_path}.dmp", "a") as outfile: |
with open(f"{blacklist_path}.dmp", "a") as outfile: |
||||
for file in files: |
for file in files: |
||||
target_file_path = self.generateTargetFilePath( |
target_file_path = self.generateTargetFilePath( |
||||
file["FileName"], download_dir |
file["FileName"], download_dir |
||||
) |
) |
||||
outfile.write(f"{target_file_path}\n") |
outfile.write(f"{target_file_path}\n") |
||||
|
|
||||
if target_filetype: |
if target_filetype: |
||||
target_file_path_convert = self.generateTargetFilePath( |
target_file_path_convert = self.generateTargetFilePath( |
||||
file["FileName"], download_dir, extention=f"{target_filetype}" |
file["FileName"], download_dir, extention=f"{target_filetype}" |
||||
) |
) |
||||
outfile.write(f"{target_file_path_convert}\n") |
outfile.write(f"{target_file_path_convert}\n") |
||||
|
|
||||
def generateTargetFilePath(self, filename, downloadDir, extention=""): |
def generateTargetFilePath(self, filename, downloadDir, extention=""): |
||||
fileExtention = Path(filename).suffix |
fileExtention = Path(filename).suffix |
||||
filenameSplit = filename.split("/") |
filenameSplit = filename.split("/") |
||||
filenameDisk = f"{filenameSplit[3]}_{filenameSplit[5][:8]}".replace(".", "-") |
filenameDisk = f"{filenameSplit[3]}_{filenameSplit[5][:8]}".replace(".", "-") |
||||
targetPathClean = f"{downloadDir}/{filenameDisk}" |
targetPathClean = f"{downloadDir}/{filenameDisk}" |
||||
|
|
||||
if extention != "": |
if extention != "": |
||||
return f"{targetPathClean}{extention}" |
return f"{targetPathClean}{extention}" |
||||
|
|
||||
return f"{targetPathClean}{fileExtention}" |
return f"{targetPathClean}{fileExtention}" |
||||
|
|
||||
def convertFile(self, sourceFile, targetFile): |
def convertFile(self, sourceFile, targetFile): |
||||
if ( |
if ( |
||||
subprocess.run( |
subprocess.run( |
||||
f"ffmpeg -framerate 15 -i {sourceFile} -b:v 1M -c:v libvpx-vp9 -c:a libopus {targetFile}", |
f"ffmpeg -framerate 15 -i {sourceFile} -b:v 1M -c:v libvpx-vp9 -c:a libopus {targetFile}", |
||||
stdout=subprocess.DEVNULL, |
stdout=subprocess.DEVNULL, |
||||
stderr=subprocess.DEVNULL, |
stderr=subprocess.DEVNULL, |
||||
shell=True, |
shell=True, |
||||
).returncode |
).returncode |
||||
!= 0 |
!= 0 |
||||
): |
): |
||||
self.logger.debug(f"Error converting video. Check {sourceFile}") |
self.logger.debug(f"Error converting video. Check {sourceFile}") |
||||
|
|
||||
self.logger.debug(f"File successfully converted: {targetFile}") |
self.logger.debug(f"File successfully converted: {targetFile}") |
||||
Path(sourceFile).unlink() |
Path(sourceFile).unlink() |
||||
self.logger.debug(f"Orginal file successfully deleted: {sourceFile}") |
self.logger.debug(f"Orginal file successfully deleted: {sourceFile}") |
||||
|
|
||||
def save_files(self, download_dir, files, blacklist=None, target_filetype=None): |
def save_files(self, download_dir, files, blacklist=None, target_filetype=None): |
||||
self.logger.debug(f"Start downloading files") |
self.logger.debug(f"Start downloading files") |
||||
|
|
||||
for file in files: |
for file in files: |
||||
target_file_path = self.generateTargetFilePath( |
target_file_path = self.generateTargetFilePath( |
||||
file["FileName"], download_dir |
file["FileName"], download_dir |
||||
) |
) |
||||
|
|
||||
target_file_path_convert = None |
target_file_path_convert = None |
||||
if target_filetype: |
if target_filetype: |
||||
target_file_path_convert = self.generateTargetFilePath( |
target_file_path_convert = self.generateTargetFilePath( |
||||
file["FileName"], download_dir, extention=f"{target_filetype}" |
file["FileName"], download_dir, extention=f"{target_filetype}" |
||||
) |
) |
||||
|
|
||||
if Path(f"{target_file_path}").is_file(): |
if Path(f"{target_file_path}").is_file(): |
||||
self.logger.debug(f"File already exists: {target_file_path}") |
self.logger.debug(f"File already exists: {target_file_path}") |
||||
continue |
continue |
||||
|
|
||||
if ( |
if ( |
||||
target_file_path_convert |
target_file_path_convert |
||||
and Path(f"{target_file_path_convert}").is_file() |
and Path(f"{target_file_path_convert}").is_file() |
||||
): |
): |
||||
self.logger.debug( |
self.logger.debug( |
||||
f"Converted file already exists: {target_file_path_convert}" |
f"Converted file already exists: {target_file_path_convert}" |
||||
) |
) |
||||
continue |
continue |
||||
|
|
||||
if blacklist: |
if blacklist: |
||||
if target_file_path in blacklist: |
if target_file_path in blacklist: |
||||
self.logger.debug(f"File is on the blacklist: {target_file_path}") |
self.logger.debug(f"File is on the blacklist: {target_file_path}") |
||||
continue |
continue |
||||
if target_file_path_convert and target_file_path_convert in blacklist: |
if target_file_path_convert and target_file_path_convert in blacklist: |
||||
self.logger.debug( |
self.logger.debug( |
||||
f"File is on the blacklist: {target_file_path_convert}" |
f"File is on the blacklist: {target_file_path_convert}" |
||||
) |
) |
||||
continue |
continue |
||||
|
|
||||
self.logger.debug(f"Downloading {target_file_path}...") |
self.logger.debug(f"Downloading {target_file_path}...") |
||||
self.cam.download_file( |
self.cam.download_file( |
||||
file["BeginTime"], file["EndTime"], file["FileName"], target_file_path |
file["BeginTime"], file["EndTime"], file["FileName"], target_file_path |
||||
) |
) |
||||
self.logger.debug(f"Finished downloading {target_file_path}...") |
self.logger.debug(f"Finished downloading {target_file_path}...") |
||||
|
|
||||
if target_file_path_convert: |
if target_file_path_convert: |
||||
self.logger.debug(f"Converting {target_file_path_convert}...") |
self.logger.debug(f"Converting {target_file_path_convert}...") |
||||
self.convertFile(target_file_path, target_file_path_convert) |
self.convertFile(target_file_path, target_file_path_convert) |
||||
self.logger.debug(f"Finished converting {target_file_path_convert}.") |
self.logger.debug(f"Finished converting {target_file_path_convert}.") |
||||
|
|
||||
self.logger.debug(f"Finish downloading files") |
self.logger.debug(f"Finish downloading files") |
||||
|
|
||||
def move_cam(self, direction, step=5): |
def move_cam(self, direction, step=5): |
||||
match direction: |
match direction: |
||||
case "up": |
case "up": |
||||
self.cam.ptz_step("DirectionUp", step=step) |
self.cam.ptz_step("DirectionUp", step=step) |
||||
case "down": |
case "down": |
||||
self.cam.ptz_step("DirectionDown", step=step) |
self.cam.ptz_step("DirectionDown", step=step) |
||||
case "left": |
case "left": |
||||
self.cam.ptz_step("DirectionLeft", step=step) |
self.cam.ptz_step("DirectionLeft", step=step) |
||||
case "right": |
case "right": |
||||
self.cam.ptz_step("DirectionRight", step=step) |
self.cam.ptz_step("DirectionRight", step=step) |
||||
case _: |
case _: |
||||
self.logger.debug(f"No direction found") |
self.logger.debug(f"No direction found") |
||||
|
|
||||
def mute_cam(self): |
def mute_cam(self): |
||||
print( |
print( |
||||
self.cam.send( |
self.cam.send( |
||||
1040, |
1040, |
||||
{ |
{ |
||||
"fVideo.Volume": [ |
"fVideo.Volume": [ |
||||
{"AudioMode": "Single", "LeftVolume": 0, "RightVolume": 0} |
{"AudioMode": "Single", "LeftVolume": 0, "RightVolume": 0} |
||||
], |
], |
||||
"Name": "fVideo.Volume", |
"Name": "fVideo.Volume", |
||||
}, |
}, |
||||
) |
) |
||||
) |
) |
||||
|
|
||||
def set_volume(self, volume): |
def set_volume(self, volume): |
||||
print( |
print( |
||||
self.cam.send( |
self.cam.send( |
||||
1040, |
1040, |
||||
{ |
{ |
||||
"fVideo.Volume": [ |
"fVideo.Volume": [ |
||||
{ |
{ |
||||
"AudioMode": "Single", |
"AudioMode": "Single", |
||||
"LeftVolume": volume, |
"LeftVolume": volume, |
||||
"RightVolume": volume, |
"RightVolume": volume, |
||||
} |
} |
||||
], |
], |
||||
"Name": "fVideo.Volume", |
"Name": "fVideo.Volume", |
||||
}, |
}, |
||||
) |
) |
||||
) |
) |
||||
|
|
||||
def get_battery(self): |
def get_battery(self): |
||||
data = self.cam.send_custom( |
data = self.cam.send_custom( |
||||
1610, |
1610, |
||||
{"Name": "OPTUpData", "OPTUpData": {"UpLoadDataType": 5}}, |
{"Name": "OPTUpData", "OPTUpData": {"UpLoadDataType": 5}}, |
||||
size=260, |
size=260, |
||||
)[87:-2].decode("utf-8") |
)[87:-2].decode("utf-8") |
||||
json_data = json.loads(data) |
json_data = json.loads(data) |
||||
return { |
return { |
||||
"BatteryPercent": json_data["Dev.ElectCapacity"]["percent"], |
"BatteryPercent": json_data["Dev.ElectCapacity"]["percent"], |
||||
"Charging": json_data["Dev.ElectCapacity"]["electable"], |
"Charging": json_data["Dev.ElectCapacity"]["electable"], |
||||
} |
} |
||||
|
|
||||
def get_storage(self): |
def get_storage(self): |
||||
# get available storage in gb |
# get available storage in gb |
||||
storage_result = [] |
storage_result = [] |
||||
data = self.cam.send(1020, {"Name": "StorageInfo"}) |
data = self.cam.send(1020, {"Name": "StorageInfo"}) |
||||
for storage_index, storage in enumerate(data["StorageInfo"]): |
for storage_index, storage in enumerate(data["StorageInfo"]): |
||||
for partition_index, partition in enumerate(storage["Partition"]): |
for partition_index, partition in enumerate(storage["Partition"]): |
||||
s = { |
s = { |
||||
"Storage": storage_index, |
"Storage": storage_index, |
||||
"Partition": partition_index, |
"Partition": partition_index, |
||||
"RemainingSpace": int(partition["RemainSpace"], 0) / 1024, |
"RemainingSpace": int(partition["RemainSpace"], 0) / 1024, |
||||
"TotalSpace": int(partition["TotalSpace"], 0) / 1024, |
"TotalSpace": int(partition["TotalSpace"], 0) / 1024, |
||||
} |
} |
||||
storage_result.append(s) |
storage_result.append(s) |
||||
return storage_result |
return storage_result |
||||
|
@ -1,244 +1,244 @@ |
|||||
#!/usr/bin/env python3 |
#!/usr/bin/env python3 |
||||
|
|
||||
from dvrip import DVRIPCam |
from dvrip import DVRIPCam |
||||
from telnetlib import Telnet |
from telnetlib import Telnet |
||||
import argparse |
import argparse |
||||
import datetime |
import datetime |
||||
import json |
import json |
||||
import os |
import os |
||||
import socket |
import socket |
||||
import time |
import time |
||||
import requests |
import requests |
||||
import zipfile |
import zipfile |
||||
|
|
||||
TELNET_PORT = 4321 |
TELNET_PORT = 4321 |
||||
ARCHIVE_URL = "https://github.com/widgetii/xmupdates/raw/main/archive" |
ARCHIVE_URL = "https://github.com/widgetii/xmupdates/raw/main/archive" |
||||
|
|
||||
""" |
""" |
||||
Tested on XM boards: |
Tested on XM boards: |
||||
IPG-53H20PL-S 53H20L_S39 00002532 |
IPG-53H20PL-S 53H20L_S39 00002532 |
||||
IPG-80H20PS-S 50H20L 00022520 |
IPG-80H20PS-S 50H20L 00022520 |
||||
IVG-85HF20PYA-S HI3516EV200_50H20AI_S38 000559A7 |
IVG-85HF20PYA-S HI3516EV200_50H20AI_S38 000559A7 |
||||
IVG-85HG50PYA-S HI3516EV300_85H50AI 000529B2 |
IVG-85HG50PYA-S HI3516EV300_85H50AI 000529B2 |
||||
|
|
||||
Issues with: "armbenv: can't load library 'libdvr.so'" |
Issues with: "armbenv: can't load library 'libdvr.so'" |
||||
IPG-50HV20PES-S 50H20L_18EV200_S38 00018520 |
IPG-50HV20PES-S 50H20L_18EV200_S38 00018520 |
||||
""" |
""" |
||||
|
|
||||
# downgrade archive (mainly Yandex.Disk) |
# downgrade archive (mainly Yandex.Disk) |
||||
# https://www.cctvsp.ru/articles/obnovlenie-proshivok-dlya-ip-kamer-ot-xiong-mai |
# https://www.cctvsp.ru/articles/obnovlenie-proshivok-dlya-ip-kamer-ot-xiong-mai |
||||
|
|
||||
XMV4 = { |
XMV4 = { |
||||
"envtool": "XmEnv", |
"envtool": "XmEnv", |
||||
"flashes": [ |
"flashes": [ |
||||
"0x00EF4017", |
"0x00EF4017", |
||||
"0x00EF4018", |
"0x00EF4018", |
||||
"0x00C22017", |
"0x00C22017", |
||||
"0x00C22018", |
"0x00C22018", |
||||
"0x00C22019", |
"0x00C22019", |
||||
"0x00C84017", |
"0x00C84017", |
||||
"0x00C84018", |
"0x00C84018", |
||||
"0x001C7017", |
"0x001C7017", |
||||
"0x001C7018", |
"0x001C7018", |
||||
"0x00207017", |
"0x00207017", |
||||
"0x00207018", |
"0x00207018", |
||||
"0x000B4017", |
"0x000B4017", |
||||
"0x000B4018", |
"0x000B4018", |
||||
], |
], |
||||
} |
} |
||||
|
|
||||
|
|
||||
def down(template, filename): |
def down(template, filename): |
||||
t = template.copy() |
t = template.copy() |
||||
t['downgrade'] = filename |
t['downgrade'] = filename |
||||
return t |
return t |
||||
|
|
||||
|
|
||||
# Borrowed from InstallDesc |
# Borrowed from InstallDesc |
||||
conf = { |
conf = { |
||||
"000559A7": down(XMV4, "General_IPC_HI3516EV200_50H20AI_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20200507_all.bin"), |
"000559A7": down(XMV4, "General_IPC_HI3516EV200_50H20AI_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20200507_all.bin"), |
||||
"000529B2": down(XMV4, "General_IPC_HI3516EV300_85H50AI_Nat_dss_OnvifS_HIK_V5_00_R02_20200507.bin"), |
"000529B2": down(XMV4, "General_IPC_HI3516EV300_85H50AI_Nat_dss_OnvifS_HIK_V5_00_R02_20200507.bin"), |
||||
"000529E9": down(XMV4, "hacked_from_HI3516EV300_85H50AI.bin"), |
"000529E9": down(XMV4, "hacked_from_HI3516EV300_85H50AI.bin"), |
||||
} |
} |
||||
|
|
||||
|
|
||||
def add_flashes(desc, swver): |
def add_flashes(desc, swver): |
||||
board = conf.get(swver) |
board = conf.get(swver) |
||||
if board is None: |
if board is None: |
||||
return |
return |
||||
|
|
||||
fls = [] |
fls = [] |
||||
for i in board["flashes"]: |
for i in board["flashes"]: |
||||
fls.append({"FlashID": i}) |
fls.append({"FlashID": i}) |
||||
desc["SupportFlashType"] = fls |
desc["SupportFlashType"] = fls |
||||
|
|
||||
|
|
||||
def get_envtool(swver): |
def get_envtool(swver): |
||||
board = conf.get(swver) |
board = conf.get(swver) |
||||
if board is None: |
if board is None: |
||||
return "armbenv" |
return "armbenv" |
||||
|
|
||||
return board["envtool"] |
return board["envtool"] |
||||
|
|
||||
|
|
||||
def make_zip(filename, data): |
def make_zip(filename, data): |
||||
zipf = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED) |
zipf = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED) |
||||
zipf.writestr("InstallDesc", data) |
zipf.writestr("InstallDesc", data) |
||||
zipf.close() |
zipf.close() |
||||
|
|
||||
|
|
||||
def check_port(host_ip, port): |
def check_port(host_ip, port): |
||||
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
result_of_check = a_socket.connect_ex((host_ip, port)) |
result_of_check = a_socket.connect_ex((host_ip, port)) |
||||
return result_of_check == 0 |
return result_of_check == 0 |
||||
|
|
||||
|
|
||||
def extract_gen(swver): |
def extract_gen(swver): |
||||
return swver.split(".")[3] |
return swver.split(".")[3] |
||||
|
|
||||
|
|
||||
def cmd_armebenv(swver): |
def cmd_armebenv(swver): |
||||
envtool = get_envtool(swver) |
envtool = get_envtool(swver) |
||||
return { |
return { |
||||
"Command": "Shell", |
"Command": "Shell", |
||||
"Script": f"{envtool} -s xmuart 0; {envtool} -s telnetctrl 1", |
"Script": f"{envtool} -s xmuart 0; {envtool} -s telnetctrl 1", |
||||
} |
} |
||||
|
|
||||
|
|
||||
def cmd_telnetd(port): |
def cmd_telnetd(port): |
||||
return { |
return { |
||||
"Command": "Shell", |
"Command": "Shell", |
||||
"Script": f"busybox telnetd -F -p {port} -l /bin/sh", |
"Script": f"busybox telnetd -F -p {port} -l /bin/sh", |
||||
} |
} |
||||
|
|
||||
|
|
||||
def cmd_backup(): |
def cmd_backup(): |
||||
return [ |
return [ |
||||
{ |
{ |
||||
"Command": "Shell", |
"Command": "Shell", |
||||
"Script": "mount -o nolock 95.217.179.189:/srv/ro /utils/", |
"Script": "mount -o nolock 95.217.179.189:/srv/ro /utils/", |
||||
}, |
}, |
||||
{"Command": "Shell", "Script": "/utils/ipctool -w"}, |
{"Command": "Shell", "Script": "/utils/ipctool -w"}, |
||||
] |
] |
||||
|
|
||||
|
|
||||
def downgrade_old_version(cam, buildtime, swver): |
def downgrade_old_version(cam, buildtime, swver): |
||||
milestone = datetime.date(2020, 5, 7) |
milestone = datetime.date(2020, 5, 7) |
||||
dto = datetime.datetime.strptime(buildtime, "%Y-%m-%d %H:%M:%S") |
dto = datetime.datetime.strptime(buildtime, "%Y-%m-%d %H:%M:%S") |
||||
if dto.date() > milestone: |
if dto.date() > milestone: |
||||
print( |
print( |
||||
f"Current firmware date {dto.date()}, but it needs to be no more than" |
f"Current firmware date {dto.date()}, but it needs to be no more than" |
||||
f" {milestone}\nConsider downgrade and only then continue.\n\n" |
f" {milestone}\nConsider downgrade and only then continue.\n\n" |
||||
) |
) |
||||
a = input("Are you sure to overwrite current firmware without backup (y/n)? ") |
a = input("Are you sure to overwrite current firmware without backup (y/n)? ") |
||||
if a == "y": |
if a == "y": |
||||
board = conf.get(swver) |
board = conf.get(swver) |
||||
if board is None: |
if board is None: |
||||
print(f"{swver} firmware is not supported yet") |
print(f"{swver} firmware is not supported yet") |
||||
return False |
return False |
||||
|
|
||||
print("DOWNGRADING\n") |
print("DOWNGRADING\n") |
||||
url = f"{ARCHIVE_URL}/{swver}/{board['downgrade']}" |
url = f"{ARCHIVE_URL}/{swver}/{board['downgrade']}" |
||||
print(f"Downloading {url}") |
print(f"Downloading {url}") |
||||
r = requests.get(url, allow_redirects=True) |
r = requests.get(url, allow_redirects=True) |
||||
if r.status_code != requests.codes.ok: |
if r.status_code != requests.codes.ok: |
||||
print("Something went wrong") |
print("Something went wrong") |
||||
return False |
return False |
||||
|
|
||||
open('upgrade.bin', 'wb').write(r.content) |
open('upgrade.bin', 'wb').write(r.content) |
||||
print(f"Upgrading...") |
print(f"Upgrading...") |
||||
cam.upgrade('upgrade.bin') |
cam.upgrade('upgrade.bin') |
||||
print("Completed. Wait a minute and then rerun") |
print("Completed. Wait a minute and then rerun") |
||||
return False |
return False |
||||
|
|
||||
return False |
return False |
||||
return True |
return True |
||||
|
|
||||
|
|
||||
def open_telnet(host_ip, port, **kwargs): |
def open_telnet(host_ip, port, **kwargs): |
||||
make_telnet = kwargs.get("telnet", False) |
make_telnet = kwargs.get("telnet", False) |
||||
make_backup = kwargs.get("backup", False) |
make_backup = kwargs.get("backup", False) |
||||
user = kwargs.get("username", "admin") |
user = kwargs.get("username", "admin") |
||||
password = kwargs.get("password", "") |
password = kwargs.get("password", "") |
||||
|
|
||||
cam = DVRIPCam(host_ip, user=user, password=password) |
cam = DVRIPCam(host_ip, user=user, password=password) |
||||
if not cam.login(): |
if not cam.login(): |
||||
print(f"Cannot connect {host_ip}") |
print(f"Cannot connect {host_ip}") |
||||
return |
return |
||||
upinfo = cam.get_upgrade_info() |
upinfo = cam.get_upgrade_info() |
||||
hw = upinfo["Hardware"] |
hw = upinfo["Hardware"] |
||||
sysinfo = cam.get_system_info() |
sysinfo = cam.get_system_info() |
||||
swver = extract_gen(sysinfo["SoftWareVersion"]) |
swver = extract_gen(sysinfo["SoftWareVersion"]) |
||||
print(f"Modifying camera {hw}, firmware {swver}") |
print(f"Modifying camera {hw}, firmware {swver}") |
||||
if not downgrade_old_version(cam, sysinfo["BuildTime"], swver): |
if not downgrade_old_version(cam, sysinfo["BuildTime"], swver): |
||||
cam.close() |
cam.close() |
||||
return |
return |
||||
|
|
||||
print(f"Firmware generation {swver}") |
print(f"Firmware generation {swver}") |
||||
|
|
||||
desc = { |
desc = { |
||||
"Hardware": hw, |
"Hardware": hw, |
||||
"DevID": f"{swver}1001000000000000", |
"DevID": f"{swver}1001000000000000", |
||||
"CompatibleVersion": 2, |
"CompatibleVersion": 2, |
||||
"Vendor": "General", |
"Vendor": "General", |
||||
"CRC": "1ce6242100007636", |
"CRC": "1ce6242100007636", |
||||
} |
} |
||||
upcmd = [] |
upcmd = [] |
||||
if make_telnet: |
if make_telnet: |
||||
upcmd.append(cmd_telnetd(port)) |
upcmd.append(cmd_telnetd(port)) |
||||
elif make_backup: |
elif make_backup: |
||||
upcmd = cmd_backup() |
upcmd = cmd_backup() |
||||
else: |
else: |
||||
upcmd.append(cmd_armebenv(swver)) |
upcmd.append(cmd_armebenv(swver)) |
||||
desc["UpgradeCommand"] = upcmd |
desc["UpgradeCommand"] = upcmd |
||||
add_flashes(desc, swver) |
add_flashes(desc, swver) |
||||
|
|
||||
zipfname = "upgrade.bin" |
zipfname = "upgrade.bin" |
||||
make_zip(zipfname, json.dumps(desc, indent=2)) |
make_zip(zipfname, json.dumps(desc, indent=2)) |
||||
cam.upgrade(zipfname) |
cam.upgrade(zipfname) |
||||
cam.close() |
cam.close() |
||||
os.remove(zipfname) |
os.remove(zipfname) |
||||
|
|
||||
if make_backup: |
if make_backup: |
||||
print("Check backup") |
print("Check backup") |
||||
return |
return |
||||
|
|
||||
if not make_telnet: |
if not make_telnet: |
||||
port = 23 |
port = 23 |
||||
print("Waiting for camera is rebooting...") |
print("Waiting for camera is rebooting...") |
||||
|
|
||||
for i in range(10): |
for i in range(10): |
||||
time.sleep(4) |
time.sleep(4) |
||||
if check_port(host_ip, port): |
if check_port(host_ip, port): |
||||
tport = f" {port}" if port != 23 else "" |
tport = f" {port}" if port != 23 else "" |
||||
print(f"Now use 'telnet {host_ip}{tport}' to login") |
print(f"Now use 'telnet {host_ip}{tport}' to login") |
||||
return |
return |
||||
|
|
||||
print("Something went wrong") |
print("Something went wrong") |
||||
return |
return |
||||
|
|
||||
|
|
||||
def main(): |
def main(): |
||||
parser = argparse.ArgumentParser() |
parser = argparse.ArgumentParser() |
||||
parser.add_argument("hostname", help="Camera IP address or hostname") |
parser.add_argument("hostname", help="Camera IP address or hostname") |
||||
parser.add_argument( |
parser.add_argument( |
||||
"-u", "--username", default="admin", help="Username for camera login" |
"-u", "--username", default="admin", help="Username for camera login" |
||||
) |
) |
||||
parser.add_argument( |
parser.add_argument( |
||||
"-p", "--password", default="", help="Password for camera login" |
"-p", "--password", default="", help="Password for camera login" |
||||
) |
) |
||||
parser.add_argument( |
parser.add_argument( |
||||
"-b", "--backup", action="store_true", help="Make backup to the cloud" |
"-b", "--backup", action="store_true", help="Make backup to the cloud" |
||||
) |
) |
||||
parser.add_argument( |
parser.add_argument( |
||||
"-t", |
"-t", |
||||
"--telnet", |
"--telnet", |
||||
action="store_true", |
action="store_true", |
||||
help="Open telnet port without rebooting camera", |
help="Open telnet port without rebooting camera", |
||||
) |
) |
||||
args = parser.parse_args() |
args = parser.parse_args() |
||||
open_telnet(args.hostname, TELNET_PORT, **vars(args)) |
open_telnet(args.hostname, TELNET_PORT, **vars(args)) |
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
if __name__ == "__main__": |
||||
main() |
main() |
||||
|
Loading…
Reference in new issue