media-dl 2.2.0 → 2.2.2
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/bin/cli.js +68 -40
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ async function installYtdlp() {
|
|
|
40
40
|
if (isWindows) {
|
|
41
41
|
execSync(
|
|
42
42
|
`powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${YTDLP_PATH}'"`,
|
|
43
|
-
{ stdio: 'inherit' }
|
|
43
|
+
{ stdio: 'inherit' },
|
|
44
44
|
);
|
|
45
45
|
} else {
|
|
46
46
|
execSync(`curl -L -# "${url}" -o "${YTDLP_PATH}"`, { stdio: 'inherit' });
|
|
@@ -49,7 +49,7 @@ async function installYtdlp() {
|
|
|
49
49
|
console.log(`\n${C.green}✅ yt-dlp berhasil dikonfigurasi!${C.reset}`);
|
|
50
50
|
} catch (e) {
|
|
51
51
|
console.error(
|
|
52
|
-
`\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}
|
|
52
|
+
`\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}`,
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -57,16 +57,17 @@ async function installYtdlp() {
|
|
|
57
57
|
async function installFfmpeg() {
|
|
58
58
|
printHeader('INSTALL FFmpeg');
|
|
59
59
|
console.log(
|
|
60
|
-
`${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n
|
|
60
|
+
`${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n`,
|
|
61
61
|
);
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
64
|
if (isMac) {
|
|
65
|
+
// ... (Kode macOS Anda sudah benar)
|
|
65
66
|
console.log(`${C.blue}⏳ Mengunduh FFmpeg untuk macOS...${C.reset}`);
|
|
66
67
|
const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
|
|
67
68
|
execSync(
|
|
68
69
|
`curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
|
|
69
|
-
{ stdio: 'inherit' }
|
|
70
|
+
{ stdio: 'inherit' },
|
|
70
71
|
);
|
|
71
72
|
execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
|
|
72
73
|
execSync(`rm "${zipPath}"`);
|
|
@@ -74,14 +75,41 @@ async function installFfmpeg() {
|
|
|
74
75
|
console.log(`\n${C.green}✅ FFmpeg aktif di macOS.${C.reset}`);
|
|
75
76
|
} else if (isWindows) {
|
|
76
77
|
console.log(
|
|
77
|
-
`${C.
|
|
78
|
+
`${C.blue}⏳ Mengunduh FFmpeg untuk Windows (Essentials)...${C.reset}`,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
|
|
82
|
+
// Link direct ke build essentials agar file tidak terlalu besar
|
|
83
|
+
const url =
|
|
84
|
+
'https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip';
|
|
85
|
+
|
|
86
|
+
// 1. Download menggunakan PowerShell
|
|
87
|
+
execSync(
|
|
88
|
+
`powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${zipPath}'"`,
|
|
89
|
+
{ stdio: 'inherit' },
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
console.log(`${C.yellow}📦 Mengekstrak FFmpeg...${C.reset}`);
|
|
93
|
+
|
|
94
|
+
// 2. Ekstrak menggunakan perintah 'tar' (Bawaan Windows 10+)
|
|
95
|
+
// Kita hanya mengambil ffmpeg.exe dari dalam folder bin di zip tersebut
|
|
96
|
+
execSync(
|
|
97
|
+
`tar -xf "${zipPath}" -C "${TOOLS_DIR}" --strip-components 2 "*/bin/ffmpeg.exe" "*/bin/ffprobe.exe"`,
|
|
98
|
+
{ stdio: 'inherit' },
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// 3. Bersihkan file zip
|
|
102
|
+
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
|
103
|
+
|
|
104
|
+
console.log(
|
|
105
|
+
`\n${C.green}✅ FFmpeg berhasil diinstal di Windows!${C.reset}`,
|
|
78
106
|
);
|
|
79
|
-
console.log(`dan letakkan di: ${C.white}${TOOLS_DIR}${C.reset}`);
|
|
80
107
|
}
|
|
81
108
|
} catch (e) {
|
|
82
109
|
console.error(
|
|
83
|
-
`${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}
|
|
110
|
+
`${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}`,
|
|
84
111
|
);
|
|
112
|
+
console.log(`${C.dim}Error: ${e.message}${C.reset}`);
|
|
85
113
|
}
|
|
86
114
|
}
|
|
87
115
|
|
|
@@ -94,7 +122,7 @@ function runSpawn(command, args) {
|
|
|
94
122
|
const output = data.toString();
|
|
95
123
|
// Regex untuk menangkap progress dari yt-dlp
|
|
96
124
|
const progressMatch = output.match(
|
|
97
|
-
/\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)
|
|
125
|
+
/\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)/,
|
|
98
126
|
);
|
|
99
127
|
|
|
100
128
|
if (progressMatch) {
|
|
@@ -127,7 +155,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
127
155
|
let { ytExists, ffExists } = checkTools();
|
|
128
156
|
if (!ytExists) {
|
|
129
157
|
console.log(
|
|
130
|
-
`\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}
|
|
158
|
+
`\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}`,
|
|
131
159
|
);
|
|
132
160
|
await askQuestion('Tekan Enter...');
|
|
133
161
|
return mainMenu();
|
|
@@ -135,7 +163,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
135
163
|
|
|
136
164
|
if (!ffExists) {
|
|
137
165
|
console.log(
|
|
138
|
-
`${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}
|
|
166
|
+
`${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}`,
|
|
139
167
|
);
|
|
140
168
|
const cont = await askQuestion('Lanjutkan saja? (y/n): ');
|
|
141
169
|
if (cont.toLowerCase() !== 'y') return mainMenu();
|
|
@@ -151,7 +179,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
151
179
|
try {
|
|
152
180
|
const rawInfo = execSync(
|
|
153
181
|
`"${YTDLP_PATH}" --flat-playlist --print "%(playlist_title)s|%(title)s" "${videoURL}"`,
|
|
154
|
-
{ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }
|
|
182
|
+
{ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] },
|
|
155
183
|
);
|
|
156
184
|
const lines = rawInfo.trim().split('\n');
|
|
157
185
|
if (lines.length > 1 || videoURL.includes('playlist?list=')) {
|
|
@@ -164,15 +192,15 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
164
192
|
let playlistSelection = null;
|
|
165
193
|
if (playlistInfo.isPlaylist) {
|
|
166
194
|
console.log(
|
|
167
|
-
`\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}
|
|
195
|
+
`\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`,
|
|
168
196
|
);
|
|
169
197
|
playlistInfo.items.forEach((item, index) => {
|
|
170
198
|
console.log(
|
|
171
|
-
`${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}
|
|
199
|
+
`${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`,
|
|
172
200
|
);
|
|
173
201
|
});
|
|
174
202
|
console.log(
|
|
175
|
-
`\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}
|
|
203
|
+
`\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}`,
|
|
176
204
|
);
|
|
177
205
|
const selectionInput = await askQuestion('Pilih nomor: ');
|
|
178
206
|
|
|
@@ -198,7 +226,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
198
226
|
console.log(
|
|
199
227
|
` ${C.green}2.${C.reset} Audio Only (MP3) ${
|
|
200
228
|
ffExists ? C.green + '✅' : C.red + '❌ (Butuh FFmpeg)'
|
|
201
|
-
}
|
|
229
|
+
}`,
|
|
202
230
|
);
|
|
203
231
|
const mode = await askQuestion('Pilihan: ');
|
|
204
232
|
|
|
@@ -208,7 +236,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
208
236
|
if (playlistInfo.isPlaylist)
|
|
209
237
|
outputDir = path.join(
|
|
210
238
|
outputDir,
|
|
211
|
-
playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_')
|
|
239
|
+
playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_'),
|
|
212
240
|
);
|
|
213
241
|
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
214
242
|
|
|
@@ -230,7 +258,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
230
258
|
'--max-sleep-interval',
|
|
231
259
|
'10',
|
|
232
260
|
'--user-agent',
|
|
233
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
|
261
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
|
|
234
262
|
);
|
|
235
263
|
}
|
|
236
264
|
|
|
@@ -240,7 +268,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
240
268
|
if (mode === '2') {
|
|
241
269
|
if (!ffExists) {
|
|
242
270
|
console.log(
|
|
243
|
-
`${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}
|
|
271
|
+
`${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}`,
|
|
244
272
|
);
|
|
245
273
|
await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
|
|
246
274
|
mainMenu();
|
|
@@ -249,7 +277,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
249
277
|
} else {
|
|
250
278
|
args.unshift(
|
|
251
279
|
'-f',
|
|
252
|
-
'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best'
|
|
280
|
+
'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best',
|
|
253
281
|
);
|
|
254
282
|
if (ffExists) args.unshift('--recode-video', 'mp4');
|
|
255
283
|
}
|
|
@@ -268,8 +296,8 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
268
296
|
isWindows
|
|
269
297
|
? `explorer "${outputDir}"`
|
|
270
298
|
: isMac
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
? `open "${outputDir}"`
|
|
300
|
+
: `xdg-open "${outputDir}"`,
|
|
273
301
|
);
|
|
274
302
|
} catch (e) {}
|
|
275
303
|
} else {
|
|
@@ -316,7 +344,7 @@ async function showSupport() {
|
|
|
316
344
|
console.log(
|
|
317
345
|
` ${C.green}${f.icon}${C.reset} ${C.white}${f.title.padEnd(15)}${
|
|
318
346
|
C.reset
|
|
319
|
-
} ${C.dim}• ${f.desc}${C.reset}
|
|
347
|
+
} ${C.dim}• ${f.desc}${C.reset}`,
|
|
320
348
|
);
|
|
321
349
|
});
|
|
322
350
|
|
|
@@ -335,7 +363,7 @@ async function showSupport() {
|
|
|
335
363
|
|
|
336
364
|
links.forEach((l) => {
|
|
337
365
|
console.log(
|
|
338
|
-
` ${C.bgBlue}${C.white}${l.label}${C.reset} ${C.blue}➜${C.reset} ${C.dim}${l.url}${C.reset}
|
|
366
|
+
` ${C.bgBlue}${C.white}${l.label}${C.reset} ${C.blue}➜${C.reset} ${C.dim}${l.url}${C.reset}`,
|
|
339
367
|
);
|
|
340
368
|
});
|
|
341
369
|
|
|
@@ -371,20 +399,20 @@ async function mainMenu() {
|
|
|
371
399
|
// --- SEKSI NAVIGASI ---
|
|
372
400
|
console.log(` ${C.bright}MAIN SERVICES${C.reset}`);
|
|
373
401
|
console.log(
|
|
374
|
-
` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}
|
|
402
|
+
` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}`,
|
|
375
403
|
);
|
|
376
404
|
console.log(
|
|
377
405
|
` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Sekarang: ${
|
|
378
406
|
safeMode ? 'Aktif' : 'Nonaktif'
|
|
379
|
-
})${C.reset}
|
|
407
|
+
})${C.reset}`,
|
|
380
408
|
);
|
|
381
409
|
|
|
382
410
|
console.log(`\n ${C.bright}SYSTEM & INFO${C.reset}`);
|
|
383
411
|
console.log(
|
|
384
|
-
` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}
|
|
412
|
+
` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}`,
|
|
385
413
|
);
|
|
386
414
|
console.log(
|
|
387
|
-
` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}
|
|
415
|
+
` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}`,
|
|
388
416
|
);
|
|
389
417
|
console.log(` ${C.cyan}0.${C.reset} 🚪 Keluar`);
|
|
390
418
|
|
|
@@ -402,7 +430,7 @@ async function mainMenu() {
|
|
|
402
430
|
console.log(
|
|
403
431
|
`\n${C.yellow} 🛡️ Safe Mode telah ${
|
|
404
432
|
safeMode ? 'DIAKTIFKAN' : 'DINONAKTIFKAN'
|
|
405
|
-
}${C.reset}
|
|
433
|
+
}${C.reset}`,
|
|
406
434
|
);
|
|
407
435
|
setTimeout(() => mainMenu(), 800);
|
|
408
436
|
break;
|
|
@@ -415,10 +443,10 @@ async function mainMenu() {
|
|
|
415
443
|
case '0':
|
|
416
444
|
console.log(`\n${C.cyan}━${'━'.repeat(48)}${C.reset}`);
|
|
417
445
|
console.log(
|
|
418
|
-
` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}
|
|
446
|
+
` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}`,
|
|
419
447
|
);
|
|
420
448
|
console.log(
|
|
421
|
-
` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}
|
|
449
|
+
` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`,
|
|
422
450
|
);
|
|
423
451
|
console.log(`${C.cyan}━${'━'.repeat(48)}${C.reset}\n`);
|
|
424
452
|
|
|
@@ -455,23 +483,23 @@ async function firstTimeSetup() {
|
|
|
455
483
|
const { ytExists, ffExists } = checkTools();
|
|
456
484
|
printHeader(
|
|
457
485
|
'FIRST-TIME SETUP',
|
|
458
|
-
'Komponen diperlukan untuk menjalankan aplikasi'
|
|
486
|
+
'Komponen diperlukan untuk menjalankan aplikasi',
|
|
459
487
|
);
|
|
460
488
|
|
|
461
489
|
console.log(`${C.white}Status Instalasi:${C.reset}`);
|
|
462
490
|
console.log(
|
|
463
491
|
` [${ytExists ? C.green + '✓' : C.red + '✗'}${
|
|
464
492
|
C.reset
|
|
465
|
-
}] Engine yt-dlp (Wajib)
|
|
493
|
+
}] Engine yt-dlp (Wajib)`,
|
|
466
494
|
);
|
|
467
495
|
console.log(
|
|
468
496
|
` [${ffExists ? C.green + '✓' : C.red + '✗'}${
|
|
469
497
|
C.reset
|
|
470
|
-
}] FFmpeg (Direkomendasikan)
|
|
498
|
+
}] FFmpeg (Direkomendasikan)`,
|
|
471
499
|
);
|
|
472
500
|
|
|
473
501
|
console.log(
|
|
474
|
-
`\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}
|
|
502
|
+
`\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`,
|
|
475
503
|
);
|
|
476
504
|
console.log(` ${C.cyan}1.${C.reset} Install Semua Komponen Otomatis`);
|
|
477
505
|
console.log(` ${C.cyan}0.${C.reset} Keluar dari Aplikasi`);
|
|
@@ -485,7 +513,7 @@ async function firstTimeSetup() {
|
|
|
485
513
|
const status = checkTools();
|
|
486
514
|
if (status.ytExists) {
|
|
487
515
|
console.log(
|
|
488
|
-
`\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}
|
|
516
|
+
`\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`,
|
|
489
517
|
);
|
|
490
518
|
await new Promise((r) => setTimeout(r, 1500));
|
|
491
519
|
return mainMenu(); // Berhasil, lanjut ke menu utama
|
|
@@ -504,19 +532,19 @@ async function systemMaintenance() {
|
|
|
504
532
|
const { ytExists, ffExists } = checkTools();
|
|
505
533
|
printHeader(
|
|
506
534
|
'SYSTEM MAINTENANCE',
|
|
507
|
-
'Update engine atau bersihkan file sistem'
|
|
535
|
+
'Update engine atau bersihkan file sistem',
|
|
508
536
|
);
|
|
509
537
|
|
|
510
538
|
console.log(`${C.white}Versi Terinstal:${C.reset}`);
|
|
511
539
|
console.log(
|
|
512
540
|
` • yt-dlp : ${ytExists ? C.green + 'Ready' : C.red + 'Not Found'}${
|
|
513
541
|
C.reset
|
|
514
|
-
}
|
|
542
|
+
}`,
|
|
515
543
|
);
|
|
516
544
|
console.log(
|
|
517
545
|
` • FFmpeg : ${ffExists ? C.green + 'Ready' : C.red + 'Not Found'}${
|
|
518
546
|
C.reset
|
|
519
|
-
}
|
|
547
|
+
}`,
|
|
520
548
|
);
|
|
521
549
|
|
|
522
550
|
console.log(`\n${C.bright}Opsi Pemeliharaan:${C.reset}`);
|
|
@@ -535,12 +563,12 @@ async function systemMaintenance() {
|
|
|
535
563
|
|
|
536
564
|
case '2':
|
|
537
565
|
const confirm = await askQuestion(
|
|
538
|
-
`${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n):
|
|
566
|
+
`${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n): `,
|
|
539
567
|
);
|
|
540
568
|
if (confirm.toLowerCase() === 'y') {
|
|
541
569
|
await cleanUp(); // Panggil fungsi penghapusan folder
|
|
542
570
|
console.log(
|
|
543
|
-
`${C.yellow}Sistem dibersihkan. Anda akan diarahkan ke Setup Wizard.${C.reset}
|
|
571
|
+
`${C.yellow}Sistem dibersihkan. Anda akan diarahkan ke Setup Wizard.${C.reset}`,
|
|
544
572
|
);
|
|
545
573
|
await askQuestion('Tekan Enter...');
|
|
546
574
|
return bootstrap(); // Kembali ke pengecekan awal
|