From 441197a9ec68204ef7b0aa2128a63d7978156e5d Mon Sep 17 00:00:00 2001 From: olzzon Date: Tue, 30 Oct 2018 09:19:39 +0100 Subject: [PATCH 1/3] Using the casparcg-state-scanner: timeLeft has now a different update frequence than general clipinfo --- package.json | 3 ++- src/components/App.js | 50 ++++++++++++++++++------------------- src/components/Thumbnail.js | 42 +++++++++++++++++++++++++++---- yarn.lock | 40 ++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index e9be8c6d..551edce8 100755 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "graphql-request": "^1.8.2", "react": "^16.5.2", "react-dom": "^16.5.2", - "rmc-tabs": "^1.2.27" + "rmc-tabs": "^1.2.27", + "subscriptions-transport-ws": "^0.9.15" }, "devDependencies": { "@babel/core": "^7.1.2", diff --git a/src/components/App.js b/src/components/App.js index 507d4b2c..d72c0e51 100755 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,8 +1,9 @@ -import React, { Component } from 'react'; +import React, { Component, PureComponent } from 'react'; import {CasparCG} from 'casparcg-connection'; import { Tabs } from 'rmc-tabs'; import { GraphQLClient, request } from 'graphql-request'; + // Components: import Thumbnail from './Thumbnail'; import SettingsPage from './Settings'; @@ -24,27 +25,27 @@ class App extends Component { super(props); this.state = { - ccgConnectionStatus: false, - showSettingsMenu: false, - activeTab: 0, - activeTabTitle: '', - activePvwPix: '', - activePgmPix: '', - pgmCounter: '', - tabData: [], - globalSettings: { - ipAddress: 'localhost', - port: '5250', - mainFolder: '', - tabData: [ - { key: 1, title: 'SCREEN 1', subFolder: '', loop: false, autoPlay: false}, - { key: 2, title: 'SCREEN 2', subFolder: '', loop: false, autoPlay: false}, - { key: 3, title: 'SCREEN 3', subFolder: '', loop: false, autoPlay: false}, - { key: 4, title: '', subFolder: '', loop: false, autoPlay: false}, - { key: 5, title: '', subFolder: '', loop: false, autoPlay: false}, - { key: 6, title: '', subFolder: '', loop: false, autoPlay: false}, - ], - }, + ccgConnectionStatus: false, + showSettingsMenu: false, + activeTab: 0, + activeTabTitle: '', + activePvwPix: '', + activePgmPix: '', + pgmCounter: '', + tabData: [], + globalSettings: { + ipAddress: 'localhost', + port: '5250', + mainFolder: '', + tabData: [ + { key: 1, title: 'SCREEN 1', subFolder: '', loop: false, autoPlay: false}, + { key: 2, title: 'SCREEN 2', subFolder: '', loop: false, autoPlay: false}, + { key: 3, title: 'SCREEN 3', subFolder: '', loop: false, autoPlay: false}, + { key: 4, title: '', subFolder: '', loop: false, autoPlay: false}, + { key: 5, title: '', subFolder: '', loop: false, autoPlay: false}, + { key: 6, title: '', subFolder: '', loop: false, autoPlay: false}, + ], + }, }; this.checkConnectionStatus = this.checkConnectionStatus.bind(this); @@ -66,11 +67,10 @@ class App extends Component { //Define Output Tabs: this.setState({tabData: mountSettings.tabData.filter((item) => { - return item.title != ""; - }) + return item.title != ""; + }) }); - // in current version of casparcg-connection the port has to be assigned as a seperate parameter. this.ccgConnection = new CasparCG( { host: mountSettings.ipAddress, diff --git a/src/components/Thumbnail.js b/src/components/Thumbnail.js index d55994b5..17efaa3d 100644 --- a/src/components/Thumbnail.js +++ b/src/components/Thumbnail.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import '../assets/css/Thumbnail.css'; import './App'; @@ -8,10 +8,11 @@ const mixDuration = 6; //thumb counterDown reference: var thumbTimer; +var thumbCountTimer; -class Thumbnail extends Component { +class Thumbnail extends PureComponent { //Props: //ccgOutputProps what output on CCG to play at //ccgConnectionProps Current CCG connection @@ -34,6 +35,7 @@ class Thumbnail extends Component { isTabActive: false, }; this.updatePlayingStatus = this.updatePlayingStatus.bind(this); + this.updateTimerStatus = this.updateTimerStatus.bind(this); this.updateThumbnail = this.updateThumbnail.bind(this); this.renderThumbnail = this.renderThumbnail.bind(this); @@ -55,6 +57,7 @@ class Thumbnail extends Component { item.tally = false; item.tallyBg = false; item.isActive = false; + item.timeLeft = 0; this.setState((prevState) => ({ thumbList: [...prevState.thumbList, item] })); @@ -70,7 +73,8 @@ class Thumbnail extends Component { // Timer playing & tally status: this.updatePlayingStatus(); - thumbTimer = setInterval(this.updatePlayingStatus, 50); + thumbTimer = setInterval(this.updatePlayingStatus, 400); + thumbCountTimer = setInterval(this.updateTimerStatus, 50); } //Shortcut for mix and take @@ -133,7 +137,8 @@ class Thumbnail extends Component { } // Update only first time or if time if file is playing: - if (forceUpdate || !infoStatus.foreground.paused) { + //if (forceUpdate || !infoStatus.foreground.paused) { + if (forceUpdate) { this.setStateThumbListElement(index, "tally", true); this.setState({thumbActiveIndex: index}); this.setStateThumbListElement(index, "isActive", true); @@ -165,6 +170,33 @@ class Thumbnail extends Component { } } + + // Timer controlled playing & tally status + updateTimerStatus() { + var forceUpdate = false; + var thisActive = this.props.getTabStateProps(this.props.ccgOutputProps); + if (!this.state.isTabActive && thisActive) { + this.setState({isTabActive: thisActive}); + forceUpdate = true; + } else if (!thisActive) + { + this.setState({isTabActive: thisActive}); + } + //only update timer when tab is selected: + if (thisActive) { + this.props.ccgStateConnectionProps.request("{ timeLeft(ch: " + this.props.ccgOutputProps + ",l: 10) }") + .then ((response)=>{ + var timeLeft = parseFloat(response.timeLeft); + this.setStateThumbListElement(this.state.thumbActiveIndex, "timeLeft", timeLeft); + this.updateThumbnail(this.state.thumbActiveIndex); + this.props.setPgmCounterProps(this.secondsToTimeCode( timeLeft)); + }) + .catch ((error)=> { + console.log(error); + }); + } + } + cleanUpFilename(filename) { // casparcg-connection library bug: returns filename with media// or media/ return (filename.replace(/\\/g, '/') @@ -281,7 +313,7 @@ class Thumbnail extends Component { /> {this.state.thumbList[index].isActive ? - this.secondsToTimeCode(this.state.thumbActiveState.foreground.length - this.state.thumbActiveState.foreground.time) + this.secondsToTimeCode(this.state.thumbList[index].timeLeft) : "" } diff --git a/yarn.lock b/yarn.lock index 04f2d050..40e4631e 100755 --- a/yarn.lock +++ b/yarn.lock @@ -563,6 +563,11 @@ async-each@^1.0.0: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" integrity sha1-GdOGodntxufByF04iu28xW0zYC0= +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -945,6 +950,11 @@ babylon@^6.18.0: resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== +backo2@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + balanced-match@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" @@ -2248,7 +2258,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^3.0.0: +eventemitter3@^3.0.0, eventemitter3@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== @@ -3414,6 +3424,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +iterall@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" + integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== + js-base64@^2.1.9: version "2.4.9" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03" @@ -6064,6 +6079,17 @@ style-loader@^0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" +subscriptions-transport-ws@^0.9.15: + version "0.9.15" + resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.15.tgz#68a8b7ba0037d8c489fb2f5a102d1494db297d0d" + integrity sha512-f9eBfWdHsePQV67QIX+VRhf++dn1adyC/PZHP6XI5AfKnZ4n0FW+v5omxwdHVpd4xq2ZijaHEcmlQrhBY79ZWQ== + dependencies: + backo2 "^1.0.2" + eventemitter3 "^3.1.0" + iterall "^1.2.1" + symbol-observable "^1.0.4" + ws "^5.2.0" + sumchecker@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e" @@ -6090,6 +6116,11 @@ supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-co dependencies: has-flag "^3.0.0" +symbol-observable@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" @@ -6676,6 +6707,13 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + xml2js@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" From 6dd25c5236a5cef904d685ba3ea4c1866078cb8f Mon Sep 17 00:00:00 2001 From: olzzon Date: Thu, 8 Nov 2018 09:49:29 +0100 Subject: [PATCH 2/3] Working CasparCG-State-Scanner based updates (still timebased, next step, subscription based GraphQl) --- src/components/Thumbnail.js | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/components/Thumbnail.js b/src/components/Thumbnail.js index 17efaa3d..70346159 100644 --- a/src/components/Thumbnail.js +++ b/src/components/Thumbnail.js @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { Component, PureComponent } from 'react'; import '../assets/css/Thumbnail.css'; import './App'; @@ -62,8 +62,13 @@ class Thumbnail extends PureComponent { thumbList: [...prevState.thumbList, item] })); this.updateThumbnail(this.state.thumbList.length - 1); + console.log("Data loaded"); }); }); + // Timer playing & tally status: + this.updatePlayingStatus(); + thumbTimer = setInterval(this.updatePlayingStatus, 400); + thumbCountTimer = setInterval(this.updateTimerStatus, 40); }) .catch ((error) => { if (error.response.code === 404 ) { @@ -71,10 +76,7 @@ class Thumbnail extends PureComponent { } }); - // Timer playing & tally status: - this.updatePlayingStatus(); - thumbTimer = setInterval(this.updatePlayingStatus, 400); - thumbCountTimer = setInterval(this.updateTimerStatus, 50); + } //Shortcut for mix and take @@ -145,7 +147,6 @@ class Thumbnail extends PureComponent { this.setStateThumbListElement(index, "loop", infoStatus.foreground.loop); this.updateThumbnail(index); this.props.setActivePgmPixProps(item.thumbPix); - this.props.setPgmCounterProps(this.secondsToTimeCode( infoStatus.foreground.length - infoStatus.foreground.time)); } } //Handle Background: @@ -171,19 +172,10 @@ class Thumbnail extends PureComponent { } - // Timer controlled playing & tally status + // Timer controlled countdown status status updateTimerStatus() { - var forceUpdate = false; - var thisActive = this.props.getTabStateProps(this.props.ccgOutputProps); - if (!this.state.isTabActive && thisActive) { - this.setState({isTabActive: thisActive}); - forceUpdate = true; - } else if (!thisActive) - { - this.setState({isTabActive: thisActive}); - } //only update timer when tab is selected: - if (thisActive) { + if (this.state.isTabActive) { this.props.ccgStateConnectionProps.request("{ timeLeft(ch: " + this.props.ccgOutputProps + ",l: 10) }") .then ((response)=>{ var timeLeft = parseFloat(response.timeLeft); From 7c077fbcc840154fbb9c7a2217cf97bd6b0f4711 Mon Sep 17 00:00:00 2001 From: olzzon Date: Fri, 9 Nov 2018 08:52:37 +0100 Subject: [PATCH 3/3] Fixed: Show right thumbnail when first Thumb on list is selected Set connectionstatus when lost connection --- src/components/App.js | 1 + src/components/Thumbnail.js | 25 +++++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index d72c0e51..f3479089 100755 --- a/src/components/App.js +++ b/src/components/App.js @@ -134,6 +134,7 @@ class App extends Component { }) .catch((error) => { console.log(error); + this.setState({ ccgConnectionStatus: false }); }); } diff --git a/src/components/Thumbnail.js b/src/components/Thumbnail.js index 70346159..979d586b 100644 --- a/src/components/Thumbnail.js +++ b/src/components/Thumbnail.js @@ -16,7 +16,7 @@ class Thumbnail extends PureComponent { //Props: //ccgOutputProps what output on CCG to play at //ccgConnectionProps Current CCG connection - //ccgStateConnectionProps Current CCG connection + //ccgStateConnectionProps Current CCG-state connection //setActivePgmPixProps Reference to Set Header PGMpix //setActivePvmPixProps Reference to Set Header Pvmpix //setPgmCounterProps Sets the timer in header @@ -107,13 +107,11 @@ class Thumbnail extends PureComponent { } - // Timer controlled playing & tally status + // Timer controlled check of playing & tally status updatePlayingStatus() { - var forceUpdate = false; var thisActive = this.props.getTabStateProps(this.props.ccgOutputProps); if (!this.state.isTabActive && thisActive) { this.setState({isTabActive: thisActive}); - forceUpdate = true; } else if (!thisActive) { this.setState({isTabActive: thisActive}); @@ -135,33 +133,28 @@ class Thumbnail extends PureComponent { this.setStateThumbListElement(this.state.thumbActiveIndex, "tally", false); this.setStateThumbListElement(this.state.thumbActiveIndex, "isActive", false); this.updateThumbnail(this.state.thumbActiveIndex); - forceUpdate = true; } - // Update only first time or if time if file is playing: - //if (forceUpdate || !infoStatus.foreground.paused) { - if (forceUpdate) { this.setStateThumbListElement(index, "tally", true); this.setState({thumbActiveIndex: index}); this.setStateThumbListElement(index, "isActive", true); this.setStateThumbListElement(index, "loop", infoStatus.foreground.loop); this.updateThumbnail(index); this.props.setActivePgmPixProps(item.thumbPix); - } } //Handle Background: if(fileNameBg === item.name) { - - if(forceUpdate || this.state.thumbActiveBgIndex != index) { + if(this.state.thumbActiveBgIndex != index) { // Remove Old Green Tally this.setStateThumbListElement(this.state.thumbActiveBgIndex, "tallyBg", false); this.updateThumbnail(this.state.thumbActiveBgIndex); - // Add Active Green Tally - this.setStateThumbListElement(index, "tallyBg", true); - this.setState({thumbActiveBgIndex: index}); - this.updateThumbnail(index); - this.props.setActivePvwPixProps(item.thumbPix); } + // Add Active Green Tally + this.setStateThumbListElement(index, "tallyBg", true); + this.setState({thumbActiveBgIndex: index}); + this.updateThumbnail(index); + this.props.setActivePvwPixProps(item.thumbPix); + } }); })