Ir al contenido

API REST

Todo lo que hace la app con bibliotecas, videos, la cola y las exportaciones también se puede pedir por HTTP, en JSON, bajo http://<equipo>:8765/api/v1. La lista completa de rutas está en Rutas de la API; aquí va lo que necesitas para usarlas.

GET /api/v1 responde {version, doc} con esa misma lista en texto, útil para comprobar que llegas al servidor y que tu clave sirve.

Cada pedido lleva una clave de API en la cabecera Authorization:

Ventana de terminal
curl -H "Authorization: Bearer vt_…" http://localhost:8765/api/v1/libraries

Una clave tiene dos propiedades:

Opciones
Alcance Todas las bibliotecas (también las que crees después) o una biblioteca.
Permiso Lectura: ver, buscar y exportar. Escritura: además, importar, borrar, cambiar el glosario y manejar la cola.
  1. Abre Preferencias → Acceso y API → Claves de API.
  2. Ponle un nombre que diga dónde la usas (p. ej. n8n), elige el alcance y Lectura o Escritura.
  3. Pulsa Crear clave y cópiala: empieza por vt_ y no se vuelve a mostrar. El servidor solo guarda su hash.

La lista de claves muestra el nombre, el alcance, el permiso, un resumen de la clave (vt_xxxx…xxxx) y cuándo se usó por última vez (se actualiza como mucho una vez por minuto). Revocar la borra: lo que la use empieza a recibir 401 al instante.

Si borras la biblioteca de una clave de una sola biblioteca, la clave deja de servir.

  • Desde el mismo equipo donde corre la app (ver Acceso), un pedido sin Authorization responde como al dueño. Así puedes probar con curl sin crear una clave.
  • Con una clave inválida o revocada, la respuesta es 401 aunque el pedido venga del mismo equipo: no se cae al acceso de dueño.
  • Desde otro equipo, sin clave, la API usa lo mismo que el navegador: la cookie de sesión vt_s, si la manda, y las bibliotecas públicas.

Las respuestas son JSON (application/json; charset=utf-8), salvo los subtítulos. Los errores traen {"error": "…"}:

Código Cuándo
200 Listo.
202 Aceptado y en marcha: importar, mandar a la cola, pedir una exportación.
400 Datos inválidos (p. ej. faltan urls, mode desconocido, kinds vacío). Un cuerpo que no es JSON válido se toma como {}.
401 Sin clave válida, o sin acceso a ninguna biblioteca.
403 La clave es de solo lectura, pausar la cola sin una clave de escritura para todas las bibliotecas, o un pedido que cambia algo desde otro sitio web sin clave (Otros sitios web).
404 La ruta, la biblioteca, el video o la exportación no existe, o la clave no puede verlo: no se distingue a propósito.
413 El cuerpo pasa de 2 MB (respuesta en texto plano).

Todas piden permiso de lectura sobre la biblioteca, salvo donde se indica.

  • GET /libraries lista las bibliotecas que la clave puede ver: id, name, count (videos), downloaded, main, color, icon, pattern. Solo el dueño recibe además path (la carpeta) y access (la visibilidad).
  • GET /libraries/{lib}/collections da id, name, kind, count, url y channel (estos dos pueden venir en null). Los id automáticos son all, recent, ch-… (canales) y cat-… (categorías).
  • GET /libraries/{lib}/search?q= busca en las transcripciones y responde {q, total, results}.
  • GET /libraries/{lib}/glossary da {entries}. PUT con {"entries": [{"src": "prompt", "dst": "instrucción"}]} lo reemplaza entero; pide escritura.

GET /libraries/{lib}/videos pagina con offset (desde 0) y limit (100 por omisión, máximo 500: más se recorta a 500). collection elige la colección (all por omisión, del último agregado al primero).

{ "total": 1240, "offset": 0, "videos": [ { "id": "yt_dQw4w9WgXcQ", "title": "…", "progress": { "asr": 1, "dub": 1 }, "queue": { "state": "listo" } } ] }

Para recorrerla entera, suma limit a offset hasta pasar total.

POST /libraries/{lib}/import pide escritura.

{ "urls": ["https://www.youtube.com/watch?v=…", "https://www.youtube.com/playlist?list=…"], "mode": "traducir", "priority": false }
  • urls acepta enlaces de YouTube a videos o playlists (o un id de 11 caracteres). También vale "url": "…" para uno solo. Lo que no es de YouTube se ignora.
  • mode: traducir (traducir y doblar) o descargar (solo bajar el video). Si lo omites, usa la preferencia Al importar de esa biblioteca.
  • priority: true los pone primero en la cola, y entonces se procesan aunque mode sea descargar.
  • Cada playlist se importa como colección de la biblioteca.

