Introducing TeleConvert cover
Research notebook

Introducing TeleConvert: A Transcoding Pool over SSH

Video conversion is easy to start and painfully slow to finish. A laptop can spend an entire weekend working through a media directory while faster desktops, servers, and old workstations sit idle nearby. TeleConvert turns those machines into a shared transcoding pool without asking you to deploy a worker service on any of them.

TeleConvert is a small Go command-line application that discovers media files, distributes jobs across local and remote workers, and displays the whole queue in a live terminal dashboard. A worker only needs SSH access and an encoder such as ffmpeg or HandBrakeCLI on its PATH. There is no agent, daemon, container, database server, or web control plane to maintain.

That makes TeleConvert useful for a home lab, a temporary group of cloud machines, or any collection of Linux and macOS systems that can already communicate over SSH.

The basic idea

You run TeleConvert on the machine that holds your source media. A YAML file describes the available workers, how many jobs each worker may run, and the encoder command to execute. TeleConvert then manages the queue:

Input files
    │
    ├─ local worker ───────────────→ encoded output
    ├─ SSH worker 1 ── upload/run ─→ encoded output
    └─ SSH worker 2 ── upload/run ─→ encoded output

Remote uploads are atomic and checksum-verified. Worker heartbeats and busy-slot detection keep the scheduler aware of available capacity, while interrupted jobs return to the pending queue instead of being recorded as complete. A clean Ctrl-C or SIGTERM shuts down active encoders and preserves recoverable state.

TeleConvert is also encoder-agnostic. Each node has a command template containing {{.Input}} and {{.Output}}; those placeholders are replaced with the paths for each job. The template can call HandBrakeCLI, ffmpeg, or another command that follows the same input/output model.

Install TeleConvert

Install the latest compiled release on Linux or macOS:

curl -fsSL https://raw.githubusercontent.com/Alchemist-Aloha/teleconvert/main/install.sh | bash

The installer places the binary in ~/.local/bin and creates a default configuration when one does not already exist. Make sure ~/.local/bin is on your shell's PATH, then verify the installation:

teleconvert -version

The install locations and release version can be overridden when needed:

export TELECONVERT_INSTALL_DIR="$HOME/bin"
export TELECONVERT_CONFIG_DIR="$HOME/.config/teleconvert"
export TELECONVERT_VERSION=v1.2.3
curl -fsSL https://raw.githubusercontent.com/Alchemist-Aloha/teleconvert/main/install.sh | bash

To build from source instead:

git clone https://github.com/Alchemist-Aloha/teleconvert.git
cd teleconvert
go mod tidy
go build -o teleconvert .

Prepare the workers

Every remote node needs:

  • SSH access from the controller machine
  • ffmpeg, HandBrakeCLI, or the command used by its template on PATH
  • Enough temporary disk space for the active input and output files
  • Write access to its configured temporary directory

SSH keys are the simplest option for unattended queues. Test each connection before starting TeleConvert:

ssh -i ~/.ssh/id_rsa user@192.168.1.100
HandBrakeCLI --version

The encoder can be different on each node. This is useful when one machine has a hardware encoder, another is tuned for high-quality CPU encoding, and the local system should handle only one lightweight job at a time.

Configure the worker pool

The default configuration path is:

~/.config/teleconvert/teleconvert.yaml

The following example defines one remote worker and one local worker:

nodes:

- name: "remote"
  address: "192.168.1.100:22"
  user: "user"
  ssh_key: "/home/user/.ssh/id_rsa"
  max_concurrent: 1
  command: "HandBrakeCLI -i {{.Input}} -o {{.Output}} -e x265 -q 27.0 -a 1 -E fdk_aac -B 160 -6 stereo -f mkv --deinterlace -w 1920 -l 1080 -O"
  tmp_dir: "/tmp/teleconvert"

- name: "local-encoder"
  address: "localhost"
  max_concurrent: 1
  command: "HandBrakeCLI -i {{.Input}} -o {{.Output}} -e x265 -q 27.0 -a 1 -E fdk_aac -B 160 -6 stereo -f mkv --deinterlace -w 1920 -l 1080 -O"
  tmp_dir: "/tmp/teleconvert"

Every command must contain both {{.Input}} and {{.Output}}. TeleConvert validates the template and substitutes the correct paths for each job.

