flux-dl 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/LICENSE +21 -0
- package/README.md +454 -0
- package/package.json +50 -0
- package/src/VideoDownloader.js +197 -0
- package/src/index.js +11 -0
- package/src/platforms/dailymotion.js +71 -0
- package/src/platforms/examplePlatform.js +45 -0
- package/src/platforms/index.js +13 -0
- package/src/platforms/vimeo.js +66 -0
- package/src/platforms/youtube-deno.js +209 -0
- package/src/platforms/youtube.js +449 -0
- package/src/utils/browserEmulation.js +241 -0
- package/src/utils/cookieManager.js +110 -0
- package/src/utils/encryption.js +102 -0
- package/src/utils/requestSpoofing.js +141 -0
- package/src/utils/signatureDecoder.js +164 -0
- package/src/utils/youtubeInnerTube.js +201 -0
- package/src/utils/youtubeParser.js +283 -0
- package/src/utils/youtubeSearch.js +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 flux-dl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# flux-dl
|
|
2
|
+
|
|
3
|
+
Eine leistungsstarke Node.js Library zum Downloaden von Videos von YouTube, Vimeo und Dailymotion - komplett in JavaScript, keine externen Binaries!
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install flux-dl
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
✅ YouTube Videos & Audio downloaden (ohne yt-dlp!)
|
|
14
|
+
✅ YouTube Suche - Song-Namen eingeben statt URLs!
|
|
15
|
+
✅ Vimeo Videos downloaden
|
|
16
|
+
✅ Dailymotion Videos downloaden
|
|
17
|
+
✅ Automatische Format-Auswahl
|
|
18
|
+
✅ Progress-Tracking
|
|
19
|
+
✅ Cookie-Support für authentifizierte Videos
|
|
20
|
+
✅ Perfekt für Bots (WhatsApp, Discord, Telegram)
|
|
21
|
+
✅ Komplett in JavaScript - keine externen Binaries
|
|
22
|
+
|
|
23
|
+
## API Dokumentation
|
|
24
|
+
|
|
25
|
+
### YouTubeSearch
|
|
26
|
+
|
|
27
|
+
Suche nach Videos ohne URL - perfekt für Bots!
|
|
28
|
+
|
|
29
|
+
#### Constructor
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
const { YouTubeSearch } = require('flux-dl');
|
|
33
|
+
|
|
34
|
+
const search = new YouTubeSearch();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Methoden
|
|
38
|
+
|
|
39
|
+
##### `search(query, maxResults)`
|
|
40
|
+
|
|
41
|
+
Sucht nach Videos auf YouTube.
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const results = await search.search('Rick Astley Never Gonna Give You Up', 5);
|
|
45
|
+
|
|
46
|
+
results.forEach(video => {
|
|
47
|
+
console.log(video.title);
|
|
48
|
+
console.log(video.url);
|
|
49
|
+
console.log(video.author);
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Response:**
|
|
54
|
+
```javascript
|
|
55
|
+
[
|
|
56
|
+
{
|
|
57
|
+
videoId: "dQw4w9WgXcQ",
|
|
58
|
+
title: "Rick Astley - Never Gonna Give You Up",
|
|
59
|
+
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
60
|
+
thumbnail: "https://...",
|
|
61
|
+
duration: 213,
|
|
62
|
+
author: "Rick Astley",
|
|
63
|
+
views: "1.7B views",
|
|
64
|
+
publishedTime: "15 years ago"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
##### `searchAndDownload(query, downloader, options)`
|
|
70
|
+
|
|
71
|
+
Sucht und lädt direkt das erste Ergebnis herunter.
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const { VideoDownloader, YouTubeSearch } = require('flux-dl');
|
|
75
|
+
|
|
76
|
+
const downloader = new VideoDownloader();
|
|
77
|
+
const search = new YouTubeSearch();
|
|
78
|
+
|
|
79
|
+
const result = await search.searchAndDownload(
|
|
80
|
+
'Rick Astley Never Gonna Give You Up',
|
|
81
|
+
downloader,
|
|
82
|
+
{
|
|
83
|
+
audioOnly: true,
|
|
84
|
+
outputPath: './downloads'
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
console.log(result.filepath);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### VideoDownloader
|
|
92
|
+
|
|
93
|
+
Die Hauptklasse für Video-Downloads.
|
|
94
|
+
|
|
95
|
+
#### Constructor
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const { VideoDownloader } = require('flux-dl');
|
|
99
|
+
|
|
100
|
+
const downloader = new VideoDownloader({
|
|
101
|
+
userAgent: 'Mozilla/5.0 ...', // Optional
|
|
102
|
+
timeout: 30000, // Optional
|
|
103
|
+
encryptionKey: 'your-key' // Optional
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Methoden
|
|
108
|
+
|
|
109
|
+
##### `getVideoInfo(url)`
|
|
110
|
+
|
|
111
|
+
Extrahiert Video-Informationen ohne Download.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const info = await downloader.getVideoInfo('https://youtube.com/watch?v=...');
|
|
115
|
+
|
|
116
|
+
console.log(info.title); // Video-Titel
|
|
117
|
+
console.log(info.author); // Autor/Kanal
|
|
118
|
+
console.log(info.duration); // Dauer in Sekunden
|
|
119
|
+
console.log(info.quality); // Qualität (z.B. "1080p")
|
|
120
|
+
console.log(info.videoUrl); // Download-URL
|
|
121
|
+
console.log(info.allFormats); // Alle verfügbaren Formate
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Response:**
|
|
125
|
+
```javascript
|
|
126
|
+
{
|
|
127
|
+
title: "Video Titel",
|
|
128
|
+
videoId: "dQw4w9WgXcQ",
|
|
129
|
+
duration: 213,
|
|
130
|
+
thumbnail: "https://...",
|
|
131
|
+
author: "Kanal Name",
|
|
132
|
+
viewCount: 1742252685,
|
|
133
|
+
videoUrl: "https://...",
|
|
134
|
+
quality: "360p",
|
|
135
|
+
format: { /* Format-Details */ },
|
|
136
|
+
allFormats: [ /* Array aller Formate */ ],
|
|
137
|
+
platform: "YouTube"
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
##### `download(url, outputPath)`
|
|
142
|
+
|
|
143
|
+
Lädt ein Video herunter.
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const result = await downloader.download(
|
|
147
|
+
'https://youtube.com/watch?v=...',
|
|
148
|
+
'./downloads' // Optional, default: './downloads'
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
console.log(result.filepath); // Pfad zur heruntergeladenen Datei
|
|
152
|
+
console.log(result.info); // Video-Informationen
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Response:**
|
|
156
|
+
```javascript
|
|
157
|
+
{
|
|
158
|
+
filepath: "./downloads/video_titel.mp3",
|
|
159
|
+
info: { /* Video-Info Objekt */ }
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
##### `downloadAudio(url, outputPath)`
|
|
164
|
+
|
|
165
|
+
Lädt nur die Audio-Spur herunter.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const result = await downloader.downloadAudio(
|
|
169
|
+
'https://youtube.com/watch?v=...',
|
|
170
|
+
'./downloads'
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
console.log(result.filepath); // Pfad zur Audio-Datei
|
|
174
|
+
console.log(result.format); // Audio-Format Details
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Beispiele
|
|
178
|
+
|
|
179
|
+
### YouTube Suche (NEU!)
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
const { VideoDownloader, YouTubeSearch } = require('flux-dl');
|
|
183
|
+
|
|
184
|
+
async function searchAndDownload() {
|
|
185
|
+
const downloader = new VideoDownloader();
|
|
186
|
+
const search = new YouTubeSearch();
|
|
187
|
+
|
|
188
|
+
// Suche nach Song-Name
|
|
189
|
+
const results = await search.search('Rick Astley', 3);
|
|
190
|
+
|
|
191
|
+
console.log('Gefundene Videos:');
|
|
192
|
+
results.forEach((video, i) => {
|
|
193
|
+
console.log(`${i + 1}. ${video.title} - ${video.author}`);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Direkt suchen und downloaden
|
|
197
|
+
const result = await search.searchAndDownload(
|
|
198
|
+
'Rick Astley Never Gonna Give You Up',
|
|
199
|
+
downloader,
|
|
200
|
+
{ audioOnly: true }
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
console.log('✓ Downloaded:', result.filepath);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
searchAndDownload();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Einfacher Download
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
const { VideoDownloader } = require('flux-dl');
|
|
213
|
+
|
|
214
|
+
async function downloadVideo() {
|
|
215
|
+
const downloader = new VideoDownloader();
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const result = await downloader.download(
|
|
219
|
+
'https://youtube.com/watch?v=dQw4w9WgXcQ'
|
|
220
|
+
);
|
|
221
|
+
console.log('✓ Video gespeichert:', result.filepath);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Fehler:', error.message);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
downloadVideo();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Video-Info abrufen
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
const { VideoDownloader } = require('flux-dl');
|
|
234
|
+
|
|
235
|
+
async function getInfo() {
|
|
236
|
+
const downloader = new VideoDownloader();
|
|
237
|
+
|
|
238
|
+
const info = await downloader.getVideoInfo(
|
|
239
|
+
'https://youtube.com/watch?v=dQw4w9WgXcQ'
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
console.log(`Titel: ${info.title}`);
|
|
243
|
+
console.log(`Autor: ${info.author}`);
|
|
244
|
+
console.log(`Dauer: ${info.duration}s`);
|
|
245
|
+
console.log(`Qualität: ${info.quality}`);
|
|
246
|
+
console.log(`Verfügbare Formate: ${info.allFormats.length}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getInfo();
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Nur Audio downloaden
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
const { VideoDownloader } = require('flux-dl');
|
|
256
|
+
|
|
257
|
+
async function downloadAudio() {
|
|
258
|
+
const downloader = new VideoDownloader();
|
|
259
|
+
|
|
260
|
+
const result = await downloader.downloadAudio(
|
|
261
|
+
'https://youtube.com/watch?v=dQw4w9WgXcQ',
|
|
262
|
+
'./music'
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
console.log('✓ Audio gespeichert:', result.filepath);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
downloadAudio();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Mit Progress-Tracking
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
const { VideoDownloader } = require('flux-dl');
|
|
275
|
+
|
|
276
|
+
async function downloadWithProgress() {
|
|
277
|
+
const downloader = new VideoDownloader();
|
|
278
|
+
|
|
279
|
+
// Info abrufen
|
|
280
|
+
const info = await downloader.getVideoInfo(
|
|
281
|
+
'https://youtube.com/watch?v=dQw4w9WgXcQ'
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
console.log(`Starte Download: ${info.title}`);
|
|
285
|
+
|
|
286
|
+
// Download mit Progress
|
|
287
|
+
const result = await downloader.download(info.videoUrl);
|
|
288
|
+
|
|
289
|
+
console.log('✓ Fertig!');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
downloadWithProgress();
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Mehrere Videos downloaden
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
const { VideoDownloader } = require('flux-dl');
|
|
299
|
+
|
|
300
|
+
async function downloadMultiple() {
|
|
301
|
+
const downloader = new VideoDownloader();
|
|
302
|
+
|
|
303
|
+
const urls = [
|
|
304
|
+
'https://youtube.com/watch?v=...',
|
|
305
|
+
'https://vimeo.com/...',
|
|
306
|
+
'https://dailymotion.com/video/...'
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
for (const url of urls) {
|
|
310
|
+
try {
|
|
311
|
+
const result = await downloader.download(url);
|
|
312
|
+
console.log('✓', result.info.title);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('✗', url, error.message);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
downloadMultiple();
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Für WhatsApp/Discord Bots
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
const { VideoDownloader, YouTubeSearch } = require('flux-dl');
|
|
326
|
+
|
|
327
|
+
// WhatsApp Bot Beispiel
|
|
328
|
+
bot.on('message', async (msg) => {
|
|
329
|
+
if (msg.body.startsWith('!song ')) {
|
|
330
|
+
const songName = msg.body.replace('!song ', '');
|
|
331
|
+
|
|
332
|
+
const downloader = new VideoDownloader();
|
|
333
|
+
const search = new YouTubeSearch();
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
msg.reply('🔍 Suche...');
|
|
337
|
+
|
|
338
|
+
const result = await search.searchAndDownload(
|
|
339
|
+
songName,
|
|
340
|
+
downloader,
|
|
341
|
+
{ audioOnly: true }
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
msg.reply('✓ Fertig!');
|
|
345
|
+
await bot.sendFile(msg.from, result.filepath);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
msg.reply('❌ Fehler: ' + error.message);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Spezifisches Format wählen
|
|
354
|
+
|
|
355
|
+
```javascript
|
|
356
|
+
const { VideoDownloader } = require('flux-dl');
|
|
357
|
+
|
|
358
|
+
async function downloadSpecificFormat() {
|
|
359
|
+
const downloader = new VideoDownloader();
|
|
360
|
+
|
|
361
|
+
// Alle Formate abrufen
|
|
362
|
+
const info = await downloader.getVideoInfo(
|
|
363
|
+
'https://youtube.com/watch?v=dQw4w9WgXcQ'
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
// Bestes 1080p Format finden
|
|
367
|
+
const format1080p = info.allFormats.find(f =>
|
|
368
|
+
f.qualityLabel === '1080p' && f.mimeType.includes('video')
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
if (format1080p) {
|
|
372
|
+
// Direkt mit Format-URL downloaden
|
|
373
|
+
const fs = require('fs');
|
|
374
|
+
const stream = await downloader.spoofing.downloadStream(
|
|
375
|
+
format1080p.url,
|
|
376
|
+
`https://youtube.com/watch?v=${info.videoId}`,
|
|
377
|
+
(percent) => console.log(`Progress: ${percent}%`)
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const writer = fs.createWriteStream('./video_1080p.mp4');
|
|
381
|
+
stream.pipe(writer);
|
|
382
|
+
|
|
383
|
+
await new Promise((resolve, reject) => {
|
|
384
|
+
writer.on('finish', resolve);
|
|
385
|
+
writer.on('error', reject);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
downloadSpecificFormat();
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Unterstützte Plattformen
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
const { supportedPlatforms } = require('flux-dl');
|
|
397
|
+
|
|
398
|
+
console.log(supportedPlatforms);
|
|
399
|
+
// ['YouTube', 'Vimeo', 'Dailymotion']
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
## Cookie-Support
|
|
403
|
+
|
|
404
|
+
Für Videos die Authentifizierung benötigen, kannst du eine `cookies.txt` Datei im Netscape-Format verwenden:
|
|
405
|
+
|
|
406
|
+
1. Exportiere Cookies mit Browser-Extension (z.B. "Get cookies.txt")
|
|
407
|
+
2. Speichere als `cookies.txt` im Projekt-Root
|
|
408
|
+
3. Die Library lädt sie automatisch
|
|
409
|
+
|
|
410
|
+
## Error Handling
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
const { VideoDownloader } = require('flux-dl');
|
|
414
|
+
|
|
415
|
+
async function safeDownload(url) {
|
|
416
|
+
const downloader = new VideoDownloader();
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const result = await downloader.download(url);
|
|
420
|
+
return { success: true, filepath: result.filepath };
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (error.message.includes('403')) {
|
|
423
|
+
return { success: false, error: 'Video nicht verfügbar oder geschützt' };
|
|
424
|
+
} else if (error.message.includes('Platform not supported')) {
|
|
425
|
+
return { success: false, error: 'Plattform nicht unterstützt' };
|
|
426
|
+
} else {
|
|
427
|
+
return { success: false, error: error.message };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Technische Details
|
|
434
|
+
|
|
435
|
+
- **Keine externen Binaries**: Komplett in JavaScript implementiert
|
|
436
|
+
- **YouTube InnerTube API**: Nutzt die offizielle (undokumentierte) API
|
|
437
|
+
- **Signatur-Entschlüsselung**: Automatische Entschlüsselung von geschützten URLs
|
|
438
|
+
- **Browser-Emulation**: Simuliert echte Browser-Requests
|
|
439
|
+
- **Multi-Platform**: Unterstützt YouTube, Vimeo und Dailymotion
|
|
440
|
+
|
|
441
|
+
## Dependencies
|
|
442
|
+
|
|
443
|
+
- `axios` - HTTP-Requests
|
|
444
|
+
- `cheerio` - HTML-Parsing (für Vimeo/Dailymotion)
|
|
445
|
+
- `m3u8-parser` - HLS-Stream-Parsing
|
|
446
|
+
|
|
447
|
+
## Lizenz
|
|
448
|
+
|
|
449
|
+
MIT
|
|
450
|
+
|
|
451
|
+
## Support
|
|
452
|
+
|
|
453
|
+
Bei Problemen oder Fragen, erstelle ein Issue auf GitHub.
|
|
454
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flux-dl",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Leistungsstarke Video-Downloader Library für YouTube, Vimeo und Dailymotion - komplett in JavaScript, keine externen Binaries",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node examples/test.js",
|
|
8
|
+
"test-encryption": "node examples/encryption-test.js",
|
|
9
|
+
"test-platforms": "node examples/test-platforms.js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"video",
|
|
13
|
+
"download",
|
|
14
|
+
"youtube",
|
|
15
|
+
"vimeo",
|
|
16
|
+
"dailymotion",
|
|
17
|
+
"streaming",
|
|
18
|
+
"downloader",
|
|
19
|
+
"video-downloader",
|
|
20
|
+
"youtube-downloader",
|
|
21
|
+
"youtube-dl",
|
|
22
|
+
"yt-dlp",
|
|
23
|
+
"no-binary",
|
|
24
|
+
"flux",
|
|
25
|
+
"flux-dl"
|
|
26
|
+
],
|
|
27
|
+
"author": "Your Name <your.email@example.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/yourusername/flux-dl.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/yourusername/flux-dl/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/yourusername/flux-dl#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=14.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"axios": "^1.6.0",
|
|
42
|
+
"cheerio": "^1.0.0-rc.12",
|
|
43
|
+
"m3u8-parser": "^7.1.0"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"src/",
|
|
47
|
+
"README.md",
|
|
48
|
+
"LICENSE"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const platforms = require('./platforms');
|
|
5
|
+
const VideoEncryption = require('./utils/encryption');
|
|
6
|
+
const RequestSpoofing = require('./utils/requestSpoofing');
|
|
7
|
+
|
|
8
|
+
class VideoDownloader {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.options = {
|
|
11
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
12
|
+
timeout: 30000,
|
|
13
|
+
encryptionKey: options.encryptionKey || 'default-key',
|
|
14
|
+
...options
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
this.encryption = new VideoEncryption(this.options.encryptionKey);
|
|
18
|
+
this.spoofing = new RequestSpoofing();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async getVideoInfo(url) {
|
|
22
|
+
const platform = this.detectPlatform(url);
|
|
23
|
+
|
|
24
|
+
if (!platform) {
|
|
25
|
+
throw new Error('Platform not supported');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await platform.extractInfo(url, this.options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async download(url, outputPath = './downloads') {
|
|
32
|
+
const info = await this.getVideoInfo(url);
|
|
33
|
+
|
|
34
|
+
if (!fs.existsSync(outputPath)) {
|
|
35
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const filename = this.sanitizeFilename(info.title) + '.mp3'; // Geändert zu .mp3
|
|
39
|
+
const filepath = path.join(outputPath, filename);
|
|
40
|
+
|
|
41
|
+
console.log(`Downloading: ${info.title}`);
|
|
42
|
+
console.log(`Quality: ${info.quality}`);
|
|
43
|
+
|
|
44
|
+
// Nutze Browser Emulation für Download
|
|
45
|
+
const platform = this.detectPlatform(url);
|
|
46
|
+
const referer = info.videoId ? `https://www.youtube.com/watch?v=${info.videoId}` : null;
|
|
47
|
+
const writer = fs.createWriteStream(filepath);
|
|
48
|
+
|
|
49
|
+
// Für YouTube: Nutze browser statt spoofing
|
|
50
|
+
const downloader = platform && platform.browser ? platform.browser : this.spoofing;
|
|
51
|
+
|
|
52
|
+
const stream = await downloader.downloadStream(info.videoUrl, referer, (percent, downloaded, total) => {
|
|
53
|
+
if (total) {
|
|
54
|
+
process.stdout.write(`\rProgress: ${percent}% (${Math.round(downloaded / 1024 / 1024)}MB / ${Math.round(total / 1024 / 1024)}MB)`);
|
|
55
|
+
} else {
|
|
56
|
+
process.stdout.write(`\rProgress: ${percent}%`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
stream.pipe(writer);
|
|
61
|
+
|
|
62
|
+
await new Promise((resolve, reject) => {
|
|
63
|
+
writer.on('finish', resolve);
|
|
64
|
+
writer.on('error', reject);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log('\n');
|
|
68
|
+
|
|
69
|
+
return { filepath, info };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async downloadAudio(url, outputPath = './downloads') {
|
|
73
|
+
const info = await this.getVideoInfo(url);
|
|
74
|
+
|
|
75
|
+
// Audio-Format auswählen
|
|
76
|
+
const audioFormat = this.selectAudioFormat(info.allFormats);
|
|
77
|
+
|
|
78
|
+
if (!audioFormat) {
|
|
79
|
+
throw new Error('No audio format available');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!fs.existsSync(outputPath)) {
|
|
83
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ext = audioFormat.mimeType.includes('mp4') ? '.m4a' : '.webm';
|
|
87
|
+
const filename = this.sanitizeFilename(info.title) + ext;
|
|
88
|
+
const filepath = path.join(outputPath, filename);
|
|
89
|
+
|
|
90
|
+
console.log(`Downloading audio: ${info.title}`);
|
|
91
|
+
console.log(`Format: ${audioFormat.mimeType}`);
|
|
92
|
+
|
|
93
|
+
// Nutze Request Spoofing
|
|
94
|
+
const referer = `https://www.youtube.com/watch?v=${info.videoId}`;
|
|
95
|
+
const writer = fs.createWriteStream(filepath);
|
|
96
|
+
|
|
97
|
+
const stream = await this.spoofing.downloadStream(audioFormat.url, referer, (percent) => {
|
|
98
|
+
process.stdout.write(`\rProgress: ${percent}%`);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
stream.pipe(writer);
|
|
102
|
+
|
|
103
|
+
await new Promise((resolve, reject) => {
|
|
104
|
+
writer.on('finish', resolve);
|
|
105
|
+
writer.on('error', reject);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
console.log('\n');
|
|
109
|
+
|
|
110
|
+
return { filepath, info, format: audioFormat };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
selectAudioFormat(formats) {
|
|
114
|
+
// Nutze InnerTube Helper falls verfügbar
|
|
115
|
+
if (this.detectPlatform && formats.length > 0) {
|
|
116
|
+
const platform = this.detectPlatform(formats[0].url || '');
|
|
117
|
+
if (platform && platform.innerTube) {
|
|
118
|
+
return platform.innerTube.selectBestAudio(formats);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Nur Audio-Formate
|
|
123
|
+
const audioFormats = formats.filter(f =>
|
|
124
|
+
f.mimeType && f.mimeType.includes('audio') && f.url
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (audioFormats.length === 0) return null;
|
|
128
|
+
|
|
129
|
+
// Sortiere nach Bitrate (höher = besser)
|
|
130
|
+
audioFormats.sort((a, b) => (b.bitrate || 0) - (a.bitrate || 0));
|
|
131
|
+
|
|
132
|
+
return audioFormats[0];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async downloadStream(url, filepath, onProgress) {
|
|
136
|
+
// Prüfe ob URL verschlüsselt ist
|
|
137
|
+
if (url.startsWith('encrypted://')) {
|
|
138
|
+
url = this.encryption.decryptUrl(url);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const writer = fs.createWriteStream(filepath);
|
|
142
|
+
|
|
143
|
+
const response = await axios({
|
|
144
|
+
url,
|
|
145
|
+
method: 'GET',
|
|
146
|
+
responseType: 'stream',
|
|
147
|
+
headers: {
|
|
148
|
+
'User-Agent': this.options.userAgent
|
|
149
|
+
},
|
|
150
|
+
httpsAgent: new (require('https').Agent)({
|
|
151
|
+
rejectUnauthorized: false
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const totalLength = response.headers['content-length'];
|
|
156
|
+
let downloadedLength = 0;
|
|
157
|
+
|
|
158
|
+
response.data.on('data', (chunk) => {
|
|
159
|
+
downloadedLength += chunk.length;
|
|
160
|
+
if (onProgress && totalLength) {
|
|
161
|
+
const percent = Math.round((downloadedLength / totalLength) * 100);
|
|
162
|
+
onProgress(percent);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
response.data.pipe(writer);
|
|
167
|
+
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
writer.on('finish', resolve);
|
|
170
|
+
writer.on('error', reject);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
detectPlatform(url) {
|
|
175
|
+
for (const [name, platform] of Object.entries(platforms)) {
|
|
176
|
+
if (platform.canHandle(url)) {
|
|
177
|
+
return platform;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
sanitizeFilename(filename) {
|
|
184
|
+
return filename.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Verschlüsselungs-Utilities exportieren
|
|
188
|
+
encryptUrl(url, expiresIn = 3600) {
|
|
189
|
+
return this.encryption.encryptUrl(url, expiresIn);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
signUrl(url, expiresIn = 3600) {
|
|
193
|
+
return this.encryption.signUrl(url, expiresIn);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = VideoDownloader;
|