Lab Notes
Tooling

Media Tools

El toolkit de media del lab: 9 herramientas que se componen end-to-end en un pipeline de movie/series research → find → subtitle/dub → playback. Comandos concretos, contratos y patrones de composición.

Propósito

El media toolkit es la colección del lab de 9 herramientas para automatización de entretenimiento. Es una aplicación concreta del patrón de Tooling Layer a un dominio específico: búsqueda de películas/series, descubrimiento de subtítulos, detección de audio doblado, extracción de metadatos técnicos y reproducción multi-backend.

Esta página documenta el contrato de cada herramienta, el pipeline end-to-end y los patrones de composición. El formato TOOL.md por herramienta está en Tool Spec. La estructura estándar de herramienta está en Tool Structure. El esquema tools.json está en Tool Registry. El Media Lab Server está en Media Lab Server.

Inventario de herramientas

#HerramientaStatusLayerFunción
1torrent-finderImplementedsearchFind BitTorrent sources for a title.
2subtitle-finderImplementedsearchFind subtitles in one or more languages.
3dubbed-finderImplementedsearchDetect releases with dubbed audio for a language.
4media-infoImplementedmetadataExtract technical video metadata via ffprobe.
5media-organizerPlannedlibraryOrganize downloaded media into a library structure.
6chromecastImplementedplaybackDiscover and control Cast devices on the LAN.
7vlcImplementedplaybackSpawn and control VLC locally or on a remote host.
8media-playbackImplementeddispatcherHigh-level dispatcher that picks the right backend.
9media-lab-serverImplementedgenerationLocal HTTP server (:8765) for image and music generation.

Las herramientas se dividen en cuatro layers: search, metadata, playback, dispatcher y generation. El Media Agent compone los layers en un pipeline end-to-end.

Quick reference: pipeline de media end-to-end

El flujo típico de request del Media Agent:

Loading diagram…
Pipeline de media end-to-end: discovery → selection → subtitles → backend dispatch.

Una request completa corre en 3 a 7 segundos (search + selection + backend dispatch) sin transcoding; con transcoding, 1-3 minutos.


torrent-finder

Herramienta CLI. Busca fuentes públicas de torrent para una película o serie por título, año, calidad o codec.

Activación

Activar cuando:

  • El usuario pide fuentes de torrent para un título específico.
  • Una request de media requiere identificación de fuente.
  • El Media Agent necesita un release candidato.

Inputs

NameTipoRequeridoConstraints
titlestringyesnon-empty
yearintegerno1900 to current year
qualitystringno480p, 720p, 1080p, 2160p
codecstringnox264, x265, h264, h265, hevc, av1
limitintegernodefault 20, max 100

Command shape

python3 -m torrent_finder search \
  --title "Snatch" \
  --year 2000 \
  --quality 2160p \
  --codec x265 \
  --limit 20

Outputs

{
  "title": "Snatch",
  "year": 2000,
  "count": 12,
  "sources": [
    {
      "name": "Snatch.2000.UHD.BluRay.2160p.x265.HDR.DV-GROUP",
      "size_bytes": 52428800000,
      "quality": "2160p",
      "codec": "x265",
      "url": "magnet:?xt=urn:btih:...",
      "seeders": 312,
      "leechers": 28,
      "uploaded_at": "2025-12-04"
    }
  ]
}

Sources y sus características

La configuración por defecto consulta tres fuentes; cada una tiene sus quirks.

SourceProtocoloSoporte de filtrosFiltros de calidadNotas
solidtorrents.netHTMLstrong4K, 1080p, HDR, DV, x265, x264Aggregator con filtros ricos.
1377x.toHTMLmedium1080p, 720pAltos seed counts; más releases 1080p.
bt4g.comHTMLweaknone built-inAlta cobertura; menor signal/noise.

La herramienta normaliza la respuesta entre fuentes para que el JSON de salida tenga una única forma.

Filtros y detección de calidad

