add custom headers to dev video test view

This commit is contained in:
Pas 2025-11-15 12:26:44 -07:00
parent f1065e9602
commit dd2422f852

View file

@ -1,14 +1,20 @@
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { prepareStream } from "@/backend/extension/streams";
import { Button } from "@/components/buttons/Button";
import { Toggle } from "@/components/buttons/Toggle";
import { Dropdown } from "@/components/form/Dropdown";
import { Icon, Icons } from "@/components/Icon";
import { usePlayer } from "@/components/player/hooks/usePlayer";
import { Title } from "@/components/text/Title";
import { AuthInputBox } from "@/components/text-inputs/AuthInputBox";
import { TextInputControl } from "@/components/text-inputs/TextInputControl";
import { Divider } from "@/components/utils/Divider";
import { PlaybackErrorPart } from "@/pages/parts/player/PlaybackErrorPart";
import { PlayerPart } from "@/pages/parts/player/PlayerPart";
import { PlayerMeta, playerStatus } from "@/stores/player/slices/source";
import { SourceSliceSource, StreamType } from "@/stores/player/utils/qualities";
import { type ExtensionStatus, getExtensionState } from "@/utils/extension";
const testMeta: PlayerMeta = {
releaseYear: 2010,
@ -32,14 +38,64 @@ export default function VideoTesterView() {
const { status, playMedia, setMeta } = usePlayer();
const [selected, setSelected] = useState("mp4");
const [inputSource, setInputSource] = useState("");
const [extensionState, setExtensionState] =
useState<ExtensionStatus>("unknown");
const [headersEnabled, setHeadersEnabled] = useState(false);
const [headers, setHeaders] = useState<Array<{ key: string; value: string }>>(
[{ key: "", value: "" }],
);
// Check extension state on mount
useEffect(() => {
getExtensionState().then(setExtensionState);
}, []);
// Header management functions
const addHeader = useCallback(() => {
setHeaders((prev) => [...prev, { key: "", value: "" }]);
}, []);
const updateHeader = useCallback(
(index: number, field: "key" | "value", value: string) => {
setHeaders((prev) =>
prev.map((header, i) =>
i === index ? { ...header, [field]: value } : header,
),
);
},
[],
);
const removeHeader = useCallback((index: number) => {
setHeaders((prev) => prev.filter((_, i) => i !== index));
}, []);
const toggleHeaders = useCallback(() => {
const newEnabled = !headersEnabled;
setHeadersEnabled(newEnabled);
if (!newEnabled) {
setHeaders([{ key: "", value: "" }]);
}
}, [headersEnabled]);
const start = useCallback(
(url: string, type: StreamType) => {
async (url: string, type: StreamType) => {
// Build headers object from enabled headers
const headersObj: Record<string, string> = {};
if (headersEnabled) {
headers.forEach(({ key, value }) => {
if (key.trim() && value.trim()) {
headersObj[key.trim()] = value.trim();
}
});
}
let source: SourceSliceSource;
if (type === "hls") {
source = {
type: "hls",
url,
...(Object.keys(headersObj).length > 0 && { headers: headersObj }),
};
} else if (type === "mp4") {
source = {
@ -50,12 +106,38 @@ export default function VideoTesterView() {
url,
},
},
...(Object.keys(headersObj).length > 0 && { headers: headersObj }),
};
} else throw new Error("Invalid type");
// Prepare stream headers if extension is active and headers are present
if (extensionState === "success" && Object.keys(headersObj).length > 0) {
// Create a mock Stream object for prepareStream
const mockStream: any = {
type: type === "hls" ? "hls" : "file",
...(type === "hls"
? { playlist: url }
: {
qualities: {
unknown: {
type: "mp4",
url,
},
},
}),
headers: headersObj,
};
try {
await prepareStream(mockStream);
} catch (error) {
console.warn("Failed to prepare stream headers:", error);
}
}
setMeta(testMeta);
playMedia(source, [], null);
},
[playMedia, setMeta],
[playMedia, setMeta, headersEnabled, headers, extensionState],
);
return (
@ -85,13 +167,73 @@ export default function VideoTesterView() {
setSelectedItem={(item) => setSelected(item.id)}
/>
</div>
{extensionState === "success" && (
<div className="flex-1 mb-4">
<div className="flex justify-between items-center gap-4">
<div className="my-3">
<p className="text-white font-bold">Headers</p>
</div>
<div>
<Toggle
onClick={toggleHeaders}
enabled={headersEnabled}
/>
</div>
</div>
{headersEnabled && (
<>
<Divider marginClass="my-6 px-8 box-content -mx-8" />
<div className="my-6 space-y-2">
{headers.length === 0 ? (
<p>No headers configured.</p>
) : (
headers.map((header, index) => (
<div
// eslint-disable-next-line react/no-array-index-key
key={index}
className="grid grid-cols-[1fr,1fr,auto] items-center gap-2"
>
<AuthInputBox
value={header.key}
onChange={(value) =>
updateHeader(index, "key", value)
}
placeholder="Key"
/>
<AuthInputBox
value={header.value}
onChange={(value) =>
updateHeader(index, "value", value)
}
placeholder="Value"
/>
<button
type="button"
onClick={() => removeHeader(index)}
className="h-full scale-90 hover:scale-100 rounded-full aspect-square bg-authentication-inputBg hover:bg-authentication-inputBgHover flex justify-center items-center transition-transform duration-200 hover:text-white cursor-pointer"
>
<Icon className="text-xl" icon={Icons.X} />
</button>
</div>
))
)}
</div>
<Button theme="purple" onClick={addHeader}>
Add header
</Button>
</>
)}
</div>
)}
<Button
onClick={() => start(inputSource, selected as StreamType)}
>
Start stream
</Button>
</div>
<div className="flex-1">
<Title>Preset tests</Title>
<div className="grid grid-cols-[1fr,1fr] gap-2">