Anuncios en la tele y música para dormir desde Claude Code

La semana pasada escribí sobre cómo conecté Claude Code a mi televisor Sony por ADB. Aquel post era la parte de preparación: encontrar la tele, activar la depuración, arreglar la región, instalar una app. Si todavía no lo has hecho, empieza por ahí.

Este va de lo que pasó cuando la conexión ya estaba ahí, sin más. La usé dos veces en menos de un día. Anoche, para ir a por un anuncio que no debería estar viendo. Esta mañana, para ponerle música a mi hija. Una de las dos cosas está terminada. La otra sigue a medias, y creo que es la que más vale la pena contar.

Un anuncio dentro de una app que ya pago

Pago una app de streaming de deportes. Cuando abro una repetición, primero me pone un anuncio, con una etiqueta pequeña en la esquina que dice Ad 1 of 1. Pagar por algo y seguir tragándote anuncios me molesta más de lo que debería, así que le pedí a Claude que los quitara.

La idea obvia es parchear la app. Antes de tocar nada, Claude comprobó si eso era siquiera posible. Sacó el APK de la tele por ADB y buscó en los archivos dex rastros de la maquinaria habitual de anuncios en vídeo: Google IMA, FreeWheel, Yospace, inserción dinámica de anuncios, VAST y VMAP.

adb shell pm path <package>
adb pull <path-from-above> app.apk
unzip -o app.apk -d app/

La respuesta fue que no. La app resultó ser un cascarón de Cordova. La parte nativa es un envoltorio fino, y el reproductor y sus anuncios llegan como JavaScript desde los servidores del servicio cuando la app arranca. Podrías parchear ese APK toda la semana y los anuncios seguirían saliendo, porque no están dentro.

Me gustó esa respuesta. Fueron unos minutos y me ahorró una noche entera con algo que nunca iba a funcionar.

Bloquear anuncios desde el DNS

Si no puedes cambiar la app, puedes cambiar a dónde puede llegar la tele. Android TV tiene un ajuste de DNS privado, el mismo que tienen los móviles Android. Lo apuntas a un servidor DNS que filtra y todas las apps de la tele le preguntan a ese servidor por las direcciones. Cuando una app pide un servidor de anuncios conocido, el filtro no le devuelve nada.

Se puede poner en los ajustes de red de la tele, o por ADB:

adb shell settings put global private_dns_mode hostname
adb shell settings put global private_dns_specifier dns.adguard-dns.com

Ese nombre es el filtro público de AdGuard. Claude lo configuró, comprobó que un dominio de anuncios conocido ya no llevaba a ninguna parte y abrió una repetición. El anuncio salió igual.

Así que el siguiente paso fue un servidor personal de AdGuard DNS. La cuenta gratuita te da tu propio servidor, una entrada de dispositivo para la tele con su propio nombre de DNS privado, un registro de consultas y reglas personalizadas. Cambiar la tele es una línea:

adb shell settings put global private_dns_specifier <your-id>.d.adguard-dns.com

El registro de consultas es el motivo de todo esto. El filtro público es una caja negra. Tu propio servidor te enseña cada consulta que hace la tele, en orden, mientras ocurre.

Solo cambia la tele

Lo primero que pregunté fue si esto iba a tocar algo en el portátil o en la wifi de casa. No toca nada. El DNS privado es un ajuste de un solo dispositivo. La tele manda sus consultas a ese nombre por una conexión cifrada, y el identificador del nombre le dice a AdGuard de qué dispositivo vienen. Mi Mac sigue usando el DNS que usaba antes. El router también, y el resto de aparatos de casa igual.

Eso también hace que sea fácil de deshacer. Esto devuelve la tele al DNS normal de la red:

adb shell settings put global private_dns_mode opportunistic

Dos repeticiones sin anuncio no demuestran nada

Después del cambio vi dos repeticiones. Ningún anuncio. Habría sido muy fácil darlo por ganado.

Claude no lo hizo, y tenía razón. Mi servidor nuevo usaba la misma lista de bloqueo que el filtro público, que ya había fallado. En el bloqueo no había cambiado nada. Lo más probable es que fuera el límite de frecuencia: el servidor de anuncios deja de enseñarte un anuncio cuando ya lo has visto unas cuantas veces. Dos repeticiones tranquilas después de una noche de pruebas se parecen muchísimo a eso.

