media-dl 2.5.2 → 2.7.0

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 +140 -513
  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,10 +6,8 @@ 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) ---
10
-
9
+ // --- CONFIGURATION ---
11
10
  const TOOLS_DIR = path.join(os.homedir(), '.media-dl');
12
-
13
11
  const isWindows = process.platform === 'win32';
14
12
  const isMac = process.platform === 'darwin';
15
13
  const isTermux =
@@ -18,28 +16,21 @@ const isTermux =
18
16
  let YTDLP_PATH = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
19
17
  let FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
20
18
 
21
- // State Aplikasi
22
19
  let safeMode = true;
23
20
 
24
21
  if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
25
22
 
26
23
  function checkTools() {
27
- // Cek jalur default lokal (internal)
28
24
  const LOCAL_YT = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
29
25
  const LOCAL_FF = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
30
26
 
31
- const isLocalYt = fs.existsSync(LOCAL_YT);
32
- const isLocalFf = fs.existsSync(LOCAL_FF);
33
-
34
- let ytExists = isLocalYt;
35
- let ffExists = isLocalFf;
27
+ let ytExists = fs.existsSync(LOCAL_YT);
28
+ let ffExists = fs.existsSync(LOCAL_FF);
36
29
 
37
- // Reset path ke default sebelum pengecekan global
38
- if (isLocalYt) YTDLP_PATH = LOCAL_YT;
39
- if (isLocalFf) FFMPEG_PATH = LOCAL_FF;
30
+ if (ytExists) YTDLP_PATH = LOCAL_YT;
31
+ if (ffExists) FFMPEG_PATH = LOCAL_FF;
40
32
 
41
- // Cek Global yt-dlp hanya jika lokal tidak ada
42
- if (!isLocalYt) {
33
+ if (!ytExists) {
43
34
  try {
44
35
  const cmd = isWindows ? 'where yt-dlp' : 'which yt-dlp';
45
36
  const pathFound = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] })
@@ -51,12 +42,11 @@ function checkTools() {
51
42
  ytExists = true;
52
43
  }
53
44
  } catch (e) {
54
- YTDLP_PATH = LOCAL_YT; // Kembalikan ke path lokal jika global pun tidak ada
45
+ YTDLP_PATH = LOCAL_YT;
55
46
  }
56
47
  }
57
48
 
58
- // Cek Global ffmpeg hanya jika lokal tidak ada
59
- if (!isLocalFf) {
49
+ if (!ffExists) {
60
50
  try {
61
51
  const cmd = isWindows ? 'where ffmpeg' : 'which ffmpeg';
62
52
  const globalPath = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] })
@@ -68,173 +58,110 @@ function checkTools() {
68
58
  ffExists = true;
69
59
  }
70
60
  } catch (e) {
71
- FFMPEG_PATH = LOCAL_FF; // Kembalikan ke path lokal jika global pun tidak ada
61
+ FFMPEG_PATH = LOCAL_FF;
72
62
  }
73
63
  }
74
64
 
75
65
  return {
76
66
  ytExists,
77
67
  ffExists,
78
- isLocalYt,
79
- isLocalFf,
68
+ isLocalYt: fs.existsSync(LOCAL_YT),
69
+ isLocalFf: fs.existsSync(LOCAL_FF),
80
70
  allReady: ytExists && ffExists,
81
71
  };
82
72
  }
83
73
 
84
74
  // --- INSTALLERS ---
85
75
  async function installYtdlp() {
86
- if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
87
-
88
76
  printHeader('INSTALL / UPDATE YT-DLP');
89
- console.log(`${C.blue}⏳ Sedang mengunduh engine terbaru...${C.reset}`);
77
+ console.log(`${C.blue}⏳ Downloading latest engine...${C.reset}`);
90
78
  const url = isWindows
91
79
  ? 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
92
80
  : 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
93
81
 
94
82
  try {
95
83
  if (isTermux) {
96
- // Di Termux, lebih disarankan menggunakan python/pip untuk stabilitas
97
- console.log(`${C.dim}Menginstall yt-dlp via python...${C.reset}`);
98
84
  execSync('pkg update && pkg install python ffmpeg -y', {
99
85
  stdio: 'inherit',
100
86
  });
101
87
  execSync('pip install -U "yt-dlp[default]"', { stdio: 'inherit' });
102
- console.log(
103
- `\n${C.green}✅ yt-dlp berhasil diinstal di Termux!${C.reset}`,
104
- );
105
88
  } else if (isWindows) {
106
89
  execSync(
107
90
  `powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${YTDLP_PATH}'"`,
108
91
  { stdio: 'inherit' },
109
92
  );
110
93
  } else {
111
- // Linux (Ubuntu) & macOS
112
94
  execSync(`curl -L -# "${url}" -o "${YTDLP_PATH}"`, { stdio: 'inherit' });
113
95
  execSync(`chmod a+rx "${YTDLP_PATH}"`);
114
96
  }
115
- if (!isTermux) {
116
- console.log(`\n${C.green}✅ yt-dlp berhasil dikonfigurasi!${C.reset}`);
117
- }
97
+ console.log(`\n${C.green}✅ yt-dlp configured successfully!${C.reset}`);
118
98
  } catch (e) {
119
- console.error(
120
- `\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}`,
121
- );
99
+ console.error(`\n${C.red}❌ Download failed.${C.reset}`);
122
100
  }
123
101
  }
