Lab Notes
Tooling

Media Tools

The lab's media toolkit: 9 tools that compose end-to-end into a movie/series research → find → subtitle/dub → playback pipeline. Concrete commands, contracts, and composition patterns.

Purpose

The media toolkit is the lab's collection of 9 tools for entertainment automation. It is a concrete application of the Tooling Layer pattern to a specific domain: movie/series search, subtitle discovery, dubbed audio detection, technical metadata extraction, and multi-backend playback.

This page documents each tool's contract, the end-to-end pipeline, and the composition patterns. The per-tool TOOL.md format is in Tool Spec. The standard tool structure is in Tool Structure. The tools.json schema is in Tool Registry. The Media Lab Server is in Media Lab Server.

Tool inventory

#ToolStatusLayerFunction
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.

The tools are split into four layers: search, metadata, playback, dispatcher, and generation. The Media Agent composes the layers into an end-to-end pipeline.

Quick reference: end-to-end media pipeline

The Media Agent's typical request flow:

Loading diagram…
End-to-end media pipeline: discovery → selection → subtitles → backend dispatch.

A full request runs in 3 to 7 seconds (search + selection + backend dispatch) without transcoding; with transcoding, 1-3 minutes.


torrent-finder

CLI tool. Searches public torrent sources for a movie or series by title, year, quality, or codec.

Activation

Activate when:

  • The user asks for torrent sources for a specific title.
  • A media request requires source identification.
  • The Media Agent needs a candidate release.

Inputs

NameTypeRequiredConstraints
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 and their characteristics

The default configuration queries three sources; each has its own quirks.

SourceProtocolFilter supportQuality filtersNotes
solidtorrents.netHTMLstrong4K, 1080p, HDR, DV, x265, x264Aggregator with rich filters.
1377x.toHTMLmedium1080p, 720pHigh seed counts; more 1080p releases.
bt4g.comHTMLweaknone built-inHigh coverage; lower signal/noise.

The tool normalizes the response across sources so the output JSON has a single shape.

Filters and quality detection

The tool runs each candidate through a multi-step filter:

  1. Title normalization. Strip the year, the resolution, the codec, the group, and any special characters.
  2. Quality match. Compare the release's quality tag to the requested quality. A 1080p release does not pass a 4K filter.
  3. Codec match. Compare the release's codec tag to the requested codec. h265 and hevc are equivalent.
  4. Size sanity check. A 4K release under 1 GB is rejected as probably CAM/TS.
  5. Seeding threshold. A release with 0 seeders is rejected.

Failure modes

FailureHandling
All three sources timeoutRetry each once; return error.
One source returns 4xxSkip; try the other two.
All sources return 4xxReturn error with the upstream status codes.
No results match the filterReturn empty sources array with count: 0.
Magnet URL is malformedSkip the entry; log the issue.
Result count exceeds limitSort by seeders, take top limit.

Configuration

VariableDefaultPurpose
TORRENT_SOURCES[solidtorrents.net, 1377x.to, bt4g.com]Sources to query.
TORRENT_TIMEOUT_S30Per-request timeout.
TORRENT_USER_AGENT(random per request)User agent string.
TORRENT_PROXY_URL(none)Optional HTTP/HTTPS proxy.
TORRENT_MIN_SEEDERS0Minimum seeders to include a release.

Dependencies

  • requests>=2.28: HTTP client.
  • beautifulsoup4>=4.11: HTML parsing.
  • lxml>=4.9: XML/HTML parser.
  • pydantic>=2.0: output schema validation.

subtitle-finder

CLI tool. Searches public subtitle sites for a movie or series.

Activation

  • The user asks for subtitles for a specific title.
  • A media request requires subtitle discovery.

Inputs

NameTypeRequiredConstraints
titlestringyesnon-empty
yearintegerno1900 to current year
seasonintegerno1 to 99 (series)
episodeintegerno1 to 999 (series)
languagestringyesISO 639-1 code (e.g., 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
All subtitle sites downReturn error with upstream status.
No resultsReturn empty subtitles array.
Release name not matchingSurface in the result metadata; user can pick.

dubbed-finder

CLI tool. Detects releases that have audio dubbed in a given language.

Activation

  • The user asks for a dubbed version of a title.
  • A media request requires audio language information.

Inputs

NameTypeRequiredConstraints
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:..."
    }
  ]
}

How it detects dubbed audio

The tool uses two signals:

  1. Release name. Strings like Spanish, Castellano, VOSE, Dual-Audio, Multi are strong signals.
  2. Media-info cross-check. When the user has a local file, media-info returns the audio track languages; the tool uses that as ground truth.

The tool's media-info cross-check is the more reliable signal; the release name is a hint.


media-info

CLI tool. Extracts technical video metadata through ffprobe or mediainfo.

Activation

  • The user asks for technical info on a video file.
  • A media request needs resolution, codec, or duration.
  • The Media Agent needs to verify the downloaded file matches the requested quality.

Inputs

NameTypeRequiredConstraints
pathstringyesabsolute path to a video file
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 not in PATHReturn error: backend unavailable.
File not foundReturn error with the path.
File is not a media fileReturn error with the format detection.
Permission deniedReturn error with the permission needed.

media-organizer (planned)

CLI tool. Organizes downloaded media into a library structure based on metadata. Status: planned; the implementation will follow the CLI Python tool template.

Planned inputs:

  • source: path to the source directory.
  • library: path to the library root.
  • strategy: movie-tmdb | series-tvdb | flat.

Planned outputs:

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

chromecast

CLI tool. Discovers Cast devices on the LAN and controls playback on a selected device.

Activation

  • The user wants to play a cloud stream on a TV.
  • The Media Agent's backend selector picks Chromecast for a request.

