147 lines
4.2 KiB
Python
147 lines
4.2 KiB
Python
import asyncio
|
|
import os
|
|
import socket
|
|
import threading
|
|
import time
|
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
|
|
import config
|
|
|
|
MEDIA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "voz_media")
|
|
_server = None
|
|
|
|
|
|
def _ip_lan():
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
try:
|
|
s.connect(("8.8.8.8", 80))
|
|
return s.getsockname()[0]
|
|
finally:
|
|
s.close()
|
|
|
|
|
|
def sintetizar(texto, nombre="anuncio.mp3"):
|
|
os.makedirs(MEDIA_DIR, exist_ok=True)
|
|
ruta = os.path.join(MEDIA_DIR, nombre)
|
|
try:
|
|
import edge_tts
|
|
|
|
async def _gen():
|
|
comunicador = edge_tts.Communicate(
|
|
texto,
|
|
config.VOZ_EDGE,
|
|
rate=config.VOZ_RATE,
|
|
pitch=config.VOZ_PITCH,
|
|
)
|
|
await comunicador.save(ruta)
|
|
|
|
asyncio.run(_gen())
|
|
return ruta
|
|
except Exception as e:
|
|
print(f"[Voz] Error sintetizando: {e}")
|
|
return None
|
|
|
|
|
|
class _Handler(SimpleHTTPRequestHandler):
|
|
def __init__(self, *a, **kw):
|
|
super().__init__(*a, directory=MEDIA_DIR, **kw)
|
|
|
|
def log_message(self, *a):
|
|
pass
|
|
|
|
|
|
def _arrancar_servidor():
|
|
global _server
|
|
if _server is not None:
|
|
return
|
|
try:
|
|
_server = HTTPServer(("0.0.0.0", config.GOOGLE_TV_PUERTO), _Handler)
|
|
threading.Thread(target=_server.serve_forever, daemon=True).start()
|
|
except Exception as e:
|
|
print(f"[Voz] No se pudo arrancar el servidor HTTP: {e}")
|
|
|
|
|
|
def _descubrir():
|
|
import pychromecast
|
|
|
|
chromecasts, browser = pychromecast.get_chromecasts(timeout=8)
|
|
try:
|
|
if config.GOOGLE_TV_NOMBRES:
|
|
return [c for c in chromecasts
|
|
if c.cast_info.friendly_name in config.GOOGLE_TV_NOMBRES]
|
|
return chromecasts
|
|
finally:
|
|
pychromecast.discovery.stop_discovery(browser)
|
|
|
|
|
|
def _url_audio(texto, nombre="anuncio.mp3"):
|
|
ruta = sintetizar(texto, nombre=nombre)
|
|
if ruta is None:
|
|
return None
|
|
_arrancar_servidor()
|
|
if _server is None:
|
|
return None
|
|
return f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
|
|
|
|
|
|
def _esperar_fin(cast, timeout=90):
|
|
mc = cast.media_controller
|
|
t0 = time.time()
|
|
while time.time() - t0 < timeout:
|
|
try:
|
|
st = mc.status
|
|
if st.player_state == "IDLE":
|
|
return True
|
|
except Exception:
|
|
pass
|
|
time.sleep(1)
|
|
return False
|
|
|
|
|
|
def anuncio_en_teles(texto):
|
|
"""Enciende la tele con el volumen configurado, reproduce el mensaje y la apaga."""
|
|
if not config.GOOGLE_TV_ACTIVO:
|
|
return
|
|
url = _url_audio(texto)
|
|
if url is None:
|
|
return
|
|
|
|
try:
|
|
import pychromecast
|
|
|
|
chromecasts, browser = pychromecast.get_chromecasts(timeout=8)
|
|
try:
|
|
if config.GOOGLE_TV_NOMBRES:
|
|
objetivos = [c for c in chromecasts
|
|
if c.cast_info.friendly_name in config.GOOGLE_TV_NOMBRES]
|
|
else:
|
|
objetivos = chromecasts
|
|
|
|
if not objetivos:
|
|
print("[Voz] No hay teles Google detectadas")
|
|
return
|
|
|
|
for c in objetivos:
|
|
try:
|
|
c.wait(timeout=10)
|
|
c.set_volume(config.GOOGLE_TV_VOLUMEN / 100.0)
|
|
c.media_controller.play_media(url, "audio/mpeg", title=texto[:80])
|
|
c.media_controller.block_until_active(timeout=10)
|
|
print(f"[Voz] Anuncio en {c.cast_info.friendly_name} "
|
|
f"(vol {config.GOOGLE_TV_VOLUMEN})")
|
|
if _esperar_fin(c):
|
|
print(f"[Voz] Anuncio terminado en {c.cast_info.friendly_name}")
|
|
if config.GOOGLE_TV_APAGAR:
|
|
c.quit_app()
|
|
print(f"[Voz] {c.cast_info.friendly_name} apagada (standby)")
|
|
except Exception as e:
|
|
print(f"[Voz] Error en {c.cast_info.friendly_name}: {e}")
|
|
finally:
|
|
pychromecast.discovery.stop_discovery(browser)
|
|
except Exception as e:
|
|
print(f"[Voz] Error de casting: {e}")
|
|
|
|
|
|
def reproducir_en_teles(texto):
|
|
anuncio_en_teles(texto)
|