unified-video-framework
Version:
Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more
90 lines • 3.58 kB
JavaScript
export class YouTubeExtractor {
static isYouTubeUrl(url) {
return this.YOUTUBE_REGEX.test(url);
}
static extractVideoId(url) {
const match = url.match(this.YOUTUBE_REGEX);
return match ? match[1] : null;
}
static async getVideoMetadata(url) {
try {
const apiUrl = `${this.YOUTUBE_NOEMBED_API}${encodeURIComponent(url)}`;
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error('Failed to fetch YouTube metadata');
}
const data = await response.json();
return {
title: data.title || 'YouTube Video',
thumbnail: data.thumbnail_url || `https://img.youtube.com/vi/${this.extractVideoId(url)}/maxresdefault.jpg`,
duration: data.duration || undefined
};
}
catch (error) {
console.warn('Failed to fetch YouTube metadata:', error);
const videoId = this.extractVideoId(url);
return {
title: 'YouTube Video',
thumbnail: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`,
duration: undefined
};
}
}
static getEmbedUrl(videoId, showControls = false) {
const controls = showControls ? 1 : 0;
return `https://www.youtube.com/embed/${videoId}?modestbranding=1&rel=0&controls=${controls}`;
}
static async getDirectStreamUrl(videoId, backendEndpoint) {
if (!backendEndpoint) {
console.warn('No backend endpoint provided for YouTube video extraction. Using fallback method.');
return this.getFallbackStreamUrl(videoId);
}
try {
const response = await fetch(backendEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ videoId })
});
if (!response.ok) {
throw new Error('Backend failed to extract stream');
}
const data = await response.json();
return data.streamUrl || null;
}
catch (error) {
console.error('Failed to get direct stream URL:', error);
return this.getFallbackStreamUrl(videoId);
}
}
static getFallbackStreamUrl(videoId) {
return `https://www.youtube.com/watch?v=${videoId}`;
}
static async prepareYouTubeSource(url, backendEndpoint) {
const videoId = this.extractVideoId(url);
if (!videoId) {
throw new Error('Invalid YouTube URL');
}
const metadata = await this.getVideoMetadata(url);
const streamUrl = await this.getDirectStreamUrl(videoId, backendEndpoint);
return {
url: streamUrl || url,
type: 'youtube',
title: metadata.title,
thumbnail: metadata.thumbnail,
duration: metadata.duration,
videoId: videoId,
isYouTube: true,
metadata: {
source: 'youtube',
videoId: videoId
}
};
}
}
YouTubeExtractor.YOUTUBE_REGEX = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|embed\/|v\/|live\/)?([a-zA-Z0-9_-]{11})/;
YouTubeExtractor.YOUTUBE_NOEMBED_API = 'https://noembed.com/embed?url=';
YouTubeExtractor.YOUTUBE_API_ENDPOINT = 'https://www.youtube.com/oembed?url=';
export default YouTubeExtractor;
//# sourceMappingURL=YouTubeExtractor.js.map