Skip to content

Commit

Permalink
Version 1.5. Details in /releases/release_1.5.md (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
tropicoo authored Sep 22, 2022
1 parent a7108c0 commit 1272528
Show file tree
Hide file tree
Showing 39 changed files with 186 additions and 152 deletions.
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 88
select = C,E,F,W,B,B950
extend-ignore = E203, E501
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,3 @@ RUN apk add --no-cache --virtual .build-deps \
&& apk --purge del .build-deps

COPY . /app

CMD ["python", "bot.py"]
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Hikvision Telegram Camera Bot
Telegram Bot which sends snapshots from your Hikvision cameras.

Version: 1.4. [Release details](releases/release_1.4.md).
Version: 1.5. [Release details](releases/release_1.5.md).

## Features
1. Send full/resized snapshots on request
2. Auto-send snapshots on **Motion**, **Line Crossing** and **Intrusion (Field) Detection**
3. Send so-called Telegram video-gifs on request and alert events from paragraph #2
1. Send full/resized pictures on request
2. Auto-send pictures on **Motion**, **Line Crossing** and **Intrusion (Field) Detection**
3. Send so-called Telegram video-gifs on request and alert events from previous paragraph
4. YouTube, Telegram and Icecast direct or re-encoded livestreams
5. DVR to local storage with upload to Telegram group
6. SRS re-stream server
Expand Down Expand Up @@ -246,7 +246,7 @@ where:
2. `101` is camera's configured stream channel.
3. `cam_2` is ID of your second configured camera.

SRS runs in a separate docker container. SRS config and `Dockerfile` placed
SRS runs in a separate docker container. SRS config and `Dockerfile` are placed
in `srs_prod` directory. Service name is `hikvision-srs-server` in `docker-compose.yml`.


Expand Down Expand Up @@ -313,7 +313,7 @@ file with DVR stream settings:
from the local storage. You need to make sure your file size will be up to 2GB since
Telegram rejects larger ones. Just experiment with segment time.
5. Local storage (the real one, not in the container) by default is `/data/dvr` in volumes mapping (the first path string, not the last).
Change it to any location you need, e.g. to `- D:\Videos:/data/dvr` if you're on Windows.
Change it to any location you need, e.g. to `- "D:\Videos:/data/dvr"` if you're on Windows.
```yaml
volumes:
- "/data/dvr:/data/dvr"
Expand Down
11 changes: 8 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3'
version: "3"

services:
hikvision-camera-bot:
Expand All @@ -9,11 +9,14 @@ services:
context: .
dockerfile: Dockerfile
volumes:
# First path is real local storage path. Change to your preferred path.
# First path is real local storage path, e.g. "D:\Videos" in Windows. Change to your preferred one.
- "/data/dvr:/data/dvr"
- "./configs:/app/configs"
restart: unless-stopped
depends_on:
- hikvision-srs-server
command: >
bash -c "python ./bot.py"
hikvision-srs-server:
container_name: "hikvision_srs_server"
Expand All @@ -26,8 +29,10 @@ services:
# Store HLS .ts files in RAM. Depending on 'hls_fragment' and 'hls_window' default size '128M' might be increased.
# Check usage inside the container with 'df -h'.
- /srs/trunk/objs/nginx/html/hls:mode=770,size=128M
volumes:
- "./srs_prod:/usr/local/srs/user_conf"
command: >
bash -c "./objs/srs -c conf/hik-docker.conf"
bash -c "./objs/srs -c user_conf/hik-docker.conf"
ports:
# SRS RTMP port. If you comment this out, you won't be able to connect with video player like VLC.
- "1935:1935"
Expand Down
4 changes: 3 additions & 1 deletion hikcamerabot/bot_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def _create_and_setup_cameras(self) -> None:

for cam_id, cam_conf in self._conf.camera_list.items():
if cam_conf.hidden:
# Skip camera and its settings.
self._log.info(
'[%s] Skipping camera config - %s', cam_id, cam_conf.description
)
continue

cam_cmds = defaultdict(list)
Expand Down
35 changes: 20 additions & 15 deletions hikcamerabot/camera.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Hikvision camera module."""

import asyncio
import logging
from datetime import datetime
Expand All @@ -10,11 +11,12 @@

from hikcamerabot.clients.hikvision import HikvisionAPI, HikvisionAPIClient
from hikcamerabot.clients.hikvision.enums import IrcutFilterType
from hikcamerabot.common.video.videogif_recorder import VideoGifRecorder
from hikcamerabot.enums import VideoGifType
from hikcamerabot.exceptions import HikvisionAPIError, HikvisionCamError
from hikcamerabot.services.manager import ServiceManager
from hikcamerabot.common.video.videogif_recorder import VideoGifRecorder
from hikcamerabot.services.abstract import AbstractService
from hikcamerabot.services.alarm import AlarmService
from hikcamerabot.services.manager import ServiceManager
from hikcamerabot.services.stream import (
DvrStreamService,
IcecastStreamService,
Expand All @@ -24,12 +26,13 @@
)
from hikcamerabot.utils.image import ImageProcessor


if TYPE_CHECKING:
from hikcamerabot.camerabot import CameraBot


class ServiceContainer:
"""Container class for all services."""

def __init__(
self, conf: Dict, api: HikvisionAPI, cam: 'HikvisionCam', bot: 'CameraBot'
) -> None:
Expand Down Expand Up @@ -76,6 +79,17 @@ def __init__(
cam=cam,
)

def get_all(self) -> list[AbstractService]:
"""Return list with all services."""
return [
self.alarm,
self.dvr_stream,
self.srs_stream,
self.stream_icecast,
self.stream_tg,
self.stream_yt,
]


class HikvisionCam:
"""Hikvision Camera Class."""
Expand All @@ -99,16 +113,7 @@ def __init__(self, id: str, conf: Dict, bot: 'CameraBot') -> None:
bot=self.bot,
)
self.service_manager = ServiceManager()
self.service_manager.register(
[
self.services.alarm,
self.services.stream_yt,
self.services.stream_icecast,
self.services.srs_stream,
self.services.dvr_stream,
self.services.stream_tg,
]
)
self.service_manager.register(self.services.get_all())

self.snapshots_taken = 0
self._videogif = VideoGifRecorder(cam=self)
Expand All @@ -120,9 +125,9 @@ async def start_videogif_record(
self,
video_type: VideoGifType = VideoGifType.ON_DEMAND,
rewind: bool = False,
context: Message = None,
message: Message = None,
) -> None:
self._videogif.start_rec(video_type=video_type, rewind=rewind, context=context)
self._videogif.start_rec(video_type=video_type, rewind=rewind, message=message)

async def set_ircut_filter(self, filter_type: IrcutFilterType) -> None:
await self._api.set_ircut_filter(filter_type)
Expand Down
2 changes: 1 addition & 1 deletion hikcamerabot/camerabot.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def send_alert_message(self, text: str, **kwargs) -> None:
for user_id in self.alert_users:
await self._send_message(text, user_id, **kwargs)

async def _send_message(self, text: str, user_id: int, **kwargs):
async def _send_message(self, text: str, user_id: int, **kwargs) -> None:
try:
await self.send_message(user_id, text, **kwargs)
except Exception:
Expand Down
9 changes: 5 additions & 4 deletions hikcamerabot/common/video/tasks/videogif.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
FFMPEG_SRS_HLS_VIDEO_SRC,
FFMPEG_SRS_RTMP_VIDEO_SRC,
RTSP_TRANSPORT_TPL,
SRS_DOCKER_CONTAINER_NAME,
SRS_LIVESTREAM_NAME_TPL,
)
from hikcamerabot.enums import Event, VideoGifType
Expand Down Expand Up @@ -54,7 +55,7 @@ def __init__(
rewind: bool,
cam: 'HikvisionCam',
video_type: VideoGifType,
context: Message = None,
message: Message = None,
):
self._log = logging.getLogger(self.__class__.__name__)
self._cam = cam
Expand All @@ -75,7 +76,7 @@ def __init__(
if self._rewind:
self._rec_time += self._gif_conf.rewind_time

self._message = context
self._message = message
self._event = self._VIDEO_TYPE_TO_EVENT[self._video_type]
self._result_queue = get_result_queue()
self._killpg = wrap(os.killpg)
Expand Down Expand Up @@ -136,7 +137,7 @@ async def _get_probe_ctx(self) -> None:
self._height = video_streams[0]['height']
self._width = video_streams[0]['width']

def _post_err_cleanup(self):
def _post_err_cleanup(self) -> None:
"""Delete video file and thumb if they exist after exception."""
for file_path in (self._file_path, self._thumb_path):
try:
Expand Down Expand Up @@ -217,7 +218,7 @@ def _build_ffmpeg_cmd(self) -> str:
)
if self._rewind:
video_source = FFMPEG_SRS_HLS_VIDEO_SRC.format(
ip_address=socket.gethostbyname('hikvision_srs_server'),
ip_address=socket.gethostbyname(SRS_DOCKER_CONTAINER_NAME),
livestream_name=livestream_name,
)
return FFMPEG_CMD_HLS_VIDEO_GIF.format(
Expand Down
8 changes: 4 additions & 4 deletions hikcamerabot/common/video/videogif_recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ def __init__(self, cam: 'HikvisionCam') -> None:
self._proc_task_queue = deque()

def start_rec(
self, video_type: VideoGifType, rewind: bool = False, context: Message = None
self, video_type: VideoGifType, rewind: bool = False, message: Message = None
) -> None:
"""Start recording video-gif."""
self._start_rec(video_type=video_type, rewind=rewind, context=context)
self._start_rec(video_type=video_type, rewind=rewind, message=message)

def _start_rec(
self, video_type: VideoGifType, rewind: bool, context: Message
self, video_type: VideoGifType, rewind: bool, message: Message
) -> None:
"""Start rtsp video stream recording to a temporary file."""
rec_task = RecordVideoGifTask(
rewind=rewind,
cam=self._cam,
video_type=video_type,
context=context,
message=message,
)
task = create_task(
rec_task.run(),
Expand Down
10 changes: 5 additions & 5 deletions hikcamerabot/config/schemas/encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class _Scale(Schema):
maxrate = f.String(required=True, validate=non_empty_str)
bufsize = f.String(required=True, validate=non_empty_str)
tune = f.String(required=True, validate=non_empty_str)
scale = f.Nested(_Scale, required=True)
scale = f.Nested(_Scale(), required=True)

_inner_validation_schema_cls = _X264

Expand Down Expand Up @@ -99,12 +99,12 @@ class _Scale(Schema):
bufsize = f.String(required=True, validate=non_empty_str)
deadline = f.String(required=True, validate=non_empty_str)
speed = f.Integer(required=True, validate=int_min_1)
scale = f.Nested(_Scale, required=True)
scale = f.Nested(_Scale(), required=True)

_inner_validation_schema_cls = _Vp9


class Encoding(Schema):
direct = f.Nested(Direct, required=True)
x264 = f.Nested(X264, required=True)
vp9 = f.Nested(Vp9, required=True)
direct = f.Nested(Direct(), required=True)
x264 = f.Nested(X264(), required=True)
vp9 = f.Nested(Vp9(), required=True)
12 changes: 6 additions & 6 deletions hikcamerabot/config/schemas/livestream.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ class _IceStream(Schema):
channel = f.Integer(required=True, validate=int_min_1)
restart_period = f.Integer(required=True, validate=int_min_1)
restart_pause = f.Integer(required=True, validate=int_min_0)
ice_stream = f.Nested(_IceStream, required=True)
ice_stream = f.Nested(_IceStream(), required=True)

_inner_validation_schema_cls = _Icecast


class Livestream(Schema):
youtube = f.Nested(Youtube, required=True)
telegram = f.Nested(Telegram, required=True)
icecast = f.Nested(Icecast, required=True)
srs = f.Nested(Srs, required=True)
dvr = f.Nested(Dvr, required=True)
youtube = f.Nested(Youtube(), required=True)
telegram = f.Nested(Telegram(), required=True)
icecast = f.Nested(Icecast(), required=True)
srs = f.Nested(Srs(), required=True)
dvr = f.Nested(Dvr(), required=True)
Loading

0 comments on commit 1272528

Please sign in to comment.