nixamp 0.5.4 → 0.5.5

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/main.js CHANGED
@@ -12,7 +12,7 @@ import { resolve } from "node:path";
12
12
  import { detectTools, formatTime, peaks, RATE, Stream, toMono, } from "./audio.js";
13
13
  import { Analyser, bandEdges, bands, decay } from "./fft.js";
14
14
  import { version } from "./meta.js";
15
- import { displayName, loadSource } from "./playlist.js";
15
+ import { displayName, loadSource, loadTagged } from "./playlist.js";
16
16
  import { isRemote } from "./sources.js";
17
17
  import { DEFAULT_PORT } from "./server.js";
18
18
  const FFT_SIZE = 2048;
@@ -317,7 +317,7 @@ export async function main() {
317
317
  // The titles, arriving into a player that is already up. Not awaited, and
318
318
  // applied only if the list is still the one it describes.
319
319
  if (!isRemote(target)) {
320
- void loadSource(tools, target, true)
320
+ void loadTagged(tools, target)
321
321
  .then((tagged) => {
322
322
  if (tagged.length !== state.tracks.length)
323
323
  return;
@@ -24,4 +24,19 @@ export declare function loadSource(tools: Tools, source: string, probeTags?: boo
24
24
  * the caller decides when to pay for it. Untagged entries still play.
25
25
  */
26
26
  export declare function loadPlaylist(tools: Tools, root: string, probeTags?: boolean): Track[];
27
+ /**
28
+ * The same tags, read without holding the process still.
29
+ *
30
+ * `probe` is a spawnSync per file, so reading a library inside one async
31
+ * function never yields: the listening socket keeps accepting connections,
32
+ * the kernel completes their handshakes, and the process answers none of them
33
+ * until the last file is done. From outside that is indistinguishable from a
34
+ * firewall -- a connection that opens and then says nothing -- and on a library
35
+ * of any size it lasts minutes.
36
+ *
37
+ * One turn of the event loop per file fixes it. A request then waits for one
38
+ * ffprobe rather than for the whole library, and the tagging still finishes in
39
+ * about the time it did.
40
+ */
41
+ export declare function loadTagged(tools: Tools, source: string): Promise<Track[]>;
27
42
  export declare function displayName(track: Track): string;
package/dist/playlist.js CHANGED
@@ -121,6 +121,32 @@ export function loadPlaylist(tools, root, probeTags = true) {
121
121
  artist: "", album: "", duration: 0,
122
122
  });
123
123
  }
124
+ /**
125
+ * The same tags, read without holding the process still.
126
+ *
127
+ * `probe` is a spawnSync per file, so reading a library inside one async
128
+ * function never yields: the listening socket keeps accepting connections,
129
+ * the kernel completes their handshakes, and the process answers none of them
130
+ * until the last file is done. From outside that is indistinguishable from a
131
+ * firewall -- a connection that opens and then says nothing -- and on a library
132
+ * of any size it lasts minutes.
133
+ *
134
+ * One turn of the event loop per file fixes it. A request then waits for one
135
+ * ffprobe rather than for the whole library, and the tagging still finishes in
136
+ * about the time it did.
137
+ */
138
+ export async function loadTagged(tools, source) {
139
+ // A URL is one thing and is never probed; a playlist carries its own titles.
140
+ if (isRemote(source) || isPlaylistFile(source))
141
+ return loadSource(tools, source, true);
142
+ const paths = findAudio(source);
143
+ const tracks = [];
144
+ for (const path of paths) {
145
+ tracks.push(probe(tools, path));
146
+ await new Promise((done) => setImmediate(done));
147
+ }
148
+ return tracks;
149
+ }
124
150
  export function displayName(track) {
125
151
  return track.artist ? `${track.artist} — ${track.title}` : track.title;
126
152
  }
package/dist/server.js CHANGED
@@ -39,7 +39,7 @@ import { extname, join, normalize, resolve, sep } from "node:path";
39
39
  import { fileURLToPath } from "node:url";
40
40
  import { detectTools, peaks, RATE, Stream, toMono, } from "./audio.js";
41
41
  import { Analyser, bandEdges, bands, decay } from "./fft.js";
42
- import { loadSource } from "./playlist.js";
42
+ import { loadSource, loadTagged } from "./playlist.js";
43
43
  import { emptySnapshot, parseCommand, } from "./protocol.js";
44
44
  const FFT_SIZE = 2048;
45
45
  export const SERVE_BAND_COUNT = 24;
@@ -2131,7 +2131,7 @@ export async function serve(argv, version = "0.1.0") {
2131
2131
  // awaited: nothing downstream needs it, and a library that takes a minute to
2132
2132
  // read should cost nobody a minute of silence.
2133
2133
  if (tracks.length > 0 && !isRemote(root)) {
2134
- void loadSource(tools, root, true)
2134
+ void loadTagged(tools, root)
2135
2135
  .then((tagged) => engine.retag(tagged, root))
2136
2136
  .catch(() => {
2137
2137
  // Filenames are a working player. A failure here is worth nothing but
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nixamp",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/main.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  } from "./audio.ts";
16
16
  import { Analyser, bandEdges, bands, decay } from "./fft.ts";
17
17
  import { version } from "./meta.ts";
18
- import { displayName, loadSource } from "./playlist.ts";
18
+ import { displayName, loadSource, loadTagged } from "./playlist.ts";
19
19
  import { isRemote } from "./sources.ts";
20
20
  import { DEFAULT_PORT } from "./server.ts";
21
21
  import type { DaemonState } from "./daemon.ts";
@@ -352,7 +352,7 @@ export async function main(): Promise<void> {
352
352
  // The titles, arriving into a player that is already up. Not awaited, and
353
353
  // applied only if the list is still the one it describes.
354
354
  if (!isRemote(target)) {
355
- void loadSource(tools, target, true)
355
+ void loadTagged(tools, target)
356
356
  .then((tagged) => {
357
357
  if (tagged.length !== state.tracks.length) return;
358
358
  if (tagged.some((track, at) => track.path !== state.tracks[at]?.path)) return;
package/src/playlist.ts CHANGED
@@ -132,6 +132,33 @@ export function loadPlaylist(tools: Tools, root: string, probeTags = true): Trac
132
132
  });
133
133
  }
134
134
 
135
+ /**
136
+ * The same tags, read without holding the process still.
137
+ *
138
+ * `probe` is a spawnSync per file, so reading a library inside one async
139
+ * function never yields: the listening socket keeps accepting connections,
140
+ * the kernel completes their handshakes, and the process answers none of them
141
+ * until the last file is done. From outside that is indistinguishable from a
142
+ * firewall -- a connection that opens and then says nothing -- and on a library
143
+ * of any size it lasts minutes.
144
+ *
145
+ * One turn of the event loop per file fixes it. A request then waits for one
146
+ * ffprobe rather than for the whole library, and the tagging still finishes in
147
+ * about the time it did.
148
+ */
149
+ export async function loadTagged(tools: Tools, source: string): Promise<Track[]> {
150
+ // A URL is one thing and is never probed; a playlist carries its own titles.
151
+ if (isRemote(source) || isPlaylistFile(source)) return loadSource(tools, source, true);
152
+
153
+ const paths = findAudio(source);
154
+ const tracks: Track[] = [];
155
+ for (const path of paths) {
156
+ tracks.push(probe(tools, path));
157
+ await new Promise<void>((done) => setImmediate(done));
158
+ }
159
+ return tracks;
160
+ }
161
+
135
162
  export function displayName(track: Track): string {
136
163
  return track.artist ? `${track.artist} — ${track.title}` : track.title;
137
164
  }
package/src/server.ts CHANGED
@@ -76,7 +76,7 @@ import {
76
76
  type Tools, type Track,
77
77
  } from "./audio.ts";
78
78
  import { Analyser, bandEdges, bands, decay } from "./fft.ts";
79
- import { loadSource } from "./playlist.ts";
79
+ import { loadSource, loadTagged } from "./playlist.ts";
80
80
  import {
81
81
  emptySnapshot, parseCommand,
82
82
  type Command, type RemoteTrack, type Snapshot,
@@ -2448,7 +2448,7 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
2448
2448
  // awaited: nothing downstream needs it, and a library that takes a minute to
2449
2449
  // read should cost nobody a minute of silence.
2450
2450
  if (tracks.length > 0 && !isRemote(root)) {
2451
- void loadSource(tools, root, true)
2451
+ void loadTagged(tools, root)
2452
2452
  .then((tagged) => engine.retag(tagged, root))
2453
2453
  .catch(() => {
2454
2454
  // Filenames are a working player. A failure here is worth nothing but
package/web/dist/sw.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* nixamp service worker — generated, do not edit */
2
- const CACHE = "nixamp-1788937565929";
2
+ const CACHE = "nixamp-1788938369729";
3
3
  const PRECACHE = [
4
4
  "/",
5
5
  "/apple-touch-icon.png",