124
102
 
125
103
  async function installFfmpeg() {
126
104
  printHeader('INSTALL FFmpeg');
127
- console.log(
128
- `${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n`,
129
- );
130
-
131
105
  try {
132
106
  if (isTermux) {
133
- console.log(
134
- `${C.blue}⏳ Mendeteksi Termux: Menginstall via pkg...${C.reset}`,
135
- );
136
107
  execSync('pkg update && pkg install ffmpeg -y', { stdio: 'inherit' });
137
- console.log(
138
- `\n${C.green}✅ FFmpeg berhasil diinstal di Termux!${C.reset}`,
139
- );
140
108
  } else if (isMac) {
141
- // ... (Kode macOS Anda sudah benar)
142
- console.log(`${C.blue}⏳ Mengunduh FFmpeg untuk macOS...${C.reset}`);
143
109
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
144
110
  execSync(
145
111
  `curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
146
112
  { stdio: 'inherit' },
147
113
  );
148
114
  execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
149
- execSync(`rm "${zipPath}"`);
115
+ if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
150
116
  execSync(`chmod a+rx "${FFMPEG_PATH}"`);
151
- console.log(`\n${C.green}✅ FFmpeg aktif di macOS.${C.reset}`);
152
117
  } else if (isWindows) {
153
- console.log(
154
- `${C.blue}⏳ Mengunduh FFmpeg untuk Windows (Essentials)...${C.reset}`,
155
- );
156
-
157
118
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
158
- // Link direct ke build essentials agar file tidak terlalu besar
159
119
  const url =
160
120
  'https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip';
161
-
162
- // 1. Download menggunakan PowerShell
163
121
  execSync(
164
122
  `powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${zipPath}'"`,
165
123
  { stdio: 'inherit' },
166
124
  );
167
-
168
- console.log(`${C.yellow}📦 Mengekstrak FFmpeg...${C.reset}`);
169
-
170
- // 2. Ekstrak menggunakan perintah 'tar' (Bawaan Windows 10+)
171
- // Kita hanya mengambil ffmpeg.exe dari dalam folder bin di zip tersebut
172
125
  execSync(
173
126
  `tar -xf "${zipPath}" -C "${TOOLS_DIR}" --strip-components 2 "*/bin/ffmpeg.exe" "*/bin/ffprobe.exe"`,
174
127
  { stdio: 'inherit' },
175
128
  );
176
-
177
- // 3. Bersihkan file zip
178
129
  if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
179
-
180
- console.log(
181
- `\n${C.green}✅ FFmpeg berhasil diinstal di Windows!${C.reset}`,
182
- );
183
130
  } else {
184
- // Asumsi Linux (Ubuntu/Debian)
185
- console.log(
186
- `${C.blue}⏳ Mendeteksi Linux: Menginstall via apt...${C.reset}`,
187
- );
188
- console.log(`${C.dim}Mungkin memerlukan password sudo.${C.reset}`);
189
131
  execSync('sudo apt update && sudo apt install ffmpeg -y', {
190
132
  stdio: 'inherit',
191
133
  });
192
- console.log(
193
- `\n${C.green}✅ FFmpeg berhasil diinstal di Linux!${C.reset}`,
194
- );
195
134
  }
135
+ console.log(`\n${C.green}✅ FFmpeg installed successfully!${C.reset}`);
196
136
  } catch (e) {
197
- console.error(
198
- `${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}`,
199
- );
200
- console.log(`${C.dim}Error: ${e.message}${C.reset}`);
137
+ console.error(`${C.red}❌ FFmpeg failed.${C.reset}`);
201
138
  }
202
139
  }
203
140
 