La herramienta corre cada candidato por un filtro multi-paso:

  1. Normalización de título. Strip el año, la resolución, el codec, el grupo y cualquier carácter especial.
  2. Match de calidad. Compara el tag de calidad del release con la calidad pedida. Un release 1080p no pasa un filtro 4K.
  3. Match de codec. Compara el tag de codec del release con el codec pedido. h265 y hevc son equivalentes.
  4. Sanity check de tamaño. Un release 4K por debajo de 1 GB se rechaza como probablemente CAM/TS.
  5. Umbral de seeders. Un release con 0 seeders se rechaza.

Failure modes

FailureHandling
Las tres fuentes timeoutReintentar cada una una vez; devolver error.
Una fuente devuelve 4xxSaltar; probar las otras dos.
Todas las fuentes devuelven 4xxDevolver error con los status codes upstream.
No hay resultados que matcheen el filtroDevolver array sources vacío con count: 0.
Magnet URL es malformedSaltar la entrada; loguear el issue.
El count de resultados excede limitOrdenar por seeders, tomar top limit.

Configuración

VariableDefaultPropósito
TORRENT_SOURCES[solidtorrents.net, 1377x.to, bt4g.com]Sources a consultar.
TORRENT_TIMEOUT_S30Timeout por request.
TORRENT_USER_AGENT(random per request)String del user agent.
TORRENT_PROXY_URL(none)Proxy HTTP/HTTPS opcional.
TORRENT_MIN_SEEDERS0Mínimo de seeders para incluir un release.

Dependencies

  • requests>=2.28: cliente HTTP.
  • beautifulsoup4>=4.11: parsing de HTML.
  • lxml>=4.9: parser de XML/HTML.
  • pydantic>=2.0: validación del esquema de salida.

subtitle-finder

Herramienta CLI. Busca sitios públicos de subtítulos para una película o serie.

Activación

  • El usuario pide subtítulos para un título específico.
  • Una request de media requiere descubrimiento de subtítulos.

Inputs

NameTipoRequeridoConstraints
titlestringyesnon-empty
yearintegerno1900 to current year
seasonintegerno1 to 99 (series)
episodeintegerno1 to 999 (series)
languagestringyesISO 639-1 code (p. ej., es, en)
limitintegernodefault 20, max 100

Command shape

python3 -m subtitle_finder search \
  --title "Snatch" \
  --year 2000 \
  --language es

Outputs

{
  "title": "Snatch",
  "year": 2000,
  "language": "es",
  "count": 8,
  "subtitles": [
    {
      "source": "subscene",
      "release_name": "Snatch.2000.1080p.BluRay.x264",
      "url": "https://...",
      "rating": 9.4,
      "downloads": 24102
    }
  ]
}

Failure modes

FailureHandling
Todos los sitios de subtítulos downDevolver error con el status upstream.
No hay resultadosDevolver array subtitles vacío.
Release name no matcheaSurface en los metadatos del resultado; el usuario puede elegir.

dubbed-finder

Herramienta CLI. Detecta releases que tienen audio doblado en un idioma dado.

Activación

  • El usuario pide una versión doblada de un título.
  • Una request de media requiere información del idioma del audio.

Inputs

NameTipoRequeridoConstraints
titlestringyesnon-empty
yearintegerno1900 to current year
languagestringyesISO 639-1 code
limitintegernodefault 20, max 100

Outputs

{
  "title": "Snatch",
  "year": 2000,
  "language": "es",
  "count": 3,
  "releases": [
    {
      "release_name": "Snatch.2000.1080p.BluRay.x264.Spanish",
      "audio_languages": ["en", "es"],
      "url": "magnet:?xt=urn:btih:..."
    }
  ]
}

Cómo detecta el audio doblado

La herramienta usa dos señales:

  1. Release name. Strings como Spanish, Castellano, VOSE, Dual-Audio, Multi son señales fuertes.
  2. Cross-check con media-info. Cuando el usuario tiene un fichero local, media-info devuelve los idiomas de las pistas de audio; la herramienta usa eso como ground truth.

El cross-check con media-info de la herramienta es la señal más fiable; el release name es un hint.


media-info

Herramienta CLI. Extrae metadatos técnicos de vídeo a través de ffprobe o mediainfo.

Activación

  • El usuario pide info técnica de un fichero de vídeo.
  • Una request de media necesita resolución, codec o duración.
  • El Media Agent necesita verificar que el fichero descargado matchea la calidad pedida.

