"""flux3_wideo.py - film z FLUX 3 Video przez fal.ai (promptowy.com, 11.10.2026)

Wymaga: Python 3.9+ i biblioteki requests (pip install requests).
Klucz: zmienna środowiskowa FAL_KEY (fal.ai -> Dashboard -> Keys). Klucza nie wpisuj w kod.

Przykłady:
  python flux3_wideo.py "A red tram passes through a rainy Krakow street at dusk" --szkic
  python flux3_wideo.py "The camera slowly dollies forward..." --obraz https://adres/obraz.jpg --czas 6
  python flux3_wideo.py "..." --czas 10 --rozdz 1080p --proporcje 9:16 --bez-dzwieku

Koszt (cennik fal.ai z 11.10.2026): 0,085 USD za sekundę pełnej jakości, 0,03 USD za sekundę szkicu.
Skrypt przed wysłaniem pokazuje szacunek i pyta o zgodę (pomiń pytanie flagą --tak).
"""
import argparse, os, sys, time, pathlib
import requests

STAWKA = {'pelna': 0.085, 'szkic': 0.03}  # USD za sekundę, fal.ai, 11.10.2026


def main():
    a = argparse.ArgumentParser(description='FLUX 3 Video przez fal.ai')
    a.add_argument('prompt', help='polecenie po angielsku; kwestie po polsku w cudzysłowie')
    a.add_argument('--obraz', help='adres URL obrazu startowego (obraz na wideo)')
    a.add_argument('--czas', type=int, default=5, help='długość w sekundach, 5-20 (domyślnie 5)')
    a.add_argument('--rozdz', default='720p', choices=['720p', '1080p'])
    a.add_argument('--proporcje', default='16:9', help='16:9, 9:16, 1:1, 4:3, 3:4, 21:9, 2:1 albo auto')
    a.add_argument('--szkic', action='store_true', help='tryb szkicu: szybciej i taniej, do przeglądania pomysłów')
    a.add_argument('--bez-dzwieku', action='store_true')
    a.add_argument('--plik', default='', help='nazwa pliku wynikowego (domyślnie flux3_<czas>.mp4)')
    a.add_argument('--tak', action='store_true', help='nie pytaj o potwierdzenie kosztu')
    x = a.parse_args()

    klucz = os.environ.get('FAL_KEY')
    if not klucz:
        sys.exit('Brak zmiennej FAL_KEY. Ustaw ją, np. w PowerShell: $env:FAL_KEY="..."')
    if not 5 <= x.czas <= 20:
        sys.exit('Długość musi mieć od 5 do 20 sekund.')

    tryb = 'image-to-video' if x.obraz else 'text-to-video'
    endpoint = f'blackforestlabs/flux-3/{tryb}' + ('/draft' if x.szkic else '')
    body = {'prompt': x.prompt, 'duration': x.czas, 'aspect_ratio': x.proporcje, 'generate_audio': not x.bez_dzwieku}
    if x.obraz:
        body['image_url'] = x.obraz
    if not x.szkic:
        body['resolution'] = x.rozdz  # szkic jest zawsze w 720p

    koszt = x.czas * STAWKA['szkic' if x.szkic else 'pelna']
    print(f'Model: {endpoint}\nDługość: {x.czas} s, szacunkowy koszt: {koszt:.3f} USD')
    if not x.tak and input('Wysłać? [t/N] ').strip().lower() != 't':
        sys.exit('Przerwane.')

    H = {'Authorization': 'Key ' + klucz, 'Content-Type': 'application/json'}
    r = requests.post('https://queue.fal.run/' + endpoint, headers=H, json=body, timeout=120)
    if r.status_code >= 300:
        sys.exit(f'Błąd {r.status_code}: {r.text[:400]}')
    zad = r.json()
    print('Zadanie przyjęte, czekam na wynik...')

    t0 = time.time()
    while True:
        time.sleep(8)
        try:
            s = requests.get(zad['status_url'], headers=H, timeout=60).json()
        except requests.RequestException:
            continue  # chwilowy problem z siecią - pytamy dalej
        stan = s.get('status')
        print(f'  {int(time.time() - t0)} s: {stan}')
        if stan == 'COMPLETED':
            break
        if stan not in ('IN_QUEUE', 'IN_PROGRESS'):
            sys.exit(f'Zadanie się nie udało: {s}')
        if time.time() - t0 > 1800:
            sys.exit('Brak wyniku po 30 minutach. Adres do ręcznego sprawdzenia: ' + zad['response_url'])

    for _ in range(6):
        try:
            wynik = requests.get(zad['response_url'], headers=H, timeout=90).json()
            break
        except requests.RequestException:
            time.sleep(10)
    else:
        sys.exit('Nie udało się pobrać wyniku. Adres: ' + zad['response_url'])

    url = wynik['video']['url']
    plik = pathlib.Path(x.plik or f'flux3_{int(time.time())}.mp4')
    plik.write_bytes(requests.get(url, timeout=300).content)
    print(f'Gotowe w {int(time.time() - t0)} s: {plik.resolve()} ({plik.stat().st_size // 1024} KB)')


if __name__ == '__main__':
    main()
