Dev scripts, Add support for rendering videos for browser sessions (#3507)
This commit is contained in:
@@ -6,10 +6,12 @@ import { getClient } from "@/api/AxiosClient";
|
||||
import { BrowserStream } from "@/components/BrowserStream";
|
||||
import { LogoMinimized } from "@/components/LogoMinimized";
|
||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||
import { BrowserSessionVideo } from "./BrowserSessionVideo";
|
||||
|
||||
function BrowserSession() {
|
||||
const { browserSessionId } = useParams();
|
||||
const [hasBrowserSession, setHasBrowserSession] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<"stream" | "videos">("stream");
|
||||
|
||||
const credentialGetter = useCredentialGetter();
|
||||
|
||||
@@ -19,12 +21,14 @@ function BrowserSession() {
|
||||
const client = await getClient(credentialGetter, "sans-api-v1");
|
||||
|
||||
try {
|
||||
await client.get(`/browser_sessions/${browserSessionId}`);
|
||||
const response = await client.get(
|
||||
`/browser_sessions/${browserSessionId}`,
|
||||
);
|
||||
setHasBrowserSession(true);
|
||||
return true;
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
setHasBrowserSession(false);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -60,12 +64,41 @@ function BrowserSession() {
|
||||
<div className="text-xl">browser session</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tab Navigation */}
|
||||
<div className="flex w-full border-b">
|
||||
<button
|
||||
className={`border-b-2 px-4 py-2 text-sm font-medium transition-colors ${
|
||||
activeTab === "stream"
|
||||
? "border-blue-500 text-blue-600"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
onClick={() => setActiveTab("stream")}
|
||||
>
|
||||
Live Stream
|
||||
</button>
|
||||
<button
|
||||
className={`border-b-2 px-4 py-2 text-sm font-medium transition-colors ${
|
||||
activeTab === "videos"
|
||||
? "border-blue-500 text-blue-600"
|
||||
: "border-transparent text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
onClick={() => setActiveTab("videos")}
|
||||
>
|
||||
Recordings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="min-h-0 w-full flex-1 rounded-lg border p-4">
|
||||
<BrowserStream
|
||||
browserSessionId={browserSessionId}
|
||||
interactive={false}
|
||||
showControlButtons={true}
|
||||
/>
|
||||
{activeTab === "stream" && (
|
||||
<BrowserStream
|
||||
browserSessionId={browserSessionId}
|
||||
interactive={false}
|
||||
showControlButtons={true}
|
||||
/>
|
||||
)}
|
||||
{activeTab === "videos" && <BrowserSessionVideo />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { getClient } from "@/api/AxiosClient";
|
||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||
|
||||
interface Recording {
|
||||
url: string;
|
||||
checksum: string;
|
||||
filename: string;
|
||||
modified_at: string;
|
||||
}
|
||||
|
||||
function BrowserSessionVideo() {
|
||||
const { browserSessionId } = useParams();
|
||||
const credentialGetter = useCredentialGetter();
|
||||
|
||||
const {
|
||||
data: browserSession,
|
||||
isLoading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ["browserSession", browserSessionId],
|
||||
queryFn: async () => {
|
||||
const client = await getClient(credentialGetter, "sans-api-v1");
|
||||
const response = await client.get(
|
||||
`/browser_sessions/${browserSessionId}`,
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
enabled: !!browserSessionId,
|
||||
});
|
||||
|
||||
// Extract recordings from browser session data
|
||||
const recordings = browserSession?.recordings || [];
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="text-lg">Loading videos...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="text-lg text-red-500">
|
||||
Error loading videos: {error.message}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!recordings || recordings.length === 0) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="mb-2 text-lg text-gray-500">
|
||||
No recordings available
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Video recordings will appear here when the browser session is active
|
||||
and recording
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-4">
|
||||
<div className="mb-4">
|
||||
<h2 className="text-xl font-semibold">Browser Session Videos</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
Recorded videos from this browser session
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4">
|
||||
{recordings.map((recording: Recording, index: number) => (
|
||||
<div
|
||||
key={recording.checksum || index}
|
||||
className="rounded-lg border p-4"
|
||||
>
|
||||
<div className="mb-2">
|
||||
<h3 className="font-medium">
|
||||
{recording.filename || `Recording ${index + 1}`}
|
||||
{recording.modified_at && (
|
||||
<span className="ml-2 text-sm text-gray-500">
|
||||
({new Date(recording.modified_at).toLocaleString()})
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{recording.url ? (
|
||||
<div className="w-full">
|
||||
<video
|
||||
controls
|
||||
className="w-full max-w-4xl rounded-lg"
|
||||
src={recording.url}
|
||||
preload="metadata"
|
||||
>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
<a
|
||||
href={recording.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:text-blue-800"
|
||||
>
|
||||
Download video
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-gray-500">
|
||||
Video URL not available - video may still be processing
|
||||
</div>
|
||||
)}
|
||||
|
||||
{recording.checksum && (
|
||||
<div className="mt-2 text-sm text-gray-600">
|
||||
Checksum: {recording.checksum}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { BrowserSessionVideo };
|
||||
Reference in New Issue
Block a user