Files
simstudioai--sim/apps/sim/blocks/blocks/youtube.ts
T
wehub-resource-sync d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

658 lines
25 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ElevenLabsIcon, YouTubeIcon } from '@/components/icons'
import type { BlockConfig, BlockMeta } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
import type { YouTubeResponse } from '@/tools/youtube/types'
export const YouTubeBlock: BlockConfig<YouTubeResponse> = {
type: 'youtube',
name: 'YouTube',
description: 'Interact with YouTube videos, channels, and playlists',
authMode: AuthMode.ApiKey,
longDescription:
'Integrate YouTube into the workflow. Can search for videos, get trending videos, get video details, get video categories, get channel information, get all videos from a channel, get channel playlists, get playlist items, and get video comments.',
docsLink: 'https://docs.sim.ai/integrations/youtube',
category: 'tools',
integrationType: IntegrationType.Communication,
bgColor: '#FF0000',
icon: YouTubeIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Search Videos', id: 'youtube_search' },
{ label: 'Get Trending Videos', id: 'youtube_trending' },
{ label: 'Get Video Details', id: 'youtube_video_details' },
{ label: 'Get Video Categories', id: 'youtube_video_categories' },
{ 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 Video Comments', id: 'youtube_comments' },
],
value: () => 'youtube_search',
},
// Search Videos operation inputs
{
id: 'query',
title: 'Search Query',
type: 'short-input',
placeholder: 'Enter search query',
required: true,
condition: { field: 'operation', value: 'youtube_search' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'channelId',
title: 'Filter by Channel ID',
type: 'short-input',
placeholder: 'Filter results to a specific channel',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'eventType',
title: 'Live Stream Filter',
type: 'dropdown',
options: [
{ label: 'All Videos', id: '' },
{ label: 'Currently Live', id: 'live' },
{ label: 'Upcoming Streams', id: 'upcoming' },
{ label: 'Past Streams', id: 'completed' },
],
value: () => '',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'publishedAfter',
title: 'Published After',
type: 'short-input',
placeholder: '2024-01-01T00:00:00Z',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
This is for filtering YouTube videos published after this date.
Examples:
- "last month" -> Calculate 30 days ago at 00:00:00Z
- "beginning of 2024" -> 2024-01-01T00:00:00Z
- "last year" -> Calculate 1 year ago at 00:00:00Z
- "past 7 days" -> Calculate 7 days ago at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start date (e.g., "last month", "beginning of 2024")...',
generationType: 'timestamp',
},
},
{
id: 'publishedBefore',
title: 'Published Before',
type: 'short-input',
placeholder: '2024-12-31T23:59:59Z',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
This is for filtering YouTube videos published before this date.
Examples:
- "today" -> Today's date at 23:59:59Z
- "end of 2024" -> 2024-12-31T23:59:59Z
- "yesterday" -> Yesterday's date at 23:59:59Z
- "end of last month" -> Last day of previous month at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end date (e.g., "today", "end of last year")...',
generationType: 'timestamp',
},
},
{
id: 'videoDuration',
title: 'Video Duration',
type: 'dropdown',
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' },
mode: 'advanced',
},
{
id: 'order',
title: 'Sort Order',
type: 'dropdown',
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' },
mode: 'advanced',
},
{
id: 'videoCategoryId',
title: 'Category ID',
type: 'short-input',
placeholder: 'Use Get Video Categories to find IDs',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'videoDefinition',
title: 'Video Quality',
type: 'dropdown',
options: [
{ label: 'Any', id: 'any' },
{ label: 'HD', id: 'high' },
{ label: 'Standard', id: 'standard' },
],
value: () => 'any',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'videoCaption',
title: 'Captions',
type: 'dropdown',
options: [
{ label: 'Any', id: 'any' },
{ label: 'Has Captions', id: 'closedCaption' },
{ label: 'No Captions', id: 'none' },
],
value: () => 'any',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'regionCode',
title: 'Region Code',
type: 'short-input',
placeholder: 'US, GB, JP',
condition: {
field: 'operation',
value: ['youtube_search', 'youtube_trending', 'youtube_video_categories'],
},
mode: 'advanced',
},
{
id: 'relevanceLanguage',
title: 'Language Code',
type: 'short-input',
placeholder: 'en, es, fr',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
{
id: 'safeSearch',
title: 'Safe Search',
type: 'dropdown',
options: [
{ label: 'Moderate', id: 'moderate' },
{ label: 'None', id: 'none' },
{ label: 'Strict', id: 'strict' },
],
value: () => 'moderate',
condition: { field: 'operation', value: 'youtube_search' },
mode: 'advanced',
},
// Get Trending Videos operation inputs
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_trending' },
},
{
id: 'videoCategoryId',
title: 'Category ID',
type: 'short-input',
placeholder: 'Use Get Video Categories to find IDs',
condition: { field: 'operation', value: 'youtube_trending' },
mode: 'advanced',
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_trending' },
mode: 'advanced',
},
// Get Video Details operation inputs
{
id: 'videoId',
title: 'Video ID',
type: 'short-input',
placeholder: 'Enter YouTube video ID',
required: true,
condition: { field: 'operation', value: 'youtube_video_details' },
},
// Get Video Categories operation inputs
{
id: 'hl',
title: 'Language',
type: 'short-input',
placeholder: 'en, es, fr (for category names)',
condition: { field: 'operation', value: 'youtube_video_categories' },
mode: 'advanced',
},
// Get Channel Info operation inputs
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
placeholder: 'Enter channel ID (or leave blank to use username)',
condition: { field: 'operation', value: 'youtube_channel_info' },
},
{
id: 'username',
title: 'Channel Username',
type: 'short-input',
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',
placeholder: 'Enter YouTube channel ID',
required: true,
condition: { field: 'operation', value: 'youtube_channel_videos' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_channel_videos' },
},
{
id: 'order',
title: 'Sort Order',
type: 'dropdown',
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' },
mode: 'advanced',
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_channel_videos' },
mode: 'advanced',
},
// Get Channel Playlists operation inputs
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
placeholder: 'Enter YouTube channel ID',
required: true,
condition: { field: 'operation', value: 'youtube_channel_playlists' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_channel_playlists' },
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_channel_playlists' },
mode: 'advanced',
},
// Get Playlist Items operation inputs
{
id: 'playlistId',
title: 'Playlist ID',
type: 'short-input',
placeholder: 'Enter YouTube playlist ID',
required: true,
condition: { field: 'operation', value: 'youtube_playlist_items' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 50,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_playlist_items' },
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_playlist_items' },
mode: 'advanced',
},
// Get Video Comments operation inputs
{
id: 'videoId',
title: 'Video ID',
type: 'short-input',
placeholder: 'Enter YouTube video ID',
required: true,
condition: { field: 'operation', value: 'youtube_comments' },
},
{
id: 'maxResults',
title: 'Max Results',
type: 'slider',
min: 1,
max: 100,
step: 1,
integer: true,
condition: { field: 'operation', value: 'youtube_comments' },
},
{
id: 'order',
title: 'Sort Order',
type: 'dropdown',
options: [
{ label: 'Most Relevant', id: 'relevance' },
{ label: 'Most Recent', id: 'time' },
],
value: () => 'relevance',
condition: { field: 'operation', value: 'youtube_comments' },
mode: 'advanced',
},
{
id: 'pageToken',
title: 'Page Token',
type: 'short-input',
placeholder: 'Token for pagination (from nextPageToken)',
condition: { field: 'operation', value: 'youtube_comments' },
mode: 'advanced',
},
// API Key (common to all operations)
{
id: 'apiKey',
title: 'YouTube API Key',
type: 'short-input',
placeholder: 'Enter YouTube API Key',
password: true,
required: true,
},
],
tools: {
access: [
'youtube_channel_info',
'youtube_channel_playlists',
'youtube_channel_videos',
'youtube_comments',
'youtube_playlist_items',
'youtube_search',
'youtube_trending',
'youtube_video_categories',
'youtube_video_details',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'youtube_search':
return 'youtube_search'
case 'youtube_trending':
return 'youtube_trending'
case 'youtube_video_details':
return 'youtube_video_details'
case 'youtube_video_categories':
return 'youtube_video_categories'
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_comments':
return 'youtube_comments'
default:
return 'youtube_search'
}
},
params: (params) => {
const result: Record<string, unknown> = {}
if (params.maxResults) result.maxResults = Number(params.maxResults)
return result
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'YouTube API key' },
// Search Videos
query: { type: 'string', description: 'Search query' },
maxResults: { type: 'number', description: 'Maximum number of results' },
pageToken: { type: 'string', description: 'Page token for pagination' },
// 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' },
eventType: { type: 'string', description: 'Live stream filter (live/upcoming/completed)' },
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' },
hl: { type: 'string', description: 'Language for category names' },
// Video Details & Comments
videoId: { type: 'string', description: 'YouTube video ID' },
// Channel Info
channelId: { type: 'string', description: 'YouTube channel ID' },
username: { type: 'string', description: 'YouTube channel username' },
// Playlist Items
playlistId: { type: 'string', description: 'YouTube playlist ID' },
// Sort Order (used by multiple operations)
order: { type: 'string', description: 'Sort order' },
},
outputs: {
// Search Videos, Trending, Playlist Items, Captions, Categories
items: { type: 'json', description: 'List of items returned' },
totalResults: { type: 'number', description: 'Total number of results' },
nextPageToken: { type: 'string', description: 'Token for next page' },
// Video Details
videoId: { type: 'string', description: 'Video ID' },
title: { type: 'string', description: 'Video or channel title' },
description: { type: 'string', description: 'Video or channel description' },
channelId: { type: 'string', description: 'Channel ID' },
channelTitle: { type: 'string', description: 'Channel name' },
publishedAt: { type: 'string', description: 'Published date' },
duration: { type: 'string', description: 'Video duration' },
viewCount: { type: 'number', description: 'View count' },
likeCount: { type: 'number', description: 'Like count' },
commentCount: { type: 'number', description: 'Comment count' },
favoriteCount: { type: 'number', description: 'Favorite count' },
thumbnail: { type: 'string', description: 'Thumbnail URL' },
tags: { type: 'json', description: 'Video tags' },
categoryId: { type: 'string', description: 'Video category ID' },
definition: { type: 'string', description: 'Video definition (hd/sd)' },
caption: { type: 'string', description: 'Has captions (true/false)' },
licensedContent: { type: 'boolean', description: 'Is licensed content' },
privacyStatus: { type: 'string', description: 'Privacy status' },
liveBroadcastContent: { type: 'string', description: 'Live broadcast status' },
defaultLanguage: { type: 'string', description: 'Default language' },
defaultAudioLanguage: { type: 'string', description: 'Default audio language' },
// Live Streaming Details
isLiveContent: { type: 'boolean', description: 'Whether video is/was a live stream' },
scheduledStartTime: { type: 'string', description: 'Scheduled start time for live streams' },
actualStartTime: { type: 'string', description: 'Actual start time of live stream' },
actualEndTime: { type: 'string', description: 'End time of live stream' },
concurrentViewers: { type: 'number', description: 'Current viewers (live only)' },
activeLiveChatId: { type: 'string', description: 'Live chat ID' },
// Channel Info
subscriberCount: { type: 'number', description: 'Subscriber count' },
videoCount: { type: 'number', description: 'Total video count' },
customUrl: { type: 'string', description: 'Channel custom URL' },
country: { type: 'string', description: 'Channel country' },
uploadsPlaylistId: { type: 'string', description: 'Uploads playlist ID' },
bannerImageUrl: { type: 'string', description: 'Channel banner URL' },
hiddenSubscriberCount: { type: 'boolean', description: 'Is subscriber count hidden' },
// Video Categories
assignable: { type: 'boolean', description: 'Whether category can be assigned' },
},
}
export const YouTubeBlockMeta = {
tags: ['marketing', 'content-management'],
url: 'https://www.youtube.com',
templates: [
{
icon: YouTubeIcon,
title: 'Content repurposer',
prompt:
'Build a workflow that takes a YouTube video URL, pulls the video details and description, researches the topic on the web for additional context, and generates a Twitter thread, LinkedIn post, and blog summary optimized for each platform.',
modules: ['agent', 'files', 'workflows'],
category: 'marketing',
tags: ['marketing', 'content', 'automation'],
},
{
icon: ElevenLabsIcon,
title: 'YouTube video audio brief',
prompt:
'Build a workflow that takes a YouTube URL, pulls the video details and top comments, summarizes them with an agent, narrates the summary with ElevenLabs, and saves the audio file for distribution.',
modules: ['agent', 'files', 'workflows'],
category: 'marketing',
tags: ['content', 'automation'],
alsoIntegrations: ['elevenlabs'],
},
{
icon: YouTubeIcon,
title: 'YouTube channel performance digest',
prompt:
'Create a scheduled weekly workflow that pulls a YouTube channels public stats and recent videos, ranks the top performers by views, writes a digest file, and emails it to the content team.',
modules: ['scheduled', 'agent', 'files', 'workflows'],
category: 'marketing',
tags: ['marketing', 'reporting'],
alsoIntegrations: ['gmail'],
},
{
icon: YouTubeIcon,
title: 'YouTube comment triage',
prompt:
'Build a scheduled workflow that pulls recent YouTube comments on a video, classifies each as helpful, question, or spam, drafts suggested answers to questions using a knowledge base, and routes them to the community team in Slack for review.',
modules: ['scheduled', 'knowledge-base', 'agent', 'workflows'],
category: 'marketing',
tags: ['marketing', 'community'],
alsoIntegrations: ['slack'],
},
{
icon: YouTubeIcon,
title: 'YouTube upload-to-blog',
prompt:
'Create a scheduled workflow that polls a YouTube channel for new uploads, pulls each videos details and description, generates a long-form blog post with proper SEO structure, and stages it as a WordPress draft.',
modules: ['scheduled', 'agent', 'workflows'],
category: 'marketing',
tags: ['marketing', 'content'],
alsoIntegrations: ['wordpress'],
},
{
icon: YouTubeIcon,
title: 'YouTube competitor watcher',
prompt:
'Build a scheduled workflow that monitors competitor YouTube channels, flags videos that exceed average performance, and writes outline of their format to a content-research table.',
modules: ['scheduled', 'tables', 'agent', 'workflows'],
category: 'marketing',
tags: ['marketing', 'research'],
},
{
icon: YouTubeIcon,
title: 'YouTube video curation finder',
prompt:
'Create a workflow that reads a tables-based topic list, finds matching YouTube videos via search, scores each for relevance, writes the candidates back to the table, and pings the editorial team for the final cut.',
modules: ['tables', 'agent', 'workflows'],
category: 'marketing',
tags: ['marketing', 'content'],
},
{
icon: YouTubeIcon,
title: 'YouTube video recap to Notion',
prompt:
'Build a workflow that takes a YouTube video URL, pulls the video details, description, and top comments, summarizes the highlights and audience reaction, and saves a recap to a Notion page for the marketing team.',
modules: ['agent', 'workflows'],
category: 'marketing',
tags: ['marketing', 'content'],
alsoIntegrations: ['notion'],
},
],
skills: [
{
name: 'find-videos-on-topic',
description:
'Search YouTube for videos on a topic with filters for duration, recency, and quality.',
content:
'# Find YouTube Videos on a Topic\n\nSurface the most relevant videos for a subject.\n\n## Steps\n1. Build a search query and choose filters: order (relevance, date, view count), duration, and definition.\n2. Call the search operation with a result limit.\n3. For promising results, get video details to read title, channel, view count, and publish date.\n4. Rank the results by relevance and signal.\n\n## Output\nReturn a ranked list of videos with title, channel, URL, view count, and publish date. Note the query and filters applied.',
},
{
name: 'analyze-channel',
description:
'Pull a YouTube channel profile and recent uploads to summarize its content and cadence.',
content:
'# Analyze a YouTube Channel\n\nProfile a channel and its recent output.\n\n## Steps\n1. Get channel info to retrieve subscriber count, total views, and description.\n2. Get channel videos to list recent uploads.\n3. Summarize the content themes, upload cadence, and the best-performing recent videos.\n\n## Output\nReturn a channel summary with key stats, dominant content themes, posting frequency, and the top recent videos by views. Cite the channel and video IDs used.',
},
{
name: 'summarize-video-comments',
description:
'Fetch comments on a YouTube video and summarize sentiment, questions, and recurring feedback.',
content:
'# Summarize YouTube Video Comments\n\nUnderstand audience reaction to a video.\n\n## Steps\n1. Identify the video ID, searching or using video details if only a title is known.\n2. Call the comments operation to fetch top or recent comments.\n3. Group comments into sentiment, recurring questions, and feature or content requests.\n\n## Output\nReturn a summary with sentiment breakdown, the most common questions, and notable feedback themes. Quote a few representative comments and cite the video ID.',
},
],
} as const satisfies BlockMeta