media-dl 1.0.6 → 2.0.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 (3) hide show
  1. package/bin/cli.js +160 -47
  2. package/package.json +1 -1
  3. package/readme.md +32 -19
package/bin/cli.js CHANGED
@@ -8,9 +8,10 @@ const os = require('os');
8
8
 
9
9
  const rl = readline.createInterface({
10
10
  input: process.stdin,
11
- output: process.stdout
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';
@@ -21,30 +22,35 @@ const FFMPEG_PATH = path.join(TOOLS_DIR, isWindows ? 'ffmpeg.exe' : 'ffmpeg');
21
22
  if (!fs.existsSync(TOOLS_DIR)) fs.mkdirSync(TOOLS_DIR, { recursive: true });
22
23
 
23
24
  function askQuestion(query) {
24
- return new Promise(resolve => rl.question(query, resolve));
25
+ return new Promise((resolve) => rl.question(query, resolve));
25
26
  }
26
27
 
27
28
  function checkTools() {
28
29
  return {
29
30
  ytExists: fs.existsSync(YTDLP_PATH),
30
- ffExists: fs.existsSync(FFMPEG_PATH)
31
+ ffExists: fs.existsSync(FFMPEG_PATH),
31
32
  };
32
33
  }
33
34
 
34
35
  async function installYtdlp() {
35
36
  console.log('\n⏳ Mengunduh yt-dlp...');
36
- const url = isWindows
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
  try {
40
41
  if (isWindows) {
41
- execSync(`powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${YTDLP_PATH}'"`, { stdio: 'inherit' });
42
+ execSync(
43
+ `powershell -Command "Invoke-WebRequest -Uri ${url} -OutFile '${YTDLP_PATH}'"`,
44
+ { stdio: 'inherit' }
45
+ );
42
46
  } else {
43
47
  execSync(`curl -L -# "${url}" -o "${YTDLP_PATH}"`, { stdio: 'inherit' });
44
48
  execSync(`chmod a+rx "${YTDLP_PATH}"`);
45
49
  }
46
50
  console.log('✅ yt-dlp berhasil diinstal.');
47
- } catch (e) { console.error('❌ Gagal mengunduh yt-dlp.'); }
51
+ } catch (e) {
52
+ console.error('❌ Gagal mengunduh yt-dlp.');
53
+ }
48
54
  }
49
55
 
50
56
  async function installFfmpeg() {
@@ -52,13 +58,34 @@ async function installFfmpeg() {
52
58
  try {
53
59
  if (isMac) {
54
60
  const zipPath = path.join(TOOLS_DIR, 'ffmpeg.zip');
55
- execSync(`curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`, { stdio: 'inherit' });
61
+ execSync(
62
+ `curl -L -# "https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip" -o "${zipPath}"`,
63
+ { stdio: 'inherit' }
64
+ );
56
65
  execSync(`unzip -o "${zipPath}" -d "${TOOLS_DIR}"`, { stdio: 'inherit' });
57
66
  execSync(`rm "${zipPath}"`);
58
67
  execSync(`chmod a+rx "${FFMPEG_PATH}"`);
59
68
  console.log('✅ FFmpeg berhasil disiapkan.');
60
69
  }
61
- } catch (e) { console.error('❌ Gagal instal FFmpeg.'); }
70
+ } catch (e) {
71
+ console.error('❌ Gagal instal FFmpeg.');
72
+ }
73
+ }
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(',');
62
89
  }
63
90
 
64
91
  async function startDownload() {
@@ -70,83 +97,169 @@ async function startDownload() {
70
97
  if (!fs.existsSync(YTDLP_PATH)) return mainMenu();
71
98
  }
72
99
 
73
- 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
+ }
130
+
74
131
  console.log('\n[PILIH FORMAT]');
75
- console.log('1. Audio (MP3)');
76
- console.log('2. Video MP4 (Sangat Kompatibel Mac/QuickTime)');
132
+ console.log('1. Video MP4 (Sangat Kompatibel Mac/QuickTime)');
133
+ console.log('2. Audio (MP3) ' + (ffExists ? '✅' : '❌ (Butuh FFmpeg)'));
77
134
  const mode = await askQuestion('Pilihan: ');
