@zapier/youtube-connector 0.0.0 → 0.0.1
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.
- package/LICENSE +93 -0
- package/NOTICE +8 -0
- package/README.md +117 -2
- package/SKILL.md +162 -0
- package/cli.js +71 -0
- package/cli.ts +5 -0
- package/connections.ts +8 -0
- package/dist/cli.js +4 -0
- package/dist/index.js +1550 -0
- package/index.ts +79 -0
- package/package.json +59 -4
- package/preflight.sh +157 -0
- package/references/youtube-api-gotchas.md +252 -0
- package/scripts/addVideoToPlaylist.ts +75 -0
- package/scripts/createPlaylist.ts +73 -0
- package/scripts/deletePlaylist.ts +46 -0
- package/scripts/deleteVideo.ts +42 -0
- package/scripts/downloadCaption.ts +66 -0
- package/scripts/getChannel.ts +85 -0
- package/scripts/getVideo.ts +71 -0
- package/scripts/listCaptions.ts +54 -0
- package/scripts/listComments.ts +98 -0
- package/scripts/listPlaylistItems.ts +90 -0
- package/scripts/listPlaylists.ts +107 -0
- package/scripts/listSubscriptions.ts +103 -0
- package/scripts/listVideoCategories.ts +63 -0
- package/scripts/postComment.ts +65 -0
- package/scripts/rateVideo.ts +50 -0
- package/scripts/removeVideoFromPlaylist.ts +49 -0
- package/scripts/replyToComment.ts +90 -0
- package/scripts/searchVideos.ts +169 -0
- package/scripts/subscribeToChannel.ts +122 -0
- package/scripts/unsubscribeFromChannel.ts +49 -0
- package/scripts/updatePlaylist.ts +75 -0
- package/scripts/updateVideo.ts +143 -0
- package/tsup.config.ts +63 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { PlaylistSchema, throwForYouTube } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
snippet: z
|
|
11
|
+
.object({
|
|
12
|
+
title: z.string().describe("Playlist title (required)."),
|
|
13
|
+
description: z.string().describe("Playlist description.").optional(),
|
|
14
|
+
defaultLanguage: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"BCP-47 language code of the title/description text, e.g. en.",
|
|
18
|
+
)
|
|
19
|
+
.optional(),
|
|
20
|
+
})
|
|
21
|
+
.strict(),
|
|
22
|
+
status: z
|
|
23
|
+
.object({
|
|
24
|
+
privacyStatus: z
|
|
25
|
+
.enum(["public", "unlisted", "private"])
|
|
26
|
+
.describe("Playlist visibility. Defaults to private if omitted.")
|
|
27
|
+
.optional(),
|
|
28
|
+
})
|
|
29
|
+
.strict()
|
|
30
|
+
.optional(),
|
|
31
|
+
part: z
|
|
32
|
+
.string()
|
|
33
|
+
.describe("Resource parts being written. Leave as the default.")
|
|
34
|
+
.default("snippet,status"),
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
const outputSchema = PlaylistSchema;
|
|
38
|
+
|
|
39
|
+
const definition = defineTool({
|
|
40
|
+
name: "createPlaylist",
|
|
41
|
+
title: "Create Playlist",
|
|
42
|
+
description:
|
|
43
|
+
"Create a new playlist on the authenticated user's channel. Returns the new playlist id for addVideoToPlaylist.",
|
|
44
|
+
inputSchema,
|
|
45
|
+
outputSchema,
|
|
46
|
+
annotations: {
|
|
47
|
+
readOnlyHint: false,
|
|
48
|
+
destructiveHint: false,
|
|
49
|
+
idempotentHint: false,
|
|
50
|
+
openWorldHint: true,
|
|
51
|
+
},
|
|
52
|
+
connection: "youtube",
|
|
53
|
+
run: async (input, ctx) => {
|
|
54
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/playlists`);
|
|
55
|
+
if (input.part !== undefined) {
|
|
56
|
+
url.searchParams.set("part", String(input.part));
|
|
57
|
+
}
|
|
58
|
+
const body: Record<string, unknown> = {};
|
|
59
|
+
if (input.snippet !== undefined) body["snippet"] = input.snippet;
|
|
60
|
+
if (input.status !== undefined) body["status"] = input.status;
|
|
61
|
+
const res = await ctx.fetch(url.toString(), {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify(body),
|
|
65
|
+
});
|
|
66
|
+
await throwForYouTube(res, "createPlaylist");
|
|
67
|
+
return res.json();
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export default definition;
|
|
72
|
+
|
|
73
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { SuccessResultSchema, throwForYouTube } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
id: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("The id of the playlist to delete (from listPlaylists)."),
|
|
13
|
+
})
|
|
14
|
+
.strict();
|
|
15
|
+
const outputSchema = SuccessResultSchema;
|
|
16
|
+
|
|
17
|
+
const definition = defineTool({
|
|
18
|
+
name: "deletePlaylist",
|
|
19
|
+
title: "Delete Playlist",
|
|
20
|
+
description:
|
|
21
|
+
"Permanently delete a playlist owned by the authenticated user. Irreversible.",
|
|
22
|
+
inputSchema,
|
|
23
|
+
outputSchema,
|
|
24
|
+
annotations: {
|
|
25
|
+
readOnlyHint: false,
|
|
26
|
+
destructiveHint: true,
|
|
27
|
+
idempotentHint: true,
|
|
28
|
+
openWorldHint: true,
|
|
29
|
+
},
|
|
30
|
+
connection: "youtube",
|
|
31
|
+
run: async (input, ctx) => {
|
|
32
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/playlists`);
|
|
33
|
+
if (input.id !== undefined) {
|
|
34
|
+
url.searchParams.set("id", String(input.id));
|
|
35
|
+
}
|
|
36
|
+
const res = await ctx.fetch(url.toString(), {
|
|
37
|
+
method: "DELETE",
|
|
38
|
+
});
|
|
39
|
+
await throwForYouTube(res, "deletePlaylist");
|
|
40
|
+
return { success: true as const };
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default definition;
|
|
45
|
+
|
|
46
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { SuccessResultSchema, throwForYouTube } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({ id: z.string().describe("The id of the video to delete.") })
|
|
10
|
+
.strict();
|
|
11
|
+
const outputSchema = SuccessResultSchema;
|
|
12
|
+
|
|
13
|
+
const definition = defineTool({
|
|
14
|
+
name: "deleteVideo",
|
|
15
|
+
title: "Delete Video",
|
|
16
|
+
description:
|
|
17
|
+
"Permanently delete a video from the authenticated user's channel. Irreversible. The caller must own the video.",
|
|
18
|
+
inputSchema,
|
|
19
|
+
outputSchema,
|
|
20
|
+
annotations: {
|
|
21
|
+
readOnlyHint: false,
|
|
22
|
+
destructiveHint: true,
|
|
23
|
+
idempotentHint: true,
|
|
24
|
+
openWorldHint: true,
|
|
25
|
+
},
|
|
26
|
+
connection: "youtube",
|
|
27
|
+
run: async (input, ctx) => {
|
|
28
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/videos`);
|
|
29
|
+
if (input.id !== undefined) {
|
|
30
|
+
url.searchParams.set("id", String(input.id));
|
|
31
|
+
}
|
|
32
|
+
const res = await ctx.fetch(url.toString(), {
|
|
33
|
+
method: "DELETE",
|
|
34
|
+
});
|
|
35
|
+
await throwForYouTube(res, "deleteVideo");
|
|
36
|
+
return { success: true as const };
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export default definition;
|
|
41
|
+
|
|
42
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// captions.download returns a raw caption FILE body (srt/vtt/sbv/scc/ttml text),
|
|
3
|
+
// not a JSON resource — so the response is read as text and wrapped here rather
|
|
4
|
+
// than parsed as JSON.
|
|
5
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
import { connectionResolvers } from "../connections.ts";
|
|
9
|
+
import { throwForYouTube } from "../lib/youtube.ts";
|
|
10
|
+
|
|
11
|
+
const inputSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
id: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("The caption track id to download (from listCaptions)."),
|
|
16
|
+
tfmt: z
|
|
17
|
+
.enum(["srt", "vtt", "sbv", "scc", "ttml"])
|
|
18
|
+
.describe("Output caption format. Defaults to srt.")
|
|
19
|
+
.default("srt"),
|
|
20
|
+
tlang: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe(
|
|
23
|
+
"Optional BCP-47 language code (e.g. es) to request a machine translation of the track.",
|
|
24
|
+
)
|
|
25
|
+
.optional(),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
|
|
29
|
+
const outputSchema = z.object({
|
|
30
|
+
caption_text: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe("The caption track text in the requested format."),
|
|
33
|
+
format: z.string().describe("The format the text was returned in."),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const definition = defineTool({
|
|
37
|
+
name: "downloadCaption",
|
|
38
|
+
title: "Download Caption",
|
|
39
|
+
description:
|
|
40
|
+
"Download a caption track's text in a chosen format (srt, vtt, sbv, scc, or ttml). Resolve the track id via listCaptions. Requires permission to edit the video (the owner or an editor) and the youtube.force-ssl scope.",
|
|
41
|
+
inputSchema,
|
|
42
|
+
outputSchema,
|
|
43
|
+
annotations: {
|
|
44
|
+
readOnlyHint: true,
|
|
45
|
+
destructiveHint: false,
|
|
46
|
+
idempotentHint: true,
|
|
47
|
+
openWorldHint: true,
|
|
48
|
+
},
|
|
49
|
+
connection: "youtube",
|
|
50
|
+
run: async (input, ctx) => {
|
|
51
|
+
const url = new URL(
|
|
52
|
+
`https://www.googleapis.com/youtube/v3/captions/${encodeURIComponent(input.id)}`,
|
|
53
|
+
);
|
|
54
|
+
url.searchParams.set("tfmt", input.tfmt);
|
|
55
|
+
if (input.tlang !== undefined) url.searchParams.set("tlang", input.tlang);
|
|
56
|
+
|
|
57
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
58
|
+
await throwForYouTube(res, "downloadCaption");
|
|
59
|
+
const caption_text = await res.text();
|
|
60
|
+
return { caption_text, format: input.tfmt };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export default definition;
|
|
65
|
+
|
|
66
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { ChannelSchema, throwForYouTube } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
part: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Resource parts to return. Leave as the default.")
|
|
13
|
+
.default("snippet,contentDetails,statistics,brandingSettings"),
|
|
14
|
+
mine: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.describe(
|
|
17
|
+
"Return the authenticated user's own channel(s). Use this to discover the caller's channel id and uploads playlist.",
|
|
18
|
+
)
|
|
19
|
+
.optional(),
|
|
20
|
+
id: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe("Look up channel(s) by id (UC..., comma-separated, max 50).")
|
|
23
|
+
.optional(),
|
|
24
|
+
forHandle: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe(
|
|
27
|
+
"Look up a channel by its @handle, e.g. @MrBeast (with or without the leading @). Mutually exclusive with mine and id.",
|
|
28
|
+
)
|
|
29
|
+
.optional(),
|
|
30
|
+
})
|
|
31
|
+
.strict()
|
|
32
|
+
// Exactly one selector — the API silently ignores conflicting selectors, so make
|
|
33
|
+
// the at-most-one rule explicit. (x-mutually-exclusive: [[mine, id, forHandle]])
|
|
34
|
+
.refine(
|
|
35
|
+
(v) =>
|
|
36
|
+
[v.mine, v.id, v.forHandle].filter((x) => x !== undefined).length <= 1,
|
|
37
|
+
{
|
|
38
|
+
message:
|
|
39
|
+
"Provide only one of mine, id, or forHandle (they are mutually exclusive).",
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
const outputSchema = z.object({
|
|
43
|
+
items: z.array(ChannelSchema).describe("The requested channels."),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const definition = defineTool({
|
|
47
|
+
name: "getChannel",
|
|
48
|
+
title: "Get Channel",
|
|
49
|
+
description:
|
|
50
|
+
"Get a channel's profile, statistics, and uploads-playlist id. Pass mine=true for the authenticated user's channel, or a channel id / handle to look up another. The resolver for channelId and for a channel's uploads playlist.",
|
|
51
|
+
inputSchema,
|
|
52
|
+
outputSchema,
|
|
53
|
+
annotations: {
|
|
54
|
+
readOnlyHint: true,
|
|
55
|
+
destructiveHint: false,
|
|
56
|
+
idempotentHint: true,
|
|
57
|
+
openWorldHint: true,
|
|
58
|
+
},
|
|
59
|
+
connection: "youtube",
|
|
60
|
+
run: async (input, ctx) => {
|
|
61
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/channels`);
|
|
62
|
+
if (input.part !== undefined) {
|
|
63
|
+
url.searchParams.set("part", String(input.part));
|
|
64
|
+
}
|
|
65
|
+
if (input.mine !== undefined) {
|
|
66
|
+
url.searchParams.set("mine", String(input.mine));
|
|
67
|
+
}
|
|
68
|
+
if (input.id !== undefined) {
|
|
69
|
+
url.searchParams.set("id", String(input.id));
|
|
70
|
+
}
|
|
71
|
+
if (input.forHandle !== undefined) {
|
|
72
|
+
url.searchParams.set("forHandle", String(input.forHandle));
|
|
73
|
+
}
|
|
74
|
+
const res = await ctx.fetch(url.toString(), {
|
|
75
|
+
method: "GET",
|
|
76
|
+
});
|
|
77
|
+
await throwForYouTube(res, "getChannel");
|
|
78
|
+
const payload = (await res.json()) as { items?: unknown };
|
|
79
|
+
return { items: payload.items ?? [] };
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export default definition;
|
|
84
|
+
|
|
85
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { NextPageToken, throwForYouTube, VideoSchema } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
part: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Resource parts to return. Leave as the default.")
|
|
13
|
+
.default("snippet,contentDetails,statistics,status"),
|
|
14
|
+
id: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe(
|
|
17
|
+
"One video id or a comma-separated list (max 50), e.g. dQw4w9WgXcQ. The 11-char id from a watch URL or searchVideos.",
|
|
18
|
+
),
|
|
19
|
+
maxResults: z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.gte(1)
|
|
23
|
+
.lte(50)
|
|
24
|
+
.describe(
|
|
25
|
+
"Max results per page when listing many ids. Defaults to 10 when omitted; pass a value when you need a specific number of results.",
|
|
26
|
+
)
|
|
27
|
+
.optional(),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
|
|
31
|
+
const outputSchema = z.object({
|
|
32
|
+
items: z.array(VideoSchema).describe("The requested videos."),
|
|
33
|
+
next_page_token: NextPageToken,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const definition = defineTool({
|
|
37
|
+
name: "getVideo",
|
|
38
|
+
title: "Get Video",
|
|
39
|
+
description:
|
|
40
|
+
"Get full details of one or more videos by id — snippet (title, description, tags, category), statistics (view/like/comment counts), contentDetails (ISO-8601 duration), and status (privacy, upload state). Counts come back as strings; like/comment counts are absent when the owner hides them. Resolve ids via searchVideos or a watch URL.",
|
|
41
|
+
inputSchema,
|
|
42
|
+
outputSchema,
|
|
43
|
+
annotations: {
|
|
44
|
+
readOnlyHint: true,
|
|
45
|
+
destructiveHint: false,
|
|
46
|
+
idempotentHint: true,
|
|
47
|
+
openWorldHint: true,
|
|
48
|
+
},
|
|
49
|
+
connection: "youtube",
|
|
50
|
+
run: async (input, ctx) => {
|
|
51
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/videos`);
|
|
52
|
+
url.searchParams.set("part", input.part);
|
|
53
|
+
url.searchParams.set("id", input.id);
|
|
54
|
+
url.searchParams.set("maxResults", String(input.maxResults ?? 10));
|
|
55
|
+
|
|
56
|
+
const res = await ctx.fetch(url.toString(), { method: "GET" });
|
|
57
|
+
await throwForYouTube(res, "getVideo");
|
|
58
|
+
const payload = (await res.json()) as {
|
|
59
|
+
items?: unknown;
|
|
60
|
+
nextPageToken?: string;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
items: payload.items ?? [],
|
|
64
|
+
next_page_token: payload.nextPageToken,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default definition;
|
|
70
|
+
|
|
71
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import { CaptionSchema, throwForYouTube } from "../lib/youtube.ts";
|
|
7
|
+
|
|
8
|
+
const inputSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
part: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Resource parts to return. Leave as the default.")
|
|
13
|
+
.default("snippet"),
|
|
14
|
+
videoId: z.string().describe("The video id whose caption tracks to list."),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
17
|
+
const outputSchema = z.object({
|
|
18
|
+
items: z.array(CaptionSchema).describe("The caption tracks for the video."),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const definition = defineTool({
|
|
22
|
+
name: "listCaptions",
|
|
23
|
+
title: "List Captions",
|
|
24
|
+
description:
|
|
25
|
+
"List the caption tracks available for a video (id, language, name, type, status). Use the track id with downloadCaption to fetch the text. Requires the youtube.force-ssl scope.",
|
|
26
|
+
inputSchema,
|
|
27
|
+
outputSchema,
|
|
28
|
+
annotations: {
|
|
29
|
+
readOnlyHint: true,
|
|
30
|
+
destructiveHint: false,
|
|
31
|
+
idempotentHint: true,
|
|
32
|
+
openWorldHint: true,
|
|
33
|
+
},
|
|
34
|
+
connection: "youtube",
|
|
35
|
+
run: async (input, ctx) => {
|
|
36
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/captions`);
|
|
37
|
+
if (input.part !== undefined) {
|
|
38
|
+
url.searchParams.set("part", String(input.part));
|
|
39
|
+
}
|
|
40
|
+
if (input.videoId !== undefined) {
|
|
41
|
+
url.searchParams.set("videoId", String(input.videoId));
|
|
42
|
+
}
|
|
43
|
+
const res = await ctx.fetch(url.toString(), {
|
|
44
|
+
method: "GET",
|
|
45
|
+
});
|
|
46
|
+
await throwForYouTube(res, "listCaptions");
|
|
47
|
+
const payload = (await res.json()) as { items?: unknown };
|
|
48
|
+
return { items: payload.items ?? [] };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export default definition;
|
|
53
|
+
|
|
54
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
CommentThreadSchema,
|
|
8
|
+
NextPageToken,
|
|
9
|
+
throwForYouTube,
|
|
10
|
+
} from "../lib/youtube.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
part: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe("Resource parts to return. Leave as the default.")
|
|
17
|
+
.default("snippet,replies"),
|
|
18
|
+
videoId: z.string().describe("The video id whose comment threads to list."),
|
|
19
|
+
order: z
|
|
20
|
+
.enum(["time", "relevance"])
|
|
21
|
+
.describe("time = newest first; relevance = YouTube's relevance ranking.")
|
|
22
|
+
.default("time"),
|
|
23
|
+
searchTerms: z
|
|
24
|
+
.string()
|
|
25
|
+
.describe("Only return comment threads matching this text.")
|
|
26
|
+
.optional(),
|
|
27
|
+
maxResults: z
|
|
28
|
+
.number()
|
|
29
|
+
.int()
|
|
30
|
+
.gte(1)
|
|
31
|
+
.lte(100)
|
|
32
|
+
.describe(
|
|
33
|
+
"Max threads per page (1-100). Defaults to 20 when omitted; pass a value when you need a specific number of results.",
|
|
34
|
+
)
|
|
35
|
+
.optional(),
|
|
36
|
+
pageToken: z
|
|
37
|
+
.string()
|
|
38
|
+
.describe(
|
|
39
|
+
"Page cursor from a previous response's next_page_token. Omit for the first page.",
|
|
40
|
+
)
|
|
41
|
+
.optional(),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
const outputSchema = z.object({
|
|
45
|
+
items: z.array(CommentThreadSchema).describe("The comment threads."),
|
|
46
|
+
next_page_token: NextPageToken,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const definition = defineTool({
|
|
50
|
+
name: "listComments",
|
|
51
|
+
title: "List Comments",
|
|
52
|
+
description:
|
|
53
|
+
"List top-level comment threads on a video (each with its first replies). Use to read what viewers commented. Resolve videoId via searchVideos/getVideo.",
|
|
54
|
+
inputSchema,
|
|
55
|
+
outputSchema,
|
|
56
|
+
annotations: {
|
|
57
|
+
readOnlyHint: true,
|
|
58
|
+
destructiveHint: false,
|
|
59
|
+
idempotentHint: true,
|
|
60
|
+
openWorldHint: true,
|
|
61
|
+
},
|
|
62
|
+
connection: "youtube",
|
|
63
|
+
run: async (input, ctx) => {
|
|
64
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/commentThreads`);
|
|
65
|
+
if (input.part !== undefined) {
|
|
66
|
+
url.searchParams.set("part", String(input.part));
|
|
67
|
+
}
|
|
68
|
+
if (input.videoId !== undefined) {
|
|
69
|
+
url.searchParams.set("videoId", String(input.videoId));
|
|
70
|
+
}
|
|
71
|
+
if (input.order !== undefined) {
|
|
72
|
+
url.searchParams.set("order", String(input.order));
|
|
73
|
+
}
|
|
74
|
+
if (input.searchTerms !== undefined) {
|
|
75
|
+
url.searchParams.set("searchTerms", String(input.searchTerms));
|
|
76
|
+
}
|
|
77
|
+
url.searchParams.set("maxResults", String(input.maxResults ?? 20));
|
|
78
|
+
if (input.pageToken !== undefined) {
|
|
79
|
+
url.searchParams.set("pageToken", String(input.pageToken));
|
|
80
|
+
}
|
|
81
|
+
const res = await ctx.fetch(url.toString(), {
|
|
82
|
+
method: "GET",
|
|
83
|
+
});
|
|
84
|
+
await throwForYouTube(res, "listComments");
|
|
85
|
+
const payload = (await res.json()) as {
|
|
86
|
+
items?: unknown;
|
|
87
|
+
nextPageToken?: string;
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
items: payload.items ?? [],
|
|
91
|
+
next_page_token: payload.nextPageToken,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export default definition;
|
|
97
|
+
|
|
98
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { defineTool, handleIfScriptMain } from "@zapier/connectors-sdk";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import { connectionResolvers } from "../connections.ts";
|
|
6
|
+
import {
|
|
7
|
+
NextPageToken,
|
|
8
|
+
PlaylistItemSchema,
|
|
9
|
+
throwForYouTube,
|
|
10
|
+
} from "../lib/youtube.ts";
|
|
11
|
+
|
|
12
|
+
const inputSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
part: z
|
|
15
|
+
.string()
|
|
16
|
+
.describe("Resource parts to return. Leave as the default.")
|
|
17
|
+
.default("snippet,contentDetails,status"),
|
|
18
|
+
playlistId: z
|
|
19
|
+
.string()
|
|
20
|
+
.describe(
|
|
21
|
+
"The playlist id whose items to list (from listPlaylists). For a channel's uploads, use the uploads playlist id from getChannel.",
|
|
22
|
+
),
|
|
23
|
+
maxResults: z
|
|
24
|
+
.number()
|
|
25
|
+
.int()
|
|
26
|
+
.gte(1)
|
|
27
|
+
.lte(50)
|
|
28
|
+
.describe(
|
|
29
|
+
"Max items per page (1-50). Defaults to 20 when omitted; pass a value when you need a specific number of results.",
|
|
30
|
+
)
|
|
31
|
+
.optional(),
|
|
32
|
+
pageToken: z
|
|
33
|
+
.string()
|
|
34
|
+
.describe(
|
|
35
|
+
"Page cursor from a previous response's next_page_token. Omit for the first page.",
|
|
36
|
+
)
|
|
37
|
+
.optional(),
|
|
38
|
+
})
|
|
39
|
+
.strict();
|
|
40
|
+
const outputSchema = z.object({
|
|
41
|
+
items: z
|
|
42
|
+
.array(PlaylistItemSchema)
|
|
43
|
+
.describe("The videos in the playlist, in order."),
|
|
44
|
+
next_page_token: NextPageToken,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const definition = defineTool({
|
|
48
|
+
name: "listPlaylistItems",
|
|
49
|
+
title: "List Playlist Items",
|
|
50
|
+
description:
|
|
51
|
+
"List the videos in a playlist, in playlist order. Each item carries its own playlistItem id (needed by removeVideoFromPlaylist) plus the video id and snippet.",
|
|
52
|
+
inputSchema,
|
|
53
|
+
outputSchema,
|
|
54
|
+
annotations: {
|
|
55
|
+
readOnlyHint: true,
|
|
56
|
+
destructiveHint: false,
|
|
57
|
+
idempotentHint: true,
|
|
58
|
+
openWorldHint: true,
|
|
59
|
+
},
|
|
60
|
+
connection: "youtube",
|
|
61
|
+
run: async (input, ctx) => {
|
|
62
|
+
const url = new URL(`https://www.googleapis.com/youtube/v3/playlistItems`);
|
|
63
|
+
if (input.part !== undefined) {
|
|
64
|
+
url.searchParams.set("part", String(input.part));
|
|
65
|
+
}
|
|
66
|
+
if (input.playlistId !== undefined) {
|
|
67
|
+
url.searchParams.set("playlistId", String(input.playlistId));
|
|
68
|
+
}
|
|
69
|
+
url.searchParams.set("maxResults", String(input.maxResults ?? 20));
|
|
70
|
+
if (input.pageToken !== undefined) {
|
|
71
|
+
url.searchParams.set("pageToken", String(input.pageToken));
|
|
72
|
+
}
|
|
73
|
+
const res = await ctx.fetch(url.toString(), {
|
|
74
|
+
method: "GET",
|
|
75
|
+
});
|
|
76
|
+
await throwForYouTube(res, "listPlaylistItems");
|
|
77
|
+
const payload = (await res.json()) as {
|
|
78
|
+
items?: unknown;
|
|
79
|
+
nextPageToken?: string;
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
items: payload.items ?? [],
|
|
83
|
+
next_page_token: payload.nextPageToken,
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export default definition;
|
|
89
|
+
|
|
90
|
+
await handleIfScriptMain(import.meta, definition, { connectionResolvers });
|