mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-03-21 22:56:47 +00:00
feat: download files, show cloud video, solf deletion with no reprocessing (#920)
* fix: move upd ports out of MacOS internal Range * feat: download files, show cloud video, solf deletion with no reprocessing
This commit is contained in:
committed by
GitHub
parent
cb1beae90d
commit
a76f114378
@@ -5,10 +5,11 @@ import useWaveform from "../useWaveform";
|
||||
import useMp3 from "../useMp3";
|
||||
import { TopicList } from "./_components/TopicList";
|
||||
import { Topic } from "../webSocketTypes";
|
||||
import React, { useEffect, useState, use } from "react";
|
||||
import React, { useEffect, useState, useCallback, use } from "react";
|
||||
import FinalSummary from "./finalSummary";
|
||||
import TranscriptTitle from "../transcriptTitle";
|
||||
import Player from "../player";
|
||||
import VideoPlayer from "../videoPlayer";
|
||||
import { useWebSockets } from "../useWebSockets";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { parseNonEmptyString } from "../../../lib/utils";
|
||||
@@ -56,6 +57,21 @@ export default function TranscriptDetails(details: TranscriptDetails) {
|
||||
const [finalSummaryElement, setFinalSummaryElement] =
|
||||
useState<HTMLDivElement | null>(null);
|
||||
|
||||
const hasCloudVideo = !!transcript.data?.has_cloud_video;
|
||||
const [videoExpanded, setVideoExpanded] = useState(false);
|
||||
const [videoNewBadge, setVideoNewBadge] = useState(() => {
|
||||
if (typeof window === "undefined") return true;
|
||||
return !localStorage.getItem(`video-seen-${transcriptId}`);
|
||||
});
|
||||
|
||||
const handleVideoToggle = useCallback(() => {
|
||||
setVideoExpanded((prev) => !prev);
|
||||
if (videoNewBadge) {
|
||||
setVideoNewBadge(false);
|
||||
localStorage.setItem(`video-seen-${transcriptId}`, "1");
|
||||
}
|
||||
}, [videoNewBadge, transcriptId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!waiting || !transcript.data) return;
|
||||
|
||||
@@ -156,8 +172,14 @@ export default function TranscriptDetails(details: TranscriptDetails) {
|
||||
<Grid
|
||||
templateColumns={{ base: "minmax(0, 1fr)", md: "repeat(2, 1fr)" }}
|
||||
templateRows={{
|
||||
base: "auto minmax(0, 1fr) minmax(0, 1fr)",
|
||||
md: "auto minmax(0, 1fr)",
|
||||
base:
|
||||
hasCloudVideo && videoExpanded
|
||||
? "auto auto minmax(0, 1fr) minmax(0, 1fr)"
|
||||
: "auto minmax(0, 1fr) minmax(0, 1fr)",
|
||||
md:
|
||||
hasCloudVideo && videoExpanded
|
||||
? "auto auto minmax(0, 1fr)"
|
||||
: "auto minmax(0, 1fr)",
|
||||
}}
|
||||
gap={4}
|
||||
gridRowGap={2}
|
||||
@@ -180,6 +202,10 @@ export default function TranscriptDetails(details: TranscriptDetails) {
|
||||
transcript={transcript.data || null}
|
||||
topics={topics.topics}
|
||||
finalSummaryElement={finalSummaryElement}
|
||||
hasCloudVideo={hasCloudVideo}
|
||||
videoExpanded={videoExpanded}
|
||||
onVideoToggle={handleVideoToggle}
|
||||
videoNewBadge={videoNewBadge}
|
||||
/>
|
||||
</Flex>
|
||||
{mp3.audioDeleted && (
|
||||
@@ -190,6 +216,16 @@ export default function TranscriptDetails(details: TranscriptDetails) {
|
||||
)}
|
||||
</Flex>
|
||||
</GridItem>
|
||||
{hasCloudVideo && videoExpanded && (
|
||||
<GridItem colSpan={{ base: 1, md: 2 }}>
|
||||
<VideoPlayer
|
||||
transcriptId={transcriptId}
|
||||
duration={transcript.data?.cloud_video_duration ?? null}
|
||||
expanded={videoExpanded}
|
||||
onClose={() => setVideoExpanded(false)}
|
||||
/>
|
||||
</GridItem>
|
||||
)}
|
||||
<TopicList
|
||||
topics={topics.topics || []}
|
||||
useActiveTopic={useActiveTopic}
|
||||
|
||||
@@ -10,11 +10,22 @@ import {
|
||||
useTranscriptUpdate,
|
||||
useTranscriptParticipants,
|
||||
} from "../../lib/apiHooks";
|
||||
import { Heading, IconButton, Input, Flex, Spacer } from "@chakra-ui/react";
|
||||
import { LuPen, LuCopy, LuCheck } from "react-icons/lu";
|
||||
import {
|
||||
Heading,
|
||||
IconButton,
|
||||
Input,
|
||||
Flex,
|
||||
Spacer,
|
||||
Spinner,
|
||||
Box,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { LuPen, LuCopy, LuCheck, LuDownload, LuVideo } from "react-icons/lu";
|
||||
import ShareAndPrivacy from "./shareAndPrivacy";
|
||||
import { buildTranscriptWithTopics } from "./buildTranscriptWithTopics";
|
||||
import { toaster } from "../../components/ui/toaster";
|
||||
import { useAuth } from "../../lib/AuthProvider";
|
||||
import { API_URL } from "../../lib/apiClient";
|
||||
|
||||
type TranscriptTitle = {
|
||||
title: string;
|
||||
@@ -25,13 +36,51 @@ type TranscriptTitle = {
|
||||
transcript: GetTranscriptWithParticipants | null;
|
||||
topics: GetTranscriptTopic[] | null;
|
||||
finalSummaryElement: HTMLDivElement | null;
|
||||
|
||||
// video props
|
||||
hasCloudVideo?: boolean;
|
||||
videoExpanded?: boolean;
|
||||
onVideoToggle?: () => void;
|
||||
videoNewBadge?: boolean;
|
||||
};
|
||||
|
||||
const TranscriptTitle = (props: TranscriptTitle) => {
|
||||
const [displayedTitle, setDisplayedTitle] = useState(props.title);
|
||||
const [preEditTitle, setPreEditTitle] = useState(props.title);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const updateTranscriptMutation = useTranscriptUpdate();
|
||||
const auth = useAuth();
|
||||
const accessToken = auth.status === "authenticated" ? auth.accessToken : null;
|
||||
const userId = auth.status === "authenticated" ? auth.user?.id : null;
|
||||
const isOwner = !!(userId && userId === props.transcript?.user_id);
|
||||
|
||||
const handleDownloadZip = async () => {
|
||||
if (!props.transcriptId || downloading) return;
|
||||
setDownloading(true);
|
||||
try {
|
||||
const headers: Record<string, string> = {};
|
||||
if (accessToken) {
|
||||
headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
}
|
||||
const resp = await fetch(
|
||||
`${API_URL}/v1/transcripts/${props.transcriptId}/download/zip`,
|
||||
{ headers },
|
||||
);
|
||||
if (!resp.ok) throw new Error("Download failed");
|
||||
const blob = await resp.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `transcript_${props.transcriptId.split("-")[0]}.zip`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (err) {
|
||||
console.error("Failed to download zip:", err);
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
const participantsQuery = useTranscriptParticipants(
|
||||
props.transcript?.id ? parseMaybeNonEmptyString(props.transcript.id) : null,
|
||||
);
|
||||
@@ -173,6 +222,51 @@ const TranscriptTitle = (props: TranscriptTitle) => {
|
||||
>
|
||||
<LuCopy />
|
||||
</IconButton>
|
||||
{isOwner && (
|
||||
<IconButton
|
||||
aria-label="Download Transcript Zip"
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
onClick={handleDownloadZip}
|
||||
disabled={downloading}
|
||||
>
|
||||
{downloading ? <Spinner size="sm" /> : <LuDownload />}
|
||||
</IconButton>
|
||||
)}
|
||||
{props.hasCloudVideo && props.onVideoToggle && (
|
||||
<Box position="relative" display="inline-flex">
|
||||
<IconButton
|
||||
aria-label={
|
||||
props.videoExpanded
|
||||
? "Hide cloud recording"
|
||||
: "Show cloud recording"
|
||||
}
|
||||
size="sm"
|
||||
variant={props.videoExpanded ? "solid" : "subtle"}
|
||||
colorPalette={props.videoExpanded ? "blue" : undefined}
|
||||
onClick={props.onVideoToggle}
|
||||
>
|
||||
<LuVideo />
|
||||
</IconButton>
|
||||
{props.videoNewBadge && (
|
||||
<Text
|
||||
position="absolute"
|
||||
top="-1"
|
||||
right="-1"
|
||||
fontSize="2xs"
|
||||
fontWeight="bold"
|
||||
color="white"
|
||||
bg="red.500"
|
||||
px={1}
|
||||
borderRadius="sm"
|
||||
lineHeight="tall"
|
||||
pointerEvents="none"
|
||||
>
|
||||
new
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
<ShareAndPrivacy
|
||||
finalSummaryElement={props.finalSummaryElement}
|
||||
transcript={props.transcript}
|
||||
|
||||
153
www/app/(app)/transcripts/videoPlayer.tsx
Normal file
153
www/app/(app)/transcripts/videoPlayer.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box, Flex, Skeleton, Text } from "@chakra-ui/react";
|
||||
import { LuVideo, LuX } from "react-icons/lu";
|
||||
import { useAuth } from "../../lib/AuthProvider";
|
||||
import { API_URL } from "../../lib/apiClient";
|
||||
|
||||
type VideoPlayerProps = {
|
||||
transcriptId: string;
|
||||
duration: number | null;
|
||||
expanded: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
function formatDuration(seconds: number): string {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
if (h > 0)
|
||||
return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
||||
return `${m}:${String(s).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
export default function VideoPlayer({
|
||||
transcriptId,
|
||||
duration,
|
||||
expanded,
|
||||
onClose,
|
||||
}: VideoPlayerProps) {
|
||||
const [videoUrl, setVideoUrl] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const auth = useAuth();
|
||||
const accessToken = auth.status === "authenticated" ? auth.accessToken : null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!expanded || !transcriptId || videoUrl) return;
|
||||
|
||||
const fetchVideoUrl = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (accessToken) {
|
||||
params.set("token", accessToken);
|
||||
}
|
||||
const url = `${API_URL}/v1/transcripts/${transcriptId}/video/url?${params}`;
|
||||
const headers: Record<string, string> = {};
|
||||
if (accessToken) {
|
||||
headers["Authorization"] = `Bearer ${accessToken}`;
|
||||
}
|
||||
const resp = await fetch(url, { headers });
|
||||
if (!resp.ok) {
|
||||
throw new Error("Failed to load video");
|
||||
}
|
||||
const data = await resp.json();
|
||||
setVideoUrl(data.url);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to load video");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchVideoUrl();
|
||||
}, [expanded, transcriptId, accessToken, videoUrl]);
|
||||
|
||||
if (!expanded) return null;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box
|
||||
borderRadius="md"
|
||||
overflow="hidden"
|
||||
bg="gray.900"
|
||||
w="fit-content"
|
||||
maxW="100%"
|
||||
>
|
||||
<Skeleton h="200px" w="400px" maxW="100%" />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !videoUrl) {
|
||||
return (
|
||||
<Box
|
||||
p={3}
|
||||
bg="red.100"
|
||||
borderRadius="md"
|
||||
role="alert"
|
||||
w="fit-content"
|
||||
maxW="100%"
|
||||
>
|
||||
<Text fontSize="sm">Failed to load video recording</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box borderRadius="md" bg="black" w="fit-content" maxW="100%" mx="auto">
|
||||
{/* Header bar with title and close button */}
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
px={3}
|
||||
py={1.5}
|
||||
bg="gray.800"
|
||||
borderTopRadius="md"
|
||||
gap={4}
|
||||
>
|
||||
<Flex align="center" gap={2}>
|
||||
<LuVideo size={14} color="white" />
|
||||
<Text fontSize="xs" fontWeight="medium" color="white">
|
||||
Cloud recording
|
||||
</Text>
|
||||
{duration != null && (
|
||||
<Text fontSize="xs" color="gray.400">
|
||||
{formatDuration(duration)}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
borderRadius="full"
|
||||
p={1}
|
||||
cursor="pointer"
|
||||
onClick={onClose}
|
||||
_hover={{ bg: "whiteAlpha.300" }}
|
||||
transition="background 0.15s"
|
||||
>
|
||||
<LuX size={14} color="white" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
{/* Video element with visible controls */}
|
||||
<video
|
||||
src={videoUrl}
|
||||
controls
|
||||
autoPlay
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
maxWidth: "640px",
|
||||
maxHeight: "45vh",
|
||||
minHeight: "180px",
|
||||
objectFit: "contain",
|
||||
background: "black",
|
||||
borderBottomLeftRadius: "0.375rem",
|
||||
borderBottomRightRadius: "0.375rem",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
248
www/app/reflector-api.d.ts
vendored
248
www/app/reflector-api.d.ts
vendored
@@ -90,8 +90,6 @@ export interface paths {
|
||||
*
|
||||
* Both cloud and raw-tracks are started via REST API to bypass enable_recording limitation of allowing only 1 recording at a time.
|
||||
* Uses different instanceIds for cloud vs raw-tracks (same won't work)
|
||||
*
|
||||
* Note: No authentication required - anonymous users supported. TODO this is a DOS vector
|
||||
*/
|
||||
post: operations["v1_start_recording"];
|
||||
delete?: never;
|
||||
@@ -561,6 +559,40 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/transcripts/{transcript_id}/download/zip": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Transcript Download Zip */
|
||||
get: operations["v1_transcript_download_zip"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/transcripts/{transcript_id}/video/url": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Transcript Get Video Url */
|
||||
get: operations["v1_transcript_get_video_url"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/transcripts/{transcript_id}/events": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -785,6 +817,23 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/auth/login": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/** Login */
|
||||
post: operations["v1_login"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
@@ -816,10 +865,7 @@ export interface components {
|
||||
};
|
||||
/** Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post */
|
||||
Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post: {
|
||||
/**
|
||||
* Chunk
|
||||
* Format: binary
|
||||
*/
|
||||
/** Chunk */
|
||||
chunk: string;
|
||||
};
|
||||
/** CalendarEventResponse */
|
||||
@@ -1034,6 +1080,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
};
|
||||
/** GetTranscriptSegmentTopic */
|
||||
GetTranscriptSegmentTopic: {
|
||||
@@ -1182,6 +1235,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
/** Participants */
|
||||
participants:
|
||||
| components["schemas"]["TranscriptParticipantWithEmail"][]
|
||||
@@ -1247,6 +1307,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
/** Participants */
|
||||
participants:
|
||||
| components["schemas"]["TranscriptParticipantWithEmail"][]
|
||||
@@ -1313,6 +1380,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
/** Participants */
|
||||
participants:
|
||||
| components["schemas"]["TranscriptParticipantWithEmail"][]
|
||||
@@ -1386,6 +1460,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
/** Participants */
|
||||
participants:
|
||||
| components["schemas"]["TranscriptParticipantWithEmail"][]
|
||||
@@ -1461,6 +1542,13 @@ export interface components {
|
||||
audio_deleted?: boolean | null;
|
||||
/** Change Seq */
|
||||
change_seq?: number | null;
|
||||
/**
|
||||
* Has Cloud Video
|
||||
* @default false
|
||||
*/
|
||||
has_cloud_video: boolean;
|
||||
/** Cloud Video Duration */
|
||||
cloud_video_duration?: number | null;
|
||||
/** Participants */
|
||||
participants:
|
||||
| components["schemas"]["TranscriptParticipantWithEmail"][]
|
||||
@@ -1532,6 +1620,25 @@ export interface components {
|
||||
/** Reason */
|
||||
reason?: string | null;
|
||||
};
|
||||
/** LoginRequest */
|
||||
LoginRequest: {
|
||||
/** Email */
|
||||
email: string;
|
||||
/** Password */
|
||||
password: string;
|
||||
};
|
||||
/** LoginResponse */
|
||||
LoginResponse: {
|
||||
/** Access Token */
|
||||
access_token: string;
|
||||
/**
|
||||
* Token Type
|
||||
* @default bearer
|
||||
*/
|
||||
token_type: string;
|
||||
/** Expires In */
|
||||
expires_in: number;
|
||||
};
|
||||
/** Meeting */
|
||||
Meeting: {
|
||||
/** Id */
|
||||
@@ -1619,26 +1726,26 @@ export interface components {
|
||||
/** Items */
|
||||
items: components["schemas"]["GetTranscriptMinimal"][];
|
||||
/** Total */
|
||||
total?: number | null;
|
||||
total: number;
|
||||
/** Page */
|
||||
page: number | null;
|
||||
page: number;
|
||||
/** Size */
|
||||
size: number | null;
|
||||
size: number;
|
||||
/** Pages */
|
||||
pages?: number | null;
|
||||
pages: number;
|
||||
};
|
||||
/** Page[RoomDetails] */
|
||||
Page_RoomDetails_: {
|
||||
/** Items */
|
||||
items: components["schemas"]["RoomDetails"][];
|
||||
/** Total */
|
||||
total?: number | null;
|
||||
total: number;
|
||||
/** Page */
|
||||
page: number | null;
|
||||
page: number;
|
||||
/** Size */
|
||||
size: number | null;
|
||||
size: number;
|
||||
/** Pages */
|
||||
pages?: number | null;
|
||||
pages: number;
|
||||
};
|
||||
/** Participant */
|
||||
Participant: {
|
||||
@@ -2269,6 +2376,22 @@ export interface components {
|
||||
msg: string;
|
||||
/** Error Type */
|
||||
type: string;
|
||||
/** Input */
|
||||
input?: unknown;
|
||||
/** Context */
|
||||
ctx?: Record<string, never>;
|
||||
};
|
||||
/** VideoUrlResponse */
|
||||
VideoUrlResponse: {
|
||||
/** Url */
|
||||
url: string;
|
||||
/** Duration */
|
||||
duration?: number | null;
|
||||
/**
|
||||
* Content Type
|
||||
* @default video/mp4
|
||||
*/
|
||||
content_type: string;
|
||||
};
|
||||
/** WebhookTestResult */
|
||||
WebhookTestResult: {
|
||||
@@ -3682,6 +3805,70 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_transcript_download_zip: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
transcript_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_transcript_get_video_url: {
|
||||
parameters: {
|
||||
query?: {
|
||||
token?: string | null;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
transcript_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["VideoUrlResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_transcript_get_websocket_events: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -4021,4 +4208,37 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_login: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["LoginRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["LoginResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user