# MD2Video: Genera video da post Hugo in .MD (con qualche compromesso)

> Trasformare Blog Post in Video con Python e MoviePy, con la possibilità di aggiugnere qualche tocco personale

- URL: https://mautoblog.com/posts/da-testo-a-video/
- Lingua: it
- Pubblicato: 2024-11-25
- Aggiornato: 2026-10-13
- Autore: Matteo
- Categorie: Progetti, Professione
- Tags: Python, Testing, Docker, Best Practices, Architettura Software

---

Vi è mai capitato di guardare un post del blog e pensare "sarebbe bello poterlo ascoltare mentre faccio altro"? Beh, è esattamente quello che mi è passato per la testa qualche giorno fa. E come ogni sviluppatore che si rispetti, invece di cercare una soluzione esistente, ho pensato "hey, potrei scriverlo io!". Perché sì, a volte reinventare la ruota può essere divertente (e istruttivo).

## L'Architettura del Progetto

Il progetto è strutturato seguendo il principio della separazione delle responsabilità, con tre componenti principali che gestiscono rispettivamente l'input dei post, la generazione degli script e la creazione dei video.

### Pipeline di Elaborazione

```plaintext
Post Markdown ->   Script XML    -> Video MP4
     ↓                  ↓               ↓
BlogProcessor -> ScriptProcessor -> VideoProcessor
```

Ogni step della pipeline è indipendente e può essere testato e modificato separatamente. Questo approccio modulare permette di:
- Sostituire facilmente i componenti
- Aggiungere nuove funzionalità senza modificare il codice esistente
- Testare ogni componente in isolamento

## Il Cuore del Sistema: I Processor

### BlogProcessor
Si occupa di leggere i file markdown e estrarre:
- Metadati (frontmatter)
- Struttura del contenuto (headings, paragrafi)
- Elementi speciali (liste, citazioni)

```python
def _parse_content(self, content: str) -> List[Dict]:
    """Parse il contenuto markdown in sezioni strutturate"""
    sections = []
    current_section = {"level": 0, "title": "", "content": []}

    for line in content.split('\n'):
        # Matching dei titoli
        heading_match = re.match(r'^(#{1,6})\s+(.+)$', line)
        
        if heading_match:
            if current_section["content"]:
                sections.append(current_section)
            
            level = len(heading_match.group(1))
            title = heading_match.group(2).strip()
            current_section = {
                "level": level,
                "title": title,
                "content": []
            }
```

### ScriptProcessor
Genera uno script XML intermedio che definisce:
- La struttura del video
- Il testo da narrare
- Le transizioni e gli effetti

```xml
<?xml version="1.0" encoding="UTF-8"?>
<script version="1.0">
    <metadata>
        <title>Titolo Post</title>
        <date>2024-11-25</date>
    </metadata>
    <content>
        <section level="1" type="intro" animation="fade">
            <heading>Introduzione</heading>
            <speech pause="0.5">Testo da narrare</speech>
        </section>
    </content>
</script>
```

In realtà questo passaggio non è necesario. E' possibile infatti generare un file XML manualmente nella dir degli script.

Inoltre è possibile aggiungere degli sfondi custom al posto di quelli default generati dallo script, tramite il parametro background di section:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<script version="1.0">
    <metadata>
        <title>Titolo Post 2</title>
        <date>2024-11-25</date>
    </metadata>
    <content>
        <section level="1" type="intro" background="intro_bg.png" animation="zoom">
            <heading>Introduzione</heading>
            <speech pause="0.5">Testo da narrare</speech>
            <speech pause="0.7">Ulteriore testo da narrare</speech>
        </section>
    </content>
</script>
```

### VideoProcessor
Il componente più complesso, che si occupa di:
1. Generare le slide con PIL
2. Creare l'audio con gTTS
3. Applicare effetti e transizioni
4. Assemblare il video finale con MoviePy

```python
def _create_slide(self, text: str, output_path: Path):
    """Crea una slide con testo"""
    image = Image.new('RGB', 
                     (self.config.VIDEO_WIDTH, 
                      self.config.VIDEO_HEIGHT))
    draw = ImageDraw.Draw(image)
    
    # Configurazione font
    font = ImageFont.truetype(self.config.FONT_PATH, 
                             self.config.FONT_SIZE)
    
    # Layout del testo
    lines = self._wrap_text(text, font, 
                           self.config.MAX_WIDTH)
    y_position = self._calculate_vertical_position(lines)
    
    # Rendering del testo
    for line in lines:
        x = self._center_text(line, font)
        draw.text((x, y_position), line, 
                 font=font, 
                 fill=self.config.TEXT_COLOR)
        y_position += self.config.LINE_HEIGHT
