nixamp 0.5.7 → 0.5.9
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/daemon.d.ts +3 -3
- package/dist/daemon.js +22 -3
- package/dist/main.js +5 -5
- package/dist/protocol.d.ts +8 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +10 -0
- package/package.json +1 -1
- package/src/daemon.ts +19 -3
- package/src/main.ts +4 -5
- package/src/protocol.ts +8 -0
- package/src/server.ts +12 -0
- package/web/dist/assets/hls-3VKVEQE3-Cj5Lwh7A.js +1 -0
- package/web/dist/assets/hls-n74Cnh8A.js +42 -0
- package/web/dist/assets/index-ABOb54bx.js +1 -0
- package/web/dist/assets/mpegts-CKFUPaK9.js +3 -0
- package/web/dist/assets/mpegts-LO6RVLD6-Bj_ZfP5s.js +1 -0
- package/web/dist/assets/native-C7JTKWJH-BUyIoj0P.js +1 -0
- package/web/dist/index.html +1 -1
- package/web/dist/sw.js +7 -2
- package/web/dist/assets/index-BD37tdcP.js +0 -1
package/dist/daemon.d.ts
CHANGED
|
@@ -49,8 +49,8 @@ export declare function status(): {
|
|
|
49
49
|
/** The URL an admin client should talk to. */
|
|
50
50
|
export declare function daemonUrl(state: DaemonState): string;
|
|
51
51
|
/**
|
|
52
|
-
* What `nixamp daemon start`
|
|
53
|
-
* starting a daemon.
|
|
52
|
+
* What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
|
|
53
|
+
* can be tested without starting a daemon.
|
|
54
54
|
*
|
|
55
55
|
* Every address the server found, not one loopback link: the point of a daemon
|
|
56
56
|
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
@@ -58,7 +58,7 @@ export declare function daemonUrl(state: DaemonState): string;
|
|
|
58
58
|
* server writes that into a log file nobody reads, not to the person who just
|
|
59
59
|
* typed the command.
|
|
60
60
|
*/
|
|
61
|
-
export declare function daemonLines(state: DaemonState): string[];
|
|
61
|
+
export declare function daemonLines(state: DaemonState, uptimeMs?: number): string[];
|
|
62
62
|
/**
|
|
63
63
|
* Start one, detached, and wait until it is actually answering before saying
|
|
64
64
|
* it started. Reporting success and leaving the user to discover a crash in a
|
package/dist/daemon.js
CHANGED
|
@@ -65,8 +65,25 @@ export function daemonUrl(state) {
|
|
|
65
65
|
return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
68
|
+
* How long it has been up, as a person would say it. Written here rather than
|
|
69
|
+
* borrowed from admin.ts, which would drag the whole terminal UI into a code
|
|
70
|
+
* path that only prints four lines.
|
|
71
|
+
*/
|
|
72
|
+
function spell(ms) {
|
|
73
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
74
|
+
if (seconds < 60)
|
|
75
|
+
return `${seconds}s`;
|
|
76
|
+
const minutes = Math.floor(seconds / 60);
|
|
77
|
+
if (minutes < 60)
|
|
78
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
79
|
+
const hours = Math.floor(minutes / 60);
|
|
80
|
+
if (hours < 24)
|
|
81
|
+
return `${hours}h ${minutes % 60}m`;
|
|
82
|
+
return `${Math.floor(hours / 24)}d ${hours % 24}h`;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
|
|
86
|
+
* can be tested without starting a daemon.
|
|
70
87
|
*
|
|
71
88
|
* Every address the server found, not one loopback link: the point of a daemon
|
|
72
89
|
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
@@ -74,7 +91,7 @@ export function daemonUrl(state) {
|
|
|
74
91
|
* server writes that into a log file nobody reads, not to the person who just
|
|
75
92
|
* typed the command.
|
|
76
93
|
*/
|
|
77
|
-
export function daemonLines(state) {
|
|
94
|
+
export function daemonLines(state, uptimeMs) {
|
|
78
95
|
const link = (url) => (state.key ? `${url}/s/${state.key}` : url);
|
|
79
96
|
// A state file written by an older nixamp has no list, so host and port
|
|
80
97
|
// still stand in rather than printing nothing at all.
|
|
@@ -84,6 +101,8 @@ export function daemonLines(state) {
|
|
|
84
101
|
for (const { label, url } of addresses)
|
|
85
102
|
lines.push(` ${label.padEnd(width)} ${link(url)}`);
|
|
86
103
|
lines.push(` ${"source".padEnd(width)} ${state.source}`);
|
|
104
|
+
if (uptimeMs !== undefined)
|
|
105
|
+
lines.push(` ${"up".padEnd(width)} ${spell(uptimeMs)}`);
|
|
87
106
|
if (state.guessedPublic) {
|
|
88
107
|
lines.push("", " That internet address is this machine's router, not this port.", ` Nothing outside reaches it until ${state.port} is forwarded here.`);
|
|
89
108
|
}
|
package/dist/main.js
CHANGED
|
@@ -223,11 +223,11 @@ async function runDaemon(argv) {
|
|
|
223
223
|
console.log(`nixamp: the daemon (pid ${state.pid}) is gone. See ${state.log}`);
|
|
224
224
|
return 1;
|
|
225
225
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
// The same lines start prints, because the question "where is it" has the
|
|
227
|
+
// same answer however you ask it -- and printing loopback alone was the
|
|
228
|
+
// one address that cannot be handed to anybody.
|
|
229
|
+
for (const line of d.daemonLines(state, Date.now() - state.startedAt))
|
|
230
|
+
console.log(line);
|
|
231
231
|
return 0;
|
|
232
232
|
}
|
|
233
233
|
// `nixamp daemon attach` is what people try before `nixamp attach`, so it is
|
package/dist/protocol.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ export interface RemoteTrack {
|
|
|
12
12
|
album: string;
|
|
13
13
|
/** Seconds; 0 when ffprobe could not tell us. */
|
|
14
14
|
duration: number;
|
|
15
|
+
/**
|
|
16
|
+
* Whether this is a film rather than a song.
|
|
17
|
+
*
|
|
18
|
+
* The server knows, because it has the path; a remote had been guessing, and
|
|
19
|
+
* guessing wrong -- every remote track went to the audio element, so a video
|
|
20
|
+
* a browser could show played its soundtrack over a blank panel.
|
|
21
|
+
*/
|
|
22
|
+
video?: boolean;
|
|
15
23
|
}
|
|
16
24
|
/** Everything a remote needs to draw the player. */
|
|
17
25
|
export interface Snapshot {
|
package/dist/server.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export interface Engine {
|
|
|
135
135
|
stop(): void;
|
|
136
136
|
}
|
|
137
137
|
export declare function toRemoteTracks(tracks: Track[]): RemoteTrack[];
|
|
138
|
+
export declare function hasPicture(path: string): boolean;
|
|
138
139
|
/**
|
|
139
140
|
* The headless player: the terminal app's engine without the terminal.
|
|
140
141
|
* One ffmpeg decodes, ffplay makes the sound, and every sample is measured on
|
package/dist/server.js
CHANGED
|
@@ -269,8 +269,18 @@ export function toRemoteTracks(tracks) {
|
|
|
269
269
|
artist: t.artist,
|
|
270
270
|
album: t.album,
|
|
271
271
|
duration: t.duration,
|
|
272
|
+
// Said out loud, because a remote cannot see the path and had been sending
|
|
273
|
+
// every track to the audio element -- a film's soundtrack over a blank
|
|
274
|
+
// panel, which is exactly what it looked like.
|
|
275
|
+
...(hasPicture(t.path) ? { video: true } : {}),
|
|
272
276
|
}));
|
|
273
277
|
}
|
|
278
|
+
/** Video containers, as opposed to the songs that are most of a library. */
|
|
279
|
+
const PICTURE = new Set([".mp4", ".mkv", ".avi", ".mov", ".m4v", ".webm", ".mpg", ".mpeg", ".wmv", ".flv"]);
|
|
280
|
+
export function hasPicture(path) {
|
|
281
|
+
const dot = path.lastIndexOf(".");
|
|
282
|
+
return dot > 0 && PICTURE.has(path.slice(dot).toLowerCase());
|
|
283
|
+
}
|
|
274
284
|
/**
|
|
275
285
|
* The headless player: the terminal app's engine without the terminal.
|
|
276
286
|
* One ffmpeg decodes, ffplay makes the sound, and every sample is measured on
|
package/package.json
CHANGED
package/src/daemon.ts
CHANGED
|
@@ -100,8 +100,23 @@ export function daemonUrl(state: DaemonState): string {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
103
|
+
* How long it has been up, as a person would say it. Written here rather than
|
|
104
|
+
* borrowed from admin.ts, which would drag the whole terminal UI into a code
|
|
105
|
+
* path that only prints four lines.
|
|
106
|
+
*/
|
|
107
|
+
function spell(ms: number): string {
|
|
108
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
109
|
+
if (seconds < 60) return `${seconds}s`;
|
|
110
|
+
const minutes = Math.floor(seconds / 60);
|
|
111
|
+
if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
|
|
112
|
+
const hours = Math.floor(minutes / 60);
|
|
113
|
+
if (hours < 24) return `${hours}h ${minutes % 60}m`;
|
|
114
|
+
return `${Math.floor(hours / 24)}d ${hours % 24}h`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
|
|
119
|
+
* can be tested without starting a daemon.
|
|
105
120
|
*
|
|
106
121
|
* Every address the server found, not one loopback link: the point of a daemon
|
|
107
122
|
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
@@ -109,7 +124,7 @@ export function daemonUrl(state: DaemonState): string {
|
|
|
109
124
|
* server writes that into a log file nobody reads, not to the person who just
|
|
110
125
|
* typed the command.
|
|
111
126
|
*/
|
|
112
|
-
export function daemonLines(state: DaemonState): string[] {
|
|
127
|
+
export function daemonLines(state: DaemonState, uptimeMs?: number): string[] {
|
|
113
128
|
const link = (url: string): string => (state.key ? `${url}/s/${state.key}` : url);
|
|
114
129
|
// A state file written by an older nixamp has no list, so host and port
|
|
115
130
|
// still stand in rather than printing nothing at all.
|
|
@@ -119,6 +134,7 @@ export function daemonLines(state: DaemonState): string[] {
|
|
|
119
134
|
const lines = [`nixamp daemon running (pid ${state.pid})`];
|
|
120
135
|
for (const { label, url } of addresses) lines.push(` ${label.padEnd(width)} ${link(url)}`);
|
|
121
136
|
lines.push(` ${"source".padEnd(width)} ${state.source}`);
|
|
137
|
+
if (uptimeMs !== undefined) lines.push(` ${"up".padEnd(width)} ${spell(uptimeMs)}`);
|
|
122
138
|
|
|
123
139
|
if (state.guessedPublic) {
|
|
124
140
|
lines.push(
|
package/src/main.ts
CHANGED
|
@@ -252,11 +252,10 @@ async function runDaemon(argv: string[]): Promise<number> {
|
|
|
252
252
|
console.log(`nixamp: the daemon (pid ${state.pid}) is gone. See ${state.log}`);
|
|
253
253
|
return 1;
|
|
254
254
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
console.log(` up ${Math.round((Date.now() - state.startedAt) / 1000)}s`);
|
|
255
|
+
// The same lines start prints, because the question "where is it" has the
|
|
256
|
+
// same answer however you ask it -- and printing loopback alone was the
|
|
257
|
+
// one address that cannot be handed to anybody.
|
|
258
|
+
for (const line of d.daemonLines(state, Date.now() - state.startedAt)) console.log(line);
|
|
260
259
|
return 0;
|
|
261
260
|
}
|
|
262
261
|
|
package/src/protocol.ts
CHANGED
|
@@ -13,6 +13,14 @@ export interface RemoteTrack {
|
|
|
13
13
|
album: string;
|
|
14
14
|
/** Seconds; 0 when ffprobe could not tell us. */
|
|
15
15
|
duration: number;
|
|
16
|
+
/**
|
|
17
|
+
* Whether this is a film rather than a song.
|
|
18
|
+
*
|
|
19
|
+
* The server knows, because it has the path; a remote had been guessing, and
|
|
20
|
+
* guessing wrong -- every remote track went to the audio element, so a video
|
|
21
|
+
* a browser could show played its soundtrack over a blank panel.
|
|
22
|
+
*/
|
|
23
|
+
video?: boolean;
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
/** Everything a remote needs to draw the player. */
|
package/src/server.ts
CHANGED
|
@@ -391,9 +391,21 @@ export function toRemoteTracks(tracks: Track[]): RemoteTrack[] {
|
|
|
391
391
|
artist: t.artist,
|
|
392
392
|
album: t.album,
|
|
393
393
|
duration: t.duration,
|
|
394
|
+
// Said out loud, because a remote cannot see the path and had been sending
|
|
395
|
+
// every track to the audio element -- a film's soundtrack over a blank
|
|
396
|
+
// panel, which is exactly what it looked like.
|
|
397
|
+
...(hasPicture(t.path) ? { video: true } : {}),
|
|
394
398
|
}));
|
|
395
399
|
}
|
|
396
400
|
|
|
401
|
+
/** Video containers, as opposed to the songs that are most of a library. */
|
|
402
|
+
const PICTURE = new Set([".mp4", ".mkv", ".avi", ".mov", ".m4v", ".webm", ".mpg", ".mpeg", ".wmv", ".flv"]);
|
|
403
|
+
|
|
404
|
+
export function hasPicture(path: string): boolean {
|
|
405
|
+
const dot = path.lastIndexOf(".");
|
|
406
|
+
return dot > 0 && PICTURE.has(path.slice(dot).toLowerCase());
|
|
407
|
+
}
|
|
408
|
+
|
|
397
409
|
/**
|
|
398
410
|
* The headless player: the terminal app's engine without the terminal.
|
|
399
411
|
* One ffmpeg decodes, ffplay makes the sound, and every sample is measured on
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./index-ABOb54bx.js";var t=3;async function n(n){let{media:r,src:i,isTv:a}=n,{default:o}=await e(async()=>{let{default:e}=await import(`./hls-n74Cnh8A.js`);return{default:e}},[]);if(!o.isSupported())return n.onError(`This browser cannot play HLS streams.`),{destroy:()=>void 0,levels:()=>[]};let s=new o({...a?{maxBufferLength:60,maxMaxBufferLength:120,backBufferLength:30,liveSyncDurationCount:4}:{backBufferLength:90},enableWorker:!0}),c=0,l=!1;s.on(o.Events.ERROR,(e,r)=>{if(!l&&r.fatal){if(c>=t){n.onError(`This stream kept failing and has been stopped.`),s.destroy();return}switch(c+=1,r.type){case o.ErrorTypes.NETWORK_ERROR:n.onNotice(`Reconnecting…`),s.startLoad();break;case o.ErrorTypes.MEDIA_ERROR:n.onNotice(`Recovering…`),s.recoverMediaError();break;default:n.onError(`This stream could not be played.`),s.destroy()}}}),s.on(o.Events.MANIFEST_PARSED,()=>{l||(n.onNotice(null),n.onReady?.({live:s.levels.length>0&&!Number.isFinite(r.duration),levels:u()}))}),s.on(o.Events.LEVEL_LOADED,(e,t)=>{l||n.onReady?.({live:t.details.live,levels:u()})}),s.on(o.Events.FRAG_BUFFERED,()=>{l||n.onNotice(null)});function u(){return s.levels.map((e,t)=>({index:t,height:e.height||null,bitrate:e.bitrate||null,label:e.height?`${String(e.height)}p`:`${String(Math.round((e.bitrate||0)/1e3))}k`}))}return s.loadSource(i),s.attachMedia(r),{destroy(){l=!0,s.destroy()},levels:u,setLevel(e){s.currentLevel=e},currentLevel:()=>s.autoLevelEnabled?-1:s.currentLevel}}export{n as createHlsEngine};
|