diff --git a/docs/contributing.md b/docs/contributing.md index 67f15ff..8502b33 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -4,17 +4,20 @@ 1. Fork the repository 2. Clone your fork: + ```bash git clone https://github.com/your-username/yt-dlp-mcp.git cd yt-dlp-mcp ``` 3. Install dependencies: + ```bash npm install ``` 4. Create a new branch: + ```bash git checkout -b feature/your-feature-name ``` @@ -31,7 +34,7 @@ git checkout -b feature/your-feature-name ### Building ```bash -npm run build +npm run prepare ``` ### Running Tests @@ -41,6 +44,7 @@ npm test ``` For specific test files: + ```bash npm test -- src/__tests__/video.test.ts ``` @@ -72,10 +76,10 @@ interface DownloadOptions { // Use enums for fixed values enum Resolution { - SD = '480p', - HD = '720p', - FHD = '1080p', - BEST = 'best' + SD = "480p", + HD = "720p", + FHD = "1080p", + BEST = "best", } ``` @@ -91,16 +95,16 @@ enum Resolution { Example: ```typescript -describe('downloadVideo', () => { - test('downloads video successfully', async () => { +describe("downloadVideo", () => { + test("downloads video successfully", async () => { const result = await downloadVideo(testUrl); expect(result).toMatch(/Video successfully downloaded/); }); - test('handles invalid URL', async () => { - await expect(downloadVideo('invalid-url')) - .rejects - .toThrow('Invalid or unsupported URL'); + test("handles invalid URL", async () => { + await expect(downloadVideo("invalid-url")).rejects.toThrow( + "Invalid or unsupported URL" + ); }); }); ``` @@ -108,6 +112,7 @@ describe('downloadVideo', () => { ### Test Coverage Aim for high test coverage: + ```bash npm run test:coverage ``` @@ -118,16 +123,16 @@ npm run test:coverage Add comprehensive JSDoc comments for all public APIs: -```typescript +````typescript /** * Downloads a video from the specified URL. - * + * * @param url - The URL of the video to download * @param config - Optional configuration object * @param resolution - Preferred video resolution * @returns Promise resolving to success message with file path * @throws {Error} When URL is invalid or download fails - * + * * @example * ```typescript * const result = await downloadVideo('https://youtube.com/watch?v=...', config); @@ -141,7 +146,7 @@ export async function downloadVideo( ): Promise { // Implementation } -``` +```` ### README Updates @@ -177,6 +182,7 @@ export async function downloadVideo( ### Version Numbers Follow semantic versioning: + - MAJOR: Breaking changes - MINOR: New features - PATCH: Bug fixes @@ -189,4 +195,4 @@ Follow semantic versioning: - Suggest improvements - Share success stories -For more information, see the [README](./README.md) and [API Reference](./api.md). \ No newline at end of file +For more information, see the [README](./README.md) and [API Reference](./api.md). diff --git a/src/modules/audio.ts b/src/modules/audio.ts index cb2927d..69a60af 100644 --- a/src/modules/audio.ts +++ b/src/modules/audio.ts @@ -30,20 +30,22 @@ import { _spawnPromise, validateUrl, getFormattedTimestamp, isYouTubeUrl } from */ export async function downloadAudio(url: string, config: Config): Promise { const timestamp = getFormattedTimestamp(); - + try { validateUrl(url); - + const outputTemplate = path.join( config.file.downloadsDir, sanitizeFilename(`%(title)s [%(id)s] ${timestamp}`, config.file) + '.%(ext)s' ); - const format = isYouTubeUrl(url) + const format = isYouTubeUrl(url) ? "140/bestaudio[ext=m4a]/bestaudio" : "bestaudio[ext=m4a]/bestaudio[ext=mp3]/bestaudio"; await _spawnPromise("yt-dlp", [ + "--ignore-config", + "--no-check-certificate", "--verbose", "--progress", "--newline", @@ -62,4 +64,4 @@ export async function downloadAudio(url: string, config: Config): Promise { try { const output = await _spawnPromise('yt-dlp', [ + '--ignore-config', '--list-subs', '--write-auto-sub', '--skip-download', @@ -81,6 +82,7 @@ export async function downloadSubtitles( try { await _spawnPromise('yt-dlp', [ + '--ignore-config', '--write-sub', '--write-auto-sub', '--sub-lang', language, @@ -139,6 +141,7 @@ export async function downloadTranscript( try { await _spawnPromise('yt-dlp', [ + '--ignore-config', '--skip-download', '--write-subs', '--write-auto-subs', @@ -166,4 +169,4 @@ export async function downloadTranscript( } finally { fs.rmSync(tempDir, { recursive: true, force: true }); } -} \ No newline at end of file +} diff --git a/src/modules/video.ts b/src/modules/video.ts index bf30821..a50db6b 100644 --- a/src/modules/video.ts +++ b/src/modules/video.ts @@ -1,12 +1,12 @@ import * as path from "path"; import type { Config } from "../config.js"; import { sanitizeFilename } from "../config.js"; -import { - _spawnPromise, - validateUrl, - getFormattedTimestamp, +import { + _spawnPromise, + validateUrl, + getFormattedTimestamp, isYouTubeUrl, - generateRandomFilename + generateRandomFilename } from "./utils.js"; /** @@ -39,11 +39,11 @@ export async function downloadVideo( resolution: "480p" | "720p" | "1080p" | "best" = "720p" ): Promise { const userDownloadsDir = config.file.downloadsDir; - + try { validateUrl(url); const timestamp = getFormattedTimestamp(); - + let format: string; if (isYouTubeUrl(url)) { // YouTube-specific format selection @@ -80,15 +80,16 @@ export async function downloadVideo( let outputTemplate: string; let expectedFilename: string; - + try { // 嘗試獲取檔案名稱 outputTemplate = path.join( userDownloadsDir, sanitizeFilename(`%(title)s [%(id)s] ${timestamp}`, config.file) + '.%(ext)s' ); - + expectedFilename = await _spawnPromise("yt-dlp", [ + "--ignore-config", "--get-filename", "-f", format, "--output", outputTemplate, @@ -101,10 +102,11 @@ export async function downloadVideo( outputTemplate = path.join(userDownloadsDir, randomFilename); expectedFilename = randomFilename; } - + // Download with progress info try { await _spawnPromise("yt-dlp", [ + "--ignore-config", "--progress", "--newline", "--no-mtime", @@ -120,4 +122,4 @@ export async function downloadVideo( } catch (error) { throw error; } -} \ No newline at end of file +}