@pipedream/zoom 0.10.1 → 0.11.0
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.
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import zoom from "../../zoom.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "zoom-get-meeting-summary",
|
|
5
|
+
name: "Get Meeting Summary",
|
|
6
|
+
description: "Retrieve the summary of a meeting or webinar. [See the documentation](https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/Getameetingsummary)",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
annotations: {
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
openWorldHint: true,
|
|
12
|
+
readOnlyHint: true,
|
|
13
|
+
},
|
|
14
|
+
props: {
|
|
15
|
+
zoom,
|
|
16
|
+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
|
|
17
|
+
info: {
|
|
18
|
+
type: "alert",
|
|
19
|
+
alertType: "info",
|
|
20
|
+
content: `
|
|
21
|
+
- The host must have a Pro, Business, or higher subscription plan.
|
|
22
|
+
- For meetings - the host's Meeting Summary with AI Companion user setting must be enabled.
|
|
23
|
+
- For webinars - the host's Webinar Summary with AI Companion user setting must be enabled.
|
|
24
|
+
- End-to-End Encrypted (E2EE) meetings do not support summaries.
|
|
25
|
+
|
|
26
|
+
Learn more about [enabling or disabling AI Companion meeting summaries](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0057960&_ics=1771446392860&irclickid=~ae~a521XQPMHICGKIJzGxnovBDKLGwCvzrhab340WULKDzsmda90).`,
|
|
27
|
+
},
|
|
28
|
+
meetingId: {
|
|
29
|
+
propDefinition: [
|
|
30
|
+
zoom,
|
|
31
|
+
"meetingId",
|
|
32
|
+
() => ({
|
|
33
|
+
type: "previous_meetings",
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
description: "The meeting ID or meeting UUID to retrieve the AI summary for. Only past meetings are listed.",
|
|
37
|
+
optional: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
async run({ $: step }) {
|
|
41
|
+
const {
|
|
42
|
+
zoom,
|
|
43
|
+
meetingId,
|
|
44
|
+
} = this;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const summary = await zoom.getMeetingSummary({
|
|
48
|
+
step,
|
|
49
|
+
meetingId,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
step.export("$summary", `Successfully retrieved AI summary for meeting ${meetingId}`);
|
|
53
|
+
return summary;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const code = error?.response?.data?.code ?? error?.data?.code ?? error?.code;
|
|
56
|
+
if (code === 3322) {
|
|
57
|
+
throw new Error(`No AI summary found for meeting "${meetingId}". Ensure the meeting has ended and AI Companion was enabled before the meeting started.`);
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import constants from "../../common/constants.mjs";
|
|
2
|
+
import zoom from "../../zoom.app.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
key: "zoom-list-all-recordings",
|
|
6
|
+
name: "List All Recordings",
|
|
7
|
+
description: "List all cloud recordings for a user. [See the documentation](https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/recordingsList)",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
type: "action",
|
|
10
|
+
annotations: {
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
openWorldHint: true,
|
|
13
|
+
readOnlyHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
zoom,
|
|
17
|
+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
|
|
18
|
+
info: {
|
|
19
|
+
type: "alert",
|
|
20
|
+
alertType: "info",
|
|
21
|
+
content: `
|
|
22
|
+
- Must have a Pro or a higher plan.
|
|
23
|
+
- Must enable Cloud Recording on the user's account. Learn more about [enabling cloud recordings](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0063923&_ics=1771436993341&irclickid=~ae~a521XQPMHICGKIJzGxnovBDKLGwCvzrhab3431UMLKGzsjgd6) and [managing cloud recording settings](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0065362&_ics=1771436993341&irclickid=~ae~a521XQPMHICGKIJzGxnovBDKLGwCvzrhab3431UMLKGzsjgd6).`,
|
|
24
|
+
},
|
|
25
|
+
userId: {
|
|
26
|
+
type: "string",
|
|
27
|
+
label: "User ID",
|
|
28
|
+
description: "The user ID or email address of the user. Use `me` to retrieve recordings for the current user.",
|
|
29
|
+
optional: true,
|
|
30
|
+
default: "me",
|
|
31
|
+
},
|
|
32
|
+
from: {
|
|
33
|
+
type: "string",
|
|
34
|
+
label: "From",
|
|
35
|
+
description: "Start date in `yyyy-MM-dd` UTC format. The maximum date range is a month. Defaults to the current date if not provided.",
|
|
36
|
+
optional: true,
|
|
37
|
+
},
|
|
38
|
+
to: {
|
|
39
|
+
type: "string",
|
|
40
|
+
label: "To",
|
|
41
|
+
description: "End date in `yyyy-MM-dd` UTC format.",
|
|
42
|
+
optional: true,
|
|
43
|
+
},
|
|
44
|
+
mc: {
|
|
45
|
+
type: "string",
|
|
46
|
+
label: "MC",
|
|
47
|
+
description: "Query metadata of recording if an On-Premise Meeting Connector was used for the meeting.",
|
|
48
|
+
optional: true,
|
|
49
|
+
},
|
|
50
|
+
trash: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
label: "Trash",
|
|
53
|
+
description: "If `true`, list recordings from trash.",
|
|
54
|
+
optional: true,
|
|
55
|
+
},
|
|
56
|
+
trashType: {
|
|
57
|
+
type: "string",
|
|
58
|
+
label: "Trash Type",
|
|
59
|
+
description: "The type of Cloud recording to retrieve from trash. Should be used together with **Trash**.",
|
|
60
|
+
optional: true,
|
|
61
|
+
options: constants.CLOUD_RECORD_TRASH_TYPE_OPTIONS,
|
|
62
|
+
},
|
|
63
|
+
max: {
|
|
64
|
+
propDefinition: [
|
|
65
|
+
zoom,
|
|
66
|
+
"max",
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
async run({ $: step }) {
|
|
71
|
+
const {
|
|
72
|
+
userId,
|
|
73
|
+
from,
|
|
74
|
+
to,
|
|
75
|
+
mc,
|
|
76
|
+
trash,
|
|
77
|
+
trashType,
|
|
78
|
+
max,
|
|
79
|
+
} = this;
|
|
80
|
+
|
|
81
|
+
const recordings = [];
|
|
82
|
+
|
|
83
|
+
const results = this.zoom.getResourcesStream({
|
|
84
|
+
resourceFn: this.zoom.listRecordings,
|
|
85
|
+
resourceFnArgs: {
|
|
86
|
+
step,
|
|
87
|
+
userId: userId || "me",
|
|
88
|
+
params: {
|
|
89
|
+
page_size: 300,
|
|
90
|
+
from,
|
|
91
|
+
to,
|
|
92
|
+
mc,
|
|
93
|
+
trash,
|
|
94
|
+
trash_type: trashType,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
resourceName: "meetings",
|
|
98
|
+
max,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
for await (const recording of results) {
|
|
102
|
+
recordings.push(recording);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
step.export("$summary", `Successfully retrieved ${recordings.length} recording${recordings.length === 1
|
|
106
|
+
? ""
|
|
107
|
+
: "s"}`);
|
|
108
|
+
|
|
109
|
+
return recordings;
|
|
110
|
+
},
|
|
111
|
+
};
|
package/common/constants.mjs
CHANGED
|
@@ -21,9 +21,21 @@ const MEETING_TYPES = [
|
|
|
21
21
|
},
|
|
22
22
|
];
|
|
23
23
|
|
|
24
|
+
const CLOUD_RECORD_TRASH_TYPE_OPTIONS = [
|
|
25
|
+
{
|
|
26
|
+
label: "List all meeting recordings from the trash",
|
|
27
|
+
value: "meeting_recordings",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: "List all individual recording files from the trash",
|
|
31
|
+
value: "recording_file",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
24
35
|
export default {
|
|
25
36
|
BASE_URL,
|
|
26
37
|
VERSION_PATH,
|
|
27
38
|
MAX_RESOURCES,
|
|
28
39
|
MEETING_TYPES,
|
|
40
|
+
CLOUD_RECORD_TRASH_TYPE_OPTIONS,
|
|
29
41
|
};
|
package/package.json
CHANGED
package/zoom.app.mjs
CHANGED
|
@@ -323,9 +323,11 @@ export default {
|
|
|
323
323
|
...args,
|
|
324
324
|
});
|
|
325
325
|
},
|
|
326
|
-
listRecordings(
|
|
326
|
+
listRecordings({
|
|
327
|
+
userId = "me", ...args
|
|
328
|
+
} = {}) {
|
|
327
329
|
return this._makeRequest({
|
|
328
|
-
path:
|
|
330
|
+
path: `/users/${userId}/recordings`,
|
|
329
331
|
...args,
|
|
330
332
|
});
|
|
331
333
|
},
|
|
@@ -391,6 +393,14 @@ export default {
|
|
|
391
393
|
...args,
|
|
392
394
|
});
|
|
393
395
|
},
|
|
396
|
+
getMeetingSummary({
|
|
397
|
+
meetingId, ...args
|
|
398
|
+
}) {
|
|
399
|
+
return this._makeRequest({
|
|
400
|
+
path: `/meetings/${utils.doubleEncode(meetingId)}/meeting_summary`,
|
|
401
|
+
...args,
|
|
402
|
+
});
|
|
403
|
+
},
|
|
394
404
|
async *getResourcesStream({
|
|
395
405
|
resourceFn,
|
|
396
406
|
resourceFnArgs,
|