@felores/mcp-video 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +3 -0
- package/CODE_OF_CONDUCT.md +49 -0
- package/COPYING +7 -0
- package/README.md +207 -0
- package/documentation/mcp-inspector-guide.md +308 -0
- package/documentation/mcp-llm-guide.md +379 -0
- package/eslint.config.mjs +88 -0
- package/lib/index.d.mts +2 -0
- package/lib/index.mjs +181 -0
- package/lib/index.mjs.map +1 -0
- package/lib/vtt2txt.d.mts +1 -0
- package/lib/vtt2txt.mjs +22 -0
- package/lib/vtt2txt.mjs.map +1 -0
- package/package.json +28 -0
- package/src/index.mts +207 -0
- package/src/vtt2txt.mts +25 -0
- package/tsconfig.json +28 -0
@@ -0,0 +1,379 @@
|
|
1
|
+
# MCP Development Guide for LLMs
|
2
|
+
|
3
|
+
This guide provides structured information for LLMs to assist in creating, modifying, and using Model Context Protocol (MCP) servers.
|
4
|
+
|
5
|
+
## 1. Core Concepts
|
6
|
+
|
7
|
+
### Protocol Overview
|
8
|
+
- MCP enables standardized communication between LLM applications and integrations
|
9
|
+
- Uses client-server architecture with JSON-RPC 2.0 message format
|
10
|
+
- Latest protocol version: 2024-11-05
|
11
|
+
- Supports stdio and SSE transports
|
12
|
+
|
13
|
+
### Key Components
|
14
|
+
1. **Hosts**: LLM applications that initiate connections (e.g., Claude Desktop)
|
15
|
+
2. **Clients**: Maintain 1:1 connections with servers
|
16
|
+
3. **Servers**: Provide context, tools, and prompts to clients
|
17
|
+
4. **Resources**: File-like data that can be read by clients
|
18
|
+
5. **Tools**: Functions that can be called by the LLM
|
19
|
+
6. **Prompts**: Pre-written templates for specific tasks
|
20
|
+
|
21
|
+
## 2. Server Implementation Guidelines
|
22
|
+
|
23
|
+
### Server Structure
|
24
|
+
1. Core Server Class:
|
25
|
+
```typescript
|
26
|
+
class Server extends Protocol<ServerRequest, ServerNotification, ServerResult> {
|
27
|
+
constructor(serverInfo: Implementation, options: ServerOptions)
|
28
|
+
// Must implement base protocol methods
|
29
|
+
}
|
30
|
+
```
|
31
|
+
|
32
|
+
2. Required Capabilities:
|
33
|
+
```typescript
|
34
|
+
interface ServerCapabilities {
|
35
|
+
experimental?: object;
|
36
|
+
logging?: object;
|
37
|
+
prompts?: { listChanged?: boolean };
|
38
|
+
resources?: {
|
39
|
+
subscribe?: boolean;
|
40
|
+
listChanged?: boolean
|
41
|
+
};
|
42
|
+
tools?: { listChanged?: boolean };
|
43
|
+
}
|
44
|
+
```
|
45
|
+
|
46
|
+
### Essential Implementation Steps
|
47
|
+
|
48
|
+
1. **Server Initialization**:
|
49
|
+
```typescript
|
50
|
+
const server = new Server(
|
51
|
+
{
|
52
|
+
name: "your-server-name",
|
53
|
+
version: "1.0.0"
|
54
|
+
},
|
55
|
+
{
|
56
|
+
capabilities: {
|
57
|
+
// Declare supported capabilities
|
58
|
+
resources: {},
|
59
|
+
tools: {},
|
60
|
+
prompts: {}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
);
|
64
|
+
```
|
65
|
+
|
66
|
+
2. **Request Handlers**:
|
67
|
+
```typescript
|
68
|
+
// Example tool handler
|
69
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
70
|
+
tools: [
|
71
|
+
{
|
72
|
+
name: "tool-name",
|
73
|
+
description: "Tool description",
|
74
|
+
inputSchema: {
|
75
|
+
type: "object",
|
76
|
+
properties: {
|
77
|
+
// Define parameters
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
]
|
82
|
+
}));
|
83
|
+
```
|
84
|
+
|
85
|
+
3. **Transport Setup**:
|
86
|
+
```typescript
|
87
|
+
const transport = new StdioServerTransport();
|
88
|
+
await server.connect(transport);
|
89
|
+
```
|
90
|
+
|
91
|
+
## 3. Core Features Implementation
|
92
|
+
|
93
|
+
### Resources
|
94
|
+
```typescript
|
95
|
+
interface Resource {
|
96
|
+
uri: string;
|
97
|
+
name: string;
|
98
|
+
description?: string;
|
99
|
+
mimeType?: string;
|
100
|
+
}
|
101
|
+
|
102
|
+
// Handler example
|
103
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
104
|
+
resources: [
|
105
|
+
{
|
106
|
+
uri: "custom://resource",
|
107
|
+
name: "Resource Name",
|
108
|
+
description: "Resource description"
|
109
|
+
}
|
110
|
+
]
|
111
|
+
}));
|
112
|
+
```
|
113
|
+
|
114
|
+
### Tools
|
115
|
+
```typescript
|
116
|
+
interface Tool {
|
117
|
+
name: string;
|
118
|
+
description?: string;
|
119
|
+
inputSchema: {
|
120
|
+
type: "object";
|
121
|
+
properties?: object;
|
122
|
+
required?: string[];
|
123
|
+
};
|
124
|
+
}
|
125
|
+
|
126
|
+
// Handler example
|
127
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => ({
|
128
|
+
content: [
|
129
|
+
{
|
130
|
+
type: "text",
|
131
|
+
text: "Tool execution result"
|
132
|
+
}
|
133
|
+
]
|
134
|
+
}));
|
135
|
+
```
|
136
|
+
|
137
|
+
### Prompts
|
138
|
+
```typescript
|
139
|
+
interface Prompt {
|
140
|
+
name: string;
|
141
|
+
description?: string;
|
142
|
+
arguments?: PromptArgument[];
|
143
|
+
}
|
144
|
+
|
145
|
+
// Handler example
|
146
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => ({
|
147
|
+
messages: [
|
148
|
+
{
|
149
|
+
role: "user",
|
150
|
+
content: {
|
151
|
+
type: "text",
|
152
|
+
text: "Prompt template"
|
153
|
+
}
|
154
|
+
}
|
155
|
+
]
|
156
|
+
}));
|
157
|
+
```
|
158
|
+
|
159
|
+
## 4. Best Practices
|
160
|
+
|
161
|
+
### Error Handling
|
162
|
+
1. Use appropriate error codes:
|
163
|
+
```typescript
|
164
|
+
enum ErrorCode {
|
165
|
+
ParseError = -32700,
|
166
|
+
InvalidRequest = -32600,
|
167
|
+
MethodNotFound = -32601,
|
168
|
+
InvalidParams = -32602,
|
169
|
+
InternalError = -32603
|
170
|
+
}
|
171
|
+
```
|
172
|
+
|
173
|
+
2. Tool errors should be in result:
|
174
|
+
```typescript
|
175
|
+
{
|
176
|
+
isError: true,
|
177
|
+
content: [{
|
178
|
+
type: "text",
|
179
|
+
text: "Error description"
|
180
|
+
}]
|
181
|
+
}
|
182
|
+
```
|
183
|
+
|
184
|
+
### Security Considerations
|
185
|
+
1. Input Validation:
|
186
|
+
- Validate all parameters against schemas
|
187
|
+
- Sanitize file paths and system commands
|
188
|
+
- Validate URLs and external identifiers
|
189
|
+
- Check parameter sizes and ranges
|
190
|
+
|
191
|
+
2. Access Control:
|
192
|
+
- Implement authentication where needed
|
193
|
+
- Use appropriate authorization checks
|
194
|
+
- Audit tool usage
|
195
|
+
- Rate limit requests
|
196
|
+
|
197
|
+
3. Resource Protection:
|
198
|
+
- Validate resource paths
|
199
|
+
- Monitor resource usage
|
200
|
+
- Implement access controls
|
201
|
+
- Rate limit requests
|
202
|
+
|
203
|
+
### Message Handling
|
204
|
+
1. Request Processing:
|
205
|
+
- Validate inputs thoroughly
|
206
|
+
- Use type-safe schemas
|
207
|
+
- Handle errors gracefully
|
208
|
+
- Implement timeouts
|
209
|
+
|
210
|
+
2. Progress Reporting:
|
211
|
+
- Use progress tokens for long operations
|
212
|
+
- Report progress incrementally
|
213
|
+
- Include total progress when known
|
214
|
+
|
215
|
+
## 5. Client Integration Guidelines
|
216
|
+
|
217
|
+
### Client Configuration
|
218
|
+
```json
|
219
|
+
{
|
220
|
+
"mcpServers": {
|
221
|
+
"server-name": {
|
222
|
+
"command": "command-to-run",
|
223
|
+
"args": ["arg1", "arg2"],
|
224
|
+
"env": {
|
225
|
+
"ENV_VAR": "value"
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
```
|
231
|
+
|
232
|
+
### Environment Variables
|
233
|
+
- Server inherits limited environment variables
|
234
|
+
- Custom variables must be specified in config
|
235
|
+
- Sensitive data should be in environment variables
|
236
|
+
|
237
|
+
## 6. Testing and Debugging
|
238
|
+
|
239
|
+
### MCP Inspector Usage
|
240
|
+
1. Start inspector with server:
|
241
|
+
```bash
|
242
|
+
npx mcp-inspector your-server-command
|
243
|
+
```
|
244
|
+
|
245
|
+
2. Features to test:
|
246
|
+
- Resource listing and reading
|
247
|
+
- Tool execution
|
248
|
+
- Prompt generation
|
249
|
+
- Error handling
|
250
|
+
- Progress reporting
|
251
|
+
|
252
|
+
### Common Issues
|
253
|
+
1. Connection Problems:
|
254
|
+
- Check transport configuration
|
255
|
+
- Verify server process is running
|
256
|
+
- Check for initialization errors
|
257
|
+
|
258
|
+
2. Message Handling:
|
259
|
+
- Validate message formats
|
260
|
+
- Check handler implementations
|
261
|
+
- Verify error responses
|
262
|
+
|
263
|
+
3. Resource Issues:
|
264
|
+
- Check file permissions
|
265
|
+
- Validate URI formats
|
266
|
+
- Verify content types
|
267
|
+
|
268
|
+
## 7. Performance and Scaling
|
269
|
+
|
270
|
+
### Best Practices
|
271
|
+
1. Resource Management:
|
272
|
+
- Cache when appropriate
|
273
|
+
- Implement cleanup
|
274
|
+
- Monitor memory usage
|
275
|
+
|
276
|
+
2. Request Handling:
|
277
|
+
- Use appropriate timeouts
|
278
|
+
- Implement rate limiting
|
279
|
+
- Handle concurrent requests
|
280
|
+
|
281
|
+
3. Error Recovery:
|
282
|
+
- Implement reconnection logic
|
283
|
+
- Handle partial failures
|
284
|
+
- Clean up resources
|
285
|
+
|
286
|
+
## 8. Documentation Requirements
|
287
|
+
|
288
|
+
### Server Documentation
|
289
|
+
1. Capabilities Documentation:
|
290
|
+
- List supported features
|
291
|
+
- Document configuration options
|
292
|
+
- Describe environment variables
|
293
|
+
|
294
|
+
2. API Documentation:
|
295
|
+
- Document all resources
|
296
|
+
- Document all tools
|
297
|
+
- Document all prompts
|
298
|
+
- Include example usage
|
299
|
+
|
300
|
+
3. Error Documentation:
|
301
|
+
- List possible error codes
|
302
|
+
- Describe error conditions
|
303
|
+
- Include recovery steps
|
304
|
+
|
305
|
+
### Integration Guide
|
306
|
+
1. Setup Instructions:
|
307
|
+
- Installation steps
|
308
|
+
- Configuration options
|
309
|
+
- Environment setup
|
310
|
+
|
311
|
+
2. Usage Examples:
|
312
|
+
- Basic usage patterns
|
313
|
+
- Common integrations
|
314
|
+
- Error handling examples
|
315
|
+
|
316
|
+
## 9. Versioning and Updates
|
317
|
+
|
318
|
+
### Version Management
|
319
|
+
1. Protocol Versioning:
|
320
|
+
- Support LATEST_PROTOCOL_VERSION
|
321
|
+
- Handle version negotiation
|
322
|
+
- Maintain compatibility
|
323
|
+
|
324
|
+
2. Server Versioning:
|
325
|
+
- Use semantic versioning
|
326
|
+
- Document breaking changes
|
327
|
+
- Provide migration guides
|
328
|
+
|
329
|
+
### Update Handling
|
330
|
+
1. Capability Updates:
|
331
|
+
- Notify clients of changes
|
332
|
+
- Handle capability negotiation
|
333
|
+
- Maintain backwards compatibility
|
334
|
+
|
335
|
+
2. Resource Updates:
|
336
|
+
- Handle resource changes
|
337
|
+
- Notify subscribed clients
|
338
|
+
- Maintain consistency
|
339
|
+
|
340
|
+
## 10. Specific Server Types
|
341
|
+
|
342
|
+
### Database Servers (like Airtable)
|
343
|
+
1. Required Capabilities:
|
344
|
+
- Resources for data access
|
345
|
+
- Tools for data manipulation
|
346
|
+
- Prompts for common operations
|
347
|
+
|
348
|
+
2. Implementation Focus:
|
349
|
+
- Connection management
|
350
|
+
- Query handling
|
351
|
+
- Data transformation
|
352
|
+
- Error handling
|
353
|
+
- Rate limiting
|
354
|
+
|
355
|
+
3. Security Considerations:
|
356
|
+
- API key management
|
357
|
+
- Access control
|
358
|
+
- Data validation
|
359
|
+
- Request sanitization
|
360
|
+
|
361
|
+
4. Tools to Implement:
|
362
|
+
- List databases/bases
|
363
|
+
- Create/modify tables
|
364
|
+
- Query data
|
365
|
+
- Update records
|
366
|
+
- Delete records
|
367
|
+
|
368
|
+
5. Resource Structure:
|
369
|
+
- Database schema
|
370
|
+
- Table contents
|
371
|
+
- Query results
|
372
|
+
|
373
|
+
6. Error Handling:
|
374
|
+
- Connection errors
|
375
|
+
- Query errors
|
376
|
+
- Rate limit errors
|
377
|
+
- Authorization errors
|
378
|
+
|
379
|
+
This guide should be used as a reference when assisting with MCP server development. Always consider the specific requirements and constraints of the project while following these guidelines.
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
2
|
+
import prettier from "eslint-plugin-prettier";
|
3
|
+
import tsParser from "@typescript-eslint/parser";
|
4
|
+
import path from "node:path";
|
5
|
+
import { fileURLToPath } from "node:url";
|
6
|
+
import js from "@eslint/js";
|
7
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
8
|
+
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
10
|
+
const __dirname = path.dirname(__filename);
|
11
|
+
const compat = new FlatCompat({
|
12
|
+
baseDirectory: __dirname,
|
13
|
+
recommendedConfig: js.configs.recommended,
|
14
|
+
allConfig: js.configs.all,
|
15
|
+
});
|
16
|
+
|
17
|
+
export default [
|
18
|
+
...compat.extends(
|
19
|
+
"eslint:recommended",
|
20
|
+
"prettier",
|
21
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
22
|
+
"plugin:@typescript-eslint/recommended",
|
23
|
+
),
|
24
|
+
{
|
25
|
+
files: ["./src/*.{ts,tsx}", "./test/*.{ts,tsx}"],
|
26
|
+
plugins: {
|
27
|
+
"@typescript-eslint": typescriptEslint,
|
28
|
+
prettier,
|
29
|
+
},
|
30
|
+
|
31
|
+
languageOptions: {
|
32
|
+
globals: {},
|
33
|
+
parser: tsParser,
|
34
|
+
ecmaVersion: 5,
|
35
|
+
sourceType: "script",
|
36
|
+
|
37
|
+
parserOptions: {
|
38
|
+
project: "./tsconfig.json",
|
39
|
+
},
|
40
|
+
},
|
41
|
+
|
42
|
+
rules: {
|
43
|
+
"prettier/prettier": "warn",
|
44
|
+
|
45
|
+
"spaced-comment": [
|
46
|
+
"error",
|
47
|
+
"always",
|
48
|
+
{
|
49
|
+
markers: ["/"],
|
50
|
+
},
|
51
|
+
],
|
52
|
+
|
53
|
+
"no-fallthrough": "error",
|
54
|
+
"@typescript-eslint/ban-ts-comment": "warn",
|
55
|
+
|
56
|
+
"@typescript-eslint/consistent-type-imports": [
|
57
|
+
"error",
|
58
|
+
{
|
59
|
+
prefer: "type-imports",
|
60
|
+
},
|
61
|
+
],
|
62
|
+
|
63
|
+
"@typescript-eslint/no-inferrable-types": [
|
64
|
+
"error",
|
65
|
+
{
|
66
|
+
ignoreParameters: false,
|
67
|
+
ignoreProperties: false,
|
68
|
+
},
|
69
|
+
],
|
70
|
+
|
71
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
72
|
+
"@typescript-eslint/no-floating-promises": "error",
|
73
|
+
|
74
|
+
"@typescript-eslint/no-unused-vars": [
|
75
|
+
"warn",
|
76
|
+
{
|
77
|
+
args: "after-used",
|
78
|
+
argsIgnorePattern: "^_",
|
79
|
+
varsIgnorePattern: "^_",
|
80
|
+
ignoreRestSiblings: true,
|
81
|
+
},
|
82
|
+
],
|
83
|
+
|
84
|
+
"@typescript-eslint/no-empty-function": ["error"],
|
85
|
+
"@typescript-eslint/restrict-template-expressions": "off",
|
86
|
+
},
|
87
|
+
},
|
88
|
+
];
|
package/lib/index.d.mts
ADDED
package/lib/index.mjs
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
5
|
+
import * as os from "os";
|
6
|
+
import * as fs from "fs";
|
7
|
+
import * as path from "path";
|
8
|
+
import { spawnPromise } from "spawn-rx";
|
9
|
+
import { rimraf } from "rimraf";
|
10
|
+
import { cleanSubtitles } from "./vtt2txt.mjs";
|
11
|
+
// Get downloads directory from env or default to project root
|
12
|
+
const DOWNLOADS_DIR = process.env.DOWNLOADS_DIR || path.join(process.cwd(), "downloads");
|
13
|
+
console.error('Using downloads directory:', DOWNLOADS_DIR);
|
14
|
+
const server = new Server({
|
15
|
+
name: "mcp-video",
|
16
|
+
version: "0.5.1",
|
17
|
+
}, {
|
18
|
+
capabilities: {
|
19
|
+
tools: {},
|
20
|
+
},
|
21
|
+
});
|
22
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
23
|
+
return {
|
24
|
+
tools: [
|
25
|
+
{
|
26
|
+
name: "get_video_transcript",
|
27
|
+
description: "Download and process video subtitles for analysis from various platforms (YouTube, Vimeo, Twitter/X, TikTok, etc.) using yt-dlp. Use this tool when asked to summarize, analyze, or extract information from any video that has subtitles/closed captions. This enables Claude to understand video content through subtitles.",
|
28
|
+
inputSchema: {
|
29
|
+
type: "object",
|
30
|
+
properties: {
|
31
|
+
url: { type: "string", description: "URL of the video from any supported platform (YouTube, Vimeo, Twitter/X, TikTok, etc.)" },
|
32
|
+
},
|
33
|
+
required: ["url"],
|
34
|
+
},
|
35
|
+
},
|
36
|
+
{
|
37
|
+
name: "download_video",
|
38
|
+
description: "Download video in best quality (limited to 1080p) from various platforms (YouTube, Vimeo, Twitter/X, TikTok, etc.) using yt-dlp. Downloads are stored in the downloads directory. IMPORTANT: Clients should always provide a sanitized filename using the platform and video ID format to ensure consistent naming and avoid conflicts.",
|
39
|
+
inputSchema: {
|
40
|
+
type: "object",
|
41
|
+
properties: {
|
42
|
+
url: { type: "string", description: "URL of the video from any supported platform (YouTube, Vimeo, Twitter/X, TikTok, etc.)" },
|
43
|
+
filename: {
|
44
|
+
type: "string",
|
45
|
+
description: "Sanitized filename using platform-id format. Examples:\n- YouTube: youtube-{video_id} (e.g. 'youtube-MhOTvvmlqLM' from youtube.com/watch?v=MhOTvvmlqLM)\n- Twitter/X: x-{tweet_id} (e.g. 'x-1876565449615217019' from x.com/user/status/1876565449615217019)\n- Vimeo: vimeo-{video_id} (e.g. 'vimeo-123456789' from vimeo.com/123456789)"
|
46
|
+
},
|
47
|
+
},
|
48
|
+
required: ["url"],
|
49
|
+
},
|
50
|
+
},
|
51
|
+
],
|
52
|
+
};
|
53
|
+
});
|
54
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
55
|
+
if (request.params.name === "get_video_transcript") {
|
56
|
+
try {
|
57
|
+
const { url } = request.params.arguments;
|
58
|
+
const tempDir = fs.mkdtempSync(`${os.tmpdir()}${path.sep}youtube-`);
|
59
|
+
await spawnPromise("yt-dlp", [
|
60
|
+
"--write-sub",
|
61
|
+
"--write-auto-sub",
|
62
|
+
"--sub-lang",
|
63
|
+
"en",
|
64
|
+
"--skip-download",
|
65
|
+
"--sub-format",
|
66
|
+
"srt",
|
67
|
+
url,
|
68
|
+
], { cwd: tempDir, detached: true });
|
69
|
+
let content = "";
|
70
|
+
try {
|
71
|
+
fs.readdirSync(tempDir).forEach((file) => {
|
72
|
+
const fileContent = fs.readFileSync(path.join(tempDir, file), "utf8");
|
73
|
+
const cleanedContent = cleanSubtitles(fileContent);
|
74
|
+
content += `${cleanedContent}\n\n`;
|
75
|
+
});
|
76
|
+
}
|
77
|
+
finally {
|
78
|
+
rimraf.sync(tempDir);
|
79
|
+
}
|
80
|
+
return {
|
81
|
+
content: [
|
82
|
+
{
|
83
|
+
type: "text",
|
84
|
+
text: content,
|
85
|
+
},
|
86
|
+
],
|
87
|
+
};
|
88
|
+
}
|
89
|
+
catch (err) {
|
90
|
+
return {
|
91
|
+
content: [
|
92
|
+
{
|
93
|
+
type: "text",
|
94
|
+
text: `Error downloading video: ${err}`,
|
95
|
+
},
|
96
|
+
],
|
97
|
+
isError: true,
|
98
|
+
};
|
99
|
+
}
|
100
|
+
}
|
101
|
+
else if (request.params.name === "download_video") {
|
102
|
+
try {
|
103
|
+
const { url, filename } = request.params.arguments;
|
104
|
+
// Create downloads directory if it doesn't exist
|
105
|
+
try {
|
106
|
+
if (!fs.existsSync(DOWNLOADS_DIR)) {
|
107
|
+
fs.mkdirSync(DOWNLOADS_DIR, { recursive: true });
|
108
|
+
}
|
109
|
+
console.error('Downloads directory created/verified at:', DOWNLOADS_DIR);
|
110
|
+
}
|
111
|
+
catch (err) {
|
112
|
+
console.error('Error creating downloads directory:', err);
|
113
|
+
throw err;
|
114
|
+
}
|
115
|
+
// Get video info first
|
116
|
+
const infoResult = await spawnPromise("yt-dlp", [
|
117
|
+
"--print",
|
118
|
+
"%(title)s",
|
119
|
+
"--print",
|
120
|
+
"%(duration)s",
|
121
|
+
"--print",
|
122
|
+
"%(resolution)s",
|
123
|
+
url,
|
124
|
+
]);
|
125
|
+
const [title, duration, resolution] = infoResult.split("\n");
|
126
|
+
// Prepare output template with absolute path
|
127
|
+
const outputTemplate = filename ?
|
128
|
+
path.join(DOWNLOADS_DIR, `${filename}.mp4`) :
|
129
|
+
path.join(DOWNLOADS_DIR, "%(title)s.%(ext)s");
|
130
|
+
// Download the video
|
131
|
+
await spawnPromise("yt-dlp", [
|
132
|
+
"-f",
|
133
|
+
"((bv*[height<=1080])/bv*)+ba/b",
|
134
|
+
"--merge-output-format",
|
135
|
+
"mp4",
|
136
|
+
"-o",
|
137
|
+
outputTemplate,
|
138
|
+
url,
|
139
|
+
]);
|
140
|
+
// Get the final file size
|
141
|
+
const finalPath = filename ?
|
142
|
+
path.join(DOWNLOADS_DIR, `${filename}.mp4`) :
|
143
|
+
path.join(DOWNLOADS_DIR, `${title}.mp4`);
|
144
|
+
const stats = fs.statSync(finalPath);
|
145
|
+
const fileSizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
146
|
+
return {
|
147
|
+
content: [
|
148
|
+
{
|
149
|
+
type: "text",
|
150
|
+
text: `Successfully downloaded video:
|
151
|
+
Title: ${title}
|
152
|
+
Duration: ${duration} seconds
|
153
|
+
Resolution: ${resolution}
|
154
|
+
File size: ${fileSizeMB} MB
|
155
|
+
Saved to: ${finalPath}`,
|
156
|
+
},
|
157
|
+
],
|
158
|
+
};
|
159
|
+
}
|
160
|
+
catch (err) {
|
161
|
+
return {
|
162
|
+
content: [
|
163
|
+
{
|
164
|
+
type: "text",
|
165
|
+
text: `Error downloading video: ${err}`,
|
166
|
+
},
|
167
|
+
],
|
168
|
+
isError: true,
|
169
|
+
};
|
170
|
+
}
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
174
|
+
}
|
175
|
+
});
|
176
|
+
async function runServer() {
|
177
|
+
const transport = new StdioServerTransport();
|
178
|
+
await server.connect(transport);
|
179
|
+
}
|
180
|
+
runServer().catch(console.error);
|
181
|
+
//# sourceMappingURL=index.mjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,8DAA8D;AAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AACzF,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,aAAa,CAAC,CAAC;AAE3D,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IAC1D,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,sBAAsB;gBAC5B,WAAW,EAAE,+TAA+T;gBAC5U,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wFAAwF,EAAE;qBAC/H;oBACD,QAAQ,EAAE,CAAC,KAAK,CAAC;iBAClB;aACF;YACD;gBACE,IAAI,EAAE,gBAAgB;gBACtB,WAAW,EAAE,yUAAyU;gBACtV,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wFAAwF,EAAE;wBAC9H,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,2UAA2U;yBACzV;qBACF;oBACD,QAAQ,EAAE,CAAC,KAAK,CAAC;iBAClB;aACF;SACF;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,SAA4B,CAAC;YAE5D,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;YACpE,MAAM,YAAY,CAChB,QAAQ,EACR;gBACE,aAAa;gBACb,kBAAkB;gBAClB,YAAY;gBACZ,IAAI;gBACJ,iBAAiB;gBACjB,cAAc;gBACd,KAAK;gBACL,GAAG;aACJ,EACD,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CACjC,CAAC;YAEF,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;oBACvC,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;oBACtE,MAAM,cAAc,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;oBACnD,OAAO,IAAI,GAAG,cAAc,MAAM,CAAC;gBACrC,CAAC,CAAC,CAAC;YACL,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,OAAO;qBACd;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4BAA4B,GAAG,EAAE;qBACxC;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,SAA+C,CAAC;YAEzF,iDAAiD;YACjD,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;oBAClC,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,aAAa,CAAC,CAAC;YAC3E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;gBAC1D,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,uBAAuB;YACvB,MAAM,UAAU,GAAG,MAAM,YAAY,CACnC,QAAQ,EACR;gBACE,SAAS;gBACT,WAAW;gBACX,SAAS;gBACT,cAAc;gBACd,SAAS;gBACT,gBAAgB;gBAChB,GAAG;aACJ,CACF,CAAC;YAEF,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE7D,6CAA6C;YAC7C,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC;gBAC/B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,QAAQ,MAAM,CAAC,CAAC,CAAC;gBAC7C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;YAEhD,qBAAqB;YACrB,MAAM,YAAY,CAChB,QAAQ,EACR;gBACE,IAAI;gBACJ,gCAAgC;gBAChC,uBAAuB;gBACvB,KAAK;gBACL,IAAI;gBACJ,cAAc;gBACd,GAAG;aACJ,CACF,CAAC;YAEF,0BAA0B;YAC1B,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,QAAQ,MAAM,CAAC,CAAC,CAAC;gBAC7C,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACrC,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAE3D,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;SACT,KAAK;YACF,QAAQ;cACN,UAAU;aACX,UAAU;YACX,SAAS,EAAE;qBACZ;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4BAA4B,GAAG,EAAE;qBACxC;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,SAAS;IACtB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function cleanSubtitles(vttContent: string): string;
|
package/lib/vtt2txt.mjs
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
export function cleanSubtitles(vttContent) {
|
2
|
+
// Split into lines
|
3
|
+
const lines = vttContent.split('\n');
|
4
|
+
const cleanedText = new Set(); // Use Set to remove duplicates
|
5
|
+
for (let line of lines) {
|
6
|
+
// Skip WebVTT header, timestamps, and empty lines
|
7
|
+
if (line.trim() === 'WEBVTT' || line.trim() === '' || /^\d{2}:\d{2}/.test(line)) {
|
8
|
+
continue;
|
9
|
+
}
|
10
|
+
// Remove HTML-style tags and clean the text
|
11
|
+
line = line.replace(/<[^>]*>/g, '')
|
12
|
+
.replace(/\[.*?\]/g, '') // Remove square brackets and their contents
|
13
|
+
.trim();
|
14
|
+
// If we have actual text, add it
|
15
|
+
if (line) {
|
16
|
+
cleanedText.add(line);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
// Convert Set back to array and join with newlines
|
20
|
+
return Array.from(cleanedText).join('\n');
|
21
|
+
}
|
22
|
+
//# sourceMappingURL=vtt2txt.mjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"vtt2txt.mjs","sourceRoot":"","sources":["../src/vtt2txt.mts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC7C,mBAAmB;IACnB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,+BAA+B;IAEtE,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;QACrB,kDAAkD;QAClD,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9E,SAAS;QACb,CAAC;QAED,4CAA4C;QAC5C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;aACxB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,4CAA4C;aACpE,IAAI,EAAE,CAAC;QAElB,iCAAiC;QACjC,IAAI,IAAI,EAAE,CAAC;YACP,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,mDAAmD;IACnD,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
|