You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
7.1 KiB
172 lines
7.1 KiB
from fastapi import FastAPI, Response, BackgroundTasks
|
|
from fastapi.responses import StreamingResponse, FileResponse
|
|
import uvicorn
|
|
import traceback
|
|
|
|
from config_parser import Config as ConfigParser
|
|
from config_parser import TranscodeStatus, TranscodeTools, Go2Rtc
|
|
from nvr_core import NVR
|
|
from nvr_types import File
|
|
|
|
class Server:
|
|
app: FastAPI = FastAPI()
|
|
config: ConfigParser = ConfigParser()
|
|
go2rtc: Go2Rtc = Go2Rtc()
|
|
|
|
API_BASE_REF = "/api/dvrip"
|
|
|
|
def __init__(self):
|
|
self.setup_events()
|
|
self.setup_routers()
|
|
|
|
def setup_events(self):
|
|
@self.app.on_event('startup')
|
|
async def on_startup():
|
|
for i in range(0, len(self.config.recorders)):
|
|
nvr:NVR = self.config.getRecorder(i).nvr
|
|
await nvr.login()
|
|
channels = await nvr.channels()
|
|
nvr.logout()
|
|
self.config.recorders[i].channels = len(channels)
|
|
print(f"{self.config.recorders[i]} channels count: {self.config.recorders[i].channels}")
|
|
await self.go2rtc.start_go2rtc(self.config.recorders)
|
|
|
|
def setup_routers(self):
|
|
@self.app.get(self.API_BASE_REF, status_code=200)
|
|
async def getRecorders(response: Response):
|
|
try:
|
|
return {"ok":True, "data":self.config.getRecorders()}
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
|
|
@self.app.get(self.API_BASE_REF + "/channels/{recorder_index}", status_code=200)
|
|
async def getRecorder(response: Response, recorder_index:int):
|
|
try:
|
|
nvr:NVR = self.config.getRecorder(recorder_index).nvr
|
|
await nvr.login()
|
|
channels = await nvr.channels()
|
|
return {"ok":True, "data":channels}
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
finally:
|
|
nvr.logout()
|
|
|
|
@self.app.get(self.API_BASE_REF + "/history/{recorder_index}/{channel}/{stream}")
|
|
async def getHistory(response: Response, recorder_index:int, channel: int, stream: int, start_date:str = None, end_date:str = None):
|
|
try:
|
|
nvr:NVR = self.config.getRecorder(recorder_index).nvr
|
|
await nvr.login()
|
|
return {"ok":True, "data":[f async for f in nvr.files(channel, start_date, end_date, stype=stream, json=True)]}
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
finally:
|
|
nvr.logout()
|
|
|
|
@self.app.get(self.API_BASE_REF + "/snapshot/{recorder_index}/{channel}")
|
|
async def getSnapshot(response: Response, recorder_index:int, channel: int):
|
|
try:
|
|
nvr:NVR = self.config.getRecorder(recorder_index).nvr
|
|
await nvr.login()
|
|
data = await nvr.client.snapshot(channel)
|
|
|
|
return Response(content=bytes(data), media_type="image/jpg")
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return b""
|
|
finally:
|
|
nvr.logout()
|
|
|
|
@self.app.get(self.API_BASE_REF + "/file/{recorder_index}")
|
|
async def getFile(response: Response, recorder_index:int, b64:str, background_tasks: BackgroundTasks):
|
|
try:
|
|
if len(b64) == 0:
|
|
response.status_code = 404
|
|
return ""
|
|
nvr:NVR = self.config.getRecorder(recorder_index).nvr
|
|
await nvr.login()
|
|
nvr.client.debug()
|
|
file: File = File.from_b64(b64 + "==")
|
|
print("open")
|
|
|
|
async def after():
|
|
try:
|
|
await nvr.client.busy.release()
|
|
except:
|
|
print("Already released")
|
|
nvr.logout()
|
|
|
|
background_tasks.add_task(after)
|
|
headers = {}
|
|
headers.update({"Content-Length":str(file.size)})
|
|
headers.update({"Content-Disposition": f'attachment; filename="{file.filename_cleared}.{file.type}"'})
|
|
return StreamingResponse(nvr.stream_file(file), media_type="application/octet-stream", headers=headers)
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
|
|
@self.app.get(self.API_BASE_REF + "/transcode/status/{recorder_index}")
|
|
async def getTranscodeStatus(response: Response, recorder_index:int, b64:str, background_tasks: BackgroundTasks):
|
|
try:
|
|
if len(b64) == 0:
|
|
response.status_code = 404
|
|
return ""
|
|
|
|
if b64 in self.config.transcode_tools.statuses:
|
|
return {"ok":True, "data":self.config.transcode_tools.statuses[b64]}
|
|
|
|
nvr:NVR = self.config.getRecorder(recorder_index).nvr
|
|
await nvr.login()
|
|
nvr.client.debug()
|
|
file: File = File.from_b64(b64 + "==")
|
|
|
|
self.config.transcode_tools.statuses[b64] = TranscodeStatus(b64)
|
|
background_tasks.add_task(self.config.transcode_tools.processing_safe, status = self.config.transcode_tools.statuses[b64], file = file, nvr = nvr)
|
|
return {"ok":True, "data":self.config.transcode_tools.statuses[b64]}
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
|
|
@self.app.get(self.API_BASE_REF + "/transcode/download")
|
|
async def getTranscodeDownload(response: Response, b64:str):
|
|
try:
|
|
if len(b64) == 0:
|
|
response.status_code = 404
|
|
return ""
|
|
|
|
if not b64 in self.config.transcode_tools.statuses:
|
|
response.status_code = 404
|
|
return ""
|
|
|
|
if self.config.transcode_tools.statuses[b64].done:
|
|
headers = {}
|
|
headers.update({"Content-Length":str(self.config.transcode_tools.statuses[b64].outSize)})
|
|
headers.update({"Content-Disposition": f'attachment; filename="{self.config.transcode_tools.statuses[b64].outName}"'})
|
|
return StreamingResponse(self.config.transcode_tools.statuses[b64].generate_bytes(), media_type="application/octet-stream", headers=headers)
|
|
else:
|
|
response.status_code = 429
|
|
return ""
|
|
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
response.status_code = 400
|
|
return {"ok":False, "error":e}
|
|
|
|
|
|
def run(self):
|
|
uvicorn.run(
|
|
self.app,
|
|
host=self.config.listen_address,
|
|
port=self.config.listen_port,
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
Server().run()
|