feat(youtube): subir Shorts al canal con /upload_short
Fase 3, con una corrección sobre lo que decía la §11 de la spec de fase 2. El bloqueo no es OAuth. Los vídeos subidos por videos.insert desde un proyecto de API sin auditar quedan restringidos a privado, y el candado es del proyecto, no del vídeo: no se abre desde Studio, se abre pasando la auditoría de cumplimiento de Google. Así que esto no publica. Deja el vídeo en el canal con los metadatos puestos y devuelve el enlace de Studio para que una persona lo revise y le dé a publicar — la misma forma que /publish con los borradores de Ghost, y por la misma razón: el informe de fundamento no sirve de nada si el vídeo ya está subido cuando lo lees. Comando aparte, no un paso de /generate short_en. - src/generator/youtube.py: refresco de token contra oauth2.googleapis.com, subida resumable en dos pasos y metadatos derivados del shot spec ya guardado (título, enlace al artículo, fuentes que el Short cita en pantalla, etiquetas del tema). Sin google-api-python-client: es síncrono y bloquearía el loop del bot; son dos peticiones HTTP y el repo ya firma los JWT de Ghost a mano. aiohttp con SAFE_ACCEPT_ENCODING como todo lo demás. - Scope youtube.upload y nada más: un token filtrado no puede leer ni borrar nada del canal, sólo subir. - forced_private detecta que YouTube devolvió "private" cuando se pidió otra cosa, y el aviso lo dice. Es la firma del candado, y tragárselo haría creer que salió publicado. - invalid_grant se traduce a su causa real: la pantalla de consentimiento quedó en "Testing" y Google revoca esos tokens a los siete días. Es el fallo que menos se adivina y el que más probable es encontrarse. - get_article_url ahora excluye las filas short_en. Su published_url pasa a ser la URL de YouTube, y sin el filtro el siguiente Short de la sesión enlazaría al Short anterior: un bucle silencioso, porque la URL es válida y nadie la mira dos veces. - scripts/youtube_oauth.py, sólo stdlib: corre en el portátil, no en el contenedor, y no debería exigir instalar nada. - Las tres claves van optional:true en el Deployment. Sin eso, una clave que aún no está en Infisical deja el pod en CreateContainerConfigError y tira el bot entero por una función que nadie ha pedido todavía. 30 tests nuevos contra un servidor falso. No hay test en vivo a propósito: cualquier ejecución real sube un vídeo a un canal de verdad, y eso no es algo que deba pasar por teclear pytest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -125,3 +125,29 @@ prompt, no contra los 126 de la sesión. Es deliberado — la pregunta es "¿lo
|
||||
sacó de lo que le dimos?" — pero produce falsos positivos cuando el dato existe
|
||||
en la sesión y no entró en el top-k. Un falso positivo cuesta un vistazo; un
|
||||
falso negativo cuesta la credibilidad del canal.
|
||||
|
||||
## YouTube por API: dos candados que no avisan al configurarlo
|
||||
|
||||
Ninguno de los dos da un error al montarlo. Los dos se descubren tarde.
|
||||
|
||||
**1. Los vídeos suben restringidos a privado y no se abren desde Studio.** La
|
||||
documentación de [`videos.insert`](https://developers.google.com/youtube/v3/docs/videos/insert)
|
||||
lo dice: *"All videos uploaded via the videos.insert endpoint from unverified API
|
||||
projects created after 28 July 2020 will be restricted to private viewing mode"*.
|
||||
El candado es del **proyecto de API**, no del vídeo, y se levanta pasando la
|
||||
auditoría de cumplimiento de Google — no cambiando la visibilidad a mano. Por eso
|
||||
`YOUTUBE_PRIVACY=public` no publica nada: `UploadedVideo.forced_private` detecta
|
||||
que YouTube devolvió `private` cuando se pidió otra cosa, y el aviso de Telegram
|
||||
lo dice en vez de dejar creer que salió.
|
||||
|
||||
**2. El refresh token caduca a los 7 días si la pantalla de consentimiento sigue
|
||||
en "Testing".** Google revoca los tokens de las apps sin publicar, así que el bot
|
||||
funciona una semana y luego empieza a dar `invalid_grant` sin que nada haya
|
||||
cambiado. La cura es pasar la pantalla a "In production" (con el aviso de "app no
|
||||
verificada" al dar permiso, que para un único usuario da igual) y volver a sacar
|
||||
el token. `_auth_error()` traduce `invalid_grant` a ese texto exacto: es el fallo
|
||||
que más cuesta adivinar y el que más probable es encontrarse.
|
||||
|
||||
Corolario de los dos juntos: `/upload_short` **no publica**, y no es una decisión
|
||||
de diseño que se pueda revertir tocando una env var. Deja el vídeo en el canal
|
||||
con los metadatos puestos y devuelve el enlace de Studio.
|
||||
|
||||
Reference in New Issue
Block a user