-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtv-cast-discover.js
More file actions
101 lines (92 loc) · 3.39 KB
/
Copy pathtv-cast-discover.js
File metadata and controls
101 lines (92 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Discover Google Cast / DLNA / SSDP services on the Hisense TVs
const dgram = require('dgram');
const http = require('http');
const https = require('https');
const tvIps = ['192.168.0.121', '192.168.0.210'];
// === 1. SSDP Discovery (UPnP/DLNA) ===
console.log('=== SSDP Discovery (multicast) ===');
const ssdp = dgram.createSocket({ type: 'udp4', reuseAddr: true });
const SSDP_ADDR = '239.255.255.250';
const SSDP_PORT = 1900;
const searchMsg = Buffer.from(
'M-SEARCH * HTTP/1.1\r\n' +
`HOST: ${SSDP_ADDR}:${SSDP_PORT}\r\n` +
'MAN: "ssdp:discover"\r\n' +
'MX: 3\r\n' +
'ST: ssdp:all\r\n\r\n'
);
const found = new Set();
ssdp.on('message', (msg, rinfo) => {
// Only care about our TVs
if (tvIps.includes(rinfo.address)) {
const key = `${rinfo.address}:${rinfo.port}`;
if (!found.has(key)) {
found.add(key);
const lines = msg.toString().split('\r\n');
const st = lines.find(l => l.toLowerCase().startsWith('st:'));
const location = lines.find(l => l.toLowerCase().startsWith('location:'));
const server = lines.find(l => l.toLowerCase().startsWith('server:'));
console.log(`\n[SSDP] ${rinfo.address}:`);
if (st) console.log(` ${st}`);
if (location) console.log(` ${location}`);
if (server) console.log(` ${server}`);
}
}
});
ssdp.bind(() => {
ssdp.addMembership(SSDP_ADDR);
ssdp.send(searchMsg, 0, searchMsg.length, SSDP_PORT, SSDP_ADDR);
// Send again after 1s
setTimeout(() => ssdp.send(searchMsg, 0, searchMsg.length, SSDP_PORT, SSDP_ADDR), 1000);
});
// === 2. Direct port probes for Cast/DLNA ===
setTimeout(async () => {
console.log('\n=== Direct port probes ===');
const ports = [
{ port: 8008, name: 'Google Cast HTTP' },
{ port: 8009, name: 'Google Cast TLS' },
{ port: 8443, name: 'HTTPS (Cast?)' },
{ port: 9080, name: 'DLNA alt' },
{ port: 7000, name: 'AirPlay' },
{ port: 49152, name: 'UPnP/DLNA' },
{ port: 1900, name: 'SSDP' },
];
for (const ip of tvIps) {
console.log(`\n--- ${ip} ---`);
for (const { port, name } of ports) {
const open = await new Promise(resolve => {
const s = new (require('net').Socket)();
s.setTimeout(2000);
s.connect(port, ip, () => { s.destroy(); resolve(true); });
s.on('error', () => resolve(false));
s.on('timeout', () => { s.destroy(); resolve(false); });
});
if (open) console.log(` ${port} (${name}): OPEN`);
}
}
// === 3. Try Cast HTTP API on 8008 ===
for (const ip of tvIps) {
for (const port of [8008, 8443]) {
const proto = port === 8443 ? https : http;
const opts = port === 8443 ? { rejectUnauthorized: false } : {};
try {
const body = await new Promise((resolve, reject) => {
const req = proto.get(`${port === 8443 ? 'https' : 'http'}://${ip}:${port}/setup/eureka_info`, opts, (res) => {
let d = '';
res.on('data', c => d += c);
res.on('end', () => resolve(d));
});
req.on('error', reject);
req.setTimeout(3000, () => { req.destroy(); reject(new Error('timeout')); });
});
console.log(`\n[Cast API] ${ip}:${port}/setup/eureka_info:`);
console.log(body.slice(0, 500));
} catch (e) {
// silent
}
}
}
setTimeout(() => { ssdp.close(); process.exit(0); }, 1000);
}, 5000);
// Timeout
setTimeout(() => { ssdp.close(); process.exit(0); }, 12000);