@nekosuneprojects/nekosunevrtools 1.1.0 → 1.1.1

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/src/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const path = require("path");
2
2
  const axios = require("axios");
3
- const { UploadClient, EarningsClient, shortenerPresets } = require("./index");
3
+ const { UploadClient, EarningsClient, shortenerPresets, DownloaderClient, downloaderPresets, LivestreamClient, livestreamPresets, GamesClient, gamesPresets } = require("./index");
4
4
 
5
5
  async function runCli(argv = process.argv.slice(2)) {
6
6
  const parsed = parseArgs(argv);
@@ -22,15 +22,587 @@ async function runCli(argv = process.argv.slice(2)) {
22
22
  if (parsed.command === "video-upload") {
23
23
  return runVideoUploadCommand(parsed);
24
24
  }
25
+ if (parsed.command === "download-mp3") {
26
+ return runDownloadJobCreateCommand(parsed, "mp3");
27
+ }
28
+ if (parsed.command === "download-mp4") {
29
+ return runDownloadJobCreateCommand(parsed, "mp4");
30
+ }
31
+ if (parsed.command === "download-job") {
32
+ return runDownloadJobStatusCommand(parsed);
33
+ }
34
+ if (parsed.command === "download-info") {
35
+ return runDownloadInfoCommand(parsed);
36
+ }
37
+ if (parsed.command === "download-search") {
38
+ return runDownloadSearchCommand(parsed);
39
+ }
40
+ if (parsed.command === "download-stream-url") {
41
+ return runDownloadStreamUrlCommand(parsed);
42
+ }
25
43
  if (parsed.command === "list-shorteners") {
26
44
  process.stdout.write(`${Object.keys(shortenerPresets).join("\n")}\n`);
27
45
  return 0;
28
46
  }
47
+ if (parsed.command === "live-kick") {
48
+ return runLivestreamCommand(parsed, "kick");
49
+ }
50
+ if (parsed.command === "live-twitch") {
51
+ return runLivestreamCommand(parsed, "twitch");
52
+ }
53
+ if (parsed.command === "live-dlive") {
54
+ return runLivestreamCommand(parsed, "dlive");
55
+ }
56
+ if (parsed.command === "live-trovo") {
57
+ return runLivestreamCommand(parsed, "trovo");
58
+ }
59
+ if (parsed.command === "live") {
60
+ return runLivestreamCommand(parsed, parsed.streamPlatform);
61
+ }
62
+ if (parsed.command === "coc-clan") {
63
+ return runClashOfClansClanCommand(parsed);
64
+ }
65
+ if (parsed.command === "coc-player") {
66
+ return runClashOfClansPlayerCommand(parsed);
67
+ }
68
+ if (parsed.command === "division2-player" || parsed.command === "td2-player") {
69
+ return runDivision2PlayerCommand(parsed);
70
+ }
71
+ if (parsed.command === "fortnite-player" || parsed.command === "fn-player") {
72
+ return runFortnitePlayerCommand(parsed);
73
+ }
74
+ if (parsed.command === "fortnite-creatorcode" || parsed.command === "fn-creatorcode") {
75
+ return runFortniteCreatorCodeCommand(parsed);
76
+ }
77
+ if (parsed.command === "fortnite-item-shop" || parsed.command === "fn-item-shop") {
78
+ return runFortniteItemShopCommand(parsed);
79
+ }
80
+ if (parsed.command === "wynncraft-profile" || parsed.command === "wc-profile") {
81
+ return runWynncraftProfileCommand(parsed);
82
+ }
83
+ if (parsed.command === "hypixel-profile" || parsed.command === "hp-profile") {
84
+ return runHypixelProfileCommand(parsed);
85
+ }
86
+ if (parsed.command === "rocketleague-player" || parsed.command === "rl-player") {
87
+ return runRocketLeaguePlayerCommand(parsed);
88
+ }
89
+ if (parsed.command === "apexlegends-player" || parsed.command === "apex-player") {
90
+ return runApexLegendsPlayerCommand(parsed);
91
+ }
92
+ if (parsed.command === "battlefield1-player" || parsed.command === "bf1-player") {
93
+ return runBattlefield1PlayerCommand(parsed);
94
+ }
95
+ if (parsed.command === "battlefield5-player" || parsed.command === "bf5-player") {
96
+ return runBattlefield5PlayerCommand(parsed);
97
+ }
98
+ if (parsed.command === "battlefield2042-player" || parsed.command === "bf2042-player") {
99
+ return runBattlefield2042PlayerCommand(parsed);
100
+ }
101
+ if (parsed.command === "battlefield6-player" || parsed.command === "bf6-player") {
102
+ return runBattlefield6PlayerCommand(parsed);
103
+ }
104
+ if (parsed.command === "list-livestream-presets") {
105
+ process.stdout.write(`${Object.keys(livestreamPresets).join("\n")}\n`);
106
+ return 0;
107
+ }
108
+ if (parsed.command === "list-games-presets") {
109
+ process.stdout.write(`${Object.keys(gamesPresets).join("\n")}\n`);
110
+ return 0;
111
+ } if (parsed.command === "list-download-presets") {
112
+ process.stdout.write(`${Object.keys(downloaderPresets).join("\n")}\n`);
113
+ return 0;
114
+ }
29
115
 
30
116
  printHelp();
31
117
  return 1;
32
118
  }
33
119
 
120
+ function buildDownloaderClient(parsed) {
121
+ return new DownloaderClient({
122
+ apiKey: parsed.apiKey || null,
123
+ baseUrl: parsed.downloaderBaseUrl || parsed.baseUrl || null,
124
+ preset: parsed.downloaderPreset || "nekosune",
125
+ timeoutMs: parsed.timeoutMs || 120000
126
+ });
127
+ }
128
+
129
+ function buildLivestreamClient(parsed) {
130
+ return new LivestreamClient({
131
+ apiKey: parsed.apiKey || null,
132
+ baseUrl: parsed.livestreamBaseUrl || null,
133
+ preset: parsed.livestreamPreset || "nekosune",
134
+ timeoutMs: parsed.timeoutMs || 60000
135
+ });
136
+ }
137
+
138
+ function buildGamesClient(parsed) {
139
+ return new GamesClient({
140
+ apiKey: parsed.apiKey || null,
141
+ baseUrl: parsed.gamesBaseUrl || null,
142
+ preset: parsed.gamesPreset || "nekosune",
143
+ timeoutMs: parsed.timeoutMs || 60000
144
+ });
145
+ }
146
+
147
+ async function runLivestreamCommand(parsed, explicitPlatform) {
148
+ const platform = explicitPlatform || parsed.streamPlatform;
149
+ const username = parsed.username || parsed.url;
150
+ if (!platform) {
151
+ throw new Error("Missing livestream platform. Use live-kick/live-twitch/live-dlive/live-trovo or --stream-platform.");
152
+ }
153
+ if (!username) {
154
+ throw new Error("Missing --username for livestream command.");
155
+ }
156
+ if (!parsed.apiKey) {
157
+ throw new Error("Missing --apikey for livestream command.");
158
+ }
159
+
160
+ const client = buildLivestreamClient(parsed);
161
+ let result;
162
+ if (platform === "kick") {
163
+ result = await client.getKick(username);
164
+ } else if (platform === "twitch") {
165
+ result = await client.getTwitch(username);
166
+ } else if (platform === "dlive") {
167
+ result = await client.getDlive(username);
168
+ } else if (platform === "trovo") {
169
+ result = await client.getTrovo(username);
170
+ } else {
171
+ result = await client.getByPlatform(platform, username);
172
+ }
173
+
174
+ if (parsed.json) {
175
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
176
+ } else {
177
+ const online = result && result.livestream ? result.livestream.online : null;
178
+ process.stdout.write(`${result.displayname || result.username || username} online=${String(Boolean(online))}\n`);
179
+ }
180
+ return 0;
181
+ }
182
+
183
+ async function runClashOfClansClanCommand(parsed) {
184
+ const tag = parsed.clanTag || parsed.tag;
185
+ if (!tag) {
186
+ throw new Error("Missing --clan-tag for coc-clan.");
187
+ }
188
+ if (!parsed.apiKey) {
189
+ throw new Error("Missing --apikey for coc-clan.");
190
+ }
191
+
192
+ const client = buildGamesClient(parsed);
193
+ const result = await client.getClashOfClansClan(tag);
194
+ if (parsed.json) {
195
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
196
+ } else {
197
+ process.stdout.write(`${result.name || "Clan"} ${result.tag || ""} members=${result.memberCount || 0}\n`);
198
+ }
199
+ return 0;
200
+ }
201
+
202
+ async function runClashOfClansPlayerCommand(parsed) {
203
+ const tag = parsed.playerTag || parsed.tag;
204
+ if (!tag) {
205
+ throw new Error("Missing --player-tag for coc-player.");
206
+ }
207
+ if (!parsed.apiKey) {
208
+ throw new Error("Missing --apikey for coc-player.");
209
+ }
210
+
211
+ const client = buildGamesClient(parsed);
212
+ const result = await client.getClashOfClansPlayer(tag);
213
+ if (parsed.json) {
214
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
215
+ } else {
216
+ process.stdout.write(`${result.name || "Player"} ${result.tag || ""} th=${result.townHallLevel || "?"}\n`);
217
+ }
218
+ return 0;
219
+ }
220
+
221
+ async function runDivision2PlayerCommand(parsed) {
222
+ const username = parsed.username || parsed.url;
223
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
224
+
225
+ if (!username) {
226
+ throw new Error("Missing --username for division2-player.");
227
+ }
228
+ if (!parsed.apiKey) {
229
+ throw new Error("Missing --apikey for division2-player.");
230
+ }
231
+
232
+ const client = buildGamesClient(parsed);
233
+ const result = await client.getDivision2Player(username, platform);
234
+ if (parsed.json) {
235
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
236
+ } else {
237
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
238
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
239
+ process.stdout.write(`${handle} platform=${platform}\n`);
240
+ }
241
+ return 0;
242
+ }
243
+
244
+ async function runFortnitePlayerCommand(parsed) {
245
+ const username = parsed.username || parsed.url;
246
+ const timeWindow = parsed.timeWindow || "lifetime";
247
+
248
+ if (!username) {
249
+ throw new Error("Missing --username for fortnite-player.");
250
+ }
251
+ if (!parsed.apiKey) {
252
+ throw new Error("Missing --apikey for fortnite-player.");
253
+ }
254
+
255
+ const client = buildGamesClient(parsed);
256
+ const result = await client.getFortnitePlayer(username, timeWindow);
257
+ if (parsed.json) {
258
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
259
+ } else {
260
+ const accountName = result && result.data && result.data.account ? result.data.account.name : username;
261
+ const wins = result && result.data && result.data.stats && result.data.stats.all && result.data.stats.all.overall
262
+ ? result.data.stats.all.overall.wins
263
+ : 0;
264
+ process.stdout.write(`${accountName} timeWindow=${timeWindow} wins=${wins}\n`);
265
+ }
266
+ return 0;
267
+ }
268
+
269
+ async function runFortniteCreatorCodeCommand(parsed) {
270
+ const creatorCode = parsed.creatorCode || parsed.code || parsed.username || parsed.url;
271
+
272
+ if (!creatorCode) {
273
+ throw new Error("Missing --creator-code for fortnite-creatorcode.");
274
+ }
275
+ if (!parsed.apiKey) {
276
+ throw new Error("Missing --apikey for fortnite-creatorcode.");
277
+ }
278
+
279
+ const client = buildGamesClient(parsed);
280
+ const result = await client.getFortniteCreatorCode(creatorCode);
281
+ if (parsed.json) {
282
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
283
+ } else {
284
+ const code = result && result.data ? result.data.code : creatorCode;
285
+ const status = result && result.data ? result.data.status : "unknown";
286
+ process.stdout.write(`${code} status=${status}\n`);
287
+ }
288
+ return 0;
289
+ }
290
+
291
+ async function runFortniteItemShopCommand(parsed) {
292
+ if (!parsed.apiKey) {
293
+ throw new Error("Missing --apikey for fortnite-item-shop.");
294
+ }
295
+
296
+ const client = buildGamesClient(parsed);
297
+ const result = await client.getFortniteItemShop();
298
+ if (parsed.json) {
299
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
300
+ } else {
301
+ const data = result && result.data ? result.data : {};
302
+ const date = data.date || "unknown";
303
+ const entries = Array.isArray(data.entries) ? data.entries.length : 0;
304
+ process.stdout.write(`date=${date} entries=${entries}\n`);
305
+ }
306
+ return 0;
307
+ }
308
+
309
+ async function runWynncraftProfileCommand(parsed) {
310
+ const username = parsed.username || parsed.url;
311
+
312
+ if (!username) {
313
+ throw new Error("Missing --username for wynncraft-profile.");
314
+ }
315
+ if (!parsed.apiKey) {
316
+ throw new Error("Missing --apikey for wynncraft-profile.");
317
+ }
318
+
319
+ const client = buildGamesClient(parsed);
320
+ const result = await client.getWynncraftProfile(username);
321
+ if (parsed.json) {
322
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
323
+ } else {
324
+ process.stdout.write(`${result.username || username} online=${String(Boolean(result.online))}\n`);
325
+ }
326
+ return 0;
327
+ }
328
+
329
+ async function runHypixelProfileCommand(parsed) {
330
+ const username = parsed.username || parsed.url;
331
+
332
+ if (!username) {
333
+ throw new Error("Missing --username for hypixel-profile.");
334
+ }
335
+ if (!parsed.apiKey) {
336
+ throw new Error("Missing --apikey for hypixel-profile.");
337
+ }
338
+
339
+ const client = buildGamesClient(parsed);
340
+ const result = await client.getHypixelProfile(username);
341
+ if (parsed.json) {
342
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
343
+ } else {
344
+ const displayName = result.displayname || username;
345
+ const level = result.level || 0;
346
+ process.stdout.write(`${displayName} level=${level}\n`);
347
+ }
348
+ return 0;
349
+ }
350
+
351
+ async function runRocketLeaguePlayerCommand(parsed) {
352
+ const username = parsed.username || parsed.url;
353
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
354
+
355
+ if (!username) {
356
+ throw new Error("Missing --username for rocketleague-player.");
357
+ }
358
+ if (!parsed.apiKey) {
359
+ throw new Error("Missing --apikey for rocketleague-player.");
360
+ }
361
+
362
+ const client = buildGamesClient(parsed);
363
+ const result = await client.getRocketLeaguePlayer(username, platform);
364
+ if (parsed.json) {
365
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
366
+ } else {
367
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
368
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
369
+ process.stdout.write(`${handle} platform=${platform}\n`);
370
+ }
371
+ return 0;
372
+ }
373
+
374
+ async function runApexLegendsPlayerCommand(parsed) {
375
+ const username = parsed.username || parsed.url;
376
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
377
+
378
+ if (!username) {
379
+ throw new Error("Missing --username for apexlegends-player.");
380
+ }
381
+ if (!parsed.apiKey) {
382
+ throw new Error("Missing --apikey for apexlegends-player.");
383
+ }
384
+
385
+ const client = buildGamesClient(parsed);
386
+ const result = await client.getApexLegendsPlayer(username, platform);
387
+ if (parsed.json) {
388
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
389
+ } else {
390
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
391
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
392
+ process.stdout.write(`${handle} platform=${platform}\n`);
393
+ }
394
+ return 0;
395
+ }
396
+
397
+ async function runBattlefield1PlayerCommand(parsed) {
398
+ const username = parsed.username || parsed.url;
399
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
400
+
401
+ if (!username) {
402
+ throw new Error("Missing --username for battlefield1-player.");
403
+ }
404
+ if (!parsed.apiKey) {
405
+ throw new Error("Missing --apikey for battlefield1-player.");
406
+ }
407
+
408
+ const client = buildGamesClient(parsed);
409
+ const result = await client.getBattlefield1Player(username, platform);
410
+ if (parsed.json) {
411
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
412
+ } else {
413
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
414
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
415
+ process.stdout.write(`${handle} platform=${platform}\n`);
416
+ }
417
+ return 0;
418
+ }
419
+
420
+ async function runBattlefield5PlayerCommand(parsed) {
421
+ const username = parsed.username || parsed.url;
422
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
423
+
424
+ if (!username) {
425
+ throw new Error("Missing --username for battlefield5-player.");
426
+ }
427
+ if (!parsed.apiKey) {
428
+ throw new Error("Missing --apikey for battlefield5-player.");
429
+ }
430
+
431
+ const client = buildGamesClient(parsed);
432
+ const result = await client.getBattlefield5Player(username, platform);
433
+ if (parsed.json) {
434
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
435
+ } else {
436
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
437
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
438
+ process.stdout.write(`${handle} platform=${platform}\n`);
439
+ }
440
+ return 0;
441
+ }
442
+
443
+ async function runBattlefield2042PlayerCommand(parsed) {
444
+ const username = parsed.username || parsed.url;
445
+ const platform = parsed.divisionPlatform || parsed.platform || "ea";
446
+
447
+ if (!username) {
448
+ throw new Error("Missing --username for battlefield2042-player.");
449
+ }
450
+ if (!parsed.apiKey) {
451
+ throw new Error("Missing --apikey for battlefield2042-player.");
452
+ }
453
+
454
+ const client = buildGamesClient(parsed);
455
+ const result = await client.getBattlefield2042Player(username, platform);
456
+ if (parsed.json) {
457
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
458
+ } else {
459
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
460
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
461
+ process.stdout.write(`${handle} platform=${platform}\n`);
462
+ }
463
+ return 0;
464
+ }
465
+
466
+ async function runBattlefield6PlayerCommand(parsed) {
467
+ const username = parsed.username || parsed.url;
468
+ const platform = parsed.divisionPlatform || parsed.platform || "psn";
469
+
470
+ if (!username) {
471
+ throw new Error("Missing --username for battlefield6-player.");
472
+ }
473
+ if (!parsed.apiKey) {
474
+ throw new Error("Missing --apikey for battlefield6-player.");
475
+ }
476
+
477
+ const client = buildGamesClient(parsed);
478
+ const result = await client.getBattlefield6Player(username, platform);
479
+ if (parsed.json) {
480
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
481
+ } else {
482
+ const source = result && result.profile && result.profile.data ? result.profile.data : null;
483
+ const handle = source && source.platformInfo ? source.platformInfo.platformUserHandle : username;
484
+ process.stdout.write(`${handle} platform=${platform}\n`);
485
+ }
486
+ return 0;
487
+ }
488
+
489
+ async function runDownloadJobCreateCommand(parsed, mode) {
490
+ if (!parsed.url) {
491
+ throw new Error(`Missing --url for download-${mode}.`);
492
+ }
493
+ if (!parsed.apiKey) {
494
+ throw new Error(`Missing --apikey for download-${mode}.`);
495
+ }
496
+
497
+ const downloader = buildDownloaderClient(parsed);
498
+ const createFn = mode === "mp3" ? downloader.createMp3Job.bind(downloader) : downloader.createMp4Job.bind(downloader);
499
+ const job = await createFn(parsed.url, {
500
+ uploadDest: parsed.uploadDest || "cdn"
501
+ });
502
+
503
+ if (parsed.wait) {
504
+ const finalState = await downloader.waitForJob(job.jobId, {
505
+ intervalMs: parsed.intervalMs || 2000,
506
+ timeoutMs: parsed.timeoutMs || 300000,
507
+ onProgress: (state) => {
508
+ const label = (state.upload && state.upload.label) || state.status || "working";
509
+ process.stdout.write(`[job ${state.job_id}] ${label}\n`);
510
+ }
511
+ });
512
+
513
+ if (parsed.json) {
514
+ process.stdout.write(`${JSON.stringify({ job, final: finalState }, null, 2)}\n`);
515
+ } else {
516
+ process.stdout.write(`job_id=${job.jobId}\n`);
517
+ process.stdout.write(`status=${finalState.status}\n`);
518
+ if (finalState.url) {
519
+ process.stdout.write(`${finalState.url}\n`);
520
+ }
521
+ }
522
+ return 0;
523
+ }
524
+
525
+ if (parsed.json) {
526
+ process.stdout.write(`${JSON.stringify(job, null, 2)}\n`);
527
+ } else {
528
+ process.stdout.write(`${job.jobId}\n`);
529
+ }
530
+ return 0;
531
+ }
532
+
533
+ async function runDownloadJobStatusCommand(parsed) {
534
+ if (!parsed.jobId) {
535
+ throw new Error("Missing --job-id for download-job.");
536
+ }
537
+ if (!parsed.apiKey) {
538
+ throw new Error("Missing --apikey for download-job.");
539
+ }
540
+
541
+ const downloader = buildDownloaderClient(parsed);
542
+ const state = await downloader.getJob(parsed.jobId);
543
+ if (parsed.json) {
544
+ process.stdout.write(`${JSON.stringify(state, null, 2)}\n`);
545
+ } else {
546
+ process.stdout.write(`status=${state.status || "unknown"}\n`);
547
+ if (state.url) {
548
+ process.stdout.write(`${state.url}\n`);
549
+ }
550
+ }
551
+ return 0;
552
+ }
553
+
554
+ async function runDownloadInfoCommand(parsed) {
555
+ if (!parsed.url) {
556
+ throw new Error("Missing --url for download-info.");
557
+ }
558
+
559
+ const downloader = buildDownloaderClient(parsed);
560
+ const info = await downloader.getInfo(parsed.url, {
561
+ flat: parsed.flat,
562
+ fields: parsed.fields || "basic",
563
+ cache: typeof parsed.cache === "number" ? parsed.cache : 1
564
+ });
565
+
566
+ if (parsed.json) {
567
+ process.stdout.write(`${JSON.stringify(info, null, 2)}\n`);
568
+ } else {
569
+ process.stdout.write(`${info.title || info.id || "ok"}\n`);
570
+ }
571
+ return 0;
572
+ }
573
+
574
+ async function runDownloadSearchCommand(parsed) {
575
+ if (!parsed.query) {
576
+ throw new Error("Missing --query for download-search.");
577
+ }
578
+
579
+ const downloader = buildDownloaderClient(parsed);
580
+ const result = await downloader.searchYouTube(parsed.query, {
581
+ limit: parsed.limit || 5
582
+ });
583
+
584
+ if (parsed.json) {
585
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
586
+ } else {
587
+ const items = Array.isArray(result.items) ? result.items : [];
588
+ for (const item of items) {
589
+ process.stdout.write(`${item.title || "(no title)"} - ${item.url || ""}\n`);
590
+ }
591
+ }
592
+ return 0;
593
+ }
594
+
595
+ async function runDownloadStreamUrlCommand(parsed) {
596
+ if (!parsed.url) {
597
+ throw new Error("Missing --url for download-stream-url.");
598
+ }
599
+
600
+ const downloader = buildDownloaderClient(parsed);
601
+ const streamUrl = downloader.getStreamUrl(parsed.url);
602
+ process.stdout.write(`${streamUrl}\n`);
603
+ return 0;
604
+ }
605
+
34
606
  async function runUploadCommand(parsed) {
35
607
  const files = parsed.files;
36
608
  if (files.length === 0) {
@@ -427,15 +999,82 @@ function parseArgs(argv) {
427
999
  result.help = true;
428
1000
  } else if (token === "--json") {
429
1001
  result.json = true;
1002
+ } else if (token === "--wait") {
1003
+ result.wait = true;
1004
+ } else if (token === "--flat" || token === "--flat-playlist") {
1005
+ result.flat = true;
430
1006
  } else if (token === "--file" || token === "-f") {
431
1007
  const value = argv[i + 1];
432
1008
  i += 1;
433
1009
  if (value) {
434
1010
  result.files.push(value);
435
1011
  }
1012
+ } else if (token === "--username") {
1013
+ result.username = argv[i + 1];
1014
+ i += 1;
1015
+ } else if (token === "--stream-platform") {
1016
+ result.streamPlatform = argv[i + 1];
1017
+ i += 1;
1018
+ } else if (token === "--clan-tag") {
1019
+ result.clanTag = argv[i + 1];
1020
+ i += 1;
1021
+ } else if (token === "--player-tag") {
1022
+ result.playerTag = argv[i + 1];
1023
+ i += 1;
1024
+ } else if (token === "--tag") {
1025
+ result.tag = argv[i + 1];
1026
+ i += 1;
1027
+ } else if (token === "--livestream-preset") {
1028
+ result.livestreamPreset = argv[i + 1];
1029
+ i += 1;
1030
+ } else if (token === "--livestream-base-url") {
1031
+ result.livestreamBaseUrl = argv[i + 1];
1032
+ i += 1;
1033
+ } else if (token === "--games-preset") {
1034
+ result.gamesPreset = argv[i + 1];
1035
+ i += 1;
1036
+ } else if (token === "--games-base-url") {
1037
+ result.gamesBaseUrl = argv[i + 1];
1038
+ i += 1;
1039
+ } else if (token === "--division-platform" || token === "--game-platform") {
1040
+ result.divisionPlatform = argv[i + 1];
1041
+ i += 1;
1042
+ } else if (token === "--time-window") {
1043
+ result.timeWindow = argv[i + 1];
1044
+ i += 1;
1045
+ } else if (token === "--creator-code" || token === "--code") {
1046
+ result.creatorCode = argv[i + 1];
1047
+ i += 1;
436
1048
  } else if (token === "--url") {
437
1049
  result.url = argv[i + 1];
438
1050
  i += 1;
1051
+ } else if (token === "--job-id") {
1052
+ result.jobId = argv[i + 1];
1053
+ i += 1;
1054
+ } else if (token === "--query" || token === "-q") {
1055
+ result.query = argv[i + 1];
1056
+ i += 1;
1057
+ } else if (token === "--limit") {
1058
+ result.limit = Number(argv[i + 1]);
1059
+ i += 1;
1060
+ } else if (token === "--fields") {
1061
+ result.fields = argv[i + 1];
1062
+ i += 1;
1063
+ } else if (token === "--cache") {
1064
+ result.cache = Number(argv[i + 1]);
1065
+ i += 1;
1066
+ } else if (token === "--upload-dest") {
1067
+ result.uploadDest = argv[i + 1];
1068
+ i += 1;
1069
+ } else if (token === "--downloader-preset") {
1070
+ result.downloaderPreset = argv[i + 1];
1071
+ i += 1;
1072
+ } else if (token === "--downloader-base-url") {
1073
+ result.downloaderBaseUrl = argv[i + 1];
1074
+ i += 1;
1075
+ } else if (token === "--interval") {
1076
+ result.intervalMs = Number(argv[i + 1]);
1077
+ i += 1;
439
1078
  } else if (token === "--platform" || token === "-p") {
440
1079
  result.platform = argv[i + 1];
441
1080
  i += 1;
@@ -498,20 +1137,93 @@ function parseArgs(argv) {
498
1137
 
499
1138
  function printHelp() {
500
1139
  const helpText = `
501
- upload2earn CLI
1140
+ nekosunevrtools CLI
502
1141
 
503
1142
  Commands:
504
- upload Upload files
505
- shorten Convert a URL into monetized short link
506
- upload-shorten Upload file then monetize resulting URL
507
- video-upload Upload video to video-earn provider
508
- list-shorteners Show built-in shortlink preset names
1143
+ upload Upload files
1144
+ shorten Convert a URL into monetized short link
1145
+ upload-shorten Upload file then monetize resulting URL
1146
+ video-upload Upload video to video-earn provider
1147
+ download-mp3 Create MP3 download job
1148
+ download-mp4 Create MP4 download job
1149
+ download-job Get download job status
1150
+ download-info Fetch metadata for URL
1151
+ download-search Search YouTube
1152
+ download-stream-url Build /api/stream URL
1153
+ live-kick Get Kick livestream data
1154
+ live-twitch Get Twitch livestream data
1155
+ live-dlive Get DLive livestream data
1156
+ live-trovo Get Trovo livestream data
1157
+ live Get livestream data by --stream-platform
1158
+ coc-clan Get Clash of Clans clan by tag
1159
+ coc-player Get Clash of Clans player by tag
1160
+ division2-player Get The Division 2 player by username/platform
1161
+ td2-player Alias for division2-player
1162
+ fortnite-player Get Fortnite player by username/time window
1163
+ fn-player Alias for fortnite-player
1164
+ fortnite-creatorcode Get Fortnite creator code details
1165
+ fn-creatorcode Alias for fortnite-creatorcode
1166
+ fortnite-item-shop Get current Fortnite item shop
1167
+ fn-item-shop Alias for fortnite-item-shop
1168
+ wynncraft-profile Get Wynncraft profile by username
1169
+ wc-profile Alias for wynncraft-profile
1170
+ hypixel-profile Get Hypixel profile by username
1171
+ hp-profile Alias for hypixel-profile
1172
+ rocketleague-player Get Rocket League player by username/platform
1173
+ rl-player Alias for rocketleague-player
1174
+ apexlegends-player Get Apex Legends player by username/platform
1175
+ apex-player Alias for apexlegends-player
1176
+ battlefield1-player Get Battlefield 1 player by username/platform
1177
+ bf1-player Alias for battlefield1-player
1178
+ battlefield5-player Get Battlefield 5 player by username/platform
1179
+ bf5-player Alias for battlefield5-player
1180
+ battlefield2042-player Get Battlefield 2042 player by username/platform
1181
+ bf2042-player Alias for battlefield2042-player
1182
+ battlefield6-player Get Battlefield 6 player by username/platform
1183
+ bf6-player Alias for battlefield6-player
1184
+ list-shorteners Show built-in shortlink preset names
1185
+ list-download-presets Show downloader API host presets
1186
+ list-livestream-presets Show livestream API host presets
1187
+ list-games-presets Show games API host presets
509
1188
 
510
1189
  Usage:
511
- upload2earn upload --file <path> [options]
512
- upload2earn shorten --url <url> --shortener-preset shrinkme --apikey <key>
513
- upload2earn upload-shorten --file <path> --upload-platform upfiles --apikey <upload-key> --shortener-preset shrinkme --shortener-apikey <short-key>
514
- upload2earn video-upload --file <video.mp4> --video-provider doodstream --apikey <key>
1190
+ nekosunevrtools download-mp3 --url <link> --apikey <key> [--upload-dest cdn]
1191
+ nekosunevrtools download-job --job-id <id> --apikey <key>
1192
+ nekosunevrtools live-kick --username mugstv --apikey YOUR_KEY
1193
+ nekosunevrtools coc-clan --clan-tag 2LLJYCUU8 --apikey YOUR_KEY
1194
+
1195
+ Downloader options:
1196
+ --downloader-preset <name> Host preset: nekosune|ballisticok (default: nekosune)
1197
+ --downloader-base-url <url> Custom compatible host base URL
1198
+ --upload-dest <name> cdn|thirdparty|ipfs
1199
+ --job-id <id> Job ID for status lookup
1200
+ --query, -q <text> Search query
1201
+ --limit <n> Search limit (1-25)
1202
+ --fields <basic|full> Info response fields
1203
+ --flat Use flat playlist mode
1204
+ --cache <0|1> Info cache toggle
1205
+ --wait Poll job until done
1206
+ --interval <ms> Poll interval (default: 2000)
1207
+ --url <url> Target URL/link
1208
+ --apikey, --api-key, -k API key for secured endpoints
1209
+
1210
+ Livestream options:
1211
+ --username <name> Streamer username
1212
+ --stream-platform <name> kick|twitch|dlive|trovo (used with live)
1213
+ --livestream-preset <name> API host preset (default: nekosune)
1214
+ --livestream-base-url <url> Custom API base URL
1215
+
1216
+ Games options:
1217
+ --clan-tag <tag> Clash of Clans clan tag
1218
+ --player-tag <tag> Clash of Clans player tag
1219
+ --tag <tag> Generic tag alias
1220
+ --username <name> Game username (varies by endpoint)
1221
+ --division-platform <name> Game platform for supported commands (default: psn)
1222
+ --game-platform <name> Alias for --division-platform
1223
+ --time-window <name> Fortnite stats window (default: lifetime)
1224
+ --creator-code <value> Fortnite creator code
1225
+ --games-preset <name> API host preset (default: nekosune)
1226
+ --games-base-url <url> Custom API base URL
515
1227
 
516
1228
  Shared options:
517
1229
  --json JSON output
@@ -520,7 +1232,6 @@ Shared options:
520
1232
  Upload options:
521
1233
  --platform, -p <name> Platform: upfiles|fileio|catbox|transfersh (default: upfiles)
522
1234
  --file, -f <path> File path (repeatable)
523
- --apikey, --api-key, -k API key for provider
524
1235
  --expires <value> file.io expiration metadata (example: 1w)
525
1236
  --meta key=value Additional metadata field (repeatable)
526
1237
  --retry, --retries, -r <n> Retry count per file (default: 0)
@@ -528,7 +1239,6 @@ Upload options:
528
1239
  --timeout <ms> Request timeout in milliseconds (default: 60000)
529
1240
 
530
1241
  Short-link options:
531
- --url <url> URL to shorten
532
1242
  --shortener-preset <name> Preset from list-shorteners
533
1243
  --base-url <url> Custom AdLinkFly-compatible API base URL
534
1244
  --shortener-apikey <key> API key for shortener (or reuse --apikey)
@@ -544,7 +1254,11 @@ Discord progress options:
544
1254
  `;
545
1255
  process.stdout.write(helpText);
546
1256
  }
547
-
548
1257
  module.exports = {
549
1258
  runCli
550
1259
  };
1260
+
1261
+
1262
+
1263
+
1264
+