Responde 202 con {"videos": 3, "playlists": 1, "elsewhere": {"Ciencia": 2}}: elsewhere cuenta los videos que ya estaban en otra biblioteca y se quedaron allá (un video vive en una sola).

  • GET /videos/{id} (lectura) da los datos del video (title, duration, channel, categories, added, library), las URL de sus archivos (media, audio, thumb), el avance y su estado en la cola:
    • progress.asr: fracción del video reconocida y traducida (0 a 1).
    • progress.dub: fracción doblada (0 a 1).
    • queue: {state, prio, error, dl} o null si el video no está en la cola. state es en cola, procesando, descargado, listo o error; dl es el avance de la descarga.
  • GET /videos/{id}/transcript (lectura) da las frases con start, end, text (inglés), translation (español) y speaker, más speaker_names y comments.
  • GET /videos/{id}/subtitles?lang=es (lectura) devuelve un archivo SRT (application/x-subrip). lang=es es el español (por omisión); cualquier otro valor da el inglés.
  • PATCH /videos/{id} (escritura) con {"title": "…"} le cambia el nombre al video; el nombre se queda aunque el video se vuelva a descargar.
  • DELETE /videos/{id} (escritura) borra el video con sus archivos.
  • GET /queue?library= (lectura) da {paused, current, blocked_until, jobs}, solo con los trabajos de las bibliotecas que la clave ve; library filtra por una. current (lo que se procesa ahora) solo se muestra al dueño. blocked_until es distinto de 0 mientras YouTube pide verificación.
  • POST /queue (escritura) con {"video": "yt_…", "priority": true} manda un video que ya está en la biblioteca a la cola; con priority, primero.
  • DELETE /queue/{id} (escritura) lo quita de la cola.
  • POST /queue/pause y POST /queue/resume pausan o reanudan todo el procesamiento. Como afectan a todas las bibliotecas, piden una clave de escritura para todas las bibliotecas.
  • POST /exports (lectura de cada video) con {"videos": ["yt_…"], "kinds": ["es_video", "srt_es"], "name": "…"} responde 202 con {id, state}. Los valores de kinds están en Rutas de la API; los desconocidos se ignoran. name es opcional.
  • GET /exports/{id} da {id, name, state, pct, detail, url, error}, solo si puedes ver todos sus videos (si no, 404). state pasa por en espera, exportando o esperando … (si el video aún no termina de procesarse) hasta listo o error.

Al quedar listo, url es una ruta del mismo servidor (empieza por /). Descárgala con la misma cabecera Authorization.

En todos, TOKEN es tu clave y la app corre en localhost:8765.

Ventana de terminal
curl -H "Authorization: Bearer $TOKEN" http://localhost:8765/api/v1/libraries
Ventana de terminal
curl -X POST http://localhost:8765/api/v1/libraries/principal/import \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "priority": true}'

Los videos de YouTube quedan con id yt_ seguido del id de YouTube (aquí, yt_dQw4w9WgXcQ).

Ventana de terminal
curl -H "Authorization: Bearer $TOKEN" http://localhost:8765/api/v1/videos/yt_dQw4w9WgXcQ
Ventana de terminal
curl -H "Authorization: Bearer $TOKEN" -o video.es.srt \
"http://localhost:8765/api/v1/videos/yt_dQw4w9WgXcQ/subtitles?lang=es"
Ventana de terminal
# 1. pedir la exportación: responde {"id": "…", "state": "en espera"}
curl -X POST http://localhost:8765/api/v1/exports \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"videos": ["yt_dQw4w9WgXcQ"], "kinds": ["es_video"]}'
# 2. repetir hasta que "state" sea "listo"
curl -H "Authorization: Bearer $TOKEN" http://localhost:8765/api/v1/exports/<id>
# 3. bajar el archivo de "url"
curl -H "Authorization: Bearer $TOKEN" -o video.mp4 "http://localhost:8765<url>"

La app web usa otras tres rutas, fuera de /api/v1, pensadas para el navegador:

  • POST /api/auth/login con {"password": "…"} (dueño) o {"password": "…", "vault": "<id>"} (biblioteca con contraseña). Si acierta, responde 200 con la cookie vt_s; si no, 401.
  • POST /api/auth/logout borra la sesión.
  • GET /api/auth/me dice qué puede la conexión: {owner, perms, local}.

Para integraciones, usa claves: no caducan y se revocan una por una.