File size: 4,112 Bytes
f0743f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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;