@edenware/dlnacasts 1.0.0 → 1.0.2
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/index.cjs +222 -198
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +216 -191
- package/dist/index.mjs.map +1 -0
- package/package.json +9 -4
- package/dist/index.js +0 -393
- package/dist/src/index.cjs +0 -373
package/dist/index.mjs
CHANGED
|
@@ -1,122 +1,146 @@
|
|
|
1
|
-
import MediaRenderer from 'upnp-mediarenderer-client'
|
|
2
|
-
import
|
|
3
|
-
import parallel from 'run-parallel'
|
|
4
|
-
import { parseString } from 'xml2js'
|
|
5
|
-
import { Client
|
|
6
|
-
import { isIP } from 'node:net'
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let localIP =
|
|
1
|
+
import MediaRenderer from 'upnp-mediarenderer-client';
|
|
2
|
+
import events from 'events';
|
|
3
|
+
import parallel from 'run-parallel';
|
|
4
|
+
import { parseString } from 'xml2js';
|
|
5
|
+
import { Client } from '@edenware/ssdp';
|
|
6
|
+
import { isIP } from 'node:net';
|
|
7
|
+
import http from 'node:http';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import thunky from 'thunky';
|
|
10
|
+
|
|
11
|
+
const noop = () => {};
|
|
12
|
+
|
|
13
|
+
var index = (options = {}) => {
|
|
14
|
+
const SERVICE_TYPE = 'urn:schemas-upnp-org:device:MediaRenderer:1';
|
|
15
|
+
const agent = new http.Agent({ keepAlive: true, maxSockets: 5 });
|
|
16
|
+
const log = options.log ? console.debug : () => {};
|
|
17
|
+
|
|
18
|
+
// Find local IP - prefer Wi-Fi/Ethernet
|
|
19
|
+
const interfaces = os.networkInterfaces();
|
|
20
|
+
log('[DLNACASTS] Network interfaces:', interfaces);
|
|
21
|
+
let localIP = null; // fallback
|
|
22
|
+
let localInterface = null;
|
|
23
|
+
// First pass: prefer Wi-Fi or Ethernet
|
|
22
24
|
for (const name of Object.keys(interfaces)) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (name.includes('Wi-Fi') || name.includes('Ethernet') || name.includes('eth') || name.includes('en') || name.includes('wlan')) {
|
|
26
|
+
for (const iface of interfaces[name]) {
|
|
27
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
28
|
+
localIP = iface.address;
|
|
29
|
+
localInterface = name;
|
|
30
|
+
log('[DLNACASTS] Using preferred interface:', name, localIP);
|
|
31
|
+
break
|
|
32
|
+
}
|
|
27
33
|
}
|
|
34
|
+
if (localIP) break
|
|
28
35
|
}
|
|
29
|
-
if (localIP !== '192.168.1.8') break
|
|
30
36
|
}
|
|
31
|
-
|
|
37
|
+
// Second pass: any IPv4 if no preferred found
|
|
38
|
+
if (!localIP) {
|
|
39
|
+
for (const name of Object.keys(interfaces)) {
|
|
40
|
+
for (const iface of interfaces[name]) {
|
|
41
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
42
|
+
localIP = iface.address;
|
|
43
|
+
localInterface = name;
|
|
44
|
+
log('[DLNACASTS] Using fallback interface:', name, localIP);
|
|
45
|
+
break
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (localIP) break
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!localIP) localIP = '127.0.0.1'; // ultimate fallback
|
|
52
|
+
log('Local IP:', localIP);
|
|
53
|
+
|
|
54
|
+
const that = new events.EventEmitter();
|
|
55
|
+
const casts = {};
|
|
56
|
+
const ssdp = Client ? new Client({
|
|
57
|
+
log: true,
|
|
58
|
+
interfaces: [localInterface],
|
|
59
|
+
explicitSocketBind: true
|
|
60
|
+
}) : null;
|
|
32
61
|
|
|
33
|
-
|
|
34
|
-
const casts = {}
|
|
35
|
-
const ssdp = SSDP ? new SSDP({
|
|
36
|
-
unicastHost: localIP,
|
|
37
|
-
log: true
|
|
38
|
-
}) : null
|
|
62
|
+
log('[DLNACASTS] SSDP client created:', !!ssdp);
|
|
39
63
|
|
|
40
|
-
that.players = []
|
|
64
|
+
that.players = [];
|
|
41
65
|
|
|
42
66
|
const emit = (cst) => {
|
|
43
67
|
if (!cst || !cst.host || cst.emitted) return
|
|
44
|
-
cst.emitted = true
|
|
68
|
+
cst.emitted = true;
|
|
45
69
|
|
|
46
|
-
const player = new EventEmitter()
|
|
47
|
-
let getStatus = undefined
|
|
48
|
-
let stopped = false // Flag para rastrear se stop foi chamado
|
|
49
|
-
let playFailed = false // Flag para rastrear se play falhou
|
|
70
|
+
const player = new events.EventEmitter();
|
|
71
|
+
let getStatus = undefined;
|
|
72
|
+
let stopped = false; // Flag para rastrear se stop foi chamado
|
|
73
|
+
let playFailed = false; // Flag para rastrear se play falhou
|
|
50
74
|
|
|
51
75
|
const connect = thunky(function reconnect (cb) {
|
|
52
|
-
const client = new MediaRenderer(player.xml)
|
|
76
|
+
const client = new MediaRenderer(player.xml);
|
|
53
77
|
|
|
54
78
|
client.on('error', (err) => {
|
|
55
|
-
log('[DLNACASTS] Client error, clearing player.client:', err)
|
|
56
|
-
try { clearInterval(getStatus) } catch(e) {}
|
|
57
|
-
player.client = undefined
|
|
58
|
-
player.emit('error', err)
|
|
59
|
-
})
|
|
79
|
+
log('[DLNACASTS] Client error, clearing player.client:', err);
|
|
80
|
+
try { clearInterval(getStatus); } catch(e) {}
|
|
81
|
+
player.client = undefined;
|
|
82
|
+
player.emit('error', err);
|
|
83
|
+
});
|
|
60
84
|
|
|
61
85
|
client.on('loading', (err) => {
|
|
62
|
-
player.emit('loading', err)
|
|
63
|
-
})
|
|
86
|
+
player.emit('loading', err);
|
|
87
|
+
});
|
|
64
88
|
|
|
65
89
|
client.on('close', () => {
|
|
66
|
-
log('[DLNACASTS] Client closed, clearing player.client')
|
|
67
|
-
try { clearInterval(getStatus) } catch(e) {}
|
|
68
|
-
player.client = undefined
|
|
69
|
-
connect = thunky(reconnect) // Reset thunky para permitir nova reconexão
|
|
70
|
-
})
|
|
90
|
+
log('[DLNACASTS] Client closed, clearing player.client');
|
|
91
|
+
try { clearInterval(getStatus); } catch(e) {}
|
|
92
|
+
player.client = undefined;
|
|
93
|
+
connect = thunky(reconnect); // Reset thunky para permitir nova reconexão
|
|
94
|
+
});
|
|
71
95
|
|
|
72
|
-
player.client = client
|
|
73
|
-
cb(null, player.client)
|
|
74
|
-
})
|
|
96
|
+
player.client = client;
|
|
97
|
+
cb(null, player.client);
|
|
98
|
+
});
|
|
75
99
|
|
|
76
100
|
const parseTime = (time) => {
|
|
77
101
|
if (!time || time.indexOf(':') === -1) return 0
|
|
78
|
-
const parts = time.split(':').map(Number)
|
|
102
|
+
const parts = time.split(':').map(Number);
|
|
79
103
|
return parts[0] * 3600 + parts[1] * 60 + parts[2]
|
|
80
|
-
}
|
|
104
|
+
};
|
|
81
105
|
|
|
82
|
-
player.name = cst.name
|
|
83
|
-
player.host = cst.host
|
|
84
|
-
player.xml = cst.xml
|
|
85
|
-
player._status = {}
|
|
86
|
-
player.MAX_VOLUME = 100
|
|
106
|
+
player.name = cst.name;
|
|
107
|
+
player.host = cst.host;
|
|
108
|
+
player.xml = cst.xml;
|
|
109
|
+
player._status = {};
|
|
110
|
+
player.MAX_VOLUME = 100;
|
|
87
111
|
|
|
88
|
-
player.connect = connect
|
|
112
|
+
player.connect = connect;
|
|
89
113
|
|
|
90
114
|
player.close = (cb = noop) => {
|
|
91
|
-
log('[DLNACASTS] Closing player, clearing player.client')
|
|
92
|
-
try { clearInterval(getStatus) } catch(e) {}
|
|
115
|
+
log('[DLNACASTS] Closing player, clearing player.client');
|
|
116
|
+
try { clearInterval(getStatus); } catch(e) {}
|
|
93
117
|
if (player.client) {
|
|
94
118
|
for (let e of ["error", "status", "loading", "close"]) {
|
|
95
|
-
player.client.removeAllListeners(e)
|
|
119
|
+
player.client.removeAllListeners(e);
|
|
96
120
|
}
|
|
97
|
-
player.client = undefined
|
|
121
|
+
player.client = undefined;
|
|
98
122
|
}
|
|
99
|
-
stopped = true // Marcar que stop foi chamado
|
|
100
|
-
cb()
|
|
101
|
-
}
|
|
123
|
+
stopped = true; // Marcar que stop foi chamado
|
|
124
|
+
cb();
|
|
125
|
+
};
|
|
102
126
|
|
|
103
127
|
player.play = (url, opts, cb = noop) => {
|
|
104
128
|
if (typeof opts === 'function') return player.play(url, null, opts)
|
|
105
|
-
if (!opts) opts = {}
|
|
129
|
+
if (!opts) opts = {};
|
|
106
130
|
if (!url) return player.resume(cb)
|
|
107
131
|
|
|
108
|
-
stopped = false // Resetar stopped ao chamar play
|
|
109
|
-
playFailed = false // Resetar playFailed ao chamar play
|
|
110
|
-
player.subtitles = opts.subtitles
|
|
132
|
+
stopped = false; // Resetar stopped ao chamar play
|
|
133
|
+
playFailed = false; // Resetar playFailed ao chamar play
|
|
134
|
+
player.subtitles = opts.subtitles;
|
|
111
135
|
|
|
112
136
|
connect((err, p) => {
|
|
113
137
|
if (err) {
|
|
114
|
-
log('[DLNACASTS] Connect failed in play:', err)
|
|
115
|
-
playFailed = true // Marcar que play falhou
|
|
138
|
+
log('[DLNACASTS] Connect failed in play:', err);
|
|
139
|
+
playFailed = true; // Marcar que play falhou
|
|
116
140
|
return cb(err)
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
try { clearInterval(getStatus) } catch(e) {}
|
|
143
|
+
try { clearInterval(getStatus); } catch(e) {}
|
|
120
144
|
|
|
121
145
|
const media = {
|
|
122
146
|
autoplay: opts.autoPlay !== false,
|
|
@@ -126,119 +150,119 @@ export default (options = {}) => {
|
|
|
126
150
|
type: 'video', // can be 'video', 'audio' or 'image'
|
|
127
151
|
subtitlesUrl: player.subtitles && player.subtitles.length ? player.subtitles[0] : null
|
|
128
152
|
}
|
|
129
|
-
}
|
|
153
|
+
};
|
|
130
154
|
if (opts.dlnaFeatures) {
|
|
131
|
-
media.dlnaFeatures = opts.dlnaFeatures || 'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000'
|
|
155
|
+
media.dlnaFeatures = opts.dlnaFeatures || 'DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000';
|
|
132
156
|
//media.dlnaFeatures = opts.dlnaFeatures; // for LG WebOS 'DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01100000000000000000000000000000' allows seeking
|
|
133
157
|
}
|
|
134
158
|
|
|
135
|
-
let callback = cb
|
|
159
|
+
let callback = cb;
|
|
136
160
|
if (opts.seek) {
|
|
137
161
|
callback = (err) => {
|
|
138
162
|
if (err) {
|
|
139
|
-
playFailed = true // Marcar que play falhou
|
|
163
|
+
playFailed = true; // Marcar que play falhou
|
|
140
164
|
return cb(err)
|
|
141
165
|
}
|
|
142
|
-
player.seek(opts.seek, cb)
|
|
143
|
-
}
|
|
166
|
+
player.seek(opts.seek, cb);
|
|
167
|
+
};
|
|
144
168
|
}
|
|
145
169
|
|
|
146
170
|
getStatus = setInterval(() => {
|
|
147
171
|
if (stopped || playFailed) {
|
|
148
|
-
log('[DLNACASTS] Skipping getStatus: stopped=%s, playFailed=%s', stopped, playFailed)
|
|
172
|
+
log('[DLNACASTS] Skipping getStatus: stopped=%s, playFailed=%s', stopped, playFailed);
|
|
149
173
|
return
|
|
150
174
|
}
|
|
151
175
|
if (!player.client) {
|
|
152
|
-
log('[DLNACASTS] player.client is undefined in getStatus, attempting to reconnect')
|
|
176
|
+
log('[DLNACASTS] player.client is undefined in getStatus, attempting to reconnect');
|
|
153
177
|
connect((err) => {
|
|
154
178
|
if (err) {
|
|
155
|
-
log('[DLNACASTS] Reconnect failed in getStatus:', err)
|
|
156
|
-
playFailed = true // Marcar falha se reconexão falhar
|
|
179
|
+
log('[DLNACASTS] Reconnect failed in getStatus:', err);
|
|
180
|
+
playFailed = true; // Marcar falha se reconexão falhar
|
|
157
181
|
return
|
|
158
182
|
}
|
|
159
|
-
log('[DLNACASTS] Reconnected successfully in getStatus')
|
|
183
|
+
log('[DLNACASTS] Reconnected successfully in getStatus');
|
|
160
184
|
player.client.callAction('AVTransport', 'GetTransportInfo', {
|
|
161
185
|
InstanceID: player.client.instanceId
|
|
162
186
|
}, (err, res) => {
|
|
163
187
|
if (err) return
|
|
164
|
-
const newStatus = res.CurrentTransportState
|
|
188
|
+
const newStatus = res.CurrentTransportState;
|
|
165
189
|
if (newStatus !== player._status.playerState) {
|
|
166
|
-
player._status.playerState = newStatus
|
|
190
|
+
player._status.playerState = newStatus;
|
|
167
191
|
player.status((err, status) => {
|
|
168
192
|
if (err) return
|
|
169
|
-
player.emit('status', status)
|
|
170
|
-
})
|
|
193
|
+
player.emit('status', status);
|
|
194
|
+
});
|
|
171
195
|
}
|
|
172
|
-
})
|
|
173
|
-
})
|
|
196
|
+
});
|
|
197
|
+
});
|
|
174
198
|
return
|
|
175
199
|
}
|
|
176
200
|
player.client.callAction('AVTransport', 'GetTransportInfo', {
|
|
177
201
|
InstanceID: player.client.instanceId
|
|
178
202
|
}, (err, res) => {
|
|
179
203
|
if (err) return
|
|
180
|
-
const newStatus = res.CurrentTransportState
|
|
204
|
+
const newStatus = res.CurrentTransportState;
|
|
181
205
|
if (newStatus !== player._status.playerState) {
|
|
182
|
-
player._status.playerState = newStatus
|
|
206
|
+
player._status.playerState = newStatus;
|
|
183
207
|
player.status((err, status) => {
|
|
184
208
|
if (err) return
|
|
185
|
-
player.emit('status', status)
|
|
186
|
-
})
|
|
209
|
+
player.emit('status', status);
|
|
210
|
+
});
|
|
187
211
|
}
|
|
188
|
-
})
|
|
189
|
-
}, 1000)
|
|
212
|
+
});
|
|
213
|
+
}, 1000);
|
|
190
214
|
|
|
191
|
-
p.load(url, media, callback)
|
|
192
|
-
})
|
|
193
|
-
}
|
|
215
|
+
p.load(url, media, callback);
|
|
216
|
+
});
|
|
217
|
+
};
|
|
194
218
|
|
|
195
219
|
player.resume = (cb = noop) => {
|
|
196
|
-
player.client.play(cb)
|
|
197
|
-
}
|
|
220
|
+
player.client.play(cb);
|
|
221
|
+
};
|
|
198
222
|
|
|
199
223
|
player.pause = (cb = noop) => {
|
|
200
|
-
player.client.pause(cb)
|
|
201
|
-
}
|
|
224
|
+
player.client.pause(cb);
|
|
225
|
+
};
|
|
202
226
|
|
|
203
227
|
player.stop = (cb = noop) => {
|
|
204
|
-
try { clearInterval(getStatus) } catch(e) {}
|
|
205
|
-
stopped = true // Marcar que stop foi chamado
|
|
206
|
-
player.client.stop(cb)
|
|
207
|
-
}
|
|
228
|
+
try { clearInterval(getStatus); } catch(e) {}
|
|
229
|
+
stopped = true; // Marcar que stop foi chamado
|
|
230
|
+
player.client.stop(cb);
|
|
231
|
+
};
|
|
208
232
|
|
|
209
233
|
player.status = (cb = noop) => {
|
|
210
234
|
if (stopped || playFailed) {
|
|
211
|
-
log('[DLNACASTS] Skipping status: stopped=%s, playFailed=%s', stopped, playFailed)
|
|
235
|
+
log('[DLNACASTS] Skipping status: stopped=%s, playFailed=%s', stopped, playFailed);
|
|
212
236
|
return cb(null, player._status)
|
|
213
237
|
}
|
|
214
238
|
if (!player.client) {
|
|
215
|
-
log('[DLNACASTS] player.client is undefined in status, attempting to reconnect')
|
|
239
|
+
log('[DLNACASTS] player.client is undefined in status, attempting to reconnect');
|
|
216
240
|
connect((err) => {
|
|
217
241
|
if (err) {
|
|
218
|
-
log('[DLNACASTS] Reconnect failed in status:', err)
|
|
219
|
-
playFailed = true // Marcar falha se reconexão falhar
|
|
242
|
+
log('[DLNACASTS] Reconnect failed in status:', err);
|
|
243
|
+
playFailed = true; // Marcar falha se reconexão falhar
|
|
220
244
|
return cb(err)
|
|
221
245
|
}
|
|
222
|
-
log('[DLNACASTS] Reconnected successfully in status')
|
|
246
|
+
log('[DLNACASTS] Reconnected successfully in status');
|
|
223
247
|
parallel({
|
|
224
248
|
currentTime: (acb) => {
|
|
225
249
|
player.client.callAction('AVTransport', 'GetPositionInfo', {
|
|
226
250
|
InstanceID: player.client.instanceId
|
|
227
251
|
}, (err, res) => {
|
|
228
252
|
if (err) return acb()
|
|
229
|
-
acb(null, parseTime(res.AbsTime) | parseTime(res.RelTime))
|
|
230
|
-
})
|
|
253
|
+
acb(null, parseTime(res.AbsTime) | parseTime(res.RelTime));
|
|
254
|
+
});
|
|
231
255
|
},
|
|
232
256
|
volume: (acb) => {
|
|
233
|
-
player.getVolume(acb)
|
|
257
|
+
player.getVolume(acb);
|
|
234
258
|
}
|
|
235
259
|
}, (err, results) => {
|
|
236
|
-
log('dlnacasts player.status results: %o', results)
|
|
237
|
-
player._status.currentTime = results.currentTime
|
|
238
|
-
player._status.volume = { level: results.volume / player.MAX_VOLUME }
|
|
260
|
+
log('dlnacasts player.status results: %o', results);
|
|
261
|
+
player._status.currentTime = results.currentTime;
|
|
262
|
+
player._status.volume = { level: results.volume / player.MAX_VOLUME };
|
|
239
263
|
return cb(err, player._status)
|
|
240
|
-
})
|
|
241
|
-
})
|
|
264
|
+
});
|
|
265
|
+
});
|
|
242
266
|
return
|
|
243
267
|
}
|
|
244
268
|
|
|
@@ -248,19 +272,19 @@ export default (options = {}) => {
|
|
|
248
272
|
InstanceID: player.client.instanceId
|
|
249
273
|
}, (err, res) => {
|
|
250
274
|
if (err) return acb()
|
|
251
|
-
acb(null, parseTime(res.AbsTime) | parseTime(res.RelTime))
|
|
252
|
-
})
|
|
275
|
+
acb(null, parseTime(res.AbsTime) | parseTime(res.RelTime));
|
|
276
|
+
});
|
|
253
277
|
},
|
|
254
278
|
volume: (acb) => {
|
|
255
|
-
player.getVolume(acb)
|
|
279
|
+
player.getVolume(acb);
|
|
256
280
|
}
|
|
257
281
|
}, (err, results) => {
|
|
258
|
-
log('dlnacasts player.status results: %o', results)
|
|
259
|
-
player._status.currentTime = results.currentTime
|
|
260
|
-
player._status.volume = { level: results.volume / player.MAX_VOLUME }
|
|
282
|
+
log('dlnacasts player.status results: %o', results);
|
|
283
|
+
player._status.currentTime = results.currentTime;
|
|
284
|
+
player._status.volume = { level: results.volume / player.MAX_VOLUME };
|
|
261
285
|
return cb(err, player._status)
|
|
262
|
-
})
|
|
263
|
-
}
|
|
286
|
+
});
|
|
287
|
+
};
|
|
264
288
|
|
|
265
289
|
player.getVolume = (cb) => {
|
|
266
290
|
player.client.callAction('RenderingControl', 'GetVolume', {
|
|
@@ -268,123 +292,124 @@ export default (options = {}) => {
|
|
|
268
292
|
Channel: 'Master'
|
|
269
293
|
}, (err, res) => {
|
|
270
294
|
if (err) return cb()
|
|
271
|
-
cb(null, res.CurrentVolume ? parseInt(res.CurrentVolume) : 0)
|
|
272
|
-
})
|
|
273
|
-
}
|
|
295
|
+
cb(null, res.CurrentVolume ? parseInt(res.CurrentVolume) : 0);
|
|
296
|
+
});
|
|
297
|
+
};
|
|
274
298
|
|
|
275
299
|
player.setVolume = (vol, cb = noop) => {
|
|
276
300
|
player.client.callAction('RenderingControl', 'SetVolume', {
|
|
277
301
|
InstanceID: player.client.instanceId,
|
|
278
302
|
Channel: 'Master',
|
|
279
303
|
DesiredVolume: vol
|
|
280
|
-
}, cb)
|
|
281
|
-
}
|
|
304
|
+
}, cb);
|
|
305
|
+
};
|
|
282
306
|
|
|
283
307
|
player.request = (target, action, data, cb = noop) => {
|
|
284
308
|
if (data.InstanceID === null) {
|
|
285
|
-
data.InstanceID = player.client.instanceId
|
|
309
|
+
data.InstanceID = player.client.instanceId;
|
|
286
310
|
}
|
|
287
|
-
player.client.callAction(target, action, data, cb)
|
|
288
|
-
}
|
|
311
|
+
player.client.callAction(target, action, data, cb);
|
|
312
|
+
};
|
|
289
313
|
|
|
290
314
|
player.seek = (time, cb = noop) => {
|
|
291
|
-
player.client.seek(time, cb)
|
|
292
|
-
}
|
|
315
|
+
player.client.seek(time, cb);
|
|
316
|
+
};
|
|
293
317
|
|
|
294
|
-
that.players.push(player)
|
|
295
|
-
that.emit('update', player)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (ssdp) {
|
|
299
|
-
// Response handler moved to update()
|
|
300
|
-
}
|
|
318
|
+
that.players.push(player);
|
|
319
|
+
that.emit('update', player);
|
|
320
|
+
};
|
|
301
321
|
|
|
302
322
|
that.validate = (name, host, xml) => {
|
|
303
323
|
if (!casts[name]) {
|
|
304
324
|
http.get(xml, { agent }, res => {
|
|
305
|
-
const {statusCode} = res
|
|
325
|
+
const {statusCode} = res;
|
|
306
326
|
if (statusCode == 200) {
|
|
307
327
|
if (!casts[name]) {
|
|
308
|
-
casts[name] = {name, host, xml}
|
|
309
|
-
emit(casts[name])
|
|
328
|
+
casts[name] = {name, host, xml};
|
|
329
|
+
emit(casts[name]);
|
|
310
330
|
} else if (isIP(casts[name].host) != 4 && isIP(host) == 4) {
|
|
311
|
-
casts[name].host = host
|
|
312
|
-
casts[name].xml = xml
|
|
313
|
-
emit(casts[name])
|
|
331
|
+
casts[name].host = host;
|
|
332
|
+
casts[name].xml = xml;
|
|
333
|
+
emit(casts[name]);
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
|
-
res.resume()
|
|
317
|
-
}).on('error', e => {})
|
|
336
|
+
res.resume();
|
|
337
|
+
}).on('error', e => {});
|
|
318
338
|
}
|
|
319
|
-
}
|
|
339
|
+
};
|
|
320
340
|
|
|
321
341
|
that.update = () => {
|
|
322
|
-
log('[DLNACASTS] querying ssdp')
|
|
342
|
+
log('[DLNACASTS] querying ssdp');
|
|
323
343
|
if (ssdp) {
|
|
324
|
-
const tasks = []
|
|
344
|
+
const tasks = [];
|
|
325
345
|
const responseHandler = (headers, statusCode, info) => {
|
|
346
|
+
log('[DLNACASTS] SSDP response received:', headers.ST || 'no ST', 'from', info.address, 'LOCATION:', headers.LOCATION || 'no LOCATION');
|
|
326
347
|
if (!headers.LOCATION) return
|
|
327
348
|
if (headers.ST !== SERVICE_TYPE) return
|
|
328
349
|
|
|
329
350
|
tasks.push((cb) => {
|
|
330
351
|
http.get(headers.LOCATION, { agent }, (res) => {
|
|
331
352
|
if (res.statusCode !== 200) return cb()
|
|
332
|
-
let body = ''
|
|
333
|
-
res.on('data', (chunk) => { body += chunk })
|
|
353
|
+
let body = '';
|
|
354
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
334
355
|
res.on('end', () => {
|
|
335
356
|
parseString(body, {explicitArray: false, explicitRoot: false},
|
|
336
357
|
(err, service) => {
|
|
337
358
|
if (err) return cb()
|
|
338
359
|
if (!service.device) return cb()
|
|
339
360
|
|
|
340
|
-
log('[DLNACASTS] ssdp device:', service.device)
|
|
361
|
+
log('[DLNACASTS] ssdp device:', service.device);
|
|
341
362
|
|
|
342
|
-
const name = service.device.friendlyName
|
|
363
|
+
const name = service.device.friendlyName;
|
|
343
364
|
|
|
344
365
|
if (!name) return cb()
|
|
345
366
|
|
|
346
|
-
const host = info.address
|
|
347
|
-
const xml = headers.LOCATION
|
|
367
|
+
const host = info.address;
|
|
368
|
+
const xml = headers.LOCATION;
|
|
348
369
|
|
|
349
370
|
if (!casts[name]) {
|
|
350
|
-
casts[name] = {name: name, host: host, xml: xml}
|
|
351
|
-
emit(casts[name])
|
|
371
|
+
casts[name] = {name: name, host: host, xml: xml};
|
|
372
|
+
emit(casts[name]);
|
|
352
373
|
} else if (casts[name] && !casts[name].host) {
|
|
353
|
-
casts[name].host = host
|
|
354
|
-
casts[name].xml = xml
|
|
355
|
-
emit(casts[name])
|
|
374
|
+
casts[name].host = host;
|
|
375
|
+
casts[name].xml = xml;
|
|
376
|
+
emit(casts[name]);
|
|
356
377
|
}
|
|
357
|
-
cb()
|
|
358
|
-
})
|
|
359
|
-
})
|
|
360
|
-
}).on('error', () => cb())
|
|
361
|
-
})
|
|
362
|
-
}
|
|
363
|
-
ssdp.on('response', responseHandler)
|
|
364
|
-
ssdp.search(SERVICE_TYPE)
|
|
378
|
+
cb();
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
}).on('error', () => cb());
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
ssdp.on('response', responseHandler);
|
|
385
|
+
ssdp.search(SERVICE_TYPE);
|
|
386
|
+
log('[DLNACASTS] SSDP search started for:', SERVICE_TYPE);
|
|
365
387
|
setTimeout(() => {
|
|
366
|
-
ssdp.removeListener('response', responseHandler)
|
|
367
|
-
parallel(tasks, () => {})
|
|
368
|
-
}, 10000)
|
|
388
|
+
ssdp.removeListener('response', responseHandler);
|
|
389
|
+
parallel(tasks, () => {});
|
|
390
|
+
}, 10000);
|
|
369
391
|
}
|
|
370
|
-
}
|
|
392
|
+
};
|
|
371
393
|
|
|
372
394
|
that.on('removeListener', () => {
|
|
373
395
|
if (ssdp && that.listenerCount('update') === 0) {
|
|
374
|
-
ssdp.stop()
|
|
396
|
+
ssdp.stop();
|
|
375
397
|
}
|
|
376
|
-
})
|
|
398
|
+
});
|
|
377
399
|
|
|
378
400
|
that.destroy = () => {
|
|
379
|
-
log('[DLNACASTS] destroying ssdp...')
|
|
401
|
+
log('[DLNACASTS] destroying ssdp...');
|
|
380
402
|
if (ssdp) {
|
|
381
|
-
ssdp.stop()
|
|
403
|
+
ssdp.stop();
|
|
382
404
|
}
|
|
383
|
-
}
|
|
405
|
+
};
|
|
384
406
|
|
|
385
407
|
that.close = () => {
|
|
386
|
-
that.removeAllListeners('update')
|
|
387
|
-
}
|
|
408
|
+
that.removeAllListeners('update');
|
|
409
|
+
};
|
|
388
410
|
|
|
389
411
|
return that
|
|
390
|
-
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export { index as default };
|
|
415
|
+
//# sourceMappingURL=index.mjs.map
|