個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Stream any video as coloured ASCII/ANSI art over telnet or netcat — and optionally spin a roulette wheel that drops each viewer into one of several streams at random.
You supply the video. asciivid renders it to a compact frame cache and serves
it over a plain TCP socket, so anyone can watch it in their terminal:
telnet your-host 2323
It ships with a small set of systemd utilities so you can run many
independent streams at once, each on its own port, managed by name — plus a
standalone roulette telnet server that bridges players to those streams at
random.
serve (no third-party packages). The roulette server
needs only Python 3.7+, also standard library only.ffprobe) on PATH — only for render / play.
Serving a pre-rendered .dat needs Python alone.telnet or nc (netcat). For the roulette wheel, an ANSI-capable
client in a real terminal.# 1. Render a video you have the rights to into a frame cache
python3 asciivid.py render input.mp4 --width 100 --fps 12 --out myclip.dat
# 2. Serve it
python3 asciivid.py serve myclip.dat --port 2323
# 3. Watch it (from anywhere that can reach the host)
telnet localhost 2323
Or do steps 1–2 in one command:
python3 asciivid.py play input.mp4 --width 100 --fps 12 --port 2323
Playback loops by default. Press Ctrl-C in the client to disconnect
cleanly — your terminal is restored to its original state.
asciivid splits the job into two stages so serving is cheap and instant:
render) uses ffmpeg to decode the video, scale it down to a
terminal-sized grid, and convert every frame to an ANSI string. All frames,
plus metadata (size, fps, colour mode), are pickled and zlib-compressed into
a single .dat file — the frame cache.serve) loads a .dat and streams its frames over TCP with
correct frame timing. It needs no ffmpeg and starts instantly, which makes it
ideal for a long-running service.Each client connection is handled in its own forked process, so multiple people can watch the same stream simultaneously.
asciivid.py has three subcommands. Run python3 asciivid.py <cmd> -h for the
full option list.
render — make a .datConverts a video into a frame cache.
python3 asciivid.py render input.mp4 --out myclip.dat
| Option | Default | Description |
|---|---|---|
--width | 100 | Frame width in characters. |
--height | 0 (auto) | Frame height in rows. 0 derives it from the source aspect ratio. |
--fps | 12 | Frames per second to sample and play back. |
--color | ansi256 | ansi256, truecolor, or mono. |
--out | frames.dat | Output cache path. |
Notes on choosing values:
100 suits a
normal terminal; 160–200 looks sharper on a maximised window but makes a
bigger .dat.--height only if
you want to force a specific grid.--color truecolor looks best on modern terminals (24-bit) but produces a
larger cache and more bytes on the wire. ansi256 is a good default; mono
is tiny and works everywhere.--fps higher than ~15 rarely helps for ASCII and inflates the cache.Example — a crisp, full-colour render:
python3 asciivid.py render input.mp4 --width 160 --fps 15 --color truecolor --out hq.dat
The command prints the resolved grid size and final cache size, e.g.:
rendering input.mp4 -> 160x60 @ 15fps (truecolor)
wrote hq.dat: 900 frames, 2048 KiB compressed
serve — stream a .datServes a pre-rendered cache over TCP.
python3 asciivid.py serve myclip.dat --host 0.0.0.0 --port 2323
| Option | Default | Description |
|---|---|---|
--host | 0.0.0.0 | Interface to bind. 0.0.0.0 = all interfaces. |
--port | 2323 | TCP port to listen on. |
--no-loop | (off) | Play once and disconnect instead of looping. |
Stop the server with Ctrl-C.
play — render + serve in one goConvenience command: renders to a temporary cache, serves it, and deletes the
cache on exit. Takes the union of render and serve options.
python3 asciivid.py play input.mp4 --width 100 --fps 12 --port 2323
Handy for a quick one-off; for anything long-running, prefer render once and
serve the resulting .dat (that's what the service does).
The repo includes a systemd template unit and three helper commands so you
can run any number of streams as managed, boot-persistent services — each with
its own name, port, and clip.
Files involved:
| File | Purpose |
|---|---|
[email protected] | systemd template unit (one file, many instances) |
install-service.sh | one-time setup: user, code, unit, helper commands |
asciivid-add | create & start a new stream by name |
asciivid-ls | list all streams, their ports and state |
asciivid-rm | stop & remove a stream |
Run the installer once. It creates an unprivileged asciivid user, copies
asciivid.py into /opt/asciivid, installs the template unit, and puts the
helper commands on your PATH (/usr/local/bin).
sudo ./install-service.sh
You do not name any streams here — instances are created on demand afterwards.
Each stream is created by name. asciivid-add expects a rendered .dat, not a
raw video — render first, then add:
# render the clip
python3 asciivid.py render never-gonna-give-you-up.mp4 --width 100 --fps 12 --out /tmp/rickroll.dat
# create + start a stream called "rickroll" on port 2323
sudo asciivid-add rickroll /tmp/rickroll.dat 2323
Syntax:
sudo asciivid-add NAME /path/to/frames.dat PORT [HOST]
-, _ only. Becomes the systemd instance
asciivid@NAME; its description shows telnet-NAME.asciivid-add warns if another instance already uses it.0.0.0.0.Each stream gets its own directory under /opt/asciivid/instances/NAME/
containing a copy of the cache (frames.dat) and an env file with its port.
The instance is enabled (starts on boot) and started immediately.
Add as many as you like, each on a distinct port:
sudo asciivid-add starwars /tmp/starwars.dat 2324
sudo asciivid-add badapple /tmp/badapple.dat 2325
asciivid-ls
NAME PORT STATE UNIT
---- ---- ----- ----
telnet-rickroll 2323 active asciivid@rickroll
telnet-starwars 2324 active asciivid@starwars
Remove one (stops it, disables boot start, deletes its directory):
sudo asciivid-rm rickroll
The helpers are thin wrappers around normal systemctl, so you can also drive
instances by hand:
sudo systemctl restart asciivid@rickroll # after swapping its frames.dat
sudo systemctl stop asciivid@rickroll
sudo systemctl start asciivid@rickroll
journalctl -u asciivid@rickroll -f # live logs for one stream
To change the clip on an existing stream, replace its cache and restart:
sudo install -m 0644 /tmp/newclip.dat /opt/asciivid/instances/rickroll/frames.dat
sudo chown asciivid:asciivid /opt/asciivid/instances/rickroll/frames.dat
sudo systemctl restart asciivid@rickroll
A telnet server that spins a roulette wheel and drops you into one of six backend services at random. Connect, watch the wheel decelerate and lock in, then get bridged straight to whatever it landed on. When a backend finishes, you're returned to the wheel to spin again.
It pairs naturally with the asciivid streams above: run a handful of clips as
services, point the roulette at their ports, and let viewers gamble on what
they get.
The server proxies to six local ports:
| Port | Service |
|---|---|
| 2323 | rickroll |
| 2324 | nyan-cat |
| 2325 | what-does-the-fox-say |
| 2326 | gangnam-style |
| 2327 | chocolate-rain |
| 2328 | baby-shark |
These are expected to be listening locally and to stream their content over a
raw TCP/telnet connection. The roulette server itself only handles the wheel
and the proxying — bring your own backends (e.g. asciivid streams).
python3 roulette_server.py
Then, from another terminal:
telnet 127.0.0.1 6666
The wheel spins, locks onto a random backend, and bridges you through. Press Enter at the finish prompt to spin again, or q to quit.
Edit the constants at the top of roulette_server.py:
LISTEN_HOST / LISTEN_PORT — where the roulette server listens (default 0.0.0.0:6666)BACKENDS — the (host, port, name) list to spin betweenIf you set LISTEN_PORT below 1024, either run as root or grant the
capability (see the systemd note below).
A unit file (roulette.service) is included. It runs the server as a
dedicated unprivileged user with a hardened sandbox.
# Place the script and create a service user
sudo mkdir -p /opt/roulette
sudo cp roulette_server.py /opt/roulette/
sudo useradd --system --no-create-home --shell /usr/sbin/nologin roulette
sudo chown -R roulette:roulette /opt/roulette
# Install and enable the unit
sudo cp roulette.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now roulette.service
# Check status and follow logs
sudo systemctl status roulette.service
journalctl -u roulette.service -f
Adjust ExecStart, User, Group, and ReadOnlyPaths in the unit if you
use a different path or user. For a privileged port (< 1024), add
AmbientCapabilities=CAP_NET_BIND_SERVICE to the [Service] section.
Each stream listens on one TCP port. Pick a distinct port per stream.
Ports below 1024 (including the real telnet port 23) require a
privileged bind. The template unit is hardened with no capabilities, so it
can't bind low ports as shipped. To use port 23, add these two lines to the
[Service] section of [email protected], replacing the empty capability
lines, then daemon-reload and restart:
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
Confirm what's listening:
ss -ltnp | grep asciivid
By default the service binds 0.0.0.0 (all interfaces), but reaching it from
outside still depends on your network:
sudo ufw allow 2323/tcp.curl ifconfig.me to find it).⚠️ This is an unauthenticated public TCP service. It forks a process per connection with only light limits (
LimitNOFILE,TasksMax), so a determined client could open many sockets. Fine for a fun demo; if you leave it up, put it behind a firewall allowlist, a reverse proxy with connection limits, or a private overlay network (e.g. a mesh VPN) rather than the open internet.
Unit [email protected] does not exist
The template unit isn't installed. Run sudo ./install-service.sh, or install
just the template:
sudo install -m 0644 [email protected] /etc/systemd/system/[email protected]
sudo systemctl daemon-reload
command not found when running asciivid-add
Either the installer hasn't run yet, or sudo's restricted PATH excludes
/usr/local/bin. Use the full path: sudo /usr/local/bin/asciivid-add ….
Permission denied running ./asciivid-add
The copy in the repo isn't executable, and it's meant to be run from
/usr/local/bin after install anyway. Run the installer, then call it by bare
name without ./.
The stream starts but is empty / errors on load
serve and asciivid-add take a .dat, not an .mp4. Render the video
first with asciivid.py render.
Terminal looks garbled after connecting with an old client
The server negotiates telnet character mode and uses the alternate screen
buffer. Very old or non-standard clients may not honour this; nc always works.
Client hangs on connect
Check the port is actually listening (ss -ltnp) and not blocked by a firewall
between you and the host.
Roulette lands on a backend but shows "unavailable"
That backend port isn't listening. Start the corresponding asciivid stream (or
other service) on the port listed in BACKENDS.
.dat format, the server, the roulette
wheel, and the systemd utilities are original to this project.Licensed under the GNU General Public License v3.0. See the LICENSE
file for the full text.