468 lines
13 KiB
TypeScript
468 lines
13 KiB
TypeScript
import http from "../http";
|
|
import axios from "axios";
|
|
|
|
const MEETING_UPLOAD_FLOW_TIMEOUT = 600000;
|
|
const MEETING_DETAIL_TIMEOUT = 120000;
|
|
|
|
export type SummaryDetailLevel = "DETAILED" | "STANDARD" | "BRIEF";
|
|
|
|
export interface MeetingCreateConfig {
|
|
offlineEnabled: boolean;
|
|
realtimeEnabled: boolean;
|
|
offlineAudioMaxSizeMb: number;
|
|
}
|
|
|
|
export interface MeetingVO {
|
|
id: number;
|
|
tenantId: number;
|
|
creatorId: number;
|
|
creatorName?: string;
|
|
hostUserId?: number;
|
|
hostName?: string;
|
|
title: string;
|
|
meetingTime: string;
|
|
participants: string;
|
|
participantIds?: number[];
|
|
tags: string;
|
|
audioUrl: string;
|
|
playbackAudioUrl?: string;
|
|
meetingType?: "OFFLINE" | "REALTIME";
|
|
meetingSource?: "WEB" | "ANDROID";
|
|
summaryDetailLevel?: SummaryDetailLevel;
|
|
audioSaveStatus?: "NONE" | "SUCCESS" | "FAILED";
|
|
audioSaveMessage?: string;
|
|
accessPassword?: string;
|
|
lastUserPrompt?: string;
|
|
summaryContent: string;
|
|
analysis?: {
|
|
overview?: string;
|
|
keywords?: string[];
|
|
chapters?: Array<{ time?: string; title?: string; summary?: string }>;
|
|
speakerSummaries?: Array<{ speaker?: string; summary?: string }>;
|
|
keyPoints?: Array<{ title?: string; summary?: string; speaker?: string; time?: string }>;
|
|
todos?: string[];
|
|
};
|
|
latestSummaryAttemptTaskId?: number;
|
|
latestSummaryAttemptStatus?: number;
|
|
latestSummaryAttemptErrorMsg?: string;
|
|
latestChapterAttemptTaskId?: number;
|
|
latestChapterAttemptStatus?: number;
|
|
latestChapterAttemptErrorMsg?: string;
|
|
status: number;
|
|
displayStatus?: number;
|
|
realtimeSessionStatus?: RealtimeMeetingSessionStatus["status"];
|
|
createdAt: string;
|
|
}
|
|
|
|
const AUDIO_MIME_TYPE_BY_EXTENSION: Record<string, string> = {
|
|
mp3: "audio/mpeg",
|
|
wav: "audio/wav",
|
|
m4a: "audio/mp4",
|
|
mp4: "audio/mp4",
|
|
aac: "audio/aac",
|
|
};
|
|
|
|
export const resolveAudioMimeType = (audioUrl?: string) => {
|
|
if (!audioUrl) {
|
|
return undefined;
|
|
}
|
|
const normalizedUrl = audioUrl.split("#")[0]?.split("?")[0] || "";
|
|
const extension = normalizedUrl.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();
|
|
return extension ? AUDIO_MIME_TYPE_BY_EXTENSION[extension] : undefined;
|
|
};
|
|
|
|
export const resolveMeetingPlaybackAudioUrl = (meeting?: Pick<MeetingVO, "audioUrl" | "playbackAudioUrl"> | null) => {
|
|
return meeting?.playbackAudioUrl || meeting?.audioUrl;
|
|
};
|
|
|
|
export interface CreateMeetingCommand {
|
|
id?: number;
|
|
title: string;
|
|
meetingTime: string;
|
|
participants: string;
|
|
tags: string;
|
|
hostUserId?: number;
|
|
hostName?: string;
|
|
audioUrl?: string;
|
|
asrModelId: number;
|
|
summaryModelId?: number;
|
|
promptId: number;
|
|
hotWordGroupId?: number;
|
|
userPrompt?: string;
|
|
summaryDetailLevel?: SummaryDetailLevel;
|
|
useSpkId?: number;
|
|
enableTextRefine?: boolean;
|
|
hotWords?: string[];
|
|
}
|
|
|
|
export type MeetingDTO = CreateMeetingCommand;
|
|
|
|
export interface CreateRealtimeMeetingCommand {
|
|
title: string;
|
|
meetingTime: string;
|
|
participants: string;
|
|
tags: string;
|
|
hostUserId?: number;
|
|
hostName?: string;
|
|
asrModelId: number;
|
|
summaryModelId?: number;
|
|
promptId: number;
|
|
hotWordGroupId?: number;
|
|
userPrompt?: string;
|
|
summaryDetailLevel?: SummaryDetailLevel;
|
|
mode?: string;
|
|
language?: string;
|
|
useSpkId?: number;
|
|
enablePunctuation?: boolean;
|
|
enableItn?: boolean;
|
|
enableTextRefine?: boolean;
|
|
saveAudio?: boolean;
|
|
hotWords?: string[];
|
|
}
|
|
|
|
export interface UpdateMeetingBasicCommand {
|
|
meetingId: number;
|
|
title?: string;
|
|
meetingTime?: string;
|
|
tags?: string;
|
|
accessPassword?: string | null;
|
|
}
|
|
|
|
export type MeetingUpdateBasicDTO = UpdateMeetingBasicCommand;
|
|
|
|
export interface UpdateMeetingSummaryCommand {
|
|
meetingId: number;
|
|
summaryContent: string;
|
|
}
|
|
|
|
export type MeetingUpdateSummaryDTO = UpdateMeetingSummaryCommand;
|
|
|
|
export const getMeetingPage = (params: {
|
|
current: number;
|
|
size: number;
|
|
title?: string;
|
|
viewType?: "all" | "created" | "involved";
|
|
status?: number;
|
|
}) => {
|
|
return http.get<{ code: string; data: { records: MeetingVO[]; total: number }; msg: string }>(
|
|
"/api/biz/meeting/page",
|
|
{ params }
|
|
);
|
|
};
|
|
|
|
export const createMeeting = (data: CreateMeetingCommand) => {
|
|
return http.post<{ code: string; data: MeetingVO; msg: string }>(
|
|
"/api/biz/meeting",
|
|
data,
|
|
{
|
|
timeout: MEETING_UPLOAD_FLOW_TIMEOUT
|
|
}
|
|
);
|
|
};
|
|
|
|
export interface RealtimeTranscriptItemDTO {
|
|
speakerId?: string;
|
|
speakerName?: string;
|
|
content: string;
|
|
startTime?: number;
|
|
endTime?: number;
|
|
}
|
|
|
|
export interface RealtimeSocketSessionVO {
|
|
sessionToken: string;
|
|
path: string;
|
|
expiresInSeconds: number;
|
|
startMessage: Record<string, any>;
|
|
}
|
|
|
|
export interface RealtimeSocketSessionRequest {
|
|
asrModelId: number;
|
|
mode?: string;
|
|
language?: string;
|
|
useSpkId?: number;
|
|
enablePunctuation?: boolean;
|
|
enableItn?: boolean;
|
|
enableTextRefine?: boolean;
|
|
saveAudio?: boolean;
|
|
hotwords?: Array<{ hotword: string; weight: number }>;
|
|
}
|
|
|
|
export interface RealtimeMeetingSessionStatus {
|
|
meetingId: number;
|
|
status: "IDLE" | "ACTIVE" | "PAUSED_EMPTY" | "PAUSED_RESUMABLE" | "COMPLETING" | "COMPLETED";
|
|
hasTranscript: boolean;
|
|
canResume: boolean;
|
|
remainingSeconds: number;
|
|
resumeExpireAt?: number;
|
|
activeConnection: boolean;
|
|
resumeConfig?: RealtimeSocketSessionRequest;
|
|
}
|
|
|
|
export const createRealtimeMeeting = (data: CreateRealtimeMeetingCommand) => {
|
|
return http.post<{ code: string; data: MeetingVO; msg: string }>(
|
|
"/api/biz/meeting/realtime/start",
|
|
data
|
|
);
|
|
};
|
|
|
|
export const appendRealtimeTranscripts = (meetingId: number, data: RealtimeTranscriptItemDTO[]) => {
|
|
return http.post<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/realtime/transcripts`,
|
|
data
|
|
);
|
|
};
|
|
|
|
export const getRealtimeMeetingSessionStatus = (meetingId: number) => {
|
|
return http.get<{ code: string; data: RealtimeMeetingSessionStatus; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/realtime/session-status`
|
|
);
|
|
};
|
|
|
|
export const getRealtimeMeetingSessionStatuses = (meetingIds: number[]) => {
|
|
return http.post<{ code: string; data: Record<number, RealtimeMeetingSessionStatus>; msg: string }>(
|
|
"/api/biz/meeting/realtime/session-status/batch",
|
|
meetingIds
|
|
);
|
|
};
|
|
|
|
export const pauseRealtimeMeeting = (meetingId: number) => {
|
|
return http.post<{ code: string; data: RealtimeMeetingSessionStatus; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/realtime/pause`,
|
|
{}
|
|
);
|
|
};
|
|
|
|
export const openRealtimeMeetingSocketSession = (
|
|
meetingId: number,
|
|
data: RealtimeSocketSessionRequest,
|
|
) => {
|
|
return http.post<{ code: string; data: RealtimeSocketSessionVO; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/realtime/socket-session`,
|
|
data
|
|
);
|
|
};
|
|
|
|
export const completeRealtimeMeeting = (meetingId: number, data?: { audioUrl?: string; overwriteAudio?: boolean }) => {
|
|
return http.post<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/realtime/complete`,
|
|
data || {}
|
|
);
|
|
};
|
|
|
|
export const deleteMeeting = (id: number) => {
|
|
return http.delete<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${id}`
|
|
);
|
|
};
|
|
|
|
export interface MeetingTranscriptVO {
|
|
id: number;
|
|
speakerId: string;
|
|
speakerName: string;
|
|
speakerLabel: string;
|
|
content: string;
|
|
startTime: number;
|
|
endTime: number;
|
|
}
|
|
|
|
export interface MeetingPreviewAccessVO {
|
|
passwordRequired: boolean;
|
|
}
|
|
|
|
export interface MeetingChapterVO {
|
|
chapterNo?: number;
|
|
title?: string;
|
|
summary?: string;
|
|
time?: string;
|
|
startTime?: number;
|
|
endTime?: number;
|
|
startTranscriptId?: number;
|
|
endTranscriptId?: number;
|
|
sourceTranscriptIds?: number[];
|
|
}
|
|
|
|
export interface PublicMeetingPreviewVO {
|
|
meeting: MeetingVO;
|
|
transcripts: MeetingTranscriptVO[];
|
|
chapters?: MeetingChapterVO[];
|
|
}
|
|
|
|
export const getMeetingDetail = (id: number, options?: { suppressErrorToast?: boolean }) => {
|
|
return http.get<{ code: string; data: MeetingVO; msg: string }>(
|
|
`/api/biz/meeting/${id}`,
|
|
{
|
|
timeout: MEETING_DETAIL_TIMEOUT,
|
|
suppressErrorToast: options?.suppressErrorToast,
|
|
}
|
|
);
|
|
};
|
|
|
|
export const getMeetingCreateConfig = () => {
|
|
return http.get<{ code: string; data: MeetingCreateConfig; msg: string }>(
|
|
"/api/biz/meeting/create-config"
|
|
);
|
|
};
|
|
|
|
export const getTranscripts = (id: number) => {
|
|
return http.get<{ code: string; data: MeetingTranscriptVO[]; msg: string }>(
|
|
`/api/biz/meeting/${id}/transcripts`
|
|
);
|
|
};
|
|
|
|
export const getMeetingChapters = (id: number) => {
|
|
return http.get<{ code: string; data: MeetingChapterVO[]; msg: string }>(
|
|
`/api/biz/meeting/${id}/chapters`
|
|
);
|
|
};
|
|
|
|
export const getMeetingPreviewAccess = (id: number) => {
|
|
return http.get<{ code: string; data: MeetingPreviewAccessVO; msg: string }>(
|
|
`/api/public/meetings/${id}/preview/access`
|
|
);
|
|
};
|
|
|
|
export const getPublicMeetingPreview = (id: number, accessPassword?: string) => {
|
|
return http.get<{ code: string; data: PublicMeetingPreviewVO; msg: string }>(
|
|
`/api/public/meetings/${id}/preview`,
|
|
{
|
|
timeout: MEETING_DETAIL_TIMEOUT,
|
|
params: accessPassword ? { accessPassword } : undefined,
|
|
}
|
|
);
|
|
};
|
|
|
|
export interface MeetingSpeakerUpdateDTO {
|
|
meetingId: number;
|
|
speakerId: string;
|
|
newName: string;
|
|
label: string;
|
|
}
|
|
|
|
export interface MeetingTranscriptUpdateDTO {
|
|
meetingId: number;
|
|
transcriptId: number;
|
|
content: string;
|
|
}
|
|
|
|
export const updateSpeakerInfo = (params: MeetingSpeakerUpdateDTO) => {
|
|
return http.put<{ code: string; data: boolean; msg: string }>(
|
|
"/api/biz/meeting/speaker",
|
|
params
|
|
);
|
|
};
|
|
|
|
export const updateMeetingTranscript = (params: MeetingTranscriptUpdateDTO) => {
|
|
return http.put<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${params.meetingId}/transcripts/${params.transcriptId}`,
|
|
params
|
|
);
|
|
};
|
|
|
|
export interface MeetingResummaryDTO {
|
|
meetingId: number;
|
|
summaryModelId: number;
|
|
promptId: number;
|
|
userPrompt?: string;
|
|
summaryDetailLevel?: SummaryDetailLevel;
|
|
}
|
|
|
|
export const reSummary = (params: MeetingResummaryDTO) => {
|
|
return http.post<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${params.meetingId}/summary/regenerate`,
|
|
params
|
|
);
|
|
};
|
|
|
|
export const retryMeetingTranscription = (meetingId: number) => {
|
|
return http.post<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${meetingId}/transcripts/regenerate`,
|
|
{}
|
|
);
|
|
};
|
|
|
|
export const updateMeetingBasic = (data: UpdateMeetingBasicCommand) => {
|
|
return http.put<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${data.meetingId}/basic`,
|
|
data
|
|
);
|
|
};
|
|
|
|
export const updateMeetingSummary = (data: UpdateMeetingSummaryCommand) => {
|
|
return http.put<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${data.meetingId}/summary`,
|
|
data
|
|
);
|
|
};
|
|
|
|
export interface UpdateMeetingParticipantsCommand {
|
|
meetingId: number;
|
|
participants: string;
|
|
}
|
|
|
|
export type MeetingParticipantsUpdateDTO = UpdateMeetingParticipantsCommand;
|
|
|
|
export const updateMeetingParticipants = (params: UpdateMeetingParticipantsCommand) => {
|
|
return http.put<{ code: string; data: boolean; msg: string }>(
|
|
`/api/biz/meeting/${params.meetingId}/participants`,
|
|
params
|
|
);
|
|
};
|
|
|
|
export const uploadAudio = (file: File, onUploadProgress?: (progressEvent: any) => void) => {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
return http.post<{ code: string; data: string; msg: string }>(
|
|
"/api/biz/meeting/upload",
|
|
formData,
|
|
{
|
|
headers: { "Content-Type": "multipart/form-data" },
|
|
timeout: MEETING_UPLOAD_FLOW_TIMEOUT,
|
|
onUploadProgress
|
|
}
|
|
);
|
|
};
|
|
|
|
export interface MeetingProgress {
|
|
percent: number;
|
|
message: string;
|
|
updateAt: number;
|
|
eta?: number;
|
|
queueAheadCount?: number;
|
|
}
|
|
|
|
export const getMeetingProgress = (id: number, options?: { suppressErrorToast?: boolean }) => {
|
|
return http.get<{ code: string; data: MeetingProgress; msg: string }>(
|
|
`/api/biz/meeting/${id}/progress`,
|
|
{
|
|
suppressErrorToast: options?.suppressErrorToast,
|
|
}
|
|
);
|
|
};
|
|
|
|
export const getMeetingProgressBatch = (ids: number[], options?: { suppressErrorToast?: boolean }) => {
|
|
return http.post<{ code: string; data: Record<number, MeetingProgress>; msg: string }>(
|
|
"/api/biz/meeting/progress/batch",
|
|
ids,
|
|
{
|
|
suppressErrorToast: options?.suppressErrorToast,
|
|
}
|
|
);
|
|
};
|
|
|
|
export const downloadMeetingSummary = (id: number, format: "pdf" | "word") => {
|
|
const token = localStorage.getItem("accessToken");
|
|
return axios.get(`/api/biz/meeting/${id}/summary/export`, {
|
|
params: { format },
|
|
responseType: "blob",
|
|
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
});
|
|
};
|
|
|
|
export const downloadMeetingTranscript = (id: number) => {
|
|
const token = localStorage.getItem("accessToken");
|
|
return axios.get(`/api/biz/meeting/${id}/transcripts/export`, {
|
|
responseType: "blob",
|
|
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
});
|
|
};
|