unreleased-cli 0.1.3 → 0.1.4
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/README.md +2 -1
- package/dist/unreleased.mjs +162 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ Commands (type `help <command>` in the shell for the details of each one):
|
|
|
52
52
|
|
|
53
53
|
- **Files:** `cd`, `ls`, `pwd`, `lcd`, `get`, `cat`, `head`, `tail`, `wc`, `grep`, `locate`, `tree`, `du`
|
|
54
54
|
- **Library:** `find`, `song`, `like`, `unlike`, `liked`, `playlists`, `playlist`, `stats`
|
|
55
|
-
- **Player:** `play`, `pause`, `toggle`, `next`, `prev`, `seek`, `volume`, `speed`, `shuffle`, `repeat`, `queue`, `status`, `sleep`, `stop`
|
|
55
|
+
- **Player:** `play`, `pause`, `toggle`, `next`, `prev`, `seek`, `volume`, `speed`, `eq`, `shuffle`, `repeat`, `queue`, `status`, `sleep`, `stop`
|
|
56
56
|
- **People:** `user`, `lookup`
|
|
57
57
|
- **Admin** (administrators only): `pending`, `proposals`, `comps`, `applications`, `inspect`, `approve`, `reject`, `reverse`, `users`, `sitebans`, `siteunban`
|
|
58
58
|
- **Fun:** `neofetch`, `fortune`, `juicesay`, `matrix`, `visualizer`, `karaoke`, `wordle`, `heardle`
|
|
@@ -130,6 +130,7 @@ These cover the Admin page's review queues and site moderation, using the same e
|
|
|
130
130
|
- `matrix` is digital rain. Any key leaves.
|
|
131
131
|
- `visualizer` (or `viz`) is a live spectrum of the song that's playing. Any key leaves. mpv can't hand its audio over, so a second mpv decodes the same stream to a temp file and the bars come from that, which means the song is downloaded a second time while the screen is open.
|
|
132
132
|
- `karaoke` (or `lyrics`) shows the lyrics of the song that's playing. Synced lyrics highlight the line being sung and scroll along; plain ones scroll with the arrow keys and Page Up/Down. Space pauses, `q` leaves. It needs a song from the library (not a file played from the tree).
|
|
133
|
+
- `eq` shows the equalizer settings. `eq list` names the presets (the site's own), `eq rock` turns the equalizer on with one, and `eq off` / `eq reset` switch it off or flatten it. `eq boost 100-200`, `eq balance -100..100`, `eq speed 0.5-2` and `eq pitch [on|off]` (the same as the `speed` command and the `pitch-shift` setting), `eq mono [on|off]` and `eq reverb [on|off|0-100] [decay 1-8]` do the rest. It runs through mpv's audio filters, so the reverb is an echo approximation of the site's. Settings carry over between sessions, and changes apply to the song that's playing.
|
|
133
134
|
- `wordle [daily | unlimited]` is the song-title Wordle, using the site's own puzzle logic, so the daily puzzle is the same one. Type a title and press Enter. Esc leaves, and your progress is saved.
|
|
134
135
|
- `heardle` gives practice rounds: Tab plays the clip, Enter guesses (an empty Enter skips), and ↑↓ picks a suggestion. The clip plays through its own mpv. Music that was playing gets paused, and `play` resumes it.
|
|
135
136
|
|
package/dist/unreleased.mjs
CHANGED
|
@@ -82,7 +82,7 @@ function saveSettings(settings) {
|
|
|
82
82
|
|
|
83
83
|
// src/api.ts
|
|
84
84
|
var DEFAULT_API = "https://juicewrldapi.com/juicewrld";
|
|
85
|
-
var VERSION = "0.1.
|
|
85
|
+
var VERSION = "0.1.4";
|
|
86
86
|
function apiBase() {
|
|
87
87
|
return (process.env.UNRELEASED_API || loadConfig().api || DEFAULT_API).replace(/\/+$/, "");
|
|
88
88
|
}
|
|
@@ -488,6 +488,68 @@ function installHistorySearch(rl, history, idle, closed = () => void 0, stdin =
|
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
+
// src/effects.ts
|
|
492
|
+
var EQ_BANDS = [32, 64, 125, 250, 500, 1e3, 2e3, 4e3, 8e3, 16e3];
|
|
493
|
+
var EQ_PRESETS = [
|
|
494
|
+
{ id: "flat", name: "Flat", gains: EQ_BANDS.map(() => 0) },
|
|
495
|
+
{ id: "bass-boost", name: "Bass Boost", gains: [6, 5, 4, 2.5, 1, 0, 0, 0, 0, 0] },
|
|
496
|
+
{ id: "bass-reducer", name: "Bass Reducer", gains: [-6, -5, -4, -2.5, -1, 0, 0, 0, 0, 0] },
|
|
497
|
+
{ id: "treble-boost", name: "Treble Boost", gains: [0, 0, 0, 0, 0, 1, 2.5, 4, 5, 6] },
|
|
498
|
+
{ id: "treble-reducer", name: "Treble Reducer", gains: [0, 0, 0, 0, 0, -1, -2.5, -4, -5, -6] },
|
|
499
|
+
{ id: "vocal", name: "Vocal Boost", gains: [-2, -3, -3, 1, 4, 4, 3, 1.5, 0, -1.5] },
|
|
500
|
+
{ id: "rock", name: "Rock", gains: [5, 4, 3, 1.5, -0.5, -1, 0.5, 2.5, 3.5, 4.5] },
|
|
501
|
+
{ id: "pop", name: "Pop", gains: [-1.5, -1, 0, 2, 4, 4, 2, 0, -1, -1.5] },
|
|
502
|
+
{ id: "hip-hop", name: "Hip-Hop", gains: [5, 4, 1.5, 3, -1, -1, 1.5, -0.5, 2, 3] },
|
|
503
|
+
{ id: "electronic", name: "Electronic", gains: [4.5, 4, 1.5, 0, -2, 2, 1, 1.5, 4, 5] },
|
|
504
|
+
{ id: "jazz", name: "Jazz", gains: [4, 3, 1.5, 2, -1.5, -1.5, 0, 1.5, 3, 4] },
|
|
505
|
+
{ id: "classical", name: "Classical", gains: [4.5, 3.5, 3, 2.5, -1.5, -1.5, 0, 2, 3, 4] },
|
|
506
|
+
{ id: "acoustic", name: "Acoustic", gains: [5, 5, 4, 1, 2, 1.5, 3.5, 4, 3.5, 2] },
|
|
507
|
+
{ id: "small-speakers", name: "Small Speakers", gains: [5.5, 4.5, 4, 2.5, 1.5, 0, -1.5, -3, -4, -4.5] }
|
|
508
|
+
];
|
|
509
|
+
var effects = () => ({
|
|
510
|
+
eq: pref("eq", false),
|
|
511
|
+
preset: pref("eq-preset", "flat"),
|
|
512
|
+
boost: pref("eq-boost", 1),
|
|
513
|
+
balance: pref("eq-balance", 0),
|
|
514
|
+
mono: pref("eq-mono", false),
|
|
515
|
+
reverb: pref("reverb", false),
|
|
516
|
+
reverbMix: pref("reverb-mix", 0.3),
|
|
517
|
+
reverbDecay: pref("reverb-decay", 2)
|
|
518
|
+
});
|
|
519
|
+
var KEYS = {
|
|
520
|
+
eq: "eq",
|
|
521
|
+
preset: "eq-preset",
|
|
522
|
+
boost: "eq-boost",
|
|
523
|
+
balance: "eq-balance",
|
|
524
|
+
mono: "eq-mono",
|
|
525
|
+
reverb: "reverb",
|
|
526
|
+
reverbMix: "reverb-mix",
|
|
527
|
+
reverbDecay: "reverb-decay"
|
|
528
|
+
};
|
|
529
|
+
function saveEffect(key, value) {
|
|
530
|
+
setPref(KEYS[key], value);
|
|
531
|
+
}
|
|
532
|
+
var num = (n) => String(Math.round(n * 1e3) / 1e3);
|
|
533
|
+
function filterChain(e = effects()) {
|
|
534
|
+
const f = [];
|
|
535
|
+
const gains = EQ_PRESETS.find((p) => p.id === e.preset)?.gains;
|
|
536
|
+
if (e.eq && gains) {
|
|
537
|
+
gains.forEach((g, i) => {
|
|
538
|
+
if (g) f.push(`equalizer=f=${EQ_BANDS[i]}:width_type=o:width=1:g=${num(g)}`);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (e.mono) f.push("pan=stereo|c0=0.5*c0+0.5*c1|c1=0.5*c0+0.5*c1");
|
|
542
|
+
if (e.balance) f.push(`stereotools=balance_out=${num(e.balance)}`);
|
|
543
|
+
if (e.reverb && e.reverbMix > 0) {
|
|
544
|
+
const delays = [0.04, 0.09, 0.17, 0.3].map((t) => Math.round(Math.min(1, t * e.reverbDecay / 2) * 1e3));
|
|
545
|
+
const decays = [0.6, 0.5, 0.4, 0.3].map((d) => num(d * e.reverbMix * 2 > 0.9 ? 0.9 : d * e.reverbMix * 2));
|
|
546
|
+
f.push(`aecho=in_gain=${num(1 - e.reverbMix * 0.4)}:out_gain=${num(0.6 + e.reverbMix * 0.3)}:delays=${delays.join("|")}:decays=${decays.join("|")}`);
|
|
547
|
+
}
|
|
548
|
+
if (e.boost > 1) f.push(`volume=${num(e.boost)}`);
|
|
549
|
+
if (f.length) f.push("alimiter=limit=0.95");
|
|
550
|
+
return f.length ? `lavfi=[${f.join(",")}]` : "";
|
|
551
|
+
}
|
|
552
|
+
|
|
491
553
|
// src/mpv.ts
|
|
492
554
|
import { spawn } from "node:child_process";
|
|
493
555
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -737,6 +799,8 @@ var Player = class {
|
|
|
737
799
|
await mpv.set("mute", this.muted);
|
|
738
800
|
await mpv.set("speed", this.speed);
|
|
739
801
|
await mpv.set("audio-pitch-correction", !this.pitchShift);
|
|
802
|
+
const chain = filterChain();
|
|
803
|
+
if (chain) await mpv.set("af", chain);
|
|
740
804
|
await mpv.command("observe_property", 1, "time-pos");
|
|
741
805
|
await mpv.command("observe_property", 2, "duration");
|
|
742
806
|
this.mpv = mpv;
|
|
@@ -958,6 +1022,10 @@ var Player = class {
|
|
|
958
1022
|
setPref("speed", s);
|
|
959
1023
|
if (this.mpv?.alive) await this.mpv.set("speed", s);
|
|
960
1024
|
}
|
|
1025
|
+
/** Swaps the audio-filter chain (the equalizer and effects) while playing. */
|
|
1026
|
+
async setFilter(chain) {
|
|
1027
|
+
if (this.mpv?.alive) await this.mpv.set("af", chain);
|
|
1028
|
+
}
|
|
961
1029
|
async setPitchShift(on) {
|
|
962
1030
|
this.pitchShift = on;
|
|
963
1031
|
setPref("pitch-shift", on);
|
|
@@ -3810,8 +3878,8 @@ async function myPlaylists(fresh = false) {
|
|
|
3810
3878
|
}
|
|
3811
3879
|
async function playlistFromArg(arg) {
|
|
3812
3880
|
const list = await myPlaylists();
|
|
3813
|
-
const
|
|
3814
|
-
if (
|
|
3881
|
+
const num2 = /^#?(\d+)$/.exec(arg.trim());
|
|
3882
|
+
if (num2) return list[Number(num2[1]) - 1] ?? fail(`pick a playlist from 1 to ${list.length}`);
|
|
3815
3883
|
return pickByName(list, (p) => p.name, arg) ?? fail(`no single playlist matches "${arg.trim()}" (try: playlists)`);
|
|
3816
3884
|
}
|
|
3817
3885
|
async function playlistSongs(id) {
|
|
@@ -4492,6 +4560,96 @@ var UPDATE_COMMANDS = [
|
|
|
4492
4560
|
}
|
|
4493
4561
|
];
|
|
4494
4562
|
|
|
4563
|
+
// src/eq.ts
|
|
4564
|
+
var WORDS = ["on", "off", "list", "reset", "reverb", "boost", "balance", "speed", "mono", "pitch", ...EQ_PRESETS.map((p) => p.id)];
|
|
4565
|
+
function describe(speed, pitch) {
|
|
4566
|
+
const e = effects();
|
|
4567
|
+
const preset = EQ_PRESETS.find((p) => p.id === e.preset)?.id ?? e.preset;
|
|
4568
|
+
return `equalizer ${e.eq ? "on" : "off"} \xB7 preset ${preset} \xB7 boost ${Math.round(e.boost * 100)}% \xB7 balance ${Math.round(e.balance * 100)}
|
|
4569
|
+
reverb ${e.reverb ? "on" : "off"} \xB7 mix ${Math.round(e.reverbMix * 100)}% \xB7 decay ${e.reverbDecay}s \xB7 speed ${speed}x \xB7 mono ${e.mono ? "on" : "off"} \xB7 pitch follows speed ${pitch ? "on" : "off"}`;
|
|
4570
|
+
}
|
|
4571
|
+
var onOff = (v, cur) => v === "on" ? true : v === "off" ? false : v === void 0 ? !cur : fail("expected on or off");
|
|
4572
|
+
function numIn(v, lo, hi, what) {
|
|
4573
|
+
const n = Number(v);
|
|
4574
|
+
if (v === void 0 || v === "" || !Number.isFinite(n) || n < lo || n > hi) fail(`${what} must be ${lo} to ${hi}`);
|
|
4575
|
+
return n;
|
|
4576
|
+
}
|
|
4577
|
+
var EQ_COMMANDS = [
|
|
4578
|
+
{
|
|
4579
|
+
name: "eq",
|
|
4580
|
+
group: "Player",
|
|
4581
|
+
usage: "eq [on | off | list | reset | <preset> | reverb [on|off|<0-100>] [decay 1-8] | boost <100-200> | balance <-100..100> | speed <0.5-2> | mono [on|off] | pitch [on|off]]",
|
|
4582
|
+
description: "Show or change the equalizer, reverb and sound effects; a preset name turns it on with that preset",
|
|
4583
|
+
complete: async (arg) => /\s/.test(arg.trim()) ? [] : WORDS.filter((w) => w.startsWith(arg.trim().toLowerCase())),
|
|
4584
|
+
run: async (args, sh) => {
|
|
4585
|
+
const player = playerOf(sh);
|
|
4586
|
+
const arg = args.trim().toLowerCase();
|
|
4587
|
+
if (!arg) {
|
|
4588
|
+
sh.print(describe(player.speed, player.pitchShift));
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
const [sub, ...rest] = arg.split(/\s+/);
|
|
4592
|
+
const apply = async (message) => {
|
|
4593
|
+
await player.setFilter(filterChain());
|
|
4594
|
+
sh.print(message, "ok");
|
|
4595
|
+
};
|
|
4596
|
+
if (arg === "list") {
|
|
4597
|
+
sh.print(EQ_PRESETS.map((p) => `${p.id.padEnd(16)}${p.name}`).join("\n"));
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
if (sub === "reverb") {
|
|
4601
|
+
const [a, b, c] = rest;
|
|
4602
|
+
const mix = a === void 0 ? void 0 : a === "on" || a === "off" ? void 0 : numIn(a, 0, 100, "reverb amount") / 100;
|
|
4603
|
+
const decayArg = b === "decay" ? c : b;
|
|
4604
|
+
const decay = decayArg === void 0 ? void 0 : numIn(decayArg, 1, 8, "reverb decay");
|
|
4605
|
+
const on = a === void 0 ? !effects().reverb : a === "off" ? false : true;
|
|
4606
|
+
if (mix !== void 0) saveEffect("reverbMix", mix);
|
|
4607
|
+
if (decay !== void 0) saveEffect("reverbDecay", decay);
|
|
4608
|
+
saveEffect("reverb", on);
|
|
4609
|
+
const e = effects();
|
|
4610
|
+
await apply(`reverb ${e.reverb ? "on" : "off"} \xB7 mix ${Math.round(e.reverbMix * 100)}% \xB7 decay ${e.reverbDecay}s`);
|
|
4611
|
+
return;
|
|
4612
|
+
}
|
|
4613
|
+
if (sub === "boost") {
|
|
4614
|
+
saveEffect("boost", numIn(rest[0], 100, 200, "boost") / 100);
|
|
4615
|
+
await apply(describe(player.speed, player.pitchShift));
|
|
4616
|
+
return;
|
|
4617
|
+
}
|
|
4618
|
+
if (sub === "balance") {
|
|
4619
|
+
saveEffect("balance", numIn(rest[0], -100, 100, "balance") / 100);
|
|
4620
|
+
await apply(describe(player.speed, player.pitchShift));
|
|
4621
|
+
return;
|
|
4622
|
+
}
|
|
4623
|
+
if (sub === "speed") {
|
|
4624
|
+
const v = rest[0] === "reset" ? 1 : numIn(rest[0]?.replace(/x$/, ""), 0.5, 2, "speed");
|
|
4625
|
+
await player.setSpeed(Math.round(v * 100) / 100);
|
|
4626
|
+
sh.print(`speed ${player.speed}x`, "ok");
|
|
4627
|
+
return;
|
|
4628
|
+
}
|
|
4629
|
+
if (sub === "pitch") {
|
|
4630
|
+
await player.setPitchShift(onOff(rest[0], player.pitchShift));
|
|
4631
|
+
sh.print(`pitch follows speed: ${player.pitchShift ? "on" : "off"}`, "ok");
|
|
4632
|
+
return;
|
|
4633
|
+
}
|
|
4634
|
+
if (sub === "mono") {
|
|
4635
|
+
saveEffect("mono", onOff(rest[0], effects().mono));
|
|
4636
|
+
await apply(`mono ${effects().mono ? "on" : "off"}`);
|
|
4637
|
+
return;
|
|
4638
|
+
}
|
|
4639
|
+
if (arg === "on" || arg === "off") {
|
|
4640
|
+
saveEffect("eq", arg === "on");
|
|
4641
|
+
await apply(describe(player.speed, player.pitchShift));
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4644
|
+
const preset = arg === "reset" ? EQ_PRESETS[0] : pickByName(EQ_PRESETS, (p) => p.id, arg) ?? pickByName(EQ_PRESETS, (p) => p.name, arg);
|
|
4645
|
+
if (!preset) fail(`no preset "${args.trim()}" (try: eq list)`);
|
|
4646
|
+
saveEffect("preset", preset.id);
|
|
4647
|
+
saveEffect("eq", true);
|
|
4648
|
+
await apply(describe(player.speed, player.pitchShift));
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
];
|
|
4652
|
+
|
|
4495
4653
|
// src/karaoke.ts
|
|
4496
4654
|
var TIME = /\[(\d{1,2}):(\d{2})[.:](\d{2,3})\]/g;
|
|
4497
4655
|
function parseLrc(lrc) {
|
|
@@ -5055,6 +5213,7 @@ ${color.dim(`local ${sh.localDir}`)}`)
|
|
|
5055
5213
|
{ name: "du", group: "Files", usage: "du [folder]", path: "dir", description: "How much is in each subfolder, and in total", run: fileTool(diskUsage) },
|
|
5056
5214
|
...LIBRARY_COMMANDS,
|
|
5057
5215
|
...PLAYER_COMMANDS,
|
|
5216
|
+
...EQ_COMMANDS,
|
|
5058
5217
|
...PEOPLE_COMMANDS,
|
|
5059
5218
|
...ADMIN_COMMANDS,
|
|
5060
5219
|
...FUN_COMMANDS,
|