@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.
Files changed (49) hide show
  1. package/out/DownloadProvider.d.ts +22 -0
  2. package/out/DownloadProvider.js +7 -0
  3. package/out/DownloadProvider.js.map +1 -0
  4. package/out/VideoDownloadModule.d.ts +27 -0
  5. package/out/VideoDownloadModule.js +39 -0
  6. package/out/VideoDownloadModule.js.map +1 -0
  7. package/out/VideoDownloader.d.ts +22 -0
  8. package/out/VideoDownloader.js +42 -0
  9. package/out/VideoDownloader.js.map +1 -0
  10. package/out/index.d.ts +5 -0
  11. package/out/index.js +6 -0
  12. package/out/index.js.map +1 -0
  13. package/out/providers/YtDlpProvider.d.ts +36 -0
  14. package/out/providers/YtDlpProvider.js +215 -0
  15. package/out/providers/YtDlpProvider.js.map +1 -0
  16. package/out/schema/DownloadConfig.d.ts +24 -0
  17. package/out/schema/DownloadConfig.js +8 -0
  18. package/out/schema/DownloadConfig.js.map +1 -0
  19. package/out/schema/DownloadResult.d.ts +26 -0
  20. package/out/schema/DownloadResult.js +2 -0
  21. package/out/schema/DownloadResult.js.map +1 -0
  22. package/out/schema/VideoMetadata.d.ts +46 -0
  23. package/out/schema/VideoMetadata.js +35 -0
  24. package/out/schema/VideoMetadata.js.map +1 -0
  25. package/out/schema/index.d.ts +3 -0
  26. package/out/schema/index.js +4 -0
  27. package/out/schema/index.js.map +1 -0
  28. package/out/test/VideoDownloadModule.test.d.ts +1 -0
  29. package/out/test/VideoDownloadModule.test.js +61 -0
  30. package/out/test/VideoDownloadModule.test.js.map +1 -0
  31. package/out/test/VideoDownloader.test.d.ts +1 -0
  32. package/out/test/VideoDownloader.test.js +132 -0
  33. package/out/test/VideoDownloader.test.js.map +1 -0
  34. package/out/test/YtDlpProvider.test.d.ts +1 -0
  35. package/out/test/YtDlpProvider.test.js +215 -0
  36. package/out/test/YtDlpProvider.test.js.map +1 -0
  37. package/out/test/channel-download.test.d.ts +1 -0
  38. package/out/test/channel-download.test.js +269 -0
  39. package/out/test/channel-download.test.js.map +1 -0
  40. package/out/test/integration.test.d.ts +1 -0
  41. package/out/test/integration.test.js +120 -0
  42. package/out/test/integration.test.js.map +1 -0
  43. package/out/test/schema.test.d.ts +1 -0
  44. package/out/test/schema.test.js +62 -0
  45. package/out/test/schema.test.js.map +1 -0
  46. package/out/test/setup.test.d.ts +5 -0
  47. package/out/test/setup.test.js +7 -0
  48. package/out/test/setup.test.js.map +1 -0
  49. package/package.json +34 -0