La prueba de verdad es más lenta:

  1. Abre el registro de consultas de la tele.
  2. Pon una repetición nueva y déjala correr hasta que salga el anuncio.
  3. Mira las consultas de los segundos justo antes del anuncio y localiza el host que lo sirvió.
  4. Añade una regla personalizada para ese host, por ejemplo ||ads.example.com^.
  5. Pon otra repetición y comprueba dos cosas: que no hay anuncio y que el partido sigue reproduciéndose.

Esa última comprobación importa. Si bloqueas el host equivocado, el vídeo se puede caer junto con el anuncio.

Y hay un punto en el que paras. Algunos servicios cosen el anuncio dentro del mismo flujo de vídeo en sus propios servidores, lo que se llama inserción de anuncios en el servidor. Si el anuncio viene del mismo host que el partido, el DNS no tiene nada que separar. Bloqueas el host y te quedas sin las dos cosas.

Todavía no sé en qué caso está esta app. Eso lo dirá la próxima repetición. Lo que tengo ahora es un método, y un filtro que puedo mirar en lugar de adivinar.

La tele como mando para dormir

Lo de esta mañana fue más sencillo. Escribí una línea en Claude Code, en inglés, tal cual:

open youtube, piano sleeping disney songs for my daughter

No dije qué tele, qué app ni cómo. Claude comprobó que la conexión ADB seguía viva y encontró el paquete de YouTube en la tele. Luego probó el camino obvio, un intent SEARCH con la búsqueda. La app de la tele lo ignoró.

Un detalle por el camino: el primer intento partió la búsqueda por los espacios, porque el shell de la tele veía cada palabra como un argumento distinto. Mete todo el am start entre comillas dentro de adb shell "...", y la búsqueda entre comillas dentro de eso.

Después apareció el selector de perfiles de YouTube. Claude eligió el perfil con Premium, para que ningún anuncio cortara una nana.

Lo que funcionó fue un intent VIEW con una URL de búsqueda normal de YouTube. La app de la tele la abre directamente en la página de resultados:

adb shell "am start -a android.intent.action.VIEW \
  -d 'https://www.youtube.com/results?search_query=piano+sleeping+disney+songs' \
  com.google.android.youtube.tv"

El foco cae en el primer resultado, así que basta con pulsar OK una vez:

adb shell input keyevent KEYCODE_DPAD_CENTER

El primer resultado era una recopilación de seis horas de canciones Disney al piano para dormir profundamente. Me valía.

Comprobar qué se está reproduciendo de verdad

Esta es la parte que me importa. Pulsar OK no significa que el trabajo esté hecho, así que Claude comprobó el resultado de dos maneras.

Primero leyó la sesión multimedia de la tele, que es como Android le cuenta al sistema qué está sonando:

adb shell dumpsys media_session | grep -E "state=|description="

Eso te da el estado de reproducción, el título y la posición. El estado 3 significa que está reproduciendo. Luego hizo una captura para ver qué había de verdad en pantalla:

adb exec-out screencap -p > tv.png

Las dos cosas coincidían. Y la posición enseñó algo que a mí se me habría pasado. El vídeo no había empezado desde el principio. Se había retomado en 4 horas y 13 minutos, de 6 horas y 14. Claude me lo dijo y se ofreció a ponerlo desde cero.

Parece poca cosa, pero es justo por eso por lo que me fío de él a la hora de dormir. Un script que pulsa OK me habría dicho que todo había ido bien. Claude me dijo qué había en la tele, incluida la parte que yo no habría mirado.

La misma conexión, dos trabajos

Las dos cosas funcionaron con la conexión ADB de la semana pasada y nada más. Ni hub de domótica ni ninguna app extra en la tele.

Visto con calma, los dos trabajos tenían la misma forma. Con los anuncios, Claude no aceptó dos repeticiones tranquilas como prueba. Con YouTube, no aceptó un botón pulsado como reproducción. Las dos veces fue a mirar antes de decirme nada, y si algo va a manejar mi tele mientras mi hija se queda dormida, eso es exactamente lo que quiero que haga.

← Todos los artículos