flask==3.1.1
gunicorn==23.0.0
psycopg2-binary==2.9.10
python-dotenv==1.1.0
flask-cors==4.0.0
pyjwt==2.8.0
bcrypt==4.1.2
cryptography>=42.0.0
requests>=2.31.0
edge-tts>=6.1.9
playwright>=1.44.0

# ── Añadidas al dockerizar: estaban en requirements.txt sin declararse ──
# icalendar: import DURO en routes/calendar.py (la ruta del ICS). Sin esto
#   la app no arranca en un entorno limpio. El propio código decía
#   "icalendar (already installed)": estaba instalado a mano en el host,
#   nunca declarado, y por eso solo reventaba dentro del contenedor.
icalendar==7.3.0

# El paquete se llama notebooklm-py pero se importa como "notebooklm" — y
# choca con el subpaquete interno backend/ai/notebooklm. Cuidado al tocar
# esto: no es el mismo.
# 0.8.4: Google migró NotebookLM a https://notebook.google.com (301 desde
# notebooklm.google.com). La 0.7.2 seguía esperando el host viejo en
# page.wait_for_url() del login, así que NUNCA detectaba el login ni escribía
# storage_state.json (el perfil no aparecía). 0.8.4 ya usa notebook.google.com.
notebooklm-py==0.8.4

# Las tres siguientes las usa backend/scraping/ y scorm_import.py, que
# server.py NO importa: no bloquean el arranque, pero sin ellas esas
# funciones fallan en runtime dentro del contenedor.
selenium==4.49.0        # requiere además un chromedriver en la imagen
PyMuPDF==1.28.2         # importado como "fitz"
Pillow==12.3.0          # importado como "PIL"

# ── YouTubeZen (Phase 8) ──
# yt-dlp se invoca como BINARIO por subprocess, no como librería
# (backend/ai/notebooklm/youtube_zen.py: _ytdlp_base_cmd → ["yt-dlp", ...]).
# El pip instala el ejecutable en /usr/local/bin, así que va aquí y no en el
# Dockerfile, pero el fallo sigue siendo de runtime: sin él, toda la
# extracción de subtítulos muere con FileNotFoundError.
#
# El suelo de versión NO es arbitrario. El código usa dos opciones que
# yt-dlp renombró hace poco:
#   --write-auto-sub / --write-sub   (antes: --write-auto-subs, con la s)
#   --remote-components ejs:github   (nuevo; descarga el solver de JS)
# Con una versión antigua el binario existe pero aborta con "unrecognized
# option", que es un fallo mucho más confuso que el FileNotFoundError.
# La ruta de subtítulos usa --skip-download, así que NO hace falta ffmpeg.
yt-dlp>=2025.6.30
# MD -> PDF (services/md_to_pdf.py). Necesita pango/cairo, que el
# Dockerfile ya instala via apt. Se importaba sin declararse: al recrear
# el contenedor la app no arrancaba (ModuleNotFoundError).
weasyprint==62.3
pydyf==0.10.0  # must match weasyprint 62.3

# ── Login interactivo de NotebookLM (noVNC) ──
# gevent + gevent-websocket son el MOTOR del bridge WebSocket ↔ RFB de
# routes/novnc_proxy.py de la mano del worker gunicorn
# gevent_ws_worker.GeventWebSocketWorker (ver Dockerfile CMD). Sin ellas el
# blueprint no importa y el login embebido no existe. NO van en requirements
# sin más: cambiar el worker de gunicorn afecta a TODA la app, no solo a /novnc.
gevent>=24.2.1
gevent-websocket>=0.10.1