204
141
  function runSpawn(command, args) {
205
142
  return new Promise((resolve) => {
206
143
  const proc = spawn(command, args);
207
- let lastOutput = '';
208
-
209
144
  proc.stdout.on('data', (data) => {
210
145
  const output = data.toString();
211
- // Regex untuk menangkap progress dari yt-dlp
212
146
  const progressMatch = output.match(
213
147
  /\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)/,
214
148
  );
215
-
216
149
  if (progressMatch) {
217
- const [_, percent, speed, eta] = progressMatch;
218
- renderProgressBar(parseFloat(percent), speed, eta);
219
- } else {
220
- // Jika bukan bar, print normal (misal: info merging/ffmpeg)
221
- if (output.trim() && !output.includes('[download]')) {
222
- process.stdout.write(`\n${C.dim}${output.trim()}${C.reset}\n`);
223
- }
150
+ renderProgressBar(
151
+ parseFloat(progressMatch[1]),
152
+ progressMatch[2],
153
+ progressMatch[3],
154
+ );
155
+ } else if (output.trim() && !output.includes('[download]')) {
156
+ process.stdout.write(`\n${C.dim}${output.trim()}${C.reset}\n`);
224
157
  }
225
158
  });
226
-
227
159
  proc.stderr.on('data', (data) => {
228
160
  const err = data.toString();
229
- if (!err.includes('WARNING')) {
161
+ if (!err.includes('WARNING'))
230
162
  process.stdout.write(`\n${C.red}⚠️ ${err}${C.reset}`);
231
- }
232
- });
233
-
234
- proc.on('close', (code) => {
235
- process.stdout.write('\n'); // Baris baru setelah selesai
236
- resolve(code);
237
163
  });
164
+ proc.on('close', resolve);
238
165
  });
239
166
  }
240
167
 
@@ -248,41 +175,25 @@ async function getEstimate(url, format) {
248
175
  timeout: 10000,
249
176
  },
250
177
  ).trim();
251
-
252
178
  if (sizeStr && sizeStr !== 'NA') {
253
179
  const bytes = parseInt(sizeStr);
254
180
  if (isNaN(bytes)) return 'N/A';
255
- if (bytes > 1024 * 1024 * 1024)
256
- return (bytes / 1024 ** 3).toFixed(2) + ' GB';
257
- return (bytes / 1024 ** 2).toFixed(2) + ' MB';
181
+ return bytes > 1024 ** 3
182
+ ? (bytes / 1024 ** 3).toFixed(2) + ' GB'
183
+ : (bytes / 1024 ** 2).toFixed(2) + ' MB';
258
184
  }
259
185
  } catch (e) {}
260
- return 'Estimasi tidak tersedia';
186
+ return 'Estimate unavailable';
261
187
  }
262
188
 
