107 lines
3.7 KiB
Python
107 lines
3.7 KiB
Python
"""
|
|
Convierte TREBEP.md a audio dividido por Títulos.
|
|
Genera un MP3 por Título (8 Títulos + Disposiciones) en audios/mp3/bloque1/.
|
|
Usa edge_tts (es-ES-AlvaroNeural) + ffmpeg, igual que md_a_audio_con_edge_y_metadatos.py
|
|
"""
|
|
import asyncio
|
|
import edge_tts
|
|
import re
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def limpiar_markdown(texto):
|
|
texto = re.sub(r'```.*?```', ' [código] ', texto, flags=re.DOTALL)
|
|
texto = re.sub(r'\|.*?\|', '', texto)
|
|
texto = re.sub(r'[#*_~`>]', '', texto)
|
|
return ' '.join(texto.split())
|
|
|
|
|
|
def dividir_por_titulos(md_path):
|
|
"""
|
|
Devuelve lista de (nombre_fichero, titulo_meta, texto_seccion).
|
|
Las secciones son los 8 Títulos + un bloque final de Disposiciones.
|
|
"""
|
|
texto = Path(md_path).read_text(encoding='utf-8')
|
|
secciones = []
|
|
|
|
# Separar por ## TÍTULO
|
|
partes = re.split(r'(?=^## TÍTULO)', texto, flags=re.MULTILINE)
|
|
|
|
for parte in partes:
|
|
if not parte.strip():
|
|
continue
|
|
|
|
match = re.match(r'^## (TÍTULO ([IVX]+)) · (.+)', parte, re.MULTILINE)
|
|
if match:
|
|
titulo_num = match.group(2)
|
|
titulo_texto = match.group(3).strip()
|
|
num_romano = {'I': 1, 'II': 2, 'III': 3, 'IV': 4,
|
|
'V': 5, 'VI': 6, 'VII': 7, 'VIII': 8}.get(titulo_num, 0)
|
|
nombre = f'trebep_titulo{num_romano:02d}_audio'
|
|
meta = f'TREBEP · Título {titulo_num}: {titulo_texto}'
|
|
|
|
# Si hay disposiciones al final de TÍTULO VIII, separarlas
|
|
idx_disp = parte.find('\n#### Disposición adicional primera')
|
|
if idx_disp != -1:
|
|
secciones.append((nombre, meta, parte[:idx_disp]))
|
|
disp_texto = parte[idx_disp:]
|
|
secciones.append(('trebep_disposiciones_audio',
|
|
'TREBEP · Disposiciones adicionales, transitorias y finales',
|
|
disp_texto))
|
|
else:
|
|
secciones.append((nombre, meta, parte))
|
|
else:
|
|
# Bloque inicial (antes del primer Título): ignorar si es mínimo
|
|
if len(parte.strip()) > 100:
|
|
secciones.append(('trebep_intro_audio', 'TREBEP · Introducción', parte))
|
|
|
|
return secciones
|
|
|
|
|
|
async def generar_audio(nombre, meta, texto, mp3_dir):
|
|
mp3_path = mp3_dir / f'{nombre.replace("_audio", "")}.mp3'
|
|
temp_path = mp3_dir / f'{nombre}.temp.mp3'
|
|
|
|
if mp3_path.exists():
|
|
print(f'⏭️ Ya existe, omitiendo: {mp3_path.name}')
|
|
return
|
|
|
|
texto_limpio = limpiar_markdown(texto)
|
|
print(f'🎙️ Generando: {mp3_path.name} ({len(texto_limpio.split())} palabras)...')
|
|
|
|
comunicar = edge_tts.Communicate(texto_limpio, 'es-ES-AlvaroNeural')
|
|
await comunicar.save(str(temp_path))
|
|
|
|
meta_escaped = meta.replace('"', '\\"')
|
|
comando = (
|
|
f'ffmpeg -i "{temp_path}" -codec:a libmp3lame -b:a 192k -ar 44100 '
|
|
f'-metadata title="{meta_escaped}" -id3v2_version 3 -write_id3v1 1 '
|
|
f'-y "{mp3_path}" > /dev/null 2>&1'
|
|
)
|
|
os.system(comando)
|
|
|
|
if temp_path.exists():
|
|
temp_path.unlink()
|
|
|
|
print(f'✅ Listo: {mp3_path.name} (Título: {meta})')
|
|
|
|
|
|
async def main():
|
|
base_dir = Path(__file__).parent.parent
|
|
md_path = base_dir / 'bloque1' / 'leyes' / 'TREBEP.md'
|
|
mp3_dir = base_dir / 'audios' / 'mp3' / 'bloque1'
|
|
mp3_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
secciones = dividir_por_titulos(md_path)
|
|
print(f'\nTREBEP.md → {len(secciones)} secciones de audio\n')
|
|
|
|
for nombre, meta, texto in secciones:
|
|
await generar_audio(nombre, meta, texto, mp3_dir)
|
|
|
|
print('\n🎧 Todos los audios del TREBEP generados en:', mp3_dir)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(main())
|