chore: bump version to 0.6.11

This commit is contained in:
kevin 2025-02-21 14:51:51 +08:00
parent 8b1a44d7b4
commit fecc2d6596
2 changed files with 63 additions and 37 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@kevinwatt/yt-dlp-mcp",
"version": "0.6.10",
"version": "0.6.11",
"description": "yt-dlp MCP Server - Download video content via Model Context Protocol",
"keywords": [
"mcp",

View File

@ -14,10 +14,10 @@ import * as path from "path";
import { spawnPromise } from "spawn-rx";
import { rimraf } from "rimraf";
const VERSION = '0.6.10';
const VERSION = '0.6.11';
/**
*
* System Configuration
*/
const CONFIG = {
MAX_FILENAME_LENGTH: 50,
@ -27,16 +27,16 @@ const CONFIG = {
} as const;
/**
*
* @throws {Error}
* Validate system configuration
* @throws {Error} when configuration is invalid
*/
async function validateConfig(): Promise<void> {
// 檢查下載目錄
// Check downloads directory
if (!fs.existsSync(CONFIG.DOWNLOADS_DIR)) {
throw new Error(`Downloads directory does not exist: ${CONFIG.DOWNLOADS_DIR}`);
}
// 檢查下載目錄權限
// Check downloads directory permissions
try {
const testFile = path.join(CONFIG.DOWNLOADS_DIR, '.write-test');
fs.writeFileSync(testFile, '');
@ -45,7 +45,7 @@ async function validateConfig(): Promise<void> {
throw new Error(`No write permission in downloads directory: ${CONFIG.DOWNLOADS_DIR}`);
}
// 檢查臨時目錄權限
// Check temporary directory permissions
try {
const testDir = fs.mkdtempSync(path.join(os.tmpdir(), CONFIG.TEMP_DIR_PREFIX));
await safeCleanup(testDir);
@ -55,8 +55,8 @@ async function validateConfig(): Promise<void> {
}
/**
*
* @throws {Error} 滿
* Check required external dependencies
* @throws {Error} when dependencies are not satisfied
*/
async function checkDependencies(): Promise<void> {
for (const tool of CONFIG.REQUIRED_TOOLS) {
@ -69,7 +69,7 @@ async function checkDependencies(): Promise<void> {
}
/**
*
* Initialize service
*/
async function initialize(): Promise<void> {
try {
@ -144,7 +144,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
});
/**
*
* Custom error types
*/
class VideoDownloadError extends Error {
constructor(
@ -165,7 +165,7 @@ class SubtitleError extends VideoDownloadError {
}
/**
*
* Error code mappings
*/
const ERROR_CODES = {
UNSUPPORTED_URL: 'Unsupported or invalid URL',
@ -180,8 +180,8 @@ const ERROR_CODES = {
} as const;
/**
*
* @param directory
* Safely clean up temporary directory
* @param directory Directory path to clean up
*/
async function safeCleanup(directory: string): Promise<void> {
try {
@ -192,9 +192,9 @@ async function safeCleanup(directory: string): Promise<void> {
}
/**
* URL
* @param url URL
* @throws {VideoDownloadError} URL
* Validate URL format
* @param url URL to validate
* @throws {VideoDownloadError} when URL is invalid
*/
function validateUrl(url: string, ErrorClass = VideoDownloadError): void {
try {
@ -208,8 +208,8 @@ function validateUrl(url: string, ErrorClass = VideoDownloadError): void {
}
/**
*
* @returns
* Generate formatted timestamp
* @returns Formatted timestamp string
*/
function getFormattedTimestamp(): string {
return new Date().toISOString()
@ -219,8 +219,8 @@ function getFormattedTimestamp(): string {
}
/**
* YouTube URL
* @param url URL
* Check if URL is a YouTube URL
* @param url URL to check
*/
function isYouTubeUrl(url: string): boolean {
try {
@ -288,12 +288,12 @@ async function downloadSubtitles(url: string, language: string = "en"): Promise<
);
}
// 首先嘗試下載一般字幕
try {
await spawnPromise(
"yt-dlp",
[
"--write-sub",
"--write-auto-sub",
"--sub-lang",
language,
"--skip-download",
@ -304,19 +304,36 @@ async function downloadSubtitles(url: string, language: string = "en"): Promise<
{ cwd: tempDirectory }
);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes('no subtitles')) {
// 如果一般字幕下載失敗,嘗試下載自動生成的字幕
try {
await spawnPromise(
"yt-dlp",
[
"--write-auto-sub",
"--sub-lang",
language,
"--skip-download",
"--sub-format",
"srt",
url,
],
{ cwd: tempDirectory }
);
} catch (autoSubError) {
const errorMessage = autoSubError instanceof Error ? autoSubError.message : String(autoSubError);
if (errorMessage.includes('no subtitles')) {
throw new SubtitleError(
ERROR_CODES.SUBTITLE_NOT_AVAILABLE,
'SUBTITLE_NOT_AVAILABLE',
autoSubError as Error
);
}
throw new SubtitleError(
ERROR_CODES.SUBTITLE_NOT_AVAILABLE,
'SUBTITLE_NOT_AVAILABLE',
error as Error
ERROR_CODES.SUBTITLE_ERROR,
'SUBTITLE_ERROR',
autoSubError as Error
);
}
throw new SubtitleError(
ERROR_CODES.SUBTITLE_ERROR,
'SUBTITLE_ERROR',
error as Error
);
}
let subtitlesContent = "";
@ -328,7 +345,16 @@ async function downloadSubtitles(url: string, language: string = "en"): Promise<
);
}
for (const file of files) {
// 只讀取 .srt 文件
const srtFiles = files.filter(file => file.endsWith('.srt'));
if (srtFiles.length === 0) {
throw new SubtitleError(
ERROR_CODES.SUBTITLE_NOT_AVAILABLE,
'SUBTITLE_NOT_AVAILABLE'
);
}
for (const file of srtFiles) {
const filePath = path.join(tempDirectory, file);
try {
const fileData = fs.readFileSync(filePath, "utf8");
@ -485,9 +511,9 @@ export async function downloadVideo(url: string, resolution = "720p"): Promise<s
}
/**
*
* @param action
* @param errorPrefix
* Handle tool execution with unified error handling
* @param action Async operation to execute
* @param errorPrefix Error message prefix
*/
async function handleToolExecution<T>(
action: () => Promise<T>,