263
- // --- DOWNLOAD ENGINE ---
264
189
  async function startDownload(videoURLFromArgs = null) {
265
- let { ytExists, ffExists } = checkTools();
266
- if (!ytExists) {
267
- console.log(
268
- `\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}`,
269
- );
270
- await backToMenu();
271
- }
190
+ const { ytExists, ffExists } = checkTools();
191
+ if (!ytExists) return console.log(`\n${C.red}❌ Engine not found.${C.reset}`);
272
192
 
273
- if (!ffExists) {
274
- console.log(
275
- `${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}`,
276
- );
277
- const cont = await askQuestion('Lanjutkan saja? (y/n): ');
278
- if (cont.toLowerCase() !== 'y') return mainMenu();
279
- }
193
+ const videoURL = videoURLFromArgs || (await askQuestion('Enter Link: '));
194
+ if (!videoURL) return;
280
195
 
281
- const videoURL =
282
- videoURLFromArgs || (await askQuestion('Masukkan Link (Video/Playlist): '));
283
- if (!videoURL) return mainMenu();
284
-
285
- console.log(`${C.dim}⏳ Menganalisa tautan...${C.reset}`);
196
+ console.log(`${C.dim}⏳ Analyzing link...${C.reset}`);
286
197
  let playlistInfo = { isPlaylist: false, title: '', items: [] };
287
198
 
288
199
  try {
@@ -297,43 +208,27 @@ async function startDownload(videoURLFromArgs = null) {
297
208
  const lines = rawInfo.trim().split('\n');
298
209
  if (lines.length > 1 || videoURL.includes('playlist?list=')) {
299
210
  playlistInfo.isPlaylist = true;
300
- playlistInfo.title = lines[0].split('|')[0] || 'Unduhan Playlist';
211
+ playlistInfo.title = lines[0].split('|')[0] || 'Playlist';
301
212
  playlistInfo.items = lines.map((l) => l.split('|')[1]).filter(Boolean);
302
213
  }
303
- } catch (e) {
304
- console.log(`\n${C.red}❌ Gagal menganalisa tautan.${C.reset}`);
305
- if (e.message.includes('ETIMEDOUT')) {
306
- console.log(
307
- `${C.yellow}⚠️ Waktu analisa habis. Periksa koneksi internet Anda.${C.reset}`,
308
- );
309
- } else {
310
- console.log(
311
- `${C.yellow}⚠️ Pastikan link valid atau tidak diprivat/dihapus.${C.reset}`,
312
- );
313
- }
314
- await backToMenu();
315
- }
214
+ } catch (e) {}
316
215
 
317
- // --- PEMILIHAN PLAYLIST (Tetap Sama) ---
318
216
  let playlistSelection = null;
319
217
  if (playlistInfo.isPlaylist) {
320
- // ... (Logika tampilan playlist sama seperti sebelumnya)
321
218
  console.log(
322
- `\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`,
219
+ `\n${C.bgBlue}${C.bright} 📂 PLAYLIST: ${playlistInfo.title} ${C.reset}`,
323
220
  );
324
- playlistInfo.items.forEach((item, index) => {
221
+ playlistInfo.items.forEach((item, i) =>
325
222
  console.log(
326
- `${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`,
327
- );
328
- });
329
- console.log(
330
- `\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}`,
223
+ `${C.cyan}${(i + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`,
224
+ ),
225
+ );
226
+ const sel = await askQuestion(
227
+ '\nNumbers (ex: 1,3,5-10 or empty for all): ',
331
228
  );
332
- const selectionInput = await askQuestion('\nPilih nomor: ');
333
- if (selectionInput) {
334
- // ... (Logika parsing nomor playlist)
229
+ if (sel) {
335
230
  const selected = new Set();
336
- selectionInput.split(',').forEach((p) => {
231
+ sel.split(',').forEach((p) => {
337
232
  if (p.includes('-')) {
338
233
  const [s, e] = p.split('-').map(Number);
339
234
  for (let i = s; i <= e; i++)
@@ -347,433 +242,165 @@ async function startDownload(videoURLFromArgs = null) {
347
242
  }
348
243
  }
349
244
 
350
- // --- MENU FORMAT UTAMA ---
351
- console.log(`\n${C.bright} [ PILIH FORMAT ]${C.reset}`);
352
- console.log(` ${C.green}1.${C.reset} Video (MP4)`);
353
- console.log(` ${C.green}2.${C.reset} Audio Only (MP3)`);
354
- const mode = await askQuestion('Pilihan: ');
245
+ console.log(
246
+ `\n${C.bright} [ FORMAT ]${C.reset}\n ${C.green}1.${C.reset} Video (MP4)\n ${C.green}2.${C.reset} Audio (MP3)`,
247
+ );
248
+ const mode = await askQuestion('Choice: ');
355
249
 
356
- // --- LOGIKA RESOLUSI OPTIMAL ---
357
250
  let formatArg = '';
358
251
  if (mode === '1') {
359
- console.log(`\n${C.bright} [ PILIH RESOLUSI ]${C.reset}`);
360
- console.log(` ${C.cyan}1.${C.reset} Best Quality (Up to 4K)`);
361
- console.log(` ${C.cyan}2.${C.reset} Tablet Optimal (720p)`);
362
- console.log(` ${C.cyan}3.${C.reset} Mobile Optimal (480p)`);
363
- const resChoice = await askQuestion('Pilihan Resolusi (1-3): ');
364
-
365
- if (resChoice === '2') {
366
- formatArg =
367
- 'bestvideo[height<=720][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=720]';
368
- } else if (resChoice === '3') {
252
+ console.log(
253
+ `\n${C.bright} [ RESOLUTION ]${C.reset}\n 1. Best\n 2. 720p\n 3. 480p`,
254
+ );
255
+ const res = await askQuestion('Select: ');
256
+ if (res === '2')
369
257
  formatArg =
370
- 'bestvideo[height<=480][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=480]';
371
- } else {
258
+ 'bestvideo[height<=720][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=720]/best';
259
+ else if (res === '3')
372
260
  formatArg =
373
- 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best';
261
+ 'bestvideo[height<=480][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=480]/best';
262
+ else
263
+ formatArg = 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best/best';
264
+
265
+ if (!playlistInfo.isPlaylist) {
266
+ const size = await getEstimate(videoURL, formatArg);
267
+ console.log(`${C.yellow}📊 Estimated Size: ${size}${C.reset}`);
268
+ if ((await askQuestion('Proceed? (y/n): ')).toLowerCase() !== 'y') return;
374
269
  }
375
270
  }
376
- // --- TAMPILKAN ESTIMASI ---
377
- if (!playlistInfo.isPlaylist && mode === '1') {
378
- console.log(`${C.dim}⏳ Menghitung estimasi ukuran file...${C.reset}`);
379
- const size = await getEstimate(videoURL, formatArg);
380
- console.log(`${C.yellow}📊 Estimasi Ukuran: ${C.bright}${size}${C.reset}`);
381
-
382
- const confirm = await askQuestion('Lanjutkan unduhan? (Y/n): ');
383
- if (confirm.toLowerCase() === 'n') return mainMenu();
384
- }
385
271
 
386
- const baseDir = path.join(os.homedir(), 'Downloads', 'media-dl');
387
- const subFolder = mode === '2' ? 'audio' : 'video';
388
- let outputDir = path.join(baseDir, subFolder);
272
+ const sub = mode === '2' ? 'audio' : 'video';
273
+ let output = path.join(os.homedir(), 'Downloads', 'media-dl', sub);
389
274
  if (playlistInfo.isPlaylist)
390
- outputDir = path.join(
391
- outputDir,
275
+ output = path.join(
276
+ output,
392
277
  playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_'),
393
278
  );
394
- if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
279
+ if (!fs.existsSync(output)) fs.mkdirSync(output, { recursive: true });
395
280
 
396
281
  let args = [
397
282
  '--ffmpeg-location',
398
283
  FFMPEG_PATH,
399
284
  '-o',
400
- `${outputDir}/%(title).100s.%(ext)s`,
285
+ `${output}/%(title).100s.%(ext)s`,
286
+ '--no-mtime',
401
287
  videoURL,
402
288
  ];
403
-
404
- // Integrasi Safe Mode (cite: cli.js)
405
- if (safeMode) {
289
+ if (safeMode)
406
290
  args.push(
407
291
  '--rate-limit',
408
292
  '5M',
409
293
  '--sleep-interval',
410
294
  '3',
411
- '--max-sleep-interval',
412
- '10',
413
295
  '--user-agent',
414
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
296
+ 'Mozilla/5.0...',
415
297
  );
416
- }
417
-
418
298
  if (playlistSelection) args.push('--playlist-items', playlistSelection);
419
- else if (!playlistInfo.isPlaylist) args.push('--no-playlist');
420
299
 
421
- if (mode === '2') {
422
- if (!ffExists) {
423
- console.log(
424
- `${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}`,
425
- );
426
- return mainMenu();
427
- }
428
- args.unshift('-x', '--audio-format', 'mp3');
429
- } else {
300
+ if (mode === '2') args.unshift('-x', '--audio-format', 'mp3');
301
+ else {
430
302
  args.unshift('-f', formatArg);
431
303
  if (ffExists) args.unshift('--recode-video', 'mp4');
432
304
  }
433
305
 
434
- args.push('--no-mtime');
435
- console.log(`\n${C.bgBlue}${C.bright} 🚀 MEMULAI PROSES... ${C.reset}\n`);
436
-
306
+ console.log(`\n${C.bgBlue}${C.bright} 🚀 STARTING... ${C.reset}\n`);
437
307
  const code = await runSpawn(YTDLP_PATH, args);
438
308
  if (code === 0) {
439
- console.log(`\n${C.green}✨ SELESAI! Cek folder: ${outputDir}${C.reset}`);
309
+ console.log(`\n${C.green}✨ DONE! Folder: ${output}${C.reset}`);
440
310
  try {
441
311
  execSync(
442
312
  isWindows
443
- ? `explorer "${outputDir}"`
313
+ ? `explorer "${output}"`
444
314
  : isMac
445
- ? `open "${outputDir}"`
446
- : `xdg-open "${outputDir}"`,
315
+ ? `open "${output}"`
316
+ : `xdg-open "${output}"`,
447
317
  );
448
318
  } catch (e) {}
449
- } else {
450
- console.log(`\n${C.red}❌ Terjadi kesalahan saat mengunduh.${C.reset}`);
451
- }
452
- await backToMenu();
453
- }
454
-
455
- async function backToMenu() {
456
- try {
457
- await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
458
- mainMenu(); // Kembali ke menu
459
- } catch (err) {
460
- // Tangani jika readline sudah tertutup (Ctrl+C ditekan sebelumnya)
461
- if (err.code === 'ERR_USE_AFTER_CLOSE') {
462
- console.log(`\n${C.dim}Program dihentikan oleh pengguna.${C.reset}`);
463
- process.exit(0);
464
- } else {
465
- // Jika error lain, lempar kembali agar bisa dideteksi (opsional)
466
- throw err;
467
- }
468
319
  }
320
+ await askQuestion('\nPress Enter to continue...');
469
321
  }
470
322
 
471
- async function showSupport() {
472
- // Menggunakan 2 parameter: Judul dan Summary
473
- printHeader('TENTANG APLIKASI', 'Media-DL Manager Pro v2.0.0 - 2026');
474
-
475
- // --- SEKSI FITUR ---
476
- console.log(` ${C.bright}${C.cyan}OVERVIEW${C.reset}`);
477
- console.log(` Terima kasih telah memilih MEDIA-DL. Skrip ini dirancang`);
478
- console.log(` untuk memudahkan manajemen unduhan media secara lokal.\n`);
479
-
480
- console.log(` ${C.bright}${C.cyan}FITUR UNGGULAN${C.reset}`);
481
- const features = [
482
- {
483
- icon: '✦',
484
- title: 'High Quality',
485
- desc: 'Mendukung hingga 4K & Audio 320kbps',
486
- },
487
- {
488
- icon: '✦',
489
- title: 'Multi-Source',
490
- desc: 'YouTube, TikTok, IG Reels, & Shorts',
491
- },
492
- {
493
- icon: '✦',
494
- title: 'Batch Mode',
495
- desc: 'Mendukung unduhan Playlist secara massal',
496
- },
497
- {
498
- icon: '✦',
499
- title: 'Safe Guard',
500
- desc: 'Mode proteksi agar akun/IP tidak terblokir',
501
- },
502
- ];
503
-
504
- features.forEach((f) => {
323
+ async function systemMaintenance() {
324
+ while (true) {
325
+ const { ytExists, ffExists } = checkTools();
326
+ printHeader('MAINTENANCE', 'Update or Reset');
505
327
  console.log(
506
- ` ${C.green}${f.icon}${C.reset} ${C.white}${f.title.padEnd(15)}${
507
- C.reset
508
- } ${C.dim}• ${f.desc}${C.reset}`,
328
+ ` • Engine: ${ytExists ? C.green + 'Ready' : C.red + 'Missing'}${C.reset}`,
509
329
  );
510
- });
511
-
512
- console.log('\n' + '─'.repeat(52));
513
-
514
- // --- SEKSI DUKUNGAN ---
515
- console.log(`\n ${C.bright}${C.magenta}DUKUNGAN & DONASI${C.reset}`);
516
- console.log(` Dukungan Anda sangat membantu pengembang untuk terus`);
517
- console.log(` memperbarui engine dan fitur aplikasi ini.\n`);
518
-
519
- // Menampilkan Link dengan label background agar menonjol
520
- const links = [
521
- { label: ' ☕ BELI KOPI ', url: 'https://app.midtrans.com/coffee' },
522
- { label: ' 🍕 BELI PIZZA', url: 'https://app.midtrans.com/pizza' },
523
- ];
524
-
525
- links.forEach((l) => {
526
330
  console.log(
527
- ` ${C.bgBlue}${C.white}${l.label}${C.reset} ${C.blue}➜${C.reset} ${C.dim}${l.url}${C.reset}`,
331
+ ` • FFmpeg: ${ffExists ? C.green + 'Ready' : C.red + 'Missing'}${C.reset}\n`,
528
332
  );
529
- });
530
-
531
- console.log(`\n${C.cyan}${''.repeat(52)}${C.reset}`);
532
-
533
- await backToMenu();
333
+ console.log(` 1. Update Engines\n 2. Reset System\n 0. Back`);
334
+ const c = await askQuestion('\nSelect: ');
335
+ if (c === '1') {
336
+ await installYtdlp();
337
+ await installFfmpeg();
338
+ await askQuestion('\nDone. Enter...');
339
+ } else if (c === '2') {
340
+ if ((await askQuestion('Delete all? (y/n): ')).toLowerCase() === 'y') {
341
+ fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
342
+ process.exit(0);
343
+ }
344
+ } else break;
345
+ }
534
346
  }
535
347
 
536
- async function mainMenu() {
537
- const status = checkTools();
538
- const { ytExists, ffExists } = status;
539
-
540
- // Menggunakan 2 parameter: Judul dan Summary status singkat
541
- printHeader('MEDIA-DL PRO 2026', 'Pusat Kendali Unduhan Media Lokal');
542
-
543
- // --- SEKSI DASHBOARD (INFO SISTEM) ---
544
- const ytLabel = status.isLocalYt
545
- ? `${C.green}Ready (Internal)${C.reset}`
546
- : status.ytExists
547
- ? `${C.cyan}Ready (System)${C.reset}`
548
- : `${C.red}Not Found${C.reset}`;
549
-
550
- const ffLabel = status.isLocalFf
551
- ? `${C.green}Ready (Internal)${C.reset}`
552
- : status.ffExists
553
- ? `${C.cyan}Ready (System)${C.reset}`
554
- : `${C.yellow}Missing${C.reset}`;
555
-
556
- const safeBadge = safeMode
557
- ? `${C.bgBlue}${C.white} ON ${C.reset}`
558
- : `${C.bgRed}${C.white} OFF ${C.reset}`;
559
-
560
- console.log(` ${C.bright}SYSTEM STATUS${C.reset}`);
561
- console.log(` 🤖 Engine : [ ${ytLabel} ] | 🎬 FFmpeg : [ ${ffLabel} ]`);
562
- console.log(` 🛡️ Safe Mode Guard : ${safeBadge}\n`);
563
-
564
- console.log(` ${C.cyan}━${'━'.repeat(48)}${C.reset}`);
565
-
566
- // --- SEKSI NAVIGASI ---
567
- console.log(` ${C.bright}MAIN SERVICES${C.reset}`);
568
- console.log(
569
- ` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}`,
570
- );
571
- console.log(
572
- ` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Sekarang: ${
573
- safeMode ? 'Aktif' : 'Nonaktif'
574
- })${C.reset}`,
575
- );
576
-
577
- console.log(`\n ${C.bright}SYSTEM & INFO${C.reset}`);
348
+ async function showSupport() {
349
+ printHeader('ABOUT', 'Media-DL Pro v2.5.2');
578
350
  console.log(
579
- ` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}`,
351
+ ` ${C.cyan}KEY FEATURES:${C.reset}\n High Quality 4K & MP3\n ✦ Multi-Source (YT, IG, FB, TikTok)\n ✦ Playlist Batch Mode\n ✦ Safe Guard Mode\n`,
580
352
  );
581
353
  console.log(
582
- ` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}`,
354
+ ` ${C.magenta}SUPPORT DEVELOPER:${C.reset}\n PayPal: https://paypal.me/Ariska138\n`,
583
355
  );
584
- console.log(` ${C.cyan}0.${C.reset} 🚪 Keluar`);
585
-
586
- console.log(` ${C.cyan}━${'━'.repeat(48)}${C.reset}`);
587
-
588
- const choice = await askQuestion('\nPilih menu (0-4): ');
589
-
590
- switch (choice) {
591
- case '1':
592
- await startDownload();
593
- break;
594
- case '2':
595
- safeMode = !safeMode;
596
- // Berikan feedback visual singkat sebelum refresh menu
597
- console.log(
598
- `\n${C.yellow} 🛡️ Safe Mode telah ${
599
- safeMode ? 'DIAKTIFKAN' : 'DINONAKTIFKAN'
600
- }${C.reset}`,
601
- );
602
- setTimeout(() => mainMenu(), 800);
603
- break;
604
- case '3':
605
- await systemMaintenance();
606
- break;
607
- case '4':
608
- await showSupport();
609
- break;
610
- case '0':
611
- console.log(`\n${C.cyan}━${'━'.repeat(48)}${C.reset}`);
612
- console.log(
613
- ` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}`,
614
- );
615
- console.log(
616
- ` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`,
617
- );
618
- console.log(`${C.cyan}━${'━'.repeat(48)}${C.reset}\n`);
619
-
620
- // Memberikan jeda sebentar sebelum benar-benar menutup terminal
621
- setTimeout(() => {
622
- rl.close();
623
- process.exit(0);
624
- }, 1000);
625
-
626
- break;
627
- default:
628
- // Jika salah input, tampilkan kembali menu
629
- mainMenu();
630
- break;
631
- }
632
- }
633
-
634
- async function cleanUp() {
635
- if (fs.existsSync(TOOLS_DIR)) {
636
- fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
637
- }
638
-
639
- // RESET PATH ke default lokal agar checkTools tidak "tersesat" menggunakan path lama
640
- YTDLP_PATH = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
641
- FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
356
+ await askQuestion('Press Enter...');
642
357
  }
643
358
 
644
- async function firstTimeSetup() {
359
+ async function mainMenu() {
645
360
  while (true) {
646
- const { ytExists, ffExists } = checkTools();
647
- printHeader(
648
- 'FIRST-TIME SETUP',
649
- 'Komponen diperlukan untuk menjalankan aplikasi',
650
- );
651
-
652
- console.log(`${C.white}Status Instalasi:${C.reset}`);
653
- console.log(
654
- ` [${ytExists ? C.green + '✓' : C.red + '✗'}${
655
- C.reset
656
- }] Engine yt-dlp (Wajib)`,
657
- );
361
+ const status = checkTools();
362
+ printHeader('MEDIA-DL PRO 2026', 'Stable Control Center');
363
+ const ytL = status.ytExists
364
+ ? C.green + 'Ready' + C.reset
365
+ : C.red + 'Missing' + C.reset;
366
+ const ffL = status.ffExists
367
+ ? C.green + 'Ready' + C.reset
368
+ : C.yellow + 'Missing' + C.reset;
369
+ console.log(` 🤖 Engine: [ ${ytL} ] | 🎬 FFmpeg: [ ${ffL} ]`);
658
370
  console.log(
659
- ` [${ffExists ? C.green + '' : C.red + ''}${
660
- C.reset
661
- }] FFmpeg (Direkomendasikan)`,
371
+ ` 🛡️ Safe Mode: ${safeMode ? C.bgBlue + ' ON ' + C.reset : C.bgRed + ' OFF ' + C.reset}\n`,
662
372
  );
663
-
664
373
  console.log(
665
- `\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`,
374
+ ` 1. 📥 Download Media\n 2. 🛡️ Toggle Safe Mode\n 3. ⚙️ Maintenance\n 4. ❤️ About\n 0. 🚪 Exit`,
666
375
  );
667
- console.log(` ${C.cyan}1.${C.reset} Install Semua Komponen Otomatis`);
668
- console.log(` ${C.cyan}0.${C.reset} Keluar dari Aplikasi`);
669
-
670
- const choice = await askQuestion('\nPilih: ');
671
-
672
- if (choice === '1') {
673
- if (!ytExists) await installYtdlp();
674
- if (!ffExists) await installFfmpeg();
675
-
676
- const status = checkTools();
677
- if (status.ytExists) {
678
- console.log(
679
- `\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`,
680
- );
681
- await new Promise((r) => setTimeout(r, 1500));
682
- return mainMenu(); // Berhasil, lanjut ke menu utama
683
- }
684
- } else if (choice === '0') {
685
- console.log('Menutup aplikasi...');
686
- process.exit(0);
687
- }
688
- }
689
- }
690
-
691
- async function systemMaintenance() {
692
- let inMaintenance = true;
693
-
694
- while (inMaintenance) {
695
- const { ytExists, ffExists } = checkTools();
696
- printHeader(
697
- 'SYSTEM MAINTENANCE',
698
- 'Update engine atau bersihkan file sistem',
699
- );
700
-
701
- console.log(`${C.white}Versi Terinstal:${C.reset}`);
702
- console.log(
703
- ` • yt-dlp : ${ytExists ? C.green + 'Ready' : C.red + 'Not Found'}${
704
- C.reset
705
- }`,
706
- );
707
- console.log(
708
- ` • FFmpeg : ${ffExists ? C.green + 'Ready' : C.red + 'Not Found'}${
709
- C.reset
710
- }`,
711
- );
712
-
713
- console.log(`\n${C.bright}Opsi Pemeliharaan:${C.reset}`);
714
- console.log(` ${C.cyan}1.${C.reset} Update / Reinstall Engines`);
715
- console.log(` ${C.cyan}2.${C.reset} 🗑️ Hapus Semua Tools (Reset System)`);
716
- console.log(` ${C.cyan}3.${C.reset} ⬅️ Kembali ke Menu Utama`);
717
-
718
- const choice = await askQuestion('\nPilih tindakan: ');
719
-
720
- switch (choice) {
721
- case '1':
722
- await installYtdlp();
723
- await installFfmpeg();
724
- await askQuestion('\nUpdate selesai. Tekan Enter...');
725
- break;
726
-
727
- case '2':
728
- const confirm = await askQuestion(
729
- `${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n): `,
730
- );
731
- if (confirm.toLowerCase() === 'y') {
732
- await cleanUp(); // Panggil fungsi penghapusan folder
733
- console.log(`${C.yellow}Folder .media-dl telah dihapus.${C.reset}`);
734
-
735
- // Cek ulang status setelah hapus
736
- const finalCheck = checkTools();
737
- if (finalCheck.isLocalYt || finalCheck.isLocalFf) {
738
- console.log(
739
- `${C.red}Gagal menghapus beberapa file. Pastikan tidak ada proses yang mengunci file.${C.reset}`,
740
- );
741
- } else {
742
- console.log(`${C.green}Reset lokal berhasil.${C.reset}`);
743
- }
744
- await askQuestion('Tekan Enter...');
745
- return bootstrap(); // Kembali ke pengecekan awal
746
- }
747
- break;
748
-
749
- case '3':
750
- inMaintenance = false;
751
- return mainMenu();
752
-
753
- default:
754
- break;
755
- }
376
+ const choice = await askQuestion('\nChoice: ');
377
+ if (choice === '1') await startDownload();
378
+ else if (choice === '2') {
379
+ safeMode = !safeMode;
380
+ console.log('\nSafe Mode Toggled.');
381
+ await new Promise((r) => setTimeout(r, 600));
382
+ } else if (choice === '3') await systemMaintenance();
383
+ else if (choice === '4') await showSupport();
384
+ else if (choice === '0') process.exit(0);
756
385
  }
757
386
  }
758
387
 
759
- // --- ENTRY POINT ---
760
388
  async function bootstrap() {
761
389
  const status = checkTools();
762
- if (!status.allReady) {
763
- await firstTimeSetup();
764
- } else {
765
- // process.argv[2] mengambil argumen pertama setelah nama perintah
766
- const urlArgument = process.argv[2];
767
-
768
- if (urlArgument) {
769
- // Jika ada URL di terminal, langsung jalankan download
770
- await startDownload(urlArgument);
771
- } else {
772
- // Jika tidak ada, masuk ke menu utama seperti biasa [cite: 113]
773
- mainMenu();
774
- }
390
+ if (!status.ytExists) {
391
+ printHeader('FIRST-TIME SETUP');
392
+ if (
393
+ (
394
+ await askQuestion('Install required components? (y/n): ')
395
+ ).toLowerCase() === 'y'
396
+ ) {
397
+ await installYtdlp();
398
+ await installFfmpeg();
399
+ } else process.exit(0);
775
400
  }
401
+ const urlArg = process.argv[2];
402
+ if (urlArg) await startDownload(urlArg);
403
+ else mainMenu();
776
404
  }
777
405
 
778
406
  bootstrap();
779
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "2.5.2",
3
+ "version": "2.7.0",
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)