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.
Files changed (2) hide show
  1. package/bin/cli.js +68 -40
  2. 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.yellow}ℹ️ Untuk Windows, disarankan mengunduh 'ffmpeg.exe' secara manual`
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
- ? `open "${outputDir}"`
272
- : `xdg-open "${outputDir}"`
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "CLI Downloader video/audio lintas platform menggunakan yt-dlp.",
5
5
  "main": "bin/cli.js",
6
6
  "scripts": {