7c5330a528
Aprovechar la cuota ociosa de MiniMax para locutar el backlog historico, empezando por Fray Marcos (2026 pendientes + 2025 = 86 articulos). - fea_post_io.php: accion `listpending <autor> <desde> <hasta> <limite> [voz]`. Devuelve los posts ES publicados de un autor que no tienen audio, mas recientes primero. Si se pasa la voz clonada del autor, tambien salen los locutados con otra voz, para rehacerlos. Esa consulta ES la idempotencia: no hay fichero de estado que mantener, lo ya locutado deja de salir solo. Relanzar o solapar no duplica ni gasta cuota. - tts_produce.py: flags --autor/--desde/--hasta/--max/--dry-run. Sin flags, el modo cartas de la carta semanal no cambia (retrocompatible). --max acota la tanda a la ventana de 5h. - tts_backlog_cron.sh: wrapper de cron con flock y gate de cuota MiniMax (semanal >=85% o ventana de 5h >=55% -> se salta la ventana, para no comerle la cuota a la carta semanal). Cola configurable por entorno. Crontab: 0 */5 * * 1,5,6,0 (lunes, viernes, sabado y domingo). SOLO LOCAL: no toca produccion. Publicar el backlog en prod (sync_audio_to_prod.py) queda bloqueado hasta despues del cutover a Hetzner (#180). De paso entran al historial los cambios de tts_produce.py que estaban sin commitear desde el 24-jul (argparse con --ids/--cartas y QUOTA_OR_RATE_ERRORS para no reintentar ante rc 2056/1039). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.6 KiB
Bash
82 lines
3.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Cron del backlog de TTS por autor (issue rafa/feadulta#188).
|
|
# Corre cada 5 h los lunes, viernes, sábados y domingos, aprovechando la cuota
|
|
# ociosa de MiniMax para locutar artículos antiguos. Por ventana:
|
|
# 1) Gate de cuota MiniMax: si la semana va alta, o si la ventana de 5 h ya la
|
|
# está usando alguien (la carta semanal, el backfill de summaraise...), se
|
|
# salta. El trabajo de fondo nunca le come la cuota al trabajo con dueño.
|
|
# 2) tts_produce.py --autor ... --max N: la cola sale de la BD, así que esto es
|
|
# idempotente por construcción — lo ya locutado no vuelve a salir.
|
|
# SOLO LOCAL: no toca producción. Publicar en prod es sync_audio_to_prod.py, que
|
|
# está bloqueado hasta después del cutover a Hetzner (#180).
|
|
# flock evita solapes si una ventana se alargara. Log por día.
|
|
set -uo pipefail
|
|
|
|
export PATH="/home/rafa/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
export HOME="/home/rafa"
|
|
|
|
REPO="/home/rafa/joomla-migration"
|
|
PY="/home/rafa/tts-local/xtts-venv/bin/python"
|
|
QUOTA="/home/rafa/ytsummaries/scripts/quota.py"
|
|
WORK="/tmp/fea-tts-backlog"
|
|
LOG="$WORK/cron-$(date +%F).log"
|
|
LOCK="$WORK/cron.lock"
|
|
|
|
# Cola: cambiar aquí (o por entorno) para pasar de un autor a otro.
|
|
# 382 Fray Marcos · 383 Pagola · 774 Sicre · 386 Arregi
|
|
AUTOR="${FEA_TTS_AUTOR:-382}"
|
|
DESDE="${FEA_TTS_DESDE:-2025}"
|
|
HASTA="${FEA_TTS_HASTA:-2026}"
|
|
BATCH="${FEA_TTS_BATCH:-10}"
|
|
|
|
# Gates de cuota (%). El semanal es el techo real del backlog. El de 5 h es
|
|
# "¿cabe esta tanda en lo que queda de ventana?": medido, cada audio gasta ~3-4 %
|
|
# de la ventana de 5 h, así que un batch de 10 pide ~40 %. Gate en 55 % deja sitio
|
|
# y frena solo cuando la ventana ya la está usando otro (la carta semanal son ~28
|
|
# audios de golpe) o cuando la está llenando este mismo cron. Si se sube BATCH,
|
|
# subir también este número o se saltarán ventanas.
|
|
MAX_SEMANA="${FEA_TTS_MAX_SEMANA:-85}"
|
|
MAX_5H="${FEA_TTS_MAX_5H:-55}"
|
|
|
|
mkdir -p "$WORK"
|
|
cd "$REPO" || exit 1
|
|
|
|
ts() { date +'%F %T'; }
|
|
exec 9>"$LOCK"
|
|
if ! flock -n 9; then
|
|
echo "[$(ts)] otra corrida en curso, salto." >> "$LOG"; exit 0
|
|
fi
|
|
|
|
echo "[$(ts)] === cron TTS backlog start (autor=$AUTOR $DESDE-$HASTA batch=$BATCH) ===" >> "$LOG"
|
|
|
|
# 1) Gate de cuota MiniMax (ventana de 5 h y semanal)
|
|
read -r PCT5 PCTW <<< "$(python3 "$QUOTA" --json --no-local 2>/dev/null | python3 -c '
|
|
import json, sys
|
|
try:
|
|
d = json.load(sys.stdin)
|
|
m = next(p for p in d["providers"] if p["provider"] == "minimax" and p.get("ok"))
|
|
print(int(m.get("five_h_pct") or 100), int(m.get("week_pct") or 100))
|
|
except Exception:
|
|
print(100, 100) # sin lectura fiable de cuota, no se gasta
|
|
' 2>/dev/null || echo "100 100")"
|
|
echo "[$(ts)] MiniMax 5h=${PCT5}% semana=${PCTW}%" >> "$LOG"
|
|
|
|
if [ "${PCTW:-100}" -ge "$MAX_SEMANA" ]; then
|
|
echo "[$(ts)] ABORT: cuota semanal ${PCTW}% >= ${MAX_SEMANA}%, salto esta ventana." >> "$LOG"
|
|
exit 0
|
|
fi
|
|
if [ "${PCT5:-100}" -ge "$MAX_5H" ]; then
|
|
echo "[$(ts)] ABORT: ventana de 5h al ${PCT5}% >= ${MAX_5H}%, no cabe la tanda; salto." >> "$LOG"
|
|
exit 0
|
|
fi
|
|
|
|
# 2) Tanda. tts_produce.py ya para solo ante rc 2056/1039 (cuota/rate limit).
|
|
echo "[$(ts)] tts_produce --autor $AUTOR --desde $DESDE --hasta $HASTA --max $BATCH ..." >> "$LOG"
|
|
"$PY" scripts/tts_produce.py --autor "$AUTOR" --desde "$DESDE" --hasta "$HASTA" \
|
|
--max "$BATCH" >> "$LOG" 2>&1
|
|
|
|
# 3) Cuánto queda tras la tanda (recuento fresco de la BD, barato y sin cuota).
|
|
QUEDAN="$("$PY" scripts/tts_produce.py --autor "$AUTOR" --desde "$DESDE" --hasta "$HASTA" \
|
|
--dry-run 2>/dev/null | sed -n 's/.*Cola: \([0-9]*\) posts.*/\1/p' | tail -1)"
|
|
echo "[$(ts)] === cron TTS backlog done. Pendientes autor $AUTOR $DESDE-$HASTA: ${QUEDAN:-?} ===" >> "$LOG"
|