lox-airplay-sender 0.2.1 → 0.3.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/README.md +3 -1
- package/dist/core/audioOut.js +4 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +40 -1
- package/dist/esm/core/audioOut.js +4 -0
- package/dist/esm/core/index.js +40 -1
- package/dist/esm/index.js +41 -1
- package/dist/esm/utils/config.js +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +41 -1
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,11 +57,13 @@ Creates and starts a sender for one AirPlay device. Returns the instance so you
|
|
|
57
57
|
- `startTimeMs` (number) Unix ms to align playback across devices.
|
|
58
58
|
- `debug` (boolean) Verbose logging from the transport stack.
|
|
59
59
|
- `log` `(level, message, data?) => void` Hook for library logs.
|
|
60
|
+
- `config` (partial) Override buffer/sync/RTSP tuning at runtime (see `src/utils/config.ts` for keys like `packets_in_buffer`, `stream_latency`, `sync_period`, retry/backoff, etc.).
|
|
60
61
|
|
|
61
62
|
**Events** (sent to `onEvent` callback)
|
|
62
63
|
- `device`: `{ event: "device", message: status, detail: { key, desc } }`
|
|
63
64
|
- `buffer`: `{ event: "buffer", message: status }` where status is `buffering|playing|drain|end`
|
|
64
65
|
- `error`: `{ event: "error", message }`
|
|
66
|
+
- `metrics`: `{ event: "metrics", detail }` sync drift snapshots emitted on each sync tick when enabled.
|
|
65
67
|
|
|
66
68
|
### `LoxAirplaySender` methods
|
|
67
69
|
- `sendPcm(chunk: Buffer)`: Push raw PCM audio. If `inputCodec` is `"alac"` you can push ALAC frames.
|
|
@@ -82,4 +84,4 @@ Use `startTimeMs` to align multiple senders to the same start clock (Unix ms). F
|
|
|
82
84
|
npm install
|
|
83
85
|
npm run build
|
|
84
86
|
npm run clean # remove dist
|
|
85
|
-
```
|
|
87
|
+
```
|
package/dist/core/audioOut.js
CHANGED
|
@@ -42,6 +42,10 @@ class AudioOut extends node_events_1.EventEmitter {
|
|
|
42
42
|
packet.timestamp = (0, numUtil_1.low32)(seq * config_1.default.frames_per_packet + 2 * config_1.default.sampling_rate);
|
|
43
43
|
if (this.hasAirTunes && seq % config_1.default.sync_period === 0) {
|
|
44
44
|
this.emit('need_sync', seq);
|
|
45
|
+
const expectedTimeMs = this.rtpTimeRef +
|
|
46
|
+
((seq * config_1.default.frames_per_packet) / config_1.default.sampling_rate) * 1000;
|
|
47
|
+
const deltaMs = Date.now() - expectedTimeMs;
|
|
48
|
+
this.emit('metrics', { type: 'sync', seq, deltaMs, latencyFrames: this.latencyFrames });
|
|
45
49
|
}
|
|
46
50
|
this.emit('packet', packet);
|
|
47
51
|
packet.release();
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Duplex } from 'node:stream';
|
|
2
2
|
import Devices from './devices';
|
|
3
|
+
import { type AirplayConfig } from '../utils/config';
|
|
3
4
|
/**
|
|
4
5
|
* High-level RAOP/AirPlay sender that wires together devices, buffering, and output.
|
|
5
6
|
* Acts as a Duplex stream: write PCM/ALAC chunks, listen to status events.
|
|
@@ -14,6 +15,7 @@ declare class AirTunes extends Duplex {
|
|
|
14
15
|
constructor(options?: {
|
|
15
16
|
packetSize?: number;
|
|
16
17
|
startTimeMs?: number;
|
|
18
|
+
config?: Partial<AirplayConfig>;
|
|
17
19
|
});
|
|
18
20
|
/** Register an AirTunes (RAOP) device and start streaming to it. */
|
|
19
21
|
add(host: string, options: Record<string, unknown>, mode?: number, txt?: string[] | string): any;
|
package/dist/core/index.js
CHANGED
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
const node_stream_1 = require("node:stream");
|
|
7
40
|
const devices_1 = __importDefault(require("./devices"));
|
|
8
|
-
const config_1 =
|
|
41
|
+
const config_1 = __importStar(require("../utils/config"));
|
|
9
42
|
const circularBuffer_1 = __importDefault(require("../utils/circularBuffer"));
|
|
10
43
|
const audioOut_1 = __importDefault(require("./audioOut"));
|
|
11
44
|
/**
|
|
@@ -21,6 +54,9 @@ class AirTunes extends node_stream_1.Duplex {
|
|
|
21
54
|
*/
|
|
22
55
|
constructor(options = {}) {
|
|
23
56
|
super({ readableObjectMode: false, writableObjectMode: false });
|
|
57
|
+
if (options.config) {
|
|
58
|
+
(0, config_1.applyConfig)(options.config);
|
|
59
|
+
}
|
|
24
60
|
const audioOut = new audioOut_1.default();
|
|
25
61
|
this.devices = new devices_1.default(audioOut);
|
|
26
62
|
this.devices.init();
|
|
@@ -33,6 +69,9 @@ class AirTunes extends node_stream_1.Duplex {
|
|
|
33
69
|
this.emit('buffer', status);
|
|
34
70
|
});
|
|
35
71
|
audioOut.init(this.devices, this.circularBuffer, options.startTimeMs);
|
|
72
|
+
audioOut.on('metrics', (metrics) => {
|
|
73
|
+
this.emit('metrics', metrics);
|
|
74
|
+
});
|
|
36
75
|
this.circularBuffer.on('drain', () => {
|
|
37
76
|
this.emit('drain');
|
|
38
77
|
});
|
|
@@ -42,6 +42,10 @@ class AudioOut extends node_events_1.EventEmitter {
|
|
|
42
42
|
packet.timestamp = (0, numUtil_1.low32)(seq * config_1.default.frames_per_packet + 2 * config_1.default.sampling_rate);
|
|
43
43
|
if (this.hasAirTunes && seq % config_1.default.sync_period === 0) {
|
|
44
44
|
this.emit('need_sync', seq);
|
|
45
|
+
const expectedTimeMs = this.rtpTimeRef +
|
|
46
|
+
((seq * config_1.default.frames_per_packet) / config_1.default.sampling_rate) * 1000;
|
|
47
|
+
const deltaMs = Date.now() - expectedTimeMs;
|
|
48
|
+
this.emit('metrics', { type: 'sync', seq, deltaMs, latencyFrames: this.latencyFrames });
|
|
45
49
|
}
|
|
46
50
|
this.emit('packet', packet);
|
|
47
51
|
packet.release();
|
package/dist/esm/core/index.js
CHANGED
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
const node_stream_1 = require("node:stream");
|
|
7
40
|
const devices_1 = __importDefault(require("./devices"));
|
|
8
|
-
const config_1 =
|
|
41
|
+
const config_1 = __importStar(require("../utils/config"));
|
|
9
42
|
const circularBuffer_1 = __importDefault(require("../utils/circularBuffer"));
|
|
10
43
|
const audioOut_1 = __importDefault(require("./audioOut"));
|
|
11
44
|
/**
|
|
@@ -21,6 +54,9 @@ class AirTunes extends node_stream_1.Duplex {
|
|
|
21
54
|
*/
|
|
22
55
|
constructor(options = {}) {
|
|
23
56
|
super({ readableObjectMode: false, writableObjectMode: false });
|
|
57
|
+
if (options.config) {
|
|
58
|
+
(0, config_1.applyConfig)(options.config);
|
|
59
|
+
}
|
|
24
60
|
const audioOut = new audioOut_1.default();
|
|
25
61
|
this.devices = new devices_1.default(audioOut);
|
|
26
62
|
this.devices.init();
|
|
@@ -33,6 +69,9 @@ class AirTunes extends node_stream_1.Duplex {
|
|
|
33
69
|
this.emit('buffer', status);
|
|
34
70
|
});
|
|
35
71
|
audioOut.init(this.devices, this.circularBuffer, options.startTimeMs);
|
|
72
|
+
audioOut.on('metrics', (metrics) => {
|
|
73
|
+
this.emit('metrics', metrics);
|
|
74
|
+
});
|
|
36
75
|
this.circularBuffer.on('drain', () => {
|
|
37
76
|
this.emit('drain');
|
|
38
77
|
});
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -8,7 +41,7 @@ exports.start = start;
|
|
|
8
41
|
const node_events_1 = require("node:events");
|
|
9
42
|
// Airtunes implementation (node_airtunes2 port) in src/core.
|
|
10
43
|
const index_1 = __importDefault(require("./core/index"));
|
|
11
|
-
const config_1 =
|
|
44
|
+
const config_1 = __importStar(require("./utils/config"));
|
|
12
45
|
class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
13
46
|
airtunes = null;
|
|
14
47
|
deviceKey = null;
|
|
@@ -32,11 +65,15 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
|
32
65
|
this.stop();
|
|
33
66
|
}
|
|
34
67
|
this.log = options.log;
|
|
68
|
+
if (options.config) {
|
|
69
|
+
(0, config_1.applyConfig)(options.config);
|
|
70
|
+
}
|
|
35
71
|
const inputCodec = options.inputCodec ?? 'pcm';
|
|
36
72
|
config_1.default.packet_size = inputCodec === 'alac' ? config_1.default.alac_packet_size : config_1.default.pcm_packet_size;
|
|
37
73
|
this.airtunes = new index_1.default({
|
|
38
74
|
packetSize: config_1.default.packet_size,
|
|
39
75
|
startTimeMs: options.startTimeMs,
|
|
76
|
+
config: options.config,
|
|
40
77
|
});
|
|
41
78
|
this.airtunes.on('device', (key, status, desc) => {
|
|
42
79
|
onEvent?.({ event: 'device', message: status, detail: { key, desc } });
|
|
@@ -47,6 +84,9 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
|
47
84
|
this.airtunes.on('error', (err) => {
|
|
48
85
|
onEvent?.({ event: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
49
86
|
});
|
|
87
|
+
this.airtunes.on('metrics', (detail) => {
|
|
88
|
+
onEvent?.({ event: 'metrics', detail });
|
|
89
|
+
});
|
|
50
90
|
const dev = this.airtunes.add(options.host, {
|
|
51
91
|
port: options.port,
|
|
52
92
|
name: options.name,
|
package/dist/esm/utils/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.config = void 0;
|
|
4
|
+
exports.applyConfig = applyConfig;
|
|
4
5
|
const numUtil_1 = require("./numUtil");
|
|
5
6
|
exports.config = {
|
|
6
7
|
user_agent: 'iTunes/11.3.1 (Windows; Microsoft Windows 10 x64 (Build 19044); x64) (dt:2)',
|
|
@@ -31,4 +32,8 @@ exports.config = {
|
|
|
31
32
|
iv_base64: 'ePRBLI0XN5ArFaaz7ncNZw',
|
|
32
33
|
rsa_aeskey_base64: 'VjVbxWcmYgbBbhwBNlCh3K0CMNtWoB844BuiHGUJT51zQS7SDpMnlbBIobsKbfEJ3SCgWHRXjYWf7VQWRYtEcfx7ejA8xDIk5PSBYTvXP5dU2QoGrSBv0leDS6uxlEWuxBq3lIxCxpWO2YswHYKJBt06Uz9P2Fq2hDUwl3qOQ8oXb0OateTKtfXEwHJMprkhsJsGDrIc5W5NJFMAo6zCiM9bGSDeH2nvTlyW6bfI/Q0v0cDGUNeY3ut6fsoafRkfpCwYId+bg3diJh+uzw5htHDyZ2sN+BFYHzEfo8iv4KDxzeya9llqg6fRNQ8d5YjpvTnoeEQ9ye9ivjkBjcAfVw',
|
|
33
34
|
};
|
|
35
|
+
function applyConfig(overrides) {
|
|
36
|
+
Object.assign(exports.config, overrides);
|
|
37
|
+
return exports.config;
|
|
38
|
+
}
|
|
34
39
|
exports.default = exports.config;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
|
+
import { type AirplayConfig } from './utils/config';
|
|
3
4
|
/**
|
|
4
5
|
* Configuration for a single AirPlay sender.
|
|
5
6
|
*/
|
|
@@ -31,6 +32,8 @@ export interface LoxAirplaySenderOptions {
|
|
|
31
32
|
startTimeMs?: number;
|
|
32
33
|
/** Logger hook for internal messages. */
|
|
33
34
|
log?: (level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: unknown) => void;
|
|
35
|
+
/** Override transport/buffer tuning without patching the module. */
|
|
36
|
+
config?: Partial<AirplayConfig>;
|
|
34
37
|
}
|
|
35
38
|
/**
|
|
36
39
|
* Metadata sent to receivers for UI display.
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -8,7 +41,7 @@ exports.start = start;
|
|
|
8
41
|
const node_events_1 = require("node:events");
|
|
9
42
|
// Airtunes implementation (node_airtunes2 port) in src/core.
|
|
10
43
|
const index_1 = __importDefault(require("./core/index"));
|
|
11
|
-
const config_1 =
|
|
44
|
+
const config_1 = __importStar(require("./utils/config"));
|
|
12
45
|
class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
13
46
|
airtunes = null;
|
|
14
47
|
deviceKey = null;
|
|
@@ -32,11 +65,15 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
|
32
65
|
this.stop();
|
|
33
66
|
}
|
|
34
67
|
this.log = options.log;
|
|
68
|
+
if (options.config) {
|
|
69
|
+
(0, config_1.applyConfig)(options.config);
|
|
70
|
+
}
|
|
35
71
|
const inputCodec = options.inputCodec ?? 'pcm';
|
|
36
72
|
config_1.default.packet_size = inputCodec === 'alac' ? config_1.default.alac_packet_size : config_1.default.pcm_packet_size;
|
|
37
73
|
this.airtunes = new index_1.default({
|
|
38
74
|
packetSize: config_1.default.packet_size,
|
|
39
75
|
startTimeMs: options.startTimeMs,
|
|
76
|
+
config: options.config,
|
|
40
77
|
});
|
|
41
78
|
this.airtunes.on('device', (key, status, desc) => {
|
|
42
79
|
onEvent?.({ event: 'device', message: status, detail: { key, desc } });
|
|
@@ -47,6 +84,9 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
|
|
|
47
84
|
this.airtunes.on('error', (err) => {
|
|
48
85
|
onEvent?.({ event: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
49
86
|
});
|
|
87
|
+
this.airtunes.on('metrics', (detail) => {
|
|
88
|
+
onEvent?.({ event: 'metrics', detail });
|
|
89
|
+
});
|
|
50
90
|
const dev = this.airtunes.add(options.host, {
|
|
51
91
|
port: options.port,
|
|
52
92
|
name: options.name,
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.config = void 0;
|
|
4
|
+
exports.applyConfig = applyConfig;
|
|
4
5
|
const numUtil_1 = require("./numUtil");
|
|
5
6
|
exports.config = {
|
|
6
7
|
user_agent: 'iTunes/11.3.1 (Windows; Microsoft Windows 10 x64 (Build 19044); x64) (dt:2)',
|
|
@@ -31,4 +32,8 @@ exports.config = {
|
|
|
31
32
|
iv_base64: 'ePRBLI0XN5ArFaaz7ncNZw',
|
|
32
33
|
rsa_aeskey_base64: 'VjVbxWcmYgbBbhwBNlCh3K0CMNtWoB844BuiHGUJT51zQS7SDpMnlbBIobsKbfEJ3SCgWHRXjYWf7VQWRYtEcfx7ejA8xDIk5PSBYTvXP5dU2QoGrSBv0leDS6uxlEWuxBq3lIxCxpWO2YswHYKJBt06Uz9P2Fq2hDUwl3qOQ8oXb0OateTKtfXEwHJMprkhsJsGDrIc5W5NJFMAo6zCiM9bGSDeH2nvTlyW6bfI/Q0v0cDGUNeY3ut6fsoafRkfpCwYId+bg3diJh+uzw5htHDyZ2sN+BFYHzEfo8iv4KDxzeya9llqg6fRNQ8d5YjpvTnoeEQ9ye9ivjkBjcAfVw',
|
|
33
34
|
};
|
|
35
|
+
function applyConfig(overrides) {
|
|
36
|
+
Object.assign(exports.config, overrides);
|
|
37
|
+
return exports.config;
|
|
38
|
+
}
|
|
34
39
|
exports.default = exports.config;
|