nixamp 0.6.0 → 0.6.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/dist/admin.d.ts +13 -0
- package/dist/admin.js +27 -3
- package/dist/server.d.ts +6 -0
- package/dist/server.js +13 -1
- package/package.json +1 -1
- package/src/admin.ts +26 -2
- package/src/server.ts +19 -1
- package/web/dist/sw.js +1 -1
package/dist/admin.d.ts
CHANGED
|
@@ -46,6 +46,19 @@ export interface AdminOptions {
|
|
|
46
46
|
export declare function resolveTarget(argv: string[]): AdminOptions;
|
|
47
47
|
/** Seconds as something a person reads at a glance. */
|
|
48
48
|
export declare function since(ms: number): string;
|
|
49
|
+
/**
|
|
50
|
+
* What a keypress contributes to a field being typed into.
|
|
51
|
+
*
|
|
52
|
+
* A paste is one event carrying the whole string, not a burst of single
|
|
53
|
+
* characters, so a handler that only accepted `key.length === 1` accepted
|
|
54
|
+
* nothing at all from a paste -- which is how you find you cannot put a URL in
|
|
55
|
+
* the box by any means except typing it out.
|
|
56
|
+
*
|
|
57
|
+
* The ambiguity is real and unavoidable: a pasted word of bare letters is
|
|
58
|
+
* indistinguishable from a key name, and loses. A URL or a path never is,
|
|
59
|
+
* because neither is spelled with letters alone.
|
|
60
|
+
*/
|
|
61
|
+
export declare function typed(key: string): string;
|
|
49
62
|
export declare function bytes(value: number): string;
|
|
50
63
|
export declare function admin(argv: string[]): Promise<void>;
|
|
51
64
|
export interface View {
|
package/dist/admin.js
CHANGED
|
@@ -44,6 +44,31 @@ export function since(ms) {
|
|
|
44
44
|
return `${hours}h ${minutes % 60}m`;
|
|
45
45
|
return `${Math.floor(hours / 24)}d ${hours % 24}h`;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* A key name rather than something somebody typed: "up", "f7", "ctrl+c".
|
|
49
|
+
* Letters and digits only, so anything with a colon or a slash in it is text.
|
|
50
|
+
*/
|
|
51
|
+
const NAMED_KEY = /^(?:[a-z]+\d*|(?:ctrl|alt|shift|meta)\+.+)$/;
|
|
52
|
+
/**
|
|
53
|
+
* What a keypress contributes to a field being typed into.
|
|
54
|
+
*
|
|
55
|
+
* A paste is one event carrying the whole string, not a burst of single
|
|
56
|
+
* characters, so a handler that only accepted `key.length === 1` accepted
|
|
57
|
+
* nothing at all from a paste -- which is how you find you cannot put a URL in
|
|
58
|
+
* the box by any means except typing it out.
|
|
59
|
+
*
|
|
60
|
+
* The ambiguity is real and unavoidable: a pasted word of bare letters is
|
|
61
|
+
* indistinguishable from a key name, and loses. A URL or a path never is,
|
|
62
|
+
* because neither is spelled with letters alone.
|
|
63
|
+
*/
|
|
64
|
+
export function typed(key) {
|
|
65
|
+
if (key.length === 1)
|
|
66
|
+
return key >= " " && key !== "\u007f" ? key : "";
|
|
67
|
+
if (NAMED_KEY.test(key))
|
|
68
|
+
return "";
|
|
69
|
+
// A paste. Control characters and newlines are not part of an address.
|
|
70
|
+
return key.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
71
|
+
}
|
|
47
72
|
export function bytes(value) {
|
|
48
73
|
const units = ["B", "KiB", "MiB", "GiB"];
|
|
49
74
|
let n = value;
|
|
@@ -109,9 +134,8 @@ export async function admin(argv) {
|
|
|
109
134
|
}
|
|
110
135
|
else if (key === "backspace")
|
|
111
136
|
restreaming = restreaming.slice(0, -1);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
restreaming += key;
|
|
137
|
+
else
|
|
138
|
+
restreaming += typed(key);
|
|
115
139
|
app.invalidate();
|
|
116
140
|
return;
|
|
117
141
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -239,6 +239,12 @@ export interface HandlerOptions {
|
|
|
239
239
|
* imported so the handler stays a plain function of a request.
|
|
240
240
|
*/
|
|
241
241
|
load: (source: string) => Promise<Track[]>;
|
|
242
|
+
/**
|
|
243
|
+
* The same source, with its tags, read without holding the event loop. Called
|
|
244
|
+
* after `load` and never awaited: the titles arrive into a player that is
|
|
245
|
+
* already playing.
|
|
246
|
+
*/
|
|
247
|
+
tag?: (source: string) => Promise<Track[]>;
|
|
242
248
|
/**
|
|
243
249
|
* The public directory, on the instance that hosts one. Only nixamp.com
|
|
244
250
|
* passes this; a nixamp on your laptop is a publisher, not a registry.
|
package/dist/server.js
CHANGED
|
@@ -1623,6 +1623,15 @@ export function createHandler(engine, options) {
|
|
|
1623
1623
|
return;
|
|
1624
1624
|
}
|
|
1625
1625
|
engine.replace(tracks, source);
|
|
1626
|
+
// Names now, tags later, here as much as at startup: re-streaming a
|
|
1627
|
+
// directory of five thousand files used to read every tag before it
|
|
1628
|
+
// answered, with the event loop held the whole time.
|
|
1629
|
+
if (options.tag) {
|
|
1630
|
+
void options
|
|
1631
|
+
.tag(source)
|
|
1632
|
+
.then((tagged) => engine.retag(tagged, source))
|
|
1633
|
+
.catch(() => { });
|
|
1634
|
+
}
|
|
1626
1635
|
json(response, 200, engine.snapshot());
|
|
1627
1636
|
}
|
|
1628
1637
|
catch (error) {
|
|
@@ -2234,7 +2243,10 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
2234
2243
|
ffmpeg: tools.ffmpeg,
|
|
2235
2244
|
ffprobe: tools.ffprobe,
|
|
2236
2245
|
...(tls ? { tls } : {}),
|
|
2237
|
-
|
|
2246
|
+
// Untagged, so a directory of five thousand files answers at once; the
|
|
2247
|
+
// tags follow through `tag` below.
|
|
2248
|
+
load: (next) => loadSource(tools, next, false),
|
|
2249
|
+
tag: (next) => loadTagged(tools, next),
|
|
2238
2250
|
...(directory ? { directory } : {}),
|
|
2239
2251
|
...(follows ? { follows, vapidPublicKey } : {}),
|
|
2240
2252
|
...(partyLine ? { partyLine } : {}),
|
package/package.json
CHANGED
package/src/admin.ts
CHANGED
|
@@ -79,6 +79,31 @@ export function since(ms: number): string {
|
|
|
79
79
|
return `${Math.floor(hours / 24)}d ${hours % 24}h`;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* A key name rather than something somebody typed: "up", "f7", "ctrl+c".
|
|
84
|
+
* Letters and digits only, so anything with a colon or a slash in it is text.
|
|
85
|
+
*/
|
|
86
|
+
const NAMED_KEY = /^(?:[a-z]+\d*|(?:ctrl|alt|shift|meta)\+.+)$/;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* What a keypress contributes to a field being typed into.
|
|
90
|
+
*
|
|
91
|
+
* A paste is one event carrying the whole string, not a burst of single
|
|
92
|
+
* characters, so a handler that only accepted `key.length === 1` accepted
|
|
93
|
+
* nothing at all from a paste -- which is how you find you cannot put a URL in
|
|
94
|
+
* the box by any means except typing it out.
|
|
95
|
+
*
|
|
96
|
+
* The ambiguity is real and unavoidable: a pasted word of bare letters is
|
|
97
|
+
* indistinguishable from a key name, and loses. A URL or a path never is,
|
|
98
|
+
* because neither is spelled with letters alone.
|
|
99
|
+
*/
|
|
100
|
+
export function typed(key: string): string {
|
|
101
|
+
if (key.length === 1) return key >= " " && key !== "\u007f" ? key : "";
|
|
102
|
+
if (NAMED_KEY.test(key)) return "";
|
|
103
|
+
// A paste. Control characters and newlines are not part of an address.
|
|
104
|
+
return key.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
105
|
+
}
|
|
106
|
+
|
|
82
107
|
export function bytes(value: number): string {
|
|
83
108
|
const units = ["B", "KiB", "MiB", "GiB"];
|
|
84
109
|
let n = value;
|
|
@@ -142,8 +167,7 @@ export async function admin(argv: string[]): Promise<void> {
|
|
|
142
167
|
restreaming = "";
|
|
143
168
|
if (url) void restream(target, headers, url).then(() => refresh());
|
|
144
169
|
} else if (key === "backspace") restreaming = restreaming.slice(0, -1);
|
|
145
|
-
|
|
146
|
-
else if (key.length === 1) restreaming += key;
|
|
170
|
+
else restreaming += typed(key);
|
|
147
171
|
app.invalidate();
|
|
148
172
|
return;
|
|
149
173
|
}
|
package/src/server.ts
CHANGED
|
@@ -751,6 +751,12 @@ export interface HandlerOptions {
|
|
|
751
751
|
* imported so the handler stays a plain function of a request.
|
|
752
752
|
*/
|
|
753
753
|
load: (source: string) => Promise<Track[]>;
|
|
754
|
+
/**
|
|
755
|
+
* The same source, with its tags, read without holding the event loop. Called
|
|
756
|
+
* after `load` and never awaited: the titles arrive into a player that is
|
|
757
|
+
* already playing.
|
|
758
|
+
*/
|
|
759
|
+
tag?: (source: string) => Promise<Track[]>;
|
|
754
760
|
/**
|
|
755
761
|
* The public directory, on the instance that hosts one. Only nixamp.com
|
|
756
762
|
* passes this; a nixamp on your laptop is a publisher, not a registry.
|
|
@@ -1916,6 +1922,15 @@ export function createHandler(engine: Engine, options: HandlerOptions) {
|
|
|
1916
1922
|
return;
|
|
1917
1923
|
}
|
|
1918
1924
|
engine.replace(tracks, source);
|
|
1925
|
+
// Names now, tags later, here as much as at startup: re-streaming a
|
|
1926
|
+
// directory of five thousand files used to read every tag before it
|
|
1927
|
+
// answered, with the event loop held the whole time.
|
|
1928
|
+
if (options.tag) {
|
|
1929
|
+
void options
|
|
1930
|
+
.tag(source)
|
|
1931
|
+
.then((tagged) => engine.retag(tagged, source))
|
|
1932
|
+
.catch(() => {});
|
|
1933
|
+
}
|
|
1919
1934
|
json(response, 200, engine.snapshot());
|
|
1920
1935
|
} catch (error) {
|
|
1921
1936
|
json(response, 422, { error: (error as Error).message.replace(/^nixamp: /, "") });
|
|
@@ -2577,7 +2592,10 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
|
|
|
2577
2592
|
ffmpeg: tools.ffmpeg,
|
|
2578
2593
|
ffprobe: tools.ffprobe,
|
|
2579
2594
|
...(tls ? { tls } : {}),
|
|
2580
|
-
|
|
2595
|
+
// Untagged, so a directory of five thousand files answers at once; the
|
|
2596
|
+
// tags follow through `tag` below.
|
|
2597
|
+
load: (next) => loadSource(tools, next, false),
|
|
2598
|
+
tag: (next) => loadTagged(tools, next),
|
|
2581
2599
|
...(directory ? { directory } : {}),
|
|
2582
2600
|
...(follows ? { follows, vapidPublicKey } : {}),
|
|
2583
2601
|
...(partyLine ? { partyLine } : {}),
|
package/web/dist/sw.js
CHANGED