02-ago: [proyecto] vigilancia - bucle de mensaje x3 (GOOGLE_TV_REPETICIONES) + texto final en pantalla 30s (GOOGLE_TV_TEXTO_FINAL_SG) antes de apagar
This commit is contained in:
@@ -51,6 +51,8 @@ GOOGLE_TV_APAGAR=1
|
|||||||
GOOGLE_TV_PITIDO_ACTIVO=1
|
GOOGLE_TV_PITIDO_ACTIVO=1
|
||||||
GOOGLE_TV_ESPERA_SG=15
|
GOOGLE_TV_ESPERA_SG=15
|
||||||
GOOGLE_TV_PITIDO_SG=6
|
GOOGLE_TV_PITIDO_SG=6
|
||||||
|
GOOGLE_TV_REPETICIONES=3
|
||||||
|
GOOGLE_TV_TEXTO_FINAL_SG=30
|
||||||
VOZ_EDGE=es-ES-AlvaroNeural
|
VOZ_EDGE=es-ES-AlvaroNeural
|
||||||
VOZ_PITCH=-15Hz
|
VOZ_PITCH=-15Hz
|
||||||
VOZ_RATE=-10%
|
VOZ_RATE=-10%
|
||||||
|
|||||||
@@ -72,3 +72,5 @@ GOOGLE_TV_APAGAR_VIA_ADB = os.getenv("GOOGLE_TV_APAGAR_VIA_ADB", "1") == "1"
|
|||||||
GOOGLE_TV_PITIDO_ACTIVO = os.getenv("GOOGLE_TV_PITIDO_ACTIVO", "1") == "1"
|
GOOGLE_TV_PITIDO_ACTIVO = os.getenv("GOOGLE_TV_PITIDO_ACTIVO", "1") == "1"
|
||||||
GOOGLE_TV_ESPERA_SG = int(os.getenv("GOOGLE_TV_ESPERA_SG", "15"))
|
GOOGLE_TV_ESPERA_SG = int(os.getenv("GOOGLE_TV_ESPERA_SG", "15"))
|
||||||
GOOGLE_TV_PITIDO_SG = float(os.getenv("GOOGLE_TV_PITIDO_SG", "6"))
|
GOOGLE_TV_PITIDO_SG = float(os.getenv("GOOGLE_TV_PITIDO_SG", "6"))
|
||||||
|
GOOGLE_TV_REPETICIONES = int(os.getenv("GOOGLE_TV_REPETICIONES", "3"))
|
||||||
|
GOOGLE_TV_TEXTO_FINAL_SG = int(os.getenv("GOOGLE_TV_TEXTO_FINAL_SG", "30"))
|
||||||
|
|||||||
@@ -141,3 +141,55 @@ def generar_video(texto, ruta_audio, oraciones, nombre="anuncio.mp4"):
|
|||||||
proc.stdin.close()
|
proc.stdin.close()
|
||||||
proc.wait()
|
proc.wait()
|
||||||
return ruta_video
|
return ruta_video
|
||||||
|
|
||||||
|
|
||||||
|
def generar_video_texto_final(texto, segundos, nombre="texto_final.mp4"):
|
||||||
|
"""Video estatico con el texto completo en pantalla durante `segundos` s."""
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
ruta_video = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"voz_media", nombre)
|
||||||
|
font = ImageFont.truetype(FUENTE, 52) if FUENTE else ImageFont.load_default()
|
||||||
|
gap = 18
|
||||||
|
|
||||||
|
lineas = []
|
||||||
|
linea = []
|
||||||
|
ancho = 0
|
||||||
|
for w in texto.split():
|
||||||
|
a = font.getlength(w)
|
||||||
|
if ancho + a + gap > W - 80 and linea:
|
||||||
|
lineas.append(linea)
|
||||||
|
linea = [w]
|
||||||
|
ancho = a
|
||||||
|
else:
|
||||||
|
linea.append(w)
|
||||||
|
ancho += a + gap
|
||||||
|
if linea:
|
||||||
|
lineas.append(linea)
|
||||||
|
|
||||||
|
n_frames = int(segundos * FPS)
|
||||||
|
cmd = [FFMPEG, "-y", "-f", "rawvideo", "-vcodec", "rawvideo",
|
||||||
|
"-s", f"{W}x{H}", "-pix_fmt", "rgb24", "-r", str(FPS), "-i", "-",
|
||||||
|
"-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
|
||||||
|
"-an", ruta_video]
|
||||||
|
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
for i in range(n_frames):
|
||||||
|
img = Image.new("RGB", (W, H), (8, 8, 8))
|
||||||
|
d = ImageDraw.Draw(img)
|
||||||
|
y = H // 2 - (len(lineas) // 2) * 80 + 30
|
||||||
|
for linea_words in lineas:
|
||||||
|
x = 40
|
||||||
|
for w in linea_words:
|
||||||
|
d.text((x, y), w, fill=(255, 255, 255), font=font)
|
||||||
|
x += font.getlength(w) + gap
|
||||||
|
y += 80
|
||||||
|
try:
|
||||||
|
proc.stdin.write(img.tobytes())
|
||||||
|
except BrokenPipeError:
|
||||||
|
break
|
||||||
|
|
||||||
|
proc.stdin.close()
|
||||||
|
proc.wait()
|
||||||
|
return ruta_video
|
||||||
|
|||||||
@@ -99,6 +99,22 @@ def _url_video(texto):
|
|||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def _url_texto_final(texto):
|
||||||
|
"""Video estatico con el texto completo (al final del bucle)."""
|
||||||
|
if config.GOOGLE_TV_TEXTO_FINAL_SG <= 0:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
from teleprompter import generar_video_texto_final
|
||||||
|
ruta = generar_video_texto_final(texto, config.GOOGLE_TV_TEXTO_FINAL_SG)
|
||||||
|
_arrancar_servidor()
|
||||||
|
if _server is None:
|
||||||
|
return None
|
||||||
|
return f"http://{_ip_lan()}:{config.GOOGLE_TV_PUERTO}/{os.path.basename(ruta)}"
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Voz] No se pudo generar el texto final: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _url_audio(texto, nombre="anuncio.mp3"):
|
def _url_audio(texto, nombre="anuncio.mp3"):
|
||||||
ruta = sintetizar(texto, nombre=nombre)
|
ruta = sintetizar(texto, nombre=nombre)
|
||||||
if ruta is None:
|
if ruta is None:
|
||||||
@@ -229,6 +245,7 @@ def anuncio_en_teles(texto):
|
|||||||
return
|
return
|
||||||
url_pitido = _url_pitido() if config.GOOGLE_TV_PITIDO_ACTIVO else None
|
url_pitido = _url_pitido() if config.GOOGLE_TV_PITIDO_ACTIVO else None
|
||||||
url_silencio = _url_silencio() if url_pitido else None
|
url_silencio = _url_silencio() if url_pitido else None
|
||||||
|
url_final = _url_texto_final(texto)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pychromecast
|
import pychromecast
|
||||||
@@ -264,10 +281,18 @@ def anuncio_en_teles(texto):
|
|||||||
c, url_pitido, "audio/wav", "pitido",
|
c, url_pitido, "audio/wav", "pitido",
|
||||||
f"Pitido de emergencia en {c.cast_info.friendly_name}")
|
f"Pitido de emergencia en {c.cast_info.friendly_name}")
|
||||||
|
|
||||||
_reproducir_y_esperar(
|
rep = max(1, config.GOOGLE_TV_REPETICIONES)
|
||||||
c, url_mensaje, tipo_mensaje, texto[:80],
|
for n in range(rep):
|
||||||
f"Anuncio en {c.cast_info.friendly_name} "
|
_reproducir_y_esperar(
|
||||||
f"(vol {config.GOOGLE_TV_VOLUMEN})")
|
c, url_mensaje, tipo_mensaje, texto[:80],
|
||||||
|
f"Anuncio {n + 1}/{rep} en {c.cast_info.friendly_name} "
|
||||||
|
f"(vol {config.GOOGLE_TV_VOLUMEN})")
|
||||||
|
|
||||||
|
if url_final:
|
||||||
|
_reproducir_y_esperar(
|
||||||
|
c, url_final, "video/mp4", "texto final",
|
||||||
|
f"Texto final {config.GOOGLE_TV_TEXTO_FINAL_SG}s en "
|
||||||
|
f"{c.cast_info.friendly_name}")
|
||||||
|
|
||||||
if config.GOOGLE_TV_APAGAR_VIA_ADB:
|
if config.GOOGLE_TV_APAGAR_VIA_ADB:
|
||||||
from tv_adb import apagar_tv
|
from tv_adb import apagar_tv
|
||||||
|
|||||||
Reference in New Issue
Block a user