Play movie on dashboard from local storage on raspberry pi

try to play a locally stored movie in the dashboard. Node-Red revision 3.0.2

The Template Node content is

<div id="video">
    <video width="300" height="200" controls>
    <source ng-src="{{msg.payload}}">

The Message send to the Template node is

msg.payload = "/data/movie/test.mpg";

The movie exists and is playing in browser locally.
If i attach "/data/movie/test.mpg" to an email node it is send properly.

The Dashboard shows up with the play sign.
Tap play sign shows the movie player with zero start and zero end time and plays nothing.

Any idea what i am doing wrong?

This is the code I use for my video review system.

I wrote it years ago for creating a template for the dashboard.
It still works for my the customers needs.

const video_URL = msg.video_URL;
const video_times = msg.video_times;
const video_uid = msg.video_uid;
var template = msg.template;
video_URL.forEach(function(url, index){
  template += '<div><center><b>' + video_times[index] + '</b></center><video id="' + video_uid[index] + '" width="300" controls preload="none" poster="' + url + '.thumb"><source src="' + url + '"type="video/mp4">Your browser does not support HTML5 video.</video><center><button class="button button1" onclick="setPlaySpeed(0.25,' + video_uid[index] + ')" type="button"><b>1/4</b></button><button class="button button1" onclick="setPlaySpeed(0.5,' + video_uid[index] + ')" type="button"><b>1/2</b></button><button class="button button2" onclick="setPlaySpeed(1,' + video_uid[index] + ')" type="button"><b>Normal</b></button><button class="button button1" onclick="setPlaySpeed(2,' + video_uid[index] + ')" type="button"><b>2x</b></button><button class="button button1" onclick="setPlaySpeed(4,' + video_uid[index] + ')" type="button"><b>4x</b></button><button class="button button1" onclick="setPlaySpeed(8,' + video_uid[index] + ')" type="button"><b>8x</b></button><button class="button button1" onclick="setPlaySpeed(16,' + video_uid[index] + ')" type="button"><b>16x</b></button><button class="button button1" onclick="setPlaySpeed(32,' + video_uid[index] + ')" type="button"><b>32x</b></button></center></div><p></p><p></p><p></p>';
template += '<script>'
  template += 'var ' + uid + ' = document.getElementById("' + uid + '");';
template += 'function setPlaySpeed(speed,id) { id.playbackRate = speed; } </script> </body> </html>';

msg.template = template;

return msg;

here is the section of the flow that is relevant.

        "id": "12b43459dd1bb58c",
        "type": "template",
        "z": "6a54a4bb6e1f85a9",
        "name": "",
        "field": "template",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html>\n<head>\n<style>\n.button {\n  background-color: #2B2B2A; /* Green */\n  border: none;\n  color: white;\n  padding: 2px 2px;\n  text-align: center;\n  text-decoration: none;\n  display: inline-block;\n  font-size: 12px;\n  margin: 0px 0px;\n  cursor: pointer;\n}\n\n.button1 {width: 30px;}\n.button2 {width: 50px;}\n</style>\n</head>\n<body>\n",
        "output": "str",
        "x": 300,
        "y": 1380,
        "wires": [
        "id": "3af83cb30ab2bf3f",
        "type": "function",
        "z": "6a54a4bb6e1f85a9",
        "name": "",
        "func": "const video_URL = msg.video_URL;\nconst video_times = msg.video_times;\nconst video_uid = msg.video_uid;\nvar template = msg.template;\nvideo_URL.forEach(function(url, index){\n  template += '<div><center><b>' + video_times[index] + '</b></center><video id=\"' + video_uid[index] + '\" width=\"300\" controls preload=\"none\" poster=\"' + url + '.thumb\"><source src=\"' + url + '\"type=\"video/mp4\">Your browser does not support HTML5 video.</video><center><button class=\"button button1\" onclick=\"setPlaySpeed(0.25,' + video_uid[index] + ')\" type=\"button\"><b>1/4</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(0.5,' + video_uid[index] + ')\" type=\"button\"><b>1/2</b></button><button class=\"button button2\" onclick=\"setPlaySpeed(1,' + video_uid[index] + ')\" type=\"button\"><b>Normal</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(2,' + video_uid[index] + ')\" type=\"button\"><b>2x</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(4,' + video_uid[index] + ')\" type=\"button\"><b>4x</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(8,' + video_uid[index] + ')\" type=\"button\"><b>8x</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(16,' + video_uid[index] + ')\" type=\"button\"><b>16x</b></button><button class=\"button button1\" onclick=\"setPlaySpeed(32,' + video_uid[index] + ')\" type=\"button\"><b>32x</b></button></center></div><p></p><p></p><p></p>';\n});\ntemplate += '<script>'\nvideo_uid.forEach(function(uid){\n  template += 'var ' + uid + ' = document.getElementById(\"' + uid + '\");';\n});\ntemplate += 'function setPlaySpeed(speed,id) { id.playbackRate = speed; } </script> </body> </html>';\n\nmsg.template = template;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 440,
        "y": 1380,
        "wires": [
        "id": "e02acfd322b3dc8d",
        "type": "change",
        "z": "6a54a4bb6e1f85a9",
        "name": "msg.payload --> msg.template",
        "rules": [
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "template",
                "tot": "msg"
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 650,
        "y": 1380,
        "wires": [
        "id": "ba4b5b54aea00f25",
        "type": "debug",
        "z": "6a54a4bb6e1f85a9",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 970,
        "y": 1420,
        "wires": []
        "id": "21db4f64d3a8f9ba",
        "type": "ui_template",
        "z": "6a54a4bb6e1f85a9",
        "group": "d9a84a21f5a6ac42",
        "name": "",
        "order": 4,
        "width": 6,
        "height": 8,
        "format": "<div ng-bind-html=\"msg.payload\"></div>",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 980,
        "y": 1380,
        "wires": [
        "id": "d9a84a21f5a6ac42",
        "type": "ui_group",
        "name": "Default",
        "tab": "1f8908e3d5c1435e",
        "order": 1,
        "disp": false,
        "width": "6",
        "collapse": false,
        "className": ""
        "id": "1f8908e3d5c1435e",
        "type": "ui_tab",
        "name": "Review",
        "icon": "dashboard",
        "order": 3,
        "disabled": false,
        "hidden": true

Check out this tutorial on html5 video

this should help you in creating your template.

1 Like

Hi meeki007,
thank you for your fast response. (i'm not so fast)

The link to the free code camp was very helpful and i learned a lot. Thank's.

The sample code you send me first makes me a litte headaches.
What ever i try it results in "TypeError: video_uid.forEach is not a function".

Do you have maybe a sample payload that you send to the function?

Oh. Did not think you would want to list all the videos from a folder like I do! Just wanted to share a rather detailed way someone can create a video player.

I am unwilling to send you a copy of the msg payload as it contains server IP's and raw customer data/information.

"TypeError: video_uid.forEach is not a function"
Its expecting a unique ID for each video for playing and playback speed for multiple videos.

This is not sensitive customer information as it is just created from the hour-min-seconds of the video file.



If your going to have multiple videos you need to create/use an id for tracking each one if you going to use/have controls.

Hi meeki007,

sorry, think i explained this wrong. I don't want any sensitive data.

Just understood not the data you send to the template. But got it hopefully now.
This is what i send as msg.payload
msg.video_URL = ["/data/filme/1.mp4", "/data/filme/2.mp4", "/data/filme/3.mp4", "/data/filme/1.mp4"];
msg.video_times = ["1","2","3","4"];
msg.video_uid = ["1","2","3","4"];
and it results not in "TypeError: video_uid.forEach is not a function". Your script works like expectet.
It is showing now the movie player with the play button, but still not playing.
If i use an accessible movie in the web it works like is should.

In my opinion there is something wrong with my basic understanding of the URL, to show a locally stored movie.

Any idea/help on this?

make sure you are using a video type that is supported by your browser. If I use a h265 encoded mp4 video, I get the same issue your having.
I'm linux based so .... using ffmpeg's ffprobe what is the output of.

$ ffprobe your_video_file_here.mp4

Question: if you right click the video window that will not play can you download the file and then play it?
If yes to the above then I'm 99% sure its your ability of your browser/system to play the file.

Other issue could be URL is not accessible from the computer your accessing node-red from.
I have had to come up with some very verbose solutions for hosting the video url's for streaming and hosting video files for the camera system using nginx.

Hi meeki007,

thank you for your help!
The URL was the issue.
Installed a light version of Nginx on my raspberry and fetched the video like http://raspberrypi/video.mp4
This did it.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.