Inputs

NameTipoRequeridoConstraints
pathstringyesruta absoluta a un fichero de vídeo
modestringnofull, quick (default full)

Command shape

python3 -m media_info info --path "/Volumes/Media/Snatch.2000.mkv"

Outputs (full)

{
  "path": "/Volumes/Media/Snatch.2000.mkv",
  "format": "matroska,webm",
  "duration_s": 7342.5,
  "size_bytes": 52428800000,
  "video": {
    "codec": "hevc",
    "width": 3840,
    "height": 2160,
    "fps": 23.976,
    "bitrate_kbps": 58000
  },
  "audio": [
    {
      "codec": "truehd",
      "language": "eng",
      "channels": 8,
      "bitrate_kbps": 4800
    },
    {
      "codec": "ac3",
      "language": "spa",
      "channels": 6,
      "bitrate_kbps": 640
    }
  ],
  "subtitles": [
    {"language": "eng", "format": "PGS"},
    {"language": "spa", "format": "PGS"}
  ]
}

Failure modes

FailureHandling
ffprobe no está en PATHDevolver error: backend unavailable.
Fichero no encontradoDevolver error con la ruta.
El fichero no es un fichero de mediaDevolver error con la detección de formato.
Permiso denegadoDevolver error con el permiso necesario.

media-organizer (planned)

Herramienta CLI. Organiza el media descargado en una estructura de librería basada en metadatos. Status: planned; la implementación seguirá la CLI Python tool template.

Inputs planeados:

  • source: ruta al directorio fuente.
  • library: ruta a la raíz de la librería.
  • strategy: movie-tmdb | series-tvdb | flat.

Outputs planeados:

{
  "moved": 12,
  "skipped": 1,
  "errors": [
    {"source": "...", "reason": "..."}
  ]
}

chromecast

Herramienta CLI. Descubre dispositivos Cast en la LAN y controla la reproducción en un dispositivo seleccionado.

Activación

  • El usuario quiere reproducir un cloud stream en una TV.
  • El selector de backend del Media Agent escoge Chromecast para una request.

Protocolo

La herramienta usa el protocolo Cast de Google. El discovery es mDNS (_googlecast._tcp.local.). El control es HTTPS en el puerto devuelto por el registro mDNS (típicamente 8009).

Acciones

AcciónInputsOutputs
discovernonelist of devices
playdevice, url, content_type, title?, thumb?playback status
pausedeviceack
resumedeviceack
stopdeviceack
statusdevicecurrent state
volumedevice, volumenew volume

Command shape

# Discover
python3 -m chromecast discover
 
# Play
python3 -m chromecast play \
  --device "Living Room TV" \
  --url "https://example.com/stream.m3u8" \
  --content_type "application/x-mpegURL" \
  --title "Snatch"

Outputs

Para discover:

{
  "devices": [
    {"name": "Living Room TV", "host": "192.168.1.42", "port": 8009, "model": "NVIDIA Shield TV"}
  ]
}

Para play:

{
  "device": "Living Room TV",
  "status": "playing",
  "media_session_id": "abc123"
}

Failure modes

FailureHandling
No hay dispositivos en la LANDevolver lista vacía; el usuario puede reintentar.
Dispositivo inaccesibleReintentar una vez; luego devolver error.
play falla (p. ej., URL mala)Devolver el mensaje de error de Cast.
Volume fuera de rangoClamp a 0.0-1.0.

vlc

Herramienta CLI. Lanza y controla VLC local o en un host remoto sobre la interfaz RC (TCP) o HTTP.

Activación

  • La fuente es un fichero local y el usuario quiere reproducirlo en un dispositivo remoto.
  • La fuente es un stream de red que necesita transcoding o enviar a un dispositivo no-Cast.
  • El usuario pide VLC explícitamente.
  • El selector de backend del Media Agent escoge VLC para una request (p. ej., cuando se requiere transcoding).

Protocolo

VLC se controla a través de su interfaz RC (TCP) o HTTP. El puerto por defecto para RC es 4212. El puerto por defecto para HTTP es 8080. La herramienta soporta ambos.

