"""
czytaj-tekst.py - polski lektor offline (Piper) z pliku tekstowego do WAV/MP3.
Autor: Piotr Olszewski, promptowy.com. Licencja skryptu: MIT.
Sprawdzone 17.10.2026: Windows 11, Python 3.13, piper-tts 1.8.0, czysty folder.

Instalacja (raz):
  pip install piper-tts

Użycie:
  python czytaj-tekst.py tekst.txt                 -> tekst.wav (głos gosia)
  python czytaj-tekst.py tekst.txt --glos darkman  -> inny głos: gosia, darkman, mc_speech, bass, mls_6892
  python czytaj-tekst.py tekst.txt --tempo 1.15    -> wolniej (1.0 = normalnie, 0.9 = szybciej)
  python czytaj-tekst.py tekst.txt --mp3           -> dodatkowo MP3 (wymaga ffmpeg w PATH)

Głos pobiera się przy pierwszym użyciu z oficjalnego repozytorium rhasspy/piper-voices
na Hugging Face do folderu "glosy" (60-115 MB na głos). Potem wszystko działa bez internetu.
Licencje głosów: gosia, darkman, mc_speech - CC0; bass - Apache 2.0; mls_6892 - CC BY 4.0.
Silnik Piper (piper1-gpl) jest na licencji GPL-3.0.

Liczby i skróty (17.03.2027 r., godz., zł) rozpisz słownie przed czytaniem - Piper czyta je dosłownie.
"""
import argparse, os, sys, time, wave, shutil, subprocess, urllib.request
from pathlib import Path

GLOSY = {'gosia': 'medium', 'darkman': 'medium', 'mc_speech': 'medium', 'bass': 'high', 'mls_6892': 'low'}
HF = 'https://huggingface.co/rhasspy/piper-voices/resolve/main/pl/pl_PL/{g}/{q}/pl_PL-{g}-{q}.onnx{ext}'


def pobierz_glos(glos, katalog):
    q = GLOSY[glos]
    katalog.mkdir(parents=True, exist_ok=True)
    model = katalog / f'pl_PL-{glos}-{q}.onnx'
    for ext in ('', '.json'):
        cel = Path(str(model) + ext)
        if cel.exists() and cel.stat().st_size > 0:
            continue
        url = HF.format(g=glos, q=q, ext=ext)
        print(f'Pobieram {cel.name} z Hugging Face (rhasspy/piper-voices)...', flush=True)
        tmp = Path(str(cel) + '.part')
        with urllib.request.urlopen(url, timeout=60) as r, open(tmp, 'wb') as f:
            shutil.copyfileobj(r, f)
        tmp.replace(cel)
    return model


def main():
    ap = argparse.ArgumentParser(description='Polski lektor offline (Piper): plik .txt -> .wav')
    ap.add_argument('plik', help='plik tekstowy UTF-8')
    ap.add_argument('--glos', default='gosia', choices=sorted(GLOSY))
    ap.add_argument('--tempo', type=float, default=1.0, help='1.0 normalnie, >1 wolniej, <1 szybciej')
    ap.add_argument('--wyjscie', help='nazwa pliku WAV (domyślnie jak plik tekstowy)')
    ap.add_argument('--glosy', default='glosy', help='folder na pobrane głosy')
    ap.add_argument('--mp3', action='store_true', help='zrób też MP3 (ffmpeg)')
    x = ap.parse_args()

    from piper import PiperVoice, SynthesisConfig

    tekst = Path(x.plik).read_text(encoding='utf-8').strip()
    if not tekst:
        sys.exit('Plik jest pusty.')
    wyj = Path(x.wyjscie) if x.wyjscie else Path(x.plik).with_suffix('.wav')

    model = pobierz_glos(x.glos, Path(x.glosy))
    t0 = time.time()
    glos = PiperVoice.load(str(model))
    t1 = time.time()
    with wave.open(str(wyj), 'wb') as w:
        glos.synthesize_wav(tekst, w, syn_config=SynthesisConfig(length_scale=x.tempo))
    t2 = time.time()
    with wave.open(str(wyj)) as w:
        dlugosc = w.getnframes() / w.getframerate()
    print(f'Gotowe: {wyj} | {dlugosc:.1f} s nagrania | głos {x.glos} wczytany w {t1 - t0:.1f} s | '
          f'synteza {t2 - t1:.1f} s ({(t2 - t1) / max(dlugosc, 0.01):.2f} s na 1 s mowy)')

    if x.mp3:
        if not shutil.which('ffmpeg'):
            print('Brak ffmpeg w PATH - pomijam MP3 (Windows: winget install Gyan.FFmpeg).')
        else:
            mp3 = wyj.with_suffix('.mp3')
            subprocess.run(['ffmpeg', '-y', '-loglevel', 'error', '-i', str(wyj), '-b:a', '96k', str(mp3)], check=True)
            print('MP3:', mp3)


if __name__ == '__main__':
    main()
