Overview
Purrl TV is an adult, stations-only Discord Activity and website (purrl.tvkitty.com) forked from Tv Kitty. Members tune into always-on stations that play from a private Plex library on the NAS; there is no watch-party mode. Everything is gated by an 18+ Discord role, and the app itself never shows explicit content until someone deliberately enters a station.
Stations from folders
Stations are scoped to Plex libraries and tags. Videos are auto-labeled with the folder they sit in, so a station named after a folder fills itself.
Blurred featured station
The home screen shows a blurred poster with an Enter button, never a live preview.
Likes and Top Rated
Anyone can like what a station is playing; the most-liked videos make the Top Rated page.
Two tiers
Everyone with the 18+ role watches stations. The owner and a chosen full-library role also browse, play, keep favorites and upload clips.
Clip uploads
Post a video in the clips channel and the bot asks which category it is (a dropdown under your post, no threads); pick one and it lands in that folder on the NAS, Plex rescans, and the Discord post is deleted (the bot needs Manage Messages there). Also from the app's upload page or /clip-upload.
Always ready
The whole library is prepared ahead of time and nothing prepared is ever deleted; disk is the only limit.
How content flows
1. Download into a category folder
The NAS share purrl has Videos\<Category> and Clips\<Category> folders (Vintage, Ebony, Interracial, Amateur, ...). Two Desktop shortcuts open them. Plex's "Purrl (18+)" library covers the whole share.
2. Labels appear on their own
Every 30 minutes the backend re-reads the library, labels each video with its folder name (clips also get "Clips"), and queues anything unprepared for preparation at the lowest priority.
3. Stations pick by tag
A station scoped to tag Vintage plays everything labeled Vintage. Run /station-rebuild after adding content to rebuild today's schedules right away instead of waiting for the nightly build.
Commands
Everyone
| Command | What it does |
|---|---|
| /stations | Browse station schedules and vote to replace upcoming items. |
| /clip-upload full tier | Upload a video attachment into a category. Age-restricted channels only. |
Owner
| Command | What it does |
|---|---|
| /connect | Link the Plex account Purrl streams from (the owner's personal account, which owns the NAS server). |
| /station-add | Create a station: name, comma-separated library titles, comma-separated tags. Blank tags = everything in those libraries. |
| /station-edit / /station-remove / /station-list | Manage stations. Type - for a field to clear it. |
| /station-scope-options | Show the libraries and tags a station can be scoped to. |
| /station-rebuild | Rebuild today's station schedules now. |
/full-library-role (+ -clear, -show) | The role that unlocks browsing, favorites, Play and uploads. |
| /clips-channel-set | The age-restricted clips channel. Any video posted there gets a category prompt (dropdowns, 15-minute timeout files it under Uncategorized) and is saved to the library; /clip-upload cards go there too. No threads. |
| /clip-channel-add #channel [category] | Optionally watch another 18+ channel too, with its own default category. /clip-channel-remove, /clip-channel-list. |
| /now-playing-channel | Age-restricted channel for station "now playing" cards. |
| /station-calendar-channel | Channel for the daily station calendar post. |
| /favorite-save-role | Role whose favorites are kept prepared permanently (inherited from Tv Kitty). |
In the app
- Home: the blurred featured station with Enter, the other station tiles, and (full tier) a big "Purrl (18+)" library tile.
- Menu: Join a Station, Top Rated, and for the full tier My Favorites (with Randomize) and Upload a Clip.
- Station view: Like what's playing; full tier also gets Favorite.
- Video page: Like for everyone; Play and Favorite for the full tier. Play prepares the file through the same pipeline the stations use, then plays it in the app.
Operations
Services
Three Windows services on the desktop, installed by scripts\install_services.ps1: PurrlTvBackend (port 8440, runs as the local tvkitty account so it can write to the NAS), PurrlTvBot (internal API 8441), PurrlTvVite (port 5174). purrl.tvkitty.com is an ingress rule in Tv Kitty's named Cloudflare tunnel; there is no tunnel service of its own.
Restart order
Frontend changes need npm run build in activity\ followed by Restart-Service PurrlTvVite.
Configuration
.env in the repo root (never committed). Notable keys: PLEX_SERVER_NAMES=tvkitty-nas, PURRL_LIBRARY_PREFIX=Purrl, CLIPS_DIR, PREPARED_MEDIA_DIR, FFMPEG_BINARY (full path - the service account has no PATH to the per-user ffmpeg), PREPARE_WHOLE_LIBRARY, PREPARE_KEEP_EVERYTHING, AUTO_LABEL_BY_FOLDER.
Watch the disk
Prepared files are kept forever by design. The cache lives on D:\purrl-prepared for now; move it to a NAS share the way Tv Kitty's was moved when it grows.
Tech
Python 3.10, FastAPI + uvicorn, discord.py 2.7, plexapi, aiosqlite, ffmpeg; vanilla JS frontend built with Vite and served as a Discord Activity and a standalone site. Repo: oldcatlady12/purrl-tv (private).