@ekipnico/video-fetch 1.0.0
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/out/DownloadProvider.d.ts +22 -0
- package/out/DownloadProvider.js +7 -0
- package/out/DownloadProvider.js.map +1 -0
- package/out/VideoDownloadModule.d.ts +27 -0
- package/out/VideoDownloadModule.js +39 -0
- package/out/VideoDownloadModule.js.map +1 -0
- package/out/VideoDownloader.d.ts +22 -0
- package/out/VideoDownloader.js +42 -0
- package/out/VideoDownloader.js.map +1 -0
- package/out/index.d.ts +5 -0
- package/out/index.js +6 -0
- package/out/index.js.map +1 -0
- package/out/providers/YtDlpProvider.d.ts +36 -0
- package/out/providers/YtDlpProvider.js +215 -0
- package/out/providers/YtDlpProvider.js.map +1 -0
- package/out/schema/DownloadConfig.d.ts +24 -0
- package/out/schema/DownloadConfig.js +8 -0
- package/out/schema/DownloadConfig.js.map +1 -0
- package/out/schema/DownloadResult.d.ts +26 -0
- package/out/schema/DownloadResult.js +2 -0
- package/out/schema/DownloadResult.js.map +1 -0
- package/out/schema/VideoMetadata.d.ts +46 -0
- package/out/schema/VideoMetadata.js +35 -0
- package/out/schema/VideoMetadata.js.map +1 -0
- package/out/schema/index.d.ts +3 -0
- package/out/schema/index.js +4 -0
- package/out/schema/index.js.map +1 -0
- package/out/test/VideoDownloadModule.test.d.ts +1 -0
- package/out/test/VideoDownloadModule.test.js +61 -0
- package/out/test/VideoDownloadModule.test.js.map +1 -0
- package/out/test/VideoDownloader.test.d.ts +1 -0
- package/out/test/VideoDownloader.test.js +132 -0
- package/out/test/VideoDownloader.test.js.map +1 -0
- package/out/test/YtDlpProvider.test.d.ts +1 -0
- package/out/test/YtDlpProvider.test.js +215 -0
- package/out/test/YtDlpProvider.test.js.map +1 -0
- package/out/test/channel-download.test.d.ts +1 -0
- package/out/test/channel-download.test.js +269 -0
- package/out/test/channel-download.test.js.map +1 -0
- package/out/test/integration.test.d.ts +1 -0
- package/out/test/integration.test.js +120 -0
- package/out/test/integration.test.js.map +1 -0
- package/out/test/schema.test.d.ts +1 -0
- package/out/test/schema.test.js +62 -0
- package/out/test/schema.test.js.map +1 -0
- package/out/test/setup.test.d.ts +5 -0
- package/out/test/setup.test.js +7 -0
- package/out/test/setup.test.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DownloadConfig } from './schema/DownloadConfig.js';
|
|
2
|
+
import { DownloadResult } from './schema/DownloadResult.js';
|
|
3
|
+
import { VideoMetadata } from './schema/VideoMetadata.js';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract contract for video download providers.
|
|
6
|
+
* Concrete implementations handle platform-specific logic.
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class DownloadProvider {
|
|
9
|
+
abstract readonly name: string;
|
|
10
|
+
/**
|
|
11
|
+
* Download video from URL.
|
|
12
|
+
*/
|
|
13
|
+
abstract download(url: string, config: DownloadConfig): Promise<DownloadResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Extract metadata without downloading video.
|
|
16
|
+
*/
|
|
17
|
+
abstract extractMetadata(url: string): Promise<VideoMetadata>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if URL is supported by this provider.
|
|
20
|
+
*/
|
|
21
|
+
abstract isSupported(url: string): Promise<boolean>;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloadProvider.js","sourceRoot":"","sources":["../src/DownloadProvider.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,OAAgB,gBAAgB;CAmBrC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Mesh } from 'mesh-ioc';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a configured Mesh for video downloading.
|
|
4
|
+
*
|
|
5
|
+
* ## Requirements
|
|
6
|
+
* - `yt-dlp` must be installed and available in PATH
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const mesh = createVideoDownloadMesh();
|
|
10
|
+
* const downloader = mesh.resolve(VideoDownloader);
|
|
11
|
+
*
|
|
12
|
+
* // Download video
|
|
13
|
+
* const result = await downloader.download('https://youtube.com/watch?v=xyz');
|
|
14
|
+
*
|
|
15
|
+
* // Extract metadata only
|
|
16
|
+
* const metadata = await downloader.extractMetadata('https://youtube.com/watch?v=xyz');
|
|
17
|
+
*/
|
|
18
|
+
export declare function createVideoDownloadMesh(): Mesh;
|
|
19
|
+
/**
|
|
20
|
+
* Register video download services into an existing Mesh.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const mesh = new Mesh('MyApp');
|
|
24
|
+
* registerVideoDownloadServices(mesh);
|
|
25
|
+
* const downloader = mesh.resolve(VideoDownloader);
|
|
26
|
+
*/
|
|
27
|
+
export declare function registerVideoDownloadServices(mesh: Mesh): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Mesh } from 'mesh-ioc';
|
|
2
|
+
import { VideoDownloader } from './VideoDownloader.js';
|
|
3
|
+
import { DownloadProvider } from './DownloadProvider.js';
|
|
4
|
+
import { YtDlpProvider } from './providers/YtDlpProvider.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates a configured Mesh for video downloading.
|
|
7
|
+
*
|
|
8
|
+
* ## Requirements
|
|
9
|
+
* - `yt-dlp` must be installed and available in PATH
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const mesh = createVideoDownloadMesh();
|
|
13
|
+
* const downloader = mesh.resolve(VideoDownloader);
|
|
14
|
+
*
|
|
15
|
+
* // Download video
|
|
16
|
+
* const result = await downloader.download('https://youtube.com/watch?v=xyz');
|
|
17
|
+
*
|
|
18
|
+
* // Extract metadata only
|
|
19
|
+
* const metadata = await downloader.extractMetadata('https://youtube.com/watch?v=xyz');
|
|
20
|
+
*/
|
|
21
|
+
export function createVideoDownloadMesh() {
|
|
22
|
+
const mesh = new Mesh('VideoDownload');
|
|
23
|
+
mesh.service(DownloadProvider, YtDlpProvider);
|
|
24
|
+
mesh.service(VideoDownloader);
|
|
25
|
+
return mesh;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Register video download services into an existing Mesh.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const mesh = new Mesh('MyApp');
|
|
32
|
+
* registerVideoDownloadServices(mesh);
|
|
33
|
+
* const downloader = mesh.resolve(VideoDownloader);
|
|
34
|
+
*/
|
|
35
|
+
export function registerVideoDownloadServices(mesh) {
|
|
36
|
+
mesh.service(DownloadProvider, YtDlpProvider);
|
|
37
|
+
mesh.service(VideoDownloader);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=VideoDownloadModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoDownloadModule.js","sourceRoot":"","sources":["../src/VideoDownloadModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB;IACnC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC;IAEvC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAE9B,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAU;IACpD,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DownloadConfig } from './schema/DownloadConfig.js';
|
|
2
|
+
import { DownloadResult } from './schema/DownloadResult.js';
|
|
3
|
+
import { VideoMetadata } from './schema/VideoMetadata.js';
|
|
4
|
+
/**
|
|
5
|
+
* Main orchestrator for video downloading.
|
|
6
|
+
* Delegates to configured DownloadProvider.
|
|
7
|
+
*/
|
|
8
|
+
export declare class VideoDownloader {
|
|
9
|
+
private provider;
|
|
10
|
+
/**
|
|
11
|
+
* Download a video from URL.
|
|
12
|
+
*/
|
|
13
|
+
download(url: string, config?: Partial<DownloadConfig>): Promise<DownloadResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Extract metadata without downloading.
|
|
16
|
+
*/
|
|
17
|
+
extractMetadata(url: string): Promise<VideoMetadata>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if URL is supported.
|
|
20
|
+
*/
|
|
21
|
+
isSupported(url: string): Promise<boolean>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { dep } from 'mesh-ioc';
|
|
11
|
+
import { DownloadProvider } from './DownloadProvider.js';
|
|
12
|
+
import { DEFAULT_DOWNLOAD_CONFIG } from './schema/DownloadConfig.js';
|
|
13
|
+
/**
|
|
14
|
+
* Main orchestrator for video downloading.
|
|
15
|
+
* Delegates to configured DownloadProvider.
|
|
16
|
+
*/
|
|
17
|
+
export class VideoDownloader {
|
|
18
|
+
/**
|
|
19
|
+
* Download a video from URL.
|
|
20
|
+
*/
|
|
21
|
+
async download(url, config) {
|
|
22
|
+
const mergedConfig = { ...DEFAULT_DOWNLOAD_CONFIG, ...config };
|
|
23
|
+
return this.provider.download(url, mergedConfig);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract metadata without downloading.
|
|
27
|
+
*/
|
|
28
|
+
async extractMetadata(url) {
|
|
29
|
+
return this.provider.extractMetadata(url);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if URL is supported.
|
|
33
|
+
*/
|
|
34
|
+
async isSupported(url) {
|
|
35
|
+
return this.provider.isSupported(url);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
__decorate([
|
|
39
|
+
dep(),
|
|
40
|
+
__metadata("design:type", DownloadProvider)
|
|
41
|
+
], VideoDownloader.prototype, "provider", void 0);
|
|
42
|
+
//# sourceMappingURL=VideoDownloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoDownloader.js","sourceRoot":"","sources":["../src/VideoDownloader.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAkB,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAIrF;;;GAGG;AACH,MAAM,OAAO,eAAe;IAIxB;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,GAAW,EAAE,MAAgC;QACxD,MAAM,YAAY,GAAG,EAAE,GAAG,uBAAuB,EAAE,GAAG,MAAM,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,GAAW;QAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,GAAW;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC;CAEJ;AAxBkB;IAAd,GAAG,EAAE;8BAAoB,gBAAgB;iDAAC"}
|
package/out/index.d.ts
ADDED
package/out/index.js
ADDED
package/out/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { DownloadProvider } from '../DownloadProvider.js';
|
|
2
|
+
import { DownloadConfig } from '../schema/DownloadConfig.js';
|
|
3
|
+
import { DownloadResult } from '../schema/DownloadResult.js';
|
|
4
|
+
import { VideoMetadata } from '../schema/VideoMetadata.js';
|
|
5
|
+
export interface YtDlpProviderOptions {
|
|
6
|
+
outputDir?: string;
|
|
7
|
+
cookiesPath?: string;
|
|
8
|
+
/** Browser to get cookies from (safari, chrome, firefox, etc.) */
|
|
9
|
+
cookiesFromBrowser?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* yt-dlp based download provider.
|
|
13
|
+
* Supports 1000+ video platforms.
|
|
14
|
+
*
|
|
15
|
+
* ## Environment Variables
|
|
16
|
+
* - `YTDLP_COOKIES_FROM_BROWSER`: Browser to get cookies from (safari, chrome, firefox)
|
|
17
|
+
* - `YTDLP_COOKIES_PATH`: Path to cookies.txt file
|
|
18
|
+
*/
|
|
19
|
+
export declare class YtDlpProvider extends DownloadProvider {
|
|
20
|
+
readonly name = "yt-dlp";
|
|
21
|
+
private outputDir;
|
|
22
|
+
private cookiesPath?;
|
|
23
|
+
private cookiesFromBrowser?;
|
|
24
|
+
constructor(options?: YtDlpProviderOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Build cookies flag for yt-dlp commands.
|
|
27
|
+
*/
|
|
28
|
+
private getCookiesFlag;
|
|
29
|
+
download(url: string, config: DownloadConfig): Promise<DownloadResult>;
|
|
30
|
+
extractMetadata(url: string): Promise<VideoMetadata>;
|
|
31
|
+
isSupported(url: string): Promise<boolean>;
|
|
32
|
+
private mapMetadata;
|
|
33
|
+
private mapChapters;
|
|
34
|
+
private buildDownloadArgs;
|
|
35
|
+
private findDownloadedFiles;
|
|
36
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { stat, mkdir, readdir, writeFile } from 'fs/promises';
|
|
5
|
+
import { join, extname } from 'path';
|
|
6
|
+
import { tmpdir } from 'os';
|
|
7
|
+
import { DownloadProvider } from '../DownloadProvider.js';
|
|
8
|
+
import { DEFAULT_DOWNLOAD_CONFIG } from '../schema/DownloadConfig.js';
|
|
9
|
+
import { DownloadError } from '@ekipnico/video-core';
|
|
10
|
+
const execAsync = promisify(exec);
|
|
11
|
+
/**
|
|
12
|
+
* yt-dlp based download provider.
|
|
13
|
+
* Supports 1000+ video platforms.
|
|
14
|
+
*
|
|
15
|
+
* ## Environment Variables
|
|
16
|
+
* - `YTDLP_COOKIES_FROM_BROWSER`: Browser to get cookies from (safari, chrome, firefox)
|
|
17
|
+
* - `YTDLP_COOKIES_PATH`: Path to cookies.txt file
|
|
18
|
+
*/
|
|
19
|
+
export class YtDlpProvider extends DownloadProvider {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
super();
|
|
22
|
+
this.name = 'yt-dlp';
|
|
23
|
+
this.outputDir = options?.outputDir || join(tmpdir(), 'video-download');
|
|
24
|
+
this.cookiesPath = options?.cookiesPath || process.env.YTDLP_COOKIES_PATH;
|
|
25
|
+
this.cookiesFromBrowser = options?.cookiesFromBrowser || process.env.YTDLP_COOKIES_FROM_BROWSER;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build cookies flag for yt-dlp commands.
|
|
29
|
+
*/
|
|
30
|
+
getCookiesFlag() {
|
|
31
|
+
if (this.cookiesFromBrowser) {
|
|
32
|
+
return `--cookies-from-browser ${this.cookiesFromBrowser}`;
|
|
33
|
+
}
|
|
34
|
+
if (this.cookiesPath) {
|
|
35
|
+
return `--cookies "${this.cookiesPath}"`;
|
|
36
|
+
}
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
async download(url, config) {
|
|
40
|
+
const mergedConfig = { ...DEFAULT_DOWNLOAD_CONFIG, ...config };
|
|
41
|
+
const outputDir = mergedConfig.outputDir || this.outputDir;
|
|
42
|
+
// First get metadata to know the video ID
|
|
43
|
+
const metadata = await this.extractMetadata(url);
|
|
44
|
+
// Create per-video folder: {outputDir}/{videoId}/
|
|
45
|
+
const videoDir = join(outputDir, metadata.id);
|
|
46
|
+
if (!existsSync(videoDir)) {
|
|
47
|
+
await mkdir(videoDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
// Output template: video.{ext} inside the video folder
|
|
50
|
+
const outputTemplate = join(videoDir, 'video.%(ext)s');
|
|
51
|
+
// Build yt-dlp command
|
|
52
|
+
const args = this.buildDownloadArgs(url, outputTemplate, mergedConfig);
|
|
53
|
+
const command = `yt-dlp ${args.join(' ')}`;
|
|
54
|
+
try {
|
|
55
|
+
await execAsync(command, {
|
|
56
|
+
timeout: (mergedConfig.timeout || 600) * 1000,
|
|
57
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
58
|
+
});
|
|
59
|
+
// Find the downloaded video file
|
|
60
|
+
const files = await this.findDownloadedFiles(videoDir);
|
|
61
|
+
if (files.length === 0) {
|
|
62
|
+
throw new DownloadError(url, new Error('No files downloaded'));
|
|
63
|
+
}
|
|
64
|
+
const videoFile = files[0];
|
|
65
|
+
const stats = await stat(videoFile.path);
|
|
66
|
+
// Save metadata to meta.json
|
|
67
|
+
const metadataPath = join(videoDir, 'meta.json');
|
|
68
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
videoDir,
|
|
72
|
+
filePath: videoFile.path,
|
|
73
|
+
metadataPath,
|
|
74
|
+
format: videoFile.ext,
|
|
75
|
+
sizeBytes: stats.size,
|
|
76
|
+
duration: metadata.duration,
|
|
77
|
+
metadata,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof DownloadError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
throw new DownloadError(url, error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async extractMetadata(url) {
|
|
88
|
+
const cookiesFlag = this.getCookiesFlag();
|
|
89
|
+
const command = `yt-dlp ${cookiesFlag} --dump-json --no-download "${url}"`;
|
|
90
|
+
try {
|
|
91
|
+
const { stdout } = await execAsync(command, {
|
|
92
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
93
|
+
});
|
|
94
|
+
const data = JSON.parse(stdout);
|
|
95
|
+
return this.mapMetadata(data, url);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new DownloadError(url, error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async isSupported(url) {
|
|
102
|
+
try {
|
|
103
|
+
const command = `yt-dlp --simulate --no-download "${url}" 2>&1`;
|
|
104
|
+
await execAsync(command);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
mapMetadata(data, url) {
|
|
112
|
+
return {
|
|
113
|
+
id: data.id,
|
|
114
|
+
platform: (data.extractor_key?.toLowerCase() || data.extractor || 'unknown'),
|
|
115
|
+
url: data.webpage_url || url,
|
|
116
|
+
title: data.title || 'Untitled',
|
|
117
|
+
description: data.description || '',
|
|
118
|
+
uploader: data.uploader || data.channel || 'Unknown',
|
|
119
|
+
uploaderUrl: data.uploader_url,
|
|
120
|
+
channel: data.channel,
|
|
121
|
+
channelId: data.channel_id,
|
|
122
|
+
uploadDate: data.upload_date,
|
|
123
|
+
uploadTimestamp: data.timestamp,
|
|
124
|
+
duration: data.duration || 0,
|
|
125
|
+
viewCount: data.view_count,
|
|
126
|
+
likeCount: data.like_count,
|
|
127
|
+
commentCount: data.comment_count,
|
|
128
|
+
width: data.width,
|
|
129
|
+
height: data.height,
|
|
130
|
+
fps: data.fps,
|
|
131
|
+
vcodec: data.vcodec,
|
|
132
|
+
acodec: data.acodec,
|
|
133
|
+
filesize: (data.filesize || data.filesize_approx),
|
|
134
|
+
categories: data.categories,
|
|
135
|
+
tags: data.tags,
|
|
136
|
+
ageLimit: data.age_limit,
|
|
137
|
+
thumbnail: data.thumbnail,
|
|
138
|
+
thumbnails: data.thumbnails,
|
|
139
|
+
subtitles: data.subtitles,
|
|
140
|
+
chapters: this.mapChapters(data.chapters),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
mapChapters(chapters) {
|
|
144
|
+
if (!chapters) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return chapters.map(ch => ({
|
|
148
|
+
title: ch.title,
|
|
149
|
+
startTime: ch.start_time,
|
|
150
|
+
endTime: ch.end_time,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
buildDownloadArgs(url, outputPath, config) {
|
|
154
|
+
const args = [];
|
|
155
|
+
// Cookies for authentication
|
|
156
|
+
if (this.cookiesFromBrowser) {
|
|
157
|
+
args.push('--cookies-from-browser', this.cookiesFromBrowser);
|
|
158
|
+
}
|
|
159
|
+
else if (this.cookiesPath && existsSync(this.cookiesPath)) {
|
|
160
|
+
args.push('--cookies', `"${this.cookiesPath}"`);
|
|
161
|
+
}
|
|
162
|
+
// Format selection (quote format string to handle shell metacharacters like < and >)
|
|
163
|
+
if (config.audioOnly) {
|
|
164
|
+
args.push('-f', '"bestaudio[ext=m4a]/bestaudio"');
|
|
165
|
+
args.push('-x'); // Extract audio
|
|
166
|
+
}
|
|
167
|
+
else if (config.quality && config.quality !== 'best') {
|
|
168
|
+
args.push('-f', `"bestvideo[height<=${config.quality}]+bestaudio/best[height<=${config.quality}]"`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
args.push('-f', '"bestvideo+bestaudio/best"');
|
|
172
|
+
}
|
|
173
|
+
// Merge to mp4 when possible
|
|
174
|
+
args.push('--merge-output-format', 'mp4');
|
|
175
|
+
// Subtitles
|
|
176
|
+
if (config.includeSubtitles) {
|
|
177
|
+
args.push('--write-subs');
|
|
178
|
+
if (config.subtitleLang) {
|
|
179
|
+
args.push('--sub-lang', config.subtitleLang);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Rate limiting
|
|
183
|
+
if (config.rateLimit) {
|
|
184
|
+
args.push('-r', `${config.rateLimit}`);
|
|
185
|
+
}
|
|
186
|
+
// Output path
|
|
187
|
+
args.push('-o', `"${outputPath}"`);
|
|
188
|
+
// Extra args
|
|
189
|
+
if (config.extraArgs) {
|
|
190
|
+
args.push(...config.extraArgs);
|
|
191
|
+
}
|
|
192
|
+
// URL (always last)
|
|
193
|
+
args.push(`"${url}"`);
|
|
194
|
+
return args;
|
|
195
|
+
}
|
|
196
|
+
async findDownloadedFiles(videoDir) {
|
|
197
|
+
const files = await readdir(videoDir);
|
|
198
|
+
const results = [];
|
|
199
|
+
for (const file of files) {
|
|
200
|
+
// Look for video.* files (video.mp4, video.webm, etc.)
|
|
201
|
+
if (file.startsWith('video.')) {
|
|
202
|
+
const ext = extname(file).slice(1);
|
|
203
|
+
// Skip subtitle files
|
|
204
|
+
if (!['vtt', 'srt', 'ass', 'json'].includes(ext)) {
|
|
205
|
+
results.push({
|
|
206
|
+
path: join(videoDir, file),
|
|
207
|
+
ext,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return results;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=YtDlpProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"YtDlpProvider.js","sourceRoot":"","sources":["../../src/providers/YtDlpProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for video download.
|
|
3
|
+
*/
|
|
4
|
+
export interface DownloadConfig {
|
|
5
|
+
/** Output directory for downloaded files */
|
|
6
|
+
outputDir?: string;
|
|
7
|
+
/** Preferred format (best, mp4, webm, etc.) */
|
|
8
|
+
format?: string;
|
|
9
|
+
/** Maximum quality (480, 720, 1080, 2160, or 'best') */
|
|
10
|
+
quality?: string | number;
|
|
11
|
+
/** Download only audio */
|
|
12
|
+
audioOnly?: boolean;
|
|
13
|
+
/** Include subtitles if available */
|
|
14
|
+
includeSubtitles?: boolean;
|
|
15
|
+
/** Preferred subtitle language */
|
|
16
|
+
subtitleLang?: string;
|
|
17
|
+
/** Timeout in seconds */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
/** Rate limit (bytes per second) */
|
|
20
|
+
rateLimit?: number;
|
|
21
|
+
/** Custom yt-dlp arguments */
|
|
22
|
+
extraArgs?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare const DEFAULT_DOWNLOAD_CONFIG: DownloadConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloadConfig.js","sourceRoot":"","sources":["../../src/schema/DownloadConfig.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAC,MAAM,uBAAuB,GAAmB;IACnD,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,MAAM;IACf,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,KAAK;IACvB,OAAO,EAAE,GAAG;CACf,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { VideoMetadata } from './VideoMetadata.js';
|
|
2
|
+
/**
|
|
3
|
+
* Result of a video download operation.
|
|
4
|
+
*/
|
|
5
|
+
export interface DownloadResult {
|
|
6
|
+
/** Whether download succeeded */
|
|
7
|
+
success: boolean;
|
|
8
|
+
/** Path to the video's folder (contains all related files) */
|
|
9
|
+
videoDir: string;
|
|
10
|
+
/** Path to downloaded video file */
|
|
11
|
+
filePath: string;
|
|
12
|
+
/** Path to metadata JSON file */
|
|
13
|
+
metadataPath: string;
|
|
14
|
+
/** Video format (mp4, webm, etc.) */
|
|
15
|
+
format: string;
|
|
16
|
+
/** File size in bytes */
|
|
17
|
+
sizeBytes: number;
|
|
18
|
+
/** Duration in seconds */
|
|
19
|
+
duration: number;
|
|
20
|
+
/** Extracted metadata from platform */
|
|
21
|
+
metadata: VideoMetadata;
|
|
22
|
+
/** Path to subtitles file (if downloaded) */
|
|
23
|
+
subtitlesPath?: string;
|
|
24
|
+
/** Path to thumbnail (if downloaded) */
|
|
25
|
+
thumbnailPath?: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloadResult.js","sourceRoot":"","sources":["../../src/schema/DownloadResult.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Schema } from 'airtight';
|
|
2
|
+
/**
|
|
3
|
+
* Video platform-specific metadata extracted during download.
|
|
4
|
+
*/
|
|
5
|
+
export interface VideoMetadata {
|
|
6
|
+
id: string;
|
|
7
|
+
platform: string;
|
|
8
|
+
url: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
uploader: string;
|
|
12
|
+
uploaderUrl?: string;
|
|
13
|
+
channel?: string;
|
|
14
|
+
channelId?: string;
|
|
15
|
+
uploadDate?: string;
|
|
16
|
+
uploadTimestamp?: number;
|
|
17
|
+
duration: number;
|
|
18
|
+
viewCount?: number;
|
|
19
|
+
likeCount?: number;
|
|
20
|
+
commentCount?: number;
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
fps?: number;
|
|
24
|
+
vcodec?: string;
|
|
25
|
+
acodec?: string;
|
|
26
|
+
filesize?: number;
|
|
27
|
+
categories?: string[];
|
|
28
|
+
tags?: string[];
|
|
29
|
+
ageLimit?: number;
|
|
30
|
+
thumbnail?: string;
|
|
31
|
+
thumbnails?: Array<{
|
|
32
|
+
url: string;
|
|
33
|
+
width?: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
}>;
|
|
36
|
+
subtitles?: Record<string, Array<{
|
|
37
|
+
ext: string;
|
|
38
|
+
url: string;
|
|
39
|
+
}>>;
|
|
40
|
+
chapters?: Array<{
|
|
41
|
+
title: string;
|
|
42
|
+
startTime: number;
|
|
43
|
+
endTime: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export declare const VideoMetadataSchema: Schema<VideoMetadata>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Schema } from 'airtight';
|
|
2
|
+
export const VideoMetadataSchema = new Schema({
|
|
3
|
+
type: 'object',
|
|
4
|
+
properties: {
|
|
5
|
+
id: { type: 'string' },
|
|
6
|
+
platform: { type: 'string' },
|
|
7
|
+
url: { type: 'string' },
|
|
8
|
+
title: { type: 'string' },
|
|
9
|
+
description: { type: 'string' },
|
|
10
|
+
uploader: { type: 'string' },
|
|
11
|
+
uploaderUrl: { type: 'string', optional: true },
|
|
12
|
+
channel: { type: 'string', optional: true },
|
|
13
|
+
channelId: { type: 'string', optional: true },
|
|
14
|
+
uploadDate: { type: 'string', optional: true },
|
|
15
|
+
uploadTimestamp: { type: 'number', optional: true },
|
|
16
|
+
duration: { type: 'number' },
|
|
17
|
+
viewCount: { type: 'number', optional: true },
|
|
18
|
+
likeCount: { type: 'number', optional: true },
|
|
19
|
+
commentCount: { type: 'number', optional: true },
|
|
20
|
+
width: { type: 'number', optional: true },
|
|
21
|
+
height: { type: 'number', optional: true },
|
|
22
|
+
fps: { type: 'number', optional: true },
|
|
23
|
+
vcodec: { type: 'string', optional: true },
|
|
24
|
+
acodec: { type: 'string', optional: true },
|
|
25
|
+
filesize: { type: 'number', optional: true },
|
|
26
|
+
categories: { type: 'array', items: { type: 'string' }, optional: true },
|
|
27
|
+
tags: { type: 'array', items: { type: 'string' }, optional: true },
|
|
28
|
+
ageLimit: { type: 'number', optional: true },
|
|
29
|
+
thumbnail: { type: 'string', optional: true },
|
|
30
|
+
thumbnails: { type: 'any', optional: true },
|
|
31
|
+
subtitles: { type: 'any', optional: true },
|
|
32
|
+
chapters: { type: 'any', optional: true },
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=VideoMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VideoMetadata.js","sourceRoot":"","sources":["../../src/schema/VideoMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAgElC,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,MAAM,CAAgB;IACzD,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACR,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACtB,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACvB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACzB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC/B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/C,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC3C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC7C,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC9C,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QACnD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC7C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC7C,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAChD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QACzC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC1C,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QACvC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC5C,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;QACxE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;QAClE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC5C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC7C,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC3C,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC5C;CACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|