media-dl 2.2.0 → 2.3.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.
- package/bin/cli.js +165 -59
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -9,11 +9,14 @@ const { C, printHeader, renderProgressBar, askQuestion, rl } = require('./ui');
|
|
|
9
9
|
// --- KONFIGURASI VISUAL (ANSI COLORS) ---
|
|
10
10
|
|
|
11
11
|
const TOOLS_DIR = path.join(os.homedir(), '.media-dl');
|
|
12
|
+
|
|
12
13
|
const isWindows = process.platform === 'win32';
|
|
13
14
|
const isMac = process.platform === 'darwin';
|
|
15
|
+
const isTermux =
|
|
16
|
+
process.env.PREFIX && process.env.PREFIX.includes('com.termux');
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
let YTDLP_PATH = path.join(TOOLS_DIR, isWindows ? 'yt-dlp.exe' : 'yt-dlp');
|
|
19
|
+
let FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
|
|
17
20
|
|
|
18
21
|
// State Aplikasi
|
|
19
22
|
let safeMode = true;
|
|
@@ -21,10 +24,40 @@ let safeMode = true;
|
|
|
21
24
|
if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
|
|
22
25
|
|
|
23
26
|
function checkTools() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
let ytExists = fs.existsSync(YTDLP_PATH);
|
|
28
|
+
let ffExists = fs.existsSync(FFMPEG_PATH);
|
|
29
|
+
|
|
30
|
+
// Cek Global yt-dlp
|
|
31
|
+
if (!ytExists) {
|
|
32
|
+
try {
|
|
33
|
+
const cmd = isWindows ? 'where yt-dlp' : 'which yt-dlp';
|
|
34
|
+
const pathFound = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
35
|
+
.toString()
|
|
36
|
+
.trim()
|
|
37
|
+
.split('\n')[0];
|
|
38
|
+
if (pathFound) {
|
|
39
|
+
YTDLP_PATH = pathFound; // UPDATE PATH KE GLOBAL
|
|
40
|
+
ytExists = true;
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Cek Global ffmpeg
|
|
46
|
+
if (!ffExists) {
|
|
47
|
+
try {
|
|
48
|
+
const cmd = isWindows ? 'where ffmpeg' : 'which ffmpeg';
|
|
49
|
+
const globalPath = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
50
|
+
.toString()
|
|
51
|
+
.trim()
|
|
52
|
+
.split('\n')[0];
|
|
53
|
+
if (globalPath) {
|
|
54
|
+
FFMPEG_PATH = globalPath; // UPDATE PATH KE GLOBAL
|
|
55
|
+
ffExists = true;
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { ytExists, ffExists, allReady: ytExists && ffExists };
|
|
28
61
|
}
|
|
29
62
|
|
|
30
63
|
// --- INSTALLERS ---
|
|
@@ -36,20 +69,34 @@ async function installYtdlp() {
|
|
|
36
69
|
const url = isWindows
|
|
37
70
|
? 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
|
|
38
71
|
: 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
|
|
72
|
+
|
|
39
73
|
try {
|
|
40
|
-
if (
|
|
74
|
+
if (isTermux) {
|
|
75
|
+
// Di Termux, lebih disarankan menggunakan python/pip untuk stabilitas
|
|
76
|
+
console.log(`${C.dim}Menginstall yt-dlp via python...${C.reset}`);
|
|
77
|
+
execSync('pkg update && pkg install python ffmpeg -y', {
|
|
78
|
+
stdio: 'inherit',
|
|
79
|
+
});
|
|
80
|
+
execSync('pip install -U "yt-dlp[default]"', { stdio: 'inherit' });
|
|
81
|
+
console.log(
|
|
82
|
+
`\n${C.green}✅ yt-dlp berhasil diinstal di Termux!${C.reset}`,
|
|
83
|
+
);
|
|
84
|
+
} else if (isWindows) {
|
|
41
85
|
execSync(
|
|
42
86
|
`powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${YTDLP_PATH}'"`,
|
|
43
|
-
{ stdio: 'inherit' }
|
|
87
|
+
{ stdio: 'inherit' },
|
|
44
88
|
);
|
|
45
89
|
} else {
|
|
90
|
+
// Linux (Ubuntu) & macOS
|
|
46
91
|
execSync(`curl -L -# "${url}" -o "${YTDLP_PATH}"`, { stdio: 'inherit' });
|
|
47
92
|
execSync(`chmod a+rx "${YTDLP_PATH}"`);
|
|
48
93
|
}
|
|
49
|
-
|
|
94
|
+
if (!isTermux) {
|
|
95
|
+
console.log(`\n${C.green}✅ yt-dlp berhasil dikonfigurasi!${C.reset}`);
|
|
96
|
+
}
|
|
50
97
|
} catch (e) {
|
|
51
98
|
console.error(
|
|
52
|
-
`\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}
|
|
99
|
+
`\n${C.red}❌ Gagal mengunduh. Periksa koneksi internet Anda.${C.reset}`,
|
|
53
100
|
);
|
|
54
101
|
}
|
|
55
102
|
}
|
|
@@ -57,16 +104,25 @@ async function installYtdlp() {
|
|
|
57
104
|
async function installFfmpeg() {
|
|
58
105
|
printHeader('INSTALL FFmpeg');
|
|
59
106
|
console.log(
|
|
60
|
-
`${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n
|
|
107
|
+
`${C.dim}FFmpeg diperlukan untuk kualitas 1080p+ dan konversi MP3.${C.reset}\n`,
|
|
61
108
|
);
|
|
62
109
|
|
|
63
110
|
try {
|
|
64
|
-
if (
|
|
111
|
+
if (isTermux) {
|
|
112
|
+
console.log(
|
|
113
|
+
`${C.blue}⏳ Mendeteksi Termux: Menginstall via pkg...${C.reset}`,
|
|
114
|
+
);
|
|
115
|
+
execSync('pkg update && pkg install ffmpeg -y', { stdio: 'inherit' });
|
|
116
|
+
console.log(
|
|
117
|
+
`\n${C.green}✅ FFmpeg berhasil diinstal di Termux!${C.reset}`,
|
|
118
|
+
);
|
|
119
|
+
} else if (isMac) {
|
|
120
|
+
// ... (Kode macOS Anda sudah benar)
|
|
65
121
|
console.log(`${C.blue}⏳ Mengunduh FFmpeg untuk macOS...${C.reset}`);
|
|
66
122
|
const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
|
|
67
123
|
execSync(
|
|
68
124
|
`curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
|
|
69
|
-
{ stdio: 'inherit' }
|
|
125
|
+
{ stdio: 'inherit' },
|
|
70
126
|
);
|
|
71
127
|
execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
|
|
72
128
|
execSync(`rm "${zipPath}"`);
|
|
@@ -74,14 +130,53 @@ async function installFfmpeg() {
|
|
|
74
130
|
console.log(`\n${C.green}✅ FFmpeg aktif di macOS.${C.reset}`);
|
|
75
131
|
} else if (isWindows) {
|
|
76
132
|
console.log(
|
|
77
|
-
`${C.
|
|
133
|
+
`${C.blue}⏳ Mengunduh FFmpeg untuk Windows (Essentials)...${C.reset}`,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
|
|
137
|
+
// Link direct ke build essentials agar file tidak terlalu besar
|
|
138
|
+
const url =
|
|
139
|
+
'https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip';
|
|
140
|
+
|
|
141
|
+
// 1. Download menggunakan PowerShell
|
|
142
|
+
execSync(
|
|
143
|
+
`powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${zipPath}'"`,
|
|
144
|
+
{ stdio: 'inherit' },
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
console.log(`${C.yellow}📦 Mengekstrak FFmpeg...${C.reset}`);
|
|
148
|
+
|
|
149
|
+
// 2. Ekstrak menggunakan perintah 'tar' (Bawaan Windows 10+)
|
|
150
|
+
// Kita hanya mengambil ffmpeg.exe dari dalam folder bin di zip tersebut
|
|
151
|
+
execSync(
|
|
152
|
+
`tar -xf "${zipPath}" -C "${TOOLS_DIR}" --strip-components 2 "*/bin/ffmpeg.exe" "*/bin/ffprobe.exe"`,
|
|
153
|
+
{ stdio: 'inherit' },
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// 3. Bersihkan file zip
|
|
157
|
+
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
|
|
158
|
+
|
|
159
|
+
console.log(
|
|
160
|
+
`\n${C.green}✅ FFmpeg berhasil diinstal di Windows!${C.reset}`,
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
// Asumsi Linux (Ubuntu/Debian)
|
|
164
|
+
console.log(
|
|
165
|
+
`${C.blue}⏳ Mendeteksi Linux: Menginstall via apt...${C.reset}`,
|
|
166
|
+
);
|
|
167
|
+
console.log(`${C.dim}Mungkin memerlukan password sudo.${C.reset}`);
|
|
168
|
+
execSync('sudo apt update && sudo apt install ffmpeg -y', {
|
|
169
|
+
stdio: 'inherit',
|
|
170
|
+
});
|
|
171
|
+
console.log(
|
|
172
|
+
`\n${C.green}✅ FFmpeg berhasil diinstal di Linux!${C.reset}`,
|
|
78
173
|
);
|
|
79
|
-
console.log(`dan letakkan di: ${C.white}${TOOLS_DIR}${C.reset}`);
|
|
80
174
|
}
|
|
81
175
|
} catch (e) {
|
|
82
176
|
console.error(
|
|
83
|
-
`${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}
|
|
177
|
+
`${C.red}❌ Gagal menginstal FFmpeg secara otomatis.${C.reset}`,
|
|
84
178
|
);
|
|
179
|
+
console.log(`${C.dim}Error: ${e.message}${C.reset}`);
|
|
85
180
|
}
|
|
86
181
|
}
|
|
87
182
|
|
|
@@ -94,7 +189,7 @@ function runSpawn(command, args) {
|
|
|
94
189
|
const output = data.toString();
|
|
95
190
|
// Regex untuk menangkap progress dari yt-dlp
|
|
96
191
|
const progressMatch = output.match(
|
|
97
|
-
/\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)
|
|
192
|
+
/\[download\]\s+(\d+\.\d+)%\s+of\s+.*\s+at\s+([\d\w\./s]+)\s+ETA\s+([\d:]+)/,
|
|
98
193
|
);
|
|
99
194
|
|
|
100
195
|
if (progressMatch) {
|
|
@@ -127,7 +222,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
127
222
|
let { ytExists, ffExists } = checkTools();
|
|
128
223
|
if (!ytExists) {
|
|
129
224
|
console.log(
|
|
130
|
-
`\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}
|
|
225
|
+
`\n${C.red}❌ Engine yt-dlp tidak ditemukan. Silakan pilih menu Update/Install.${C.reset}`,
|
|
131
226
|
);
|
|
132
227
|
await askQuestion('Tekan Enter...');
|
|
133
228
|
return mainMenu();
|
|
@@ -135,7 +230,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
135
230
|
|
|
136
231
|
if (!ffExists) {
|
|
137
232
|
console.log(
|
|
138
|
-
`${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}
|
|
233
|
+
`${C.yellow}⚠️ Peringatan: FFmpeg tidak ditemukan. Video mungkin tidak tergabung dengan audio.${C.reset}`,
|
|
139
234
|
);
|
|
140
235
|
const cont = await askQuestion('Lanjutkan saja? (y/n): ');
|
|
141
236
|
if (cont.toLowerCase() !== 'y') return mainMenu();
|
|
@@ -150,8 +245,12 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
150
245
|
|
|
151
246
|
try {
|
|
152
247
|
const rawInfo = execSync(
|
|
153
|
-
`"${YTDLP_PATH}" --flat-playlist --print "%(playlist_title)s|%(title)s" "${videoURL}"`,
|
|
154
|
-
{
|
|
248
|
+
`"${YTDLP_PATH}" --flat-playlist --no-warnings --print "%(playlist_title)s|%(title)s" "${videoURL}"`,
|
|
249
|
+
{
|
|
250
|
+
encoding: 'utf-8',
|
|
251
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
252
|
+
timeout: 20000,
|
|
253
|
+
},
|
|
155
254
|
);
|
|
156
255
|
const lines = rawInfo.trim().split('\n');
|
|
157
256
|
if (lines.length > 1 || videoURL.includes('playlist?list=')) {
|
|
@@ -159,20 +258,35 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
159
258
|
playlistInfo.title = lines[0].split('|')[0] || 'Unduhan Playlist';
|
|
160
259
|
playlistInfo.items = lines.map((l) => l.split('|')[1]).filter(Boolean);
|
|
161
260
|
}
|
|
162
|
-
} catch (e) {
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.log(`\n${C.red}❌ Gagal menganalisa tautan.${C.reset}`);
|
|
263
|
+
|
|
264
|
+
if (e.message.includes('ETIMEDOUT')) {
|
|
265
|
+
console.log(
|
|
266
|
+
`${C.yellow}⚠️ Waktu analisa habis. Periksa koneksi internet Anda.${C.reset}`,
|
|
267
|
+
);
|
|
268
|
+
} else {
|
|
269
|
+
console.log(
|
|
270
|
+
`${C.yellow}⚠️ Pastikan link valid atau tidak diprivat/dihapus.${C.reset}`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
await askQuestion('\nTekan Enter untuk kembali ke menu...');
|
|
275
|
+
return mainMenu(); // Keluar dari fungsi dan balik ke menu utama
|
|
276
|
+
}
|
|
163
277
|
|
|
164
278
|
let playlistSelection = null;
|
|
165
279
|
if (playlistInfo.isPlaylist) {
|
|
166
280
|
console.log(
|
|
167
|
-
`\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}
|
|
281
|
+
`\n${C.bgBlue}${C.bright} 📂 PLAYLIST TERDETEKSI: ${playlistInfo.title} ${C.reset}`,
|
|
168
282
|
);
|
|
169
283
|
playlistInfo.items.forEach((item, index) => {
|
|
170
284
|
console.log(
|
|
171
|
-
`${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}
|
|
285
|
+
`${C.cyan}${(index + 1).toString().padStart(3, ' ')}.${C.reset} ${item}`,
|
|
172
286
|
);
|
|
173
287
|
});
|
|
174
288
|
console.log(
|
|
175
|
-
`\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}
|
|
289
|
+
`\n${C.dim}Contoh pilih nomor: 1,3,5-10 atau biarkan kosong untuk semua.${C.reset}`,
|
|
176
290
|
);
|
|
177
291
|
const selectionInput = await askQuestion('Pilih nomor: ');
|
|
178
292
|
|
|
@@ -198,7 +312,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
198
312
|
console.log(
|
|
199
313
|
` ${C.green}2.${C.reset} Audio Only (MP3) ${
|
|
200
314
|
ffExists ? C.green + '✅' : C.red + '❌ (Butuh FFmpeg)'
|
|
201
|
-
}
|
|
315
|
+
}`,
|
|
202
316
|
);
|
|
203
317
|
const mode = await askQuestion('Pilihan: ');
|
|
204
318
|
|
|
@@ -208,7 +322,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
208
322
|
if (playlistInfo.isPlaylist)
|
|
209
323
|
outputDir = path.join(
|
|
210
324
|
outputDir,
|
|
211
|
-
playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_')
|
|
325
|
+
playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_'),
|
|
212
326
|
);
|
|
213
327
|
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
214
328
|
|
|
@@ -230,7 +344,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
230
344
|
'--max-sleep-interval',
|
|
231
345
|
'10',
|
|
232
346
|
'--user-agent',
|
|
233
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
|
347
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
|
|
234
348
|
);
|
|
235
349
|
}
|
|
236
350
|
|
|
@@ -240,7 +354,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
240
354
|
if (mode === '2') {
|
|
241
355
|
if (!ffExists) {
|
|
242
356
|
console.log(
|
|
243
|
-
`${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}
|
|
357
|
+
`${C.red}❌ Error: Anda wajib menginstal FFmpeg untuk mengunduh audio.${C.reset}`,
|
|
244
358
|
);
|
|
245
359
|
await askQuestion('Tekan Enter untuk kembali ke Menu Utama...');
|
|
246
360
|
mainMenu();
|
|
@@ -249,7 +363,7 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
249
363
|
} else {
|
|
250
364
|
args.unshift(
|
|
251
365
|
'-f',
|
|
252
|
-
'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best'
|
|
366
|
+
'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best',
|
|
253
367
|
);
|
|
254
368
|
if (ffExists) args.unshift('--recode-video', 'mp4');
|
|
255
369
|
}
|
|
@@ -268,8 +382,8 @@ async function startDownload(videoURLFromArgs = null) {
|
|
|
268
382
|
isWindows
|
|
269
383
|
? `explorer "${outputDir}"`
|
|
270
384
|
: isMac
|
|
271
|
-
|
|
272
|
-
|
|
385
|
+
? `open "${outputDir}"`
|
|
386
|
+
: `xdg-open "${outputDir}"`,
|
|
273
387
|
);
|
|
274
388
|
} catch (e) {}
|
|
275
389
|
} else {
|
|
@@ -316,7 +430,7 @@ async function showSupport() {
|
|
|
316
430
|
console.log(
|
|
317
431
|
` ${C.green}${f.icon}${C.reset} ${C.white}${f.title.padEnd(15)}${
|
|
318
432
|
C.reset
|
|
319
|
-
} ${C.dim}• ${f.desc}${C.reset}
|
|
433
|
+
} ${C.dim}• ${f.desc}${C.reset}`,
|
|
320
434
|
);
|
|
321
435
|
});
|
|
322
436
|
|
|
@@ -335,7 +449,7 @@ async function showSupport() {
|
|
|
335
449
|
|
|
336
450
|
links.forEach((l) => {
|
|
337
451
|
console.log(
|
|
338
|
-
` ${C.bgBlue}${C.white}${l.label}${C.reset} ${C.blue}➜${C.reset} ${C.dim}${l.url}${C.reset}
|
|
452
|
+
` ${C.bgBlue}${C.white}${l.label}${C.reset} ${C.blue}➜${C.reset} ${C.dim}${l.url}${C.reset}`,
|
|
339
453
|
);
|
|
340
454
|
});
|
|
341
455
|
|
|
@@ -371,20 +485,20 @@ async function mainMenu() {
|
|
|
371
485
|
// --- SEKSI NAVIGASI ---
|
|
372
486
|
console.log(` ${C.bright}MAIN SERVICES${C.reset}`);
|
|
373
487
|
console.log(
|
|
374
|
-
` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}
|
|
488
|
+
` ${C.cyan}1.${C.reset} 📥 Download Media ${C.dim}(Video, Music, Playlist)${C.reset}`,
|
|
375
489
|
);
|
|
376
490
|
console.log(
|
|
377
491
|
` ${C.cyan}2.${C.reset} 🛡️ Toggle Safe Mode ${C.dim}(Sekarang: ${
|
|
378
492
|
safeMode ? 'Aktif' : 'Nonaktif'
|
|
379
|
-
})${C.reset}
|
|
493
|
+
})${C.reset}`,
|
|
380
494
|
);
|
|
381
495
|
|
|
382
496
|
console.log(`\n ${C.bright}SYSTEM & INFO${C.reset}`);
|
|
383
497
|
console.log(
|
|
384
|
-
` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}
|
|
498
|
+
` ${C.cyan}3.${C.reset} ⚙️ Maintenance & Update ${C.dim}(Update engine / Cleanup)${C.reset}`,
|
|
385
499
|
);
|
|
386
500
|
console.log(
|
|
387
|
-
` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}
|
|
501
|
+
` ${C.cyan}4.${C.reset} ❤️ Tentang Aplikasi ${C.dim}(Dukungan & Fitur)${C.reset}`,
|
|
388
502
|
);
|
|
389
503
|
console.log(` ${C.cyan}0.${C.reset} 🚪 Keluar`);
|
|
390
504
|
|
|
@@ -402,7 +516,7 @@ async function mainMenu() {
|
|
|
402
516
|
console.log(
|
|
403
517
|
`\n${C.yellow} 🛡️ Safe Mode telah ${
|
|
404
518
|
safeMode ? 'DIAKTIFKAN' : 'DINONAKTIFKAN'
|
|
405
|
-
}${C.reset}
|
|
519
|
+
}${C.reset}`,
|
|
406
520
|
);
|
|
407
521
|
setTimeout(() => mainMenu(), 800);
|
|
408
522
|
break;
|
|
@@ -415,10 +529,10 @@ async function mainMenu() {
|
|
|
415
529
|
case '0':
|
|
416
530
|
console.log(`\n${C.cyan}━${'━'.repeat(48)}${C.reset}`);
|
|
417
531
|
console.log(
|
|
418
|
-
` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}
|
|
532
|
+
` ${C.bright}${C.white}Terima kasih telah menggunakan MEDIA-DL!${C.reset}`,
|
|
419
533
|
);
|
|
420
534
|
console.log(
|
|
421
|
-
` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}
|
|
535
|
+
` ${C.green}✨ Semoga Anda sukses, jaya, dan sehat selalu! ✨${C.reset}`,
|
|
422
536
|
);
|
|
423
537
|
console.log(`${C.cyan}━${'━'.repeat(48)}${C.reset}\n`);
|
|
424
538
|
|
|
@@ -442,36 +556,28 @@ async function cleanUp() {
|
|
|
442
556
|
fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
|
|
443
557
|
}
|
|
444
558
|
|
|
445
|
-
function checkTools() {
|
|
446
|
-
return {
|
|
447
|
-
ytExists: fs.existsSync(YTDLP_PATH),
|
|
448
|
-
ffExists: fs.existsSync(FFMPEG_PATH),
|
|
449
|
-
allReady: fs.existsSync(YTDLP_PATH) && fs.existsSync(FFMPEG_PATH),
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
|
|
453
559
|
async function firstTimeSetup() {
|
|
454
560
|
while (true) {
|
|
455
561
|
const { ytExists, ffExists } = checkTools();
|
|
456
562
|
printHeader(
|
|
457
563
|
'FIRST-TIME SETUP',
|
|
458
|
-
'Komponen diperlukan untuk menjalankan aplikasi'
|
|
564
|
+
'Komponen diperlukan untuk menjalankan aplikasi',
|
|
459
565
|
);
|
|
460
566
|
|
|
461
567
|
console.log(`${C.white}Status Instalasi:${C.reset}`);
|
|
462
568
|
console.log(
|
|
463
569
|
` [${ytExists ? C.green + '✓' : C.red + '✗'}${
|
|
464
570
|
C.reset
|
|
465
|
-
}] Engine yt-dlp (Wajib)
|
|
571
|
+
}] Engine yt-dlp (Wajib)`,
|
|
466
572
|
);
|
|
467
573
|
console.log(
|
|
468
574
|
` [${ffExists ? C.green + '✓' : C.red + '✗'}${
|
|
469
575
|
C.reset
|
|
470
|
-
}] FFmpeg (Direkomendasikan)
|
|
576
|
+
}] FFmpeg (Direkomendasikan)`,
|
|
471
577
|
);
|
|
472
578
|
|
|
473
579
|
console.log(
|
|
474
|
-
`\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}
|
|
580
|
+
`\n${C.yellow}Aplikasi belum siap digunakan. Pilih opsi:${C.reset}`,
|
|
475
581
|
);
|
|
476
582
|
console.log(` ${C.cyan}1.${C.reset} Install Semua Komponen Otomatis`);
|
|
477
583
|
console.log(` ${C.cyan}0.${C.reset} Keluar dari Aplikasi`);
|
|
@@ -485,7 +591,7 @@ async function firstTimeSetup() {
|
|
|
485
591
|
const status = checkTools();
|
|
486
592
|
if (status.ytExists) {
|
|
487
593
|
console.log(
|
|
488
|
-
`\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}
|
|
594
|
+
`\n${C.green}✨ Setup Selesai! Membuka Menu Utama...${C.reset}`,
|
|
489
595
|
);
|
|
490
596
|
await new Promise((r) => setTimeout(r, 1500));
|
|
491
597
|
return mainMenu(); // Berhasil, lanjut ke menu utama
|
|
@@ -504,19 +610,19 @@ async function systemMaintenance() {
|
|
|
504
610
|
const { ytExists, ffExists } = checkTools();
|
|
505
611
|
printHeader(
|
|
506
612
|
'SYSTEM MAINTENANCE',
|
|
507
|
-
'Update engine atau bersihkan file sistem'
|
|
613
|
+
'Update engine atau bersihkan file sistem',
|
|
508
614
|
);
|
|
509
615
|
|
|
510
616
|
console.log(`${C.white}Versi Terinstal:${C.reset}`);
|
|
511
617
|
console.log(
|
|
512
618
|
` • yt-dlp : ${ytExists ? C.green + 'Ready' : C.red + 'Not Found'}${
|
|
513
619
|
C.reset
|
|
514
|
-
}
|
|
620
|
+
}`,
|
|
515
621
|
);
|
|
516
622
|
console.log(
|
|
517
623
|
` • FFmpeg : ${ffExists ? C.green + 'Ready' : C.red + 'Not Found'}${
|
|
518
624
|
C.reset
|
|
519
|
-
}
|
|
625
|
+
}`,
|
|
520
626
|
);
|
|
521
627
|
|
|
522
628
|
console.log(`\n${C.bright}Opsi Pemeliharaan:${C.reset}`);
|
|
@@ -535,12 +641,12 @@ async function systemMaintenance() {
|
|
|
535
641
|
|
|
536
642
|
case '2':
|
|
537
643
|
const confirm = await askQuestion(
|
|
538
|
-
`${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n):
|
|
644
|
+
`${C.bgRed}${C.white} KONFIRMASI ${C.reset} Hapus semua tools? (y/n): `,
|
|
539
645
|
);
|
|
540
646
|
if (confirm.toLowerCase() === 'y') {
|
|
541
647
|
await cleanUp(); // Panggil fungsi penghapusan folder
|
|
542
648
|
console.log(
|
|
543
|
-
`${C.yellow}Sistem dibersihkan. Anda akan diarahkan ke Setup Wizard.${C.reset}
|
|
649
|
+
`${C.yellow}Sistem dibersihkan. Anda akan diarahkan ke Setup Wizard.${C.reset}`,
|
|
544
650
|
);
|
|
545
651
|
await askQuestion('Tekan Enter...');
|
|
546
652
|
return bootstrap(); // Kembali ke pengecekan awal
|