The flow on its own wouldn't help because it doesn't include the source code for the index.(html|js) nor the .vue file.
Here is a working example with the player loaded directly with no video. And an inject that sends the video url then starts the playback when the video has loaded - all standard code taken from the videojs website and adjusted into the standard uibuilder template.
It also has a working example of the .vue file dynamically loaded via the http loader. I haven't bothered to do anything with it though as I don't think that a vue component is warranted unless you want to do more complex things.
[{"id":"dfcd2046.f0d35","type":"inject","z":"ff1a7711.244f48","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"src\": \"//vjs.zencdn.net/v/oceans.mp4\", \"type\": \"video/mp4\"}","payloadType":"json","x":130,"y":1060,"wires":[["de3ed4a5.ddb688"]]},{"id":"de3ed4a5.ddb688","type":"uibuilder","z":"ff1a7711.244f48","name":"","topic":"","url":"videojs","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"x":330,"y":1060,"wires":[["b9248f71.f5d22"],["b3f325aa.1ea888"]]},{"id":"b9248f71.f5d22","type":"debug","z":"ff1a7711.244f48","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":1040,"wires":[]},{"id":"b3f325aa.1ea888","type":"debug","z":"ff1a7711.244f48","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":1080,"wires":[]}]
index.html
<!doctype html><html lang="en"><head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Node-RED UI Builder - Video.JS Tests</title>
<meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue default template">
<link rel="icon" href="./images/node-blue.ico">
<link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
<link href="https://vjs.zencdn.net/7.10.2/video-js.css" rel="stylesheet" />
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
</head><body>
<div id="app" v-cloak>
<b-container id="app_container">
<div>
<video id="my-video"
class="video-js"
controls
preload="auto"
width="640"
height="264"
data-setup="{}">
<!-- <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4"> -->
</video>
<hr>
<video-player :options="videoOptions"/>
</div>
<h2>Dynamic Data</h2>
<div>
<p>Uses Vue to dynamically update in response to messages from Node-RED.</p>
<p>
Check out the <code>mounted</code> function in <code>index.js</code> to See
how easy it is to update Vue data from Node-RED.
</p>
<b-card class="mt-3" header="Status" border-variant="info" header-bg-variant="info" header-text-variant="white" align="center" >
<p class="float-left">Socket.io Connection Status: <b>{{socketConnectedState}}</b></p>
<p class="float-right">Time offset between browser and server: <b>{{serverTimeOffset}}</b> hours</p>
</b-card>
<b-card class="mt-3" header="Normal Messages" border-variant="primary" header-bg-variant="primary" header-text-variant="white" align="left" >
<p>
Messages: Received=<b>{{msgsReceived}}</b>, Sent=<b>{{msgsSent}}</b>
</p>
<pre v-html="hLastRcvd" class="syntax-highlight"></pre>
<pre v-html="hLastSent" class="syntax-highlight"></pre>
<p slot="footer" class="mb-0">
The received message is from the input to the uibuilder node.
The send message will appear out of port #1 of the node.
</p>
</b-card>
<b-card class="mt-3" header="Control Messages" border-variant="secondary" header-bg-variant="secondary" header-text-variant="white" align="left" >
<p>
Control Messages: Received=<b>{{msgsControl}}</b>, Sent=<b>{{msgsCtrlSent}}</b>
</p>
<pre v-html="hLastCtrlRcvd" class="syntax-highlight"></pre>
<pre v-html="hLastCtrlSent" class="syntax-highlight"></pre>
<p slot="footer" class="mb-0">
Control messages always appear out of port #2 of the uibuilder node
whether they are from the server or the client. The <code>from</code> property
of the message tells you where it came from.
</p>
</b-card>
</div>
</b-container>
</div>
<script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
<script src="../uibuilder/vendor/vue/dist/vue.js"></script>
<script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
<script src="https://vjs.zencdn.net/7.10.2/video.js"></script>
<script src="./uibuilderfe.js"></script>
<script src="./index.js"></script>
</body></html>
index.js
'use strict'
new Vue({
el: '#app',
components: {
'video-player': httpVueLoader('video-player.vue'),
}, // --- End of components --- //
data: {
player: null,
videoOptions: {
autoplay: false,
controls: true,
sources: [
{
src:
'//vjs.zencdn.net/v/oceans.mp4',
type: 'video/mp4'
}
]
},
videoUrl: '',
startMsg : 'Vue has started, waiting for messages',
feVersion : '',
counterBtn : 0,
inputText : null,
inputChkBox : false,
socketConnectedState : false,
serverTimeOffset : '[unknown]',
imgProps : { width: 75, height: 75 },
msgRecvd : '[Nothing]',
msgsReceived: 0,
msgCtrl : '[Nothing]',
msgsControl : 0,
msgSent : '[Nothing]',
msgsSent : 0,
msgCtrlSent : '[Nothing]',
msgsCtrlSent: 0,
isLoggedOn : false,
userId : null,
userPw : null,
inputId : '',
}, // --- End of data --- //
computed: {
hLastRcvd: function() {
var msgRecvd = this.msgRecvd
if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
},
hLastSent: function() {
var msgSent = this.msgSent
if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
},
hLastCtrlRcvd: function() {
var msgCtrl = this.msgCtrl
if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
},
hLastCtrlSent: function() {
var msgCtrlSent = this.msgCtrlSent
if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
//else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])
else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
},
}, // --- End of computed --- //
methods: {
increment: function(event) {
console.log('Button Pressed. Event Data: ', event)
// Increment the count by one
this.counterBtn = this.counterBtn + 1
var topic = this.msgRecvd.topic || 'uibuilder/vue'
uibuilder.send( {
'topic': topic,
'payload': {
'type': 'counterBtn',
'btnCount': this.counterBtn,
'message': this.inputText,
'inputChkBox': this.inputChkBox
}
} )
}, // --- End of increment --- //
doLogon: function() {
uibuilder.logon( {
'id': this.inputId,
} )
}, // --- End of doLogon --- //
doLogoff: function() {
uibuilder.logoff()
}, // --- End of doLogon --- //
// return formatted HTML version of JSON object
syntaxHighlight: function(json) {
json = JSON.stringify(json, undefined, 4)
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number'
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key'
} else {
cls = 'string'
}
} else if (/true|false/.test(match)) {
cls = 'boolean'
} else if (/null/.test(match)) {
cls = 'null'
}
return '<span class="' + cls + '">' + match + '</span>'
})
return json
}, // --- End of syntaxHighlight --- //
}, // --- End of methods --- //
// Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured
/** Called after the Vue app has been created. A good place to put startup code */
created: function() {
// Example of retrieving data from uibuilder
this.feVersion = uibuilder.get('version')
/** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
* Pass the namespace and ioPath variables if hosting page is not in the instance root folder
* e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
* e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names
* @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.
* @param {string=} ioPath Optional. changes self.ioPath from the default
* @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.
*/
uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.
//console.log(this)
},
/** Called once all Vue component instances have been loaded and the virtual DOM built */
mounted: function(){
//console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
var app = this // Reference to `this` in case we need it for more complex functions
app.player = videojs('my-video')
console.log(app.player)
// If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
// newVal relates to the attribute being listened to.
uibuilder.onChange('msg', function(msg){
//console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
app.msgRecvd = msg
app.msgsReceived = uibuilder.get('msgsReceived')
if ( msg.payload.src ) {
app.player.src(msg.payload)
app.player.ready(function() {
app.player.play();
})
}
})
//#region ---- Debug info, can be removed for live use ---- //
/** You can use the following to help trace how messages flow back and forth.
* You can then amend this processing to suite your requirements.
*/
// If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
uibuilder.onChange('ctrlMsg', function(msg){
//console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)
app.msgCtrl = msg
app.msgsControl = uibuilder.get('msgsCtrl')
})
/** You probably only need these to help you understand the order of processing
* If a message is sent back to Node-RED, we can grab a copy here if we want to
*/
uibuilder.onChange('sentMsg', function(msg){
//console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)
app.msgSent = msg
app.msgsSent = uibuilder.get('msgsSent')
})
/** If we send a control message to Node-RED, we can get a copy of it here */
uibuilder.onChange('sentCtrlMsg', function(msg){
//console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)
app.msgCtrlSent = msg
app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')
})
/** If Socket.IO connects/disconnects, we get true/false here */
uibuilder.onChange('ioConnected', function(connected){
//console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)
app.socketConnectedState = connected
})
/** If Server Time Offset changes */
uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){
//console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)
app.serverTimeOffset = serverTimeOffset
})
/** If user is logged on/off */
uibuilder.onChange('isAuthorised', function(isAuthorised){
//console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)
//console.log('authData: ', uibuilder.get('authData'))
//console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))
app.isLoggedOn = isAuthorised
})
//#endregion ---- Debug info, can be removed for live use ---- //
} // --- End of mounted hook --- //
}) // --- End of app1 --- //
// EOF
video-player.vue
<template>
<div>
<video ref="videoPlayer" class="video-js"></video>
</div>
</template>
<script>
//import videojs from 'video.js';
//export default {
module.exports = {
name: "VideoPlayer",
props: {
options: {
type: Object,
default() {
return {};
}
}
},
data() {
return {
player: null
}
},
mounted() {
this.player = videojs(this.$refs.videoPlayer, this.options, function onPlayerReady() {
console.log('onPlayerReady', this);
})
},
beforeDestroy() {
if (this.player) {
this.player.dispose()
}
}
}
</script>