@@ -0,0 +1,269 @@
1
+ import 'dotenv/config';
2
+ import assert from 'node:assert';
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { YtDlpProvider } from '../providers/YtDlpProvider.js';
8
+ const execAsync = promisify(exec);
9
+ // Configuration from environment variables
10
+ const CHANNEL_URL = process.env.CHANNEL_URL || 'https://www.youtube.com/@theseriouscto';
11
+ const OUTPUT_DIR = process.env.OUTPUT_DIR || join(process.cwd(), 'downloads', 'theseriouscto');
12
+ const MAX_VIDEOS = parseInt(process.env.MAX_VIDEOS || '0', 10) || Infinity;
13
+ const SKIP_EXISTING = process.env.SKIP_EXISTING !== 'false';
14
+ // Channel-level files go in _channel/ subfolder
15
+ const CHANNEL_DIR = join(OUTPUT_DIR, '_channel');
16
+ const PROGRESS_FILE = join(CHANNEL_DIR, 'progress.json');
17
+ /**
18
+ * Check if yt-dlp is installed and available.
19
+ */
20
+ async function isYtDlpInstalled() {
21
+ try {
22
+ await execAsync('yt-dlp --version');
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * List all videos from a YouTube channel using yt-dlp.
31
+ */
32
+ async function listChannelVideos(channelUrl) {
33
+ console.log(`\n Fetching video list from channel: ${channelUrl}`);
34
+ // Use --flat-playlist to only get video info without downloading
35
+ // Use videos tab to get all videos
36
+ const videosUrl = `${channelUrl}/videos`;
37
+ const command = `yt-dlp --flat-playlist --dump-json "${videosUrl}"`;
38
+ try {
39
+ const { stdout } = await execAsync(command, {
40
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large channels
41
+ timeout: 300000, // 5 minute timeout for listing
42
+ });
43
+ const videos = [];
44
+ const lines = stdout.trim().split('\n');
45
+ for (const line of lines) {
46
+ if (!line.trim()) {
47
+ continue;
48
+ }
49
+ try {
50
+ const data = JSON.parse(line);
51
+ videos.push({
52
+ id: data.id,
53
+ url: data.url || `https://www.youtube.com/watch?v=${data.id}`,
54
+ title: data.title || 'Unknown',
55
+ availability: data.availability || null,
56
+ });
57
+ }
58
+ catch (parseErr) {
59
+ console.log(` Warning: Failed to parse line: ${line.substring(0, 50)}...`);
60
+ }
61
+ }
62
+ // Filter out members-only videos
63
+ const publicVideos = videos.filter(v => v.availability !== 'subscriber_only');
64
+ const membersOnly = videos.length - publicVideos.length;
65
+ if (membersOnly > 0) {
66
+ console.log(` Filtered out ${membersOnly} members-only videos`);
67
+ }
68
+ return publicVideos;
69
+ }
70
+ catch (error) {
71
+ console.error(' Error listing channel videos:', error.message);
72
+ throw error;
73
+ }
74
+ }
75
+ /**
76
+ * Load or initialize download progress.
77
+ */
78
+ function loadProgress() {
79
+ // Ensure _channel directory exists
80
+ if (!existsSync(CHANNEL_DIR)) {
81
+ mkdirSync(CHANNEL_DIR, { recursive: true });
82
+ }
83
+ if (existsSync(PROGRESS_FILE)) {
84
+ try {
85
+ const data = readFileSync(PROGRESS_FILE, 'utf-8');
86
+ return JSON.parse(data);
87
+ }
88
+ catch {
89
+ // Corrupted file, start fresh
90
+ }
91
+ }
92
+ return {
93
+ totalVideos: 0,
94
+ downloaded: [],
95
+ failed: [],
96
+ skipped: [],
97
+ };
98
+ }
99
+ /**
100
+ * Save download progress.
101
+ */
102
+ function saveProgress(progress) {
103
+ writeFileSync(PROGRESS_FILE, JSON.stringify(progress, null, 2));
104
+ }
105
+ describe('Channel Download Tests (@theseriouscto)', function () {
106
+ let ytDlpAvailable = false;
107
+ let provider;
108
+ // Very long timeout for channel downloads
109
+ this.timeout(0); // No timeout
110
+ before(async function () {
111
+ ytDlpAvailable = await isYtDlpInstalled();
112
+ if (!ytDlpAvailable) {
113
+ console.log(' ⚠️ yt-dlp not installed, skipping channel download tests');
114
+ return;
115
+ }
116
+ // Create output directory and _channel subfolder
117
+ if (!existsSync(OUTPUT_DIR)) {
118
+ mkdirSync(OUTPUT_DIR, { recursive: true });
119
+ }
120
+ if (!existsSync(CHANNEL_DIR)) {
121
+ mkdirSync(CHANNEL_DIR, { recursive: true });
122
+ }
123
+ provider = new YtDlpProvider({ outputDir: OUTPUT_DIR });
124
+ });
125
+ describe('List channel videos', function () {
126
+ it('lists all videos from @theseriouscto channel', async function () {
127
+ if (!ytDlpAvailable) {
128
+ this.skip();
129
+ return;
130
+ }
131
+ const videos = await listChannelVideos(CHANNEL_URL);
132
+ console.log(`\n Found ${videos.length} videos on the channel`);
133
+ // Save video list to _channel/ folder
134
+ const listFile = join(CHANNEL_DIR, 'video-list.json');
135
+ writeFileSync(listFile, JSON.stringify(videos, null, 2));
136
+ console.log(` Video list saved to: ${listFile}`);
137
+ assert.ok(videos.length > 0, 'Channel should have at least one video');
138
+ // Print first few videos
139
+ console.log('\n First 5 videos:');
140
+ for (const video of videos.slice(0, 5)) {
141
+ console.log(` - ${video.title} (${video.id})`);
142
+ }
143
+ });
144
+ });
145
+ describe('Download channel videos', function () {
146
+ it('downloads all videos from channel (with resume support)', async function () {
147
+ if (!ytDlpAvailable) {
148
+ this.skip();
149
+ return;
150
+ }
151
+ // First list all videos
152
+ const videos = await listChannelVideos(CHANNEL_URL);
153
+ console.log(`\n Total videos to process: ${videos.length}`);
154
+ // Load existing progress
155
+ const progress = loadProgress();
156
+ progress.totalVideos = videos.length;
157
+ const maxVideos = Math.min(videos.length, MAX_VIDEOS);
158
+ console.log(` Max videos to download: ${maxVideos === Infinity ? 'all' : maxVideos}`);
159
+ console.log(` Skip existing: ${SKIP_EXISTING}`);
160
+ console.log(` Output directory: ${OUTPUT_DIR}`);
161
+ console.log(` Already downloaded: ${progress.downloaded.length}`);
162
+ console.log(` Already failed: ${progress.failed.length}`);
163
+ let downloadCount = 0;
164
+ let failCount = 0;
165
+ let skipCount = 0;
166
+ for (let i = 0; i < Math.min(videos.length, maxVideos); i++) {
167
+ const video = videos[i];
168
+ const videoNum = i + 1;
169
+ // Skip if already processed
170
+ if (SKIP_EXISTING && progress.downloaded.includes(video.id)) {
171
+ console.log(` [${videoNum}/${videos.length}] Skipping (already downloaded): ${video.title}`);
172
+ skipCount++;
173
+ continue;
174
+ }
175
+ if (SKIP_EXISTING && progress.failed.includes(video.id)) {
176
+ console.log(` [${videoNum}/${videos.length}] Skipping (previously failed): ${video.title}`);
177
+ skipCount++;
178
+ continue;
179
+ }
180
+ console.log(`\n [${videoNum}/${videos.length}] Downloading: ${video.title}`);
181
+ console.log(` URL: ${video.url}`);
182
+ try {
183
+ const startTime = Date.now();
184
+ const result = await provider.download(video.url, {
185
+ quality: 720, // Limit to 720p to save space/time
186
+ timeout: 600, // 10 minute timeout per video
187
+ });
188
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
189
+ const sizeMB = (result.sizeBytes / 1024 / 1024).toFixed(1);
190
+ console.log(` ✓ Downloaded: ${result.filePath}`);
191
+ console.log(` Size: ${sizeMB} MB, Duration: ${result.duration}s, Time: ${elapsed}s`);
192
+ progress.downloaded.push(video.id);
193
+ downloadCount++;
194
+ saveProgress(progress);
195
+ }
196
+ catch (error) {
197
+ console.log(` ✗ Failed: ${error.message}`);
198
+ progress.failed.push(video.id);
199
+ failCount++;
200
+ saveProgress(progress);
201
+ }
202
+ }
203
+ console.log('\n ═══════════════════════════════════════');
204
+ console.log(` Download Summary:`);
205
+ console.log(` Total videos: ${videos.length}`);
206
+ console.log(` Downloaded this run: ${downloadCount}`);
207
+ console.log(` Failed this run: ${failCount}`);
208
+ console.log(` Skipped: ${skipCount}`);
209
+ console.log(` Total downloaded: ${progress.downloaded.length}`);
210
+ console.log(` Total failed: ${progress.failed.length}`);
211
+ console.log(` Progress saved to: ${PROGRESS_FILE}`);
212
+ console.log(' ═══════════════════════════════════════\n');
213
+ // Write final summary to _channel/ folder
214
+ const summaryFile = join(CHANNEL_DIR, 'download-summary.json');
215
+ writeFileSync(summaryFile, JSON.stringify({
216
+ channelUrl: CHANNEL_URL,
217
+ totalVideos: videos.length,
218
+ downloaded: progress.downloaded.length,
219
+ failed: progress.failed.length,
220
+ downloadedIds: progress.downloaded,
221
+ failedIds: progress.failed,
222
+ timestamp: new Date().toISOString(),
223
+ }, null, 2));
224
+ assert.ok(true, 'Download process completed');
225
+ });
226
+ });
227
+ describe('Extract metadata only', function () {
228
+ it('extracts metadata for all videos without downloading', async function () {
229
+ if (!ytDlpAvailable) {
230
+ this.skip();
231
+ return;
232
+ }
233
+ // First list all videos
234
+ const videos = await listChannelVideos(CHANNEL_URL);
235
+ const maxVideos = Math.min(videos.length, MAX_VIDEOS);
236
+ console.log(`\n Extracting metadata for ${maxVideos === Infinity ? videos.length : maxVideos} videos...`);
237
+ const metadata = [];
238
+ const errors = [];
239
+ for (let i = 0; i < maxVideos; i++) {
240
+ const video = videos[i];
241
+ const videoNum = i + 1;
242
+ process.stdout.write(`\r [${videoNum}/${maxVideos}] Processing: ${video.id}...`);
243
+ try {
244
+ const meta = await provider.extractMetadata(video.url);
245
+ metadata.push(meta);
246
+ }
247
+ catch (error) {
248
+ errors.push({ id: video.id, error: error.message });
249
+ }
250
+ }
251
+ console.log('\n');
252
+ console.log(` ✓ Successfully extracted: ${metadata.length}`);
253
+ console.log(` ✗ Failed: ${errors.length}`);
254
+ // Save metadata to _channel/ folder
255
+ const metadataFile = join(CHANNEL_DIR, 'all-metadata.json');
256
+ writeFileSync(metadataFile, JSON.stringify(metadata, null, 2));
257
+ console.log(` Metadata saved to: ${metadataFile}`);
258
+ // Calculate total duration
259
+ const totalDuration = metadata.reduce((sum, m) => sum + (m.duration || 0), 0);
260
+ const totalHours = (totalDuration / 3600).toFixed(1);
261
+ console.log(` Total content duration: ${totalHours} hours`);
262
+ // Calculate total views
263
+ const totalViews = metadata.reduce((sum, m) => sum + (m.viewCount || 0), 0);
264
+ console.log(` Total views: ${totalViews.toLocaleString()}`);
265
+ assert.ok(metadata.length > 0, 'Should have extracted at least some metadata');
266
+ });
267
+ });
268
+ });
269
+ //# sourceMappingURL=channel-download.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-download.test.js","sourceRoot":"","sources":["../../src/test/channel-download.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ import assert from 'node:assert';
2
+ import { exec } from 'child_process';
3
+ import { promisify } from 'util';
4
+ import { YtDlpProvider } from '../providers/YtDlpProvider.js';
5
+ import { createVideoDownloadMesh } from '../VideoDownloadModule.js';
6
+ import { VideoDownloader } from '../VideoDownloader.js';
7
+ const execAsync = promisify(exec);
8
+ /**
9
+ * Check if yt-dlp is installed and available.
10
+ */
11
+ async function isYtDlpInstalled() {
12
+ try {
13
+ await execAsync('yt-dlp --version');
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ describe('Integration Tests (require yt-dlp)', function () {
21
+ let ytDlpAvailable = false;
22
+ before(async function () {
23
+ ytDlpAvailable = await isYtDlpInstalled();
24
+ if (!ytDlpAvailable) {
25
+ console.log(' ⚠️ yt-dlp not installed, skipping integration tests');
26
+ }
27
+ });
28
+ describe('YtDlpProvider.extractMetadata', function () {
29
+ // Increase timeout for network requests
30
+ this.timeout(30000);
31
+ it('extracts metadata from YouTube video', async function () {
32
+ if (!ytDlpAvailable) {
33
+ this.skip();
34
+ return;
35
+ }
36
+ const provider = new YtDlpProvider();
37
+ // Use a well-known, stable YouTube video (Rick Astley - Never Gonna Give You Up)
38
+ const metadata = await provider.extractMetadata('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
39
+ assert.strictEqual(metadata.id, 'dQw4w9WgXcQ');
40
+ assert.strictEqual(metadata.platform, 'youtube');
41
+ assert.ok(metadata.title.includes('Rick Astley') || metadata.title.includes('Never Gonna'));
42
+ assert.ok(metadata.duration > 0);
43
+ assert.ok(metadata.uploader);
44
+ });
45
+ it('throws error for invalid URL', async function () {
46
+ if (!ytDlpAvailable) {
47
+ this.skip();
48
+ return;
49
+ }
50
+ const provider = new YtDlpProvider();
51
+ await assert.rejects(() => provider.extractMetadata('https://youtube.com/watch?v=invalid_nonexistent_video_id_123'), (error) => {
52
+ assert.ok(error.message.includes('Failed to download'));
53
+ return true;
54
+ });
55
+ });
56
+ });
57
+ describe('YtDlpProvider.isSupported', function () {
58
+ this.timeout(15000);
59
+ it('returns true for YouTube URL', async function () {
60
+ if (!ytDlpAvailable) {
61
+ this.skip();
62
+ return;
63
+ }
64
+ const provider = new YtDlpProvider();
65
+ const result = await provider.isSupported('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
66
+ assert.strictEqual(result, true);
67
+ });
68
+ it('returns true for Vimeo URL', async function () {
69
+ if (!ytDlpAvailable) {
70
+ this.skip();
71
+ return;
72
+ }
73
+ const provider = new YtDlpProvider();
74
+ // Use a known public Vimeo video
75
+ // Note: Vimeo videos can become unavailable or geo-restricted
76
+ const result = await provider.isSupported('https://vimeo.com/148751763');
77
+ if (!result) {
78
+ // Skip if Vimeo video is unavailable (external dependency)
79
+ console.log(' ⚠️ Vimeo video unavailable, skipping');
80
+ this.skip();
81
+ return;
82
+ }
83
+ assert.strictEqual(result, true);
84
+ });
85
+ it('returns false for unsupported URL', async function () {
86
+ if (!ytDlpAvailable) {
87
+ this.skip();
88
+ return;
89
+ }
90
+ const provider = new YtDlpProvider();
91
+ const result = await provider.isSupported('https://example.com/not-a-video');
92
+ assert.strictEqual(result, false);
93
+ });
94
+ });
95
+ describe('VideoDownloader via DI', function () {
96
+ this.timeout(30000);
97
+ it('works through mesh DI', async function () {
98
+ if (!ytDlpAvailable) {
99
+ this.skip();
100
+ return;
101
+ }
102
+ const mesh = createVideoDownloadMesh();
103
+ const downloader = mesh.resolve(VideoDownloader);
104
+ const metadata = await downloader.extractMetadata('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
105
+ assert.strictEqual(metadata.id, 'dQw4w9WgXcQ');
106
+ assert.ok(metadata.title);
107
+ });
108
+ it('isSupported works through DI', async function () {
109
+ if (!ytDlpAvailable) {
110
+ this.skip();
111
+ return;
112
+ }
113
+ const mesh = createVideoDownloadMesh();
114
+ const downloader = mesh.resolve(VideoDownloader);
115
+ const supported = await downloader.isSupported('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
116
+ assert.strictEqual(supported, true);
117
+ });
118
+ });
119
+ });
120
+ //# sourceMappingURL=integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../../src/test/integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC;;GAEG;AACH,KAAK,UAAU,gBAAgB;IAC3B,IAAI,CAAC;QACD,MAAM,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,oCAAoC,EAAE;IAE3C,IAAI,cAAc,GAAG,KAAK,CAAC;IAE3B,MAAM,CAAC,KAAK;QACR,cAAc,GAAG,MAAM,gBAAgB,EAAE,CAAC;QAC1C,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;QAC5E,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,+BAA+B,EAAE;QAEtC,wCAAwC;QACxC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEpB,EAAE,CAAC,sCAAsC,EAAE,KAAK;YAC5C,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;YAErC,iFAAiF;YACjF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,6CAA6C,CAAC,CAAC;YAE/F,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YAC/C,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YACjD,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;YAC5F,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK;YACpC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;YAErC,MAAM,MAAM,CAAC,OAAO,CAChB,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,8DAA8D,CAAC,EAC9F,CAAC,KAAY,EAAE,EAAE;gBACb,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC;gBACxD,OAAO,IAAI,CAAC;YAChB,CAAC,CACJ,CAAC;QACN,CAAC,CAAC,CAAC;IAEP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2BAA2B,EAAE;QAElC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEpB,EAAE,CAAC,8BAA8B,EAAE,KAAK;YACpC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,6CAA6C,CAAC,CAAC;YAEzF,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK;YAClC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;YACrC,iCAAiC;YACjC,8DAA8D;YAC9D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,6BAA6B,CAAC,CAAC;YAEzE,IAAI,CAAC,MAAM,EAAE,CAAC;gBACV,2DAA2D;gBAC3D,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;gBACzD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK;YACzC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,iCAAiC,CAAC,CAAC;YAE7E,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IAEP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE;QAE/B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEpB,EAAE,CAAC,uBAAuB,EAAE,KAAK;YAC7B,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,IAAI,GAAG,uBAAuB,EAAE,CAAC;YACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAEjD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,eAAe,CAAC,6CAA6C,CAAC,CAAC;YAEjG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YAC/C,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK;YACpC,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO;YACX,CAAC;YAED,MAAM,IAAI,GAAG,uBAAuB,EAAE,CAAC;YACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAEjD,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,6CAA6C,CAAC,CAAC;YAE9F,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IAEP,CAAC,CAAC,CAAC;AAEP,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import assert from 'node:assert';
2
+ import { DEFAULT_DOWNLOAD_CONFIG } from '../schema/DownloadConfig.js';
3
+ describe('Schemas', () => {
4
+ describe('DownloadConfig', () => {
5
+ describe('DEFAULT_DOWNLOAD_CONFIG', () => {
6
+ it('has correct default values', () => {
7
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.format, 'best');
8
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.quality, 'best');
9
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.audioOnly, false);
10
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.includeSubtitles, false);
11
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.timeout, 600);
12
+ });
13
+ it('does not have outputDir set by default', () => {
14
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.outputDir, undefined);
15
+ });
16
+ it('does not have rateLimit set by default', () => {
17
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.rateLimit, undefined);
18
+ });
19
+ it('does not have extraArgs set by default', () => {
20
+ assert.strictEqual(DEFAULT_DOWNLOAD_CONFIG.extraArgs, undefined);
21
+ });
22
+ });
23
+ describe('config merging', () => {
24
+ it('merges partial config with defaults', () => {
25
+ const partial = {
26
+ quality: 720,
27
+ audioOnly: true,
28
+ };
29
+ const merged = { ...DEFAULT_DOWNLOAD_CONFIG, ...partial };
30
+ assert.strictEqual(merged.format, 'best');
31
+ assert.strictEqual(merged.quality, 720);
32
+ assert.strictEqual(merged.audioOnly, true);
33
+ assert.strictEqual(merged.includeSubtitles, false);
34
+ assert.strictEqual(merged.timeout, 600);
35
+ });
36
+ it('allows overriding all defaults', () => {
37
+ const full = {
38
+ outputDir: '/custom/path',
39
+ format: 'mp4',
40
+ quality: 1080,
41
+ audioOnly: false,
42
+ includeSubtitles: true,
43
+ subtitleLang: 'en',
44
+ timeout: 300,
45
+ rateLimit: 1000000,
46
+ extraArgs: ['--verbose'],
47
+ };
48
+ const merged = { ...DEFAULT_DOWNLOAD_CONFIG, ...full };
49
+ assert.strictEqual(merged.outputDir, '/custom/path');
50
+ assert.strictEqual(merged.format, 'mp4');
51
+ assert.strictEqual(merged.quality, 1080);
52
+ assert.strictEqual(merged.audioOnly, false);
53
+ assert.strictEqual(merged.includeSubtitles, true);
54
+ assert.strictEqual(merged.subtitleLang, 'en');
55
+ assert.strictEqual(merged.timeout, 300);
56
+ assert.strictEqual(merged.rateLimit, 1000000);
57
+ assert.deepStrictEqual(merged.extraArgs, ['--verbose']);
58
+ });
59
+ });
60
+ });
61
+ });
62
+ //# sourceMappingURL=schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/test/schema.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Test setup file for video-download.
3
+ * This file is loaded before all tests.
4
+ */
5
+ import 'reflect-metadata';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Test setup file for video-download.
3
+ * This file is loaded before all tests.
4
+ */
5
+ // Import reflect-metadata for mesh-ioc decorators
6
+ import 'reflect-metadata';
7
+ //# sourceMappingURL=setup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.test.js","sourceRoot":"","sources":["../../src/test/setup.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kDAAkD;AAClD,OAAO,kBAAkB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@ekipnico/video-fetch",
3
+ "version": "1.0.0",
4
+ "description": "Video metadata extraction and processing utilities",
5
+ "type": "module",
6
+ "main": "out/index.js",
7
+ "types": "out/index.d.ts",
8
+ "files": ["out"],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "npm run build && mocha",
12
+ "test:unit": "npm run build && mocha --grep 'Integration' --invert",
13
+ "test:integration": "npm run build && mocha --grep 'Integration'",
14
+ "test:channel": "npm run build && mocha --grep 'Channel Download'",
15
+ "prepublishOnly": "npm run build",
16
+ "publish:patch": "npm version patch && npm publish",
17
+ "publish:minor": "npm version minor && npm publish",
18
+ "publish:major": "npm version major && npm publish"
19
+ },
20
+ "dependencies": {
21
+ "@ekipnico/video-core": "^1.0.0",
22
+ "airtight": "^5.7.2",
23
+ "dotenv": "^16.4.5",
24
+ "mesh-ioc": "^4.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "mocha": "^10.4.0",
28
+ "@types/mocha": "^10.0.6",
29
+ "reflect-metadata": "^0.2.2"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }