diff --git a/plugins/chromecast/package-lock.json b/plugins/chromecast/package-lock.json index acab24fd1..a8fa29ffb 100644 --- a/plugins/chromecast/package-lock.json +++ b/plugins/chromecast/package-lock.json @@ -1,24 +1,23 @@ { "name": "@scrypted/chromecast", - "version": "0.1.32", + "version": "0.1.34", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/chromecast", - "version": "0.1.32", + "version": "0.1.34", "license": "Apache-2.0", "dependencies": { "@scrypted/sdk": "file:../../sdk", "castv2-promise": "^1.0.0", - "mdns": "^2.7.2", "memoize-one": "^5.1.1", "mime": "^2.5.2", - "query-string": "^7.0.0" + "multicast-dns": "^7.2.3" }, "devDependencies": { - "@types/mdns": "^0.0.34", "@types/mime": "^2.0.3", + "@types/multicast-dns": "^7.2.1", "@types/node": "^16.9.0" } }, @@ -74,6 +73,11 @@ "../sdk": { "extraneous": true }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", + "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==" + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -132,26 +136,36 @@ "resolved": "../../sdk", "link": true }, - "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "node_modules/@types/mdns": { - "version": "0.0.34", - "resolved": "https://registry.npmjs.org/@types/mdns/-/mdns-0.0.34.tgz", - "integrity": "sha512-4Rrt/0wRAudtOnmhfDdoFhy5r20yHe0KiDK+/+I9RBBMW67F4S6y8tJH06AzrUDZzS/SH/U2pw1W0lrgQ+OlPg==", + "node_modules/@types/dns-packet": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.2.2.tgz", + "integrity": "sha512-Rqq7rGKIdn7OTWG7gq+mZi+F5D6CnbeLKsPByH6TU8TCJKhdKDLJrIyjVHv+vQv1MfMFqvCNo1IG7yifDK+fIw==", "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, "node_modules/@types/mime": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "dev": true }, + "node_modules/@types/multicast-dns": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/multicast-dns/-/multicast-dns-7.2.1.tgz", + "integrity": "sha512-A2PmB8MRcNVEkw6wzGT5rtBHqyHOVjiRMkJH+zpJKXipSi+GGkHg6JjNFApDiYK9WefJqkVG0taln1VMl4TGfw==", + "dev": true, + "dependencies": { + "@types/dns-packet": "*", + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.0.tgz", @@ -206,20 +220,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/dns-packet": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.3.0.tgz", + "integrity": "sha512-Nce7YLu6YCgWRvOmDBsJMo9M5/jV3lEZ5vUWnWXYmwURvPylHvq7nkDWhNmk1ZQoZZOP7oQh/S0lSxbisKOfHg==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=", - "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, "node_modules/long": { @@ -227,21 +236,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "node_modules/mdns": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/mdns/-/mdns-2.7.2.tgz", - "integrity": "sha512-NBOQT22DKvuNWVY7nKNbs6w9eGRyPwnc4ZjKOsCG2G/4wNt1+IyiHvc+5yhcAUZLG46cOY321YW7Ufz3lMtrhw==", - "hasInstallScript": true, - "dependencies": { - "bindings": "~1.2.1", - "nan": "^2.14.0" - } - }, - "node_modules/mdns/node_modules/bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - }, "node_modules/memoize-one": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", @@ -263,10 +257,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + "node_modules/multicast-dns": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.3.tgz", + "integrity": "sha512-TzxgGSLRLB7tqAlzjgd2x2ZE0cDsGFq4rs9W4yE5xp+7hlRXeUQGtXZsTGfGw2FwWB45rfe8DtXMYBpZGMLUng==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } }, "node_modules/node-dns-sd": { "version": "0.4.2", @@ -302,41 +303,18 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.46.tgz", "integrity": "sha512-dqpbzK/KDsOlEt+oyB3rv+u1IxlLFziZu/Z0adfRKoelkr+sTd6QcgiQC+HWq/vkYkHwG5ot2LxgV05aAjnhcg==" }, - "node_modules/query-string": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.0.tgz", - "integrity": "sha512-Iy7moLybliR5ZgrK/1R3vjrXq03S13Vz4Rbm5Jg3EFq1LUmQppto0qtXz4vqZ386MSRjZgnTSZ9QC+NZOSd/XA==", - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/split-on-first": { + "node_modules/thunky": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", - "engines": { - "node": ">=4" - } + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" } }, "dependencies": { + "@leichtgewicht/ip-codec": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", + "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==" + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -430,26 +408,36 @@ "webpack-inject-plugin": "^1.0.2" } }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "@types/mdns": { - "version": "0.0.34", - "resolved": "https://registry.npmjs.org/@types/mdns/-/mdns-0.0.34.tgz", - "integrity": "sha512-4Rrt/0wRAudtOnmhfDdoFhy5r20yHe0KiDK+/+I9RBBMW67F4S6y8tJH06AzrUDZzS/SH/U2pw1W0lrgQ+OlPg==", + "@types/dns-packet": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.2.2.tgz", + "integrity": "sha512-Rqq7rGKIdn7OTWG7gq+mZi+F5D6CnbeLKsPByH6TU8TCJKhdKDLJrIyjVHv+vQv1MfMFqvCNo1IG7yifDK+fIw==", "dev": true, "requires": { "@types/node": "*" } }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, "@types/mime": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "dev": true }, + "@types/multicast-dns": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@types/multicast-dns/-/multicast-dns-7.2.1.tgz", + "integrity": "sha512-A2PmB8MRcNVEkw6wzGT5rtBHqyHOVjiRMkJH+zpJKXipSi+GGkHg6JjNFApDiYK9WefJqkVG0taln1VMl4TGfw==", + "dev": true, + "requires": { + "@types/dns-packet": "*", + "@types/node": "*" + } + }, "@types/node": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.0.tgz", @@ -508,37 +496,19 @@ } } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=" + "dns-packet": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.3.0.tgz", + "integrity": "sha512-Nce7YLu6YCgWRvOmDBsJMo9M5/jV3lEZ5vUWnWXYmwURvPylHvq7nkDWhNmk1ZQoZZOP7oQh/S0lSxbisKOfHg==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "mdns": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/mdns/-/mdns-2.7.2.tgz", - "integrity": "sha512-NBOQT22DKvuNWVY7nKNbs6w9eGRyPwnc4ZjKOsCG2G/4wNt1+IyiHvc+5yhcAUZLG46cOY321YW7Ufz3lMtrhw==", - "requires": { - "bindings": "~1.2.1", - "nan": "^2.14.0" - }, - "dependencies": { - "bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - } - } - }, "memoize-one": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", @@ -554,10 +524,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + "multicast-dns": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.3.tgz", + "integrity": "sha512-TzxgGSLRLB7tqAlzjgd2x2ZE0cDsGFq4rs9W4yE5xp+7hlRXeUQGtXZsTGfGw2FwWB45rfe8DtXMYBpZGMLUng==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } }, "node-dns-sd": { "version": "0.4.2", @@ -591,26 +565,10 @@ } } }, - "query-string": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.0.tgz", - "integrity": "sha512-Iy7moLybliR5ZgrK/1R3vjrXq03S13Vz4Rbm5Jg3EFq1LUmQppto0qtXz4vqZ386MSRjZgnTSZ9QC+NZOSd/XA==", - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "split-on-first": { + "thunky": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" } } } diff --git a/plugins/chromecast/package.json b/plugins/chromecast/package.json index 2f9a829f2..f9b2399ef 100644 --- a/plugins/chromecast/package.json +++ b/plugins/chromecast/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/chromecast", - "version": "0.1.32", + "version": "0.1.34", "description": "Send video, audio, and text to speech notifications to Chromecast and Google Home devices", "author": "Scrypted", "license": "Apache-2.0", @@ -32,14 +32,13 @@ "dependencies": { "@scrypted/sdk": "file:../../sdk", "castv2-promise": "^1.0.0", - "mdns": "^2.7.2", "memoize-one": "^5.1.1", "mime": "^2.5.2", - "query-string": "^7.0.0" + "multicast-dns": "^7.2.3" }, "devDependencies": { - "@types/mdns": "^0.0.34", "@types/mime": "^2.0.3", + "@types/multicast-dns": "^7.2.1", "@types/node": "^16.9.0" } } diff --git a/plugins/chromecast/src/main.ts b/plugins/chromecast/src/main.ts index 0ffdd71ae..7f146bbe5 100644 --- a/plugins/chromecast/src/main.ts +++ b/plugins/chromecast/src/main.ts @@ -1,7 +1,7 @@ import util from 'util'; import sdk, { Device, DeviceProvider, EngineIOHandler, HttpRequest, MediaObject, MediaPlayer, MediaPlayerOptions, MediaPlayerState, MediaStatus, Refresh, RTCAVMessage, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes } from '@scrypted/sdk'; import { EventEmitter } from 'events'; -import mdns from 'mdns'; +import mdns from 'multicast-dns'; import mime from 'mime'; const { mediaManager, systemManager, endpointManager, deviceManager, log } = sdk; @@ -18,15 +18,15 @@ util.inherits(ScryptedMediaReceiver, DefaultMediaReceiver); // in the quickjs environment. function toBuffer(buffer) { if (buffer && (buffer.constructor.name === ArrayBuffer.name || buffer.constructor.name === Uint8Array.name)) { - var ret = Buffer.from(buffer); + const ret = Buffer.from(buffer); return ret; } return buffer; } const BufferConcat = Buffer.concat; Buffer.concat = function (bufs) { - var copy = []; - for (var buf of bufs) { + const copy = []; + for (const buf of bufs) { copy.push(toBuffer(buf)); } return BufferConcat(copy); @@ -66,7 +66,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng return this.playerPromise = this.connectClient() .then(client => { return new Promise((resolve, reject) => { - this.log.i('launching'); + this.console.log('launching'); client.launch(app, (err, player) => { if (err) { reject(err); @@ -74,12 +74,12 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } player.on('close', () => { - this.log.i('player closed'); + this.console.log('player closed'); player.removeAllListeners(); this.playerPromise = undefined; }); - this.log.i('player launched.'); + this.console.log('player launched.'); resolve(player); }); }); @@ -96,9 +96,9 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng return this.clientPromise; } - var promise; + let promise; return this.clientPromise = promise = new Promise((resolve, reject) => { - var client = new Client(); + const client = new Client(); const cleanup = () => { client.removeAllListeners(); @@ -109,12 +109,12 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } client.on('close', cleanup); client.on('error', err => { - this.log.i(`Client error: ${err.message}`); + this.console.log(`Client error: ${err.message}`); cleanup(); reject(err); }); client.on('status', async status => { - this.log.i(JSON.stringify(status)); + this.console.log(JSON.stringify(status)); try { await this.joinPlayer(); } @@ -122,7 +122,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } }) client.connect(this.host, () => { - this.log.i(`client connected.`); + this.console.log(`client connected.`); resolve(client); }); }) @@ -131,7 +131,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng tokens = new Map(); async sendMediaToClient(title: string, mediaUrl: string, mimeType: string, opts?: any) { - var media: any = { + const media: any = { // Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType. contentId: mediaUrl, contentType: mimeType, @@ -156,14 +156,14 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng const player = await this.connectPlayer(DefaultMediaReceiver) player.load(media, opts, (err, status) => { if (err) { - this.log.e(`load error: ${err}`); + this.console.error(`load error: ${err}`); return; } - this.log.i(`media loaded playerState=${status.playerState}`); + this.console.log(`media loaded playerState=${status.playerState}`); }); } - async load(media: string|MediaObject, options: MediaPlayerOptions) { + async load(media: string | MediaObject, options: MediaPlayerOptions) { // check to see if this is url friendly media. if (typeof media === 'string') media = mediaManager.createMediaObject(media, ScryptedMimeTypes.Url); @@ -194,7 +194,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng const token = Math.random().toString(); this.tokens.set(token, media); - var castMedia: any = { + const castMedia: any = { contentId: cameraStreamAuthToken, contentType: ScryptedMimeTypes.LocalUrl, streamType: 'LIVE', @@ -218,10 +218,10 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng const player = await this.connectPlayer(ScryptedMediaReceiver as any) player.load(castMedia, opts, (err, status) => { if (err) { - this.log.e(`load error: ${err}`); + this.console.error(`load error: ${err}`); return; } - this.log.i(`media loaded playerState=${status.playerState}`); + this.console.log(`media loaded playerState=${status.playerState}`); }); } @@ -230,45 +230,45 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng const ws = new WebSocket(webSocketUrl); ws.onmessage = async (message) => { - const token = message.data as string; + const token = message.data as string; - const videoStream = this.tokens.get(token); - if (!videoStream) { - ws.close(); - return; - } + const videoStream = this.tokens.get(token); + if (!videoStream) { + ws.close(); + return; + } - const offer: RTCAVMessage = JSON.parse((await mediaManager.convertMediaObjectToBuffer( - videoStream, - ScryptedMimeTypes.RTCAVOffer - )).toString()); + const offer: RTCAVMessage = JSON.parse((await mediaManager.convertMediaObjectToBuffer( + videoStream, + ScryptedMimeTypes.RTCAVOffer + )).toString()); - ws.send(JSON.stringify(offer)); + ws.send(JSON.stringify(offer)); - const answer = await new Promise(resolve => ws.onmessage = (message) => resolve(message.data)) as string; - const mo = mediaManager.createMediaObject(Buffer.from(answer), ScryptedMimeTypes.RTCAVAnswer); + const answer = await new Promise(resolve => ws.onmessage = (message) => resolve(message.data)) as string; + const mo = mediaManager.createMediaObject(Buffer.from(answer), ScryptedMimeTypes.RTCAVAnswer); + const result = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.RTCAVOffer); + ws.send(result.toString()); + + ws.onmessage = async (message) => { + const mo = mediaManager.createMediaObject(Buffer.from(message.data), ScryptedMimeTypes.RTCAVAnswer); const result = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.RTCAVOffer); ws.send(result.toString()); + } - ws.onmessage = async (message) => { - const mo = mediaManager.createMediaObject(Buffer.from(message.data), ScryptedMimeTypes.RTCAVAnswer); - const result = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.RTCAVOffer); - ws.send(result.toString()); - } - - const emptyObject = JSON.stringify({ - description: null, - id: offer.id, - candidates: [], - configuration: null, - }); - while (true ){ - const mo = mediaManager.createMediaObject(Buffer.from(emptyObject), ScryptedMimeTypes.RTCAVAnswer); - const result = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.RTCAVOffer); - ws.send(result.toString()); - } + const emptyObject = JSON.stringify({ + description: null, + id: offer.id, + candidates: [], + configuration: null, + }); + while (true) { + const mo = mediaManager.createMediaObject(Buffer.from(emptyObject), ScryptedMimeTypes.RTCAVAnswer); + const result = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.RTCAVOffer); + ws.send(result.toString()); + } } -} + } mediaPlayerPromise: Promise; @@ -278,10 +278,10 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng return this.mediaPlayerPromise; } - this.log.i('attempting to join session2'); + this.console.log('attempting to join session2'); return this.mediaPlayerPromise = this.connectClient() .then(client => { - this.log.i('attempting to join session'); + this.console.log('attempting to join session'); return new Promise((resolve, reject) => { client.getSessions((err, applications) => { if (err) { @@ -302,7 +302,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng } player.on('close', () => { - this.log.i('player closed'); + this.console.log('player closed'); player.removeAllListeners(); this.mediaPlayerPromise = undefined; this.mediaPlayerStatus = undefined; @@ -333,7 +333,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng }); }) .catch(e => { - this.log.e(`Error connecting to current session ${e}`); + this.console.error(`Error connecting to current session ${e}`); this.mediaPlayerPromise = undefined; throw e; }) @@ -385,7 +385,7 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng return this.getMediaStatusInternal(); } getMediaStatusInternal(): MediaStatus { - var mediaPlayerState: MediaPlayerState = this.parseState(); + const mediaPlayerState: MediaPlayerState = this.parseState(); const media = this.mediaPlayerStatus && this.mediaPlayerStatus.media; const metadata = media && media.metadata; let position = this.mediaPlayerStatus && this.mediaPlayerStatus.currentTime; @@ -438,59 +438,87 @@ class CastDevice extends ScryptedDeviceBase implements MediaPlayer, Refresh, Eng class CastDeviceProvider extends ScryptedDeviceBase implements DeviceProvider { devices: any = {}; search = new EventEmitter(); - browser = mdns.createBrowser(mdns.tcp('googlecast')); + browser = mdns() searching: boolean; constructor() { super(null); - this.browser.on('serviceUp', (service) => { - this.log.i(JSON.stringify(service)); - var id = service.txtRecord.id; - if (!id) { - // wtf? - return; + + this.browser.on('response', response => { + for (const additional of response.additionals) { + if (additional.name.endsWith('_googlecast._tcp.local') && additional.type === 'TXT') { + const txt = new Map(); + for (const d of additional.data as any) { + const parts = d.toString().split('='); + txt.set(parts[0], parts[1]); + } + + const id = txt.get('id'); + if (!id) { + // wtf? + return; + } + const model = txt.get('md'); + const name = txt.get('fn');; + + const service = response.additionals.find(check => check.type === 'SRV' && check.name === additional.name); + if (!service) { + console.warn('no SRV found for', additional.name); + continue; + } + const host = (service.data as any).target; + let arec = response.additionals.find(check => check.name === host && check.type === 'A'); + if (!arec) + arec = response.additionals.find(check => check.name === host && check.type === 'AAAA'); + if (!arec) { + console.warn('no A/AAAA record found for', additional.name); + continue; + } + const ip = arec.data as string; + const port = (service.data as any).port; + + this.onDiscover(id, name, model, ip, port); + } } - var model = service.txtRecord.md; - var name = service.txtRecord.fn; - var type = (model && model.indexOf('Google Home') != -1 && model.indexOf('Hub') == -1) ? ScryptedDeviceType.Speaker : ScryptedDeviceType.Display; - - var interfaces = [ - ScryptedInterface.MediaPlayer, - ScryptedInterface.Refresh, - ScryptedInterface.StartStop, - ScryptedInterface.Pause, - ScryptedInterface.EngineIOHandler, - ]; - - var device: Device = { - nativeId: id, - name, - info: { - model, - }, - type, - interfaces, - }; - - const host = service.addresses[0]; - const port = service.port; - - - this.log.i(`found cast device: ${name}`); - - var castDevice = this.devices[id] || (this.devices[id] = new CastDevice(this, device.nativeId)); - castDevice.device = device; - castDevice.host = host; - castDevice.port = port; - - this.search.emit(id); - deviceManager.onDeviceDiscovered(device); - }); + }) this.discoverDevices(30000); } + onDiscover(id: string, name: string, model: string, ip: string, port: number) { + + const interfaces = [ + ScryptedInterface.MediaPlayer, + ScryptedInterface.Refresh, + ScryptedInterface.StartStop, + ScryptedInterface.Pause, + ScryptedInterface.EngineIOHandler, + ]; + + const type = (model && model.indexOf('Google Home') != -1 && model.indexOf('Hub') == -1) ? ScryptedDeviceType.Speaker : ScryptedDeviceType.Display; + + const device: Device = { + nativeId: id, + name, + info: { + model, + }, + type, + interfaces, + }; + + console.log(`found cast device: ${name}`); + + const castDevice = this.devices[id] || (this.devices[id] = new CastDevice(this, device.nativeId)); + castDevice.device = device; + castDevice.host = ip; + castDevice.port = port; + + this.search.emit(id); + deviceManager.onDeviceDiscovered(device); + } + getDevice(nativeId: string) { return this.devices[nativeId] || (this.devices[nativeId] = new CastDevice(this, nativeId)); } @@ -501,12 +529,24 @@ class CastDeviceProvider extends ScryptedDeviceBase implements DeviceProvider { } this.searching = true; duration = duration || 10000; - setTimeout(() => { - this.searching = false; - this.browser.stop(); - }, duration) + // setTimeout(() => { + // this.searching = false; + // this.browser.stop(); + // }, duration) - this.browser.start(); + // this.browser.start(); + for (let i = 0; i < 6; i++) { + setTimeout(() => { + this.browser.query([ + { + type: 'PTR', + name: '_googlecast._tcp.local' + } + ]); + }, i * 10000) + } + + // this.querySSDP(); } }