/** Where a channel can stream to, and what each platform needs. */ export interface Platform { id: string; label: string; /** Default ingest server; empty means the platform gives you one per stream. */ ingest: string; /** Where to find the stream key. */ keyHelp: string; /** Shown 9:16 on phones: use a portrait channel. */ vertical: boolean; /** How the platform keeps the recording. */ vods: string; /** Brand color, for its badge. */ color: string; } export const PLATFORMS: Platform[] = [ { id: 'twitch', label: 'Twitch', color: '#9146FF', ingest: 'rtmp://ingest.global-contribute.live-video.net/app', keyHelp: 'Twitch Creator Dashboard → Settings → Stream → Primary stream key.', vertical: false, vods: 'Turn on “Store past broadcasts” in Twitch → Settings → Stream. Twitch keeps them 7 days, or 60 for Partners, Turbo and Prime.' }, { id: 'youtube', label: 'YouTube', color: '#FF0033', ingest: 'rtmps://a.rtmps.youtube.com/live2', keyHelp: 'YouTube Studio → Create → Go live → Stream → Stream key. A persistent key works for every stream.', vertical: false, vods: 'YouTube keeps every stream as a video on the channel by itself.' }, { id: 'facebook', label: 'Facebook', color: '#1877F2', ingest: 'rtmps://live-api-s.facebook.com:443/rtmp', keyHelp: 'Facebook Live Producer → Streaming software → “Use a persistent stream key”.', vertical: false, vods: 'Facebook posts the stream as a video when it ends.' }, { id: 'x', label: 'X (Twitter)', color: '#e7e9ea', ingest: '', keyHelp: 'X Media Studio → Producer → Sources: copy the RTMP URL and the stream key.', vertical: false, vods: 'X keeps the broadcast as a post.' }, { id: 'instagram', label: 'Instagram', color: '#E1306C', ingest: '', keyHelp: 'instagram.com → Create → Live video: copy the stream URL and key. Instagram makes a new key for every broadcast.', vertical: true, vods: 'Share the live video to your profile when it ends.' }, { id: 'tiktok', label: 'TikTok', color: '#25F4EE', ingest: '', keyHelp: 'TikTok LIVE Center (or LIVE Studio) → Stream key: copy the server URL and key. Needs an account that can go LIVE.', vertical: true, vods: 'TikTok offers a replay for a while after the stream.' }, { id: 'custom', label: 'Other RTMP server', color: '#a1a1aa', ingest: '', keyHelp: 'Any RTMP or RTMPS server: its URL, and the stream key if it has one.', vertical: false, vods: 'Up to the server.' } ]; export const platform = (id: string) => PLATFORMS.find((each) => each.id === id) ?? PLATFORMS[PLATFORMS.length - 1]; export const QUALITIES: Record< string, { label: string; height: number; fps: number; bitrate: number } > = { '720p30': { label: '720p, 30 fps (3 Mbps)', height: 720, fps: 30, bitrate: 3000 }, '1080p30': { label: '1080p, 30 fps (4.5 Mbps)', height: 1080, fps: 30, bitrate: 4500 }, '1080p60': { label: '1080p, 60 fps (6 Mbps)', height: 1080, fps: 60, bitrate: 6000 } }; /** Output size for a channel: 16:9, or 9:16 for portrait. */ export function outputSize(format: string, quality: string) { const q = QUALITIES[quality] ?? QUALITIES['1080p30']; const long = Math.round((q.height * 16) / 9); const { height: short, ...rest } = q; return format === 'portrait' ? { ...rest, width: short, height: long } : { ...rest, width: long, height: short }; }