feat(celular): rosto e app usam SSE e token; remove telas legadas
- carinha.html assina /eventos (expressao, app_config, conversa, reacao, recarregar) e volta ao polling de /fila sozinho se o SSE cair; foto dos ajustes a cada (re)conexao; token (?t=) vai em todo fetch ao servidor - APK 0.8-token: token nas preferencias e no menu de toque longo, enviado na pagina, no HTTP do Java e ao llama-server (--api-key); trocar servidor por Intent exige o token salvo; camera/microfone so para o servidor configurado ou a copia local - cerebro/conversa mandam a chave ao llama-server do S10 - servidor: evento SSE 'recarregar'; remove /camera/frame e /app/connect; enderecos antigos redirecionam para a secao do painel novo - remove dashboard, painel, conversa, emojis, treinamento, braco, servos, api, scripts e CSS antigos, audio-robot.js e servir-local.bat - logica pura do painel em web/src/lib/nucleo.ts com testes node --test (substitui test_movimentos.js e test_rastreamento.js) - testes de navegador portados para o painel novo (rtc, rosto, rastreamento) - README, docs/PAINEL.md e docs/SEGURANCA.md reescritos; versao 0.8.0; carinha-pc.service le /etc/carinha.env Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+31
-19
@@ -1,4 +1,6 @@
|
||||
"""Real WebRTC sender and panel in Chromium, using a synthetic camera."""
|
||||
"""WebRTC real entre o transmissor do app e o painel novo (web/dist), com câmera sintética.
|
||||
|
||||
Requer `npm run build` em web/."""
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
@@ -27,6 +29,7 @@ def main():
|
||||
server = kiosk.Servidor(('127.0.0.1', 0), Handler)
|
||||
server.controle = kiosk.ControlePC(Path(tmp) / 'config.json')
|
||||
server.controle.atualizar({'preview': True,'rastrearRosto':False,'rastrearMaos':False})
|
||||
kiosk.ligar_eventos(server) # telemetria e ajustes chegam ao painel por SSE
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
base = 'http://127.0.0.1:' + str(server.server_port)
|
||||
@@ -62,40 +65,49 @@ def main():
|
||||
},200);
|
||||
}""")
|
||||
sender.request.post(base + '/app/status', data={'rosto':'carinha'})
|
||||
# Como o celular real, manda batimento a cada 1 s (senão o painel o considera offline).
|
||||
sender.evaluate("""() => setInterval(() => fetch('/app/status',{method:'POST',
|
||||
headers:{'Content-Type':'application/json'},body:JSON.stringify({rosto:'carinha'})}), 1000)""")
|
||||
panel = context.new_page()
|
||||
panel.on('pageerror', lambda e: errors.append(str(e)))
|
||||
panel.goto(base + '/painel')
|
||||
panel.wait_for_function("document.getElementById('cameraVideo').videoWidth > 0", timeout=20000)
|
||||
first = panel.evaluate("document.getElementById('cameraVideo').getVideoPlaybackQuality().totalVideoFrames")
|
||||
panel.goto(base + '/')
|
||||
video = "document.querySelector('.camera video')"
|
||||
panel.wait_for_function(video + ".videoWidth > 0", timeout=20000)
|
||||
first = panel.evaluate(video + ".getVideoPlaybackQuality().totalVideoFrames")
|
||||
panel.wait_for_timeout(2200)
|
||||
count = panel.evaluate("document.getElementById('cameraVideo').getVideoPlaybackQuality().totalVideoFrames") - first
|
||||
count = panel.evaluate(video + ".getVideoPlaybackQuality().totalVideoFrames") - first
|
||||
assert count >= 45, 'Vídeo não está fluido: %s quadros em 2.2 s' % count
|
||||
assert panel.locator('#cameraVideo').is_visible()
|
||||
assert 'fps' in panel.locator('#videoQualidade').inner_text()
|
||||
assert panel.locator('.camera video').is_visible()
|
||||
qualidade = panel.locator('.camera .rodape .mono').inner_text()
|
||||
assert 'fps' in qualidade, qualidade
|
||||
assert not errors, errors
|
||||
print('Vídeo WebRTC real: %s quadros em 2.2 s; %s' % (count, panel.locator('#videoQualidade').inner_text()))
|
||||
print('Vídeo WebRTC real: %s quadros em 2.2 s; %s' % (count, qualidade))
|
||||
# Canal de rastreamento (DataChannel) chega ao transmissor.
|
||||
sender.request.post(base+'/app/config',data={'rastrearMaos':True})
|
||||
sender.wait_for_function('config.rastrearMaos === true')
|
||||
panel.wait_for_function('CarinhaVideo.rastreamentoConectado()')
|
||||
panel.evaluate("CarinhaVideo.rastrear({tipo:'mao',x:.8,y:.3,atividade:true})")
|
||||
panel.wait_for_function('window.carinhaVideo?.rastreamentoConectado')
|
||||
panel.wait_for_timeout(1000) # sem vendor/ o rastreador falha e manda null uma vez
|
||||
panel.evaluate("carinhaVideo.rastrear({tipo:'mao',x:.8,y:.3,atividade:true})")
|
||||
sender.wait_for_function("window.testAlvo?.tipo === 'mao' && testAlvo.x === .8")
|
||||
panel.evaluate('CarinhaVideo.rastrear(null)')
|
||||
panel.evaluate('carinhaVideo.rastrear(null)')
|
||||
sender.wait_for_function('window.testAlvo === null')
|
||||
sender.request.post(base+'/app/config',data={'rastrearMaos':False})
|
||||
panel.locator('#ajuste-videoResolucao').select_option('480')
|
||||
# Ajustes chegam ao celular; o painel acompanha por SSE.
|
||||
sender.request.post(base+'/app/config',data={'videoResolucao':'480'})
|
||||
sender.wait_for_function("stream && stream.getVideoTracks()[0].getSettings().height === 480", timeout=10000)
|
||||
panel.locator('#ajuste-videoFPS').select_option('240')
|
||||
sender.request.post(base+'/app/config',data={'videoFPS':240})
|
||||
sender.wait_for_function("aviso.includes('indisponível')", timeout=10000)
|
||||
assert sender.evaluate('stream.getVideoTracks()[0].readyState') == 'live'
|
||||
panel.locator('#ajuste-sensorCamera').uncheck()
|
||||
panel.wait_for_function("document.getElementById('cameraVideo').srcObject === null", timeout=10000)
|
||||
sender.request.post(base+'/app/config',data={'sensorCamera':False})
|
||||
panel.wait_for_function(video + ".srcObject === null", timeout=10000)
|
||||
sender.wait_for_function('stream === null', timeout=10000)
|
||||
panel.locator('#ajuste-sensorCamera').check()
|
||||
panel.wait_for_function("document.getElementById('cameraVideo').videoWidth > 0 && document.getElementById('cameraVideo').srcObject !== null", timeout=15000)
|
||||
sender.request.post(base+'/app/config',data={'sensorCamera':True})
|
||||
panel.wait_for_function(video + ".videoWidth > 0 && " + video + ".srcObject !== null", timeout=15000)
|
||||
assert not errors, errors
|
||||
panel.get_by_role('button', name='Desligar câmera').click()
|
||||
# Desligar pela chave da tela Ao vivo.
|
||||
panel.get_by_role('switch', name='Transmitir câmera do celular').locator('xpath=..').click()
|
||||
panel.wait_for_timeout(1400)
|
||||
assert panel.evaluate("document.getElementById('cameraVideo').srcObject === null")
|
||||
assert panel.evaluate(video + ".srcObject === null")
|
||||
preview = context.new_page()
|
||||
preview.on('pageerror', lambda e: errors.append(str(e)))
|
||||
preview.goto(base + '/camera.html?lado=user&resolucao=480&fps=30')
|
||||
|
||||
Reference in New Issue
Block a user