Skip to main content
Glama

YouTube MCP Server

by kevinwatt
config.ts6.93 kB
import * as os from "os"; import * as path from "path"; type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; }; /** * Configuration type definitions */ export interface Config { // File-related configuration file: { maxFilenameLength: number; downloadsDir: string; tempDirPrefix: string; // Filename processing configuration sanitize: { // Character to replace illegal characters replaceChar: string; // Suffix when truncating filenames truncateSuffix: string; // Regular expression for illegal characters illegalChars: RegExp; // List of reserved names reservedNames: readonly string[]; }; }; // Tool-related configuration tools: { required: readonly string[]; }; // Download-related configuration download: { defaultResolution: "480p" | "720p" | "1080p" | "best"; defaultAudioFormat: "m4a" | "mp3"; defaultSubtitleLanguage: string; }; } /** * Default configuration */ const defaultConfig: Config = { file: { maxFilenameLength: 50, downloadsDir: path.join(os.homedir(), "Downloads"), tempDirPrefix: "ytdlp-", sanitize: { replaceChar: '_', truncateSuffix: '...', illegalChars: /[<>:"/\\|?*\x00-\x1F]/g, // Windows illegal characters reservedNames: [ 'CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9' ] } }, tools: { required: ['yt-dlp'] }, download: { defaultResolution: "720p", defaultAudioFormat: "m4a", defaultSubtitleLanguage: "en" } }; /** * Load configuration from environment variables */ function loadEnvConfig(): DeepPartial<Config> { const envConfig: DeepPartial<Config> = {}; // File configuration const fileConfig: DeepPartial<Config['file']> = { sanitize: { replaceChar: process.env.YTDLP_SANITIZE_REPLACE_CHAR, truncateSuffix: process.env.YTDLP_SANITIZE_TRUNCATE_SUFFIX, illegalChars: process.env.YTDLP_SANITIZE_ILLEGAL_CHARS ? new RegExp(process.env.YTDLP_SANITIZE_ILLEGAL_CHARS) : undefined, reservedNames: process.env.YTDLP_SANITIZE_RESERVED_NAMES?.split(',') } }; if (process.env.YTDLP_MAX_FILENAME_LENGTH) { fileConfig.maxFilenameLength = parseInt(process.env.YTDLP_MAX_FILENAME_LENGTH); } if (process.env.YTDLP_DOWNLOADS_DIR) { fileConfig.downloadsDir = process.env.YTDLP_DOWNLOADS_DIR; } if (process.env.YTDLP_TEMP_DIR_PREFIX) { fileConfig.tempDirPrefix = process.env.YTDLP_TEMP_DIR_PREFIX; } if (Object.keys(fileConfig).length > 0) { envConfig.file = fileConfig; } // Download configuration const downloadConfig: Partial<Config['download']> = {}; if (process.env.YTDLP_DEFAULT_RESOLUTION && ['480p', '720p', '1080p', 'best'].includes(process.env.YTDLP_DEFAULT_RESOLUTION)) { downloadConfig.defaultResolution = process.env.YTDLP_DEFAULT_RESOLUTION as Config['download']['defaultResolution']; } if (process.env.YTDLP_DEFAULT_AUDIO_FORMAT && ['m4a', 'mp3'].includes(process.env.YTDLP_DEFAULT_AUDIO_FORMAT)) { downloadConfig.defaultAudioFormat = process.env.YTDLP_DEFAULT_AUDIO_FORMAT as Config['download']['defaultAudioFormat']; } if (process.env.YTDLP_DEFAULT_SUBTITLE_LANG) { downloadConfig.defaultSubtitleLanguage = process.env.YTDLP_DEFAULT_SUBTITLE_LANG; } if (Object.keys(downloadConfig).length > 0) { envConfig.download = downloadConfig; } return envConfig; } /** * Validate configuration */ function validateConfig(config: Config): void { // Validate filename length if (config.file.maxFilenameLength < 5) { throw new Error('maxFilenameLength must be at least 5'); } // Validate downloads directory if (!config.file.downloadsDir) { throw new Error('downloadsDir must be specified'); } // Validate temporary directory prefix if (!config.file.tempDirPrefix) { throw new Error('tempDirPrefix must be specified'); } // Validate default resolution if (!['480p', '720p', '1080p', 'best'].includes(config.download.defaultResolution)) { throw new Error('Invalid defaultResolution'); } // Validate default audio format if (!['m4a', 'mp3'].includes(config.download.defaultAudioFormat)) { throw new Error('Invalid defaultAudioFormat'); } // Validate default subtitle language if (!/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/i.test(config.download.defaultSubtitleLanguage)) { throw new Error('Invalid defaultSubtitleLanguage'); } } /** * Merge configuration */ function mergeConfig(base: Config, override: DeepPartial<Config>): Config { return { file: { maxFilenameLength: override.file?.maxFilenameLength || base.file.maxFilenameLength, downloadsDir: override.file?.downloadsDir || base.file.downloadsDir, tempDirPrefix: override.file?.tempDirPrefix || base.file.tempDirPrefix, sanitize: { replaceChar: override.file?.sanitize?.replaceChar || base.file.sanitize.replaceChar, truncateSuffix: override.file?.sanitize?.truncateSuffix || base.file.sanitize.truncateSuffix, illegalChars: (override.file?.sanitize?.illegalChars || base.file.sanitize.illegalChars) as RegExp, reservedNames: (override.file?.sanitize?.reservedNames || base.file.sanitize.reservedNames) as readonly string[] } }, tools: { required: (override.tools?.required || base.tools.required) as readonly string[] }, download: { defaultResolution: override.download?.defaultResolution || base.download.defaultResolution, defaultAudioFormat: override.download?.defaultAudioFormat || base.download.defaultAudioFormat, defaultSubtitleLanguage: override.download?.defaultSubtitleLanguage || base.download.defaultSubtitleLanguage } }; } /** * Load configuration */ export function loadConfig(): Config { const envConfig = loadEnvConfig(); const config = mergeConfig(defaultConfig, envConfig); validateConfig(config); return config; } /** * Safe filename processing function */ export function sanitizeFilename(filename: string, config: Config['file']): string { // Remove illegal characters let safe = filename.replace(config.sanitize.illegalChars, config.sanitize.replaceChar); // Check reserved names const basename = path.parse(safe).name.toUpperCase(); if (config.sanitize.reservedNames.includes(basename)) { safe = `_${safe}`; } // Handle length limitation if (safe.length > config.maxFilenameLength) { const ext = path.extname(safe); const name = safe.slice(0, config.maxFilenameLength - ext.length - config.sanitize.truncateSuffix.length); safe = `${name}${config.sanitize.truncateSuffix}${ext}`; } return safe; } // Export current configuration instance export const CONFIG = loadConfig();

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