mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
feat(bs): production prepping
This commit is contained in:
@@ -28,5 +28,6 @@ NEXT_PUBLIC_MEDIAMTX_WHIP_ROUTE_HQ=http://localhost:8889
|
||||
# MEDIAMTX_API_ASIA=http://localhost:9999
|
||||
# NEXT_PUBLIC_MEDIAMTX_INGEST_ROUTE_ASIA=localhost:8990
|
||||
|
||||
# idt you should change this
|
||||
MEDIAMTX_PUBLISH_KEY=rjq1xdpCPA4qyt3jge
|
||||
# generate with `openssl rand -base64 20`
|
||||
MEDIAMTX_PUBLISH_KEY=
|
||||
MEDIAMTX_API_KEY=
|
||||
|
||||
@@ -80,6 +80,13 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
return finish('authorized', 200, 'authorized_read');
|
||||
}
|
||||
if (parsedAction === 'api') {
|
||||
if (password === process.env.MEDIAMTX_API_KEY) {
|
||||
return finish('authorized api', 200, 'authorized_api');
|
||||
}
|
||||
|
||||
return finish('unauthorized api', 401, 'unauthorized_api');
|
||||
}
|
||||
|
||||
return finish('uhh', 401, 'unauthorized');
|
||||
}
|
||||
|
||||
@@ -259,7 +259,11 @@ export default function Page() {
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{serverOptions.map((server) => (
|
||||
<SelectItem key={server.value} value={server.value}>
|
||||
<SelectItem
|
||||
key={server.value}
|
||||
value={server.value}
|
||||
disabled={!server.whipEnabled}
|
||||
>
|
||||
{server.label} {server.emoji}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
@@ -15,7 +15,18 @@ import { logout } from '@/lib/auth/actions';
|
||||
import { useSession } from '@/lib/providers/SessionProvider';
|
||||
import Link from 'next/link';
|
||||
import { ThemeSwitcher } from '../ThemeSwitcher/ThemeSwitcher';
|
||||
import { IdCard, Shield, Settings, Users, PenSquare, LogOut, Code, Github, Heart } from 'lucide-react';
|
||||
import {
|
||||
IdCard,
|
||||
Shield,
|
||||
Settings,
|
||||
Users,
|
||||
PenSquare,
|
||||
LogOut,
|
||||
Code,
|
||||
Github,
|
||||
Heart,
|
||||
Radio,
|
||||
} from 'lucide-react';
|
||||
import { SidebarTrigger } from '@/components/ui/sidebar';
|
||||
import Image from 'next/image';
|
||||
import Logo from '@/lib/assets/logo.webp';
|
||||
@@ -52,6 +63,16 @@ export default function Navbar(props: Props) {
|
||||
|
||||
{/* Right Side Items */}
|
||||
<div className="flex items-center gap-1 md:gap-3 shrink-0">
|
||||
{user && (
|
||||
<Link href="/stream">
|
||||
<Button variant="outline" size="sm" className="gap-1 md:gap-2 text-xs md:text-sm">
|
||||
<Radio className="w-3 h-3 md:w-4 md:h-4" />
|
||||
<span className="hidden sm:inline">Go live</span>
|
||||
<span className="sm:hidden">Live</span>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{props.editLivestream && <div className="hidden sm:block">{props.editLivestream}</div>}
|
||||
|
||||
{user ? (
|
||||
|
||||
@@ -90,7 +90,15 @@ export async function syncStream() {
|
||||
|
||||
for (const r of regions) {
|
||||
const region = MEDIAMTX_SERVER_REGIONS[r];
|
||||
const response = await fetch(`${region.apiUrl}/v3/paths/list?itemsPerPage=1000`);
|
||||
if (!region.apiAuthHeader) {
|
||||
throw new Error('MEDIAMTX_API_KEY is required when querying the MediaMTX API');
|
||||
}
|
||||
|
||||
const response = await fetch(`${region.apiUrl}/v3/paths/list?itemsPerPage=1000`, {
|
||||
headers: {
|
||||
Authorization: region.apiAuthHeader,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
recordStreamSyncScrape(r, 'error');
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface MediaMTXClientEnvs {
|
||||
publicUrl: string;
|
||||
ingestRoute: string;
|
||||
whip: string;
|
||||
whipEnabled: boolean;
|
||||
emoji: string;
|
||||
string: string;
|
||||
}
|
||||
@@ -13,6 +14,7 @@ export interface MediaMTXClientRegionOption {
|
||||
value: MediaMTXRegion;
|
||||
emoji: string;
|
||||
label: string;
|
||||
whipEnabled: boolean;
|
||||
}
|
||||
|
||||
export function getMediamtxClientEnvs(region: MediaMTXRegion = 'hq'): MediaMTXClientEnvs {
|
||||
@@ -21,9 +23,18 @@ export function getMediamtxClientEnvs(region: MediaMTXRegion = 'hq'): MediaMTXCl
|
||||
publicUrl: getEnv('NEXT_PUBLIC_MEDIAMTX_URL_HQ')!,
|
||||
ingestRoute: getEnv('NEXT_PUBLIC_MEDIAMTX_INGEST_ROUTE_HQ')!,
|
||||
whip: getEnv('NEXT_PUBLIC_MEDIAMTX_WHIP_ROUTE_HQ')!,
|
||||
whipEnabled: false,
|
||||
emoji: '🇺🇸',
|
||||
string: 'HQ Server A',
|
||||
},
|
||||
ethande: {
|
||||
publicUrl: getEnv('NEXT_PUBLIC_MEDIAMTX_URL_ETHANDE')!,
|
||||
ingestRoute: getEnv('NEXT_PUBLIC_MEDIAMTX_INGEST_ROUTE_ETHANDE')!,
|
||||
whip: getEnv('NEXT_PUBLIC_MEDIAMTX_WHIP_ROUTE_ETHANDE')!,
|
||||
whipEnabled: true,
|
||||
emoji: '🇩🇪',
|
||||
string: 'eth0\'s VPS',
|
||||
},
|
||||
};
|
||||
|
||||
const regionEnvs = envs[region];
|
||||
@@ -41,6 +52,7 @@ export function getMediamtxClientRegionOptions(): MediaMTXClientRegionOption[] {
|
||||
value: 'hq',
|
||||
emoji: '🇺🇸',
|
||||
label: 'HQ Server A',
|
||||
whipEnabled: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type MediaMTXRegion = 'hq';
|
||||
export type MediaMTXRegion = 'hq' | 'ethande';
|
||||
|
||||
@@ -2,11 +2,13 @@ import { MediaMTXRegion } from './regions';
|
||||
|
||||
export interface MediaMTXEnvs {
|
||||
apiUrl: string;
|
||||
apiAuthHeader?: string;
|
||||
}
|
||||
|
||||
export const MEDIAMTX_SERVER_REGIONS: Record<MediaMTXRegion, MediaMTXEnvs> = {
|
||||
hq: {
|
||||
apiUrl: process.env.MEDIAMTX_API_HQ!,
|
||||
apiAuthHeader: getMediamtxApiAuthHeader(),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,3 +21,13 @@ export function getMediamtxEnvs(region: MediaMTXRegion = 'hq'): MediaMTXEnvs {
|
||||
|
||||
return envs;
|
||||
}
|
||||
|
||||
function getMediamtxApiAuthHeader() {
|
||||
const apiKey = process.env.MEDIAMTX_API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return `Basic ${Buffer.from(`hctv-api:${apiKey}`).toString('base64')}`;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,14 @@ hlsPartDuration: 1s
|
||||
hlsSegmentCount: 10
|
||||
|
||||
webrtc: yes
|
||||
webrtcAddress: :8889
|
||||
webrtcLocalUDPAddress: :8189
|
||||
webrtcAdditionalHosts: []
|
||||
|
||||
authMethod: http
|
||||
authHTTPAddress: http://hctv:3000/api/mediamtx/publish
|
||||
authHTTPAddress: https://hackclub.tv/api/mediamtx/publish
|
||||
|
||||
api: yes
|
||||
apiAddress: 0.0.0.0:9997
|
||||
metrics: yes
|
||||
metricsAddress: :9998
|
||||
|
||||
12
docker/mediamtx/mirror/.env.example
Normal file
12
docker/mediamtx/mirror/.env.example
Normal file
@@ -0,0 +1,12 @@
|
||||
ACME_EMAIL=ops@hackclub.tv
|
||||
|
||||
# public hostnames and stuff
|
||||
MEDIAMTX_HLS_HOST=hls.hackclub.tv
|
||||
MEDIAMTX_WEBRTC_HOST=whip.hackclub.tv
|
||||
MEDIAMTX_API_HOST=mmtxapi.hackclub.tv
|
||||
|
||||
# public ip for webrtc stuff
|
||||
MEDIAMTX_WEBRTC_ADDITIONAL_HOSTS=203.0.113.10
|
||||
|
||||
# mediamtx publish route on hctv
|
||||
MEDIAMTX_AUTH_HTTP_ADDRESS=https://hackclub.tv/api/mediamtx/publish
|
||||
63
docker/mediamtx/mirror/docker-compose.yml
Normal file
63
docker/mediamtx/mirror/docker-compose.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.5
|
||||
command:
|
||||
- --providers.docker=true
|
||||
- --providers.docker.exposedbydefault=false
|
||||
- --entrypoints.web.address=:80
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --entrypoints.srt.address=:8890/udp
|
||||
- --entrypoints.webrtc-ice.address=:8189/udp
|
||||
- --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}
|
||||
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
|
||||
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
|
||||
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- 8890:8890/udp
|
||||
- 8189:8189/udp
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- letsencrypt:/letsencrypt
|
||||
restart: unless-stopped
|
||||
|
||||
mediamtx:
|
||||
image: bluenviron/mediamtx:1
|
||||
volumes:
|
||||
- ./mediamtx.yml:/mediamtx.yml:ro
|
||||
environment:
|
||||
MTX_WEBRTCADDITIONALHOSTS: ${MEDIAMTX_WEBRTC_ADDITIONAL_HOSTS}
|
||||
MTX_AUTHHTTPADDRESS: ${MEDIAMTX_AUTH_HTTP_ADDRESS}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
|
||||
- traefik.http.routers.mediamtx-hls.rule=Host(`${MEDIAMTX_HLS_HOST}`)
|
||||
- traefik.http.routers.mediamtx-hls.entrypoints=websecure
|
||||
- traefik.http.routers.mediamtx-hls.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.mediamtx-hls.service=mediamtx-hls
|
||||
- traefik.http.services.mediamtx-hls.loadbalancer.server.port=8888
|
||||
|
||||
- traefik.http.routers.mediamtx-webrtc.rule=Host(`${MEDIAMTX_WEBRTC_HOST}`)
|
||||
- traefik.http.routers.mediamtx-webrtc.entrypoints=websecure
|
||||
- traefik.http.routers.mediamtx-webrtc.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.mediamtx-webrtc.service=mediamtx-webrtc
|
||||
- traefik.http.services.mediamtx-webrtc.loadbalancer.server.port=8889
|
||||
|
||||
- traefik.http.routers.mediamtx-api.rule=Host(`${MEDIAMTX_API_HOST}`)
|
||||
- traefik.http.routers.mediamtx-api.entrypoints=websecure
|
||||
- traefik.http.routers.mediamtx-api.tls.certresolver=letsencrypt
|
||||
- traefik.http.routers.mediamtx-api.service=mediamtx-api
|
||||
- traefik.http.services.mediamtx-api.loadbalancer.server.port=9997
|
||||
|
||||
- traefik.udp.routers.mediamtx-srt.entrypoints=srt
|
||||
- traefik.udp.routers.mediamtx-srt.service=mediamtx-srt
|
||||
- traefik.udp.services.mediamtx-srt.loadbalancer.server.port=8890
|
||||
|
||||
- traefik.udp.routers.mediamtx-webrtc-ice.entrypoints=webrtc-ice
|
||||
- traefik.udp.routers.mediamtx-webrtc-ice.service=mediamtx-webrtc-ice
|
||||
- traefik.udp.services.mediamtx-webrtc-ice.loadbalancer.server.port=8189
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
letsencrypt:
|
||||
26
docker/mediamtx/mirror/mediamtx.yml
Normal file
26
docker/mediamtx/mirror/mediamtx.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
paths:
|
||||
all:
|
||||
source: publisher
|
||||
|
||||
srt: yes
|
||||
srtAddress: :8890
|
||||
|
||||
hls: yes
|
||||
hlsVariant: lowLatency
|
||||
hlsSegmentDuration: 2s
|
||||
hlsPartDuration: 1s
|
||||
hlsSegmentCount: 10
|
||||
|
||||
webrtc: yes
|
||||
webrtcAddress: :8889
|
||||
webrtcLocalUDPAddress: :8189
|
||||
webrtcAdditionalHosts: []
|
||||
|
||||
authMethod: http
|
||||
authHTTPAddress: https://hackclub.tv/api/mediamtx/publish
|
||||
|
||||
api: yes
|
||||
apiAddress: :9997
|
||||
|
||||
metrics: yes
|
||||
metricsAddress: :9998
|
||||
Reference in New Issue
Block a user