media-dl 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/cli.js +125 -64
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ const rl = readline.createInterface({
11
11
  output: process.stdout,
12
12
  });
13
13
 
14
+ // Penentuan folder Tools berdasarkan OS
14
15
  const TOOLS_DIR = path.join(os.homedir(), '.media-dl');
15
16
  const isWindows = process.platform === 'win32';
16
17
  const isMac = process.platform === 'darwin';
@@ -36,7 +37,6 @@ async function installYtdlp() {
36
37
  const url = isWindows
37
38
  ? 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
38
39
  : 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
39
-
40
40
  try {
41
41
  if (isWindows) {
42
42
  execSync(
@@ -54,7 +54,7 @@ async function installYtdlp() {
54
54
  }
55
55
 
56
56
  async function installFfmpeg() {
57
- console.log('\n⏳ Mengunduh FFmpeg (Diperlukan untuk kualitas HD)...');
57
+ console.log('\n⏳ Mengunduh FFmpeg (Diperlukan untuk konversi)...');
58
58
  try {
59
59
  if (isMac) {
60
60
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
@@ -62,25 +62,34 @@ async function installFfmpeg() {
62
62
  `curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
63
63
  { stdio: 'inherit' }
64
64
  );
65
- console.log('📦 Mengekstrak FFmpeg...');
66
65
  execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
67
66
  execSync(`rm "${zipPath}"`);
68
67
  execSync(`chmod a+rx "${FFMPEG_PATH}"`);
69
68
  console.log('✅ FFmpeg berhasil disiapkan.');
70
- } else {
71
- console.log(
72
- '📝 Untuk Windows, silakan unduh manual dan letakkan ffmpeg.exe di: ' +
73
- TOOLS_DIR
74
- );
75
69
  }
76
70
  } catch (e) {
77
- console.error('❌ Gagal mengunduh FFmpeg secara otomatis.');
71
+ console.error('❌ Gagal instal FFmpeg.');
78
72
  }
79
73
  }
80
74
 
75
+ function parseSelection(input, max) {
76
+ if (!input || input.toLowerCase() === 'all') return null;
77
+ const selected = new Set();
78
+ const parts = input.split(',');
79
+ parts.forEach((part) => {
80
+ if (part.includes('-')) {
81
+ const [start, end] = part.split('-').map(Number);
82
+ for (let i = start; i <= end; i++) if (i > 0 && i <= max) selected.add(i);
83
+ } else {
84
+ const num = parseInt(part.trim());
85
+ if (num > 0 && num <= max) selected.add(num);
86
+ }
87
+ });
88
+ return Array.from(selected).join(',');
89
+ }
90
+
81
91
  async function startDownload() {
82
92
  let { ytExists, ffExists } = checkTools();
83
-
84
93
  if (!ytExists) {
85
94
  console.log('\n⚠️ yt-dlp belum terpasang.');
86
95
  const ans = await askQuestion('Instal sekarang? (y/n): ');
@@ -88,69 +97,96 @@ async function startDownload() {
88
97
  if (!fs.existsSync(YTDLP_PATH)) return mainMenu();
89
98
  }
90
99
 
91
- const videoURL = await askQuestion('\n🔗 Masukkan Link: ');
100
+ const videoURL = await askQuestion('\n🔗 Masukkan Link (Video/Playlist): ');
101
+
102
+ console.log('⏳ Mengecek link...');
103
+ let playlistInfo = { isPlaylist: false, title: '', items: [] };
104
+
105
+ try {
106
+ const rawInfo = execSync(
107
+ `"${YTDLP_PATH}" --flat-playlist --print "%(playlist_title)s|%(index)s. %(title)s" "${videoURL}"`,
108
+ { encoding: 'utf-8' }
109
+ );
110
+ const lines = rawInfo.trim().split('\n');
111
+ if (lines.length > 1 || videoURL.includes('playlist?list=')) {
112
+ playlistInfo.isPlaylist = true;
113
+ playlistInfo.title = lines[0].split('|')[0] || 'Unduhan Playlist';
114
+ playlistInfo.items = lines.map((l) => l.split('|')[1]).filter(Boolean);
115
+ }
116
+ } catch (e) {}
117
+
118
+ let playlistSelection = null;
119
+ if (playlistInfo.isPlaylist) {
120
+ console.log(`\n📂 PLAYLIST TERDETEKSI: ${playlistInfo.title}`);
121
+ playlistInfo.items.forEach((item) => console.log(item));
122
+ const selectionInput = await askQuestion(
123
+ '\nPilih nomor video (contoh: 1,3,5-10) atau tekan Enter untuk semua: '
124
+ );
125
+ playlistSelection = parseSelection(
126
+ selectionInput,
127
+ playlistInfo.items.length
128
+ );
129
+ }
92
130
 
93
131
  console.log('\n[PILIH FORMAT]');
94
- console.log('1. Audio (MP3) ' + (ffExists ? '✅' : '❌ (Butuh FFmpeg)'));
95
- console.log('2. Video MP4 (Pilih Resolusi)');
132
+ console.log('1. Video MP4 (Sangat Kompatibel Mac/QuickTime)');
133
+ console.log('2. Audio (MP3) ' + (ffExists ? '✅' : '❌ (Butuh FFmpeg)'));
96
134
  const mode = await askQuestion('Pilihan: ');
97
135
 
98
- const outputDir = path.join(process.cwd(), 'downloads');
99
- if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
136
+ const baseDir = path.join(os.homedir(), 'Downloads', 'media-dl');
137
+ const subFolder = mode === '2' ? 'audio' : 'video';
138
+ let finalOutputDir = path.join(baseDir, subFolder);
139
+
140
+ if (playlistInfo.isPlaylist) {
141
+ const folderName = playlistInfo.title.replace(/[\\/:"*?<>|]/g, '_');
142
+ finalOutputDir = path.join(finalOutputDir, folderName);
143
+ }
100
144
 
101
- // PERBAIKAN: Menggunakan %(title).100s untuk membatasi panjang nama file agar tidak Error 63
145
+ if (!fs.existsSync(finalOutputDir))
146
+ fs.mkdirSync(finalOutputDir, { recursive: true });
147
+
148
+ const qtFormat =
149
+ 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best';
102
150
  let args = [
103
151
  '--ffmpeg-location',
104
152
  FFMPEG_PATH,
105
153
  '-o',
106
- `${outputDir}/%(title).100s.%(ext)s`,
154
+ `${finalOutputDir}/%(title).100s.%(ext)s`,
107
155
  videoURL,
108
156
  ];
109
157
 
110
- if (mode === '1') {
158
+ if (playlistSelection) {
159
+ args.push('--playlist-items', playlistSelection);
160
+ } else if (!playlistInfo.isPlaylist) {
161
+ args.push('--no-playlist');
162
+ }
163
+
164
+ if (mode === '2') {
111
165
  if (!ffExists) {
112
- console.log(
113
- '❌ MP3 butuh FFmpeg. Silakan instal FFmpeg terlebih dahulu.'
114
- );
166
+ console.log('❌ Butuh FFmpeg.');
115
167
  return mainMenu();
116
168
  }
117
169
  args.unshift('-x', '--audio-format', 'mp3');
118
170
  } else {
119
- console.log('\n[PILIH RESOLUSI]');
120
- console.log('1. Terbaik (Best Quality)');
121
- console.log('2. 1080p (Jika ada)');
171
+ console.log('\n[PILIH KUALITAS]');
172
+ console.log('1. Terbaik (Auto-Conversion ke MP4)');
173
+ console.log('2. 1080p');
122
174
  console.log('3. 720p');
123
- console.log('4. 480p');
124
- const resChoice = await askQuestion('Pilih resolusi: ');
125
-
126
- let formatCode = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best';
127
- if (resChoice === '2')
128
- formatCode =
129
- 'bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080][ext=mp4]/best';
130
- if (resChoice === '3')
131
- formatCode =
132
- 'bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720][ext=mp4]/best';
133
- if (resChoice === '4')
134
- formatCode =
135
- 'bestvideo[height<=480][ext=mp4]+bestaudio[ext=m4a]/best[height<=480][ext=mp4]/best';
136
-
137
- args.unshift('-f', formatCode);
175
+ const res = await askQuestion('Pilih: ');
138
176
 
139
- if (!ffExists) {
140
- console.log(
141
- '⚠️ FFmpeg tidak terdeteksi. Hanya bisa mengunduh kualitas standar.'
142
- );
143
- args = [
144
- '-f',
145
- 'best[ext=mp4]',
146
- '-o',
147
- `${outputDir}/%(title).100s.%(ext)s`,
148
- videoURL,
149
- ];
150
- }
177
+ let fCode = qtFormat;
178
+ if (res === '2')
179
+ fCode =
180
+ 'bestvideo[height<=1080][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=1080]/best';
181
+ if (res === '3')
182
+ fCode =
183
+ 'bestvideo[height<=720][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=720]/best';
184
+
185
+ args.unshift('-f', fCode);
186
+ if (ffExists) args.unshift('--recode-video', 'mp4');
151
187
  }
152
188
 
153
- console.log('\n🚀 Memulai proses...');
189
+ console.log('\n🚀 Memulai proses unduhan...');
154
190
  const download = spawn(YTDLP_PATH, args);
155
191
 
156
192
  download.stdout.on('data', (data) => process.stdout.write(data));
@@ -158,29 +194,51 @@ async function startDownload() {
158
194
 
159
195
  download.on('close', (code) => {
160
196
  if (code === 0) {
161
- console.log(`\n✅ SELESAI! Folder: ${outputDir}`);
162
- execSync(isMac ? `open "${outputDir}"` : `explorer "${outputDir}"`);
197
+ console.log(`\n✅ BERHASIL! File disimpan di: ${finalOutputDir}`);
198
+ execSync(
199
+ isMac ? `open "${finalOutputDir}"` : `explorer "${finalOutputDir}"`
200
+ );
163
201
  } else {
164
- console.log('\n❌ Terjadi kesalahan selama proses download.');
202
+ console.log('\n❌ Gagal.');
165
203
  }
166
204
  mainMenu();
167
205
  });
168
206
  }
169
207
 
208
+ async function showSupport() {
209
+ console.log('\n====================================');
210
+ console.log(' ❤️ SUPPORT DEVELOPER ');
211
+ console.log('====================================');
212
+ console.log('Terima kasih telah menggunakan MEDIA-DL!');
213
+ console.log('Dukungan Anda sangat berarti bagi pengembangan skrip ini.');
214
+ console.log(
215
+ '\n* ☕ Beli Kopi: https://app.midtrans.com/payment-links/coffee-developer'
216
+ );
217
+ console.log(
218
+ '* 🍕 Beli Pizza: https://app.midtrans.com/payment-links/pizza-developer'
219
+ );
220
+ console.log('------------------------------------');
221
+ await askQuestion('\nTekan Enter untuk kembali ke Menu Utama...');
222
+ mainMenu();
223
+ }
224
+
170
225
  async function mainMenu() {
171
226
  const { ytExists, ffExists } = checkTools();
172
227
  console.log('\n====================================');
173
228
  console.log(' MEDIA-DL MANAGER 2026 ');
174
229
  console.log('====================================');
175
- console.log(` OS : ${process.platform}`);
176
- console.log(` yt-dlp : ${ytExists ? '✅' : '❌'}`);
177
- console.log(` ffmpeg : ${ffExists ? '✅' : '❌'}`);
230
+ console.log(
231
+ ` OS : ${process.platform} | yt-dlp: ${
232
+ ytExists ? '✅' : '❌'
233
+ } | ffmpeg: ${ffExists ? '✅' : '❌'}`
234
+ );
178
235
  console.log('------------------------------------');
179
- console.log(' 1. 📥 Download Media');
236
+ console.log(' 1. 📥 Download Media (Single/Playlist)');
180
237
  console.log(' 2. ⚙️ Update yt-dlp');
181
238
  console.log(' 3. 🔨 Instal FFmpeg (macOS)');
182
- console.log(' 4. 🗑️ Uninstall & Hapus Data');
183
- console.log(' 5. 🚪 Keluar');
239
+ console.log(' 4. ❤️ Support Developer');
240
+ console.log(' 5. 🗑️ Uninstall & Hapus Data');
241
+ console.log(' 6. 🚪 Keluar');
184
242
 
185
243
  const choice = await askQuestion('\nPilih menu: ');
186
244
  if (choice === '1') await startDownload();
@@ -190,10 +248,13 @@ async function mainMenu() {
190
248
  } else if (choice === '3') {
191
249
  await installFfmpeg();
192
250
  mainMenu();
193
- } else if (choice === '4') {
194
- const confirm = await askQuestion('Hapus semua data? (y/n): ');
195
- if (confirm.toLowerCase() === 'y')
251
+ } else if (choice === '4') await showSupport();
252
+ else if (choice === '5') {
253
+ const confirm = await askQuestion('Hapus semua? (y/n): ');
254
+ if (confirm.toLowerCase() === 'y') {
196
255
  fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
256
+ console.log('✅ Folder tools dibersihkan.');
257
+ }
197
258
  mainMenu();
198
259
  } else {
199
260
  rl.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "CLI Downloader video/audio lintas platform menggunakan yt-dlp.",
5
5
  "main": "bin/cli.js",
6
6
  "scripts": {