media-dl 2.5.1 → 2.5.3

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 (4) hide show
  1. package/bin/cli.js +161 -142
  2. package/package.json +1 -1
  3. package/readme.id.md +103 -0
  4. package/readme.md +113 -103
package/bin/cli.js CHANGED
@@ -6,7 +6,7 @@ const fs = require('fs');
6
6
  const os = require('os');
7
7
  const { C, printHeader, renderProgressBar, askQuestion, rl } = require('./ui');
8
8
 
9
- // --- KONFIGURASI VISUAL (ANSI COLORS) ---
9
+ // --- VISUAL CONFIGURATION (ANSI COLORS) ---
10
10
 
11
11
  const TOOLS_DIR = path.join(os.homedir(), '.media-dl');
12
12
 
@@ -18,13 +18,13 @@ const isTermux =
18
18
  let YTDLP_PATH = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
19
19
  let FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
20
20
 
21
- // State Aplikasi
21
+ // Application State
22
22
  let safeMode = true;
23
23
 
24
24
  if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
25
25
 
26
26
  function checkTools() {
27
- // Cek jalur default lokal (internal)
27
+ // Check default local path (internal)
28
28
  const LOCAL_YT = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
29
29
  const LOCAL_FF = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
30
30
 
@@ -34,11 +34,11 @@ function checkTools() {
34
34
  let ytExists = isLocalYt;
35
35
  let ffExists = isLocalFf;
36
36
 
37
- // Reset path ke default sebelum pengecekan global
37
+ // Reset path to default before global check
38
38
  if (isLocalYt) YTDLP_PATH = LOCAL_YT;
39
39
  if (isLocalFf) FFMPEG_PATH = LOCAL_FF;
40
40
 
41
- // Cek Global yt-dlp hanya jika lokal tidak ada
41
+ // Check Global yt-dlp only if local is missing
42
42
  if (!isLocalYt) {
43
43
  try {
44
44
  const cmd = isWindows ? 'where yt-dlp' : 'which yt-dlp';
@@ -51,11 +51,11 @@ function checkTools() {
51
51
  ytExists = true;
52
52
  }
53
53
  } catch (e) {
54
- YTDLP_PATH = LOCAL_YT; // Kembalikan ke path lokal jika global pun tidak ada
54
+ YTDLP_PATH = LOCAL_YT; // Revert to local path if global is also missing
55
55
  }
56
56
  }
57
57
 
58
- // Cek Global ffmpeg hanya jika lokal tidak ada
58
+ // Check Global ffmpeg only if local is missing
59
59
  if (!isLocalFf) {
60
60
  try {
61
61
  const cmd = isWindows ? 'where ffmpeg' : 'which ffmpeg';
@@ -68,7 +68,7 @@ function checkTools() {
68
68
  ffExists = true;
69
69
  }
70
70
  } catch (e) {
71
- FFMPEG_PATH = LOCAL_FF; // Kembalikan ke path lokal jika global pun tidak ada
71
+ FFMPEG_PATH = LOCAL_FF; // Revert to local path if global is also missing
72
72
  }
73
73
  }
74
74
 
@@ -86,21 +86,21 @@ async function installYtdlp() {
86
86
  if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
87
87
 
88
88
  printHeader('INSTALL / UPDATE YT-DLP');
89
- console.log(`${C.blue}⏳ Sedang mengunduh engine terbaru...${C.reset}`);
89
+ console.log(`${C.blue}⏳ Downloading latest engine...${C.reset}`);
90
90
  const url = isWindows
91
91
  ? 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
92
92
  : 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
93
93
 
94
94
  try {
95
95
  if (isTermux) {
96
- // Di Termux, lebih disarankan menggunakan python/pip untuk stabilitas
97
- console.log(`${C.dim}Menginstall yt-dlp via python...${C.reset}`);
96
+ // On Termux, using python/pip is recommended for stability
97
+ console.log(`${C.dim}Installing yt-dlp via python...${C.reset}`);
98
98
  execSync('pkg update && pkg install python ffmpeg -y', {
99
99
  stdio: 'inherit',
100
100
  });
101
101
  execSync('pip install -U "yt-dlp[default]"', { stdio: 'inherit' });
102
102
  console.log(
103
- `\n${C.green}✅ yt-dlp berhasil diinstal di Termux!${C.reset}`,
103
+ `\n${C.green}✅ yt-dlp installed successfully on Termux!${C.reset}`,
104
104
  );
105
105
  } else if (isWindows) {
106
106
  execSync(
@@ -113,11 +113,11 @@ async function installYtdlp() {
113
113
  execSync(`chmod a+rx "${YTDLP_PATH}"`);
114
114
  }
115
115
  if (!isTermux) {
116
- console.log(`\n${C.green}✅ yt-dlp berhasil dikonfigurasi!${C.reset}`);
116
+ console.log(`\n${C.green}✅ yt-dlp configured successfully!${C.reset}`);
117
117
  }
118
118
  } catch (e) {
119
119
  console.error(
120
- `\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}`,
120
+ `\n${C.red}❌ Failed to download. Check your internet connection.${C.reset}`,
121
121
  );
122
122
  }
123
123
  }
@@ -125,21 +125,21 @@ async function installYtdlp() {
125
125
  async function installFfmpeg() {
126
126
  printHeader('INSTALL FFmpeg');
127
127
  console.log(
128
- `${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n`,
128
+ `${C.dim}FFmpeg is required for 1080p+ quality and MP3 conversion.${C.reset}\n`,
129
129
  );
130
130
 
131
131
  try {
132
132
  if (isTermux) {
133
133
  console.log(
134
- `${C.blue}⏳ Mendeteksi Termux: Menginstall via pkg...${C.reset}`,
134
+ `${C.blue}⏳ Termux detected: Installing via pkg...${C.reset}`,
135
135
  );
136
136
  execSync('pkg update && pkg install ffmpeg -y', { stdio: 'inherit' });
137
137
  console.log(
138
- `\n${C.green}✅ FFmpeg berhasil diinstal di Termux!${C.reset}`,
138
+ `\n${C.green}✅ FFmpeg installed successfully on Termux!${C.reset}`,
139
139
  );
140
140
  } else if (isMac) {
141
- // ... (Kode macOS Anda sudah benar)
142
- console.log(`${C.blue}⏳ Mengunduh FFmpeg untuk macOS...${C.reset}`);
141
+ // ... (Your macOS code is correct)
142
+ console.log(`${C.blue}⏳ Downloading FFmpeg for macOS...${C.reset}`);
143
143
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
144
144
  execSync(
145
145
  `curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
@@ -148,54 +148,54 @@ async function installFfmpeg() {
148
148
  execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
149
149
  execSync(`rm "${zipPath}"`);
150
150
  execSync(`chmod a+rx "${FFMPEG_PATH}"`);
151
- console.log(`\n${C.green}✅ FFmpeg aktif di macOS.${C.reset}`);
151
+ console.log(`\n${C.green}✅ FFmpeg active on macOS.${C.reset}`);
152
152
  } else if (isWindows) {
153
153
  console.log(
154
- `${C.blue}⏳ Mengunduh FFmpeg untuk Windows (Essentials)...${C.reset}`,
154
+ `${C.blue}⏳ Downloading FFmpeg for Windows (Essentials)...${C.reset}`,
155
155
  );
156
156
 
157
157
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
158
- // Link direct ke build essentials agar file tidak terlalu besar
158
+ // Direct link to build essentials so the file isn't too large
159
159
  const url =
160
160
  'https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip';
161
161
 
162
- // 1. Download menggunakan PowerShell
162
+ // 1. Download using PowerShell
163
163
  execSync(
164
164
  `powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${zipPath}'"`,
165
165
  { stdio: 'inherit' },
166
166
  );
167
167
 
168
- console.log(`${C.yellow}📦 Mengekstrak FFmpeg...${C.reset}`);
168
+ console.log(`${C.yellow}📦 Extracting FFmpeg...${C.reset}`);
169
169
 
170
- // 2. Ekstrak menggunakan perintah 'tar' (Bawaan Windows 10+)
171
- // Kita hanya mengambil ffmpeg.exe dari dalam folder bin di zip tersebut
170
+ // 2. Extract using 'tar' command (Built-in Windows 10+)
171
+ // We only take ffmpeg.exe from the bin folder inside the zip
172
172
  execSync(
173
173
  `tar -xf "${zipPath}" -C "${TOOLS_DIR}" --strip-components 2 "*/bin/ffmpeg.exe" "*/bin/ffprobe.exe"`,
174
174
  { stdio: 'inherit' },
175
175
  );
176
176
 
177
- // 3. Bersihkan file zip
177
+ // 3. Clean up zip file
178
178
  if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
179
179
 
180
180
  console.log(
181
- `\n${C.green}✅ FFmpeg berhasil diinstal di Windows!${C.reset}`,
181
+ `\n${C.green}✅ FFmpeg installed successfully on Windows!${C.reset}`,
182
182
  );
183
183
  } else {
184
- // Asumsi Linux (Ubuntu/Debian)
184
+ // Assume Linux (Ubuntu/Debian)
185
185
  console.log(
186
- `${C.blue}⏳ Mendeteksi Linux: Menginstall via apt...${C.reset}`,
186
+ `${C.blue}⏳ Linux detected: Installing via apt...${C.reset}`,
187
187
  );
188
- console.log(`${C.dim}Mungkin memerlukan password sudo.${C.reset}`);
188
+ console.log(`${C.dim}May require sudo password.${C.reset}`);
189
189
  execSync('sudo apt update && sudo apt install ffmpeg -y', {
190
190
  stdio: 'inherit',
191
191
  });
192
192
  console.log(
193
- `\n${C.green}✅ FFmpeg berhasil diinstal di Linux!${C.reset}`,
193
+ `\n${C.green}✅ FFmpeg installed successfully on Linux!${C.reset}`,
194
194
  );
195
195
  }
196
196
  } catch (e) {
197
197
  console.error(
198
- `${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}`,
198
+ `${C.red}❌ Failed to install FFmpeg automatically.${C.reset}`,
199
199
  );
200
200
  console.log(`${C.dim}Error: ${e.message}${C.reset}`);
201
201
  }
@@ -204,11 +204,10 @@ async function installFfmpeg() {
204
204
  function runSpawn(command, args) {
205
205
  return new Promise((resolve) => {
206
206
  const proc = spawn(command, args);
207
- let lastOutput = '';
208
207
 
209
208
  proc.stdout.on('data', (data) => {
210
209
  const output = data.toString();
211
- // Regex untuk menangkap progress dari yt-dlp
210
+ // Regex to capture progress from yt-dlp
212
211
  const progressMatch = output.match(
213
212
  /\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)/,
214
213
  );
@@ -217,7 +216,7 @@ function runSpawn(command, args) {
217
216
  const [_, percent, speed, eta] = progressMatch;
218
217
  renderProgressBar(parseFloat(percent), speed, eta);
219
218
  } else {
220
- // Jika bukan bar, print normal (misal: info merging/ffmpeg)
219
+ // If not a bar, print normal (e.g., merging info/ffmpeg)
221
220
  if (output.trim() && !output.includes('[download]')) {
222
221
  process.stdout.write(`\n${C.dim}${output.trim()}${C.reset}\n`);
223
222
  }
@@ -232,7 +231,7 @@ function runSpawn(command, args) {
232
231
  });
233
232
 
234
233
  proc.on('close', (code) => {
235
- process.stdout.write('\n'); // Baris baru setelah selesai
234
+ process.stdout.write('\n'); // New line after completion
236
235
  resolve(code);
237
236
  });
238
237
  });
@@ -257,7 +256,7 @@ async function getEstimate(url, format) {
257
256
  return (bytes / 1024 ** 2).toFixed(2) + ' MB';
258
257
  }
259
258
  } catch (e) {}
260
- return 'Estimasi tidak tersedia';
259
+ return 'Estimate unavailable';
261
260
  }
262
261
 
263
262
  // --- DOWNLOAD ENGINE ---
@@ -265,25 +264,24 @@ async function startDownload(videoURLFromArgs = null) {
265
264
  let { ytExists, ffExists } = checkTools();
266
265
  if (!ytExists) {
267
266
  console.log(
268
- `\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}`,
267
+ `\n${C.red}❌ yt-dlp engine not found. Please select Update/Install menu.${C.reset}`,
269
268
  );
270
- await askQuestion('Tekan Enter...');
271
- return mainMenu();
269
+ await backToMenu();
272
270
  }
273
271
 
274
272
  if (!ffExists) {
275
273
  console.log(
276
- `${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}`,
274
+ `${C.yellow}⚠️ Warning: FFmpeg not found. Video might not be merged with audio.${C.reset}`,
277
275
  );
278
- const cont = await askQuestion('Lanjutkan saja? (y/n): ');
276
+ const cont = await askQuestion('Continue anyway? (y/n): ');
279
277
  if (cont.toLowerCase() !== 'y') return mainMenu();
280
278
  }
281
279
 
282
280
  const videoURL =
283
- videoURLFromArgs || (await askQuestion('Masukkan Link (Video/Playlist): '));
281
+ videoURLFromArgs || (await askQuestion('Enter Link (Video/Playlist): '));
284
282
  if (!videoURL) return mainMenu();
285
283
 
286
- console.log(`${C.dim}⏳ Menganalisa tautan...${C.reset}`);
284
+ console.log(`${C.dim}⏳ Analyzing link...${C.reset}`);
287
285
  let playlistInfo = { isPlaylist: false, title: '', items: [] };
288
286
 
289
287
  try {
@@ -298,30 +296,29 @@ async function startDownload(videoURLFromArgs = null) {
298
296
  const lines = rawInfo.trim().split('\n');
299
297
  if (lines.length > 1 || videoURL.includes('playlist?list=')) {
300
298
  playlistInfo.isPlaylist = true;
301
- playlistInfo.title = lines[0].split('|')[0] || 'Unduhan Playlist';
299
+ playlistInfo.title = lines[0].split('|')[0] || 'Playlist Download';
302
300
  playlistInfo.items = lines.map((l) => l.split('|')[1]).filter(Boolean);
303
301
  }
304
302
  } catch (e) {
305
- console.log(`\n${C.red}❌ Gagal menganalisa tautan.${C.reset}`);
303
+ console.log(`\n${C.red}❌ Failed to analyze link.${C.reset}`);
306
304
  if (e.message.includes('ETIMEDOUT')) {
307
305
  console.log(
308
- `${C.yellow}⚠️ Waktu analisa habis. Periksa koneksi internet Anda.${C.reset}`,
306
+ `${C.yellow}⚠️ Analysis timed out. Check your internet connection.${C.reset}`,
309
307
  );
310
308
  } else {
311
309
  console.log(
312
- `${C.yellow}⚠️ Pastikan link valid atau tidak diprivat/dihapus.${C.reset}`,
310
+ `${C.yellow}⚠️ Ensure link is valid or not private/deleted.${C.reset}`,
313
311
  );
314
312
  }
315
- await askQuestion('\nTekan Enter untuk kembali ke menu...');
316
- return mainMenu();
313
+ await backToMenu();
317
314
  }
318
315
 
319
- // --- PEMILIHAN PLAYLIST (Tetap Sama) ---
316
+ // --- PLAYLIST SELECTION (Logic remains same) ---
320
317
  let playlistSelection = null;
321
318
  if (playlistInfo.isPlaylist) {
322
- // ... (Logika tampilan playlist sama seperti sebelumnya)
319
+ // ... (Playlist display logic same as before)
323
320
  console.log(
324
- `\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`,
321
+ `\n${C.bgBlue}${C.bright} 📂 PLAYLIST DETECTED: ${playlistInfo.title} ${C.reset}`,
325
322
  );
326
323
  playlistInfo.items.forEach((item, index) => {
327
324
  console.log(
@@ -329,11 +326,11 @@ async function startDownload(videoURLFromArgs = null) {
329
326
  );
330
327
  });
331
328
  console.log(
332
- `\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}`,
329
+ `\n${C.dim}Example: 1,3,5-10 or leave empty for all.${C.reset}`,
333
330
  );
334
- const selectionInput = await askQuestion('\nPilih nomor: ');
331
+ const selectionInput = await askQuestion('\nSelect numbers: ');
335
332
  if (selectionInput) {
336
- // ... (Logika parsing nomor playlist)
333
+ // ... (Playlist parsing logic)
337
334
  const selected = new Set();
338
335
  selectionInput.split(',').forEach((p) => {
339
336
  if (p.includes('-')) {
@@ -349,20 +346,20 @@ async function startDownload(videoURLFromArgs = null) {
349
346
  }
350
347
  }
351
348
 
352
- // --- MENU FORMAT UTAMA ---
353
- console.log(`\n${C.bright} [ PILIH FORMAT ]${C.reset}`);
349
+ // --- MAIN FORMAT MENU ---
350
+ console.log(`\n${C.bright} [ SELECT FORMAT ]${C.reset}`);
354
351
  console.log(` ${C.green}1.${C.reset} Video (MP4)`);
355
352
  console.log(` ${C.green}2.${C.reset} Audio Only (MP3)`);
356
- const mode = await askQuestion('Pilihan: ');
353
+ const mode = await askQuestion('Choice: ');
357
354
 
358
- // --- LOGIKA RESOLUSI OPTIMAL ---
355
+ // --- OPTIMAL RESOLUTION LOGIC ---
359
356
  let formatArg = '';
360
357
  if (mode === '1') {
361
- console.log(`\n${C.bright} [ PILIH RESOLUSI ]${C.reset}`);
358
+ console.log(`\n${C.bright} [ SELECT RESOLUTION ]${C.reset}`);
362
359
  console.log(` ${C.cyan}1.${C.reset} Best Quality (Up to 4K)`);
363
360
  console.log(` ${C.cyan}2.${C.reset} Tablet Optimal (720p)`);
364
361
  console.log(` ${C.cyan}3.${C.reset} Mobile Optimal (480p)`);
365
- const resChoice = await askQuestion('Pilihan Resolusi (1-3): ');
362
+ const resChoice = await askQuestion('Select Resolution (1-3): ');
366
363
 
367
364
  if (resChoice === '2') {
368
365
  formatArg =
@@ -375,13 +372,13 @@ async function startDownload(videoURLFromArgs = null) {
375
372
  'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best';
376
373
  }
377
374
  }
378
- // --- TAMPILKAN ESTIMASI ---
375
+ // --- DISPLAY ESTIMATE ---
379
376
  if (!playlistInfo.isPlaylist && mode === '1') {
380
- console.log(`${C.dim}⏳ Menghitung estimasi ukuran file...${C.reset}`);
377
+ console.log(`${C.dim}⏳ Calculating file size estimate...${C.reset}`);
381
378
  const size = await getEstimate(videoURL, formatArg);
382
- console.log(`${C.yellow}📊 Estimasi Ukuran: ${C.bright}${size}${C.reset}`);
379
+ console.log(`${C.yellow}📊 Estimated Size: ${C.bright}${size}${C.reset}`);
383
380
 
384
- const confirm = await askQuestion('Lanjutkan unduhan? (Y/n): ');
381
+ const confirm = await askQuestion('Proceed with download? (Y/n): ');
385
382
  if (confirm.toLowerCase() === 'n') return mainMenu();
386
383
  }
387
384
 
@@ -403,7 +400,7 @@ async function startDownload(videoURLFromArgs = null) {
403
400
  videoURL,
404
401
  ];
405
402
 
406
- // Integrasi Safe Mode (cite: cli.js)
403
+ // Integrate Safe Mode (cite: cli.js)
407
404
  if (safeMode) {
408
405
  args.push(
409
406
  '--rate-limit',
@@ -423,7 +420,7 @@ async function startDownload(videoURLFromArgs = null) {
423
420
  if (mode === '2') {
424
421
  if (!ffExists) {
425
422
  console.log(
426
- `${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}`,
423
+ `${C.red}❌ Error: FFmpeg is required to download audio.${C.reset}`,
427
424
  );
428
425
  return mainMenu();
429
426
  }
@@ -434,11 +431,11 @@ async function startDownload(videoURLFromArgs = null) {
434
431
  }
435
432
 
436
433
  args.push('--no-mtime');
437
- console.log(`\n${C.bgBlue}${C.bright} 🚀 MEMULAI PROSES... ${C.reset}\n`);
434
+ console.log(`\n${C.bgBlue}${C.bright} 🚀 STARTING PROCESS... ${C.reset}\n`);
438
435
 
439
436
  const code = await runSpawn(YTDLP_PATH, args);
440
437
  if (code === 0) {
441
- console.log(`\n${C.green}✨ SELESAI! Cek folder: ${outputDir}${C.reset}`);
438
+ console.log(`\n${C.green}✨ DONE! Check folder: ${outputDir}${C.reset}`);
442
439
  try {
443
440
  execSync(
444
441
  isWindows
@@ -449,27 +446,42 @@ async function startDownload(videoURLFromArgs = null) {
449
446
  );
450
447
  } catch (e) {}
451
448
  } else {
452
- console.log(`\n${C.red}❌ Terjadi kesalahan saat mengunduh.${C.reset}`);
449
+ console.log(`\n${C.red}❌ An error occurred while downloading.${C.reset}`);
450
+ }
451
+ await backToMenu();
452
+ }
453
+
454
+ async function backToMenu() {
455
+ try {
456
+ await askQuestion('Press Enter to return to Main Menu...');
457
+ mainMenu(); // Return to menu
458
+ } catch (err) {
459
+ // Handle if readline is already closed (Ctrl+C pressed previously)
460
+ if (err.code === 'ERR_USE_AFTER_CLOSE') {
461
+ console.log(`\n${C.dim}Program terminated by user.${C.reset}`);
462
+ process.exit(0);
463
+ } else {
464
+ // Throw other errors if necessary
465
+ throw err;
466
+ }
453
467
  }
454
- await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
455
- mainMenu();
456
468
  }
457
469
 
458
470
  async function showSupport() {
459
- // Menggunakan 2 parameter: Judul dan Summary
460
- printHeader('TENTANG APLIKASI', 'Media-DL Manager Pro v2.0.0 - 2026');
471
+ // Using 2 parameters: Title and Summary
472
+ printHeader('ABOUT APPLICATION', 'Media-DL Manager Pro v2.0.0 - 2026');
461
473
 
462
- // --- SEKSI FITUR ---
474
+ // --- FEATURES SECTION ---
463
475
  console.log(` ${C.bright}${C.cyan}OVERVIEW${C.reset}`);
464
- console.log(` Terima kasih telah memilih MEDIA-DL. Skrip ini dirancang`);
465
- console.log(` untuk memudahkan manajemen unduhan media secara lokal.\n`);
476
+ console.log(` Thank you for choosing MEDIA-DL. This script is designed`);
477
+ console.log(` to facilitate local media download management.\n`);
466
478
 
467
- console.log(` ${C.bright}${C.cyan}FITUR UNGGULAN${C.reset}`);
479
+ console.log(` ${C.bright}${C.cyan}KEY FEATURES${C.reset}`);
468
480
  const features = [
469
481
  {
470
482
  icon: '✦',
471
483
  title: 'High Quality',
472
- desc: 'Mendukung hingga 4K & Audio 320kbps',
484
+ desc: 'Supports up to 4K & 320kbps Audio',
473
485
  },
474
486
  {
475
487
  icon: '✦',
@@ -479,12 +491,12 @@ async function showSupport() {
479
491
  {
480
492
  icon: '✦',
481
493
  title: 'Batch Mode',
482
- desc: 'Mendukung unduhan Playlist secara massal',
494
+ desc: 'Supports bulk Playlist downloading',
483
495
  },
484
496
  {
485
497
  icon: '✦',
486
498
  title: 'Safe Guard',
487
- desc: 'Mode proteksi agar akun/IP tidak terblokir',
499
+ desc: 'Protection mode to prevent account/IP blocks',
488
500
  },
489
501
  ];
490
502
 
@@ -498,15 +510,25 @@ async function showSupport() {
498
510
 
499
511
  console.log('\n' + '─'.repeat(52));
500
512
 
501
- // --- SEKSI DUKUNGAN ---
502
- console.log(`\n ${C.bright}${C.magenta}DUKUNGAN & DONASI${C.reset}`);
503
- console.log(` Dukungan Anda sangat membantu pengembang untuk terus`);
504
- console.log(` memperbarui engine dan fitur aplikasi ini.\n`);
513
+ // --- SUPPORT SECTION ---
514
+ console.log(`\n ${C.bright}${C.magenta}SUPPORT & DONATION${C.reset}`);
515
+ console.log(` Your support helps the developer to keep updating`);
516
+ console.log(` the engine and features of this application.\n`);
505
517
 
506
- // Menampilkan Link dengan label background agar menonjol
518
+ // Display Links with background label to stand out
507
519
  const links = [
508
- { label: ' ☕ BELI KOPI ', url: 'https://app.midtrans.com/coffee' },
509
- { label: ' 🍕 BELI PIZZA', url: 'https://app.midtrans.com/pizza' },
520
+ {
521
+ label: ' BUY COFFEE (Paypal)',
522
+ url: 'https://www.paypal.com/ncp/payment/RSXEBXBQGDYN4',
523
+ },
524
+ {
525
+ label: ' ☕ BUY COFFEE (Midtrans)',
526
+ url: 'https://app.midtrans.com/coffee',
527
+ },
528
+ {
529
+ label: ' 🍕 BUY PIZZA (Midtrans)',
530
+ url: 'https://app.midtrans.com/pizza',
531
+ },
510
532
  ];
511
533
 
512
534
  links.forEach((l) => {
@@ -517,18 +539,17 @@ async function showSupport() {
517
539
 
518
540
  console.log(`\n${C.cyan}${'━'.repeat(52)}${C.reset}`);
519
541
 
520
- await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
521
- mainMenu();
542
+ await backToMenu();
522
543
  }
523
544
 
524
545
  async function mainMenu() {
525
546
  const status = checkTools();
526
547
  const { ytExists, ffExists } = status;
527
548
 
528
- // Menggunakan 2 parameter: Judul dan Summary status singkat
529
- printHeader('MEDIA-DL PRO 2026', 'Pusat Kendali Unduhan Media Lokal');
549
+ // Using 2 parameters: Title and short summary status
550
+ printHeader('MEDIA-DL PRO 2026', 'Local Media Download Control Center');
530
551
 
531
- // --- SEKSI DASHBOARD (INFO SISTEM) ---
552
+ // --- DASHBOARD SECTION (SYSTEM INFO) ---
532
553
  const ytLabel = status.isLocalYt
533
554
  ? `${C.green}Ready (Internal)${C.reset}`
534
555
  : status.ytExists
@@ -551,14 +572,14 @@ async function mainMenu() {
551
572
 
552
573
  console.log(` ${C.cyan}━${'━'.repeat(48)}${C.reset}`);
553
574
 
554
- // --- SEKSI NAVIGASI ---
575
+ // --- NAVIGATION SECTION ---
555
576
  console.log(` ${C.bright}MAIN SERVICES${C.reset}`);
556
577
  console.log(
557
578
  ` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}`,
558
579
  );
559
580
  console.log(
560
- ` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Sekarang: ${
561
- safeMode ? 'Aktif' : 'Nonaktif'
581
+ ` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Current: ${
582
+ safeMode ? 'Active' : 'Inactive'
562
583
  })${C.reset}`,
563
584
  );
564
585
 
@@ -567,13 +588,13 @@ async function mainMenu() {
567
588
  ` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}`,
568
589
  );
569
590
  console.log(
570
- ` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}`,
591
+ ` ${C.cyan}4.${C.reset} ❤️ About Application ${C.dim}(Support & Features)${C.reset}`,
571
592
  );
572
- console.log(` ${C.cyan}0.${C.reset} 🚪 Keluar`);
593
+ console.log(` ${C.cyan}0.${C.reset} 🚪 Exit`);
573
594
 
574
595
  console.log(` ${C.cyan}━${'━'.repeat(48)}${C.reset}`);
575
596
 
576
- const choice = await askQuestion('\nPilih menu (0-4): ');
597
+ const choice = await askQuestion('\nSelect menu (0-4): ');
577
598
 
578
599
  switch (choice) {
579
600
  case '1':
@@ -581,10 +602,10 @@ async function mainMenu() {
581
602
  break;
582
603
  case '2':
583
604
  safeMode = !safeMode;
584
- // Berikan feedback visual singkat sebelum refresh menu
605
+ // Provide short visual feedback before refreshing menu
585
606
  console.log(
586
- `\n${C.yellow} 🛡️ Safe Mode telah ${
587
- safeMode ? 'DIAKTIFKAN' : 'DINONAKTIFKAN'
607
+ `\n${C.yellow} 🛡️ Safe Mode has been ${
608
+ safeMode ? 'ENABLED' : 'DISABLED'
588
609
  }${C.reset}`,
589
610
  );
590
611
  setTimeout(() => mainMenu(), 800);
@@ -598,14 +619,14 @@ async function mainMenu() {
598
619
  case '0':
599
620
  console.log(`\n${C.cyan}━${'━'.repeat(48)}${C.reset}`);
600
621
  console.log(
601
- ` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}`,
622
+ ` ${C.bright}${C.white}Thank you for using MEDIA-DL!${C.reset}`,
602
623
  );
603
624
  console.log(
604
- ` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`,
625
+ ` ${C.green}✨ Wishing you success, prosperity, and good health! ✨${C.reset}`,
605
626
  );
606
627
  console.log(`${C.cyan}━${'━'.repeat(48)}${C.reset}\n`);
607
628
 
608
- // Memberikan jeda sebentar sebelum benar-benar menutup terminal
629
+ // Give a short delay before actually closing terminal
609
630
  setTimeout(() => {
610
631
  rl.close();
611
632
  process.exit(0);
@@ -613,7 +634,7 @@ async function mainMenu() {
613
634
 
614
635
  break;
615
636
  default:
616
- // Jika salah input, tampilkan kembali menu
637
+ // If input incorrect, show menu again
617
638
  mainMenu();
618
639
  break;
619
640
  }
@@ -624,7 +645,7 @@ async function cleanUp() {
624
645
  fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
625
646
  }
626
647
 
627
- // RESET PATH ke default lokal agar checkTools tidak "tersesat" menggunakan path lama
648
+ // RESET PATH to local default so checkTools doesn't "get lost" using old path
628
649
  YTDLP_PATH = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
629
650
  FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
630
651
  }
@@ -634,28 +655,28 @@ async function firstTimeSetup() {
634
655
  const { ytExists, ffExists } = checkTools();
635
656
  printHeader(
636
657
  'FIRST-TIME SETUP',
637
- 'Komponen diperlukan untuk menjalankan aplikasi',
658
+ 'Components required to run the application',
638
659
  );
639
660
 
640
- console.log(`${C.white}Status Instalasi:${C.reset}`);
661
+ console.log(`${C.white}Installation Status:${C.reset}`);
641
662
  console.log(
642
663
  ` [${ytExists ? C.green + '✓' : C.red + '✗'}${
643
664
  C.reset
644
- }] Engine yt-dlp (Wajib)`,
665
+ }] yt-dlp Engine (Required)`,
645
666
  );
646
667
  console.log(
647
668
  ` [${ffExists ? C.green + '✓' : C.red + '✗'}${
648
669
  C.reset
649
- }] FFmpeg (Direkomendasikan)`,
670
+ }] FFmpeg (Recommended)`,
650
671
  );
651
672
 
652
673
  console.log(
653
- `\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`,
674
+ `\n${C.yellow}Application is not ready. Select option:${C.reset}`,
654
675
  );
655
- console.log(` ${C.cyan}1.${C.reset} Install Semua Komponen Otomatis`);
656
- console.log(` ${C.cyan}0.${C.reset} Keluar dari Aplikasi`);
676
+ console.log(` ${C.cyan}1.${C.reset} Install All Components Automatically`);
677
+ console.log(` ${C.cyan}0.${C.reset} Exit Application`);
657
678
 
658
- const choice = await askQuestion('\nPilih: ');
679
+ const choice = await askQuestion('\nSelect: ');
659
680
 
660
681
  if (choice === '1') {
661
682
  if (!ytExists) await installYtdlp();
@@ -664,13 +685,13 @@ async function firstTimeSetup() {
664
685
  const status = checkTools();
665
686
  if (status.ytExists) {
666
687
  console.log(
667
- `\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`,
688
+ `\n${C.green}✨ Setup Complete! Opening Main Menu...${C.reset}`,
668
689
  );
669
690
  await new Promise((r) => setTimeout(r, 1500));
670
- return mainMenu(); // Berhasil, lanjut ke menu utama
691
+ return mainMenu(); // Success, proceed to main menu
671
692
  }
672
693
  } else if (choice === '0') {
673
- console.log('Menutup aplikasi...');
694
+ console.log('Closing application...');
674
695
  process.exit(0);
675
696
  }
676
697
  }
@@ -681,12 +702,9 @@ async function systemMaintenance() {
681
702
 
682
703
  while (inMaintenance) {
683
704
  const { ytExists, ffExists } = checkTools();
684
- printHeader(
685
- 'SYSTEM MAINTENANCE',
686
- 'Update engine atau bersihkan file sistem',
687
- );
705
+ printHeader('SYSTEM MAINTENANCE', 'Update engine or clean system files');
688
706
 
689
- console.log(`${C.white}Versi Terinstal:${C.reset}`);
707
+ console.log(`${C.white}Installed Versions:${C.reset}`);
690
708
  console.log(
691
709
  ` • yt-dlp : ${ytExists ? C.green + 'Ready' : C.red + 'Not Found'}${
692
710
  C.reset
@@ -698,39 +716,41 @@ async function systemMaintenance() {
698
716
  }`,
699
717
  );
700
718
 
701
- console.log(`\n${C.bright}Opsi Pemeliharaan:${C.reset}`);
719
+ console.log(`\n${C.bright}Maintenance Options:${C.reset}`);
702
720
  console.log(` ${C.cyan}1.${C.reset} Update / Reinstall Engines`);
703
- console.log(` ${C.cyan}2.${C.reset} 🗑️ Hapus Semua Tools (Reset System)`);
704
- console.log(` ${C.cyan}3.${C.reset} ⬅️ Kembali ke Menu Utama`);
721
+ console.log(` ${C.cyan}2.${C.reset} 🗑️ Delete All Tools (System Reset)`);
722
+ console.log(` ${C.cyan}3.${C.reset} ⬅️ Return to Main Menu`);
705
723
 
706
- const choice = await askQuestion('\nPilih tindakan: ');
724
+ const choice = await askQuestion('\nSelect action: ');
707
725
 
708
726
  switch (choice) {
709
727
  case '1':
710
728
  await installYtdlp();
711
729
  await installFfmpeg();
712
- await askQuestion('\nUpdate selesai. Tekan Enter...');
730
+ await askQuestion('\nUpdate complete. Press Enter...');
713
731
  break;
714
732
 
715
733
  case '2':
716
734
  const confirm = await askQuestion(
717
- `${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n): `,
735
+ `${C.bgRed}${C.white} CONFIRMATION ${C.reset} Delete all tools? (y/n): `,
718
736
  );
719
737
  if (confirm.toLowerCase() === 'y') {
720
- await cleanUp(); // Panggil fungsi penghapusan folder
721
- console.log(`${C.yellow}Folder .media-dl telah dihapus.${C.reset}`);
738
+ await cleanUp(); // Call folder deletion function
739
+ console.log(
740
+ `${C.yellow}Folder .media-dl has been deleted.${C.reset}`,
741
+ );
722
742
 
723
- // Cek ulang status setelah hapus
743
+ // Re-check status after deletion
724
744
  const finalCheck = checkTools();
725
745
  if (finalCheck.isLocalYt || finalCheck.isLocalFf) {
726
746
  console.log(
727
- `${C.red}Gagal menghapus beberapa file. Pastikan tidak ada proses yang mengunci file.${C.reset}`,
747
+ `${C.red}Failed to delete some files. Ensure no processes are locking them.${C.reset}`,
728
748
  );
729
749
  } else {
730
- console.log(`${C.green}Reset lokal berhasil.${C.reset}`);
750
+ console.log(`${C.green}Local reset successful.${C.reset}`);
731
751
  }
732
- await askQuestion('Tekan Enter...');
733
- return bootstrap(); // Kembali ke pengecekan awal
752
+ await askQuestion('Press Enter...');
753
+ return bootstrap(); // Return to initial check
734
754
  }
735
755
  break;
736
756
 
@@ -750,18 +770,17 @@ async function bootstrap() {
750
770
  if (!status.allReady) {
751
771
  await firstTimeSetup();
752
772
  } else {
753
- // process.argv[2] mengambil argumen pertama setelah nama perintah
773
+ // process.argv[2] takes the first argument after the command name
754
774
  const urlArgument = process.argv[2];
755
775
 
756
776
  if (urlArgument) {
757
- // Jika ada URL di terminal, langsung jalankan download
777
+ // If there is a URL in terminal, run download directly
758
778
  await startDownload(urlArgument);
759
779
  } else {
760
- // Jika tidak ada, masuk ke menu utama seperti biasa [cite: 113]
780
+ // If none, enter main menu as usual
761
781
  mainMenu();
762
782
  }
763
783
  }
764
784
  }
765
785
 
766
786
  bootstrap();
767
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "CLI Downloader video/audio lintas platform menggunakan yt-dlp.",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
package/readme.id.md ADDED
@@ -0,0 +1,103 @@
1
+ # 🚀 Media-DL Pro 2026
2
+
3
+ **The Ultimate Cross-Platform Media Engine Manager**
4
+
5
+ Media-DL Pro adalah *CLI wrapper* canggih berbasis `yt-dlp` yang dirancang untuk kecepatan, keteraturan, dan keamanan. Bukan sekadar pengunduh, ini adalah manajer media lokal yang cerdas dengan sistem instalasi otomatis.
6
+
7
+ ---
8
+
9
+ ## ✨ Fitur Unggulan Baru
10
+
11
+ ### 1. ⚡ Direct Download & Menu Mode
12
+
13
+ Sekarang kamu bisa memilih dua cara penggunaan:
14
+
15
+ * **Interactive Mode**: Cukup ketik `media-dl` untuk masuk ke menu utama yang cantik.
16
+ * **Fast Mode**: Ketik `media-dl <url>` untuk langsung masuk ke proses download tanpa basa-basi.
17
+
18
+ ### 2. 📱 Android (Termux) Ready
19
+
20
+ Dukungan penuh untuk pengguna mobile via Termux dengan script instalasi otomatis yang menyesuaikan lingkungan Linux Android.
21
+
22
+ ### 3. 🛡️ Safe Mode Guard™ (Updated)
23
+
24
+ Menghindari deteksi bot dengan:
25
+
26
+ * **Rate Limiting**: Dibatasi hingga 5 MB/s.
27
+ * **Smart Sleep**: Jeda acak 3–10 detik.
28
+ * **Modern User-Agent**: Identitas browser terbaru agar tetap aman.
29
+
30
+ ---
31
+
32
+ ## 🎞️ Platform yang Didukung
33
+
34
+ Berkat engine `yt-dlp` yang selalu diperbarui, kamu bisa mengunduh dari:
35
+
36
+ * **YouTube**: Video, Shorts, & Playlist.
37
+ * **Social Media**: TikTok, Instagram Reels, Twitter (X).
38
+ * **VOD Services**: Dan ratusan platform video lainnya.
39
+
40
+ ---
41
+
42
+ ## 📦 Instalasi
43
+
44
+ ### Prasyarat
45
+
46
+ * **Node.js**: Versi 14.0.0 atau lebih tinggi.
47
+
48
+ ### Cara Install
49
+
50
+ ```bash
51
+ npm install -g media-dl
52
+
53
+ ```
54
+
55
+ ### Penggunaan
56
+
57
+ ```bash
58
+ # Buka menu utama
59
+ media-dl
60
+
61
+ # Download langsung tanpa menu
62
+ media-dl https://www.youtube.com/watch?v=example
63
+
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🛠️ Navigasi Sistem
69
+
70
+ 1. **📥 Download Media**: Mendukung pemilihan kualitas (Video/Audio MP3) dan seleksi playlist (misal: `1,3,5-10`).
71
+ 2. **🛡️ Toggle Safe Mode**: Aktifkan perlindungan tambahan secara *on-the-fly*.
72
+ 3. **⚙️ Maintenance**: Update otomatis `yt-dlp` dan `FFmpeg` langsung dari aplikasi tanpa perlu download manual.
73
+ 4. **🗑️ Reset System**: Hapus semua engine untuk instalasi ulang yang bersih.
74
+
75
+ ---
76
+
77
+ ## 💻 Kompatibilitas Sistem
78
+
79
+ | Sistem Operasi | Status | Cara Kerja |
80
+ | --- | --- | --- |
81
+ | **Windows** | ✅ Supported | Auto-download `.exe` ke folder `~/.media-dl` |
82
+ | **macOS** | ✅ Supported | Auto-download via `curl` |
83
+ | **Linux** | ✅ Supported | Integrasi via `apt` (Debian/Ubuntu) |
84
+ | **Termux** | ✅ Supported | Integrasi via `pkg` & `pip` |
85
+
86
+ ---
87
+
88
+ ## 📂 Struktur Penyimpanan
89
+
90
+ Unduhan kamu akan tersimpan rapi di:
91
+
92
+ * **Video**: `~/Downloads/media-dl/video/`
93
+ * **Audio**: `~/Downloads/media-dl/audio/`
94
+ * **Playlist**: Sub-folder otomatis berdasarkan nama playlist.
95
+
96
+ ---
97
+
98
+ ## ❤️ Dukungan
99
+
100
+ Aplikasi ini dikembangkan oleh **Ariska Hidayat**. Jika bermanfaat, kamu bisa memberikan dukungan untuk biaya pemeliharaan server/engine:
101
+
102
+ * **☕ Traktir Kopi**: [Midtrans Coffee](https://app.midtrans.com/coffee)
103
+ * **🍕 Beli Pizza**: [Midtrans Pizza](https://app.midtrans.com/pizza)
package/readme.md CHANGED
@@ -1,103 +1,113 @@
1
- # 🚀 Media-DL Pro 2026
2
-
3
- **The Ultimate Cross-Platform Media Engine Manager**
4
-
5
- Media-DL Pro adalah *CLI wrapper* canggih berbasis `yt-dlp` yang dirancang untuk kecepatan, keteraturan, dan keamanan. Bukan sekadar pengunduh, ini adalah manajer media lokal yang cerdas dengan sistem instalasi otomatis.
6
-
7
- ---
8
-
9
- ## ✨ Fitur Unggulan Baru
10
-
11
- ### 1. ⚡ Direct Download & Menu Mode
12
-
13
- Sekarang kamu bisa memilih dua cara penggunaan:
14
-
15
- * **Interactive Mode**: Cukup ketik `media-dl` untuk masuk ke menu utama yang cantik.
16
- * **Fast Mode**: Ketik `media-dl <url>` untuk langsung masuk ke proses download tanpa basa-basi.
17
-
18
- ### 2. 📱 Android (Termux) Ready
19
-
20
- Dukungan penuh untuk pengguna mobile via Termux dengan script instalasi otomatis yang menyesuaikan lingkungan Linux Android.
21
-
22
- ### 3. 🛡️ Safe Mode Guard™ (Updated)
23
-
24
- Menghindari deteksi bot dengan:
25
-
26
- * **Rate Limiting**: Dibatasi hingga 5 MB/s.
27
- * **Smart Sleep**: Jeda acak 3–10 detik.
28
- * **Modern User-Agent**: Identitas browser terbaru agar tetap aman.
29
-
30
- ---
31
-
32
- ## 🎞️ Platform yang Didukung
33
-
34
- Berkat engine `yt-dlp` yang selalu diperbarui, kamu bisa mengunduh dari:
35
-
36
- * **YouTube**: Video, Shorts, & Playlist.
37
- * **Social Media**: TikTok, Instagram Reels, Twitter (X).
38
- * **VOD Services**: Dan ratusan platform video lainnya.
39
-
40
- ---
41
-
42
- ## 📦 Instalasi
43
-
44
- ### Prasyarat
45
-
46
- * **Node.js**: Versi 14.0.0 atau lebih tinggi.
47
-
48
- ### Cara Install
49
-
50
- ```bash
51
- npm install -g media-dl
52
-
53
- ```
54
-
55
- ### Penggunaan
56
-
57
- ```bash
58
- # Buka menu utama
59
- media-dl
60
-
61
- # Download langsung tanpa menu
62
- media-dl https://www.youtube.com/watch?v=example
63
-
64
- ```
65
-
66
- ---
67
-
68
- ## 🛠️ Navigasi Sistem
69
-
70
- 1. **📥 Download Media**: Mendukung pemilihan kualitas (Video/Audio MP3) dan seleksi playlist (misal: `1,3,5-10`).
71
- 2. **🛡️ Toggle Safe Mode**: Aktifkan perlindungan tambahan secara *on-the-fly*.
72
- 3. **⚙️ Maintenance**: Update otomatis `yt-dlp` dan `FFmpeg` langsung dari aplikasi tanpa perlu download manual.
73
- 4. **🗑️ Reset System**: Hapus semua engine untuk instalasi ulang yang bersih.
74
-
75
- ---
76
-
77
- ## 💻 Kompatibilitas Sistem
78
-
79
- | Sistem Operasi | Status | Cara Kerja |
80
- | --- | --- | --- |
81
- | **Windows** | ✅ Supported | Auto-download `.exe` ke folder `~/.media-dl` |
82
- | **macOS** | ✅ Supported | Auto-download via `curl` |
83
- | **Linux** | ✅ Supported | Integrasi via `apt` (Debian/Ubuntu) |
84
- | **Termux** | ✅ Supported | Integrasi via `pkg` & `pip` |
85
-
86
- ---
87
-
88
- ## 📂 Struktur Penyimpanan
89
-
90
- Unduhan kamu akan tersimpan rapi di:
91
-
92
- * **Video**: `~/Downloads/media-dl/video/`
93
- * **Audio**: `~/Downloads/media-dl/audio/`
94
- * **Playlist**: Sub-folder otomatis berdasarkan nama playlist.
95
-
96
- ---
97
-
98
- ## ❤️ Dukungan
99
-
100
- Aplikasi ini dikembangkan oleh **Ariska Hidayat**. Jika bermanfaat, kamu bisa memberikan dukungan untuk biaya pemeliharaan server/engine:
101
-
102
- * **☕ Traktir Kopi**: [Midtrans Coffee](https://app.midtrans.com/coffee)
103
- * **🍕 Beli Pizza**: [Midtrans Pizza](https://app.midtrans.com/pizza)
1
+ # 🚀 Media-DL Pro 2026
2
+
3
+ **The Ultimate Cross-Platform Media Engine Manager**
4
+
5
+ Media-DL Pro is an advanced *CLI wrapper* built on top of `yt-dlp`, designed for speed, structure, and security. It is not just a downloader, but a smart local media manager with an automated installation system.
6
+
7
+ ---
8
+
9
+ ## ✨ New Key Features
10
+
11
+ ### 1. ⚡ Direct Download & Menu Mode
12
+
13
+ You can now choose between two usage styles:
14
+
15
+ * **Interactive Mode**: Simply run `media-dl` to access a clean, user-friendly main menu.
16
+ * **Fast Mode**: Run `media-dl <url>` to start downloading immediately without entering the menu.
17
+
18
+ ### 2. 📱 Android (Termux) Ready
19
+
20
+ Full support for mobile users via Termux, with automatic installation scripts tailored for the Android Linux environment.
21
+
22
+ ### 3. 🛡️ Safe Mode Guard™ (Updated)
23
+
24
+ Designed to avoid bot detection through:
25
+
26
+ * **Rate Limiting**: Capped at 5 MB/s.
27
+ * **Smart Sleep**: Random delays between 3–10 seconds.
28
+ * **Modern User-Agent**: Uses up-to-date browser identifiers for safer requests.
29
+
30
+ ---
31
+
32
+ ## 🎞️ Supported Platforms
33
+
34
+ Powered by the continuously updated `yt-dlp` engine, Media-DL Pro supports downloads from:
35
+
36
+ * **YouTube**: Videos, Shorts, and Playlists.
37
+ * **Social Media**: TikTok, Instagram Reels, Twitter (X).
38
+ * **VOD Services**: And hundreds of other video platforms.
39
+
40
+ ---
41
+
42
+ ## 📦 Installation
43
+
44
+ ### Requirements
45
+
46
+ * **Node.js**: Version 14.0.0 or later.
47
+
48
+ ### Install
49
+
50
+ ```bash
51
+ npm install -g media-dl
52
+ ```
53
+
54
+ ### Usage
55
+
56
+ ```bash
57
+ # Open the main menu
58
+ media-dl
59
+
60
+ # Direct download without menu
61
+ media-dl https://www.youtube.com/watch?v=example
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 🛠️ System Navigation
67
+
68
+ 1. **📥 Download Media**
69
+ Supports quality selection (Video / MP3 Audio) and playlist filtering (e.g. `1,3,5-10`).
70
+
71
+ 2. **🛡️ Toggle Safe Mode**
72
+ Enable or disable additional protection on the fly.
73
+
74
+ 3. **⚙️ Maintenance**
75
+ Automatically update `yt-dlp` and `FFmpeg` directly from the app—no manual downloads required.
76
+
77
+ 4. **🗑️ Reset System**
78
+ Remove all engines for a clean reinstallation.
79
+
80
+ ---
81
+
82
+ ## 💻 System Compatibility
83
+
84
+ | Operating System | Status | Method |
85
+ | ---------------- | ----------- | --------------------------------------- |
86
+ | **Windows** | ✅ Supported | Auto-download `.exe` into `~/.media-dl` |
87
+ | **macOS** | ✅ Supported | Auto-download via `curl` |
88
+ | **Linux** | Supported | Integrated via `apt` (Debian/Ubuntu) |
89
+ | **Termux** | ✅ Supported | Integrated via `pkg` & `pip` |
90
+
91
+ ---
92
+
93
+ ## 📂 Storage Structure
94
+
95
+ Your downloads are neatly organized under:
96
+
97
+ * **Video**: `~/Downloads/media-dl/video/`
98
+ * **Audio**: `~/Downloads/media-dl/audio/`
99
+ * **Playlists**: Automatically grouped into subfolders by playlist name.
100
+
101
+ ---
102
+
103
+ ## ❤️ Support
104
+
105
+ This project is developed and maintained by **Ariska Hidayat**.
106
+ If you find it useful, you can support ongoing development and server/engine maintenance via:
107
+
108
+ * **☕ Buy Me a Coffee (Indonesia)**:
109
+ [https://app.midtrans.com/coffee](https://app.midtrans.com/coffee)
110
+ * **🍕 Buy Me a Pizza (Indonesia)**:
111
+ [https://app.midtrans.com/pizza](https://app.midtrans.com/pizza)
112
+ * **🌍 PayPal (International)**:
113
+ [https://www.paypal.com/ncp/payment/RSXEBXBQGDYN4](https://www.paypal.com/ncp/payment/RSXEBXBQGDYN4)