import { BasePlatform, type ActorConfig } from './base.js';
import type { UnifiedPost } from '../types.js';
export class YouTubePlatform extends BasePlatform {
readonly config: ActorConfig = {
actorId: 'streamers/youtube-scraper',
platform: 'youtube',
defaultMaxResults: 30,
};
buildInput(params: Record<string, unknown>): Record<string, unknown> {
const input: Record<string, unknown> = {
maxResults: params.max_results ?? this.config.defaultMaxResults,
};
if (params.search) {
input.searchKeywords = params.search;
input.type = 'search';
}
if (params.channel_urls) {
input.startUrls = (params.channel_urls as string[]).map(url => ({ url }));
input.type = 'channel';
}
return input;
}
normalize(raw: Record<string, unknown>[]): UnifiedPost[] {
return raw.map(item => {
const text = this.safeString(item.title ?? '');
const description = this.safeString(item.description ?? '');
const combined = `${text}\n${description}`.trim();
return {
id: this.makeId(this.safeString(item.id ?? item.videoId ?? '')),
platform: 'youtube' as const,
author: {
username: this.safeString(item.channelName ?? item.channelTitle ?? ''),
displayName: this.safeString(item.channelName ?? item.channelTitle ?? ''),
followers: this.safeNumber(item.subscriberCount),
verified: Boolean(item.isVerified),
},
content: combined,
url: this.safeString(item.url ?? `https://www.youtube.com/watch?v=${item.id ?? item.videoId}`),
timestamp: this.safeDate(item.date ?? item.publishedAt ?? item.uploadDate),
engagement: {
likes: this.safeNumber(item.likes ?? item.likeCount),
comments: this.safeNumber(item.commentsCount ?? item.commentCount),
shares: 0,
views: this.safeNumber(item.viewCount ?? item.views),
},
media: {
type: 'video',
urls: [this.safeString(item.thumbnailUrl ?? item.thumbnail ?? '')].filter(Boolean),
},
hashtags: this.extractHashtags(combined),
metadata: {
duration: item.duration,
category: item.category,
isLive: item.isLive,
isShort: item.isShort,
},
};
});
}
}