@lox-audioserver/node-airplay-sender 0.4.5 → 0.4.7
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.
|
@@ -36,10 +36,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const elliptic = require('elliptic');
|
|
40
39
|
const crypto_1 = __importDefault(require("crypto"));
|
|
41
40
|
const ed = __importStar(require("@noble/ed25519"));
|
|
42
41
|
const util_1 = require("../utils/util");
|
|
42
|
+
if (!ed.utils.sha512Sync) {
|
|
43
|
+
ed.utils.sha512Sync = (...messages) => {
|
|
44
|
+
const hash = crypto_1.default.createHash('sha512');
|
|
45
|
+
for (const message of messages) {
|
|
46
|
+
hash.update(message);
|
|
47
|
+
}
|
|
48
|
+
return new Uint8Array(hash.digest());
|
|
49
|
+
};
|
|
50
|
+
}
|
|
43
51
|
// ...
|
|
44
52
|
// Note: All functions expect parameters to be hex strings.
|
|
45
53
|
function pair_setup_aes_key(K) {
|
|
@@ -75,7 +83,8 @@ function pair_verify_aes_iv(shared) {
|
|
|
75
83
|
// ...
|
|
76
84
|
// Public.
|
|
77
85
|
function a_pub(a) {
|
|
78
|
-
|
|
86
|
+
const publicKey = ed.sync.getPublicKey((0, util_1.hexString2ArrayBuffer)(a));
|
|
87
|
+
return (0, util_1.buf2hex)(publicKey);
|
|
79
88
|
}
|
|
80
89
|
function confirm(a, K) {
|
|
81
90
|
const key = pair_setup_aes_key(K);
|
|
@@ -107,8 +116,9 @@ function shared(v_pri, atv_pub) {
|
|
|
107
116
|
return (0, util_1.buf2hex)(Buffer.from(ed.curve25519.scalarMult((0, util_1.hexString2ArrayBuffer)(v_pri), (0, util_1.hexString2ArrayBuffer)(atv_pub))));
|
|
108
117
|
}
|
|
109
118
|
function signed(a, v_pub, atv_pub) {
|
|
110
|
-
const
|
|
111
|
-
|
|
119
|
+
const message = (0, util_1.hexString2ArrayBuffer)(v_pub + atv_pub);
|
|
120
|
+
const signature = ed.sync.sign(message, (0, util_1.hexString2ArrayBuffer)(a));
|
|
121
|
+
return (0, util_1.buf2hex)(signature);
|
|
112
122
|
}
|
|
113
123
|
function signature(shared, atv_data, signed) {
|
|
114
124
|
const cipher = crypto_1.default.createCipheriv('aes-128-ctr', (0, util_1.hexString2ArrayBuffer)(pair_verify_aes_key(shared)), (0, util_1.hexString2ArrayBuffer)(pair_verify_aes_iv(shared)));
|
|
@@ -297,8 +297,7 @@ AirTunesDevice.prototype.doHandshake = function () {
|
|
|
297
297
|
this.rtsp.on('ready', () => {
|
|
298
298
|
this.status = 'playing';
|
|
299
299
|
this.emit('status', 'playing');
|
|
300
|
-
|
|
301
|
-
this.relayAudio();
|
|
300
|
+
this.relayAudio();
|
|
302
301
|
});
|
|
303
302
|
this.rtsp.on('need_password', () => {
|
|
304
303
|
this.emit('status', 'need_password');
|
|
@@ -328,6 +327,9 @@ AirTunesDevice.prototype.doHandshake = function () {
|
|
|
328
327
|
AirTunesDevice.prototype.relayAudio = function () {
|
|
329
328
|
this.status = 'ready';
|
|
330
329
|
this.emit('status', 'ready');
|
|
330
|
+
let packetCount = 0;
|
|
331
|
+
let byteCount = 0;
|
|
332
|
+
let lastLogAt = 0;
|
|
331
333
|
this.audioCallback = (packet) => {
|
|
332
334
|
const airTunes = makeAirTunesPacket(packet, this.encoder, this.requireEncryption, this.alacEncoding, this.credentials, this.inputCodec);
|
|
333
335
|
// if (self.credentials) {
|
|
@@ -335,8 +337,37 @@ AirTunesDevice.prototype.relayAudio = function () {
|
|
|
335
337
|
// }
|
|
336
338
|
if (this.audioSocket == null) {
|
|
337
339
|
this.audioSocket = node_dgram_1.default.createSocket('udp4');
|
|
340
|
+
this.audioSocket.on('error', (err) => {
|
|
341
|
+
this.logLine?.('audio socket error', {
|
|
342
|
+
host: this.host,
|
|
343
|
+
port: this.serverPort,
|
|
344
|
+
message: err instanceof Error ? err.message : String(err),
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
this.audioSocket.send(airTunes, 0, airTunes.length, this.serverPort, this.host, (err) => {
|
|
349
|
+
if (err) {
|
|
350
|
+
this.logLine?.('audio packet send failed', {
|
|
351
|
+
host: this.host,
|
|
352
|
+
port: this.serverPort,
|
|
353
|
+
message: err instanceof Error ? err.message : String(err),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
packetCount += 1;
|
|
358
|
+
byteCount += airTunes.length;
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
if (now - lastLogAt > 5000) {
|
|
361
|
+
lastLogAt = now;
|
|
362
|
+
if (this.options?.debug) {
|
|
363
|
+
this.logLine?.('audio packet send stats', {
|
|
364
|
+
host: this.host,
|
|
365
|
+
port: this.serverPort,
|
|
366
|
+
packets: packetCount,
|
|
367
|
+
bytes: byteCount,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
338
370
|
}
|
|
339
|
-
this.audioSocket.send(airTunes, 0, airTunes.length, this.serverPort, this.host);
|
|
340
371
|
};
|
|
341
372
|
// this.sendAirTunesPacket = function(airTunes) {
|
|
342
373
|
// try{
|
|
@@ -36,10 +36,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const elliptic = require('elliptic');
|
|
40
39
|
const crypto_1 = __importDefault(require("crypto"));
|
|
41
40
|
const ed = __importStar(require("@noble/ed25519"));
|
|
42
41
|
const util_1 = require("../utils/util");
|
|
42
|
+
if (!ed.utils.sha512Sync) {
|
|
43
|
+
ed.utils.sha512Sync = (...messages) => {
|
|
44
|
+
const hash = crypto_1.default.createHash('sha512');
|
|
45
|
+
for (const message of messages) {
|
|
46
|
+
hash.update(message);
|
|
47
|
+
}
|
|
48
|
+
return new Uint8Array(hash.digest());
|
|
49
|
+
};
|
|
50
|
+
}
|
|
43
51
|
// ...
|
|
44
52
|
// Note: All functions expect parameters to be hex strings.
|
|
45
53
|
function pair_setup_aes_key(K) {
|
|
@@ -75,7 +83,8 @@ function pair_verify_aes_iv(shared) {
|
|
|
75
83
|
// ...
|
|
76
84
|
// Public.
|
|
77
85
|
function a_pub(a) {
|
|
78
|
-
|
|
86
|
+
const publicKey = ed.sync.getPublicKey((0, util_1.hexString2ArrayBuffer)(a));
|
|
87
|
+
return (0, util_1.buf2hex)(publicKey);
|
|
79
88
|
}
|
|
80
89
|
function confirm(a, K) {
|
|
81
90
|
const key = pair_setup_aes_key(K);
|
|
@@ -107,8 +116,9 @@ function shared(v_pri, atv_pub) {
|
|
|
107
116
|
return (0, util_1.buf2hex)(Buffer.from(ed.curve25519.scalarMult((0, util_1.hexString2ArrayBuffer)(v_pri), (0, util_1.hexString2ArrayBuffer)(atv_pub))));
|
|
108
117
|
}
|
|
109
118
|
function signed(a, v_pub, atv_pub) {
|
|
110
|
-
const
|
|
111
|
-
|
|
119
|
+
const message = (0, util_1.hexString2ArrayBuffer)(v_pub + atv_pub);
|
|
120
|
+
const signature = ed.sync.sign(message, (0, util_1.hexString2ArrayBuffer)(a));
|
|
121
|
+
return (0, util_1.buf2hex)(signature);
|
|
112
122
|
}
|
|
113
123
|
function signature(shared, atv_data, signed) {
|
|
114
124
|
const cipher = crypto_1.default.createCipheriv('aes-128-ctr', (0, util_1.hexString2ArrayBuffer)(pair_verify_aes_key(shared)), (0, util_1.hexString2ArrayBuffer)(pair_verify_aes_iv(shared)));
|
|
@@ -297,8 +297,7 @@ AirTunesDevice.prototype.doHandshake = function () {
|
|
|
297
297
|
this.rtsp.on('ready', () => {
|
|
298
298
|
this.status = 'playing';
|
|
299
299
|
this.emit('status', 'playing');
|
|
300
|
-
|
|
301
|
-
this.relayAudio();
|
|
300
|
+
this.relayAudio();
|
|
302
301
|
});
|
|
303
302
|
this.rtsp.on('need_password', () => {
|
|
304
303
|
this.emit('status', 'need_password');
|
|
@@ -328,6 +327,9 @@ AirTunesDevice.prototype.doHandshake = function () {
|
|
|
328
327
|
AirTunesDevice.prototype.relayAudio = function () {
|
|
329
328
|
this.status = 'ready';
|
|
330
329
|
this.emit('status', 'ready');
|
|
330
|
+
let packetCount = 0;
|
|
331
|
+
let byteCount = 0;
|
|
332
|
+
let lastLogAt = 0;
|
|
331
333
|
this.audioCallback = (packet) => {
|
|
332
334
|
const airTunes = makeAirTunesPacket(packet, this.encoder, this.requireEncryption, this.alacEncoding, this.credentials, this.inputCodec);
|
|
333
335
|
// if (self.credentials) {
|
|
@@ -335,8 +337,37 @@ AirTunesDevice.prototype.relayAudio = function () {
|
|
|
335
337
|
// }
|
|
336
338
|
if (this.audioSocket == null) {
|
|
337
339
|
this.audioSocket = node_dgram_1.default.createSocket('udp4');
|
|
340
|
+
this.audioSocket.on('error', (err) => {
|
|
341
|
+
this.logLine?.('audio socket error', {
|
|
342
|
+
host: this.host,
|
|
343
|
+
port: this.serverPort,
|
|
344
|
+
message: err instanceof Error ? err.message : String(err),
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
this.audioSocket.send(airTunes, 0, airTunes.length, this.serverPort, this.host, (err) => {
|
|
349
|
+
if (err) {
|
|
350
|
+
this.logLine?.('audio packet send failed', {
|
|
351
|
+
host: this.host,
|
|
352
|
+
port: this.serverPort,
|
|
353
|
+
message: err instanceof Error ? err.message : String(err),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
packetCount += 1;
|
|
358
|
+
byteCount += airTunes.length;
|
|
359
|
+
const now = Date.now();
|
|
360
|
+
if (now - lastLogAt > 5000) {
|
|
361
|
+
lastLogAt = now;
|
|
362
|
+
if (this.options?.debug) {
|
|
363
|
+
this.logLine?.('audio packet send stats', {
|
|
364
|
+
host: this.host,
|
|
365
|
+
port: this.serverPort,
|
|
366
|
+
packets: packetCount,
|
|
367
|
+
bytes: byteCount,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
338
370
|
}
|
|
339
|
-
this.audioSocket.send(airTunes, 0, airTunes.length, this.serverPort, this.host);
|
|
340
371
|
};
|
|
341
372
|
// this.sendAirTunesPacket = function(airTunes) {
|
|
342
373
|
// try{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lox-audioserver/node-airplay-sender",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "AirPlay sender (RAOP/AirPlay 1; AirPlay 2 control/auth, best-effort audio)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -52,11 +52,10 @@
|
|
|
52
52
|
"bplist-parser": "^0.3.2",
|
|
53
53
|
"crypto-js": "^4.2.0",
|
|
54
54
|
"curve25519-js": "^0.0.4",
|
|
55
|
-
"elliptic": "^6.5.5",
|
|
56
55
|
"fast-srp-hap": "^2.0.4",
|
|
57
56
|
"js-crypto-aes": "^1.0.6",
|
|
58
57
|
"js-sha1": "^0.7.0",
|
|
59
|
-
"lodash": "^4.17.
|
|
58
|
+
"lodash": "^4.17.23",
|
|
60
59
|
"parse-raw-http": "0.0.1",
|
|
61
60
|
"python-struct": "^1.1.3",
|
|
62
61
|
"simple-plist": "^1.4.0",
|