Embed: Support for subtitles and metadata tracks over websocket
LSP: Preview tab: Show meta information for metadata tracks LSP/embed: use new inclzero flag to receive sources that are enabled, but that don't currently have active tracks; and bugfix for metadata update for tracks not firing if sources have also changed Embed: dashjs: disable stock subtitles unless activated
This commit is contained in:
parent
f3ba13d6bb
commit
c337fff614
11 changed files with 514 additions and 85 deletions
File diff suppressed because one or more lines are too long
|
@ -67,6 +67,10 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans
|
|||
.mistvideo{line-height:1.2;font-size:14.5px}
|
||||
.mistvideo svg{margin:2.5px}
|
||||
.mistvideo-video{display:flex;align-items:center;justify-content:center}
|
||||
.mistvideo-subtitles{position:absolute;width:100%;height:100%;pointer-events:none;display:flex;align-items:flex-end;justify-content:center}
|
||||
.mistvideo-subtitles>*{margin-bottom:.5em;padding:.1em .3em;text-align:center;background:rgba(0,0,0,.6);white-space:pre-wrap}
|
||||
.mistvideo-subtitles>:empty{display:none}
|
||||
.mistvideo[data-fullscreen] .mistvideo-subtitles{font-size:3vh}
|
||||
.mistvideo-background{background-color:$background}
|
||||
.mistvideo-totalTime:before{content:'/';margin:.2em}
|
||||
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
|
||||
|
|
|
@ -67,6 +67,10 @@ svg.icon .spin,svg.icon.spin{animation:mistvideo-spin 1.5s infinite linear;trans
|
|||
.mistvideo{line-height:1.2;font-size:14.5px}
|
||||
.mistvideo svg{margin:2.5px}
|
||||
.mistvideo-video{display:flex;align-items:center;justify-content:center}
|
||||
.mistvideo-subtitles{position:absolute;width:100%;height:100%;pointer-events:none;display:flex;align-items:flex-end;justify-content:center}
|
||||
.mistvideo-subtitles>*{margin-bottom:.5em;padding:.1em .3em;text-align:center;background:rgba(0,0,0,.6);white-space:pre-wrap}
|
||||
.mistvideo-subtitles>:empty{display:none}
|
||||
.mistvideo[data-fullscreen] .mistvideo-subtitles{font-size:3vh}
|
||||
.mistvideo-background{background-color:$background}
|
||||
.mistvideo-totalTime:before{content:'/';margin:.2em}
|
||||
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
|
||||
|
|
|
@ -1 +1 @@
|
|||
mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,i){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+e+") won't load if the page is run via file://");return false}if(!("MediaSource"in window)){return false}if(!MediaSource.isTypeSupported){return true}var r={};var a=false;for(var s in i.info.meta.tracks){if(i.info.meta.tracks[s].type=="meta"){if(i.info.meta.tracks[s].codec=="subtitle"){a=true}continue}if(!(i.info.meta.tracks[s].type in r)){r[i.info.meta.tracks[s].type]={}}r[i.info.meta.tracks[s].type][MistUtil.tracks.translateCodec(i.info.meta.tracks[s])]=1}var o=[];for(var n in r){var l=false;for(var f in r[n]){if(MediaSource.isTypeSupported('video/mp4;codecs="'+f+'"')){l=true;break}}if(l){o.push(n)}}if(a){for(var s in i.info.source){if(i.info.source[s].type=="html5/text/vtt"){o.push("subtitle");break}}}return o.length?o:false},player:function(){this.onreadylist=[]},scriptsrc:function(e){return e+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=this;this.onDashLoad=function(){if(e.destroyed){return}e.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in a.get){return a.get[t].apply(e,arguments)}var r=e[t];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,t,i){if(t in a.set){return a.set[t].call(e,i)}return e[t]=i}});if(e.info.type=="live"){a.get.duration=function(){var t=0;if(this.buffered.length){t=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-e.player.api.lastProgress.getTime())*.001;return t+i+-1*e.player.api.liveOffset+45};a.set.currentTime=function(t){var i=t-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(i*-10)/10+"s from live)");e.video.currentTime=t};MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});e.player.api.lastProgress=new Date;e.player.api.liveOffset=0}}else{i.api=r}if(e.options.autoplay){r.setAttribute("autoplay","")}if(e.options.loop&&e.info.type!="live"){r.setAttribute("loop","")}if(e.options.poster){r.setAttribute("poster",e.options.poster)}if(e.options.muted){r.muted=true}if(e.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,e.source.url,e.options.autoplay);i.dash=s;var o=["METRIC_ADDED","METRIC_UPDATED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];for(var n in dashjs.MediaPlayer.events){if(o.indexOf(n)<0){i.dash.on(dashjs.MediaPlayer.events[n],function(t){e.log("Player event fired: "+t.type)})}}e.player.setSize=function(e){this.api.style.width=e.width+"px";this.api.style.height=e.height+"px"};e.player.api.setSource=function(t){e.player.dash.attachSource(t)};var l=false;i.dash.on("allTextTracksAdded",function(){l=true});e.player.api.setSubtitle=function(t){if(!l){var r=function(){e.player.api.setSubtitle(t);i.dash.off("allTextTracksAdded",r)};i.dash.on("allTextTracksAdded",r);return}if(!t){i.dash.enableText(false);return}var a=i.dash.getTracksFor("text");for(var s in a){var o="idx"in t?t.idx:t.trackid;if(a[s].id==o){i.dash.setTextTrack(s);if(!i.dash.isTextEnabled()){i.dash.enableText()}return true}}return false};MistUtil.event.addListener(r,"progress",function(t){if(e.container.getAttribute("data-loading")=="stalled"){e.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};e.log("Built html");t(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(e.urlappend(mistplayers.dashjs.scriptsrc(e.options.host)),{onerror:function(t){var i="Failed to load dashjs.js";if(t.message){i+=": "+t.message}e.showError(i)},onload:i.onDashLoad},e)}};
|
||||
mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,i){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+e+") won't load if the page is run via file://");return false}if(!("MediaSource"in window)){return false}if(!MediaSource.isTypeSupported){return true}var r={};var a=false;for(var s in i.info.meta.tracks){if(i.info.meta.tracks[s].type=="meta"){if(i.info.meta.tracks[s].codec=="subtitle"){a=true}continue}if(!(i.info.meta.tracks[s].type in r)){r[i.info.meta.tracks[s].type]={}}r[i.info.meta.tracks[s].type][MistUtil.tracks.translateCodec(i.info.meta.tracks[s])]=1}var o=[];for(var n in r){var l=false;for(var f in r[n]){if(MediaSource.isTypeSupported('video/mp4;codecs="'+f+'"')){l=true;break}}if(l){o.push(n)}}if(a){for(var s in i.info.source){if(i.info.source[s].type=="html5/text/vtt"){o.push("subtitle");break}}}return o.length?o:false},player:function(){this.onreadylist=[]},scriptsrc:function(e){return e+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=this;this.onDashLoad=function(){if(e.destroyed){return}e.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in a.get){return a.get[t].apply(e,arguments)}var r=e[t];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,t,i){if(t in a.set){return a.set[t].call(e,i)}return e[t]=i}});if(e.info.type=="live"){a.get.duration=function(){var t=0;if(this.buffered.length){t=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-e.player.api.lastProgress.getTime())*.001;return t+i+-1*e.player.api.liveOffset+45};a.set.currentTime=function(t){var i=t-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(i*-10)/10+"s from live)");e.video.currentTime=t};MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});e.player.api.lastProgress=new Date;e.player.api.liveOffset=0}}else{i.api=r}if(e.options.autoplay){r.setAttribute("autoplay","")}if(e.options.loop&&e.info.type!="live"){r.setAttribute("loop","")}if(e.options.poster){r.setAttribute("poster",e.options.poster)}if(e.options.muted){r.muted=true}if(e.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,e.source.url,e.options.autoplay);i.dash=s;var o=["METRIC_ADDED","METRIC_UPDATED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];for(var n in dashjs.MediaPlayer.events){if(o.indexOf(n)<0){i.dash.on(dashjs.MediaPlayer.events[n],function(t){e.log("Player event fired: "+t.type)})}}e.player.setSize=function(e){this.api.style.width=e.width+"px";this.api.style.height=e.height+"px"};e.player.api.setSource=function(t){e.player.dash.attachSource(t)};if(e.options.controls!="stock"){i.dash.updateSettings({streaming:{text:{defaultEnabled:false}}})}var l=false;i.dash.on("allTextTracksAdded",function(){l=true});e.player.api.setSubtitle=function(t){if(!l){var r=function(){e.player.api.setSubtitle(t);i.dash.off("allTextTracksAdded",r)};i.dash.on("allTextTracksAdded",r);return}if(!t){i.dash.enableText(false);return}var a=i.dash.getTracksFor("text");for(var s in a){var o="idx"in t?t.idx:t.trackid;if(a[s].id==o){i.dash.setTextTrack(s);if(!i.dash.isTextEnabled()){i.dash.enableText()}return true}}return false};MistUtil.event.addListener(r,"progress",function(t){if(e.container.getAttribute("data-loading")=="stalled"){e.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};e.log("Built html");t(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(e.urlappend(mistplayers.dashjs.scriptsrc(e.options.host)),{onerror:function(t){var i="Failed to load dashjs.js";if(t.message){i+=": "+t.message}e.showError(i)},onload:i.onDashLoad},e)}};
|
261
embed/player.js
261
embed/player.js
|
@ -35,7 +35,8 @@ function MistVideo(streamName,options) {
|
|||
maxheight: false, //no max height (apart from targets dimensions)
|
||||
ABR_resize: true, //for supporting wrappers: when the player resizes, request a video track that matches the resolution best
|
||||
ABR_bitrate: true, //for supporting wrappers: when there are playback issues, request a lower bitrate video track
|
||||
useDateTime: true, //when the unix timestamp of the stream is known, display the date/time
|
||||
useDateTime: true, //when the unix timestamp of the stream is known, display the date/time,
|
||||
subscribeToMetaTrack: false, //pass [[track index,callback]]; the callback function will be called whenever the specified meta data track receives a message.
|
||||
MistVideoObject: false//no reference object is passed
|
||||
},options);
|
||||
if (options.host) { options.host = MistUtil.http.url.sanitizeHost(options.host); }
|
||||
|
@ -115,7 +116,6 @@ function MistVideo(streamName,options) {
|
|||
this.log("A reloadDelay of more than an hour was set: assuming milliseconds were intended. ReloadDelay is now "+options.reloadDelay+"s");
|
||||
}
|
||||
|
||||
|
||||
new MistSkin(this);
|
||||
|
||||
this.checkCombo = function(options,quiet) {
|
||||
|
@ -664,6 +664,250 @@ function MistVideo(streamName,options) {
|
|||
if (MistVideo.monitor) { MistVideo.monitor.reset(); }
|
||||
});
|
||||
}
|
||||
|
||||
if ("currentTime" in MistVideo.player.api) {
|
||||
var json_source = MistUtil.sources.find(MistVideo.info.source,{
|
||||
type: "html5/text/javascript",
|
||||
protocol: "ws"+(location.protocol.charAt(location.protocol.length-2) == "s" ? "s" : "")+":"
|
||||
});
|
||||
if (json_source) {
|
||||
MistVideo.metaTrackSubscriptions = {
|
||||
subscriptions: {},
|
||||
socket: null,
|
||||
listeners: {},
|
||||
init: function(){
|
||||
var me = this;
|
||||
this.socket = new WebSocket(MistUtil.http.url.addParam(MistVideo.urlappend(json_source.url),{rate:1}));
|
||||
me.send_queue = [];
|
||||
me.checktimer = null;
|
||||
me.s = function(obj){
|
||||
if (me.socket.readyState == me.socket.OPEN) {
|
||||
me.socket.send(JSON.stringify(obj));
|
||||
return true;
|
||||
}
|
||||
if (me.socket.readyState >= me.socket.CLOSING) {
|
||||
//reopen websocket
|
||||
me.init();
|
||||
}
|
||||
//add message to queue
|
||||
this.send_queue.push(obj);
|
||||
};
|
||||
|
||||
var stayahead = 5; //ask MistServer to fastforward to stayahead seconds ahead, so we receive messages earlier
|
||||
var isfarahead = false; //for rate limiting the 'pause when too far ahead'-function
|
||||
|
||||
me.socket.setTracks = function(){
|
||||
me.s({type:"tracks",meta:MistUtil.object.keys(me.subscriptions).join(",")});
|
||||
};
|
||||
me.socket.onopen = function(){
|
||||
MistVideo.log("Metadata socket opened");
|
||||
|
||||
me.socket.setTracks();
|
||||
if (MistVideo.player.api.playbackRate != 1) { me.s({type:"set_speed",play_rate:MistVideo.player.api.playbackRate}); }
|
||||
me.s({type:"seek",seek_time:Math.round(MistVideo.player.api.currentTime*1e3),ff_to:Math.round((MistVideo.player.api.currentTime+stayahead)*1e3)});
|
||||
me.socket.addEventListener("message",function(e){
|
||||
if (!e.data) { MistVideo.log("Subtitle websocket received empty message."); return; }
|
||||
var message = JSON.parse(e.data);
|
||||
if (!message) { MistVideo.log("Subtitle websocket received invalid message."); return; }
|
||||
|
||||
if (("time" in message) && ("track" in message) && ("data" in message)) {
|
||||
if (message.track in me.subscriptions) {
|
||||
//console.warn("received:",message.track,message.data);
|
||||
me.subscriptions[message.track].buffer.push(message);
|
||||
console.warn("received:",message.track,message.time*1e-3,"currentTime:",MistVideo.player.api.currentTime,"latency",Math.round(MistVideo.player.api.currentTime-message.time*1e-3),"bufferlength:",me.subscriptions[message.track].buffer.length,"timer:",!!me.checktimer);
|
||||
|
||||
if (!me.checktimer) {
|
||||
me.check();
|
||||
}
|
||||
else {
|
||||
var willCheckAt = MistVideo.timers.list[me.checktimer];
|
||||
if (willCheckAt) {
|
||||
var messageAt = (new Date()).getTime() + message.time - MistVideo.player.api.currentTime*1e3;
|
||||
if (willCheckAt > messageAt) {
|
||||
MistVideo.log("The metadata socket received a message that should be displayed sooner than the current check time; resetting");
|
||||
MistVideo.timers.stop(me.checktimer);
|
||||
me.checktimer = null;
|
||||
me.check();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//per track, the messages should arrive in the correct order and we shouldn't need to do sorting
|
||||
|
||||
|
||||
if ("type" in message) {
|
||||
switch (message.type) {
|
||||
case "on_time": {
|
||||
if (!isfarahead && (message.data.current > (MistVideo.player.api.currentTime + stayahead*6)*1e3)) {
|
||||
//the playing point for the metadata track is very far ahead of the player;
|
||||
isfarahead = true;
|
||||
me.s({type:"hold"});
|
||||
MistVideo.log("Pausing metadata buffer because it is very far ahead, checking again in 5 seconds: "+message.data.current+" > "+MistVideo.player.api.currentTime*1e3)
|
||||
MistVideo.timers.start(function(){
|
||||
if (!MistVideo.player.api.paused) { me.s({type:"play"}); }
|
||||
me.s({type:"fast_forward",ff_to:Math.round((MistVideo.player.api.currentTime+stayahead)*1e3)});
|
||||
},5e3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "seek": {
|
||||
for (var i in me.subscriptions) {
|
||||
me.subscriptions[i].buffer = [];
|
||||
}
|
||||
MistVideo.log("Cleared metadata buffer after completed seek");
|
||||
if (me.checktimer) {
|
||||
//there might be a timer going for some time in the future: stop it,
|
||||
MistVideo.timers.stop(me.checktimer);
|
||||
me.checktimer = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
me.socket.onclose = function(){
|
||||
//dont me.init();, send function will reopen if needed instead
|
||||
MistVideo.log("Metadata socket closed");
|
||||
}
|
||||
|
||||
while (me.send_queue.length && (me.socket.readyState == me.socket.OPEN)) {
|
||||
me.s(me.send_queue.shift());
|
||||
}
|
||||
};
|
||||
if (!("seeked" in this.listeners)) { //prevent duplication
|
||||
var lastff = (new Date()).getTime(); //init at now, as a seek with ff_to is also sent at init time
|
||||
|
||||
me.check = function(){
|
||||
//console.warn(me.checktimer,"check");
|
||||
if (me.checktimer) {
|
||||
MistVideo.timers.stop(me.checktimer);
|
||||
me.checktimer = null;
|
||||
}
|
||||
|
||||
if (MistVideo.player.api.paused) { return; }
|
||||
|
||||
var nextAtGlobal = null;
|
||||
for (var i in me.subscriptions) {
|
||||
var buffer = me.subscriptions[i].buffer;
|
||||
while (buffer.length && (buffer[0].time <= MistVideo.player.api.currentTime*1e3)) {
|
||||
var message = buffer.shift();
|
||||
|
||||
if (message.time < (MistVideo.player.api.currentTime - 5) * 1e3) {
|
||||
//the message is at least 5 seconds older than the video time
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
for (var j in me.subscriptions[i].callbacks) {
|
||||
me.subscriptions[i].callbacks[j].call(MistVideo,message);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer.length) {
|
||||
//save when the next message should be played
|
||||
nextAtGlobal = Math.min(nextAtGlobal === null ? 1e9 : nextAtGlobal,buffer[0].time);
|
||||
}
|
||||
}
|
||||
|
||||
//add rate limiting: do not ask for fast forward more than once every 5 seconds
|
||||
var now = (new Date()).getTime()
|
||||
if (now > lastff+5e3) {
|
||||
me.s({type:"fast_forward",ff_to:Math.round((MistVideo.player.api.currentTime+stayahead)*1e3)});
|
||||
lastff = now;
|
||||
}
|
||||
|
||||
|
||||
if (nextAtGlobal) {
|
||||
var delay = nextAtGlobal-MistVideo.player.api.currentTime*1e3;
|
||||
me.checktimer = MistVideo.timers.start(function(){
|
||||
me.check();
|
||||
},delay);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.listeners.seeked = MistUtil.event.addListener(MistVideo.video,"seeked",function(){
|
||||
for (var i in me.subscriptions) {
|
||||
me.subscriptions[i].buffer = [];
|
||||
}
|
||||
me.s({type:"seek",seek_time:Math.round(MistVideo.player.api.currentTime*1e3),ff_to:Math.round((MistVideo.player.api.currentTime+stayahead)*1e3)});
|
||||
lastff = (new Date()).getTime();
|
||||
//console.warn("seek to",Math.round(MistVideo.player.api.currentTime*1e3));
|
||||
});
|
||||
this.listeners.pause = MistUtil.event.addListener(MistVideo.video,"pause",function(){
|
||||
me.s({type:"hold"});
|
||||
MistVideo.timers.stop(me.checktimer);
|
||||
me.checktimer = null;
|
||||
});
|
||||
this.listeners.playing = MistUtil.event.addListener(MistVideo.video,"playing",function(){
|
||||
me.s({type:"play"});
|
||||
if (!me.checktimer) me.check();
|
||||
});
|
||||
this.listeners.ratechange = MistUtil.event.addListener(MistVideo.video,"ratechange",function(){
|
||||
me.s({type:"set_speed",play_rate:MistVideo.player.api.playbackRate});
|
||||
});
|
||||
}
|
||||
},
|
||||
destroy: function(){
|
||||
MistVideo.log("Closing metadata socket..");
|
||||
this.socket.close();
|
||||
this.socket = null;
|
||||
this.subscriptions = {};
|
||||
for (var i in this.listeners) {
|
||||
MistUtil.event.removeListener(this.listeners[i]);
|
||||
}
|
||||
this.listeners = {};
|
||||
},
|
||||
add: function (trackid,callback) {
|
||||
if (typeof callback != "function") { return; }
|
||||
|
||||
if (!(trackid in this.subscriptions)) {
|
||||
this.subscriptions[trackid] = {
|
||||
buffer: [],
|
||||
callbacks: []
|
||||
};
|
||||
}
|
||||
this.subscriptions[trackid].callbacks.push(callback);
|
||||
|
||||
if (this.socket === null) {
|
||||
this.init();
|
||||
}
|
||||
else {
|
||||
this.socket.setTracks();
|
||||
}
|
||||
},
|
||||
remove: function(trackid,callback){
|
||||
if (trackid in this.subscriptions) {
|
||||
for (var i in this.subscriptions[trackid].callbacks) {
|
||||
if (callback == this.subscriptions[trackid].callbacks[i]) {
|
||||
this.subscriptions[trackid].callbacks.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.subscriptions[trackid].callbacks.length == 0) {
|
||||
delete this.subscriptions[trackid];
|
||||
if (MistUtil.object.keys(this.subscriptions).length) {
|
||||
this.socket.setTracks();
|
||||
}
|
||||
else {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (options.subscribeToMetaTrack.length) {
|
||||
if (typeof options.subscribeToMetaTrack[0] != "object") {
|
||||
options.subscribeToMetaTrack = [options.subscribeToMetaTrack];
|
||||
}
|
||||
for (var i in options.subscribeToMetaTrack) {
|
||||
MistVideo.metaTrackSubscriptions.add.apply(MistVideo.metaTrackSubscriptions,options.subscribeToMetaTrack[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//remove placeholder and add UI structure
|
||||
|
@ -1051,7 +1295,7 @@ function MistVideo(streamName,options) {
|
|||
//switch to polling-mode if websockets are not supported
|
||||
|
||||
function openWithGet() {
|
||||
var url = MistUtil.http.url.addParam(MistVideo.urlappend(options.host+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1});
|
||||
var url = MistUtil.http.url.addParam(MistVideo.urlappend(options.host+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1,inclzero:1});
|
||||
MistVideo.log("Requesting stream info from "+url);
|
||||
MistUtil.http.get(url,function(d){
|
||||
if (MistVideo.destroyed) { return; }
|
||||
|
@ -1070,7 +1314,7 @@ function MistVideo(streamName,options) {
|
|||
function openSocket() {
|
||||
MistVideo.log("Opening stream status stream through websocket..");
|
||||
var url = MistVideo.options.host.replace(/^http/i,"ws");
|
||||
url = MistUtil.http.url.addParam(MistVideo.urlappend(url+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1});
|
||||
url = MistUtil.http.url.addParam(MistVideo.urlappend(url+"/json_"+encodeURIComponent(MistVideo.stream)+".js"),{metaeverywhere:1,inclzero:1});
|
||||
var socket;
|
||||
try {
|
||||
socket = new WebSocket(url);
|
||||
|
@ -1415,10 +1659,8 @@ function MistVideo(streamName,options) {
|
|||
if (diff) {
|
||||
//console.log("Difference",diff,data,MistVideo.info);
|
||||
|
||||
if ("source" in diff) {
|
||||
if ("error" in MistVideo.info) {
|
||||
MistVideo.reload("Reloading, stream info has error");
|
||||
}
|
||||
if (("source" in diff) && ("error" in MistVideo.info)) {
|
||||
MistVideo.reload("Reloading, stream info has error");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1513,6 +1755,9 @@ function MistVideo(streamName,options) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (this.metaTrackSubscriptions && this.metaTrackSubscriptions.socket) {
|
||||
this.metaTrackSubscriptions.destroy();
|
||||
}
|
||||
if ((this.UI) && (this.UI.elements)) {
|
||||
for (var i in this.UI.elements) {
|
||||
var e = this.UI.elements[i];
|
||||
|
|
124
embed/skins.js
124
embed/skins.js
|
@ -59,7 +59,8 @@ MistSkins["default"] = {
|
|||
type: "container",
|
||||
children: [
|
||||
{type: "videobackground", alwaysDisplay: false, delay: 5 },
|
||||
{type: "video"}
|
||||
{type: "video"},
|
||||
{type: "subtitles"}
|
||||
]
|
||||
},
|
||||
controls: {
|
||||
|
@ -1534,19 +1535,29 @@ MistSkins["default"] = {
|
|||
var type = tracktypes[j];
|
||||
var t = tracks[type];
|
||||
|
||||
if (MistUtil.array.indexOf(["video","audio","subtitle"],type) <= -1) {
|
||||
//Do not display this track type
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == "subtitle") {
|
||||
if ((!("player" in MistVideo)) || (!("api" in MistVideo.player)) || (!("setSubtitle" in MistVideo.player.api))) {
|
||||
if ((!("player" in MistVideo)) || (!("api" in MistVideo.player)) || (!("setWSSubtitle" in MistVideo.player.api) && !("setSubtitle" in MistVideo.player.api))) {
|
||||
//this player does not support adding subtitles, don't show track selection in the interface
|
||||
MistVideo.log("Subtitle selection was disabled as this player does not support it.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var mime = "html5/text/vtt"
|
||||
if ("setWSSubtitle" in MistVideo.player.api) {
|
||||
mime = "html5/text/javascript";
|
||||
}
|
||||
|
||||
//check if the VTT output is available
|
||||
var subtitleSource = false;
|
||||
for (var i in MistVideo.info.source) {
|
||||
var source = MistVideo.info.source[i];
|
||||
//this is a subtitle source, and it's the same protocol (HTTP/HTTPS) as the video source
|
||||
if ((source.type == "html5/text/vtt") && (MistUtil.http.url.split(source.url).protocol == MistUtil.http.url.split(MistVideo.source.url).protocol.replace(/^ws/,"http"))) {
|
||||
if ((source.type == mime) && (MistUtil.http.url.split(source.url).protocol == MistUtil.http.url.split(MistVideo.source.url).protocol.replace(/^ws/,"http"))) {
|
||||
subtitleSource = source.url.replace(/.srt$/,".vtt");
|
||||
break;
|
||||
}
|
||||
|
@ -1554,7 +1565,7 @@ MistSkins["default"] = {
|
|||
|
||||
if (!subtitleSource) {
|
||||
//if we can't find a subtitle output, don't show track selection in the interface
|
||||
MistVideo.log("Subtitle selection was disabled as an SRT source could not be found.");
|
||||
MistVideo.log("Subtitle selection was disabled as a source could not be found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1722,16 +1733,21 @@ MistSkins["default"] = {
|
|||
}
|
||||
catch (e) {}
|
||||
|
||||
if (this.value != "") {
|
||||
//gather metadata for this subtitle track here
|
||||
var trackinfo = MistUtil.object.extend({},t[this.value]);
|
||||
trackinfo.label = orderValues(trackinfo.describe).join(" ");
|
||||
|
||||
trackinfo.src = MistUtil.http.url.addParam(subtitleSource,{track:this.value});
|
||||
MistVideo.player.api.setSubtitle(trackinfo);
|
||||
if ("setWSSubtitle" in MistVideo.player.api) {
|
||||
MistVideo.player.api.setWSSubtitle(this.value == "" ? undefined : this.value);
|
||||
}
|
||||
else {
|
||||
MistVideo.player.api.setSubtitle();
|
||||
if (this.value != "") {
|
||||
//gather metadata for this subtitle track here
|
||||
var trackinfo = MistUtil.object.extend({},t[this.value]);
|
||||
trackinfo.label = orderValues(trackinfo.describe).join(" ");
|
||||
|
||||
trackinfo.src = MistUtil.http.url.addParam(subtitleSource,{track:this.value});
|
||||
MistVideo.player.api.setSubtitle(trackinfo);
|
||||
}
|
||||
else {
|
||||
MistVideo.player.api.setSubtitle();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2101,6 +2117,7 @@ MistSkins["default"] = {
|
|||
}
|
||||
|
||||
if (event.defaultPrevented) {
|
||||
MistVideo.log("Error event was defaultPrevented, not showing.");
|
||||
container.clear();
|
||||
}
|
||||
};
|
||||
|
@ -2277,8 +2294,89 @@ MistSkins["default"] = {
|
|||
|
||||
|
||||
return ele;
|
||||
}
|
||||
},
|
||||
subtitles: function(options){
|
||||
if (!("WebSocket" in window)) { return false; }
|
||||
var MistVideo = this;
|
||||
if (!("player" in MistVideo) || !("api" in MistVideo.player) || !("currentTime" in MistVideo.player.api)) { return false; }
|
||||
|
||||
if (!("metaTrackSubscriptions" in MistVideo)) { return false; }
|
||||
|
||||
function clearFormatting(str) {
|
||||
str = str.replace(/\<\/?[bui]\>/gi,""); //remove <b>,</b>,<u>,</u>,<i>,</i>
|
||||
str = str.replace(/{\/?[bui]}/gi,""); //remove {b},{/b},{u},{/u},{i},{/i}
|
||||
str = str.replace(/{\\a\d+}/gi,""); //remove {\a3} (line position)
|
||||
str = str.replace(/\<\/?font[^>]*?\>/gi,""); //remove <font color="white">,</font>
|
||||
return str;
|
||||
}
|
||||
|
||||
var container = document.createElement("div");
|
||||
|
||||
var c = document.createElement("span");
|
||||
container.appendChild(c);
|
||||
var textNode = document.createTextNode("");
|
||||
c.appendChild(textNode);
|
||||
|
||||
var timer = false;
|
||||
function displayMessage(message) {
|
||||
textNode.nodeValue = clearFormatting(message.data);
|
||||
if (timer) {
|
||||
//a previous message is still being displayed, remove the timer so that it doesn't remove the new message
|
||||
MistVideo.timers.stop(timer);
|
||||
timer = null;
|
||||
}
|
||||
|
||||
|
||||
function setTimer(delay) {
|
||||
timer = MistVideo.timers.start(function(){
|
||||
|
||||
if (MistVideo.player.api.paused) {
|
||||
//leave the subtitle for now, and start a new timer once the video starts playing
|
||||
|
||||
var playing = MistUtil.event.addListener(MistVideo.video,"playing",function(){
|
||||
setTimer(message.time + ("duration" in message ? message.duration : 5e3) - MistVideo.player.api.currentTime*1e3);
|
||||
MistUtil.event.removeListener(playing);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
textNode.nodeValue = "";
|
||||
},delay);
|
||||
}
|
||||
setTimer("duration" in message ? message.duration : 5e3);
|
||||
|
||||
}
|
||||
|
||||
//when seeking, clear the current subtitle message
|
||||
MistUtil.event.addListener(MistVideo.video,"seeked",function(){
|
||||
textNode.nodeValue = "";
|
||||
if (timer) { MistVideo.timers.stop(timer); }
|
||||
timer = null;
|
||||
});
|
||||
|
||||
if (!("setWSSubtitle" in MistVideo.player.api)) {
|
||||
//insert generic subtitle function unless it already exists
|
||||
var trackid = false;
|
||||
MistVideo.player.api.setWSSubtitle = function(id){
|
||||
if (id == trackid) { return; } //already selected
|
||||
|
||||
//first add, then remove: this prevents the websocket closing because no tracks are selected
|
||||
|
||||
if (typeof id != "undefined") {
|
||||
MistVideo.metaTrackSubscriptions.add(id,displayMessage);
|
||||
}
|
||||
|
||||
if (id != trackid) {
|
||||
MistVideo.metaTrackSubscriptions.remove(trackid,displayMessage);
|
||||
}
|
||||
|
||||
trackid = (id == "undefined" ? false : id);
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
},
|
||||
colors: {
|
||||
fill: "#fff",
|
||||
|
|
|
@ -10,6 +10,28 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.mistvideo-subtitles {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
.mistvideo-subtitles > * {
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.1em 0.3em;
|
||||
text-align: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.mistvideo-subtitles > *:empty {
|
||||
display: none;
|
||||
}
|
||||
.mistvideo[data-fullscreen] .mistvideo-subtitles {
|
||||
font-size: 3vh;
|
||||
}
|
||||
.mistvideo-background { background-color: $background; }
|
||||
.mistvideo-totalTime:before {
|
||||
content: '/';
|
||||
|
|
|
@ -848,7 +848,7 @@ var MistUtil = {
|
|||
}
|
||||
break;
|
||||
case "rate":
|
||||
name[j] = Math.round(track.rate)+"Khz";
|
||||
name[j] = Math.round(track.rate*1e-3)+"Khz";
|
||||
break;
|
||||
case "language":
|
||||
if (track[j] != "Undetermined") { name[j] = track[j]; }
|
||||
|
@ -1130,5 +1130,35 @@ var MistUtil = {
|
|||
getAndroid: function(){
|
||||
var match = navigator.userAgent.toLowerCase().match(/android\s([\d\.]*)/i);
|
||||
return match ? match[1] : false;
|
||||
},
|
||||
sources: {
|
||||
find: function(sources,matchObj){
|
||||
/*
|
||||
Example use:
|
||||
MistUtil.sources.find(MistVideo.info.source,{
|
||||
type: "html5/text/javascript",
|
||||
protocol: "wss:"
|
||||
})
|
||||
*/
|
||||
|
||||
outer:
|
||||
for (var i in sources) {
|
||||
for (var j in matchObj) {
|
||||
if (j == "protocol") {
|
||||
if (sources[i].url.slice(0,matchObj.protocol.length) != matchObj.protocol) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sources[i][j] != matchObj[j]) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
//if any key of matchObj did not match the source, the outer loop was continued and this code does not execute
|
||||
return sources[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -177,6 +177,9 @@ p.prototype.build = function (MistVideo,callback) {
|
|||
MistVideo.player.dash.attachSource(url);
|
||||
};
|
||||
|
||||
if (MistVideo.options.controls != "stock"){
|
||||
me.dash.updateSettings({streaming:{text:{defaultEnabled:false}}});
|
||||
}
|
||||
var subsloaded = false;
|
||||
me.dash.on("allTextTracksAdded",function(){
|
||||
subsloaded = true;
|
||||
|
|
|
@ -36,15 +36,15 @@ $controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checkl
|
|||
1E3)});break;case "selectinput":e=$("<div>").addClass("selectinput");l=$("<select>");e.append(l);l.data("input",!1);for(c in d.selectinput)o=$("<option>"),l.append(o),"string"==typeof d.selectinput[c]?o.text(d.selectinput[c]):(o.text(d.selectinput[c][1]),"string"==typeof d.selectinput[c][0]?o.val(d.selectinput[c][0]):(o.val("CUSTOM"),l.data("input")||l.data("input",UI.buildUI([d.selectinput[c][0]]).children())));l.data("input")&&e.append(l.data("input"));l.change(function(){"CUSTOM"==$(this).val()?
|
||||
$(this).data("input").css("display","flex"):$(this).data("input").hide()});l.trigger("change");break;case "inputlist":e=$("<div>").addClass("inputlist");e.data("newitem",function(){var a;if("input"in d)a=UI.buildUI([d.input]).find(".field_container");else{var b=Object.assign({},d);delete b.validate;delete b.pointer;b.type="str";a=UI.buildUI([b]).find(".field_container")}a.removeClass("isSetting");a.addClass("listitem");var c=function(b){$(this).is(":last-child")?""!=$(this).find(".field").getval()?
|
||||
(b=a.clone().keyup(c),b.find(".field").setval(""),$(this).after(b)):8==b.which&&$(this).prev().find(".field").focus():""==$(this).find(".field").getval()&&(b=$(this).prev(),b.length||(b=$(this).next()),b.find(".field").focus(),$(this).remove())};a.keyup(c);return a});e.append(e.data("newitem"));break;case "sublist":e=$("<div>").addClass("sublist");l=$("<div>").addClass("curvals");l.append($("<span>").text("None."));var x=$("<div>").addClass("itemsettings"),i=$("<button>").text("New "+d.itemLabel),
|
||||
g=d.sublist,f=d,k=e,q=m;e.data("build",function(a,b){for(var c in f.saveas)c in a||delete f.saveas[c];f.saveas=Object.assign(f.saveas,a);c="New";"undefined"!=typeof b&&(c="Edit");c=UI.buildUI([$("<h4>").text(c+" "+f.itemLabel)].concat(g).concat([{label:"Save first",type:"str",classes:["onlyshowhelp"],validate:[function(){return{msg:"Did you want to save this "+f.itemLabel+"?",classes:["red"]}}]},{type:"buttons",buttons:[{label:"Cancel",type:"cancel","function":function(){x.html("");i.show();q.show()}},
|
||||
{label:"Save "+f.itemLabel,type:"save",preSave:function(){$(this).closest(".input_container").find(".onlyshowhelp").closest("label").hide()},failedValidate:function(){$(this).closest(".input_container").find(".onlyshowhelp").closest("label").show()},"function":function(){var a=k.getval(),c=Object.assign({},f.saveas),d;for(d in c)null===c[d]&&delete c[d];"undefined"==typeof b?a.push(c):a[b]=c;k.setval(a);x.html("");i.show();q.show()}}]}]));x.html(c);i.hide();q.hide()});var G=e;i.click(function(){G.data("build")({})});
|
||||
g=d.sublist,f=d,k=e,r=m;e.data("build",function(a,b){for(var c in f.saveas)c in a||delete f.saveas[c];f.saveas=Object.assign(f.saveas,a);c="New";"undefined"!=typeof b&&(c="Edit");c=UI.buildUI([$("<h4>").text(c+" "+f.itemLabel)].concat(g).concat([{label:"Save first",type:"str",classes:["onlyshowhelp"],validate:[function(){return{msg:"Did you want to save this "+f.itemLabel+"?",classes:["red"]}}]},{type:"buttons",buttons:[{label:"Cancel",type:"cancel","function":function(){x.html("");i.show();r.show()}},
|
||||
{label:"Save "+f.itemLabel,type:"save",preSave:function(){$(this).closest(".input_container").find(".onlyshowhelp").closest("label").hide()},failedValidate:function(){$(this).closest(".input_container").find(".onlyshowhelp").closest("label").show()},"function":function(){var a=k.getval(),c=Object.assign({},f.saveas),d;for(d in c)null===c[d]&&delete c[d];"undefined"==typeof b?a.push(c):a[b]=c;k.setval(a);x.html("");i.show();r.show()}}]}]));x.html(c);i.hide();r.hide()});var G=e;i.click(function(){G.data("build")({})});
|
||||
g.unshift({type:"str",label:"Human readable name",placeholder:"none",help:"A convenient name to describe this "+d.itemLabel+". It won't be used by MistServer.",pointer:{main:d.saveas,index:"x-LSP-name"}});e.data("savelist",[]);e.append(l).append(i);b.append(x);break;case "json":e=$("<textarea>").on("keydown",function(a){a.stopPropagation()}).on("keyup change",function(){this.style.height="";this.style.height=(this.scrollHeight?this.scrollHeight+20:14*this.value.split("\n").length+20)+"px"}).css("min-height",
|
||||
"3em");l=function(a,b){if(""!=$(b).val()&&null===a)return{msg:"Invalid json",classes:["red"]}};"validate"in d?d.validate.push(l):d.validate=[l];break;case "bitmask":e=$("<div>").addClass("bitmask");for(c in d.bitmask)e.append($("<label>").append($("<input>").attr("type","checkbox").attr("name","bitmask_"+("pointer"in d?d.pointer.index:"")).attr("value",d.bitmask[c][0]).addClass("field")).append($("<span>").text(d.bitmask[c][1])));m.attr("for","none");break;default:e=$("<input>").attr("type","text"),
|
||||
"maxlength"in d&&e.attr("maxlength",d.maxlength),"minlength"in d&&e.attr("minlength",d.minlength)}e.addClass("field").data("opts",d);"pointer"in d&&e.attr("name",d.pointer.index);h.append(e);if("classes"in d)for(j in d.classes)e.addClass(d.classes[j]);"placeholder"in d&&e.attr("placeholder",d.placeholder);"default"in d&&e.attr("placeholder",d["default"]);"unit"in d&&h.append($("<span>").addClass("unit").html(d.unit));"prefix"in d&&h.prepend($("<span>").addClass("unit").html(d.prefix));"readonly"in
|
||||
d&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in d&&h.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")&&
|
||||
h.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);if("dependent"in d)for(c in d.dependent)m.attr("data-dependent-"+c,d.dependent[c]);switch(d.type){case "browse":l=$("<div>").addClass("grouper").append(m);b.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});h.append(l);l.click(function(){function a(b){h.text("Loading..");mist.send(function(a){e.text(a.browse.path[0]);mist.data.LTS&&d.setval(a.browse.path[0]+"/");h.html(i.clone(true).text("..").attr("title",
|
||||
"Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var f=a.browse.subdirectories[b];h.append(i.clone(true).attr("title",e.text()+m+f).text(f))}}if(a.browse.files){a.browse.files.sort();for(b in a.browse.files){var f=a.browse.files[b],l=e.text()+m+f,f=$("<a>").text(f).addClass("file").attr("title",l);h.append(f);if(k){var j=true,q;for(q in k)if(typeof k[q]!="undefined"&&mist.inputMatch(k[q],l)){j=false;break}j&&f.hide()}f.click(function(){var a=
|
||||
"Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var b in a.browse.subdirectories){var f=a.browse.subdirectories[b];h.append(i.clone(true).attr("title",e.text()+m+f).text(f))}}if(a.browse.files){a.browse.files.sort();for(b in a.browse.files){var f=a.browse.files[b],l=e.text()+m+f,f=$("<a>").text(f).addClass("file").attr("title",l);h.append(f);if(k){var j=true,r;for(r in k)if(typeof k[r]!="undefined"&&mist.inputMatch(k[r],l)){j=false;break}j&&f.hide()}f.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),f=$("<button>").text("Stop browsing").click(function(){g.show();c.remove();d.removeAttr("readonly").css("opacity",1)}),e=$("<span>").addClass("field"),h=$("<div>").addClass("browse_contents"),i=$("<a>").addClass("folder"),k=d.data("filetypes");
|
||||
b.append(c);c.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(e).append(f))).append(h);var m="/";mist.data.config.version.indexOf("indows")>-1&&(m="\\");i.click(function(){var b=e.text()+m+$(this).text();a(b)});b=d.getval();f=b.split("://");f.length>1&&(b=f[0]=="file"?f[1]:"");b=b.split(m);b.pop();b=b.join(m);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",
|
||||
|
@ -94,7 +94,7 @@ value:m},$("<br>"),$("<h3>").text("Write config now"),{type:"help",help:"Tick th
|
|||
a)}}]}]));if(mist.data.LTS){var f=function(a){function b(a){if(a.update){var d="";"progress"in a.update&&(d=" ("+a.update.progress+"%)");e.text("Updating.."+d);c(a.log);setTimeout(function(){mist.send(function(a){b(a)},{update:true})},1E3)}else UI.showTab("Overview")}function c(a){a=a.filter(function(a){return a[1]=="UPDR"});if(a.length){var b=$("<div>");e.append(b);for(var d in a)b.append($("<div>").text(a[d][2]))}}if(!a.update||!("uptodate"in a.update)){e.text("Unknown, checking..");setTimeout(function(){mist.send(function(a){"update"in
|
||||
a&&f(a)},{checkupdate:true})},5E3)}else if(a.update.error)e.addClass("red").text(a.update.error);else if(a.update.uptodate)e.text("Your version is up to date.").addClass("green");else{if(a.update.progress){e.addClass("orange").removeClass("red").text("Updating..");b(a)}else{e.text("");e.append($("<span>").addClass("red").text("On "+(new Date(a.update.date)).toLocaleDateString()+" version "+a.update.version+" became available."));(!a.update.url||a.update.url.slice(-4)!=".zip")&&e.append($("<button>").text("Rolling update").css({"font-size":"1em",
|
||||
"margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){e.addClass("orange").removeClass("red").text("Rolling update command sent..");mist.send(function(a){b(a)},{autoupdate:true})}}));var d=$("<a>").attr("href",a.update.url).attr("target","_blank").text("Manual download");d[0].protocol="https:";e.append($("<div>").append(d))}c(a.log)}};f(mist.data);if("license"in mist.data.config){if("active_products"in mist.data.config.license&&Object.keys(mist.data.config.license.active_products).length){var k=
|
||||
$("<table>").css("text-indent","0");o.html(k);k.append($("<tr>").append($("<th>").append("Product")).append($("<th>").append("Updates until")).append($("<th>").append("Use until")).append($("<th>").append("Max. simul. instances")));for(var q in mist.data.config.license.active_products){var G=mist.data.config.license.active_products[q];k.append($("<tr>").append($("<td>").append(G.name)).append($("<td>").append(G.updates_final?G.updates_final:"∞")).append($("<td>").append(G.use_final?G.use_final:
|
||||
$("<table>").css("text-indent","0");o.html(k);k.append($("<tr>").append($("<th>").append("Product")).append($("<th>").append("Updates until")).append($("<th>").append("Use until")).append($("<th>").append("Max. simul. instances")));for(var r in mist.data.config.license.active_products){var G=mist.data.config.license.active_products[r];k.append($("<tr>").append($("<td>").append(G.name)).append($("<td>").append(G.updates_final?G.updates_final:"∞")).append($("<td>").append(G.use_final?G.use_final:
|
||||
"∞")).append($("<td>").append(G.amount?G.amount:"∞")))}}else o.text("None. ");o.append($("<a>").text("More details").attr("href","https://shop.mistserver.org/myinvoices").attr("target","_blank"))}}else e.text("");var Da=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities=true;mist.send(function(){Ea()},a)},Ea=function(){j.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..";h.text(a);t.text(UI.format.dateTime(mist.data.config.time,"long"));m.html("");a=0;"license"in mist.data.config&&"user_msg"in mist.data.config.license&&mist.data.log.unshift([mist.data.config.license.time,"ERROR",mist.data.config.license.user_msg]);for(var 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]));m.append($("<div>").append($("<span>").append(UI.format.time(c[0]))).append(d));if(a==5)break}}a==0&&m.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)}l.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);
|
||||
|
@ -131,36 +131,36 @@ c=d.length?c+d.join(", "):c+"None.";if(d.length!=a.length){a=a.filter(function(a
|
|||
"both")).append($("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Protocol")).append($("<th>").text("Status")).append($("<th>").text("Settings")).append($("<th>")))).append(F));var Ha=function(){function a(b){var c=mist.data.capabilities.connectors[b.connector];if(!c)return"";var d=[],e=["required","optional"],w;for(w in e)for(var g in c[e[w]])b[g]&&b[g]!=""?d.push(g+": "+b[g]):c[e[w]][g]["default"]&&d.push(g+": "+c[e[w]][g]["default"]);return $("<span>").addClass("description").text(d.join(", "))}
|
||||
F.html("");for(var b in mist.data.config.protocols){var c=mist.data.config.protocols[b],d=mist.data.capabilities.connectors[c.connector];F.append($("<tr>").data("index",b).append($("<td>").text(d&&d.friendly?d.friendly:c.connector)).append($("<td>").html(UI.format.status(c))).append($("<td>").html(a(c))).append($("<td>").css("text-align","right").html($("<button>").text("Edit").click(function(){UI.navto("Edit Protocol",$(this).closest("tr").data("index"))})).append($("<button>").text("Delete").click(function(){var a=
|
||||
$(this).closest("tr").data("index");if(confirm('Are you sure you want to delete the protocol "'+mist.data.config.protocols[a].connector+'"?')){mist.send(function(){UI.navto("Protocols")},{deleteprotocol:mist.data.config.protocols[a]});mist.data.config.protocols.splice(a,1)}}))))}};Ha();UI.interval.set(function(){mist.send(function(){Ha()})},1E4);break;case "Edit Protocol":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,b)},{capabilities:!0});c.append("Loading..");return}z=
|
||||
!1;""!=b&&0<=b&&(z=!0);var V={};for(q in mist.data.config.protocols)V[mist.data.config.protocols[q].connector]=1;var Ja=function(a){var b=mist.data.capabilities.connectors[a],c=mist.convertBuildOptions(b,p);if(z)var d=$.extend({},p);c.push({type:"hidden",pointer:{main:p,index:"connector"},value:a});c.push({type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={};z?a.updateprotocol=[d,p]:a.addprotocol=p;mist.send(function(){UI.navto("Protocols")},a)}},{type:"cancel",label:"Cancel",
|
||||
!1;""!=b&&0<=b&&(z=!0);var V={};for(r in mist.data.config.protocols)V[mist.data.config.protocols[r].connector]=1;var Ja=function(a){var b=mist.data.capabilities.connectors[a],c=mist.convertBuildOptions(b,p);if(z)var d=$.extend({},p);c.push({type:"hidden",pointer:{main:p,index:"connector"},value:a});c.push({type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={};z?a.updateprotocol=[d,p]:a.addprotocol=p;mist.send(function(){UI.navto("Protocols")},a)}},{type:"cancel",label:"Cancel",
|
||||
"function":function(){UI.navto("Protocols")}}]});if("deps"in b&&b.deps!=""){k=$("<span>").text("Dependencies:");$ul=$("<ul>");k.append($ul);if(typeof b.deps=="string")b.deps=b.deps.split(", ");for(var e in b.deps){a=$("<li>").text(b.deps[e]+" ");$ul.append(a);typeof V[b.deps[e]]!="undefined"||typeof V[b.deps[e]+".exe"]!="undefined"?a.append($("<span>").addClass("green").text("(Configured)")):a.append($("<span>").addClass("red").text("(Not yet configured)"))}c.unshift({type:"text",text:k[0].innerHTML})}return UI.buildUI(c)},
|
||||
V={};for(q in mist.data.config.protocols)V[mist.data.config.protocols[q].connector]=1;if(z){var s=mist.data.config.protocols[b],p=s;c.find("h2").append(' "'+s.connector+'"');c.append(Ja(s.connector))}else{c.html($("<h2>").text("New Protocol"));var p={},u=[["",""]];for(q in mist.data.capabilities.connectors)u.push([q,mist.data.capabilities.connectors[q].friendly?mist.data.capabilities.connectors[q].friendly:q]);var S=$("<span>");c.append(UI.buildUI([{label:"Protocol",type:"select",select:u,"function":function(){$(this).getval()!=
|
||||
V={};for(r in mist.data.config.protocols)V[mist.data.config.protocols[r].connector]=1;if(z){var s=mist.data.config.protocols[b],p=s;c.find("h2").append(' "'+s.connector+'"');c.append(Ja(s.connector))}else{c.html($("<h2>").text("New Protocol"));var p={},u=[["",""]];for(r in mist.data.capabilities.connectors)u.push([r,mist.data.capabilities.connectors[r].friendly?mist.data.capabilities.connectors[r].friendly:r]);var S=$("<span>");c.append(UI.buildUI([{label:"Protocol",type:"select",select:u,"function":function(){$(this).getval()!=
|
||||
""&&S.html(Ja($(this).getval()))}}])).append(S)}break;case "Streams":if(!("capabilities"in mist.data)){c.html("Loading..");mist.send(function(){UI.navto(a)},{capabilities:!0});return}var La=$("<button>"),O=$("<span>").text("Loading..");c.append(UI.buildUI([{type:"help",help:"Here you can create, edit or delete new and existing streams. Go to stream preview or embed a video player on your website."},$("<div>").css({width:"45.25em",display:"flex","justify-content":"flex-end"}).append(La).append($("<button>").text("Create a new stream").click(function(){UI.navto("Edit")}))])).append(O);
|
||||
""==b&&(g=mist.stored.get(),"viewmode"in g&&(b=g.viewmode));La.text("Switch to "+("thumbnails"==b?"list":"thumbnail")+" view").click(function(){mist.stored.set("viewmode",b=="thumbnails"?"list":"thumbnails");UI.navto("Streams",b=="thumbnails"?"list":"thumbnails")});var C=$.extend(!0,{},mist.data.streams),oa=function(a,b){var c=$.extend({},b);delete c.meta;delete c.error;c.online=2;c.name=a;c.ischild=true;return c},pa=function(b,d,e){O.remove();switch(b){case "thumbnails":var g=$("<div>").addClass("preview_icons"),
|
||||
f;f=e||[];d.sort();d.unshift("");O.remove();c.append($("<h2>").text(a)).append(UI.buildUI([{label:"Filter the streams",type:"datalist",datalist:d,pointer:{main:{},index:"stream"},help:"If you type something here, the box below will only show streams with names that contain your text.","function":function(){var a=$(this).val();g.children().each(function(){$(this).hide();$(this).attr("data-stream").indexOf(a)>-1&&$(this).show()})}}]));d.shift();c.append($("<span>").addClass("description").text("Choose a stream below.")).append(g);
|
||||
for(var h in d){var b=d[h],i="",k=$("<button>").text("Delete").click(function(){var a=$(this).closest("div").attr("data-stream");if(confirm('Are you sure you want to delete the stream "'+a+'"?')){delete mist.data.streams[a];var b={};b.deletestream=[a];mist.send(function(){UI.navto("Streams")},b)}}),l=$("<button>").text("Settings").click(function(){UI.navto("Edit",$(this).closest("div").attr("data-stream"))}),e=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("div").attr("data-stream"))}),
|
||||
Ka=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("div").attr("data-stream"))}),r=$("<span>").addClass("image");if(b.indexOf("+")>-1){i=b.split("+");i=mist.data.streams[i[0]].source+i[1];l=k="";r.addClass("wildcard")}else{i=mist.data.streams[b].source;if(f.indexOf(b)>-1){Ka=e="";r.addClass("folder")}}g.append($("<div>").append($("<span>").addClass("streamname").text(b)).append(r).append($("<span>").addClass("description").text(i)).append($("<span>").addClass("button_container").append(l).append(k).append(e).append(Ka)).attr("title",
|
||||
Ka=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("div").attr("data-stream"))}),q=$("<span>").addClass("image");if(b.indexOf("+")>-1){i=b.split("+");i=mist.data.streams[i[0]].source+i[1];l=k="";q.addClass("wildcard")}else{i=mist.data.streams[b].source;if(f.indexOf(b)>-1){Ka=e="";q.addClass("folder")}}g.append($("<div>").append($("<span>").addClass("streamname").text(b)).append(q).append($("<span>").addClass("description").text(i)).append($("<span>").addClass("button_container").append(l).append(k).append(e).append(Ka)).attr("title",
|
||||
b).attr("data-stream",b))}break;default:var j=$("<tbody>").append($("<tr>").append("<td>").attr("colspan",6).text("Loading.."));h=$("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Stream name").attr("data-sort-type","string").addClass("sorting-asc")).append($("<th>").text("Source").attr("data-sort-type","string")).append($("<th>").text("Status").attr("data-sort-type","int")).append($("<th>").css("text-align","right").text("Connections").attr("data-sort-type","int")).append($("<th>")).append($("<th>")))).append(j);
|
||||
c.append(h);h.stupidtable();var m=function(){var a=[],b;for(b in mist.data.active_streams)a.push({streams:[mist.data.active_streams[b]],fields:["clients"],start:-2});mist.send(function(){$.extend(true,C,mist.data.streams);var a=0;j.html("");d.sort();for(var b in d){var c=d[b],e;e=c in mist.data.streams?mist.data.streams[c]:C[c];var g=$("<td>").css("text-align","right").html($("<span>").addClass("description").text("Loading..")),w=0;if(typeof mist.data.totals!="undefined"&&typeof mist.data.totals[c]!=
|
||||
"undefined"){var f=mist.data.totals[c].all_protocols.clients,w=0;if(f.length){for(a in f)w=w+f[a][1];w=Math.round(w/f.length)}}g.html(UI.format.number(w));if(w==0&&e.online==1)e.online=2;w=$("<td>").css("text-align","right").css("white-space","nowrap");(!("ischild"in e)||!e.ischild)&&w.html($("<button>").text("Settings").click(function(){UI.navto("Edit",$(this).closest("tr").data("index"))})).append($("<button>").text("Delete").click(function(){var a=$(this).closest("tr").data("index");if(confirm('Are you sure you want to delete the stream "'+
|
||||
a+'"?')){delete mist.data.streams[a];var b={};mist.data.LTS?b.deletestream=[a]:b.streams=mist.data.streams;mist.send(function(){UI.navto("Streams")},b)}}));f=$("<span>").text(e.name);e.ischild&&f.css("padding-left","1em");var h=UI.format.status(e),i=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("tr").data("index"))}),r=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("tr").data("index"))});if("filesfound"in C[c]||e.online<0){h.html("");
|
||||
i="";g.html("");r=""}j.append($("<tr>").data("index",c).html($("<td>").html(f).attr("title",e.name=="..."?"The results were truncated":e.name).addClass("overflow_ellipsis")).append($("<td>").text(e.source).attr("title",e.source).addClass("description").addClass("overflow_ellipsis").css("max-width","20em")).append($("<td>").data("sort-value",e.online).html(h)).append(g).append($("<td>").css("white-space","nowrap").html(i).append(r)).append(w));a++}},{totals:a,active_streams:true})};if(mist.data.LTS){var q=
|
||||
a+'"?')){delete mist.data.streams[a];var b={};mist.data.LTS?b.deletestream=[a]:b.streams=mist.data.streams;mist.send(function(){UI.navto("Streams")},b)}}));f=$("<span>").text(e.name);e.ischild&&f.css("padding-left","1em");var h=UI.format.status(e),i=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("tr").data("index"))}),q=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("tr").data("index"))});if("filesfound"in C[c]||e.online<0){h.html("");
|
||||
i="";g.html("");q=""}j.append($("<tr>").data("index",c).html($("<td>").html(f).attr("title",e.name=="..."?"The results were truncated":e.name).addClass("overflow_ellipsis")).append($("<td>").text(e.source).attr("title",e.source).addClass("description").addClass("overflow_ellipsis").css("max-width","20em")).append($("<td>").data("sort-value",e.online).html(h)).append(g).append($("<td>").css("white-space","nowrap").html(i).append(q)).append(w));a++}},{totals:a,active_streams:true})};if(mist.data.LTS){var r=
|
||||
0,Ma=0;for(f in mist.data.streams){h=mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"];if(!h)break;if(mist.inputMatch(h.source_match,mist.data.streams[f].source)){C[f].source=C[f].source+"*";C[f].filesfound=null;mist.send(function(a,b){var c=b.stream,d=0,e;a:for(e in a.browse.files){var g;for(g in mist.data.capabilities.inputs)if(!(g.indexOf("Buffer")>=0||g.indexOf("Buffer.exe")>=0||g.indexOf("Folder")>=0||g.indexOf("Folder.exe")>=0)&&mist.inputMatch(mist.data.capabilities.inputs[g].source_match,
|
||||
"/"+a.browse.files[e])){var w=c+"+"+a.browse.files[e];C[w]=oa(w,mist.data.streams[c]);C[w].source=mist.data.streams[c].source+a.browse.files[e];d++;if(d>=500){C[c+"+zzzzzzzzz"]={ischild:true,name:"...",online:-1};break a}}}"files"in a.browse&&a.browse.files.length?C[c].filesfound=true:mist.data.streams[c].filesfound=false;Ma++;if(q==Ma){mist.send(function(){m()},{active_streams:true});UI.interval.set(function(){m()},5E3)}},{browse:mist.data.streams[f].source},{stream:f});q++}}if(q==0){mist.send(function(){m()},
|
||||
"/"+a.browse.files[e])){var w=c+"+"+a.browse.files[e];C[w]=oa(w,mist.data.streams[c]);C[w].source=mist.data.streams[c].source+a.browse.files[e];d++;if(d>=500){C[c+"+zzzzzzzzz"]={ischild:true,name:"...",online:-1};break a}}}"files"in a.browse&&a.browse.files.length?C[c].filesfound=true:mist.data.streams[c].filesfound=false;Ma++;if(r==Ma){mist.send(function(){m()},{active_streams:true});UI.interval.set(function(){m()},5E3)}},{browse:mist.data.streams[f].source},{stream:f});r++}}if(r==0){mist.send(function(){m()},
|
||||
{active_streams:true});UI.interval.set(function(){m()},5E3)}}else{mist.send(function(){m()},{active_streams:true});UI.interval.set(function(){m()},5E3)}}};if(mist.data.LTS){var qa=0,Na=0,u={},Oa=[];for(g in mist.data.streams)if(mist.inputMatch((mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"]).source_match,mist.data.streams[g].source))Oa.push(g),mist.send(function(a,c){var d=c.stream,e=0,g;a:for(g in a.browse.files){var f;for(f in mist.data.capabilities.inputs)if(!(f.indexOf("Buffer")>=
|
||||
0||f.indexOf("Folder")>=0)&&mist.inputMatch(mist.data.capabilities.inputs[f].source_match,"/"+a.browse.files[g])){u[d+"+"+a.browse.files[g]]=true;e++;if(e>=500){u[d+"+zzzzzzzzz"]=true;break a}}}Na++;qa==Na&&mist.send(function(){for(var a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");if(c.length>1&&c[0]in mist.data.streams){u[mist.data.active_streams[a]]=true;C[mist.data.active_streams[a]]=oa(mist.data.active_streams[a],mist.data.streams[c[0]])}}u=Object.keys(u);u=u.concat(Object.keys(mist.data.streams));
|
||||
u.sort();pa(b,u,Oa)},{active_streams:true})},{browse:mist.data.streams[g].source},{stream:g}),qa++;0==qa&&mist.send(function(){for(var a in mist.data.active_streams){var c=mist.data.active_streams[a].split("+");if(c.length>1&&c[0]in mist.data.streams){u[mist.data.active_streams[a]]=true;C[mist.data.active_streams[a]]=oa(mist.data.active_streams[a],mist.data.streams[c[0]])}}u=Object.keys(u);mist.data.streams&&(u=u.concat(Object.keys(mist.data.streams)));u.sort();pa(b,u)},{active_streams:!0})}else pa(b,
|
||||
Object.keys(mist.data.streams));break;case "Edit":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,b)},{capabilities:!0});c.append("Loading..");return}z=!1;""!=b&&(z=!0);if(z){var Pa=b,p=mist.data.streams[Pa];c.find("h2").append(' "'+Pa+'"')}else c.html($("<h2>").text("New Stream")),p={};var Qa=[];for(q in mist.data.capabilities.inputs)Qa.push(mist.data.capabilities.inputs[q].source_match);var fa=$("<div>"),Ra=function(a){var c={};if(!mist.data.streams)mist.data.streams=
|
||||
Object.keys(mist.data.streams));break;case "Edit":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,b)},{capabilities:!0});c.append("Loading..");return}z=!1;""!=b&&(z=!0);if(z){var Pa=b,p=mist.data.streams[Pa];c.find("h2").append(' "'+Pa+'"')}else c.html($("<h2>").text("New Stream")),p={};var Qa=[];for(r in mist.data.capabilities.inputs)Qa.push(mist.data.capabilities.inputs[r].source_match);var fa=$("<div>"),Ra=function(a){var c={};if(!mist.data.streams)mist.data.streams=
|
||||
{};mist.data.streams[p.name]=p;b!=p.name&&delete mist.data.streams[b];c.addstream={};c.addstream[p.name]=p;if(b!=p.name)c.deletestream=[b];if(p.stop_sessions&&b!=""){c.stop_sessions=b;delete p.stop_sessions}mist.send(function(){delete mist.data.streams[p.name].online;delete mist.data.streams[p.name].error;UI.navto(a,a=="Preview"?p.name:"")},c)},Sa=$("<style>").text("button.saveandpreview { display: none; }"),P=$("<span>"),ra=function(){var a=c.find("[name=name]").val();if(a){var b=parseURL(mist.user.host),
|
||||
d=c.find("[name=source]").val(),e=d.match(/@.*/);e&&(e=e[0].substring(1));var g=d.replace(/(?:.+?):\/\//,""),g=g.split("/"),g=g[0],g=g.split(":"),g=g[0];(d=d.match(/:\d+/))&&(d=d[0]);var f={},h=["RTMP","RTSP","RTMP.exe","RTSP.exe"],i;for(i in h)h[i]in mist.data.capabilities.connectors&&(f[h[i]]=mist.data.capabilities.connectors[h[i]].optional.port["default"]);var h={RTMP:1935,"RTMP.exe":1935,RTSP:554,"RTSP.exe":554,TS:-1,"TS.exe":-1},k;for(k in f){for(i in mist.data.config.protocols){var l=mist.data.config.protocols[i];
|
||||
if(l.connector==k){if("port"in l)f[k]=l.port;break}}f[k]=f[k]==h[k]?"":":"+f[k]}f.TS="";f["TS.exe"]="";P.find(".field").closest("label").hide();for(i in f){var r;k=d?d:f[i];switch(i){case "RTMP":case "RTMP.exe":r="rtmp://"+b.host+k+"/"+(e?e:"live")+"/";P.find(".field.RTMPurl").setval(r).closest("label").show();P.find(".field.RTMPkey").setval(a==""?"STREAMNAME":a).closest("label").show();r=r+(a==""?"STREAMNAME":a);break;case "RTSP":case "RTSP.exe":r="rtsp://"+b.host+k+"/"+(a==""?"STREAMNAME":a)+(e?
|
||||
"?pass="+e:"");break;case "TS":case "TS.exe":r="udp://"+(g==""?b.host:g)+k+"/"}P.find(".field."+i.replace(".exe","")).setval(r).closest("label").show()}}},Ta=$("<div>"),sa={},u=[],Ua=$("<div>");for(q in mist.data.capabilities.processes)u.push([q,mist.data.capabilities.processes[q].hrn?mist.data.capabilities.processes[q].hrn:mist.data.capabilities.processes[q].name]);if(u.length){var jb=[{label:"New process",type:"select",select:u,value:u[0][0],pointer:{main:sa,index:"process"},"function":function(){var a=
|
||||
if(l.connector==k){if("port"in l)f[k]=l.port;break}}f[k]=f[k]==h[k]?"":":"+f[k]}f.TS="";f["TS.exe"]="";P.find(".field").closest("label").hide();for(i in f){var q;k=d?d:f[i];switch(i){case "RTMP":case "RTMP.exe":q="rtmp://"+b.host+k+"/"+(e?e:"live")+"/";P.find(".field.RTMPurl").setval(q).closest("label").show();P.find(".field.RTMPkey").setval(a==""?"STREAMNAME":a).closest("label").show();q=q+(a==""?"STREAMNAME":a);break;case "RTSP":case "RTSP.exe":q="rtsp://"+b.host+k+"/"+(a==""?"STREAMNAME":a)+(e?
|
||||
"?pass="+e:"");break;case "TS":case "TS.exe":q="udp://"+(g==""?b.host:g)+k+"/"}P.find(".field."+i.replace(".exe","")).setval(q).closest("label").show()}}},Ta=$("<div>"),sa={},u=[],Ua=$("<div>");for(r in mist.data.capabilities.processes)u.push([r,mist.data.capabilities.processes[r].hrn?mist.data.capabilities.processes[r].hrn:mist.data.capabilities.processes[r].name]);if(u.length){var jb=[{label:"New process",type:"select",select:u,value:u[0][0],pointer:{main:sa,index:"process"},"function":function(){var a=
|
||||
$(this).getval();if(a!=null){var a=mist.data.capabilities.processes[a],b=[$("<h4>").text(a.name+" Process options")];Ua.html(UI.buildUI(b.concat(mist.convertBuildOptions(a,sa))))}}},Ua];Ta.append(UI.buildUI([$("<br>"),$("<h3>").text("Stream processes"),{label:"Stream processes",itemLabel:"stream process",type:"sublist",sublist:jb,saveas:sa,pointer:{main:p,index:"processes"}}]))}c.append(UI.buildUI([{label:"Stream name",type:"str",validate:["required","streamname"],pointer:{main:p,index:"name"},help:"Set the name this stream will be recognised by for players and/or stream pushing."},
|
||||
{label:"Source",type:"browse",filetypes:Qa,pointer:{main:p,index:"source"},help:"<p> Below is the explanation of the input methods for MistServer. Anything between brackets () will go to default settings if not specified. </p> <table class=valigntop> <tr> <th colspan=3><b>File inputs</b></th> </tr> <tr> <th>File</th> <td> Linux/MacOS: /PATH/FILE<br> Windows: /cygdrive/DRIVE/PATH/FILE </td> <td> For file input please specify the proper path and file.<br> Supported inputs are: DTSC, FLV, MP3. MistServer Pro has TS, MP4, ISMV added as input. </td> </tr> <th> Folder </th> <td> Linux/MacOS: /PATH/<br> Windows: /cygdrive/DRIVE/PATH/ </td> <td> A folder stream makes all the recognised files in the selected folder available as a stream. </td> </tr> <tr><td colspan=3> </td></tr> <tr> <th colspan=3><b>Push inputs</b></th> </tr> <tr> <th>RTMP</th> <td>push://(IP)(@PASSWORD)</td> <td> IP is white listed IP for pushing towards MistServer, if left empty all are white listed.<br> PASSWORD is the application under which to push to MistServer, if it doesn't match the stream will be rejected. PASSWORD is MistServer Pro only. </td> </tr> <tr> <th>RTSP</th> <td>push://(IP)(@PASSWORD)</td> <td>IP is white listed IP for pushing towards MistServer, if left empty all are white listed.</td> </tr> <tr> <th>TS</th> <td>tsudp://(IP):PORT(/INTERFACE)</td> <td> IP is the IP address used to listen for this stream, multi-cast IP range is: 224.0.0.0 - 239.255.255.255. If IP is not set all addresses will listened to.<br> PORT is the port you reserve for this stream on the chosen IP.<br> INTERFACE is the interface used, if left all interfaces will be used. </td> </tr> <tr><td colspan=3> </td></tr> <tr> <th colspan=3><b>Pull inputs</b></th> </tr> <tr> <th>DTSC</th> <td>dtsc://MISTSERVER_IP:PORT/(STREAMNAME)</td> <td>MISTSERVER_IP is the IP of another MistServer to pull from.<br> PORT is the DTSC port of the other MistServer. (default is 4200)<br> STREAMNAME is the name of the target stream on the other MistServer. If left empty, the name of this stream will be used. </td> </tr> <tr> <th>HLS</th> <td>http://URL/TO/STREAM.m3u8</td> <td>The URL where the HLS stream is available to MistServer.</td> </tr> <tr> <th>RTSP</th> <td>rtsp://(USER:PASSWORD@)IP(:PORT)(/path)</td> <td> USER:PASSWORD is the account used if authorization is required.<br> IP is the IP address used to pull this stream from.<br> PORT is the port used to connect through.<br> PATH is the path to be used to identify the correct stream. </td> </tr> </table>",
|
||||
"function":function(){var a=$(this).val();Sa.remove();P.html("");if(a!=""){var b=null,d;for(d in mist.data.capabilities.inputs)if(typeof mist.data.capabilities.inputs[d].source_match!="undefined"&&mist.inputMatch(mist.data.capabilities.inputs[d].source_match,a)){b=d;break}if(b===null)fa.html($("<h3>").text("Unrecognized input").addClass("red")).append($("<span>").text("Please edit the stream source.").addClass("red"));else{b=mist.data.capabilities.inputs[b];fa.html($("<h3>").text(b.name+" Input options"));
|
||||
var e=mist.convertBuildOptions(b,p);"always_match"in mist.data.capabilities.inputs[d]&&mist.inputMatch(mist.data.capabilities.inputs[d].always_match,a)&&e.push({label:"Always on",type:"checkbox",help:"Keep this input available at all times, even when there are no active viewers.",pointer:{main:p,index:"always_on"}});fa.append(UI.buildUI(e));if(b.name=="Folder")c.append(Sa);else if(["Buffer","Buffer.exe","TS","TS.exe"].indexOf(b.name)>-1){d=[$("<br>"),$("<span>").text("Configure your source to push to:")];
|
||||
switch(b.name){case "Buffer":case "Buffer.exe":d.push({label:"RTMP full url",type:"span",clipboard:true,readonly:true,classes:["RTMP"],help:"Use this RTMP url if your client doesn't ask for a stream key"});d.push({label:"RTMP url",type:"span",clipboard:true,readonly:true,classes:["RTMPurl"],help:"Use this RTMP url if your client also asks for a stream key"});d.push({label:"RTMP stream key",type:"span",clipboard:true,readonly:true,classes:["RTMPkey"],help:"Use this key if your client asks for a stream key"});
|
||||
d.push({label:"RTSP",type:"span",clipboard:true,readonly:true,classes:["RTSP"]});break;case "TS":case "TS.exe":a.charAt(0)=="/"?d=[]:d.push({label:"TS",type:"span",clipboard:true,readonly:true,classes:["TS"]})}P.html(UI.buildUI(d));ra()}}}}},{label:"Stop sessions",type:"checkbox",help:"When saving these stream settings, kill this stream's current connections.",pointer:{main:p,index:"stop_sessions"}},P,$("<br>"),{type:"custom",custom:fa},Ta,{type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Streams")}},
|
||||
{type:"save",label:"Save","function":function(){Ra("Streams")}},{type:"save",label:"Save and Preview","function":function(){Ra("Preview")},classes:["saveandpreview"]}]}]));c.find("[name=name]").keyup(function(){ra()});ra();break;case "Preview":""==b&&UI.navto("Streams");var Q=parseURL(mist.user.host),W=Q.protocol,T=Q.host,J=":8080",v=W+T+J+"/";for(q in mist.data.config.protocols)if(s=mist.data.config.protocols[q],"HTTP"==s.connector||"HTTP.exe"==s.connector){s.pubaddr&&s.pubaddr.length?"string"==
|
||||
{type:"save",label:"Save","function":function(){Ra("Streams")}},{type:"save",label:"Save and Preview","function":function(){Ra("Preview")},classes:["saveandpreview"]}]}]));c.find("[name=name]").keyup(function(){ra()});ra();break;case "Preview":""==b&&UI.navto("Streams");var Q=parseURL(mist.user.host),W=Q.protocol,T=Q.host,J=":8080",v=W+T+J+"/";for(r in mist.data.config.protocols)if(s=mist.data.config.protocols[r],"HTTP"==s.connector||"HTTP.exe"==s.connector){s.pubaddr&&s.pubaddr.length?"string"==
|
||||
typeof s.pubaddr?v=s.pubaddr.replace(/\/$/,"")+"/":s.pubaddr.length&&(v=s.pubaddr[0].replace(/\/$/,"")+"/"):(J=s.port?":"+s.port:":8080",v=W+T+J+"/");break}var S=$("<div>").css({display:"flex","flex-flow":"row wrap","flex-shrink":1,"min-width":"auto"}),X="";-1==b.indexOf("+")&&(X=$("<button>").text("Settings").addClass("settings").click(function(){UI.navto("Edit",b)}));c.html($("<div>").addClass("bigbuttons").append(X).append($("<button>").text("Embed").addClass("embed").click(function(){UI.navto("Embed",
|
||||
b)})).append($("<button>").addClass("cancel").addClass("return").text("Return").click(function(){UI.navto("Streams")}))).append($("<h2>").text('Preview of "'+b+'"')).append(S);var K=encodeURIComponent(b),Va=$("<div>").css("flex-shrink","1").css("min-width","auto").css("max-width","100%");S.append(Va);var Wa=$("<div>"),Y=$("<div>").text("Loading player..").css("max-width","100%").css("flex-shrink","1").css("min-width","auto"),ta=$("<div>").addClass("controls");Va.append(Y).append(Wa).append(ta);$("link#devcss").length||
|
||||
c.append($("<link>").attr("rel","stylesheet").attr("type","text/css").attr("href",v+"skins/dev.css").attr("id","devcss"));var Xa=function(){Wa.text("");var d=document.createElement("script");c.append(d);d.src=v+"player.js";d.onerror=function(){Y.html($("<p>").append('Failed to load <a href="'+v+'player.js">'+v+"player.js</a>.")).append($("<p>").append("Please check if you've activated the HTTP protocol, if your http port is blocked, or if you're trying to load HTTPS on an HTTP page.")).append($("<button>").text("Reload").css("display",
|
||||
|
@ -168,20 +168,21 @@ c.append($("<link>").attr("rel","stylesheet").attr("type","text/css").attr("href
|
|||
style:{"max-width":"30em","flex-flow":"column nowrap"}},{type:"container",classes:["mistvideo-column","mistvideo-devcontrols"],children:[{type:"text",text:"Player control"},{type:"container",classes:["mistvideo-devbuttons"],style:{"flex-wrap":"wrap"},children:[{"if":function(){return!(!this.player||!this.player.api)},then:{type:"button",title:"Reload the video source",label:"Reload video",onclick:function(){this.player.api.load()}}},{type:"button",title:"Build MistVideo again",label:"Reload player",
|
||||
onclick:function(){this.reload()}},{type:"button",title:"Switch to the next available player and source combination",label:"Try next combination",onclick:function(){this.nextCombo()}}]},{type:"forcePlayer"},{type:"forceType"},{type:"forceSource"}]},{type:"log"}]}))};if(!(a!="Preview"||!b||b==""||e!=b)){Y[0].addEventListener("initialized",g);Y[0].addEventListener("initializeFailed",g);MistVideoObject.reference=mistPlay(e,{target:Y[0],host:v,skin:"dev",loop:true,MistVideoObject:MistVideoObject})}c[0].removeChild(d)};
|
||||
MistVideoObject.reference={unload:function(){d.onload=function(){this.parentElement&&this.parentElement.removeChild(this)}}}};Xa();var ua=$("<div>").append($("<h3>").text("Meta information")),ga=$("<span>").text("Loading..");ua.append(ga);var Ya=$("<div>").addClass("process_info");ua.append(Ya);S.append(ua);if(""!=K){$.ajax({type:"GET",url:v+"json_"+K+".js",success:function(a){var b=function(a,b){return"maxbps"in a?UI.format.bytes(a[b],1):b=="maxbps"?UI.format.bytes(a.bps,1):"unknown"},c=a.meta;if(!c||
|
||||
!c.tracks)ga.html("No meta information available.");else{a=[];a.push({label:"Type",type:"span",value:c.live?"Live":"Pre-recorded (VoD)"});"format"in c&&a.push({label:"Format",type:"span",value:c.format});c.live&&a.push({label:"Buffer window",type:"span",value:UI.format.addUnit(c.buffer_window,"ms")});var d={audio:{vheader:"Audio",labels:["Codec","Duration","Avg bitrate","Peak bitrate","Channels","Samplerate","Language","Track index"],content:[]},video:{vheader:"Video",labels:["Codec","Duration","Avg bitrate",
|
||||
"Peak bitrate","Size","Framerate","Language","Track index","Has B-Frames"],content:[]},subtitle:{vheader:"Subtitles",labels:["Codec","Duration","Avg bitrate","Peak bitrate","Language","Track index"],content:[]}},e=Object.keys(c.tracks);e.sort(function(a,b){a=a.split("_").pop();b=b.split("_").pop();return a-b});var g=1,f=1,h=1,i;for(i in e){var k=e[i],r=c.tracks[k];switch(r.type){case "audio":d.audio.content.push({header:"Track "+k.split("_").pop(),body:[r.codec,UI.format.duration((r.lastms-r.firstms)/
|
||||
1E3)+"<br><span class=description>"+UI.format.duration(r.firstms/1E3)+" to "+UI.format.duration(r.lastms/1E3)+"</span>",b(r,"bps"),b(r,"maxbps"),r.channels,UI.format.addUnit(UI.format.number(r.rate),"Hz"),"language"in r?r.language:"unknown",g]});g++;break;case "video":d.video.content.push({header:"Track "+k.split("_").pop(),body:[r.codec,UI.format.duration((r.lastms-r.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(r.firstms/1E3)+" to "+UI.format.duration(r.lastms/1E3)+"</span>",b(r,
|
||||
"bps"),b(r,"maxbps"),UI.format.addUnit(r.width,"x ")+UI.format.addUnit(r.height,"px"),UI.format.addUnit(UI.format.number(r.fpks/1E3),"fps"),"language"in r?r.language:"unknown",f,"bframes"in r?"yes":"no"]});f++;break;case "meta":case "subtitle":if(r.codec=="subtitle"||r.type=="subtitle"){d.subtitle.content.push({header:"Track "+k.split("_").pop(),body:[r.codec,UI.format.duration((r.lastms-r.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(r.firstms/1E3)+" to "+UI.format.duration(r.lastms/
|
||||
1E3)+"</span>",b(r,"bps"),b(r,"maxbps"),"language"in r?r.language:"unknown",h]});h++}}}b=["audio","video","subtitle"];i=$("<div>").css({display:"flex","flex-flow":"row wrap","font-size":"0.9em"});for(k in b)d[b[k]].content.length&&i.append(UI.buildVheaderTable(d[b[k]]).css("width","auto"));a.push($("<span>").text("Tracks:"));a.push(i);ga.html(UI.buildUI(a))}},error:function(){ga.html("Error while retrieving stream info.")}});var Za=function(){var a={proc_list:b};if(!mist.data.capabilities)a.capabilities=
|
||||
true;mist.send(function(a){if(a.proc_list){var b=$("<table>").css("width","auto"),c={"Process type:":function(a){return $("<b>").text(a.process)},"Source:":function(a){var b=$("<span>").text(a.source);a.source_tracks&&a.source_tracks.length&&b.append($("<span>").addClass("description").text(" track "+a.source_tracks.slice(0,-2).concat(a.source_tracks.slice(-2).join(" and ")).join(", ")));return b},"Sink:":function(a){var b=$("<span>").text(a.sink);a.sink_tracks&&a.sink_tracks.length&&b.append($("<span>").addClass("description").text(" track "+
|
||||
a.sink_tracks.slice(0,-2).concat(a.sink_tracks.slice(-2).join(" and ")).join(", ")));return b},"Active for:":function(a){var b=(new Date).setSeconds((new Date).getSeconds()-a.active_seconds);return $("<span>").append($("<span>").text(UI.format.duration(a.active_seconds))).append($("<span>").addClass("description").text(" since "+UI.format.time(b/1E3)))},"Pid:":function(a,b){return b},"Logs:":function(a){var b=$("<div>").text("None.");if(a.logs&&a.logs.length){b.html("").addClass("description").css({overflow:"auto",
|
||||
maxHeight:"6em",display:"flex",flexFlow:"column-reverse nowrap"});for(var c in a.logs){var d=a.logs[c];b.prepend($("<div>").append(UI.format.time(d[0])+" ["+d[1]+"] "+d[2]))}}return b},"Additional info:":function(a){var b;if(a.ainfo&&Object.keys(a.ainfo).length){b=$("<table>");for(var c in a.ainfo){var d=mist.data.capabilities.processes[a.process].ainfo[c];d||(d={name:c});b.append($("<tr>").append($("<th>").text(d.name+":")).append($("<td>").html(a.ainfo[c]).append(d.unit?$("<span>").addClass("unit").text(d.unit):
|
||||
"")))}}else b=$("<span>").addClass("description").text("N/A");return b}};Ya.html($("<h4>").text("Stream processes")).append(b);for(var d in c){var e=$("<tr>");b.append(e);e.append($("<th>").text(d).css("vertical-align","top"));for(var g in a.proc_list){$out=c[d](a.proc_list[g],g);e.append($("<td>").html($out).css("vertical-align","top"))}}}},a)};UI.interval.set(function(){Za()},5E3);Za()}break;case "Embed":""==b&&UI.navTo("Streams");X="";-1==b.indexOf("+")&&(X=$("<button>").addClass("settings").text("Settings").click(function(){UI.navto("Edit",
|
||||
b)}));c.html($("<div>").addClass("bigbuttons").append(X).append($("<button>").text("Preview").addClass("preview").click(function(){UI.navto("Preview",b)})).append($("<button>").addClass("cancel").addClass("return").text("Return").click(function(){UI.navto("Streams")}))).append($("<h2>").text('Embed "'+b+'"'));var Z=$("<span>");c.append(Z);var K=encodeURIComponent(b),Q=parseURL(mist.user.host),W=Q.protocol,T=Q.host,J=":8080",aa,ha={},v={http:W+T+J+"/"};for(q in mist.data.config.protocols)if(s=mist.data.config.protocols[q],
|
||||
"HTTP"==s.connector||"HTTP.exe"==s.connector)s.pubaddr?("string"==typeof s.pubaddr?v.http=s.pubaddr.replace(/\/$/,"")+"/":s.pubaddr.length&&(v.http=s.pubaddr[0].replace(/\/$/,"")+"/"),ha.http=s.pubaddr):(J=s.port?":"+s.port:":8080",v.http=W+T+J+"/");else if("HTTPS"==s.connector||"HTTPS.exe"==s.connector)s.pubaddr&&s.pubaddr.length?("string"==typeof s.pubaddr?v.https=s.pubaddr.replace(/\/$/,"")+"/":s.pubaddr.length&&(v.https=s.pubaddr[0].replace(/\/$/,"")+"/"),ha.https=s.pubaddr):(aa=s.port?":"+s.port:
|
||||
":4433",v.https="https://"+T+aa+"/");var R=v.http,D={http:v.http};"https"in v&&(D.https=v.https);if(otherhost.host||otherhost.https){R=(otherhost.https&&aa?"https://":"http://")+(otherhost.host?otherhost.host:Q.host)+(otherhost.https&&aa?aa:J)+"/";if(otherhost.host&&("http"in ha||(D.http=parseURL(D.http,{hostname:otherhost.host}).full),"https"in D&&!("https"in ha)))D.https=parseURL(D.https,{hostname:otherhost.host}).full;R=otherhost.https?D.https:D.http}var ba=!1,va={forcePlayer:"",forceType:"",controls:!0,
|
||||
autoplay:!0,loop:!1,muted:!1,fillSpace:!1,poster:"",urlappend:"",setTracks:{}},n=$.extend({},va),$a=UI.stored.getOpts();"embedoptions"in $a&&(n=$.extend(n,$a.embedoptions,!0),"object"!=typeof n.setTracks&&(n.setTracks={}));var ia={};switch(n.controls){case "stock":ia.controls="stock";break;case !0:ia.controls=1;break;case !1:ia.controls=0}var A=function(){function a(b){switch(typeof b){case "string":return $.isNumeric(b)?b:'"'+b+'"';case "object":return JSON.stringify(b);default:return b}}ba&&UI.stored.saveOpt("embedoptions",
|
||||
n);for(var c=b+"_",d=12,e="";d--;){var g;g=Math.floor(Math.random()*62);g=g<10?g:g<36?String.fromCharCode(g+55):String.fromCharCode(g+61);e=e+g}var c=c+e,d=['target: document.getElementById("'+c+'")'],f;for(f in n)f=="prioritize_type"?n[f]&&n[f]!=""&&d.push("forcePriority: "+JSON.stringify({source:[["type",[n[f]]]]})):f=="monitor_action"?n[f]&&n[f]!=""&&n[f]=="nextCombo"&&d.push('monitor: {\n action: function(){\n this.MistVideo.log("Switching to nextCombo because of poor playback in "+this.MistVideo.source.type+" ("+Math.round(this.vars.score*1000)/10+"%)");\n this.MistVideo.nextCombo();\n }\n }'):
|
||||
!c.tracks)ga.html("No meta information available.");else{a=[];a.push({label:"Type",type:"span",value:c.live?"Live":"Pre-recorded (VoD)"});"format"in c&&a.push({label:"Format",type:"span",value:c.format});c.live&&a.push({label:"Buffer window",type:"span",value:UI.format.addUnit(c.buffer_window,"ms")});var d={audio:{vheader:"Audio",labels:["Codec","Duration","Avg bitrate","Peak bitrate","Channels","Samplerate","Language","Track index",""],content:[]},video:{vheader:"Video",labels:["Codec","Duration",
|
||||
"Avg bitrate","Peak bitrate","Size","Framerate","Language","Track index","Has B-Frames"],content:[]},subtitle:{vheader:"Subtitles",labels:["Codec","Duration","Avg bitrate","Peak bitrate","Language","Track index","","",""],content:[]},meta:{vheader:"Metadata",labels:["Codec","Duration","Avg bitrate","Peak bitrate","","","","",""],content:[]}},e=Object.keys(c.tracks);e.sort(function(a,b){a=a.split("_").pop();b=b.split("_").pop();return a-b});var g=1,f=1,h=1,i;for(i in e){var k=e[i],q=c.tracks[k];switch(q.type){case "audio":d.audio.content.push({header:"Track "+
|
||||
k.split("_").pop(),body:[q.codec,UI.format.duration((q.lastms-q.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(q.firstms/1E3)+" to "+UI.format.duration(q.lastms/1E3)+"</span>",b(q,"bps"),b(q,"maxbps"),q.channels,UI.format.addUnit(UI.format.number(q.rate),"Hz"),"language"in q?q.language:"unknown",g,""]});g++;break;case "video":d.video.content.push({header:"Track "+k.split("_").pop(),body:[q.codec,UI.format.duration((q.lastms-q.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(q.firstms/
|
||||
1E3)+" to "+UI.format.duration(q.lastms/1E3)+"</span>",b(q,"bps"),b(q,"maxbps"),UI.format.addUnit(q.width,"x ")+UI.format.addUnit(q.height,"px"),UI.format.addUnit(UI.format.number(q.fpks/1E3),"fps"),"language"in q?q.language:"unknown",f,"bframes"in q?"yes":"no"]});f++;break;case "meta":case "subtitle":if(q.codec=="subtitle"||q.type=="subtitle"){d.subtitle.content.push({header:"Track "+k.split("_").pop(),body:[q.codec,UI.format.duration((q.lastms-q.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(q.firstms/
|
||||
1E3)+" to "+UI.format.duration(q.lastms/1E3)+"</span>",b(q,"bps"),b(q,"maxbps"),"language"in q?q.language:"unknown",h,"","",""]});h++}else d.meta.content.push({header:"Track "+k.split("_").pop(),body:[q.codec,UI.format.duration((q.lastms-q.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(q.firstms/1E3)+" to "+UI.format.duration(q.lastms/1E3)+"</span>",b(q,"bps"),b(q,"maxbps"),"","","","",""]})}}b=["audio","video","subtitle","meta"];i=$("<div>").css({display:"flex","flex-flow":"row wrap",
|
||||
"font-size":"0.9em"});for(k in b)d[b[k]].content.length&&i.append(UI.buildVheaderTable(d[b[k]]).css("width","auto"));a.push($("<span>").text("Tracks:"));a.push(i);ga.html(UI.buildUI(a))}},error:function(){ga.html("Error while retrieving stream info.")}});var Za=function(){var a={proc_list:b};if(!mist.data.capabilities)a.capabilities=true;mist.send(function(a){if(a.proc_list){var b=$("<table>").css("width","auto"),c={"Process type:":function(a){return $("<b>").text(a.process)},"Source:":function(a){var b=
|
||||
$("<span>").text(a.source);a.source_tracks&&a.source_tracks.length&&b.append($("<span>").addClass("description").text(" track "+a.source_tracks.slice(0,-2).concat(a.source_tracks.slice(-2).join(" and ")).join(", ")));return b},"Sink:":function(a){var b=$("<span>").text(a.sink);a.sink_tracks&&a.sink_tracks.length&&b.append($("<span>").addClass("description").text(" track "+a.sink_tracks.slice(0,-2).concat(a.sink_tracks.slice(-2).join(" and ")).join(", ")));return b},"Active for:":function(a){var b=
|
||||
(new Date).setSeconds((new Date).getSeconds()-a.active_seconds);return $("<span>").append($("<span>").text(UI.format.duration(a.active_seconds))).append($("<span>").addClass("description").text(" since "+UI.format.time(b/1E3)))},"Pid:":function(a,b){return b},"Logs:":function(a){var b=$("<div>").text("None.");if(a.logs&&a.logs.length){b.html("").addClass("description").css({overflow:"auto",maxHeight:"6em",display:"flex",flexFlow:"column-reverse nowrap"});for(var c in a.logs){var d=a.logs[c];b.prepend($("<div>").append(UI.format.time(d[0])+
|
||||
" ["+d[1]+"] "+d[2]))}}return b},"Additional info:":function(a){var b;if(a.ainfo&&Object.keys(a.ainfo).length){b=$("<table>");for(var c in a.ainfo){var d=mist.data.capabilities.processes[a.process].ainfo[c];d||(d={name:c});b.append($("<tr>").append($("<th>").text(d.name+":")).append($("<td>").html(a.ainfo[c]).append(d.unit?$("<span>").addClass("unit").text(d.unit):"")))}}else b=$("<span>").addClass("description").text("N/A");return b}};Ya.html($("<h4>").text("Stream processes")).append(b);for(var d in c){var e=
|
||||
$("<tr>");b.append(e);e.append($("<th>").text(d).css("vertical-align","top"));for(var g in a.proc_list){$out=c[d](a.proc_list[g],g);e.append($("<td>").html($out).css("vertical-align","top"))}}}},a)};UI.interval.set(function(){Za()},5E3);Za()}break;case "Embed":""==b&&UI.navTo("Streams");X="";-1==b.indexOf("+")&&(X=$("<button>").addClass("settings").text("Settings").click(function(){UI.navto("Edit",b)}));c.html($("<div>").addClass("bigbuttons").append(X).append($("<button>").text("Preview").addClass("preview").click(function(){UI.navto("Preview",
|
||||
b)})).append($("<button>").addClass("cancel").addClass("return").text("Return").click(function(){UI.navto("Streams")}))).append($("<h2>").text('Embed "'+b+'"'));var Z=$("<span>");c.append(Z);var K=encodeURIComponent(b),Q=parseURL(mist.user.host),W=Q.protocol,T=Q.host,J=":8080",aa,ha={},v={http:W+T+J+"/"};for(r in mist.data.config.protocols)if(s=mist.data.config.protocols[r],"HTTP"==s.connector||"HTTP.exe"==s.connector)s.pubaddr?("string"==typeof s.pubaddr?v.http=s.pubaddr.replace(/\/$/,"")+"/":s.pubaddr.length&&
|
||||
(v.http=s.pubaddr[0].replace(/\/$/,"")+"/"),ha.http=s.pubaddr):(J=s.port?":"+s.port:":8080",v.http=W+T+J+"/");else if("HTTPS"==s.connector||"HTTPS.exe"==s.connector)s.pubaddr&&s.pubaddr.length?("string"==typeof s.pubaddr?v.https=s.pubaddr.replace(/\/$/,"")+"/":s.pubaddr.length&&(v.https=s.pubaddr[0].replace(/\/$/,"")+"/"),ha.https=s.pubaddr):(aa=s.port?":"+s.port:":4433",v.https="https://"+T+aa+"/");var R=v.http,D={http:v.http};"https"in v&&(D.https=v.https);if(otherhost.host||otherhost.https){R=
|
||||
(otherhost.https&&aa?"https://":"http://")+(otherhost.host?otherhost.host:Q.host)+(otherhost.https&&aa?aa:J)+"/";if(otherhost.host&&("http"in ha||(D.http=parseURL(D.http,{hostname:otherhost.host}).full),"https"in D&&!("https"in ha)))D.https=parseURL(D.https,{hostname:otherhost.host}).full;R=otherhost.https?D.https:D.http}var ba=!1,va={forcePlayer:"",forceType:"",controls:!0,autoplay:!0,loop:!1,muted:!1,fillSpace:!1,poster:"",urlappend:"",setTracks:{}},n=$.extend({},va),$a=UI.stored.getOpts();"embedoptions"in
|
||||
$a&&(n=$.extend(n,$a.embedoptions,!0),"object"!=typeof n.setTracks&&(n.setTracks={}));var ia={};switch(n.controls){case "stock":ia.controls="stock";break;case !0:ia.controls=1;break;case !1:ia.controls=0}var A=function(){function a(b){switch(typeof b){case "string":return $.isNumeric(b)?b:'"'+b+'"';case "object":return JSON.stringify(b);default:return b}}ba&&UI.stored.saveOpt("embedoptions",n);for(var c=b+"_",d=12,e="";d--;){var g;g=Math.floor(Math.random()*62);g=g<10?g:g<36?String.fromCharCode(g+
|
||||
55):String.fromCharCode(g+61);e=e+g}var c=c+e,d=['target: document.getElementById("'+c+'")'],f;for(f in n)f=="prioritize_type"?n[f]&&n[f]!=""&&d.push("forcePriority: "+JSON.stringify({source:[["type",[n[f]]]]})):f=="monitor_action"?n[f]&&n[f]!=""&&n[f]=="nextCombo"&&d.push('monitor: {\n action: function(){\n this.MistVideo.log("Switching to nextCombo because of poor playback in "+this.MistVideo.source.type+" ("+Math.round(this.vars.score*1000)/10+"%)");\n this.MistVideo.nextCombo();\n }\n }'):
|
||||
n[f]!=va[f]&&(n[f]!=null&&(typeof n[f]!="object"||JSON.stringify(n[f])!=JSON.stringify(va[f])))&&d.push(f+": "+a(n[f]));f=[];f.push('<div class="mistvideo" id="'+c+'">');f.push(" <noscript>");f.push(' <a href="'+(otherhost.https?D.https:D.http)+K+'.html" target="_blank">');f.push(" Click here to play this video");f.push(" </a>");f.push(" </noscript>");f.push(" <script>");f.push(" var a = function(){");f.push(' mistPlay("'+b+'",{');f.push(" "+d.join(",\n "));f.push(" });");
|
||||
f.push(" };");f.push(" if (!window.mistplayers) {");f.push(' var p = document.createElement("script");');if("https"in v&&parseURL(v.http).protocol!="https://"){f.push(' if (location.protocol == "https:") { p.src = "'+D.https+'player.js" } ');f.push(' else { p.src = "'+D.http+'player.js" } ')}else f.push(' p.src = "'+R+'player.js"');f.push(" document.head.appendChild(p);");f.push(" p.onload = a;");f.push(" }");f.push(" else { a(); }");f.push(" <\/script>");
|
||||
f.push("</div>");return f.join("\n")},wa=$("<span>").text("Loading.."),ab=A(n),U=$("<div>").text("Loading..").css("display","flex").css("flex-flow","column nowrap"),bb="";"https"in v&&(bb=UI.buildUI([{label:"Use HTTPS",type:"checkbox","function":function(){if($(this).getval()!=otherhost.https){otherhost.https=$(this).getval();UI.navto("Embed",b)}},value:otherhost.https}]).find("label"));Z.append($("<span>").addClass("input_container").append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Use a different host:")).append($("<span>").addClass("field_container").append($("<input>").attr("type",
|
||||
|
@ -193,9 +194,9 @@ $("<h4>").text("Embed code options (optional)").css("margin-top",0),{type:"help"
|
|||
$(".embed_code").setval(A(n))},help:"Whether or not the video should play as the page is loaded."},{label:"Loop",type:"checkbox",pointer:{main:n,index:"loop"},"function":function(){n.loop=$(this).getval();$(".embed_code").setval(A(n))},help:"If the video should restart when the end is reached."},{label:"Start muted",type:"checkbox",pointer:{main:n,index:"muted"},"function":function(){n.muted=$(this).getval();$(".embed_code").setval(A(n))},help:"If the video should restart when the end is reached."},
|
||||
{label:"Fill available space",type:"checkbox",pointer:{main:n,index:"fillSpace"},"function":function(){n.fillSpace=$(this).getval();$(".embed_code").setval(A(n))},help:"The video will fit the available space in its container, even if the video stream has a smaller resolution."},{label:"Poster",type:"str",pointer:{main:n,index:"poster"},"function":function(){n.poster=$(this).getval();$(".embed_code").setval(A(n))},help:"URL to an image that is displayed when the video is not playing."},{label:"Video URL addition",
|
||||
type:"str",pointer:{main:n,index:"urlappend"},help:"The embed script will append this string to the video url, useful for sending through params.",classes:["embed_code_forceprotocol"],"function":function(){n.urlappend=$(this).getval();$(".embed_code").setval(A(n))}},{label:"Preselect tracks",type:"DOMfield",DOMfield:U,help:"Pre-select these tracks."},{label:"Monitoring action",type:"select",select:[["","Ask the viewer what to do"],["nextCombo","Try the next source / player combination"]],pointer:{main:n,
|
||||
index:"monitor_action"},"function":function(){n.monitor_action=$(this).getval();$(".embed_code").setval(A(n))},help:"What the player should do when playback is poor."},$("<h3>").text("Protocol stream urls"),wa]));$.ajax({type:"GET",url:R+"json_"+K+".js",success:function(a){var b=[],c=Z.find(".field.forceType"),d=Z.find(".field.prioritize_type"),e;for(e in a.source){var f=a.source[e],g=UI.humanMime(f.type);b.push({label:g?g+" <span class=description>("+f.type+")</span>":UI.format.capital(f.type),type:"str",
|
||||
value:f.url,readonly:true,qrcode:true,clipboard:true});g=UI.humanMime(f.type);if(c.children('option[value="'+f.type+'"]').length==0){c.append($("<option>").text(g?g+" ("+f.type+")":UI.format.capital(f.type)).val(f.type));d.append($("<option>").text(g?g+" ("+f.type+")":UI.format.capital(f.type)).val(f.type))}}c.val(n.forceType);d.val(n.prioritize_type);wa.html(UI.buildUI(b));U.html("");b={};for(e in a.meta.tracks){c=a.meta.tracks[e];if(c.codec=="subtitle")c.type="subtitle";if(!(c.type!="audio"&&c.type!=
|
||||
"video"&&c.type!="subtitle")){c.type in b||(b[c.type]=c.type=="subtitle"?[]:[["","Autoselect "+c.type]]);b[c.type].push([c.trackid,UI.format.capital(c.type)+" track "+(b[c.type].length+(c.type=="subtitle"?1:0))])}}if(Object.keys(b).length){U.closest("label").show();var a=["audio","video","subtitle"],h;for(h in a){e=a[h];if(b[e]&&b[e].length){c=$("<select>").attr("data-type",e).css("flex-grow","1").change(function(){$(this).val()==""?delete n.setTracks[$(this).attr("data-type")]:n.setTracks[$(this).attr("data-type")]=
|
||||
index:"monitor_action"},"function":function(){n.monitor_action=$(this).getval();$(".embed_code").setval(A(n))},help:"What the player should do when playback is poor."},$("<h3>").text("Protocol stream urls"),wa]));$.ajax({type:"GET",url:R+"json_"+K+".js?inclzero=1",success:function(a){var b=[],c=Z.find(".field.forceType"),d=Z.find(".field.prioritize_type"),e;for(e in a.source){var f=a.source[e],g=UI.humanMime(f.type);b.push({label:g?g+" <span class=description>("+f.type+")</span>":UI.format.capital(f.type),
|
||||
type:"str",value:f.url,readonly:true,qrcode:true,clipboard:true});g=UI.humanMime(f.type);if(c.children('option[value="'+f.type+'"]').length==0){c.append($("<option>").text(g?g+" ("+f.type+")":UI.format.capital(f.type)).val(f.type));d.append($("<option>").text(g?g+" ("+f.type+")":UI.format.capital(f.type)).val(f.type))}}c.val(n.forceType);d.val(n.prioritize_type);wa.html(UI.buildUI(b));U.html("");b={};if(a.meta)for(e in a.meta.tracks){c=a.meta.tracks[e];if(c.codec=="subtitle")c.type="subtitle";if(!(c.type!=
|
||||
"audio"&&c.type!="video"&&c.type!="subtitle")){c.type in b||(b[c.type]=c.type=="subtitle"?[]:[["","Autoselect "+c.type]]);b[c.type].push([c.trackid,UI.format.capital(c.type)+" track "+(b[c.type].length+(c.type=="subtitle"?1:0))])}}if(Object.keys(b).length){U.closest("label").show();var a=["audio","video","subtitle"],h;for(h in a){e=a[h];if(b[e]&&b[e].length){c=$("<select>").attr("data-type",e).css("flex-grow","1").change(function(){$(this).val()==""?delete n.setTracks[$(this).attr("data-type")]:n.setTracks[$(this).attr("data-type")]=
|
||||
$(this).val();$(".embed_code").setval(A(n))});U.append(c);e=="subtitle"?b[e].unshift(["","No "+e]):b[e].push([-1,"No "+e]);for(var i in b[e])c.append($("<option>").val(b[e][i][0]).text(b[e][i][1]));if(e in n.setTracks){c.val(n.setTracks[e]);if(c.val()==null){c.val("");delete n.setTracks[e];$(".embed_code").setval(A(n))}}}}}else U.closest("label").hide();ba=true},error:function(){wa.html("Error while retrieving stream info.");U.closest("label").hide();n.setTracks={}}});var ja=document.createElement("script");
|
||||
ja.src=v.http+"player.js";document.head.appendChild(ja);ja.onload=function(){var a=Z.find(".field.forcePlayer"),b;for(b in mistplayers)a.append($("<option>").text(mistplayers[b].name).val(b));document.head.removeChild(this)};ja.onerror=function(){document.head.removeChild(this)};break;case "Push":var L=$("<div>").text("Loading..");c.append(L);mist.send(function(a){function b(a){setTimeout(function(){mist.send(function(c){var d=false;if("push_list"in c&&c.push_list&&c.push_list.length){var d=true,
|
||||
f;for(f in c.push_list)if(a.indexOf(c.push_list[f][0])>-1){d=false;break}}else d=true;if(d)for(f in a)e.find("tr[data-pushid="+a[f]+"]").remove();else b()},{push_list:1})},1E3)}function c(f,g){var h=$("<span>"),i=$("<span>");if(g=="Automatic"&&f.length>=4){var k=function(a,b,c){a=""+("$"+a+" ");switch(Number(b)){case 0:a=a+"is true";break;case 1:a=a+"is false";break;case 2:a=a+("== "+c);break;case 3:a=a+("!= "+c);break;case 10:a=a+("> (numerical) "+c);break;case 11:a=a+(">= (numerical) "+c);break;
|
||||
|
@ -214,7 +215,7 @@ var f={},g;for(g in a.push_list)if((c==""||a.push_list[g][1]==c)&&(d==""||a.push
|
|||
e.find("tr").first();e.empty();e.append(b);for(var d in a.push_list)e.append(c(a.push_list[d]))},{push_list:1})},5E3)},{push_settings:1,push_list:1,push_auto_list:1});break;case "Start Push":if(!("capabilities"in mist.data)||!("variable_list"in mist.data)||!("external_writer_list"in mist.data)){c.append("Loading Mist capabilities..");mist.send(function(){UI.navto("Start Push",b)},{capabilities:1,variable_list:!0,external_writer_list:!0});return}var y,ka=function(a){var d=false,f=b.split("_");b=f[0];
|
||||
f.length==2&&(d=f[1]);if(d!==false&&typeof a=="undefined")mist.send(function(a){ka(a.push_auto_list[d])},{push_auto_list:1});else{var e=[],g=[],h={},i=[],k;for(k in mist.data.capabilities.connectors){f=mist.data.capabilities.connectors[k];if("push_urls"in f){h[k]=f.push_urls;for(var l in f.push_urls)f.push_urls[l][0]=="/"?e.push(f.push_urls[l]):g.push(f.push_urls[l])}}if(mist.data.external_writer_list)for(k in mist.data.external_writer_list){f=mist.data.external_writer_list[k];if(f.length>=3)for(l in f[2])i.push(f[2][l]+
|
||||
"://")}e.sort();g.sort();b=="auto"&&c.find("h2").text("Add automatic push");var j={params:{}},m=[];if(b=="auto"&&typeof a!="undefined"){j={stream:a[0],target:a[1],params:{}};l=j.target.split("?");if(l.length>1){m=l.pop();j.target=l.join("?");m=m.split("&");for(k in m){l=m[k].split("=");j.params[l.shift()]=l.join("=")}}if(a.length>=3)j.scheduletime=a[2]!=0?a[2]:null;if(a.length>=4)j.completetime=a[3]!=0?a[3]:null;if(a.length>=5)j.startVariableName=a[4]!=""?a[4]:null;if(a.length>=6)j.startVariableOperator=
|
||||
a[5]!=""?a[5]:null;if(a.length>=7)j.startVariableValue=a[6]!=""?a[6]:null;if(a.length>=8)j.endVariableName=a[7]!=""?a[7]:null;if(a.length>=9)j.endVariableOperator=a[8]!=""?a[8]:null;if(a.length>=10)j.endVariableValue=a[9]!=""?a[9]:null}var q=$("<div>").css("margin","1em 0"),n=$("<div>"),o;if(b=="auto"){n.css("margin","1em 0").html(UI.buildUI([{label:"This push should be active",help:"When 'based on server time' is selected, a start and/or end timestamp can be configured. When it's 'based on a variable', the push will be activated while the specified variable matches the specified value.",
|
||||
a[5]!=""?a[5]:null;if(a.length>=7)j.startVariableValue=a[6]!=""?a[6]:null;if(a.length>=8)j.endVariableName=a[7]!=""?a[7]:null;if(a.length>=9)j.endVariableOperator=a[8]!=""?a[8]:null;if(a.length>=10)j.endVariableValue=a[9]!=""?a[9]:null}var r=$("<div>").css("margin","1em 0"),n=$("<div>"),o;if(b=="auto"){n.css("margin","1em 0").html(UI.buildUI([{label:"This push should be active",help:"When 'based on server time' is selected, a start and/or end timestamp can be configured. When it's 'based on a variable', the push will be activated while the specified variable matches the specified value.",
|
||||
type:"select",select:[["time","Based on server time"],["variable","Based on a variable"]],value:j.startVariableName||j.endVariableName?"variable":"time",classes:["activewhen"],"function":function(){var a=n.find(".varbased").closest(".UIelement"),b=n.find(".timebased").closest(".UIelement");if($(this).getval()=="time"){a.hide();b.css("display","")}else{b.hide();a.css("display","");n.find('[name="startVariableOperator"]').trigger("change");n.find('[name="endVariableOperator"]').trigger("change")}}},
|
||||
$("<br>"),$("<span>").addClass("UIelement").append($("<h3>").text("Start the push").addClass("varbased")),{classes:["varbased"],label:"Use this variable",type:"str",help:"This variable should be used to determine if this push should be started.",prefix:"$",datalist:Object.keys(mist.data.variable_list||[]),pointer:{main:j,index:"startVariableName"}},{classes:["varbased"],label:"Comparison operator",type:"select",select:[[0,"is true"],[1,"is false"],[2,"=="],[3,"!="],[10,"> (numerical)"],[11,">= (numerical)"],
|
||||
[12,"< (numerical)"],[13,"<= (numerical)"],[20,"> (lexical)"],[21,">= (lexical)"],[22,"< (lexical)"],[23,"<= (lexical)"]],value:2,css:{display:"none"},help:"How would you like to compare this variable?",pointer:{main:j,index:"startVariableOperator"},"function":function(){var a=n.find('[name="startVariableValue"]').closest(".UIelement");Number($(this).getval())<2?a.hide():a.css("display","")}},{classes:["varbased"],label:"Variable value",type:"str",help:"The variable will be compared with this value to determine if this push should be started.<br>You can also enter another variable here!",
|
||||
|
@ -226,18 +227,18 @@ help:"This may either be a full stream name, a partial wildcard stream name, or
|
|||
pointer:{main:j,index:"stream"},validate:["required",function(a){a=a.split("+");a=a[0];return a in mist.data.streams?false:{msg:"'"+a+"' is not a stream name.",classes:["orange"],"break":false}}],datalist:y},{label:"Target",type:"str",help:"Where the stream will be pushed to.<br> Valid push formats: <ul> <li>"+g.join("</li><li>")+"</li> </ul> Valid file formats: <ul> <li>"+e.join("</li><li>")+
|
||||
"</li> </ul> "+(i.length?"Additionally, the following protocols (from generic writers) may be used in combination with any of the above file formats:<ul><li>"+i.join("</li><li>")+"</li></ul>":"")+" Valid text replacements: <ul> <li>$stream - inserts the stream name used to push to MistServer</li> <li>$day - inserts the current day number</li><li>$month - inserts the current month number</li> <li>$year - inserts the current year number</li><li>$hour - inserts the hour timestamp when stream was received</li> <li>$minute - inserts the minute timestamp the stream was received</li> <li>$seconds - inserts the seconds timestamp when the stream was received</li> <li>$datetime - inserts $year.$month.$day.$hour.$minute.$seconds timestamp when the stream was received</li> </ul>",
|
||||
pointer:{main:j,index:"target"},validate:["required",function(a){for(var b in g)if(mist.inputMatch(g[b],a))return false;for(b in e){if(mist.inputMatch(e[b],a))return false;for(var c in i)if(mist.inputMatch(i[c]+e[b].slice(1),a))return false}return{msg:"Does not match a valid target.<br>Valid push formats: <ul> <li>"+g.join("</li><li>")+"</li> </ul> Valid file formats: <ul> <li>"+e.join("</li><li>")+
|
||||
"</li> </ul> "+(i.length?"Additionally, the following protocols may be used in combination with any of the above file formats:<ul><li>"+i.join("</li><li>")+"</li></ul>":""),classes:["red"]}}],"function":function(){var a=false,b=$(this).getval();for(connector in h)for(var c in h[connector]){if(mist.inputMatch(h[connector][c],b)){a=connector;break}if(h[connector][c][0]=="/")for(var d in i)if(mist.inputMatch(i[d]+h[connector][c].slice(1),b)){a=connector;break}}if(a){q.html($("<h3>").text(mist.data.capabilities.connectors[a].friendly.replace("over HTTP",
|
||||
"</li> </ul> "+(i.length?"Additionally, the following protocols may be used in combination with any of the above file formats:<ul><li>"+i.join("</li><li>")+"</li></ul>":""),classes:["red"]}}],"function":function(){var a=false,b=$(this).getval();for(connector in h)for(var c in h[connector]){if(mist.inputMatch(h[connector][c],b)){a=connector;break}if(h[connector][c][0]=="/")for(var d in i)if(mist.inputMatch(i[d]+h[connector][c].slice(1),b)){a=connector;break}}if(a){r.html($("<h3>").text(mist.data.capabilities.connectors[a].friendly.replace("over HTTP",
|
||||
"")));o={};for(c in mist.data.capabilities.connectors[a].push_parameters){d=mist.data.capabilities.connectors[a].push_parameters[c];if(!d.prot_only||!(String().match&&b.match(/.+\:\/\/.+/)===null))d.file_only&&b[0]!="/"||(o[c]=d)}a={desc:mist.data.capabilities.connectors[a].desc.replace("over HTTP",""),optional:o,sort:"sort"};a=mist.convertBuildOptions(a,j.params);b=[];for(c in m){d=m[c].split("=");var f=d[0];f in o||b.push(f+(d.length>1?"="+d.slice(1).join("="):""))}a.push($("<br>"));a.push({type:"inputlist",
|
||||
label:"Custom url parameters",value:b,classes:["custom_url_parameters"],input:{type:"str",placeholder:"name=value",prefix:""},help:"Any custom url parameters not covered by the parameters configurable above.",pointer:{main:j,index:"custom_url_params"}});q.append(UI.buildUI(a))}else q.html($("<h4>").addClass("red").text("Unrecognized target.")).append($("<span>").text("Please edit the push target."))}},n,q];k.push({type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Push")}},
|
||||
label:"Custom url parameters",value:b,classes:["custom_url_parameters"],input:{type:"str",placeholder:"name=value",prefix:""},help:"Any custom url parameters not covered by the parameters configurable above.",pointer:{main:j,index:"custom_url_params"}});r.append(UI.buildUI(a))}else r.html($("<h4>").addClass("red").text("Unrecognized target.")).append($("<span>").text("Please edit the push target."))}},n,r];k.push({type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Push")}},
|
||||
{type:"save",label:"Save",preSave:function(){delete j.startVariableName;delete j.startVariableOperator;delete j.startVariableValue;delete j.endVariableName;delete j.endVariableOperator;delete j.endVariableValue;delete j.completetime;delete j.scheduletime},"function":function(){var c=j.params,d;for(d in c)c[d]===null?delete c[d]:d in o||delete c[d];if(j.startVariableName||j.endVariableName){j.scheduletime=0;j.completetime=0}if(j.startVariableName===null){delete j.startVariableName;delete j.startVariableOperator;
|
||||
delete j.startVariableValue}if(j.endVariableName===null){delete j.endVariableName;delete j.endVariableOperator;delete j.endVariableValue}if(j.scheduletime)c.recstartunix=j.scheduletime;if(Object.keys(c).length||j.custom_url_params&&j.custom_url_params.length){var f="?",e=j.target.split("?");if(e.length>1){f="&";e=e[e.length-1];e=e.split("&");for(d in e){var g=e[d].split("=")[0];g in c&&delete c[g]}}if(Object.keys(c).length||j.custom_url_params&&j.custom_url_params.length){e=[];for(d in c)e.push(d+
|
||||
"="+c[d]);for(d in j.custom_url_params)e.push(j.custom_url_params[d]);f=f+e.join("&");j.target=j.target+f}}delete j.params;delete j.custom_url_params;c={};c[b=="auto"?"push_auto_add":"push_start"]=j;if(typeof a!="undefined"&&(a[0]!=j.stream||a[1]!=j.target))c.push_auto_remove=[a];mist.send(function(){UI.navto("Push")},c)}}]});c.append(UI.buildUI(k))}};mist.data.LTS?mist.send(function(a){(y=a.active_streams)||(y=[]);var a=[],b;for(b in y)y[b].indexOf("+")!=-1&&a.push(y[b].replace(/\+.*/,"")+"+");y=
|
||||
y.concat(a);var c=0,d=0;for(b in mist.data.streams){y.push(b);if(mist.inputMatch(UI.findInput("Folder").source_match,mist.data.streams[b].source)){y.push(b+"+");mist.send(function(a,b){var f=b.stream,e;for(e in a.browse.files)for(var g in mist.data.capabilities.inputs)g.indexOf("Buffer")>=0||(g.indexOf("Folder")>=0||g.indexOf("Buffer.exe")>=0||g.indexOf("Folder.exe")>=0)||mist.inputMatch(mist.data.capabilities.inputs[g].source_match,"/"+a.browse.files[e])&&y.push(f+"+"+a.browse.files[e]);d++;if(c==
|
||||
d){y=y.filter(function(a,b,c){return c.lastIndexOf(a)===b}).sort();ka()}},{browse:mist.data.streams[b].source},{stream:b});c++}}if(c==d){y=y.filter(function(a,b,c){return c.lastIndexOf(a)===b}).sort();ka()}},{active_streams:1}):(y=Object.keys(mist.data.streams),ka());break;case "Triggers":if(!("triggers"in mist.data.config)||!mist.data.config.triggers)mist.data.config.triggers={};var F=$("<tbody>"),cb=$("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Trigger on").attr("data-sort-type",
|
||||
"string").addClass("sorting-asc")).append($("<th>").text("Applies to").attr("data-sort-type","string")).append($("<th>").text("Handler").attr("data-sort-type","string")).append($("<th>")))).append(F);c.append(UI.buildUI([{type:"help",help:"Triggers are a way to react to events that occur inside MistServer. These allow you to block specific users, redirect streams, keep tabs on what is being pushed where, etcetera. For full documentation, please refer to the developer documentation section on the MistServer website."}])).append($("<button>").text("New trigger").click(function(){UI.navto("Edit Trigger")})).append(cb);
|
||||
cb.stupidtable();var xa=mist.data.config.triggers;for(q in xa)for(var db in xa[q]){var ya=triggerRewrite(xa[q][db]);F.append($("<tr>").attr("data-index",q+","+db).append($("<td>").text(q)).append($("<td>").text("streams"in ya?ya.streams.join(", "):"")).append($("<td>").text(ya.handler)).append($("<td>").html($("<button>").text("Edit").click(function(){UI.navto("Edit Trigger",$(this).closest("tr").attr("data-index"))})).append($("<button>").text("Delete").click(function(){var a=$(this).closest("tr").attr("data-index").split(",");
|
||||
cb.stupidtable();var xa=mist.data.config.triggers;for(r in xa)for(var db in xa[r]){var ya=triggerRewrite(xa[r][db]);F.append($("<tr>").attr("data-index",r+","+db).append($("<td>").text(r)).append($("<td>").text("streams"in ya?ya.streams.join(", "):"")).append($("<td>").text(ya.handler)).append($("<td>").html($("<button>").text("Edit").click(function(){UI.navto("Edit Trigger",$(this).closest("tr").attr("data-index"))})).append($("<button>").text("Delete").click(function(){var a=$(this).closest("tr").attr("data-index").split(",");
|
||||
if(confirm("Are you sure you want to delete this "+a[0]+" trigger?")){mist.data.config.triggers[a[0]].splice(a[1],1);mist.data.config.triggers[a[0]].length==0&&delete mist.data.config.triggers[a[0]];mist.send(function(){UI.navto("Triggers")},{config:mist.data.config})}}))))}break;case "Edit Trigger":if(!("triggers"in mist.data.config)||!mist.data.config.triggers)mist.data.config.triggers={};if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,b)},{capabilities:!0});c.append("Loading..");
|
||||
return}if(b)var b=b.split(","),ca=triggerRewrite(mist.data.config.triggers[b[0]][b[1]]),p={triggeron:b[0],appliesto:ca.streams,url:ca.handler,async:ca.sync,"default":ca["default"],params:ca.params};else c.html($("<h2>").text("New Trigger")),p={};var eb=[];for(q in mist.data.capabilities.triggers)eb.push([q,q+": "+mist.data.capabilities.triggers[q].when]);var za=$("<div>").addClass("desc"),fb=$("<div>");c.append(UI.buildUI([{label:"Trigger on",pointer:{main:p,index:"triggeron"},help:"For what event this trigger should activate.",
|
||||
return}if(b)var b=b.split(","),ca=triggerRewrite(mist.data.config.triggers[b[0]][b[1]]),p={triggeron:b[0],appliesto:ca.streams,url:ca.handler,async:ca.sync,"default":ca["default"],params:ca.params};else c.html($("<h2>").text("New Trigger")),p={};var eb=[];for(r in mist.data.capabilities.triggers)eb.push([r,r+": "+mist.data.capabilities.triggers[r].when]);var za=$("<div>").addClass("desc"),fb=$("<div>");c.append(UI.buildUI([{label:"Trigger on",pointer:{main:p,index:"triggeron"},help:"For what event this trigger should activate.",
|
||||
type:"select",select:eb,validate:["required"],"function":function(){var a=$(this).getval(),b=mist.data.capabilities.triggers[a];za.html("");if(b){a=[$("<h4>").text("Trigger properties"),{type:"help",help:'The trigger "<i>'+a+'</i>" has the following properties:'},{type:"span",label:"Triggers",value:b.when,help:"When this trigger is activated"}];b.payload!=""&&a.push({label:"Payload",type:"textarea",value:b.payload,rows:b.payload.split("\n").length,readonly:true,clipboard:true,help:"The information this trigger sends to the handler."});
|
||||
a.push({type:"span",label:"Requires response",value:function(a){switch(a){case "ignored":return"No. The trigger will ignore the response of the handler.";case "always":return"Yes. The trigger needs a response to proceed.";case "when-blocking":return"The trigger needs a response to proceed if it is configured to be blocking.";default:return a}}(b.response),help:"Whether this trigger requires a response from the trigger handler"});a.push({type:"span",label:"Response action",value:b.response_action,
|
||||
help:"What this trigger will do with its handler's response"});za.append(UI.buildUI(a));b.stream_specific?$("[name=appliesto]").closest(".UIelement").show():$("[name=appliesto]").setval([]).closest(".UIelement").hide();if(b.argument){$("[name=params]").closest(".UIelement").show();fb.text(b.argument)}else $("[name=params]").setval("").closest(".UIelement").hide()}}},za,$("<h4>").text("Trigger settings"),{label:"Applies to",pointer:{main:p,index:"appliesto"},help:"For triggers that can apply to specific streams, this value decides what streams they are triggered for. (none checked = always triggered)",
|
||||
|
@ -247,15 +248,15 @@ pointer:{main:p,index:"async"}},{label:"Parameters",type:"str",help:$("<div>").t
|
|||
mist.send(function(){Aa();gb.text("Refresh now")})}).css("padding","0.2em 0.5em").css("flex-grow",0);c.append(UI.buildUI([{type:"help",help:"Here you have an overview of all edited settings within MistServer and possible warnings or errors MistServer has encountered. MistServer stores up to 100 logs at a time."},{label:"Refresh every",type:"select",select:[[10,"10 seconds"],[30,"30 seconds"],[60,"minute"],[300,"5 minutes"]],value:30,"function":function(){UI.interval.clear();UI.interval.set(function(){mist.send(function(){Aa()})},
|
||||
$(this).val()*1E3)},help:"How often the table below should be updated."},{label:"..or",type:"DOMfield",DOMfield:gb,help:"Instantly refresh the table below."}]));c.append($("<button>").text("Purge logs").click(function(){mist.send(function(){mist.data.log=[];UI.navto("Logs")},{clearstatlogs:true})}));F=$("<tbody>").css("font-size","0.9em");c.append($("<table>").addClass("logs").append(F));var kb=function(a){var b=$("<span>").text(a);switch(a){case "WARN":b.addClass("orange");break;case "ERROR":case "FAIL":b.addClass("red")}return b},
|
||||
Aa=function(){var a=mist.data.log;if(a){a.length>=2&&a[0][0]<a[a.length-1][0]&&a.reverse();F.html("");for(var b in a){var c=$("<span>").addClass("content"),d=a[b][2].split("|"),f;for(f in d)c.append($("<span>").text(d[f]));F.append($("<tr>").html($("<td>").text(UI.format.dateTime(a[b][0],"long")).css("white-space","nowrap")).append($("<td>").html(kb(a[b][1])).css("text-align","center")).append($("<td>").html(c).css("text-align","left")))}}};Aa();break;case "Statistics":var H=$("<span>").text("Loading..");
|
||||
c.append(H);var p={graph:"new"},B=mist.stored.get().graphs?$.extend(!0,{},mist.stored.get().graphs):{},da={};for(q in mist.data.streams)da[q]=!0;for(q in mist.data.active_streams)da[mist.data.active_streams[q]]=!0;var da=Object.keys(da).sort(),Ba=[];for(q in mist.data.config.protocols)Ba.push(mist.data.config.protocols[q].connector);Ba.sort();mist.send(function(){UI.plot.datatype.templates.cpuload.cores=0;for(var a in mist.data.capabilities.cpu)UI.plot.datatype.templates.cpuload.cores=UI.plot.datatype.templates.cpuload.cores+
|
||||
c.append(H);var p={graph:"new"},B=mist.stored.get().graphs?$.extend(!0,{},mist.stored.get().graphs):{},da={};for(r in mist.data.streams)da[r]=!0;for(r in mist.data.active_streams)da[mist.data.active_streams[r]]=!0;var da=Object.keys(da).sort(),Ba=[];for(r in mist.data.config.protocols)Ba.push(mist.data.config.protocols[r].connector);Ba.sort();mist.send(function(){UI.plot.datatype.templates.cpuload.cores=0;for(var a in mist.data.capabilities.cpu)UI.plot.datatype.templates.cpuload.cores=UI.plot.datatype.templates.cpuload.cores+
|
||||
mist.data.capabilities.cpu[a].cores;H.html(UI.buildUI([{type:"help",help:"Here you will find the MistServer stream statistics, you can select various categories yourself. All statistics are live: up to five minutes are saved."},$("<h3>").text("Select the data to display"),{label:"Add to",type:"select",select:[["new","New graph"]],pointer:{main:p,index:"graph"},classes:["graph_ids"],"function":function(){if($(this).val()){var a=H.find(".graph_xaxis"),b=H.find(".graph_id");if($(this).val()=="new"){a.children("option").prop("disabled",
|
||||
false);b.setval("Graph "+(Object.keys(B).length+1)).closest("label").show()}else{var c=B[$(this).val()].xaxis;a.children("option").prop("disabled",true).filter('[value="'+c+'"]').prop("disabled",false);b.closest("label").hide()}a.children('option[value="'+a.val()+'"]:disabled').length&&a.val(a.children("option:enabled").first().val());a.trigger("change")}}},{label:"Graph id",type:"str",pointer:{main:p,index:"id"},classes:["graph_id"],validate:[function(a){return a in B?{msg:"This graph id has already been used. Please enter something else.",
|
||||
classes:["red"]}:false}]},{label:"Axis type",type:"select",select:[["time","Time line"]],pointer:{main:p,index:"xaxis"},value:"time",classes:["graph_xaxis"],"function":function(){$s=H.find(".graph_datatype");switch($(this).getval()){case "coords":$s.children("option").prop("disabled",true).filter('[value="coords"]').prop("disabled",false);break;case "time":$s.children("option").prop("disabled",false).filter('[value="coords"]').prop("disabled",true)}if(!$s.val()||$s.children('option[value="'+$s.val()+
|
||||
'"]:disabled').length){$s.val($s.children("option:enabled").first().val());$s.trigger("change")}}},{label:"Data type",type:"select",select:[["clients","Connections"],["upbps","Bandwidth (up)"],["downbps","Bandwidth (down)"],["cpuload","CPU use"],["memload","Memory load"],["coords","Client location"],["perc_lost","Lost packages"],["perc_retrans","Re-transmitted packages"]],pointer:{main:p,index:"datatype"},classes:["graph_datatype"],"function":function(){$s=H.find(".graph_origin");switch($(this).getval()){case "cpuload":case "memload":$s.find("input[type=radio]").not('[value="total"]').prop("disabled",
|
||||
true);$s.find('input[type=radio][value="total"]').prop("checked",true);break;default:$s.find("input[type=radio]").prop("disabled",false)}}},{label:"Data origin",type:"radioselect",radioselect:[["total","All"],["stream","The stream:",da],["protocol","The protocol:",Ba]],pointer:{main:p,index:"origin"},value:["total"],classes:["graph_origin"]},{type:"buttons",buttons:[{label:"Add data set",type:"save","function":function(){var a;if(p.graph=="new"){a=UI.plot.addGraph(p,b);B[a.id]=a;H.find("input.graph_id").val("");
|
||||
H.find("select.graph_ids").append($("<option>").text(a.id)).val(a.id).trigger("change")}else a=B[p.graph];var c=UI.plot.datatype.getOptions({datatype:p.datatype,origin:p.origin});a.datasets.push(c);UI.plot.save(a);UI.plot.go(B)}}]}]));var b=$("<div>").addClass("graph_container");c.append(b);var d=H.find("select.graph_ids");for(a in B){var f=UI.plot.addGraph(B[a],b);d.append($("<option>").text(f.id)).val(f.id);var e=[],g;for(g in B[a].datasets){var h=UI.plot.datatype.getOptions({datatype:B[a].datasets[g].datatype,
|
||||
origin:B[a].datasets[g].origin});e.push(h)}f.datasets=e;B[f.id]=f}d.trigger("change");UI.plot.go(B);UI.interval.set(function(){UI.plot.go(B)},1E4)},{active_streams:!0,capabilities:!0});break;case "Server Stats":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var Ca=$("<table>"),O=$("<table>"),hb={vheader:"CPUs",labels:["Model","Processor speed","Amount of cores","Amount of threads"],content:[]};for(q in mist.data.capabilities.cpu){var la=
|
||||
mist.data.capabilities.cpu[q];hb.content.push({header:"CPU #"+(Number(q)+1),body:[la.model,UI.format.addUnit(UI.format.number(la.mhz),"MHz"),la.cores,la.threads]})}var lb=UI.buildVheaderTable(hb),ib=function(){var a=mist.data.capabilities.mem,b=mist.data.capabilities.load,a={vheader:"Memory",labels:["Used","Cached","Available","Total"],content:[{header:"Physical memory",body:[UI.format.bytes(a.used*1048576)+" ("+UI.format.addUnit(b.memory,"%")+")",UI.format.bytes(a.cached*1048576),UI.format.bytes(a.free*
|
||||
origin:B[a].datasets[g].origin});e.push(h)}f.datasets=e;B[f.id]=f}d.trigger("change");UI.plot.go(B);UI.interval.set(function(){UI.plot.go(B)},1E4)},{active_streams:!0,capabilities:!0});break;case "Server Stats":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});c.append("Loading..");return}var Ca=$("<table>"),O=$("<table>"),hb={vheader:"CPUs",labels:["Model","Processor speed","Amount of cores","Amount of threads"],content:[]};for(r in mist.data.capabilities.cpu){var la=
|
||||
mist.data.capabilities.cpu[r];hb.content.push({header:"CPU #"+(Number(r)+1),body:[la.model,UI.format.addUnit(UI.format.number(la.mhz),"MHz"),la.cores,la.threads]})}var lb=UI.buildVheaderTable(hb),ib=function(){var a=mist.data.capabilities.mem,b=mist.data.capabilities.load,a={vheader:"Memory",labels:["Used","Cached","Available","Total"],content:[{header:"Physical memory",body:[UI.format.bytes(a.used*1048576)+" ("+UI.format.addUnit(b.memory,"%")+")",UI.format.bytes(a.cached*1048576),UI.format.bytes(a.free*
|
||||
1048576),UI.format.bytes(a.total*1048576)]},{header:"Swap memory",body:[UI.format.bytes((a.swaptotal-a.swapfree)*1048576),UI.format.addUnit("","N/A"),UI.format.bytes(a.swapfree*1048576),UI.format.bytes(a.swaptotal*1048576)]}]},a=UI.buildVheaderTable(a);Ca.replaceWith(a);Ca=a;b={vheader:"Load average",labels:["CPU use","1 minute","5 minutes","15 minutes"],content:[{header:" ",body:[UI.format.addUnit(UI.format.number(mist.data.capabilities.cpu_use/10),"%"),UI.format.number(b.one/100),UI.format.number(b.five/
|
||||
100),UI.format.number(b.fifteen/100)]}]};b=UI.buildVheaderTable(b);O.replaceWith(b);O=b};ib();c.append(UI.buildUI([{type:"help",help:"You can find general server statistics here. Note that memory and CPU usage is for your entire machine, not just MistServer."}])).append($("<table>").css("width","auto").addClass("nolay").append($("<tr>").append($("<td>").append(Ca)).append($("<td>").append(O))).append($("<tr>").append($("<td>").append(lb).attr("colspan",2))));UI.interval.set(function(){mist.send(function(){ib()},
|
||||
{capabilities:true})},3E4);break;case "Email for Help":var I=$.extend({},mist.data);delete I.statistics;delete I.totals;delete I.clients;delete I.capabilities;I=JSON.stringify(I);I="Version: "+mist.data.config.version+"\n\nConfig:\n"+I;p={};c.append(UI.buildUI([{type:"help",help:"You can use this form to email MistServer support if you're having difficulties.<br>A copy of your server config file will automatically be included."},{type:"str",label:"Your name",validate:["required"],pointer:{main:p,
|
||||
|
|
60
lsp/mist.js
60
lsp/mist.js
|
@ -4932,7 +4932,7 @@ var UI = {
|
|||
var tables = {
|
||||
audio: {
|
||||
vheader: 'Audio',
|
||||
labels: ['Codec','Duration','Avg bitrate','Peak bitrate','Channels','Samplerate','Language','Track index'],
|
||||
labels: ['Codec','Duration','Avg bitrate','Peak bitrate','Channels','Samplerate','Language','Track index',""],
|
||||
content: []
|
||||
},
|
||||
video: {
|
||||
|
@ -4942,7 +4942,12 @@ var UI = {
|
|||
},
|
||||
subtitle: {
|
||||
vheader: 'Subtitles',
|
||||
labels: ['Codec','Duration','Avg bitrate','Peak bitrate','Language','Track index'],
|
||||
labels: ['Codec','Duration','Avg bitrate','Peak bitrate','Language','Track index',"","",""],
|
||||
content: []
|
||||
},
|
||||
meta: {
|
||||
vheader: 'Metadata',
|
||||
labels: ['Codec','Duration','Avg bitrate','Peak bitrate',"","","","",""],
|
||||
content: []
|
||||
}
|
||||
}
|
||||
|
@ -4979,7 +4984,8 @@ var UI = {
|
|||
track.channels,
|
||||
UI.format.addUnit(UI.format.number(track.rate),'Hz'),
|
||||
('language' in track ? track.language : 'unknown'),
|
||||
(trackindex.audio)
|
||||
(trackindex.audio),
|
||||
""
|
||||
]
|
||||
});
|
||||
trackindex.audio++;
|
||||
|
@ -5012,15 +5018,29 @@ var UI = {
|
|||
peakoravg(track,"bps"),
|
||||
peakoravg(track,"maxbps"),
|
||||
('language' in track ? track.language : 'unknown'),
|
||||
(trackindex.subtitle)
|
||||
(trackindex.subtitle),
|
||||
"","",""
|
||||
]
|
||||
});
|
||||
trackindex.subtitle++
|
||||
break;
|
||||
}
|
||||
else {
|
||||
tables.meta.content.push({
|
||||
header: 'Track '+i.split('_').pop(),
|
||||
body: [
|
||||
track.codec,
|
||||
UI.format.duration((track.lastms-track.firstms)/1000)+'<br><span class=description>'+UI.format.duration(track.firstms/1000)+' to '+UI.format.duration(track.lastms/1000)+'</span>',
|
||||
peakoravg(track,"bps"),
|
||||
peakoravg(track,"maxbps"),
|
||||
"","","","",""
|
||||
]
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var tracktypes = ['audio','video','subtitle'];
|
||||
var tracktypes = ['audio','video','subtitle','meta'];
|
||||
var $c = $('<div>').css({
|
||||
'display': 'flex',
|
||||
'flex-flow': 'row wrap',
|
||||
|
@ -5603,7 +5623,7 @@ var UI = {
|
|||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: otherbase+'json_'+escapedstream+'.js',
|
||||
url: otherbase+'json_'+escapedstream+'.js?inclzero=1',
|
||||
success: function(d) {
|
||||
|
||||
var build = [];
|
||||
|
@ -5637,22 +5657,24 @@ var UI = {
|
|||
|
||||
$setTracks.html('');
|
||||
var tracks = {};
|
||||
for (var i in d.meta.tracks) {
|
||||
var t = d.meta.tracks[i];
|
||||
if (t.codec == "subtitle") {
|
||||
t.type = "subtitle";
|
||||
}
|
||||
if ((t.type != 'audio') && (t.type != 'video') && (t.type != "subtitle")) { continue; }
|
||||
if (d.meta) {
|
||||
for (var i in d.meta.tracks) {
|
||||
var t = d.meta.tracks[i];
|
||||
if (t.codec == "subtitle") {
|
||||
t.type = "subtitle";
|
||||
}
|
||||
if ((t.type != 'audio') && (t.type != 'video') && (t.type != "subtitle")) { continue; }
|
||||
|
||||
if (!(t.type in tracks)) {
|
||||
if (t.type == "subtitle") {
|
||||
tracks[t.type] = [];
|
||||
}
|
||||
else {
|
||||
tracks[t.type] = [[(''),"Autoselect "+t.type]];
|
||||
if (!(t.type in tracks)) {
|
||||
if (t.type == "subtitle") {
|
||||
tracks[t.type] = [];
|
||||
}
|
||||
else {
|
||||
tracks[t.type] = [[(''),"Autoselect "+t.type]];
|
||||
}
|
||||
}
|
||||
tracks[t.type].push([t.trackid,UI.format.capital(t.type)+' track '+(tracks[t.type].length+(t.type == "subtitle" ? 1 : 0))]);
|
||||
}
|
||||
tracks[t.type].push([t.trackid,UI.format.capital(t.type)+' track '+(tracks[t.type].length+(t.type == "subtitle" ? 1 : 0))]);
|
||||
}
|
||||
if (Object.keys(tracks).length) {
|
||||
$setTracks.closest('label').show();
|
||||
|
|
Loading…
Add table
Reference in a new issue