mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
feat(bs): server selector
This commit is contained in:
@@ -3,12 +3,25 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChannelSelect } from '@/components/app/ChannelSelect/ChannelSelect';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { useChannelStreamKey } from '@/lib/hooks/useChannelStreamKey';
|
||||
import { useOwnedChannels } from '@/lib/hooks/useUserList';
|
||||
import { useScreensharePublisher } from '@/lib/hooks/useScreensharePublisher';
|
||||
import { getMediamtxClientRegionOptions } from '@/lib/utils/mediamtx/client';
|
||||
import type { MediaMTXRegion } from '@/lib/utils/mediamtx/regions';
|
||||
|
||||
export default function Page() {
|
||||
const serverOptions = getMediamtxClientRegionOptions();
|
||||
const [selectedChannel, setSelectedChannel] = useState('');
|
||||
const [selectedRegion, setSelectedRegion] = useState<MediaMTXRegion>(
|
||||
serverOptions[0]?.value ?? 'hq'
|
||||
);
|
||||
const { channels, isLoading: isLoadingChannels } = useOwnedChannels();
|
||||
const ownedChannels = channels.map(({ channel }) => channel);
|
||||
const {
|
||||
@@ -28,10 +41,12 @@ export default function Page() {
|
||||
stopPublishing,
|
||||
} = useScreensharePublisher({
|
||||
channelName: selectedChannel,
|
||||
region: selectedRegion,
|
||||
streamKey,
|
||||
});
|
||||
|
||||
const hasChannels = ownedChannels.length > 0;
|
||||
const hasServerOptions = serverOptions.length > 0;
|
||||
const canStartPublishing =
|
||||
!isSessionActive && Boolean(selectedChannel) && Boolean(streamKey) && !isLoadingStreamKey;
|
||||
const channelPlaceholder = isLoadingChannels ? 'Loading channels...' : 'Select a channel';
|
||||
@@ -53,7 +68,7 @@ export default function Page() {
|
||||
broadcast.
|
||||
</p>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-[220px_1fr]">
|
||||
<div className="grid gap-4 md:grid-cols-[220px_220px]">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">Channel</p>
|
||||
<ChannelSelect
|
||||
@@ -65,6 +80,26 @@ export default function Page() {
|
||||
triggerClassName="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">Server</p>
|
||||
<Select
|
||||
value={selectedRegion}
|
||||
onValueChange={(value) => setSelectedRegion(value as MediaMTXRegion)}
|
||||
disabled={isSessionActive || !hasServerOptions}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Select server" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{serverOptions.map((server) => (
|
||||
<SelectItem key={server.value} value={server.value}>
|
||||
{server.label} {server.emoji}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!hasChannels && !isLoadingChannels ? (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// completely generated by gpt-5.4
|
||||
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { getMediamtxClientEnvs } from '@/lib/utils/mediamtx/client';
|
||||
import type { MediaMTXRegion } from '@/lib/utils/mediamtx/regions';
|
||||
import MediaMTXWebRTCPublisher from '@/lib/utils/mediamtx/webrtc';
|
||||
|
||||
const HLS_COMPATIBLE_VIDEO_CODECS = [
|
||||
@@ -23,6 +23,7 @@ const DISPLAY_MEDIA_OPTIONS: ScreenCaptureOptions = {
|
||||
|
||||
export function useScreensharePublisher({
|
||||
channelName,
|
||||
region,
|
||||
streamKey,
|
||||
}: UseScreensharePublisherOptions) {
|
||||
const previewRef = useRef<HTMLVideoElement>(null);
|
||||
@@ -123,7 +124,7 @@ export function useScreensharePublisher({
|
||||
commitCaptureStream(stream);
|
||||
|
||||
const publisher = new MediaMTXWebRTCPublisher({
|
||||
url: getWhipUrl(channelName),
|
||||
url: getWhipUrl(channelName, region),
|
||||
stream,
|
||||
videoCodec,
|
||||
videoBitrate: 2000,
|
||||
@@ -155,7 +156,7 @@ export function useScreensharePublisher({
|
||||
setPublishState('idle');
|
||||
setError(getErrorMessage(err, 'Failed to start publishing'));
|
||||
}
|
||||
}, [channelName, commitCaptureStream, disposeCurrentSession, streamKey]);
|
||||
}, [channelName, commitCaptureStream, disposeCurrentSession, region, streamKey]);
|
||||
|
||||
const changeSource = useCallback(async () => {
|
||||
const publisher = publisherRef.current;
|
||||
@@ -204,8 +205,10 @@ async function requestCaptureStream() {
|
||||
return navigator.mediaDevices.getDisplayMedia(DISPLAY_MEDIA_OPTIONS as DisplayMediaStreamOptions);
|
||||
}
|
||||
|
||||
function getWhipUrl(channelName: string) {
|
||||
return `http://localhost:8889/${encodeURIComponent(channelName)}/whip`;
|
||||
function getWhipUrl(channelName: string, region: MediaMTXRegion) {
|
||||
const { whip } = getMediamtxClientEnvs(region);
|
||||
|
||||
return `${whip.replace(/\/$/, '')}/${encodeURIComponent(channelName)}/whip`;
|
||||
}
|
||||
|
||||
function stopTracks(stream: MediaStream | null) {
|
||||
@@ -243,6 +246,7 @@ type PublishState = 'idle' | 'connecting' | 'live' | 'switching';
|
||||
|
||||
type UseScreensharePublisherOptions = {
|
||||
channelName: string;
|
||||
region: MediaMTXRegion;
|
||||
streamKey?: string | null;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,15 +4,23 @@ import { getEnv } from '@/lib/env';
|
||||
export interface MediaMTXClientEnvs {
|
||||
publicUrl: string;
|
||||
ingestRoute: string;
|
||||
whip: string;
|
||||
emoji: string;
|
||||
string: string;
|
||||
}
|
||||
|
||||
export interface MediaMTXClientRegionOption {
|
||||
value: MediaMTXRegion;
|
||||
emoji: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function getMediamtxClientEnvs(region: MediaMTXRegion = 'hq'): MediaMTXClientEnvs {
|
||||
const envs: Record<MediaMTXRegion, MediaMTXClientEnvs> = {
|
||||
hq: {
|
||||
publicUrl: getEnv('NEXT_PUBLIC_MEDIAMTX_URL_HQ')!,
|
||||
ingestRoute: getEnv('NEXT_PUBLIC_MEDIAMTX_INGEST_ROUTE_HQ')!,
|
||||
whip: getEnv('NEXT_PUBLIC_MEDIAMTX_WHIP_ROUTE_HQ')!,
|
||||
emoji: '🇺🇸',
|
||||
string: 'HQ Server A',
|
||||
},
|
||||
@@ -27,3 +35,12 @@ export function getMediamtxClientEnvs(region: MediaMTXRegion = 'hq'): MediaMTXCl
|
||||
return regionEnvs;
|
||||
}
|
||||
|
||||
export function getMediamtxClientRegionOptions(): MediaMTXClientRegionOption[] {
|
||||
return [
|
||||
{
|
||||
value: 'hq',
|
||||
emoji: '🇺🇸',
|
||||
label: 'HQ Server A',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user