feat(ao-vivo): camera sempre visivel, lista estilo iOS e dados do aparelho

- palco fixo no topo: camera grande com o rosto num quadrinho no canto
  (toque troca os dois); botao "Ligar camera" direto no palco
- controles em lista agrupada estilo Ajustes do iPhone (Grupo, Linha,
  Interruptor): camera e rastreamento, gestos, aparencia, personalidade
- cartao do celular com modelo, Android, versao do app, bateria, Wi-Fi,
  IP e estado do rosto
- APK 0.8.1 informa ip/modelo/android/app/wifi (ACCESS_WIFI_STATE, so o
  nivel do sinal); servidor usa o IP informado pelo app em vez do
  endereco da conexao

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-05 23:07:52 -03:00
co-authored by Claude Opus 5.5
parent 3e51266644
commit a058c6f453
13 changed files with 705 additions and 192 deletions
+12 -1
View File
@@ -1,4 +1,5 @@
"""Configuração persistente e telemetria do celular; sinalização do vídeo WebRTC."""
import ipaddress
import json
import math
from pathlib import Path
@@ -51,6 +52,16 @@ def validar(dados):
return dados
def ip_informado(dados):
"""IP que o próprio app informa (aparelho.ip); o endereço da conexão pode ser de um proxy."""
aparelho = dados.get('aparelho')
ip = aparelho.get('ip') if isinstance(aparelho, dict) else None
try:
return str(ipaddress.IPv4Address(ip)) if isinstance(ip, str) else None
except ValueError:
return None
class ControlePC:
def __init__(self, arquivo):
self.arquivo = Path(arquivo)
@@ -134,7 +145,7 @@ class ControlePC:
raise ValueError('Estado deve ser objeto JSON')
with self.lock:
self.estado = dados
self.celular_ip = h.client_address[0]
self.celular_ip = ip_informado(dados) or h.client_address[0]
if self.recarga["id"] and str(dados.get("tela_atualizada", "")) == self.recarga["id"]:
self.recarga["confirmada"] = True
self.status_at = time.monotonic()