From b0326ccd1eb499c779481e89874e876fc6bde830 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Tue, 20 Dec 2016 16:57:56 +0100 Subject: [PATCH 01/11] LSP: thumbnailing; currently disabled --- CMakeLists.txt | 6 +++ embed/test.html | 1 + embed/wrappers/img.js | 22 ++++++++++ lsp/main.css | 7 ++++ lsp/minified.js | 15 +++---- lsp/mist.js | 62 +++++++++++++++++++++++++---- src/output/output_http_internal.cpp | 6 +++ 7 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 embed/wrappers/img.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d898d53..fb5ba476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,7 @@ set(libSources ${SOURCE_DIR}/lib/util.cpp ${SOURCE_DIR}/lib/vorbis.cpp ) + ######################################## # MistLib - Build # ######################################## @@ -319,6 +320,7 @@ add_executable(MistOutHTTP generated/polytrope.js.h generated/dashjs.js.h generated/videojs.js.h + generated/img.js.h generated/playerdash.js.h generated/playervideo.js.h generated/core.js.h @@ -418,6 +420,10 @@ add_custom_command(OUTPUT generated/videojs.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js video_js generated/videojs.js.h DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js ) +add_custom_command(OUTPUT generated/img.js.h + COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/img.js img_js generated/img.js.h + DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/img.js +) add_custom_command(OUTPUT generated/playerdash.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js playerdash_js generated/playerdash.js.h DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js diff --git a/embed/test.html b/embed/test.html index e3309ae4..ec097278 100644 --- a/embed/test.html +++ b/embed/test.html @@ -31,6 +31,7 @@ <script src=wrappers/videojs.js></script> <script src=wrappers/dashjs.js></script> <script src=wrappers/flash_strobe.js></script> + <script src=wrappers/img.js></script> <script src=wrappers/silverlight.js></script> <script src=wrappers/polytrope.js></script> diff --git a/embed/wrappers/img.js b/embed/wrappers/img.js new file mode 100644 index 00000000..7485dc41 --- /dev/null +++ b/embed/wrappers/img.js @@ -0,0 +1,22 @@ +mistplayers.img = { + name: 'HTML img tag', + mimes: ['html5/image/jpeg'], + priority: Object.keys(mistplayers).length + 1, + isMimeSupported: function (mimetype) { + return (this.mimes.indexOf(mimetype) == -1 ? false : true); + }, + isBrowserSupported: function (mimetype,source,options,streaminfo) { + //only use this if we are sure we just want an image + if ((options.forceType) || (options.forceSource) || (options.forcePlayer)) { return true; } + return false; + }, + player: function(){} +}; +var p = mistplayers.img.player; +p.prototype = new MistPlayer(); +p.prototype.build = function (options) { + var ele = this.element('img'); + ele.src = options.src; + ele.style.display = 'block'; + return ele; +} diff --git a/lsp/main.css b/lsp/main.css index 03317178..5b5f1f02 100644 --- a/lsp/main.css +++ b/lsp/main.css @@ -792,6 +792,13 @@ button.return:before { background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiIGlkPSJzdmcyIiBoZWlnaHQ9IjQ1IiB3aWR0aD0iNDUiPjxkZWZzIGlkPSJkZWZzNCIgLz48bWV0YWRhdGEgaWQ9Im1ldGFkYXRhNyI+PHJkZjpSREY+PGNjOldvcmsgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+PGRjOnRpdGxlPjwvZGM6dGl0bGU+PC9jYzpXb3JrPjwvcmRmOlJERj48L21ldGFkYXRhPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsLTEwMDcuMzYyMikiIGlkPSJsYXllcjEiPjxwYXRoIHRyYW5zZm9ybT0ibWF0cml4KDEuMDE0MTgyNywtMC41ODU1Mzg2NywwLjU4NTUzODY3LDEuMDE0MTgyNywtMC40ODQxOTgzMSwxMDIyLjg4OTMpIiBkPSJNIDEwLjMxMjUsLTYuMzQzNzUgQSAyLjk0MTYxODYsMi45NDE2MTg2IDAgMCAwIDcuOTA2MjUsLTQuODc1IGwgLTE0LjEyNSwyNC41IGEgMi45NDE2MTg2LDIuOTQxNjE4NiAwIDAgMCAyLjU2MjUsNC40MDYyNSBsIDI4LjI4MTI1LDAgQSAyLjk0MTYxODYsMi45NDE2MTg2IDAgMCAwIDI3LjE1NjI1LDE5LjYyNSBMIDEzLC00Ljg3NSBhIDIuOTQxNjE4NiwyLjk0MTYxODYgMCAwIDAgLTIuNjg3NSwtMS40Njg3NSB6IiBpZD0icGF0aDM4MDkiIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLW9wYWNpdHk6MSIgLz48L2c+PC9zdmc+"); background-repeat: no-repeat; background-position: center center; + line-height: 100px; +} +.preview_icons .image img { + max-width: 200px; + max-height: 100px; + min-width: 0px; + vertical-align: middle; } .preview_icons .image.folder { background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2ZXJzaW9uPSIxLjEiICBpZD0ic3ZnMiIgIHZpZXdCb3g9IjAgMCA0NSA0NSIgIGhlaWdodD0iNDUiICB3aWR0aD0iNDUiPiA8ZGVmcyAgIGlkPSJkZWZzNCIgLz4gPG1ldGFkYXRhICAgaWQ9Im1ldGFkYXRhNyI+ICA8cmRmOlJERj4gICA8Y2M6V29yayAgICAgcmRmOmFib3V0PSIiPiAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4gICAgPGRjOnR5cGUgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPiAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4gICA8L2NjOldvcms+ICA8L3JkZjpSREY+IDwvbWV0YWRhdGE+IDxnICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMTAwNy4zNjIyKSIgICBpZD0ibGF5ZXIxIj4gIDxwYXRoICAgIGlkPSJyZWN0NDE0NyIgICAgZD0ibSAyNi41MDAwMDEsMTAxNC4yOTk3IC01LjMzMzMzNCw1LjkyNzEgLTE4LjY2NjY2NywwIDAsMjkuNjM1NCAyNCwwIDE2LDAgYyAwLC0xMS44NTQyIDAsLTIzLjcwODMgMCwtMzUuNTYyNSB6IiAgICBzdHlsZT0ib3BhY2l0eToxO2ZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MTtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjEiIC8+IDwvZz48L3N2Zz4="); diff --git a/lsp/minified.js b/lsp/minified.js index d2304f92..8ebabd65 100644 --- a/lsp/minified.js +++ b/lsp/minified.js @@ -76,13 +76,14 @@ type:"password",validate:["required",function(a,b){return a!=$(".match_password" delete mist.user.rawpassword}}]}]));break;case "Account created":UI.elements.menu.addClass("hide");c.append($("<p>").text("Your account has been created succesfully.")).append(UI.buildUI([{type:"text",text:"Would you like to enable all (currently) available protocols with their default settings?"},{type:"buttons",buttons:[{label:"Enable protocols",type:"save","function":function(){if(mist.data.config.protocols)c.append("Unable to enable all protocols as protocol settings already exist.<br>");else{c.append("Retrieving available protocols..<br>"); mist.send(function(a){var b=[],d;for(d in a.capabilities.connectors)if(a.capabilities.connectors[d].required)c.append('Could not enable protocol "'+d+'" because it has required settings.<br>');else{b.push({connector:d});c.append('Enabled protocol "'+d+'".<br>')}c.append("Saving protocol settings..<br>");mist.send(function(){c.append("Protocols enabled. Redirecting..");setTimeout(function(){UI.navto("Overview")},5E3)},{config:{protocols:b}})},{capabilities:true})}}},{label:"Skip",type:"cancel","function":function(){UI.navto("Overview")}}]}])); break;case "Overview":var f=$("<span>").text("Loading.."),q=$("<span>"),p=$("<span>").addClass("logs"),l=$("<span>"),u=$("<span>"),h=$("<span>"),i=$("<span>");c.append(UI.buildUI([{type:"help",help:"You can find most basic information about your MistServer here.<br>You can also set the debug level and force a save to the config.json file that MistServer uses to save your settings. "},{type:"span",label:"Version",pointer:{main:mist.data.config,index:"version"}},{type:"span",label:"Version check",value:f, -LTSonly:!0},{type:"span",label:"Server time",value:u},{type:"span",label:"Configured streams",value:mist.data.streams?Object.keys(mist.data.streams).length:0},{type:"span",label:"Active streams",value:q},{type:"span",label:"Current connections",value:l},{type:"span",label:"Enabled protocols",value:h},{type:"span",label:"Disabled protocols",value:i},{type:"span",label:"Recent problems",value:p},$("<br>"),{type:"str",label:"Human readable name",pointer:{main:mist.data.config,index:"name"},help:"You can name your MistServer here for personal use. You'll still need to set host name within your network yourself."}, -{type:"debug",label:"Debug level",pointer:{main:mist.data.config,index:"debug"},help:"You can set the amount of debug information MistServer saves in the log. A full reboot of MistServer is required before some components of MistServer can post debug information."},{type:"checkbox",label:"Force configurations save",pointer:{main:mist.data,index:"save"},help:"Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don't forget to press save after ticking the box."}, -{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={config:mist.data.config};if(mist.data.save)a.save=mist.data.save;mist.send(function(){UI.navto("Overview")},a)}}]}]));if(mist.data.LTS){var k=function(){var a=mist.stored.get().update||{};"uptodate"in a?a.error?f.addClass("red").text(a.error):a.uptodate?f.text("Your version is up to date.").addClass("green"):f.addClass("red").text("Version outdated!").append($("<button>").text("Update").css({"font-size":"1em","margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){f.addClass("orange").removeClass("red").text("Rolling update command sent.."); -mist.stored.del("update");mist.send(function(){UI.navto("Overview")},{autoupdate:true})}})):f.text("Unknown")};if(!mist.stored.get().update||36E5<(new Date).getTime()-mist.stored.get().update.lastchecked){var j=mist.stored.get().update||{};j.lastchecked=(new Date).getTime();mist.send(function(a){mist.stored.set("update",$.extend(true,j,a.update));k()},{checkupdate:!0})}else k()}else f.text("");g=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities= -true;mist.send(function(){ga()},a)};var ga=function(){q.text("active_streams"in mist.data?mist.data.active_streams?mist.data.active_streams.length:0:"?");if("totals"in mist.data&&"all_streams"in mist.data.totals)var a=mist.data.totals.all_streams.all_protocols.clients,a=a.length?UI.format.number(a[a.length-1][1]):0;else a="Loading..";l.text(a);u.text(UI.format.dateTime(mist.data.config.time,"long"));p.html("");var a=0,b;for(b in mist.data.log){var c=mist.data.log[b];if(["FAIL","ERROR"].indexOf(c[1])> --1){a++;var d=$("<span>").addClass("content").addClass("red"),e=c[2].split("|");for(b in e)d.append($("<span>").text(e[b]));p.append($("<div>").append($("<span>").append(UI.format.time(c[0]))).append(d));if(a==5)break}}a==0&&p.html("None.");a=[];c=[];for(b in mist.data.config.protocols){d=mist.data.config.protocols[b];a.indexOf(d.connector)>-1||a.push(d.connector)}h.text(a.length?a.join(", "):"None.");if("capabilities"in mist.data){for(b in mist.data.capabilities.connectors)a.indexOf(b)==-1&&c.push(b); -i.text(c.length?c.join(", "):"None.")}else i.text("Loading..")};g();ga();UI.interval.set(g,3E4);break;case "Protocols":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var z=$("<tbody>");c.append(UI.buildUI([{type:"help",help:"You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols."}])).append($("<button>").text("Delete all protocols").click(function(){if(confirm("Are you sure you want to delete all currently configured protocols?")){mist.data.config.protocols= +LTSonly:!0},{type:"span",label:"Server time",value:u},{type:"span",label:"Licensed to","default":"unknown",pointer:{main:mist.data.config.license,index:"user"},LTSonly:!0},{type:"span",label:"Configured streams",value:mist.data.streams?Object.keys(mist.data.streams).length:0},{type:"span",label:"Active streams",value:q},{type:"span",label:"Current connections",value:l},{type:"span",label:"Enabled protocols",value:h},{type:"span",label:"Disabled protocols",value:i},{type:"span",label:"Recent problems", +value:p},$("<br>"),{type:"str",label:"Human readable name",pointer:{main:mist.data.config,index:"name"},help:"You can name your MistServer here for personal use. You'll still need to set host name within your network yourself."},{type:"debug",label:"Debug level",pointer:{main:mist.data.config,index:"debug"},help:"You can set the amount of debug information MistServer saves in the log. A full reboot of MistServer is required before some components of MistServer can post debug information."},{type:"checkbox", +label:"Force configurations save",pointer:{main:mist.data,index:"save"},help:"Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don't forget to press save after ticking the box."},{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={config:mist.data.config};if(mist.data.save)a.save=mist.data.save;mist.send(function(){UI.navto("Overview")},a)}}]}]));if(mist.data.LTS){var k= +function(a){"uptodate"in a?a.error?f.addClass("red").text(a.error):a.uptodate?f.text("Your version is up to date.").addClass("green"):f.addClass("red").text("Version outdated!").append($("<button>").text("Update").css({"font-size":"1em","margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){f.addClass("orange").removeClass("red").text("Rolling update command sent..");mist.stored.del("update");mist.send(function(){UI.navto("Overview")},{autoupdate:true})}})): +f.text("Unknown")};if(!mist.stored.get().update||36E5<(new Date).getTime()-mist.stored.get().update.lastchecked){var j={};j.lastchecked=(new Date).getTime();mist.send(function(a){mist.stored.set("update",j);k(a.update)},{checkupdate:!0})}else mist.send(function(a){k(a.update)},{update:!0})}else f.text("");g=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities=true;mist.send(function(){ga()},a)};var ga=function(){q.text("active_streams"in +mist.data?mist.data.active_streams?mist.data.active_streams.length:0:"?");if("totals"in mist.data&&"all_streams"in mist.data.totals)var a=mist.data.totals.all_streams.all_protocols.clients,a=a.length?UI.format.number(a[a.length-1][1]):0;else a="Loading..";l.text(a);u.text(UI.format.dateTime(mist.data.config.time,"long"));p.html("");var a=0,b;for(b in mist.data.log){var c=mist.data.log[b];if(["FAIL","ERROR"].indexOf(c[1])>-1){a++;var d=$("<span>").addClass("content").addClass("red"),e=c[2].split("|"); +for(b in e)d.append($("<span>").text(e[b]));p.append($("<div>").append($("<span>").append(UI.format.time(c[0]))).append(d));if(a==5)break}}a==0&&p.html("None.");a=[];c=[];for(b in mist.data.config.protocols){d=mist.data.config.protocols[b];a.indexOf(d.connector)>-1||a.push(d.connector)}h.text(a.length?a.join(", "):"None.");if("capabilities"in mist.data){for(b in mist.data.capabilities.connectors)a.indexOf(b)==-1&&c.push(b);i.text(c.length?c.join(", "):"None.")}else i.text("Loading..")};g();ga();UI.interval.set(g, +3E4);break;case "Protocols":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var z=$("<tbody>");c.append(UI.buildUI([{type:"help",help:"You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols."}])).append($("<button>").text("Delete all protocols").click(function(){if(confirm("Are you sure you want to delete all currently configured protocols?")){mist.data.config.protocols= [];mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append($("<button>").text("Enable default protocols").click(function(){var a=Object.keys(mist.data.capabilities.connectors),b;for(b in mist.data.config.protocols){var c=a.indexOf(mist.data.config.protocols[b].connector);c>-1&&a.splice(c,1)}var d=[];for(b in a)(!("required"in mist.data.capabilities.connectors[a[b]])||Object.keys(mist.data.capabilities.connectors[a[b]].required).length==0)&&d.push(a[b]);c="Click OK to enable disabled protocols with their default settings:\n "; c=d.length?c+d.join(", "):c+"None.";if(d.length!=a.length){a=a.filter(function(a){return d.indexOf(a)<0});c=c+("\n\nThe following protocols can only be set manually:\n "+a.join(", "))}if(confirm(c)&&d.length){for(b in d)mist.data.config.protocols.push({connector:d[b]});mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append("<br>").append($("<button>").text("New protocol").click(function(){UI.navto("Edit Protocol")}).css("clear","both")).append($("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Protocol")).append($("<th>").text("Status")).append($("<th>").text("Settings")).append($("<th>")))).append(z)); var K=function(){function a(b){var c=mist.data.capabilities.connectors[b.connector];if(!c)return"";var d=[],e=["required","optional"],g;for(g in e)for(var t in c[e[g]])b[t]&&b[t]!=""?d.push(t+": "+b[t]):c[e[g]][t]["default"]&&d.push(t+": "+c[e[g]][t]["default"]);return $("<span>").addClass("description").text(d.join(", "))}z.html("");for(var b in mist.data.config.protocols){var c=mist.data.config.protocols[b];z.append($("<tr>").data("index",b).append($("<td>").text(c.connector)).append($("<td>").html(UI.format.status(c))).append($("<td>").html(a(c))).append($("<td>").css("text-align", diff --git a/lsp/mist.js b/lsp/mist.js index 9faebce8..e021e0ad 100644 --- a/lsp/mist.js +++ b/lsp/mist.js @@ -1388,7 +1388,6 @@ var UI = { break; case 'coords': - //TODO break; } } @@ -2067,6 +2066,15 @@ var UI = { type: 'span', label: 'Server time', value: $servertime + },{ + type: 'span', + label: 'Licensed to', + 'default': 'unknown', + pointer: { + main: mist.data.config.license, + index: 'user' + }, + LTSonly: true },{ type: 'span', label: 'Configured streams', @@ -2133,8 +2141,7 @@ var UI = { } ])); if (mist.data.LTS) { - function update_update() { - var info = mist.stored.get().update || {}; + function update_update(info) { if (!('uptodate' in info)) { $versioncheck.text('Unknown'); return; @@ -2163,15 +2170,17 @@ var UI = { } if ((!mist.stored.get().update) || ((new Date()).getTime()-mist.stored.get().update.lastchecked > 3600e3)) { - var update = mist.stored.get().update || {}; + var update = {}; update.lastchecked = (new Date()).getTime(); mist.send(function(d){ - mist.stored.set('update',$.extend(true,update,d.update)); - update_update(); + mist.stored.set('update',update); + update_update(d.update); },{checkupdate: true}); } else { - update_update(); + mist.send(function(d){ + update_update(d.update); + },{update: true}); } } else { @@ -2620,6 +2629,33 @@ var UI = { $main.append( $('<span>').addClass('description').text('Choose a stream below.') ).append($shortcuts); + + //if there is a JPG output, add actual thumnails \o/ + var thumbnails = false; + ///\todo activate this code when the backend is ready + /* + if (UI.findOutput('JPG')) { + var jpgport = false; + //find the http port and make sure JPG is enabled + for (var i in mist.data.config.protocols) { + var protocol = mist.data.config.protocols[i]; + if ((protocol.connector == 'HTTP') || (protocol.connector == 'HTTP.exe')) { + jpgport = (protocol.port ? ':'+protocol.port : ':8080'); + } + if ((protocol.connector == 'JPG') || (protocol.connector == 'JPG.exe')) { + thumbnails = true; + } + } + if ((thumbnails) && (jpgport)) { + //now we get to use it as a magical function wheee! + jpgport = parseURL(mist.user.host).host+jpgport; + thumbnails = function(streamname) { + return 'http://'+jpgport+'/'+encodeURIComponent(streamname)+'.jpg'; + } + } + } + */ + for (var i in select) { var streamname = select[i]; var source = ''; @@ -2649,6 +2685,17 @@ var UI = { UI.navto('Embed',$(this).closest('div').attr('data-stream')); }); var $image = $('<span>').addClass('image'); + + if ((thumbnails) && (folders.indexOf(streamname) == -1)) { + //there is a JPG output and this isn't a folder + $image.append( + $('<img>').attr('src',thumbnails(streamname)).error(function(){ + $(this).hide(); + }) + ); + } + + //its a wildcard stream if (streamname.indexOf('+') > -1) { var streambits = streamname.split('+'); source = mist.data.streams[streambits[0]].source+streambits[1]; @@ -2658,6 +2705,7 @@ var UI = { } else { source = mist.data.streams[streamname].source; + //its a folder stream if (folders.indexOf(streamname) > -1) { $preview = ''; $embed = ''; diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 0f6fcb7d..43d2247a 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -89,6 +89,7 @@ namespace Mist { //capa["optional"]["wrappers"]["allowed"].append("polytrope"); //currently borked capa["optional"]["wrappers"]["allowed"].append("flash_strobe"); capa["optional"]["wrappers"]["allowed"].append("silverlight"); + capa["optional"]["wrappers"]["allowed"].append("img"); capa["optional"]["wrappers"]["option"] = "--wrappers"; capa["optional"]["wrappers"]["short"] = "w"; cfg->addConnectorOptions(8080, capa); @@ -577,6 +578,11 @@ namespace Mist { response.append((char*)video_js, (size_t)video_js_len); used = true; } + if (it->asStringRef() == "img"){ + #include "img.js.h" + response.append((char*)img, (size_t)img_len); + used = true; + } if (!used) { WARN_MSG("Unknown player type: %s",it->asStringRef().c_str()); } From 17c1368235dd6106c98ec726b6c51ab2c3cb1f1d Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Tue, 6 Dec 2016 15:19:16 +0100 Subject: [PATCH 02/11] Embed: clear target element before appending player --- embed/core.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embed/core.js b/embed/core.js index 8b2e7902..20173b00 100644 --- a/embed/core.js +++ b/embed/core.js @@ -863,6 +863,7 @@ function mistPlay(streamName,options) { embedLog('Checking available players..'); var source = false; + var mistPlayer = false; function checkPlayer(p_shortname) { if ((startCombo) && (!startCombo.started.player)) { @@ -950,6 +951,7 @@ function mistPlay(streamName,options) { } } + options.target.innerHTML = ''; if (mistPlayer) { //create the options to send to the player @@ -1026,6 +1028,7 @@ function mistPlay(streamName,options) { if (player.setTracks(false)) { //gather track info + //tracks var tracks = { video: [], audio: [], @@ -1112,6 +1115,7 @@ function mistPlay(streamName,options) { if (player.setTracks(false)) { player.onready(function(){ + //player.setTracks(usetracks); if ('setTracks' in options) { player.setTracks(options.setTracks); } }); } From a41929168d723e327e933a3e34a72de735bed3f1 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Fri, 23 Dec 2016 10:58:53 +0100 Subject: [PATCH 03/11] Embed: dont attempt to loop live videos --- embed/core.js | 2 +- embed/wrappers/img.js | 2 +- embed/wrappers/videojs.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embed/core.js b/embed/core.js index 20173b00..1afc71c6 100644 --- a/embed/core.js +++ b/embed/core.js @@ -966,7 +966,7 @@ function mistPlay(streamName,options) { //pass player options and handle defaults playerOpts.autoplay = options.autoplay; playerOpts.controls = options.controls; - playerOpts.loop = options.loop; + playerOpts.loop = (playerOpts.live ? false : options.loop); playerOpts.poster = options.poster; function calcSize() { diff --git a/embed/wrappers/img.js b/embed/wrappers/img.js index 7485dc41..8b3aed32 100644 --- a/embed/wrappers/img.js +++ b/embed/wrappers/img.js @@ -15,7 +15,7 @@ mistplayers.img = { var p = mistplayers.img.player; p.prototype = new MistPlayer(); p.prototype.build = function (options) { - var ele = this.element('img'); + var ele = this.getElement('img'); ele.src = options.src; ele.style.display = 'block'; return ele; diff --git a/embed/wrappers/videojs.js b/embed/wrappers/videojs.js index c3f91f41..ebc07b00 100644 --- a/embed/wrappers/videojs.js +++ b/embed/wrappers/videojs.js @@ -118,7 +118,7 @@ p.prototype.build = function (options) { } } me.adderror(msg); - },true); + }); var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress']; for (var i in events) { ele.addEventListener(events[i],function(e){ From 4b1608646dbde6ede320d130f1d68ac196f1cd49 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Tue, 10 Jan 2017 13:44:34 +0100 Subject: [PATCH 04/11] remove all option for checklist --- lsp/mist.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lsp/mist.js b/lsp/mist.js index e021e0ad..0c258a7b 100644 --- a/lsp/mist.js +++ b/lsp/mist.js @@ -600,8 +600,7 @@ var UI = { case 'checklist': $field = $('<div>').addClass('checkcontainer'); $controls = $('<div>').addClass('controls'); - $checklist = $('<div>').addClass('checklist'); - $field.append($controls).append($checklist); + /* All tends to be confusing: disable it for now $controls.append( $('<label>').text('All').prepend( $('<input>').attr('type','checkbox').click(function(){ @@ -614,6 +613,10 @@ var UI = { }) ) ); + $field.append($controls); + */ + $checklist = $('<div>').addClass('checklist'); + $field.append($checklist); for (var i in e.checklist) { if (typeof e.checklist[i] == 'string') { e.checklist[i] = [e.checklist[i], e.checklist[i]]; From aa3527dcb0f8830d0378b3fbcb483aa4595849f8 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Thu, 19 Jan 2017 13:31:05 +0100 Subject: [PATCH 05/11] Embed: custom streaminfo --- embed/core.js | 62 ++++++++++++++++++----------- embed/test.html | 2 + src/output/output_http_internal.cpp | 2 +- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/embed/core.js b/embed/core.js index 1afc71c6..3b7c1fba 100644 --- a/embed/core.js +++ b/embed/core.js @@ -745,12 +745,13 @@ function mistPlay(streamName,options) { var local = options; var global = (typeof mistoptions == 'undefined' ? {} : mistoptions); var options = { - host: null, - autoplay: true, - controls: true, - loop: false, - poster: null, - callback: false + host: null, //override mistserver host (default is the host that player.js is loaded from) + autoplay: true, //start playing when loaded + controls: true, //show controls (MistControls when available) + loop: false, //don't loop when the stream has finished + poster: null, //don't show an image before the stream has started + callback: false, //don't call a function when the player has finished building + streaminfo: false //don't use this streaminfo but collect it from the mistserverhost }; for (var i in global) { options[i] = global[i]; @@ -781,28 +782,12 @@ function mistPlay(streamName,options) { } } - //get info js - var info = document.createElement('script'); - info.src = options.host+'/info_'+encodeURIComponent(streamName)+'.js'; - embedLog('Retrieving stream info from '+info.src); - document.head.appendChild(info); - info.onerror = function(){ - options.target.innerHTML = ''; - options.target.removeAttribute('data-loading'); - mistError('Error while loading stream info.'); - protoplay.report({ - type: 'init', - error: 'Failed to load '+info.src - }); - } - info.onload = function(){ + + function onstreaminfo() { options.target.innerHTML = ''; options.target.removeAttribute('data-loading'); embedLog('Stream info was loaded succesfully'); - //clean up info script - document.head.removeChild(info); - //get streaminfo data var streaminfo = mistvideo[streamName]; //embedLog('Stream info contents: '+JSON.stringify(streaminfo)); @@ -1256,4 +1241,33 @@ function mistPlay(streamName,options) { mistError(str); } } + if ((options.streaminfo) && (typeof options.streaminfo == 'object') && ('type' in options.streaminfo) + && ('source' in options.streaminfo) && (options.streaminfo.source.length) + && ('meta' in options.streaminfo) && ('tracks' in options.streaminfo.meta)) { //catch some of the most problematic stuff + if (typeof mistvideo == 'undefined') { mistvideo = {}; } + mistvideo[streamName] = options.streaminfo; + onstreaminfo(); + } + else { + //get info js + var info = document.createElement('script'); + info.src = options.host+'/info_'+encodeURIComponent(streamName)+'.js'; + embedLog('Retrieving stream info from '+info.src); + document.head.appendChild(info); + info.onerror = function(){ + options.target.innerHTML = ''; + options.target.removeAttribute('data-loading'); + mistError('Error while loading stream info.'); + protoplay.report({ + type: 'init', + error: 'Failed to load '+info.src + }); + } + info.onload = function(){ + //clean up info script + document.head.removeChild(info); + + onstreaminfo(); + } + } } diff --git a/embed/test.html b/embed/test.html index ec097278..5e425698 100644 --- a/embed/test.html +++ b/embed/test.html @@ -85,6 +85,8 @@ //tryplayers.push('html5'); tryplayers.push('dashjs'); //tryplayers.push('videojs'); + tryplayers.push('img'); + //tryplayers.push('dashjs'); //tryplayers.push('flash_strobe'); //tryplayers.push('silverlight'); streams = []; diff --git a/src/output/output_http_internal.cpp b/src/output/output_http_internal.cpp index 43d2247a..d9210010 100644 --- a/src/output/output_http_internal.cpp +++ b/src/output/output_http_internal.cpp @@ -580,7 +580,7 @@ namespace Mist { } if (it->asStringRef() == "img"){ #include "img.js.h" - response.append((char*)img, (size_t)img_len); + response.append((char*)img_js, (size_t)img_js_len); used = true; } if (!used) { From 4fd6d147443ff7089009fea91676c784e11f0ea7 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Tue, 24 Jan 2017 11:53:49 +0100 Subject: [PATCH 06/11] Embed: support without localStorage --- embed/core.js | 6 +++--- embed/test.html | 22 ++++++++++++++++++++++ embed/wrappers/html5.js | 1 - 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/embed/core.js b/embed/core.js index 3b7c1fba..64ad322d 100644 --- a/embed/core.js +++ b/embed/core.js @@ -303,7 +303,7 @@ MistPlayer.prototype.buildMistControls = function(){ } volume.className = 'volume'; sound.title = 'Volume'; - if ('mistVolume' in localStorage) { + if (('localStorage' in window) && (localStorage != null) && ('mistVolume' in localStorage)) { ele.volume = localStorage['mistVolume']; volume.style.height = ele.volume*100+'%'; } @@ -452,7 +452,7 @@ MistPlayer.prototype.buildMistControls = function(){ } if (i == 'subtitle') { s.value = 0; - if ('mistSubtitle' in localStorage) { + if (('localStorage' in window) && (localStorage != null) && ('mistSubtitle' in localStorage)) { var option = s.querySelector('[data-lang="'+localStorage['mistSubtitle']+'"]'); if (option) { s.value = option.value; @@ -1082,7 +1082,7 @@ function mistPlay(streamName,options) { catch (e) { //show the next player/reload buttons if there is an error in the player build code options.target.appendChild(player.element); - player.askNextCombo('Error while building player'); + player.askNextCombo('Error while building player: '+e.stack); throw e; player.report({ type: 'init', diff --git a/embed/test.html b/embed/test.html index 5e425698..9d862b5e 100644 --- a/embed/test.html +++ b/embed/test.html @@ -119,6 +119,28 @@ //forceSource: 3, loop: true, //controls: 'stock' + streaminfo: { + source: [{ + type: 'html5/video/mp4', + url: 'http://localhost:8080/bunny.mp4' + },{ + type: 'html5/application/vnd.apple.mpegurl', + url: 'http://localhost:8080/hls/bunny/index.m3u8' + },{ + type: 'dash/video/mp4', + url: 'http://localhost:8080/dash/bunny/index.mpd' + },{ + player_url: '/flashplayer.swf', + type: 'flash/10', + url: 'rtmp://localhost:1935/play/bunny' + }], + meta: { + tracks: [] + }, + height: 404, + width: 720, + type: 'vod' + }, callback: function(player) { var button = document.createElement('button'); button.innerHTML = 'askNextCombo();'; diff --git a/embed/wrappers/html5.js b/embed/wrappers/html5.js index abb951b9..cbf45a0b 100644 --- a/embed/wrappers/html5.js +++ b/embed/wrappers/html5.js @@ -162,7 +162,6 @@ p.prototype.build = function (options) { me.addlog('Player event fired: '+e.type); },true); } - return cont; } p.prototype.play = function(){ return this.element.play(); }; From f5da3614fe66f4e5421f836ba555d51af6a7521b Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Wed, 25 Jan 2017 15:03:02 +0100 Subject: [PATCH 07/11] Embed: quadratic volume and reload button timeout --- embed/core.js | 43 ++++++++++++++++++++++++++++++++----------- embed/mist.css | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/embed/core.js b/embed/core.js index 64ad322d..565a99da 100644 --- a/embed/core.js +++ b/embed/core.js @@ -298,8 +298,11 @@ MistPlayer.prototype.buildMistControls = function(){ var pos0 = sound.getBoundingClientRect().top - parseInt(style.borderTopWidth,10); var perc = (ypos - pos0 * zoom) / sound.offsetHeight / zoom; - var secs = Math.max(0,perc) * ele.duration; - return 1 - Math.min(1,Math.max(0,perc)); + + perc = 1 - Math.min(1,Math.max(0,perc)); //linear range between 0 and 1 + perc = -1 * Math.pow((1-perc),2) + 1; //transform to quadratic range between 0 and 1 + + return perc; } volume.className = 'volume'; sound.title = 'Volume'; @@ -313,6 +316,7 @@ MistPlayer.prototype.buildMistControls = function(){ }; var mouseup = function(e){ document.removeEventListener('mousemove',mousemove); + controls.removeEventListener('mousemove',mousemove); document.removeEventListener('touchmove',mousemove); document.removeEventListener('mouseup',mouseup); document.removeEventListener('touchend',mouseup); @@ -322,6 +326,7 @@ MistPlayer.prototype.buildMistControls = function(){ catch (e) {} }; document.addEventListener('mousemove',mousemove); + controls.addEventListener('mousemove',mousemove); //this one is added because the controls hiding mechanism stops propagation to the document document.addEventListener('touchmove',mousemove); document.addEventListener('mouseup',mouseup); document.addEventListener('touchend',mouseup); @@ -510,7 +515,9 @@ MistPlayer.prototype.buildMistControls = function(){ play.setAttribute('data-state','paused'); }); ele.addEventListener('volumechange',function(){ - volume.style.height = ele.volume*100+'%'; + //-1 * Math.pow((1-sound.getPos(e.clientY)),2) + 1; //transform to quadratic range between 0 and 1 + var vol = 1 - Math.pow(1-ele.volume,0.5); + volume.style.height = vol*100+'%'; //transform back from quadratic if (ele.volume == 0) { speaker.setAttribute('data-muted',''); } @@ -615,6 +622,9 @@ MistPlayer.prototype.askNextCombo = function(msg){ me.nextCombo(); } var button = document.createElement('button'); + var i = document.createElement('div'); //a css countdown clock for 10sec + i.className = 'countdown10'; + button.appendChild(i); var t = document.createTextNode('Reload this player'); button.appendChild(t); err.appendChild(button); @@ -628,6 +638,12 @@ MistPlayer.prototype.askNextCombo = function(msg){ this.target.appendChild(err); this.element.style.opacity = '0.2'; + + //after 10 seconds, reload the player + err.timeOut = setTimeout(function(){ + button.click(); + },10e3); + }; MistPlayer.prototype.cancelAskNextCombo = function(){ if (this.errorstate) { @@ -637,6 +653,7 @@ MistPlayer.prototype.cancelAskNextCombo = function(){ var err = this.target.querySelector('.error'); if (err) { this.target.removeChild(err); + if (err.timeOut) { clearTimeout(err.timeOut); } } } }; @@ -1152,9 +1169,14 @@ function mistPlay(streamName,options) { element.checkStalledTimeout = false; player.cancelAskNextCombo(); } + if (element.checkStalledTimeout) { + clearTimeout(element.checkStalledTimeout); + element.checkStalledTimeout = false; + player.cancelAskNextCombo(); + } }; - //element.addEventListener('progress',progress,true); - //element.addEventListener('playing',progress,true); + element.addEventListener('progress',progress,true); + element.addEventListener('playing',progress,true); element.addEventListener('play',function(){ player.paused = false; if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) { @@ -1162,11 +1184,10 @@ function mistPlay(streamName,options) { var lasttime = player.element.currentTime; element.checkProgressTimeout = setInterval(function(){ var newtime = player.element.currentTime; - progress(); if (newtime == 0) { return; } - var progressed = newtime - lasttime; + var progress = newtime - lasttime; lasttime = newtime; - if (progressed == 0) { + if (progress == 0) { var msg = 'There should be playback but nothing was played'; var r = { type: 'playback', @@ -1188,8 +1209,8 @@ function mistPlay(streamName,options) { return; } player.cancelAskNextCombo(); - if (progressed < 1) { - var msg = 'It seems playback is lagging (progressed '+Math.round(progressed*100)/100+'/2s)' + if (progress < 4) { + var msg = 'It seems playback is lagging (progressed '+Math.round(progress*100)/100+'/8s)' player.addlog(msg); player.report({ type: 'playback', @@ -1197,7 +1218,7 @@ function mistPlay(streamName,options) { }); return; } - },2e3); + },8e3); } },true); element.addEventListener('pause',function(){ diff --git a/embed/mist.css b/embed/mist.css index 71fddc0c..7aa74c8e 100644 --- a/embed/mist.css +++ b/embed/mist.css @@ -350,6 +350,39 @@ transform: rotate(360deg); } } + +.countdown10 { + height: 1em; + width: 1em; + display: inline-block; + vertical-align: bottom; + border-radius: 50%; + background-image: linear-gradient(to right,black 50%,#ddd 0); + border: 1px solid black; + margin: 0 0.2em; + opacity: 0; + animation: appear 10s step-start 1; +} +.countdown10:before { + content: ''; + display: block; + margin-left: 50%; + height: 100%; + border-radius: 0 100% 100% 0 / 50%; + background-color: black; + transform-origin: 0 50%; + animation: rotate 5s linear 2, bg 10s step-end 1; +} +@keyframes rotate { + to { transform: rotate(.5turn); } +} +@keyframes bg { + 50% { background: #ddd; } +} +@keyframes appear { + to { opacity: 1; } +} + .video-js .vjs-big-play-button:before, .video-js .vjs-control:before, .video-js .vjs-modal-dialog, .vjs-modal-dialog .vjs-modal-dialog-content { position: absolute; top: 0; From dc6d0643bb0b3799155b9c72aa1988338c51c087 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Fri, 10 Feb 2017 12:30:12 +0100 Subject: [PATCH 08/11] embed: partial rewrite --- embed/core.js | 281 +++++++++++++++++++++++------------------------- embed/test.html | 34 ++++-- 2 files changed, 160 insertions(+), 155 deletions(-) diff --git a/embed/core.js b/embed/core.js index 565a99da..8e9c543b 100644 --- a/embed/core.js +++ b/embed/core.js @@ -213,6 +213,9 @@ MistPlayer.prototype.buildMistControls = function(){ play.setAttribute('data-state','paused'); play.onclick = function(){ if (ele.paused) { + if (options.live) { + me.load(); + } me.play(); } else { @@ -300,7 +303,7 @@ MistPlayer.prototype.buildMistControls = function(){ var perc = (ypos - pos0 * zoom) / sound.offsetHeight / zoom; perc = 1 - Math.min(1,Math.max(0,perc)); //linear range between 0 and 1 - perc = -1 * Math.pow((1-perc),2) + 1; //transform to quadratic range between 0 and 1 + perc = 1 - Math.pow((1-perc),1/2); //transform to quadratic range between 0 and 1 return perc; } @@ -515,9 +518,8 @@ MistPlayer.prototype.buildMistControls = function(){ play.setAttribute('data-state','paused'); }); ele.addEventListener('volumechange',function(){ - //-1 * Math.pow((1-sound.getPos(e.clientY)),2) + 1; //transform to quadratic range between 0 and 1 - var vol = 1 - Math.pow(1-ele.volume,0.5); - volume.style.height = vol*100+'%'; //transform back from quadratic + var vol = 1 - Math.pow(1-ele.volume,2); //transform back from quadratic + volume.style.height = vol*100+'%'; if (ele.volume == 0) { speaker.setAttribute('data-muted',''); } @@ -610,17 +612,32 @@ MistPlayer.prototype.askNextCombo = function(msg){ me.errorstate = true; me.addlog('Showing error window'); + //show the error var err = document.createElement('div'); var msgnode = document.createTextNode(msg ? msg : 'Player or stream error detected'); err.appendChild(msgnode); err.className = 'error'; - var button = document.createElement('button'); - var t = document.createTextNode('Try next source/player'); - button.appendChild(t); - err.appendChild(button); - button.onclick = function(){ - me.nextCombo(); + err.style.position = 'absolute'; + err.style.top = 0; + err.style.width = '100%'; + err.style['margin-left'] = 0; + this.target.appendChild(err); + this.element.style.opacity = '0.2'; + + //if there is a next source/player, show a button to activate it + var opts = this.mistplaySettings.options; + opts.startCombo = this.mistplaySettings.startCombo; + if (mistCheck(mistvideo[this.mistplaySettings.streamname],opts)) { + var button = document.createElement('button'); + var t = document.createTextNode('Try next source/player'); + button.appendChild(t); + err.appendChild(button); + button.onclick = function(){ + me.nextCombo(); + } } + + //show a button to reload with the current settings var button = document.createElement('button'); var i = document.createElement('div'); //a css countdown clock for 10sec i.className = 'countdown10'; @@ -631,13 +648,6 @@ MistPlayer.prototype.askNextCombo = function(msg){ button.onclick = function(){ me.reload(); } - err.style.position = 'absolute'; - err.style.top = 0; - err.style.width = '100%'; - err.style['margin-left'] = 0; - - this.target.appendChild(err); - this.element.style.opacity = '0.2'; //after 10 seconds, reload the player err.timeOut = setTimeout(function(){ @@ -721,6 +731,90 @@ MistPlayer.prototype.unload = function(){ this.target.innerHTML = ''; }; +function mistCheck(streaminfo,options,embedLog) { + if (typeof embedLog != 'function') { embedLog = function(){}; } + + embedLog('Checking available players..'); + + var source = false; + var mistPlayer = false; + + if (options.startCombo) { + options.startCombo.started = { + player: false, + source: false + }; + } + + function checkPlayer(p_shortname) { + if ((options.startCombo) && (!options.startCombo.started.player)) { + if (p_shortname != options.startCombo.player) { return false; } + else { + options.startCombo.started.player = true; + } + } + + embedLog('Checking '+mistplayers[p_shortname].name+' (priority: '+mistplayers[p_shortname].priority+') ..'); + + //loop over the available sources and check if this player can play it + var loop; + if (options.forceSource) { + loop = [streaminfo.source[options.forceSource]]; + } + else { + loop = streaminfo.source; + } + for (var s in loop) { + if ((options.startCombo) && (!options.startCombo.started.source)) { + if (s == options.startCombo.source) { + options.startCombo.started.source = true; + } + continue; + } + if ((options.forceType) && (loop[s].type != options.forceType)) { + continue; + } + + if (mistplayers[p_shortname].isMimeSupported(loop[s].type)) { + //this player supports this mime + if (mistplayers[p_shortname].isBrowserSupported(loop[s].type,loop[s],options,streaminfo,embedLog)) { + //this browser is supported + embedLog('Found a working combo: '+mistplayers[p_shortname].name+' with '+loop[s].type+' @ '+loop[s].url); + mistPlayer = p_shortname; + source = loop[s]; + source.index = s; + return p_shortname; + } + else { + embedLog('This browser does not support '+loop[s].type); + } + } + } + + return false; + } + + if (options.forcePlayer) { + checkPlayer(options.forcePlayer); + } + else { + //sort the players + var sorted = Object.keys(mistplayers); + sorted.sort(function(a,b){ + return mistplayers[a].priority - mistplayers[b].priority; + }); + for (var n in sorted) { + var p_shortname = sorted[n]; + if (checkPlayer(p_shortname)) { break; } + } + } + + return ((source && mistPlayer) ? { + source: source, + mistPlayer: mistPlayer + } : false); +} + ///////////////////////////////////////////////// // SELECT AND ADD A VIDEO PLAYER TO THE TARGET // ///////////////////////////////////////////////// @@ -728,13 +822,13 @@ MistPlayer.prototype.unload = function(){ function mistPlay(streamName,options) { var protoplay = new MistPlayer(); - protoplay.streamname = streamName; - function embedLog(msg) { + protoplay.streamname = streamName; + var embedLog = function(msg) { protoplay.sendEvent('log',msg,options.target); - } + }; function mistError(msg) { var info = {}; - if ((typeof mistvideo != 'undefined') && ('streamName' in mistvideo)) { info = mistvideo[streamName]; } + if ((typeof mistvideo != 'undefined') && (streamName in mistvideo)) { info = mistvideo[streamName]; } var displaymsg = msg; if ('on_error' in info) { displaymsg = info.on_error; } @@ -768,7 +862,11 @@ function mistPlay(streamName,options) { loop: false, //don't loop when the stream has finished poster: null, //don't show an image before the stream has started callback: false, //don't call a function when the player has finished building - streaminfo: false //don't use this streaminfo but collect it from the mistserverhost + streaminfo: false, //don't use this streaminfo but collect it from the mistserverhost + startCombo: false, + forceType: false, + forcePlayer: false, + forceSource: false }; for (var i in global) { options[i] = global[i]; @@ -799,7 +897,6 @@ function mistPlay(streamName,options) { } } - function onstreaminfo() { options.target.innerHTML = ''; options.target.removeAttribute('data-loading'); @@ -819,139 +916,34 @@ function mistPlay(streamName,options) { return; } - //sort the sources by priority and mime, but prefer HTTPS - streaminfo.source.sort(function(a,b){ - return (b.priority - a.priority) || a.type.localeCompare(b.type) || b.url.localeCompare(a.url); - }); - - var mistPlayer = false; - var source; - var forceType = false; if (('forceType' in options) && (options.forceType)) { embedLog('Forcing '+options.forceType); - forceType = options.forceType; } - var forceSource = false; if (('forceSource' in options) && (options.forceSource)) { - forceSource = options.forceSource; - forceType = streaminfo.source[forceSource].type; - embedLog('Forcing source '+options.forceSource+': '+forceType+' @ '+streaminfo.source[forceSource].url); + options.forceType = streaminfo.source[options.forceSource].type; + embedLog('Forcing source '+options.forceSource+': '+options.forceType+' @ '+streaminfo.source[options.forceSource].url); } - var forceSupportCheck = false; - if (('forceSupportCheck' in options) && (options.forceSupportCheck)) { - embedLog('Forcing a full support check'); - forceSupportCheck = true; - } - var forcePlayer = false; if (('forcePlayer' in options) && (options.forcePlayer)) { if (options.forcePlayer in mistplayers) { embedLog('Forcing '+mistplayers[options.forcePlayer].name); - forcePlayer = options.forcePlayer; } else { embedLog('The forced player ('+options.forcePlayer+') isn\'t known, ignoring. Possible values are: '+Object.keys(mistplayers).join(', ')); + options.forcePlayer = false; } } - var startCombo = false; - if ('startCombo' in options) { - startCombo = options.startCombo; - startCombo.started = { - player: false, - source: false - }; - embedLog('Selecting a new player/source combo, starting after '+mistplayers[startCombo.player].name+' with '+streaminfo.source[startCombo.source].type+' @ '+streaminfo.source[startCombo.source].url); + if (('startCombo' in options) && (options.startCombo)) { + embedLog('Selecting a new player/source combo, starting after '+mistplayers[options.startCombo.player].name+' with '+streaminfo.source[options.startCombo.source].type+' @ '+streaminfo.source[options.startCombo.source].url); } - embedLog('Checking available players..'); + //sort the sources by simultracks, priority and mime, but prefer HTTPS + streaminfo.source.sort(function(a,b){ + return (b.simul_tracks - a.simul_tracks) || (b.priority - a.priority) || a.type.localeCompare(b.type) || b.url.localeCompare(a.url); + }); - var source = false; - var mistPlayer = false; - - function checkPlayer(p_shortname) { - if ((startCombo) && (!startCombo.started.player)) { - if (p_shortname != startCombo.player) { return false; } - else { - startCombo.started.player = true; - } - } - - embedLog('Checking '+mistplayers[p_shortname].name+' (priority: '+mistplayers[p_shortname].priority+') ..'); - streaminfo.working[p_shortname] = []; - - if (forceType) { - if ((mistplayers[p_shortname].mimes.indexOf(forceType) > -1) && (checkMime(p_shortname,forceType))) { - return p_shortname; - } - } - else { - for (var m in mistplayers[p_shortname].mimes) { - if ((checkMime(p_shortname,mistplayers[p_shortname].mimes[m])) && (!forceSupportCheck)) { - return p_shortname; - } - } - } - return false; - } - function checkMime(p_shortname,mime) { - var loop; - if (forceSource) { - loop = [streaminfo.source[forceSource]]; - } - else { - loop = streaminfo.source; - } - var broadcast = false; - for (var s in loop) { - if (loop[s].type == mime) { - broadcast = true; - - if ((startCombo) && (!startCombo.started.source)) { - if (s == startCombo.source) { - startCombo.started.source = true; - } - continue; - } - - if (mistplayers[p_shortname].isBrowserSupported(mime,loop[s],options,streaminfo,embedLog)) { - embedLog('Found a working combo: '+mistplayers[p_shortname].name+' with '+mime+' @ '+loop[s].url); - streaminfo.working[p_shortname].push(mime); - if (!source) { - mistPlayer = p_shortname; - source = loop[s]; - source.index = s; - } - if (!forceSupportCheck) { - return source; - } - } - else { - embedLog('This browser does not support '+mime); - } - } - - } - if (!broadcast) { - embedLog('Mist doesn\'t broadcast '+mime); - } - - return false; - } - - streaminfo.working = {}; - if (forcePlayer) { - checkPlayer(forcePlayer); - } - else { - //sort the players - var sorted = Object.keys(mistplayers); - sorted.sort(function(a,b){ - return mistplayers[a].priority - mistplayers[b].priority; - }); - for (var n in sorted) { - var p_shortname = sorted[n]; - if (checkPlayer(p_shortname)) { break; } - } - } + var r = mistCheck(streaminfo,options,embedLog); + var mistPlayer = r.mistPlayer; + var source = r.source; options.target.innerHTML = ''; if (mistPlayer) { @@ -1086,7 +1078,7 @@ function mistPlay(streamName,options) { //build the player player.mistplaySettings = { streamname: streamName, - options: local, + options: options, startCombo: { player: mistPlayer, source: source.index @@ -1157,7 +1149,7 @@ function mistPlay(streamName,options) { player.askNextCombo('Playback has stalled'); player.report({ 'type': 'playback', - 'warn': 'Playback was stalled for > 10 sec' + 'warn': 'Playback was stalled for > 8 sec' }); },10e3); }; @@ -1187,6 +1179,7 @@ function mistPlay(streamName,options) { if (newtime == 0) { return; } var progress = newtime - lasttime; lasttime = newtime; + if (progress < 0) { return; } if (progress == 0) { var msg = 'There should be playback but nothing was played'; var r = { @@ -1249,8 +1242,8 @@ function mistPlay(streamName,options) { } else if (('source' in streaminfo) && (streaminfo.source.length)) { var str = 'Could not find a compatible player and protocol combination for this stream and browser. '; - if (forceType) { str += "\n"+'The mimetype '+forceType+' was enforced. '; } - if (forcePlayer) { str += "\n"+'The player '+mistplayers[forcePlayer].name+' was enforced. '; } + if (options.forceType) { str += "\n"+'The mimetype '+options.forceType+' was enforced. '; } + if (options.forcePlayer) { str += "\n"+'The player '+options.forcePlayer+' was enforced. '; } } else { var str = 'Stream not found.'; diff --git a/embed/test.html b/embed/test.html index 9d862b5e..3277ea6a 100644 --- a/embed/test.html +++ b/embed/test.html @@ -16,11 +16,11 @@ // global options can be set here var mistoptions = { //host: 'http://cat.mistserver.org:8080' - host: 'http://thulmk3:8080' + //host: 'http://thulmk3:8080' //host: 'https://cat.mistserver.org:4433' //host: 'http://localhost:8080' //host: 'http://live.us.picarto.tv:8080' - //host: 'balderlaptop:8080' + host: 'http://cattop:8080' }; </script> @@ -47,10 +47,16 @@ margin: 0; max-width: 100vw; max-height: 100vh; + background-color: #0f0f0f; + color: #aaa; } .mistvideo { margin: 1px; } + .log { + max-height: 15em; + overflow-y: auto; + } </style> @@ -69,23 +75,26 @@ div.appendChild(msg); div.style.color = 'red'; logele.appendChild(div); + logele.scrollTop = logele.scrollHeight; }); document.addEventListener('log',function(e){ + if (e.message.indexOf('Player event fired') > -1) { return; } + console.log('[log] '+e.message); return; var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message); var div = document.createElement('div'); div.appendChild(msg); logele.appendChild(div); + logele.scrollTop = logele.scrollHeight; }); //tryplayers = Object.keys(mistplayers); tryplayers = []; - //tryplayers.push('automatic'); + tryplayers.push('automatic'); //tryplayers.push('html5'); - tryplayers.push('dashjs'); //tryplayers.push('videojs'); - tryplayers.push('img'); + //tryplayers.push('img'); //tryplayers.push('dashjs'); //tryplayers.push('flash_strobe'); //tryplayers.push('silverlight'); @@ -97,8 +106,7 @@ //streams.push('vids+mist.mp4'); //streams.push('vids+hahalol.mp3'); //streams.push('lama'); - //streams.push('bunny'); - streams.push('golive+SockyChannel'); + streams.push('bunny'); for (var j in streams) { for (var i in tryplayers) { @@ -116,10 +124,11 @@ //forceType: 'html5/audio/mp3', //forceType: 'html5/application/vnd.apple.mpegurl', //forceType: 'dash/video/mp4', - //forceSource: 3, + //forceType: 'rtsp', + //forceSource: 6, loop: true, //controls: 'stock' - streaminfo: { + /*streaminfo: { source: [{ type: 'html5/video/mp4', url: 'http://localhost:8080/bunny.mp4' @@ -140,15 +149,18 @@ height: 404, width: 720, type: 'vod' - }, + },*/ callback: function(player) { var button = document.createElement('button'); button.innerHTML = 'askNextCombo();'; button.onclick = function(){ player.askNextCombo('Button was clicked'); - d.removeChild(this); + //d.removeChild(this); }; d.append(button); + var i = document.createElement('div'); + i.className = 'countdown10'; + button.appendChild(i); } }); From 899ee1088af641d5681dd55860e24a8c929c8329 Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Mon, 27 Feb 2017 14:27:33 +0100 Subject: [PATCH 09/11] various embed tweaks --- .gitignore | 1 + embed/core.js | 276 +++++++++++++++++++++++--------------- embed/mist.css | 39 +++--- embed/test.html | 235 -------------------------------- embed/wrappers/html5.js | 58 ++++++-- embed/wrappers/videojs.js | 17 ++- 6 files changed, 256 insertions(+), 370 deletions(-) delete mode 100644 embed/test.html diff --git a/.gitignore b/.gitignore index bfe2791c..058288f2 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ rules.ninja .ninja_log .ninja_deps aes_ctr128 +/embed/testing diff --git a/embed/core.js b/embed/core.js index 8e9c543b..8930e3f0 100644 --- a/embed/core.js +++ b/embed/core.js @@ -27,10 +27,10 @@ MistPlayer.prototype.sendEvent = function(type,message,target) { return true; } MistPlayer.prototype.addlog = function(msg) { - this.sendEvent('log',msg,this.target); + this.sendEvent('log',msg,this.element); } MistPlayer.prototype.adderror = function(msg) { - this.sendEvent('error',msg,this.target); + this.sendEvent('error',msg,this.element); } MistPlayer.prototype.build = function () { this.addlog('Error in player implementation'); @@ -40,6 +40,32 @@ MistPlayer.prototype.build = function () { err.className = 'error'; return err; } +MistPlayer.prototype.timer = { + timers: {}, + add: function(callback,delay){ + var me = this; + var i = setTimeout(function(){ + delete me.timers[i]; + callback(); + },delay); + this.timers[i] = { + delay: delay, + callback: callback + }; + return i; + }, + remove: function(i){ + clearTimeout(i); + delete this.timers[i]; + }, + clear: function(){ + for (var i in this.timers) { + clearTimeout(i); + } + this.timers = {}; + } +}; + //creates the player element, including custom functions MistPlayer.prototype.getElement = function(tag){ var ele = document.createElement(tag); @@ -164,14 +190,15 @@ MistPlayer.prototype.buildMistControls = function(){ str.push(('0'+secs).slice(-2)); return str.join(':'); } + var timestampValue, bar; function whilePlaying() { timestampValue.nodeValue = formatTime(ele.currentTime); bar.style.width = ((ele.currentTime-ele.startTime)/ele.duration*100)+'%'; - setTimeout(function(){ + me.timer.add(function(){ if (!ele.paused) { whilePlaying(); } - },0.1e3); + },0.5e3); }; function whileLivePlaying(track) { @@ -180,11 +207,11 @@ MistPlayer.prototype.buildMistControls = function(){ timestampValue.nodeValue = formatTime((playtime + track.lastms)/1e3); - setTimeout(function(){ + me.timer.add(function(){ if (!ele.paused) { whileLivePlaying(track); } - },0.1e3); + },0.5e3); }; @@ -216,7 +243,9 @@ MistPlayer.prototype.buildMistControls = function(){ if (options.live) { me.load(); } - me.play(); + else { + me.play(); + } } else { me.pause(); @@ -258,7 +287,7 @@ MistPlayer.prototype.buildMistControls = function(){ bar.style.left = (ele.startTime/ele.duration*100)+'%'; }; progress.ondragstart = function() { return false; }; - var bar = document.createElement('div'); + bar = document.createElement('div'); progress.appendChild(bar); bar.className = 'bar'; var buffers = []; @@ -271,6 +300,11 @@ MistPlayer.prototype.buildMistControls = function(){ ele.addEventListener('seeking',function(){ me.target.setAttribute('data-loading',''); }); + ele.addEventListener('seeked',function(){ + me.target.removeAttribute('data-loading'); + bar.style.left = (ele.currentTime/ele.duration*100)+'%'; + //TODO reset lasttime + }); ele.addEventListener('canplay',function(){ me.target.removeAttribute('data-loading'); }); @@ -285,7 +319,7 @@ MistPlayer.prototype.buildMistControls = function(){ var timestamp = document.createElement('div'); controls.appendChild(timestamp); timestamp.className = 'button timestamp'; - var timestampValue = document.createTextNode('-:--'); + timestampValue = document.createTextNode('-:--'); timestamp.title = 'Time'; timestamp.appendChild(timestampValue); @@ -417,6 +451,9 @@ MistPlayer.prototype.buildMistControls = function(){ name = tracks[i][j].lang; o.setAttribute('data-lang',tracks[i][j].lang); } + else if ('desc' in tracks[i][j]) { + name = tracks[i][j].desc; + } else { name = 'Track '+(Number(j)+1); } @@ -516,6 +553,9 @@ MistPlayer.prototype.buildMistControls = function(){ }); ele.addEventListener('ended',function(){ play.setAttribute('data-state','paused'); + if (options.live) { + me.load(); + } }); ele.addEventListener('volumechange',function(){ var vol = 1 - Math.pow(1-ele.volume,2); //transform back from quadratic @@ -610,7 +650,11 @@ MistPlayer.prototype.askNextCombo = function(msg){ var me = this; if (me.errorstate) { return; } me.errorstate = true; - me.addlog('Showing error window'); + me.report({ + type: 'playback', + warn: 'Showing error window', + msg: msg + }); //show the error var err = document.createElement('div'); @@ -618,7 +662,6 @@ MistPlayer.prototype.askNextCombo = function(msg){ err.appendChild(msgnode); err.className = 'error'; err.style.position = 'absolute'; - err.style.top = 0; err.style.width = '100%'; err.style['margin-left'] = 0; this.target.appendChild(err); @@ -626,7 +669,6 @@ MistPlayer.prototype.askNextCombo = function(msg){ //if there is a next source/player, show a button to activate it var opts = this.mistplaySettings.options; - opts.startCombo = this.mistplaySettings.startCombo; if (mistCheck(mistvideo[this.mistplaySettings.streamname],opts)) { var button = document.createElement('button'); var t = document.createTextNode('Try next source/player'); @@ -640,7 +682,7 @@ MistPlayer.prototype.askNextCombo = function(msg){ //show a button to reload with the current settings var button = document.createElement('button'); var i = document.createElement('div'); //a css countdown clock for 10sec - i.className = 'countdown10'; + i.className = 'countdown'; button.appendChild(i); var t = document.createTextNode('Reload this player'); button.appendChild(t); @@ -649,10 +691,14 @@ MistPlayer.prototype.askNextCombo = function(msg){ me.reload(); } - //after 10 seconds, reload the player - err.timeOut = setTimeout(function(){ + //after 20 seconds, reload the player + err.timeOut = me.timer.add(function(){ + me.report({ + type: 'playback', + warn: 'Automatically reloaded the current player after playback error' + }); button.click(); - },10e3); + },20e3); }; MistPlayer.prototype.cancelAskNextCombo = function(){ @@ -663,13 +709,17 @@ MistPlayer.prototype.cancelAskNextCombo = function(){ var err = this.target.querySelector('.error'); if (err) { this.target.removeChild(err); - if (err.timeOut) { clearTimeout(err.timeOut); } + if (err.timeOut) { this.timer.remove(err.timeOut); } } } }; MistPlayer.prototype.reload = function(){ this.unload(); mistPlay(this.mistplaySettings.streamname,this.mistplaySettings.options); + this.report({ + type: 'init', + info: 'Reloading player' + }); }; MistPlayer.prototype.nextCombo = function(){ this.unload(); @@ -680,8 +730,6 @@ MistPlayer.prototype.nextCombo = function(){ ///send information back to mistserver ///\param msg object containing the information to report MistPlayer.prototype.report = function(msg) { - return false; ///\todo Remove this when the backend reporting function has been coded - ///send a http post request ///\param url (string) url to send to @@ -717,6 +765,10 @@ MistPlayer.prototype.report = function(msg) { msg.userinfo.time = Math.round(((new Date) - this.options.initTime)/1e3); //seconds since the info js was loaded } + this.sendEvent('report',JSON.stringify(msg),this.element); + + return false; ///\todo Remove this when the backend reporting function has been coded + try { httpPost(this.options.host+'/report',{ report: JSON.stringify(msg) @@ -725,9 +777,10 @@ MistPlayer.prototype.report = function(msg) { catch (e) { } } MistPlayer.prototype.unload = function(){ + this.addlog('Unloading..'); if (('pause' in this) && (this.pause)) { this.pause(); } - if ('updateSrc' in this) { this.updateSrc(''); } - //delete this.element; + if ('updateSrc' in this) { this.updateSrc(''); this.load(); } + this.timer.clear(); this.target.innerHTML = ''; }; @@ -786,7 +839,7 @@ function mistCheck(streaminfo,options,embedLog) { return p_shortname; } else { - embedLog('This browser does not support '+loop[s].type); + embedLog('This browser does not support '+loop[s].type+' via '+loop[s].url); } } } @@ -862,11 +915,11 @@ function mistPlay(streamName,options) { loop: false, //don't loop when the stream has finished poster: null, //don't show an image before the stream has started callback: false, //don't call a function when the player has finished building - streaminfo: false, //don't use this streaminfo but collect it from the mistserverhost - startCombo: false, - forceType: false, - forcePlayer: false, - forceSource: false + streaminfo: false, //don't use this streaminfo but collect it from the mistserverhost + startCombo: false, //start looking for a player/source match at the start + forceType: false, //don't force a mimetype + forcePlayer: false, //don't force a player + forceSource: false //don't force a source }; for (var i in global) { options[i] = global[i]; @@ -879,9 +932,19 @@ function mistPlay(streamName,options) { mistError('MistServer host undefined.'); return; } + if (!options.target) { + mistError('Target container undefined'); + return; + } options.target.setAttribute('data-loading',''); + var classes = options.target.className.split(' '); + if (classes.indexOf('mistvideo') == -1) { + classes.push('mistvideo'); + options.target.className = classes.join(' '); + } + //check if the css is loaded if (!document.getElementById('mist_player_css')) { var css = document.createElement('link'); @@ -1033,20 +1096,26 @@ function mistPlay(streamName,options) { var skip = false; switch (t.type) { case 'video': - t.desc = ['['+t.codec+']',t.width+'x'+t.height,Math.round(t.bps/1024)+'kbps',t.fpks/1e3+'fps',t.lang]; + t.desc = [t.width+'x'+t.height,/*Math.round(t.bps/1024)+'kbps',*/t.fpks/1e3+'fps',t.codec]; + if (t.lang) { + t.desc.unshift(t.lang); + } break; case 'audio': - t.desc = ['['+t.codec+']',t.channels+' channels',Math.round(t.bps/1024)+'kbps',t.rate+'Hz',t.lang]; + t.desc = [(t.channels == 2 ? 'Stereo' : (t.channels == 1 ? 'Mono' : t.channels+' channels')),/*Math.round(t.bps/1024)+'kbps',*/Math.round(t.rate/1000)+'kHz',t.codec]; + if (t.lang) { + t.desc.unshift(t.lang); + } break; case 'subtitle': - t.desc = ['['+t.codec+']',t.lang]; + t.desc = [t.lang,t.codec]; break; default: skip = true; break; } if (skip) { continue; } - t.desc = t.desc.join(', '); + t.desc = t.desc.join(' '); tracks[t.type].push(t); } player.tracks = tracks; @@ -1115,8 +1184,6 @@ function mistPlay(streamName,options) { } //monitor for errors - element.checkStalledTimeout = false; - element.checkProgressTimeout = false; element.sendPingTimeout = setInterval(function(){ if (player.paused) { return; } player.report({ @@ -1128,8 +1195,7 @@ function mistPlay(streamName,options) { player.askNextCombo('The player has thrown an error'); var r = { type: 'playback', - error: 'The player has thrown an error', - origin: e.target.outerHTML.slice(0,e.target.outerHTML.indexOf('>')+1), + error: 'The player has thrown an error' }; if ('readyState' in player.element) { r.readyState = player.element.readyState; @@ -1137,90 +1203,90 @@ function mistPlay(streamName,options) { if ('networkState' in player.element) { r.networkState = player.element.networkState; } - if (('error' in player.element) && ('code' in player.element.error)) { + if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) { r.code = player.element.error.code; } player.report(r); }); + element.checkStalledTimeout = false; var stalled = function(e){ if (element.checkStalledTimeout) { return; } - element.checkStalledTimeout = setTimeout(function(){ - if (player.paused) { return; } + var curpos = player.element.currentTime; + if (curpos == 0) { return; } + element.checkStalledTimeout = player.timer.add(function(){ + if ((player.paused) || (curpos != player.element.currentTime)) { return; } player.askNextCombo('Playback has stalled'); player.report({ 'type': 'playback', - 'warn': 'Playback was stalled for > 8 sec' + 'warn': 'Playback was stalled for > 30 sec' }); - },10e3); + },30e3); }; element.addEventListener('stalled',stalled,true); element.addEventListener('waiting',stalled,true); - var progress = function(e){ - if (element.checkStalledTimeout) { - clearTimeout(element.checkStalledTimeout); - element.checkStalledTimeout = false; - player.cancelAskNextCombo(); - } - if (element.checkStalledTimeout) { - clearTimeout(element.checkStalledTimeout); - element.checkStalledTimeout = false; - player.cancelAskNextCombo(); - } - }; - element.addEventListener('progress',progress,true); - element.addEventListener('playing',progress,true); - element.addEventListener('play',function(){ - player.paused = false; - if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) { - //check if the progress made is equal to the time spent - var lasttime = player.element.currentTime; - element.checkProgressTimeout = setInterval(function(){ - var newtime = player.element.currentTime; - if (newtime == 0) { return; } - var progress = newtime - lasttime; - lasttime = newtime; - if (progress < 0) { return; } - if (progress == 0) { - var msg = 'There should be playback but nothing was played'; - var r = { - type: 'playback', - warn: msg - }; - player.addlog(msg); - if ('readyState' in player.element) { - r.readyState = player.element.readyState; - } - if ('networkState' in player.element) { - r.networkState = player.element.networkState; - } - if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) { - r.code = player.element.error.code; - } - player.report(r); - player.askNextCombo('No playback'); - if ('load' in player.element) { player.element.load(); } - return; - } + + if (playerOpts.live) { + element.checkProgressTimeout = false; + var progress = function(e){ + if (element.checkStalledTimeout) { + player.timer.remove(element.checkStalledTimeout); + element.checkStalledTimeout = false; player.cancelAskNextCombo(); - if (progress < 4) { - var msg = 'It seems playback is lagging (progressed '+Math.round(progress*100)/100+'/8s)' - player.addlog(msg); - player.report({ - type: 'playback', - warn: msg - }); - return; - } - },8e3); - } - },true); - element.addEventListener('pause',function(){ - player.paused = true; - if (element.checkProgressTimeout) { - clearInterval(element.checkProgressTimeout); - element.checkProgressTimeout = false; - } - },true); + } + }; + //element.addEventListener('progress',progress,true); //sometimes, there is progress but no playback + element.addEventListener('playing',progress,true); + element.addEventListener('play',function(){ + player.paused = false; + if ((!element.checkProgressTimeout) && (player.element) && ('currentTime' in player.element)) { + //check if the progress made is equal to the time spent + var lasttime = player.element.currentTime; + element.checkProgressTimeout = setInterval(function(){ + var newtime = player.element.currentTime; + var progress = newtime - lasttime; + lasttime = newtime; + if (progress < 0) { return; } //its probably a looping VOD or we've just seeked + if (progress == 0) { + var msg = 'There should be playback but nothing was played'; + var r = { + type: 'playback', + warn: msg + }; + player.addlog(msg); + if ('readyState' in player.element) { + r.readyState = player.element.readyState; + } + if ('networkState' in player.element) { + r.networkState = player.element.networkState; + } + if (('error' in player.element) && (player.element.error) && ('code' in player.element.error)) { + r.code = player.element.error.code; + } + player.report(r); + player.askNextCombo('No playback'); + return; + } + player.cancelAskNextCombo(); + if (progress < 20) { + var msg = 'It seems playback is lagging (progressed '+Math.round(progress*100)/100+'/30s)' + player.addlog(msg); + player.report({ + type: 'playback', + warn: msg + }); + return; + } + },30e3); + } + },true); + element.addEventListener('pause',function(){ + player.paused = true; + if (element.checkProgressTimeout) { + clearInterval(element.checkProgressTimeout); + element.checkProgressTimeout = false; + } + },true); + } if (player.resize) { //monitor for resizes and fire if needed diff --git a/embed/mist.css b/embed/mist.css index 7aa74c8e..a8793eaf 100644 --- a/embed/mist.css +++ b/embed/mist.css @@ -1,16 +1,15 @@ .mistvideo { background: black center none no-repeat; - /*LTS - background-size: auto 30%; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBpZD0ic3ZnNTI4MCIgaGVpZ2h0PSIxNTAiIHdpZHRoPSIyMDIuNTIwMTciIHZlcnNpb249IjEuMSI+PGRlZnMgaWQ9ImRlZnM1MjgyIiAvPjxtZXRhZGF0YSBpZD0ibWV0YWRhdGE1Mjg1Ij48cmRmOlJERj48Y2M6V29yayByZGY6YWJvdXQ9IiI+PGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+PGRjOnR5cGUgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48ZGM6dGl0bGU+PC9kYzp0aXRsZT48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQ4LjcyNjIzNCwtNDQ5Ljc1MjA0KSI+PHBhdGggc3R5bGU9ImZpbGw6IzhjYjNjZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgaWQ9InBhdGg0NiIgZD0ibSA2OS4wODIxNTMsNTM0LjI1MzE0IDgxLjAyODYzNyw0My4yNjQ4OCA4MS4wMjYwNSwtNDMuMjY0ODggLTMwLjUwNzA3LC02OS4wMDg5NCAtMTAxLjAzOTA2OCwwIC0zMC41MDg1NDksNjkuMDA4OTQgMCwwIHogbSA4MS4wMjg2MzcsNTAuMTE3MzIgLTg4LjgyNjQ4NiwtNDcuNDMwODggMzQuMzY5NDE4LC03Ny43NDA3OSAxMDguOTEzNTY4LDAgMzQuMzY5MDUsNzcuNzQwNzkgLTg4LjgyNTU1LDQ3LjQzMDg4IDAsMCIgLz48cGF0aCBzdHlsZT0iZmlsbDojYjVkM2UyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDQ4IiBkPSJtIDIzNC44NzM1Miw1MzguMDM5NTkgLTE2Ni43NTE1MTksMCAwLC02LjA0NTQxIDE2Ni43NTE1MTksMCAwLDYuMDQ1NDEiIC8+PHBhdGggc3R5bGU9ImZpbGw6IzhjYjNjZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgaWQ9InBhdGg1MCIgZD0ibSA4My40ODcwMDksNTM1LjUyMDgyIGMgMCw5LjU5OTY4IC03Ljc4MTA4LDE3LjM4MDc1IC0xNy4zNzk2NTEsMTcuMzgwNzUgLTkuNTk5MTIyLDAgLTE3LjM4MTEyNCwtNy43ODEwNiAtMTcuMzgxMTI0LC0xNy4zODA3NSAwLC05LjU5Nzg0IDcuNzgyMDAyLC0xNy4zODA3NiAxNy4zODExMjQsLTE3LjM4MDc2IDkuNTk4NTcxLDAgMTcuMzc5NjUxLDcuNzgyOTIgMTcuMzc5NjUxLDE3LjM4MDc2IiAvPjxwYXRoIHN0eWxlPSJmaWxsOiM4Y2IzY2Y7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIGlkPSJwYXRoNTIiIGQ9Im0gMjUxLjI0NjQsNTM1LjUyMDgyIGMgMCw5LjU5OTY4IC03Ljc4MTA3LDE3LjM4MDc1IC0xNy4zODA3NCwxNy4zODA3NSAtOS41OTk2OCwwIC0xNy4zNzg5MiwtNy43ODEwNiAtMTcuMzc4OTIsLTE3LjM4MDc1IDAsLTkuNTk3ODQgNy43NzkyNCwtMTcuMzgwNzYgMTcuMzc4OTIsLTE3LjM4MDc2IDkuNTk5NjcsMCAxNy4zODA3NCw3Ljc4MjkyIDE3LjM4MDc0LDE3LjM4MDc2IiAvPjxwYXRoIHN0eWxlPSJmaWxsOiNiNWQzZTI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIGlkPSJwYXRoNTQiIGQ9Im0gMTU1Ljc4NzY4LDUzNy4xODI4IC01LjA1MjI4LC0zLjMyMjEyIDQ5LjM3MTA4LC03NS4wNjM1NiA1LjA1MDQzLDMuMzIyMTEgLTQ5LjM2OTIzLDc1LjA2MzU3IDAsMCIgLz48cGF0aCBzdHlsZT0iZmlsbDojYjVkM2UyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDU2IiBkPSJtIDE0OC43ODYsNTM3Ljc4MTYyIC01Mi44OTY5NzcsLTc0LjA1NTY5IDQuOTE5NDE3LC0zLjUxNTU4IDUyLjg5NTMzLDc0LjA1NzU0IC00LjkxNzc3LDMuNTEzNzMgMCwwIiAvPjxwYXRoIHN0eWxlPSJmaWxsOiM4Y2IzY2Y7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIGlkPSJwYXRoNTgiIGQ9Im0gMjE0LjU5NjI4LDQ2Mi41OTgyOSBjIDAsNy4wOTc1IC01Ljc1MjQyLDEyLjg0ODEgLTEyLjg0NjI0LDEyLjg0ODEgLTcuMDk1NjUsMCAtMTIuODQ2MjUsLTUuNzUwNiAtMTIuODQ2MjUsLTEyLjg0ODEgMCwtNy4wOTM4MSA1Ljc1MDYsLTEyLjg0NjI1IDEyLjg0NjI1LC0xMi44NDYyNSA3LjA5MzgyLDAgMTIuODQ2MjQsNS43NTI0NCAxMi44NDYyNCwxMi44NDYyNSIgLz48cGF0aCBzdHlsZT0iZmlsbDojOGNiM2NmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDYwIiBkPSJtIDExMS44MjQ4NCw0NjIuNTk4MjkgYyAwLDcuMDk3NSAtNS43NTEzMywxMi44NDgxIC0xMi44NDU4NzMsMTIuODQ4MSAtNy4wOTUyODUsMCAtMTIuODQ3NTMyLC01Ljc1MDYgLTEyLjg0NzUzMiwtMTIuODQ4MSAwLC03LjA5MzgxIDUuNzUyMjQ3LC0xMi44NDYyNSAxMi44NDc1MzIsLTEyLjg0NjI1IDcuMDk0NTQzLDAgMTIuODQ1ODczLDUuNzUyNDQgMTIuODQ1ODczLDEyLjg0NjI1IiAvPjxwYXRoIHN0eWxlPSJmaWxsOiM4Y2IzY2Y7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIGlkPSJwYXRoNjIiIGQ9Im0gMTY4Ljg3NzE0LDQ4Ny41MzUzNSBjIDAsOS4zOTE0NSAtNy42MTE1NywxNy4wMDQ4NyAtMTcuMDAxMTksMTcuMDA0ODcgLTkuMzg5NjMsMCAtMTcuMDAzMDQsLTcuNjEzNDIgLTE3LjAwMzA0LC0xNy4wMDQ4NyAwLC05LjM4OTYyIDcuNjEzNDEsLTE3LjAwMTIgMTcuMDAzMDQsLTE3LjAwMTIgOS4zODk2MiwwIDE3LjAwMTE5LDcuNjExNTggMTcuMDAxMTksMTcuMDAxMiIgLz48cGF0aCBzdHlsZT0iZmlsbDojYjVkM2UyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDY0IiBkPSJtIDE2NS44NTUzNiw1MzYuNDAxNTYgYyAwLDcuNzIzOTYgLTYuMjU5MTMsMTMuOTgxMjYgLTEzLjk3OTQxLDEzLjk4MTI2IC03LjcyMDI5LDAgLTEzLjk4MTI2LC02LjI1NzMgLTEzLjk4MTI2LC0xMy45ODEyNiAwLC03LjcyMDI5IDYuMjYwOTcsLTEzLjk3OTQxIDEzLjk4MTI2LC0xMy45Nzk0MSA3LjcyMDI4LDAgMTMuOTc5NDEsNi4yNTkxMiAxMy45Nzk0MSwxMy45Nzk0MSIgLz48cGF0aCBzdHlsZT0iZmlsbDojOGNiM2NmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDY2IiBkPSJtIDY4LjMzNTczNiw1MzcuNDA1NzQgLTIuOTQ1ODY2LC01LjI4MDc0IDgzLjg3ODg4LC00Ny45ODU0NyA0LjQ1NzEyLDYuNzkxNjIgLTg1LjM5MDEzNCw0Ni40NzQ1OSAwLDAiIC8+PHBhdGggc3R5bGU9ImZpbGw6IzhjYjNjZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgaWQ9InBhdGg2OCIgZD0ibSAxNDkuNDY5NTgsNDkwLjY1NDc3IC01Mi4xNDE3MjQsLTI1LjMxNDc2IDIuNTQ1NjY5LC01LjQ4MTU5IDUzLjY1MzM1NSwyMy44MDIwNCAtMy4zMDE4NSw2LjIzODg3IC0wLjc1NTQ1LDAuNzU1NDQiIC8+PHBhdGggc3R5bGU9ImZpbGw6IzhjYjNjZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgaWQ9InBhdGg3MCIgZD0ibSAxNDkuNDE0Myw0ODQuMDYzOTcgNTEuMDA3MjYsLTIzLjgwMjAzIDIuNjU1MSw1LjQyOTk5IC00OS40OTYzNSwyNS4zMTQ3NyIgLz48cGF0aCBzdHlsZT0iZmlsbDojOGNiM2NmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDcyIiBkPSJtIDIzMy4wOTM2Miw1MzguNTA3NTggLTgzLjg4MDE2LC00Ny45ODU0NyA0LjU2NzY4LC02LjcyNzE0IDgyLjM2NzQzLDQ5LjQ5NjM3IC0zLjA1NDk1LDUuMjE2MjQgMCwwIiAvPjxwYXRoIHN0eWxlPSJmaWxsOiM4Y2IzY2Y7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIGlkPSJwYXRoNzQiIGQ9Im0gMTU0LjUyMDAxLDU4MC40ODQ1MiAtNi44MDA4NCwwIC0yLjI2NjM0LC05My43MDI3OCAxMS4zMzUzNiwwIC0yLjI2ODE4LDkzLjcwMjc4IDAsMCIgLz48cGF0aCBzdHlsZT0iZmlsbDojOGNiM2NmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0icGF0aDc2IiBkPSJtIDE2OC4xMjE2OSw1ODIuMzczMTMgYyAwLDkuNTk5NjggLTcuNzgxMDcsMTcuMzc4OTEgLTE3LjM4MDc1LDE3LjM3ODkxIC05LjU5Nzg0LDAgLTE3LjM3ODkxLC03Ljc3OTIzIC0xNy4zNzg5MSwtMTcuMzc4OTEgMCwtOS41OTk2OCA3Ljc4MTA3LC0xNy4zODA3NiAxNy4zNzg5MSwtMTcuMzgwNzYgOS41OTk2OCwwIDE3LjM4MDc1LDcuNzgxMDggMTcuMzgwNzUsMTcuMzgwNzYiIC8+PC9nPjwvc3ZnPg=='); - LTS*/ display: inline-block; color: white; font-family: sans-serif; text-align: center; position: relative; text-shadow: 0 0 1px black, 0 0 1px black; + max-width: 100%; + max-height: 100%; + overflow: hidden; } .mistvideo[data-loading] { background-image: none; @@ -36,14 +35,23 @@ z-index: 5; } .mistvideo .error { - margin: 225px 20px 20px; - min-width: 300px; + margin: 2em 0.5em; z-index: 69; + bottom: 40%; } .mistvideo .error button { + background: black; + color: rgba(255,255,255,0.8); + border: 1px solid rgba(255,255,255,0.2); + font-family: inherit; + padding: 0.5em; margin: 5px auto; display: block; } +.mistvideo .error button:hover { + color: white; + border-color: white; +} .mistvideo .vjs-error-display:before { content: '' !important; } @@ -121,7 +129,8 @@ font-size: 8px; } .mistplayer .controls .button.play { - height: 45px; + height: 30px; + width: 30px; margin-left: 15px; } .mistplayer .controls.minimal .button.play, @@ -145,7 +154,7 @@ .mistplayer .controls .progress_container { flex-grow: 1; position: relative; - margin: 15px; + margin: 5px; } .mistplayer .controls.smaller .progress_container { margin: 5px; @@ -351,33 +360,33 @@ } } -.countdown10 { +.countdown { height: 1em; width: 1em; display: inline-block; vertical-align: bottom; border-radius: 50%; - background-image: linear-gradient(to right,black 50%,#ddd 0); + background-image: linear-gradient(to right,#bbb 50%,#333 0); border: 1px solid black; margin: 0 0.2em; opacity: 0; - animation: appear 10s step-start 1; + animation: appear 20s step-start 1; } -.countdown10:before { +.countdown:before { content: ''; display: block; margin-left: 50%; height: 100%; border-radius: 0 100% 100% 0 / 50%; - background-color: black; + background-color: #bbb; transform-origin: 0 50%; - animation: rotate 5s linear 2, bg 10s step-end 1; + animation: rotate 10s linear 2, bg 20s step-end 1; } @keyframes rotate { to { transform: rotate(.5turn); } } @keyframes bg { - 50% { background: #ddd; } + 50% { background: #333; } } @keyframes appear { to { opacity: 1; } diff --git a/embed/test.html b/embed/test.html deleted file mode 100644 index 3277ea6a..00000000 --- a/embed/test.html +++ /dev/null @@ -1,235 +0,0 @@ -<html> - <head> - <title>Embed test</title> - - <!-- - include script for paid players - - jwplayer - - theoplayer - --> - - <!--<script type='text/javascript' src='//cdn.theoplayer.com/latest/41718edc-cc2d-40d0-83d4-67c50c60f68f/theoplayer.loader.js'></script>--> - <!--<script src=players/jwplayer.js></script> - <script>jwplayer.key="2z0zTRsxD2HkL6m/LgDqvtUy2EThVn+gk1gN1Q==";</script>--> - - <script> - // global options can be set here - var mistoptions = { - //host: 'http://cat.mistserver.org:8080' - //host: 'http://thulmk3:8080' - //host: 'https://cat.mistserver.org:4433' - //host: 'http://localhost:8080' - //host: 'http://live.us.picarto.tv:8080' - host: 'http://cattop:8080' - }; - </script> - - <script src=core.js></script> - <!--<script src=wrappers/theoplayer.js></script>--> - <!--<script src=wrappers/jwplayer.js></script>--> - <script src=wrappers/html5.js></script> - <script src=wrappers/videojs.js></script> - <script src=wrappers/dashjs.js></script> - <script src=wrappers/flash_strobe.js></script> - <script src=wrappers/img.js></script> - <script src=wrappers/silverlight.js></script> - <script src=wrappers/polytrope.js></script> - - <script src=players/dash.js></script> - <script src=players/videojs.js></script> - - <link rel=stylesheet href=mist.css id=mist_player_css> - <style> - /* the website can override the css at will */ - - body { - padding: 0; - margin: 0; - max-width: 100vw; - max-height: 100vh; - background-color: #0f0f0f; - color: #aaa; - } - .mistvideo { - margin: 1px; - } - .log { - max-height: 15em; - overflow-y: auto; - } - - </style> - - - - <script> - - - function mistinit(){ - var logele = document.querySelector('.log'); - var contele = document.querySelector('.cont'); - document.addEventListener('error',function(e){ - console.log('[Error] '+e.message,e.target); - var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message+' from '+e.target.outerHTML.slice(0,e.target.outerHTML.indexOf('>')+1)); - var div = document.createElement('div'); - div.appendChild(msg); - div.style.color = 'red'; - logele.appendChild(div); - logele.scrollTop = logele.scrollHeight; - }); - document.addEventListener('log',function(e){ - if (e.message.indexOf('Player event fired') > -1) { return; } - - console.log('[log] '+e.message); - return; - var msg = document.createTextNode('['+(new Date()).toTimeString().split(' ')[0]+'] '+e.message); - var div = document.createElement('div'); - div.appendChild(msg); - logele.appendChild(div); - logele.scrollTop = logele.scrollHeight; - }); - - //tryplayers = Object.keys(mistplayers); - tryplayers = []; - tryplayers.push('automatic'); - //tryplayers.push('html5'); - //tryplayers.push('videojs'); - //tryplayers.push('img'); - //tryplayers.push('dashjs'); - //tryplayers.push('flash_strobe'); - //tryplayers.push('silverlight'); - streams = []; - //streams.push('live'); - //streams.push('golive+emitan'); - //streams.push('subtel'); - //streams.push('ogg'); - //streams.push('vids+mist.mp4'); - //streams.push('vids+hahalol.mp3'); - //streams.push('lama'); - streams.push('bunny'); - - for (var j in streams) { - for (var i in tryplayers) { - var d = document.createElement('div'); - var c = document.createElement('div'); - c.className = 'mistvideo'; - c.title = tryplayers[i]; - d.appendChild(c); - contele.appendChild(d); - var p = mistPlay(streams[j],{ - target: c, - maxwidth: 800, - forcePlayer: tryplayers[i], - //forceType: 'html5/video/mp4', - //forceType: 'html5/audio/mp3', - //forceType: 'html5/application/vnd.apple.mpegurl', - //forceType: 'dash/video/mp4', - //forceType: 'rtsp', - //forceSource: 6, - loop: true, - //controls: 'stock' - /*streaminfo: { - source: [{ - type: 'html5/video/mp4', - url: 'http://localhost:8080/bunny.mp4' - },{ - type: 'html5/application/vnd.apple.mpegurl', - url: 'http://localhost:8080/hls/bunny/index.m3u8' - },{ - type: 'dash/video/mp4', - url: 'http://localhost:8080/dash/bunny/index.mpd' - },{ - player_url: '/flashplayer.swf', - type: 'flash/10', - url: 'rtmp://localhost:1935/play/bunny' - }], - meta: { - tracks: [] - }, - height: 404, - width: 720, - type: 'vod' - },*/ - callback: function(player) { - var button = document.createElement('button'); - button.innerHTML = 'askNextCombo();'; - button.onclick = function(){ - player.askNextCombo('Button was clicked'); - //d.removeChild(this); - }; - d.append(button); - var i = document.createElement('div'); - i.className = 'countdown10'; - button.appendChild(i); - } - }); - - } - } - - }; - - /* - thumbnailing :') - document.addEventListener('initialized',function(e){ - var canvas = document.createElement('canvas'); - canvas.width = 180; - document.body.appendChild(canvas); - var context = canvas.getContext('2d'); - var embedded; - for (var i in mistvideo) { - embedded = mistvideo[i].embedded[0]; - break; - } - var video = embedded.player.element; - var f = video.width / canvas.width; - canvas.height = video.height / f; - video.addEventListener('canplay',function(){ - context.drawImage(video,0,0,canvas.width,canvas.height); - var img = canvas.toDataURL('image/jpeg'); - document.write('<img src="'+img+'">'); - }); - }); - */ - </script> - - </head> - <body onload=mistinit()> - <h1>Sup</h1> - <!-- - <div class='mistvideo' id='bunny_84yt98eh9g8ht'> - <noscript> - <video controls autoplay> - <source src='http://localhost:8080/bunny.mp4' type='video/mp4'> - <a href='http://localhost:8080/bunny.html' target='_blank'> - Click here to play video - </a> - </video> - </noscript> - <script> - (function(){ - var play = function(){ - mistPlay('vids+subtel.mp4',{ - //mistPlay('bunny',{ - target: document.getElementById('bunny_84yt98eh9g8ht'), - //forcePlayer: 'dashjs' - }); - } - if (!window.mistplayers) { //import shit - var p = document.createElement('script'); - p.src = 'http://localhost:8080/player.js'; - document.head.appendChild(p); - p.onload = function(){ - play(); - } - } - else { - play(); - } - })(); - </script> - </div>--> - <div class=cont></div> - <div class=log></div> - </body> -</html> diff --git a/embed/wrappers/html5.js b/embed/wrappers/html5.js index cbf45a0b..7f9e13e6 100644 --- a/embed/wrappers/html5.js +++ b/embed/wrappers/html5.js @@ -95,11 +95,15 @@ p.prototype.build = function (options) { else { ele.pause(); } }; - if (options.live) { - ele.addEventListener('error',function(e){ + this.addlog('Built html'); + + //forward events + ele.addEventListener('error',function(e){ + + if (options.live) { if ((ele.error) && (ele.error.code == 3)) { - e.stopPropagation(); - ele.load(); + e.stopPropagation(); //dont let this error continue to prevent the core from trying to handle the error + me.load(); me.cancelAskNextCombo(); e.message = 'Handled decoding error'; me.addlog('Decoding error: reloading..'); @@ -107,18 +111,19 @@ p.prototype.build = function (options) { type: 'playback', warn: 'A decoding error was encountered, but handled' }); + return; } - },true); - } - - this.addlog('Built html'); - - //forward events - ele.addEventListener('error',function(e){ + } + var msg; if ('message' in e) { msg = e.message; } + else if ((e.target.tagName == 'SOURCE') && (e.target.getAttribute('src') == '')) { + e.stopPropagation(); + //this error is triggered because the unload function was fired + return; + } else { msg = 'readyState: '; switch (me.element.readyState) { @@ -155,12 +160,12 @@ p.prototype.build = function (options) { } } me.adderror(msg); - },true); + }); var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress']; for (var i in events) { ele.addEventListener(events[i],function(e){ me.addlog('Player event fired: '+e.type); - },true); + }); } return cont; } @@ -176,7 +181,32 @@ p.prototype.loop = function(bool){ } return this.element.loop = bool; }; -p.prototype.load = function(){ return this.element.load(); }; +p.prototype.load = function(){ + var load; + if (this.element.paused) { + load = this.element.load(); + } + else { + //sometimes there is a play / pause interrupt: try again + //TODO figure out if this happens on paused or on playing + this.load(); + return; + } + + //this helps to prevent the player from just showing a black screen after a reload + if (this.element.paused) { + var me = this; + var unpause = function(){ + if (me.element.paused) { + me.element.play(); + } + me.element.removeEventListener('progress',unpause); + } + this.element.addEventListener('progress',unpause); + } + + return load; +}; if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) { p.prototype.fullscreen = function(){ if(this.element.requestFullscreen) { diff --git a/embed/wrappers/videojs.js b/embed/wrappers/videojs.js index ebc07b00..3facb28d 100644 --- a/embed/wrappers/videojs.js +++ b/embed/wrappers/videojs.js @@ -6,15 +6,30 @@ mistplayers.videojs = { return (this.mimes.indexOf(mimetype) == -1 ? false : true); }, isBrowserSupported: function (mimetype,source,options,streaminfo,logfunc) { + + //dont use https if the player is loaded over http if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { if (logfunc) { logfunc('HTTP/HTTPS mismatch for this source'); } return false; } - var support = true; + + //dont use videojs if this location is loaded over file:// if ((location.protocol == 'file:') && (mimetype == 'html5/application/vnd.apple.mpegurl')) { if (logfunc) { logfunc('This source ('+mimetype+') won\'t work if the page is run via file://'); } return false; } + + //dont use HLS if there is an MP3 audio track, unless we're on apple or edge + if ((mimetype == 'html5/application/vnd.apple.mpegurl') && (['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) == -1) && (navigator.userAgent.indexOf('Edge') == -1)) { + for (var i in streaminfo.meta.tracks) { + var t = streaminfo.meta.tracks[i]; + if (t.codec == 'MP3') { + return false; + } + } + } + + return ('MediaSource' in window); }, player: function(){}, From cf942c5700048d376766d03e11f018da5a9b6f7b Mon Sep 17 00:00:00 2001 From: Cat <carinasadres@gmail.com> Date: Thu, 2 Mar 2017 14:34:45 +0100 Subject: [PATCH 10/11] changed event forward method --- embed/core.js | 5 ++++- embed/wrappers/dashjs.js | 12 +++++++++--- embed/wrappers/html5.js | 8 +++++++- embed/wrappers/videojs.js | 10 ++++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/embed/core.js b/embed/core.js index 8930e3f0..42536729 100644 --- a/embed/core.js +++ b/embed/core.js @@ -779,7 +779,10 @@ MistPlayer.prototype.report = function(msg) { MistPlayer.prototype.unload = function(){ this.addlog('Unloading..'); if (('pause' in this) && (this.pause)) { this.pause(); } - if ('updateSrc' in this) { this.updateSrc(''); this.load(); } + if ('updateSrc' in this) { + this.updateSrc(''); + this.element.load(); //dont use this.load() to avoid interrupting play/pause + } this.timer.clear(); this.target.innerHTML = ''; }; diff --git a/embed/wrappers/dashjs.js b/embed/wrappers/dashjs.js index 07215c04..c5bc4493 100644 --- a/embed/wrappers/dashjs.js +++ b/embed/wrappers/dashjs.js @@ -82,13 +82,19 @@ p.prototype.build = function (options,callback) { break; } } - me.adderror(msg); - },true); + //prevent onerror loops + if (e.target == me.element) { + e.message = msg; + } + else { + me.adderror(msg); + } + }); var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting']; for (var i in events) { ele.addEventListener(events[i],function(e){ me.addlog('Player event fired: '+e.type); - },true); + }); } var player = dashjs.MediaPlayer().create(); diff --git a/embed/wrappers/html5.js b/embed/wrappers/html5.js index 7f9e13e6..073bd26f 100644 --- a/embed/wrappers/html5.js +++ b/embed/wrappers/html5.js @@ -159,7 +159,13 @@ p.prototype.build = function (options) { break; } } - me.adderror(msg); + //prevent onerror loops + if (e.target == me.element) { + e.message = msg; + } + else { + me.adderror(msg); + } }); var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress']; for (var i in events) { diff --git a/embed/wrappers/videojs.js b/embed/wrappers/videojs.js index 3facb28d..fde60e77 100644 --- a/embed/wrappers/videojs.js +++ b/embed/wrappers/videojs.js @@ -132,13 +132,19 @@ p.prototype.build = function (options) { break; } } - me.adderror(msg); + //prevent onerror loops + if (e.target == me.element) { + e.message = msg; + } + else { + me.adderror(msg); + } }); var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress']; for (var i in events) { ele.addEventListener(events[i],function(e){ me.addlog('Player event fired: '+e.type); - },true); + }); } return cont; From bea605fd18ab94a232ab97f37a9c85f08073fa67 Mon Sep 17 00:00:00 2001 From: Thulinma <jaron@vietors.com> Date: Mon, 6 Mar 2017 11:52:55 +0100 Subject: [PATCH 11/11] Updated minified.js file --- lsp/minified.js | 80 ++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/lsp/minified.js b/lsp/minified.js index 8ebabd65..bfeaac8b 100644 --- a/lsp/minified.js +++ b/lsp/minified.js @@ -30,46 +30,46 @@ $(this).data("validate")(this,!0))return!1});b||(a.find(".isSetting").each(funct [3,"3 - Previous level, and warning messages"],[4,"4 - Previous level, and status messages for development"],[5,"5 - Previous level, and more status messages for development"],[6,"6 - Previous level, and verbose debugging messages"],[7,"7 - Previous level, and very verbose debugging messages"],[8,"8 - Report everything in extreme detail"],[9,"9 - Report everything in insane detail"],[10,"10 - All messages enabled"]];case "select":e=$("<select>");for(g in d.select){var p=$("<option>");"string"==typeof d.select[g]? p.text(d.select[g]):p.val(d.select[g][0]).text(d.select[g][1]);e.append(p)}break;case "textarea":e=$("<textarea>").on("keydown",function(a){a.stopPropagation()});break;case "checkbox":e=$("<input>").attr("type","checkbox");break;case "hidden":e=$("<input>").attr("type","hidden");m.hide();break;case "email":e=$("<input>").attr("type","email").attr("autocomplete","on").attr("required","");break;case "browse":e=$("<input>").attr("type","text");"filetypes"in d&&e.data("filetypes",d.filetypes);break;case "geolimited":case "hostlimited":e= $("<input>").attr("type","hidden");break;case "radioselect":e=$("<div>").addClass("radioselect");for(c in d.radioselect){var l=$("<input>").attr("type","radio").val(d.radioselect[c][0]).attr("name",d.label);("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0);p=$("<label>").append(l).append($("<span>").html(d.radioselect[c][1]));e.append(p);if(2<d.radioselect[c].length)for(g in l=$("<select>").change(function(){$(this).parent().find("input[type=radio]:enabled").prop("checked","true")}), -p.append(l),("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0),d.radioselect[c][2])p=$("<option>"),l.append(p),d.radioselect[c][2][g]instanceof Array?p.val(d.radioselect[c][2][g][0]).html(d.radioselect[c][2][g][1]):p.html(d.radioselect[c][2][g])}break;case "checklist":e=$("<div>").addClass("checkcontainer");$controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checklist");e.append($controls).append($checklist);$controls.append($("<label>").text("All").prepend($("<input>").attr("type", -"checkbox").click(function(){$(this).is(":checked")?$(this).closest(".checkcontainer").find("input[type=checkbox]").prop("checked",!0):$(this).closest(".checkcontainer").find("input[type=checkbox]").prop("checked",!1)})));for(c in d.checklist)"string"==typeof d.checklist[c]&&(d.checklist[c]=[d.checklist[c],d.checklist[c]]),$checklist.append($("<label>").text(d.checklist[c][1]).prepend($("<input>").attr("type","checkbox").attr("name",d.checklist[c][0])));break;case "DOMfield":e=d.DOMfield;break;default:e= -$("<input>").attr("type","text")}e.addClass("field").data("opts",d);"pointer"in d&&e.attr("name",d.pointer.index);f.append(e);if("classes"in d)for(g in d.classes)e.addClass(d.classes[g]);"placeholder"in d&&e.attr("placeholder",d.placeholder);"default"in d&&e.attr("placeholder",d["default"]);"unit"in d&&f.append($("<span>").addClass("unit").html(d.unit));"readonly"in d&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in d&&f.append($("<span>").addClass("unit").html($("<button>").text("QR").on("keydown", -function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=$("<div>").addClass("qrcode");UI.popup.show($("<span>").addClass("qr_container").append($("<p>").text(a)).append(b));b.qrcode({text:a,size:Math.min(b.width(),b.height())})})));"clipboard"in d&&document.queryCommandSupported("copy")&&f.append($("<span>").addClass("unit").html($("<button>").text("Copy").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()), -b=document.createElement("textarea");b.value=a;document.body.appendChild(b);b.select();var c=false;try{c=document.execCommand("copy")}catch(d){}if(c){$(this).text("Copied to clipboard!");document.body.removeChild(b);var g=$(this);setTimeout(function(){g.text("Copy")},5E3)}else{document.body.removeChild(b);alert("Failed to copy:\n"+a)}})));"rows"in d&&e.attr("rows",d.rows);"LTSonly"in d&&!mist.data.LTS&&(f.addClass("LTSonly"),e.prop("disabled",!0));switch(d.type){case "browse":l=$("<div>").addClass("grouper").append(m); -b.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});f.append(l);l.click(function(){function a(b){m.text("Loading..");mist.send(function(a){f.text(a.browse.path[0]);mist.data.LTS&&d.setval(a.browse.path[0]+"/");m.html(l.clone(true).text("..").attr("title","Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var e=a.browse.subdirectories[b];m.append(l.clone(true).attr("title",f.text()+q+e).text(e))}}if(a.browse.files){a.browse.files.sort(); -for(b in a.browse.files){var e=a.browse.files[b],h=f.text()+q+e,e=$("<a>").text(e).addClass("file").attr("title",h);m.append(e);if(p){var n=true,u;for(u in p)if(typeof p[u]!="undefined"&&mist.inputMatch(p[u],h)){n=false;break}n&&e.hide()}e.click(function(){var a=$(this).attr("title");d.setval(a).removeAttr("readonly").css("opacity",1);g.show();c.remove()})}}},{browse:b})}var b=$(this).closest(".grouper"),c=$("<div>").addClass("browse_container"),d=b.find(".field").attr("readonly","readonly").css("opacity", -0.5),g=$(this),e=$("<button>").text("Stop browsing").click(function(){g.show();c.remove();d.removeAttr("readonly").css("opacity",1)}),f=$("<span>").addClass("field"),m=$("<div>").addClass("browse_contents"),l=$("<a>").addClass("folder"),p=d.data("filetypes");b.append(c);c.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(f).append(e))).append(m);var q="/";mist.data.config.version.indexOf("indows")> --1&&(q="\\");l.click(function(){var b=f.text()+q+$(this).text();a(b)});b=d.getval();e=b.split("://");e.length>1&&(b=e[0]=="file"?e[1]:"");b=b.split(q);b.pop();b=b.join(q);g.hide();a(b)});break;case "geolimited":case "hostlimited":l={field:e};l.blackwhite=$("<select>").append($("<option>").val("-").text("Blacklist")).append($("<option>").val("+").text("Whitelist"));l.values=$("<span>").addClass("limit_value_list");switch(d.type){case "geolimited":l.prototype=$("<select>").append($("<option>").val("").text("[Select a country]")); -for(c in UI.countrylist)l.prototype.append($("<option>").val(c).html(UI.countrylist[c]));break;case "hostlimited":l.prototype=$("<input>").attr("type","text").attr("placeholder","type a host")}l.prototype.on("change keyup",function(){$(this).closest(".field_container").data("subUI").blackwhite.trigger("change")});l.blackwhite.change(function(){var a=$(this).closest(".field_container").data("subUI"),b=[],c=false;a.values.children().each(function(){c=$(this).val();c!=""?b.push(c):$(this).remove()}); -a.values.append(a.prototype.clone(true));b.length>0?a.field.val($(this).val()+b.join(" ")):a.field.val("");a.field.trigger("change")});"LTSonly"in d&&!mist.data.LTS&&(l.blackwhite.prop("disabled",!0),l.prototype.prop("disabled",!0));l.values.append(l.prototype.clone(!0));f.data("subUI",l).addClass("limit_list").append(l.blackwhite).append(l.values)}"pointer"in d&&(e.data("pointer",d.pointer).addClass("isSetting"),l=d.pointer.main[d.pointer.index],"undefined"!=l&&e.setval(l));"value"in d&&e.setval(d.value); -if("datalist"in d)for(c in l="datalist_"+c+MD5(e[0].outerHTML),e.attr("list",l),l=$("<datalist>").attr("id",l),f.append(l),d.datalist)l.append($("<option>").val(d.datalist[c]));f=$("<span>").addClass("help_container");m.append(f);"help"in d&&(f.append($("<span>").addClass("ih_balloon").html(d.help)),e.on("focus mouseover",function(){$(this).closest("label").addClass("active")}).on("blur mouseout",function(){$(this).closest("label").removeClass("active")}));if("validate"in d){m=[];for(g in d.validate){l= -d.validate[g];if("function"!=typeof l)switch(l){case "required":l=function(a){return a==""?{msg:"This is a required field.",classes:["red"]}:false};break;case "int":l=function(a,b){var c=$(b).data("opts");if(!$(b)[0].validity.valid){var d=[];"min"in c&&d.push(" greater than or equal to "+c.min);"max"in c&&d.push(" smaller than or equal to "+c.max);return{msg:"Please enter an integer"+d.join(" and")+".",classes:["red"]}}if(parseInt(Number(a))!=a)return{msg:"Please enter an integer.",classes:["red"]}}; -break;case "streamname":l=function(a,b){if(!isNaN(a.charAt(0)))return{msg:"The first character may not be a number.",classes:["red"]};if(a.toLowerCase()!=a)return{msg:"Uppercase letters are not allowed.",classes:["red"]};if(a.replace(/[^\da-z_]/g,"")!=a)return{msg:"Special characters (except for underscores) are not allowed.",classes:["red"]};if("streams"in mist.data&&a in mist.data.streams&&$(b).data("pointer").main.name!=a)return{msg:"This streamname already exists.<br>If you want to edit an existing stream, please click edit on the the streams tab.", -classes:["red"]}};break;default:l=function(){}}m.push(l)}e.data("validate_functions",m).data("help_container",f).data("validate",function(a,b){var c=$(a).getval(),d=$(a).data("validate_functions"),e=$(a).data("help_container");e.find(".err_balloon").remove();for(var g in d){var f=d[g](c,a);if(f){$err=$("<span>").addClass("err_balloon").html(f.msg);for(var m in f.classes)$err.addClass(f.classes[m]);e.prepend($err);b&&$(a).focus();return true}}return false}).addClass("hasValidate").on("change keyup", -function(){$(this).data("validate")($(this))});""!=e.getval()&&e.trigger("change")}"function"in d&&(e.on("change keyup",d["function"]),e.trigger("change"))}}b.on("keydown",function(a){switch(a.which){case 13:$(this).find("button.save").first().trigger("click");break;case 27:$(this).find("button.cancel").first().trigger("click")}});return b},buildVheaderTable:function(a){var b=$("<table>").css("margin","0.2em"),c=$("<tr>").addClass("header").append($("<td>").addClass("vheader").attr("rowspan",a.labels.length+ -1).append($("<span>").text(a.vheader))),d=[];c.append($("<td>"));for(var e in a.labels)d.push($("<tr>").append($("<td>").html(""==a.labels[e]?" ":a.labels[e]+":")));for(var g in a.content)for(e in c.append($("<td>").html(a.content[g].header)),a.content[g].body)d[e].append($("<td>").html(a.content[g].body[e]));b.append($("<tbody>").append(c).append(d));return b},plot:{addGraph:function(a,b){var c={id:a.id,xaxis:a.xaxis,datasets:[],elements:{cont:$("<div>").addClass("graph"),plot:$("<div>").addClass("plot"), -legend:$("<div>").addClass("legend").attr("draggable","true")}};UI.draggable(c.elements.legend);c.elements.cont.append(c.elements.plot).append(c.elements.legend);b.append(c.elements.cont);return c},go:function(a){if(!(1>Object.keys(a).length)){var b={totals:[],clients:[]},c;for(c in a)for(var d in a[c].datasets){var e=a[c].datasets[d];switch(e.datatype){case "clients":case "upbps":case "downbps":switch(e.origin[0]){case "total":b.totals.push({fields:[e.datatype],end:-15});break;case "stream":b.totals.push({fields:[e.datatype], -streams:[e.origin[1]],end:-15});break;case "protocol":b.totals.push({fields:[e.datatype],protocols:[e.origin[1]],end:-15})}break;case "cpuload":case "memload":b.capabilities={}}}0==b.totals.length&&delete b.totals;0==b.clients.length&&delete b.clients;mist.send(function(){for(var b in a){var c=a[b];if(1>c.datasets.length){c.elements.plot.html("");c.elements.legend.html("");break}switch(c.xaxis){case "time":var d=[];c.yaxes={};var e=[],p;for(p in c.datasets){var l=c.datasets[p];l.display&&(l.getdata(), -l.yaxistype in c.yaxes||(d.push(UI.plot.yaxes[l.yaxistype]),c.yaxes[l.yaxistype]=d.length),l.yaxis=c.yaxes[l.yaxistype],e.push(l))}d[0]&&(d[0].color=0);c.plot=$.plot(c.elements.plot,e,{legend:{show:!1},xaxis:UI.plot.xaxes[c.xaxis],yaxes:d,grid:{hoverable:!0,borderWidth:{top:0,right:0,bottom:1,left:1},color:"black",backgroundColor:{colors:["rgba(0,0,0,0)","rgba(0,0,0,0.025)"]}},crosshair:{mode:"x"}});d=$("<table>").addClass("legend-list").addClass("nolay").html($("<tr>").html($("<td>").html($("<h3>").text(c.id))).append($("<td>").css("padding-right", -"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").data("opts",c).text("X").addClass("close").click(function(){var b=$(this).data("opts");if(confirm("Are you sure you want to remove "+b.id+"?")){b.elements.cont.remove();var c=$(".graph_ids option:contains("+b.id+")"),d=c.parent();c.remove();UI.plot.del(b.id);delete a[b.id];d.trigger("change");UI.plot.go(a)}}))));c.elements.legend.html(d);var u=function(a){var b=c.elements.legend.find(".value"),d=1;if(typeof a== -"undefined")b.eq(0).html("Latest:");else{var e=c.plot.getXAxes()[0],a=Math.min(e.max,a),a=Math.max(e.min,a);b.eq(0).html(UI.format.time(a/1E3))}for(var g in c.datasets){var f=" ";if(c.datasets[g].display){var e=UI.plot.yaxes[c.datasets[g].yaxistype].tickFormatter,h=c.datasets[g].data;if(a)for(var l in h){if(h[l][0]==a){f=e(h[l][1]);break}if(h[l][0]>a){if(l!=0){f=h[l];h=h[l-1];f=e(f[1]+(a-f[0])*(h[1]-f[1])/(h[0]-f[0]))}break}}else f=e(c.datasets[g].data[c.datasets[g].data.length-1][1])}b.eq(d).html(f); -d++}};c.plot.getOptions();for(p in c.datasets)e=$("<input>").attr("type","checkbox").data("index",p).data("graph",c).click(function(){var a=$(this).data("graph");$(this).is(":checked")?a.datasets[$(this).data("index")].display=true:a.datasets[$(this).data("index")].display=false;var b={};b[a.id]=a;UI.plot.go(b)}),c.datasets[p].display&&e.attr("checked","checked"),d.append($("<tr>").html($("<td>").html($("<label>").html(e).append($("<div>").addClass("series-color").css("background-color",c.datasets[p].color)).append(c.datasets[p].label))).append($("<td>").css("padding-right", -"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").text("X").addClass("close").data("index",p).data("graph",c).click(function(){var b=$(this).data("index"),c=$(this).data("graph");if(confirm("Are you sure you want to remove "+c.datasets[b].label+" from "+c.id+"?")){c.datasets.splice(b,1);if(c.datasets.length==0){c.elements.cont.remove();var b=$(".graph_ids option:contains("+c.id+")"),d=b.parent();b.remove();d.trigger("change");UI.plot.del(c.id);delete a[c.id]; -UI.plot.go(a)}else{UI.plot.save(c);b={};b[c.id]=c;UI.plot.go(b)}}}))));u();var h=!1;c.elements.plot.on("plothover",function(a,b,c){if(b.x!=h){u(b.x);h=b.x}if(c){a=$("<span>").append($("<h3>").text(c.series.label).prepend($("<div>").addClass("series-color").css("background-color",c.series.color))).append($("<table>").addClass("nolay").html($("<tr>").html($("<td>").text("Time:")).append($("<td>").html(UI.format.dateTime(c.datapoint[0]/1E3,"long")))).append($("<tr>").html($("<td>").text("Value:")).append($("<td>").html(c.series.yaxis.tickFormatter(c.datapoint[1], -c.series.yaxis)))));UI.tooltip.show(b,a.children())}else UI.tooltip.hide()}).on("mouseout",function(){u()})}}},b)}},save:function(a){var b={id:a.id,xaxis:a.xaxis,datasets:[]},c;for(c in a.datasets)b.datasets.push({origin:a.datasets[c].origin,datatype:a.datasets[c].datatype});a=mist.stored.get().graphs||{};a[b.id]=b;mist.stored.set("graphs",a)},del:function(a){var b=mist.stored.get().graphs||{};delete b[a];mist.stored.set("graphs",b)},datatype:{getOptions:function(a){var b=$.extend(!0,{},UI.plot.datatype.templates.general), -c=$.extend(!0,{},UI.plot.datatype.templates[a.datatype]),a=$.extend(!0,c,a),a=$.extend(!0,b,a);switch(a.origin[0]){case "total":switch(a.datatype){case "cpuload":case "memload":break;default:a.label+=" (total)"}break;case "stream":case "protocol":a.label+=" ("+a.origin[1]+")"}var b=[],d;for(d in a.basecolor)c=a.basecolor[d],c+=50*(0.5-Math.random()),c=Math.round(c),c=Math.min(255,Math.max(0,c)),b.push(c);a.color="rgb("+b.join(",")+")";return a},templates:{general:{display:!0,datatype:"general",label:"", -yaxistype:"amount",data:[],lines:{show:!0},points:{show:!1},getdata:function(){var a=mist.data.totals["stream"==this.origin[0]?this.origin[1]:"all_streams"]["protocol"==this.origin[0]?this.origin[1]:"all_protocols"][this.datatype];return this.data=a}},cpuload:{label:"CPU use",yaxistype:"percentage",basecolor:[237,194,64],cores:1,getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time, -mist.data.capabilities.cpu_use/10]);return this.data}},memload:{label:"Memory load",yaxistype:"percentage",basecolor:[175,216,248],getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.load.memory]);return this.data}},clients:{label:"Connections",basecolor:[203,75,75]},upbps:{label:"Bandwidth up",yaxistype:"bytespersec",basecolor:[77,167,77]},downbps:{label:"Bandwidth down", -yaxistype:"bytespersec",basecolor:[148,64,237]}}},yaxes:{percentage:{name:"percentage",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.addUnit(UI.format.number(a),"%")},tickLength:0,min:0,max:100},amount:{name:"amount",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.number(a)},tickLength:0,min:0},bytespersec:{name:"bytespersec",color:"black",tickColor:0,tickDecimals:1,tickFormatter:function(a){return UI.format.bytes(a,!0)},tickLength:0, -ticks:function(a){var b=0.3*Math.sqrt($(".graph").first().height()),b=(a.max-a.min)/b,c=Math.floor(Math.log(Math.abs(b))/Math.log(1024)),d=b/Math.pow(1024,c),e=-Math.floor(Math.log(d)/Math.LN10),g=a.tickDecimals;null!=g&&e>g&&(e=g);var m=Math.pow(10,-e),d=d/m,f;if(1.5>d)f=1;else if(3>d){if(f=2,2.25<d&&(null==g||e+1<=g))f=2.5,++e}else f=7.5>d?5:10;f=f*m*Math.pow(1024,c);null!=a.minTickSize&&f<a.minTickSize&&(f=a.minTickSize);a.delta=b;a.tickDecimals=Math.max(0,null!=g?g:e);a.tickSize=f;b=[];c=a.tickSize* -Math.floor(a.min/a.tickSize);e=0;g=Number.NaN;do m=g,g=c+e*a.tickSize,b.push(g),++e;while(g<a.max&&g!=m);return b},min:0}},xaxes:{time:{name:"time",mode:"time",timezone:"browser",ticks:5}}},draggable:function(a){a.attr("draggable",!0);a.on("dragstart",function(a){$(this).css("opacity",0.4).data("dragstart",{click:{x:a.originalEvent.pageX,y:a.originalEvent.pageY},ele:{x:this.offsetLeft,y:this.offsetTop}})}).on("dragend",function(a){var c=$(this).data("dragstart"),d=c.ele.x-c.click.x+a.originalEvent.pageX, -a=c.ele.y-c.click.y+a.originalEvent.pageY;$(this).css({opacity:1,top:a,left:d,right:"auto",bottom:"auto"})});a.parent().on("dragleave",function(){})},format:{time:function(a,b){var c=new Date(1E3*a),d=[];d.push(("0"+c.getHours()).slice(-2));d.push(("0"+c.getMinutes()).slice(-2));"short"!=b&&d.push(("0"+c.getSeconds()).slice(-2));return d.join(":")},date:function(a,b){var c=new Date(1E3*a),d="Sun Mon Tue Wed Thu Fri Sat".split(" "),e=[];"long"==b&&e.push(d[c.getDay()]);e.push(("0"+c.getDate()).slice(-2)); -e.push("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[c.getMonth()]);"short"!=b&&e.push(c.getFullYear());return e.join(" ")},dateTime:function(a,b){return UI.format.date(a,b)+", "+UI.format.time(a,b)},duration:function(a){var b=[0.001,1E3,60,60,24,7,1E9],c="ms sec min hr day week".split(" "),d={},e;for(e in c){var a=a/b[e],g=Math.round(a%b[Number(e)+1]);d[c[e]]=g;a-=g}var m;for(e=c.length-1;0<=e;e--)if(0<d[c[e]]){m=c[e];break}b=$("<span>");switch(m){case "week":b.append(UI.format.addUnit(d.week, -"wks, ")).append(UI.format.addUnit(d.day,"days"));break;case "day":b.append(UI.format.addUnit(d.day,"days, ")).append(UI.format.addUnit(d.hr,"hrs"));break;default:b.append([("0"+d.hr).slice(-2),("0"+d.min).slice(-2),("0"+d.sec).slice(-2)+(d.ms?"."+d.ms:"")].join(":"))}return b[0].innerHTML},number:function(a){if(isNaN(Number(a))||0==a)return a;var b=Math.pow(10,3-Math.floor(Math.log(a)/Math.LN10)-1),a=Math.round(a*b)/b;if(1E4<a){number=a.toString().split(".");for(a=/(\d+)(\d{3})/;a.test(number[0]);)number[0]= -number[0].replace(a,"$1 $2");a=number.join(".")}return a},status:function(a){var b=$("<span>");if("undefined"==typeof a.online)return b.text("Unknown, checking.."),"undefined"!=typeof a.error&&b.text(a.error),b;switch(a.online){case -1:b.text("Enabling");break;case 0:b.text("Unavailable").addClass("red");break;case 1:b.text("Active").addClass("green");break;case 2:b.text("Standby").addClass("orange");break;default:b.text(a.online)}"error"in a&&b.text(a.error);return b},capital:function(a){return a.charAt(0).toUpperCase()+ -a.substring(1)},addUnit:function(a,b){var c=$("<span>").html(a);c.append($("<span>").addClass("unit").html(b));return c[0].innerHTML},bytes:function(a,b){var c="bytes KiB MiB GiB TiB PiB".split(" ");if(0==a)unit=c[0];else{var d=Math.floor(Math.log(Math.abs(a))/Math.log(1024));0>d?unit=c[0]:(a/=Math.pow(1024,d),unit=c[d])}return UI.format.addUnit(UI.format.number(a),unit+(b?"/s":""))}},navto:function(a,b){var c=location.hash,d=c.split("@");d[0]=[mist.user.name,mist.user.host].join("&");d[1]=[a,b].join("&"); -"undefined"!=typeof screenlog&&screenlog.navto(d[1]);location.hash=d.join("@");location.hash==c&&$(window).trigger("hashchange")},showTab:function(a,b){var c=UI.elements.main;if(mist.user.loggedin&&!("ui_settings"in mist.data))c.html("Loading.."),mist.send(function(){UI.showTab(a,b)},{ui_settings:!0});else{var d=UI.elements.menu.removeClass("hide").find('.plain:contains("'+a+'")').closest(".button");0<d.length&&(UI.elements.menu.find(".button.active").removeClass("active"),d.addClass("active"));if("undefined"!= -typeof mistvideo)for(var e in mistvideo)if("embedded"in mistvideo[e])for(var g in mistvideo[e].embedded)try{mistvideo[e].embedded[g].player.unload()}catch(m){}UI.interval.clear();c.html($("<h2>").text(a));switch(a){case "Login":if(mist.user.loggedin){UI.navto("Overview");return}UI.elements.menu.addClass("hide");UI.elements.connection.status.text("Disconnected").removeClass("green").addClass("red");c.append(UI.buildUI([{type:"help",help:"Please provide your account details.<br>You were asked to set these when MistController was started for the first time. If you did not yet set any account details, log in with your desired credentials to create a new account."}, -{label:"Host",help:"Url location of the MistServer API. Generally located at http://MistServerIP:4242/api","default":"http://localhost:4242/api",pointer:{main:mist.user,index:"host"}},{label:"Username",help:"Please enter your username here.",validate:["required"],pointer:{main:mist.user,index:"name"}},{label:"Password",type:"password",help:"Please enter your password here.",validate:["required"],pointer:{main:mist.user,index:"rawpassword"}},{type:"buttons",buttons:[{label:"Login",type:"save","function":function(){mist.user.password= +p.append(l),("LTSonly"in d&&!mist.data.LTS||d.readonly)&&l.prop("disabled",!0),d.radioselect[c][2])p=$("<option>"),l.append(p),d.radioselect[c][2][g]instanceof Array?p.val(d.radioselect[c][2][g][0]).html(d.radioselect[c][2][g][1]):p.html(d.radioselect[c][2][g])}break;case "checklist":e=$("<div>").addClass("checkcontainer");$controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checklist");e.append($checklist);for(c in d.checklist)"string"==typeof d.checklist[c]&&(d.checklist[c]= +[d.checklist[c],d.checklist[c]]),$checklist.append($("<label>").text(d.checklist[c][1]).prepend($("<input>").attr("type","checkbox").attr("name",d.checklist[c][0])));break;case "DOMfield":e=d.DOMfield;break;default:e=$("<input>").attr("type","text")}e.addClass("field").data("opts",d);"pointer"in d&&e.attr("name",d.pointer.index);f.append(e);if("classes"in d)for(g in d.classes)e.addClass(d.classes[g]);"placeholder"in d&&e.attr("placeholder",d.placeholder);"default"in d&&e.attr("placeholder",d["default"]); +"unit"in d&&f.append($("<span>").addClass("unit").html(d.unit));"readonly"in d&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in d&&f.append($("<span>").addClass("unit").html($("<button>").text("QR").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=$("<div>").addClass("qrcode");UI.popup.show($("<span>").addClass("qr_container").append($("<p>").text(a)).append(b));b.qrcode({text:a, +size:Math.min(b.width(),b.height())})})));"clipboard"in d&&document.queryCommandSupported("copy")&&f.append($("<span>").addClass("unit").html($("<button>").text("Copy").on("keydown",function(a){a.stopPropagation()}).click(function(){var a=String($(this).closest(".field_container").find(".field").getval()),b=document.createElement("textarea");b.value=a;document.body.appendChild(b);b.select();var c=false;try{c=document.execCommand("copy")}catch(d){}if(c){$(this).text("Copied to clipboard!");document.body.removeChild(b); +var g=$(this);setTimeout(function(){g.text("Copy")},5E3)}else{document.body.removeChild(b);alert("Failed to copy:\n"+a)}})));"rows"in d&&e.attr("rows",d.rows);"LTSonly"in d&&!mist.data.LTS&&(f.addClass("LTSonly"),e.prop("disabled",!0));switch(d.type){case "browse":l=$("<div>").addClass("grouper").append(m);b.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});f.append(l);l.click(function(){function a(b){m.text("Loading..");mist.send(function(a){f.text(a.browse.path[0]); +mist.data.LTS&&d.setval(a.browse.path[0]+"/");m.html(l.clone(true).text("..").attr("title","Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var e=a.browse.subdirectories[b];m.append(l.clone(true).attr("title",f.text()+q+e).text(e))}}if(a.browse.files){a.browse.files.sort();for(b in a.browse.files){var e=a.browse.files[b],h=f.text()+q+e,e=$("<a>").text(e).addClass("file").attr("title",h);m.append(e);if(p){var n=true,u;for(u in p)if(typeof p[u]!= +"undefined"&&mist.inputMatch(p[u],h)){n=false;break}n&&e.hide()}e.click(function(){var a=$(this).attr("title");d.setval(a).removeAttr("readonly").css("opacity",1);g.show();c.remove()})}}},{browse:b})}var b=$(this).closest(".grouper"),c=$("<div>").addClass("browse_container"),d=b.find(".field").attr("readonly","readonly").css("opacity",0.5),g=$(this),e=$("<button>").text("Stop browsing").click(function(){g.show();c.remove();d.removeAttr("readonly").css("opacity",1)}),f=$("<span>").addClass("field"), +m=$("<div>").addClass("browse_contents"),l=$("<a>").addClass("folder"),p=d.data("filetypes");b.append(c);c.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(f).append(e))).append(m);var q="/";mist.data.config.version.indexOf("indows")>-1&&(q="\\");l.click(function(){var b=f.text()+q+$(this).text();a(b)});b=d.getval();e=b.split("://");e.length>1&&(b=e[0]=="file"?e[1]:"");b=b.split(q);b.pop(); +b=b.join(q);g.hide();a(b)});break;case "geolimited":case "hostlimited":l={field:e};l.blackwhite=$("<select>").append($("<option>").val("-").text("Blacklist")).append($("<option>").val("+").text("Whitelist"));l.values=$("<span>").addClass("limit_value_list");switch(d.type){case "geolimited":l.prototype=$("<select>").append($("<option>").val("").text("[Select a country]"));for(c in UI.countrylist)l.prototype.append($("<option>").val(c).html(UI.countrylist[c]));break;case "hostlimited":l.prototype=$("<input>").attr("type", +"text").attr("placeholder","type a host")}l.prototype.on("change keyup",function(){$(this).closest(".field_container").data("subUI").blackwhite.trigger("change")});l.blackwhite.change(function(){var a=$(this).closest(".field_container").data("subUI"),b=[],c=false;a.values.children().each(function(){c=$(this).val();c!=""?b.push(c):$(this).remove()});a.values.append(a.prototype.clone(true));b.length>0?a.field.val($(this).val()+b.join(" ")):a.field.val("");a.field.trigger("change")});"LTSonly"in d&& +!mist.data.LTS&&(l.blackwhite.prop("disabled",!0),l.prototype.prop("disabled",!0));l.values.append(l.prototype.clone(!0));f.data("subUI",l).addClass("limit_list").append(l.blackwhite).append(l.values)}"pointer"in d&&(e.data("pointer",d.pointer).addClass("isSetting"),l=d.pointer.main[d.pointer.index],"undefined"!=l&&e.setval(l));"value"in d&&e.setval(d.value);if("datalist"in d)for(c in l="datalist_"+c+MD5(e[0].outerHTML),e.attr("list",l),l=$("<datalist>").attr("id",l),f.append(l),d.datalist)l.append($("<option>").val(d.datalist[c])); +f=$("<span>").addClass("help_container");m.append(f);"help"in d&&(f.append($("<span>").addClass("ih_balloon").html(d.help)),e.on("focus mouseover",function(){$(this).closest("label").addClass("active")}).on("blur mouseout",function(){$(this).closest("label").removeClass("active")}));if("validate"in d){m=[];for(g in d.validate){l=d.validate[g];if("function"!=typeof l)switch(l){case "required":l=function(a){return a==""?{msg:"This is a required field.",classes:["red"]}:false};break;case "int":l=function(a, +b){var c=$(b).data("opts");if(!$(b)[0].validity.valid){var d=[];"min"in c&&d.push(" greater than or equal to "+c.min);"max"in c&&d.push(" smaller than or equal to "+c.max);return{msg:"Please enter an integer"+d.join(" and")+".",classes:["red"]}}if(parseInt(Number(a))!=a)return{msg:"Please enter an integer.",classes:["red"]}};break;case "streamname":l=function(a,b){if(!isNaN(a.charAt(0)))return{msg:"The first character may not be a number.",classes:["red"]};if(a.toLowerCase()!=a)return{msg:"Uppercase letters are not allowed.", +classes:["red"]};if(a.replace(/[^\da-z_]/g,"")!=a)return{msg:"Special characters (except for underscores) are not allowed.",classes:["red"]};if("streams"in mist.data&&a in mist.data.streams&&$(b).data("pointer").main.name!=a)return{msg:"This streamname already exists.<br>If you want to edit an existing stream, please click edit on the the streams tab.",classes:["red"]}};break;default:l=function(){}}m.push(l)}e.data("validate_functions",m).data("help_container",f).data("validate",function(a,b){var c= +$(a).getval(),d=$(a).data("validate_functions"),e=$(a).data("help_container");e.find(".err_balloon").remove();for(var g in d){var f=d[g](c,a);if(f){$err=$("<span>").addClass("err_balloon").html(f.msg);for(var m in f.classes)$err.addClass(f.classes[m]);e.prepend($err);b&&$(a).focus();return true}}return false}).addClass("hasValidate").on("change keyup",function(){$(this).data("validate")($(this))});""!=e.getval()&&e.trigger("change")}"function"in d&&(e.on("change keyup",d["function"]),e.trigger("change"))}}b.on("keydown", +function(a){switch(a.which){case 13:$(this).find("button.save").first().trigger("click");break;case 27:$(this).find("button.cancel").first().trigger("click")}});return b},buildVheaderTable:function(a){var b=$("<table>").css("margin","0.2em"),c=$("<tr>").addClass("header").append($("<td>").addClass("vheader").attr("rowspan",a.labels.length+1).append($("<span>").text(a.vheader))),d=[];c.append($("<td>"));for(var e in a.labels)d.push($("<tr>").append($("<td>").html(""==a.labels[e]?" ":a.labels[e]+ +":")));for(var g in a.content)for(e in c.append($("<td>").html(a.content[g].header)),a.content[g].body)d[e].append($("<td>").html(a.content[g].body[e]));b.append($("<tbody>").append(c).append(d));return b},plot:{addGraph:function(a,b){var c={id:a.id,xaxis:a.xaxis,datasets:[],elements:{cont:$("<div>").addClass("graph"),plot:$("<div>").addClass("plot"),legend:$("<div>").addClass("legend").attr("draggable","true")}};UI.draggable(c.elements.legend);c.elements.cont.append(c.elements.plot).append(c.elements.legend); +b.append(c.elements.cont);return c},go:function(a){if(!(1>Object.keys(a).length)){var b={totals:[],clients:[]},c;for(c in a)for(var d in a[c].datasets){var e=a[c].datasets[d];switch(e.datatype){case "clients":case "upbps":case "downbps":switch(e.origin[0]){case "total":b.totals.push({fields:[e.datatype],end:-15});break;case "stream":b.totals.push({fields:[e.datatype],streams:[e.origin[1]],end:-15});break;case "protocol":b.totals.push({fields:[e.datatype],protocols:[e.origin[1]],end:-15})}break;case "cpuload":case "memload":b.capabilities= +{}}}0==b.totals.length&&delete b.totals;0==b.clients.length&&delete b.clients;mist.send(function(){for(var b in a){var c=a[b];if(1>c.datasets.length){c.elements.plot.html("");c.elements.legend.html("");break}switch(c.xaxis){case "time":var d=[];c.yaxes={};var e=[],p;for(p in c.datasets){var l=c.datasets[p];l.display&&(l.getdata(),l.yaxistype in c.yaxes||(d.push(UI.plot.yaxes[l.yaxistype]),c.yaxes[l.yaxistype]=d.length),l.yaxis=c.yaxes[l.yaxistype],e.push(l))}d[0]&&(d[0].color=0);c.plot=$.plot(c.elements.plot, +e,{legend:{show:!1},xaxis:UI.plot.xaxes[c.xaxis],yaxes:d,grid:{hoverable:!0,borderWidth:{top:0,right:0,bottom:1,left:1},color:"black",backgroundColor:{colors:["rgba(0,0,0,0)","rgba(0,0,0,0.025)"]}},crosshair:{mode:"x"}});d=$("<table>").addClass("legend-list").addClass("nolay").html($("<tr>").html($("<td>").html($("<h3>").text(c.id))).append($("<td>").css("padding-right","2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").data("opts",c).text("X").addClass("close").click(function(){var b= +$(this).data("opts");if(confirm("Are you sure you want to remove "+b.id+"?")){b.elements.cont.remove();var c=$(".graph_ids option:contains("+b.id+")"),d=c.parent();c.remove();UI.plot.del(b.id);delete a[b.id];d.trigger("change");UI.plot.go(a)}}))));c.elements.legend.html(d);var u=function(a){var b=c.elements.legend.find(".value"),d=1;if(typeof a=="undefined")b.eq(0).html("Latest:");else{var e=c.plot.getXAxes()[0],a=Math.min(e.max,a),a=Math.max(e.min,a);b.eq(0).html(UI.format.time(a/1E3))}for(var g in c.datasets){var f= +" ";if(c.datasets[g].display){var e=UI.plot.yaxes[c.datasets[g].yaxistype].tickFormatter,h=c.datasets[g].data;if(a)for(var l in h){if(h[l][0]==a){f=e(h[l][1]);break}if(h[l][0]>a){if(l!=0){f=h[l];h=h[l-1];f=e(f[1]+(a-f[0])*(h[1]-f[1])/(h[0]-f[0]))}break}}else f=e(c.datasets[g].data[c.datasets[g].data.length-1][1])}b.eq(d).html(f);d++}};c.plot.getOptions();for(p in c.datasets)e=$("<input>").attr("type","checkbox").data("index",p).data("graph",c).click(function(){var a=$(this).data("graph");$(this).is(":checked")? +a.datasets[$(this).data("index")].display=true:a.datasets[$(this).data("index")].display=false;var b={};b[a.id]=a;UI.plot.go(b)}),c.datasets[p].display&&e.attr("checked","checked"),d.append($("<tr>").html($("<td>").html($("<label>").html(e).append($("<div>").addClass("series-color").css("background-color",c.datasets[p].color)).append(c.datasets[p].label))).append($("<td>").css("padding-right","2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").text("X").addClass("close").data("index", +p).data("graph",c).click(function(){var b=$(this).data("index"),c=$(this).data("graph");if(confirm("Are you sure you want to remove "+c.datasets[b].label+" from "+c.id+"?")){c.datasets.splice(b,1);if(c.datasets.length==0){c.elements.cont.remove();var b=$(".graph_ids option:contains("+c.id+")"),d=b.parent();b.remove();d.trigger("change");UI.plot.del(c.id);delete a[c.id];UI.plot.go(a)}else{UI.plot.save(c);b={};b[c.id]=c;UI.plot.go(b)}}}))));u();var h=!1;c.elements.plot.on("plothover",function(a,b,c){if(b.x!= +h){u(b.x);h=b.x}if(c){a=$("<span>").append($("<h3>").text(c.series.label).prepend($("<div>").addClass("series-color").css("background-color",c.series.color))).append($("<table>").addClass("nolay").html($("<tr>").html($("<td>").text("Time:")).append($("<td>").html(UI.format.dateTime(c.datapoint[0]/1E3,"long")))).append($("<tr>").html($("<td>").text("Value:")).append($("<td>").html(c.series.yaxis.tickFormatter(c.datapoint[1],c.series.yaxis)))));UI.tooltip.show(b,a.children())}else UI.tooltip.hide()}).on("mouseout", +function(){u()})}}},b)}},save:function(a){var b={id:a.id,xaxis:a.xaxis,datasets:[]},c;for(c in a.datasets)b.datasets.push({origin:a.datasets[c].origin,datatype:a.datasets[c].datatype});a=mist.stored.get().graphs||{};a[b.id]=b;mist.stored.set("graphs",a)},del:function(a){var b=mist.stored.get().graphs||{};delete b[a];mist.stored.set("graphs",b)},datatype:{getOptions:function(a){var b=$.extend(!0,{},UI.plot.datatype.templates.general),c=$.extend(!0,{},UI.plot.datatype.templates[a.datatype]),a=$.extend(!0, +c,a),a=$.extend(!0,b,a);switch(a.origin[0]){case "total":switch(a.datatype){case "cpuload":case "memload":break;default:a.label+=" (total)"}break;case "stream":case "protocol":a.label+=" ("+a.origin[1]+")"}var b=[],d;for(d in a.basecolor)c=a.basecolor[d],c+=50*(0.5-Math.random()),c=Math.round(c),c=Math.min(255,Math.max(0,c)),b.push(c);a.color="rgb("+b.join(",")+")";return a},templates:{general:{display:!0,datatype:"general",label:"",yaxistype:"amount",data:[],lines:{show:!0},points:{show:!1},getdata:function(){var a= +mist.data.totals["stream"==this.origin[0]?this.origin[1]:"all_streams"]["protocol"==this.origin[0]?this.origin[1]:"all_protocols"][this.datatype];return this.data=a}},cpuload:{label:"CPU use",yaxistype:"percentage",basecolor:[237,194,64],cores:1,getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.cpu_use/10]);return this.data}},memload:{label:"Memory load", +yaxistype:"percentage",basecolor:[175,216,248],getdata:function(){var a=!1,b;for(b in this.data)this.data[b][0]<1E3*(mist.data.config.time-600)&&(a=b);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.load.memory]);return this.data}},clients:{label:"Connections",basecolor:[203,75,75]},upbps:{label:"Bandwidth up",yaxistype:"bytespersec",basecolor:[77,167,77]},downbps:{label:"Bandwidth down",yaxistype:"bytespersec",basecolor:[148,64,237]}}},yaxes:{percentage:{name:"percentage", +color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.addUnit(UI.format.number(a),"%")},tickLength:0,min:0,max:100},amount:{name:"amount",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.number(a)},tickLength:0,min:0},bytespersec:{name:"bytespersec",color:"black",tickColor:0,tickDecimals:1,tickFormatter:function(a){return UI.format.bytes(a,!0)},tickLength:0,ticks:function(a){var b=0.3*Math.sqrt($(".graph").first().height()),b=(a.max- +a.min)/b,c=Math.floor(Math.log(Math.abs(b))/Math.log(1024)),d=b/Math.pow(1024,c),e=-Math.floor(Math.log(d)/Math.LN10),g=a.tickDecimals;null!=g&&e>g&&(e=g);var m=Math.pow(10,-e),d=d/m,f;if(1.5>d)f=1;else if(3>d){if(f=2,2.25<d&&(null==g||e+1<=g))f=2.5,++e}else f=7.5>d?5:10;f=f*m*Math.pow(1024,c);null!=a.minTickSize&&f<a.minTickSize&&(f=a.minTickSize);a.delta=b;a.tickDecimals=Math.max(0,null!=g?g:e);a.tickSize=f;b=[];c=a.tickSize*Math.floor(a.min/a.tickSize);e=0;g=Number.NaN;do m=g,g=c+e*a.tickSize, +b.push(g),++e;while(g<a.max&&g!=m);return b},min:0}},xaxes:{time:{name:"time",mode:"time",timezone:"browser",ticks:5}}},draggable:function(a){a.attr("draggable",!0);a.on("dragstart",function(a){$(this).css("opacity",0.4).data("dragstart",{click:{x:a.originalEvent.pageX,y:a.originalEvent.pageY},ele:{x:this.offsetLeft,y:this.offsetTop}})}).on("dragend",function(a){var c=$(this).data("dragstart"),d=c.ele.x-c.click.x+a.originalEvent.pageX,a=c.ele.y-c.click.y+a.originalEvent.pageY;$(this).css({opacity:1, +top:a,left:d,right:"auto",bottom:"auto"})});a.parent().on("dragleave",function(){})},format:{time:function(a,b){var c=new Date(1E3*a),d=[];d.push(("0"+c.getHours()).slice(-2));d.push(("0"+c.getMinutes()).slice(-2));"short"!=b&&d.push(("0"+c.getSeconds()).slice(-2));return d.join(":")},date:function(a,b){var c=new Date(1E3*a),d="Sun Mon Tue Wed Thu Fri Sat".split(" "),e=[];"long"==b&&e.push(d[c.getDay()]);e.push(("0"+c.getDate()).slice(-2));e.push("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[c.getMonth()]); +"short"!=b&&e.push(c.getFullYear());return e.join(" ")},dateTime:function(a,b){return UI.format.date(a,b)+", "+UI.format.time(a,b)},duration:function(a){var b=[0.001,1E3,60,60,24,7,1E9],c="ms sec min hr day week".split(" "),d={},e;for(e in c){var a=a/b[e],g=Math.round(a%b[Number(e)+1]);d[c[e]]=g;a-=g}var m;for(e=c.length-1;0<=e;e--)if(0<d[c[e]]){m=c[e];break}b=$("<span>");switch(m){case "week":b.append(UI.format.addUnit(d.week,"wks, ")).append(UI.format.addUnit(d.day,"days"));break;case "day":b.append(UI.format.addUnit(d.day, +"days, ")).append(UI.format.addUnit(d.hr,"hrs"));break;default:b.append([("0"+d.hr).slice(-2),("0"+d.min).slice(-2),("0"+d.sec).slice(-2)+(d.ms?"."+d.ms:"")].join(":"))}return b[0].innerHTML},number:function(a){if(isNaN(Number(a))||0==a)return a;var b=Math.pow(10,3-Math.floor(Math.log(a)/Math.LN10)-1),a=Math.round(a*b)/b;if(1E4<a){number=a.toString().split(".");for(a=/(\d+)(\d{3})/;a.test(number[0]);)number[0]=number[0].replace(a,"$1 $2");a=number.join(".")}return a},status:function(a){var b=$("<span>"); +if("undefined"==typeof a.online)return b.text("Unknown, checking.."),"undefined"!=typeof a.error&&b.text(a.error),b;switch(a.online){case -1:b.text("Enabling");break;case 0:b.text("Unavailable").addClass("red");break;case 1:b.text("Active").addClass("green");break;case 2:b.text("Standby").addClass("orange");break;default:b.text(a.online)}"error"in a&&b.text(a.error);return b},capital:function(a){return a.charAt(0).toUpperCase()+a.substring(1)},addUnit:function(a,b){var c=$("<span>").html(a);c.append($("<span>").addClass("unit").html(b)); +return c[0].innerHTML},bytes:function(a,b){var c="bytes KiB MiB GiB TiB PiB".split(" ");if(0==a)unit=c[0];else{var d=Math.floor(Math.log(Math.abs(a))/Math.log(1024));0>d?unit=c[0]:(a/=Math.pow(1024,d),unit=c[d])}return UI.format.addUnit(UI.format.number(a),unit+(b?"/s":""))}},navto:function(a,b){var c=location.hash,d=c.split("@");d[0]=[mist.user.name,mist.user.host].join("&");d[1]=[a,b].join("&");"undefined"!=typeof screenlog&&screenlog.navto(d[1]);location.hash=d.join("@");location.hash==c&&$(window).trigger("hashchange")}, +showTab:function(a,b){var c=UI.elements.main;if(mist.user.loggedin&&!("ui_settings"in mist.data))c.html("Loading.."),mist.send(function(){UI.showTab(a,b)},{ui_settings:!0});else{var d=UI.elements.menu.removeClass("hide").find('.plain:contains("'+a+'")').closest(".button");0<d.length&&(UI.elements.menu.find(".button.active").removeClass("active"),d.addClass("active"));if("undefined"!=typeof mistvideo)for(var e in mistvideo)if("embedded"in mistvideo[e])for(var g in mistvideo[e].embedded)try{mistvideo[e].embedded[g].player.unload()}catch(m){}UI.interval.clear(); +c.html($("<h2>").text(a));switch(a){case "Login":if(mist.user.loggedin){UI.navto("Overview");return}UI.elements.menu.addClass("hide");UI.elements.connection.status.text("Disconnected").removeClass("green").addClass("red");c.append(UI.buildUI([{type:"help",help:"Please provide your account details.<br>You were asked to set these when MistController was started for the first time. If you did not yet set any account details, log in with your desired credentials to create a new account."},{label:"Host", +help:"Url location of the MistServer API. Generally located at http://MistServerIP:4242/api","default":"http://localhost:4242/api",pointer:{main:mist.user,index:"host"}},{label:"Username",help:"Please enter your username here.",validate:["required"],pointer:{main:mist.user,index:"name"}},{label:"Password",type:"password",help:"Please enter your password here.",validate:["required"],pointer:{main:mist.user,index:"rawpassword"}},{type:"buttons",buttons:[{label:"Login",type:"save","function":function(){mist.user.password= MD5(mist.user.rawpassword);delete mist.user.rawpassword;mist.send(function(){UI.navto("Overview")})}}]}]));break;case "Create a new account":UI.elements.menu.addClass("hide");c.append($("<p>").text("No account has been created yet in the MistServer at ").append($("<i>").text(mist.user.host)).append("."));c.append(UI.buildUI([{type:"buttons",buttons:[{label:"Select other host",type:"cancel",css:{"float":"left"},"function":function(){UI.navto("Login")}}]},{type:"custom",custom:$("<br>")},{label:"Desired username", type:"str",validate:["required"],help:"Enter your desired username. In the future, you will need this to access the Management Interface.",pointer:{main:mist.user,index:"name"}},{label:"Desired password",type:"password",validate:["required",function(a,b){$(".match_password").not($(b)).trigger("change");return false}],help:"Enter your desired password. In the future, you will need this to access the Management Interface.",pointer:{main:mist.user,index:"rawpassword"},classes:["match_password"]},{label:"Repeat password", type:"password",validate:["required",function(a,b){return a!=$(".match_password").not($(b)).val()?{msg:'The fields "Desired password" and "Repeat password" do not match.',classes:["red"]}:false}],help:"Repeat your desired password.",classes:["match_password"]},{type:"buttons",buttons:[{type:"save",label:"Create new account","function":function(){mist.send(function(){UI.navto("Account created")},{authorize:{new_username:mist.user.name,new_password:mist.user.rawpassword}});mist.user.password=MD5(mist.user.rawpassword);