78
135
 
79
136
  const baseDir = path.join(os.homedir(), 'Downloads', 'media-dl');
80
- const subFolder = (mode === '1') ? 'audio' : 'video';
81
- const finalOutputDir = path.join(baseDir, subFolder);
82
- if (!fs.existsSync(finalOutputDir)) fs.mkdirSync(finalOutputDir, { recursive: true });
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
+ }
144
+
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';
150
+ let args = [
151
+ '--ffmpeg-location',
152
+ FFMPEG_PATH,
153
+ '-o',
154
+ `${finalOutputDir}/%(title).100s.%(ext)s`,
155
+ videoURL,
156
+ ];
83
157
 
84
- // Filter Format: Prioritaskan AVC1 + AAC
85
- const qtFormat = 'bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[vcodec^=avc1]/best';
86
-
87
- let args = ['--ffmpeg-location', FFMPEG_PATH, '-o', `${finalOutputDir}/%(title).100s.%(ext)s`, videoURL];
158
+ if (playlistSelection) {
159
+ args.push('--playlist-items', playlistSelection);
160
+ } else if (!playlistInfo.isPlaylist) {
161
+ args.push('--no-playlist');
162
+ }
88
163
 
89
- if (mode === '1') {
90
- if (!ffExists) { console.log('❌ Butuh FFmpeg.'); return mainMenu(); }
164
+ if (mode === '2') {
165
+ if (!ffExists) {
166
+ console.log('❌ Butuh FFmpeg.');
167
+ return mainMenu();
168
+ }
91
169
  args.unshift('-x', '--audio-format', 'mp3');
92
170
  } else {
93
171
  console.log('\n[PILIH KUALITAS]');
94
172
  console.log('1. Terbaik (Auto-Conversion ke MP4)');
95
- console.log('2. 1080p (Pasti MP4)');
96
- console.log('3. 720p (Pasti MP4)');
173
+ console.log('2. 1080p');
174
+ console.log('3. 720p');
97
175
  const res = await askQuestion('Pilih: ');
98
-
176
+
99
177
  let fCode = qtFormat;
100
- if (res === '2') fCode = 'bestvideo[height<=1080][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=1080]/best';
101
- if (res === '3') fCode = 'bestvideo[height<=720][vcodec^=avc1]+bestaudio[acodec^=mp4a]/best[height<=720]/best';
102
-
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
+
103
185
  args.unshift('-f', fCode);
104
-
105
- // FITUR BARU: Jika FFmpeg ada, paksa konversi ke MP4 agar pasti bisa dibuka QuickTime
106
- if (ffExists) {
107
- args.unshift('--recode-video', 'mp4');
108
- }
186
+ if (ffExists) args.unshift('--recode-video', 'mp4');
109
187
  }
110
188
 
111
- console.log('\n🚀 Memulai proses (dan konversi jika perlu)...');
189
+ console.log('\n🚀 Memulai proses unduhan...');
112
190
  const download = spawn(YTDLP_PATH, args);
191
+
113
192
  download.stdout.on('data', (data) => process.stdout.write(data));
114
193
  download.stderr.on('data', (data) => process.stderr.write(data));
115
194
 
116
195
  download.on('close', (code) => {
117
196
  if (code === 0) {
118
- console.log(`\n✅ BERHASIL! File tersimpan di: ${finalOutputDir}`);
119
- execSync(isMac ? `open "${finalOutputDir}"` : `explorer "${finalOutputDir}"`);
120
- } else { console.log('\n❌ Gagal saat mendownload atau mengonversi.'); }
197
+ console.log(`\n✅ BERHASIL! File disimpan di: ${finalOutputDir}`);
198
+ execSync(
199
+ isMac ? `open "${finalOutputDir}"` : `explorer "${finalOutputDir}"`
200
+ );
201
+ } else {
202
+ console.log('\n❌ Gagal.');
203
+ }
121
204
  mainMenu();
122
205
  });
123
206
  }
