media-dl 2.1.4 → 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 +81 -46
  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) {
@@ -123,11 +151,11 @@ function runSpawn(command, args) {
123
151
  }
124
152
 
125
153
  // --- DOWNLOAD ENGINE ---
126
- async function startDownload() {
154
+ 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,13 +163,14 @@ async function startDownload() {
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();
142
170
  }
143
171
 
144
- const videoURL = await askQuestion('Masukkan Link (Video/Playlist): ');
172
+ const videoURL =
173
+ videoURLFromArgs || (await askQuestion('Masukkan Link (Video/Playlist): '));
145
174
  if (!videoURL) return mainMenu();
146
175
 
147
176
  console.log(`${C.dim}⏳ Menganalisa tautan...${C.reset}`);
@@ -150,7 +179,7 @@ async function startDownload() {
150
179
  try {
151
180
  const rawInfo = execSync(
152
181
  `"${YTDLP_PATH}" --flat-playlist --print "%(playlist_title)s|%(title)s" "${videoURL}"`,
153
- { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }
182
+ { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] },
154
183
  );
155
184
  const lines = rawInfo.trim().split('\n');
156
185
  if (lines.length > 1 || videoURL.includes('playlist?list=')) {
@@ -163,15 +192,15 @@ async function startDownload() {
163
192
  let playlistSelection = null;
164
193
  if (playlistInfo.isPlaylist) {
165
194
  console.log(
166
- `\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`
195
+ `\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`,
167
196
  );
168
197
  playlistInfo.items.forEach((item, index) => {
169
198
  console.log(
170
- `${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`
199
+ `${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`,
171
200
  );
172
201
  });
173
202
  console.log(
174
- `\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}`,
175
204
  );
176
205
  const selectionInput = await askQuestion('Pilih nomor: ');
177
206
 
@@ -197,7 +226,7 @@ async function startDownload() {
197
226
  console.log(
198
227
  ` ${C.green}2.${C.reset} Audio Only (MP3) ${
199
228
  ffExists ? C.green + '✅' : C.red + '❌ (Butuh FFmpeg)'
200
- }`
229
+ }`,
201
230
  );
202
231
  const mode = await askQuestion('Pilihan: ');
203
232
 
@@ -207,7 +236,7 @@ async function startDownload() {
207
236
  if (playlistInfo.isPlaylist)
208
237
  outputDir = path.join(
209
238
  outputDir,
210
- playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_')
239
+ playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_'),
211
240
  );
212
241
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
213
242
 
@@ -229,7 +258,7 @@ async function startDownload() {
229
258
  '--max-sleep-interval',
230
259
  '10',
231
260
  '--user-agent',
232
- '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',
233
262
  );
234
263
  }
235
264
 
@@ -239,7 +268,7 @@ async function startDownload() {
239
268
  if (mode === '2') {
240
269
  if (!ffExists) {
241
270
  console.log(
242
- `${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}`,
243
272
  );
244
273
  await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
245
274
  mainMenu();
@@ -248,7 +277,7 @@ async function startDownload() {
248
277
  } else {
249
278
  args.unshift(
250
279
  '-f',
251
- 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best'
280
+ 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best',
252
281
  );
253
282
  if (ffExists) args.unshift('--recode-video', 'mp4');
254
283
  }
@@ -267,8 +296,8 @@ async function startDownload() {
267
296
  isWindows
268
297
  ? `explorer "${outputDir}"`
269
298
  : isMac
270
- ? `open "${outputDir}"`
271
- : `xdg-open "${outputDir}"`
299
+ ? `open "${outputDir}"`
300
+ : `xdg-open "${outputDir}"`,
272
301
  );
273
302
  } catch (e) {}
274
303
  } else {
@@ -315,7 +344,7 @@ async function showSupport() {
315
344
  console.log(
316
345
  ` ${C.green}${f.icon}${C.reset} ${C.white}${f.title.padEnd(15)}${
317
346
  C.reset
318
- } ${C.dim}• ${f.desc}${C.reset}`
347
+ } ${C.dim}• ${f.desc}${C.reset}`,
319
348
  );
320
349
  });
321
350
 
@@ -334,7 +363,7 @@ async function showSupport() {
334
363
 
335
364
  links.forEach((l) => {
336
365
  console.log(
337
- ` ${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}`,
338
367
  );
339
368
  });
340
369
 
@@ -370,20 +399,20 @@ async function mainMenu() {
370
399
  // --- SEKSI NAVIGASI ---
371
400
  console.log(` ${C.bright}MAIN SERVICES${C.reset}`);
372
401
  console.log(
373
- ` ${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}`,
374
403
  );
375
404
  console.log(
376
405
  ` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Sekarang: ${
377
406
  safeMode ? 'Aktif' : 'Nonaktif'
378
- })${C.reset}`
407
+ })${C.reset}`,
379
408
  );
380
409
 
381
410
  console.log(`\n ${C.bright}SYSTEM & INFO${C.reset}`);
382
411
  console.log(
383
- ` ${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}`,
384
413
  );
385
414
  console.log(
386
- ` ${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}`,
387
416
  );
388
417
  console.log(` ${C.cyan}0.${C.reset} 🚪 Keluar`);
389
418
 
@@ -401,7 +430,7 @@ async function mainMenu() {
401
430
  console.log(
402
431
  `\n${C.yellow} 🛡️ Safe Mode telah ${
403
432
  safeMode ? 'DIAKTIFKAN' : 'DINONAKTIFKAN'
404
- }${C.reset}`
433
+ }${C.reset}`,
405
434
  );
406
435
  setTimeout(() => mainMenu(), 800);
407
436
  break;
@@ -414,10 +443,10 @@ async function mainMenu() {
414
443
  case '0':
415
444
  console.log(`\n${C.cyan}━${'━'.repeat(48)}${C.reset}`);
416
445
  console.log(
417
- ` ${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}`,
418
447
  );
419
448
  console.log(
420
- ` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`
449
+ ` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`,
421
450
  );
422
451
  console.log(`${C.cyan}━${'━'.repeat(48)}${C.reset}\n`);
423
452
 
@@ -454,23 +483,23 @@ async function firstTimeSetup() {
454
483
  const { ytExists, ffExists } = checkTools();
455
484
  printHeader(
456
485
  'FIRST-TIME SETUP',
457
- 'Komponen diperlukan untuk menjalankan aplikasi'
486
+ 'Komponen diperlukan untuk menjalankan aplikasi',
458
487
  );
459
488
 
460
489
  console.log(`${C.white}Status Instalasi:${C.reset}`);
461
490
  console.log(
462
491
  ` [${ytExists ? C.green + '✓' : C.red + '✗'}${
463
492
  C.reset
464
- }] Engine yt-dlp (Wajib)`
493
+ }] Engine yt-dlp (Wajib)`,
465
494
  );
466
495
  console.log(
467
496
  ` [${ffExists ? C.green + '✓' : C.red + '✗'}${
468
497
  C.reset
469
- }] FFmpeg (Direkomendasikan)`
498
+ }] FFmpeg (Direkomendasikan)`,
470
499
  );
471
500
 
472
501
  console.log(
473
- `\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`
502
+ `\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`,
474
503
  );
475
504
  console.log(` ${C.cyan}1.${C.reset} Install Semua Komponen Otomatis`);
476
505
  console.log(` ${C.cyan}0.${C.reset} Keluar dari Aplikasi`);
@@ -484,7 +513,7 @@ async function firstTimeSetup() {
484
513
  const status = checkTools();
485
514
  if (status.ytExists) {
486
515
  console.log(
487
- `\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`
516
+ `\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`,
488
517
  );
489
518
  await new Promise((r) => setTimeout(r, 1500));
490
519
  return mainMenu(); // Berhasil, lanjut ke menu utama
@@ -503,19 +532,19 @@ async function systemMaintenance() {
503
532
  const { ytExists, ffExists } = checkTools();
504
533
  printHeader(
505
534
  'SYSTEM MAINTENANCE',
506
- 'Update engine atau bersihkan file sistem'
535
+ 'Update engine atau bersihkan file sistem',
507
536
  );
508
537
 
509
538
  console.log(`${C.white}Versi Terinstal:${C.reset}`);
510
539
  console.log(
511
540
  ` • yt-dlp : ${ytExists ? C.green + 'Ready' : C.red + 'Not Found'}${
512
541
  C.reset
513
- }`
542
+ }`,
514
543
  );
515
544
  console.log(
516
545
  ` • FFmpeg : ${ffExists ? C.green + 'Ready' : C.red + 'Not Found'}${
517
546
  C.reset
518
- }`
547
+ }`,
519
548
  );
520
549
 
521
550
  console.log(`\n${C.bright}Opsi Pemeliharaan:${C.reset}`);
@@ -534,12 +563,12 @@ async function systemMaintenance() {
534
563
 
535
564
  case '2':
536
565
  const confirm = await askQuestion(
537
- `${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): `,
538
567
  );
539
568
  if (confirm.toLowerCase() === 'y') {
540
569
  await cleanUp(); // Panggil fungsi penghapusan folder
541
570
  console.log(
542
- `${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}`,
543
572
  );
544
573
  await askQuestion('Tekan Enter...');
545
574
  return bootstrap(); // Kembali ke pengecekan awal
@@ -559,13 +588,19 @@ async function systemMaintenance() {
559
588
  // --- ENTRY POINT ---
560
589
  async function bootstrap() {
561
590
  const status = checkTools();
562
-
563
591
  if (!status.allReady) {
564
- // Jika ada yang kurang, masuk ke mode instalasi
565
592
  await firstTimeSetup();
566
593
  } else {
567
- // Jika semua siap, langsung ke menu download
568
- mainMenu();
594
+ // process.argv[2] mengambil argumen pertama setelah nama perintah
595
+ const urlArgument = process.argv[2];
596
+
597
+ if (urlArgument) {
598
+ // Jika ada URL di terminal, langsung jalankan download
599
+ await startDownload(urlArgument);
600
+ } else {
601
+ // Jika tidak ada, masuk ke menu utama seperti biasa [cite: 113]
602
+ mainMenu();
603
+ }
569
604
  }
570
605
  }
571
606
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "2.1.4",
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": {