#Sources and deployment
Where frames come from, how to queue them, and how to run the server somewhere other than your laptop.
#Choosing a source
# One file
python stream_server.py video.mp4 --cols 240
# A whole folder, played in filesystem order
python stream_server.py --folder videos --cols 200 --loop
# A JSON playlist with per-video overrides
python stream_server.py --playlist playlist.json --cols 220
# A YouTube video or playlist (needs the optional yt-dlp extra)
python stream_server.py "https://youtu.be/VIDEO_ID" --cols 240
# Your webcam
python stream_server.py --webcam --cols 240
Open http://localhost:8000 in a browser once the server is up.
#Playlists
Each entry can override the global --mode, --pixel, --vol and --cols:
[
{ "video": "intro.mp4", "mode": 1, "vol": 1 },
{ "video": "main.mp4", "pixel": true, "vol": 3, "cols": 520 },
{ "video": "https://youtu.be/VIDEO_ID", "mode": 4, "vol": 2, "cols": 240 }
]
File names are resolved against both the project root and videos/, so a bare name is usually enough.
#YouTube and other URLs
Install the extra first: pip install ".[ytdlp]". ASCII output only needs a small grid, so downloads are fetched at 480p or lower, normalized to H.264/AAC at a constant frame rate (so A/V sync holds), and cached in videos/ by video ID. The first play is slow; replays are instant. Cap disk use with --cache-limit (in MB, default 10240).
#Webcam
python stream_server.py --webcam --webcam-device 1 --webcam-fps 60
python stream_server.py --webcam --no-mirror
The webcam is mirrored by default. A webcam stream cannot seek or pause.
#Audio volume
--vol takes 0–5. At 0 the FFmpeg audio path never runs, which saves CPU and bandwidth.
#In the terminal
You can skip the browser entirely and render in an ANSI terminal:
python ascii_video_player2.py video.mp4 --cols 100
python ascii_video_player2.py --webcam --cols 100
python ascii_video_player2.py video.mp4 --half-block
Half-block mode packs two pixels into each cell (▀ with foreground and background colors) for double the vertical resolution, and redraws only cells that changed. Don't resize the terminal while it plays; wrapping will corrupt the layout.
#Serving on a network
python stream_server.py video.mp4 --host 0.0.0.0
--host exposes the server to your LAN. Treat it as a development server: put it behind your own access control before exposing it further.
#Docker
The repository ships a Dockerfile and docker-compose.yml for the web streaming server. The image is based on python:3.11-slim, installs FFmpeg and uses the headless OpenCV build.
docker compose up --build
This starts stream_server.py --folder videos --host 0.0.0.0 --port 8000 and mounts ./videos into the container, so any file you drop into that folder appears without a rebuild. Plain Docker works too:
docker build -t asciline .
docker run -p 8000:8000 -v $(pwd)/videos:/app/videos asciline --folder videos --cols 220 --loop
Things the container does not do:
- No yt-dlp. Add
RUN pip install ".[ytdlp]"to the Dockerfile for URL playback. - No Rust engine. The image runs the Python server only.
- No webcam or terminal player. Docker is for the web server.
#Health and status endpoints
GET /health returns {"status":"ok"} when the HTTP server responds. It does not open a socket or decode anything, and the Docker image polls it every 30 seconds.
GET /info returns a small JSON snapshot of the server's configuration (queue_size, current_index, cols, rows, loop, debug). rows: 0 means automatic sizing, and individual sessions may use different dimensions.
#Customizing the look
The root web client reads CSS variables, so a quick recolor is two lines:
:root {
--accent-color: #00ff41;
--bg-color: #050505;
}
In the player, press F (or click FX) for live filters: contrast, brightness, gamma, sharpen, invert, and palettes (Default, Flat/Anime, Block). From your own page, the same controls are available through player.setFilters().