FireVision IPTV Server
July 14, 2026 · View on GitHub
Complete inventory of every feature in the application.
Authentication
- Login with username or email and password
- 24-hour session expiry with automatic cleanup
- Session tracks IP address, user agent, and last activity timestamp
- Users can have multiple active sessions across devices
- View all active sessions with device/location info
- Revoke individual sessions remotely
- Force logout on all other devices when password is changed
- Cleanup expired sessions manually or via cron
JWT Authentication
- Access token with 15-minute expiry
- Refresh token with 30-day expiry
- Token refresh without re-entering credentials
- Token revocation on logout
- Bearer token validation on protected routes
OAuth2 Social Login
- Sign in with Google (OpenID, profile, email scopes)
- Sign in with GitHub (user profile and email scopes)
- Automatic account creation on first OAuth sign-in with random password
- Link OAuth provider to existing account if email matches
- Redirect back to app with session after OAuth completion
User Registration
- Self-service registration with username, email, and password
- Username must be 3–50 characters, alphanumeric and underscore only
- Password minimum 6 characters (session auth) or 8 characters (JWT signup)
- Email format validation
- Username and email uniqueness enforcement
- Prevents registration with reserved super admin username
- Rate limiting on public signup: 10 per IP per hour (configurable)
- Automatic 6-character alphanumeric channel list code generated on signup
User Profile
- Update username and email with uniqueness checks
- Change password with current password verification
- Upload profile picture (JPEG, PNG, GIF up to 5 MB)
- Delete profile picture
- Old profile picture automatically deleted when uploading new one
- Profile pictures stored on disk with user-specific filenames
- Regenerate channel list code on demand
User Roles & Access Control
- Two roles: Admin and User
- Role-based middleware protecting admin-only routes
- Non-admin users can only view and edit their own profile
- Admin users have full access to all management features
- Account active/inactive status controlling login ability
Admin User Management
- View all registered users with role and status info
- Create new users with specified role and credentials
- Update any user's username, email, password, role, or active status
- Deactivate user accounts (prevents login without deleting data)
- Reactivate previously deactivated accounts
- Delete users along with all their sessions
- Regenerate any user's channel list code
Super Admin Initialization
- Automatic super admin creation on first server startup
- Configurable super admin username, password, email, and channel code via environment variables
- Default credentials provided if not configured
- Option to force-update admin password on startup
- Updates channel list code if environment variable changes
Channel Management
- Create channels with name, URL, logo, group/category, and sort order
- Update and delete individual channels
- DRM support fields for protected streams (key and type)
- TVG metadata fields for M3U compatibility (tvg-name, tvg-logo)
- Channel metadata including country, language, resolution, and tags
- Duplicate channel ID prevention on creation
- Channels automatically sorted by group then order
Channel Search & Filtering
- Search channels by name, group, or ID with case-insensitive matching
- Filter channels by group/category
- View all channels grouped by category
- Full-text search on channel names
Channel Bulk Operations
- Import channels from M3U playlist content
- M3U parser extracts tvg-id, tvg-name, tvg-logo, group-title, channel name, and stream URL
- Option to clear all existing channels before import
- Bulk delete all channels at once
Channel Details Preview
- Channel details modal showing metadata before deciding to play
- Displays channel logo, name, group/category, language, country, and stream URL
- Available on admin channels page (triggered by clicking a channel row)
- Available on IPTV-org page (triggered by info button)
- "Preview Stream" button in the details modal launches the channel player
- Direct play button also available on each row to skip the details modal
- User channels page has direct play button only (no details modal)
Channel Player
- In-browser HLS video player using HLS.js library
- Plays channels directly from the admin and IPTV-org pages via modal popup
- Extra-large responsive modal with centered layout and black background
- Native HTML5 video controls: play/pause, seekbar, volume, fullscreen
- HLS.js with Web Worker support and low-latency mode enabled
- Adaptive bitrate streaming with automatic quality switching
- Buffering configuration: 30-second max buffer, 90-second back buffer
- Manifest loading with 10-second timeout and 3 retries
- Fragment loading with 20-second timeout and 3 retries
- Safari native HLS fallback when HLS.js is not supported
- Unsupported browser detection with user-friendly message
- Automatic stream proxying for external URLs to bypass CORS restrictions
- VLC user-agent spoofing on proxied requests to maximize source compatibility
- M3U8 playlist rewriting: relative URLs converted to absolute and routed through proxy
- Session-based authentication passed to proxy via header
- Live status indicators in player footer: loading, buffering, playing, paused, error
- Stream URL display with truncation and hover tooltip
- Loading spinner overlay with dark background during stream initialization
- Error overlay with descriptive messages for network, media, and decode errors
- Automatic error recovery: restarts loading on network errors, recovers on media errors
- Detailed error messages: aborted, network error, decode error, format not supported
- Full resource cleanup on player close: HLS instance destroyed, listeners removed, video buffer cleared
- DRM metadata fields available on channels (key and type) but not yet wired into playback
- No manual quality selector, subtitle support, or casting integration
Channel Stream Testing
- Test individual channel stream availability
- Batch test multiple selected channels sequentially
- Test all channels with pagination (configurable batch size and offset)
- Session-based lock prevents concurrent test operations (5-minute auto-expiry)
- HTTP status validation (200–399 considered working)
- M3U8 manifest validation checking for proper HLS tags
- Extracts manifest info: live vs VOD, video presence, segment count
- Records test results: last tested timestamp, working status, response time
- Descriptive error messages for timeout, DNS failure, connection refused, and HTTP errors
- VLC user agent used for stream requests to maximize compatibility
Stream Metrics
- Cumulative health counters per channel: deadCount, aliveCount, unresponsiveCount, playCount
- Timestamps for last occurrence of each status: lastDeadAt, lastAliveAt, lastPlayedAt, lastUnresponsiveAt
- Client-reported stream status via
POST /api/v1/channels/:id/report-status(dead, alive, unresponsive) - Client-reported playback confirmation via
POST /api/v1/channels/:id/report-play - Bulk health sync from scanner/client via
POST /api/v1/channels/health-sync(up to 100 reports per call) - In-memory rate limiting: 1 status report per channel per device per 5 minutes, 1 play report per channel per device per 1 minute, 1 health sync per device per 5 minutes
- Server-side probe results (channel test) automatically increment alive/dead counters
- Enhanced admin stream health analytics: most failing streams, most popular streams, removal candidates (high failures + zero plays), unresponsive streams
- Aggregate metrics in admin stats: total dead/alive/unresponsive/play counts across all channels
- Admin dashboard Stream Health summary card with alive/dead/unresponsive/play totals and proportional health bar
- Admin stats page shows detailed stream metric tables: Most Failing, Most Popular, Removal Candidates, Unresponsive streams
- Play count column in admin and user channel tables
- Channel detail modal shows full metrics (admin: all counts + timestamps, user: counts)
- User dashboard "My Channels" card shows working/failing breakdown with colored status dots
- Web player reports play events via
POST /channels/:id/report-playon first playback per channel per session - Proxy play tracking:
proxyPlayCountmetric records how many plays used the server proxy instead of direct stream - Web player sends
proxyPlay: truewhen stream fell back to proxy playback - Android TV app sends
proxyPlay: truewhen ExoPlayer switched to proxy after direct failures - Admin dashboard and stats page show "Proxy Plays" alongside "Total Plays"
- Channel detail modal shows proxy play count for admin users
Global M3U Playlist
- Generate M3U playlist of all channels in the system
- Protected by playlist code (query parameter or header)
- Accepted codes configured via environment variables
- Standard M3U format with EXTINF metadata tags
TV/Device Pairing (PIN-Based)
- TV device requests a 6-digit numeric PIN
- PIN is unique among all active pending requests
- PIN expires after configurable duration (default 10 minutes)
- Expired PINs automatically cleaned up via database TTL index
- TV displays PIN and polls server for pairing status
- Authenticated web user confirms pairing by entering the PIN
- On confirmation, user's channel list code is linked to the device
- Device metadata (name, model) recorded on the user profile
- Pairing status endpoint returns pending, completed, or expired state
- Completed status includes the channel list code for playlist retrieval
TV/Device Pairing (Legacy Code-Based)
- TV device pairs using the user's 6-character channel list code directly
- Code verification endpoint to check validity without pairing
- Device metadata updated on the user profile on legacy pairing
- No authentication required for TV-side endpoints
TV Playlist Access
- Retrieve user's playlist in M3U format using channel list code
- Retrieve user's playlist in JSON format using channel list code
- Code must be exactly 6 uppercase alphanumeric characters
- User must be active for playlist retrieval to succeed
- Admin users get all channels; regular users get only their assigned channels
- Channels sorted by group then order in generated playlists
- Last login timestamp updated on each playlist retrieval
User Playlist Management
- View list of personally assigned channels
- Replace entire channel assignment list at once
- Add channels to personal list (deduplicates automatically)
- Remove channels from personal list
- Download personal channel list as M3U file
- M3U file named with username for easy identification
- Admin users automatically have access to all channels regardless of assignment
M3U Playlist Generation
- Standard M3U format with
#EXTM3Uheader - Playlist name tag with username
- Each channel includes tvg-id, tvg-name, tvg-logo, and group-title attributes
- Channel name as display title in EXTINF line
- Stream URL on the following line
App Version Checking (GitHub-Based)
- Fetch latest release information from a configurable GitHub repository
- Match APK asset by configurable filename pattern
- Extract version code from release tag name
- Compare client version against latest available version
- Return whether an update is available
- Include release notes/changelog from GitHub release body
- Provide direct download URL to APK asset
- Support for mandatory update flag
- Minimum compatible version support
- Direct redirect to APK download URL
App Version Management (Database-Based)
- Upload APK files to server storage
- Store version metadata: name, code, filename, file size, release notes
- Mark versions as active or inactive
- Set mandatory update flag per version
- Set minimum compatible version per version
- List all stored versions
- Update version metadata
- Delete versions with automatic file cleanup from disk
Statistics — Channels
- Total channel count
- Active channel count (streams that passed last test)
- Inactive channel count (streams that failed last test)
- Channel count grouped by category
Statistics — Users
- Total registered user count
- Active user count
- 10 most recently registered users
Statistics — Sessions
- Total session count in database
- Active (non-expired) session count
- 20 most recent active sessions with IP and user agent
- Session count grouped by IP address/location
Statistics — Device Pairing
- Total pairing request count
- Pending pairing count
- Completed pairing count
- Today's pairing count
- 10 most recent pairing requests
Statistics — Activity Timeline
- Combined feed of recent logins, registrations, and device pairings
- Each entry has type, title, description, and timestamp
- 15 most recent events across all activity types
- Sorted by timestamp descending
Statistics — Charts & Visualizations
- Donut/pie chart showing channel distribution by group
- Horizontal bar chart showing active sessions by location
- Activity timeline component with relative timestamps
- Trend line/area charts for user signups, sessions, and device pairings over time
- Selectable time ranges for trend charts (7 days, 30 days, 90 days)
- Backend aggregation endpoints (
GET /api/v1/admin/stats/trends/:type) for daily time-series data - Responsive layout working across desktop, tablet, and mobile
- Light and dark theme support via CSS variable-based chart colors
IPTV-Org Integration
- Fetch channel database from iptv-org public API
- Fetch stream URLs from iptv-org
- Fetch language reference data from iptv-org
- Fetch EPG guide data from iptv-org
- Fetch feed data from iptv-org
- In-memory cache with 1-hour TTL for all fetched data
- Cache status reporting per data type
- Manual cache clearing
- Enriched channel view merging streams with channel metadata and language names
- Comprehensive fetch endpoint with cross-referencing across all data sources
- Filter by country, category, language, and result limit
- Language code conversion between 2-letter and 3-letter formats
- Language priority resolution: feeds > guides > channel metadata
- Predefined playlist filters (India all, kids Hindi, news India, movies Hindi, sports India)
- Import iptv-org channels to system database with deduplication
- Import iptv-org channels directly to user's personal playlist
- Reuses existing channels if stream URL already exists in system
Smart Stream Grouping & Auto-Fallback
- IPTV-org channels grouped by channel ID in import UI (multiple streams per logical channel)
- Stream ranking algorithm: liveness (alive > unknown > dead) > quality (1080p > 720p > 480p > SD) > response speed
- Best stream auto-selected during import, admin can override via expandable row with radio buttons
- Alternate streams stored on Channel model (
alternateStreams[]) with quality, liveness, userAgent, referrer - Grouped import endpoint creates channels with primary + alternates in one operation
- Channel detail modal shows alternate streams with promote button (swap alternate to primary)
- Bad stream flagging system: users can report streams as looping, frozen, wrong-content, or other
- Flag reasons recorded with user, timestamp; admins can clear flags
- Flagging available for both primary and alternate streams
- Fallback API endpoint (
/channels/:id/with-fallbacks) returns only alive, non-flagged alternates - User playlist fallback endpoint (
/user-playlist/me/channels-with-fallbacks) for Android app - M3U playlist generation auto-substitutes alive non-flagged alternate when primary is dead or flagged
- Stream health scheduler task (default: 4 hours) probes primary streams, auto-promotes best alive non-flagged alternate
- Flagged streams excluded from auto-promotion and export
Stream Proxy
- Proxy HLS/DASH streams through the server to avoid client CORS issues
- VLC user agent for upstream requests to maximize compatibility
- M3U8 manifest rewriting: converts all relative URLs to absolute
- M3U8 manifest rewriting: rewrites all URLs to route through proxy
- Binary media segments (.ts files) piped directly without rewriting
- 30-second timeout with up to 5 redirects
- CORS headers added to all proxied responses
- Upstream timeout returns 504, other errors return 502
- SSRF protection: validates URLs before fetching, blocks redirects to private/internal addresses
TV Stream Proxy
- TV-code-authenticated stream proxy at
GET /api/v1/tv/stream/:code?url=<stream_url> - Mirrors full stream proxy behavior (HLS manifest rewriting, VLC user agent, SSRF protection) but authenticated via channel list code in URL path instead of session
- HLS manifest URLs rewritten to route through the TV proxy endpoint
- Android TV app uses this as fallback when direct stream playback fails
- Proxy URL constructed from server URL + TV code + encoded stream URL
Alternate Stream Fallback (Android TV)
- Android TV app receives up to 3 alternate stream URLs per channel via
GET /channelsresponse - Alternates filtered server-side: dead and flagged streams excluded, capped at 3, slimmed to
{streamUrl, quality} - ErrorRecoveryManager uses a StreamSlot queue for multi-URL retry with proxy fallback per URL
- Retry strategy per channel: primary direct (2x) → primary proxy (1x) → alt1 direct (1x) → alt1 proxy (1x) → alt2 ... → dead
- Maximum 9 recovery attempts with 3 alternates (3 + 2 + 2 + 2), fewer if no proxy available
- Alternates cached in-memory in ChannelRepositoryImpl (not persisted to Room DB — no migration needed)
- Cold start with empty cache degrades gracefully to primary-only retry
- When an alternate stream plays successfully for 10 seconds, the app reports the working
streamUrlviaPOST /channels/:id/report-play - Server auto-promotes the working alternate to primary: swaps
channelUrl, demotes old primary to alternates withliveness.status = dead - Promotion is idempotent: multiple devices reporting the same alternate converge to the same state
RecoveringOverlayprogress indicator reflects actual max attempts based on available slots
Image Proxy
- Proxy channel logos and images through the server
- In-memory cache with 24-hour TTL per image
- Cache key based on MD5 hash of source URL
- Automatic cache cleanup every hour
- Returns 1x1 transparent PNG placeholder on fetch errors
- Error responses cached for 5 minutes to prevent repeated failed fetches
- Cache hit/miss indicated via X-Cache response header
- Cache statistics endpoint showing size, entries, and age
- Manual cache clearing
Security — Headers & CORS
- Helmet.js security headers on all responses
- Content Security Policy restricting script, style, image, media, and frame sources
- CORS with configurable allowed origins (default: all)
- Credentials support in CORS
- Frame embedding blocked via CSP
Security — Rate Limiting
- Global API rate limit: 1000 requests per 15 minutes per authenticated user (session or JWT)
- Unauthenticated requests rate-limited by IP address
- Admin sessions fully exempt from API rate limits
- Auth endpoints (login, register): 20 requests per 15 minutes per IP
- Email actions (forgot-password, resend-verification): 5 per hour per IP
- TV pairing mutations: 10 per 5 minutes per IP (prevents PIN brute-force)
- TV pairing status polling: 120 per 10 minutes per IP
- In-memory per-device rate limits for health sync and stream reporting
- Standard
RateLimit-*headers in all API responses - Login page shows specific rate-limit error message when 429 is returned
Security — Request Handling
- Gzip compression on responses
- JSON body parser with 50 MB limit
- URL-encoded body parser with 50 MB limit
- HTTP request logging via Morgan
Security — Passwords
- Bcrypt hashing with 10 salt rounds
- Minimum password length enforcement
- Secure comparison via bcrypt.compare
Health Check
- Server status (ok/error)
- Server uptime in seconds
- MongoDB connection status (connected/disconnected)
- Response timestamp
- Docker health check configured: every 30 seconds, 10-second timeout, 3 retries
Email Verification & Welcome Emails
- Verification email sent to new users after signup with a unique token
- Verification token expires after 24 hours
- HTML email templates with personalization (username, verification link)
- Separate welcome email sent after successful verification
- Dedicated
/verify-emailpage handles token validation and confirmation - MailHog integration in development for email testing
Scheduler System
- Dedicated scheduler microservice running as a separate process
- Three built-in scheduled tasks:
- Channel Liveness Check: probes all cached streams for alive/dead status (default 24h interval)
- EPG Guide Refresh: fetches electronic program guide data (default 6h interval)
- IPTV-org Cache Refresh: updates channel cache from upstream (default 1h interval)
- Database-backed task tracking with full audit trail (status, trigger type, triggered by, start/end times, duration, results)
- Subtask tracking within each run for granular progress reporting
- Atomic concurrency guard via MongoDB unique partial index prevents duplicate running tasks
- Stale run recovery: tasks stuck in "running" for 4+ hours automatically marked as failed
- Manual trigger support from admin UI
- Admin scheduler page showing task list, next run times, last run status
- Run history page with pagination, filtering by task, and detailed run inspection
External Sources (Pluto TV & Samsung TV Plus)
- Two free external streaming sources: Pluto TV (ad-supported) and Samsung TV Plus
- Region/country-based channel browsing with 2-letter country codes
- Pluto TV JWT session management with automatic token caching per region
- Channel liveness checking: individual stream probing with alive/dead/unknown status and response times
- Bulk liveness checks per source/region running in background
- Import selected channels to system database (admin only, up to 10k per import)
- Import selected channels to personal playlist (any user, up to 500, with deduplication)
- User and admin source browsing pages with filtering
- 1-hour cache TTL on external source channels with automatic background refresh
- Liveness stats display showing alive/dead/unknown counts per source/region
EPG (Electronic Program Guide)
- Centralized EPG management fetching XMLTV format data from iptv-epg.org
- Multi-source EPG discovery automatically mapped to system channels
- Concurrent fetching of up to 5 EPG sources in parallel
- Batch upserts inserting programs in 500-item batches
- Startup detection: initial fetch on first run, staleness check (6h default) on subsequent runs
- Admin EPG page showing stats: total programs, channels with EPG, last/next refresh, sources discovered
Next.js Frontend
- Full migration to Next.js 14 App Router with route groups (
(auth),(dashboard)) - Zustand for global state management, React Query for server data fetching
- TypeScript throughout with strict type safety
- Tailwind CSS 3.4 with custom HSL color variables
- Lucide React icon library
- Dark mode support via next-themes
- Separate Dockerfiles for production (standalone output) and development (hot reload)
- API proxy: all
/api/v1/*requests proxied through Next.js to backend - Mobile-responsive sidebar: hidden off-screen on small screens, opens as overlay drawer with backdrop
- Hamburger menu button in header on mobile to toggle sidebar
- Sidebar auto-closes on navigation link tap (mobile)
- Desktop sidebar retains collapsible behavior with expand/collapse toggle
- Reduced content padding on mobile for better space utilization
Quick Pick Wizard
- 6-step guided wizard for discovering and adding channels:
- Choose sources (IPTV-org, Pluto TV, Samsung TV Plus)
- Select country per source
- Select languages
- Select categories
- Review recommendations with filtering
- Confirm selection and add to playlist
- Multi-source support: mix channels from different sources in one flow
- Step indicator showing progress through the wizard
- Available for both users and admins via separate routes
- Channel selection with checkboxes, count display, and bulk actions
Unified Role-Aware Pages
- Channels, External Sources, and Import pages share a single component per page with a
modeprop (admin|user) - Admin mode: full CRUD, M3U import, Test All, bulk delete, server-side pagination, extended column filters (country, language)
- User mode: add from system pool, remove, M3U playlist URL copy, client-side sorting/filtering, Quick Pick link
- External Sources: admin sees liveness stats bar, batch liveness check, and replace-existing option; user sees simplified import to personal list
- Import IPTV: admin has liveness testing, batch check, replace existing, and raw data in detail modal; user has auto-select-all and import to personal list
- Follows the WizardShell pattern established by Quick Pick for role-based component sharing
Stream Player
- Persistent player context maintaining playback across page navigations
- HLS.js integration for HLS/M3U8 stream playback
- Dual playback modes: proxy-only or direct-with-fallback-to-proxy
- Mini player mode: draggable minimized player that persists position
- Full-screen modal mode with ESC to close and body scroll blocking
- Stream swapping: switch between streams while keeping player open
- Error handling with meaningful messages for CORS, manifest, and timeout issues
- Lazy loaded via Suspense for performance
Reusable UI Components
- Generic DataTable component with grid layout, sorting, responsive breakpoints, expandable rows, resizable columns
- ChannelDataTable: specialized table for channels with logo, name, and action buttons
- ColumnFilter: multi-select dropdown with search, select-all/clear buttons
- SelectionToolbar: shows filtered/total counts, selection count, page/all select controls
- useBulkSelection hook: Set-based selection state management (toggle, select/unselect many/all)
- useClientSideTable hook: memoized search, column filtering, sorting, and pagination
- useDebouncedSearch hook: 300ms debounce on search input to prevent excessive API calls
- SearchInput component with built-in debounce and clear button
Docker Deployment
- Node 18 Alpine-based container image
- Non-root user (nodejs, UID 1001) for security
- Build dependencies for native Node modules (python3, make, g++)
- Persistent volumes for APK storage and user uploads
- MongoDB 7.0 service with localhost-only port binding
- MongoDB health check using mongosh ping
- API service depends on healthy MongoDB before starting
- Configurable via environment variables
- Nginx reverse proxy support
- Separate scheduler service container with own entrypoint
- Frontend container with multi-stage build for production (standalone) and dev (hot reload)
- Redis and MailHog services in development compose
- Mongo Express for database inspection in development
- Makefile targets:
make up-prod,make up, log tailing, db management (reset/drop/shell)
Configuration
- All behavior configurable via environment variables
- Server port, environment mode, and database URI
- Super admin credentials and channel list code
- OAuth client IDs and secrets for Google and GitHub
- JWT secrets and token lifetimes
- GitHub repository details for APK update checking
- APK storage path and max file size
- Pairing PIN expiry duration
- Signup rate limit threshold
- CORS allowed origins
- Server info endpoint reporting name, version, status, and enabled features
- Config defaults endpoint for client applications
Error Monitoring (Sentry)
- Sentry SDK integrated into the backend for production error tracking
- Automatic capture of unhandled exceptions and rejected promises
- Sentry source maps uploaded in CI for readable stack traces
- Codecov integration for test coverage reporting in CI pipeline
- Sentry DSN and Codecov token configured via environment variables
Admin User Assigned Channels
- Admin user detail view shows all channels personally assigned to a user
- Displays per-channel stats: alive/dead status, play count, last played timestamp
- Richer table layout with logo, channel name, category, and health indicators
- Admin can manage (add/remove) a user's assigned channels directly from the user detail page
TV Pairing Rate Limiting
- Dedicated lower-threshold rate limiter on the pairing status polling endpoint
- Prevents TV devices from hammering the status endpoint during pairing flow
- Rate limit is separate from the global API rate limiter to avoid blocking other traffic
Channel Ownership & Playlist Privacy
- Channels carry an
ownerId:null= shared admin catalog, a user id = that user's private channel - User M3U/Xtream imports create private channels owned by the importer — never pooled into the shared catalog
- Private playlist URLs (which may embed subscription credentials) are hidden from admins and the public demo
- Admin catalog views, stats, category counts, and the M3U export are scoped to catalog channels only
- Admin catalog re-import/wipe (
replaceExisting/delete-all) affects only catalog channels; users' private channels are preserved channelIduniqueness is per-owner (compound index), so different users can import the same channel independently- Import dedup is scoped per user, so re-importing a playlist won't create duplicates
- "Browse Demo Channels" pairs to a dedicated
DEMO_CHANNEL_LIST_CODE; the admin/TV channel sync is bounded byTV_CHANNELS_MAX - One-time migration backfills ownership with conservative attribution and guarantees no user loses channels
Database Growth Prevention & Serving Performance
- EPG ingest bounded to
EPG_MAX_LOOKAHEAD_HOURS(default 48h) ahead — upstream guides carrying ~6 days no longer bloat storage - Noisy audit actions (
test_channel*,check_liveness*) are no longer persisted; audit retention tightened to 180 days (TTL) - Scheduled task-run history auto-expires after 30 days (TTL); redundant indexes dropped from channels/iptv-org/audit collections
- Optional scheduler step prunes stale dead cache streams after each liveness sweep (
DEAD_STREAM_PRUNE_ENABLED,DEAD_STREAM_PRUNE_DAYS) - Migration
npm run migrate:sync-indexesreconciles live indexes with the schemas on deploy (dry-run by default,--committo apply)
Import Hygiene: Categorization, Clubbing & Dedup
- M3U imports without
group-titleresolve a category from the iptv-org cache (by tvg-id, then name), then from name patterns (seriesS01 E09, movies(2021), genre keywords,DE:-style country prefixes) before falling back toUncategorized - Multiple streams sharing a tvg-id are clubbed into one channel with
alternateStreams(capped at 50) instead of duplicate rows - Admin catalog imports skip URLs already present in the catalog, so re-imports no longer create duplicate channels
- EXTINF titles are extracted after the attribute list (not the first comma), so attribute values containing commas (Cloudinary logo URLs, user-agent strings) no longer corrupt channel names
Channel Serving: Caching, Payloads & Limits
- Shared catalog responses (
/channels,/channels/grouped,/playlist.m3u) and rendered EPG (XMLTV + JSON) are Redis-cached with invalidation on catalog mutations - Channel list endpoints return a whitelisted field set verified against what the TV app and web frontend actually read (~40% smaller payloads)
- M3U playlist generation streams via a lean cursor instead of hydrating the full catalog in memory
- Auth middleware fetches only the user fields it needs; every channel list response is bounded by
TV_CHANNELS_MAX - Per-user channel lists are capped at
USER_CHANNELS_MAX(default 5000) additions; admins can setallCatalogon a user to serve them the whole shared catalog instead