Always-on retro cartoon web TV built with Nuxt 4.
The app plays channel blocks (YouTube and Dailymotion), supports live channel switching, has a Discord-authenticated admin area, tracks viewer analytics, and broadcasts live viewer/chat updates over WebSockets.
- 24/7 channel playback with a TV-style UI
- Multiple channels with per-channel active blocks
- Block Maker for creating/editing playlists
- Schedule editor (times stored in
America/Chicago) - Live viewer counts and channel chat via WebSockets (
/api/viewers) - Discord OAuth for authentication
- Admin analytics dashboard (unique viewers, returning %, visits, channel breakdown)
- Nuxt 4 / Vue 3
- Nitro server + WebSocket handlers
- JSON file storage under
assets/(blocks, channels, schedules) - PM2 for production process management
- GitHub Actions SSH deploy to server
- Node.js
24.1.0(matches deploy workflow) - npm
Install dependencies:
npm ciCreate .env in project root:
NODE_ENV=development
PORT=3000
DISCORD_CLIENT_ID=your_discord_app_client_id
DISCORD_CLIENT_SECRET=your_discord_app_client_secret
DISCORD_REDIRECT_URI=http://localhost:3000/api/auth/discord/callback
DISCORD_ALLOWED_IDS=123456789012345678,987654321098765432
SESSION_SECRET=replace-with-a-long-random-string
# Optional but recommended for YouTube auto-fill in Block Maker
YOUTUBE_API_KEY=your_youtube_data_api_key
# Optional analytics output file (defaults vary by NODE_ENV)
# ANALYTICS_FILE=.data/analytics.json
# Optional extra chat censor terms (comma or newline separated)
# CHAT_CENSOR_EXTRA=phrase one,phrase twoStart dev server:
npm run devOpen http://localhost:3000.
npm run dev- start local dev servernpm run build- build production outputnpm run preview- run built app locallynpm run generate- static generate (if needed)
This project stores most runtime content as JSON:
assets/blocks/*.json- block playlistsassets/blocks/active-blocks.json- active block per channelassets/blocks/blocks-index.json- block metadata (created/updated/by)assets/channels/channels-index.json- channel list and namesassets/schedules/schedules.json- scheduled block switchesassets/discord-users.json- Discord ID to username cache
Default channel payloads currently exist in:
assets/channels/toonami.jsonassets/channels/adult-swim.jsonassets/channels/saturday-morning.json
- Discord login endpoints:
/api/auth/discord/login/api/auth/discord/callback
- Admin routes (for allowed Discord IDs):
/admin/admin/block-maker/admin/schedule/admin/analytics
- Chat login uses Discord with
scope=chatand is separate from admin allow-list checks.
On push to master, .github/workflows/deploy.yml deploys over SSH and runs:
git pull origin masteron/var/www/cartoonrewatchnpm cinpm run buildpm2 restart 0 --update-env
PM2 config is in ecosystem.config.cjs and runs .output/server/index.mjs.