Migrating ui template from Dashboard-1 to Dashboard-2

Here is working m3u8

chrome_Ppks6QJc5m

It still needs work :wink:

Like cleaning up, error handling, sanity checking etc.

[{"id":"88d4cf6b97770beb","type":"ui-template","z":"a77bbd614562e1ee","group":"00328f3e4d88b179","page":"","ui":"","name":"mediaplayer","order":1,"width":0,"height":0,"head":"","format":"<template>\n    <div class=\"nrdb-widget--media-player\">\n        <div v-if=\"loaded && !loading\">\n            <div v-if=\"error_\" class=\"centered\">\n                {{ error_msg || 'Error loading media' }}\n            </div>\n\n            <div v-if=\"def_\" class=\"centered\">\n                <v-img :src=\"def_src\" class=\"main_monitor\" title=\"Default\"></v-img>\n            </div>\n\n            <div v-if=\"pic_\" class=\"centered\">\n                <v-img :src=\"pic_src\" class=\"main_monitor\" title=\"Mediafile\"></v-img>\n            </div>\n\n            <div v-if=\"str_\" class=\"centered\">\n                <v-img :src=\"str_src\" class=\"main_monitor\" title=\"Click for snapshot\" @click=\"send({payload:'snapshot?mainview_url', topic:'Snapshot'})\">\n                </v-img>\n            </div>\n\n            <div v-show=\"vid_\" class=\"centered\">\n                <video\n                    height=\"400\" width=\"100%\"\n                    ref=\"vid_\" autoplay controls :src=\"vid_src\" class=\"main_monitor\" title=\"Main display\"\n                    type=\"video/mp4\"\n                />\n            </div>\n\n            <div v-show=\"hls_\" class=\"centered\">\n                <video\n                    ref=\"hls_\" autoplay muted class=\"main_monitor\"\n                    height=\"400\" width=\"100%\"\n                    title=\"Playing m3u8, click for snapshot\" type=\"video/mp4\"\n                    @click=\"send({payload:'snapshot?mainview_url', topic:'Snapshot'})\"\n                />\n            </div>\n\n            <div v-show=\"saf_\" class=\"centered\">\n                <video\n                    ref=\"saf_\" autoplay muted :src=\"saf_src || ''\" class=\"main_monitor\"\n                    height=\"400\" width=\"100%\"\n                    title=\"Playing m3u8, click for snapshot\" type=\"video/mp4\"\n                    @click=\"send({payload:'snapshot?mainview_url', topic:'Snapshot'})\"\n                />\n            </div>\n\n            <div v-show=\"cap_\" class=\"centered\">\n                <iframe ref=\"cap_\" height=\"400\" width=\"100%\" src=\"\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n            </div>\n        </div>\n        <div v-else>\n            <v-progress-circular indeterminate />\n        </div>\n    </div>\n</template>\n\n\n<script>\nexport default {\n    name: 'MediaPlayer',\n    data () {\n        return {\n            loaded: false,\n            loading: false,\n            unsupported: false,\n            hls_m: null,\n\n            // simple booleans that reactively show/hide components\n            def_: false,\n            pic_: false,\n            str_: false,\n            vid_: false,\n            hls_: false,\n            saf_: false,\n            cap_: true,\n\n            // the source for each type of media - these are bound to the :src attributes in the template\n            def_src: '',\n            vid_src: '',\n            hls_src: '',\n            saf_src: '',\n            cap_src: ''\n        }\n    },\n    computed: {\n        str_src () {\n            return this.str_ ? (this.msg?.url + '?' + this.msg?._msgid || '') : ''\n        },\n        pic_src () {\n            return this.pic_ ? (this.msg?.url + '?' + this.msg?._msgid || '') : ''\n        }\n    },\n    mounted () {\n        console.log('mounted')\n        // code here when the component is first loaded\n        const interval = setInterval(() => {\n            if (window.Hls) {\n                console.log('Hls has loaded')\n                clearInterval(interval)\n                this.loaded = true\n                this.init()\n            }\n        }, 100)\n    },\n    methods: {\n        init () {\n            console.log('init')\n            this.$socket.on('msg-input:' + this.id, (msg) => {\n                // do stuff with msg. runs only when new messages are received\n                this.onMsg(msg)\n            })\n            this.send({ payload: 'do_init' })\n            this.loaded = true\n        },\n        isSafari () {\n            if (/apple/i.test(navigator.vendor)) {\n                // alert(\"Safari\");\n                return true\n            } else {\n                return false\n            }\n        },\n        stopVideos (prev) {\n            // TODO: not sure what this is supposed to be doing\n        //     if(prev.indexOf('recordings')>-1 || prev.indexOf('youtube')>-1) {\n        //         $(\"iframe\").each(function() {\n        //             var src= $(this).attr('src');\n        //             if(prev.indexOf('youtube')>-1) {\n        //                 $(this).attr('src','');\n        //             }else{\n        //                 $(this).attr('src',src);\n        //             }\n        //         });\n        //     }\n        //     if(prev.indexOf('.mp4')>-1) {\n        //         $(\"[title~='Main']\").trigger('pause');\n        //     }\n        },\n        show (which, src) {\n            console.log('showing', which, src)\n            this.loading = false\n            this.def_src = which !== 'def_' ? '' : (src || '')\n            this.vid_src = which !== 'vid_' ? '' : (src || '')\n            this.hls_src = which !== 'hls_' ? '' : (src || '')\n            this.saf_src = which !== 'saf_' ? '' : (src || '')\n            this.cap_src = which !== 'cap_' ? '' : (src || '')\n            this.error_msg = which !== 'error_' ? '' : (src || '')\n            this.def_ = which === 'def_'\n            this.pic_ = which === 'pic_'\n            this.str_ = which === 'str_'\n            this.vid_ = which === 'vid_'\n            this.hls_ = which === 'hls_'\n            this.saf_ = which === 'saf_'\n            this.cap_ = which === 'cap_'\n            this.error_ = which === 'error_'\n        },\n        stopHls() {\n            this.$refs.hls_?.stop && this.$refs.hls_.stop()\n            if (this.hls_m) {\n                try {\n                    this.hls_m.destroy()\n                } catch (err) {}\n                this.hls_m = null\n            }\n        },\n        onMsg (msg) {\n            console.log('msg', msg)\n            if (msg.topic === 'stop') {\n                this.stopHls()\n                this.show('')\n            }\n            if (msg.payload?.indexOf('goback') > -1) {\n                window.history.back()\n            }\n            if (msg.prev?.indexOf('recordings') > -1 || msg.prev?.indexOf('.mp4') > -1 || msg.prev?.indexOf('motions') > -1 || msg.prev?.indexOf('youtube') > -1) {\n                this.stopVideos(msg.prev)\n            }\n            if (msg.payload?.indexOf('snapshots') > -1 || msg.payload?.indexOf('recordings') > -1 || msg.payload?.indexOf('motions') > -1) {\n                const ip = location.host.split(':')[0]\n                this.stopHls()\n                this.show('cap_', 'http://' + ip + msg.url)\n            }\n            if (msg.payload?.indexOf('med') < 0 && msg.payload?.indexOf('default') > -1) {\n                this.stopHls()\n                const ip = location.host.split(':')[0]\n                this.show('def_', 'http://' + ip + msg.url)\n            }\n            if (msg.payload?.indexOf('med') > -1 && msg.payload?.indexOf('default') < 0) {\n                this.stopHls()\n\n                if (msg.url?.indexOf('.mp4') > -1) {\n                    this.show('vid_', msg.url)\n                } else {\n                    this.show('pic_')\n                }\n            }\n            if (msg.payload?.indexOf('cam') > -1 && msg.url?.indexOf('.m3u8') < 0 && msg.url?.indexOf('youtube') < 0) {\n                this.stopHls()\n                this.show('str_')\n            } else if (msg.url?.indexOf('.m3u8') > -1) {\n                this.stopHls()\n                if (this.isSafari()) {\n                    this.show('saf_')\n                } else {\n                    this.send({ payload: 'show hls' })\n                    this.show('hls_')\n                    if (window.Hls?.isSupported()) {\n                        try {\n                            const video = this.$refs.hls_\n                            const hsl = new window.Hls()\n                            this.hls_m = hsl\n                            console.log('hls is supported')\n                            this.loading = true\n                            \n                            hsl.on(Hls.Events.MEDIA_ATTACHED, function () {\n                                console.log('video and hls.js are now bound together !');\n                            });\n                            hsl.on(Hls.Events.MANIFEST_PARSED, function (event, data) {\n                                console.log(\n                                    'manifest loaded, found ' + data.levels.length + ' quality level',\n                                );\n                            });\n                            hsl.loadSource(msg.url);\n                            hsl.attachMedia(video);\n                            this.loading = false\n                        } catch (err) {\n                            this.show('error_')\n                            node.send( { error: { message: err.message } } )\n                        }\n                    } else {\n                        this.show('error_', 'Hls Error: ' + err.message)\n                        node.send( { error: { message: 'Hls Not supported' } } )\n                    }\n                }\n            } else if (msg.url?.indexOf('youtube') > -1) {\n                this.stopHls()\n                this.$refs.cap_.src = msg.url\n                this.show('cap_')\n            } else if (msg.url?.endsWith('.mp4')) {\n                this.stopHls()\n                this.show('vid_', msg.url)\n            }\n        }\n    }\n}\n</script>\n\n<script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/hls.js@latest\"></script>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1450,"y":340,"wires":[["be79f6a038a78723"]]},{"id":"bb7d17187016273a","type":"inject","z":"a77bbd614562e1ee","name":"youtube for cat lovers","props":[{"p":"url","v":"https://www.youtube.com/embed/oZFAcp-Qfbs?si=dVMgWw6fuTof_om-","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"cam-1","payloadType":"str","x":1200,"y":300,"wires":[["88d4cf6b97770beb"]]},{"id":"5df6be2507f19659","type":"inject","z":"a77bbd614562e1ee","name":"youtube for dog lovers","props":[{"p":"url","v":"https://www.youtube.com/embed/c2OTHeCKsBE?si=M4Bq3U0gJTe5uwIr","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1200,"y":340,"wires":[["88d4cf6b97770beb"]]},{"id":"6d27f6b22732560e","type":"inject","z":"a77bbd614562e1ee","name":"stop","props":[{"p":"url","v":"","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"stop","x":1150,"y":380,"wires":[["88d4cf6b97770beb"]]},{"id":"be79f6a038a78723","type":"debug","z":"a77bbd614562e1ee","name":"debug 2566","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1450,"y":380,"wires":[]},{"id":"e11ce29786db928d","type":"inject","z":"a77bbd614562e1ee","name":"BigBuckBunny.mp4","props":[{"p":"url","v":"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":1190,"y":260,"wires":[["88d4cf6b97770beb"]]},{"id":"91525ca5a6fd15cc","type":"inject","z":"a77bbd614562e1ee","name":"longtailvideo m3u8","props":[{"p":"url","v":"http://playertest.longtailvideo.com/adaptive/wowzaid3/playlist.m3u8","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":1190,"y":180,"wires":[["88d4cf6b97770beb"]]},{"id":"780b18b4cac8b3b2","type":"inject","z":"a77bbd614562e1ee","name":"another m3u8","props":[{"p":"url","v":"https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":1170,"y":220,"wires":[["88d4cf6b97770beb"]]},{"id":"00328f3e4d88b179","type":"ui-group","name":"mediaplayer","page":"b6154633432f57b1","width":"6","height":"1","order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"b6154633432f57b1","type":"ui-page","name":"mediaplayer","ui":"72c1e5a9ec204878","path":"/mediaplayer","icon":"home","layout":"grid","theme":"0d92c765bfad87e6","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"72c1e5a9ec204878","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control","ui-chart"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"0d92c765bfad87e6","type":"ui-theme","name":"Basic Blue Theme","colors":{"surface":"#4d58ff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px","density":"default"}}]
3 Likes