$(function(){ UI.elements = { menu: $('nav > .menu'), secondary_menu: $('nav.secondary_menu'), main: $('main'), connection: { status: $('#connection'), user_and_host: $('#user_and_host'), msg: $('#message') } }; UI.buildMenu(); UI.stored.getOpts(); //check if username and host have been stored in the url if (location.hash) { var hash = decodeURIComponent(location.hash).substring(1).split('@'); var user = hash[0].split('&'); mist.user.name = user[0]; if (user[1]) { mist.user.host = user[1]; } } //check if we are logged in mist.send(function(d){ //we're logged in $(window).trigger('hashchange'); },{},{timeout: 5, hide: true}); }); $(window).on('hashchange', function(e) { var loc = decodeURIComponent(location.hash).substring(1).split('@'); if (!loc[1]) { loc[1] = ''; } var tab = loc[1].split('&'); if (tab[0] == '') { tab[0] = 'Overview'; } UI.showTab(tab[0],tab[1]); }); var UI = { debug: false, elements: {}, stored: { getOpts: function(){ var stored = localStorage['stored']; if (stored) { stored = JSON.parse(stored); } $.extend(true,this.vars,stored); return this.vars; }, saveOpt: function(name,val){ this.vars[name] = val; localStorage['stored'] = JSON.stringify(this.vars); return this.vars; }, vars: { helpme: true } }, interval: { clear: function(){ if (typeof this.opts == 'undefined') { return; } clearInterval(this.opts.id); delete this.opts; }, set: function(callback,delay){ if (this.opts) { log('[interval]','Set called on interval, but an interval is already active.'); } this.opts = { delay: delay, callback: callback }; this.opts.id = setInterval(callback,delay); } }, returnTab: ['Overview'], countrylist: {'AF':'Afghanistan','AX':'Åland Islands','AL':'Albania','DZ':'Algeria','AS':'American Samoa','AD':'Andorra', 'AO':'Angola','AI':'Anguilla','AQ':'Antarctica','AG':'Antigua and Barbuda','AR':'Argentina','AM':'Armenia','AW':'Aruba', 'AU':'Australia','AT':'Austria','AZ':'Azerbaijan','BS':'Bahamas','BH':'Bahrain','BD':'Bangladesh','BB':'Barbados', 'BY':'Belarus','BE':'Belgium','BZ':'Belize','BJ':'Benin','BM':'Bermuda','BT':'Bhutan','BO':'Bolivia, Plurinational State of', 'BQ':'Bonaire, Sint Eustatius and Saba','BA':'Bosnia and Herzegovina','BW':'Botswana','BV':'Bouvet Island','BR':'Brazil', 'IO':'British Indian Ocean Territory','BN':'Brunei Darussalam','BG':'Bulgaria','BF':'Burkina Faso','BI':'Burundi','KH':'Cambodia', 'CM':'Cameroon','CA':'Canada','CV':'Cape Verde','KY':'Cayman Islands','CF':'Central African Republic','TD':'Chad','CL':'Chile', 'CN':'China','CX':'Christmas Island','CC':'Cocos (Keeling) Islands','CO':'Colombia','KM':'Comoros','CG':'Congo', 'CD':'Congo, the Democratic Republic of the','CK':'Cook Islands','CR':'Costa Rica','CI':'Côte d\'Ivoire','HR':'Croatia', 'CU':'Cuba','CW':'Curaçao','CY':'Cyprus','CZ':'Czech Republic','DK':'Denmark','DJ':'Djibouti','DM':'Dominica', 'DO':'Dominican Republic','EC':'Ecuador','EG':'Egypt','SV':'El Salvador','GQ':'Equatorial Guinea','ER':'Eritrea','EE':'Estonia', 'ET':'Ethiopia','FK':'Falkland Islands (Malvinas)','FO':'Faroe Islands','FJ':'Fiji','FI':'Finland','FR':'France','GF':'French Guiana', 'PF':'French Polynesia','TF':'French Southern Territories','GA':'Gabon','GM':'Gambia','GE':'Georgia','DE':'Germany','GH':'Ghana', 'GI':'Gibraltar','GR':'Greece','GL':'Greenland','GD':'Grenada','GP':'Guadeloupe','GU':'Guam','GT':'Guatemala','GG':'Guernsey', 'GN':'Guinea','GW':'Guinea-Bissau','GY':'Guyana','HT':'Haiti','HM':'Heard Island and McDonald Islands', 'VA':'Holy See (Vatican City State)','HN':'Honduras','HK':'Hong Kong','HU':'Hungary','IS':'Iceland','IN':'India','ID':'Indonesia', 'IR':'Iran, Islamic Republic of','IQ':'Iraq','IE':'Ireland','IM':'Isle of Man','IL':'Israel','IT':'Italy','JM':'Jamaica', 'JP':'Japan','JE':'Jersey','JO':'Jordan','KZ':'Kazakhstan','KE':'Kenya','KI':'Kiribati', 'KP':'Korea, Democratic People\'s Republic of','KR':'Korea, Republic of','KW':'Kuwait','KG':'Kyrgyzstan', 'LA':'Lao People\'s Democratic Republic','LV':'Latvia','LB':'Lebanon','LS':'Lesotho','LR':'Liberia','LY':'Libya', 'LI':'Liechtenstein','LT':'Lithuania','LU':'Luxembourg','MO':'Macao','MK':'Macedonia, the former Yugoslav Republic of', 'MG':'Madagascar','MW':'Malawi','MY':'Malaysia','MV':'Maldives','ML':'Mali','MT':'Malta','MH':'Marshall Islands', 'MQ':'Martinique','MR':'Mauritania','MU':'Mauritius','YT':'Mayotte','MX':'Mexico','FM':'Micronesia, Federated States of', 'MD':'Moldova, Republic of','MC':'Monaco','MN':'Mongolia','ME':'Montenegro','MS':'Montserrat','MA':'Morocco','MZ':'Mozambique', 'MM':'Myanmar','NA':'Namibia','NR':'Nauru','NP':'Nepal','NL':'Netherlands','NC':'New Caledonia','NZ':'New Zealand','NI':'Nicaragua', 'NE':'Niger','NG':'Nigeria','NU':'Niue','NF':'Norfolk Island','MP':'Northern Mariana Islands','NO':'Norway','OM':'Oman', 'PK':'Pakistan','PW':'Palau','PS':'Palestine, State of','PA':'Panama','PG':'Papua New Guinea','PY':'Paraguay','PE':'Peru', 'PH':'Philippines','PN':'Pitcairn','PL':'Poland','PT':'Portugal','PR':'Puerto Rico','QA':'Qatar','RE':'Réunion', 'RO':'Romania','RU':'Russian Federation','RW':'Rwanda','BL':'Saint Barthélemy','SH':'Saint Helena, Ascension and Tristan da Cunha', 'KN':'Saint Kitts and Nevis','LC':'Saint Lucia','MF':'Saint Martin (French part)','PM':'Saint Pierre and Miquelon', 'VC':'Saint Vincent and the Grenadines','WS':'Samoa','SM':'San Marino','ST':'Sao Tome and Principe','SA':'Saudi Arabia', 'SN':'Senegal','RS':'Serbia','SC':'Seychelles','SL':'Sierra Leone','SG':'Singapore','SX':'Sint Maarten (Dutch part)','SK':'Slovakia', 'SI':'Slovenia','SB':'Solomon Islands','SO':'Somalia','ZA':'South Africa','GS':'South Georgia and the South Sandwich Islands', 'SS':'South Sudan','ES':'Spain','LK':'Sri Lanka','SD':'Sudan','SR':'Suriname','SJ':'Svalbard and Jan Mayen','SZ':'Swaziland', 'SE':'Sweden','CH':'Switzerland','SY':'Syrian Arab Republic','TW':'Taiwan, Province of China','TJ':'Tajikistan', 'TZ':'Tanzania, United Republic of','TH':'Thailand','TL':'Timor-Leste','TG':'Togo','TK':'Tokelau','TO':'Tonga', 'TT':'Trinidad and Tobago','TN':'Tunisia','TR':'Turkey','TM':'Turkmenistan','TC':'Turks and Caicos Islands','TV':'Tuvalu', 'UG':'Uganda','UA':'Ukraine','AE':'United Arab Emirates','GB':'United Kingdom','US':'United States', 'UM':'United States Minor Outlying Islands','UY':'Uruguay','UZ':'Uzbekistan','VU':'Vanuatu','VE':'Venezuela, Bolivarian Republic of', 'VN':'Viet Nam','VG':'Virgin Islands, British','VI':'Virgin Islands, U.S.','WF':'Wallis and Futuna','EH':'Western Sahara','YE':'Yemen', 'ZM':'Zambia','ZW':'Zimbabwe' }, tooltip: { show: function (pos,contents){ $tooltip = this.element; if (!$.contains(document.body,$tooltip[0])) { $('body').append($tooltip); } $tooltip.html(contents); clearTimeout(this.hiding); delete this.hiding; var mh = $(document).height() - $tooltip.outerHeight(); var mw = $(document).width() - $tooltip.outerWidth(); $tooltip.css('left',Math.min(pos.pageX+10,mw-10)); $tooltip.css('top',Math.min(pos.pageY+25,mh-10)); $tooltip.show().addClass('show'); }, hide: function() { $tooltip = this.element; $tooltip.removeClass('show'); this.hiding = setTimeout(function(){ $tooltip.hide(); },500); }, element: $('
').text(text) ).append($qr) ); $qr.qrcode({ text: text, size: Math.min($qr.width(),$qr.height()) }) }) ) ); } if ('rows' in e) { $field.attr('rows',e.rows); } if (('LTSonly' in e) && (!mist.data.LTS)) { $fc.addClass('LTSonly'); $field.prop('disabled',true); } //additional field type code switch (e.type) { case 'browse': var $master = $('
').text('No account has been created yet in the MistServer at ').append( $('').text(mist.user.host) ).append('.') ); $main.append(UI.buildUI([ { type: 'buttons', buttons: [{ label: 'Select other host', type: 'cancel', css: {'float': 'left'}, 'function': function(){ UI.navto('Login'); } }] },{ type: 'custom', custom: $('') },{ label: 'Desired username', type: 'str', validate: ['required'], help: 'Enter your desired username. In the future, you will need this to access the Management Interface.', pointer: { main: mist.user, index: 'name' } },{ label: 'Desired password', type: 'password', validate: ['required',function(val,me){ $('.match_password').not($(me)).trigger('change'); return false; }], help: 'Enter your desired password. In the future, you will need this to access the Management Interface.', pointer: { main: mist.user, index: 'password' }, classes: ['match_password'] },{ label: 'Repeat password', type: 'password', validate: ['required',function(val,me){ if (val != $('.match_password').not($(me)).val()) { return { msg:'The fields "Desired password" and "Repeat password" do not match.', classes: ['red'] } } return false; }], help: 'Repeat your desired password.', classes: ['match_password'] },{ type: 'buttons', buttons: [{ type: 'save', label: 'Create new account', 'function': function(){ mist.send(function(){ UI.navto('Account created'); },{ authorize: { new_username: mist.user.name, new_password: mist.user.password } }); } }] }])); break; case 'Account created': UI.elements.menu.addClass('hide');; $main.append( $('
').text('Your account has been created succesfully.') ).append(UI.buildUI([ { type: 'text', text: 'Would you like to enable all (currently) available protocols with their default settings?' },{ type: 'buttons', buttons: [{ label: 'Enable protocols', type: 'save', 'function': function(){ if (mist.data.config.protocols) { $main.append('Unable to enable all protocols as protocol settings already exist.'); return; } $main.append('Retrieving available protocols..'); mist.send(function(d){ var protocols = []; for (var i in d.capabilities.connectors) { var connector = d.capabilities.connectors[i]; if (connector.required) { $main.append('Could not enable protocol "'+i+'" because it has required settings.'); continue; } protocols.push( {connector: i} ); $main.append('Enabled protocol "'+i+'".'); } $main.append('Saving protocol settings..') mist.send(function(d){ $main.append('Protocols enabled. Redirecting..'); setTimeout(function(){ UI.navto('Overview'); },5000); },{config:{protocols:protocols}}); },{capabilities:true}); } },{ label: 'Skip', type: 'cancel', 'function': function(){ UI.navto('Overview'); } }] } ])); break; case 'Overview': var $versioncheck = $('').text('Loading..'); var $streamsonline = $(''); var $viewers = $(''); var $servertime = $(''); $main.append(UI.buildUI([ { type: 'help', help: 'You can find most basic information about your MistServer here.You can also set the debug level and force a save to the config.json file that MistServer uses to save your settings. ' },{ type: 'span', label: 'Version', pointer: { main: mist.data.config, index: 'version' } },{ type: 'span', label: 'Version check', value: $versioncheck, LTSonly: true },{ type: 'span', label: 'Server time', value: $servertime },{ type: 'span', label: 'Current streams', value: $streamsonline },{ type: 'span', label: 'Current connections', value: $viewers },$(''),{ type: 'str', label: 'Human readable name', pointer: { main: mist.data.config, index: 'name' }, help: 'You can name your MistServer here for personal use. You\'ll still need to set host name within your network yourself.' },{ type: 'debug', label: 'Debug level', pointer: { main: mist.data.config, index: 'debug' }, help: 'You can set the amount of debug information MistServer saves in the log. A full reboot of MistServer is required before some components of MistServer can post debug information.' },{ type: 'checkbox', label: 'Force configurations save', pointer: { main: mist.data, index: 'save' }, help: 'Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don\'t forget to press save after ticking the box.' },{ type: 'buttons', buttons: [{ type: 'save', label: 'Save', 'function': function(){ var send = {config: mist.data.config}; if (mist.data.save) { send.save = mist.data.save; } mist.send(function(){ UI.navto('Overview'); },send) } }] } ])); if (mist.data.LTS) { function update_update() { var info = mist.stored.get().update || {}; if (!('uptodate' in info)) { $versioncheck.text('Unknown'); return; } else if (info.error) { $versioncheck.addClass('red').text(info.error); return; } else if (info.uptodate) { $versioncheck.text('Your version is up to date.').addClass('green'); return; } else { $versioncheck.addClass('red').text('Version outdated!').append( $('').text('Update').css({'font-size':'1em','margin-left':'1em'}).click(function(){ if (confirm('Are you sure you want to execute a rolling update?')) { $versioncheck.addClass('orange').removeClass('red').text('Rolling update command sent..'); mist.stored.del('update'); mist.send(function(d){ UI.navto('Overview'); },{autoupdate: true}); } }) ); } } if ((!mist.stored.get().update) || ((new Date()).getTime()-mist.stored.get().update.lastchecked > 3600e3)) { var update = mist.stored.get().update || {}; update.lastchecked = (new Date()).getTime(); mist.send(function(d){ mist.stored.set('update',$.extend(true,update,d.update)); update_update(); },{checkupdate: true}); } else { update_update(); } } else { $versioncheck.text(''); } function updateViewers() { mist.send(function(d){ enterStats() },{ totals:{ fields: ['clients'], start: -10 }, active_streams: true }); } function enterStats() { if ('active_streams' in mist.data) { var active = (mist.data.active_streams ? mist.data.active_streams.length : 0) } else { var active = '?'; } $streamsonline.text(active+' active, '+(mist.data.streams ? Object.keys(mist.data.streams).length : 0)+' configured'); if (('totals' in mist.data) && ('all_streams' in mist.data.totals)) { var clients = mist.data.totals.all_streams.all_protocols.clients; clients = (clients.length ? UI.format.number(clients[clients.length-1][1]) : 0); } else { clients = 'Loading..'; } $viewers.text(clients); $servertime.text(UI.format.dateTime(mist.data.config.time,'long')); } updateViewers(); enterStats(); UI.interval.set(updateViewers,30e3); break; case 'Protocols': if (typeof mist.data.capabilities == 'undefined') { mist.send(function(d){ UI.navto(tab); },{capabilities: true}); $main.append('Loading..'); return; } var $tbody = $(''); $main.append( UI.buildUI([{ type: 'help', help: 'You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols.' }]) ).append( $('').text('New protocol').click(function(){ UI.navto('Edit Protocol'); }) ).append( $('').html( $('').html( $('').html( $('').text('Protocol') ).append( $('').text('Status') ).append( $('').text('Settings') ).append( $('') ) ) ).append( $tbody ) ); function updateProtocols() { function displaySettings(protocol){ var capabilities = mist.data.capabilities.connectors[protocol.connector]; if (!capabilities) { return ''; } var str = []; var types = ['required','optional'] for (var j in types) { for (var i in capabilities[types[j]]) { if ((protocol[i]) && (protocol[i] != '')) { str.push(i+': '+protocol[i]); } else if (capabilities[types[j]][i]['default']) { str.push(i+': '+capabilities[types[j]][i]['default']); } } } return $('').addClass('description').text(str.join(', ')); } $tbody.html(''); for (var i in mist.data.config.protocols) { var protocol = mist.data.config.protocols[i]; $tbody.append( $('').data('index',i).append( $('').text(protocol.connector) ).append( $('').html(UI.format.status(protocol)) ).append( $('').html(displaySettings(protocol)) ).append( $('').css('text-align','right').html( $('').text('Edit').click(function(){ UI.navto('Edit Protocol',$(this).closest('tr').data('index')); }) ).append( $('').text('Delete').click(function(){ var index = $(this).closest('tr').data('index'); if (confirm('Are you sure you want to delete the protocol "'+mist.data.config.protocols[index].connector+'"?')) { mist.data.config.protocols.splice(index,1); mist.send(function(d){ UI.navto('Protocols'); },{config: mist.data.config}); } }) ) ) ); } } updateProtocols(); UI.interval.set(function(){ mist.send(function(){ updateProtocols(); }); },30e3); break; case 'Edit Protocol': if (typeof mist.data.capabilities == 'undefined') { mist.send(function(d){ UI.navto(tab,other); },{capabilities: true}); $main.append('Loading..'); return; } var editing = false; if ((other != '') && (other >= 0)) { editing = true; } var current = {}; for (var i in mist.data.config.protocols) { current[mist.data.config.protocols[i].connector] = 1; } function buildProtocolSettings(kind) { var input = mist.data.capabilities.connectors[kind]; var build = mist.convertBuildOptions(input,saveas); build.push({ type: 'hidden', pointer: { main: saveas, index: 'connector' }, value: kind }); build.push({ type: 'buttons', buttons: [ { type: 'save', label: 'Save', 'function': function(){ if (editing) { mist.data.config.protocols[other] = saveas; } else { if (!mist.data.config.protocols) { mist.data.config.protocols = []; } mist.data.config.protocols.push(saveas); } mist.send(function(d){ UI.navto('Protocols'); },{config: mist.data.config}); } },{ type: 'cancel', label: 'Cancel', 'function': function(){ UI.navto('Protocols'); } } ] }); if (('deps' in input) && (input.deps != '')) { $t = $('').text('Dependencies:'); $ul = $(''); $t.append($ul); if (typeof input.deps == 'string') { input.deps = input.deps.split(', '); } for (var i in input.deps) { var $li = $('').text(input.deps[i]+' '); $ul.append($li); if ((typeof current[input.deps[i]] != 'undefined') || (typeof current[input.deps[i]+'.exe'] != 'undefined')) { //also check for the windows executable $li.append( $('').addClass('green').text('(Configured)') ); } else { $li.append( $('').addClass('red').text('(Not yet configured)') ); } } build.unshift({ type: 'text', text: $t[0].innerHTML }); } return UI.buildUI(build); } var current = {}; for (var i in mist.data.config.protocols) { current[mist.data.config.protocols[i].connector] = 1; } if (!editing) { //new $main.html( $('').text('New Protocol') ); var saveas = {}; var select = []; for (var i in mist.data.capabilities.connectors) { select.push([i,i]); } var $cont = $(''); $main.append(UI.buildUI([{ label: 'Protocol', type: 'select', select: select, 'function': function(){ $cont.html(buildProtocolSettings($(this).getval())); } }])).append( $cont ); } else { //editing var protocol = mist.data.config.protocols[other]; var saveas = protocol; $main.find('h2').append(' "'+protocol.connector+'"'); $main.append(buildProtocolSettings(protocol.connector)); } break; case 'Streams': if (!('capabilities' in mist.data)) { $main.html('Loading..'); mist.send(function(){ UI.navto(tab); },{capabilities: true}); return; } var $tbody = $('').append($('').append('').attr('colspan',6).text('Loading..')); var $table = $('').html( $('').html( $('').html( $('').text('Stream name').attr('data-sort-type','string').addClass('sorting-asc') ).append( $('').text('Source').attr('data-sort-type','string') ).append( $('').text('Status').attr('data-sort-type','int') ).append( $('').css('text-align','right').text('Connections').attr('data-sort-type','int') ).append( $('') ).append( $('') ) ) ).append($tbody); $main.append( UI.buildUI([{ type: 'help', help: 'Here you can create, edit or delete new and existing streams. Immidiately go to the stream preview or view the information available about the stream with the info button.' }]) ).append( $('').text('New stream').click(function(){ UI.navto('Edit Stream'); }) ).append($table); $table.stupidtable(); function buildStreamTable() { var i = 0; $tbody.html(''); if (mist.data.LTS) { //insert active wildcard streams (should overwrite active folder wildcard streams) for (var i in mist.data.active_streams) { var streamsplit = mist.data.active_streams[i].split('+'); if (streamsplit.length < 2) { continue; } if (streamsplit[0] in mist.data.streams) { var wcstream = createWcStreamObject(mist.data.active_streams[i],mist.data.streams[streamsplit[0]]); wcstream.online = 1; //it's in active_streams, so it's active. Go figure. allstreams[mist.data.active_streams[i]] = wcstream; } } } var streams = Object.keys(allstreams); streams.sort(); for (var s in streams) { var streamname = streams[s]; var stream; if (streamname in mist.data.streams) { stream = mist.data.streams[streamname]; } else { stream = allstreams[streamname]; } var $viewers = $('').css('text-align','right').html($('').addClass('description').text('Loading..')); var v = 0; if ((typeof mist.data.totals != 'undefined') && (typeof mist.data.totals[streamname] != 'undefined')) { var data = mist.data.totals[streamname].all_protocols.clients; var v = 0; //get the average value if (data.length) { for (var i in data) { v += data[i][1]; } v = Math.round(v / data.length); } } $viewers.html(UI.format.number(v)); if ((v == 0) && (stream.online == 1)) { stream.online = 2; } var $buttons = $('').css('text-align','right').css('white-space','nowrap'); if ((!('ischild' in stream)) || (!stream.ischild)) { $buttons.html( $('').text('Edit').click(function(){ UI.navto('Edit Stream',$(this).closest('tr').data('index')); }) ).append( $('').text('Delete').click(function(){ var index = $(this).closest('tr').data('index'); if (confirm('Are you sure you want to delete the stream "'+index+'"?')) { delete mist.data.streams[index]; var send = {}; if (mist.data.LTS) { send.deletestream = [index]; } else { send.streams = mist.data.streams; } mist.send(function(d){ UI.navto('Streams'); },send); } }) ); } var $streamnamelabel = $('').text(streamname); if (stream.ischild) { $streamnamelabel.css('padding-left','1em'); } var $online = UI.format.status(stream); var $preview = $('').text('Preview').click(function(){ UI.navto('Preview',$(this).closest('tr').data('index')); }); if (stream.filesfound) { $online.html(''); $preview = ''; $viewers.html(''); } $tbody.append( $('').data('index',streamname).html( $('').html($streamnamelabel).attr('title',streamname).addClass('overflow_ellipsis') ).append( $('').text(stream.source).attr('title',stream.source).addClass('description').addClass('overflow_ellipsis').css('max-width','20em') ).append( $('').data('sort-value',stream.online).html($online) ).append( $viewers ).append( $('').html( $preview ) ).append( $buttons ) ); i++; } } function updateStreams() { var totals = []; for (var i in mist.data.active_streams) { totals.push({ streams: [mist.data.active_streams[i]], fields: ['clients'], start: -2 }); } mist.send(function(){ $.extend(true,allstreams,mist.data.streams); buildStreamTable(); },{ totals: totals, active_streams: true }); } var allstreams = $.extend(true,{},mist.data.streams); function createWcStreamObject(streamname,parent) { var wcstream = $.extend({},parent); delete wcstream.meta; delete wcstream.error; wcstream.online = 2; //should either be available (2) or active (1) wcstream.name = streamname; wcstream.ischild = true; return wcstream; } if (mist.data.LTS) { //insert folder streams var browserequests = 0; var browsecomplete = 0; for (var s in mist.data.streams) { var inputs_f = mist.data.capabilities.inputs.Folder || mist.data.capabilities.inputs['Folder.exe']; if (mist.inputMatch(inputs_f.source_match,mist.data.streams[s].source)) { //this is a folder stream allstreams[s].source += '*'; mist.send(function(d,opts){ var s = opts.stream; for (var i in d.browse.files) { for (var j in mist.data.capabilities.inputs) { if ((j.indexOf('Buffer') >= 0) || (j.indexOf('Folder') >= 0)) { continue; } if (mist.inputMatch(mist.data.capabilities.inputs[j].source_match,'/'+d.browse.files[i])) { var streamname = s+'+'+d.browse.files[i]; allstreams[streamname] = createWcStreamObject(streamname,mist.data.streams[s]); allstreams[streamname].source = mist.data.streams[s].source+d.browse.files[i]; } } } if (('files' in d.browse) && (d.browse.files.length)) { allstreams[s].filesfound = true; } else { mist.data.streams[s].filesfound = false; } browsecomplete++; if (browserequests == browsecomplete) { mist.send(function(){ updateStreams(); },{active_streams: true}); UI.interval.set(function(){ updateStreams(); },10e3); } },{browse:mist.data.streams[s].source},{stream: s}); browserequests++; } } if (browserequests == 0) { mist.send(function(){ updateStreams(); },{active_streams: true}); UI.interval.set(function(){ updateStreams(); },30e3); } } else { mist.send(function(){ updateStreams(); },{active_streams: true}); UI.interval.set(function(){ updateStreams(); },10e3); } break; case 'Edit Stream': if (typeof mist.data.capabilities == 'undefined') { mist.send(function(d){ UI.navto(tab,other); },{capabilities: true}); $main.append('Loading..'); return; } var editing = false; if (other != '') { editing = true; } if (!editing) { //new $main.html( $('').text('New Stream') ); var saveas = {}; } else { //editing var streamname = other; var saveas = mist.data.streams[streamname]; $main.find('h2').append(' "'+streamname+'"'); } var filetypes = []; for (var i in mist.data.capabilities.inputs) { filetypes.push(mist.data.capabilities.inputs[i].source_match); } var $inputoptions = $(''); $main.append(UI.buildUI([ { label: 'Stream name', type: 'str', validate: ['required','streamname'], pointer: { main: saveas, index: 'name' }, help: 'Set the name this stream will be recognised by for players and/or stream pushing.' },{ label: 'Source', type: 'browse', validate: ['required'], filetypes: filetypes, pointer: { main: saveas, index: 'source' }, help: 'Set the stream source.VoD:You can browse to the file or folder as a source or simply enter the path to the file.Live:You\'ll need to enter "push://IP" with the IP of the machine pushing towards MistServer.You can use "push://" to accept any source.(Pro only)Use "push://(IP)@password" to set a password protection for pushes.If you\'re unsure how to set the source properly, please view our Live pushing guide at the tools section.', 'function': function(){ var source = $(this).val(); if (source == '') { return; } var type = null; for (var i in mist.data.capabilities.inputs) { if (typeof mist.data.capabilities.inputs[i].source_match == 'undefined') { continue; } if (mist.inputMatch(mist.data.capabilities.inputs[i].source_match,source)) { type = i; break; } } if (type === null) { $inputoptions.html( $('').text('Unrecognized input').addClass('red') ).append( $('').text('Please edit the stream source.').addClass('red') ); return; } var input = mist.data.capabilities.inputs[type]; $inputoptions.html( $('').text(input.name+' Input options') ); var build = mist.convertBuildOptions(input,saveas); if (('always_match' in mist.data.capabilities.inputs[i]) && (mist.inputMatch(mist.data.capabilities.inputs[i].always_match,source))) { build.push({ label: 'Always on', type: 'checkbox', help: 'Keep this input available at all times, even when there are no active viewers.', pointer: { main: saveas, index: 'always_on' } }); } $inputoptions.append(UI.buildUI(build)); } },{ label: 'Stop sessions', type: 'checkbox', help: 'When saving these stream settings, kill this stream\'s current connections.', pointer: { main: saveas, index: 'stop_sessions' } },$(''),{ type: 'custom', custom: $inputoptions },$(''),$('').text('Encryption'),{ type: 'help', help: 'To enable encryption, the licence acquisition url must be entered, as well as either the content key or the key ID and seed.Unsure how you should fill in your encryption or missing your preferred encryption? Please contact us.' },{ label: 'License acquisition url', type: 'str', LTSonly: true, pointer: { main: saveas, index: 'la_url' } },$(''),{ label: 'Content key', type: 'str', LTSonly: true, pointer: { main: saveas, index: 'contentkey' } },{ type: 'text', text: ' - or - ' },{ label: 'Key ID', type: 'str', LTSonly: true, pointer: { main: saveas, index: 'keyid' } },{ label: 'Key seed', type: 'str', LTSonly: true, pointer: { main: saveas, index: 'keyseed' } },{ type: 'buttons', buttons: [ { type: 'cancel', label: 'Cancel', 'function': function(){ UI.navto('Streams'); } },{ type: 'save', label: 'Save', 'function': function(){ if (!mist.data.streams) { mist.data.streams = {}; } mist.data.streams[saveas.name] = saveas; if (other != saveas.name) { delete mist.data.streams[other]; } var send = {}; if (mist.data.LTS) { send.addstream = {}; send.addstream[saveas.name] = saveas; if (other != saveas.name) { send.deletestream = [other]; } } else { send.streams = mist.data.streams; } if ((saveas.stop_sessions) && (other != '')) { send.stop_sessions = other; delete saveas.stop_sessions; } mist.send(function(){ delete mist.data.streams[saveas.name].online; delete mist.data.streams[saveas.name].error; UI.navto('Streams'); },send); } } ] } ])); break; case 'Preview': if (other == '') { $main.append('Loading..'); function selectastream(select) { var saveas = {}; select.sort(); $main.html( $('').text(tab) ).append(UI.buildUI([ { label: 'Select a stream', type: 'select', select: select, pointer: { main: saveas, index: 'stream' } },{ type: 'buttons', buttons: [{ type: 'save', label: 'Go', 'function': function(){ UI.navto(tab,saveas.stream); } }] } ])); UI.elements.secondary_menu.html('').append( $('').addClass('button').addClass('active').text('Choose stream').click(function(){ UI.navto('Preview'); }) ); var $shortcuts = $('').addClass('preview_icons'); $main.append( $('').addClass('description').text('Or, click a stream from the list below.') ).append($shortcuts); for (var i in select) { var streamname = select[i]; var source = ''; if (streamname.indexOf('+') > -1) { var streambits = streamname.split('+'); source = mist.data.streams[streambits[0]].source+streambits[1]; } else { source = mist.data.streams[streamname].source; } $shortcuts.append( $('').append( $('').text(streamname) ).append( $('').addClass('description').text(source) ).attr('title',streamname).attr('data-stream',streamname).click(function(){ UI.navto('Preview',$(this).attr('data-stream')); }) ); } } if (mist.data.LTS) { if (typeof mist.data.capabilities == 'undefined') { mist.send(function(d){ UI.navto(tab,other); },{capabilities: true}); $main.append('Loading..'); return; } //insert folder streams var browserequests = 0; var browsecomplete = 0; var select = {}; for (var s in mist.data.streams) { var inputs_f = mist.data.capabilities.inputs.Folder || mist.data.capabilities.inputs['Folder.exe']; if (mist.inputMatch(inputs_f.source_match,mist.data.streams[s].source)) { //this is a folder stream mist.send(function(d,opts){ var s = opts.stream; for (var i in d.browse.files) { for (var j in mist.data.capabilities.inputs) { if ((j.indexOf('Buffer') >= 0) || (j.indexOf('Folder') >= 0)) { continue; } if (mist.inputMatch(mist.data.capabilities.inputs[j].source_match,'/'+d.browse.files[i])) { select[s+'+'+d.browse.files[i]] = true; } } } browsecomplete++; if (browserequests == browsecomplete) { mist.send(function(){ for (var i in mist.data.active_streams) { var split = mist.data.active_streams[i].split('+'); if ((split.length > 1) && (split[0] in mist.data.streams)) { select[mist.data.active_streams[i]] = true; } } select = Object.keys(select); select = select.concat(Object.keys(mist.data.streams)); select.sort(); selectastream(select); },{active_streams: true}); } },{browse:mist.data.streams[s].source},{stream: s}); browserequests++; } } if (browserequests == 0) { mist.send(function(){ var select = []; for (var i in mist.data.active_streams) { var split = mist.data.active_streams[i].split('+'); if ((split.length > 1) && (split[0] in mist.data.streams)) { select[mist.data.active_streams[i]] = true; } } if (mist.data.streams) { select = select.concat(Object.keys(mist.data.streams)); } if (select.length == 0) { $main.html( $('').text(tab) ).append( 'Please set up a stream first.' ); return; } select.sort(); selectastream(select); },{active_streams: true}); } } else { selectastream(Object.keys(mist.data.streams)); } return; } $main.find('h2').append(' of "'+other+'"'); var http_port = ':8080'; for (var i in mist.data.config.protocols) { var protocol = mist.data.config.protocols[i]; if ((protocol.connector == 'HTTP') || (protocol.connector == 'HTTP.exe')) { http_port = (protocol.port ? ':'+protocol.port : ':8080'); } } //actual page var tabs = {}; var $embedlinks = $('').hide(); tabs['Embed urls'] = $embedlinks; $main.append($embedlinks); function parseURL(url) { var a = document.createElement('a'); a.href = url; return { protocol: a.protocol+'//', host: a.hostname, port: (a.port ? ':'+a.port : '') }; } var parsed = parseURL(mist.user.host); var embedbase = parsed.protocol+parsed.host+http_port+'/'; var embedoptions = {autoplay: true}; function embedhtml(opts) { var open = ['div']; var inner = "\n"+'