From 8b459daa55a87d4d1b7b5e9c0d6bab9b0cfcb6db Mon Sep 17 00:00:00 2001 From: deeily Date: Thu, 23 Apr 2026 18:09:11 +0300 Subject: [PATCH] =?UTF-8?q?dvr=5Fadmin=20v2.4.1:=20hotfix=20Docker=20?= =?UTF-8?q?=E2=80=94=20stale=20session=20500=20+=20Xvfb=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - login_required валидирует существование юзера в БД: при устаревшей session (user_id ссылается на удалённую запись после пересоздания БД) чистим session и редиректим на /login вместо 500 на user["is_admin"] - entrypoint.sh чистит /tmp/.X1-lock перед Xvfb — иначе после креша контейнера повторный старт падает с "Server is already active" - CHANGELOG: запись v2.4.0 (Novicam integration) + v2.4.1 Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 45 ++++++++++++++++++++++++++++++++++ dvr_admin/app.py | 5 ++++ dvr_admin_docker/entrypoint.sh | 3 +++ 3 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ec7f0..1f8ae44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,51 @@ Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/). +## [2.4.1] — 2026-04-23 + +Hotfix для Docker: 500-я ошибка при первом открытии после пересоздания БД, stale Xvfb lock. + +### Исправлено +- **`login_required` валидирует юзера в БД** — если session содержит `user_id` но записи в `web_users` нет (например после пересоздания БД со старым SECRET_KEY), чистим session и редиректим на `/login` вместо 500 Internal Server Error на `user["is_admin"]` +- **Xvfb stale lock** в Docker — `entrypoint.sh` чистит `/tmp/.X1-lock` и `/tmp/.X11-unix/X1` перед запуском. Без этого после крешнутого контейнера Xvfb не стартует с ошибкой «Server is already active for display 1» + +--- + +## [2.4.0] — 2026-04-23 + +Частичная интеграция Novicam NR1604 (firmware V3.4.96) рядом с Hikvision. Изолировано через IP-whitelist `_NOVICAM_IPS_TEMP` — Hik-пути не затронуты. Cam 2/3/4 работают полноценно (live + encrypted playback). Cam 1 остаётся открытым вопросом. + +### Добавлено — Novicam infrastructure +- **`_novicam_session_login()`** — отдельный login flow: sessionID извлекается из тела ``, используется как `Cookie: WebSession=` (Novicam не выставляет Set-Cookie) +- **`_sdk_http_live_reader_novicam()`** — live stream reader с Novicam-спецификой: HB 0x00030006 каждые 3с, без `/ISAPI/Security/token` (endpoint отсутствует, 404), `_cam_offset=32` сразу (NVR) +- **`_sdk_http_playback_reader_encrypted(_novicam=True)`** — ветка encrypted playback для Novicam. HB 0x00030109 шлётся при `rate=1` (плагин делает так же), warmup=0, Cookie WebSession вместо token +- **Fallback `_fetch_channels_inline`** → `/ISAPI/ContentMgmt/InputProxy/channels` при 403 от `/ISAPI/Streaming/channels`. Маппинг `id=N` → `N01` +- **Pre-activation probes** перед `/SDK/play`: + - `/ISAPI/ContentMgmt/InputProxy/channels?security=1&iv=` + `/channels/status?security=1&iv=` — AES-encrypted запросы которые эталонный плагин отправляет до каждого /SDK-вызова (найдено реверсом pcap в IE-mode) + - `/ISAPI/PTZCtrl/channels/{N}/patrols`, `/ISAPI/ContentMgmt/PTZCtrlProxy/channels/{N}/patterns` + `/capabilities` +- **HTTP-заголовки идентичны эталонному плагину**: `Accept: text/html,application/xhtml+xml...`, `Accept-Language: ZH-cn;zh;q=0.5`, `Accept-Charset: gb2312,utf8;q=0.7,*;q=0.7` — DVR меняет формат потока в зависимости от этих заголовков + +### Исправлено — encrypted playback прыжки каждые 15с +- **PlayM4_GetSourceBufferRemain throttle перед InputData** — DVR заливает архивное видео в scan-режиме (~5.8 MB/s, в 10× быстрее реалтайма 540 KB/s плагина). Без троттлинга libPlayCtrl захлёбывается и дропает кадры → визуальные прыжки. Ждём свободного места в source buffer (2MB) → TCP flow control естественно замедляет DVR до темпа декодера. Причина найдена через сравнение tshark `-z conv,tcp` нашего потока vs эталонного плагина +- **PTS-based `current_start` через `buf.video_ms`** при reconnect — защита от прыжков вперёд на неотыгранный интервал + +### Инфраструктура для cam 1 (готова, отключена `_use_nov_wine=False`) +- **`_NovMuxWine`** singleton — отдельный wine-процесс с Novicam `PlayCtrl.dll` (194 экспорта, не Hik) через `playm4_mux_server.exe` в `wine_playm4_nov/` +- **Diагностика cam 1 через Spy DLL** (в `doc/Web Components spy/`) — хук PlayM4-вызовов эталонного плагина на Windows: + - Key buffer байт-в-байт идентичен нашему `_hik_stream_kdf_128` для того же stream_key + - IMKH header идентичен нашему + - PlayM4 flow (GetPort → SetSecretKey → OpenStream → Play) идентичен + - Wire packets (344B control + 1376B encrypted video) приходят к нам идентично эталону + - Единственное отличие: плагин вызывает `Play(port, HWND)` с ненулевым HWND. Windows PlayCtrl.dll через HWND рендерит DirectDraw. Linux libPlayCtrl.so только pull-mode GetJPEG — не декодит cam 1 mainstream (codec flag `04 00 00 01` в IMKH) + +### Изменено +- **v2.4.0** в номере версии на UI (статусной строке) + +### Известные ограничения +- **Cam 1 Novicam mainstream** — Linux libPlayCtrl декодер не возвращает JPEG. Wine-путь готов но не включён (требует доотладки dec_callback vs HWND rendering). Детали в memory `project_novicam_integration.md` + +--- + ## [2.3.1] — 2026-04-23 Ревью безопасности / ресурсов по итогам аудита. Security hardening и cleanup-фиксы перед релизом. Docker-образ с wine для encrypted H.265+. diff --git a/dvr_admin/app.py b/dvr_admin/app.py index df71e3e..68cfbee 100644 --- a/dvr_admin/app.py +++ b/dvr_admin/app.py @@ -489,6 +489,11 @@ def login_required(f): def inner(*a, **kw): if not session.get("user_id"): return redirect(url_for("login", next=request.path)) + # Session может ссылаться на удалённого/несуществующего юзера + # (например после пересоздания БД с прежним SECRET_KEY). + if current_user() is None: + session.clear() + return redirect(url_for("login", next=request.path)) return f(*a, **kw) return inner diff --git a/dvr_admin_docker/entrypoint.sh b/dvr_admin_docker/entrypoint.sh index aabb67c..a543428 100644 --- a/dvr_admin_docker/entrypoint.sh +++ b/dvr_admin_docker/entrypoint.sh @@ -2,6 +2,9 @@ # Запускает Xvfb на DISPLAY :1 для wine PlayCtrl.dll, потом gunicorn. set -e +# Очищаем stale lock от предыдущего крешнутого запуска +rm -f /tmp/.X1-lock /tmp/.X11-unix/X1 2>/dev/null || true + # Xvfb в фоне. -nolisten tcp чтобы не ловили извне. Xvfb :1 -screen 0 640x480x8 -nolisten tcp & XVFB_PID=$!