c9ai
Version:
Universal AI assistant with vibe-based workflows, hybrid cloud+local AI, and comprehensive tool integration
247 lines (208 loc) • 7.34 kB
JavaScript
/**
* YouTube Data API Integration
* Provides video search functionality
*/
;
const fetch = (...a) => import("node-fetch").then(({default:f}) => f(...a));
const { APIConfig } = require('./api-config');
class YouTubeAPI {
constructor() {
this.config = new APIConfig();
this.baseUrl = 'https://www.googleapis.com/youtube/v3';
}
/**
* Search for videos on YouTube
*/
async searchVideos(query, options = {}) {
try {
const {
maxResults = 1,
type = 'video',
order = 'relevance',
publishedAfter,
publishedBefore,
channelId,
regionCode,
safeSearch = 'moderate'
} = options;
if (!query || query.trim() === '') {
throw new Error('Search query is required');
}
const apiKey = this.config.getYouTubeApiKey();
// Build search URL with parameters
const searchParams = new URLSearchParams({
part: 'snippet',
q: query.trim(),
type: type,
key: apiKey,
maxResults: Math.min(maxResults, 50), // YouTube API limit
order: order,
safeSearch: safeSearch
});
// Add optional parameters
if (publishedAfter) searchParams.append('publishedAfter', publishedAfter);
if (publishedBefore) searchParams.append('publishedBefore', publishedBefore);
if (channelId) searchParams.append('channelId', channelId);
if (regionCode) searchParams.append('regionCode', regionCode);
const url = `${this.baseUrl}/search?${searchParams.toString()}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'User-Agent': 'c9ai-client/1.0'
}
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`YouTube API request failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const data = await response.json();
// Process and format the results
const processedResults = data.items?.map(item => ({
videoId: item.id.videoId,
title: item.snippet.title,
description: item.snippet.description,
thumbnails: item.snippet.thumbnails,
channelTitle: item.snippet.channelTitle,
channelId: item.snippet.channelId,
publishedAt: item.snippet.publishedAt,
url: `https://www.youtube.com/watch?v=${item.id.videoId}`,
embedUrl: `https://www.youtube.com/embed/${item.id.videoId}`
})) || [];
return {
success: true,
query: query,
totalResults: data.pageInfo?.totalResults || 0,
resultsPerPage: data.pageInfo?.resultsPerPage || 0,
videos: processedResults,
searchedAt: new Date().toISOString()
};
} catch (error) {
throw new Error(`YouTube search failed: ${error.message}`);
}
}
/**
* Get video details by ID
*/
async getVideoDetails(videoId) {
try {
if (!videoId) {
throw new Error('Video ID is required');
}
const apiKey = this.config.getYouTubeApiKey();
const searchParams = new URLSearchParams({
part: 'snippet,statistics,contentDetails',
id: videoId,
key: apiKey
});
const url = `${this.baseUrl}/videos?${searchParams.toString()}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'User-Agent': 'c9ai-client/1.0'
}
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`YouTube API request failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const data = await response.json();
if (!data.items || data.items.length === 0) {
throw new Error('Video not found');
}
const video = data.items[0];
return {
success: true,
video: {
videoId: video.id,
title: video.snippet.title,
description: video.snippet.description,
thumbnails: video.snippet.thumbnails,
channelTitle: video.snippet.channelTitle,
channelId: video.snippet.channelId,
publishedAt: video.snippet.publishedAt,
duration: video.contentDetails?.duration,
viewCount: video.statistics?.viewCount,
likeCount: video.statistics?.likeCount,
commentCount: video.statistics?.commentCount,
url: `https://www.youtube.com/watch?v=${video.id}`,
embedUrl: `https://www.youtube.com/embed/${video.id}`
},
fetchedAt: new Date().toISOString()
};
} catch (error) {
throw new Error(`YouTube video details fetch failed: ${error.message}`);
}
}
/**
* Search for trending videos
*/
async getTrendingVideos(regionCode = 'US', maxResults = 10) {
try {
const apiKey = this.config.getYouTubeApiKey();
const searchParams = new URLSearchParams({
part: 'snippet,statistics',
chart: 'mostPopular',
regionCode: regionCode,
maxResults: Math.min(maxResults, 50),
key: apiKey
});
const url = `${this.baseUrl}/videos?${searchParams.toString()}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'User-Agent': 'c9ai-client/1.0'
}
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`YouTube API request failed: ${response.status} ${response.statusText} - ${errorText}`);
}
const data = await response.json();
const trendingVideos = data.items?.map(item => ({
videoId: item.id,
title: item.snippet.title,
description: item.snippet.description,
thumbnails: item.snippet.thumbnails,
channelTitle: item.snippet.channelTitle,
channelId: item.snippet.channelId,
publishedAt: item.snippet.publishedAt,
viewCount: item.statistics?.viewCount,
likeCount: item.statistics?.likeCount,
url: `https://www.youtube.com/watch?v=${item.id}`,
embedUrl: `https://www.youtube.com/embed/${item.id}`
})) || [];
return {
success: true,
region: regionCode,
videos: trendingVideos,
count: trendingVideos.length,
fetchedAt: new Date().toISOString()
};
} catch (error) {
throw new Error(`YouTube trending videos fetch failed: ${error.message}`);
}
}
/**
* Helper method to get API status
*/
async getAPIStatus() {
const status = {
hasApiKey: !!this.config.config.youtubeApiKey,
baseUrl: this.baseUrl,
endpoints: {
search: '/search',
videos: '/videos'
}
};
// Test basic connectivity with a simple search
try {
await this.searchVideos('test', { maxResults: 1 });
status.connectivity = 'ok';
} catch (error) {
status.connectivity = 'failed';
status.error = error.message;
}
return status;
}
}
module.exports = { YouTubeAPI };