From 079888e8e21451f2e4649688e31858d691282df5 Mon Sep 17 00:00:00 2001 From: Filip Hanes Date: Sun, 22 Jul 2018 18:43:15 +0200 Subject: [PATCH 1/4] add downstream keys, update npm dependecies --- package.json | 16 +++++++------- public/index.html | 50 ++++++++++++++++++++++++++++++++++++------- server.coffee | 23 ++++++++++++++++++++ src/coffee/app.coffee | 39 +++++++++++++++++++++------------ 4 files changed, 98 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 4988d26..4573ae9 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,17 @@ "author": "", "license": "ISC", "dependencies": { - "applest-atem": "^0.1.2", - "body-parser": "^1.13.2", - "express": "^4.13.1" + "applest-atem": "^0.2.4", + "body-parser": "^1.18.2", + "express": "^4.16.1" }, "devDependencies": { - "coffee-script": "^1.9.3", + "coffeescript": "^1.9.3", "gulp": "^3.9.0", - "gulp-coffee": "^2.3.1", - "gulp-notify": "^2.2.0", + "gulp-coffee": "^3.0.2", + "gulp-notify": "^3.2.0", "gulp-plumber": "^1.0.1", - "gulp-sass": "^2.0.4", - "gulp-watch": "^4.3.3" + "gulp-sass": "^4.0.0", + "gulp-watch": "^5.0.0" } } diff --git a/public/index.html b/public/index.html index 0c11a05..51988dc 100644 --- a/public/index.html +++ b/public/index.html @@ -111,10 +111,10 @@
Cut
Auto
PREV
@@ -127,13 +127,13 @@
MIX
DIP
WIPE
@@ -144,16 +144,50 @@
BKGD
KEY1
ONAIR
+ +
+
+ Downstream Key 1 +
+
+
TIE
+
ONAIR
+
AUTO
+
+
+ +
+
+ Downstream Key 2 +
+
+
TIE
+
ONAIR
+
AUTO
+
+
diff --git a/server.coffee b/server.coffee index b3db840..eb5e595 100644 --- a/server.coffee +++ b/server.coffee @@ -92,4 +92,27 @@ app.post('/api/changeUpstreamKeyNextState', (req, res) -> res.send('success') ) +app.post('/api/changeDownstreamKeyOn', (req, res) -> + device = req.body.device + number = req.body.number + state = req.body.state + switchers[device].changeDownstreamKeyOn(number, state) + res.send('success') +) + +app.post('/api/changeDownstreamKeyTie', (req, res) -> + device = req.body.device + number = req.body.number + state = req.body.state + switchers[device].changeDownstreamKeyTie(number, state) + res.send('success') +) + +app.post('/api/autoDownstreamKey', (req, res) -> + device = req.body.device + number = req.body.number + switchers[device].autoDownstreamKey(number) + res.send('success') +) + app.listen(8080) diff --git a/src/coffee/app.coffee b/src/coffee/app.coffee index d281339..f09ba03 100644 --- a/src/coffee/app.coffee +++ b/src/coffee/app.coffee @@ -14,28 +14,28 @@ angular return channel if channel.device == device && channel.chainDevice == targetDevice getParentProgramChannel = -> - findChannel(0, $scope.state[0].video.programInput) + findChannel(0, $scope.state[0].video.ME[0].programInput) getVirtualProgramChannel = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.programInput) + parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) if parentProgramChannel.chainDevice? - findChannel(parentProgramChannel.chainDevice, $scope.state[parentProgramChannel.chainDevice].video.programInput) + findChannel(parentProgramChannel.chainDevice, $scope.state[parentProgramChannel.chainDevice].video.ME[0].programInput) else - findChannel(0, $scope.state[0].video.programInput) + findChannel(0, $scope.state[0].video.ME[0].programInput) getVirtualPreviewChannel = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.programInput) - parentPreviewChannel = findChannel(0, $scope.state[0].video.previewInput) + parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) + parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput) if parentPreviewChannel.chainDevice? && parentProgramChannel.chainDevice == parentPreviewChannel.chainDevice - findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.previewInput) + findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].previewInput) else if parentPreviewChannel.chainDevice? - findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.programInput) + findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].programInput) else - findChannel(0, $scope.state[0].video.previewInput) + findChannel(0, $scope.state[0].video.ME[0].previewInput) getTransitionDevice = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.programInput) - parentPreviewChannel = findChannel(0, $scope.state[0].video.previewInput) + parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) + parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput) console.log parentProgramChannel, parentPreviewChannel if parentPreviewChannel.chainDevice? && parentProgramChannel.chainDevice == parentPreviewChannel.chainDevice parentPreviewChannel.chainDevice @@ -84,17 +84,28 @@ angular $http.post('/api/changeTransitionType', type: type).success(defaultSuccess) $scope.toggleUpstreamKeyNextBackground = -> - state = !$scope.state[0].video.upstreamKeyNextBackground + state = !$scope.state[0].video.ME[0].upstreamKeyNextBackground $http.post('/api/changeUpstreamKeyNextBackground', device: 0, state: state).success(defaultSuccess) $scope.toggleUpstreamKeyNextState = (number) -> - state = !$scope.state[0].video.upstreamKeyNextState[number] + state = !$scope.state[0].video.ME[0].upstreamKeyNextState[number] $http.post('/api/changeUpstreamKeyNextState', device: 0, number: number, state: state).success(defaultSuccess) $scope.toggleUpstreamKeyState = (number) -> - state = !$scope.state[0].video.upstreamKeyState[number] + state = !$scope.state[0].video.ME[0].upstreamKeyState[number] $http.post('/api/changeUpstreamKeyState', device: 0, number: number, state: state).success(defaultSuccess) + $scope.toggleDownstreamKeyTie = (number) -> + state = !$scope.state[0].video.downstreamKeyTie[number] + $http.post('/api/changeDownstreamKeyTie', device: 0, number: number, state: state).success(defaultSuccess) + + $scope.toggleDownstreamKeyOn = (number) -> + state = !$scope.state[0].video.downstreamKeyOn[number] + $http.post('/api/changeDownstreamKeyOn', device: 0, number: number, state: state).success(defaultSuccess) + + $scope.autoDownstreamKey = (number) -> + $http.post('/api/autoDownstreamKey', device: 0, number: number).success(defaultSuccess) + registerSlider((err, percent) -> $scope.changeTransitionPosition(percent); ) From 73e25b0efd70357a53cd039325969454f9ecc765 Mon Sep 17 00:00:00 2001 From: Filip Hanes Date: Sun, 22 Jul 2018 23:34:49 +0200 Subject: [PATCH 2/4] simplify dependencies - remove bower, gulp, sass, coffee - move bower deps to npm - add webpack - new npm scripts - now can be managed by PM2 --- .bowerrc | 4 - .gitignore | 3 +- README.md | 15 +- bower.json | 21 --- config.json.sample | 2 +- gulpfile.coffee | 23 --- package.json | 21 ++- src/scss/style.scss => public/css/style.css | 181 ++++++++------------ public/index.html | 6 +- server.coffee | 118 ------------- src/app.js | 148 ++++++++++++++++ src/coffee/app.coffee | 137 --------------- src/server.js | 134 +++++++++++++++ webpack.config.js | 28 +++ 14 files changed, 405 insertions(+), 436 deletions(-) delete mode 100644 .bowerrc delete mode 100644 bower.json delete mode 100644 gulpfile.coffee rename src/scss/style.scss => public/css/style.css (69%) delete mode 100644 server.coffee create mode 100644 src/app.js delete mode 100644 src/coffee/app.coffee create mode 100644 src/server.js create mode 100644 webpack.config.js diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index e483376..0000000 --- a/.bowerrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "directory": "public/bower_components", - "json": "bower.json" -} diff --git a/.gitignore b/.gitignore index 4f1d302..34540ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ config.json node_modules/ public/js/ -public/css/ -public/bower_components/ +.vscode/ diff --git a/README.md b/README.md index 8313cb5..1896319 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,11 @@ Installation -------- - Copy `config.json.sample` to `config.json` - Install dependency components with bower -- Compile front app sources with gulp ```sh -$ cp config.json.sample config.json -$ bower install -$ gulp +cp config.json.sample config.json +npm install +npm run webpack ``` Run @@ -19,9 +18,13 @@ Run - Run the app server ```sh -$ coffee server.coffee +npm start ``` -You will be able to see soon: `http://:8080/` +or +```sh +node ./src/server.js +``` +Then type to your browser address bar: `http://localhost:8080/` Example -------- diff --git a/bower.json b/bower.json deleted file mode 100644 index 5754550..0000000 --- a/bower.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "live-controller", - "version": "0.0.0", - "authors": [ - "Yusei Yamanaka " - ], - "main": "index.html", - "license": "MIT", - "homepage": "https://github.com/applest/live-controller", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "angular": "~1.4.3", - "hammerjs": "~2.0.4" - } -} diff --git a/config.json.sample b/config.json.sample index f253553..92d3a62 100644 --- a/config.json.sample +++ b/config.json.sample @@ -1,6 +1,6 @@ { "switchers": [ - { "type": "atem", "addr": "172.16.0.101" } + { "type": "atem", "addr": "192.168.88.240" } ], "channels": [ { "device": 0, "input": 1 }, diff --git a/gulpfile.coffee b/gulpfile.coffee deleted file mode 100644 index c495bde..0000000 --- a/gulpfile.coffee +++ /dev/null @@ -1,23 +0,0 @@ -gulp = require 'gulp' -coffee = require 'gulp-coffee' -sass = require 'gulp-sass' -plumber = require 'gulp-plumber' -notify = require 'gulp-notify' - -gulp.task 'compile-coffee', -> - gulp.src 'src/coffee/**/*.coffee' - .pipe(plumber(errorHandler: notify.onError('<%= error.message %>'))) - .pipe coffee() - .pipe gulp.dest('public/js') - -gulp.task 'compile-sass', -> - gulp.src 'src/scss/**/*.scss' - .pipe(plumber(errorHandler: notify.onError('<%= error.message %>'))) - .pipe sass() - .pipe gulp.dest('public/css') - -gulp.task 'watch', -> - gulp.watch 'src/coffee/**/*.coffee', ['compile-coffee'] - gulp.watch 'src/scss/**/*.scss', ['compile-sass'] - -gulp.task 'default', ['compile-coffee', 'compile-sass'] diff --git a/package.json b/package.json index 4573ae9..b9ff4db 100644 --- a/package.json +++ b/package.json @@ -4,22 +4,21 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "start": "node src/server.js", + "test": "echo \"Error: no test specified\" && exit 1", + "webpack": "webpack" }, "author": "", - "license": "ISC", + "license": "MIT", "dependencies": { - "applest-atem": "^0.2.4", + "applest-atem": "file:../node-applest-atem", "body-parser": "^1.18.2", - "express": "^4.16.1" + "express": "^4.16.1", + "angular": "~1.4.3", + "hammerjs": "~2.0.4" }, "devDependencies": { - "coffeescript": "^1.9.3", - "gulp": "^3.9.0", - "gulp-coffee": "^3.0.2", - "gulp-notify": "^3.2.0", - "gulp-plumber": "^1.0.1", - "gulp-sass": "^4.0.0", - "gulp-watch": "^5.0.0" + "copy-webpack-plugin": "^4.0.1", + "webpack": "^2.6.1" } } diff --git a/src/scss/style.scss b/public/css/style.css similarity index 69% rename from src/scss/style.scss rename to public/css/style.css index 5a65c31..8dd6ac8 100644 --- a/src/scss/style.scss +++ b/public/css/style.css @@ -1,19 +1,17 @@ +@charset "UTF-8"; * { margin: 0; - padding: 0; -} + padding: 0; } body { margin-top: 20px; background-color: #333; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABIFJREFUeNrEWk1rGzEQXWuX0prQECcQYjAUFvbqs3NMfnz+QyDXQiBpaA+hPRhXMpIrT97MPKXQGhRJu/pYab7ezCR0XTfFMu52u67Uso2eyffyXS7zUpy1Fi37yu9NdYh/tqk1m83GUst26aP3pY4LPtTjUj/Xr6nIder58f232F7I57Itf2WP0ums27AooFBBXUPeYtVe5DIiqmscc7QGsQk8gEdu60OsD9P6HqsNFukACzxIFkLklqxRsdmDfK6tZY0F8/qOEeh86ncpBUtANXZkuaD+tllulN9LOnA+9YsmbPJ20Lj6eRmPFAKaV1MRUVb292uIU5/l9hkrtJ5MaVRA81hKozlB3MaLvBlNBSP50W5ZzOkR5czbBnIi5WWQgxHroPdoPCnMW+WA1iVM8d09EvoyZtD42vpAjRKIly3+1+aAfbcOlf/Ykc1ms8x8t9JkwtIyjC1h+b5x3aRNp4OM3N3dfapPe3t7e2ppLUQVC+oUG6TxvaatCHu13Rfl5CtPk7BwhrEpFvRpmMcZtKp9zn50CxtphxDrn5UC5vE3lNvn5UCsTHgHYICrZ+c6dtP4+5zrj7Gc5vbSA4kWSs7lKj+/sgQbUOboICHpaM0wKbr+Zyzfc/sro16lahXteV5/7sGd5+fnkN8tkCmgoHuhSqFMTRHvFhlgKN5N3lpyz8GD4B5I1CwtMnCmQTMQBeIUqfIHyxojy2v5Ih4CljbFWLeGMX0NTxT27wNaqPU25AfWxk+TO+25IqOTBuVLO8gbQrcshUpujCy2FzBg9spj95a7nodYubNwFfLEEiZjBbcea6lgw8eZNNUux82KeWc8MVYONLnQ5mqs7cjRkQgEJn6E5ODm5uY1CZkXB5P2RMqPxqZWfAvKLAshvBAQG/ph3NtGZHAc1/JCoP8RKI7MmJDJNrf8AOm/a2TXQqKIHbwoDFLp6dn19fVS04QjCjJ70T4POnhxMgcKja1RSai1LPCoxagYO+FpJU97WXZpYDZiLHyttTQoAVhvqrMB1fheBhyYgB9kh6enpwC1A6kEPEPGuMG1QfTYc5YbEN16lPHCqchHsVC0pyjMb9mfxrCYbDxX28zSVkZ0XVr9y9h/THXsniAEERi44IHKFtVqwXkUuMv9x7rWZG782+wU4m3P8jNF7HlpqfmAgtUSMkuojqC7YIt7Vp03tE+kRnwTMm1JtVlazDNujS7D2OIyBAbtahESLdvLQBwme5vCuEg20b4H9fuv/AZrfS0LYM19k0Ns/WcBFp6zGeDGtPgkFUpgsrmpXq/XvzS1ivIbKPiA1KyFnFFkv4qybOu1BtZlVUlahW8AS6zys1XsfrCsuZX08VLXB1dXE1SN56ubvc+qtrdQQXSLf2hC68mBlsc0XV0mD86ESKdpWuX+qhTWO2RVOtjf99M9J6fV1fWUxXuSSoFJcWkyY+EsLTuLWNBLuKJnUs6CyBteXVxcfDHy2n0JASH5QTZF8PwWpRs8h83735RDEFsTbMZAeinp6gK6ojKJ8Z2kuob5yrzfAgwASaYfY2UgQSYAAAAASUVORK5CYII=); - font-family:'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro',メイリオ,Meiryo,'MS Pゴシック',Osaka,sans-serif; -} + font-family: 'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro',メイリオ,Meiryo,'MS Pゴシック',Osaka,sans-serif; } @font-face { font-family: 'LetsGoDigitalRegular'; - src: url('../font/7barSPBd.TTF') format('truetype'); -} + src: url("../font/7barSPBd.TTF") format("truetype"); } .time { vertical-align: middle; @@ -24,8 +22,7 @@ body { color: #ddd; line-height: 42px; font-family: 'LetsGoDigitalRegular'; - text-shadow: 0 0 5px #fff; -} + text-shadow: 0 0 5px #fff; } .section { position: relative; @@ -33,8 +30,8 @@ body { margin-bottom: 14px; color: #999; font-size: 10pt; - font-weight: bold; -} + font-weight: bold; } + .section:after { content: ' '; position: absolute; @@ -44,8 +41,7 @@ body { height: 0; border-top: solid 1px #000; border-bottom: solid 1px #3f3f3f; - z-index: -1; -} + z-index: -1; } /* * Button @@ -56,21 +52,18 @@ body { margin-right: 10px; margin-bottom: 5px; position: relative; - box-shadow: 0 0 0 2px rgba(0, 0, 0, 1), - 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, - 0 0 0 50px rgba(255, 255, 255, .1) inset; + box-shadow: 0 0 0 2px black, 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, 0 0 0 50px rgba(255, 255, 255, 0.1) inset; border-radius: 5px; display: block; width: 75px; height: 75px; - /*padding-top: 26px;*/ line-height: 75px; color: #000; font-weight: bold; font-size: 1.1em; - text-align: center; -} + text-align: center; } + .button:before { content: ' '; position: absolute; @@ -78,70 +71,51 @@ body { top: 6px; width: 63px; height: 63px; - background-color: rgba(255, 255, 255, .7); + background-color: rgba(255, 255, 255, 0.7); border-radius: 3px; + z-index: -2; } - z-index: -2; -} .button:after { position: absolute; left: 6px; top: 6px; height: 63px; width: 63px; - background: linear-gradient(rgba(0, 0, 0, .15), rgba(200, 200, 200, .15)); + background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(200, 200, 200, 0.15)); content: " "; border-radius: 50%; - box-shadow: 0 0 5px 2px rgba(255, 255, 255, .8); - z-index: -1; -} + box-shadow: 0 0 5px 2px rgba(255, 255, 255, 0.8); + z-index: -1; } + .button.red { - box-shadow: 0 0 0 2px rgba(0, 0, 0, 1), - 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, - 0 0 0 50px rgba(255, 255, 255, .1) inset, - 0 0 30px 0 red, - 0 0 30px 5px red inset; -} + box-shadow: 0 0 0 2px black, 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, 0 0 0 50px rgba(255, 255, 255, 0.1) inset, 0 0 30px 0 red, 0 0 30px 5px red inset; } + .button.red:before { - background-color: rgba(255, 150, 150, .7); -} + background-color: rgba(255, 150, 150, 0.7); } + .button.green { - box-shadow: 0 0 0 2px rgba(0, 0, 0, 1), - 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, - 0 0 0 50px rgba(255, 255, 255, .1) inset, - 0 0 30px 0 green, - 0 0 30px 5px green inset; -} + box-shadow: 0 0 0 2px black, 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, 0 0 0 50px rgba(255, 255, 255, 0.1) inset, 0 0 30px 0 green, 0 0 30px 5px green inset; } + .button.green:before { - background-color: rgba(150, 255, 150, .7); -} + background-color: rgba(150, 255, 150, 0.7); } + .button.yellow { - box-shadow: 0 0 0 2px rgba(0, 0, 0, 1), - 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, - 0 0 0 50px rgba(255, 255, 255, .1) inset, - 0 0 30px 0 rgba(255, 233, 0, 0.8), - 0 0 10px 5px rgb(255, 194, 0) inset, - 0 0 50px 13px rgba(255, 165, 0, 0.7) inset; -} + box-shadow: 0 0 0 2px black, 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, 0 0 0 50px rgba(255, 255, 255, 0.1) inset, 0 0 30px 0 rgba(255, 233, 0, 0.8), 0 0 10px 5px #ffc200 inset, 0 0 50px 13px rgba(255, 165, 0, 0.7) inset; } + .button.yellow:before { - background-color: rgba(255, 255, 150, .7); -} + background-color: rgba(255, 255, 150, 0.7); } .button:active { - box-shadow: 0 0 0 2px rgba(0, 0, 0, 1), - 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, - 0 0 0 50px rgba(255, 255, 255, .1) inset, - 0 0 10px 10px rgba(0, 0, 0, .5) inset; -} + box-shadow: 0 0 0 2px black, 0 1px 1px 0 rgba(255, 255, 255, 0.3) inset, 0 0 0 50px rgba(255, 255, 255, 0.1) inset, 0 0 10px 10px rgba(0, 0, 0, 0.5) inset; } + .button:active:before { - background-color: rgba(150, 150, 150, .7); -} + background-color: rgba(150, 150, 150, 0.7); } + .button:active:after { left: 8px; top: 8px; height: 59px; - width: 59px; -} + width: 59px; } /* * Slider @@ -150,8 +124,8 @@ body { position: relative; width: 300px; height: 75px; - margin-top: 6px; -} + margin-top: 6px; } + .sliders:before { content: ' '; position: absolute; @@ -159,8 +133,8 @@ body { width: 100%; height: 5px; background-color: #030303; - z-index: -2; -} + z-index: -2; } + .sliders:after { content: ' '; position: absolute; @@ -169,8 +143,8 @@ body { height: 13px; background-color: #444; border-radius: 5px; - z-index: -3; -} + z-index: -3; } + .slider { position: relative; margin-left: -17px; @@ -180,8 +154,8 @@ body { height: 70px; cursor: pointer; border-radius: 5px; - background: linear-gradient(to right, #a7a5a8 0%, #aba9ac 15%, #ededed 30%, #7a7a7e 74%, #7a7a7e 75%, #6d6d6f 100%); -} + background: linear-gradient(to right, #a7a5a8 0%, #aba9ac 15%, #ededed 30%, #7a7a7e 74%, #7a7a7e 75%, #6d6d6f 100%); } + .slider:before { content: ' '; position: absolute; @@ -189,8 +163,8 @@ body { width: 100%; height: 3px; background: linear-gradient(to right, #030303 0%, #5e5957 40%, #55514f 45%, #1e1c1b 50%, #030303 70%, #474a4d 75%, #353332 90%, #040404 100%); - z-index: 1; -} + z-index: 1; } + .slider:after { content: ' '; position: absolute; @@ -200,13 +174,12 @@ body { border-radius: 13px; background: linear-gradient(to right, #818081 0%, #b2adad 40%, #a09d9d 45%, #757679 70%, #75777b 100%); box-shadow: #000 1px 1px 10px 0; - z-index: -1; -} + z-index: -1; } .channels { /*margin-top: 10px;*/ - margin-left: 10px; -} + margin-left: 10px; } + .channels.after-section:after { content: ' '; position: absolute; @@ -216,33 +189,29 @@ body { /*height: 0;*/ border-left: solid 1px #000; border-right: solid 1px #3f3f3f; - z-index: -1; -} + z-index: -1; } + .channel { display: inline-block; - /*float: left;*/ -} + /*float: left;*/ } .meter { display: inline-block; - margin-left: 15px; -} + margin-left: 15px; } .slider { /*display: inline-block;*/ /*margin-top: -30px;*/ - /*margin-bottom: -30px;*/ -} + /*margin-bottom: -30px;*/ } .box { padding: 10px; position: relative; border: solid 1px #000; - border-radius: 10px; -} + border-radius: 10px; } + .status { - width: 250px; -} + width: 250px; } .box .title { margin: -10px -10px 10px -10px; @@ -257,8 +226,7 @@ body { line-height: 30px; font-size: 10pt; text-align: center; - font-weight: bold; -} + font-weight: bold; } .status .part { height: 24px; @@ -266,20 +234,17 @@ body { margin-bottom: -24px; font-size: 10pt; color: #fff; - line-height: 28px; -} + line-height: 28px; } .status .led { left: 110px; - line-height: 28px; -} + line-height: 28px; } .led { position: relative; margin-left: 40px; font-size: 10pt; - color: #fff; -} + color: #fff; } .led.green:before { content: ' '; @@ -289,39 +254,35 @@ body { /*margin: 20px auto;*/ width: 12px; height: 12px; - background-color: rgb(6, 228, 0); + background-color: #06e400; border-radius: 50%; - box-shadow: #000 0 -1px 7px 1px, inset #460 0 -1px 9px, #7D0 0 2px 12px; -} + box-shadow: #000 0 -1px 7px 1px, inset #460 0 -1px 9px, #7D0 0 2px 12px; } .float-left { - float: left; -} + float: left; } + .float-right { - float: right; -} + float: right; } + .after-clear:after { content: "."; display: block; height: 0; visibility: hidden; - clear: both; -} + clear: both; } + .inline { - display: inline-block; -} + display: inline-block; } .row { display: table-row; - border-spacing: 0; -} + border-spacing: 0; } + .cell { display: table-cell; - vertical-align: top; -} + vertical-align: top; } table, td, th { border-spacing: 20px 20px; border: none; - vertical-align: top; -} + vertical-align: top; } diff --git a/public/index.html b/public/index.html index 51988dc..c414a81 100644 --- a/public/index.html +++ b/public/index.html @@ -8,8 +8,9 @@ - - + + + - diff --git a/server.coffee b/server.coffee deleted file mode 100644 index eb5e595..0000000 --- a/server.coffee +++ /dev/null @@ -1,118 +0,0 @@ -express = require 'express' -app = express() -bodyParser = require 'body-parser' -ATEM = require 'applest-atem' -config = require './config.json' - -switchers = [] -for switcher in config.switchers - atem = new ATEM - atem.event.setMaxListeners(5) - atem.connect(switcher.addr, switcher.port) - switchers.push(atem) - -app.use(bodyParser.json()) -app.use('/', express.static(__dirname + '/public')) - -app.get('/api/channels', (req, res) -> - res.send(JSON.stringify(config.channels)) -) - -app.get('/api/switchersState', (req, res) -> - res.send(JSON.stringify((atem.state for atem in switchers))) -) - -app.get('/api/switchersStatePolling', (req, res) -> - for atem in switchers - atem.once('stateChanged', (err, state) -> - res.end(JSON.stringify((atem.state for atem in switchers))) - ) -) - -app.post('/api/changePreviewInput', (req, res) -> - device = req.body.device - input = req.body.input - switchers[device].changePreviewInput(input) - res.send('success') -) - -app.post('/api/changeProgramInput', (req, res) -> - device = req.body.device - input = req.body.input - switchers[device].changeProgramInput(input) - res.send('success') -) - -app.post('/api/autoTransition', (req, res) -> - device = req.body.device - switchers[device].autoTransition() - res.send('success') -) - -app.post('/api/cutTransition', (req, res) -> - device = req.body.device - switchers[device].cutTransition() - res.send('success') -) - -app.post('/api/changeTransitionPosition', (req, res) -> - device = req.body.device - position = req.body.position - switchers[device].changeTransitionPosition(position) - res.send('success') -) - -app.post('/api/changeTransitionType', (req, res) -> - type = req.body.type - for switcher in switchers - switcher.changeTransitionType(type) - res.send('success') -) - -app.post('/api/changeUpstreamKeyState', (req, res) -> - device = req.body.device - number = req.body.number - state = req.body.state - switchers[device].changeUpstreamKeyState(number, state) - res.send('success') -) - -app.post('/api/changeUpstreamKeyNextBackground', (req, res) -> - device = req.body.device - state = req.body.state - switchers[device].changeUpstreamKeyNextBackground(state) - res.send('success') -) - -app.post('/api/changeUpstreamKeyNextState', (req, res) -> - device = req.body.device - number = req.body.number - state = req.body.state - switchers[device].changeUpstreamKeyNextState(number, state) - res.send('success') -) - -app.post('/api/changeDownstreamKeyOn', (req, res) -> - device = req.body.device - number = req.body.number - state = req.body.state - switchers[device].changeDownstreamKeyOn(number, state) - res.send('success') -) - -app.post('/api/changeDownstreamKeyTie', (req, res) -> - device = req.body.device - number = req.body.number - state = req.body.state - switchers[device].changeDownstreamKeyTie(number, state) - res.send('success') -) - -app.post('/api/autoDownstreamKey', (req, res) -> - device = req.body.device - number = req.body.number - switchers[device].autoDownstreamKey(number) - res.send('success') -) - -app.listen(8080) diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..4eaad10 --- /dev/null +++ b/src/app.js @@ -0,0 +1,148 @@ +angular + .module('liveController', []) + .controller('MainCtrl', ['$scope', '$http', '$interval', '$timeout', + function($scope, $http, $interval, $timeout) { + const defaultSuccess = function(data) {}; + // console.log(data) + + const findChannel = function(device, input) { + for (let channel of $scope.channels) { + if ((channel.device === device) && (channel.input === input)) { return channel; } + } + }; + + const findChainChannel = function(device, targetDevice) { + for (let channel of $scope.channels) { + if ((channel.device === device) && (channel.chainDevice === targetDevice)) { return channel; } + } + }; + + const getParentProgramChannel = () => findChannel(0, $scope.state[0].video.ME[0].programInput); + + const getVirtualProgramChannel = function() { + const parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput); + if (parentProgramChannel.chainDevice != null) { + return findChannel(parentProgramChannel.chainDevice, $scope.state[parentProgramChannel.chainDevice].video.ME[0].programInput); + } else { + return findChannel(0, $scope.state[0].video.ME[0].programInput); + } + }; + + const getVirtualPreviewChannel = function() { + const parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput); + const parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput); + if ((parentPreviewChannel.chainDevice != null) && (parentProgramChannel.chainDevice === parentPreviewChannel.chainDevice)) { + return findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].previewInput); + } else if (parentPreviewChannel.chainDevice != null) { + return findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].programInput); + } else { + return findChannel(0, $scope.state[0].video.ME[0].previewInput); + } + }; + + const getTransitionDevice = function() { + const parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput); + const parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput); + console.log(parentProgramChannel, parentPreviewChannel); + if ((parentPreviewChannel.chainDevice != null) && (parentProgramChannel.chainDevice === parentPreviewChannel.chainDevice)) { + return parentPreviewChannel.chainDevice; + } else { + return 0; + } + }; + + $scope.isProgramChannel = function(channel) { + const programChannel = getVirtualProgramChannel(); + return (programChannel.device === channel.device) && (programChannel.input === channel.input); + }; + + $scope.isPreviewChannel = function(channel) { + const previewChannel = getVirtualPreviewChannel(); + return (previewChannel.device === channel.device) && (previewChannel.input === channel.input); + }; + + $scope.getChannelInput = channel => $scope.state[channel.device].channels[channel.input]; + + const changePreviewInput = (device, input) => $http.post('/api/changePreviewInput', {device, input}).success(defaultSuccess); + + const changeProgramInput = (device, input) => $http.post('/api/changeProgramInput', {device, input}).success(defaultSuccess); + + $scope.changeInput = function(channel) { + const isParentDevice = channel.device === 0; + if (isParentDevice) { + return changePreviewInput(0, channel.input); + } else { + const chainChannel = findChainChannel(0, channel.device); + changePreviewInput(chainChannel.device, chainChannel.input); + if (getParentProgramChannel().chainDevice === channel.device) { + return changePreviewInput(channel.device, channel.input); + } else { + return changeProgramInput(channel.device, channel.input); + } + } + }; + + $scope.autoTransition = (device = getTransitionDevice()) => $http.post('/api/autoTransition', {device}).success(defaultSuccess); + + $scope.cutTransition = (device = getTransitionDevice()) => $http.post('/api/cutTransition', {device}).success(defaultSuccess); + + $scope.changeTransitionPosition = (percent, device = getTransitionDevice()) => $http.post('/api/changeTransitionPosition', {device, position: parseInt(percent*10000)}).success(defaultSuccess); + + $scope.changeTransitionType = type => $http.post('/api/changeTransitionType', {type}).success(defaultSuccess); + + $scope.toggleUpstreamKeyNextBackground = function() { + const state = !$scope.state[0].video.ME[0].upstreamKeyNextBackground; + return $http.post('/api/changeUpstreamKeyNextBackground', {device: 0, state}).success(defaultSuccess); + }; + + $scope.toggleUpstreamKeyNextState = function(number) { + const state = !$scope.state[0].video.ME[0].upstreamKeyNextState[number]; + return $http.post('/api/changeUpstreamKeyNextState', {device: 0, number, state}).success(defaultSuccess); + }; + + $scope.toggleUpstreamKeyState = function(number) { + const state = !$scope.state[0].video.ME[0].upstreamKeyState[number]; + return $http.post('/api/changeUpstreamKeyState', {device: 0, number, state}).success(defaultSuccess); + }; + + $scope.toggleDownstreamKeyTie = function(number) { + const state = !$scope.state[0].video.downstreamKeyTie[number]; + return $http.post('/api/changeDownstreamKeyTie', {device: 0, number, state}).success(defaultSuccess); + }; + + $scope.toggleDownstreamKeyOn = function(number) { + const state = !$scope.state[0].video.downstreamKeyOn[number]; + return $http.post('/api/changeDownstreamKeyOn', {device: 0, number, state}).success(defaultSuccess); + }; + + $scope.autoDownstreamKey = function(number) { + return $http.post('/api/autoDownstreamKey', {device: 0, number}).success(defaultSuccess); + } + + registerSlider((err, percent) => $scope.changeTransitionPosition(percent)); + + $scope.refresh = () => + $http.get('/api/switchersStatePolling').success(function(data) { + $scope.state = data; + return $timeout($scope.refresh, 0); + }) + ; + $timeout($scope.refresh, 0); + + $interval( function() { + $http.get('/api/switchersState').success(data => $scope.state = data) + } + , 500); + + $interval( function() { + const date = new Date(); + const hours = (`0${date.getHours()}`).slice(-2); + const minutes = (`0${date.getMinutes()}`).slice(-2); + const seconds = (`0${date.getSeconds()}`).slice(-2); + return $scope.time = `${hours}:${minutes}:${seconds}`; + } + , 1000); + + return $http.get('/api/channels').success(data => $scope.channels = data); + } + ]); diff --git a/src/coffee/app.coffee b/src/coffee/app.coffee deleted file mode 100644 index f09ba03..0000000 --- a/src/coffee/app.coffee +++ /dev/null @@ -1,137 +0,0 @@ -angular - .module('liveController', []) - .controller('MainCtrl', ['$scope', '$http', '$interval', '$timeout', - ($scope, $http, $interval, $timeout) -> - defaultSuccess = (data) -> - # console.log(data) - - findChannel = (device, input) -> - for channel in $scope.channels - return channel if channel.device == device && channel.input == input - - findChainChannel = (device, targetDevice) -> - for channel in $scope.channels - return channel if channel.device == device && channel.chainDevice == targetDevice - - getParentProgramChannel = -> - findChannel(0, $scope.state[0].video.ME[0].programInput) - - getVirtualProgramChannel = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) - if parentProgramChannel.chainDevice? - findChannel(parentProgramChannel.chainDevice, $scope.state[parentProgramChannel.chainDevice].video.ME[0].programInput) - else - findChannel(0, $scope.state[0].video.ME[0].programInput) - - getVirtualPreviewChannel = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) - parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput) - if parentPreviewChannel.chainDevice? && parentProgramChannel.chainDevice == parentPreviewChannel.chainDevice - findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].previewInput) - else if parentPreviewChannel.chainDevice? - findChannel(parentPreviewChannel.chainDevice, $scope.state[parentPreviewChannel.chainDevice].video.ME[0].programInput) - else - findChannel(0, $scope.state[0].video.ME[0].previewInput) - - getTransitionDevice = -> - parentProgramChannel = findChannel(0, $scope.state[0].video.ME[0].programInput) - parentPreviewChannel = findChannel(0, $scope.state[0].video.ME[0].previewInput) - console.log parentProgramChannel, parentPreviewChannel - if parentPreviewChannel.chainDevice? && parentProgramChannel.chainDevice == parentPreviewChannel.chainDevice - parentPreviewChannel.chainDevice - else - 0 - - $scope.isProgramChannel = (channel) -> - programChannel = getVirtualProgramChannel() - programChannel.device == channel.device && programChannel.input == channel.input - - $scope.isPreviewChannel = (channel) -> - previewChannel = getVirtualPreviewChannel() - previewChannel.device == channel.device && previewChannel.input == channel.input - - $scope.getChannelInput = (channel) -> - $scope.state[channel.device].channels[channel.input] - - changePreviewInput = (device, input) -> - $http.post('/api/changePreviewInput', device: device, input: input).success(defaultSuccess) - - changeProgramInput = (device, input) -> - $http.post('/api/changeProgramInput', device: device, input: input).success(defaultSuccess) - - $scope.changeInput = (channel) -> - isParentDevice = channel.device == 0 - if isParentDevice - changePreviewInput(0, channel.input) - else - chainChannel = findChainChannel(0, channel.device) - changePreviewInput(chainChannel.device, chainChannel.input) - if getParentProgramChannel().chainDevice == channel.device - changePreviewInput(channel.device, channel.input) - else - changeProgramInput(channel.device, channel.input) - - $scope.autoTransition = (device = getTransitionDevice()) -> - $http.post('/api/autoTransition', device: device).success(defaultSuccess) - - $scope.cutTransition = (device = getTransitionDevice()) -> - $http.post('/api/cutTransition', device: device).success(defaultSuccess) - - $scope.changeTransitionPosition = (percent, device = getTransitionDevice()) -> - $http.post('/api/changeTransitionPosition', device: device, position: parseInt(percent*10000)).success(defaultSuccess) - - $scope.changeTransitionType = (type) -> - $http.post('/api/changeTransitionType', type: type).success(defaultSuccess) - - $scope.toggleUpstreamKeyNextBackground = -> - state = !$scope.state[0].video.ME[0].upstreamKeyNextBackground - $http.post('/api/changeUpstreamKeyNextBackground', device: 0, state: state).success(defaultSuccess) - - $scope.toggleUpstreamKeyNextState = (number) -> - state = !$scope.state[0].video.ME[0].upstreamKeyNextState[number] - $http.post('/api/changeUpstreamKeyNextState', device: 0, number: number, state: state).success(defaultSuccess) - - $scope.toggleUpstreamKeyState = (number) -> - state = !$scope.state[0].video.ME[0].upstreamKeyState[number] - $http.post('/api/changeUpstreamKeyState', device: 0, number: number, state: state).success(defaultSuccess) - - $scope.toggleDownstreamKeyTie = (number) -> - state = !$scope.state[0].video.downstreamKeyTie[number] - $http.post('/api/changeDownstreamKeyTie', device: 0, number: number, state: state).success(defaultSuccess) - - $scope.toggleDownstreamKeyOn = (number) -> - state = !$scope.state[0].video.downstreamKeyOn[number] - $http.post('/api/changeDownstreamKeyOn', device: 0, number: number, state: state).success(defaultSuccess) - - $scope.autoDownstreamKey = (number) -> - $http.post('/api/autoDownstreamKey', device: 0, number: number).success(defaultSuccess) - - registerSlider((err, percent) -> - $scope.changeTransitionPosition(percent); - ) - - $scope.refresh = -> - $http.get('/api/switchersStatePolling').success((data) -> - $scope.state = data - $timeout($scope.refresh, 0) - ) - $timeout($scope.refresh, 0) - - $interval( -> - $http.get('/api/switchersState').success((data) -> - $scope.state = data - ) - , 500) - - $interval( -> - date = new Date() - hours = ("0" + date.getHours()).slice(-2) - minutes = ("0" + date.getMinutes()).slice(-2) - seconds = ("0" + date.getSeconds()).slice(-2) - $scope.time = "#{hours}:#{minutes}:#{seconds}" - , 1000) - - $http.get('/api/channels').success((data) -> - $scope.channels = data - ) - ]) diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..5900dcc --- /dev/null +++ b/src/server.js @@ -0,0 +1,134 @@ +const express = require('express'); +const app = express(); +const bodyParser = require('body-parser'); +const ATEM = require('applest-atem'); +const config = require('../config.json'); + +let atem; +const switchers = []; +for (var switcher of config.switchers) { + atem = new ATEM; + atem.event.setMaxListeners(5); + atem.connect(switcher.addr, switcher.port); + switchers.push(atem); +} + +app.use(bodyParser.json()); +app.use('/', express.static(__dirname + '/../public')); + +app.get('/api/channels', (req, res) => res.send(JSON.stringify(config.channels))); + +app.get('/api/switchersState', (req, res) => { + const result = []; + for (atem of switchers) { + result.push(atem.state); + } + res.send(JSON.stringify(result)); + } +); + +app.get('/api/switchersStatePolling', (req, res) => { + const result = []; + for (atem of switchers) { + result.push( + atem.once('stateChanged', (err, state) => { + const result1 = []; + for (atem of switchers) { + result1.push(atem.state); + } + res.end(JSON.stringify(result1)) + }) + ); + } + return result; + } +); + +app.post('/api/changePreviewInput', function(req, res) { + const { device } = req.body; + const { input } = req.body; + switchers[device].changePreviewInput(input); + return res.send('success'); +}); + +app.post('/api/changeProgramInput', function(req, res) { + const { device } = req.body; + const { input } = req.body; + switchers[device].changeProgramInput(input); + return res.send('success'); +}); + +app.post('/api/autoTransition', function(req, res) { + const { device } = req.body; + switchers[device].autoTransition(); + return res.send('success'); +}); + +app.post('/api/cutTransition', function(req, res) { + const { device } = req.body; + switchers[device].cutTransition(); + return res.send('success'); +}); + +app.post('/api/changeTransitionPosition', function(req, res) { + const { device } = req.body; + const { position } = req.body; + switchers[device].changeTransitionPosition(position); + return res.send('success'); +}); + +app.post('/api/changeTransitionType', function(req, res) { + const { type } = req.body; + for (switcher of switchers) { + switcher.changeTransitionType(type); + } + return res.send('success'); +}); + +app.post('/api/changeUpstreamKeyState', function(req, res) { + const { device } = req.body; + const { number } = req.body; + const { state } = req.body; + switchers[device].changeUpstreamKeyState(number, state); + return res.send('success'); +}); + +app.post('/api/changeUpstreamKeyNextBackground', function(req, res) { + const { device } = req.body; + const { state } = req.body; + switchers[device].changeUpstreamKeyNextBackground(state); + return res.send('success'); +}); + +app.post('/api/changeUpstreamKeyNextState', function(req, res) { + const { device } = req.body; + const { number } = req.body; + const { state } = req.body; + switchers[device].changeUpstreamKeyNextState(number, state); + return res.send('success'); +}); + +app.post('/api/changeDownstreamKeyOn', function(req, res) { + const { device } = req.body; + const { number } = req.body; + const { state } = req.body; + switchers[device].changeDownstreamKeyOn(number, state); + return res.send('success'); +}); + +app.post('/api/changeDownstreamKeyTie', function(req, res) { + const { device } = req.body; + const { number } = req.body; + const { state } = req.body; + switchers[device].changeDownstreamKeyTie(number, state); + return res.send('success'); +}); + +app.post('/api/autoDownstreamKey', function(req, res) { + const { device } = req.body; + const { number } = req.body; + switchers[device].autoDownstreamKey(number); + return res.send('success'); +}); + +app.listen(8080); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..1b7c95c --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,28 @@ +const path = require('path'); +const webpack = require('webpack'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +const libs = [ + 'angular/angular.min.js', + 'hammerjs/hammer.min.js' +]; + +module.exports = { + entry: { + app: "./src/app.js", + }, + output: { + path: __dirname + "/public/js/", + filename: "[name].bundle.js" + }, + plugins: [ + new CopyWebpackPlugin( + libs.map(asset => { + return { + from: path.resolve(__dirname, `./node_modules/${asset}`), + to: path.resolve(__dirname, './public/js') + }; + }) + ) + ] +}; \ No newline at end of file From 6810419aa3b8434aabde85eedf9d3f16cc02bc1a Mon Sep 17 00:00:00 2001 From: Filip Hanes Date: Mon, 23 Jul 2018 18:37:20 +0200 Subject: [PATCH 3/4] fix applet-atem version --- package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b9ff4db..e5879da 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,11 @@ "author": "", "license": "MIT", "dependencies": { - "applest-atem": "file:../node-applest-atem", + "applest-atem": "^0.2.4", "body-parser": "^1.18.2", "express": "^4.16.1", "angular": "~1.4.3", - "hammerjs": "~2.0.4" - }, - "devDependencies": { + "hammerjs": "~2.0.4", "copy-webpack-plugin": "^4.0.1", "webpack": "^2.6.1" } From 04c7ccdfa3635c53f9995d57b4728cbf60aada19 Mon Sep 17 00:00:00 2001 From: Filip Hanes Date: Tue, 24 Jul 2018 19:42:35 +0200 Subject: [PATCH 4/4] Update install steps in README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1896319..5a46a81 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ The customizable video switchers web controller. Installation -------- - Copy `config.json.sample` to `config.json` -- Install dependency components with bower +- Install dependencies with npm +- Prepare assets with webpack ```sh cp config.json.sample config.json