Skip to main content
Glama

YouTube MCP Server

by kevinwatt
subtitle.ts8.15 kB
import * as fs from "fs"; import * as path from "path"; import * as os from "os"; import type { Config } from '../config.js'; import { _spawnPromise, validateUrl, cleanSubtitleToTranscript } from "./utils.js"; /** * Lists all available subtitles for a video. * * @param url - The URL of the video * @returns Promise resolving to a string containing the list of available subtitles * @throws {Error} When URL is invalid or subtitle listing fails * * @example * ```typescript * try { * const subtitles = await listSubtitles('https://youtube.com/watch?v=...'); * console.log('Available subtitles:', subtitles); * } catch (error) { * console.error('Failed to list subtitles:', error); * } * ``` */ export async function listSubtitles(url: string): Promise<string> { if (!validateUrl(url)) { throw new Error('Invalid or unsupported URL format. Please provide a valid video URL (e.g., https://youtube.com/watch?v=...)'); } try { const output = await _spawnPromise('yt-dlp', [ '--ignore-config', '--list-subs', '--write-auto-sub', '--skip-download', '--verbose', url ]); return output; } catch (error) { if (error instanceof Error) { if (error.message.includes("Unsupported URL") || error.message.includes("not supported")) { throw new Error(`Unsupported platform or video URL: ${url}. Ensure the URL is from a supported platform like YouTube.`); } if (error.message.includes("Video unavailable") || error.message.includes("private")) { throw new Error(`Video is unavailable or private: ${url}. Check the URL and video privacy settings.`); } if (error.message.includes("network") || error.message.includes("Connection")) { throw new Error("Network error while fetching subtitles. Check your internet connection and retry."); } } throw error; } } /** * Downloads subtitles for a video in the specified language. * * @param url - The URL of the video * @param language - Language code (e.g., 'en', 'zh-Hant', 'ja') * @param config - Configuration object * @returns Promise resolving to the subtitle content * @throws {Error} When URL is invalid, language is not available, or download fails * * @example * ```typescript * try { * // Download English subtitles * const enSubs = await downloadSubtitles('https://youtube.com/watch?v=...', 'en', config); * console.log('English subtitles:', enSubs); * * // Download Traditional Chinese subtitles * const zhSubs = await downloadSubtitles('https://youtube.com/watch?v=...', 'zh-Hant', config); * console.log('Chinese subtitles:', zhSubs); * } catch (error) { * if (error.message.includes('No subtitle files found')) { * console.warn('No subtitles available in the requested language'); * } else { * console.error('Failed to download subtitles:', error); * } * } * ``` */ export async function downloadSubtitles( url: string, language: string, config: Config ): Promise<string> { if (!validateUrl(url)) { throw new Error('Invalid or unsupported URL format. Please provide a valid video URL (e.g., https://youtube.com/watch?v=...)'); } const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), config.file.tempDirPrefix)); try { await _spawnPromise('yt-dlp', [ '--ignore-config', '--write-sub', '--write-auto-sub', '--sub-lang', language, '--skip-download', '--output', path.join(tempDir, '%(title)s.%(ext)s'), url ]); const subtitleFiles = fs.readdirSync(tempDir) .filter(file => file.endsWith('.vtt')); if (subtitleFiles.length === 0) { throw new Error(`No subtitle files found for language '${language}'. Use ytdlp_list_subtitle_languages to check available options.`); } let output = ''; for (const file of subtitleFiles) { output += fs.readFileSync(path.join(tempDir, file), 'utf8'); } // Check character limit if (output.length > config.limits.characterLimit) { output = output.substring(0, config.limits.characterLimit); output += "\n\n⚠️ Subtitle content truncated due to size. Consider using ytdlp_download_transcript for plain text."; } return output; } catch (error) { if (error instanceof Error) { if (error.message.includes("Unsupported URL") || error.message.includes("not supported")) { throw new Error(`Unsupported platform or video URL: ${url}. Ensure the URL is from a supported platform like YouTube.`); } if (error.message.includes("Video unavailable") || error.message.includes("private")) { throw new Error(`Video is unavailable or private: ${url}. Check the URL and video privacy settings.`); } if (error.message.includes("network") || error.message.includes("Connection")) { throw new Error("Network error while downloading subtitles. Check your internet connection and retry."); } } throw error; } finally { fs.rmSync(tempDir, { recursive: true, force: true }); } } /** * Downloads and cleans subtitles to produce a plain text transcript. * * @param url - The URL of the video * @param language - Language code (e.g., 'en', 'zh-Hant', 'ja') * @param config - Configuration object * @returns Promise resolving to the cleaned transcript text * @throws {Error} When URL is invalid, language is not available, or download fails * * @example * ```typescript * try { * const transcript = await downloadTranscript('https://youtube.com/watch?v=...', 'en', config); * console.log('Transcript:', transcript); * } catch (error) { * console.error('Failed to download transcript:', error); * } * ``` */ export async function downloadTranscript( url: string, language: string, config: Config ): Promise<string> { if (!validateUrl(url)) { throw new Error('Invalid or unsupported URL format. Please provide a valid video URL (e.g., https://youtube.com/watch?v=...)'); } const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), config.file.tempDirPrefix)); try { await _spawnPromise('yt-dlp', [ '--ignore-config', '--skip-download', '--write-subs', '--write-auto-subs', '--sub-lang', language, '--sub-format', 'ttml', '--convert-subs', 'srt', '--output', path.join(tempDir, 'transcript.%(ext)s'), url ]); const srtFiles = fs.readdirSync(tempDir) .filter(file => file.endsWith('.srt')); if (srtFiles.length === 0) { throw new Error(`No subtitle files found for transcript generation in language '${language}'. Use ytdlp_list_subtitle_languages to check available options.`); } let transcriptContent = ''; for (const file of srtFiles) { const srtContent = fs.readFileSync(path.join(tempDir, file), 'utf8'); transcriptContent += cleanSubtitleToTranscript(srtContent) + ' '; } transcriptContent = transcriptContent.trim(); // Transcripts can be larger than standard limit if (transcriptContent.length > config.limits.maxTranscriptLength) { const truncated = transcriptContent.substring(0, config.limits.maxTranscriptLength); transcriptContent = truncated + "\n\n⚠️ Transcript truncated due to length. This is a partial transcript."; } return transcriptContent; } catch (error) { if (error instanceof Error) { if (error.message.includes("Unsupported URL") || error.message.includes("not supported")) { throw new Error(`Unsupported platform or video URL: ${url}. Ensure the URL is from a supported platform like YouTube.`); } if (error.message.includes("Video unavailable") || error.message.includes("private")) { throw new Error(`Video is unavailable or private: ${url}. Check the URL and video privacy settings.`); } if (error.message.includes("network") || error.message.includes("Connection")) { throw new Error("Network error while downloading transcript. Check your internet connection and retry."); } } throw error; } finally { fs.rmSync(tempDir, { recursive: true, force: true }); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kevinwatt/yt-dlp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server