124
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
+
125
225
  async function mainMenu() {
126
226
  const { ytExists, ffExists } = checkTools();
127
227
  console.log('\n====================================');
128
228
  console.log(' MEDIA-DL MANAGER 2026 ');
129
229
  console.log('====================================');
130
- console.log(` OS : ${process.platform} | yt-dlp: ${ytExists?'✅':'❌'} | ffmpeg: ${ffExists?'✅':'❌'}`);
230
+ console.log(
231
+ ` OS : ${process.platform} | yt-dlp: ${
232
+ ytExists ? '✅' : '❌'
233
+ } | ffmpeg: ${ffExists ? '✅' : '❌'}`
234
+ );
131
235
  console.log('------------------------------------');
132
- console.log(' 1. 📥 Download Media');
236
+ console.log(' 1. 📥 Download Media (Single/Playlist)');
133
237
  console.log(' 2. ⚙️ Update yt-dlp');
134
238
  console.log(' 3. 🔨 Instal FFmpeg (macOS)');
135
- console.log(' 4. 🗑️ Uninstall & Hapus Data');
136
- console.log(' 5. 🚪 Keluar');
137
-
239
+ console.log(' 4. ❤️ Support Developer');
240
+ console.log(' 5. 🗑️ Uninstall & Hapus Data');
241
+ console.log(' 6. 🚪 Keluar');
242
+
138
243
  const choice = await askQuestion('\nPilih menu: ');
139
244
  if (choice === '1') await startDownload();
140
- else if (choice === '2') { await installYtdlp(); mainMenu(); }
141
- else if (choice === '3') { await installFfmpeg(); mainMenu(); }
142
- else if (choice === '4') {
245
+ else if (choice === '2') {
246
+ await installYtdlp();
247
+ mainMenu();
248
+ } else if (choice === '3') {
249
+ await installFfmpeg();
250
+ mainMenu();
251
+ } else if (choice === '4') await showSupport();
252
+ else if (choice === '5') {
143
253
  const confirm = await askQuestion('Hapus semua? (y/n): ');
144
254
  if (confirm.toLowerCase() === 'y') {
145
- fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
146
- console.log('✅ Folder tools dibersihkan.');
255
+ fs.rmSync(TOOLS_DIR, { recursive: true, force: true });
256
+ console.log('✅ Folder tools dibersihkan.');
147
257
  }
148
258
  mainMenu();
149
- } else { rl.close(); process.exit(0); }
259
+ } else {
260
+ rl.close();
261
+ process.exit(0);
262
+ }
150
263
  }
151
264
 
152
- mainMenu();
265
+ mainMenu();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-dl",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "CLI Downloader video/audio lintas platform menggunakan yt-dlp.",
5
5
  "main": "bin/cli.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -1,16 +1,18 @@
1
- # Media-DL CLI 🚀
1
+ # Media-DL CLI 2026 🚀
2
2
 
3
- Alat baris perintah (CLI) lintas platform yang cepat dan sederhana untuk mengunduh video atau audio dari berbagai platform menggunakan kekuatan `yt-dlp`.
3
+ **Media-DL CLI** adalah pengunduh media berbasis terminal generasi terbaru yang dirancang untuk kecepatan, kemudahan, dan kompatibilitas penuh. Ditenagai oleh `yt-dlp`, alat ini memastikan video yang Anda unduh selalu siap diputar di perangkat apa pun tanpa kendala codec.
4
4
 
5
- ## ✨ Fitur
6
- - 🎥 **Download Video**: Mendukung format MP4.
7
- - 🎵 **Download Audio**: Konversi otomatis ke MP3 kualitas tinggi.
8
- - 💻 **Lintas Platform**: Berjalan lancar di Windows, macOS, dan Linux.
9
- - 🛠️ **Auto-Installer**: Membantu mengunduh dan menyiapkan `yt-dlp` secara otomatis.
5
+ ## ✨ Fitur Unggulan
6
+
7
+ * 📥 **YouTube Playlist Power**: Deteksi otomatis playlist. Pilih video tertentu (misal: `1,3,5-10`) atau unduh semua sekaligus.
8
+ * 🍏 **Apple Ecosystem Ready**: Secara otomatis mengonversi dan mengoptimalkan video ke format **H.264 (AVC1)** & **AAC** agar lancar diputar di **QuickTime Player**, iPhone, dan iPad.
9
+ * 🛠️ **Smart Auto-Installer**: Tidak perlu pusing mengunduh `yt-dlp` atau `ffmpeg` secara manual. Skrip akan menyiapkannya untuk Anda (Optimal untuk macOS).
10
+ * 📂 **Organized Storage**: Hasil unduhan tersimpan rapi di folder standar sistem `~/Downloads/media-dl/` yang dipisahkan berdasarkan kategori `video` dan `audio`.
11
+ * 🛡️ **Fail-Safe Filename**: Fitur pemotongan judul otomatis untuk menghindari error *filename too long* pada sistem macOS/Windows.
10
12
 
