feat(youtube): la visibilidad se comprueba desde fuera, no se le cree a la API
Build & Deploy ResearchOwl / build-and-push (push) Successful in 10s
Build & Deploy ResearchOwl / build-and-push (push) Successful in 10s
`/upload_short` contaba lo que la respuesta de la subida decía del vídeo. Eso es la palabra de la API sobre sí misma, y todo el flujo de revisión descansa en ella: el informe de fundamento se lee ANTES de publicar sólo si subir no publica. El 2026-08-12, mirando un vídeo recién subido, la respuesta decía `privacyStatus: private` y el vídeo se veía sin sesión — resultó ser un clic humano en Studio y no un fallo, pero el episodio dejó claro que no había forma de distinguir un caso del otro. Ahora se contrasta: oEmbed contesta 200 a un vídeo que se ve sin sesión y 404 a uno que no. Sin credenciales, sin tocar el scope — `youtube.upload` no puede preguntar por el estado de un vídeo, y ampliarlo a uno que sí pueda significa darle a un token de subida permiso para vaciar el canal. Dos decisiones que van con esto: - **Sólo el 200 es una prueba.** Un 404 no demuestra que el vídeo sea privado: también lo devuelve uno que YouTube aún no ha indexado. Por eso el negativo se mira dos veces y, si sigue negativo, se cuenta como "no se ve desde fuera", no como "es privado". - **No haber podido comprobar no es haber comprobado que no.** Un fallo de red deja `reachable=None` y el parte lo dice, en vez de heredar la garantía que no tiene. Cuando la API dice privado y el vídeo se ve, el aviso va en la PRIMERA línea del mensaje de Telegram: enterarse tiene que costar cero atención. Verificado contra la realidad — el mismo vídeo daba True antes de ocultarlo y False después; un vídeo borrado y uno privado dan False, y uno público True. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,40 @@ def test_upload_message_warns_when_the_description_has_no_article():
|
||||
assert "force" in text
|
||||
|
||||
|
||||
def test_upload_message_shouts_when_the_video_is_already_watchable():
|
||||
"""El caso serio, y va PRIMERO en el mensaje.
|
||||
|
||||
Si subir publica, el informe de fundamento se lee cuando el vídeo ya está en
|
||||
la calle — el orden entero del flujo deja de significar nada. Enterarse
|
||||
tiene que costar cero atención: en la primera línea o no sirve.
|
||||
"""
|
||||
from src.bot.bot import _upload_message
|
||||
text = _upload_message(_uploaded(reachable=True), {}, "https://x.test/")
|
||||
|
||||
assert "SE VE SIN INICIAR SESIÓN" in text.split("\n")[0]
|
||||
assert "studio.youtube.com/video/abc123/edit" in text
|
||||
# Y no se cuenta a la vez el cuento tranquilizador del candado.
|
||||
assert "auditoría" not in text
|
||||
|
||||
|
||||
def test_upload_message_confirms_a_video_nobody_can_watch():
|
||||
from src.bot.bot import _upload_message
|
||||
text = _upload_message(_uploaded(reachable=False), {}, "https://x.test/")
|
||||
|
||||
assert "no se ve sin sesión" in text
|
||||
assert "SE VE SIN INICIAR SESIÓN" not in text
|
||||
|
||||
|
||||
def test_upload_message_admits_when_it_could_not_check():
|
||||
"""No haber podido comprobar no es haber comprobado que no. Decir "privado"
|
||||
a secas aquí sería dar por garantía lo que sólo es la palabra de la API."""
|
||||
from src.bot.bot import _upload_message
|
||||
text = _upload_message(_uploaded(reachable=None), {}, "https://x.test/")
|
||||
|
||||
assert "No se pudo comprobar" in text
|
||||
assert "Míralo en Studio" in text
|
||||
|
||||
|
||||
def test_upload_message_is_plain_text():
|
||||
"""Va sin parse_mode: lleva el título del modelo, y un Markdown roto haría
|
||||
que Telegram rechazara justo el mensaje que trae el enlace."""
|
||||
|
||||
Reference in New Issue
Block a user