Protocol

The tool uses Google's Cast protocol. The discovery is mDNS (_googlecast._tcp.local.). The control is HTTPS on the port returned by the mDNS record (typically 8009).

Actions

ActionInputsOutputs
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

For discover:

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

For play:

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

Failure modes

FailureHandling
No devices on the LANReturn empty list; user can retry.
Device unreachableRetry once; then return error.
play fails (e.g., bad URL)Return the Cast error message.
Volume out of rangeClamp to 0.0-1.0.

vlc

CLI tool. Spawns and controls VLC locally or on a remote host over the RC (TCP) or HTTP interface.

Activation

  • The source is a local file and the user wants to play it on a remote device.
  • The source is a network stream that needs transcoding or sending to a non-Cast device.
  • The user explicitly requests VLC.
  • The Media Agent's backend selector picks VLC for a request (e.g., when transcoding is required).

Protocol

VLC is controlled through its RC interface (TCP) or HTTP interface. The default port for RC is 4212. The default port for HTTP is 8080. The tool supports both.

Actions

ActionInputsOutputs
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 to HLS for low-bandwidth clients
python3 -m vlc transcode \
  --host localhost \
  --source /Volumes/Media/Snatch.2000.mkv \
  --profile hls-1080p

Outputs

For play:

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

For transcode:

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

Profiles

The profile argument is a named VLC sout profile. Built-in profiles:

ProfileOutputUse case
hls-1080pHLS 1080p @ 5 MbpsLocal network streaming to a TV
hls-720pHLS 720p @ 2.5 MbpsLow-bandwidth or older devices
dash-1080pDASH 1080p @ 5 MbpsModern smart TVs
raw-copyCopy the source without re-encodingLocal playback
audio-onlyStrip video, keep audioCasting to speakers

Custom profiles can be added in the configuration.

Failure modes

FailureResult
Host unreachable (TCP connect refused)Return a "host unreachable" result.
RC authentication failsReturn an "auth failed" result.
add or play returns a VLC errorReturn the error message in the result.
sout URL port already in useIncrement and retry once.
VLC binary not found in PATHReturn a "backend unavailable" result.
Source file not foundReturn a "source not found" result.

Configuration

VariableDefaultPurpose
VLC_BINARYvlcPath to the VLC binary.
VLC_RC_PORT4212Default RC port.
VLC_RC_PASSWORD(none)Default RC password.
VLC_HOSTS(none)Named hosts (e.g., kitchen:192.168.1.50).

media-playback

High-level dispatcher. Selects the right playback backend (Chromecast, VLC, or direct) for a given source and target, then invokes the appropriate tool. The Media Agent's request flow calls this tool instead of calling chromecast, vlc, or the direct player directly.

Backend selection algorithm

The dispatcher uses the following rules in order:

  1. Explicit backend. If the caller passed backend explicitly, use it.
  2. Target device type. If the target is a Cast device (name matches a discovered Cast), use chromecast.
  3. Source type and target. If the source is a local file and the target is a named VLC host, use vlc.
  4. Source type and transcoding. If the source is a local file and transcoding is required, use vlc.
  5. Source is a network stream. If the source is an HLS/DASH stream and the target is a Cast device, use chromecast.
  6. Default. If none of the above match, use direct (spawn the system default player).

Inputs

NameTypeRequiredConstraints
sourceobjectyesthe source to play (URL, file path, or stream descriptor)
targetobjectnothe target device (Cast name, VLC host, or localhost)
backendstringnoexplicit backend choice: chromecast, vlc, or direct
optionsobjectnobackend-specific options

Outputs

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

Direct player

The direct player spawns the OS default media player:

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

The direct player is the simplest backend; it has no controls and no state tracking.


media-lab-server

A local HTTP server that generates images and music. Not a CLI tool but an HTTP service. Documented in detail at Media Lab Server.

Quick reference:

  • Listens on 127.0.0.1:8765.
  • Exposes /api/image and /api/music.
  • Web UI at http://127.0.0.1:8765/.

Tool coordination

The media tools compose into a pipeline. The Media Agent runs the pipeline.

Pipeline stages

  1. Discoverytorrent-finder produces a candidate list.
  2. Selection — the user (or the agent, by default) picks one candidate.
  3. Subtitle discovery (optional) — subtitle-finder produces subtitle candidates.
  4. Dubbed audio discovery (optional) — dubbed-finder finds releases with the requested language.
  5. Metadata (optional) — media-info extracts technical info on the local file.
  6. Backend selectionmedia-playback picks a backend.
  7. Dispatchmedia-playback invokes the chosen backend tool.
  8. Playback — the user watches/listens.

The pipeline is the basis of the Media Agent workflow.

Sequential vs parallel

Stages 1, 3, and 4 can run in parallel when the search candidates are independent. Stages 5 and 7 must run sequentially because they depend on a single source. Stage 6 must run after stages 1-5 because it needs the selected source.

A typical parallel run:

  • 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 — only when the file is already downloaded

Caching

The tools cache their results:

  • torrent-finder caches the search results in a local file (~/.cache/torrent-finder/) for 1 hour.
  • subtitle-finder caches for 1 hour.
  • media-info caches the per-file result indefinitely (the file's mtime is the cache key).

The cache reduces duplicate work when the user revisits the same title.

Error recovery

The pipeline is resilient: a stage that fails does not abort the pipeline unless the user requires its output. For example, if subtitle-finder fails, the user can still play the release without subtitles.

The Media Agent surfaces the error to the user and continues with the next stage.


Registry entry examples

The media tools are registered in tools.json. Real entries:

{
  "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"]
    }
  ]
}

The tags are used by the skill loader to activate the right tool when a relevant topic appears in a request.


See also