Skip to main content
Glama

YouTube MCP Server

by kevinwatt
metadata.test.ts8.02 kB
// @ts-nocheck // @jest-environment node import { describe, test, expect, beforeAll } from '@jest/globals'; import { getVideoMetadata, getVideoMetadataSummary } from '../modules/metadata.js'; import type { VideoMetadata } from '../modules/metadata.js'; import { CONFIG } from '../config.js'; // 設置 Python 環境 process.env.PYTHONPATH = ''; process.env.PYTHONHOME = ''; describe('Video Metadata Extraction', () => { const testUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw'; describe('getVideoMetadata', () => { test('should extract basic metadata from YouTube video', async () => { const metadataJson = await getVideoMetadata(testUrl); const metadata: VideoMetadata = JSON.parse(metadataJson); // 驗證基本字段存在 expect(metadata).toHaveProperty('id'); expect(metadata).toHaveProperty('title'); expect(metadata).toHaveProperty('uploader'); expect(metadata).toHaveProperty('duration'); expect(metadata.id).toBe('jNQXAC9IVRw'); expect(typeof metadata.title).toBe('string'); expect(typeof metadata.uploader).toBe('string'); expect(typeof metadata.duration).toBe('number'); }); test('should extract specific fields when requested', async () => { const fields = ['id', 'title', 'description', 'channel', 'timestamp']; const metadataJson = await getVideoMetadata(testUrl, fields); const metadata = JSON.parse(metadataJson); // 應該只包含請求的字段 expect(Object.keys(metadata)).toEqual(expect.arrayContaining(fields.filter(f => metadata[f] !== undefined))); // 不應該包含其他字段(如果它們存在於原始數據中) expect(metadata).not.toHaveProperty('formats'); expect(metadata).not.toHaveProperty('thumbnails'); }); test('should handle empty fields array gracefully', async () => { const metadataJson = await getVideoMetadata(testUrl, []); const metadata = JSON.parse(metadataJson); // 空數組應該返回空對象 expect(metadata).toEqual({}); }); test('should handle non-existent fields gracefully', async () => { const fields = ['id', 'title', 'non_existent_field', 'another_fake_field']; const metadataJson = await getVideoMetadata(testUrl, fields); const metadata = JSON.parse(metadataJson); // 應該包含存在的字段 expect(metadata).toHaveProperty('id'); expect(metadata).toHaveProperty('title'); // 不應該包含不存在的字段 expect(metadata).not.toHaveProperty('non_existent_field'); expect(metadata).not.toHaveProperty('another_fake_field'); }); test('should throw error for invalid URL', async () => { await expect(getVideoMetadata('invalid-url')).rejects.toThrow(); await expect(getVideoMetadata('https://invalid-domain.com/video')).rejects.toThrow(); }); test('should include requested metadata fields from issue #16', async () => { const fields = ['id', 'title', 'description', 'creators', 'timestamp', 'channel', 'channel_id', 'channel_url']; const metadataJson = await getVideoMetadata(testUrl, fields); const metadata = JSON.parse(metadataJson); // 驗證 issue #16 中請求的字段 expect(metadata).toHaveProperty('id'); expect(metadata).toHaveProperty('title'); expect(metadata.id).toBe('jNQXAC9IVRw'); expect(typeof metadata.title).toBe('string'); // 這些字段可能存在也可能不存在,取決於視頻 if (metadata.description !== undefined) { expect(typeof metadata.description).toBe('string'); } if (metadata.creators !== undefined) { expect(Array.isArray(metadata.creators)).toBe(true); } if (metadata.timestamp !== undefined) { expect(typeof metadata.timestamp).toBe('number'); } if (metadata.channel !== undefined) { expect(typeof metadata.channel).toBe('string'); } if (metadata.channel_id !== undefined) { expect(typeof metadata.channel_id).toBe('string'); } if (metadata.channel_url !== undefined) { expect(typeof metadata.channel_url).toBe('string'); } }); }); describe('getVideoMetadataSummary', () => { test('should generate human-readable summary', async () => { const summary = await getVideoMetadataSummary(testUrl); expect(typeof summary).toBe('string'); expect(summary.length).toBeGreaterThan(0); // 應該包含基本信息 expect(summary).toMatch(/Title:/); // 可能包含的其他字段 const commonFields = ['Channel:', 'Duration:', 'Views:', 'Upload Date:']; const hasAtLeastOneField = commonFields.some(field => summary.includes(field)); expect(hasAtLeastOneField).toBe(true); }); test('should handle videos with different metadata availability', async () => { const summary = await getVideoMetadataSummary(testUrl); // 摘要應該是有效的字符串 expect(typeof summary).toBe('string'); expect(summary.trim().length).toBeGreaterThan(0); // 每行應該有意義的格式 (字段: 值) - 但要注意有些標題可能包含特殊字符 const lines = summary.split('\n').filter(line => line.trim()); expect(lines.length).toBeGreaterThan(0); // 至少應該有一行包含冒號(格式為 "字段: 值") const hasFormattedLines = lines.some(line => line.includes(':')); expect(hasFormattedLines).toBe(true); }, 30000); test('should throw error for invalid URL', async () => { await expect(getVideoMetadataSummary('invalid-url')).rejects.toThrow(); }, 30000); }); describe('Error Handling', () => { test('should provide helpful error message for unavailable video', async () => { const unavailableUrl = 'https://www.youtube.com/watch?v=invalid_video_id_123456789'; await expect(getVideoMetadata(unavailableUrl)).rejects.toThrow(/unavailable|private|not available/i); }); test('should handle network errors gracefully', async () => { // 使用一個應該引起網路錯誤的 URL const badNetworkUrl = 'https://httpstat.us/500'; await expect(getVideoMetadata(badNetworkUrl)).rejects.toThrow(); }); test('should handle unsupported URLs', async () => { const unsupportedUrl = 'https://example.com/not-a-video'; await expect(getVideoMetadata(unsupportedUrl)).rejects.toThrow(); }, 10000); }); describe('Real-world Integration', () => { test('should work with different video platforms supported by yt-dlp', async () => { // 只測試 YouTube,因為其他平台的可用性可能會變化 const youtubeUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw'; const metadataJson = await getVideoMetadata(youtubeUrl, ['id', 'title', 'extractor']); const metadata = JSON.parse(metadataJson); expect(metadata.extractor).toMatch(/youtube/i); expect(metadata.id).toBe('jNQXAC9IVRw'); }); test('should extract metadata that matches issue #16 requirements', async () => { const requiredFields = ['id', 'title', 'description', 'creators', 'timestamp', 'channel', 'channel_id', 'channel_url']; const metadataJson = await getVideoMetadata(testUrl, requiredFields); const metadata = JSON.parse(metadataJson); // 驗證至少有基本字段 expect(metadata).toHaveProperty('id'); expect(metadata).toHaveProperty('title'); // 記錄實際返回的字段以便調試 console.log('Available metadata fields for issue #16:', Object.keys(metadata)); // 檢查每個請求的字段是否存在或者有合理的替代 const availableFields = Object.keys(metadata); const hasRequiredBasics = availableFields.includes('id') && availableFields.includes('title'); expect(hasRequiredBasics).toBe(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