{"id":51016005,"url":"https://github.com/antlis/tg-streaming-bot","last_synced_at":"2026-06-21T10:31:01.831Z","repository":{"id":363537232,"uuid":"1263370903","full_name":"antlis/tg-streaming-bot","owner":"antlis","description":"Self-hosted Telegram bot that streams music \u0026 video into group voice chats — YouTube, internet radio, a local media library, recording, and full playback controls. Built with Pyrogram + py-tgcalls.","archived":false,"fork":false,"pushed_at":"2026-06-09T11:00:59.000Z","size":492,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T11:08:56.220Z","etag":null,"topics":["ffmpeg","music-bot","pyrogram","pytgcalls","python","self-hosted","telegram","telegram-bot","video-streaming","voice-chat"],"latest_commit_sha":null,"homepage":"https://antlis.github.io/tg-streaming-bot/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antlis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-08T22:25:51.000Z","updated_at":"2026-06-09T11:03:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/antlis/tg-streaming-bot","commit_stats":null,"previous_names":["antlis/tg-streaming-bot"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/antlis/tg-streaming-bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antlis%2Ftg-streaming-bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antlis%2Ftg-streaming-bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antlis%2Ftg-streaming-bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antlis%2Ftg-streaming-bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antlis","download_url":"https://codeload.github.com/antlis/tg-streaming-bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antlis%2Ftg-streaming-bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34607126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ffmpeg","music-bot","pyrogram","pytgcalls","python","self-hosted","telegram","telegram-bot","video-streaming","voice-chat"],"created_at":"2026-06-21T10:31:01.127Z","updated_at":"2026-06-21T10:31:01.822Z","avatar_url":"https://github.com/antlis.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tg-streaming-bot\n\nTelegram bot that streams **music \u0026 video into group voice chats**, built with [Pyrogram](https://docs.pyrogram.org) and [py-tgcalls](https://github.com/pytgcalls/pytgcalls).\n\n## ✨ Features\n- **Music \u0026 video** into group voice chats — from YouTube (search **or** URL), an audio/video file posted in Telegram, or a live link (m3u8 / YouTube-live)\n- **`/search`** — pick from YouTube results (🎵 audio or 🎬 video) instead of auto-playing the first hit\n- **Internet radio** — dozens of built-in stations, with the live now-playing track shown on the video card\n- **Local media library** — browse \u0026 play your own folders, with audio-track and subtitle selection\n- **Recording** — capture the radio/audio to a voice message or the video to an H.264 mp4 and upload it (toggle on/off, live tracklist)\n- **Screenshot** — grab the current video frame to the chat\n- **Full controls** — pause / resume / skip / seek, **master volume**, mute, loop / shuffle / clear, seek-to-% buttons and a queue — one inline now-playing panel plus `/info`\n- **Hardware transcoding** (VAAPI) or CPU for HEVC/MKV sources\n- **Auto-DJ** (`/autoplay`) — keep playing related tracks when the queue runs out · **SponsorBlock** to skip sponsor/intro segments\n- **Self-healing** — auto-reconnect on drops, resume after a restart, idle auto-leave\n- Per-user **rate limiting** + a **max-queue** cap; everything env-configured for self-hosting\n\n## How it works\nTwo Telegram identities are required:\n- **The bot** (command interface) — must be a group **admin** with *Manage video chats*, *Delete messages*, and *Add users*.\n- **An assistant user account** — logs in via a Pyrogram session string (`SESSION_NAME`) and is what actually joins the voice chat and streams. It must be a **member of every group** it plays in (play commands auto-join it), and should be a **dedicated account**: Telegram allows only one voice-chat connection per account, so don't use an account a human listens with.\n\n## Setup\n1. Clone and enter the repo:\n   ```sh\n   git clone https://github.com/antlis/tg-streaming-bot.git\n   cd tg-streaming-bot\n   ```\n2. Copy the template and fill it in:\n   ```sh\n   cp example.env .env\n   ```\n   See the table below — only the **Required** block is mandatory.\n3. Generate the assistant session string — log in as the **assistant** account\n   and paste the printed string into `SESSION_NAME` in `.env`:\n   ```sh\n   docker run -it --rm --env-file .env \\\n     -v \"$(pwd)/gen_session.py:/gen.py\" python:3.11-slim \\\n     sh -c \"pip install -q kurigram tgcrypto \u0026\u0026 python3 /gen.py\"\n   ```\n4. Build \u0026 run (compose is the easy way — includes the cache volume and a healthcheck):\n   ```sh\n   docker compose up -d --build\n   ```\n   or plain docker:\n   ```sh\n   docker build -t musicbot .\n   docker run -d --name musicbot --env-file .env -e PYTHONUNBUFFERED=1 \\\n     -v musicbot_downloads:/app/downloads \\\n     --restart unless-stopped musicbot\n   ```\n5. In the group: add the bot (as admin), start a voice chat, then `/play \u003csong\u003e`.\n\n## Configuration (`.env`)\n| Key | Required | Purpose |\n|---|---|---|\n| `API_ID` / `API_HASH` | ✅ | from my.telegram.org |\n| `BOT_TOKEN` | ✅ | from @BotFather |\n| `BOT_USERNAME` | ✅ | bot username without @ |\n| `BOT_NAME` | ✅ | display name |\n| `SESSION_NAME` | ✅ | assistant session string (step 2) |\n| `SUDO_USERS` | ✅ | space-separated admin user ids |\n| `DURATION_LIMIT` | — | max track length (minutes) |\n| `DOWNLOADS_CACHE_LIMIT_MB` | — | downloads-cache cap in MB, pruned after each stream (default 4096; 0 = delete after playing) |\n| `LIBRARY_HOST_DIR` / `LIBRARY_ROOT` / `LIBRARY_CATEGORIES` | — | local media library: host folder to mount read-only, the in-container path (`/library`), and an optional comma-separated category allowlist; leave unset to disable |\n| `RADIO_STATIONS` | — | override the built-in `/radio` presets (`Name=URL,Name=URL,…`) |\n| `RADIO_IMG` | — | card image shown while streaming radio (defaults to `IMG_1`) |\n| `TRANSCODE_HWACCEL` | — | `vaapi` for GPU transcode/record (needs `/dev/dri` mounted + VA drivers); empty = CPU |\n| `IDLE_LEAVE_MINUTES` | — | leave the voice chat after N minutes with no listeners (default 10; 0 = never) |\n| `MAX_QUEUE_SIZE` | — | max upcoming tracks per chat (default 50; 0 = unlimited) |\n| `RATE_LIMIT_MAX` / `RATE_LIMIT_WINDOW` | — | per-user command rate limit (default 5 commands / 10s; sudo users exempt) |\n| `SPONSORBLOCK_REMOVE` | — | comma-separated SponsorBlock categories to cut from YouTube downloads (e.g. `sponsor,selfpromo,music_offtopic`); empty = off |\n| `COMMAND_PREFIXES` | — | accepted command prefixes (default `/ ! .`) |\n| `ASSISTANT_NAME` | — | assistant @username (without @), used in messages |\n| `OWNER_NAME` / `ALIVE_NAME` | — | owner link \u0026 name for `/start` and `/alive`; empty = hidden |\n| `GROUP_SUPPORT` / `UPDATES_CHANNEL` | — | username or `+invitehash` for the Group/Channel buttons; empty = hidden |\n| `ALIVE_IMG`, `IMG_1`–`IMG_4` | — | card images (URL or file path); bundled placeholder by default |\n| `UPSTREAM_REPO` | — | your repo's git URL (shown as the \"Source Code\" button on `/start`) |\n| `PROXY_*` | — | optional proxy for Telegram signaling |\n\n## 🛠 Commands\n| Command | Description |\n| ------ | ------ |\n| `/play \u003cquery\\|URL\u003e` | play music (or reply to an audio / voice / audio-file message) |\n| `/vplay \u003cquery\\|URL\u003e [720\\|480\\|360] [mute]` | play video (or reply to a video); `mute` starts it muted |\n| `/vstream \u003clink\u003e` | stream a live link / m3u8 / YouTube live |\n| `/search \u003cquery\u003e` | pick from YouTube results — 🎵 audio or 🎬 video |\n| `/radio` | internet radio — pick a station |\n| `/record [secs]` · `/stoprec` | record the current audio/video and send it (⏺ on the panel toggles; auto-stops at 1 h) |\n| `/library` · `/lplay \u003cname\u003e` | browse / play the local media library |\n| `/screenshot` | send a frame of the current video |\n| `/pause` `/resume` `/skip` `/stop` | playback control (admins) |\n| `/seek 12:30` · `/continue` | jump to a time / resume after a drop |\n| `/volume 0-200` · `/vmute` `/vunmute` | master volume / mute (applied to the stream, heard by everyone) |\n| `/loop` `/shuffle` `/clear` · `/autoplay` | queue modes · auto-DJ related tracks (admins) |\n| `/info` · `/playlist` | now-playing panel with controls / the queue |\n| `/song \u003cquery\u003e` · `/video \u003cquery\u003e` | download instead of stream |\n| `/userbotjoin` `/userbotleave` · `/reload` | assistant join / leave · refresh admin cache |\n| `/ping` `/alive` `/uptime` | status checks |\n\n## Notes\n- YouTube playback **downloads first, then streams** (yt-dlp with the `android_vr` client; H.264+AAC mp4) — direct stream URLs are blocked by YouTube these days. Expect a short delay before playback starts.\n- Large Telegram files also download fully before streaming — a progress bar is shown; big files just take a while.\n\n## Running where Telegram is filtered (Reality VPN sidecar)\n\nWhere Telegram — especially the voice servers — is blocked or throttled, a SOCKS\nproxy isn't enough: it carries only the **signaling**, not the **voice UDP** that\npy-tgcalls streams on. So `docker-compose.yml` ships an optional **`reality-vpn`\nsidecar**: a [sing-box](https://sing-box.sagernet.org) container that raises a TUN\nand tunnels **all** of the bot's traffic (signaling **and** voice) through a\n[VLESS + Reality](https://github.com/XTLS/REALITY) server. The `bot` joins the\nsidecar's network namespace (`network_mode: \"service:reality-vpn\"`), so everything\nit does exits via your VPN — no per-client proxy config needed, and it doesn't\naffect anything else on the host.\n\n1. Copy the template and fill in your Reality server:\n   ```sh\n   cp reality-vpn.json.example reality-vpn.json\n   ```\n   Set `server` / `server_port` / `uuid` / `public_key` / `short_id` / `server_name`\n   (the borrowed SNI) — straight from your server's Xray/sing-box Reality config or\n   its `vless://…` share link.\n2. Start everything — the bot waits until the VPN is healthy, then routes through it:\n   ```sh\n   docker compose up -d\n   ```\n   Verify the exit is your server, not your real IP:\n   ```sh\n   docker compose exec bot wget -qO- https://api.ipify.org\n   ```\n\n**Notes**\n- The bot shares the sidecar's netns, so to switch servers edit `reality-vpn.json`\n  then `docker compose up -d --force-recreate` (recreates both).\n- Don't need a full tunnel? Skip the sidecar entirely and set the `PROXY_*` env\n  vars instead — but note voice will then go direct.\n- `reality-vpn.json` is gitignored (live credentials); only `.example` is committed.\n\n## Roadmap / TODO\n\nDone so far: modern stack (Python 3.11, Pyrogram 2.x / py-tgcalls 2.x), YouTube\nsearch picker, internet radio, local media library with audio/subtitle\nselection, audio **and** video recording, screenshots, master volume + mute,\nseek / seek-to-%, loop / shuffle / clear, auto-reconnect \u0026 resume, idle\nauto-leave, hardware (VAAPI) transcoding, download-cache GC, rate limiting,\nstartup config validation, logging, and CI.\n\nStill open:\n- [ ] **Stream-while-downloading** — begin playback once enough is buffered instead of waiting for the full download (large files sit silent for a while today).\n- [ ] **Queue management** — show durations, remove an arbitrary item, reorder.\n- [ ] **More sources** — Spotify / SoundCloud / direct-URL routing (yt-dlp already supports most; mainly needs URL routing + metadata).\n- [ ] **Deduplicate `music.py` / `video.py`** — `ytsearch()`, `ytdl()`, the permission gate and assistant-join logic are near-identical copies; extract shared helpers.\n- [ ] **More tests** — only a smoke/import check runs in CI today; add unit tests for the queue, seek/position, and `ytsearch` URL detection.\n- [ ] **i18n** — user-facing strings are hardcoded English; extract to a strings module.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantlis%2Ftg-streaming-bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantlis%2Ftg-streaming-bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantlis%2Ftg-streaming-bot/lists"}