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 = { 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 | 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; } 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; 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; 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}` } : {} }); };