Acciones

AcciónInputsOutputs
playhost, sourcestarted / enqueued / failed
enqueuehost, sourcestarted / enqueued / failed
pausehostack
resumehostack
stophostack
statushoststate and position
transcodehost, source, profilestarted / failed with sout URL
duplicatehost, source, profilestarted / failed with duplicate URL
quithostack

Command shape

# Local play
python3 -m vlc play --host localhost --source /Volumes/Media/Snatch.2000.mkv
 
# Remote play
python3 -m vlc play --host kitchen --source /Volumes/Media/Snatch.2000.mkv
 
# Transcode a HLS para clientes de bajo ancho de banda
python3 -m vlc transcode \
  --host localhost \
  --source /Volumes/Media/Snatch.2000.mkv \
  --profile hls-1080p

Outputs

Para play:

{
  "host": "localhost",
  "status": "started",
  "pid": 12345
}

Para transcode:

{
  "host": "localhost",
  "sout_url": "http://localhost:8080/stream.m3u8",
  "profile": "hls-1080p",
  "status": "started"
}

Profiles

El argumento profile es un perfil sout de VLC con nombre. Profiles built-in:

ProfileOutputCaso de uso
hls-1080pHLS 1080p @ 5 MbpsStreaming en red local a una TV
hls-720pHLS 720p @ 2.5 MbpsDispositivos de bajo ancho de banda o antiguos
dash-1080pDASH 1080p @ 5 MbpsSmart TVs modernas
raw-copyCopy the source without re-encodingLocal playback
audio-onlyStrip video, keep audioCasting to speakers

Los profiles custom se pueden añadir en la configuración.

Failure modes

FailureResultado
Host inaccesible (TCP connect refused)Devolver un resultado "host unreachable".
Autenticación RC fallaDevolver un resultado "auth failed".
add o play devuelve un error de VLCDevolver el mensaje de error en el resultado.
Puerto de la URL sout ya en usoIncrementar y reintentar una vez.
Binario de VLC no encontrado en PATHDevolver un resultado "backend unavailable".
Fichero fuente no encontradoDevolver un resultado "source not found".

Configuración

VariableDefaultPropósito
VLC_BINARYvlcRuta al binario de VLC.
VLC_RC_PORT4212Puerto RC por defecto.
VLC_RC_PASSWORD(none)Password RC por defecto.
VLC_HOSTS(none)Hosts con nombre (p. ej., kitchen:192.168.1.50).

media-playback

Dispatcher de alto nivel. Selecciona el backend de playback correcto (Chromecast, VLC o direct) para una fuente y target dados, y luego invoca la herramienta apropiada. El flujo de request del Media Agent llama a esta herramienta en lugar de llamar directamente a chromecast, vlc o al direct player.

Algoritmo de selección de backend

El dispatcher usa las siguientes reglas en orden:

  1. Backend explícito. Si el caller pasó backend explícitamente, usarlo.
  2. Tipo de dispositivo target. Si el target es un dispositivo Cast (el nombre matchea un Cast descubierto), usar chromecast.
  3. Tipo de fuente y target. Si la fuente es un fichero local y el target es un host VLC con nombre, usar vlc.
  4. Tipo de fuente y transcoding. Si la fuente es un fichero local y se requiere transcoding, usar vlc.
  5. La fuente es un stream de red. Si la fuente es un stream HLS/DASH y el target es un dispositivo Cast, usar chromecast.
  6. Default. Si ninguno de los anteriores matchea, usar direct (lanzar el reproductor por defecto del SO).

Inputs

NameTipoRequeridoConstraints
sourceobjectyesla fuente a reproducir (URL, ruta de fichero o descriptor de stream)
targetobjectnoel dispositivo target (nombre Cast, host VLC o localhost)
backendstringnoelección explícita de backend: chromecast, vlc o direct
optionsobjectnoopciones específicas del backend

Outputs

{
  "backend": "chromecast",
  "device": "Living Room TV",
  "status": "playing",
  "session_id": "abc123"
}

Direct player

El direct player lanza el reproductor de media por defecto del SO:

  • macOS: open <path> u open <url>.
  • Linux: xdg-open <path> o xdg-open <url>.