```

## Sfide Tecniche e Soluzioni

### 1. Sincronizzazione Audio-Video
Una delle sfide principali è stata sincronizzare perfettamente l'audio con le slide. La soluzione? Utilizzare i metadati dell'audio generato:

```python
def _sync_audio_video(self, audio_clip, video_clip):
    """Sincronizza audio e video considerando le pause"""
    audio_duration = audio_clip.duration
    video_duration = audio_duration + self.config.PAUSE_DURATION
    
    # Estendi il video per coprire l'audio più la pausa
    extended_video = video_clip.set_duration(video_duration)
    
    # Applica l'audio
    final_clip = extended_video.set_audio(audio_clip)
    
    return final_clip
```

### 2. Gestione della Memoria
La generazione di video può essere memory-intensive. Ho implementato un sistema di pulizia progressiva:

```python
def cleanup(self):
    """Pulisce i file temporanei durante l'elaborazione"""
    temp_dir = Path(self.config.TEMP_DIR)
    if temp_dir.exists():
        for file in temp_dir.glob('*'):
            try:
                file.unlink()
            except Exception as e:
                self.logger.error(f"Errore pulizia: {str(e)}")
```

### 3. Text-to-Speech: Il Punto Dolente
Attualmente utilizzo gTTS per la sintesi vocale. Non è la soluzione ideale per diversi motivi:
- Qualità audio non eccezionale
- Necessità di connessione internet
- Limiti di utilizzo
- Poca naturalezza nella prosodia

Ho valutato alternative come:
- Amazon Polly (costoso per uso personale)
- Mozilla TTS (complesso da configurare)
- Coqui TTS (promettente ma ancora acerbo)

Per ora gTTS rimane la scelta più pratica per un progetto open source, ma sono aperto a suggerimenti per alternative migliori.

## Testing e Qualità del Codice

Ho implementato una suite di test completa utilizzando pytest:

```python
@pytest.fixture
def video_processor(tmp_path):
    """Crea un VideoProcessor configurato per i test"""
    processor = VideoProcessor()
    processor.config.TEMP_DIR = tmp_path / "temp"
    processor.config.OUTPUT_DIR = tmp_path / "output"
    return processor

def test_video_generation(video_processor):
    """Verifica la generazione completa del video"""
    script_path = "test_script.xml"
    output_file = video_processor.process(script_path)
    
    assert Path(output_file).exists()
    assert Path(output_file).stat().st_size > 0
```

## Work in Progress

Il progetto è ancora in fase di sviluppo attivo e ci sono diverse aree di miglioramento:

1. **Performance**
    - Parallelizzazione della generazione delle slide
    - Caching dei font e degli elementi grafici comuni
    - Ottimizzazione della pipeline video

2. **Features Pianificate**
    - Supporto per temi personalizzabili
    - Più effetti di transizione
    - Miglior gestione del layout per codice e tabelle

3. **Documentazione**
    - Guida completa all'installazione e all'utilizzo
    - API reference
    - Tutorial per l'estensione

## Contribuire al Progetto

Il codice è disponibile su [GitHub](https://github.com/Mauto82/md2video) e sono ben accetti contributi di ogni tipo:
- Bug fix
- Nuove feature
- Miglioramenti alla documentazione
- Suggerimenti per alternative a gTTS
- Report di bug e feedback

Per contribuire:
1. Forka il repository
2. Crea un branch per la tua feature
3. Implementa le modifiche
4. Aggiungi o aggiorna i test
5. Invia una pull request

## Conclusioni

MD2Video è nato come esperimento personale ma ha il potenziale per diventare uno strumento utile per chi vuole rendere i propri contenuti più accessibili. Nonostante le limitazioni attuali (soprattutto legate al text-to-speech), il progetto fornisce una base solida per generare video da contenuti markdown.

Se sei interessato a [contribuire](https://github.com/Mauto82/md2video) o hai suggerimenti, special modo riguardo alternative a gTTS, ogni contributo è benvenuto!
