@pipedream/zoom 0.10.1 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
axios, ConfigurationError,
|
|
3
|
+
} from "@pipedream/platform";
|
|
1
4
|
import zoom from "../../zoom.app.mjs";
|
|
2
5
|
|
|
3
6
|
export default {
|
|
4
7
|
key: "zoom-get-meeting-transcript",
|
|
5
8
|
name: "Get Meeting Transcript",
|
|
6
|
-
description: "Get the transcript of a meeting. [See the documentation](https://developers.zoom.us/docs/api/meetings/#tag/cloud-recording/get/meetings/{meetingId}/transcript)",
|
|
7
|
-
version: "0.0
|
|
9
|
+
description: "Get the transcript of a past meeting. Fetches the VTT file server-side using your OAuth token and returns speaker-attributed plain text alongside the original authenticated URL. [See the documentation](https://developers.zoom.us/docs/api/meetings/#tag/cloud-recording/get/meetings/{meetingId}/transcript)",
|
|
10
|
+
version: "0.1.0",
|
|
8
11
|
annotations: {
|
|
9
12
|
destructiveHint: false,
|
|
10
13
|
openWorldHint: true,
|
|
@@ -21,17 +24,119 @@ export default {
|
|
|
21
24
|
type: "previous_meetings",
|
|
22
25
|
}),
|
|
23
26
|
],
|
|
24
|
-
description: "The meeting
|
|
27
|
+
description: "The ID of a past meeting to retrieve the transcript for. Only meetings with cloud recording and audio transcription enabled will have transcripts available.",
|
|
25
28
|
optional: false,
|
|
26
29
|
},
|
|
27
30
|
},
|
|
31
|
+
methods: {
|
|
32
|
+
fetchTranscriptContent({
|
|
33
|
+
step, url,
|
|
34
|
+
}) {
|
|
35
|
+
return axios(step, {
|
|
36
|
+
url,
|
|
37
|
+
headers: this.zoom._getHeaders(),
|
|
38
|
+
responseType: "text",
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
parseVtt(vttContent) {
|
|
42
|
+
const normalized = vttContent
|
|
43
|
+
.replace(/\r\n/g, "\n")
|
|
44
|
+
.replace(/\r/g, "\n")
|
|
45
|
+
.trim();
|
|
46
|
+
const blocks = normalized.split(/\n{2,}/);
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const block of blocks) {
|
|
49
|
+
const lines = block.trim().split("\n");
|
|
50
|
+
if (lines[0]?.trim().startsWith("WEBVTT")) continue;
|
|
51
|
+
|
|
52
|
+
const timestampIdx = lines.findIndex((l) => l.includes(" --> "));
|
|
53
|
+
if (timestampIdx === -1) continue;
|
|
54
|
+
|
|
55
|
+
const textLines = lines.slice(timestampIdx + 1);
|
|
56
|
+
if (!textLines.length) continue;
|
|
57
|
+
|
|
58
|
+
let currentSpeaker = null;
|
|
59
|
+
const textParts = textLines
|
|
60
|
+
.map((line) => {
|
|
61
|
+
const speakerMatch = line.match(/<v\s+([^>]+)>/);
|
|
62
|
+
if (speakerMatch) {
|
|
63
|
+
currentSpeaker = speakerMatch[1].trim();
|
|
64
|
+
}
|
|
65
|
+
const cleanText = line.replace(/<[^>]+>/g, "").trim();
|
|
66
|
+
if (!cleanText) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return currentSpeaker
|
|
70
|
+
? `${currentSpeaker}: ${cleanText}`
|
|
71
|
+
: cleanText;
|
|
72
|
+
})
|
|
73
|
+
.filter((t) => t);
|
|
74
|
+
|
|
75
|
+
if (!textParts.length) continue;
|
|
76
|
+
|
|
77
|
+
result.push(...textParts);
|
|
78
|
+
}
|
|
79
|
+
return result.join("\n");
|
|
80
|
+
},
|
|
81
|
+
},
|
|
28
82
|
async run({ $: step }) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
83
|
+
let transcriptResponse;
|
|
84
|
+
try {
|
|
85
|
+
transcriptResponse = await this.zoom.getMeetingTranscript({
|
|
86
|
+
step,
|
|
87
|
+
meetingId: this.meetingId,
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error?.response?.status === 404 || error?.status === 404) {
|
|
91
|
+
throw new ConfigurationError(
|
|
92
|
+
"No recording found for this meeting. Ensure cloud recording was enabled before the meeting started.",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const transcriptUrl = transcriptResponse?.download_url;
|
|
99
|
+
if (!transcriptUrl) {
|
|
100
|
+
throw new ConfigurationError(
|
|
101
|
+
"No transcript found for this meeting. Ensure audio transcription is enabled in the host's Zoom account settings before the meeting starts.",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let vttContent;
|
|
106
|
+
try {
|
|
107
|
+
vttContent = await this.fetchTranscriptContent({
|
|
108
|
+
step,
|
|
109
|
+
url: transcriptUrl,
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error?.response?.status === 404 || error?.status === 404) {
|
|
113
|
+
throw new ConfigurationError(
|
|
114
|
+
transcriptUrl
|
|
115
|
+
? "Transcript is still being processed. Please try again shortly."
|
|
116
|
+
: "Transcript file could not be retrieved. It may have expired or been deleted.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const trimmed = vttContent?.trim() ?? "";
|
|
123
|
+
if (!trimmed || trimmed === "WEBVTT") {
|
|
124
|
+
throw new ConfigurationError(
|
|
125
|
+
"Transcript is still being processed. Please try again shortly.",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const transcriptText = this.parseVtt(vttContent);
|
|
130
|
+
if (!transcriptText) {
|
|
131
|
+
throw new ConfigurationError(
|
|
132
|
+
"Transcript is still being processed. Please try again shortly.",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
33
135
|
|
|
34
136
|
step.export("$summary", `Retrieved transcript for meeting ${this.meetingId}`);
|
|
35
|
-
return
|
|
137
|
+
return {
|
|
138
|
+
transcript_url: transcriptUrl,
|
|
139
|
+
transcript_text: transcriptText,
|
|
140
|
+
};
|
|
36
141
|
},
|
|
37
142
|
};
|