mirror of
https://gitlab.com/futo-org/fcast.git
synced 2025-06-24 21:25:23 +00:00
Implemented main window with QR code for desktop receiver.
This commit is contained in:
parent
85530ca218
commit
cefe19e9ff
18 changed files with 501 additions and 35 deletions
|
@ -81,6 +81,8 @@ class PlayerActivity : AppCompatActivity() {
|
|||
} else if (playbackState == ExoPlayer.STATE_BUFFERING) {
|
||||
setStatus(true, null)
|
||||
}
|
||||
|
||||
//TODO: Send playback update
|
||||
}
|
||||
|
||||
override fun onPlayerError(error: PlaybackException) {
|
||||
|
@ -103,6 +105,8 @@ class PlayerActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: Send error notification
|
||||
|
||||
setStatus(false, getFullExceptionMessage(error))
|
||||
}
|
||||
|
||||
|
@ -110,7 +114,7 @@ class PlayerActivity : AppCompatActivity() {
|
|||
super.onVolumeChanged(volume)
|
||||
_scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
NetworkService.instance?.sendCastVolumeUpdate(VolumeUpdateMessage(volume.toDouble()))
|
||||
NetworkService.instance?.sendCastVolumeUpdate(VolumeUpdateMessage(System.currentTimeMillis(), volume.toDouble()))
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "Unhandled error sending volume update", e)
|
||||
}
|
||||
|
|
200
receivers/electron/package-lock.json
generated
200
receivers/electron/package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.8",
|
||||
"qrcode": "^1.5.3",
|
||||
"utf-8-validate": "^6.0.3",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
|
@ -486,11 +487,18 @@
|
|||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
|
@ -602,6 +610,14 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001451",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz",
|
||||
|
@ -643,6 +659,16 @@
|
|||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-deep": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
|
||||
|
@ -673,7 +699,6 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
|
@ -684,8 +709,7 @@
|
|||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.19",
|
||||
|
@ -730,6 +754,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decompress-response": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||
|
@ -790,6 +822,11 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
|
||||
},
|
||||
"node_modules/dns-js": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-js/-/dns-js-0.2.1.tgz",
|
||||
|
@ -848,6 +885,16 @@
|
|||
"integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/encode-utf8": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz",
|
||||
"integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw=="
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
|
@ -1044,7 +1091,6 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
|
@ -1073,6 +1119,14 @@
|
|||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
|
@ -1296,6 +1350,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
|
@ -1426,7 +1488,6 @@
|
|||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"p-locate": "^4.1.0"
|
||||
},
|
||||
|
@ -1627,7 +1688,6 @@
|
|||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"p-try": "^2.0.0"
|
||||
},
|
||||
|
@ -1642,7 +1702,6 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"p-limit": "^2.2.0"
|
||||
},
|
||||
|
@ -1654,7 +1713,6 @@
|
|||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
@ -1663,7 +1721,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
@ -1719,6 +1776,14 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
|
@ -1756,6 +1821,23 @@
|
|||
"node": ">=0.4.x"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
|
||||
"integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==",
|
||||
"dependencies": {
|
||||
"dijkstrajs": "^1.0.1",
|
||||
"encode-utf8": "^1.0.3",
|
||||
"pngjs": "^5.0.0",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"qrcode": "bin/qrcode"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
|
@ -1789,6 +1871,19 @@
|
|||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
|
@ -1942,6 +2037,11 @@
|
|||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||
},
|
||||
"node_modules/shallow-clone": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||
|
@ -2001,6 +2101,30 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sumchecker": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
|
@ -2377,12 +2501,30 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
|
||||
},
|
||||
"node_modules/wildcard": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
@ -2409,12 +2551,50 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dependencies": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dependencies": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"author": "Koen Jeukendrup",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "rm -rf dist/ && webpack --config ./webpack.config.js && cp src/*.html dist/ && cp src/*.js dist/ && cp src/*.css dist/ && cp app.ico dist/ && cp app.png dist/",
|
||||
"build": "rm -rf dist/ && webpack --config ./webpack.config.js && cp -r src/player dist/player && cp -r src/main dist/main && cp app.ico dist/ && cp app.png dist/",
|
||||
"start": "npm run build && electron ."
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -21,6 +21,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.8",
|
||||
"qrcode": "^1.5.3",
|
||||
"utf-8-validate": "^6.0.3",
|
||||
"ws": "^8.14.2"
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron';
|
||||
import path = require('path');
|
||||
import { TcpListenerService } from './TcpListenerService';
|
||||
import { PlaybackUpdateMessage, SetVolumeMessage, VolumeUpdateMessage } from './Packets';
|
||||
import { PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets';
|
||||
import { DiscoveryService } from './DiscoveryService';
|
||||
import { Updater } from './Updater';
|
||||
import { WebSocketListenerService } from './WebSocketListenerService';
|
||||
import * as os from 'os';
|
||||
|
||||
export default class Main {
|
||||
static shouldOpenMainWindow = true;
|
||||
static playerWindow: Electron.BrowserWindow;
|
||||
static mainWindow: Electron.BrowserWindow;
|
||||
static application: Electron.App;
|
||||
static tcpListenerService: TcpListenerService;
|
||||
|
@ -19,6 +22,10 @@ export default class Main {
|
|||
const trayicon = nativeImage.createFromPath(icon)
|
||||
const tray = new Tray(trayicon.resize({ width: 16 }));
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Open window',
|
||||
click: () => Main.openMainWindow()
|
||||
},
|
||||
{
|
||||
label: 'Check for updates',
|
||||
click: async () => {
|
||||
|
@ -83,10 +90,6 @@ export default class Main {
|
|||
tray.setContextMenu(contextMenu);
|
||||
this.tray = tray;
|
||||
}
|
||||
|
||||
private static onClose() {
|
||||
Main.mainWindow = null;
|
||||
}
|
||||
|
||||
private static onReady() {
|
||||
Main.createTray();
|
||||
|
@ -100,38 +103,40 @@ export default class Main {
|
|||
|
||||
listeners.forEach(l => {
|
||||
l.emitter.on("play", (message) => {
|
||||
if (Main.mainWindow == null) {
|
||||
Main.mainWindow = new BrowserWindow({
|
||||
if (Main.playerWindow == null) {
|
||||
Main.playerWindow = new BrowserWindow({
|
||||
fullscreen: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
preload: path.join(__dirname, 'player/preload.js')
|
||||
}
|
||||
});
|
||||
|
||||
Main.mainWindow.setAlwaysOnTop(false, 'pop-up-menu');
|
||||
Main.mainWindow.show();
|
||||
Main.playerWindow.setAlwaysOnTop(false, 'pop-up-menu');
|
||||
Main.playerWindow.show();
|
||||
|
||||
Main.mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
Main.mainWindow.on('ready-to-show', () => {
|
||||
Main.mainWindow?.webContents?.send("play", message);
|
||||
Main.playerWindow.loadFile(path.join(__dirname, 'player/index.html'));
|
||||
Main.playerWindow.on('ready-to-show', () => {
|
||||
Main.playerWindow?.webContents?.send("play", message);
|
||||
});
|
||||
Main.playerWindow.on('closed', () => {
|
||||
Main.playerWindow = null;
|
||||
});
|
||||
Main.mainWindow.on('closed', Main.onClose);
|
||||
} else {
|
||||
Main.mainWindow?.webContents?.send("play", message);
|
||||
Main.playerWindow?.webContents?.send("play", message);
|
||||
}
|
||||
});
|
||||
|
||||
l.emitter.on("pause", () => Main.mainWindow?.webContents?.send("pause"));
|
||||
l.emitter.on("resume", () => Main.mainWindow?.webContents?.send("resume"));
|
||||
l.emitter.on("pause", () => Main.playerWindow?.webContents?.send("pause"));
|
||||
l.emitter.on("resume", () => Main.playerWindow?.webContents?.send("resume"));
|
||||
|
||||
l.emitter.on("stop", () => {
|
||||
Main.mainWindow.close();
|
||||
Main.mainWindow = null;
|
||||
Main.playerWindow.close();
|
||||
Main.playerWindow = null;
|
||||
});
|
||||
|
||||
l.emitter.on("seek", (message) => Main.mainWindow?.webContents?.send("seek", message));
|
||||
l.emitter.on("setvolume", (message) => Main.mainWindow?.webContents?.send("setvolume", message));
|
||||
l.emitter.on("seek", (message) => Main.playerWindow?.webContents?.send("seek", message));
|
||||
l.emitter.on("setvolume", (message) => Main.playerWindow?.webContents?.send("setvolume", message));
|
||||
l.start();
|
||||
|
||||
ipcMain.on('send-playback-update', (event: IpcMainEvent, value: PlaybackUpdateMessage) => {
|
||||
|
@ -144,7 +149,7 @@ export default class Main {
|
|||
});
|
||||
|
||||
ipcMain.on('toggle-full-screen', () => {
|
||||
const window = Main.mainWindow;
|
||||
const window = Main.playerWindow;
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
@ -153,17 +158,70 @@ export default class Main {
|
|||
});
|
||||
|
||||
ipcMain.on('exit-full-screen', () => {
|
||||
const window = Main.mainWindow;
|
||||
const window = Main.playerWindow;
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.setFullScreen(false);
|
||||
});
|
||||
|
||||
if (Main.shouldOpenMainWindow) {
|
||||
Main.openMainWindow();
|
||||
}
|
||||
}
|
||||
|
||||
static getAllIPv4Addresses() {
|
||||
const interfaces = os.networkInterfaces();
|
||||
const ipv4Addresses: string[] = [];
|
||||
|
||||
for (const interfaceName in interfaces) {
|
||||
const addresses = interfaces[interfaceName];
|
||||
if (!addresses) continue;
|
||||
|
||||
for (const addressInfo of addresses) {
|
||||
if (addressInfo.family === 'IPv4' && !addressInfo.internal) {
|
||||
ipv4Addresses.push(addressInfo.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ipv4Addresses;
|
||||
}
|
||||
|
||||
static openMainWindow() {
|
||||
if (Main.mainWindow) {
|
||||
Main.mainWindow.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
Main.mainWindow = new BrowserWindow({
|
||||
fullscreen: false,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'main/preload.js')
|
||||
}
|
||||
});
|
||||
|
||||
Main.mainWindow.loadFile(path.join(__dirname, 'main/index.html'));
|
||||
Main.mainWindow.on('closed', () => {
|
||||
Main.mainWindow = null;
|
||||
});
|
||||
|
||||
Main.mainWindow.show();
|
||||
|
||||
Main.mainWindow.on('ready-to-show', () => {
|
||||
Main.mainWindow.webContents.send("device-info", {name: os.hostname(), addresses: Main.getAllIPv4Addresses()});
|
||||
});
|
||||
}
|
||||
|
||||
static main(app: Electron.App) {
|
||||
Main.application = app;
|
||||
const argv = process.argv;
|
||||
if (argv.includes('--no-main-window')) {
|
||||
Main.shouldOpenMainWindow = false;
|
||||
}
|
||||
|
||||
Main.application.on('ready', Main.onReady);
|
||||
Main.application.on('window-all-closed', () => { });
|
||||
}
|
||||
|
|
BIN
receivers/electron/src/main/c.mp4
Normal file
BIN
receivers/electron/src/main/c.mp4
Normal file
Binary file not shown.
38
receivers/electron/src/main/index.html
Normal file
38
receivers/electron/src/main/index.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link href="./video-js.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
<title>FCast Receiver</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
<video id="video-player" class="video-js" controls preload="auto" data-setup='{}' style="object-fit: cover;">
|
||||
<p class="vjs-no-js">
|
||||
To view this video please enable JavaScript, and consider upgrading to a web browser that
|
||||
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
|
||||
</p>
|
||||
</video>
|
||||
<div id="overlay">
|
||||
<div id="title">FCast</div>
|
||||
<div id="connection-info">
|
||||
<div id="waiting-for-connection">Waiting for a connection</div>
|
||||
<div id="spinner" class="lds-ring"><div></div><div></div><div></div><div></div></div>
|
||||
<div id="manual-connection-info">Manual connection information</div>
|
||||
<div>
|
||||
<div id="ips">IPs</div><br />
|
||||
<div>Port<br>46899 (TCP), 46898 (WS)</div>
|
||||
</div>
|
||||
<div id="automatic-discovery">Automatic discovery is available via mDNS</div>
|
||||
<div id="qr-code"></div>
|
||||
<div id="scan-to-connect">Scan to connect</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>window.HELP_IMPROVE_VIDEOJS = false;</script>
|
||||
<script src="./video.min.js"></script>
|
||||
<script src="./qrcode.min.js"></script>
|
||||
<script src="./renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
5
receivers/electron/src/main/preload.js
Normal file
5
receivers/electron/src/main/preload.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onDeviceInfo: (callback) => ipcRenderer.on("device-info", callback)
|
||||
});
|
1
receivers/electron/src/main/qrcode.min.js
vendored
Normal file
1
receivers/electron/src/main/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
45
receivers/electron/src/main/renderer.js
Normal file
45
receivers/electron/src/main/renderer.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
const options = {
|
||||
textTrackSettings: false,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
controls: false
|
||||
};
|
||||
|
||||
const player = videojs("video-player", options, function onPlayerReady() {
|
||||
player.src({ type: "video/mp4", src: "./c.mp4" });
|
||||
});
|
||||
|
||||
|
||||
window.electronAPI.onDeviceInfo((_event, value) => {
|
||||
console.log("onDeviceInfo", value);
|
||||
|
||||
const ipsElement = document.getElementById('ips');
|
||||
if (ipsElement) {
|
||||
ipsElement.innerHTML = `IPs<br>${value.addresses.join('<br>')}`;
|
||||
}
|
||||
|
||||
const fcastConfig = {
|
||||
name: value.name,
|
||||
addresses: value.addresses,
|
||||
services: [
|
||||
{ port: 46899, type: 0 },
|
||||
{ port: 46898, type: 1 }
|
||||
]
|
||||
};
|
||||
|
||||
const json = JSON.stringify(fcastConfig);
|
||||
let base64 = btoa(json);
|
||||
base64 = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
const url = `fcast://r/${base64}`;
|
||||
console.log("qr", {json, url, base64});
|
||||
|
||||
const qrCodeElement = document.getElementById('qr-code');
|
||||
new QRCode(qrCodeElement, {
|
||||
text: url,
|
||||
width: 256,
|
||||
height: 256,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
correctLevel : QRCode.CorrectLevel.H
|
||||
});
|
||||
});
|
106
receivers/electron/src/main/style.css
Normal file
106
receivers/electron/src/main/style.css
Normal file
|
@ -0,0 +1,106 @@
|
|||
body, html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.video-js {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
#title {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#connection-info {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#qr-code {
|
||||
display: flex;
|
||||
margin-top: 20px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#update-dialog, #waiting-for-connection, #manual-connection-info, #ips, #automatic-discovery, #scan-to-connect {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#update-button {
|
||||
background: blue;
|
||||
padding: 10px 28px;
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#progress-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#progress-text {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.lds-ring {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.lds-ring div {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 8px;
|
||||
border: 8px solid #fff;
|
||||
border-radius: 50%;
|
||||
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
border-color: #fff transparent transparent transparent;
|
||||
}
|
||||
.lds-ring div:nth-child(1) {
|
||||
animation-delay: -0.45s;
|
||||
}
|
||||
.lds-ring div:nth-child(2) {
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
.lds-ring div:nth-child(3) {
|
||||
animation-delay: -0.15s;
|
||||
}
|
||||
@keyframes lds-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<link href="./video-js.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
<title>FCast Receiver</title>
|
||||
</head>
|
||||
<body>
|
||||
<video id="video-player" class="video-js" controls preload="auto" data-setup='{}'>
|
1
receivers/electron/src/player/video-js.min.css
vendored
Normal file
1
receivers/electron/src/player/video-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
26
receivers/electron/src/player/video.min.js
vendored
Normal file
26
receivers/electron/src/player/video.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue