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/src/index.js ADDED
@@ -0,0 +1,11 @@
1
+ const VideoDownloader = require('./VideoDownloader');
2
+ const VideoEncryption = require('./utils/encryption');
3
+ const YouTubeSearch = require('./utils/youtubeSearch');
4
+ const { supportedPlatforms } = require('./platforms');
5
+
6
+ module.exports = {
7
+ VideoDownloader,
8
+ VideoEncryption,
9
+ YouTubeSearch,
10
+ supportedPlatforms
11
+ };
@@ -0,0 +1,71 @@
1
+ const axios = require('axios');
2
+
3
+ module.exports = {
4
+ name: 'Dailymotion',
5
+
6
+ canHandle(url) {
7
+ return url.includes('dailymotion.com') || url.includes('dai.ly');
8
+ },
9
+
10
+ async extractInfo(url, options) {
11
+ try {
12
+ const videoId = this.extractVideoId(url);
13
+ if (!videoId) {
14
+ throw new Error('Invalid Dailymotion URL');
15
+ }
16
+
17
+ // Dailymotion API
18
+ const apiUrl = `https://api.dailymotion.com/video/${videoId}?fields=title,duration,thumbnail_url,owner.screenname,views_total`;
19
+
20
+ const response = await axios.get(apiUrl, {
21
+ headers: {
22
+ 'User-Agent': options.userAgent
23
+ },
24
+ timeout: options.timeout,
25
+ httpsAgent: new (require('https').Agent)({
26
+ rejectUnauthorized: false
27
+ })
28
+ });
29
+
30
+ const data = response.data;
31
+
32
+ // Video-Seite für Stream-URL laden
33
+ const pageResponse = await axios.get(`https://www.dailymotion.com/video/${videoId}`, {
34
+ headers: {
35
+ 'User-Agent': options.userAgent
36
+ },
37
+ httpsAgent: new (require('https').Agent)({
38
+ rejectUnauthorized: false
39
+ })
40
+ });
41
+
42
+ // M3U8 URL extrahieren
43
+ const m3u8Match = pageResponse.data.match(/"(https:\/\/[^"]+\.m3u8[^"]*)"/);
44
+ const videoUrl = m3u8Match ? m3u8Match[1] : null;
45
+
46
+ return {
47
+ title: data.title,
48
+ videoId: videoId,
49
+ duration: data.duration,
50
+ thumbnail: data.thumbnail_url,
51
+ author: data['owner.screenname'],
52
+ viewCount: data.views_total,
53
+ videoUrl: videoUrl,
54
+ quality: 'auto',
55
+ allFormats: [],
56
+ platform: this.name
57
+ };
58
+
59
+ } catch (error) {
60
+ throw new Error(`Failed to extract Dailymotion info: ${error.message}`);
61
+ }
62
+ },
63
+
64
+ extractVideoId(url) {
65
+ let match = url.match(/dailymotion\.com\/video\/([a-zA-Z0-9]+)/);
66
+ if (match) return match[1];
67
+
68
+ match = url.match(/dai\.ly\/([a-zA-Z0-9]+)/);
69
+ return match ? match[1] : null;
70
+ }
71
+ };
@@ -0,0 +1,45 @@
1
+ const axios = require('axios');
2
+ const cheerio = require('cheerio');
3
+
4
+ module.exports = {
5
+ name: 'Example Platform',
6
+
7
+ canHandle(url) {
8
+ // Prüfe ob URL zu dieser Plattform gehört
9
+ return url.includes('example.com');
10
+ },
11
+
12
+ async extractInfo(url, options) {
13
+ try {
14
+ const response = await axios.get(url, {
15
+ headers: {
16
+ 'User-Agent': options.userAgent
17
+ },
18
+ timeout: options.timeout,
19
+ httpsAgent: new (require('https').Agent)({
20
+ rejectUnauthorized: false
21
+ })
22
+ });
23
+
24
+ const $ = cheerio.load(response.data);
25
+
26
+ // Beispiel: Video-Infos extrahieren
27
+ const title = $('title').text();
28
+ const videoUrl = $('video source').attr('src');
29
+
30
+ if (!videoUrl) {
31
+ throw new Error('Video URL not found');
32
+ }
33
+
34
+ return {
35
+ title,
36
+ videoUrl,
37
+ thumbnail: $('meta[property="og:image"]').attr('content'),
38
+ duration: null,
39
+ platform: this.name
40
+ };
41
+ } catch (error) {
42
+ throw new Error(`Failed to extract info: ${error.message}`);
43
+ }
44
+ }
45
+ };
@@ -0,0 +1,13 @@
1
+ const examplePlatform = require('./examplePlatform');
2
+ const youtube = require('./youtube');
3
+ const vimeo = require('./vimeo');
4
+ const dailymotion = require('./dailymotion');
5
+
6
+ const supportedPlatforms = {
7
+ example: examplePlatform,
8
+ youtube: youtube, // Eigene Implementation (deno deaktiviert)
9
+ vimeo: vimeo,
10
+ dailymotion: dailymotion
11
+ };
12
+
13
+ module.exports = supportedPlatforms;
@@ -0,0 +1,66 @@
1
+ const axios = require('axios');
2
+
3
+ module.exports = {
4
+ name: 'Vimeo',
5
+
6
+ canHandle(url) {
7
+ return url.includes('vimeo.com');
8
+ },
9
+
10
+ async extractInfo(url, options) {
11
+ try {
12
+ const videoId = this.extractVideoId(url);
13
+ if (!videoId) {
14
+ throw new Error('Invalid Vimeo URL');
15
+ }
16
+
17
+ // Vimeo API nutzen (öffentliche Videos)
18
+ const apiUrl = `https://player.vimeo.com/video/${videoId}/config`;
19
+
20
+ const response = await axios.get(apiUrl, {
21
+ headers: {
22
+ 'User-Agent': options.userAgent
23
+ },
24
+ timeout: options.timeout,
25
+ httpsAgent: new (require('https').Agent)({
26
+ rejectUnauthorized: false
27
+ })
28
+ });
29
+
30
+ const data = response.data;
31
+ const video = data.video;
32
+ const files = data.request.files;
33
+
34
+ // Beste Qualität finden
35
+ let videoUrl = null;
36
+ let quality = 'unknown';
37
+
38
+ if (files.progressive && files.progressive.length > 0) {
39
+ const sorted = files.progressive.sort((a, b) => b.height - a.height);
40
+ videoUrl = sorted[0].url;
41
+ quality = sorted[0].quality;
42
+ }
43
+
44
+ return {
45
+ title: video.title,
46
+ videoId: videoId,
47
+ duration: video.duration,
48
+ thumbnail: video.thumbs['640'],
49
+ author: video.owner.name,
50
+ viewCount: null,
51
+ videoUrl: videoUrl,
52
+ quality: quality,
53
+ allFormats: files.progressive || [],
54
+ platform: this.name
55
+ };
56
+
57
+ } catch (error) {
58
+ throw new Error(`Failed to extract Vimeo info: ${error.message}`);
59
+ }
60
+ },
61
+
62
+ extractVideoId(url) {
63
+ const match = url.match(/vimeo\.com\/(\d+)/);
64
+ return match ? match[1] : null;
65
+ }
66
+ };
@@ -0,0 +1,209 @@
1
+ const { exec } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ module.exports = {
6
+ name: 'YouTube (deno)',
7
+
8
+ canHandle(url) {
9
+ return url.includes('youtube.com') || url.includes('youtu.be');
10
+ },
11
+
12
+ async extractInfo(url, options) {
13
+ return new Promise((resolve, reject) => {
14
+ // Prüfe ob cookies.txt existiert
15
+ const cookiesArg = fs.existsSync('cookies.txt') ? '--cookies cookies.txt' : '';
16
+
17
+ const cmd = `deno run --allow-net --allow-read --allow-write https://deno.land/x/youtube_dl/mod.ts --dump-json ${cookiesArg} "${url}"`;
18
+
19
+ console.log('Using deno for YouTube extraction...');
20
+
21
+ exec(cmd, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
22
+ if (error) {
23
+ // Fallback: Versuche mit yt-dlp
24
+ const ytdlpCmd = `yt-dlp --dump-json ${cookiesArg} "${url}"`;
25
+
26
+ exec(ytdlpCmd, { maxBuffer: 10 * 1024 * 1024 }, (error2, stdout2, stderr2) => {
27
+ if (error2) {
28
+ reject(new Error(`Neither deno nor yt-dlp found. Install one of them:\n- deno: https://deno.land/\n- yt-dlp: https://github.com/yt-dlp/yt-dlp`));
29
+ return;
30
+ }
31
+
32
+ try {
33
+ const data = JSON.parse(stdout2);
34
+ resolve(this.parseVideoData(data, url));
35
+ } catch (parseError) {
36
+ reject(new Error(`Failed to parse output: ${parseError.message}`));
37
+ }
38
+ });
39
+ return;
40
+ }
41
+
42
+ try {
43
+ const data = JSON.parse(stdout);
44
+ resolve(this.parseVideoData(data, url));
45
+ } catch (parseError) {
46
+ reject(new Error(`Failed to parse deno output: ${parseError.message}`));
47
+ }
48
+ });
49
+ });
50
+ },
51
+
52
+ parseVideoData(data, url) {
53
+ return {
54
+ title: data.title,
55
+ videoId: data.id,
56
+ duration: data.duration,
57
+ thumbnail: data.thumbnail,
58
+ author: data.uploader || data.channel,
59
+ viewCount: data.view_count,
60
+ videoUrl: url,
61
+ quality: data.format_note || 'best',
62
+ format: data,
63
+ allFormats: data.formats || [],
64
+ platform: this.name,
65
+ _useCli: true
66
+ };
67
+ },
68
+
69
+ async download(url, outputPath, onProgress) {
70
+ return new Promise((resolve, reject) => {
71
+ if (!fs.existsSync(outputPath)) {
72
+ fs.mkdirSync(outputPath, { recursive: true });
73
+ }
74
+
75
+ const cookiesArg = fs.existsSync('cookies.txt') ? '--cookies cookies.txt' : '';
76
+ const outputTemplate = path.join(outputPath, '%(title)s.%(ext)s');
77
+
78
+ // Versuche zuerst deno
79
+ let cmd = `deno run --allow-net --allow-read --allow-write https://deno.land/x/youtube_dl/mod.ts -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" ${cookiesArg} -o "${outputTemplate}" "${url}"`;
80
+
81
+ console.log('Downloading with deno...');
82
+
83
+ let process = exec(cmd, { maxBuffer: 50 * 1024 * 1024 });
84
+
85
+ let usedDeno = true;
86
+
87
+ process.on('error', (err) => {
88
+ // Fallback zu yt-dlp
89
+ console.log('deno failed, trying yt-dlp...');
90
+ usedDeno = false;
91
+
92
+ cmd = `yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" ${cookiesArg} -o "${outputTemplate}" "${url}"`;
93
+ process = exec(cmd, { maxBuffer: 50 * 1024 * 1024 });
94
+
95
+ process.stdout.on('data', (data) => {
96
+ const output = data.toString();
97
+ console.log(output);
98
+
99
+ const match = output.match(/(\d+\.\d+)%/);
100
+ if (match && onProgress) {
101
+ onProgress(parseFloat(match[1]));
102
+ }
103
+ });
104
+
105
+ process.stderr.on('data', (data) => {
106
+ console.error(data.toString());
107
+ });
108
+
109
+ process.on('close', (code) => {
110
+ if (code === 0) {
111
+ resolve({ success: true, outputPath, tool: 'yt-dlp' });
112
+ } else {
113
+ reject(new Error(`yt-dlp exited with code ${code}`));
114
+ }
115
+ });
116
+ });
117
+
118
+ if (usedDeno) {
119
+ process.stdout.on('data', (data) => {
120
+ const output = data.toString();
121
+ console.log(output);
122
+
123
+ const match = output.match(/(\d+\.\d+)%/);
124
+ if (match && onProgress) {
125
+ onProgress(parseFloat(match[1]));
126
+ }
127
+ });
128
+
129
+ process.stderr.on('data', (data) => {
130
+ console.error(data.toString());
131
+ });
132
+
133
+ process.on('close', (code) => {
134
+ if (code === 0) {
135
+ resolve({ success: true, outputPath, tool: 'deno' });
136
+ } else {
137
+ // Versuche yt-dlp als Fallback
138
+ console.log('deno failed, trying yt-dlp...');
139
+
140
+ const ytdlpCmd = `yt-dlp -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" ${cookiesArg} -o "${outputTemplate}" "${url}"`;
141
+ const ytdlpProcess = exec(ytdlpCmd, { maxBuffer: 50 * 1024 * 1024 });
142
+
143
+ ytdlpProcess.stdout.on('data', (data) => console.log(data.toString()));
144
+ ytdlpProcess.stderr.on('data', (data) => console.error(data.toString()));
145
+
146
+ ytdlpProcess.on('close', (ytdlpCode) => {
147
+ if (ytdlpCode === 0) {
148
+ resolve({ success: true, outputPath, tool: 'yt-dlp' });
149
+ } else {
150
+ reject(new Error(`Both deno and yt-dlp failed`));
151
+ }
152
+ });
153
+ }
154
+ });
155
+ }
156
+ });
157
+ },
158
+
159
+ async downloadAudio(url, outputPath, onProgress) {
160
+ return new Promise((resolve, reject) => {
161
+ if (!fs.existsSync(outputPath)) {
162
+ fs.mkdirSync(outputPath, { recursive: true });
163
+ }
164
+
165
+ const cookiesArg = fs.existsSync('cookies.txt') ? '--cookies cookies.txt' : '';
166
+ const outputTemplate = path.join(outputPath, '%(title)s.%(ext)s');
167
+
168
+ let cmd = `deno run --allow-net --allow-read --allow-write https://deno.land/x/youtube_dl/mod.ts -f "bestaudio[ext=m4a]/bestaudio" ${cookiesArg} -o "${outputTemplate}" "${url}"`;
169
+
170
+ console.log('Downloading audio with deno...');
171
+
172
+ const process = exec(cmd, { maxBuffer: 50 * 1024 * 1024 });
173
+
174
+ process.on('error', () => {
175
+ // Fallback zu yt-dlp
176
+ const ytdlpCmd = `yt-dlp -f "bestaudio[ext=m4a]/bestaudio" ${cookiesArg} -o "${outputTemplate}" "${url}"`;
177
+ exec(ytdlpCmd, { maxBuffer: 50 * 1024 * 1024 }, (err, stdout, stderr) => {
178
+ if (err) {
179
+ reject(new Error('Both deno and yt-dlp failed'));
180
+ } else {
181
+ resolve({ success: true, outputPath, tool: 'yt-dlp' });
182
+ }
183
+ });
184
+ });
185
+
186
+ process.stdout.on('data', (data) => {
187
+ const output = data.toString();
188
+ console.log(output);
189
+
190
+ const match = output.match(/(\d+\.\d+)%/);
191
+ if (match && onProgress) {
192
+ onProgress(parseFloat(match[1]));
193
+ }
194
+ });
195
+
196
+ process.stderr.on('data', (data) => {
197
+ console.error(data.toString());
198
+ });
199
+
200
+ process.on('close', (code) => {
201
+ if (code === 0) {
202
+ resolve({ success: true, outputPath, tool: 'deno' });
203
+ } else {
204
+ reject(new Error(`deno exited with code ${code}`));
205
+ }
206
+ });
207
+ });
208
+ }
209
+ };