max_concurrent controls the number of encoder processes assigned to that node. Start with 1. Increase it only after checking CPU, GPU, memory, storage throughput, and encoder behavior under simultaneous jobs. A machine that completes one transcode quickly may become slower overall when too many encoders compete for the same hardware.

Run a conversion queue

Point TeleConvert at one file or an entire directory:

teleconvert \
  -input /path/to/videos \
  -output-dir /path/to/output \
  -output-ext .mp4 \
  -config ~/.config/teleconvert/teleconvert.yaml

If -output-dir is omitted, TeleConvert creates a converted directory beside each source file. The default output extension is .mp4.

Available flags include:

FlagPurpose
-configSelect a YAML configuration file
-inputChoose the required input file or directory
-output-dirChoose one destination directory
-output-extSet the output extension; default .mp4
-delete-sourceDelete each source only after its job succeeds
-continue-on-errorContinue after individual failures; enabled by default
-verbose or -vShow verbose lifecycle logging
-poll-intervalSet the worker monitor interval; default 2s
-versionPrint the embedded build version and exit

Treat -delete-source as an explicit cleanup mode, not a routine default. Run a normal queue first and inspect its outputs before allowing any automated source deletion.

Follow progress in the terminal dashboard

In an interactive terminal, TeleConvert opens a full-screen dashboard. The top bar shows total completion and failure counts. The worker panel lists every slot, its current state, progress, elapsed time, and input file. Two log panes separate TeleConvert lifecycle events from the raw output of the selected encoder.

┌─ TELECONVERT  [=========         ]  7/15 done  0 failed ───────────────┐
┌─ Workers ──────────────────────────────────────────────────────────────┐
│ > remote         encoding   [==============]  42.3%  01:23  video1.mkv│
│   local-encoder  encoding   [=============== ] 78.5%  02:07  video4.mkv│
└────────────────────────────────────────────────────────────────────────┘
┌─ TeleConvert output ───────────────────────────────────────────────────┐
│ 14:23:01  remote:video1.mkv encoding started                           │
└────────────────────────────────────────────────────────────────────────┘
┌─ Encoder output — remote [live] ───────────────────────────────────────┐
│ Encoding: task 1 of 1, 42.37 % (12.00 fps)                             │
└────────────────────────────────────────────────────────────────────────┘

Dashboard controls:

  • / , j / k, or Tab selects a worker slot
  • 19 selects a worker directly
  • Page Up / Page Down or u / d scrolls encoder output
  • g / Home jumps to the oldest retained output
  • G / End returns to live output
  • q or Ctrl-C stops active encoders cleanly and exits

After a successful interactive run, the dashboard stays open so you can review the final logs. Press q or Ctrl-C when finished. Redirected and other non-interactive runs remain line-oriented and exit automatically after the queue completes.

A practical first run

Before handing TeleConvert a large archive:

  1. Confirm that SSH key authentication works for every remote node.
  2. Run the configured encoder command manually on a small sample file.
  3. Start each node with max_concurrent: 1.
  4. Process a copied test directory without -delete-source.
  5. Inspect playback, audio, subtitles, resolution, and output size.
  6. Increase concurrency one node at a time while watching system load.

This short test verifies more than connectivity. It confirms that the exact encoder options make sense for your media before the same template is applied across the full queue.

Troubleshooting

  • The remote node never starts: Test the configured address, user, and SSH key with the regular ssh command.
  • Encoder command not found: Install the encoder on that worker and make sure it is available on the non-interactive SSH PATH.
  • Configuration is rejected: Check that every node command includes both {{.Input}} and {{.Output}} exactly.
  • Remote job cannot create files: Check free space and permissions for the node's tmp_dir.
  • One file fails repeatedly: Run again with -verbose, select that worker in the dashboard, and inspect its raw encoder output.
  • A worker is overloaded: Reduce its max_concurrent value before changing global behavior.
  • You need to stop the queue: Use q or Ctrl-C. TeleConvert stops active encoder processes cleanly and returns interrupted work to a recoverable pending state.

Use the machines you already have

TeleConvert does not attempt to be a general cluster scheduler. It solves the narrower problem of assigning file conversions to machines that are already reachable over SSH. The controller owns the queue, each worker runs a familiar encoder command, and the dashboard keeps the entire operation visible.

That small operating model is the main advantage: add a machine with a few YAML lines, remove it just as easily, and use otherwise idle hardware without deploying another permanent service.

Visit the TeleConvert repository for current releases, source code, and configuration examples.