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.
- package/bin/cli.js +81 -46
- 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) {
|
|
@@ -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 =
|
|
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
|
-
|
|
271
|
-
|
|
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
|
-
//
|
|
568
|
-
|
|
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
|
|