| const { ytToolkit } = require('@librechat/api'); |
| const { tool } = require('@langchain/core/tools'); |
| const { youtube } = require('@googleapis/youtube'); |
| const { logger } = require('@librechat/data-schemas'); |
| const { YoutubeTranscript } = require('youtube-transcript'); |
| const { getApiKey } = require('./credentials'); |
|
|
| function extractVideoId(url) { |
| const rawIdRegex = /^[a-zA-Z0-9_-]{11}$/; |
| if (rawIdRegex.test(url)) { |
| return url; |
| } |
|
|
| const regex = new RegExp( |
| '(?:youtu\\.be/|youtube(?:\\.com)?/(?:' + |
| '(?:watch\\?v=)|(?:embed/)|(?:shorts/)|(?:live/)|(?:v/)|(?:/))?)' + |
| '([a-zA-Z0-9_-]{11})(?:\\S+)?$', |
| ); |
| const match = url.match(regex); |
| return match ? match[1] : null; |
| } |
|
|
| function parseTranscript(transcriptResponse) { |
| if (!Array.isArray(transcriptResponse)) { |
| return ''; |
| } |
|
|
| return transcriptResponse |
| .map((entry) => entry.text.trim()) |
| .filter((text) => text) |
| .join(' ') |
| .replaceAll(''', "'"); |
| } |
|
|
| function createYouTubeTools(fields = {}) { |
| const envVar = 'YOUTUBE_API_KEY'; |
| const override = fields.override ?? false; |
| const apiKey = fields.apiKey ?? fields[envVar] ?? getApiKey(envVar, override); |
|
|
| const youtubeClient = youtube({ |
| version: 'v3', |
| auth: apiKey, |
| }); |
|
|
| const searchTool = tool(async ({ query, maxResults = 5 }) => { |
| const response = await youtubeClient.search.list({ |
| part: 'snippet', |
| q: query, |
| type: 'video', |
| maxResults: maxResults || 5, |
| }); |
| const result = response.data.items.map((item) => ({ |
| title: item.snippet.title, |
| description: item.snippet.description, |
| url: `https://www.youtube.com/watch?v=${item.id.videoId}`, |
| })); |
| return JSON.stringify(result, null, 2); |
| }, ytToolkit.youtube_search); |
|
|
| const infoTool = tool(async ({ url }) => { |
| const videoId = extractVideoId(url); |
| if (!videoId) { |
| throw new Error('Invalid YouTube URL or video ID'); |
| } |
|
|
| const response = await youtubeClient.videos.list({ |
| part: 'snippet,statistics', |
| id: videoId, |
| }); |
|
|
| if (!response.data.items?.length) { |
| throw new Error('Video not found'); |
| } |
| const video = response.data.items[0]; |
|
|
| const result = { |
| title: video.snippet.title, |
| description: video.snippet.description, |
| views: video.statistics.viewCount, |
| likes: video.statistics.likeCount, |
| comments: video.statistics.commentCount, |
| }; |
| return JSON.stringify(result, null, 2); |
| }, ytToolkit.youtube_info); |
|
|
| const commentsTool = tool(async ({ url, maxResults = 10 }) => { |
| const videoId = extractVideoId(url); |
| if (!videoId) { |
| throw new Error('Invalid YouTube URL or video ID'); |
| } |
|
|
| const response = await youtubeClient.commentThreads.list({ |
| part: 'snippet', |
| videoId, |
| maxResults: maxResults || 10, |
| }); |
|
|
| const result = response.data.items.map((item) => ({ |
| author: item.snippet.topLevelComment.snippet.authorDisplayName, |
| text: item.snippet.topLevelComment.snippet.textDisplay, |
| likes: item.snippet.topLevelComment.snippet.likeCount, |
| })); |
| return JSON.stringify(result, null, 2); |
| }, ytToolkit.youtube_comments); |
|
|
| const transcriptTool = tool(async ({ url }) => { |
| const videoId = extractVideoId(url); |
| if (!videoId) { |
| throw new Error('Invalid YouTube URL or video ID'); |
| } |
|
|
| try { |
| try { |
| const transcript = await YoutubeTranscript.fetchTranscript(videoId, { lang: 'en' }); |
| return parseTranscript(transcript); |
| } catch (e) { |
| logger.error(e); |
| } |
|
|
| try { |
| const transcript = await YoutubeTranscript.fetchTranscript(videoId, { lang: 'de' }); |
| return parseTranscript(transcript); |
| } catch (e) { |
| logger.error(e); |
| } |
|
|
| const transcript = await YoutubeTranscript.fetchTranscript(videoId); |
| return parseTranscript(transcript); |
| } catch (error) { |
| throw new Error(`Failed to fetch transcript: ${error.message}`); |
| } |
| }, ytToolkit.youtube_transcript); |
|
|
| return [searchTool, infoTool, commentsTool, transcriptTool]; |
| } |
|
|
| module.exports = createYouTubeTools; |
|
|