11
13
  ## 📦 Instalasi
12
14
 
13
- Pastikan Anda sudah menginstal [Node.js](https://nodejs.org/). Kemudian instal paket ini secara global:
15
+ Cukup pastikan Anda memiliki [Node.js](https://nodejs.org/) terinstal, lalu jalankan:
14
16
 
15
17
  ```bash
16
18
  npm install -g media-dl
@@ -19,33 +21,44 @@ npm install -g media-dl
19
21
 
20
22
  ## 🚀 Cara Penggunaan
21
23
 
22
- Cukup buka terminal/command prompt Anda dan ketik:
24
+ Jalankan perintah berikut di terminal Anda:
23
25
 
24
26
  ```bash
25
27
  media-dl
26
28
 
27
29
  ```
28
30
 
29
- Ikuti petunjuk di layar untuk mulai mengunduh atau melakukan pembaruan tools.
31
+ ### Panduan Menu:
32
+
33
+ 1. **Download Media**: Masukkan link video atau playlist.
34
+ 2. **Pilih Format**: Video MP4 (QuickTime Compatible) atau Audio MP3.
35
+ 3. **Seleksi Playlist**: Masukkan angka video yang diinginkan (Contoh: `1,3-5`) atau tekan `Enter` untuk semua.
36
+ 4. **Auto-Open**: Folder tujuan akan otomatis terbuka setelah proses selesai.
37
+
38
+ ## 📁 Lokasi Penyimpanan
39
+
40
+ Skrip ini menggunakan struktur folder yang rapi:
41
+
42
+ * **Tools**: `~/.media-dl/` (Tersembunyi agar sistem tetap bersih).
43
+ * **Video**: `~/Downloads/media-dl/video/`
44
+ * **Audio**: `~/Downloads/media-dl/audio/`
30
45
 
31
46
  ## 📋 Persyaratan Sistem
32
47
 
33
48
  * **Node.js**: v14.0.0 atau lebih tinggi.
34
- * **FFmpeg**: Diperlukan untuk konversi audio ke MP3. Alat ini akan memberikan instruksi lokasi peletakan file binary FFmpeg di dalam menu.
49
+ * **FFmpeg**: Diperlukan untuk konversi tingkat lanjut. Gunakan **Menu 3** di dalam aplikasi untuk instalasi otomatis (macOS).
35
50
 
36
- ## Dukungan & Donasi
51
+ ## ❤️ Dukungan & Donasi
37
52
 
38
- Jika alat ini membantu pekerjaan Anda, Anda bisa memberikan dukungan kepada pengembang melalui link di bawah ini:
53
+ Skrip ini dikembangkan secara terbuka. Jika alat ini membantu produktivitas Anda, pertimbangkan untuk traktir pengembang:
39
54
 
40
55
  * **Beli Kopi ☕**: [Donasi via Midtrans](https://app.midtrans.com/payment-links/coffee-developer)
41
56
  * **Beli Pizza 🍕**: [Donasi via Midtrans](https://app.midtrans.com/payment-links/pizza-developer)
42
57
 
43
- Dukungan Anda sangat berarti untuk pengembangan fitur-fitur selanjutnya!
44
-
45
- ## ⭐ Ulasan
46
-
47
- > "Alat paling praktis untuk urusan download cepat lewat terminal tanpa ribet iklan!" - *Community User*
58
+ ---
48
59
 
49
60
  ## 📄 Lisensi
50
61
 
51
- MIT
62
+ Distribusi di bawah Lisensi MIT. Bebas digunakan dan dikembangkan kembali.
63
+
64
+ ---