feat(tools): added additional youtube search params, get channel playlists videos and related videos tools (#1814)

This commit is contained in:
Waleed
2025-11-05 11:26:45 -08:00
committed by GitHub
parent 11fd1544bb
commit 2056e8ae5f
9 changed files with 846 additions and 9 deletions
+72 -3
View File
@@ -40,7 +40,7 @@ In Sim, the YouTube integration enables your agents to programmatically search a
## Usage Instructions
Integrate YouTube into the workflow. Can search for videos, get video details, get channel information, get playlist items, and get video comments.
Integrate YouTube into the workflow. Can search for videos, get video details, get channel information, get all videos from a channel, get channel playlists, get playlist items, find related videos, and get video comments.
@@ -48,15 +48,26 @@ Integrate YouTube into the workflow. Can search for videos, get video details, g
### `youtube_search`
Search for videos on YouTube using the YouTube Data API.
Search for videos on YouTube using the YouTube Data API. Supports advanced filtering by channel, date range, duration, category, quality, captions, and more.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `query` | string | Yes | Search query for YouTube videos |
| `maxResults` | number | No | Maximum number of videos to return |
| `maxResults` | number | No | Maximum number of videos to return \(1-50\) |
| `apiKey` | string | Yes | YouTube API Key |
| `channelId` | string | No | Filter results to a specific YouTube channel ID |
| `publishedAfter` | string | No | Only return videos published after this date \(RFC 3339 format: "2024-01-01T00:00:00Z"\) |
| `publishedBefore` | string | No | Only return videos published before this date \(RFC 3339 format: "2024-01-01T00:00:00Z"\) |
| `videoDuration` | string | No | Filter by video length: "short" \(<4 min\), "medium" \(4-20 min\), "long" \(>20 min\), "any" |
| `order` | string | No | Sort results by: "date", "rating", "relevance" \(default\), "title", "videoCount", "viewCount" |
| `videoCategoryId` | string | No | Filter by YouTube category ID \(e.g., "10" for Music, "20" for Gaming\) |
| `videoDefinition` | string | No | Filter by video quality: "high" \(HD\), "standard", "any" |
| `videoCaption` | string | No | Filter by caption availability: "closedCaption" \(has captions\), "none" \(no captions\), "any" |
| `regionCode` | string | No | Return results relevant to a specific region \(ISO 3166-1 alpha-2 country code, e.g., "US", "GB"\) |
| `relevanceLanguage` | string | No | Return results most relevant to a language \(ISO 639-1 code, e.g., "en", "es"\) |
| `safeSearch` | string | No | Content filtering level: "moderate" \(default\), "none", "strict" |
#### Output
@@ -118,6 +129,45 @@ Get detailed information about a YouTube channel.
| `thumbnail` | string | Channel thumbnail URL |
| `customUrl` | string | Channel custom URL |
### `youtube_channel_videos`
Get all videos from a specific YouTube channel, with sorting options.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `channelId` | string | Yes | YouTube channel ID to get videos from |
| `maxResults` | number | No | Maximum number of videos to return \(1-50\) |
| `order` | string | No | Sort order: "date" \(newest first\), "rating", "relevance", "title", "viewCount" |
| `pageToken` | string | No | Page token for pagination |
| `apiKey` | string | Yes | YouTube API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of videos from the channel |
### `youtube_channel_playlists`
Get all playlists from a specific YouTube channel.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `channelId` | string | Yes | YouTube channel ID to get playlists from |
| `maxResults` | number | No | Maximum number of playlists to return \(1-50\) |
| `pageToken` | string | No | Page token for pagination |
| `apiKey` | string | Yes | YouTube API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of playlists from the channel |
### `youtube_playlist_items`
Get videos from a YouTube playlist.
@@ -137,6 +187,25 @@ Get videos from a YouTube playlist.
| --------- | ---- | ----------- |
| `items` | array | Array of videos in the playlist |
### `youtube_related_videos`
Find videos related to a specific YouTube video.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `videoId` | string | Yes | YouTube video ID to find related videos for |
| `maxResults` | number | No | Maximum number of related videos to return \(1-50\) |
| `pageToken` | string | No | Page token for pagination |
| `apiKey` | string | Yes | YouTube API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of related videos |
### `youtube_comments`
Get comments from a YouTube video.
+220 -4
View File
@@ -9,7 +9,7 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
description: 'Interact with YouTube videos, channels, and playlists',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate YouTube into the workflow. Can search for videos, get video details, get channel information, get playlist items, and get video comments.',
'Integrate YouTube into the workflow. Can search for videos, get video details, get channel information, get all videos from a channel, get channel playlists, get playlist items, find related videos, and get video comments.',
docsLink: 'https://docs.sim.ai/tools/youtube',
category: 'tools',
bgColor: '#FF0000',
@@ -24,7 +24,10 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
{ label: 'Search Videos', id: 'youtube_search' },
{ label: 'Get Video Details', id: 'youtube_video_details' },
{ label: 'Get Channel Info', id: 'youtube_channel_info' },
{ label: 'Get Channel Videos', id: 'youtube_channel_videos' },
{ label: 'Get Channel Playlists', id: 'youtube_channel_playlists' },
{ label: 'Get Playlist Items', id: 'youtube_playlist_items' },
{ label: 'Get Related Videos', id: 'youtube_related_videos' },
{ label: 'Get Video Comments', id: 'youtube_comments' },
],
value: () => 'youtube_search',
@@ -50,13 +53,129 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
integer: true,
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'channelId',
title: 'Filter by Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Filter results to a specific channel',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'publishedAfter',
title: 'Published After',
type: 'short-input',
layout: 'half',
placeholder: '2024-01-01T00:00:00Z',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'publishedBefore',
title: 'Published Before',
type: 'short-input',
layout: 'half',
placeholder: '2024-12-31T23:59:59Z',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'videoDuration',
title: 'Video Duration',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Any', id: 'any' },
{ label: 'Short (<4 min)', id: 'short' },
{ label: 'Medium (4-20 min)', id: 'medium' },
{ label: 'Long (>20 min)', id: 'long' },
],
value: () => 'any',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'order',
title: 'Sort Order',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Relevance', id: 'relevance' },
{ label: 'Date (Newest First)', id: 'date' },
{ label: 'View Count', id: 'viewCount' },
{ label: 'Rating', id: 'rating' },
{ label: 'Title', id: 'title' },
],
value: () => 'relevance',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'videoCategoryId',
title: 'Category ID',
type: 'short-input',
layout: 'half',
placeholder: '10 for Music, 20 for Gaming',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'videoDefinition',
title: 'Video Quality',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Any', id: 'any' },
{ label: 'HD', id: 'high' },
{ label: 'Standard', id: 'standard' },
],
value: () => 'any',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'videoCaption',
title: 'Captions',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Any', id: 'any' },
{ label: 'Has Captions', id: 'closedCaption' },
{ label: 'No Captions', id: 'none' },
],
value: () => 'any',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'regionCode',
title: 'Region Code',
type: 'short-input',
layout: 'half',
placeholder: 'US, GB, JP',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'relevanceLanguage',
title: 'Language Code',
type: 'short-input',
layout: 'half',
placeholder: 'en, es, fr',
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'safeSearch',
title: 'Safe Search',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Moderate', id: 'moderate' },
{ label: 'None', id: 'none' },
{ label: 'Strict', id: 'strict' },
],
value: () => 'moderate',
condition: { field: 'operation', value: 'youtube_search' },
},
// Get Video Details operation inputs
{
id: 'videoId',
title: 'Video ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter YouTube video ID (e.g., dQw4w9WgXcQ)',
placeholder: 'Enter YouTube video ID',
required: true,
condition: { field: 'operation', value: 'youtube_video_details' },
},
@@ -77,6 +196,63 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
placeholder: 'Enter channel username (if not using channel ID)',
condition: { field: 'operation', value: 'youtube_channel_info' },
},
// Get Channel Videos operation inputs
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter YouTube channel ID',
required: true,
condition: { field: 'operation', value: 'youtube_channel_videos' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
layout: 'half',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_channel_videos' },
},
{
id: 'order',
title: 'Sort Order',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'Date (Newest First)', id: 'date' },
{ label: 'Relevance', id: 'relevance' },
{ label: 'View Count', id: 'viewCount' },
{ label: 'Rating', id: 'rating' },
{ label: 'Title', id: 'title' },
],
value: () => 'date',
condition: { field: 'operation', value: 'youtube_channel_videos' },
},
// Get Channel Playlists operation inputs
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter YouTube channel ID',
required: true,
condition: { field: 'operation', value: 'youtube_channel_playlists' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
layout: 'half',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_channel_playlists' },
},
// Get Playlist Items operation inputs
{
id: 'playlistId',
@@ -98,6 +274,27 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
integer: true,
condition: { field: 'operation', value: 'youtube_playlist_items' },
},
// Get Related Videos operation inputs
{
id: 'videoId',
title: 'Video ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter YouTube video ID to find related videos',
required: true,
condition: { field: 'operation', value: 'youtube_related_videos' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
layout: 'half',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_related_videos' },
},
// Get Video Comments operation inputs
{
id: 'videoId',
@@ -147,7 +344,10 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
'youtube_search',
'youtube_video_details',
'youtube_channel_info',
'youtube_channel_videos',
'youtube_channel_playlists',
'youtube_playlist_items',
'youtube_related_videos',
'youtube_comments',
],
config: {
@@ -164,8 +364,14 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
return 'youtube_video_details'
case 'youtube_channel_info':
return 'youtube_channel_info'
case 'youtube_channel_videos':
return 'youtube_channel_videos'
case 'youtube_channel_playlists':
return 'youtube_channel_playlists'
case 'youtube_playlist_items':
return 'youtube_playlist_items'
case 'youtube_related_videos':
return 'youtube_related_videos'
case 'youtube_comments':
return 'youtube_comments'
default:
@@ -180,6 +386,16 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
// Search Videos
query: { type: 'string', description: 'Search query' },
maxResults: { type: 'number', description: 'Maximum number of results' },
// Search Filters
publishedAfter: { type: 'string', description: 'Published after date (RFC 3339)' },
publishedBefore: { type: 'string', description: 'Published before date (RFC 3339)' },
videoDuration: { type: 'string', description: 'Video duration filter' },
videoCategoryId: { type: 'string', description: 'YouTube category ID' },
videoDefinition: { type: 'string', description: 'Video quality filter' },
videoCaption: { type: 'string', description: 'Caption availability filter' },
regionCode: { type: 'string', description: 'Region code (ISO 3166-1)' },
relevanceLanguage: { type: 'string', description: 'Language code (ISO 639-1)' },
safeSearch: { type: 'string', description: 'Safe search level' },
// Video Details & Comments
videoId: { type: 'string', description: 'YouTube video ID' },
// Channel Info
@@ -187,8 +403,8 @@ export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
username: { type: 'string', description: 'YouTube channel username' },
// Playlist Items
playlistId: { type: 'string', description: 'YouTube playlist ID' },
// Comments
order: { type: 'string', description: 'Sort order for comments' },
// Sort Order (used by multiple operations)
order: { type: 'string', description: 'Sort order' },
},
outputs: {
// Search Videos & Playlist Items
+6
View File
@@ -224,8 +224,11 @@ import { workflowExecutorTool } from '@/tools/workflow'
import { xReadTool, xSearchTool, xUserTool, xWriteTool } from '@/tools/x'
import {
youtubeChannelInfoTool,
youtubeChannelPlaylistsTool,
youtubeChannelVideosTool,
youtubeCommentsTool,
youtubePlaylistItemsTool,
youtubeRelatedVideosTool,
youtubeSearchTool,
youtubeVideoDetailsTool,
} from '@/tools/youtube'
@@ -289,6 +292,9 @@ export const tools: Record<string, ToolConfig> = {
youtube_channel_info: youtubeChannelInfoTool,
youtube_playlist_items: youtubePlaylistItemsTool,
youtube_comments: youtubeCommentsTool,
youtube_channel_videos: youtubeChannelVideosTool,
youtube_channel_playlists: youtubeChannelPlaylistsTool,
youtube_related_videos: youtubeRelatedVideosTool,
notion_read: notionReadTool,
notion_read_database: notionReadDatabaseTool,
notion_write: notionWriteTool,
+123
View File
@@ -0,0 +1,123 @@
import type { ToolConfig } from '@/tools/types'
import type {
YouTubeChannelPlaylistsParams,
YouTubeChannelPlaylistsResponse,
} from '@/tools/youtube/types'
export const youtubeChannelPlaylistsTool: ToolConfig<
YouTubeChannelPlaylistsParams,
YouTubeChannelPlaylistsResponse
> = {
id: 'youtube_channel_playlists',
name: 'YouTube Channel Playlists',
description: 'Get all playlists from a specific YouTube channel.',
version: '1.0.0',
params: {
channelId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'YouTube channel ID to get playlists from',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-only',
default: 10,
description: 'Maximum number of playlists to return (1-50)',
},
pageToken: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Page token for pagination',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'YouTube API Key',
},
},
request: {
url: (params: YouTubeChannelPlaylistsParams) => {
let url = `https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=${encodeURIComponent(
params.channelId
)}&key=${params.apiKey}`
url += `&maxResults=${params.maxResults || 10}`
if (params.pageToken) {
url += `&pageToken=${params.pageToken}`
}
return url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response): Promise<YouTubeChannelPlaylistsResponse> => {
const data = await response.json()
if (!data.items) {
return {
success: false,
output: {
items: [],
totalResults: 0,
},
error: 'No playlists found',
}
}
const items = (data.items || []).map((item: any) => ({
playlistId: item.id,
title: item.snippet?.title || '',
description: item.snippet?.description || '',
thumbnail:
item.snippet?.thumbnails?.medium?.url ||
item.snippet?.thumbnails?.default?.url ||
item.snippet?.thumbnails?.high?.url ||
'',
itemCount: item.contentDetails?.itemCount || 0,
publishedAt: item.snippet?.publishedAt || '',
}))
return {
success: true,
output: {
items,
totalResults: data.pageInfo?.totalResults || 0,
nextPageToken: data.nextPageToken,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of playlists from the channel',
items: {
type: 'object',
properties: {
playlistId: { type: 'string', description: 'YouTube playlist ID' },
title: { type: 'string', description: 'Playlist title' },
description: { type: 'string', description: 'Playlist description' },
thumbnail: { type: 'string', description: 'Playlist thumbnail URL' },
itemCount: { type: 'number', description: 'Number of videos in playlist' },
publishedAt: { type: 'string', description: 'Playlist creation date' },
},
},
},
totalResults: {
type: 'number',
description: 'Total number of playlists in the channel',
},
nextPageToken: {
type: 'string',
description: 'Token for accessing the next page of results',
optional: true,
},
},
}
+117
View File
@@ -0,0 +1,117 @@
import type { ToolConfig } from '@/tools/types'
import type {
YouTubeChannelVideosParams,
YouTubeChannelVideosResponse,
} from '@/tools/youtube/types'
export const youtubeChannelVideosTool: ToolConfig<
YouTubeChannelVideosParams,
YouTubeChannelVideosResponse
> = {
id: 'youtube_channel_videos',
name: 'YouTube Channel Videos',
description: 'Get all videos from a specific YouTube channel, with sorting options.',
version: '1.0.0',
params: {
channelId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'YouTube channel ID to get videos from',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-only',
default: 10,
description: 'Maximum number of videos to return (1-50)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: "date" (newest first), "rating", "relevance", "title", "viewCount"',
},
pageToken: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Page token for pagination',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'YouTube API Key',
},
},
request: {
url: (params: YouTubeChannelVideosParams) => {
let url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&channelId=${encodeURIComponent(
params.channelId
)}&key=${params.apiKey}`
url += `&maxResults=${params.maxResults || 10}`
if (params.order) {
url += `&order=${params.order}`
}
if (params.pageToken) {
url += `&pageToken=${params.pageToken}`
}
return url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response): Promise<YouTubeChannelVideosResponse> => {
const data = await response.json()
const items = (data.items || []).map((item: any) => ({
videoId: item.id?.videoId,
title: item.snippet?.title,
description: item.snippet?.description,
thumbnail:
item.snippet?.thumbnails?.medium?.url ||
item.snippet?.thumbnails?.default?.url ||
item.snippet?.thumbnails?.high?.url ||
'',
publishedAt: item.snippet?.publishedAt || '',
}))
return {
success: true,
output: {
items,
totalResults: data.pageInfo?.totalResults || 0,
nextPageToken: data.nextPageToken,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of videos from the channel',
items: {
type: 'object',
properties: {
videoId: { type: 'string', description: 'YouTube video ID' },
title: { type: 'string', description: 'Video title' },
description: { type: 'string', description: 'Video description' },
thumbnail: { type: 'string', description: 'Video thumbnail URL' },
publishedAt: { type: 'string', description: 'Video publish date' },
},
},
},
totalResults: {
type: 'number',
description: 'Total number of videos in the channel',
},
nextPageToken: {
type: 'string',
description: 'Token for accessing the next page of results',
optional: true,
},
},
}
+6
View File
@@ -1,6 +1,9 @@
import { youtubeChannelInfoTool } from '@/tools/youtube/channel_info'
import { youtubeChannelPlaylistsTool } from '@/tools/youtube/channel_playlists'
import { youtubeChannelVideosTool } from '@/tools/youtube/channel_videos'
import { youtubeCommentsTool } from '@/tools/youtube/comments'
import { youtubePlaylistItemsTool } from '@/tools/youtube/playlist_items'
import { youtubeRelatedVideosTool } from '@/tools/youtube/related_videos'
import { youtubeSearchTool } from '@/tools/youtube/search'
import { youtubeVideoDetailsTool } from '@/tools/youtube/video_details'
@@ -9,3 +12,6 @@ export { youtubeVideoDetailsTool }
export { youtubeChannelInfoTool }
export { youtubePlaylistItemsTool }
export { youtubeCommentsTool }
export { youtubeChannelVideosTool }
export { youtubeChannelPlaylistsTool }
export { youtubeRelatedVideosTool }
+108
View File
@@ -0,0 +1,108 @@
import type { ToolConfig } from '@/tools/types'
import type {
YouTubeRelatedVideosParams,
YouTubeRelatedVideosResponse,
} from '@/tools/youtube/types'
export const youtubeRelatedVideosTool: ToolConfig<
YouTubeRelatedVideosParams,
YouTubeRelatedVideosResponse
> = {
id: 'youtube_related_videos',
name: 'YouTube Related Videos',
description: 'Find videos related to a specific YouTube video.',
version: '1.0.0',
params: {
videoId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'YouTube video ID to find related videos for',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-only',
default: 10,
description: 'Maximum number of related videos to return (1-50)',
},
pageToken: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Page token for pagination',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'YouTube API Key',
},
},
request: {
url: (params: YouTubeRelatedVideosParams) => {
let url = `https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&relatedToVideoId=${encodeURIComponent(
params.videoId
)}&key=${params.apiKey}`
url += `&maxResults=${params.maxResults || 10}`
if (params.pageToken) {
url += `&pageToken=${params.pageToken}`
}
return url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response): Promise<YouTubeRelatedVideosResponse> => {
const data = await response.json()
const items = (data.items || []).map((item: any) => ({
videoId: item.id?.videoId,
title: item.snippet?.title,
description: item.snippet?.description,
thumbnail:
item.snippet?.thumbnails?.medium?.url ||
item.snippet?.thumbnails?.default?.url ||
item.snippet?.thumbnails?.high?.url ||
'',
channelTitle: item.snippet?.channelTitle || '',
}))
return {
success: true,
output: {
items,
totalResults: data.pageInfo?.totalResults || 0,
nextPageToken: data.nextPageToken,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of related videos',
items: {
type: 'object',
properties: {
videoId: { type: 'string', description: 'YouTube video ID' },
title: { type: 'string', description: 'Video title' },
description: { type: 'string', description: 'Video description' },
thumbnail: { type: 'string', description: 'Video thumbnail URL' },
channelTitle: { type: 'string', description: 'Channel name' },
},
},
},
totalResults: {
type: 'number',
description: 'Total number of related videos available',
},
nextPageToken: {
type: 'string',
description: 'Token for accessing the next page of results',
optional: true,
},
},
}
+115 -2
View File
@@ -4,7 +4,8 @@ import type { YouTubeSearchParams, YouTubeSearchResponse } from '@/tools/youtube
export const youtubeSearchTool: ToolConfig<YouTubeSearchParams, YouTubeSearchResponse> = {
id: 'youtube_search',
name: 'YouTube Search',
description: 'Search for videos on YouTube using the YouTube Data API.',
description:
'Search for videos on YouTube using the YouTube Data API. Supports advanced filtering by channel, date range, duration, category, quality, captions, and more.',
version: '1.0.0',
params: {
query: {
@@ -18,7 +19,7 @@ export const youtubeSearchTool: ToolConfig<YouTubeSearchParams, YouTubeSearchRes
required: false,
visibility: 'user-only',
default: 5,
description: 'Maximum number of videos to return',
description: 'Maximum number of videos to return (1-50)',
},
apiKey: {
type: 'string',
@@ -26,6 +27,80 @@ export const youtubeSearchTool: ToolConfig<YouTubeSearchParams, YouTubeSearchRes
visibility: 'user-only',
description: 'YouTube API Key',
},
// Priority 1: Essential filters
channelId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter results to a specific YouTube channel ID',
},
publishedAfter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Only return videos published after this date (RFC 3339 format: "2024-01-01T00:00:00Z")',
},
publishedBefore: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Only return videos published before this date (RFC 3339 format: "2024-01-01T00:00:00Z")',
},
videoDuration: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by video length: "short" (<4 min), "medium" (4-20 min), "long" (>20 min), "any"',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Sort results by: "date", "rating", "relevance" (default), "title", "videoCount", "viewCount"',
},
videoCategoryId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by YouTube category ID (e.g., "10" for Music, "20" for Gaming)',
},
// Priority 2: Very useful filters
videoDefinition: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by video quality: "high" (HD), "standard", "any"',
},
videoCaption: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by caption availability: "closedCaption" (has captions), "none" (no captions), "any"',
},
regionCode: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Return results relevant to a specific region (ISO 3166-1 alpha-2 country code, e.g., "US", "GB")',
},
relevanceLanguage: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Return results most relevant to a language (ISO 639-1 code, e.g., "en", "es")',
},
safeSearch: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Content filtering level: "moderate" (default), "none", "strict"',
},
},
request: {
@@ -34,6 +109,44 @@ export const youtubeSearchTool: ToolConfig<YouTubeSearchParams, YouTubeSearchRes
params.query
)}`
url += `&maxResults=${params.maxResults || 5}`
// Add Priority 1 filters if provided
if (params.channelId) {
url += `&channelId=${encodeURIComponent(params.channelId)}`
}
if (params.publishedAfter) {
url += `&publishedAfter=${encodeURIComponent(params.publishedAfter)}`
}
if (params.publishedBefore) {
url += `&publishedBefore=${encodeURIComponent(params.publishedBefore)}`
}
if (params.videoDuration) {
url += `&videoDuration=${params.videoDuration}`
}
if (params.order) {
url += `&order=${params.order}`
}
if (params.videoCategoryId) {
url += `&videoCategoryId=${params.videoCategoryId}`
}
// Add Priority 2 filters if provided
if (params.videoDefinition) {
url += `&videoDefinition=${params.videoDefinition}`
}
if (params.videoCaption) {
url += `&videoCaption=${params.videoCaption}`
}
if (params.regionCode) {
url += `&regionCode=${params.regionCode}`
}
if (params.relevanceLanguage) {
url += `&relevanceLanguage=${params.relevanceLanguage}`
}
if (params.safeSearch) {
url += `&safeSearch=${params.safeSearch}`
}
return url
},
method: 'GET',
+79
View File
@@ -5,6 +5,17 @@ export interface YouTubeSearchParams {
query: string
maxResults?: number
pageToken?: string
channelId?: string
publishedAfter?: string
publishedBefore?: string
videoDuration?: 'any' | 'short' | 'medium' | 'long'
order?: 'date' | 'rating' | 'relevance' | 'title' | 'videoCount' | 'viewCount'
videoCategoryId?: string
videoDefinition?: 'any' | 'high' | 'standard'
videoCaption?: 'any' | 'closedCaption' | 'none'
regionCode?: string
relevanceLanguage?: string
safeSearch?: 'moderate' | 'none' | 'strict'
}
export interface YouTubeSearchResponse extends ToolResponse {
@@ -111,9 +122,77 @@ export interface YouTubeCommentsResponse extends ToolResponse {
}
}
export interface YouTubeChannelVideosParams {
apiKey: string
channelId: string
maxResults?: number
order?: 'date' | 'rating' | 'relevance' | 'title' | 'viewCount'
pageToken?: string
}
export interface YouTubeChannelVideosResponse extends ToolResponse {
output: {
items: Array<{
videoId: string
title: string
description: string
thumbnail: string
publishedAt: string
}>
totalResults: number
nextPageToken?: string
}
}
export interface YouTubeChannelPlaylistsParams {
apiKey: string
channelId: string
maxResults?: number
pageToken?: string
}
export interface YouTubeChannelPlaylistsResponse extends ToolResponse {
output: {
items: Array<{
playlistId: string
title: string
description: string
thumbnail: string
itemCount: number
publishedAt: string
}>
totalResults: number
nextPageToken?: string
}
}
export interface YouTubeRelatedVideosParams {
apiKey: string
videoId: string
maxResults?: number
pageToken?: string
}
export interface YouTubeRelatedVideosResponse extends ToolResponse {
output: {
items: Array<{
videoId: string
title: string
description: string
thumbnail: string
channelTitle: string
}>
totalResults: number
nextPageToken?: string
}
}
export type YouTubeResponse =
| YouTubeSearchResponse
| YouTubeVideoDetailsResponse
| YouTubeChannelInfoResponse
| YouTubePlaylistItemsResponse
| YouTubeCommentsResponse
| YouTubeChannelVideosResponse
| YouTubeChannelPlaylistsResponse
| YouTubeRelatedVideosResponse