/
smaker21
/
YouTube-transcript-using-supadata
Обзор
Документация
Войти
/
smaker21
/
YouTube-transcript-using-supadata
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
backend/src/services/supadata.ts
120 строк
3 KB
Ilya Ostapov
new file: .env.example
12 фев 2026, 18:43
12 фев 2026, 18:43
7016baa
Код
Авторство
О чём код?
import axios from 'axios'; import { env } from '../config/env.js'; import type { VideoMetadata } from '../types/index.js'; /** * Extract video ID from YouTube URL */ function extractVideoId(url: string): string | null { const patterns = [ /(?:youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/, /(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/, ]; for (const pattern of patterns) { const match = url.match(pattern); if (match) return match[1]; } return null; } /** * Format duration from seconds to mm:ss or hh:mm:ss */ function formatDuration(seconds: number): string { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; if (hours > 0) { return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } return `${minutes}:${secs.toString().padStart(2, '0')}`; } /** * Fetch transcript from Supadata API */ export async function fetchTranscript( url: string, lang: string = 'en' ): Promise<string> { try { const { data } = await axios.get(`${env.SUPADATA_BASE_URL}/v1/youtube/transcript`, { headers: { 'x-api-key': env.SUPADATA_API_KEY, }, params: { url, lang, format: 'text', }, timeout: 15000, }); if (!data.content) { throw new Error('No transcript available for this video'); } return data.content; } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === 404) { throw new Error('Transcript not found for this video'); } if (error.response?.status === 403) { throw new Error('Access denied to video transcript'); } throw new Error( `Failed to fetch transcript: ${error.response?.data?.message || error.message}` ); } throw error; } } /** * Fetch video metadata from Supadata API */ export async function fetchVideoMetadata(url: string): Promise<VideoMetadata> { const videoId = extractVideoId(url); if (!videoId) { throw new Error('Invalid YouTube URL'); } try { const { data } = await axios.get(`${env.SUPADATA_BASE_URL}/v1/youtube/video`, { headers: { 'x-api-key': env.SUPADATA_API_KEY, }, params: { id: videoId, }, timeout: 10000, }); return { title: data.title || 'Unknown Title', channel: data.channel?.name || 'Unknown Channel', duration: formatDuration(data.duration || 0), thumbnail: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, }; } catch (error) { if (axios.isAxiosError(error)) { // If metadata fails, return defaults console.warn('Failed to fetch video metadata, using defaults'); return { title: 'YouTube Video', channel: 'Unknown Channel', duration: '0:00', thumbnail: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, }; } throw error; } }