diff --git a/README.md b/README.md index bf636c1..173a8da 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,33 @@ HTML5 media elements also have a different `Cast...` context menu item that trig ## Building ### Requirements +* Node.js v12.x.x +* Native build tools (see [here](https://github.com/nodejs/node-gyp#installation)) +* Bonjour/Avahi (on Windows/Linux respectively) -* Node.js <= 13.x +Cross-compiling native depedencies may be possible, but isn't tested or supported. Build script options are provided for building/packaging on other platforms, but assume they won't work. Packaging on Linux for other Linux package formats should work fine. + +### Installing dependencies + +#### Windows: +* [Bonjour SDK for Windows](https://developer.apple.com/download/more/?=Bonjour%20SDK%20for%20Windows) +* [NSIS](https://nsis.sourceforge.io/Download) + +#### Debian / Ubuntu: +````sh +sudo apt install libavahi-compat-libdnssd-dev dpkg rpm +```` + +#### Fedora: +````sh +sudo dnf install avahi-compat-libdns_sd-devel dpkg rpm-build +```` + +#### Arch Linux: +````sh +sudo pacman -S avahi dpkg +yay -S rpm-org +```` ### Instructions @@ -47,7 +72,12 @@ git clone https://github.com/hensm/fx_cast.git cd fx_cast npm install npm run build + +# Install manifest for dist/ build. Installs to +# user-specific location and overrides a system-wide +# install. Call `remove-manifest` to restore previous state. npm run install-manifest +npm run remove-manifest ```` This will build the ext and app, outputting to `dist/`: @@ -82,38 +112,6 @@ npm run start --prefix ./ext ### Packaging -#### Requirements -* `dpkg-deb` (if building .deb packages) -* `rpmbuild` (if building .rpm packages) -* `makensis` (if building Windows installer packages) -* macOS (if building macOS installer packages) - * Xcode (optional if not building native receiver selector) - -_**Note**: macOS packages can only be built on macOS._ - -#### Installing dependencies - -##### macOS: -````sh -brew install dpkg rpm makensis -```` - -##### Debian / Ubuntu: -````sh -sudo apt install dpkg rpm nsis -```` - -##### Fedora: -````sh -sudo dnf install dpkg rpm-build mingw-nsis -```` - -##### Arch Linux: -```sh -sudo pacman -S dpkg -yay -S rpm-org nsis -``` - Build and package extension and bridge application for current platform: ````sh diff --git a/app/bin/build.js b/app/bin/build.js index cf22f69..5975029 100644 --- a/app/bin/build.js +++ b/app/bin/build.js @@ -93,6 +93,10 @@ fs.ensureDirSync(BUILD_PATH); fs.ensureDirSync(DIST_PATH, { recursive: true }); +const MDNS_BINDING_PATH = path.join( + __dirname, "../node_modules/mdns/build/Release/"); +const MDNS_BINDING_NAME = "dns_sd_bindings.node"; + async function build () { /** * Because the native receiver selector can only be built on @@ -195,6 +199,9 @@ async function build () { , "--output", path.join(BUILD_PATH, executableName[argv.platform]) ]); + fs.copySync(path.join(MDNS_BINDING_PATH, MDNS_BINDING_NAME) + , path.join(BUILD_PATH, MDNS_BINDING_NAME)); + // Build NativeMacReceiverSelector if (isBuildingForMacOnMac && !argv.skipNativeBuilds) { const selectorPath = path.join(__dirname, "../selector/mac/"); @@ -241,6 +248,10 @@ async function build () { path.join(BUILD_PATH, executableName[argv.platform]) , path.join(DIST_PATH, executableName[argv.platform]) , { overwrite: true }); + fs.moveSync( + path.join(BUILD_PATH, MDNS_BINDING_NAME) + , path.join(DIST_PATH, MDNS_BINDING_NAME) + , { overwrite: true }); if (isBuildingForMacOnMac && !argv.skipNativeBuilds) { fs.moveSync( @@ -251,7 +262,7 @@ async function build () { } // Remove build directory - fs.removeSync(BUILD_PATH); + //fs.removeSync(BUILD_PATH); } /** @@ -331,6 +342,8 @@ function packageDarwin ( // Move files to root fs.moveSync(path.join(BUILD_PATH, platformExecutableName) , path.join(rootExecutablePath, platformExecutableName)); + fs.moveSync(path.join(BUILD_PATH, MDNS_BINDING_NAME) + , path.join(rootExecutablePath, MDNS_BINDING_NAME)); fs.moveSync(path.join(BUILD_PATH, manifestName) , path.join(rootManifestPath, manifestName)); @@ -417,11 +430,13 @@ function packageLinuxDeb ( fs.moveSync( path.join(BUILD_PATH, platformExecutableName) , path.join(rootExecutablePath, platformExecutableName)); + fs.moveSync( + path.join(BUILD_PATH, MDNS_BINDING_NAME) + , path.join(rootExecutablePath, MDNS_BINDING_NAME)); fs.moveSync( path.join(BUILD_PATH, manifestName) , path.join(rootManifestPath, manifestName)); - const controlDir = path.join(__dirname, "../packaging/linux/deb/DEBIAN/"); const controlOutputDir = path.join(rootPath, path.basename(controlDir)); const controlFilePath = path.join(controlOutputDir, "control"); @@ -480,6 +495,7 @@ function packageLinuxRpm ( , manifestPath: platformManifestPath , executableName: platformExecutableName , manifestName + , bindingName: MDNS_BINDING_NAME }; fs.writeFileSync(specOutputPath @@ -528,6 +544,7 @@ function packageWin32 ( , executableName: platformExecutableName , executablePath: platformExecutablePath , manifestName + , bindingName: MDNS_BINDING_NAME , winRegistryKey: WIN_REGISTRY_KEY , outputName , licensePath: LICENSE_PATH diff --git a/app/package-lock.json b/app/package-lock.json index 9e06ae6..0f3cb96 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -181,16 +181,6 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, - "@types/dnssd": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/dnssd/-/dnssd-0.4.1.tgz", - "integrity": "sha512-YJKWk0eKxTKvyWeXmnDqVLCRBqAJCDoAaCT1QA5zp0Wp7yhYmY0/F3aIm4QvRffCuzrkZV4XYVGdmpLce44eZw==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/node": "*" - } - }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -213,6 +203,15 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" }, + "@types/mdns": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/mdns/-/mdns-0.0.33.tgz", + "integrity": "sha512-Uhm8lyPXIe2hz+aiiD6PXkGc7dlFk6Kc2HJlqmWXhAJktD5Arg27EYre0wv1SmmpSGQZ30X6iCjZyIGYKxmCxw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mime-types": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", @@ -338,6 +337,11 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.44.tgz", "integrity": "sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ==" }, + "bindings": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", + "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" + }, "bplist-creator": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz", @@ -503,11 +507,6 @@ "path-type": "^4.0.0" } }, - "dnssd": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/dnssd/-/dnssd-0.4.1.tgz", - "integrity": "sha512-mEz5Ii+o+k3kYHTXY6fTLOjCwraX8TQowIgUySAbEYuGqtSMbfBc/tvDZ8wGPywnmlLE6/XeXi6qPcAKVTvPUQ==" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -922,6 +921,15 @@ "@nsis/language-data": "^0.7.1" } }, + "mdns": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/mdns/-/mdns-2.5.1.tgz", + "integrity": "sha512-JglS7Ed3Yf0BCpyC7LXA1MUrumMV8jj4g67nT3+m886SFYllz2HWBg8ObywFXWbBSv5gW0meMOOS4vVa2jZGCw==", + "requires": { + "bindings": "~1.2.1", + "nan": "^2.14.0" + } + }, "merge2": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", @@ -999,6 +1007,11 @@ "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==", "dev": true }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", diff --git a/app/package.json b/app/package.json index 8750fe3..b85fc2b 100644 --- a/app/package.json +++ b/app/package.json @@ -14,14 +14,14 @@ "bplist-creator": "0.0.8", "bplist-parser": "^0.2.0", "castv2": "^0.1.10", - "dnssd": "^0.4.1", "fast-srp-hap": "^1.0.1", + "mdns": "^2.5.1", "mime-types": "^2.1.26", "node-fetch": "^2.6.0", "tweetnacl": "^1.0.1" }, "devDependencies": { - "@types/dnssd": "^0.4.1", + "@types/mdns": "0.0.33", "@types/mime-types": "^2.1.0", "@types/node": "^12.12.24", "@types/node-fetch": "^2.5.4", diff --git a/app/packaging/linux/deb/DEBIAN/control b/app/packaging/linux/deb/DEBIAN/control index 8ec3e9f..7a29a49 100644 --- a/app/packaging/linux/deb/DEBIAN/control +++ b/app/packaging/linux/deb/DEBIAN/control @@ -4,3 +4,4 @@ Priority: optional Maintainer: {{{author}}} Architecture: amd64 Description: {{applicationName}} +Depends: avahi-daemon, libavahi-compat-libdnssd1 diff --git a/app/packaging/linux/rpm/package.spec b/app/packaging/linux/rpm/package.spec index 5bebc44..be2cfa8 100644 --- a/app/packaging/linux/rpm/package.spec +++ b/app/packaging/linux/rpm/package.spec @@ -3,6 +3,7 @@ Summary: {{applicationName}} Version: {{applicationVersion}} Release: 1 License: MIT +Requires: avahi, avahi-compat-libdns_sd %description {{applicationName}} @@ -13,6 +14,7 @@ mkdir -p $RPM_BUILD_ROOT/{{{executablePath}}} \ $RPM_BUILD_ROOT/{{{manifestPath}}} cp %{_distdir}/{{{executableName}}} $RPM_BUILD_ROOT/{{{executablePath}}} +cp ${_distdir}/{{{bindingName}}} $RPM_BUILD_ROOT/{{{executablePath}}} cp %{_distdir}/{{{manifestName}}} $RPM_BUILD_ROOT/{{{manifestPath}}} %files diff --git a/app/packaging/win/installer.nsi b/app/packaging/win/installer.nsi index 8728e58..fd3fdd8 100644 --- a/app/packaging/win/installer.nsi +++ b/app/packaging/win/installer.nsi @@ -44,6 +44,7 @@ Section # Main executable File "{{executableName}}" + File "{{bindingName}}" File "{{manifestName}}" # Native manifest key diff --git a/app/src/bridge/StatusListener.ts b/app/src/bridge/StatusListener.ts index b2d97cf..616e757 100644 --- a/app/src/bridge/StatusListener.ts +++ b/app/src/bridge/StatusListener.ts @@ -26,6 +26,10 @@ export default class StatusListener extends EventEmitter { this.client.on("close", () => { clearInterval(this.clientHeartbeatIntervalId!); }); + + this.client.on("error", () => { + clearInterval(this.clientHeartbeatIntervalId!); + }); } /** diff --git a/app/src/bridge/index.ts b/app/src/bridge/index.ts index 8bebef7..658fa8b 100755 --- a/app/src/bridge/index.ts +++ b/app/src/bridge/index.ts @@ -1,4 +1,4 @@ -import dnssd from "dnssd"; +import mdns from "mdns"; import child_process from "child_process"; import events from "events"; @@ -74,7 +74,7 @@ let receiverSelectorAppClosed = true; // Local media server let mediaServer: http.Server; -let browser: dnssd.Browser; +let browser: mdns.Browser; // Existing counterpart Media/Session objects @@ -483,8 +483,8 @@ async function handleMediaServerMessage (message: Message) { function initialize (options: InitializeOptions) { - browser = new dnssd.Browser(dnssd.tcp("googlecast")); - browser.on("error", err => { + browser = new mdns.Browser(mdns.tcp("googlecast")) + browser.on("error", (err: any) => { console.error("Discovery failed", err); }); @@ -498,23 +498,23 @@ function initialize (options: InitializeOptions) { browser.start(); - function onBrowserServiceUp (service: dnssd.Service) { + function onBrowserServiceUp (service: mdns.Service) { sendMessage({ subject: "main:/serviceUp" , data: { host: service.addresses[0] , port: service.port - , id: service.txt.id - , friendlyName: service.txt.fn + , id: service.txtRecord.id + , friendlyName: service.txtRecord.fn } }); } - function onBrowserServiceDown (service: dnssd.Service) { + function onBrowserServiceDown (service: mdns.Service) { sendMessage({ subject: "main:/serviceDown" , data: { - id: service.txt.id + id: service.txtRecord.id } }); } @@ -523,8 +523,8 @@ function initialize (options: InitializeOptions) { // Receiver status listeners for status mode const statusListeners = new Map(); - function onStatusBrowserServiceUp (service: dnssd.Service) { - const { id } = service.txt; + function onStatusBrowserServiceUp (service: mdns.Service) { + const { id } = service.txtRecord; const listener = new StatusListener( service.addresses[0] @@ -561,8 +561,8 @@ function initialize (options: InitializeOptions) { statusListeners.set(id, listener); } - function onStatusBrowserServiceDown (service: dnssd.Service) { - const { id } = service.txt; + function onStatusBrowserServiceDown (service: mdns.Service) { + const { id } = service.txtRecord; if (statusListeners.has(id)) { statusListeners.get(id)!.deregister(); diff --git a/package.json b/package.json index 4c7aaad..f59df50 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "postinstall:ext": "cd ext && npm install", "build": "npm run build:app && npm run build:ext", "build:app": "cd app && npm run build", - "build:ext": "cd ext &&npm run build", + "build:ext": "cd ext && npm run build", "package": "npm run package:app && npm run package:ext", "package:app": "cd app && npm run package", "package:ext": "cd ext && npm run package",