diff --git a/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx b/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx
index e4f10ea1..5ac6e6d0 100644
--- a/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx
+++ b/skyvern-frontend/src/routes/browserSession/BrowserSession.tsx
@@ -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() {
browser session
+
+ {/* Tab Navigation */}
+
+
+
+
+
+ {/* Tab Content */}
-
+ {activeTab === "stream" && (
+
+ )}
+ {activeTab === "videos" && }
diff --git a/skyvern-frontend/src/routes/browserSession/BrowserSessionVideo.tsx b/skyvern-frontend/src/routes/browserSession/BrowserSessionVideo.tsx
new file mode 100644
index 00000000..da70e327
--- /dev/null
+++ b/skyvern-frontend/src/routes/browserSession/BrowserSessionVideo.tsx
@@ -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 (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+ Error loading videos: {error.message}
+
+
+ );
+ }
+
+ if (!recordings || recordings.length === 0) {
+ return (
+
+
+
+ No recordings available
+
+
+ Video recordings will appear here when the browser session is active
+ and recording
+
+
+
+ );
+ }
+
+ return (
+
+
+
Browser Session Videos
+
+ Recorded videos from this browser session
+
+
+
+
+ {recordings.map((recording: Recording, index: number) => (
+
+
+
+ {recording.filename || `Recording ${index + 1}`}
+ {recording.modified_at && (
+
+ ({new Date(recording.modified_at).toLocaleString()})
+
+ )}
+
+
+
+ {recording.url ? (
+
+
+
+
+ ) : (
+
+ Video URL not available - video may still be processing
+
+ )}
+
+ {recording.checksum && (
+
+ Checksum: {recording.checksum}
+
+ )}
+
+ ))}
+
+
+ );
+}
+
+export { BrowserSessionVideo };