El direct player es el backend más simple; no tiene controles ni tracking de estado.


media-lab-server

Un servidor HTTP local que genera imágenes y música. No es una herramienta CLI sino un servicio HTTP. Documentado en detalle en Media Lab Server.

Quick reference:

  • Escucha en 127.0.0.1:8765.
  • Expone /api/image y /api/music.
  • Web UI en http://127.0.0.1:8765/.

Coordinación de herramientas

Las herramientas de media se componen en un pipeline. El Media Agent corre el pipeline.

Etapas del pipeline

  1. Discoverytorrent-finder produce una lista de candidatos.
  2. Selection — el usuario (o el agente, por defecto) escoge un candidato.
  3. Descubrimiento de subtítulos (opcional) — subtitle-finder produce candidatos de subtítulos.
  4. Descubrimiento de audio doblado (opcional) — dubbed-finder encuentra releases con el idioma pedido.
  5. Metadatos (opcional) — media-info extrae info técnica del fichero local.
  6. Selección de backendmedia-playback escoge un backend.
  7. Dispatchmedia-playback invoca la herramienta de backend elegida.
  8. Playback — el usuario ve/escucha.

El pipeline es la base del workflow del Media Agent.

Sequential vs parallel

Las etapas 1, 3 y 4 pueden correr en paralelo cuando los candidatos de búsqueda son independientes. Las etapas 5 y 7 deben correr secuencialmente porque dependen de una única fuente. La etapa 6 debe correr tras las etapas 1-5 porque necesita la fuente seleccionada.

Una run paralela típica:

  • torrent-finder (search sources) — 5-15 s
  • subtitle-finder (find subs) — 2-5 s — parallel
  • dubbed-finder (find dubbed) — 2-5 s — parallel
  • media-info (verify local file) — 1-2 s — solo cuando el fichero ya está descargado

Caché

Las herramientas cachean sus resultados:

  • torrent-finder cachea los resultados de búsqueda en un fichero local (~/.cache/torrent-finder/) durante 1 hora.
  • subtitle-finder cachea durante 1 hora.
  • media-info cachea el resultado por fichero indefinidamente (el mtime del fichero es la clave de caché).

La caché reduce trabajo duplicado cuando el usuario vuelve a un mismo título.

Recovery de errores

El pipeline es resiliente: una etapa que falla no aborta el pipeline a menos que el usuario requiera su salida. Por ejemplo, si subtitle-finder falla, el usuario puede reproducir el release sin subtítulos igualmente.

El Media Agent surface el error al usuario y continúa con la siguiente etapa.


Ejemplos de entradas del registry

Las herramientas de media se registran en tools.json. Entradas reales:

{
  "version": "1.0.0",
  "tools": [
    {
      "name": "torrent-finder",
      "description": "Search torrent sources for movies and series",
      "location": "tools/torrent-finder/main.py",
      "status": "ready",
      "capabilities": ["search", "open"],
      "tags": ["media", "search", "torrent", "movies", "series"]
    },
    {
      "name": "chromecast",
      "description": "Discover and control Cast devices on the LAN",
      "location": "tools/chromecast/main.py",
      "status": "ready",
      "capabilities": ["discover", "play", "pause", "resume", "stop", "status", "volume"],
      "tags": ["media", "playback", "chromecast", "cast", "lan"]
    },
    {
      "name": "vlc",
      "description": "Spawn and control VLC locally or on a remote host",
      "location": "tools/vlc/main.py",
      "status": "ready",
      "capabilities": ["play", "enqueue", "pause", "resume", "stop", "status", "transcode", "duplicate", "quit"],
      "tags": ["media", "playback", "vlc", "local", "network", "transcode", "headless"]
    },
    {
      "name": "media-playback",
      "description": "High-level dispatcher that selects the right playback backend",
      "location": "tools/media-playback/main.py",
      "status": "ready",
      "capabilities": ["play"],
      "tags": ["media", "playback", "dispatcher"]
    }
  ]
}

Los tags los usa el skill loader para activar la herramienta correcta cuando aparece un topic relevante en una request.


Ver también