Visualização ao vivo, gravação por movimento e acesso remoto de câmeras IP via browser, sem necessidade de port forwarding — usando Cloudflare Tunnels.
Acesso remoto: https://dvr.regivan.tec.br
[Câmeras IP RTSP]
192.168.1.5 (cam1) ─┐
192.168.1.6 (cam2) ─┤
▼
[ PC local — rede doméstica ]
┌─────────────────────────────────────────────────┐
│ rtsp_proxy.py (porta 8191, 8192) │
│ ffmpeg → captura frame RTSP → serve JPEG │
│ │
│ motion_recorder.py │
│ detecta movimento → grava MP4 │
│ saída: recordings/cam1/ e recordings/cam2/ │
│ │
│ recordings_relay.py (porta 8290) │
│ serve galeria de gravações (Flask) │
│ │
│ tunnel_relay.py │
│ cloudflared → expõe 8191/8192 na internet │
│ cloudflared → expõe 8290 na internet │
└─────────────────────────────────────────────────┘
│ HTTPS (trycloudflare.com)
▼
[ dvr.regivan.tec.br ]
┌───────────────────────────────┐
│ app.py (Flask + Passenger) │
│ Apache + ModSecurity │
│ DirectAdmin (Python 3.11) │
└───────────────────────────────┘
│
▼
[ Browser / celular ]
Fluxo de imagem ao vivo:
Câmera RTSP → ffmpeg (local) → JPEG via HTTP → Cloudflare Tunnel → DVR (dvr.regivan.tec.br) → Browser
Fluxo de gravações:
motion_recorder.py detecta movimento → MP4 salvo localmente → recordings_relay.py → Cloudflare Tunnel → DVR /recordings
Não é necessário port forwarding no roteador.
pip install -r requirements.txt
python app.py
# Acesse: http://localhost:8000//home/agspejkk/domains/dvr.regivan.tec.br/
├── app.py
├── passenger_wsgi.py ← ponto de entrada do Passenger
├── cameras_config.json ← configurações e credenciais
└── public_html/
└── .htaccess ← configuração do Passenger
# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION BEGIN
PassengerAppRoot "/home/agspejkk/domains/dvr.regivan.tec.br"
PassengerBaseURI "/"
PassengerPython "/home/agspejkk/virtualenv/domains/dvr.regivan.tec.br/3.11/bin/python"
# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION END
AuthType Noneimport os
_KEY = 'sua-chave-de-64-caracteres-aqui' # gere em: https://generate-secret.vercel.app/64
os.environ['DVR_SECRET_KEY'] = _KEY
from app import app as application
application.secret_key = _KEY
⚠️ A chave precisa ser fixa — todos os workers do Passenger compartilham a mesma para não perder sessões.
{
"cameras": {},
"auth": { "user": "admin", "password": "sua-senha-aqui" }
}Rode na rede local onde estão as câmeras:
# Edite as configurações no topo do arquivo primeiro
python discover_cameras.pyO script faz tudo automaticamente:
- Detecta o IP público do roteador
- Escaneia toda a subnet
/24(TCP + HTTP) - Atualiza o
cameras_config.jsonlocal (IPs internos) - Faz login e cadastra no DVR remoto com IP público + porta redirecionada
- Exibe o resumo de port forwarding para configurar no roteador
Configurações do script (discover_cameras.py):
| Variável | Descrição |
|---|---|
DVR_URL |
URL do app remoto |
DVR_USER / DVR_PASSWORD |
Credenciais do DVR app |
CAM_USER / CAM_PASSWORD |
Credenciais das câmeras |
CAM_MODEL |
generic, hikvision, dahua, intelbras, iscee |
PORT_FORWARD_START |
Primeira porta externa do roteador (ex: 8081) |
AUTO_REGISTER |
True = cadastra no remoto automaticamente |
SAVE_LOCAL_CONFIG |
True = salva no cameras_config.json local |
Acesse https://dvr.regivan.tec.br/cameras → Adicionar Nova Câmera
Para que o servidor acesse as câmeras de fora da rede local:
-
Configure port forwarding no roteador — uma porta por câmera:
IP_público:8081 → 192.168.1.x:80 (câmera 1) IP_público:8082 → 192.168.1.y:80 (câmera 2) -
IP público dinâmico? Configure DDNS:
-
O
discover_cameras.pydetecta o IP público e exibe o resumo de port forwarding automaticamente.
| Modelo | Paths testados |
|---|---|
| ISCEE / Genérico | /snapshot.cgi, /tmpfs/auto.jpg, /cgi-bin/snapshot.cgi, /image.jpg |
| Hikvision | /ISAPI/Streaming/channels/1/picture, /Streaming/channels/1/picture |
| Dahua | /cgi-bin/snapshot.cgi, /onvif/snapshot |
| Intelbras | /cgi-bin/snapshot.cgi, /snapshot.cgi |
| Genérico | Testa todos os paths acima |
| Rota | Descrição |
|---|---|
/ |
Dashboard com câmeras |
/login / /logout |
Autenticação |
/cameras |
Configuração de câmeras |
/scan |
Scanner de rede (server-side) |
/camera/<id> |
Stream MJPEG |
/api/camera/add |
POST — adicionar câmera |
/api/camera/edit/<id> |
POST — editar |
/api/camera/delete/<id> |
POST — remover |
/api/camera/toggle/<id> |
POST — ativar/desativar |
/api/auth/set |
POST — alterar credenciais |
- Login com
secrets.compare_digest(resistente a timing attack) - Cookie
HttpOnly+Secure+SameSite=Lax - User-Agent customizado (evita WAF rule 913113 do ModSecurity)
- Secret key fixa no
passenger_wsgi.py(consistência entre workers) - Validação de
next_urlno login (evita open redirect)
O DirectAdmin intercepta algumas rotas antes do Passenger/Flask:
| Rota bloqueada | Alternativa |
|---|---|
/config |
/cameras ✅ |
Se outras rotas redirecionarem para :2222, renomeie-as.
| Problema | Solução |
|---|---|
| Câmera não conecta | Verifique IP, porta, usuário/senha e modelo |
| Sessão perdida após login | Verifique se DVR_SECRET_KEY está fixo no passenger_wsgi.py |
| WAF bloqueando requisições | User-Agent já está configurado como Mozilla |
| Porta 8000 ocupada (local) | Edite o port=8000 na última linha do app.py |
flask==3.1.0
requests==2.32.3
- Python 3.7 ou superior
- Câmeras IP acessíveis na rede
pip install -r requirements.txtpython app.pyAbra o navegador em: http://localhost:8000/
- Acesse http://localhost:8000/config
- Preencha os dados:
- Nome: Ex: "Câmera Entrada"
- IP: Ex: 192.168.1.100
- Porta: Geralmente 80
- Usuário: admin (ou conforme sua câmera)
- Senha: Senha da câmera
- Modelo: Selecione o modelo correto
- Clique em Adicionar Câmera
O sistema irá testar automaticamente a conexão antes de salvar!
- Acesse http://localhost:8000/scan
- Clique em Iniciar Escaneamento
- Aguarde alguns minutos enquanto busca na rede
- Adicione manualmente as câmeras encontradas
- Paths testados:
/snapshot.cgi,/tmpfs/auto.jpg,/cgi-bin/snapshot.cgi,/image.jpg
- Paths testados:
/ISAPI/Streaming/channels/1/picture,/Streaming/channels/1/picture
- Paths testados:
/cgi-bin/snapshot.cgi,/onvif/snapshot
- Paths testados:
/cgi-bin/snapshot.cgi,/snapshot.cgi
- Testa múltiplos paths comuns de câmeras IP
Copie estes arquivos para o outro PC:
📁 dvr_local/
├── app.py
├── requirements.txt
└── cameras_config.json (criado automaticamente após configurar)
- Instalar Python: https://www.python.org/downloads/
- Abrir PowerShell na pasta do projeto
- Instalar dependências:
pip install -r requirements.txt
- Executar:
python app.py
- Verifique se o IP está correto:
ping SEU_IP_CAMERA - Teste no navegador:
http://SEU_IP_CAMERA/snapshot.cgi - Confirme usuário e senha
- Tente outro modelo de câmera
Edite a última linha de app.py:
app.run(host='0.0.0.0', port=OUTRA_PORTA, threaded=True, debug=False)- Aguarde o escaneamento completo (pode levar 5-10 minutos)
- Verifique se está na mesma rede das câmeras
- Adicione manualmente se souber o IP
O arquivo cameras_config.json é criado automaticamente. Você pode editá-lo manualmente:
{
"cameras": {
"1234567890": {
"name": "Câmera Entrada",
"ip": "SEU_IP_AQUI",
"port": "80",
"user": "SEU_USUARIO",
"password": "SUA_SENHA",
"model": "iscee",
"path": "/snapshot.cgi",
"enabled": true
}
}
}- ✅ Visualização simultânea de múltiplas câmeras
- ✅ Modo tela cheia (clique na câmera)
- ✅ Adicionar/remover câmeras via web
- ✅ Ativar/desativar câmeras temporariamente
- ✅ Scanner de rede automático
- ✅ Teste de conexão antes de salvar
- ✅ Suporte a múltiplos modelos de câmera
- ✅ Interface responsiva (funciona em celular)
- ✅ Totalmente standalone (não depende de Agent DVR ou outros softwares)
Para acessar de outros dispositivos na rede:
- Descubra o IP do PC:
ipconfig(Windows) ouifconfig(Linux/Mac) - Acesse de outro dispositivo:
http://SEU_IP:8000
Exemplo: http://192.168.1.X:8000
Logs são exibidos no terminal onde você executou python app.py.
Verifique mensagens de erro para diagnosticar problemas.
Desenvolvido para execução standalone em qualquer PC com Python instalado.